Combo System
Track consecutive hits. Multiply your score. Break the combo on miss.
Consistency pays off. This unit adds a combo system that rewards consecutive hits.
Hit notes in succession and your combo climbs. Higher combos unlock score multipliers: 2x at 10 hits, 3x at 25, 4x at 50. Miss a single note and the combo breaks, resetting to zero. The risk/reward creates tension - that 49-hit combo could become 4x on the next hit, or crash to nothing.
Run It
Assemble and run:
acme -f cbm -o symphony.prg symphony.asm

The “COMBO: 000 1x” display shows your current combo count and multiplier. Build a combo and watch both the number and colour change as you reach new tiers.
The Combo System
The combo tracks consecutive hits without misses:
; ----------------------------------------------------------------------------
; Combo System - Track consecutive hits and calculate multiplier
; ----------------------------------------------------------------------------
; Combo settings
COMBO_TIER_2 = 10 ; 2x multiplier at 10 combo
COMBO_TIER_3 = 25 ; 3x multiplier at 25 combo
COMBO_TIER_4 = 50 ; 4x multiplier at 50 combo
; Variables
combo: !byte 0 ; Current consecutive hits
max_combo: !byte 0 ; Best combo this song
; ----------------------------------------------------------------------------
; Initialize Combo
; ----------------------------------------------------------------------------
init_combo:
lda #0
sta combo
sta max_combo
jsr display_combo
rts
; ----------------------------------------------------------------------------
; Get Multiplier - Returns 1-4 based on current combo
; ----------------------------------------------------------------------------
get_multiplier:
lda combo
cmp #COMBO_TIER_4
bcs mult_4x
cmp #COMBO_TIER_3
bcs mult_3x
cmp #COMBO_TIER_2
bcs mult_2x
; Default: 1x
lda #1
rts
mult_2x:
lda #2
rts
mult_3x:
lda #3
rts
mult_4x:
lda #4
rts
; ----------------------------------------------------------------------------
; Increment Combo - Called on every successful hit
; ----------------------------------------------------------------------------
increment_combo:
inc combo
; Update max combo if this is the best
lda combo
cmp max_combo
bcc combo_not_max
sta max_combo
combo_not_max:
jsr display_combo
rts
; ----------------------------------------------------------------------------
; Break Combo - Called on every miss
; ----------------------------------------------------------------------------
break_combo:
lda combo
beq combo_already_zero
; Reset combo to zero
lda #0
sta combo
jsr display_combo
combo_already_zero:
rts
Two variables do the work: combo for current consecutive hits, max_combo for the best this song. The multiplier calculation uses simple comparisons - check if combo exceeds each threshold, return the appropriate tier.
Multiplier Tiers
The tiers reward sustained accuracy:
| Combo | Multiplier | Why |
|---|---|---|
| 0-9 | 1x | Baseline - no bonus yet |
| 10-24 | 2x | Getting warmed up |
| 25-49 | 3x | In the zone |
| 50+ | 4x | Maximum - perfect consistency |
A perfect hit normally scores 100 points. At 4x multiplier, that same hit scores 400. Over a full song, the difference between 1x and 4x is massive.
Applying the Multiplier
Multiplying on the 6502 requires repeated addition:
; ----------------------------------------------------------------------------
; Apply Multiplier - Multiply base score by current combo multiplier
; ----------------------------------------------------------------------------
; Input: A = base score (e.g., 100 for perfect, 50 for good)
; Output: score_add_lo/hi = base score * multiplier
apply_multiplier:
sta base_score
jsr get_multiplier
sta current_mult
; Start with base score
lda base_score
sta score_add_lo
lda #0
sta score_add_hi
; If multiplier is 1, we're done
lda current_mult
cmp #1
beq mult_done_apply
; Multiply by adding base_score (mult-1) more times
dec current_mult
mult_add_loop:
lda score_add_lo
clc
adc base_score
sta score_add_lo
lda score_add_hi
adc #0
sta score_add_hi
dec current_mult
bne mult_add_loop
mult_done_apply:
rts
base_score: !byte 0
current_mult: !byte 0
score_add_lo: !byte 0
score_add_hi: !byte 0
; ----------------------------------------------------------------------------
; Usage in award_points
; ----------------------------------------------------------------------------
award_points:
; Increment combo first (combo builds BEFORE score)
jsr increment_combo
lda hit_quality
cmp #2
beq award_perfect
; Good hit: 50 * multiplier
lda #GOOD_SCORE ; 50
jsr apply_multiplier ; Returns in score_add_lo/hi
jsr add_score
; ...
award_perfect:
; Perfect hit: 100 * multiplier
lda #PERFECT_SCORE ; 100
jsr apply_multiplier ; Returns in score_add_lo/hi
jsr add_score
; ...
The routine stores the base score, gets the multiplier, then adds the base score that many times. For 3x, it adds the base twice (starting with one copy already in place). The 16-bit result handles scores up to 65535.
Combo Display
The display uses colour to communicate multiplier tier:
; ----------------------------------------------------------------------------
; Display Combo - Show combo count and multiplier with colour feedback
; ----------------------------------------------------------------------------
COMBO_ROW = 2 ; Second row of screen
display_combo:
; Draw "COMBO:" label
ldx #0
draw_combo_label:
lda combo_label,x
beq draw_combo_value
sta SCREEN + (COMBO_ROW * 40) + 12,x
lda #11 ; Grey for label
sta COLRAM + (COMBO_ROW * 40) + 12,x
inx
jmp draw_combo_label
draw_combo_value:
; Display 3-digit combo value
lda combo
; Hundreds digit
ldx #0
combo_div_100:
cmp #100
bcc combo_done_100
sec
sbc #100
inx
jmp combo_div_100
combo_done_100:
pha
txa
ora #$30 ; Convert to screen code
sta SCREEN + (COMBO_ROW * 40) + 18
pla
; Tens digit
ldx #0
combo_div_10:
cmp #10
bcc combo_done_10
sec
sbc #10
inx
jmp combo_div_10
combo_done_10:
pha
txa
ora #$30
sta SCREEN + (COMBO_ROW * 40) + 19
pla
; Ones digit
ora #$30
sta SCREEN + (COMBO_ROW * 40) + 20
; Colour based on multiplier tier
jsr get_multiplier
cmp #4
beq combo_col_4x
cmp #3
beq combo_col_3x
cmp #2
beq combo_col_2x
lda #11 ; 1x = grey (no bonus)
jmp set_combo_col
combo_col_2x:
lda #7 ; 2x = yellow
jmp set_combo_col
combo_col_3x:
lda #5 ; 3x = green
jmp set_combo_col
combo_col_4x:
lda #1 ; 4x = white (maximum!)
set_combo_col:
sta COLRAM + (COMBO_ROW * 40) + 18
sta COLRAM + (COMBO_ROW * 40) + 19
sta COLRAM + (COMBO_ROW * 40) + 20
; Display multiplier (e.g., "2x")
jsr get_multiplier
ora #$30
sta SCREEN + (COMBO_ROW * 40) + 22
lda #$18 ; 'x' character
sta SCREEN + (COMBO_ROW * 40) + 23
rts
combo_label:
!scr "combo:"
!byte 0
Grey means no bonus (1x). Yellow signals 2x. Green shows 3x. White indicates maximum 4x. Players instantly know their multiplier state without reading numbers.
Breaking the Combo
Missing breaks the combo dramatically:
handle_miss:
inc miss_count
jsr play_miss_sound
; ... visual feedback ...
; Break the combo!
jsr break_combo
rts
A single line - jsr break_combo - undoes potentially dozens of consecutive hits. This creates the tension that makes combos meaningful.
Max Combo on Results
The results screen now shows max combo:
SONG COMPLETE!
FINAL SCORE: 05200
PERFECTS: 18
GOODS: 08
MISSES: 02
MAX COMBO: 31
ACCURACY: 092%
Max combo tells players how close they came to a perfect run. A score of 5000 with max combo 10 plays differently than 5000 with max combo 45.
The Complete Code
; ============================================================================
; SID SYMPHONY - Unit 12: Combo System
; ============================================================================
; Consecutive hits build a combo. Higher combos increase the score multiplier.
; Miss a note and the combo resets to zero. Consistency is rewarded.
;
; Controls: Z = Track 1 (high), X = Track 2 (mid), C = Track 3 (low)
; ============================================================================
; ============================================================================
; CUSTOMISATION SECTION
; ============================================================================
; SID Voice Settings
VOICE1_WAVE = $21
VOICE2_WAVE = $41
VOICE3_WAVE = $11
VOICE1_FREQ = $1C
VOICE2_FREQ = $0E
VOICE3_FREQ = $07
VOICE_AD = $09
VOICE_SR = $00
PULSE_WIDTH = $08
; Miss sound settings
MISS_FREQ = $08
MISS_WAVE = $81
MISS_AD = $00
MISS_SR = $90
; Visual Settings
BORDER_COL = 0
BG_COL = 0
TRACK1_NOTE_COL = 10
TRACK2_NOTE_COL = 13
TRACK3_NOTE_COL = 14
TRACK_LINE_COL = 11
HIT_ZONE_COL = 7
HIT_COL = 1
PERFECT_COL = 1
GOOD_COL = 7
MISS_COL = 2
HEALTH_COL = 5
PROGRESS_COL = 3
COMBO_COL = 13 ; Yellow for combo display
; ============================================================================
; SCORING SETTINGS
; ============================================================================
PERFECT_SCORE = 100
GOOD_SCORE = 50
; ============================================================================
; COMBO SETTINGS
; ============================================================================
COMBO_TIER_2 = 10 ; 2x multiplier at 10 combo
COMBO_TIER_3 = 25 ; 3x multiplier at 25 combo
COMBO_TIER_4 = 50 ; 4x multiplier at 50 combo
; ============================================================================
; HEALTH SETTINGS
; ============================================================================
HEALTH_MAX = 64
HEALTH_START = 32
HEALTH_PERFECT = 4
HEALTH_GOOD = 2
HEALTH_MISS = 8
; ============================================================================
; HIT DETECTION SETTINGS
; ============================================================================
HIT_ZONE_MIN = 2
HIT_ZONE_MAX = 5
HIT_ZONE_CENTRE = 3
; ============================================================================
; SONG SETTINGS
; ============================================================================
SONG_LENGTH = 64
PROGRESS_WIDTH = 16
; ============================================================================
; GAME STATES
; ============================================================================
STATE_PLAYING = 0
STATE_RESULTS = 1
STATE_GAMEOVER = 2
; ============================================================================
; MEMORY MAP
; ============================================================================
SCREEN = $0400
COLRAM = $D800
BORDER = $D020
BGCOL = $D021
CHARPTR = $D018
CHARSET = $3000
; SID registers
SID = $D400
SID_V1_FREQ_LO = $D400
SID_V1_FREQ_HI = $D401
SID_V1_PWHI = $D403
SID_V1_CTRL = $D404
SID_V1_AD = $D405
SID_V1_SR = $D406
SID_V2_FREQ_LO = $D407
SID_V2_FREQ_HI = $D408
SID_V2_PWHI = $D40A
SID_V2_CTRL = $D40B
SID_V2_AD = $D40C
SID_V2_SR = $D40D
SID_V3_FREQ_LO = $D40E
SID_V3_FREQ_HI = $D40F
SID_V3_PWHI = $D411
SID_V3_CTRL = $D412
SID_V3_AD = $D413
SID_V3_SR = $D414
SID_VOLUME = $D418
; CIA keyboard
CIA1_PRA = $DC00
CIA1_PRB = $DC01
; Track positions
TRACK1_ROW = 8
TRACK2_ROW = 12
TRACK3_ROW = 16
; HUD positions
HEALTH_ROW = 23
PROGRESS_ROW = 24
; Combo display position
COMBO_ROW = 2
COMBO_COL_POS = 17
; Hit zone
HIT_ZONE_COLUMN = 3
; Custom character codes
CHAR_NOTE = 128
CHAR_TRACK = 129
CHAR_HITZONE = 130
CHAR_SPACE = 32
CHAR_BAR_FULL = 131
CHAR_BAR_EMPTY = 132
; Note settings
MAX_NOTES = 8
NOTE_SPAWN_COL = 37
; Timing
FRAMES_PER_BEAT = 25
END_DELAY_FRAMES = 75
; Zero page
ZP_PTR = $FB
ZP_PTR_HI = $FC
; Variables
frame_count = $02
beat_count = $03
song_pos = $04
song_pos_hi = $05
temp_track = $06
key_pressed = $07
hit_quality = $08
border_flash = $09
miss_track = $0A
game_state = $0B
hit_note_freq = $0C
song_beat = $0D
song_ended = $0E
end_delay = $0F
; ----------------------------------------------------------------------------
; BASIC Stub
; ----------------------------------------------------------------------------
* = $0801
!byte $0C, $08
!byte $0A, $00
!byte $9E
!text "2064"
!byte $00
!byte $00, $00
; ----------------------------------------------------------------------------
; Main Program
; ----------------------------------------------------------------------------
* = $0810
start:
jsr copy_charset
jsr init_game
main_loop:
lda #$FF
wait_raster:
cmp $D012
bne wait_raster
lda game_state
cmp #STATE_PLAYING
beq do_playing
cmp #STATE_RESULTS
beq do_results
jmp do_gameover
do_playing:
jsr update_playing
jmp main_loop
do_results:
jsr update_results
jmp main_loop
do_gameover:
jsr update_gameover
jmp main_loop
; ----------------------------------------------------------------------------
; Initialize Game
; ----------------------------------------------------------------------------
init_game:
jsr init_screen
jsr init_sid
jsr init_notes
jsr init_score
jsr init_health
jsr init_combo
jsr init_song
lda #STATE_PLAYING
sta game_state
rts
; ----------------------------------------------------------------------------
; Initialize Combo
; ----------------------------------------------------------------------------
init_combo:
lda #0
sta combo
sta max_combo
jsr display_combo
rts
; ----------------------------------------------------------------------------
; Update Playing State
; ----------------------------------------------------------------------------
update_playing:
inc frame_count
lda frame_count
cmp #FRAMES_PER_BEAT
bcc no_new_beat
lda #0
sta frame_count
jsr advance_song
jsr check_spawn_note
no_new_beat:
jsr update_notes
jsr reset_track_colours
jsr update_border_flash
jsr check_keys
jsr check_song_end
rts
; ----------------------------------------------------------------------------
; Update Results State
; ----------------------------------------------------------------------------
update_results:
lda #$00
sta CIA1_PRA
lda CIA1_PRB
cmp #$FF
beq results_wait
jsr init_game
results_wait:
lda #$FF
sta CIA1_PRA
rts
; ----------------------------------------------------------------------------
; Update Game Over State
; ----------------------------------------------------------------------------
update_gameover:
lda #$00
sta CIA1_PRA
lda CIA1_PRB
cmp #$FF
beq gameover_wait
jsr init_game
gameover_wait:
lda #$FF
sta CIA1_PRA
rts
; ----------------------------------------------------------------------------
; Check Song End
; ----------------------------------------------------------------------------
check_song_end:
lda song_ended
beq song_not_ended
ldx #0
check_notes_clear:
lda note_track,x
bne notes_still_active
inx
cpx #MAX_NOTES
bne check_notes_clear
dec end_delay
bne song_not_ended
jsr show_results
lda #STATE_RESULTS
sta game_state
notes_still_active:
song_not_ended:
rts
; ----------------------------------------------------------------------------
; Initialize Song
; ----------------------------------------------------------------------------
init_song:
lda #<song_data
sta song_pos
lda #>song_data
sta song_pos_hi
lda #0
sta frame_count
sta beat_count
sta song_beat
sta border_flash
sta song_ended
lda #END_DELAY_FRAMES
sta end_delay
jsr display_progress
rts
; ----------------------------------------------------------------------------
; Advance Song
; ----------------------------------------------------------------------------
advance_song:
lda song_ended
bne advance_done
inc beat_count
inc song_beat
lda song_beat
cmp #SONG_LENGTH
bcc song_continues
lda #1
sta song_ended
song_continues:
jsr display_progress
advance_done:
rts
; ----------------------------------------------------------------------------
; Display Progress
; ----------------------------------------------------------------------------
display_progress:
lda song_beat
lsr
lsr
sta temp_progress
ldx #0
lda temp_progress
beq draw_empty_progress
draw_full_progress:
lda #CHAR_BAR_FULL
sta SCREEN + (PROGRESS_ROW * 40) + 12,x
lda #PROGRESS_COL
sta COLRAM + (PROGRESS_ROW * 40) + 12,x
inx
cpx temp_progress
bne draw_full_progress
draw_empty_progress:
cpx #PROGRESS_WIDTH
beq progress_done
lda #CHAR_BAR_EMPTY
sta SCREEN + (PROGRESS_ROW * 40) + 12,x
lda #11
sta COLRAM + (PROGRESS_ROW * 40) + 12,x
inx
jmp draw_empty_progress
progress_done:
rts
temp_progress: !byte 0
; ----------------------------------------------------------------------------
; Get Multiplier - Returns multiplier (1-4) based on combo
; ----------------------------------------------------------------------------
get_multiplier:
lda combo
cmp #COMBO_TIER_4
bcs mult_4x
cmp #COMBO_TIER_3
bcs mult_3x
cmp #COMBO_TIER_2
bcs mult_2x
; Default: 1x
lda #1
rts
mult_2x:
lda #2
rts
mult_3x:
lda #3
rts
mult_4x:
lda #4
rts
; ----------------------------------------------------------------------------
; Display Combo
; ----------------------------------------------------------------------------
display_combo:
; Draw "COMBO:" label
ldx #0
draw_combo_label:
lda combo_label,x
beq draw_combo_value
sta SCREEN + (COMBO_ROW * 40) + 12,x
lda #11
sta COLRAM + (COMBO_ROW * 40) + 12,x
inx
jmp draw_combo_label
draw_combo_value:
; Display 3-digit combo value
lda combo
; Hundreds
ldx #0
combo_div_100:
cmp #100
bcc combo_done_100
sec
sbc #100
inx
jmp combo_div_100
combo_done_100:
pha
txa
ora #$30
sta SCREEN + (COMBO_ROW * 40) + 18
pla
; Tens
ldx #0
combo_div_10:
cmp #10
bcc combo_done_10
sec
sbc #10
inx
jmp combo_div_10
combo_done_10:
pha
txa
ora #$30
sta SCREEN + (COMBO_ROW * 40) + 19
pla
; Ones
ora #$30
sta SCREEN + (COMBO_ROW * 40) + 20
; Colour based on multiplier tier
jsr get_multiplier
cmp #4
beq combo_col_4x
cmp #3
beq combo_col_3x
cmp #2
beq combo_col_2x
; 1x = grey
lda #11
jmp set_combo_col
combo_col_2x:
lda #7 ; Yellow
jmp set_combo_col
combo_col_3x:
lda #5 ; Green
jmp set_combo_col
combo_col_4x:
lda #1 ; White (best)
set_combo_col:
sta COLRAM + (COMBO_ROW * 40) + 18
sta COLRAM + (COMBO_ROW * 40) + 19
sta COLRAM + (COMBO_ROW * 40) + 20
; Display multiplier
jsr get_multiplier
ora #$30
sta SCREEN + (COMBO_ROW * 40) + 22
lda #$18 ; 'x' character
sta SCREEN + (COMBO_ROW * 40) + 23
jsr get_multiplier
cmp #4
beq mult_col_4x
cmp #3
beq mult_col_3x
cmp #2
beq mult_col_2x
lda #11
jmp set_mult_col
mult_col_2x:
lda #7
jmp set_mult_col
mult_col_3x:
lda #5
jmp set_mult_col
mult_col_4x:
lda #1
set_mult_col:
sta COLRAM + (COMBO_ROW * 40) + 22
sta COLRAM + (COMBO_ROW * 40) + 23
rts
combo_label:
!scr "combo:"
!byte 0
; ----------------------------------------------------------------------------
; Increment Combo
; ----------------------------------------------------------------------------
increment_combo:
inc combo
; Update max combo if needed
lda combo
cmp max_combo
bcc combo_not_max
sta max_combo
combo_not_max:
jsr display_combo
rts
; ----------------------------------------------------------------------------
; Break Combo
; ----------------------------------------------------------------------------
break_combo:
lda combo
beq combo_already_zero
; Combo was active - show break
lda #0
sta combo
jsr display_combo
combo_already_zero:
rts
; ----------------------------------------------------------------------------
; Show Results Screen
; ----------------------------------------------------------------------------
show_results:
; Clear screen
ldx #0
lda #CHAR_SPACE
clear_for_results:
sta SCREEN,x
sta SCREEN+$100,x
sta SCREEN+$200,x
sta SCREEN+$2E8,x
inx
bne clear_for_results
; Title
ldx #0
draw_results_title:
lda results_title,x
beq draw_results_title_done
sta SCREEN + (3 * 40) + 14,x
lda #1
sta COLRAM + (3 * 40) + 14,x
inx
jmp draw_results_title
draw_results_title_done:
; Score
ldx #0
draw_final_score_label:
lda final_score_label,x
beq draw_final_score_done
sta SCREEN + (6 * 40) + 10,x
lda #7
sta COLRAM + (6 * 40) + 10,x
inx
jmp draw_final_score_label
draw_final_score_done:
jsr display_final_score
; Perfects
ldx #0
draw_perfects_label:
lda perfects_label,x
beq draw_perfects_done
sta SCREEN + (8 * 40) + 10,x
lda #1
sta COLRAM + (8 * 40) + 10,x
inx
jmp draw_perfects_label
draw_perfects_done:
lda perfect_count
jsr display_stat_at_8
; Goods
ldx #0
draw_goods_label:
lda goods_label,x
beq draw_goods_done
sta SCREEN + (9 * 40) + 10,x
lda #7
sta COLRAM + (9 * 40) + 10,x
inx
jmp draw_goods_label
draw_goods_done:
lda good_count
jsr display_stat_at_9
; Misses
ldx #0
draw_misses_label:
lda misses_label,x
beq draw_misses_done
sta SCREEN + (10 * 40) + 10,x
lda #2
sta COLRAM + (10 * 40) + 10,x
inx
jmp draw_misses_label
draw_misses_done:
lda miss_count
jsr display_stat_at_10
; Max Combo
ldx #0
draw_max_combo_label:
lda max_combo_label,x
beq draw_max_combo_done
sta SCREEN + (12 * 40) + 10,x
lda #13
sta COLRAM + (12 * 40) + 10,x
inx
jmp draw_max_combo_label
draw_max_combo_done:
lda max_combo
jsr display_stat_at_12
; Accuracy
ldx #0
draw_accuracy_label:
lda accuracy_label,x
beq draw_accuracy_done
sta SCREEN + (14 * 40) + 10,x
lda #5
sta COLRAM + (14 * 40) + 10,x
inx
jmp draw_accuracy_label
draw_accuracy_done:
jsr calculate_accuracy
jsr display_accuracy
; Press any key
ldx #0
draw_press_key:
lda press_key_text,x
beq draw_press_key_done
sta SCREEN + (18 * 40) + 10,x
lda #11
sta COLRAM + (18 * 40) + 10,x
inx
jmp draw_press_key
draw_press_key_done:
lda #5
sta BORDER
rts
results_title:
!scr "song complete!"
!byte 0
final_score_label:
!scr "final score:"
!byte 0
perfects_label:
!scr "perfects:"
!byte 0
goods_label:
!scr "goods:"
!byte 0
misses_label:
!scr "misses:"
!byte 0
max_combo_label:
!scr "max combo:"
!byte 0
accuracy_label:
!scr "accuracy:"
!byte 0
press_key_text:
!scr "press any key"
!byte 0
; ----------------------------------------------------------------------------
; Display Final Score (at row 6)
; ----------------------------------------------------------------------------
display_final_score:
lda score_lo
sta work_lo
lda score_hi
sta work_hi
ldx #0
fs_div_10000:
lda work_lo
sec
sbc #<10000
tay
lda work_hi
sbc #>10000
bcc fs_done_10000
sta work_hi
sty work_lo
inx
jmp fs_div_10000
fs_done_10000:
txa
ora #$30
sta SCREEN + (6 * 40) + 23
ldx #0
fs_div_1000:
lda work_lo
sec
sbc #<1000
tay
lda work_hi
sbc #>1000
bcc fs_done_1000
sta work_hi
sty work_lo
inx
jmp fs_div_1000
fs_done_1000:
txa
ora #$30
sta SCREEN + (6 * 40) + 24
ldx #0
fs_div_100:
lda work_lo
sec
sbc #100
bcc fs_done_100
sta work_lo
inx
jmp fs_div_100
fs_done_100:
txa
ora #$30
sta SCREEN + (6 * 40) + 25
ldx #0
fs_div_10:
lda work_lo
sec
sbc #10
bcc fs_done_10
sta work_lo
inx
jmp fs_div_10
fs_done_10:
txa
ora #$30
sta SCREEN + (6 * 40) + 26
lda work_lo
ora #$30
sta SCREEN + (6 * 40) + 27
lda #7
sta COLRAM + (6 * 40) + 23
sta COLRAM + (6 * 40) + 24
sta COLRAM + (6 * 40) + 25
sta COLRAM + (6 * 40) + 26
sta COLRAM + (6 * 40) + 27
rts
; ----------------------------------------------------------------------------
; Display Stat (2-digit) at various rows
; ----------------------------------------------------------------------------
display_stat_at_8:
ldx #0
stat8_div:
cmp #10
bcc stat8_done
sec
sbc #10
inx
jmp stat8_div
stat8_done:
pha
txa
ora #$30
sta SCREEN + (8 * 40) + 23
pla
ora #$30
sta SCREEN + (8 * 40) + 24
lda #1
sta COLRAM + (8 * 40) + 23
sta COLRAM + (8 * 40) + 24
rts
display_stat_at_9:
ldx #0
stat9_div:
cmp #10
bcc stat9_done
sec
sbc #10
inx
jmp stat9_div
stat9_done:
pha
txa
ora #$30
sta SCREEN + (9 * 40) + 23
pla
ora #$30
sta SCREEN + (9 * 40) + 24
lda #7
sta COLRAM + (9 * 40) + 23
sta COLRAM + (9 * 40) + 24
rts
display_stat_at_10:
ldx #0
stat10_div:
cmp #10
bcc stat10_done
sec
sbc #10
inx
jmp stat10_div
stat10_done:
pha
txa
ora #$30
sta SCREEN + (10 * 40) + 23
pla
ora #$30
sta SCREEN + (10 * 40) + 24
lda #2
sta COLRAM + (10 * 40) + 23
sta COLRAM + (10 * 40) + 24
rts
display_stat_at_12:
ldx #0
stat12_div:
cmp #10
bcc stat12_done
sec
sbc #10
inx
jmp stat12_div
stat12_done:
pha
txa
ora #$30
sta SCREEN + (12 * 40) + 23
pla
ora #$30
sta SCREEN + (12 * 40) + 24
lda #13
sta COLRAM + (12 * 40) + 23
sta COLRAM + (12 * 40) + 24
rts
; ----------------------------------------------------------------------------
; Calculate Accuracy
; ----------------------------------------------------------------------------
calculate_accuracy:
lda perfect_count
clc
adc good_count
sta total_hits
clc
adc miss_count
sta total_notes
beq accuracy_zero
lda total_hits
sta dividend_lo
lda #0
sta dividend_hi
ldx #100
mult_loop:
dex
beq mult_done
lda dividend_lo
clc
adc total_hits
sta dividend_lo
lda dividend_hi
adc #0
sta dividend_hi
jmp mult_loop
mult_done:
lda #0
sta accuracy
div_loop:
lda dividend_lo
sec
sbc total_notes
tay
lda dividend_hi
sbc #0
bcc div_done
sta dividend_hi
sty dividend_lo
inc accuracy
jmp div_loop
div_done:
rts
accuracy_zero:
lda #0
sta accuracy
rts
total_hits: !byte 0
total_notes: !byte 0
dividend_lo: !byte 0
dividend_hi: !byte 0
accuracy: !byte 0
; ----------------------------------------------------------------------------
; Display Accuracy
; ----------------------------------------------------------------------------
display_accuracy:
lda accuracy
ldx #0
acc_div_100:
cmp #100
bcc acc_done_100
sec
sbc #100
inx
jmp acc_div_100
acc_done_100:
pha
txa
ora #$30
sta SCREEN + (14 * 40) + 23
pla
ldx #0
acc_div_10:
cmp #10
bcc acc_done_10
sec
sbc #10
inx
jmp acc_div_10
acc_done_10:
pha
txa
ora #$30
sta SCREEN + (14 * 40) + 24
pla
ora #$30
sta SCREEN + (14 * 40) + 25
lda #$25
sta SCREEN + (14 * 40) + 26
lda #5
sta COLRAM + (14 * 40) + 23
sta COLRAM + (14 * 40) + 24
sta COLRAM + (14 * 40) + 25
sta COLRAM + (14 * 40) + 26
rts
; ----------------------------------------------------------------------------
; Copy Character Set from ROM to RAM
; ----------------------------------------------------------------------------
copy_charset:
sei
lda $01
pha
and #$FB
sta $01
ldx #0
copy_loop:
lda $D000,x
sta CHARSET,x
lda $D100,x
sta CHARSET+$100,x
lda $D200,x
sta CHARSET+$200,x
lda $D300,x
sta CHARSET+$300,x
lda $D400,x
sta CHARSET+$400,x
lda $D500,x
sta CHARSET+$500,x
lda $D600,x
sta CHARSET+$600,x
lda $D700,x
sta CHARSET+$700,x
inx
bne copy_loop
pla
sta $01
cli
jsr define_custom_chars
lda #$1C
sta CHARPTR
rts
; ----------------------------------------------------------------------------
; Define Custom Characters
; ----------------------------------------------------------------------------
define_custom_chars:
lda #%00000110
sta CHARSET + (CHAR_NOTE * 8) + 0
lda #%00011110
sta CHARSET + (CHAR_NOTE * 8) + 1
lda #%01111110
sta CHARSET + (CHAR_NOTE * 8) + 2
lda #%11111110
sta CHARSET + (CHAR_NOTE * 8) + 3
lda #%11111110
sta CHARSET + (CHAR_NOTE * 8) + 4
lda #%01111110
sta CHARSET + (CHAR_NOTE * 8) + 5
lda #%00011110
sta CHARSET + (CHAR_NOTE * 8) + 6
lda #%00000110
sta CHARSET + (CHAR_NOTE * 8) + 7
lda #%00000000
sta CHARSET + (CHAR_TRACK * 8) + 0
sta CHARSET + (CHAR_TRACK * 8) + 1
sta CHARSET + (CHAR_TRACK * 8) + 2
lda #%11111111
sta CHARSET + (CHAR_TRACK * 8) + 3
sta CHARSET + (CHAR_TRACK * 8) + 4
lda #%00000000
sta CHARSET + (CHAR_TRACK * 8) + 5
sta CHARSET + (CHAR_TRACK * 8) + 6
sta CHARSET + (CHAR_TRACK * 8) + 7
lda #%01100110
sta CHARSET + (CHAR_HITZONE * 8) + 0
sta CHARSET + (CHAR_HITZONE * 8) + 1
sta CHARSET + (CHAR_HITZONE * 8) + 2
sta CHARSET + (CHAR_HITZONE * 8) + 3
sta CHARSET + (CHAR_HITZONE * 8) + 4
sta CHARSET + (CHAR_HITZONE * 8) + 5
sta CHARSET + (CHAR_HITZONE * 8) + 6
sta CHARSET + (CHAR_HITZONE * 8) + 7
lda #%11111111
sta CHARSET + (CHAR_BAR_FULL * 8) + 0
sta CHARSET + (CHAR_BAR_FULL * 8) + 1
sta CHARSET + (CHAR_BAR_FULL * 8) + 2
sta CHARSET + (CHAR_BAR_FULL * 8) + 3
sta CHARSET + (CHAR_BAR_FULL * 8) + 4
sta CHARSET + (CHAR_BAR_FULL * 8) + 5
sta CHARSET + (CHAR_BAR_FULL * 8) + 6
sta CHARSET + (CHAR_BAR_FULL * 8) + 7
lda #%11111111
sta CHARSET + (CHAR_BAR_EMPTY * 8) + 0
lda #%10000001
sta CHARSET + (CHAR_BAR_EMPTY * 8) + 1
sta CHARSET + (CHAR_BAR_EMPTY * 8) + 2
sta CHARSET + (CHAR_BAR_EMPTY * 8) + 3
sta CHARSET + (CHAR_BAR_EMPTY * 8) + 4
sta CHARSET + (CHAR_BAR_EMPTY * 8) + 5
sta CHARSET + (CHAR_BAR_EMPTY * 8) + 6
lda #%11111111
sta CHARSET + (CHAR_BAR_EMPTY * 8) + 7
rts
; ----------------------------------------------------------------------------
; Initialize Health
; ----------------------------------------------------------------------------
init_health:
lda #HEALTH_START
sta health
jsr display_health
rts
; ----------------------------------------------------------------------------
; Initialize Score
; ----------------------------------------------------------------------------
init_score:
lda #0
sta score_lo
sta score_hi
sta miss_count
sta perfect_count
sta good_count
jsr display_score
jsr display_misses
rts
; ----------------------------------------------------------------------------
; Initialize Notes
; ----------------------------------------------------------------------------
init_notes:
ldx #MAX_NOTES-1
lda #0
init_notes_loop:
sta note_track,x
sta note_col,x
sta note_freq,x
dex
bpl init_notes_loop
rts
; ----------------------------------------------------------------------------
; Check Spawn Note
; ----------------------------------------------------------------------------
check_spawn_note:
lda song_ended
bne spawn_done_early
ldy #0
spawn_check_loop:
lda (song_pos),y
cmp #$FF
beq spawn_song_end
cmp beat_count
beq spawn_match
bcs spawn_done_early
jmp spawn_advance
spawn_match:
iny
lda (song_pos),y
sta temp_track
iny
lda (song_pos),y
pha
lda temp_track
jsr spawn_note_with_freq
pla
dey
dey
spawn_advance:
lda song_pos
clc
adc #3
sta song_pos
lda song_pos_hi
adc #0
sta song_pos_hi
jmp spawn_check_loop
spawn_song_end:
lda #1
sta song_ended
spawn_done_early:
rts
; ----------------------------------------------------------------------------
; Spawn Note With Frequency
; ----------------------------------------------------------------------------
spawn_note_with_freq:
sta temp_track
ldx #0
spawn_find_slot:
lda note_track,x
beq spawn_found_slot
inx
cpx #MAX_NOTES
bne spawn_find_slot
rts
spawn_found_slot:
lda temp_track
sta note_track,x
lda #NOTE_SPAWN_COL
sta note_col,x
tsx
lda $0103,x
sta note_freq,x
jsr draw_note
rts
; ----------------------------------------------------------------------------
; Update Notes
; ----------------------------------------------------------------------------
update_notes:
ldx #0
update_loop:
lda note_track,x
beq update_next
jsr erase_note
dec note_col,x
lda note_col,x
cmp #1
bcc update_miss
jsr draw_note
jmp update_next
update_miss:
lda note_track,x
sta miss_track
lda #0
sta note_track,x
jsr handle_miss
update_next:
inx
cpx #MAX_NOTES
bne update_loop
rts
; ----------------------------------------------------------------------------
; Handle Miss - Now breaks combo
; ----------------------------------------------------------------------------
handle_miss:
inc miss_count
jsr play_miss_sound
lda #MISS_COL
sta BORDER
lda #8
sta border_flash
jsr display_misses
jsr decrease_health
; Break the combo!
jsr break_combo
rts
; ----------------------------------------------------------------------------
; Decrease Health
; ----------------------------------------------------------------------------
decrease_health:
lda health
sec
sbc #HEALTH_MISS
bcc health_zero
sta health
jsr display_health
jsr check_game_over
rts
health_zero:
lda #0
sta health
jsr display_health
jsr check_game_over
rts
; ----------------------------------------------------------------------------
; Increase Health
; ----------------------------------------------------------------------------
increase_health:
clc
adc health
cmp #HEALTH_MAX
bcc health_ok
lda #HEALTH_MAX
health_ok:
sta health
jsr display_health
rts
; ----------------------------------------------------------------------------
; Check Game Over
; ----------------------------------------------------------------------------
check_game_over:
lda health
bne not_game_over
lda #STATE_GAMEOVER
sta game_state
jsr show_game_over
not_game_over:
rts
; ----------------------------------------------------------------------------
; Show Game Over
; ----------------------------------------------------------------------------
show_game_over:
ldx #0
game_over_loop:
lda game_over_text,x
beq game_over_done
sta SCREEN + (12 * 40) + 15,x
lda #2
sta COLRAM + (12 * 40) + 15,x
inx
jmp game_over_loop
game_over_done:
lda #2
sta BORDER
rts
game_over_text:
!scr "game over"
!byte 0
; ----------------------------------------------------------------------------
; Display Health
; ----------------------------------------------------------------------------
display_health:
lda health
lsr
lsr
lsr
sta temp_health
ldx #0
lda temp_health
beq draw_empty_bars
draw_full_bars:
lda #CHAR_BAR_FULL
sta SCREEN + (HEALTH_ROW * 40) + 12,x
lda #HEALTH_COL
sta COLRAM + (HEALTH_ROW * 40) + 12,x
inx
cpx temp_health
bne draw_full_bars
draw_empty_bars:
cpx #8
beq health_done
lda #CHAR_BAR_EMPTY
sta SCREEN + (HEALTH_ROW * 40) + 12,x
lda #11
sta COLRAM + (HEALTH_ROW * 40) + 12,x
inx
jmp draw_empty_bars
health_done:
rts
temp_health: !byte 0
; ----------------------------------------------------------------------------
; Play Miss Sound
; ----------------------------------------------------------------------------
play_miss_sound:
lda #0
sta SID_V3_FREQ_LO
lda #MISS_FREQ
sta SID_V3_FREQ_HI
lda #MISS_AD
sta SID_V3_AD
lda #MISS_SR
sta SID_V3_SR
lda #MISS_WAVE
ora #$01
sta SID_V3_CTRL
rts
; ----------------------------------------------------------------------------
; Draw Note
; ----------------------------------------------------------------------------
draw_note:
lda note_track,x
cmp #1
beq draw_note_t1
cmp #2
beq draw_note_t2
cmp #3
beq draw_note_t3
rts
draw_note_t1:
lda note_col,x
clc
adc #<(SCREEN + TRACK1_ROW * 40)
sta ZP_PTR
lda #>(SCREEN + TRACK1_ROW * 40)
adc #0
sta ZP_PTR_HI
ldy #0
lda #CHAR_NOTE
sta (ZP_PTR),y
lda note_col,x
clc
adc #<(COLRAM + TRACK1_ROW * 40)
sta ZP_PTR
lda #>(COLRAM + TRACK1_ROW * 40)
adc #0
sta ZP_PTR_HI
lda #TRACK1_NOTE_COL
sta (ZP_PTR),y
rts
draw_note_t2:
lda note_col,x
clc
adc #<(SCREEN + TRACK2_ROW * 40)
sta ZP_PTR
lda #>(SCREEN + TRACK2_ROW * 40)
adc #0
sta ZP_PTR_HI
ldy #0
lda #CHAR_NOTE
sta (ZP_PTR),y
lda note_col,x
clc
adc #<(COLRAM + TRACK2_ROW * 40)
sta ZP_PTR
lda #>(COLRAM + TRACK2_ROW * 40)
adc #0
sta ZP_PTR_HI
lda #TRACK2_NOTE_COL
sta (ZP_PTR),y
rts
draw_note_t3:
lda note_col,x
clc
adc #<(SCREEN + TRACK3_ROW * 40)
sta ZP_PTR
lda #>(SCREEN + TRACK3_ROW * 40)
adc #0
sta ZP_PTR_HI
ldy #0
lda #CHAR_NOTE
sta (ZP_PTR),y
lda note_col,x
clc
adc #<(COLRAM + TRACK3_ROW * 40)
sta ZP_PTR
lda #>(COLRAM + TRACK3_ROW * 40)
adc #0
sta ZP_PTR_HI
lda #TRACK3_NOTE_COL
sta (ZP_PTR),y
rts
; ----------------------------------------------------------------------------
; Erase Note
; ----------------------------------------------------------------------------
erase_note:
lda note_track,x
cmp #1
beq erase_note_t1
cmp #2
beq erase_note_t2
cmp #3
beq erase_note_t3
rts
erase_note_t1:
lda note_col,x
clc
adc #<(SCREEN + TRACK1_ROW * 40)
sta ZP_PTR
lda #>(SCREEN + TRACK1_ROW * 40)
adc #0
sta ZP_PTR_HI
ldy #0
lda #CHAR_TRACK
sta (ZP_PTR),y
lda note_col,x
clc
adc #<(COLRAM + TRACK1_ROW * 40)
sta ZP_PTR
lda #>(COLRAM + TRACK1_ROW * 40)
adc #0
sta ZP_PTR_HI
lda #TRACK_LINE_COL
sta (ZP_PTR),y
rts
erase_note_t2:
lda note_col,x
clc
adc #<(SCREEN + TRACK2_ROW * 40)
sta ZP_PTR
lda #>(SCREEN + TRACK2_ROW * 40)
adc #0
sta ZP_PTR_HI
ldy #0
lda #CHAR_TRACK
sta (ZP_PTR),y
lda note_col,x
clc
adc #<(COLRAM + TRACK2_ROW * 40)
sta ZP_PTR
lda #>(COLRAM + TRACK2_ROW * 40)
adc #0
sta ZP_PTR_HI
lda #TRACK_LINE_COL
sta (ZP_PTR),y
rts
erase_note_t3:
lda note_col,x
clc
adc #<(SCREEN + TRACK3_ROW * 40)
sta ZP_PTR
lda #>(SCREEN + TRACK3_ROW * 40)
adc #0
sta ZP_PTR_HI
ldy #0
lda #CHAR_TRACK
sta (ZP_PTR),y
lda note_col,x
clc
adc #<(COLRAM + TRACK3_ROW * 40)
sta ZP_PTR
lda #>(COLRAM + TRACK3_ROW * 40)
adc #0
sta ZP_PTR_HI
lda #TRACK_LINE_COL
sta (ZP_PTR),y
rts
; ----------------------------------------------------------------------------
; Initialize Screen
; ----------------------------------------------------------------------------
init_screen:
lda #BORDER_COL
sta BORDER
lda #BG_COL
sta BGCOL
ldx #0
lda #CHAR_SPACE
clr_screen:
sta SCREEN,x
sta SCREEN+$100,x
sta SCREEN+$200,x
sta SCREEN+$2E8,x
inx
bne clr_screen
ldx #0
lda #TRACK_LINE_COL
clr_colour:
sta COLRAM,x
sta COLRAM+$100,x
sta COLRAM+$200,x
sta COLRAM+$2E8,x
inx
bne clr_colour
jsr draw_tracks
jsr draw_hit_zones
jsr draw_labels
rts
; ----------------------------------------------------------------------------
; Draw Tracks
; ----------------------------------------------------------------------------
draw_tracks:
lda #CHAR_TRACK
ldx #0
draw_t1:
sta SCREEN + (TRACK1_ROW * 40),x
inx
cpx #38
bne draw_t1
ldx #0
draw_t2:
sta SCREEN + (TRACK2_ROW * 40),x
inx
cpx #38
bne draw_t2
ldx #0
draw_t3:
sta SCREEN + (TRACK3_ROW * 40),x
inx
cpx #38
bne draw_t3
rts
; ----------------------------------------------------------------------------
; Draw Hit Zones
; ----------------------------------------------------------------------------
draw_hit_zones:
lda #CHAR_HITZONE
sta SCREEN + ((TRACK1_ROW-2) * 40) + HIT_ZONE_COLUMN
sta SCREEN + ((TRACK1_ROW-1) * 40) + HIT_ZONE_COLUMN
sta SCREEN + (TRACK1_ROW * 40) + HIT_ZONE_COLUMN
sta SCREEN + ((TRACK1_ROW+1) * 40) + HIT_ZONE_COLUMN
sta SCREEN + ((TRACK2_ROW-1) * 40) + HIT_ZONE_COLUMN
sta SCREEN + (TRACK2_ROW * 40) + HIT_ZONE_COLUMN
sta SCREEN + ((TRACK2_ROW+1) * 40) + HIT_ZONE_COLUMN
sta SCREEN + ((TRACK3_ROW-1) * 40) + HIT_ZONE_COLUMN
sta SCREEN + (TRACK3_ROW * 40) + HIT_ZONE_COLUMN
sta SCREEN + ((TRACK3_ROW+1) * 40) + HIT_ZONE_COLUMN
sta SCREEN + ((TRACK3_ROW+2) * 40) + HIT_ZONE_COLUMN
lda #HIT_ZONE_COL
sta COLRAM + ((TRACK1_ROW-2) * 40) + HIT_ZONE_COLUMN
sta COLRAM + ((TRACK1_ROW-1) * 40) + HIT_ZONE_COLUMN
sta COLRAM + (TRACK1_ROW * 40) + HIT_ZONE_COLUMN
sta COLRAM + ((TRACK1_ROW+1) * 40) + HIT_ZONE_COLUMN
sta COLRAM + ((TRACK2_ROW-1) * 40) + HIT_ZONE_COLUMN
sta COLRAM + (TRACK2_ROW * 40) + HIT_ZONE_COLUMN
sta COLRAM + ((TRACK2_ROW+1) * 40) + HIT_ZONE_COLUMN
sta COLRAM + ((TRACK3_ROW-1) * 40) + HIT_ZONE_COLUMN
sta COLRAM + (TRACK3_ROW * 40) + HIT_ZONE_COLUMN
sta COLRAM + ((TRACK3_ROW+1) * 40) + HIT_ZONE_COLUMN
sta COLRAM + ((TRACK3_ROW+2) * 40) + HIT_ZONE_COLUMN
rts
; ----------------------------------------------------------------------------
; Draw Labels
; ----------------------------------------------------------------------------
draw_labels:
ldx #0
draw_score_label:
lda score_label,x
beq draw_score_label_done
sta SCREEN + 1,x
lda #1
sta COLRAM + 1,x
inx
bne draw_score_label
draw_score_label_done:
ldx #0
draw_miss_label:
lda miss_label,x
beq draw_miss_label_done
sta SCREEN + 15,x
lda #2
sta COLRAM + 15,x
inx
bne draw_miss_label
draw_miss_label_done:
ldx #0
draw_title:
lda title_text,x
beq draw_title_done
sta SCREEN + 27,x
lda #1
sta COLRAM + 27,x
inx
bne draw_title
draw_title_done:
ldx #0
draw_health_label:
lda health_label,x
beq draw_health_label_done
sta SCREEN + (HEALTH_ROW * 40) + 4,x
lda #5
sta COLRAM + (HEALTH_ROW * 40) + 4,x
inx
bne draw_health_label
draw_health_label_done:
ldx #0
draw_progress_label:
lda progress_label,x
beq draw_progress_label_done
sta SCREEN + (PROGRESS_ROW * 40) + 4,x
lda #3
sta COLRAM + (PROGRESS_ROW * 40) + 4,x
inx
bne draw_progress_label
draw_progress_label_done:
lda #$1A
sta SCREEN + (TRACK1_ROW * 40)
lda #TRACK1_NOTE_COL
sta COLRAM + (TRACK1_ROW * 40)
lda #$18
sta SCREEN + (TRACK2_ROW * 40)
lda #TRACK2_NOTE_COL
sta COLRAM + (TRACK2_ROW * 40)
lda #$03
sta SCREEN + (TRACK3_ROW * 40)
lda #TRACK3_NOTE_COL
sta COLRAM + (TRACK3_ROW * 40)
rts
score_label:
!scr "score:"
!byte 0
miss_label:
!scr "miss:"
!byte 0
title_text:
!scr "sid symphony"
!byte 0
health_label:
!scr "health:"
!byte 0
progress_label:
!scr "song:"
!byte 0
; ----------------------------------------------------------------------------
; Initialize SID
; ----------------------------------------------------------------------------
init_sid:
ldx #$18
lda #0
clear_sid:
sta SID,x
dex
bpl clear_sid
lda #$0F
sta SID_VOLUME
lda #$00
sta SID_V1_FREQ_LO
lda #VOICE1_FREQ
sta SID_V1_FREQ_HI
lda #PULSE_WIDTH
sta SID_V1_PWHI
lda #VOICE_AD
sta SID_V1_AD
lda #VOICE_SR
sta SID_V1_SR
lda #$00
sta SID_V2_FREQ_LO
lda #VOICE2_FREQ
sta SID_V2_FREQ_HI
lda #PULSE_WIDTH
sta SID_V2_PWHI
lda #VOICE_AD
sta SID_V2_AD
lda #VOICE_SR
sta SID_V2_SR
lda #$00
sta SID_V3_FREQ_LO
lda #VOICE3_FREQ
sta SID_V3_FREQ_HI
lda #PULSE_WIDTH
sta SID_V3_PWHI
lda #VOICE_AD
sta SID_V3_AD
lda #VOICE_SR
sta SID_V3_SR
rts
; ----------------------------------------------------------------------------
; Reset Track Colours
; ----------------------------------------------------------------------------
reset_track_colours:
ldx #0
lda #TRACK_LINE_COL
reset_t1:
sta COLRAM + (TRACK1_ROW * 40),x
inx
cpx #38
bne reset_t1
ldx #0
reset_t2:
sta COLRAM + (TRACK2_ROW * 40),x
inx
cpx #38
bne reset_t2
ldx #0
reset_t3:
sta COLRAM + (TRACK3_ROW * 40),x
inx
cpx #38
bne reset_t3
lda #TRACK1_NOTE_COL
sta COLRAM + (TRACK1_ROW * 40)
lda #TRACK2_NOTE_COL
sta COLRAM + (TRACK2_ROW * 40)
lda #TRACK3_NOTE_COL
sta COLRAM + (TRACK3_ROW * 40)
lda #HIT_ZONE_COL
sta COLRAM + (TRACK1_ROW * 40) + HIT_ZONE_COLUMN
sta COLRAM + (TRACK2_ROW * 40) + HIT_ZONE_COLUMN
sta COLRAM + (TRACK3_ROW * 40) + HIT_ZONE_COLUMN
jsr redraw_all_notes
rts
; ----------------------------------------------------------------------------
; Redraw All Notes
; ----------------------------------------------------------------------------
redraw_all_notes:
ldx #0
redraw_loop:
lda note_track,x
beq redraw_next
jsr draw_note
redraw_next:
inx
cpx #MAX_NOTES
bne redraw_loop
rts
; ----------------------------------------------------------------------------
; Update Border Flash
; ----------------------------------------------------------------------------
update_border_flash:
lda border_flash
beq flash_done
dec border_flash
bne flash_done
lda #BORDER_COL
sta BORDER
flash_done:
rts
; ----------------------------------------------------------------------------
; Check Keys
; ----------------------------------------------------------------------------
check_keys:
lda #$FD
sta CIA1_PRA
lda CIA1_PRB
and #$10
bne check_x_key
lda #1
sta key_pressed
jsr check_hit
bcc check_x_key
jsr play_voice1_note
jsr flash_track1_hit
jsr award_points
check_x_key:
lda #$FB
sta CIA1_PRA
lda CIA1_PRB
and #$80
bne check_c_key
lda #2
sta key_pressed
jsr check_hit
bcc check_c_key
jsr play_voice2_note
jsr flash_track2_hit
jsr award_points
check_c_key:
lda #$FB
sta CIA1_PRA
lda CIA1_PRB
and #$10
bne check_keys_done
lda #3
sta key_pressed
jsr check_hit
bcc check_keys_done
jsr play_voice3_note
jsr flash_track3_hit
jsr award_points
check_keys_done:
lda #$FF
sta CIA1_PRA
rts
; ----------------------------------------------------------------------------
; Check Hit
; ----------------------------------------------------------------------------
check_hit:
ldx #0
check_hit_loop:
lda note_track,x
beq check_hit_next
cmp key_pressed
bne check_hit_next
lda note_col,x
cmp #HIT_ZONE_MIN
bcc check_hit_next
cmp #HIT_ZONE_MAX+1
bcs check_hit_next
lda note_freq,x
sta hit_note_freq
lda note_col,x
cmp #HIT_ZONE_CENTRE
bcc hit_good
cmp #HIT_ZONE_CENTRE+2
bcs hit_good
lda #2
sta hit_quality
jmp hit_found
hit_good:
lda #1
sta hit_quality
hit_found:
jsr erase_note
lda #0
sta note_track,x
sec
rts
check_hit_next:
inx
cpx #MAX_NOTES
bne check_hit_loop
lda #0
sta hit_quality
clc
rts
; ----------------------------------------------------------------------------
; Award Points - Now uses combo multiplier
; ----------------------------------------------------------------------------
award_points:
; Increment combo first
jsr increment_combo
; Get base score
lda hit_quality
cmp #2
beq award_perfect
; Good hit: 50 * multiplier
lda #GOOD_SCORE
jsr apply_multiplier
jsr add_score
inc good_count
lda #GOOD_COL
sta BORDER
lda #4
sta border_flash
lda #HEALTH_GOOD
jsr increase_health
jmp award_done
award_perfect:
; Perfect hit: 100 * multiplier
lda #PERFECT_SCORE
jsr apply_multiplier
jsr add_score
inc perfect_count
lda #PERFECT_COL
sta BORDER
lda #6
sta border_flash
lda #HEALTH_PERFECT
jsr increase_health
award_done:
jsr display_score
rts
; ----------------------------------------------------------------------------
; Apply Multiplier - A = base score, returns multiplied score in score_add_lo/hi
; ----------------------------------------------------------------------------
apply_multiplier:
sta base_score
jsr get_multiplier
sta current_mult
; Start with base score
lda base_score
sta score_add_lo
lda #0
sta score_add_hi
; If multiplier is 1, we're done
lda current_mult
cmp #1
beq mult_done_apply
; Multiply by adding base_score (mult-1) more times
dec current_mult
mult_add_loop:
lda score_add_lo
clc
adc base_score
sta score_add_lo
lda score_add_hi
adc #0
sta score_add_hi
dec current_mult
bne mult_add_loop
mult_done_apply:
rts
base_score: !byte 0
current_mult: !byte 0
score_add_lo: !byte 0
score_add_hi: !byte 0
; ----------------------------------------------------------------------------
; Add Score - Adds score_add_lo/hi to score
; ----------------------------------------------------------------------------
add_score:
lda score_lo
clc
adc score_add_lo
sta score_lo
lda score_hi
adc score_add_hi
sta score_hi
rts
; ----------------------------------------------------------------------------
; Display Score
; ----------------------------------------------------------------------------
display_score:
lda score_lo
sta work_lo
lda score_hi
sta work_hi
ldx #0
div_10000:
lda work_lo
sec
sbc #<10000
tay
lda work_hi
sbc #>10000
bcc done_10000
sta work_hi
sty work_lo
inx
jmp div_10000
done_10000:
txa
ora #$30
sta SCREEN + 8
ldx #0
div_1000:
lda work_lo
sec
sbc #<1000
tay
lda work_hi
sbc #>1000
bcc done_1000
sta work_hi
sty work_lo
inx
jmp div_1000
done_1000:
txa
ora #$30
sta SCREEN + 9
ldx #0
div_100:
lda work_lo
sec
sbc #100
bcc done_100
sta work_lo
inx
jmp div_100
done_100:
txa
ora #$30
sta SCREEN + 10
ldx #0
div_10:
lda work_lo
sec
sbc #10
bcc done_10
sta work_lo
inx
jmp div_10
done_10:
txa
ora #$30
sta SCREEN + 11
lda work_lo
ora #$30
sta SCREEN + 12
lda #7
sta COLRAM + 8
sta COLRAM + 9
sta COLRAM + 10
sta COLRAM + 11
sta COLRAM + 12
rts
; ----------------------------------------------------------------------------
; Display Misses
; ----------------------------------------------------------------------------
display_misses:
lda miss_count
ldx #0
miss_div_10:
cmp #10
bcc miss_done_10
sec
sbc #10
inx
jmp miss_div_10
miss_done_10:
pha
txa
ora #$30
sta SCREEN + 21
pla
ora #$30
sta SCREEN + 22
lda #2
sta COLRAM + 21
sta COLRAM + 22
rts
work_lo: !byte 0
work_hi: !byte 0
; ----------------------------------------------------------------------------
; Play Voices With Note Frequency
; ----------------------------------------------------------------------------
play_voice1_note:
lda #0
sta SID_V1_FREQ_LO
lda hit_note_freq
sta SID_V1_FREQ_HI
lda #VOICE1_WAVE
ora #$01
sta SID_V1_CTRL
rts
play_voice2_note:
lda #0
sta SID_V2_FREQ_LO
lda hit_note_freq
sta SID_V2_FREQ_HI
lda #VOICE2_WAVE
ora #$01
sta SID_V2_CTRL
rts
play_voice3_note:
lda #0
sta SID_V3_FREQ_LO
lda hit_note_freq
sta SID_V3_FREQ_HI
lda #VOICE3_WAVE
ora #$01
sta SID_V3_CTRL
rts
; ----------------------------------------------------------------------------
; Flash Tracks on Hit
; ----------------------------------------------------------------------------
flash_track1_hit:
ldx #0
lda #HIT_COL
flash_t1h_loop:
sta COLRAM + (TRACK1_ROW * 40),x
inx
cpx #38
bne flash_t1h_loop
lda #1
sta COLRAM + (TRACK1_ROW * 40)
rts
flash_track2_hit:
ldx #0
lda #HIT_COL
flash_t2h_loop:
sta COLRAM + (TRACK2_ROW * 40),x
inx
cpx #38
bne flash_t2h_loop
lda #1
sta COLRAM + (TRACK2_ROW * 40)
rts
flash_track3_hit:
ldx #0
lda #HIT_COL
flash_t3h_loop:
sta COLRAM + (TRACK3_ROW * 40),x
inx
cpx #38
bne flash_t3h_loop
lda #1
sta COLRAM + (TRACK3_ROW * 40)
rts
; ============================================================================
; SONG DATA
; ============================================================================
song_data:
!byte 0, 1, $47
!byte 2, 2, $2C
!byte 4, 3, $11
!byte 8, 1, $3B
!byte 10, 2, $27
!byte 12, 3, $13
!byte 16, 1, $35
!byte 17, 2, $2C
!byte 18, 1, $3B
!byte 20, 3, $16
!byte 24, 1, $47
!byte 26, 2, $35
!byte 28, 3, $11
!byte 32, 2, $2F
!byte 34, 1, $4F
!byte 36, 3, $17
!byte 40, 1, $58
!byte 42, 2, $2C
!byte 44, 3, $11
!byte 46, 2, $27
!byte 48, 1, $6A
!byte 49, 2, $35
!byte 50, 1, $58
!byte 52, 3, $1A
!byte 54, 2, $2F
!byte 56, 1, $47
!byte 58, 2, $2C
!byte 60, 3, $11
!byte 62, 1, $35
!byte $FF
; ----------------------------------------------------------------------------
; Note Arrays
; ----------------------------------------------------------------------------
note_track:
!fill MAX_NOTES, 0
note_col:
!fill MAX_NOTES, 0
note_freq:
!fill MAX_NOTES, 0
; ----------------------------------------------------------------------------
; Game Variables
; ----------------------------------------------------------------------------
score_lo: !byte 0
score_hi: !byte 0
miss_count: !byte 0
perfect_count: !byte 0
good_count: !byte 0
health: !byte 0
combo: !byte 0
max_combo: !byte 0
Try This: Combo Sound
Add an ascending tone as combo builds:
play_combo_sound:
lda combo
lsr ; Divide by 2
clc
adc #$10 ; Base frequency
sta SID_V1_FREQ_HI
; ... trigger note ...
Higher combos produce higher pitches.
Try This: Combo Milestones
Flash the screen when reaching new tiers:
increment_combo:
inc combo
lda combo
cmp #COMBO_TIER_2
beq combo_milestone
cmp #COMBO_TIER_3
beq combo_milestone
cmp #COMBO_TIER_4
beq combo_milestone
jmp no_milestone
combo_milestone:
; Flash border yellow briefly
lda #7
sta BORDER
lda #8
sta border_flash
no_milestone:
; ... rest of routine ...
Celebrate reaching 10, 25, and 50.
What You’ve Learnt
- Combo tracking - Increment on hit, reset on miss
- Multiplier tiers - Thresholds that increase rewards
- 8-bit multiplication - Repeated addition for score scaling
- Colour feedback - Visual encoding of game state
- Risk/reward design - Tension between safety and bonus
What’s Next
In Unit 13, we’ll add a title screen. The game will start with a proper presentation, wait for player input, then begin play.
What Changed
| 1 | 1 | ; ============================================================================ | |
| 2 | - | ; SID SYMPHONY - Unit 11: Song End and Results | |
| 2 | + | ; SID SYMPHONY - Unit 12: Combo System | |
| 3 | 3 | ; ============================================================================ | |
| 4 | - | ; Detect song completion. Display results with score breakdown. The game | |
| 5 | - | ; has a proper ending now - victory or defeat, then replay. | |
| 4 | + | ; Consecutive hits build a combo. Higher combos increase the score multiplier. | |
| 5 | + | ; Miss a note and the combo resets to zero. Consistency is rewarded. | |
| 6 | 6 | ; | |
| 7 | 7 | ; Controls: Z = Track 1 (high), X = Track 2 (mid), C = Track 3 (low) | |
| 8 | 8 | ; ============================================================================ | |
| ... | |||
| 48 | 48 | | |
| 49 | 49 | HEALTH_COL = 5 | |
| 50 | 50 | PROGRESS_COL = 3 | |
| 51 | + | COMBO_COL = 13 ; Yellow for combo display | |
| 51 | 52 | | |
| 52 | 53 | ; ============================================================================ | |
| 53 | 54 | ; SCORING SETTINGS | |
| ... | |||
| 55 | 56 | | |
| 56 | 57 | PERFECT_SCORE = 100 | |
| 57 | 58 | GOOD_SCORE = 50 | |
| 59 | + | | |
| 60 | + | ; ============================================================================ | |
| 61 | + | ; COMBO SETTINGS | |
| 62 | + | ; ============================================================================ | |
| 63 | + | | |
| 64 | + | COMBO_TIER_2 = 10 ; 2x multiplier at 10 combo | |
| 65 | + | COMBO_TIER_3 = 25 ; 3x multiplier at 25 combo | |
| 66 | + | COMBO_TIER_4 = 50 ; 4x multiplier at 50 combo | |
| 58 | 67 | | |
| 59 | 68 | ; ============================================================================ | |
| 60 | 69 | ; HEALTH SETTINGS | |
| ... | |||
| 138 | 147 | ; HUD positions | |
| 139 | 148 | HEALTH_ROW = 23 | |
| 140 | 149 | PROGRESS_ROW = 24 | |
| 150 | + | | |
| 151 | + | ; Combo display position | |
| 152 | + | COMBO_ROW = 2 | |
| 153 | + | COMBO_COL_POS = 17 | |
| 141 | 154 | | |
| 142 | 155 | ; Hit zone | |
| 143 | 156 | HIT_ZONE_COLUMN = 3 | |
| ... | |||
| 156 | 169 | | |
| 157 | 170 | ; Timing | |
| 158 | 171 | FRAMES_PER_BEAT = 25 | |
| 159 | - | END_DELAY_FRAMES = 75 ; Wait after last note before results | |
| 172 | + | END_DELAY_FRAMES = 75 | |
| 160 | 173 | | |
| 161 | 174 | ; Zero page | |
| 162 | 175 | ZP_PTR = $FB | |
| ... | |||
| 175 | 188 | game_state = $0B | |
| 176 | 189 | hit_note_freq = $0C | |
| 177 | 190 | song_beat = $0D | |
| 178 | - | song_ended = $0E ; Flag: song data exhausted | |
| 179 | - | end_delay = $0F ; Countdown after last note | |
| 191 | + | song_ended = $0E | |
| 192 | + | end_delay = $0F | |
| 180 | 193 | | |
| 181 | 194 | ; ---------------------------------------------------------------------------- | |
| 182 | 195 | ; BASIC Stub | |
| ... | |||
| 236 | 249 | jsr init_notes | |
| 237 | 250 | jsr init_score | |
| 238 | 251 | jsr init_health | |
| 252 | + | jsr init_combo | |
| 239 | 253 | jsr init_song | |
| 240 | 254 | | |
| 241 | 255 | lda #STATE_PLAYING | |
| 242 | 256 | sta game_state | |
| 257 | + | | |
| 258 | + | rts | |
| 259 | + | | |
| 260 | + | ; ---------------------------------------------------------------------------- | |
| 261 | + | ; Initialize Combo | |
| 262 | + | ; ---------------------------------------------------------------------------- | |
| 243 | 263 | | |
| 264 | + | init_combo: | |
| 265 | + | lda #0 | |
| 266 | + | sta combo | |
| 267 | + | sta max_combo | |
| 268 | + | jsr display_combo | |
| 244 | 269 | rts | |
| 245 | 270 | | |
| 246 | 271 | ; ---------------------------------------------------------------------------- | |
| ... | |||
| 263 | 288 | jsr reset_track_colours | |
| 264 | 289 | jsr update_border_flash | |
| 265 | 290 | jsr check_keys | |
| 266 | - | | |
| 267 | - | ; Check for song end | |
| 268 | 291 | jsr check_song_end | |
| 269 | 292 | | |
| 270 | 293 | rts | |
| ... | |||
| 274 | 297 | ; ---------------------------------------------------------------------------- | |
| 275 | 298 | | |
| 276 | 299 | update_results: | |
| 277 | - | ; Wait for any key to restart | |
| 278 | 300 | lda #$00 | |
| 279 | 301 | sta CIA1_PRA | |
| 280 | 302 | lda CIA1_PRB | |
| 281 | 303 | cmp #$FF | |
| 282 | 304 | beq results_wait | |
| 283 | 305 | | |
| 284 | - | ; Key pressed - restart game | |
| 285 | 306 | jsr init_game | |
| 286 | 307 | | |
| 287 | 308 | results_wait: | |
| ... | |||
| 294 | 315 | ; ---------------------------------------------------------------------------- | |
| 295 | 316 | | |
| 296 | 317 | update_gameover: | |
| 297 | - | ; Wait for any key to restart | |
| 298 | 318 | lda #$00 | |
| 299 | 319 | sta CIA1_PRA | |
| 300 | 320 | lda CIA1_PRB | |
| 301 | 321 | cmp #$FF | |
| 302 | 322 | beq gameover_wait | |
| 303 | 323 | | |
| 304 | - | ; Key pressed - restart game | |
| 305 | 324 | jsr init_game | |
| 306 | 325 | | |
| 307 | 326 | gameover_wait: | |
| ... | |||
| 314 | 333 | ; ---------------------------------------------------------------------------- | |
| 315 | 334 | | |
| 316 | 335 | check_song_end: | |
| 317 | - | ; First check if song data exhausted | |
| 318 | 336 | lda song_ended | |
| 319 | 337 | beq song_not_ended | |
| 320 | 338 | | |
| 321 | - | ; Song data done - wait for all notes to clear | |
| 322 | 339 | ldx #0 | |
| 323 | 340 | check_notes_clear: | |
| 324 | 341 | lda note_track,x | |
| ... | |||
| 327 | 344 | cpx #MAX_NOTES | |
| 328 | 345 | bne check_notes_clear | |
| 329 | 346 | | |
| 330 | - | ; All notes cleared - count down delay | |
| 331 | 347 | dec end_delay | |
| 332 | 348 | bne song_not_ended | |
| 333 | 349 | | |
| 334 | - | ; Delay done - show results | |
| 335 | 350 | jsr show_results | |
| 336 | 351 | lda #STATE_RESULTS | |
| 337 | 352 | sta game_state | |
| ... | |||
| 378 | 393 | cmp #SONG_LENGTH | |
| 379 | 394 | bcc song_continues | |
| 380 | 395 | | |
| 381 | - | ; Song reached end | |
| 382 | 396 | lda #1 | |
| 383 | 397 | sta song_ended | |
| 384 | 398 | | |
| ... | |||
| 425 | 439 | rts | |
| 426 | 440 | | |
| 427 | 441 | temp_progress: !byte 0 | |
| 442 | + | | |
| 443 | + | ; ---------------------------------------------------------------------------- | |
| 444 | + | ; Get Multiplier - Returns multiplier (1-4) based on combo | |
| 445 | + | ; ---------------------------------------------------------------------------- | |
| 446 | + | | |
| 447 | + | get_multiplier: | |
| 448 | + | lda combo | |
| 449 | + | cmp #COMBO_TIER_4 | |
| 450 | + | bcs mult_4x | |
| 451 | + | cmp #COMBO_TIER_3 | |
| 452 | + | bcs mult_3x | |
| 453 | + | cmp #COMBO_TIER_2 | |
| 454 | + | bcs mult_2x | |
| 455 | + | | |
| 456 | + | ; Default: 1x | |
| 457 | + | lda #1 | |
| 458 | + | rts | |
| 459 | + | | |
| 460 | + | mult_2x: | |
| 461 | + | lda #2 | |
| 462 | + | rts | |
| 463 | + | | |
| 464 | + | mult_3x: | |
| 465 | + | lda #3 | |
| 466 | + | rts | |
| 467 | + | | |
| 468 | + | mult_4x: | |
| 469 | + | lda #4 | |
| 470 | + | rts | |
| 471 | + | | |
| 472 | + | ; ---------------------------------------------------------------------------- | |
| 473 | + | ; Display Combo | |
| 474 | + | ; ---------------------------------------------------------------------------- | |
| 475 | + | | |
| 476 | + | display_combo: | |
| 477 | + | ; Draw "COMBO:" label | |
| 478 | + | ldx #0 | |
| 479 | + | draw_combo_label: | |
| 480 | + | lda combo_label,x | |
| 481 | + | beq draw_combo_value | |
| 482 | + | sta SCREEN + (COMBO_ROW * 40) + 12,x | |
| 483 | + | lda #11 | |
| 484 | + | sta COLRAM + (COMBO_ROW * 40) + 12,x | |
| 485 | + | inx | |
| 486 | + | jmp draw_combo_label | |
| 487 | + | | |
| 488 | + | draw_combo_value: | |
| 489 | + | ; Display 3-digit combo value | |
| 490 | + | lda combo | |
| 491 | + | | |
| 492 | + | ; Hundreds | |
| 493 | + | ldx #0 | |
| 494 | + | combo_div_100: | |
| 495 | + | cmp #100 | |
| 496 | + | bcc combo_done_100 | |
| 497 | + | sec | |
| 498 | + | sbc #100 | |
| 499 | + | inx | |
| 500 | + | jmp combo_div_100 | |
| 501 | + | combo_done_100: | |
| 502 | + | pha | |
| 503 | + | txa | |
| 504 | + | ora #$30 | |
| 505 | + | sta SCREEN + (COMBO_ROW * 40) + 18 | |
| 506 | + | pla | |
| 507 | + | | |
| 508 | + | ; Tens | |
| 509 | + | ldx #0 | |
| 510 | + | combo_div_10: | |
| 511 | + | cmp #10 | |
| 512 | + | bcc combo_done_10 | |
| 513 | + | sec | |
| 514 | + | sbc #10 | |
| 515 | + | inx | |
| 516 | + | jmp combo_div_10 | |
| 517 | + | combo_done_10: | |
| 518 | + | pha | |
| 519 | + | txa | |
| 520 | + | ora #$30 | |
| 521 | + | sta SCREEN + (COMBO_ROW * 40) + 19 | |
| 522 | + | pla | |
| 523 | + | | |
| 524 | + | ; Ones | |
| 525 | + | ora #$30 | |
| 526 | + | sta SCREEN + (COMBO_ROW * 40) + 20 | |
| 527 | + | | |
| 528 | + | ; Colour based on multiplier tier | |
| 529 | + | jsr get_multiplier | |
| 530 | + | cmp #4 | |
| 531 | + | beq combo_col_4x | |
| 532 | + | cmp #3 | |
| 533 | + | beq combo_col_3x | |
| 534 | + | cmp #2 | |
| 535 | + | beq combo_col_2x | |
| 536 | + | | |
| 537 | + | ; 1x = grey | |
| 538 | + | lda #11 | |
| 539 | + | jmp set_combo_col | |
| 540 | + | | |
| 541 | + | combo_col_2x: | |
| 542 | + | lda #7 ; Yellow | |
| 543 | + | jmp set_combo_col | |
| 544 | + | | |
| 545 | + | combo_col_3x: | |
| 546 | + | lda #5 ; Green | |
| 547 | + | jmp set_combo_col | |
| 548 | + | | |
| 549 | + | combo_col_4x: | |
| 550 | + | lda #1 ; White (best) | |
| 551 | + | | |
| 552 | + | set_combo_col: | |
| 553 | + | sta COLRAM + (COMBO_ROW * 40) + 18 | |
| 554 | + | sta COLRAM + (COMBO_ROW * 40) + 19 | |
| 555 | + | sta COLRAM + (COMBO_ROW * 40) + 20 | |
| 556 | + | | |
| 557 | + | ; Display multiplier | |
| 558 | + | jsr get_multiplier | |
| 559 | + | ora #$30 | |
| 560 | + | sta SCREEN + (COMBO_ROW * 40) + 22 | |
| 561 | + | lda #$18 ; 'x' character | |
| 562 | + | sta SCREEN + (COMBO_ROW * 40) + 23 | |
| 563 | + | | |
| 564 | + | jsr get_multiplier | |
| 565 | + | cmp #4 | |
| 566 | + | beq mult_col_4x | |
| 567 | + | cmp #3 | |
| 568 | + | beq mult_col_3x | |
| 569 | + | cmp #2 | |
| 570 | + | beq mult_col_2x | |
| 571 | + | | |
| 572 | + | lda #11 | |
| 573 | + | jmp set_mult_col | |
| 574 | + | | |
| 575 | + | mult_col_2x: | |
| 576 | + | lda #7 | |
| 577 | + | jmp set_mult_col | |
| 578 | + | | |
| 579 | + | mult_col_3x: | |
| 580 | + | lda #5 | |
| 581 | + | jmp set_mult_col | |
| 582 | + | | |
| 583 | + | mult_col_4x: | |
| 584 | + | lda #1 | |
| 585 | + | | |
| 586 | + | set_mult_col: | |
| 587 | + | sta COLRAM + (COMBO_ROW * 40) + 22 | |
| 588 | + | sta COLRAM + (COMBO_ROW * 40) + 23 | |
| 589 | + | | |
| 590 | + | rts | |
| 591 | + | | |
| 592 | + | combo_label: | |
| 593 | + | !scr "combo:" | |
| 594 | + | !byte 0 | |
| 595 | + | | |
| 596 | + | ; ---------------------------------------------------------------------------- | |
| 597 | + | ; Increment Combo | |
| 598 | + | ; ---------------------------------------------------------------------------- | |
| 599 | + | | |
| 600 | + | increment_combo: | |
| 601 | + | inc combo | |
| 602 | + | | |
| 603 | + | ; Update max combo if needed | |
| 604 | + | lda combo | |
| 605 | + | cmp max_combo | |
| 606 | + | bcc combo_not_max | |
| 607 | + | sta max_combo | |
| 608 | + | combo_not_max: | |
| 609 | + | | |
| 610 | + | jsr display_combo | |
| 611 | + | rts | |
| 612 | + | | |
| 613 | + | ; ---------------------------------------------------------------------------- | |
| 614 | + | ; Break Combo | |
| 615 | + | ; ---------------------------------------------------------------------------- | |
| 616 | + | | |
| 617 | + | break_combo: | |
| 618 | + | lda combo | |
| 619 | + | beq combo_already_zero | |
| 620 | + | | |
| 621 | + | ; Combo was active - show break | |
| 622 | + | lda #0 | |
| 623 | + | sta combo | |
| 624 | + | jsr display_combo | |
| 625 | + | | |
| 626 | + | combo_already_zero: | |
| 627 | + | rts | |
| 428 | 628 | | |
| 429 | 629 | ; ---------------------------------------------------------------------------- | |
| 430 | 630 | ; Show Results Screen | |
| ... | |||
| 447 | 647 | draw_results_title: | |
| 448 | 648 | lda results_title,x | |
| 449 | 649 | beq draw_results_title_done | |
| 450 | - | sta SCREEN + (4 * 40) + 14,x | |
| 650 | + | sta SCREEN + (3 * 40) + 14,x | |
| 451 | 651 | lda #1 | |
| 452 | - | sta COLRAM + (4 * 40) + 14,x | |
| 652 | + | sta COLRAM + (3 * 40) + 14,x | |
| 453 | 653 | inx | |
| 454 | 654 | jmp draw_results_title | |
| 455 | 655 | draw_results_title_done: | |
| 456 | 656 | | |
| 457 | - | ; Score label and value | |
| 657 | + | ; Score | |
| 458 | 658 | ldx #0 | |
| 459 | 659 | draw_final_score_label: | |
| 460 | 660 | lda final_score_label,x | |
| 461 | 661 | beq draw_final_score_done | |
| 462 | - | sta SCREEN + (8 * 40) + 10,x | |
| 662 | + | sta SCREEN + (6 * 40) + 10,x | |
| 463 | 663 | lda #7 | |
| 464 | - | sta COLRAM + (8 * 40) + 10,x | |
| 664 | + | sta COLRAM + (6 * 40) + 10,x | |
| 465 | 665 | inx | |
| 466 | 666 | jmp draw_final_score_label | |
| 467 | 667 | draw_final_score_done: | |
| ... | |||
| 472 | 672 | draw_perfects_label: | |
| 473 | 673 | lda perfects_label,x | |
| 474 | 674 | beq draw_perfects_done | |
| 475 | - | sta SCREEN + (10 * 40) + 10,x | |
| 675 | + | sta SCREEN + (8 * 40) + 10,x | |
| 476 | 676 | lda #1 | |
| 477 | - | sta COLRAM + (10 * 40) + 10,x | |
| 677 | + | sta COLRAM + (8 * 40) + 10,x | |
| 478 | 678 | inx | |
| 479 | 679 | jmp draw_perfects_label | |
| 480 | 680 | draw_perfects_done: | |
| 481 | 681 | lda perfect_count | |
| 482 | - | jsr display_stat_at_10 | |
| 682 | + | jsr display_stat_at_8 | |
| 483 | 683 | | |
| 484 | 684 | ; Goods | |
| 485 | 685 | ldx #0 | |
| 486 | 686 | draw_goods_label: | |
| 487 | 687 | lda goods_label,x | |
| 488 | 688 | beq draw_goods_done | |
| 489 | - | sta SCREEN + (11 * 40) + 10,x | |
| 689 | + | sta SCREEN + (9 * 40) + 10,x | |
| 490 | 690 | lda #7 | |
| 491 | - | sta COLRAM + (11 * 40) + 10,x | |
| 691 | + | sta COLRAM + (9 * 40) + 10,x | |
| 492 | 692 | inx | |
| 493 | 693 | jmp draw_goods_label | |
| 494 | 694 | draw_goods_done: | |
| 495 | 695 | lda good_count | |
| 496 | - | jsr display_stat_at_11 | |
| 696 | + | jsr display_stat_at_9 | |
| 497 | 697 | | |
| 498 | 698 | ; Misses | |
| 499 | 699 | ldx #0 | |
| 500 | 700 | draw_misses_label: | |
| 501 | 701 | lda misses_label,x | |
| 502 | 702 | beq draw_misses_done | |
| 503 | - | sta SCREEN + (12 * 40) + 10,x | |
| 703 | + | sta SCREEN + (10 * 40) + 10,x | |
| 504 | 704 | lda #2 | |
| 505 | - | sta COLRAM + (12 * 40) + 10,x | |
| 705 | + | sta COLRAM + (10 * 40) + 10,x | |
| 506 | 706 | inx | |
| 507 | 707 | jmp draw_misses_label | |
| 508 | 708 | draw_misses_done: | |
| 509 | 709 | lda miss_count | |
| 710 | + | jsr display_stat_at_10 | |
| 711 | + | | |
| 712 | + | ; Max Combo | |
| 713 | + | ldx #0 | |
| 714 | + | draw_max_combo_label: | |
| 715 | + | lda max_combo_label,x | |
| 716 | + | beq draw_max_combo_done | |
| 717 | + | sta SCREEN + (12 * 40) + 10,x | |
| 718 | + | lda #13 | |
| 719 | + | sta COLRAM + (12 * 40) + 10,x | |
| 720 | + | inx | |
| 721 | + | jmp draw_max_combo_label | |
| 722 | + | draw_max_combo_done: | |
| 723 | + | lda max_combo | |
| 510 | 724 | jsr display_stat_at_12 | |
| 511 | 725 | | |
| 512 | - | ; Accuracy (simplified: just show hit percentage) | |
| 726 | + | ; Accuracy | |
| 513 | 727 | ldx #0 | |
| 514 | 728 | draw_accuracy_label: | |
| 515 | 729 | lda accuracy_label,x | |
| ... | |||
| 523 | 737 | jsr calculate_accuracy | |
| 524 | 738 | jsr display_accuracy | |
| 525 | 739 | | |
| 526 | - | ; Press any key message | |
| 740 | + | ; Press any key | |
| 527 | 741 | ldx #0 | |
| 528 | 742 | draw_press_key: | |
| 529 | 743 | lda press_key_text,x | |
| ... | |||
| 535 | 749 | jmp draw_press_key | |
| 536 | 750 | draw_press_key_done: | |
| 537 | 751 | | |
| 538 | - | ; Green border for victory | |
| 539 | 752 | lda #5 | |
| 540 | 753 | sta BORDER | |
| 541 | 754 | | |
| ... | |||
| 559 | 772 | | |
| 560 | 773 | misses_label: | |
| 561 | 774 | !scr "misses:" | |
| 775 | + | !byte 0 | |
| 776 | + | | |
| 777 | + | max_combo_label: | |
| 778 | + | !scr "max combo:" | |
| 562 | 779 | !byte 0 | |
| 563 | 780 | | |
| 564 | 781 | accuracy_label: | |
| ... | |||
| 570 | 787 | !byte 0 | |
| 571 | 788 | | |
| 572 | 789 | ; ---------------------------------------------------------------------------- | |
| 573 | - | ; Display Final Score (at row 8) | |
| 790 | + | ; Display Final Score (at row 6) | |
| 574 | 791 | ; ---------------------------------------------------------------------------- | |
| 575 | 792 | | |
| 576 | 793 | display_final_score: | |
| ... | |||
| 595 | 812 | fs_done_10000: | |
| 596 | 813 | txa | |
| 597 | 814 | ora #$30 | |
| 598 | - | sta SCREEN + (8 * 40) + 23 | |
| 815 | + | sta SCREEN + (6 * 40) + 23 | |
| 599 | 816 | | |
| 600 | 817 | ldx #0 | |
| 601 | 818 | fs_div_1000: | |
| ... | |||
| 613 | 830 | fs_done_1000: | |
| 614 | 831 | txa | |
| 615 | 832 | ora #$30 | |
| 616 | - | sta SCREEN + (8 * 40) + 24 | |
| 833 | + | sta SCREEN + (6 * 40) + 24 | |
| 617 | 834 | | |
| 618 | 835 | ldx #0 | |
| 619 | 836 | fs_div_100: | |
| ... | |||
| 627 | 844 | fs_done_100: | |
| 628 | 845 | txa | |
| 629 | 846 | ora #$30 | |
| 630 | - | sta SCREEN + (8 * 40) + 25 | |
| 847 | + | sta SCREEN + (6 * 40) + 25 | |
| 631 | 848 | | |
| 632 | 849 | ldx #0 | |
| 633 | 850 | fs_div_10: | |
| ... | |||
| 641 | 858 | fs_done_10: | |
| 642 | 859 | txa | |
| 643 | 860 | ora #$30 | |
| 644 | - | sta SCREEN + (8 * 40) + 26 | |
| 861 | + | sta SCREEN + (6 * 40) + 26 | |
| 645 | 862 | | |
| 646 | 863 | lda work_lo | |
| 647 | 864 | ora #$30 | |
| 648 | - | sta SCREEN + (8 * 40) + 27 | |
| 865 | + | sta SCREEN + (6 * 40) + 27 | |
| 649 | 866 | | |
| 650 | 867 | lda #7 | |
| 651 | - | sta COLRAM + (8 * 40) + 23 | |
| 652 | - | sta COLRAM + (8 * 40) + 24 | |
| 653 | - | sta COLRAM + (8 * 40) + 25 | |
| 654 | - | sta COLRAM + (8 * 40) + 26 | |
| 655 | - | sta COLRAM + (8 * 40) + 27 | |
| 868 | + | sta COLRAM + (6 * 40) + 23 | |
| 869 | + | sta COLRAM + (6 * 40) + 24 | |
| 870 | + | sta COLRAM + (6 * 40) + 25 | |
| 871 | + | sta COLRAM + (6 * 40) + 26 | |
| 872 | + | sta COLRAM + (6 * 40) + 27 | |
| 656 | 873 | | |
| 657 | 874 | rts | |
| 658 | 875 | | |
| ... | |||
| 660 | 877 | ; Display Stat (2-digit) at various rows | |
| 661 | 878 | ; ---------------------------------------------------------------------------- | |
| 662 | 879 | | |
| 663 | - | display_stat_at_10: | |
| 880 | + | display_stat_at_8: | |
| 664 | 881 | ldx #0 | |
| 665 | - | stat10_div: | |
| 882 | + | stat8_div: | |
| 666 | 883 | cmp #10 | |
| 667 | - | bcc stat10_done | |
| 884 | + | bcc stat8_done | |
| 668 | 885 | sec | |
| 669 | 886 | sbc #10 | |
| 670 | 887 | inx | |
| 671 | - | jmp stat10_div | |
| 672 | - | stat10_done: | |
| 888 | + | jmp stat8_div | |
| 889 | + | stat8_done: | |
| 673 | 890 | pha | |
| 674 | 891 | txa | |
| 675 | 892 | ora #$30 | |
| 676 | - | sta SCREEN + (10 * 40) + 23 | |
| 893 | + | sta SCREEN + (8 * 40) + 23 | |
| 677 | 894 | pla | |
| 678 | 895 | ora #$30 | |
| 679 | - | sta SCREEN + (10 * 40) + 24 | |
| 896 | + | sta SCREEN + (8 * 40) + 24 | |
| 680 | 897 | lda #1 | |
| 681 | - | sta COLRAM + (10 * 40) + 23 | |
| 682 | - | sta COLRAM + (10 * 40) + 24 | |
| 898 | + | sta COLRAM + (8 * 40) + 23 | |
| 899 | + | sta COLRAM + (8 * 40) + 24 | |
| 683 | 900 | rts | |
| 684 | 901 | | |
| 685 | - | display_stat_at_11: | |
| 902 | + | display_stat_at_9: | |
| 686 | 903 | ldx #0 | |
| 687 | - | stat11_div: | |
| 904 | + | stat9_div: | |
| 688 | 905 | cmp #10 | |
| 689 | - | bcc stat11_done | |
| 906 | + | bcc stat9_done | |
| 690 | 907 | sec | |
| 691 | 908 | sbc #10 | |
| 692 | 909 | inx | |
| 693 | - | jmp stat11_div | |
| 694 | - | stat11_done: | |
| 910 | + | jmp stat9_div | |
| 911 | + | stat9_done: | |
| 695 | 912 | pha | |
| 696 | 913 | txa | |
| 697 | 914 | ora #$30 | |
| 698 | - | sta SCREEN + (11 * 40) + 23 | |
| 915 | + | sta SCREEN + (9 * 40) + 23 | |
| 699 | 916 | pla | |
| 700 | 917 | ora #$30 | |
| 701 | - | sta SCREEN + (11 * 40) + 24 | |
| 918 | + | sta SCREEN + (9 * 40) + 24 | |
| 702 | 919 | lda #7 | |
| 703 | - | sta COLRAM + (11 * 40) + 23 | |
| 704 | - | sta COLRAM + (11 * 40) + 24 | |
| 920 | + | sta COLRAM + (9 * 40) + 23 | |
| 921 | + | sta COLRAM + (9 * 40) + 24 | |
| 922 | + | rts | |
| 923 | + | | |
| 924 | + | display_stat_at_10: | |
| 925 | + | ldx #0 | |
| 926 | + | stat10_div: | |
| 927 | + | cmp #10 | |
| 928 | + | bcc stat10_done | |
| 929 | + | sec | |
| 930 | + | sbc #10 | |
| 931 | + | inx | |
| 932 | + | jmp stat10_div | |
| 933 | + | stat10_done: | |
| 934 | + | pha | |
| 935 | + | txa | |
| 936 | + | ora #$30 | |
| 937 | + | sta SCREEN + (10 * 40) + 23 | |
| 938 | + | pla | |
| 939 | + | ora #$30 | |
| 940 | + | sta SCREEN + (10 * 40) + 24 | |
| 941 | + | lda #2 | |
| 942 | + | sta COLRAM + (10 * 40) + 23 | |
| 943 | + | sta COLRAM + (10 * 40) + 24 | |
| 705 | 944 | rts | |
| 706 | 945 | | |
| 707 | 946 | display_stat_at_12: | |
| ... | |||
| 721 | 960 | pla | |
| 722 | 961 | ora #$30 | |
| 723 | 962 | sta SCREEN + (12 * 40) + 24 | |
| 724 | - | lda #2 | |
| 963 | + | lda #13 | |
| 725 | 964 | sta COLRAM + (12 * 40) + 23 | |
| 726 | 965 | sta COLRAM + (12 * 40) + 24 | |
| 727 | 966 | rts | |
| 728 | 967 | | |
| 729 | 968 | ; ---------------------------------------------------------------------------- | |
| 730 | - | ; Calculate Accuracy - (perfects + goods) * 100 / total_notes | |
| 969 | + | ; Calculate Accuracy | |
| 731 | 970 | ; ---------------------------------------------------------------------------- | |
| 732 | 971 | | |
| 733 | 972 | calculate_accuracy: | |
| 734 | - | ; Total hits = perfects + goods | |
| 735 | 973 | lda perfect_count | |
| 736 | 974 | clc | |
| 737 | 975 | adc good_count | |
| 738 | 976 | sta total_hits | |
| 739 | 977 | | |
| 740 | - | ; Total notes = hits + misses | |
| 741 | 978 | clc | |
| 742 | 979 | adc miss_count | |
| 743 | 980 | sta total_notes | |
| 744 | 981 | | |
| 745 | - | ; Avoid division by zero | |
| 746 | 982 | beq accuracy_zero | |
| 747 | - | | |
| 748 | - | ; Simple percentage: (hits * 100) / total | |
| 749 | - | ; We'll approximate with (hits * 128) / total then scale | |
| 750 | - | ; Or simpler: multiply hits by 100, divide by total | |
| 751 | 983 | | |
| 752 | 984 | lda total_hits | |
| 753 | 985 | sta dividend_lo | |
| 754 | 986 | lda #0 | |
| 755 | 987 | sta dividend_hi | |
| 756 | 988 | | |
| 757 | - | ; Multiply by 100 | |
| 758 | 989 | ldx #100 | |
| 759 | 990 | mult_loop: | |
| 760 | 991 | dex | |
| ... | |||
| 769 | 1000 | jmp mult_loop | |
| 770 | 1001 | mult_done: | |
| 771 | 1002 | | |
| 772 | - | ; Divide by total_notes | |
| 773 | 1003 | lda #0 | |
| 774 | 1004 | sta accuracy | |
| 775 | 1005 | div_loop: | |
| ... | |||
| 799 | 1029 | accuracy: !byte 0 | |
| 800 | 1030 | | |
| 801 | 1031 | ; ---------------------------------------------------------------------------- | |
| 802 | - | ; Display Accuracy (3 digits + %) | |
| 1032 | + | ; Display Accuracy | |
| 803 | 1033 | ; ---------------------------------------------------------------------------- | |
| 804 | 1034 | | |
| 805 | 1035 | display_accuracy: | |
| 806 | 1036 | lda accuracy | |
| 807 | 1037 | | |
| 808 | - | ; Hundreds | |
| 809 | 1038 | ldx #0 | |
| 810 | 1039 | acc_div_100: | |
| 811 | 1040 | cmp #100 | |
| ... | |||
| 821 | 1050 | sta SCREEN + (14 * 40) + 23 | |
| 822 | 1051 | pla | |
| 823 | 1052 | | |
| 824 | - | ; Tens | |
| 825 | 1053 | ldx #0 | |
| 826 | 1054 | acc_div_10: | |
| 827 | 1055 | cmp #10 | |
| ... | |||
| 837 | 1065 | sta SCREEN + (14 * 40) + 24 | |
| 838 | 1066 | pla | |
| 839 | 1067 | | |
| 840 | - | ; Ones | |
| 841 | 1068 | ora #$30 | |
| 842 | 1069 | sta SCREEN + (14 * 40) + 25 | |
| 843 | 1070 | | |
| 844 | - | ; Percent sign | |
| 845 | - | lda #$25 ; % | |
| 1071 | + | lda #$25 | |
| 846 | 1072 | sta SCREEN + (14 * 40) + 26 | |
| 847 | 1073 | | |
| 848 | 1074 | lda #5 | |
| ... | |||
| 1121 | 1347 | rts | |
| 1122 | 1348 | | |
| 1123 | 1349 | ; ---------------------------------------------------------------------------- | |
| 1124 | - | ; Handle Miss | |
| 1350 | + | ; Handle Miss - Now breaks combo | |
| 1125 | 1351 | ; ---------------------------------------------------------------------------- | |
| 1126 | 1352 | | |
| 1127 | 1353 | handle_miss: | |
| ... | |||
| 1136 | 1362 | | |
| 1137 | 1363 | jsr display_misses | |
| 1138 | 1364 | jsr decrease_health | |
| 1365 | + | | |
| 1366 | + | ; Break the combo! | |
| 1367 | + | jsr break_combo | |
| 1139 | 1368 | | |
| 1140 | 1369 | rts | |
| 1141 | 1370 | | |
| ... | |||
| 1874 | 2103 | rts | |
| 1875 | 2104 | | |
| 1876 | 2105 | ; ---------------------------------------------------------------------------- | |
| 1877 | - | ; Award Points - Now tracks perfects and goods separately | |
| 2106 | + | ; Award Points - Now uses combo multiplier | |
| 1878 | 2107 | ; ---------------------------------------------------------------------------- | |
| 1879 | 2108 | | |
| 1880 | 2109 | award_points: | |
| 2110 | + | ; Increment combo first | |
| 2111 | + | jsr increment_combo | |
| 2112 | + | | |
| 2113 | + | ; Get base score | |
| 1881 | 2114 | lda hit_quality | |
| 1882 | 2115 | cmp #2 | |
| 1883 | 2116 | beq award_perfect | |
| 1884 | 2117 | | |
| 1885 | - | ; Good hit | |
| 1886 | - | inc good_count | |
| 2118 | + | ; Good hit: 50 * multiplier | |
| 2119 | + | lda #GOOD_SCORE | |
| 2120 | + | jsr apply_multiplier | |
| 2121 | + | jsr add_score | |
| 1887 | 2122 | | |
| 1888 | - | lda score_lo | |
| 1889 | - | clc | |
| 1890 | - | adc #GOOD_SCORE | |
| 1891 | - | sta score_lo | |
| 1892 | - | lda score_hi | |
| 1893 | - | adc #0 | |
| 1894 | - | sta score_hi | |
| 2123 | + | inc good_count | |
| 1895 | 2124 | | |
| 1896 | 2125 | lda #GOOD_COL | |
| 1897 | 2126 | sta BORDER | |
| ... | |||
| 1904 | 2133 | jmp award_done | |
| 1905 | 2134 | | |
| 1906 | 2135 | award_perfect: | |
| 1907 | - | ; Perfect hit | |
| 1908 | - | inc perfect_count | |
| 2136 | + | ; Perfect hit: 100 * multiplier | |
| 2137 | + | lda #PERFECT_SCORE | |
| 2138 | + | jsr apply_multiplier | |
| 2139 | + | jsr add_score | |
| 1909 | 2140 | | |
| 1910 | - | lda score_lo | |
| 1911 | - | clc | |
| 1912 | - | adc #PERFECT_SCORE | |
| 1913 | - | sta score_lo | |
| 1914 | - | lda score_hi | |
| 1915 | - | adc #0 | |
| 1916 | - | sta score_hi | |
| 2141 | + | inc perfect_count | |
| 1917 | 2142 | | |
| 1918 | 2143 | lda #PERFECT_COL | |
| 1919 | 2144 | sta BORDER | |
| ... | |||
| 1925 | 2150 | | |
| 1926 | 2151 | award_done: | |
| 1927 | 2152 | jsr display_score | |
| 2153 | + | rts | |
| 2154 | + | | |
| 2155 | + | ; ---------------------------------------------------------------------------- | |
| 2156 | + | ; Apply Multiplier - A = base score, returns multiplied score in score_add_lo/hi | |
| 2157 | + | ; ---------------------------------------------------------------------------- | |
| 2158 | + | | |
| 2159 | + | apply_multiplier: | |
| 2160 | + | sta base_score | |
| 2161 | + | jsr get_multiplier | |
| 2162 | + | sta current_mult | |
| 2163 | + | | |
| 2164 | + | ; Start with base score | |
| 2165 | + | lda base_score | |
| 2166 | + | sta score_add_lo | |
| 2167 | + | lda #0 | |
| 2168 | + | sta score_add_hi | |
| 2169 | + | | |
| 2170 | + | ; If multiplier is 1, we're done | |
| 2171 | + | lda current_mult | |
| 2172 | + | cmp #1 | |
| 2173 | + | beq mult_done_apply | |
| 2174 | + | | |
| 2175 | + | ; Multiply by adding base_score (mult-1) more times | |
| 2176 | + | dec current_mult | |
| 2177 | + | mult_add_loop: | |
| 2178 | + | lda score_add_lo | |
| 2179 | + | clc | |
| 2180 | + | adc base_score | |
| 2181 | + | sta score_add_lo | |
| 2182 | + | lda score_add_hi | |
| 2183 | + | adc #0 | |
| 2184 | + | sta score_add_hi | |
| 2185 | + | dec current_mult | |
| 2186 | + | bne mult_add_loop | |
| 2187 | + | | |
| 2188 | + | mult_done_apply: | |
| 2189 | + | rts | |
| 2190 | + | | |
| 2191 | + | base_score: !byte 0 | |
| 2192 | + | current_mult: !byte 0 | |
| 2193 | + | score_add_lo: !byte 0 | |
| 2194 | + | score_add_hi: !byte 0 | |
| 2195 | + | | |
| 2196 | + | ; ---------------------------------------------------------------------------- | |
| 2197 | + | ; Add Score - Adds score_add_lo/hi to score | |
| 2198 | + | ; ---------------------------------------------------------------------------- | |
| 2199 | + | | |
| 2200 | + | add_score: | |
| 2201 | + | lda score_lo | |
| 2202 | + | clc | |
| 2203 | + | adc score_add_lo | |
| 2204 | + | sta score_lo | |
| 2205 | + | lda score_hi | |
| 2206 | + | adc score_add_hi | |
| 2207 | + | sta score_hi | |
| 1928 | 2208 | rts | |
| 1929 | 2209 | | |
| 1930 | 2210 | ; ---------------------------------------------------------------------------- | |
| ... | |||
| 2188 | 2468 | perfect_count: !byte 0 | |
| 2189 | 2469 | good_count: !byte 0 | |
| 2190 | 2470 | health: !byte 0 | |
| 2471 | + | combo: !byte 0 | |
| 2472 | + | max_combo: !byte 0 | |
| 2191 | 2473 | |