Making It Your Own
Customise the SID voices and visual style. Learn the internals by changing them.
The game works. Now make it yours.
This unit teaches the SID chip and colour system by having you change them. You won’t just read about waveforms and ADSR - you’ll modify them and hear the results. By the end, you’ll understand exactly how the C64’s sound chip works because you’ll have bent it to your will.
Run It
Assemble and run:
acme -f cbm -o symphony.prg symphony.asm

This doesn’t look or sound like the previous versions. The border is blue. The track lines are dark grey. The bottom says “CUSTOMISE ME!” And when you press Z, X, or C - the sounds have their own character. Sawtooth, pulse, triangle.
Same game, completely different feel.
The Customisation Section
All the changes come from one section at the top of the code:
; ============================================================================
; CUSTOMISATION SECTION - Change these values and reassemble!
; ============================================================================
; --- SID Voice Settings ---
; Waveforms: $11=triangle, $21=sawtooth, $41=pulse, $81=noise
VOICE1_WAVE = $21 ; Sawtooth - bright, buzzy
VOICE2_WAVE = $41 ; Pulse - hollow, reedy
VOICE3_WAVE = $11 ; Triangle - soft, mellow
; Frequencies (higher = higher pitch)
; Common notes: $07=C3, $0E=C4, $1C=C5, $38=C6
VOICE1_FREQ = $1C ; High pitch (C5)
VOICE2_FREQ = $0E ; Mid pitch (C4)
VOICE3_FREQ = $07 ; Low pitch (C3)
; ADSR - Attack/Decay/Sustain/Release
; Attack: 0-15 (0=2ms, 15=8s) Decay: 0-15 (0=6ms, 15=24s)
; Sustain: 0-15 (volume level) Release: 0-15 (0=6ms, 15=24s)
VOICE_AD = $09 ; Attack=0 (instant), Decay=9 (medium)
VOICE_SR = $00 ; Sustain=0 (none), Release=0 (instant)
; Pulse width (only affects pulse wave, $41)
PULSE_WIDTH = $08 ; $08 = 50% duty cycle (square wave)
; --- Visual Settings ---
; Colours: 0=black, 1=white, 2=red, 3=cyan, 4=purple, 5=green
; 6=blue, 7=yellow, 8=orange, 9=brown, 10=light red
; 11=dark grey, 12=grey, 13=light green, 14=light blue, 15=light grey
BORDER_COL = 6 ; Blue border
BG_COL = 0 ; Black background
TRACK1_NOTE_COL = 10 ; Light red notes on track 1
TRACK2_NOTE_COL = 13 ; Light green notes on track 2
TRACK3_NOTE_COL = 14 ; Light blue notes on track 3
TRACK_LINE_COL = 11 ; Dark grey track lines
HIT_ZONE_COL = 7 ; Yellow hit zone
; ============================================================================
; END OF CUSTOMISATION - Code below uses the settings above
; ============================================================================
This is your control panel. Change these values, reassemble, and hear/see the difference immediately.
Understanding Waveforms
The SID has four waveform types. Each has a distinct sound:
; SID Waveforms - Each has a distinct character
;
; $11 = Triangle - Pure, soft, flute-like
; Smooth wave with no harsh harmonics
; Good for bass and mellow leads
;
; $21 = Sawtooth - Bright, buzzy, aggressive
; Rich in harmonics, sounds "sharp"
; Good for leads and brass-like sounds
;
; $41 = Pulse (square) - Hollow, woody, reedy
; Character changes with pulse width
; 50% = pure square, other widths = more nasal
;
; $81 = Noise - Hiss, crash, explosion
; Random waveform, no pitch
; Good for drums, percussion, effects
;
; The waveform value goes in the control register (SID_V1_CTRL etc.)
; Add $01 (gate bit) to trigger the sound: $21 + $01 = $22 won't work!
; Must OR the gate bit: $21 OR $01 = $21 (waveform already has bit 0 clear)
play_voice1:
lda #VOICE1_WAVE ; e.g., $21 for sawtooth
ora #$01 ; Add gate bit to trigger
sta SID_V1_CTRL
rts
Try This: All Triangle
Make all three voices use triangle waves:
VOICE1_WAVE = $11 ; Triangle
VOICE2_WAVE = $11 ; Triangle
VOICE3_WAVE = $11 ; Triangle
The game will sound softer, more mellow. Like a flute choir.
Try This: All Noise
For a chaotic, percussive feel:
VOICE1_WAVE = $81 ; Noise
VOICE2_WAVE = $81 ; Noise
VOICE3_WAVE = $81 ; Noise
No pitch - just white noise bursts. Good for drum machines, terrible for melody.
Try This: Mixed Character
Create contrast between voices:
VOICE1_WAVE = $81 ; Noise - percussion hit
VOICE2_WAVE = $21 ; Sawtooth - bright lead
VOICE3_WAVE = $11 ; Triangle - soft bass
Now each track has its own sonic identity.
Understanding ADSR
ADSR shapes how sound evolves over time:
; ADSR Envelope - Shapes how the sound evolves over time
;
; Attack: How quickly the sound reaches full volume (0=instant, 15=slow)
; Decay: How quickly it drops to sustain level (0=instant, 15=slow)
; Sustain: Volume level while key is held (0=silent, 15=full)
; Release: How quickly it fades when released (0=instant, 15=slow)
;
; Packed into two bytes:
; AD register: upper nibble = Attack, lower nibble = Decay
; SR register: upper nibble = Sustain, lower nibble = Release
;
; Examples:
;
; $09, $00 = Instant attack, medium decay, no sustain, instant release
; Good for: Plucked strings, percussion hits
;
; $00, $F0 = Instant everything, but sustain at max while gate is on
; Good for: Organ, held notes
;
; $50, $90 = Slow attack, instant decay, high sustain, medium release
; Good for: Pads, strings, atmospheric sounds
;
; $0C, $00 = Instant attack, long decay, no sustain
; Good for: Piano-like decay
VOICE_AD = $09 ; Attack=0, Decay=9
VOICE_SR = $00 ; Sustain=0, Release=0
Try This: Slow Attack
VOICE_AD = $59 ; Attack=5 (slow rise), Decay=9
VOICE_SR = $00 ; Sustain=0, Release=0
Notes now “swell in” instead of hitting instantly. Like a violin bow.
Try This: Long Sustain
VOICE_AD = $00 ; Attack=0 (instant), Decay=0 (instant)
VOICE_SR = $F0 ; Sustain=15 (max), Release=0
Notes hold at full volume as long as you’d hold the key (in a real game). With our trigger-and-release approach, you’ll hear the maximum sustain level briefly.
Try This: Piano-Style Decay
VOICE_AD = $0C ; Attack=0, Decay=12 (long)
VOICE_SR = $00 ; Sustain=0, Release=0
Notes ring out with a natural decay, like piano strings.
Understanding Colours
The C64 has 16 colours:
; C64 Colour Palette (16 colours)
;
; Value Colour Notes
; ----- ----------- ---------------------------------
; 0 Black Background, shadows
; 1 White Highlights, text
; 2 Red Danger, track 1
; 3 Cyan Cool, water
; 4 Purple Rich, royal
; 5 Green Nature, track 2, success
; 6 Blue Sky, calm, track 3
; 7 Yellow Warning, attention, hit zone
; 8 Orange Warm, energy
; 9 Brown Earth, wood
; 10 Light Red Pink, softer danger
; 11 Dark Grey Subtle, inactive
; 12 Grey Neutral, disabled
; 13 Light Green Bright nature, success
; 14 Light Blue Sky, highlight
; 15 Light Grey Soft neutral
;
; Use colour for meaning:
; - Hit zone in yellow (attention, "hit here!")
; - Track lines in dark grey (background, don't distract)
; - Notes in bright colours (important, focus here)
; - Flash colours matching note colours
TRACK1_NOTE_COL = 10 ; Light red - danger zone
TRACK2_NOTE_COL = 13 ; Light green - go zone
TRACK3_NOTE_COL = 14 ; Light blue - cool zone
Try This: Warm Theme
BORDER_COL = 8 ; Orange border
BG_COL = 0 ; Black background
TRACK1_NOTE_COL = 2 ; Red
TRACK2_NOTE_COL = 8 ; Orange
TRACK3_NOTE_COL = 7 ; Yellow
TRACK_LINE_COL = 9 ; Brown
Try This: Cool Theme
BORDER_COL = 14 ; Light blue border
BG_COL = 6 ; Blue background
TRACK1_NOTE_COL = 3 ; Cyan
TRACK2_NOTE_COL = 14 ; Light blue
TRACK3_NOTE_COL = 1 ; White
TRACK_LINE_COL = 6 ; Blue (subtle on blue bg)
Try This: High Contrast
BORDER_COL = 1 ; White border
BG_COL = 0 ; Black background
TRACK1_NOTE_COL = 1 ; White
TRACK2_NOTE_COL = 1 ; White
TRACK3_NOTE_COL = 1 ; White
TRACK_LINE_COL = 12 ; Grey
The Complete Code
; ============================================================================
; SID SYMPHONY - Unit 3: Making It Your Own
; ============================================================================
; Customise the game to make it yours. Change the SID voices, pick your own
; colours, experiment with waveforms and ADSR. Learn the internals by
; modifying them and hearing/seeing the results.
;
; Controls: Z = Track 1 (high), X = Track 2 (mid), C = Track 3 (low)
; ============================================================================
; ============================================================================
; CUSTOMISATION SECTION - Change these values and reassemble!
; ============================================================================
; --- SID Voice Settings ---
; Waveforms: $11=triangle, $21=sawtooth, $41=pulse, $81=noise
VOICE1_WAVE = $21 ; Sawtooth - bright, buzzy
VOICE2_WAVE = $41 ; Pulse - hollow, reedy
VOICE3_WAVE = $11 ; Triangle - soft, mellow
; Frequencies (higher = higher pitch)
; Common notes: $07=C3, $0E=C4, $1C=C5, $38=C6
VOICE1_FREQ = $1C ; High pitch (C5)
VOICE2_FREQ = $0E ; Mid pitch (C4)
VOICE3_FREQ = $07 ; Low pitch (C3)
; ADSR - Attack/Decay/Sustain/Release
; Attack: 0-15 (0=2ms, 15=8s) Decay: 0-15 (0=6ms, 15=24s)
; Sustain: 0-15 (volume level) Release: 0-15 (0=6ms, 15=24s)
VOICE_AD = $09 ; Attack=0 (instant), Decay=9 (medium)
VOICE_SR = $00 ; Sustain=0 (none), Release=0 (instant)
; Pulse width (only affects pulse wave, $41)
PULSE_WIDTH = $08 ; $08 = 50% duty cycle (square wave)
; --- Visual Settings ---
; Colours: 0=black, 1=white, 2=red, 3=cyan, 4=purple, 5=green
; 6=blue, 7=yellow, 8=orange, 9=brown, 10=light red
; 11=dark grey, 12=grey, 13=light green, 14=light blue, 15=light grey
BORDER_COL = 6 ; Blue border
BG_COL = 0 ; Black background
TRACK1_NOTE_COL = 10 ; Light red notes on track 1
TRACK2_NOTE_COL = 13 ; Light green notes on track 2
TRACK3_NOTE_COL = 14 ; Light blue notes on track 3
TRACK_LINE_COL = 11 ; Dark grey track lines
HIT_ZONE_COL = 7 ; Yellow hit zone
; Track flash colours (when key pressed)
FLASH1_COL = 2 ; Red flash for track 1
FLASH2_COL = 5 ; Green flash for track 2
FLASH3_COL = 6 ; Blue flash for track 3
; ============================================================================
; END OF CUSTOMISATION - Code below uses the settings above
; ============================================================================
; ----------------------------------------------------------------------------
; Memory Addresses
; ----------------------------------------------------------------------------
SCREEN = $0400
COLRAM = $D800
BORDER = $D020
BGCOL = $D021
; SID registers
SID = $D400
SID_V1_FREQ_LO = $D400
SID_V1_FREQ_HI = $D401
SID_V1_PWLO = $D402
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_PWLO = $D409
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_PWLO = $D410
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 column
HIT_ZONE_COLUMN = 3
; Note settings
NOTE_CHAR = $57 ; Filled dot character
TRACK_CHAR = $2D ; Minus character
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
; ----------------------------------------------------------------------------
; BASIC Stub
; ----------------------------------------------------------------------------
* = $0801
!byte $0C, $08
!byte $0A, $00
!byte $9E
!text "2064"
!byte $00
!byte $00, $00
; ----------------------------------------------------------------------------
; Main Program
; ----------------------------------------------------------------------------
* = $0810
start:
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
; ----------------------------------------------------------------------------
; 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 #NOTE_CHAR
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 #NOTE_CHAR
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 #NOTE_CHAR
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 #TRACK_CHAR
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 #TRACK_CHAR
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 #TRACK_CHAR
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 #$20
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:
ldx #0
lda #TRACK_CHAR
draw_t1:
sta SCREEN + (TRACK1_ROW * 40),x
inx
cpx #38
bne draw_t1
ldx #0
draw_t2:
sta SCREEN + (TRACK2_ROW * 40),x
inx
cpx #38
bne draw_t2
ldx #0
draw_t3:
sta SCREEN + (TRACK3_ROW * 40),x
inx
cpx #38
bne draw_t3
rts
; ----------------------------------------------------------------------------
; Draw Hit Zones
; ----------------------------------------------------------------------------
draw_hit_zones:
lda #$7D
sta SCREEN + (TRACK1_ROW * 40) + HIT_ZONE_COLUMN
sta SCREEN + ((TRACK1_ROW-1) * 40) + HIT_ZONE_COLUMN
sta SCREEN + ((TRACK1_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 + ((TRACK2_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+1) * 40) + HIT_ZONE_COLUMN
lda #HIT_ZONE_COL
sta COLRAM + (TRACK1_ROW * 40) + HIT_ZONE_COLUMN
sta COLRAM + ((TRACK1_ROW-1) * 40) + HIT_ZONE_COLUMN
sta COLRAM + ((TRACK1_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 + ((TRACK2_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+1) * 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 ; White
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) + 8,x
lda #TRACK_LINE_COL
sta COLRAM + (23 * 40) + 8,x
inx
bne draw_instr
draw_instr_done:
rts
title_text:
!scr "sid symphony"
!byte 0
instr_text:
!scr "customise me!"
!byte 0
; ----------------------------------------------------------------------------
; Initialize SID - Uses customisation constants
; ----------------------------------------------------------------------------
init_sid:
ldx #$18
lda #0
clear_sid:
sta SID,x
dex
bpl clear_sid
lda #$0F
sta SID_VOLUME
; Voice 1 - uses VOICE1_* constants
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
; Voice 2 - uses VOICE2_* constants
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
; Voice 3 - uses VOICE3_* constants
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
; Restore key labels
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)
; Restore hit zone colours
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
; ----------------------------------------------------------------------------
check_keys:
lda #$FD
sta CIA1_PRA
lda CIA1_PRB
and #$10
bne check_x_key
jsr play_voice1
jsr flash_track1
check_x_key:
lda #$FB
sta CIA1_PRA
lda CIA1_PRB
and #$80
bne check_c_key
jsr play_voice2
jsr flash_track2
check_c_key:
lda #$FB
sta CIA1_PRA
lda CIA1_PRB
and #$10
bne check_keys_done
jsr play_voice3
jsr flash_track3
check_keys_done:
lda #$FF
sta CIA1_PRA
rts
; ----------------------------------------------------------------------------
; Play Voices - Uses customisation waveforms
; ----------------------------------------------------------------------------
play_voice1:
lda #VOICE1_WAVE
ora #$01 ; Add gate bit
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 - Uses customisation colours
; ----------------------------------------------------------------------------
flash_track1:
ldx #0
lda #FLASH1_COL
flash_t1_loop:
sta COLRAM + (TRACK1_ROW * 40),x
inx
cpx #38
bne flash_t1_loop
lda #1 ; White label
sta COLRAM + (TRACK1_ROW * 40)
rts
flash_track2:
ldx #0
lda #FLASH2_COL
flash_t2_loop:
sta COLRAM + (TRACK2_ROW * 40),x
inx
cpx #38
bne flash_t2_loop
lda #1
sta COLRAM + (TRACK2_ROW * 40)
rts
flash_track3:
ldx #0
lda #FLASH3_COL
flash_t3_loop:
sta COLRAM + (TRACK3_ROW * 40),x
inx
cpx #38
bne flash_t3_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
Challenge: Create Your Theme
Design a complete audio-visual style:
- Pick waveforms that complement each other
- Choose ADSR values that fit your waveforms
- Select colours that create the right mood
- Test by playing - does it feel cohesive?
Document your choices with comments. Share your theme.
What You’ve Learnt
- Waveforms - Triangle (soft), sawtooth (bright), pulse (hollow), noise (chaotic)
- ADSR envelopes - Attack (rise time), Decay (fall to sustain), Sustain (held level), Release (fade out)
- Colour palette - 16 colours from 0-15, each with its own character
- Customisation patterns - Constants at the top of code for easy modification
- Learning by doing - Understanding comes from changing things and observing results
What’s Next
In Unit 4, we’ll design custom graphics using the C64’s character set. The game will start looking professional.
What Changed
| 1 | 1 | ; ============================================================================ | |
| 2 | - | ; SID SYMPHONY - Unit 2: Notes Appear | |
| 2 | + | ; SID SYMPHONY - Unit 3: Making It Your Own | |
| 3 | 3 | ; ============================================================================ | |
| 4 | - | ; Notes scroll from right to left across three tracks. When they reach the | |
| 5 | - | ; hit zone, press the matching key to play the sound. A simple test pattern | |
| 6 | - | ; loops continuously so you can practice timing. | |
| 4 | + | ; Customise the game to make it yours. Change the SID voices, pick your own | |
| 5 | + | ; colours, experiment with waveforms and ADSR. Learn the internals by | |
| 6 | + | ; modifying them and hearing/seeing the results. | |
| 7 | 7 | ; | |
| 8 | 8 | ; Controls: Z = Track 1 (high), X = Track 2 (mid), C = Track 3 (low) | |
| 9 | + | ; ============================================================================ | |
| 10 | + | | |
| 11 | + | ; ============================================================================ | |
| 12 | + | ; CUSTOMISATION SECTION - Change these values and reassemble! | |
| 13 | + | ; ============================================================================ | |
| 14 | + | | |
| 15 | + | ; --- SID Voice Settings --- | |
| 16 | + | ; Waveforms: $11=triangle, $21=sawtooth, $41=pulse, $81=noise | |
| 17 | + | VOICE1_WAVE = $21 ; Sawtooth - bright, buzzy | |
| 18 | + | VOICE2_WAVE = $41 ; Pulse - hollow, reedy | |
| 19 | + | VOICE3_WAVE = $11 ; Triangle - soft, mellow | |
| 20 | + | | |
| 21 | + | ; Frequencies (higher = higher pitch) | |
| 22 | + | ; Common notes: $07=C3, $0E=C4, $1C=C5, $38=C6 | |
| 23 | + | VOICE1_FREQ = $1C ; High pitch (C5) | |
| 24 | + | VOICE2_FREQ = $0E ; Mid pitch (C4) | |
| 25 | + | VOICE3_FREQ = $07 ; Low pitch (C3) | |
| 26 | + | | |
| 27 | + | ; ADSR - Attack/Decay/Sustain/Release | |
| 28 | + | ; Attack: 0-15 (0=2ms, 15=8s) Decay: 0-15 (0=6ms, 15=24s) | |
| 29 | + | ; Sustain: 0-15 (volume level) Release: 0-15 (0=6ms, 15=24s) | |
| 30 | + | VOICE_AD = $09 ; Attack=0 (instant), Decay=9 (medium) | |
| 31 | + | VOICE_SR = $00 ; Sustain=0 (none), Release=0 (instant) | |
| 32 | + | | |
| 33 | + | ; Pulse width (only affects pulse wave, $41) | |
| 34 | + | PULSE_WIDTH = $08 ; $08 = 50% duty cycle (square wave) | |
| 35 | + | | |
| 36 | + | ; --- Visual Settings --- | |
| 37 | + | ; Colours: 0=black, 1=white, 2=red, 3=cyan, 4=purple, 5=green | |
| 38 | + | ; 6=blue, 7=yellow, 8=orange, 9=brown, 10=light red | |
| 39 | + | ; 11=dark grey, 12=grey, 13=light green, 14=light blue, 15=light grey | |
| 40 | + | | |
| 41 | + | BORDER_COL = 6 ; Blue border | |
| 42 | + | BG_COL = 0 ; Black background | |
| 43 | + | | |
| 44 | + | TRACK1_NOTE_COL = 10 ; Light red notes on track 1 | |
| 45 | + | TRACK2_NOTE_COL = 13 ; Light green notes on track 2 | |
| 46 | + | TRACK3_NOTE_COL = 14 ; Light blue notes on track 3 | |
| 47 | + | | |
| 48 | + | TRACK_LINE_COL = 11 ; Dark grey track lines | |
| 49 | + | HIT_ZONE_COL = 7 ; Yellow hit zone | |
| 50 | + | | |
| 51 | + | ; Track flash colours (when key pressed) | |
| 52 | + | FLASH1_COL = 2 ; Red flash for track 1 | |
| 53 | + | FLASH2_COL = 5 ; Green flash for track 2 | |
| 54 | + | FLASH3_COL = 6 ; Blue flash for track 3 | |
| 55 | + | | |
| 56 | + | ; ============================================================================ | |
| 57 | + | ; END OF CUSTOMISATION - Code below uses the settings above | |
| 9 | 58 | ; ============================================================================ | |
| 10 | 59 | | |
| 11 | 60 | ; ---------------------------------------------------------------------------- | |
| 12 | 61 | ; Memory Addresses | |
| 13 | 62 | ; ---------------------------------------------------------------------------- | |
| 14 | 63 | | |
| 15 | - | SCREEN = $0400 ; Screen memory base | |
| 16 | - | COLRAM = $D800 ; Colour RAM base | |
| 17 | - | BORDER = $D020 ; Border colour | |
| 18 | - | BGCOL = $D021 ; Background colour | |
| 64 | + | SCREEN = $0400 | |
| 65 | + | COLRAM = $D800 | |
| 66 | + | BORDER = $D020 | |
| 67 | + | BGCOL = $D021 | |
| 19 | 68 | | |
| 20 | 69 | ; SID registers | |
| 21 | - | SID = $D400 ; SID base address | |
| 22 | - | SID_V1_FREQ_LO = $D400 ; Voice 1 frequency low | |
| 23 | - | SID_V1_FREQ_HI = $D401 ; Voice 1 frequency high | |
| 24 | - | SID_V1_PWLO = $D402 ; Voice 1 pulse width low | |
| 25 | - | SID_V1_PWHI = $D403 ; Voice 1 pulse width high | |
| 26 | - | SID_V1_CTRL = $D404 ; Voice 1 control register | |
| 27 | - | SID_V1_AD = $D405 ; Voice 1 attack/decay | |
| 28 | - | SID_V1_SR = $D406 ; Voice 1 sustain/release | |
| 70 | + | SID = $D400 | |
| 71 | + | SID_V1_FREQ_LO = $D400 | |
| 72 | + | SID_V1_FREQ_HI = $D401 | |
| 73 | + | SID_V1_PWLO = $D402 | |
| 74 | + | SID_V1_PWHI = $D403 | |
| 75 | + | SID_V1_CTRL = $D404 | |
| 76 | + | SID_V1_AD = $D405 | |
| 77 | + | SID_V1_SR = $D406 | |
| 29 | 78 | | |
| 30 | - | SID_V2_FREQ_LO = $D407 ; Voice 2 frequency low | |
| 31 | - | SID_V2_FREQ_HI = $D408 ; Voice 2 frequency high | |
| 32 | - | SID_V2_PWLO = $D409 ; Voice 2 pulse width low | |
| 33 | - | SID_V2_PWHI = $D40A ; Voice 2 pulse width high | |
| 34 | - | SID_V2_CTRL = $D40B ; Voice 2 control register | |
| 35 | - | SID_V2_AD = $D40C ; Voice 2 attack/decay | |
| 36 | - | SID_V2_SR = $D40D ; Voice 2 sustain/release | |
| 79 | + | SID_V2_FREQ_LO = $D407 | |
| 80 | + | SID_V2_FREQ_HI = $D408 | |
| 81 | + | SID_V2_PWLO = $D409 | |
| 82 | + | SID_V2_PWHI = $D40A | |
| 83 | + | SID_V2_CTRL = $D40B | |
| 84 | + | SID_V2_AD = $D40C | |
| 85 | + | SID_V2_SR = $D40D | |
| 37 | 86 | | |
| 38 | - | SID_V3_FREQ_LO = $D40E ; Voice 3 frequency low | |
| 39 | - | SID_V3_FREQ_HI = $D40F ; Voice 3 frequency high | |
| 40 | - | SID_V3_PWLO = $D410 ; Voice 3 pulse width low | |
| 41 | - | SID_V3_PWHI = $D411 ; Voice 3 pulse width high | |
| 42 | - | SID_V3_CTRL = $D412 ; Voice 3 control register | |
| 43 | - | SID_V3_AD = $D413 ; Voice 3 attack/decay | |
| 44 | - | SID_V3_SR = $D414 ; Voice 3 sustain/release | |
| 87 | + | SID_V3_FREQ_LO = $D40E | |
| 88 | + | SID_V3_FREQ_HI = $D40F | |
| 89 | + | SID_V3_PWLO = $D410 | |
| 90 | + | SID_V3_PWHI = $D411 | |
| 91 | + | SID_V3_CTRL = $D412 | |
| 92 | + | SID_V3_AD = $D413 | |
| 93 | + | SID_V3_SR = $D414 | |
| 45 | 94 | | |
| 46 | - | SID_FLTLO = $D415 ; Filter cutoff low | |
| 47 | - | SID_FLTHI = $D416 ; Filter cutoff high | |
| 48 | - | SID_FLTCTRL = $D417 ; Filter control | |
| 49 | - | SID_VOLUME = $D418 ; Volume and filter mode | |
| 95 | + | SID_VOLUME = $D418 | |
| 50 | 96 | | |
| 51 | 97 | ; CIA keyboard | |
| 52 | - | CIA1_PRA = $DC00 ; CIA1 Port A (keyboard column) | |
| 53 | - | CIA1_PRB = $DC01 ; CIA1 Port B (keyboard row) | |
| 54 | - | | |
| 55 | - | ; Colours | |
| 56 | - | BLACK = 0 | |
| 57 | - | WHITE = 1 | |
| 58 | - | RED = 2 | |
| 59 | - | CYAN = 3 | |
| 60 | - | PURPLE = 4 | |
| 61 | - | GREEN = 5 | |
| 62 | - | BLUE = 6 | |
| 63 | - | YELLOW = 7 | |
| 64 | - | ORANGE = 8 | |
| 65 | - | BROWN = 9 | |
| 66 | - | LIGHT_RED = 10 | |
| 67 | - | DARK_GREY = 11 | |
| 68 | - | GREY = 12 | |
| 69 | - | LIGHT_GREEN = 13 | |
| 70 | - | LIGHT_BLUE = 14 | |
| 71 | - | LIGHT_GREY = 15 | |
| 98 | + | CIA1_PRA = $DC00 | |
| 99 | + | CIA1_PRB = $DC01 | |
| 72 | 100 | | |
| 73 | - | ; Track positions (row on screen) | |
| 74 | - | TRACK1_ROW = 8 ; High voice track | |
| 75 | - | TRACK2_ROW = 12 ; Mid voice track | |
| 76 | - | TRACK3_ROW = 16 ; Low voice track | |
| 101 | + | ; Track positions | |
| 102 | + | TRACK1_ROW = 8 | |
| 103 | + | TRACK2_ROW = 12 | |
| 104 | + | TRACK3_ROW = 16 | |
| 77 | 105 | | |
| 78 | 106 | ; Hit zone column | |
| 79 | - | HIT_ZONE_COL = 3 ; Where notes need to be hit | |
| 107 | + | HIT_ZONE_COLUMN = 3 | |
| 80 | 108 | | |
| 81 | 109 | ; Note settings | |
| 82 | - | NOTE_CHAR = $57 ; Character for note (filled dot/ball) | |
| 83 | - | TRACK_CHAR = $2D ; Minus character for track line | |
| 84 | - | MAX_NOTES = 8 ; Maximum notes on screen at once | |
| 85 | - | NOTE_SPAWN_COL = 37 ; Where notes appear (right side) | |
| 110 | + | NOTE_CHAR = $57 ; Filled dot character | |
| 111 | + | TRACK_CHAR = $2D ; Minus character | |
| 112 | + | MAX_NOTES = 8 | |
| 113 | + | NOTE_SPAWN_COL = 37 | |
| 86 | 114 | | |
| 87 | 115 | ; Timing | |
| 88 | - | FRAMES_PER_BEAT = 25 ; ~120 BPM at 50Hz (PAL) | |
| 116 | + | FRAMES_PER_BEAT = 25 | |
| 89 | 117 | | |
| 90 | - | ; Zero page pointers | |
| 91 | - | ZP_PTR = $FB ; General purpose pointer | |
| 118 | + | ; Zero page | |
| 119 | + | ZP_PTR = $FB | |
| 92 | 120 | ZP_PTR_HI = $FC | |
| 93 | - | | |
| 94 | - | ; ---------------------------------------------------------------------------- | |
| 95 | - | ; Variables (in low memory) | |
| 96 | - | ; ---------------------------------------------------------------------------- | |
| 97 | 121 | | |
| 98 | - | frame_count = $02 ; Frame counter (0-255) | |
| 99 | - | beat_count = $03 ; Current beat in song (0-255) | |
| 100 | - | song_pos = $04 ; Position in song data (word) | |
| 122 | + | ; Variables | |
| 123 | + | frame_count = $02 | |
| 124 | + | beat_count = $03 | |
| 125 | + | song_pos = $04 | |
| 101 | 126 | song_pos_hi = $05 | |
| 102 | - | temp_track = $06 ; Temporary storage for track number | |
| 127 | + | temp_track = $06 | |
| 103 | 128 | | |
| 104 | 129 | ; ---------------------------------------------------------------------------- | |
| 105 | - | ; BASIC Stub - SYS 2064 | |
| 130 | + | ; BASIC Stub | |
| 106 | 131 | ; ---------------------------------------------------------------------------- | |
| 107 | 132 | | |
| 108 | 133 | * = $0801 | |
| 109 | 134 | | |
| 110 | - | !byte $0C, $08 ; Pointer to next line | |
| 111 | - | !byte $0A, $00 ; Line number 10 | |
| 112 | - | !byte $9E ; SYS token | |
| 113 | - | !text "2064" ; Address | |
| 114 | - | !byte $00 ; End of line | |
| 115 | - | !byte $00, $00 ; End of program | |
| 135 | + | !byte $0C, $08 | |
| 136 | + | !byte $0A, $00 | |
| 137 | + | !byte $9E | |
| 138 | + | !text "2064" | |
| 139 | + | !byte $00 | |
| 140 | + | !byte $00, $00 | |
| 116 | 141 | | |
| 117 | 142 | ; ---------------------------------------------------------------------------- | |
| 118 | 143 | ; Main Program | |
| ... | |||
| 121 | 146 | * = $0810 | |
| 122 | 147 | | |
| 123 | 148 | start: | |
| 124 | - | jsr init_screen ; Set up the display | |
| 125 | - | jsr init_sid ; Configure SID chip | |
| 126 | - | jsr init_notes ; Clear note arrays | |
| 149 | + | jsr init_screen | |
| 150 | + | jsr init_sid | |
| 151 | + | jsr init_notes | |
| 127 | 152 | | |
| 128 | - | ; Initialize song position | |
| 129 | 153 | lda #<song_data | |
| 130 | 154 | sta song_pos | |
| 131 | 155 | lda #>song_data | |
| 132 | 156 | sta song_pos_hi | |
| 133 | 157 | | |
| 134 | - | ; Initialize counters | |
| 135 | 158 | lda #0 | |
| 136 | 159 | sta frame_count | |
| 137 | 160 | sta beat_count | |
| 138 | 161 | | |
| 139 | 162 | main_loop: | |
| 140 | - | ; Wait for raster (smooth timing) | |
| 141 | 163 | lda #$FF | |
| 142 | 164 | wait_raster: | |
| 143 | 165 | cmp $D012 | |
| 144 | 166 | bne wait_raster | |
| 145 | 167 | | |
| 146 | - | ; Update frame counter | |
| 147 | 168 | inc frame_count | |
| 148 | 169 | lda frame_count | |
| 149 | 170 | cmp #FRAMES_PER_BEAT | |
| 150 | 171 | bcc no_new_beat | |
| 151 | 172 | | |
| 152 | - | ; New beat! | |
| 153 | 173 | lda #0 | |
| 154 | 174 | sta frame_count | |
| 155 | 175 | jsr check_spawn_note | |
| 156 | 176 | inc beat_count | |
| 157 | 177 | | |
| 158 | 178 | no_new_beat: | |
| 159 | - | ; Move all notes left | |
| 160 | 179 | jsr update_notes | |
| 161 | - | | |
| 162 | - | ; Reset track colours to default | |
| 163 | 180 | jsr reset_track_colours | |
| 164 | - | | |
| 165 | - | ; Check keyboard and play sounds | |
| 166 | 181 | jsr check_keys | |
| 167 | 182 | | |
| 168 | 183 | jmp main_loop | |
| ... | |||
| 184 | 199 | ; ---------------------------------------------------------------------------- | |
| 185 | 200 | ; Check Spawn Note | |
| 186 | 201 | ; ---------------------------------------------------------------------------- | |
| 187 | - | ; Song data is in beat order. Process all entries matching current beat, | |
| 188 | - | ; then return. Entries are (beat, track) pairs ending with $FF. | |
| 189 | 202 | | |
| 190 | 203 | check_spawn_note: | |
| 191 | 204 | ldy #0 | |
| 192 | 205 | | |
| 193 | 206 | spawn_check_loop: | |
| 194 | - | ; Read beat number for current entry | |
| 195 | 207 | lda (song_pos),y | |
| 196 | 208 | cmp #$FF | |
| 197 | 209 | beq spawn_restart_song | |
| 198 | 210 | | |
| 199 | - | ; If entry beat > current beat, we're done for this beat | |
| 200 | 211 | cmp beat_count | |
| 201 | - | beq spawn_match ; Equal - spawn this note | |
| 202 | - | bcs spawn_done ; Greater - done for now (entries are ordered) | |
| 212 | + | beq spawn_match | |
| 213 | + | bcs spawn_done | |
| 203 | 214 | | |
| 204 | - | ; Entry beat < current beat - shouldn't happen, but skip it | |
| 205 | 215 | jmp spawn_advance | |
| 206 | 216 | | |
| 207 | 217 | spawn_match: | |
| 208 | - | ; Get track number (next byte) | |
| 209 | 218 | iny | |
| 210 | 219 | lda (song_pos),y | |
| 211 | 220 | jsr spawn_note | |
| 212 | - | dey ; Reset Y for next iteration | |
| 221 | + | dey | |
| 213 | 222 | | |
| 214 | 223 | spawn_advance: | |
| 215 | - | ; Move song_pos forward by 2 bytes | |
| 216 | 224 | lda song_pos | |
| 217 | 225 | clc | |
| 218 | 226 | adc #2 | |
| ... | |||
| 237 | 245 | ; ---------------------------------------------------------------------------- | |
| 238 | 246 | ; Spawn Note | |
| 239 | 247 | ; ---------------------------------------------------------------------------- | |
| 240 | - | ; Input: A = track number (1-3) | |
| 241 | 248 | | |
| 242 | 249 | spawn_note: | |
| 243 | 250 | sta temp_track | |
| ... | |||
| 249 | 256 | inx | |
| 250 | 257 | cpx #MAX_NOTES | |
| 251 | 258 | bne spawn_find_slot | |
| 252 | - | rts ; No empty slot | |
| 259 | + | rts | |
| 253 | 260 | | |
| 254 | 261 | spawn_found_slot: | |
| 255 | 262 | lda temp_track | |
| ... | |||
| 293 | 300 | ; ---------------------------------------------------------------------------- | |
| 294 | 301 | ; Draw Note | |
| 295 | 302 | ; ---------------------------------------------------------------------------- | |
| 296 | - | ; Input: X = note index | |
| 297 | 303 | | |
| 298 | 304 | draw_note: | |
| 299 | 305 | lda note_track,x | |
| ... | |||
| 325 | 331 | lda #>(COLRAM + TRACK1_ROW * 40) | |
| 326 | 332 | adc #0 | |
| 327 | 333 | sta ZP_PTR_HI | |
| 328 | - | lda #LIGHT_RED | |
| 334 | + | lda #TRACK1_NOTE_COL | |
| 329 | 335 | sta (ZP_PTR),y | |
| 330 | 336 | rts | |
| 331 | 337 | | |
| ... | |||
| 349 | 355 | lda #>(COLRAM + TRACK2_ROW * 40) | |
| 350 | 356 | adc #0 | |
| 351 | 357 | sta ZP_PTR_HI | |
| 352 | - | lda #LIGHT_GREEN | |
| 358 | + | lda #TRACK2_NOTE_COL | |
| 353 | 359 | sta (ZP_PTR),y | |
| 354 | 360 | rts | |
| 355 | 361 | | |
| ... | |||
| 373 | 379 | lda #>(COLRAM + TRACK3_ROW * 40) | |
| 374 | 380 | adc #0 | |
| 375 | 381 | sta ZP_PTR_HI | |
| 376 | - | lda #LIGHT_BLUE | |
| 382 | + | lda #TRACK3_NOTE_COL | |
| 377 | 383 | sta (ZP_PTR),y | |
| 378 | 384 | rts | |
| 379 | 385 | | |
| 380 | 386 | ; ---------------------------------------------------------------------------- | |
| 381 | 387 | ; Erase Note | |
| 382 | 388 | ; ---------------------------------------------------------------------------- | |
| 383 | - | ; Input: X = note index | |
| 384 | 389 | | |
| 385 | 390 | erase_note: | |
| 386 | 391 | lda note_track,x | |
| ... | |||
| 412 | 417 | lda #>(COLRAM + TRACK1_ROW * 40) | |
| 413 | 418 | adc #0 | |
| 414 | 419 | sta ZP_PTR_HI | |
| 415 | - | lda #GREY | |
| 420 | + | lda #TRACK_LINE_COL | |
| 416 | 421 | sta (ZP_PTR),y | |
| 417 | 422 | rts | |
| 418 | 423 | | |
| ... | |||
| 436 | 441 | lda #>(COLRAM + TRACK2_ROW * 40) | |
| 437 | 442 | adc #0 | |
| 438 | 443 | sta ZP_PTR_HI | |
| 439 | - | lda #GREY | |
| 444 | + | lda #TRACK_LINE_COL | |
| 440 | 445 | sta (ZP_PTR),y | |
| 441 | 446 | rts | |
| 442 | 447 | | |
| ... | |||
| 460 | 465 | lda #>(COLRAM + TRACK3_ROW * 40) | |
| 461 | 466 | adc #0 | |
| 462 | 467 | sta ZP_PTR_HI | |
| 463 | - | lda #GREY | |
| 468 | + | lda #TRACK_LINE_COL | |
| 464 | 469 | sta (ZP_PTR),y | |
| 465 | 470 | rts | |
| 466 | 471 | | |
| ... | |||
| 469 | 474 | ; ---------------------------------------------------------------------------- | |
| 470 | 475 | | |
| 471 | 476 | init_screen: | |
| 472 | - | lda #BLACK | |
| 477 | + | lda #BORDER_COL | |
| 473 | 478 | sta BORDER | |
| 479 | + | lda #BG_COL | |
| 474 | 480 | sta BGCOL | |
| 475 | 481 | | |
| 476 | 482 | ldx #0 | |
| ... | |||
| 484 | 490 | bne clr_screen | |
| 485 | 491 | | |
| 486 | 492 | ldx #0 | |
| 487 | - | lda #GREY | |
| 493 | + | lda #TRACK_LINE_COL | |
| 488 | 494 | clr_colour: | |
| 489 | 495 | sta COLRAM,x | |
| 490 | 496 | sta COLRAM+$100,x | |
| ... | |||
| 533 | 539 | ; ---------------------------------------------------------------------------- | |
| 534 | 540 | | |
| 535 | 541 | draw_hit_zones: | |
| 536 | - | lda #$7D ; Pipe character | |
| 542 | + | lda #$7D | |
| 537 | 543 | | |
| 538 | - | sta SCREEN + (TRACK1_ROW * 40) + HIT_ZONE_COL | |
| 539 | - | sta SCREEN + ((TRACK1_ROW-1) * 40) + HIT_ZONE_COL | |
| 540 | - | sta SCREEN + ((TRACK1_ROW+1) * 40) + HIT_ZONE_COL | |
| 544 | + | sta SCREEN + (TRACK1_ROW * 40) + HIT_ZONE_COLUMN | |
| 545 | + | sta SCREEN + ((TRACK1_ROW-1) * 40) + HIT_ZONE_COLUMN | |
| 546 | + | sta SCREEN + ((TRACK1_ROW+1) * 40) + HIT_ZONE_COLUMN | |
| 541 | 547 | | |
| 542 | - | sta SCREEN + (TRACK2_ROW * 40) + HIT_ZONE_COL | |
| 543 | - | sta SCREEN + ((TRACK2_ROW-1) * 40) + HIT_ZONE_COL | |
| 544 | - | sta SCREEN + ((TRACK2_ROW+1) * 40) + HIT_ZONE_COL | |
| 548 | + | sta SCREEN + (TRACK2_ROW * 40) + HIT_ZONE_COLUMN | |
| 549 | + | sta SCREEN + ((TRACK2_ROW-1) * 40) + HIT_ZONE_COLUMN | |
| 550 | + | sta SCREEN + ((TRACK2_ROW+1) * 40) + HIT_ZONE_COLUMN | |
| 545 | 551 | | |
| 546 | - | sta SCREEN + (TRACK3_ROW * 40) + HIT_ZONE_COL | |
| 547 | - | sta SCREEN + ((TRACK3_ROW-1) * 40) + HIT_ZONE_COL | |
| 548 | - | sta SCREEN + ((TRACK3_ROW+1) * 40) + HIT_ZONE_COL | |
| 552 | + | sta SCREEN + (TRACK3_ROW * 40) + HIT_ZONE_COLUMN | |
| 553 | + | sta SCREEN + ((TRACK3_ROW-1) * 40) + HIT_ZONE_COLUMN | |
| 554 | + | sta SCREEN + ((TRACK3_ROW+1) * 40) + HIT_ZONE_COLUMN | |
| 549 | 555 | | |
| 550 | - | lda #YELLOW | |
| 551 | - | sta COLRAM + (TRACK1_ROW * 40) + HIT_ZONE_COL | |
| 552 | - | sta COLRAM + ((TRACK1_ROW-1) * 40) + HIT_ZONE_COL | |
| 553 | - | sta COLRAM + ((TRACK1_ROW+1) * 40) + HIT_ZONE_COL | |
| 556 | + | lda #HIT_ZONE_COL | |
| 557 | + | sta COLRAM + (TRACK1_ROW * 40) + HIT_ZONE_COLUMN | |
| 558 | + | sta COLRAM + ((TRACK1_ROW-1) * 40) + HIT_ZONE_COLUMN | |
| 559 | + | sta COLRAM + ((TRACK1_ROW+1) * 40) + HIT_ZONE_COLUMN | |
| 554 | 560 | | |
| 555 | - | sta COLRAM + (TRACK2_ROW * 40) + HIT_ZONE_COL | |
| 556 | - | sta COLRAM + ((TRACK2_ROW-1) * 40) + HIT_ZONE_COL | |
| 557 | - | sta COLRAM + ((TRACK2_ROW+1) * 40) + HIT_ZONE_COL | |
| 561 | + | sta COLRAM + (TRACK2_ROW * 40) + HIT_ZONE_COLUMN | |
| 562 | + | sta COLRAM + ((TRACK2_ROW-1) * 40) + HIT_ZONE_COLUMN | |
| 563 | + | sta COLRAM + ((TRACK2_ROW+1) * 40) + HIT_ZONE_COLUMN | |
| 558 | 564 | | |
| 559 | - | sta COLRAM + (TRACK3_ROW * 40) + HIT_ZONE_COL | |
| 560 | - | sta COLRAM + ((TRACK3_ROW-1) * 40) + HIT_ZONE_COL | |
| 561 | - | sta COLRAM + ((TRACK3_ROW+1) * 40) + HIT_ZONE_COL | |
| 565 | + | sta COLRAM + (TRACK3_ROW * 40) + HIT_ZONE_COLUMN | |
| 566 | + | sta COLRAM + ((TRACK3_ROW-1) * 40) + HIT_ZONE_COLUMN | |
| 567 | + | sta COLRAM + ((TRACK3_ROW+1) * 40) + HIT_ZONE_COLUMN | |
| 562 | 568 | | |
| 563 | 569 | rts | |
| 564 | 570 | | |
| ... | |||
| 572 | 578 | lda title_text,x | |
| 573 | 579 | beq draw_title_done | |
| 574 | 580 | sta SCREEN + 13,x | |
| 575 | - | lda #WHITE | |
| 581 | + | lda #1 ; White | |
| 576 | 582 | sta COLRAM + 13,x | |
| 577 | 583 | inx | |
| 578 | 584 | bne draw_title | |
| ... | |||
| 580 | 586 | | |
| 581 | 587 | lda #$1A ; Z | |
| 582 | 588 | sta SCREEN + (TRACK1_ROW * 40) | |
| 583 | - | lda #LIGHT_RED | |
| 589 | + | lda #TRACK1_NOTE_COL | |
| 584 | 590 | sta COLRAM + (TRACK1_ROW * 40) | |
| 585 | 591 | | |
| 586 | 592 | lda #$18 ; X | |
| 587 | 593 | sta SCREEN + (TRACK2_ROW * 40) | |
| 588 | - | lda #LIGHT_GREEN | |
| 594 | + | lda #TRACK2_NOTE_COL | |
| 589 | 595 | sta COLRAM + (TRACK2_ROW * 40) | |
| 590 | 596 | | |
| 591 | 597 | lda #$03 ; C | |
| 592 | 598 | sta SCREEN + (TRACK3_ROW * 40) | |
| 593 | - | lda #LIGHT_BLUE | |
| 599 | + | lda #TRACK3_NOTE_COL | |
| 594 | 600 | sta COLRAM + (TRACK3_ROW * 40) | |
| 595 | 601 | | |
| 596 | 602 | ldx #0 | |
| 597 | 603 | draw_instr: | |
| 598 | 604 | lda instr_text,x | |
| 599 | 605 | beq draw_instr_done | |
| 600 | - | sta SCREEN + (23 * 40) + 3,x | |
| 601 | - | lda #GREY | |
| 602 | - | sta COLRAM + (23 * 40) + 3,x | |
| 606 | + | sta SCREEN + (23 * 40) + 8,x | |
| 607 | + | lda #TRACK_LINE_COL | |
| 608 | + | sta COLRAM + (23 * 40) + 8,x | |
| 603 | 609 | inx | |
| 604 | 610 | bne draw_instr | |
| 605 | 611 | draw_instr_done: | |
| ... | |||
| 611 | 617 | !byte 0 | |
| 612 | 618 | | |
| 613 | 619 | instr_text: | |
| 614 | - | !scr "hit notes when they reach |" | |
| 620 | + | !scr "customise me!" | |
| 615 | 621 | !byte 0 | |
| 616 | 622 | | |
| 617 | 623 | ; ---------------------------------------------------------------------------- | |
| 618 | - | ; Initialize SID | |
| 624 | + | ; Initialize SID - Uses customisation constants | |
| 619 | 625 | ; ---------------------------------------------------------------------------- | |
| 620 | 626 | | |
| 621 | 627 | init_sid: | |
| ... | |||
| 629 | 635 | lda #$0F | |
| 630 | 636 | sta SID_VOLUME | |
| 631 | 637 | | |
| 632 | - | ; Voice 1 - High pitch, sawtooth | |
| 638 | + | ; Voice 1 - uses VOICE1_* constants | |
| 633 | 639 | lda #$00 | |
| 634 | 640 | sta SID_V1_FREQ_LO | |
| 635 | - | lda #$1C | |
| 641 | + | lda #VOICE1_FREQ | |
| 636 | 642 | sta SID_V1_FREQ_HI | |
| 637 | - | lda #$09 | |
| 643 | + | lda #PULSE_WIDTH | |
| 644 | + | sta SID_V1_PWHI | |
| 645 | + | lda #VOICE_AD | |
| 638 | 646 | sta SID_V1_AD | |
| 639 | - | lda #$00 | |
| 647 | + | lda #VOICE_SR | |
| 640 | 648 | sta SID_V1_SR | |
| 641 | 649 | | |
| 642 | - | ; Voice 2 - Mid pitch, pulse | |
| 650 | + | ; Voice 2 - uses VOICE2_* constants | |
| 643 | 651 | lda #$00 | |
| 644 | 652 | sta SID_V2_FREQ_LO | |
| 645 | - | lda #$0E | |
| 653 | + | lda #VOICE2_FREQ | |
| 646 | 654 | sta SID_V2_FREQ_HI | |
| 647 | - | lda #$08 | |
| 655 | + | lda #PULSE_WIDTH | |
| 648 | 656 | sta SID_V2_PWHI | |
| 649 | - | lda #$09 | |
| 657 | + | lda #VOICE_AD | |
| 650 | 658 | sta SID_V2_AD | |
| 651 | - | lda #$00 | |
| 659 | + | lda #VOICE_SR | |
| 652 | 660 | sta SID_V2_SR | |
| 653 | 661 | | |
| 654 | - | ; Voice 3 - Low pitch, triangle | |
| 662 | + | ; Voice 3 - uses VOICE3_* constants | |
| 655 | 663 | lda #$00 | |
| 656 | 664 | sta SID_V3_FREQ_LO | |
| 657 | - | lda #$07 | |
| 665 | + | lda #VOICE3_FREQ | |
| 658 | 666 | sta SID_V3_FREQ_HI | |
| 659 | - | lda #$09 | |
| 667 | + | lda #PULSE_WIDTH | |
| 668 | + | sta SID_V3_PWHI | |
| 669 | + | lda #VOICE_AD | |
| 660 | 670 | sta SID_V3_AD | |
| 661 | - | lda #$00 | |
| 671 | + | lda #VOICE_SR | |
| 662 | 672 | sta SID_V3_SR | |
| 663 | 673 | | |
| 664 | 674 | rts | |
| ... | |||
| 669 | 679 | | |
| 670 | 680 | reset_track_colours: | |
| 671 | 681 | ldx #0 | |
| 672 | - | lda #GREY | |
| 682 | + | lda #TRACK_LINE_COL | |
| 673 | 683 | reset_t1: | |
| 674 | 684 | sta COLRAM + (TRACK1_ROW * 40),x | |
| 675 | 685 | inx | |
| ... | |||
| 691 | 701 | bne reset_t3 | |
| 692 | 702 | | |
| 693 | 703 | ; Restore key labels | |
| 694 | - | lda #LIGHT_RED | |
| 704 | + | lda #TRACK1_NOTE_COL | |
| 695 | 705 | sta COLRAM + (TRACK1_ROW * 40) | |
| 696 | - | lda #LIGHT_GREEN | |
| 706 | + | lda #TRACK2_NOTE_COL | |
| 697 | 707 | sta COLRAM + (TRACK2_ROW * 40) | |
| 698 | - | lda #LIGHT_BLUE | |
| 708 | + | lda #TRACK3_NOTE_COL | |
| 699 | 709 | sta COLRAM + (TRACK3_ROW * 40) | |
| 700 | 710 | | |
| 701 | 711 | ; Restore hit zone colours | |
| 702 | - | lda #YELLOW | |
| 703 | - | sta COLRAM + (TRACK1_ROW * 40) + HIT_ZONE_COL | |
| 704 | - | sta COLRAM + (TRACK2_ROW * 40) + HIT_ZONE_COL | |
| 705 | - | sta COLRAM + (TRACK3_ROW * 40) + HIT_ZONE_COL | |
| 712 | + | lda #HIT_ZONE_COL | |
| 713 | + | sta COLRAM + (TRACK1_ROW * 40) + HIT_ZONE_COLUMN | |
| 714 | + | sta COLRAM + (TRACK2_ROW * 40) + HIT_ZONE_COLUMN | |
| 715 | + | sta COLRAM + (TRACK3_ROW * 40) + HIT_ZONE_COLUMN | |
| 706 | 716 | | |
| 707 | - | ; Redraw note colours | |
| 708 | 717 | jsr redraw_all_notes | |
| 709 | 718 | | |
| 710 | 719 | rts | |
| ... | |||
| 762 | 771 | rts | |
| 763 | 772 | | |
| 764 | 773 | ; ---------------------------------------------------------------------------- | |
| 765 | - | ; Play Voices | |
| 774 | + | ; Play Voices - Uses customisation waveforms | |
| 766 | 775 | ; ---------------------------------------------------------------------------- | |
| 767 | 776 | | |
| 768 | 777 | play_voice1: | |
| 769 | - | lda #$21 | |
| 778 | + | lda #VOICE1_WAVE | |
| 779 | + | ora #$01 ; Add gate bit | |
| 770 | 780 | sta SID_V1_CTRL | |
| 771 | 781 | rts | |
| 772 | 782 | | |
| 773 | 783 | play_voice2: | |
| 774 | - | lda #$41 | |
| 784 | + | lda #VOICE2_WAVE | |
| 785 | + | ora #$01 | |
| 775 | 786 | sta SID_V2_CTRL | |
| 776 | 787 | rts | |
| 777 | 788 | | |
| 778 | 789 | play_voice3: | |
| 779 | - | lda #$11 | |
| 790 | + | lda #VOICE3_WAVE | |
| 791 | + | ora #$01 | |
| 780 | 792 | sta SID_V3_CTRL | |
| 781 | 793 | rts | |
| 782 | 794 | | |
| 783 | 795 | ; ---------------------------------------------------------------------------- | |
| 784 | - | ; Flash Tracks | |
| 796 | + | ; Flash Tracks - Uses customisation colours | |
| 785 | 797 | ; ---------------------------------------------------------------------------- | |
| 786 | 798 | | |
| 787 | 799 | flash_track1: | |
| 788 | 800 | ldx #0 | |
| 789 | - | lda #RED | |
| 801 | + | lda #FLASH1_COL | |
| 790 | 802 | flash_t1_loop: | |
| 791 | 803 | sta COLRAM + (TRACK1_ROW * 40),x | |
| 792 | 804 | inx | |
| 793 | 805 | cpx #38 | |
| 794 | 806 | bne flash_t1_loop | |
| 795 | - | lda #WHITE | |
| 807 | + | lda #1 ; White label | |
| 796 | 808 | sta COLRAM + (TRACK1_ROW * 40) | |
| 797 | 809 | rts | |
| 798 | 810 | | |
| 799 | 811 | flash_track2: | |
| 800 | 812 | ldx #0 | |
| 801 | - | lda #GREEN | |
| 813 | + | lda #FLASH2_COL | |
| 802 | 814 | flash_t2_loop: | |
| 803 | 815 | sta COLRAM + (TRACK2_ROW * 40),x | |
| 804 | 816 | inx | |
| 805 | 817 | cpx #38 | |
| 806 | 818 | bne flash_t2_loop | |
| 807 | - | lda #WHITE | |
| 819 | + | lda #1 | |
| 808 | 820 | sta COLRAM + (TRACK2_ROW * 40) | |
| 809 | 821 | rts | |
| 810 | 822 | | |
| 811 | 823 | flash_track3: | |
| 812 | 824 | ldx #0 | |
| 813 | - | lda #BLUE | |
| 825 | + | lda #FLASH3_COL | |
| 814 | 826 | flash_t3_loop: | |
| 815 | 827 | sta COLRAM + (TRACK3_ROW * 40),x | |
| 816 | 828 | inx | |
| 817 | 829 | cpx #38 | |
| 818 | 830 | bne flash_t3_loop | |
| 819 | - | lda #WHITE | |
| 831 | + | lda #1 | |
| 820 | 832 | sta COLRAM + (TRACK3_ROW * 40) | |
| 821 | 833 | rts | |
| 822 | 834 | | |
| 823 | 835 | ; ---------------------------------------------------------------------------- | |
| 824 | 836 | ; Song Data | |
| 825 | 837 | ; ---------------------------------------------------------------------------- | |
| 826 | - | ; Format: beat, track (1-3) | |
| 827 | - | ; $FF marks end of song | |
| 828 | 838 | | |
| 829 | 839 | song_data: | |
| 830 | - | ; Simple test pattern - 4 bars at 120 BPM | |
| 831 | - | ; Bar 1 | |
| 832 | 840 | !byte 0, 1 | |
| 833 | 841 | !byte 2, 2 | |
| 834 | 842 | !byte 4, 3 | |
| 835 | 843 | !byte 6, 1 | |
| 836 | 844 | | |
| 837 | - | ; Bar 2 | |
| 838 | 845 | !byte 8, 2 | |
| 839 | 846 | !byte 10, 3 | |
| 840 | 847 | !byte 12, 1 | |
| 841 | 848 | !byte 14, 2 | |
| 842 | 849 | | |
| 843 | - | ; Bar 3 | |
| 844 | 850 | !byte 16, 3 | |
| 845 | 851 | !byte 18, 1 | |
| 846 | 852 | !byte 20, 2 | |
| 847 | 853 | !byte 22, 3 | |
| 848 | 854 | | |
| 849 | - | ; Bar 4 - faster pattern | |
| 850 | 855 | !byte 24, 1 | |
| 851 | 856 | !byte 25, 2 | |
| 852 | 857 | !byte 26, 3 | |
| ... | |||
| 854 | 859 | !byte 29, 2 | |
| 855 | 860 | !byte 30, 3 | |
| 856 | 861 | | |
| 857 | - | !byte $FF ; End marker | |
| 862 | + | !byte $FF | |
| 858 | 863 | | |
| 859 | 864 | ; ---------------------------------------------------------------------------- | |
| 860 | 865 | ; Note Arrays |