; ----------------------------------------------------------- ; 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 ; ---- the road: two diagonals from the horizon apex down to ; the screen corners, plus a tree on the left shoulder ---- draw_road: ldx #0 dr_loop: txa clc adc #6 ; road runs from char row 6 to row 24 tay lda mul40lo,y sta scrlo lda mul40hi,y clc adc #>SCREEN sta scrhi stx drtmp lda #19 ; left edge: one column left per row sec sbc drtmp tay lda #$4e ; '/' diagonal sta (scrlo),y lda #20 ; right edge: one column right per row clc adc drtmp tay lda #$4d ; '\' diagonal sta (scrlo),y inx cpx #19 bne dr_loop ; tree on the left shoulder lda #$51 ; ball sta SCREEN+10*40+3 sta SCREEN+11*40+2 sta SCREEN+11*40+3 sta SCREEN+11*40+4 lda #$5d ; trunk sta SCREEN+12*40+3 rts ; ---- input ---- ; read joystick port 2, 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 rts ; fire or space held? returns A != 0 when pressed start_pressed: jsr read_joy 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 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, 0, 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_press: !scr "press fire to start" txt_info: !scr "joystick 2 moves fire starts" txt_over: !scr "game over" txt_retry: !scr "fire = retry" txt_blank: !fill 19, $20 ; ---- shared variables ---- framectr: !byte 0 ; frame counter, incremented by the main loop seed: !byte $a7 ; rand8 LFSR state fheld: !byte 0 ; fire/space edge detection state joyb: !byte 0 ; joystick bits from read_joy (active high) 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 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