<< Back to contents.

CharPad 2.3 User Manual - Subchrist Software, 2019.

VIC-II Sub-routine Library.

The code below can be copied to a plain text file, included in a project and assembled using the 64TASS (or similar) assembler.


; vic_lib.asm

; A small library of sub-routines for performing common tasks on the
; Commodore 64's VIC-II video chip.

; Author    : Stewart Wilson, Subchrist Software.
; Created   : Sept 2012
; Updated   : Jan 2017
; Platform  : Commodore 64
; Processor : MOS 6510
; Assembler : 64TASS 1.46


; Contents...

; vic_select_bank    (A = 16KB video bank, 0-3)
; vic_select_vmatrix (A = 1KB video matrix, 0-15)
; vic_select_charset (A = 2KB character set, 0-7)
; vic_select_bitmap  (A = 8KB bitmap image, 0 or 1)
; vic_blank_on
; vic_blank_off
; vic_mode_text
; vic_mode_bitmap
; vic_mode_ecm
; vic_multicolour_on
; vic_multicolour_off
; vic_fatborderLR_on 
; vic_fatborderLR_off
; vic_fatborderTB_on 
; vic_fatborderTB_off 
; vic_reset_scroll
; vic_reset_sprites
; vic_set_scroll (X,Y = offsets)
; vic_wait_offmatrix
; vic_wait_offscreen
; vic_wait_one_sec
; vic_getbanknumber (result in A)
; vic_getmatrixnumber (result in A)
; vic_getmatrixaddress (result in A)
; vic_fill_cmatrix (A = colour nybble value)
; vic_fill_vmatrix (A = byte value)
; vic_test_vmatrix 



; Define some colour constants...

BLACK      = 0
WHITE      = 1
RED        = 2
CYAN       = 3
PURPLE     = 4
GREEN      = 5
BLUE       = 6
YELLOW     = 7
ORANGE     = 8
BROWN      = 9
PINK       = 10
DARKGREY   = 11
GREY       = 12
LIGHTGREEN = 13
LIGHTBLUE  = 14
LIGHTGREY  = 15


;---------------------------------------------
; vic_select_bank - selects a 16KB video bank.
;
; parameters: A = video bank (0-3).
; returns: none.
;
; video bank 0 is $0000-$3fff (nb. char ROM (4KB) available at $1000-$1fff, slots 2 & 3).
; video bank 1 is $4000-$7fff
; video bank 2 is $8000-$bfff (nb. char ROM (4KB) available at $9000-$9fff, slots 2 & 3).
; video bank 3 is $c000-$ffff
;---------------------------------------------

vic_select_bank

