Hit Detection
Detect when keypresses match notes in the hit zone. The core rhythm game mechanic.
Now it’s a real game. Press the right key when a note reaches the hit zone.
Previously, pressing Z/X/C always played sounds regardless of timing. This unit adds the core rhythm game mechanic: hit detection. Press the key when a note is in the hit zone - you hear the sound and the note disappears. Miss the timing - nothing happens.
Run It
Assemble and run:
acme -f cbm -o symphony.prg symphony.asm

Watch notes scroll toward the hit zone. Press Z when a note on track Z reaches the yellow bar. If timed correctly, the note vanishes and the SID plays. Press too early or late - nothing.
The Hit Zone
Notes must be within a range of columns to count as “hittable”:
; Hit zone boundaries (column positions)
HIT_ZONE_MIN = 2 ; Left edge of hit zone
HIT_ZONE_MAX = 5 ; Right edge of hit zone
The hit zone spans columns 2-5. A note at column 2 is about to leave the zone. A note at column 5 is just entering. This gives the player a window of 4 columns to hit each note.
How Hit Detection Works
When a key is pressed, the game searches through all active notes looking for one that:
- Is on the correct track (Z key = track 1, X = track 2, C = track 3)
- Has an X position within the hit zone
; ----------------------------------------------------------------------------
; Check Hit - Find a note in the hit zone for the pressed track
; ----------------------------------------------------------------------------
; Input: key_pressed = track number (1-3)
; Output: Carry set if hit found, X = note index
; Carry clear if no hit
; Side effect: Removes hit note from play
check_hit:
ldx #0
check_hit_loop:
; Check if this note slot is active
lda note_track,x
beq check_hit_next ; Empty slot - skip
; Check if note is on the pressed track
cmp key_pressed
bne check_hit_next ; Wrong track - skip
; Check if note is in the hit zone
lda note_col,x
cmp #HIT_ZONE_MIN
bcc check_hit_next ; Too far left - skip
cmp #HIT_ZONE_MAX+1
bcs check_hit_next ; Too far right - skip
; HIT! Note is in zone on correct track
; Erase the note from screen
jsr erase_note
; Deactivate the note
lda #0
sta note_track,x
; Return with carry set (hit found)
sec
rts
check_hit_next:
inx
cpx #MAX_NOTES
bne check_hit_loop
; No hit found - return with carry clear
clc
rts
The routine uses the carry flag to signal success or failure:
- Carry set (SEC) - Hit found, note removed
- Carry clear (CLC) - No hit, nothing changed
This is a common 6502 pattern. The branch instructions BCS (branch if carry set) and BCC (branch if carry clear) let the caller respond to the result.
Updating Key Handling
The key checking code now calls check_hit and only plays sound on success:
; ----------------------------------------------------------------------------
; Check Keys - Now with hit detection
; ----------------------------------------------------------------------------
; Checks if a key is pressed and if there's a matching note in the hit zone.
; Only plays sound and removes note on a successful hit.
check_keys:
; Check Z key (track 1)
lda #$FD
sta CIA1_PRA
lda CIA1_PRB
and #$10
bne check_x_key
; Z pressed - check for hit on track 1
lda #1
sta key_pressed
jsr check_hit
bcc check_x_key ; No hit - don't play sound
jsr play_voice1
jsr flash_track1_hit
check_x_key:
; Check X key (track 2)
lda #$FB
sta CIA1_PRA
lda CIA1_PRB
and #$80
bne check_c_key
; X pressed - check for hit on track 2
lda #2
sta key_pressed
jsr check_hit
bcc check_c_key ; No hit - don't play sound
jsr play_voice2
jsr flash_track2_hit
check_c_key:
; Check C key (track 3)
lda #$FB
sta CIA1_PRA
lda CIA1_PRB
and #$10
bne check_keys_done
; C pressed - check for hit on track 3
lda #3
sta key_pressed
jsr check_hit
bcc check_keys_done ; No hit - don't play sound
jsr play_voice3
jsr flash_track3_hit
check_keys_done:
lda #$FF
sta CIA1_PRA
rts
Compare this to Unit 4 where every keypress triggered a sound. Now BCC skips the sound if no hit was found.
The Complete Code
; ============================================================================
; SID SYMPHONY - Unit 5: Hit Detection
; ============================================================================
; Detect when keypresses match notes in the hit zone. Press the right key at
; the right time to hit notes and hear the SID play. Wrong timing does nothing.
;
; 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
; 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
; ============================================================================
; HIT DETECTION SETTINGS
; ============================================================================
; Hit zone boundaries (column positions)
HIT_ZONE_MIN = 2 ; Left edge of hit zone
HIT_ZONE_MAX = 5 ; Right edge of hit zone
HIT_ZONE_CENTRE = 3 ; Perfect hit position
; ============================================================================
; MEMORY MAP
; ============================================================================
SCREEN = $0400 ; Screen memory
COLRAM = $D800 ; Colour RAM
BORDER = $D020 ; Border colour
BGCOL = $D021 ; Background colour
CHARPTR = $D018 ; Character memory pointer
CHARSET = $3000 ; Custom character set location
; 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 ; Filled arrow/circle for notes
CHAR_TRACK = 129 ; Thin horizontal line for tracks
CHAR_HITZONE = 130 ; Vertical bar for hit zone
CHAR_SPACE = 32 ; Space
; 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 ; Which track key was pressed (0=none)
; ----------------------------------------------------------------------------
; 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 ; Copy ROM charset and add custom chars
jsr init_screen
jsr init_sid
jsr init_notes
lda #<song_data
sta song_pos
lda #>song_data
sta song_pos_hi
lda #0
sta frame_count
sta beat_count
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 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:
; Character 128: Note (filled chevron pointing left)
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
; Character 129: Track line (centered horizontal line)
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
; Character 130: Hit zone (vertical bars)
lda #%01100110
sta CHARSET + (CHAR_HITZONE * 8) + 0
lda #%01100110
sta CHARSET + (CHAR_HITZONE * 8) + 1
lda #%01100110
sta CHARSET + (CHAR_HITZONE * 8) + 2
lda #%01100110
sta CHARSET + (CHAR_HITZONE * 8) + 3
lda #%01100110
sta CHARSET + (CHAR_HITZONE * 8) + 4
lda #%01100110
sta CHARSET + (CHAR_HITZONE * 8) + 5
lda #%01100110
sta CHARSET + (CHAR_HITZONE * 8) + 6
lda #%01100110
sta CHARSET + (CHAR_HITZONE * 8) + 7
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
; ----------------------------------------------------------------------------
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_deactivate
jsr draw_note
jmp update_next
update_deactivate:
lda #0
sta note_track,x
update_next:
inx
cpx #MAX_NOTES
bne update_loop
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 - Restores track line character
; ----------------------------------------------------------------------------
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:
ldx #0
draw_title:
lda title_text,x
beq draw_title_done
sta SCREEN + 13,x
lda #1
sta COLRAM + 13,x
inx
bne draw_title
draw_title_done:
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)
ldx #0
draw_instr:
lda instr_text,x
beq draw_instr_done
sta SCREEN + (23 * 40) + 6,x
lda #TRACK_LINE_COL
sta COLRAM + (23 * 40) + 6,x
inx
bne draw_instr
draw_instr_done:
rts
title_text:
!scr "sid symphony"
!byte 0
instr_text:
!scr "hit detection active"
!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
; ----------------------------------------------------------------------------
; Check Keys - Now with hit detection
; ----------------------------------------------------------------------------
; Checks if a key is pressed and if there's a matching note in the hit zone.
; Only plays sound and removes note on a successful hit.
check_keys:
; Check Z key (track 1)
lda #$FD
sta CIA1_PRA
lda CIA1_PRB
and #$10
bne check_x_key
; Z pressed - check for hit on track 1
lda #1
sta key_pressed
jsr check_hit
bcc check_x_key ; No hit - don't play sound
jsr play_voice1
jsr flash_track1_hit
check_x_key:
; Check X key (track 2)
lda #$FB
sta CIA1_PRA
lda CIA1_PRB
and #$80
bne check_c_key
; X pressed - check for hit on track 2
lda #2
sta key_pressed
jsr check_hit
bcc check_c_key ; No hit - don't play sound
jsr play_voice2
jsr flash_track2_hit
check_c_key:
; Check C key (track 3)
lda #$FB
sta CIA1_PRA
lda CIA1_PRB
and #$10
bne check_keys_done
; C pressed - check for hit on track 3
lda #3
sta key_pressed
jsr check_hit
bcc check_keys_done ; No hit - don't play sound
jsr play_voice3
jsr flash_track3_hit
check_keys_done:
lda #$FF
sta CIA1_PRA
rts
; ----------------------------------------------------------------------------
; Check Hit - Find a note in the hit zone for the pressed track
; ----------------------------------------------------------------------------
; Input: key_pressed = track number (1-3)
; Output: Carry set if hit found, X = note index
; Carry clear if no hit
; Side effect: Removes hit note from play
check_hit:
ldx #0
check_hit_loop:
; Check if this note slot is active
lda note_track,x
beq check_hit_next ; Empty slot - skip
; Check if note is on the pressed track
cmp key_pressed
bne check_hit_next ; Wrong track - skip
; Check if note is in the hit zone
lda note_col,x
cmp #HIT_ZONE_MIN
bcc check_hit_next ; Too far left - skip
cmp #HIT_ZONE_MAX+1
bcs check_hit_next ; Too far right - skip
; HIT! Note is in zone on correct track
; Erase the note from screen
jsr erase_note
; Deactivate the note
lda #0
sta note_track,x
; Return with carry set (hit found)
sec
rts
check_hit_next:
inx
cpx #MAX_NOTES
bne check_hit_loop
; No hit found - return with carry clear
clc
rts
; ----------------------------------------------------------------------------
; 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 - White flash for successful hits
; ----------------------------------------------------------------------------
flash_track1_hit:
ldx #0
lda #HIT_COL ; White for hits
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
Try This: Narrower Hit Zone
Make timing more challenging:
HIT_ZONE_MIN = 3 ; Tighter window
HIT_ZONE_MAX = 4 ; Only 2 columns wide
The game becomes much harder - you need precise timing.
Try This: Wider Hit Zone
Make it more forgiving:
HIT_ZONE_MIN = 1 ; Very wide window
HIT_ZONE_MAX = 6 ; 6 columns to hit
Good for practice or accessibility.
What You’ve Learnt
- Position-based collision - Check if a value falls within a range
- Carry flag signalling - Use SEC/CLC and BCS/BCC for function results
- Note consumption - One keypress removes one note
- Conditional sound - Only play audio on successful game actions
What’s Next
In Unit 6, we’ll add scoring and visual feedback. Perfect hits will award more points than late hits. The screen will flash to celebrate success.
What Changed
| 1 | 1 | ; ============================================================================ | |
| 2 | - | ; SID SYMPHONY - Unit 4: Custom Graphics | |
| 2 | + | ; SID SYMPHONY - Unit 5: Hit Detection | |
| 3 | 3 | ; ============================================================================ | |
| 4 | - | ; Design custom characters for a polished look. Notes are arrows, tracks are | |
| 5 | - | ; clean lines, hit zones have distinctive markers. The game looks professional. | |
| 4 | + | ; Detect when keypresses match notes in the hit zone. Press the right key at | |
| 5 | + | ; the right time to hit notes and hear the SID play. Wrong timing does nothing. | |
| 6 | 6 | ; | |
| 7 | 7 | ; Controls: Z = Track 1 (high), X = Track 2 (mid), C = Track 3 (low) | |
| 8 | 8 | ; ============================================================================ | |
| ... | |||
| 38 | 38 | FLASH1_COL = 2 ; Red | |
| 39 | 39 | FLASH2_COL = 5 ; Green | |
| 40 | 40 | FLASH3_COL = 6 ; Blue | |
| 41 | + | | |
| 42 | + | HIT_COL = 1 ; White - flash on successful hit | |
| 43 | + | | |
| 44 | + | ; ============================================================================ | |
| 45 | + | ; HIT DETECTION SETTINGS | |
| 46 | + | ; ============================================================================ | |
| 47 | + | | |
| 48 | + | ; Hit zone boundaries (column positions) | |
| 49 | + | HIT_ZONE_MIN = 2 ; Left edge of hit zone | |
| 50 | + | HIT_ZONE_MAX = 5 ; Right edge of hit zone | |
| 51 | + | HIT_ZONE_CENTRE = 3 ; Perfect hit position | |
| 41 | 52 | | |
| 42 | 53 | ; ============================================================================ | |
| 43 | 54 | ; MEMORY MAP | |
| ... | |||
| 88 | 99 | ; Hit zone | |
| 89 | 100 | HIT_ZONE_COLUMN = 3 | |
| 90 | 101 | | |
| 91 | - | ; Custom character codes (we'll define these) | |
| 102 | + | ; Custom character codes | |
| 92 | 103 | CHAR_NOTE = 128 ; Filled arrow/circle for notes | |
| 93 | 104 | CHAR_TRACK = 129 ; Thin horizontal line for tracks | |
| 94 | 105 | CHAR_HITZONE = 130 ; Vertical bar for hit zone | |
| ... | |||
| 111 | 122 | song_pos = $04 | |
| 112 | 123 | song_pos_hi = $05 | |
| 113 | 124 | temp_track = $06 | |
| 125 | + | key_pressed = $07 ; Which track key was pressed (0=none) | |
| 114 | 126 | | |
| 115 | 127 | ; ---------------------------------------------------------------------------- | |
| 116 | 128 | ; BASIC Stub | |
| ... | |||
| 172 | 184 | ; ---------------------------------------------------------------------------- | |
| 173 | 185 | ; Copy Character Set from ROM to RAM | |
| 174 | 186 | ; ---------------------------------------------------------------------------- | |
| 175 | - | ; Copies the standard character ROM to $3000, then adds custom characters | |
| 176 | 187 | | |
| 177 | 188 | copy_charset: | |
| 178 | - | ; Disable interrupts during ROM access | |
| 179 | 189 | sei | |
| 180 | 190 | | |
| 181 | - | ; Make character ROM visible at $D000 | |
| 182 | 191 | lda $01 | |
| 183 | - | pha ; Save processor port | |
| 184 | - | and #$FB ; Clear bit 2 (CHAREN) | |
| 185 | - | sta $01 ; Character ROM now at $D000 | |
| 192 | + | pha | |
| 193 | + | and #$FB | |
| 194 | + | sta $01 | |
| 186 | 195 | | |
| 187 | - | ; Copy 2KB (256 characters * 8 bytes) | |
| 188 | 196 | ldx #0 | |
| 189 | 197 | copy_loop: | |
| 190 | 198 | lda $D000,x | |
| ... | |||
| 206 | 214 | inx | |
| 207 | 215 | bne copy_loop | |
| 208 | 216 | | |
| 209 | - | ; Restore processor port | |
| 210 | 217 | pla | |
| 211 | 218 | sta $01 | |
| 212 | 219 | | |
| 213 | - | ; Re-enable interrupts | |
| 214 | 220 | cli | |
| 215 | 221 | | |
| 216 | - | ; Define our custom characters | |
| 217 | 222 | jsr define_custom_chars | |
| 218 | 223 | | |
| 219 | - | ; Point VIC-II to our charset at $3000 | |
| 220 | - | ; Screen at $0400 = %0001 in upper nibble | |
| 221 | - | ; Charset at $3000 = %110 in bits 1-3 = $0C | |
| 222 | 224 | lda #$1C | |
| 223 | 225 | sta CHARPTR | |
| 224 | 226 | | |
| ... | |||
| 227 | 229 | ; ---------------------------------------------------------------------------- | |
| 228 | 230 | ; Define Custom Characters | |
| 229 | 231 | ; ---------------------------------------------------------------------------- | |
| 230 | - | ; Creates note, track, and hit zone graphics | |
| 231 | 232 | | |
| 232 | 233 | define_custom_chars: | |
| 233 | 234 | ; Character 128: Note (filled chevron pointing left) | |
| 234 | - | lda #%00000110 ; Row 0 | |
| 235 | + | lda #%00000110 | |
| 235 | 236 | sta CHARSET + (CHAR_NOTE * 8) + 0 | |
| 236 | - | lda #%00011110 ; Row 1 | |
| 237 | + | lda #%00011110 | |
| 237 | 238 | sta CHARSET + (CHAR_NOTE * 8) + 1 | |
| 238 | - | lda #%01111110 ; Row 2 | |
| 239 | + | lda #%01111110 | |
| 239 | 240 | sta CHARSET + (CHAR_NOTE * 8) + 2 | |
| 240 | - | lda #%11111110 ; Row 3 | |
| 241 | + | lda #%11111110 | |
| 241 | 242 | sta CHARSET + (CHAR_NOTE * 8) + 3 | |
| 242 | - | lda #%11111110 ; Row 4 | |
| 243 | + | lda #%11111110 | |
| 243 | 244 | sta CHARSET + (CHAR_NOTE * 8) + 4 | |
| 244 | - | lda #%01111110 ; Row 5 | |
| 245 | + | lda #%01111110 | |
| 245 | 246 | sta CHARSET + (CHAR_NOTE * 8) + 5 | |
| 246 | - | lda #%00011110 ; Row 6 | |
| 247 | + | lda #%00011110 | |
| 247 | 248 | sta CHARSET + (CHAR_NOTE * 8) + 6 | |
| 248 | - | lda #%00000110 ; Row 7 | |
| 249 | + | lda #%00000110 | |
| 249 | 250 | sta CHARSET + (CHAR_NOTE * 8) + 7 | |
| 250 | 251 | | |
| 251 | 252 | ; Character 129: Track line (centered horizontal line) | |
| 252 | - | lda #%00000000 ; Row 0 | |
| 253 | + | lda #%00000000 | |
| 253 | 254 | sta CHARSET + (CHAR_TRACK * 8) + 0 | |
| 254 | - | lda #%00000000 ; Row 1 | |
| 255 | + | lda #%00000000 | |
| 255 | 256 | sta CHARSET + (CHAR_TRACK * 8) + 1 | |
| 256 | - | lda #%00000000 ; Row 2 | |
| 257 | + | lda #%00000000 | |
| 257 | 258 | sta CHARSET + (CHAR_TRACK * 8) + 2 | |
| 258 | - | lda #%11111111 ; Row 3 - the line | |
| 259 | + | lda #%11111111 | |
| 259 | 260 | sta CHARSET + (CHAR_TRACK * 8) + 3 | |
| 260 | - | lda #%11111111 ; Row 4 - the line | |
| 261 | + | lda #%11111111 | |
| 261 | 262 | sta CHARSET + (CHAR_TRACK * 8) + 4 | |
| 262 | - | lda #%00000000 ; Row 5 | |
| 263 | + | lda #%00000000 | |
| 263 | 264 | sta CHARSET + (CHAR_TRACK * 8) + 5 | |
| 264 | - | lda #%00000000 ; Row 6 | |
| 265 | + | lda #%00000000 | |
| 265 | 266 | sta CHARSET + (CHAR_TRACK * 8) + 6 | |
| 266 | - | lda #%00000000 ; Row 7 | |
| 267 | + | lda #%00000000 | |
| 267 | 268 | sta CHARSET + (CHAR_TRACK * 8) + 7 | |
| 268 | 269 | | |
| 269 | 270 | ; Character 130: Hit zone (vertical bars) | |
| 270 | - | lda #%01100110 ; Row 0 | |
| 271 | + | lda #%01100110 | |
| 271 | 272 | sta CHARSET + (CHAR_HITZONE * 8) + 0 | |
| 272 | - | lda #%01100110 ; Row 1 | |
| 273 | + | lda #%01100110 | |
| 273 | 274 | sta CHARSET + (CHAR_HITZONE * 8) + 1 | |
| 274 | - | lda #%01100110 ; Row 2 | |
| 275 | + | lda #%01100110 | |
| 275 | 276 | sta CHARSET + (CHAR_HITZONE * 8) + 2 | |
| 276 | - | lda #%01100110 ; Row 3 | |
| 277 | + | lda #%01100110 | |
| 277 | 278 | sta CHARSET + (CHAR_HITZONE * 8) + 3 | |
| 278 | - | lda #%01100110 ; Row 4 | |
| 279 | + | lda #%01100110 | |
| 279 | 280 | sta CHARSET + (CHAR_HITZONE * 8) + 4 | |
| 280 | - | lda #%01100110 ; Row 5 | |
| 281 | + | lda #%01100110 | |
| 281 | 282 | sta CHARSET + (CHAR_HITZONE * 8) + 5 | |
| 282 | - | lda #%01100110 ; Row 6 | |
| 283 | + | lda #%01100110 | |
| 283 | 284 | sta CHARSET + (CHAR_HITZONE * 8) + 6 | |
| 284 | - | lda #%01100110 ; Row 7 | |
| 285 | + | lda #%01100110 | |
| 285 | 286 | sta CHARSET + (CHAR_HITZONE * 8) + 7 | |
| 286 | 287 | | |
| 287 | 288 | rts | |
| ... | |||
| 402 | 403 | rts | |
| 403 | 404 | | |
| 404 | 405 | ; ---------------------------------------------------------------------------- | |
| 405 | - | ; Draw Note - Uses custom note character | |
| 406 | + | ; Draw Note | |
| 406 | 407 | ; ---------------------------------------------------------------------------- | |
| 407 | 408 | | |
| 408 | 409 | draw_note: | |
| ... | |||
| 425 | 426 | sta ZP_PTR_HI | |
| 426 | 427 | | |
| 427 | 428 | ldy #0 | |
| 428 | - | lda #CHAR_NOTE ; Custom note character | |
| 429 | + | lda #CHAR_NOTE | |
| 429 | 430 | sta (ZP_PTR),y | |
| 430 | 431 | | |
| 431 | 432 | lda note_col,x | |
| ... | |||
| 511 | 512 | sta ZP_PTR_HI | |
| 512 | 513 | | |
| 513 | 514 | ldy #0 | |
| 514 | - | lda #CHAR_TRACK ; Custom track character | |
| 515 | + | lda #CHAR_TRACK | |
| 515 | 516 | sta (ZP_PTR),y | |
| 516 | 517 | | |
| 517 | 518 | lda note_col,x | |
| ... | |||
| 583 | 584 | lda #BG_COL | |
| 584 | 585 | sta BGCOL | |
| 585 | 586 | | |
| 586 | - | ; Clear screen with spaces | |
| 587 | 587 | ldx #0 | |
| 588 | 588 | lda #CHAR_SPACE | |
| 589 | 589 | clr_screen: | |
| ... | |||
| 594 | 594 | inx | |
| 595 | 595 | bne clr_screen | |
| 596 | 596 | | |
| 597 | - | ; Set all colours to track line colour | |
| 598 | 597 | ldx #0 | |
| 599 | 598 | lda #TRACK_LINE_COL | |
| 600 | 599 | clr_colour: | |
| ... | |||
| 612 | 611 | rts | |
| 613 | 612 | | |
| 614 | 613 | ; ---------------------------------------------------------------------------- | |
| 615 | - | ; Draw Tracks - Uses custom track character | |
| 614 | + | ; Draw Tracks | |
| 616 | 615 | ; ---------------------------------------------------------------------------- | |
| 617 | 616 | | |
| 618 | 617 | draw_tracks: | |
| 619 | - | ; Track 1 | |
| 620 | 618 | lda #CHAR_TRACK | |
| 621 | 619 | ldx #0 | |
| 622 | 620 | draw_t1: | |
| ... | |||
| 625 | 623 | cpx #38 | |
| 626 | 624 | bne draw_t1 | |
| 627 | 625 | | |
| 628 | - | ; Track 2 | |
| 629 | 626 | lda #CHAR_TRACK | |
| 630 | 627 | ldx #0 | |
| 631 | 628 | draw_t2: | |
| ... | |||
| 634 | 631 | cpx #38 | |
| 635 | 632 | bne draw_t2 | |
| 636 | 633 | | |
| 637 | - | ; Track 3 | |
| 638 | 634 | lda #CHAR_TRACK | |
| 639 | 635 | ldx #0 | |
| 640 | 636 | draw_t3: | |
| ... | |||
| 646 | 642 | rts | |
| 647 | 643 | | |
| 648 | 644 | ; ---------------------------------------------------------------------------- | |
| 649 | - | ; Draw Hit Zones - Uses custom hit zone character | |
| 645 | + | ; Draw Hit Zones | |
| 650 | 646 | ; ---------------------------------------------------------------------------- | |
| 651 | 647 | | |
| 652 | 648 | draw_hit_zones: | |
| 653 | - | lda #CHAR_HITZONE ; Custom hit zone character | |
| 649 | + | lda #CHAR_HITZONE | |
| 654 | 650 | | |
| 655 | - | ; Draw hit zone bars spanning all three tracks | |
| 656 | 651 | sta SCREEN + ((TRACK1_ROW-2) * 40) + HIT_ZONE_COLUMN | |
| 657 | 652 | sta SCREEN + ((TRACK1_ROW-1) * 40) + HIT_ZONE_COLUMN | |
| 658 | 653 | sta SCREEN + (TRACK1_ROW * 40) + HIT_ZONE_COLUMN | |
| ... | |||
| 667 | 662 | sta SCREEN + ((TRACK3_ROW+1) * 40) + HIT_ZONE_COLUMN | |
| 668 | 663 | sta SCREEN + ((TRACK3_ROW+2) * 40) + HIT_ZONE_COLUMN | |
| 669 | 664 | | |
| 670 | - | ; Colour the hit zones | |
| 671 | 665 | lda #HIT_ZONE_COL | |
| 672 | 666 | sta COLRAM + ((TRACK1_ROW-2) * 40) + HIT_ZONE_COLUMN | |
| 673 | 667 | sta COLRAM + ((TRACK1_ROW-1) * 40) + HIT_ZONE_COLUMN | |
| ... | |||
| 695 | 689 | lda title_text,x | |
| 696 | 690 | beq draw_title_done | |
| 697 | 691 | sta SCREEN + 13,x | |
| 698 | - | lda #1 ; White | |
| 692 | + | lda #1 | |
| 699 | 693 | sta COLRAM + 13,x | |
| 700 | 694 | inx | |
| 701 | 695 | bne draw_title | |
| 702 | 696 | draw_title_done: | |
| 703 | 697 | | |
| 704 | - | ; Track labels - use standard characters for letters | |
| 705 | - | lda #$1A ; Z (screen code) | |
| 698 | + | lda #$1A ; Z | |
| 706 | 699 | sta SCREEN + (TRACK1_ROW * 40) | |
| 707 | 700 | lda #TRACK1_NOTE_COL | |
| 708 | 701 | sta COLRAM + (TRACK1_ROW * 40) | |
| ... | |||
| 735 | 728 | !byte 0 | |
| 736 | 729 | | |
| 737 | 730 | instr_text: | |
| 738 | - | !scr "custom graphics active" | |
| 731 | + | !scr "hit detection active" | |
| 739 | 732 | !byte 0 | |
| 740 | 733 | | |
| 741 | 734 | ; ---------------------------------------------------------------------------- | |
| ... | |||
| 815 | 808 | cpx #38 | |
| 816 | 809 | bne reset_t3 | |
| 817 | 810 | | |
| 818 | - | ; Restore key labels | |
| 819 | 811 | lda #TRACK1_NOTE_COL | |
| 820 | 812 | sta COLRAM + (TRACK1_ROW * 40) | |
| 821 | 813 | lda #TRACK2_NOTE_COL | |
| ... | |||
| 823 | 815 | lda #TRACK3_NOTE_COL | |
| 824 | 816 | sta COLRAM + (TRACK3_ROW * 40) | |
| 825 | 817 | | |
| 826 | - | ; Restore hit zone colours | |
| 827 | 818 | lda #HIT_ZONE_COL | |
| 828 | 819 | sta COLRAM + (TRACK1_ROW * 40) + HIT_ZONE_COLUMN | |
| 829 | 820 | sta COLRAM + (TRACK2_ROW * 40) + HIT_ZONE_COLUMN | |
| ... | |||
| 850 | 841 | rts | |
| 851 | 842 | | |
| 852 | 843 | ; ---------------------------------------------------------------------------- | |
| 853 | - | ; Check Keys | |
| 844 | + | ; Check Keys - Now with hit detection | |
| 854 | 845 | ; ---------------------------------------------------------------------------- | |
| 846 | + | ; Checks if a key is pressed and if there's a matching note in the hit zone. | |
| 847 | + | ; Only plays sound and removes note on a successful hit. | |
| 855 | 848 | | |
| 856 | 849 | check_keys: | |
| 850 | + | ; Check Z key (track 1) | |
| 857 | 851 | lda #$FD | |
| 858 | 852 | sta CIA1_PRA | |
| 859 | 853 | lda CIA1_PRB | |
| 860 | 854 | and #$10 | |
| 861 | 855 | bne check_x_key | |
| 856 | + | | |
| 857 | + | ; Z pressed - check for hit on track 1 | |
| 858 | + | lda #1 | |
| 859 | + | sta key_pressed | |
| 860 | + | jsr check_hit | |
| 861 | + | bcc check_x_key ; No hit - don't play sound | |
| 862 | 862 | jsr play_voice1 | |
| 863 | - | jsr flash_track1 | |
| 863 | + | jsr flash_track1_hit | |
| 864 | 864 | | |
| 865 | 865 | check_x_key: | |
| 866 | + | ; Check X key (track 2) | |
| 866 | 867 | lda #$FB | |
| 867 | 868 | sta CIA1_PRA | |
| 868 | 869 | lda CIA1_PRB | |
| 869 | 870 | and #$80 | |
| 870 | 871 | bne check_c_key | |
| 872 | + | | |
| 873 | + | ; X pressed - check for hit on track 2 | |
| 874 | + | lda #2 | |
| 875 | + | sta key_pressed | |
| 876 | + | jsr check_hit | |
| 877 | + | bcc check_c_key ; No hit - don't play sound | |
| 871 | 878 | jsr play_voice2 | |
| 872 | - | jsr flash_track2 | |
| 879 | + | jsr flash_track2_hit | |
| 873 | 880 | | |
| 874 | 881 | check_c_key: | |
| 882 | + | ; Check C key (track 3) | |
| 875 | 883 | lda #$FB | |
| 876 | 884 | sta CIA1_PRA | |
| 877 | 885 | lda CIA1_PRB | |
| 878 | 886 | and #$10 | |
| 879 | 887 | bne check_keys_done | |
| 888 | + | | |
| 889 | + | ; C pressed - check for hit on track 3 | |
| 890 | + | lda #3 | |
| 891 | + | sta key_pressed | |
| 892 | + | jsr check_hit | |
| 893 | + | bcc check_keys_done ; No hit - don't play sound | |
| 880 | 894 | jsr play_voice3 | |
| 881 | - | jsr flash_track3 | |
| 895 | + | jsr flash_track3_hit | |
| 882 | 896 | | |
| 883 | 897 | check_keys_done: | |
| 884 | 898 | lda #$FF | |
| 885 | 899 | sta CIA1_PRA | |
| 900 | + | rts | |
| 901 | + | | |
| 902 | + | ; ---------------------------------------------------------------------------- | |
| 903 | + | ; Check Hit - Find a note in the hit zone for the pressed track | |
| 904 | + | ; ---------------------------------------------------------------------------- | |
| 905 | + | ; Input: key_pressed = track number (1-3) | |
| 906 | + | ; Output: Carry set if hit found, X = note index | |
| 907 | + | ; Carry clear if no hit | |
| 908 | + | ; Side effect: Removes hit note from play | |
| 909 | + | | |
| 910 | + | check_hit: | |
| 911 | + | ldx #0 | |
| 912 | + | | |
| 913 | + | check_hit_loop: | |
| 914 | + | ; Check if this note slot is active | |
| 915 | + | lda note_track,x | |
| 916 | + | beq check_hit_next ; Empty slot - skip | |
| 917 | + | | |
| 918 | + | ; Check if note is on the pressed track | |
| 919 | + | cmp key_pressed | |
| 920 | + | bne check_hit_next ; Wrong track - skip | |
| 921 | + | | |
| 922 | + | ; Check if note is in the hit zone | |
| 923 | + | lda note_col,x | |
| 924 | + | cmp #HIT_ZONE_MIN | |
| 925 | + | bcc check_hit_next ; Too far left - skip | |
| 926 | + | cmp #HIT_ZONE_MAX+1 | |
| 927 | + | bcs check_hit_next ; Too far right - skip | |
| 928 | + | | |
| 929 | + | ; HIT! Note is in zone on correct track | |
| 930 | + | ; Erase the note from screen | |
| 931 | + | jsr erase_note | |
| 932 | + | | |
| 933 | + | ; Deactivate the note | |
| 934 | + | lda #0 | |
| 935 | + | sta note_track,x | |
| 936 | + | | |
| 937 | + | ; Return with carry set (hit found) | |
| 938 | + | sec | |
| 939 | + | rts | |
| 940 | + | | |
| 941 | + | check_hit_next: | |
| 942 | + | inx | |
| 943 | + | cpx #MAX_NOTES | |
| 944 | + | bne check_hit_loop | |
| 945 | + | | |
| 946 | + | ; No hit found - return with carry clear | |
| 947 | + | clc | |
| 886 | 948 | rts | |
| 887 | 949 | | |
| 888 | 950 | ; ---------------------------------------------------------------------------- | |
| ... | |||
| 908 | 970 | rts | |
| 909 | 971 | | |
| 910 | 972 | ; ---------------------------------------------------------------------------- | |
| 911 | - | ; Flash Tracks | |
| 973 | + | ; Flash Tracks on Hit - White flash for successful hits | |
| 912 | 974 | ; ---------------------------------------------------------------------------- | |
| 913 | 975 | | |
| 914 | - | flash_track1: | |
| 976 | + | flash_track1_hit: | |
| 915 | 977 | ldx #0 | |
| 916 | - | lda #FLASH1_COL | |
| 917 | - | flash_t1_loop: | |
| 978 | + | lda #HIT_COL ; White for hits | |
| 979 | + | flash_t1h_loop: | |
| 918 | 980 | sta COLRAM + (TRACK1_ROW * 40),x | |
| 919 | 981 | inx | |
| 920 | 982 | cpx #38 | |
| 921 | - | bne flash_t1_loop | |
| 983 | + | bne flash_t1h_loop | |
| 922 | 984 | lda #1 | |
| 923 | 985 | sta COLRAM + (TRACK1_ROW * 40) | |
| 924 | 986 | rts | |
| 925 | 987 | | |
| 926 | - | flash_track2: | |
| 988 | + | flash_track2_hit: | |
| 927 | 989 | ldx #0 | |
| 928 | - | lda #FLASH2_COL | |
| 929 | - | flash_t2_loop: | |
| 990 | + | lda #HIT_COL | |
| 991 | + | flash_t2h_loop: | |
| 930 | 992 | sta COLRAM + (TRACK2_ROW * 40),x | |
| 931 | 993 | inx | |
| 932 | 994 | cpx #38 | |
| 933 | - | bne flash_t2_loop | |
| 995 | + | bne flash_t2h_loop | |
| 934 | 996 | lda #1 | |
| 935 | 997 | sta COLRAM + (TRACK2_ROW * 40) | |
| 936 | 998 | rts | |
| 937 | 999 | | |
| 938 | - | flash_track3: | |
| 1000 | + | flash_track3_hit: | |
| 939 | 1001 | ldx #0 | |
| 940 | - | lda #FLASH3_COL | |
| 941 | - | flash_t3_loop: | |
| 1002 | + | lda #HIT_COL | |
| 1003 | + | flash_t3h_loop: | |
| 942 | 1004 | sta COLRAM + (TRACK3_ROW * 40),x | |
| 943 | 1005 | inx | |
| 944 | 1006 | cpx #38 | |
| 945 | - | bne flash_t3_loop | |
| 1007 | + | bne flash_t3h_loop | |
| 946 | 1008 | lda #1 | |
| 947 | 1009 | sta COLRAM + (TRACK3_ROW * 40) | |
| 948 | 1010 | rts |