c64-basic/tv-fireplace/tv-fireplace.asm

648 lines
10 KiB
NASM

!cpu 6510
; TV FIREPLACE
; A warm PETSCII fireplace with animated flames, embers and SID crackles.
; 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_frame
jmp exit_demo
wait_frame:
lda $a2
cmp last_jiffy
beq main_loop
sta last_jiffy
lda paused
bne main_loop
inc frame_divider
lda frame_divider
cmp animation_speed
bcc sound_only
lda #0
sta frame_divider
jsr update_fire
sound_only:
lda sound_muted
bne main_loop
jsr update_sound
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
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 sound_muted
sta quit_flag
sta frame_divider
sta flame_phase
sta palette_index
sta stoke_timer
sta crackle_timer
lda #2
sta animation_speed
lda #$6d
sta random_seed
lda $a2
sta last_jiffy
lda #0
sta $d020
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
lda #147
jsr CHROUT
jsr clear_screen
jsr draw_fireplace
jsr initialize_sid
jsr update_fire
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 #0
sta COLOR,x
sta COLOR+256,x
sta COLOR+512,x
sta COLOR+768,x
inx
bne clear_loop
rts
draw_fireplace:
; Broad wooden mantel on rows 3 and 4.
ldx #0
mantel_loop:
lda #160
sta SCREEN+124,x
sta SCREEN+164,x
lda #9
sta COLOR+124,x
lda #8
sta COLOR+164,x
inx
cpx #32
bne mantel_loop
; Brick columns frame the firebox.
lda #5
sta draw_row
brick_row_loop:
ldx draw_row
lda screen_row_lo,x
sta $fb
lda screen_row_hi,x
sta $fc
lda color_row_lo,x
sta $fd
lda color_row_hi,x
sta $fe
ldy #5
left_bricks:
tya
eor draw_row
and #1
tax
lda brick_chars,x
sta ($fb),y
lda palette_index
asl
tax
tya
and #1
beq left_color
inx
left_color:
lda brick_colors,x
sta ($fd),y
iny
cpy #11
bne left_bricks
ldy #29
right_bricks:
tya
eor draw_row
and #1
tax
lda brick_chars,x
sta ($fb),y
lda palette_index
asl
tax
tya
and #1
beq right_color
inx
right_color:
lda brick_colors,x
sta ($fd),y
iny
cpy #35
bne right_bricks
inc draw_row
lda draw_row
cmp #20
bne brick_row_loop
; Stone hearth and warm wooden logs.
ldx #0
hearth_loop:
lda #160
sta SCREEN+804,x
sta SCREEN+844,x
lda #11
sta COLOR+804,x
lda #12
sta COLOR+844,x
inx
cpx #32
bne hearth_loop
ldx #0
logs_loop:
lda log_chars,x
sta SCREEN+771,x
lda log_colors,x
sta COLOR+771,x
inx
cpx #18
bne logs_loop
rts
update_fire:
inc flame_phase
lda stoke_timer
beq build_heights
dec stoke_timer
build_heights:
ldx #0
height_loop:
txa
clc
adc flame_phase
and #15
tay
lda flame_wave,y
sta work_value
jsr random_byte
and #1
clc
adc work_value
ldy stoke_timer
beq store_height
cmp #0
beq store_height
clc
adc #2
cmp #10
bcc store_height
lda #9
store_height:
sta flame_heights,x
inx
cpx #16
bne height_loop
lda #0
sta flame_level
level_loop:
ldx flame_level
lda flame_row_lo,x
sta $fb
lda flame_row_hi,x
sta $fc
lda flame_color_lo,x
sta $fd
lda flame_color_hi,x
sta $fe
ldx #0
cell_loop:
lda flame_heights,x
cmp flame_level
beq empty_cell
bcc empty_cell
stx flame_column
jsr random_byte
and #3
tay
ldx flame_level
cpx #3
bcc choose_body_char
lda top_chars,y
bne write_flame_char
choose_body_char:
lda body_chars,y
write_flame_char:
sta temp_char
ldx flame_column
txa
clc
adc #12
tay
sta work_column
lda temp_char
store_flame:
sta ($fb),y
ldx palette_index
lda palette_offsets,x
clc
adc flame_level
tax
lda flame_colors,x
sta ($fd),y
ldx flame_column
jmp next_cell
empty_cell:
txa
clc
adc #12
tay
lda #32
sta ($fb),y
lda #0
sta ($fd),y
next_cell:
inx
cpx #16
bne cell_loop
inc flame_level
lda flame_level
cmp #9
beq levels_done
jmp level_loop
levels_done:
jsr draw_sparks
jsr draw_embers
rts
draw_sparks:
; Clear the two rows above the flames.
ldx #0
clear_sparks:
lda #32
sta SCREEN+332,x
sta SCREEN+372,x
lda #0
sta COLOR+332,x
sta COLOR+372,x
inx
cpx #16
bne clear_sparks
ldx #0
spark_loop:
jsr random_byte
and #15
tay
lda #46
sta SCREEN+332,y
lda spark_colors,x
sta COLOR+332,y
jsr random_byte
and #15
tay
lda #42
sta SCREEN+372,y
lda spark_colors+1,x
sta COLOR+372,y
inx
cpx #3
bne spark_loop
rts
draw_embers:
ldx #0
ember_loop:
jsr random_byte
and #3
tay
lda ember_chars,y
sta SCREEN+732,x
lda ember_colors,y
sta COLOR+732,x
inx
cpx #16
bne ember_loop
rts
random_byte:
lda random_seed
asl
bcc random_no_xor
eor #$1d
random_no_xor:
adc #$17
sta random_seed
rts
initialize_sid:
ldx #0
clear_sid:
lda #0
sta $d400,x
inx
cpx #25
bne clear_sid
lda #$40
sta $d405
lda #$58
sta $d406
lda #$28
sta $d400
lda #$03
sta $d401
lda #$11
sta $d404
lda #$0f
sta $d418
rts
update_sound:
lda crackle_timer
beq maybe_crackle
dec crackle_timer
bne sound_done
lda #$80
sta $d412
rts
maybe_crackle:
jsr random_byte
and #31
bne sound_done
lda random_seed
sta $d40e
and #7
ora #2
sta $d40f
lda #$08
sta $d413
lda #$28
sta $d414
lda #$81
sta $d412
lda #2
sta crackle_timer
sound_done:
rts
read_controls:
jsr GETIN
beq controls_done
cmp #81
bne check_one
lda #1
sta quit_flag
rts
check_one:
cmp #49
bne check_two
lda #4
sta animation_speed
rts
check_two:
cmp #50
bne check_three
lda #2
sta animation_speed
rts
check_three:
cmp #51
bne check_stoke
lda #1
sta animation_speed
rts
check_stoke:
cmp #70
bne check_palette
lda #90
sta stoke_timer
rts
check_palette:
cmp #67
bne check_sound
inc palette_index
lda palette_index
cmp #3
bcc redraw_bricks
lda #0
sta palette_index
redraw_bricks:
jsr draw_fireplace
rts
check_sound:
cmp #83
bne check_pause
lda sound_muted
eor #1
sta sound_muted
beq restore_sound
lda #0
sta $d418
rts
restore_sound:
jsr initialize_sid
rts
check_pause:
cmp #32
bne controls_done
lda paused
eor #1
sta paused
controls_done:
rts
show_splash:
lda #147
jsr CHROUT
lda #<splash_text
sta $fb
lda #>splash_text
sta $fc
ldy #0
print_splash:
lda ($fb),y
beq wait_for_space
jsr CHROUT
iny
bne print_splash
inc $fc
jmp print_splash
wait_for_space:
jsr GETIN
cmp #32
bne wait_for_space
rts
exit_demo:
ldx #0
restore_sid:
lda old_sid,x
sta $d400,x
inx
cpx #25
bne restore_sid
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 " TV FIREPLACE"
!byte 13,13,5
!text " A WARM HEARTH OF FLAME AND EMBERS"
!byte 13,13,13,153
!text " CONTROLS"
!byte 13,13,158
!text " 1-3"
!byte 5
!text " SPEED "
!byte 158
!text "F"
!byte 5
!text " STOKE "
!byte 158
!text "C"
!byte 5
!text " PALETTE"
!byte 13,158
!text " SPACE"
!byte 5
!text " PAUSE "
!byte 158
!text "S"
!byte 5
!text " SOUND "
!byte 158
!text "Q"
!byte 5
!text " QUIT"
!byte 13,13,13,5
!text " HTTPS://C64.P20.DEV"
!byte 13,13,152
!text " RELEASED 24 OCTOBER 2026"
!byte 13,13,13,158
!text " PRESS SPACE TO START"
!byte 0
brick_chars: !byte 160,102
brick_colors: !byte 9,8,2,9,12,15
palette_offsets: !byte 0,9,18
flame_colors:
!byte 7,7,7,8,8,8,2,2,2
!byte 1,7,7,7,8,8,10,2,2
!byte 7,7,3,3,14,14,6,6,6
body_chars: !byte 160,160,102,160
top_chars: !byte 102,46,42,102
flame_wave: !byte 0,2,4,1,3,0,5,2,0,4,1,3,5,0,2,4
spark_colors: !byte 7,8,2,7
ember_chars: !byte 46,42,102,160
ember_colors: !byte 2,8,9,7
log_chars: !byte 109,109,109,110,110,110,109,109,110,110,109,109,110,110,109,109,110,110
log_colors: !byte 9,9,8,9,8,9,9,8,9,8,9,9,8,9,8,9,9,8
screen_row_lo:
!for row,0,24 { !byte <(SCREEN + row*40) }
screen_row_hi:
!for row,0,24 { !byte >(SCREEN + row*40) }
color_row_lo:
!for row,0,24 { !byte <(COLOR + row*40) }
color_row_hi:
!for row,0,24 { !byte >(COLOR + row*40) }
flame_row_lo:
!byte <(SCREEN+720),<(SCREEN+680),<(SCREEN+640),<(SCREEN+600),<(SCREEN+560),<(SCREEN+520),<(SCREEN+480),<(SCREEN+440),<(SCREEN+400)
flame_row_hi:
!byte >(SCREEN+720),>(SCREEN+680),>(SCREEN+640),>(SCREEN+600),>(SCREEN+560),>(SCREEN+520),>(SCREEN+480),>(SCREEN+440),>(SCREEN+400)
flame_color_lo:
!byte <(COLOR+720),<(COLOR+680),<(COLOR+640),<(COLOR+600),<(COLOR+560),<(COLOR+520),<(COLOR+480),<(COLOR+440),<(COLOR+400)
flame_color_hi:
!byte >(COLOR+720),>(COLOR+680),>(COLOR+640),>(COLOR+600),>(COLOR+560),>(COLOR+520),>(COLOR+480),>(COLOR+440),>(COLOR+400)
old_border: !byte 0
old_background: !byte 0
old_d011: !byte 0
old_d016: !byte 0
old_d018: !byte 0
old_sid: !fill 25,0
paused: !byte 0
sound_muted: !byte 0
quit_flag: !byte 0
frame_divider: !byte 0
animation_speed: !byte 0
flame_phase: !byte 0
palette_index: !byte 0
stoke_timer: !byte 0
crackle_timer: !byte 0
random_seed: !byte 0
last_jiffy: !byte 0
draw_row: !byte 0
flame_level: !byte 0
flame_column: !byte 0
flame_heights: !fill 16,0
work_value: !byte 0
work_column: !byte 0
temp_char: !byte 0