and #3     ; be sure to only use a 2-bit parameter value (0-3).
eor #3     ; binary invert the parameter value.
sta vsb_b1 ; store the result temporarily.
lda $dd02  ; read Port-A Data Direction register (CIA #2).
ora #3     ; set the lowest two bits (outputs).
sta $dd02  ; write Port-A Data Direction register (CIA #2).
lda $dd00  ; read Port-A data register (CIA#2).
and #$fc   ; preserve the upper 6 bits, clear the lower 2.
ora vsb_b1 ; add in the adjusted 2-bit parameter value.
sta $dd00  ; write Port-A data register (CIA#2).
rts

vsb_b1 .byte 0

;-------------------------------------------------
; vic_select_vmatrix - selects a 1KB video matrix.
;
; parameters: A = video matrix (0-15).
; returns: none.
;-------------------------------------------------

vic_select_vmatrix

and #$0f   ; be sure to only use a 4-bit parameter value (0-f).
asl        ; shift the value to the high nybble...
asl
asl
asl
sta vsm_b1 ; store the result temporarily.
lda $d018  ; read the VIC-II pointer register.
and #$0f   ; preserve the low nybble, clear the high nybble.
ora vsm_b1 ; add in the adjusted 4-bit parameter value.
sta $d018  ; write the VIC-II pointer register.
rts

vsm_b1 .byte 0

;--------------------------------------------------
; vic_select_charset - selects a 2KB character set.
;
; parameters: A = character set (0-7).
; returns: none.
;--------------------------------------------------

vic_select_charset

and #7     ; be sure to only use a 3-bit parameter value (0-7).
asl        ; shift the parameter value left one binary place.
sta vsc_b1 ; store the result temporarily.
lda $d018  ; read the VIC-II pointer register.
and #$f0   ; preserve the upper nybble, clear the lower nybble.
ora vsc_b1 ; add in the adjusted 3-bit parameter value.
sta $d018  ; write the VIC-II pointer register.
rts

vsc_b1 .byte 0

;-------------------------------------------------
; vic_select_bitmap - selects an 8KB bitmap image.
;
; parameters: A = bitmap image (0 or 1).
; returns: none.
;-------------------------------------------------

vic_select_bitmap

and #1     ; be sure to only use a 1-bit parameter value (0 or 1).
asl        ; shift the parameter left 3 binary places...
asl       
asl
sta vsi_b1 ; store the result temporarily.
lda $d018  ; read the VIC-II pointer register. 
and #$f0   ; preserve the upper nybble, clear the lower nybble.
ora vsi_b1 ; add in the adjusted 1-bit parameter value.
sta $d018  ; write the VIC-II pointer register.
rts

vsi_b1 .byte 0

;-------------------------------------------------------------------
; vic_blank_on - blanks the screen matrix area to the border colour.
;
; parameters: none.
; returns: none.
;-------------------------------------------------------------------

vic_blank_on

lda $d011
and #$ef ; clear bit 4 of vic control register 1.
sta $d011
rts

;-------------------------------------------------
; vic_blank_off - unblanks the screen matrix area.
;
; parameters: none.
; returns: none.
;-------------------------------------------------

vic_blank_off

lda $d011
ora #$10 ; set bit 4 of vic control register 1.
sta $d011
rts

;--------------------------------------------
; vic_mode_text - enables standard text mode.
;
; parameters: none.
; returns: none.
;--------------------------------------------

vic_mode_text

lda $d011
and #$1f ; clear bits 5,6,7 of vic control register 1.
sta $d011
rts

;---------------------------------------
; vic_mode_bitmap - enables bitmap mode.
;
; parameters: none.
; returns: none.
;---------------------------------------

vic_mode_bitmap

lda $d011
and #$1f ; clear bits 5,6,7 of vic control register 1.
ora #$20 ; set bit 5.
sta $d011
rts

;----------------------------------------------------
; vic_mode_ecm - enables "extended colour" text mode.
;
; parameters: none.
; returns: none.
;----------------------------------------------------

vic_mode_ecm

lda $d011
and #$1f ; clear bits 5,6,7 of vic control register 1.
ora #$40 ; set bit 6.
sta $d011
rts

;-----------------------------------------------------------------
; vic_multicolour_on - enables character/bitmap multi-colour mode.
;
; parameters: none.
; returns: none.
;-----------------------------------------------------------------

vic_multicolour_on

lda $d016
ora #$10 ; set bit 4 of vic control register 2.
sta $d016
rts

;-------------------------------------------------------------------
; vic_multicolour_off - disables character/bitmap multi-colour mode.
;
; parameters: none.
; returns: none.
;-------------------------------------------------------------------

vic_multicolour_off

lda $d016
and #$ef ; clear bit 4 of vic control register 2.
sta $d016
rts

;-----------------------------------------------
; vic_fatborderLR_on - enables "38 column" mode. 
;
; parameters: none.
; returns: none.
;-----------------------------------------------

vic_fatborderLR_on 

lda $d016
and #$f7 ; clear bit 3 of vic control register 2.
sta $d016
rts

;------------------------------------------------
; vic_fatborderLR_off - enables "40 column" mode. 
;
; parameters: none.
; returns: none.
;------------------------------------------------

vic_fatborderLR_off 

lda $d016
ora #8 ; set bit 3 of vic control register 2.
sta $d016
rts

;--------------------------------------------
; vic_fatborderTB_on - enables "24 row" mode.
;
; parameters: none.
; returns: none.
;--------------------------------------------

vic_fatborderTB_on 

lda $d011
and #$f7 ; clear bit 3 of vic control register 1.
sta $d011
rts

;---------------------------------------------
; vic_fatborderTB_off - enables "25 row" mode.
;
; parameters: none.
; returns: none.
;---------------------------------------------

vic_fatborderTB_off 

lda $d011
ora #8 ; set bit 3 of vic control register 1.
sta $d011
rts

;-----------------------------------------------------------------------------------------
; vic_reset_scroll - sets both screen scroll registers to their default values (x:0, y:3).
;
; parameters: none.
; returns: none.
;-----------------------------------------------------------------------------------------

vic_reset_scroll

lda $d016 ; reset horizontal scroll (0)...
and #$f8
sta $d016
lda $d011 ; reset vertical scroll (3)...
and #$f8
ora #3
sta $d011
rts

;--------------------------------------------------------------------------
; vic_reset_sprites - turns all sprites off + resets all sprite attributes.
;
; parameters: none.
; returns: none.
;--------------------------------------------------------------------------

vic_reset_sprites

     lda #0
     sta $d015   ; turn all sprites off.
     sta $d017   ; disable vertical expansion for all sprites.
     sta $d01d   ; disable horizontal expansion for all sprites.
     sta $d01b   ; give all sprites priority over background graphics.

     ldx #15     ; zero all sprite positions...
     lda #0      ; (clears $d000 - $d010)
vrs0 sta $d000,x
     dex
     bpl vrs0

     ldx #7      ; zero all sprite colours (black)...
     lda #0      ; (clears $d027 - $d02e)
vrs1 sta $d027,x
     dex
     bpl vrs1
     rts

;---------------------------------------------------------------------------------------
; vic_set_scroll - sets both the horizontal and vertical screen screen scroll registers.
;
; parameters: X,Y = horizontal and vertical scroll offsets (0-7).
; returns: none.
;---------------------------------------------------------------------------------------

vic_set_scroll

txa
and #7    ; ensure the parameter (x) is 0-7.
sta vss_x
tya
and #7    ; ensure the parameter (y) is 0-7.
sta vss_y

lda $d016
and #$f8
ora vss_x
sta $d016

lda $d011
and #$f8
ora vss_y
sta $d011
rts

vss_x .byte 0
vss_y .byte 0

;---------------------------------------------------------------------------------------
; vic_wait_offmatrix - waits for the raster beam to leave the visible matrix (line 251).
;
; parameters: none.
; returns: none.
;---------------------------------------------------------------------------------------

vic_wait_offmatrix

vwom0 lda $d012 ; wait for raster line 251...
      cmp #251
      bne vwom0
      rts

;---------------------------------------------------------------------------------------
; vic_wait_offscreen - waits for the raster beam to leave the visible screen (line 300).
;
; parameters: none.
; returns: none.
;---------------------------------------------------------------------------------------

vic_wait_offscreen

vwos0 lda $d011 ; wait for raster msb clear..
      and #$80
      bne vwos0
vwos1 lda $d011 ; wait for raster msb set (line 256)..
      and #$80
      beq vwos1
vwos2 lda $d012 ; wait for raster line 300 (256 + 44)...
      cmp #44
      bne vwos2
      rts

;---------------------------------------------------------------------------------
; vic_wait_one_sec - uses the raster beam to wait for approx one second (50 raster 
; cycles/PAL).
;
; nb. all used cpu registers are saved and restored after execution, this makes it
; simple to use this routine in a loop involving the cpu registers.
;
; parameters: none.
; returns: none.
;---------------------------------------------------------------------------------

vic_wait_one_sec

     pha ; save cpu registers...
     txa
     pha
     tya
     pha

     ldx #50
vws0 jsr vic_wait_offmatrix
     jsr vic_wait_offscreen
     dex
     bne vws0

     pla ; restore cpu registers...
     tay
     pla
     tax
     pla
     rts

;-----------------------------------------------------------
; vic_getbanknumber - returns the current video bank number.
;
; parameters: none.
; returns: A = current video bank number (0-3).
;-----------------------------------------------------------

vic_getbanknumber

lda $dd00 ; read the port-A data register of CIA#2.
and #3    ; keep the lowest 2 bits.
eor #3    ; invert them to give the video bank number.
rts

;---------------------------------------------------------------------
; vic_getmatrixnumber - returns the current screen matrix slot number.
;
; parameters: none.
; returns: A = active screen matrix slot number (0-15).
;---------------------------------------------------------------------

vic_getmatrixnumber

lda $d018 ; compute current matrix base address...
lsr
lsr
lsr
lsr
rts

;-----------------------------------------------------------------------------
; vic_getmatrixaddress - returns the active screen matrix address (high byte).
;
; parameters: none.
; returns: A = high byte of active screen matrix.
;-----------------------------------------------------------------------------

vic_getmatrixaddress

jsr vic_getbanknumber
asl
asl
asl
asl
asl
asl
sta vgma_b1

jsr vic_getmatrixnumber
asl
asl
sta vgma_b2

lda vgma_b1
clc
adc vgma_b2
rts

vgma_b1 .byte 0
vgma_b2 .byte 0

;-----------------------------------------------------------------------------------
; vic_fill_cmatrix - fills the colour matrix with a specified colour (nybble) value.
;
; nb. this fills only the 1000 "visible" nybbles of the (1024 * 4-bit) colour matrix. 
;
; parameters: A = colour (0-15).
; returns: none.
;-----------------------------------------------------------------------------------

vic_fill_cmatrix

     ldx #250    ; nb. the used counter values are 250 down to 1...   
vfc0 sta $d7ff,x ; instead of 249 down to 0...
     sta $d8f9,x ; this allows for a small code optimization.
     sta $d9f3,x ; in the final comparison (just a bne needed).
     sta $daed,x ; part of which is these addresses being one less than expected.
     dex         ; as this loop runs 250 times it makes a difference.
     bne vfc0    
     rts

;------------------------------------------------------------------------------
; vic_fill_vmatrix - fills the active video matrix with a specified byte value.
;
; nb. this fills only the 1000 "visible" bytes of the 1KB video matrix. 
;
; parameters: A = value.
; returns: none.
;------------------------------------------------------------------------------

vic_fill_vmatrix

     sta vfv_b0 ; save the parameter.

     lda #0 ; create a pointer to the active video matrix in zero page ($02,$03)...
     sta $02
     jsr vic_getmatrixaddress
     sta $03

     ldx #4 ; fill the matrix (4 x 250 = 1000 bytes)...
vfv0 ldy #0
     lda vfv_b0
vfv1 sta ($02),y
     iny
     cpy #250
     bne vfv1

     lda $02
     clc
     adc #250
     sta $02
     bcc vfv2
     inc $03

vfv2 dex
     bne vfv0
     rts

vfv_b0 .byte 0

;------------------------------------------------------------------------
; vic_test_vmatrix - fills the active video matrix with ascending values.
;
; nb. this fills the 1000 visible bytes of the 1KB video matrix. 
;
; parameters: none.
; returns: none.
;------------------------------------------------------------------------

vic_test_vmatrix

     lda #0 ; create a pointer to the active video matrix in zero page ($02,$03)...
     sta $02
     jsr vic_getmatrixaddress
     sta $03

     ldx #4 ; fill the matrix (4 x 250 = 1000 bytes)...
vtv0 ldy #0
vtv1 tya
     sta ($02),y
     iny
     cpy #250
     bne vtv1

     lda $02
     clc
     adc #250
     sta $02
     bcc vtv2
     inc $03

vtv2 dex
     bne vtv0
     rts