Add TV Fireplace demo

This commit is contained in:
Christian Johannesen 2026-10-24 09:18:32 +02:00
parent 7102a31a49
commit 437a8dd8f8
7 changed files with 664 additions and 2 deletions

View file

@ -1,6 +1,6 @@
# C64 Program Collection
Forty small, polished programs for the Commodore 64, written in stock Commodore BASIC V2 and 6502 machine code. The collection mixes arcade action, puzzles, simulations, mathematics, physics, creative tools, music, utilities, and visual effects. Every entry is self-contained and runs on an ordinary PAL or NTSC C64 without a cartridge or BASIC extension.
Forty-one small, polished programs for the Commodore 64, written in stock Commodore BASIC V2 and 6502 machine code. The collection mixes arcade action, puzzles, simulations, mathematics, physics, creative tools, music, utilities, and visual effects. Every entry is self-contained and runs on an ordinary PAL or NTSC C64 without a cartridge or BASIC extension.
Browse and run the collection at [c64.p20.dev](https://c64.p20.dev).
@ -48,6 +48,7 @@ Browse and run the collection at [c64.p20.dev](https://c64.p20.dev).
| Orbit Lab | 2026-10-22 | Physics | BASIC | `1`-`3`, `T`, `R`, Space, `Q` | Explore how velocity and gravity shape a simple two-dimensional orbit. | [BAS](orbit-lab/orbit-lab.bas) | [Run](https://c64.p20.dev/program/orbit-lab) |
| Haunted Night | 2026-10-23 | Demo | Machine Code | `1`-`3`, `L`, `C`, `M`, Space, `Q` | Visit a moonlit haunted manor alive with ghosts, bats, lightning, and a sinister three-voice SID score. | [ASM](haunted-night/haunted-night.asm) | [Run](https://c64.p20.dev/program/haunted-night) |
| Happy Birthday, Beca! | 2026-10-23 | Demo | Machine Code | `F`, `C`, `M`, Space, `Q` | Celebrate Beca's fifth birthday with cake, balloons, confetti, purrs, and a cheerful three-voice SID serenade. | [ASM](happy-birthday-beca/happy-birthday-beca.asm) | [Run](https://c64.p20.dev/program/happy-birthday-beca) |
| TV Fireplace | 2026-10-24 | Demo | Machine Code | `1`-`3`, `F`, `C`, `S`, Space, `Q` | Settle beside a classic brick fireplace with dancing flames, glowing embers, rising sparks, and crackling SID sound. | [ASM](tv-fireplace/tv-fireplace.asm) | [Run](https://c64.p20.dev/program/tv-fireplace) |
## Running the programs

View file

@ -238,5 +238,11 @@
"description": "Celebrate Beca's fifth birthday with cake, balloons, confetti, purrs, and a cheerful three-voice SID serenade.",
"controls": [{"keys":["F"],"description":"Replay the birthday song"},{"keys":["C"],"description":"Launch a fresh confetti burst"},{"keys":["M"],"description":"Mute or restore the SID music"},{"keys":["SPACE"],"description":"Pause or resume the celebration"},{"keys":["Q"],"description":"Return to BASIC"}],
"path": "happy-birthday-beca/happy-birthday-beca.prg", "source": "happy-birthday-beca/happy-birthday-beca.asm"
},
{
"title": "TV Fireplace", "category": "Demo", "language": "machine-code", "releaseDate": "2026-10-24",
"description": "Settle beside a classic brick fireplace with dancing flames, glowing embers, rising sparks, and crackling SID sound.",
"controls": [{"keys":["1","2","3"],"description":"Select slow, medium, or lively flames"},{"keys":["F"],"description":"Stoke the fire"},{"keys":["C"],"description":"Change the fireplace palette"},{"keys":["S"],"description":"Mute or restore the crackling sound"},{"keys":["SPACE"],"description":"Pause or resume the fire"},{"keys":["Q"],"description":"Return to BASIC"}],
"path": "tv-fireplace/tv-fireplace.prg", "source": "tv-fireplace/tv-fireplace.asm"
}
]

View file

@ -84,5 +84,11 @@
{ "key": "C", "waitAfterMs": 400 }
],
"captureAfterMs": 250
},
"tv-fireplace": {
"actions": [
{ "key": "SPACE", "waitAfterMs": 5000 }
],
"captureAfterMs": 500
}
}

View file

@ -16,7 +16,8 @@ for source in \
neon-rift/neon-rift.asm \
infinite-road/infinite-road.asm \
haunted-night/haunted-night.asm \
happy-birthday-beca/happy-birthday-beca.asm
happy-birthday-beca/happy-birthday-beca.asm \
tv-fireplace/tv-fireplace.asm
do
output="${source%.asm}.prg"
echo "Assembling $source -> $output"

BIN
tv-fireplace/screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

View file

@ -0,0 +1,648 @@
!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

Binary file not shown.