

core_routines.asm
As was mentioned earlier on the page “C64 Assembly Project Main Setup”, the WaitFrame subroutine below is used to check individual scan lines that the display generates (from an electronical level) to draw a picture on the screen. These scan lines can be accessed with the variable (VIC_RASTER_LINE) from memory location $D012 (53266). The first example just utilizes a delay to control the game timing at line $F8 (248). Recall earlier that the display can read up to 262 scan lines on the American monitors and 312 scan lines on a European monitor. So this line 248 is almost reaching the bottom of the screen, which is waiting nearly for a complete screen refresh rate. It is also repeated here in the routine (WaitStep2) to ensure a more smoother game display.
; Wait for the raster to reach line $f8 - if it's aleady there, wait for ; the next screen blank. This prevents mistimings if the code runs too fast
WaitFrame
lda VIC_RASTER_LINE ; fetch the current raster line cmp #$F8 ; wait here till l beq WaitFrame
@WaitStep2
lda VIC_RASTER_LINE cmp #$F8 bne @WaitStep2 rts
Now we begin to increase the variable (TIMER) in the subroutine called UpdateTimers. We slowly cycle through 16 bits of information (using our AND mask). After this it transfers to the second routine where the variable (SLOW_TIMER) kicks off yet another timer.
; 2 basic timers - a fast TIMER that is updated every frame,
; and a SLOW_TIMER updated every 16 frames
;-----------------------------------------------------------------------
UpdateTimers
inc TIMER ; increment TIMER by 1
lda TIMER
and #$0F ; check if it's equal to 16
beq @updateSlowTimer ; if so we update SLOW_TIMER
rts
@updateSlowTimer inc SLOW_TIMER ; increment slow timer rts
; Trying this a different way this time. Rather than hitting the joystick registers then ; doing something every time - The results will be stored in JOY_X and JOY_Y with values ; -1 to 1 , with 0 meaning 'no input' - I should be able to just add this to a sprite for a ; simple move, while still being able to do an easy check for more complicated movement ; later on ;-------------------------------------------------------------------------------------------
At the beginning of our subroutine ReadJoystick we need to clear out the variables (JOY_X) and (JOY_Y) to ensure no joystick movement values remain from a previous read. Then we can check for all individual movement as seen in the variables below:
checkup = Waits until the joystick is pressed up before writing a value there.
checkdown = Waits until the joystick is pressed up before writing a value there.
ReadJoystick
lda #$00 ; Reset JOY X and Y variables sta JOY_X sta JOY_Y sta NE_DIR
@testUp
; Test for Up pressed lda checkup ; Mask for bit 0 bit JOY_2 ; test bit 0 for press bne @testDown lda #$FF ; set JOY_Y to -1 ($FF) sta JOY_Y jmp @testLeft ; Can't be up AND down
@testDown ; Test for Down
lda checkdown ; Mask for bit 1
bit JOY_2
bne @testLeft
lda #$01 ; set JOY_Y to 1 ($01)
sta JOY_Y
rts
Now we begin testing our other joystick values.
checkleft = Waits until the joystick is pressed up before writing a value there.
checkright = Waits until the joystick is pressed up before writing a value there.
@testLeft ; Test for Left lda checkleft ; Mask for bit 2 bit JOY_2 bne @testRight lda #$FF sta JOY_X rts
@testRight ; Test for Right lda checkright ; Mask for bit 3 bit JOY_2 bne @checkUpLeft lda #$01 sta JOY_X rts
Finally we test for a button press (using the binary mask #%0001000). Then we test for the joystick down to the right as seen below.
checkdownright = Waits until the joystick is pressed down to the right before writing a value there in the variable (NE_DIR).
@checkUpLeft ; check zero = button pressed ; continue other checks ; no more checks
lda #%00010000 bit JOY_2 ; check zero = button pressed bne @testDownRight ; continue other checks
@testUpLeft
lda #1
sta NE_DIR
rts
@testDownRight ; Test for Right
lda checkdownright ; Mask for bit 3
bit JOY_2
bne @done
lda #$02
sta NE_DIR
rts
@done ; Nothing pressed
rts
; Notifies the state of the fire button on JOYSTICK 2.
; BUTTON_ACTION is set to one on a single press (that is when the button is released)
; BUTTON_PRESSED is set to 1 while the button is held down.
; So either a long press, or a single press can be accounted for.
; TODO I might put a 'press counter' in here to test how long the button is down for..
;--------------------------------------------------------------------------------------
In the subroutine below JoyButton we check the variable (BUTTON_ACTION) to see if the fire button was pressed and then set a value in variable (JOY_2). So if the fire button was pressed a value is set in (BUTTON_ACTION).
The next routine below this will check the variable (BUTTON_PRESSED) to see if the fire button was actually pressed. So once the button is pressed we clear the variable (BUTTON_PRESSED) and then set the value in (BUTTON_ACTION).
JoyButton
lda #1 ; checks for a previous button action
cmp BUTTON_ACTION ; and clears it if set
bne @buttonTest
lda #0
sta BUTTON_ACTION
@buttonTest
lda #$10 ; test bit #4 in JOY_2 Register
bit JOY_2
bne @buttonNotPressed
lda #1 ; if it's pressed - save the result
sta BUTTON_PRESSED ; and return - we want a single press
rts
@buttonNotPressed lda BUTTON_PRESSED ; and check to see if it was pressed first bne @buttonAction ; if it was we go and set BUTTON_ACTION rts
@buttonAction
lda #0 sta BUTTON_PRESSED lda #1 sta BUTTON_ACTION rts
; Copy the custom character set into the VIC Memory Bank (2048 bytes)
; ZEROPAGE_POINTER_1 = Source
; ZEROPAGE_POINTER_2 = Dest
;
; Returns A,X,Y and PARAM2 intact
;-------------------------------------------------------------------------------------------
CopyChars
saveRegs
ldx #$00 ; clear X, Y, A and PARAM2
ldy #$00
lda #$00
sta PARAM2
@NextLine ; CHAR_MEM = ZEROPAGE_POINTER_1 ; LEVEL_1_CHARS = ZEROPAGE_POINTER_2 lda (ZEROPAGE_POINTER_1),Y ; copy from source to target sta (ZEROPAGE_POINTER_2),Y
The first part of the subroutine CopyChars will copy source data contained in ZEROPAGE_POINTER_1 to ZEROPAGE_POINTER_2). So earlier when we passed information into the zero page pointers it pointed to the information here:
ZEROPAGE_POINTER_1 = CHAR_MEM (This contains the character set data loaded from CharPad)
ZEROPAGE_POINTER_2 = LEVEL_1_CHARS (This contains the map level data loaded from CharPad)
saveRegs
ldx #$00 ; clear X, Y, A and PARAM2
ldy #$00
lda #$00
sta PARAM2
@NextLine ; CHAR_MEM = ZEROPAGE_POINTER_1 ; LEVEL_1_CHARS = ZEROPAGE_POINTER_2 lda (ZEROPAGE_POINTER_1),Y ; copy from source to target sta (ZEROPAGE_POINTER_2),Y inx ; increment x / y iny cpx #$08 ; test for next character block (8 bytes) bne @NextLine ; copy next line cpy #$00 ; test for edge of page (256 wraps back to 0) bne @PageBoundryNotReached inc ZEROPAGE_POINTER_1 + 1 ; if reached 256 bytes, increment high byte inc ZEROPAGE_POINTER_2 + 1 ; of source and target
Then we begin to copy 8 characters and continue copying until we have reached 256 characters.
@PageBoundryNotReached
inc PARAM2 ; Only copy 254 characters (to keep irq vectors intact)
lda PARAM2 ; If copying to F000-FFFF block
cmp #255
beq @CopyCharactersDone
ldx #$00
jmp @NextLine
@CopyCharactersDone restoreRegs
rts
The first part of the subroutine (WaterAnimation) extracts data values from several tables that contain our animation.
ZEROPAGE_POINTER_1 (low and high bytes) = RIVER_ANIM1_LO/HI bytes.
ZEROPAGE_POINTER_2 (low and high bytes) = RIVER_ANIM2_LO/HI bytes.
ZEROPAGE_POINTER_3 (low and high bytes) = RIVER_ANIM3_LO/HI bytes.
ZEROPAGE_POINTER_4 (low and high bytes) = RIVER_ANIM4_LO/HI bytes.
WaterAnimation
lda RIVER_ANIM1_LO
sta ZEROPAGE_POINTER_1
lda RIVER_ANIM1_HI
sta ZEROPAGE_POINTER_1 + 1
lda RIVER_ANIM2_LO
sta ZEROPAGE_POINTER_2
lda RIVER_ANIM2_HI
sta ZEROPAGE_POINTER_2 + 1
lda RIVER_ANIM3_LO
sta ZEROPAGE_POINTER_3
lda RIVER_ANIM3_HI
sta ZEROPAGE_POINTER_3 + 1
lda RIVER_ANIM4_LO
sta ZEROPAGE_POINTER_4
lda RIVER_ANIM4_HI
sta ZEROPAGE_POINTER_4 + 1
ldy #0 ldx #2 ; Still looping the animation @shiftPixelsRight lda (ZEROPAGE_POINTER_1),y lsr a bcc @store2 clc adc #128 ; Get all 8 bits (128,64,32,16,8,4,2,1)
@store2
sta (ZEROPAGE_POINTER_1),y
lda (ZEROPAGE_POINTER_2),y
lsr a
bcc @store3
clc
adc #128
@store3
sta (ZEROPAGE_POINTER_2),y
lda (ZEROPAGE_POINTER_3),y
lsr a
bcc @store4
clc
adc #128
@store5
sta (ZEROPAGE_POINTER_4),y
iny ; This comparison checks for the tile width + (all 4 tiles)
; In a 4x4 matrix. Because 4 x 4 = 16
cpy #28
bcc @shiftPixelsRight
rts
UPLEFT byte 0
UPRIGHT byte 0
RIVER_ANIM1_LO byte <CHRADR1
RIVER_ANIM1_HI byte >CHRADR1
RIVER_ANIM2_LO byte <CHRADR2
RIVER_ANIM2_HI byte >CHRADR2
RIVER_ANIM3_LO byte <CHRADR3
RIVER_ANIM3_HI byte >CHRADR3
RIVER_ANIM4_LO byte <CHRADR4
RIVER_ANIM4_HI byte >CHRADR4
Y_ANIM_COUNT byte 0
[…] jsr CopyChars lda PARAM1 ; restore ram setup sta PROC_PORT […]