Files
rabbitc64/common.asm
T
2026-07-15 23:00:52 +02:00

649 lines
14 KiB
NASM

; -----------------------------------------------------------
; common.asm - routines shared by the states: screen drawing,
; text printing, input, RNG and the rabbit sprite positioning
; -----------------------------------------------------------
; ---- clear screen RAM, fill color RAM with black ----
clear_screen:
ldx #0
cls_loop:
lda #$20 ; space
sta SCREEN,x
sta SCREEN+$100,x
sta SCREEN+$200,x
sta SCREEN+$2e8,x
lda #BLACK
sta COLRAM,x
sta COLRAM+$100,x
sta COLRAM+$200,x
sta COLRAM+$2e8,x
inx
bne cls_loop
rts
; ---- print a text line: txtlo/hi, prow, pcol, pcolor, plen ----
print_text:
ldx prow
lda mul40lo,x
clc
adc pcol
sta scrlo
sta collo
lda mul40hi,x
adc #>SCREEN ; carry still valid from the low-byte add
sta scrhi
clc
adc #$d4 ; COLRAM - SCREEN high byte distance
sta colhi
ldy #0
pt_loop:
lda (txtlo),y
sta (scrlo),y
lda pcolor
sta (collo),y
iny
cpy plen
bne pt_loop
rts
; ---- set scrlo/hi + collo/hi to the start of char row X ----
row_ptr:
lda mul40lo,x
sta scrlo
sta collo
lda mul40hi,x
clc
adc #>SCREEN
sta scrhi
clc
adc #$d4 ; COLRAM - SCREEN high byte distance
sta colhi
rts
; ---- the road: grey surface between two painted edge lines,
; from the horizon apex down to the screen corners, then the
; trees along both shoulders ----
draw_road:
ldx #0
dr_loop:
stx drtmp
jsr road_row
ldx drtmp
inx
cpx #19
bne dr_loop
jmp draw_trees
; ---- redraw road row drtmp (0-18, char row 6+drtmp):
; edge chars + grey fill; also erases text printed over it ----
road_row:
lda drtmp
clc
adc #6 ; road runs from char row 6 to row 24
tax
jsr row_ptr
lda #19 ; left edge: one column left per row
sec
sbc drtmp
tay
sty drleft
lda #$4e ; '/' diagonal
sta (scrlo),y
lda #COL_EDGE
sta (collo),y
lda #20 ; right edge: one column right per row
clc
adc drtmp
tay
lda #$4d ; '\' diagonal
sta (scrlo),y
lda #COL_EDGE
sta (collo),y
dey ; fill the surface between the edges
rr_fill:
cpy drleft
beq rr_done
lda #$a0 ; inverse space: solid block
sta (scrlo),y
lda #COL_ROAD
sta (collo),y
dey
jmp rr_fill
rr_done:
rts
; ---- the roadside trees ----
draw_trees:
lda #0
sta ttidx
dt_loop:
ldx ttidx
lda tree_tbl,x
sta trow
lda tree_tbl+1,x
sta tcol
jsr draw_tree
inc ttidx
inc ttidx
lda ttidx
cmp #NTREES*2
bne dt_loop
rts
; ---- one tree: crown of balls on trow, top ball above,
; trunk below (trow/tcol = middle row, left column) ----
draw_tree:
ldx trow
dex
jsr row_ptr
ldy tcol
iny
jsr tree_ball
ldx trow
jsr row_ptr
ldy tcol
jsr tree_ball
iny
jsr tree_ball
iny
jsr tree_ball
ldx trow
inx
jsr row_ptr
ldy tcol
iny
lda #$5d ; trunk
sta (scrlo),y
lda #BROWN
sta (collo),y
rts
tree_ball:
lda #$51 ; foliage ball
sta (scrlo),y
lda #LGREEN
sta (collo),y
rts
NTREES = 6
tree_tbl: ; middle row, left column - far trees sit
!byte 9, 4 ; higher and closer to the road
!byte 14, 6
!byte 19, 3
!byte 8, 31
!byte 12, 34
!byte 17, 36
; ---- input ----
; read joystick port 2 merged with the a/d keys,
; return active-high bits in A and joyb
read_joy:
lda #$ff ; deselect every keyboard column first
sta CIA1_PA
lda CIA1_PA
eor #$ff
and #$1f
sta joyb
lda #$fd ; 'a' = left: keyboard column 1, row bit 2
sta CIA1_PA
lda CIA1_PB
and #$04
bne rj_noleft
lda joyb
ora #JOY_LEFT
sta joyb
rj_noleft:
lda #$fb ; 'd' = right: keyboard column 2, row bit 2
sta CIA1_PA
lda CIA1_PB
and #$04
bne rj_noright
lda joyb
ora #JOY_RIGHT
sta joyb
rj_noright:
lda joyb
rts
; read joystick port 1 merged with the j/l keys (player 2),
; return active-high bits in A and joyb2
read_joy2:
lda #$ff ; no column selected: port B = joystick 1
sta CIA1_PA
lda CIA1_PB
eor #$ff
and #$1f
sta joyb2
and #$0f ; a held direction grounds the same port B
bne rj2_done ; lines the key rows use: skip the scan
lda #$ef ; 'j' = left: keyboard column 4, row bit 2
sta CIA1_PA
lda CIA1_PB
and #$04
bne rj2_noleft
lda joyb2
ora #JOY_LEFT
sta joyb2
rj2_noleft:
lda #$df ; 'l' = right: keyboard column 5, row bit 2
sta CIA1_PA
lda CIA1_PB
and #$04
bne rj2_done
lda joyb2
ora #JOY_RIGHT
sta joyb2
rj2_done:
lda joyb2
rts
; fire on either stick or space held? returns A != 0 when pressed
start_pressed:
jsr read_joy
and #JOY_FIRE
bne sp_done
lda #$ff ; deselect the columns: port B = joystick 1
sta CIA1_PA
lda CIA1_PB
eor #$ff
and #JOY_FIRE
bne sp_done
lda #$7f ; space: keyboard column 7, row bit 4
sta CIA1_PA
lda CIA1_PB
and #$10
eor #$10
sp_done:
rts
; edge-detected start: A = 1 only on the frame fire/space goes down
start_edge:
jsr start_pressed
beq se_up
lda fheld
bne se_no
lda #1
sta fheld
rts ; A = 1: fresh press
se_up:
lda #0
sta fheld
se_no:
lda #0
rts
; ---- RNG: 8-bit LFSR stirred with the free-running CIA timer
; (the timer counts every cycle, so call timing = entropy) ----
rand8:
lda seed
asl
bcc r8_tap
eor #$1d
r8_tap:
eor CIA1_TA
sta seed
rts
; random lane 0-4
rand5:
jsr rand8
and #7
cmp #LANES
bcs rand5 ; every retry steps the LFSR, so it terminates
rts
; ---- write the per-frame sprite register shadows ----
write_shadows:
lda en_sh
sta SPR_EN
lda msb_sh
sta SPR_MSBX
lda xex_sh
sta SPR_XEX
lda yex_sh
sta SPR_YEX
rts
; ============================================================
; rab_pos - place rabbit slot X on hardware sprite X+1
; reads r_thi/r_lane, picks size stage + hop frame, writes
; the position registers and ORs the shadow bits; keeps X
; ============================================================
rab_pos:
stx rslot
lda r_thi,x
sta rt
lda r_lane,x
sta rlane
; hop offset: 8-entry wave, one hop per 64 t units
lda rt
lsr
lsr
lsr
and #7
tay
lda hoptab,y
sta rhop
; animation frame 0-3
lda rt
lsr
lsr
lsr
lsr
and #3
sta rfrm
lda #0
sta rexp
lda rt
cmp #T_BIG
bcs rp_big
; -- far: small frames --
lda #SPRP_SMALL
clc
adc rfrm
sta rptr
lda #12
sta ranchor
jmp rp_y
rp_big:
; -- near: big frames, stronger hop --
lda #SPRP_BIG
clc
adc rfrm
sta rptr
asl rhop
lda #12
sta ranchor
lda rt
cmp #T_EXP
bcc rp_y
; -- closest: the same frames double-sized --
lda #1
sta rexp
lda #24
sta ranchor
rp_y:
; sprite y: feet ride the perspective line (art bottom row)
ldy rt
lda ytab,y
sec
sbc rhop
sec
sbc #21
ldx rexp
beq rp_ydone
sec
sbc #21 ; expanded sprite is 42 tall
rp_ydone:
sta ry
; x center = ROAD_CX +/- lane offset on the t^2 curve
ldy rt
lda offtab,y
sta roff
lda #ROAD_CX
sta rxlo
lda #0
sta rxhi
ldy rlane
cpy #2
beq rp_xdone ; middle lane: stay on the center line
bcs rp_right
lda roff ; lanes 0-1: left of center
cpy #1
bne rp_lfull
lsr ; lane 1 fans out half as far
rp_lfull:
sta rtmp
lda rxlo
sec
sbc rtmp ; max offset is 107, never underflows
sta rxlo
jmp rp_xdone
rp_right:
lda roff ; lanes 3-4: right of center
cpy #3
bne rp_rfull
lsr
rp_rfull:
sta rtmp
lda rxlo
clc
adc rtmp
sta rxlo
lda rxhi
adc #0
sta rxhi
rp_xdone:
; sprite left edge = center - anchor (16 bit)
lda rxlo
sec
sbc ranchor
sta rxlo
lda rxhi
sbc #0
sta rxhi
; ---- write the hardware registers + shadow bits ----
ldx rslot
ldy sprxreg,x
lda rxlo
sta SPRPOS,y
lda ry
sta SPRPOS+1,y
lda rptr
sta SPRPTR+1,x
lda sprbit,x
ora en_sh
sta en_sh
lda rxhi
beq rp_nomsb
lda sprbit,x
ora msb_sh
sta msb_sh
rp_nomsb:
lda rexp
beq rp_done
lda sprbit,x
ora xex_sh
sta xex_sh
lda sprbit,x
ora yex_sh
sta yex_sh
rp_done:
rts
; ---- draw a text line with big 3x5 block letters ----
; txtlo/hi = text (screen codes), bigrow/bigcol = top-left cell,
; bigcolor = color, biglen = char count; each letter is 3 cells
; wide + 1 gap, 5 cells tall
draw_big:
lda #0
sta bigi
db_char:
ldy bigi
lda (txtlo),y
cmp #$20 ; space: leave the cell empty
beq db_next
tax
lda bigmap,x ; screen code -> glyph index
sta glyphbase
asl ; glyph base = index * 5
asl
clc
adc glyphbase
sta glyphbase
lda bigi ; cell x = bigcol + charindex * 4
asl
asl
clc
adc bigcol
sta charx
lda #0
sta rowidx
db_row:
lda bigrow ; row pointers from the row*40 tables
clc
adc rowidx
tax
lda mul40lo,x
sta scrlo
sta collo
lda mul40hi,x
clc
adc #>SCREEN
sta scrhi
clc
adc #$d4
sta colhi
lda glyphbase
clc
adc rowidx
tax
lda bigglyphs,x
asl ; 3-bit pattern up to bits 7-5
asl
asl
asl
asl
sta curbits
ldy charx
lda #3
sta bitcnt
db_bit:
asl curbits ; leftmost pixel first
bcc db_nobit
lda #$a0 ; inverse space = solid block
sta (scrlo),y
lda bigcolor
sta (collo),y
db_nobit:
iny
dec bitcnt
bne db_bit
inc rowidx
lda rowidx
cmp #5
bne db_row
db_next:
inc bigi
lda bigi
cmp biglen
beq db_done
jmp db_char ; too far for a relative branch
db_done:
rts
; ---- shared tables ----
; row * 40 lookup tables (25 rows)
mul40lo:
!byte $00, $28, $50, $78, $a0, $c8, $f0, $18, $40, $68
!byte $90, $b8, $e0, $08, $30, $58, $80, $a8, $d0, $f8
!byte $20, $48, $70, $98, $c0
mul40hi:
!byte $00, $00, $00, $00, $00, $00, $00, $01, $01, $01
!byte $01, $01, $01, $02, $02, $02, $02, $02, $02, $02
!byte $03, $03, $03, $03, $03
; 3x5 block font for the big title (5 bytes per glyph,
; low 3 bits used, MSB side = left)
bigglyphs:
!byte 0, 0, 0, 0, 0 ; 0 space
!byte 2, 5, 7, 5, 5 ; 1 A
!byte 3, 4, 4, 4, 3 ; 2 C
!byte 6, 5, 5, 5, 6 ; 3 D
!byte 7, 4, 6, 4, 7 ; 4 E
!byte 7, 4, 6, 4, 4 ; 5 F
!byte 7, 2, 2, 2, 7 ; 6 I
!byte 4, 4, 4, 4, 7 ; 7 L
!byte 5, 7, 7, 5, 5 ; 8 M
!byte 6, 5, 5, 5, 5 ; 9 N
!byte 7, 5, 5, 5, 7 ; 10 O
!byte 6, 5, 6, 4, 4 ; 11 P
!byte 6, 5, 6, 5, 5 ; 12 R
!byte 3, 4, 2, 1, 6 ; 13 S
!byte 7, 2, 2, 2, 2 ; 14 T
!byte 5, 5, 2, 2, 2 ; 15 Y
!byte 3, 4, 5, 5, 7 ; 16 G
!byte 6, 5, 6, 5, 6 ; 17 B
!byte 5, 5, 5, 5, 2 ; 18 V
bigmap: ; screen code (a=1..z=26) -> glyph index
!byte 0 ; (unused)
!byte 1, 17, 2, 3, 4, 5, 16, 0, 6, 0 ; a-j
!byte 0, 7, 8, 9, 10, 11, 0, 12, 13, 14 ; k-t
!byte 0, 18, 0, 0, 15, 0 ; u-z
; ---- texts (screen codes) ----
txt_score: !scr "score"
txt_miss: !scr "miss"
txt_rabbit: !scr "rabbit"
txt_scooter: !scr "scooter"
txt_1p: !scr "single player"
txt_2p: !scr "multiplayer"
txt_info: !scr "p1: joystick 2 or a/d"
txt_info2: !scr "p2: joystick 1 or j/l"
txt_ttgname: !scr "teletype games"
txt_presents:!scr "presents"
txt_over: !scr "game over"
txt_return: !scr "fire = return"
txt_blank: !fill 19, $20
washtbl: ; gold glow ramp (16 entries, cyclic)
!byte $09, $02, $08, $0a, $07, $07, $01, $01
!byte $01, $01, $07, $07, $0a, $08, $02, $09
; ---- shared variables ----
framectr: !byte 0 ; frame counter, incremented by the main loop
washphase: !byte 0 ; gold wash offset on the intro letters
bg_top: !byte COL_SKY ; background color above / below the
bg_bot: !byte COL_GRASS ; horizon raster split
seed: !byte $a7 ; rand8 LFSR state
fheld: !byte 0 ; fire/space edge detection state
joyb: !byte 0 ; player 1 input bits from read_joy
joyb2: !byte 0 ; player 2 input bits from read_joy2
mp_mode: !byte 0 ; 0 = single player, 1 = multiplayer
k12held: !byte 0 ; menu '1'/'2' key edge detection state
en_sh: !byte 0 ; per-frame sprite register shadows
msb_sh: !byte 0
xex_sh: !byte 0
yex_sh: !byte 0
prow: !byte 0 ; print_text: row, column, color, length
pcol: !byte 0
pcolor: !byte 0
plen: !byte 0
bigrow: !byte 0 ; draw_big: row, column, color, length
bigcol: !byte 0
bigcolor: !byte 0
biglen: !byte 0
bigi: !byte 0 ; draw_big internals
glyphbase: !byte 0
charx: !byte 0
rowidx: !byte 0
bitcnt: !byte 0
curbits: !byte 0
drtmp: !byte 0 ; draw_road internals
drleft: !byte 0
ttidx: !byte 0 ; draw_trees internals
trow: !byte 0
tcol: !byte 0
rslot: !byte 0 ; rab_pos internals
rt: !byte 0
rlane: !byte 0
rhop: !byte 0
rfrm: !byte 0
rexp: !byte 0
rptr: !byte 0
ranchor: !byte 0
roff: !byte 0
rxlo: !byte 0
rxhi: !byte 0
ry: !byte 0
rtmp: !byte 0
rtmp2: !byte 0