Skip to content

Instantly share code, notes, and snippets.

@ytmytm
Last active April 6, 2022 21:58
Show Gist options
  • Save ytmytm/265d6ae1f5b1df7bd7ef0ba67bdaa816 to your computer and use it in GitHub Desktop.
Save ytmytm/265d6ae1f5b1df7bd7ef0ba67bdaa816 to your computer and use it in GitHub Desktop.
VDC setup for 256x200 bitmap mode
// VDC 256x200 modes
// by YTM/Elysium, 2022
//
// in C64 mode (with cartridge or due to holding C= key) call VDC_Init first
// in C128 mode proper VDC timings are already initialized by Kernal
//
// (for KickAssembler)
// BasicUpstart128 macro from https://github.com/wiebow/examples.c128
.macro BasicUpstart128(address) {
.pc = $1c01 "C128 Basic"
.word upstartEnd // link address
.word 10 // line num
.byte $9e // sys
.text toIntString(address)
.byte 0
upstartEnd:
.word 0 // empty link signals the end of the program
.pc = $1c0e "Basic End"
}
///////////////////////////////////////////////////////////////////////////////////////
// vdc.h
// I/O registers (available also in 64 mode)
.const VDC_REG = $d600 // VDC address/status register, bits on read: 7=status, 6=lightpen, 5=vblank
.const VDC_DATA_REG = $d601 // VDC data register
// VDC internal registers
.const VDC_HTOTAL =0 // horizontal total
.const VDC_HDISPLAYED =1 // horizontal displayed
.const VDC_HSYNCPOS =2 // horizontal sync position
.const VDC_VHSYNCW =3 // vertical/horizontal sync width
.const VDC_VTOTAL =4 // vertical total
.const VDC_VTOTALFINE =5 // vertical total fine adjustment
.const VDC_VDISPLAYED =6 // vertical displayed
.const VDC_VSYNCPOS =7 // vertical sync position
.const VDC_INTERLACE =8 // interlace mode
.const VDC_CVTOTAL =9 // character vertical total
.const VDC_CSRMODE =10 // cursor mode/start scanline
.const VDC_CSREND =11 // cursor end scanline
.const VDC_DSPHI =12 // display start (hi)
.const VDC_DSPLO =13 // display start (lo)
.const VDC_CSRHI =14 // cursor position (hi)
.const VDC_CSRLO =15 // cursor position (lo)
.const VDC_VLPEN =16 // light pen vertical
.const VDC_HLPEN =17 // light pen horizontal
.const VDC_DATAHI =18 // update address (hi)
.const VDC_DATALO =19 // update address (lo)
.const VDC_ATTHI =20 // attribute map address (hi)
.const VDC_ATTLO =21 // attribute map address (lo)
.const VDC_CHSIZE =22 // character horizontal size control
.const VDC_VCPSPC =23 // vertical character pixel space
.const VDC_VSCROLL =24 // block/rvs/vertical scroll
.const VDC_HSCROLL =25 // diff. mode sw./horizontal scroll
.const VDC_COLORS =26 // fore/background colors
.const VDC_ROWINC =27 // row address increment
.const VDC_CSET =28 // character set A13-15, ram size
.const VDC_ULINE =29 // underline scanline
.const VDC_COUNT =30 // word count (-1)
.const VDC_DATA =31 // data
.const VDC_SRCHI =32 // block copy source (hi)
.const VDC_SRCLO =33 // block copy source (lo)
.const VDC_DEBEGIN =34 // display enable begin
.const VDC_DEEND =35 // display enable end
.const VDC_REFRESH =36 // DRAM refresh rate
///////////////////////////////////////////////////////////////////////////////////////
.const SCREEN_BASE =$0000 // screen base in VDC RAM
.const CPU_SCREEN_BASE =$0400 // screen buffer base in C128 RAM (for VDC_BlitBitmap_)
// C128 native
//BasicUpstart128(start)
// C128 in C64 mode
BasicUpstart2(start)
start: // jump tab to test things from BASIC - FAST:BANK15:SYSDEC("1C0F")+{0,3,6,9,...}:SLOW
// in C64 mode: SYS 2063+{0,3,6,9}
rts
jmp VDC_Init
jmp VDC_Start_256x200_Narrow
jmp VDC_Start_256x200_Wide
jmp VDC_BlitBitmap
///////////////////////////////////////////////////////////////////////////////////////
// VDC support functions
VDC_WriteReg: // X = register #
stx VDC_REG
!: bit VDC_REG
bpl !-
sta VDC_DATA_REG
rts
VDC_WriteWordReg: // A=address HI, Y=address LO, X=VDC register of address HI (e.g. VDC_DSPHI)
stx VDC_REG
!: bit VDC_REG
bpl !-
sta VDC_DATA_REG
inx
stx VDC_REG
sty VDC_DATA_REG // no need to wait for status bit here
rts
.macro VDC_POKE(reg,value) {
ldx #reg
lda #value
jsr VDC_WriteReg
}
///////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////
// VDC init after power on
// this is needed only if booting directly into C64 mode (with cartridge or due to holding C= key)
// skipped font copying routine (can be extracted from ROM @ CE1C or from my LUnix Next Generation vdc_console_init.s)
VDC_Init:
ldx #0
!: lda vdcinitab,x
cpx #VDC_COUNT // ignore this...
beq !+
cpx #VDC_DATA // ...and that
beq !+
jsr VDC_WriteReg
!: inx
cpx #VDC_REFRESH+1
bne !--
rts
vdcinitab: // default, power-on VDC register values for C128 08-column text mode, PAL
.byte $7e // 0
.byte 80 // 1 - # of columns
.byte $66, $49, $26, $00// 2-5
.byte 25 // 6 - # of lines
.byte $20 // 7
.byte %00000000 // 8 - interlace - none
.byte %00000111 // 9 - 8 pixels for line
.byte %00100000 // 10 - cursor not visible, start 0
.byte %00000111 // 11 - cursor end 7 (block cursor)
.byte 0,0 // 12/13 - current screen position
.byte 0,0 // 14/15 - current cursor position
.byte 0,0 // 16/17 - r/ - lightpen position
.byte 0,0 // 18/19 - current data address
.byte 0,0 // 20/21 - attribute map position
.byte $78, $08 // 22-23
.byte %00000000 // 24 - block mode fill
// - non-reversed display
.byte %00000000 // 25 - text display
// - monochrome, w/o attribute map
// - pixel= 1dot clock
.byte %11110000 // 26 - colors (text/back)
.byte 0 // 27
.byte $20 // 28 - A13-15 of font, bit 4 - ram size (0=16K, unreliable)
.byte $07 // 29
.byte $4f, $20 // 30 - # of fill/copy cycles; 31 - data for write/fill
.byte 0,0 // 32/33 - source address of block copy
.byte $7d,$64 // 34/35 - start/end of display
.byte $05 // 36 - # of DRAM refresh cycles
vdcinitabend:
///////////////////////////////////////////////////////////////////////////////////////
// 256x200 mode
VDC_Start_256x200_Narrow:
VDC_POKE(VDC_HSCROLL, %10000111) // gfx mode, monochrome, not stretched
VDC_POKE(VDC_HDISPLAYED,32) // 32 displayed columns
VDC_POKE(VDC_HTOTAL, 127) // all of total horizontal columns
VDC_POKE(VDC_HSYNCPOS, 78) // recenter at 102-(80-32)/2, 102 is the default
VDC_POKE(VDC_COLORS, $E0) // light gray (dark white) on black
VDC_POKE(VDC_CHSIZE, $78) // default character horizontal size (just to be sure if switching between standard and double-pixel mode)
VDC_POKE(VDC_REFRESH, 0) // reduce DRAM refresh rate
lda #>SCREEN_BASE
ldy #<SCREEN_BASE
ldx #VDC_DSPHI
jsr VDC_WriteWordReg
rts
///////////////////////////////////////////////////////////////////////////////////////
// 256x200 double-pixel mode
VDC_Start_256x200_Wide:
VDC_POKE(VDC_HSCROLL, %10010111) // gfx mode, monochrome, stretched
VDC_POKE(VDC_HDISPLAYED,32) // 32 displayed columns
VDC_POKE(VDC_HTOTAL, 63) // half of total horizontal columns
VDC_POKE(VDC_HSYNCPOS, 51) // recenter screen
VDC_POKE(VDC_COLORS, $E0) // light gray (dark white) on black
VDC_POKE(VDC_CHSIZE, $89) // increase character horizontal size by 1 to make double pixel mode work
VDC_POKE(VDC_REFRESH, 0) // reduce DRAM refresh rate
lda #>SCREEN_BASE
ldy #<SCREEN_BASE
ldx #VDC_DSPHI
jsr VDC_WriteWordReg
rts
///////////////////////////////////////////////////////////////////////////////////////
// copy bitmap (256x200=6400 bytes) from C128 RAM to VDC RAM
VDC_BlitBitmap:
// setup chip ram address
lda #>SCREEN_BASE
ldy #<SCREEN_BASE
ldx #VDC_DATAHI
jsr VDC_WriteWordReg
// setup write to VDC_DATA register
ldx #VDC_DATA
stx VDC_REG
// setup fast ram address
lda #<CPU_SCREEN_BASE
ldx #>CPU_SCREEN_BASE
sta loop+1
stx loop+2
ldx #0
ldy #0
loop: lda $0000,y
!: bit VDC_REG
bpl !-
sta VDC_DATA_REG
iny
bne loop
inc loop+2
inx
cpx #$19
bne loop
rts
@ytmytm
Copy link
Author

ytmytm commented Apr 6, 2022

VDC_Init - initialize VDC video and timings, run this in C64 mode only; no need to do this in C128 native mode - Kernal does it for us

VDC_Start_256x200_Narrow - start monochrome bitmap mode 256x200 with narrow pixels (wrong aspect ratio); screen base is setup at $0000. Line 0 starts at SCREEN_BASE+0, line 1 at SCREEN_BASE+32

VDC_Start_256x200_Wide - same but in double-pixel mode; should look the same as VIC

VDC_BlitBitmap - copy (200*32=) $1900 bytes from CPU_SCREEN_BASE into VDC SCREEN_BASE

Note: VICE doesn't emulate VDC synchronization properly. Sync code (bit / bpl !-) in VDC_BlitBitmap can be removed and the result remains a perfect memory copy. That is not possible on real hardware, VDC would drop some bytes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment