Skip to content
Game 1 Unit 7 of 64 1 hr learning time

Miss Handling

Notes that scroll past unhit trigger punishment. A harsh sound and red flash for failure.

11% of SID Symphony

Reward without risk means nothing. Now missed notes punish the player with harsh feedback.

This unit adds miss detection. When a note scrolls past the hit zone without being hit, it counts as a miss. The border flashes red. A harsh noise burst plays. The miss counter increments. Players will feel their failures.

Run It

Assemble and run:

acme -f cbm -o symphony.prg symphony.asm

Unit 7 Screenshot

The miss counter displays next to the score. Let a note scroll off screen and watch it climb. Notice the red flash and harsh sound - distinctly unpleasant compared to the satisfying hit feedback.

Miss handling — unhit notes increment the miss counter

Detecting Misses

A miss happens when a note scrolls past column 0 without being hit. The update_notes routine already moves notes left each frame. Now it checks if they’ve gone too far:

; ----------------------------------------------------------------------------
; Update Notes - Now detects misses
; ----------------------------------------------------------------------------

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     ; Note passed hit zone - MISS!

            jsr draw_note
            jmp update_next

update_miss:
            ; Note fell through - record the miss
            lda note_track,x
            sta miss_track      ; Save which track for flash
            lda #0
            sta note_track,x    ; Deactivate note
            jsr handle_miss     ; Process the miss

update_next:
            inx
            cpx #MAX_NOTES
            bne update_loop
            rts

; ----------------------------------------------------------------------------
; Handle Miss - Negative feedback for missed notes
; ----------------------------------------------------------------------------

handle_miss:
            ; Increment miss counter
            inc miss_count

            ; Play miss sound (harsh noise burst)
            jsr play_miss_sound

            ; Red border flash
            lda #MISS_COL
            sta BORDER
            lda #8              ; Flash for 8 frames
            sta border_flash

            ; Update miss display
            jsr display_misses

            rts

The key change is the cmp #1 and bcc update_miss sequence. When a note’s column drops below 1, it’s past the hit zone - a miss. The handle_miss routine processes the failure: increment counter, play sound, flash border, update display.

The Miss Sound

Good feedback makes failure feel like failure. The SID’s noise waveform creates a harsh, discordant sound perfect for punishment:

; ----------------------------------------------------------------------------
; Miss Sound Settings
; ----------------------------------------------------------------------------

MISS_FREQ   = $08               ; Low rumble
MISS_WAVE   = $81               ; Noise waveform
MISS_AD     = $00               ; Instant attack, no decay
MISS_SR     = $90               ; High sustain, fast release

; ----------------------------------------------------------------------------
; Play Miss Sound - Harsh noise burst
; ----------------------------------------------------------------------------

play_miss_sound:
            ; Use voice 3 for miss sound (temporary override)
            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            ; Gate on
            sta SID_V3_CTRL
            rts

The noise waveform ($81) generates random noise instead of a musical tone. Combined with a low frequency ($08), it creates a harsh rumble. The high sustain ($90) lets it ring out briefly before fading - long enough to feel the sting.

Why Noise Works

The SID has four waveforms:

WaveformValueCharacter
Triangle$11Soft, mellow
Sawtooth$21Bright, buzzy
Pulse$41Variable, classic
Noise$81Harsh, random

For hit sounds, musical waveforms reward success with pleasant tones. For misses, noise punishes failure with discord. The contrast makes hits feel better and misses feel worse.

Miss Counter Display

The miss counter uses simpler maths than the score - just two digits (0-99):

display_misses:
            lda miss_count

            ; Tens digit
            ldx #0
miss_div_10:
            cmp #10
            bcc miss_done_10
            sec
            sbc #10
            inx
            jmp miss_div_10
miss_done_10:
            ; X = tens, A = ones
            pha                 ; Save ones
            txa
            ora #$30
            sta SCREEN + 21     ; Tens digit
            pla
            ora #$30
            sta SCREEN + 22     ; Ones digit

            rts

Repeated subtraction by 10 extracts the tens digit. What remains is the ones digit. Simple division without hardware support.

The Complete Code

; ============================================================================
; SID SYMPHONY - Unit 7: Miss Handling
; ============================================================================
; Notes that scroll past unhit trigger negative feedback. A harsh noise burst
; and red flash punish mistakes. Miss count tracks failures.
;
; Controls: Z = Track 1 (high), X = Track 2 (mid), C = Track 3 (low)
; ============================================================================

; ============================================================================
; CUSTOMISATION SECTION
; ============================================================================

; SID Voice Settings
VOICE1_WAVE = $21               ; Sawtooth
VOICE2_WAVE = $41               ; Pulse
VOICE3_WAVE = $11               ; Triangle

VOICE1_FREQ = $1C               ; High pitch
VOICE2_FREQ = $0E               ; Mid pitch
VOICE3_FREQ = $07               ; Low pitch

VOICE_AD    = $09               ; Attack=0, Decay=9
VOICE_SR    = $00               ; Sustain=0, Release=0
PULSE_WIDTH = $08               ; 50% duty cycle

; Miss sound settings
MISS_FREQ   = $08               ; Low rumble
MISS_WAVE   = $81               ; Noise waveform
MISS_AD     = $00               ; Instant attack, no decay
MISS_SR     = $90               ; High sustain, fast release

; Visual Settings
BORDER_COL  = 0                 ; Black border
BG_COL      = 0                 ; Black background

TRACK1_NOTE_COL = 10            ; Light red
TRACK2_NOTE_COL = 13            ; Light green
TRACK3_NOTE_COL = 14            ; Light blue

TRACK_LINE_COL = 11             ; Dark grey
HIT_ZONE_COL = 7                ; Yellow

FLASH1_COL  = 2                 ; Red
FLASH2_COL  = 5                 ; Green
FLASH3_COL  = 6                 ; Blue

HIT_COL     = 1                 ; White - flash on successful hit
PERFECT_COL = 1                 ; White - perfect hit border flash
GOOD_COL    = 7                 ; Yellow - good hit border flash
MISS_COL    = 2                 ; Red - miss border flash

; ============================================================================
; SCORING SETTINGS
; ============================================================================

PERFECT_SCORE = 100
GOOD_SCORE    = 50

; ============================================================================
; HIT DETECTION SETTINGS
; ============================================================================

HIT_ZONE_MIN = 2
HIT_ZONE_MAX = 5
HIT_ZONE_CENTRE = 3

; ============================================================================
; 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

; Hit zone
HIT_ZONE_COLUMN = 3

; Custom character codes
CHAR_NOTE   = 128
CHAR_TRACK  = 129
CHAR_HITZONE = 130
CHAR_SPACE  = 32

; Note settings
MAX_NOTES   = 8
NOTE_SPAWN_COL = 37

; Timing
FRAMES_PER_BEAT = 25

; 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               ; Track of missed note (for flash)

; ----------------------------------------------------------------------------
; 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_screen
            jsr init_sid
            jsr init_notes
            jsr init_score

            lda #<song_data
            sta song_pos
            lda #>song_data
            sta song_pos_hi

            lda #0
            sta frame_count
            sta beat_count
            sta border_flash

main_loop:
            lda #$FF
wait_raster:
            cmp $D012
            bne wait_raster

            inc frame_count
            lda frame_count
            cmp #FRAMES_PER_BEAT
            bcc no_new_beat

            lda #0
            sta frame_count
            jsr check_spawn_note
            inc beat_count

no_new_beat:
            jsr update_notes
            jsr reset_track_colours
            jsr update_border_flash
            jsr check_keys

            jmp main_loop

; ----------------------------------------------------------------------------
; 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
            lda #%00000000
            sta CHARSET + (CHAR_TRACK * 8) + 1
            lda #%00000000
            sta CHARSET + (CHAR_TRACK * 8) + 2
            lda #%11111111
            sta CHARSET + (CHAR_TRACK * 8) + 3
            lda #%11111111
            sta CHARSET + (CHAR_TRACK * 8) + 4
            lda #%00000000
            sta CHARSET + (CHAR_TRACK * 8) + 5
            lda #%00000000
            sta CHARSET + (CHAR_TRACK * 8) + 6
            lda #%00000000
            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

            rts

; ----------------------------------------------------------------------------
; Initialize Score
; ----------------------------------------------------------------------------

init_score:
            lda #0
            sta score_lo
            sta score_hi
            sta miss_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
            dex
            bpl init_notes_loop
            rts

; ----------------------------------------------------------------------------
; Check Spawn Note
; ----------------------------------------------------------------------------

check_spawn_note:
            ldy #0

spawn_check_loop:
            lda (song_pos),y
            cmp #$FF
            beq spawn_restart_song

            cmp beat_count
            beq spawn_match
            bcs spawn_done

            jmp spawn_advance

spawn_match:
            iny
            lda (song_pos),y
            jsr spawn_note
            dey

spawn_advance:
            lda song_pos
            clc
            adc #2
            sta song_pos
            lda song_pos_hi
            adc #0
            sta song_pos_hi
            jmp spawn_check_loop

spawn_done:
            rts

spawn_restart_song:
            lda #<song_data
            sta song_pos
            lda #>song_data
            sta song_pos_hi
            lda #0
            sta beat_count
            rts

; ----------------------------------------------------------------------------
; Spawn Note
; ----------------------------------------------------------------------------

spawn_note:
            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
            jsr draw_note
            rts

; ----------------------------------------------------------------------------
; Update Notes - Now detects misses
; ----------------------------------------------------------------------------

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     ; Note passed hit zone - MISS!

            jsr draw_note
            jmp update_next

update_miss:
            ; Note fell through - record the miss
            lda note_track,x
            sta miss_track      ; Save which track for flash
            lda #0
            sta note_track,x    ; Deactivate note
            jsr handle_miss     ; Process the miss

update_next:
            inx
            cpx #MAX_NOTES
            bne update_loop
            rts

; ----------------------------------------------------------------------------
; Handle Miss - Negative feedback for missed notes
; ----------------------------------------------------------------------------

handle_miss:
            ; Increment miss counter
            inc miss_count

            ; Play miss sound (harsh noise burst)
            jsr play_miss_sound

            ; Red border flash
            lda #MISS_COL
            sta BORDER
            lda #8              ; Flash for 8 frames
            sta border_flash

            ; Update miss display
            jsr display_misses

            rts

; ----------------------------------------------------------------------------
; Play Miss Sound - Harsh noise burst
; ----------------------------------------------------------------------------

play_miss_sound:
            ; Use voice 3 for miss sound (temporary override)
            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            ; Gate on
            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

            lda #CHAR_TRACK
            ldx #0
draw_t2:
            sta SCREEN + (TRACK2_ROW * 40),x
            inx
            cpx #38
            bne draw_t2

            lda #CHAR_TRACK
            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:
            ; Draw "SCORE:" label
            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:

            ; Draw "MISS:" label
            ldx #0
draw_miss_label:
            lda miss_label,x
            beq draw_miss_label_done
            sta SCREEN + 15,x
            lda #2              ; Red
            sta COLRAM + 15,x
            inx
            bne draw_miss_label
draw_miss_label_done:

            ; Draw title
            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:

            ; Track labels
            lda #$1A            ; Z
            sta SCREEN + (TRACK1_ROW * 40)
            lda #TRACK1_NOTE_COL
            sta COLRAM + (TRACK1_ROW * 40)

            lda #$18            ; X
            sta SCREEN + (TRACK2_ROW * 40)
            lda #TRACK2_NOTE_COL
            sta COLRAM + (TRACK2_ROW * 40)

            lda #$03            ; C
            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

; ----------------------------------------------------------------------------
; 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
            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
            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
            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

            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
; ----------------------------------------------------------------------------

award_points:
            lda hit_quality
            cmp #2
            beq award_perfect

            lda score_lo
            clc
            adc #GOOD_SCORE
            sta score_lo
            lda score_hi
            adc #0
            sta score_hi

            lda #GOOD_COL
            sta BORDER
            lda #4
            sta border_flash

            jmp award_done

award_perfect:
            lda score_lo
            clc
            adc #PERFECT_SCORE
            sta score_lo
            lda score_hi
            adc #0
            sta score_hi

            lda #PERFECT_COL
            sta BORDER
            lda #6
            sta border_flash

award_done:
            jsr display_score
            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 - Simple 2-digit display
; ----------------------------------------------------------------------------

display_misses:
            lda miss_count

            ; Tens digit
            ldx #0
miss_div_10:
            cmp #10
            bcc miss_done_10
            sec
            sbc #10
            inx
            jmp miss_div_10
miss_done_10:
            ; X = tens, A = ones
            pha                 ; Save ones
            txa
            ora #$30
            sta SCREEN + 21     ; Tens digit
            pla
            ora #$30
            sta SCREEN + 22     ; Ones digit

            lda #2              ; Red
            sta COLRAM + 21
            sta COLRAM + 22

            rts

work_lo:    !byte 0
work_hi:    !byte 0

; ----------------------------------------------------------------------------
; Play Voices
; ----------------------------------------------------------------------------

play_voice1:
            lda #VOICE1_WAVE
            ora #$01
            sta SID_V1_CTRL
            rts

play_voice2:
            lda #VOICE2_WAVE
            ora #$01
            sta SID_V2_CTRL
            rts

play_voice3:
            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
            !byte 2, 2
            !byte 4, 3
            !byte 6, 1

            !byte 8, 2
            !byte 10, 3
            !byte 12, 1
            !byte 14, 2

            !byte 16, 3
            !byte 18, 1
            !byte 20, 2
            !byte 22, 3

            !byte 24, 1
            !byte 25, 2
            !byte 26, 3
            !byte 28, 1
            !byte 29, 2
            !byte 30, 3

            !byte $FF

; ----------------------------------------------------------------------------
; Note Arrays
; ----------------------------------------------------------------------------

note_track:
            !fill MAX_NOTES, 0

note_col:
            !fill MAX_NOTES, 0

; ----------------------------------------------------------------------------
; Game Variables
; ----------------------------------------------------------------------------

score_lo:   !byte 0
score_hi:   !byte 0
miss_count: !byte 0

Try This: Harsher Miss Sound

Make misses more punishing:

MISS_FREQ   = $20               ; Higher pitch (more grating)
MISS_SR     = $F0               ; Longer sustain

The higher pitch cuts through more sharply. The longer sustain extends the discomfort.

Try This: Longer Flash

Extend the visual punishment:

; In handle_miss:
lda #16             ; Flash for 16 frames (double)
sta border_flash

What You’ve Learnt

  • Miss detection - Checking when notes scroll past the playable zone
  • Noise waveform - Using $81 for harsh, non-musical sound
  • Negative feedback - Making failure feel distinctly unpleasant
  • Simple division - Repeated subtraction for display conversion

What’s Next

In Unit 8, we’ll add a health system. Misses will now cost lives. Too many failures and the game ends. Stakes create tension.

What Changed

Unit 6 → Unit 7
+136-69
11 ; ============================================================================
2-; SID SYMPHONY - Unit 6: Scoring and Feedback
2+; SID SYMPHONY - Unit 7: Miss Handling
33 ; ============================================================================
4-; Score points for hitting notes. Perfect timing (centre of hit zone) awards
5-; more points than good timing (edges). Visual feedback rewards accuracy.
4+; Notes that scroll past unhit trigger negative feedback. A harsh noise burst
5+; and red flash punish mistakes. Miss count tracks failures.
66 ;
77 ; Controls: Z = Track 1 (high), X = Track 2 (mid), C = Track 3 (low)
88 ; ============================================================================
...
2323 VOICE_AD = $09 ; Attack=0, Decay=9
2424 VOICE_SR = $00 ; Sustain=0, Release=0
2525 PULSE_WIDTH = $08 ; 50% duty cycle
26+
27+; Miss sound settings
28+MISS_FREQ = $08 ; Low rumble
29+MISS_WAVE = $81 ; Noise waveform
30+MISS_AD = $00 ; Instant attack, no decay
31+MISS_SR = $90 ; High sustain, fast release
2632
2733 ; Visual Settings
2834 BORDER_COL = 0 ; Black border
...
4248 HIT_COL = 1 ; White - flash on successful hit
4349 PERFECT_COL = 1 ; White - perfect hit border flash
4450 GOOD_COL = 7 ; Yellow - good hit border flash
51+MISS_COL = 2 ; Red - miss border flash
4552
4653 ; ============================================================================
4754 ; SCORING SETTINGS
4855 ; ============================================================================
4956
50-PERFECT_SCORE = 100 ; Points for perfect hit
51-GOOD_SCORE = 50 ; Points for good hit
57+PERFECT_SCORE = 100
58+GOOD_SCORE = 50
5259
5360 ; ============================================================================
5461 ; HIT DETECTION SETTINGS
5562 ; ============================================================================
5663
57-HIT_ZONE_MIN = 2 ; Left edge of hit zone
58-HIT_ZONE_MAX = 5 ; Right edge of hit zone
59-HIT_ZONE_CENTRE = 3 ; Perfect hit position (and 4)
64+HIT_ZONE_MIN = 2
65+HIT_ZONE_MAX = 5
66+HIT_ZONE_CENTRE = 3
6067
6168 ; ============================================================================
6269 ; MEMORY MAP
6370 ; ============================================================================
6471
65-SCREEN = $0400 ; Screen memory
66-COLRAM = $D800 ; Colour RAM
67-BORDER = $D020 ; Border colour
68-BGCOL = $D021 ; Background colour
69-CHARPTR = $D018 ; Character memory pointer
72+SCREEN = $0400
73+COLRAM = $D800
74+BORDER = $D020
75+BGCOL = $D021
76+CHARPTR = $D018
7077
71-CHARSET = $3000 ; Custom character set location
78+CHARSET = $3000
7279
7380 ; SID registers
7481 SID = $D400
...
131138 song_pos_hi = $05
132139 temp_track = $06
133140 key_pressed = $07
134-hit_quality = $08 ; 0=none, 1=good, 2=perfect
135-border_flash = $09 ; Frames remaining for border flash
141+hit_quality = $08
142+border_flash = $09
143+miss_track = $0A ; Track of missed note (for flash)
136144
137145 ; ----------------------------------------------------------------------------
138146 ; BASIC Stub
...
244252 ; ----------------------------------------------------------------------------
245253
246254 define_custom_chars:
247- ; Character 128: Note (filled chevron pointing left)
248255 lda #%00000110
249256 sta CHARSET + (CHAR_NOTE * 8) + 0
250257 lda #%00011110
...
262269 lda #%00000110
263270 sta CHARSET + (CHAR_NOTE * 8) + 7
264271
265- ; Character 129: Track line (centered horizontal line)
266272 lda #%00000000
267273 sta CHARSET + (CHAR_TRACK * 8) + 0
268274 lda #%00000000
...
280286 lda #%00000000
281287 sta CHARSET + (CHAR_TRACK * 8) + 7
282288
283- ; Character 130: Hit zone (vertical bars)
284289 lda #%01100110
285290 sta CHARSET + (CHAR_HITZONE * 8) + 0
286- lda #%01100110
287291 sta CHARSET + (CHAR_HITZONE * 8) + 1
288- lda #%01100110
289292 sta CHARSET + (CHAR_HITZONE * 8) + 2
290- lda #%01100110
291293 sta CHARSET + (CHAR_HITZONE * 8) + 3
292- lda #%01100110
293294 sta CHARSET + (CHAR_HITZONE * 8) + 4
294- lda #%01100110
295295 sta CHARSET + (CHAR_HITZONE * 8) + 5
296- lda #%01100110
297296 sta CHARSET + (CHAR_HITZONE * 8) + 6
298- lda #%01100110
299297 sta CHARSET + (CHAR_HITZONE * 8) + 7
300298
301299 rts
...
308306 lda #0
309307 sta score_lo
310308 sta score_hi
309+ sta miss_count
311310 jsr display_score
311+ jsr display_misses
312312 rts
313313
314314 ; ----------------------------------------------------------------------------
...
396396 rts
397397
398398 ; ----------------------------------------------------------------------------
399-; Update Notes
399+; Update Notes - Now detects misses
400400 ; ----------------------------------------------------------------------------
401401
402402 update_notes:
...
411411 dec note_col,x
412412 lda note_col,x
413413 cmp #1
414- bcc update_deactivate
414+ bcc update_miss ; Note passed hit zone - MISS!
415415
416416 jsr draw_note
417417 jmp update_next
418418
419-update_deactivate:
419+update_miss:
420+ ; Note fell through - record the miss
421+ lda note_track,x
422+ sta miss_track ; Save which track for flash
420423 lda #0
421- sta note_track,x
424+ sta note_track,x ; Deactivate note
425+ jsr handle_miss ; Process the miss
422426
423427 update_next:
424428 inx
425429 cpx #MAX_NOTES
426430 bne update_loop
431+ rts
432+
433+; ----------------------------------------------------------------------------
434+; Handle Miss - Negative feedback for missed notes
435+; ----------------------------------------------------------------------------
436+
437+handle_miss:
438+ ; Increment miss counter
439+ inc miss_count
440+
441+ ; Play miss sound (harsh noise burst)
442+ jsr play_miss_sound
443+
444+ ; Red border flash
445+ lda #MISS_COL
446+ sta BORDER
447+ lda #8 ; Flash for 8 frames
448+ sta border_flash
449+
450+ ; Update miss display
451+ jsr display_misses
452+
453+ rts
454+
455+; ----------------------------------------------------------------------------
456+; Play Miss Sound - Harsh noise burst
457+; ----------------------------------------------------------------------------
458+
459+play_miss_sound:
460+ ; Use voice 3 for miss sound (temporary override)
461+ lda #0
462+ sta SID_V3_FREQ_LO
463+ lda #MISS_FREQ
464+ sta SID_V3_FREQ_HI
465+ lda #MISS_AD
466+ sta SID_V3_AD
467+ lda #MISS_SR
468+ sta SID_V3_SR
469+ lda #MISS_WAVE
470+ ora #$01 ; Gate on
471+ sta SID_V3_CTRL
427472 rts
428473
429474 ; ----------------------------------------------------------------------------
...
714759 lda score_label,x
715760 beq draw_score_label_done
716761 sta SCREEN + 1,x
717- lda #1 ; White
762+ lda #1
718763 sta COLRAM + 1,x
719764 inx
720765 bne draw_score_label
721766 draw_score_label_done:
767+
768+ ; Draw "MISS:" label
769+ ldx #0
770+draw_miss_label:
771+ lda miss_label,x
772+ beq draw_miss_label_done
773+ sta SCREEN + 15,x
774+ lda #2 ; Red
775+ sta COLRAM + 15,x
776+ inx
777+ bne draw_miss_label
778+draw_miss_label_done:
722779
723780 ; Draw title
724781 ldx #0
...
752809
753810 score_label:
754811 !scr "score:"
812+ !byte 0
813+
814+miss_label:
815+ !scr "miss:"
755816 !byte 0
756817
757818 title_text:
...
870931 ; ----------------------------------------------------------------------------
871932 ; Update Border Flash
872933 ; ----------------------------------------------------------------------------
873-; Decrements flash counter and resets border when done
874934
875935 update_border_flash:
876936 lda border_flash
877937 beq flash_done
878938 dec border_flash
879939 bne flash_done
880- ; Flash finished - reset border
881940 lda #BORDER_COL
882941 sta BORDER
883942 flash_done:
884943 rts
885944
886945 ; ----------------------------------------------------------------------------
887-; Check Keys - With hit detection and scoring
946+; Check Keys
888947 ; ----------------------------------------------------------------------------
889948
890949 check_keys:
891- ; Check Z key (track 1)
892950 lda #$FD
893951 sta CIA1_PRA
894952 lda CIA1_PRB
...
904962 jsr award_points
905963
906964 check_x_key:
907- ; Check X key (track 2)
908965 lda #$FB
909966 sta CIA1_PRA
910967 lda CIA1_PRB
...
920977 jsr award_points
921978
922979 check_c_key:
923- ; Check C key (track 3)
924980 lda #$FB
925981 sta CIA1_PRA
926982 lda CIA1_PRB
...
941997 rts
942998
943999 ; ----------------------------------------------------------------------------
944-; Check Hit - Find note and determine hit quality
1000+; Check Hit
9451001 ; ----------------------------------------------------------------------------
946-; Input: key_pressed = track number (1-3)
947-; Output: Carry set if hit, hit_quality set (1=good, 2=perfect)
948-; Carry clear if no hit
9491002
9501003 check_hit:
9511004 ldx #0
...
9631016 cmp #HIT_ZONE_MAX+1
9641017 bcs check_hit_next
9651018
966- ; HIT! Determine quality based on position
967- ; Centre columns (3-4) = perfect, edges (2, 5) = good
9681019 cmp #HIT_ZONE_CENTRE
969- bcc hit_good ; Column 2 = good
1020+ bcc hit_good
9701021 cmp #HIT_ZONE_CENTRE+2
971- bcs hit_good ; Column 5 = good
1022+ bcs hit_good
9721023
973- ; Perfect hit (columns 3-4)
9741024 lda #2
9751025 sta hit_quality
9761026 jmp hit_found
9771027
9781028 hit_good:
979- ; Good hit (columns 2 or 5)
9801029 lda #1
9811030 sta hit_quality
9821031
...
9981047 rts
9991048
10001049 ; ----------------------------------------------------------------------------
1001-; Award Points - Add score based on hit quality
1050+; Award Points
10021051 ; ----------------------------------------------------------------------------
10031052
10041053 award_points:
...
10061055 cmp #2
10071056 beq award_perfect
10081057
1009- ; Good hit - add 50 points
10101058 lda score_lo
10111059 clc
10121060 adc #GOOD_SCORE
...
10151063 adc #0
10161064 sta score_hi
10171065
1018- ; Yellow border flash for good
10191066 lda #GOOD_COL
10201067 sta BORDER
1021- lda #4 ; Flash for 4 frames
1068+ lda #4
10221069 sta border_flash
10231070
10241071 jmp award_done
10251072
10261073 award_perfect:
1027- ; Perfect hit - add 100 points
10281074 lda score_lo
10291075 clc
10301076 adc #PERFECT_SCORE
...
10331079 adc #0
10341080 sta score_hi
10351081
1036- ; White border flash for perfect
10371082 lda #PERFECT_COL
10381083 sta BORDER
1039- lda #6 ; Flash for 6 frames
1084+ lda #6
10401085 sta border_flash
10411086
10421087 award_done:
...
10441089 rts
10451090
10461091 ; ----------------------------------------------------------------------------
1047-; Display Score - Convert 16-bit score to decimal and show
1092+; Display Score
10481093 ; ----------------------------------------------------------------------------
10491094
10501095 display_score:
1051- ; Convert score to 5-digit decimal display
1052- ; Uses simple repeated subtraction method
1053-
1054- ; Copy score to working area
10551096 lda score_lo
10561097 sta work_lo
10571098 lda score_hi
10581099 sta work_hi
10591100
1060- ; Ten-thousands digit
10611101 ldx #0
10621102 div_10000:
10631103 lda work_lo
...
10731113 jmp div_10000
10741114 done_10000:
10751115 txa
1076- ora #$30 ; Convert to screen code
1116+ ora #$30
10771117 sta SCREEN + 8
10781118
1079- ; Thousands digit
10801119 ldx #0
10811120 div_1000:
10821121 lda work_lo
...
10951134 ora #$30
10961135 sta SCREEN + 9
10971136
1098- ; Hundreds digit
10991137 ldx #0
11001138 div_100:
11011139 lda work_lo
...
11101148 ora #$30
11111149 sta SCREEN + 10
11121150
1113- ; Tens digit
11141151 ldx #0
11151152 div_10:
11161153 lda work_lo
...
11251162 ora #$30
11261163 sta SCREEN + 11
11271164
1128- ; Ones digit
11291165 lda work_lo
11301166 ora #$30
11311167 sta SCREEN + 12
11321168
1133- ; Set score digit colours
1134- lda #7 ; Yellow for score
1169+ lda #7
11351170 sta COLRAM + 8
11361171 sta COLRAM + 9
11371172 sta COLRAM + 10
...
11401175
11411176 rts
11421177
1143-; Working variables for division
1178+; ----------------------------------------------------------------------------
1179+; Display Misses - Simple 2-digit display
1180+; ----------------------------------------------------------------------------
1181+
1182+display_misses:
1183+ lda miss_count
1184+
1185+ ; Tens digit
1186+ ldx #0
1187+miss_div_10:
1188+ cmp #10
1189+ bcc miss_done_10
1190+ sec
1191+ sbc #10
1192+ inx
1193+ jmp miss_div_10
1194+miss_done_10:
1195+ ; X = tens, A = ones
1196+ pha ; Save ones
1197+ txa
1198+ ora #$30
1199+ sta SCREEN + 21 ; Tens digit
1200+ pla
1201+ ora #$30
1202+ sta SCREEN + 22 ; Ones digit
1203+
1204+ lda #2 ; Red
1205+ sta COLRAM + 21
1206+ sta COLRAM + 22
1207+
1208+ rts
1209+
11441210 work_lo: !byte 0
11451211 work_hi: !byte 0
11461212
...
12461312 !fill MAX_NOTES, 0
12471313
12481314 ; ----------------------------------------------------------------------------
1249-; Score Variables
1315+; Game Variables
12501316 ; ----------------------------------------------------------------------------
12511317
12521318 score_lo: !byte 0
12531319 score_hi: !byte 0
1320+miss_count: !byte 0
12541321