c64-basic/happy-birthday-beca/happy-birthday-beca.asm

799 lines
14 KiB
NASM

!cpu 6510
; HAPPY BIRTHDAY, BECA!
; A fifth-birthday celebration with balloons, confetti, cake and SID music.
; Self-starting BASIC line: 10 SYS4096
* = $0801
!word basic_end
!word 10
!byte $9e
!text "4096"
!byte 0
basic_end: !word 0
* = $1000
SCREEN = $0400
COLOR = $d800
GETIN = $ffe4
CHROUT = $ffd2
start:
jsr save_state
lda #0
sta $d020
sta $d021
jsr show_splash
jsr initialize_demo
main_loop:
jsr read_controls
lda quit_flag
beq wait_jiffy
jmp exit_demo
wait_jiffy:
lda $a2
cmp last_jiffy
beq main_loop
sta last_jiffy
lda paused
bne main_loop
lda music_muted
bne skip_music
jsr update_music
skip_music:
inc frame_divider
lda frame_divider
cmp #3
bcc main_loop
lda #0
sta frame_divider
jsr update_balloons
jsr update_confetti
jsr animate_candles
jmp main_loop
save_state:
lda $d020
sta old_border
lda $d021
sta old_background
lda $d011
sta old_d011
lda $d016
sta old_d016
lda $d018
sta old_d018
lda $d015
sta old_sprite_enable
lda $d010
sta old_sprite_msb
lda $d017
sta old_sprite_expand_y
lda $d01d
sta old_sprite_expand_x
lda $d01b
sta old_sprite_priority
lda $d01c
sta old_sprite_multicolor
ldx #0
save_sprite_positions:
lda $d000,x
sta old_sprite_positions,x
inx
cpx #16
bne save_sprite_positions
ldx #0
save_sprite_colors:
lda $d027,x
sta old_sprite_colors,x
inx
cpx #8
bne save_sprite_colors
ldx #0
save_sprite_pointers:
lda $07f8,x
sta old_sprite_pointers,x
inx
cpx #8
bne save_sprite_pointers
ldx #0
save_sid:
lda $d400,x
sta old_sid,x
inx
cpx #25
bne save_sid
rts
initialize_demo:
lda #0
sta paused
sta music_muted
sta quit_flag
sta frame_divider
sta animation_phase
sta music_tick
sta music_index
sta note_ticks
sta candle_phase
lda $a2
sta last_jiffy
lda #6
sta $d020
lda #14
sta $d021
lda old_d011
and #$9f
ora #$10
sta $d011
lda old_d016
and #$e8
ora #8
sta $d016
lda old_d018
and #$f1
ora #$14
sta $d018
jsr clear_screen
jsr draw_banner
jsr draw_cake
jsr initialize_confetti
jsr initialize_sprites
jsr initialize_sid
rts
clear_screen:
ldx #0
clear_loop:
lda #32
sta SCREEN,x
sta SCREEN+256,x
sta SCREEN+512,x
sta SCREEN+768,x
lda #6
sta COLOR,x
sta COLOR+256,x
sta COLOR+512,x
sta COLOR+768,x
inx
bne clear_loop
rts
draw_banner:
lda #<banner_line_one
sta $fb
lda #>banner_line_one
sta $fc
lda #<(SCREEN+47)
sta $fd
lda #>(SCREEN+47)
sta $fe
lda #7
sta text_color
jsr draw_text
lda #<banner_line_two
sta $fb
lda #>banner_line_two
sta $fc
lda #<(SCREEN+83)
sta $fd
lda #>(SCREEN+83)
sta $fe
lda #1
sta text_color
jsr draw_text
rts
draw_text:
ldy #0
draw_text_loop:
lda ($fb),y
beq draw_text_done
cmp #65
bcc store_text_char
cmp #91
bcs store_text_char
sec
sbc #64
store_text_char:
sta ($fd),y
lda text_color
pha
lda $fd
clc
adc #<$d400
sta $f9
lda $fe
adc #>$d400
sta $fa
pla
sta ($f9),y
iny
bne draw_text_loop
draw_text_done:
rts
draw_cake:
; Plate.
ldx #0
plate_loop:
lda #102
sta SCREEN+890,x
lda #3
sta COLOR+890,x
inx
cpx #20
bne plate_loop
; Two cake layers.
ldx #0
cake_loop:
lda #160
sta SCREEN+812,x
sta SCREEN+852,x
sta SCREEN+772,x
lda #10
sta COLOR+812,x
sta COLOR+852,x
lda #1
sta COLOR+772,x
inx
cpx #16
bne cake_loop
; Five candles and flames.
ldx #0
candle_loop:
lda candle_columns,x
tay
lda #160
sta SCREEN+692,y
sta SCREEN+732,y
lda candle_colors,x
sta COLOR+692,y
sta COLOR+732,y
lda #81
sta SCREEN+652,y
lda #7
sta COLOR+652,y
inx
cpx #5
bne candle_loop
rts
initialize_confetti:
ldx #0
confetti_init_loop:
lda confetti_start_x,x
sta confetti_x,x
lda confetti_start_y,x
sta confetti_y,x
inx
cpx #18
bne confetti_init_loop
jsr draw_all_confetti
rts
draw_all_confetti:
ldx #0
confetti_draw_loop:
jsr draw_one_confetti
inx
cpx #18
bne confetti_draw_loop
rts
draw_one_confetti:
lda confetti_y,x
tay
lda row_lo,y
sta $fb
lda row_hi,y
sta $fc
lda color_row_lo,y
sta $fd
lda color_row_hi,y
sta $fe
ldy confetti_x,x
lda confetti_chars,x
sta ($fb),y
lda confetti_colors,x
sta ($fd),y
rts
erase_one_confetti:
lda confetti_y,x
tay
lda row_lo,y
sta $fb
lda row_hi,y
sta $fc
ldy confetti_x,x
lda #32
sta ($fb),y
rts
update_confetti:
ldx #0
confetti_update_loop:
jsr erase_one_confetti
inc confetti_y,x
lda confetti_y,x
cmp #15
bcc confetti_keep_y
lda #3
sta confetti_y,x
lda confetti_x,x
clc
adc #11
cmp #39
bcc confetti_store_x
sbc #38
confetti_store_x:
sta confetti_x,x
confetti_keep_y:
jsr draw_one_confetti
inx
cpx #18
bne confetti_update_loop
rts
burst_confetti:
ldx #0
burst_loop:
jsr erase_one_confetti
txa
asl
clc
adc animation_phase
and #31
clc
adc #4
sta confetti_x,x
txa
and #7
clc
adc #3
sta confetti_y,x
inx
cpx #18
bne burst_loop
jsr draw_all_confetti
rts
initialize_sprites:
lda #$e0
sta $07f8
sta $07f9
sta $07fa
lda #$e1
sta $07fb
lda #$e2
sta $07fc
lda #$1f
sta $d015
lda #0
sta $d010
sta $d01c
sta $d01b
lda #$18
sta $d017
sta $d01d
lda #2
sta $d027
lda #5
sta $d028
lda #13
sta $d029
lda #1
sta $d02a
lda #9
sta $d02b
lda #44
sta $d000
lda #88
sta $d001
lda #214
sta $d002
lda #70
sta $d003
lda #235
sta $d004
lda #126
sta $d005
lda #128
sta $d006
sta $d008
lda #104
sta $d007
sta $d009
rts
update_balloons:
inc animation_phase
lda animation_phase
and #31
tax
lda balloon_wave,x
clc
adc #64
sta $d001
lda animation_phase
clc
adc #9
and #31
tax
lda balloon_wave,x
clc
adc #54
sta $d003
lda animation_phase
clc
adc #18
and #31
tax
lda balloon_wave,x
clc
adc #96
sta $d005
; Beca gently nods while the tabby markings remain aligned.
lda animation_phase
and #15
tax
lda cat_bob,x
clc
adc #98
sta $d007
sta $d009
rts
animate_candles:
inc candle_phase
lda candle_phase
and #1
beq warm_flames
lda #8
bne paint_flames
warm_flames:
lda #7
paint_flames:
ldx #0
flame_loop:
ldy candle_columns,x
sta COLOR+652,y
inx
cpx #5
bne flame_loop
rts
initialize_sid:
ldx #0
sid_clear_loop:
lda #0
sta $d400,x
inx
cpx #25
bne sid_clear_loop
lda #$08
sta $d405
lda #$a8
sta $d406
lda #$05
sta $d40c
lda #$98
sta $d40d
lda #$02
sta $d413
lda #$58
sta $d414
lda #$21
sta $d404
lda #$41
sta $d40b
lda #$81
sta $d412
lda #$0f
sta $d418
jsr start_note
rts
update_music:
inc music_tick
lda music_tick
cmp #3
bcc music_done
lda #0
sta music_tick
inc note_ticks
ldx music_index
lda note_ticks
cmp note_durations,x
bcc music_done
lda #0
sta note_ticks
inc music_index
lda music_index
cmp #25
bcc start_note
lda #0
sta music_index
start_note:
ldx music_index
lda melody_lo,x
sta $d400
lda melody_hi,x
sta $d401
lda bass_lo,x
sta $d407
lda bass_hi,x
sta $d408
txa
and #3
bne close_drum
lda #$28
sta $d40e
lda #$02
sta $d40f
lda #$81
sta $d412
rts
close_drum:
lda #$80
sta $d412
music_done:
rts
read_controls:
jsr GETIN
bne have_key
rts
have_key:
cmp #81
bne check_pause
lda #1
sta quit_flag
rts
check_pause:
cmp #32
bne check_mute
lda paused
eor #1
sta paused
bne silence_pause
lda music_muted
bne controls_done
lda #$0f
sta $d418
rts
silence_pause:
lda #0
sta $d418
rts
check_mute:
cmp #77
bne check_fanfare
lda music_muted
eor #1
sta music_muted
bne silence_music
lda paused
bne controls_done
lda #$0f
sta $d418
rts
silence_music:
lda #0
sta $d418
rts
check_fanfare:
cmp #70
bne check_confetti
lda #0
sta music_index
sta note_ticks
sta music_tick
jsr start_note
rts
check_confetti:
cmp #67
bne controls_done
jsr burst_confetti
controls_done:
rts
show_splash:
lda #147
jsr CHROUT
lda #<splash_text
sta $fb
lda #>splash_text
sta $fc
ldy #0
splash_loop:
lda ($fb),y
beq splash_wait
jsr CHROUT
iny
bne splash_loop
inc $fc
jmp splash_loop
splash_wait:
jsr GETIN
cmp #32
bne splash_wait
rts
exit_demo:
lda #0
sta $d418
ldx #0
restore_sid:
lda old_sid,x
sta $d400,x
inx
cpx #25
bne restore_sid
lda old_sprite_enable
sta $d015
lda old_sprite_msb
sta $d010
lda old_sprite_expand_y
sta $d017
lda old_sprite_expand_x
sta $d01d
lda old_sprite_priority
sta $d01b
lda old_sprite_multicolor
sta $d01c
ldx #0
restore_sprite_positions:
lda old_sprite_positions,x
sta $d000,x
inx
cpx #16
bne restore_sprite_positions
ldx #0
restore_sprite_colors:
lda old_sprite_colors,x
sta $d027,x
inx
cpx #8
bne restore_sprite_colors
ldx #0
restore_sprite_pointers:
lda old_sprite_pointers,x
sta $07f8,x
inx
cpx #8
bne restore_sprite_pointers
lda old_d011
sta $d011
lda old_d016
sta $d016
lda old_d018
sta $d018
lda old_border
sta $d020
lda old_background
sta $d021
lda #147
jsr CHROUT
rts
splash_text:
!byte 13,13,13,154
!text " HAPPY BIRTHDAY, BECA!"
!byte 13,13,5
!text " A FIVE-CANDLE CELEBRATION FOR BECA"
!byte 13,13,13,153
!text " CONTROLS"
!byte 13,13,158
!text " F"
!byte 5
!text " REPLAY SONG "
!byte 158
!text "C"
!byte 5
!text " CONFETTI"
!byte 13,158
!text " SPACE"
!byte 5
!text " PAUSE "
!byte 158
!text "M"
!byte 5
!text " MUSIC "
!byte 158
!text "Q"
!byte 5
!text " QUIT"
!byte 13,13,13,5
!text " HTTPS://C64.P20.DEV"
!byte 13,13,152
!text " RELEASED 23 OCTOBER 2026"
!byte 13,13,13,158
!text " PRESS SPACE TO START"
!byte 0
banner_line_one: !text "HAPPY 5TH BIRTHDAY, BECA!",0
banner_line_two: !text "CAKE, BALLOONS AND PURRS FOR YOU",0
text_color: !byte 0
candle_columns: !byte 1,4,7,10,13
candle_colors: !byte 2,5,7,13,3
; Happy Birthday melody: G G A G C B / G G A G D C /
; G G G5 E5 C5 B A / F5 F5 E5 C5 D5 C5.
melody_lo:
!byte $b1,$b1,$d6,$b1,$4b,$5f,$b1,$b1,$d6,$b1,$7e,$4b
!byte $b1,$b1,$4b,$9a,$4b,$5f,$d6,$e3,$e3,$9a,$4b,$7e,$4b
melody_hi:
!byte $19,$19,$1c,$19,$22,$20,$19,$19,$1c,$19,$26,$22
!byte $19,$19,$32,$2b,$22,$20,$1c,$2d,$2d,$2b,$22,$26,$22
note_durations:
!byte 2,2,4,4,4,8,2,2,4,4,4,8,2,2,4,4,4,4,8,2,2,4,4,4,10
bass_lo:
!byte $25,$25,$25,$25,$4b,$4b,$25,$25,$25,$25,$3f,$4b
!byte $25,$25,$4b,$4b,$4b,$4b,$36,$72,$72,$4b,$4b,$3f,$4b
bass_hi:
!byte $11,$11,$11,$11,$11,$11,$11,$11,$11,$11,$13,$11
!byte $11,$11,$11,$11,$11,$11,$0e,$16,$16,$11,$11,$13,$11
balloon_wave:
!byte 8,7,6,5,4,3,2,1,0,0,1,2,3,4,5,6
!byte 8,10,11,12,13,14,15,16,16,16,15,14,13,12,11,10
cat_bob: !byte 2,1,1,0,0,0,1,1,2,3,3,4,4,4,3,3
confetti_start_x: !byte 2,5,8,11,15,18,22,26,30,34,37,4,9,14,20,25,31,36
confetti_start_y: !byte 3,8,5,11,4,9,6,12,3,8,5,13,7,10,4,12,6,9
confetti_x: !fill 18,0
confetti_y: !fill 18,0
confetti_chars: !byte 42,46,43,42,46,43,42,46,43,42,46,43,42,46,43,42,46,43
confetti_colors: !byte 2,5,7,8,10,13,3,4,1,2,5,7,8,10,13,3,4,1
row_lo: !for i,0,24 { !byte <(SCREEN+i*40) }
row_hi: !for i,0,24 { !byte >(SCREEN+i*40) }
color_row_lo: !for i,0,24 { !byte <(COLOR+i*40) }
color_row_hi: !for i,0,24 { !byte >(COLOR+i*40) }
old_border: !byte 0
old_background: !byte 0
old_d011: !byte 0
old_d016: !byte 0
old_d018: !byte 0
old_sprite_enable: !byte 0
old_sprite_msb: !byte 0
old_sprite_expand_y: !byte 0
old_sprite_expand_x: !byte 0
old_sprite_priority: !byte 0
old_sprite_multicolor: !byte 0
old_sprite_positions: !fill 16,0
old_sprite_colors: !fill 8,0
old_sprite_pointers: !fill 8,0
old_sid: !fill 25,0
paused: !byte 0
music_muted: !byte 0
quit_flag: !byte 0
frame_divider: !byte 0
animation_phase: !byte 0
music_tick: !byte 0
music_index: !byte 0
note_ticks: !byte 0
candle_phase: !byte 0
last_jiffy: !byte 0
; Sprite data at $3800: balloon, white cat head and tabby markings.
* = $3800
balloon_sprite:
!byte $00,$18,$00,$00,$7e,$00,$00,$ff,$00,$01,$ff,$80,$03,$ff,$c0,$03,$ff,$c0
!byte $07,$ff,$e0,$07,$ff,$e0,$07,$ff,$e0,$03,$ff,$c0,$03,$ff,$c0,$01,$ff,$80
!byte $00,$ff,$00,$00,$7e,$00,$00,$3c,$00,$00,$18,$00,$00,$18,$00,$00,$18,$00
!byte $00,$18,$00,$00,$08,$00,$00,$00,$00,0
cat_base_sprite:
!byte $18,$00,$18,$3c,$00,$3c,$7e,$00,$7e,$ff,$00,$ff,$ff,$81,$ff,$ff,$ff,$ff
!byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff
!byte $7f,$ff,$fe,$3f,$ff,$fc,$1f,$ff,$f8,$0f,$ff,$f0,$07,$ff,$e0,$03,$ff,$c0
!byte $01,$ff,$80,$00,$7e,$00,$00,$00,$00,0
cat_markings_sprite:
!byte $10,$00,$08,$30,$00,$0c,$60,$00,$06,$c0,$00,$03,$c0,$00,$03,$80,$00,$01
!byte $81,$81,$81,$03,$c3,$c0,$03,$c3,$c0,$00,$24,$00,$00,$18,$00,$00,$18,$00
!byte $00,$00,$00,$00,$42,$00,$00,$3c,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
!byte $00,$00,$00,$00,$00,$00,$00,$00,$00,0