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

Making It Your Own

Customise the SID voices and visual style. Learn the internals by changing them.

5% of SID Symphony

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

Unit 3 Screenshot

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.

Customised colours and waveforms — same mechanics, different feel

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:

  1. Pick waveforms that complement each other
  2. Choose ADSR values that fit your waveforms
  3. Select colours that create the right mood
  4. 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

Unit 2 → Unit 3
+191-186
11 ; ============================================================================
2-; SID SYMPHONY - Unit 2: Notes Appear
2+; SID SYMPHONY - Unit 3: Making It Your Own
33 ; ============================================================================
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.
77 ;
88 ; 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
958 ; ============================================================================
1059
1160 ; ----------------------------------------------------------------------------
1261 ; Memory Addresses
1362 ; ----------------------------------------------------------------------------
1463
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
1968
2069 ; 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
2978
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
3786
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
4594
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
5096
5197 ; 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
72100
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
77105
78106 ; Hit zone column
79-HIT_ZONE_COL = 3 ; Where notes need to be hit
107+HIT_ZONE_COLUMN = 3
80108
81109 ; 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
86114
87115 ; Timing
88-FRAMES_PER_BEAT = 25 ; ~120 BPM at 50Hz (PAL)
116+FRAMES_PER_BEAT = 25
89117
90-; Zero page pointers
91-ZP_PTR = $FB ; General purpose pointer
118+; Zero page
119+ZP_PTR = $FB
92120 ZP_PTR_HI = $FC
93-
94-; ----------------------------------------------------------------------------
95-; Variables (in low memory)
96-; ----------------------------------------------------------------------------
97121
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
101126 song_pos_hi = $05
102-temp_track = $06 ; Temporary storage for track number
127+temp_track = $06
103128
104129 ; ----------------------------------------------------------------------------
105-; BASIC Stub - SYS 2064
130+; BASIC Stub
106131 ; ----------------------------------------------------------------------------
107132
108133 * = $0801
109134
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
116141
117142 ; ----------------------------------------------------------------------------
118143 ; Main Program
...
121146 * = $0810
122147
123148 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
127152
128- ; Initialize song position
129153 lda #<song_data
130154 sta song_pos
131155 lda #>song_data
132156 sta song_pos_hi
133157
134- ; Initialize counters
135158 lda #0
136159 sta frame_count
137160 sta beat_count
138161
139162 main_loop:
140- ; Wait for raster (smooth timing)
141163 lda #$FF
142164 wait_raster:
143165 cmp $D012
144166 bne wait_raster
145167
146- ; Update frame counter
147168 inc frame_count
148169 lda frame_count
149170 cmp #FRAMES_PER_BEAT
150171 bcc no_new_beat
151172
152- ; New beat!
153173 lda #0
154174 sta frame_count
155175 jsr check_spawn_note
156176 inc beat_count
157177
158178 no_new_beat:
159- ; Move all notes left
160179 jsr update_notes
161-
162- ; Reset track colours to default
163180 jsr reset_track_colours
164-
165- ; Check keyboard and play sounds
166181 jsr check_keys
167182
168183 jmp main_loop
...
184199 ; ----------------------------------------------------------------------------
185200 ; Check Spawn Note
186201 ; ----------------------------------------------------------------------------
187-; Song data is in beat order. Process all entries matching current beat,
188-; then return. Entries are (beat, track) pairs ending with $FF.
189202
190203 check_spawn_note:
191204 ldy #0
192205
193206 spawn_check_loop:
194- ; Read beat number for current entry
195207 lda (song_pos),y
196208 cmp #$FF
197209 beq spawn_restart_song
198210
199- ; If entry beat > current beat, we're done for this beat
200211 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
203214
204- ; Entry beat < current beat - shouldn't happen, but skip it
205215 jmp spawn_advance
206216
207217 spawn_match:
208- ; Get track number (next byte)
209218 iny
210219 lda (song_pos),y
211220 jsr spawn_note
212- dey ; Reset Y for next iteration
221+ dey
213222
214223 spawn_advance:
215- ; Move song_pos forward by 2 bytes
216224 lda song_pos
217225 clc
218226 adc #2
...
237245 ; ----------------------------------------------------------------------------
238246 ; Spawn Note
239247 ; ----------------------------------------------------------------------------
240-; Input: A = track number (1-3)
241248
242249 spawn_note:
243250 sta temp_track
...
249256 inx
250257 cpx #MAX_NOTES
251258 bne spawn_find_slot
252- rts ; No empty slot
259+ rts
253260
254261 spawn_found_slot:
255262 lda temp_track
...
293300 ; ----------------------------------------------------------------------------
294301 ; Draw Note
295302 ; ----------------------------------------------------------------------------
296-; Input: X = note index
297303
298304 draw_note:
299305 lda note_track,x
...
325331 lda #>(COLRAM + TRACK1_ROW * 40)
326332 adc #0
327333 sta ZP_PTR_HI
328- lda #LIGHT_RED
334+ lda #TRACK1_NOTE_COL
329335 sta (ZP_PTR),y
330336 rts
331337
...
349355 lda #>(COLRAM + TRACK2_ROW * 40)
350356 adc #0
351357 sta ZP_PTR_HI
352- lda #LIGHT_GREEN
358+ lda #TRACK2_NOTE_COL
353359 sta (ZP_PTR),y
354360 rts
355361
...
373379 lda #>(COLRAM + TRACK3_ROW * 40)
374380 adc #0
375381 sta ZP_PTR_HI
376- lda #LIGHT_BLUE
382+ lda #TRACK3_NOTE_COL
377383 sta (ZP_PTR),y
378384 rts
379385
380386 ; ----------------------------------------------------------------------------
381387 ; Erase Note
382388 ; ----------------------------------------------------------------------------
383-; Input: X = note index
384389
385390 erase_note:
386391 lda note_track,x
...
412417 lda #>(COLRAM + TRACK1_ROW * 40)
413418 adc #0
414419 sta ZP_PTR_HI
415- lda #GREY
420+ lda #TRACK_LINE_COL
416421 sta (ZP_PTR),y
417422 rts
418423
...
436441 lda #>(COLRAM + TRACK2_ROW * 40)
437442 adc #0
438443 sta ZP_PTR_HI
439- lda #GREY
444+ lda #TRACK_LINE_COL
440445 sta (ZP_PTR),y
441446 rts
442447
...
460465 lda #>(COLRAM + TRACK3_ROW * 40)
461466 adc #0
462467 sta ZP_PTR_HI
463- lda #GREY
468+ lda #TRACK_LINE_COL
464469 sta (ZP_PTR),y
465470 rts
466471
...
469474 ; ----------------------------------------------------------------------------
470475
471476 init_screen:
472- lda #BLACK
477+ lda #BORDER_COL
473478 sta BORDER
479+ lda #BG_COL
474480 sta BGCOL
475481
476482 ldx #0
...
484490 bne clr_screen
485491
486492 ldx #0
487- lda #GREY
493+ lda #TRACK_LINE_COL
488494 clr_colour:
489495 sta COLRAM,x
490496 sta COLRAM+$100,x
...
533539 ; ----------------------------------------------------------------------------
534540
535541 draw_hit_zones:
536- lda #$7D ; Pipe character
542+ lda #$7D
537543
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
541547
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
545551
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
549555
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
554560
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
558564
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
562568
563569 rts
564570
...
572578 lda title_text,x
573579 beq draw_title_done
574580 sta SCREEN + 13,x
575- lda #WHITE
581+ lda #1 ; White
576582 sta COLRAM + 13,x
577583 inx
578584 bne draw_title
...
580586
581587 lda #$1A ; Z
582588 sta SCREEN + (TRACK1_ROW * 40)
583- lda #LIGHT_RED
589+ lda #TRACK1_NOTE_COL
584590 sta COLRAM + (TRACK1_ROW * 40)
585591
586592 lda #$18 ; X
587593 sta SCREEN + (TRACK2_ROW * 40)
588- lda #LIGHT_GREEN
594+ lda #TRACK2_NOTE_COL
589595 sta COLRAM + (TRACK2_ROW * 40)
590596
591597 lda #$03 ; C
592598 sta SCREEN + (TRACK3_ROW * 40)
593- lda #LIGHT_BLUE
599+ lda #TRACK3_NOTE_COL
594600 sta COLRAM + (TRACK3_ROW * 40)
595601
596602 ldx #0
597603 draw_instr:
598604 lda instr_text,x
599605 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
603609 inx
604610 bne draw_instr
605611 draw_instr_done:
...
611617 !byte 0
612618
613619 instr_text:
614- !scr "hit notes when they reach |"
620+ !scr "customise me!"
615621 !byte 0
616622
617623 ; ----------------------------------------------------------------------------
618-; Initialize SID
624+; Initialize SID - Uses customisation constants
619625 ; ----------------------------------------------------------------------------
620626
621627 init_sid:
...
629635 lda #$0F
630636 sta SID_VOLUME
631637
632- ; Voice 1 - High pitch, sawtooth
638+ ; Voice 1 - uses VOICE1_* constants
633639 lda #$00
634640 sta SID_V1_FREQ_LO
635- lda #$1C
641+ lda #VOICE1_FREQ
636642 sta SID_V1_FREQ_HI
637- lda #$09
643+ lda #PULSE_WIDTH
644+ sta SID_V1_PWHI
645+ lda #VOICE_AD
638646 sta SID_V1_AD
639- lda #$00
647+ lda #VOICE_SR
640648 sta SID_V1_SR
641649
642- ; Voice 2 - Mid pitch, pulse
650+ ; Voice 2 - uses VOICE2_* constants
643651 lda #$00
644652 sta SID_V2_FREQ_LO
645- lda #$0E
653+ lda #VOICE2_FREQ
646654 sta SID_V2_FREQ_HI
647- lda #$08
655+ lda #PULSE_WIDTH
648656 sta SID_V2_PWHI
649- lda #$09
657+ lda #VOICE_AD
650658 sta SID_V2_AD
651- lda #$00
659+ lda #VOICE_SR
652660 sta SID_V2_SR
653661
654- ; Voice 3 - Low pitch, triangle
662+ ; Voice 3 - uses VOICE3_* constants
655663 lda #$00
656664 sta SID_V3_FREQ_LO
657- lda #$07
665+ lda #VOICE3_FREQ
658666 sta SID_V3_FREQ_HI
659- lda #$09
667+ lda #PULSE_WIDTH
668+ sta SID_V3_PWHI
669+ lda #VOICE_AD
660670 sta SID_V3_AD
661- lda #$00
671+ lda #VOICE_SR
662672 sta SID_V3_SR
663673
664674 rts
...
669679
670680 reset_track_colours:
671681 ldx #0
672- lda #GREY
682+ lda #TRACK_LINE_COL
673683 reset_t1:
674684 sta COLRAM + (TRACK1_ROW * 40),x
675685 inx
...
691701 bne reset_t3
692702
693703 ; Restore key labels
694- lda #LIGHT_RED
704+ lda #TRACK1_NOTE_COL
695705 sta COLRAM + (TRACK1_ROW * 40)
696- lda #LIGHT_GREEN
706+ lda #TRACK2_NOTE_COL
697707 sta COLRAM + (TRACK2_ROW * 40)
698- lda #LIGHT_BLUE
708+ lda #TRACK3_NOTE_COL
699709 sta COLRAM + (TRACK3_ROW * 40)
700710
701711 ; 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
706716
707- ; Redraw note colours
708717 jsr redraw_all_notes
709718
710719 rts
...
762771 rts
763772
764773 ; ----------------------------------------------------------------------------
765-; Play Voices
774+; Play Voices - Uses customisation waveforms
766775 ; ----------------------------------------------------------------------------
767776
768777 play_voice1:
769- lda #$21
778+ lda #VOICE1_WAVE
779+ ora #$01 ; Add gate bit
770780 sta SID_V1_CTRL
771781 rts
772782
773783 play_voice2:
774- lda #$41
784+ lda #VOICE2_WAVE
785+ ora #$01
775786 sta SID_V2_CTRL
776787 rts
777788
778789 play_voice3:
779- lda #$11
790+ lda #VOICE3_WAVE
791+ ora #$01
780792 sta SID_V3_CTRL
781793 rts
782794
783795 ; ----------------------------------------------------------------------------
784-; Flash Tracks
796+; Flash Tracks - Uses customisation colours
785797 ; ----------------------------------------------------------------------------
786798
787799 flash_track1:
788800 ldx #0
789- lda #RED
801+ lda #FLASH1_COL
790802 flash_t1_loop:
791803 sta COLRAM + (TRACK1_ROW * 40),x
792804 inx
793805 cpx #38
794806 bne flash_t1_loop
795- lda #WHITE
807+ lda #1 ; White label
796808 sta COLRAM + (TRACK1_ROW * 40)
797809 rts
798810
799811 flash_track2:
800812 ldx #0
801- lda #GREEN
813+ lda #FLASH2_COL
802814 flash_t2_loop:
803815 sta COLRAM + (TRACK2_ROW * 40),x
804816 inx
805817 cpx #38
806818 bne flash_t2_loop
807- lda #WHITE
819+ lda #1
808820 sta COLRAM + (TRACK2_ROW * 40)
809821 rts
810822
811823 flash_track3:
812824 ldx #0
813- lda #BLUE
825+ lda #FLASH3_COL
814826 flash_t3_loop:
815827 sta COLRAM + (TRACK3_ROW * 40),x
816828 inx
817829 cpx #38
818830 bne flash_t3_loop
819- lda #WHITE
831+ lda #1
820832 sta COLRAM + (TRACK3_ROW * 40)
821833 rts
822834
823835 ; ----------------------------------------------------------------------------
824836 ; Song Data
825837 ; ----------------------------------------------------------------------------
826-; Format: beat, track (1-3)
827-; $FF marks end of song
828838
829839 song_data:
830- ; Simple test pattern - 4 bars at 120 BPM
831- ; Bar 1
832840 !byte 0, 1
833841 !byte 2, 2
834842 !byte 4, 3
835843 !byte 6, 1
836844
837- ; Bar 2
838845 !byte 8, 2
839846 !byte 10, 3
840847 !byte 12, 1
841848 !byte 14, 2
842849
843- ; Bar 3
844850 !byte 16, 3
845851 !byte 18, 1
846852 !byte 20, 2
847853 !byte 22, 3
848854
849- ; Bar 4 - faster pattern
850855 !byte 24, 1
851856 !byte 25, 2
852857 !byte 26, 3
...
854859 !byte 29, 2
855860 !byte 30, 3
856861
857- !byte $FF ; End marker
862+ !byte $FF
858863
859864 ; ----------------------------------------------------------------------------
860865 ; Note Arrays