Custom Graphics
Design custom characters for notes, tracks, and hit zones. The game looks professional.
The game works. Now make it look like a real game.
This unit replaces the placeholder ASCII characters with custom graphics. You’ll learn how the C64’s character system works by designing your own characters - notes that look like arrows, tracks that look like clean lines, hit zones that stand out.
Run It
Assemble and run:
acme -f cbm -o symphony.prg symphony.asm

Compare this to the previous units. The tracks are now clean horizontal lines. The hit zone is a distinctive yellow double bar. Notes are chevrons pointing left - arrows showing the direction of travel. The game looks professional.
The C64 Character System
The C64 displays characters from an 8×8 pixel grid. There are 256 characters, each defined by 8 bytes (one byte per row). The default characters live in ROM at $D000, but ROM is read-only.
To create custom characters:
- Copy the ROM characters to RAM
- Modify the RAM copy
- Point the VIC-II chip to the RAM location
Copying the Character ROM
The character ROM shares address space with I/O chips. To read it, we temporarily remap memory:
; Copy Character ROM to RAM
; The C64's character ROM is read-only. To modify characters, we copy
; the ROM to RAM and point the VIC-II to our RAM copy.
CHARSET = $3000 ; Our custom charset location
copy_charset:
; Disable interrupts - ROM switching must be atomic
sei
; The C64's processor port at $01 controls memory mapping
; Bit 2 (CHAREN) = 0 makes character ROM visible at $D000
lda $01
pha ; Save current state
and #$FB ; Clear bit 2
sta $01 ; Character ROM now at $D000-$DFFF
; Copy all 256 characters (256 * 8 = 2048 bytes)
ldx #0
copy_loop:
lda $D000,x ; Copy 256 bytes at a time
sta CHARSET,x ; To our RAM location
lda $D100,x
sta CHARSET+$100,x
; ... (continues for all 8 pages)
inx
bne copy_loop
; Restore processor port
pla
sta $01
; Re-enable interrupts
cli
rts
The processor port at $01 controls what appears at certain addresses. When we clear bit 2, the character ROM becomes visible at $D000-$DFFF. We copy all 2KB to our RAM location at $3000.
Designing Characters
Each character is 8 bytes. Each bit in a byte is one pixel:
; Character Design - 8x8 Pixel Grids
; Each character is 8 bytes, one byte per row.
; Each bit is one pixel (1=foreground, 0=background).
; Character 128: Note (chevron pointing left)
; Design:
; ......XX = $06
; ....XXXX = $1E
; ..XXXXXX = $7E
; XXXXXXXX = $FE
; XXXXXXXX = $FE
; ..XXXXXX = $7E
; ....XXXX = $1E
; ......XX = $06
CHAR_NOTE = 128
lda #%00000110 ; Row 0: tip of arrow
sta CHARSET + (CHAR_NOTE * 8) + 0
lda #%00011110 ; Row 1: wider
sta CHARSET + (CHAR_NOTE * 8) + 1
lda #%01111110 ; Row 2: wider still
sta CHARSET + (CHAR_NOTE * 8) + 2
lda #%11111110 ; Row 3: full width (minus 1 pixel)
sta CHARSET + (CHAR_NOTE * 8) + 3
lda #%11111110 ; Row 4: same
sta CHARSET + (CHAR_NOTE * 8) + 4
lda #%01111110 ; Row 5: narrowing
sta CHARSET + (CHAR_NOTE * 8) + 5
lda #%00011110 ; Row 6: narrower
sta CHARSET + (CHAR_NOTE * 8) + 6
lda #%00000110 ; Row 7: tip
sta CHARSET + (CHAR_NOTE * 8) + 7
The note character is a chevron pointing left - an arrow showing “this note is moving toward the hit zone.” The design uses binary literals so you can see the pixel pattern directly in the code.
Try This: Design Your Own Note
; Filled circle note:
; ..XXXX.. = %00111100 = $3C
; .XXXXXX. = %01111110 = $7E
; XXXXXXXX = %11111111 = $FF
; XXXXXXXX = %11111111 = $FF
; XXXXXXXX = %11111111 = $FF
; XXXXXXXX = %11111111 = $FF
; .XXXXXX. = %01111110 = $7E
; ..XXXX.. = %00111100 = $3C
lda #$3C
sta CHARSET + (CHAR_NOTE * 8) + 0
lda #$7E
sta CHARSET + (CHAR_NOTE * 8) + 1
; ... etc
Try This: Diamond Note
; Diamond shape:
; ...XX... = %00011000 = $18
; ..XXXX.. = %00111100 = $3C
; .XXXXXX. = %01111110 = $7E
; XXXXXXXX = %11111111 = $FF
; XXXXXXXX = %11111111 = $FF
; .XXXXXX. = %01111110 = $7E
; ..XXXX.. = %00111100 = $3C
; ...XX... = %00011000 = $18
Pointing VIC-II to Custom Characters
The VIC-II chip needs to know where to find character data:
; VIC-II Character Memory Pointer ($D018)
;
; The VIC-II chip uses $D018 to find both screen memory and character data.
;
; Bits 4-7: Screen memory location (within VIC bank)
; Bits 1-3: Character memory location (within VIC bank)
; Bit 0: Not used for memory selection
;
; Character memory calculation:
; Location = (bits 1-3) * $0800
;
; %000 = $0000 (not usable - zero page)
; %001 = $0800
; %010 = $1000 (default: character ROM mapped here)
; %011 = $1800
; %100 = $2000
; %101 = $2800
; %110 = $3000 <- We use this
; %111 = $3800
;
; For charset at $3000 with screen at $0400:
; Screen: $0400 / $0400 = 1, bits 4-7 = %0001 = $10
; Charset: $3000 / $0800 = 6, bits 1-3 = %110 = $0C
; Combined: $10 | $0C = $1C
CHARPTR = $D018
lda #$1C ; Screen at $0400, charset at $3000
sta CHARPTR ; VIC now uses our custom characters
The $D018 register controls both screen memory and character memory locations. We set it to $1C to use our charset at $3000 while keeping the screen at the default $0400.
Character Codes
We use character codes 128-130 for our custom graphics:
| Code | Character | Purpose |
|---|---|---|
| 128 | Chevron | Note graphic |
| 129 | Horizontal line | Track line |
| 130 | Vertical bars | Hit zone marker |
These codes (128+) are in the “reverse video” range of the original character set. We overwrite them with our custom designs. The standard letters and numbers (codes 0-127) remain intact for text display.
The Complete Code
; ============================================================================
; SID SYMPHONY - Unit 4: Custom Graphics
; ============================================================================
; Design custom characters for a polished look. Notes are arrows, tracks are
; clean lines, hit zones have distinctive markers. The game looks professional.
;
; Controls: Z = Track 1 (high), X = Track 2 (mid), C = Track 3 (low)
; ============================================================================
; ============================================================================
; CUSTOMISATION SECTION
; ============================================================================
; SID Voice Settings
VOICE1_WAVE = $21 ; Sawtooth
VOICE2_WAVE = $41 ; Pulse
VOICE3_WAVE = $11 ; Triangle
VOICE1_FREQ = $1C ; High pitch
VOICE2_FREQ = $0E ; Mid pitch
VOICE3_FREQ = $07 ; Low pitch
VOICE_AD = $09 ; Attack=0, Decay=9
VOICE_SR = $00 ; Sustain=0, Release=0
PULSE_WIDTH = $08 ; 50% duty cycle
; Visual Settings
BORDER_COL = 0 ; Black border
BG_COL = 0 ; Black background
TRACK1_NOTE_COL = 10 ; Light red
TRACK2_NOTE_COL = 13 ; Light green
TRACK3_NOTE_COL = 14 ; Light blue
TRACK_LINE_COL = 11 ; Dark grey
HIT_ZONE_COL = 7 ; Yellow
FLASH1_COL = 2 ; Red
FLASH2_COL = 5 ; Green
FLASH3_COL = 6 ; Blue
; ============================================================================
; MEMORY MAP
; ============================================================================
SCREEN = $0400 ; Screen memory
COLRAM = $D800 ; Colour RAM
BORDER = $D020 ; Border colour
BGCOL = $D021 ; Background colour
CHARPTR = $D018 ; Character memory pointer
CHARSET = $3000 ; Custom character set location
; SID registers
SID = $D400
SID_V1_FREQ_LO = $D400
SID_V1_FREQ_HI = $D401
SID_V1_PWHI = $D403
SID_V1_CTRL = $D404
SID_V1_AD = $D405
SID_V1_SR = $D406
SID_V2_FREQ_LO = $D407
SID_V2_FREQ_HI = $D408
SID_V2_PWHI = $D40A
SID_V2_CTRL = $D40B
SID_V2_AD = $D40C
SID_V2_SR = $D40D
SID_V3_FREQ_LO = $D40E
SID_V3_FREQ_HI = $D40F
SID_V3_PWHI = $D411
SID_V3_CTRL = $D412
SID_V3_AD = $D413
SID_V3_SR = $D414
SID_VOLUME = $D418
; CIA keyboard
CIA1_PRA = $DC00
CIA1_PRB = $DC01
; Track positions
TRACK1_ROW = 8
TRACK2_ROW = 12
TRACK3_ROW = 16
; Hit zone
HIT_ZONE_COLUMN = 3
; Custom character codes (we'll define these)
CHAR_NOTE = 128 ; Filled arrow/circle for notes
CHAR_TRACK = 129 ; Thin horizontal line for tracks
CHAR_HITZONE = 130 ; Vertical bar for hit zone
CHAR_SPACE = 32 ; Space
; Note settings
MAX_NOTES = 8
NOTE_SPAWN_COL = 37
; Timing
FRAMES_PER_BEAT = 25
; Zero page
ZP_PTR = $FB
ZP_PTR_HI = $FC
; Variables
frame_count = $02
beat_count = $03
song_pos = $04
song_pos_hi = $05
temp_track = $06
; ----------------------------------------------------------------------------
; BASIC Stub
; ----------------------------------------------------------------------------
* = $0801
!byte $0C, $08
!byte $0A, $00
!byte $9E
!text "2064"
!byte $00
!byte $00, $00
; ----------------------------------------------------------------------------
; Main Program
; ----------------------------------------------------------------------------
* = $0810
start:
jsr copy_charset ; Copy ROM charset and add custom chars
jsr init_screen
jsr init_sid
jsr init_notes
lda #<song_data
sta song_pos
lda #>song_data
sta song_pos_hi
lda #0
sta frame_count
sta beat_count
main_loop:
lda #$FF
wait_raster:
cmp $D012
bne wait_raster
inc frame_count
lda frame_count
cmp #FRAMES_PER_BEAT
bcc no_new_beat
lda #0
sta frame_count
jsr check_spawn_note
inc beat_count
no_new_beat:
jsr update_notes
jsr reset_track_colours
jsr check_keys
jmp main_loop
; ----------------------------------------------------------------------------
; Copy Character Set from ROM to RAM
; ----------------------------------------------------------------------------
; Copies the standard character ROM to $3000, then adds custom characters
copy_charset:
; Disable interrupts during ROM access
sei
; Make character ROM visible at $D000
lda $01
pha ; Save processor port
and #$FB ; Clear bit 2 (CHAREN)
sta $01 ; Character ROM now at $D000
; Copy 2KB (256 characters * 8 bytes)
ldx #0
copy_loop:
lda $D000,x
sta CHARSET,x
lda $D100,x
sta CHARSET+$100,x
lda $D200,x
sta CHARSET+$200,x
lda $D300,x
sta CHARSET+$300,x
lda $D400,x
sta CHARSET+$400,x
lda $D500,x
sta CHARSET+$500,x
lda $D600,x
sta CHARSET+$600,x
lda $D700,x
sta CHARSET+$700,x
inx
bne copy_loop
; Restore processor port
pla
sta $01
; Re-enable interrupts
cli
; Define our custom characters
jsr define_custom_chars
; Point VIC-II to our charset at $3000
; Screen at $0400 = %0001 in upper nibble
; Charset at $3000 = %110 in bits 1-3 = $0C
lda #$1C
sta CHARPTR
rts
; ----------------------------------------------------------------------------
; Define Custom Characters
; ----------------------------------------------------------------------------
; Creates note, track, and hit zone graphics
define_custom_chars:
; Character 128: Note (filled chevron pointing left)
lda #%00000110 ; Row 0
sta CHARSET + (CHAR_NOTE * 8) + 0
lda #%00011110 ; Row 1
sta CHARSET + (CHAR_NOTE * 8) + 1
lda #%01111110 ; Row 2
sta CHARSET + (CHAR_NOTE * 8) + 2
lda #%11111110 ; Row 3
sta CHARSET + (CHAR_NOTE * 8) + 3
lda #%11111110 ; Row 4
sta CHARSET + (CHAR_NOTE * 8) + 4
lda #%01111110 ; Row 5
sta CHARSET + (CHAR_NOTE * 8) + 5
lda #%00011110 ; Row 6
sta CHARSET + (CHAR_NOTE * 8) + 6
lda #%00000110 ; Row 7
sta CHARSET + (CHAR_NOTE * 8) + 7
; Character 129: Track line (centered horizontal line)
lda #%00000000 ; Row 0
sta CHARSET + (CHAR_TRACK * 8) + 0
lda #%00000000 ; Row 1
sta CHARSET + (CHAR_TRACK * 8) + 1
lda #%00000000 ; Row 2
sta CHARSET + (CHAR_TRACK * 8) + 2
lda #%11111111 ; Row 3 - the line
sta CHARSET + (CHAR_TRACK * 8) + 3
lda #%11111111 ; Row 4 - the line
sta CHARSET + (CHAR_TRACK * 8) + 4
lda #%00000000 ; Row 5
sta CHARSET + (CHAR_TRACK * 8) + 5
lda #%00000000 ; Row 6
sta CHARSET + (CHAR_TRACK * 8) + 6
lda #%00000000 ; Row 7
sta CHARSET + (CHAR_TRACK * 8) + 7
; Character 130: Hit zone (vertical bars)
lda #%01100110 ; Row 0
sta CHARSET + (CHAR_HITZONE * 8) + 0
lda #%01100110 ; Row 1
sta CHARSET + (CHAR_HITZONE * 8) + 1
lda #%01100110 ; Row 2
sta CHARSET + (CHAR_HITZONE * 8) + 2
lda #%01100110 ; Row 3
sta CHARSET + (CHAR_HITZONE * 8) + 3
lda #%01100110 ; Row 4
sta CHARSET + (CHAR_HITZONE * 8) + 4
lda #%01100110 ; Row 5
sta CHARSET + (CHAR_HITZONE * 8) + 5
lda #%01100110 ; Row 6
sta CHARSET + (CHAR_HITZONE * 8) + 6
lda #%01100110 ; Row 7
sta CHARSET + (CHAR_HITZONE * 8) + 7
rts
; ----------------------------------------------------------------------------
; Initialize Notes
; ----------------------------------------------------------------------------
init_notes:
ldx #MAX_NOTES-1
lda #0
init_notes_loop:
sta note_track,x
sta note_col,x
dex
bpl init_notes_loop
rts
; ----------------------------------------------------------------------------
; Check Spawn Note
; ----------------------------------------------------------------------------
check_spawn_note:
ldy #0
spawn_check_loop:
lda (song_pos),y
cmp #$FF
beq spawn_restart_song
cmp beat_count
beq spawn_match
bcs spawn_done
jmp spawn_advance
spawn_match:
iny
lda (song_pos),y
jsr spawn_note
dey
spawn_advance:
lda song_pos
clc
adc #2
sta song_pos
lda song_pos_hi
adc #0
sta song_pos_hi
jmp spawn_check_loop
spawn_done:
rts
spawn_restart_song:
lda #<song_data
sta song_pos
lda #>song_data
sta song_pos_hi
lda #0
sta beat_count
rts
; ----------------------------------------------------------------------------
; Spawn Note
; ----------------------------------------------------------------------------
spawn_note:
sta temp_track
ldx #0
spawn_find_slot:
lda note_track,x
beq spawn_found_slot
inx
cpx #MAX_NOTES
bne spawn_find_slot
rts
spawn_found_slot:
lda temp_track
sta note_track,x
lda #NOTE_SPAWN_COL
sta note_col,x
jsr draw_note
rts
; ----------------------------------------------------------------------------
; Update Notes
; ----------------------------------------------------------------------------
update_notes:
ldx #0
update_loop:
lda note_track,x
beq update_next
jsr erase_note
dec note_col,x
lda note_col,x
cmp #1
bcc update_deactivate
jsr draw_note
jmp update_next
update_deactivate:
lda #0
sta note_track,x
update_next:
inx
cpx #MAX_NOTES
bne update_loop
rts
; ----------------------------------------------------------------------------
; Draw Note - Uses custom note character
; ----------------------------------------------------------------------------
draw_note:
lda note_track,x
cmp #1
beq draw_note_t1
cmp #2
beq draw_note_t2
cmp #3
beq draw_note_t3
rts
draw_note_t1:
lda note_col,x
clc
adc #<(SCREEN + TRACK1_ROW * 40)
sta ZP_PTR
lda #>(SCREEN + TRACK1_ROW * 40)
adc #0
sta ZP_PTR_HI
ldy #0
lda #CHAR_NOTE ; Custom note character
sta (ZP_PTR),y
lda note_col,x
clc
adc #<(COLRAM + TRACK1_ROW * 40)
sta ZP_PTR
lda #>(COLRAM + TRACK1_ROW * 40)
adc #0
sta ZP_PTR_HI
lda #TRACK1_NOTE_COL
sta (ZP_PTR),y
rts
draw_note_t2:
lda note_col,x
clc
adc #<(SCREEN + TRACK2_ROW * 40)
sta ZP_PTR
lda #>(SCREEN + TRACK2_ROW * 40)
adc #0
sta ZP_PTR_HI
ldy #0
lda #CHAR_NOTE
sta (ZP_PTR),y
lda note_col,x
clc
adc #<(COLRAM + TRACK2_ROW * 40)
sta ZP_PTR
lda #>(COLRAM + TRACK2_ROW * 40)
adc #0
sta ZP_PTR_HI
lda #TRACK2_NOTE_COL
sta (ZP_PTR),y
rts
draw_note_t3:
lda note_col,x
clc
adc #<(SCREEN + TRACK3_ROW * 40)
sta ZP_PTR
lda #>(SCREEN + TRACK3_ROW * 40)
adc #0
sta ZP_PTR_HI
ldy #0
lda #CHAR_NOTE
sta (ZP_PTR),y
lda note_col,x
clc
adc #<(COLRAM + TRACK3_ROW * 40)
sta ZP_PTR
lda #>(COLRAM + TRACK3_ROW * 40)
adc #0
sta ZP_PTR_HI
lda #TRACK3_NOTE_COL
sta (ZP_PTR),y
rts
; ----------------------------------------------------------------------------
; Erase Note - Restores track line character
; ----------------------------------------------------------------------------
erase_note:
lda note_track,x
cmp #1
beq erase_note_t1
cmp #2
beq erase_note_t2
cmp #3
beq erase_note_t3
rts
erase_note_t1:
lda note_col,x
clc
adc #<(SCREEN + TRACK1_ROW * 40)
sta ZP_PTR
lda #>(SCREEN + TRACK1_ROW * 40)
adc #0
sta ZP_PTR_HI
ldy #0
lda #CHAR_TRACK ; Custom track character
sta (ZP_PTR),y
lda note_col,x
clc
adc #<(COLRAM + TRACK1_ROW * 40)
sta ZP_PTR
lda #>(COLRAM + TRACK1_ROW * 40)
adc #0
sta ZP_PTR_HI
lda #TRACK_LINE_COL
sta (ZP_PTR),y
rts
erase_note_t2:
lda note_col,x
clc
adc #<(SCREEN + TRACK2_ROW * 40)
sta ZP_PTR
lda #>(SCREEN + TRACK2_ROW * 40)
adc #0
sta ZP_PTR_HI
ldy #0
lda #CHAR_TRACK
sta (ZP_PTR),y
lda note_col,x
clc
adc #<(COLRAM + TRACK2_ROW * 40)
sta ZP_PTR
lda #>(COLRAM + TRACK2_ROW * 40)
adc #0
sta ZP_PTR_HI
lda #TRACK_LINE_COL
sta (ZP_PTR),y
rts
erase_note_t3:
lda note_col,x
clc
adc #<(SCREEN + TRACK3_ROW * 40)
sta ZP_PTR
lda #>(SCREEN + TRACK3_ROW * 40)
adc #0
sta ZP_PTR_HI
ldy #0
lda #CHAR_TRACK
sta (ZP_PTR),y
lda note_col,x
clc
adc #<(COLRAM + TRACK3_ROW * 40)
sta ZP_PTR
lda #>(COLRAM + TRACK3_ROW * 40)
adc #0
sta ZP_PTR_HI
lda #TRACK_LINE_COL
sta (ZP_PTR),y
rts
; ----------------------------------------------------------------------------
; Initialize Screen
; ----------------------------------------------------------------------------
init_screen:
lda #BORDER_COL
sta BORDER
lda #BG_COL
sta BGCOL
; Clear screen with spaces
ldx #0
lda #CHAR_SPACE
clr_screen:
sta SCREEN,x
sta SCREEN+$100,x
sta SCREEN+$200,x
sta SCREEN+$2E8,x
inx
bne clr_screen
; Set all colours to track line colour
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 - Uses custom track character
; ----------------------------------------------------------------------------
draw_tracks:
; Track 1
lda #CHAR_TRACK
ldx #0
draw_t1:
sta SCREEN + (TRACK1_ROW * 40),x
inx
cpx #38
bne draw_t1
; Track 2
lda #CHAR_TRACK
ldx #0
draw_t2:
sta SCREEN + (TRACK2_ROW * 40),x
inx
cpx #38
bne draw_t2
; Track 3
lda #CHAR_TRACK
ldx #0
draw_t3:
sta SCREEN + (TRACK3_ROW * 40),x
inx
cpx #38
bne draw_t3
rts
; ----------------------------------------------------------------------------
; Draw Hit Zones - Uses custom hit zone character
; ----------------------------------------------------------------------------
draw_hit_zones:
lda #CHAR_HITZONE ; Custom hit zone character
; Draw hit zone bars spanning all three tracks
sta SCREEN + ((TRACK1_ROW-2) * 40) + HIT_ZONE_COLUMN
sta SCREEN + ((TRACK1_ROW-1) * 40) + HIT_ZONE_COLUMN
sta SCREEN + (TRACK1_ROW * 40) + HIT_ZONE_COLUMN
sta SCREEN + ((TRACK1_ROW+1) * 40) + HIT_ZONE_COLUMN
sta SCREEN + ((TRACK2_ROW-1) * 40) + HIT_ZONE_COLUMN
sta SCREEN + (TRACK2_ROW * 40) + HIT_ZONE_COLUMN
sta SCREEN + ((TRACK2_ROW+1) * 40) + HIT_ZONE_COLUMN
sta SCREEN + ((TRACK3_ROW-1) * 40) + HIT_ZONE_COLUMN
sta SCREEN + (TRACK3_ROW * 40) + HIT_ZONE_COLUMN
sta SCREEN + ((TRACK3_ROW+1) * 40) + HIT_ZONE_COLUMN
sta SCREEN + ((TRACK3_ROW+2) * 40) + HIT_ZONE_COLUMN
; Colour the hit zones
lda #HIT_ZONE_COL
sta COLRAM + ((TRACK1_ROW-2) * 40) + HIT_ZONE_COLUMN
sta COLRAM + ((TRACK1_ROW-1) * 40) + HIT_ZONE_COLUMN
sta COLRAM + (TRACK1_ROW * 40) + HIT_ZONE_COLUMN
sta COLRAM + ((TRACK1_ROW+1) * 40) + HIT_ZONE_COLUMN
sta COLRAM + ((TRACK2_ROW-1) * 40) + HIT_ZONE_COLUMN
sta COLRAM + (TRACK2_ROW * 40) + HIT_ZONE_COLUMN
sta COLRAM + ((TRACK2_ROW+1) * 40) + HIT_ZONE_COLUMN
sta COLRAM + ((TRACK3_ROW-1) * 40) + HIT_ZONE_COLUMN
sta COLRAM + (TRACK3_ROW * 40) + HIT_ZONE_COLUMN
sta COLRAM + ((TRACK3_ROW+1) * 40) + HIT_ZONE_COLUMN
sta COLRAM + ((TRACK3_ROW+2) * 40) + HIT_ZONE_COLUMN
rts
; ----------------------------------------------------------------------------
; Draw Labels
; ----------------------------------------------------------------------------
draw_labels:
ldx #0
draw_title:
lda title_text,x
beq draw_title_done
sta SCREEN + 13,x
lda #1 ; White
sta COLRAM + 13,x
inx
bne draw_title
draw_title_done:
; Track labels - use standard characters for letters
lda #$1A ; Z (screen code)
sta SCREEN + (TRACK1_ROW * 40)
lda #TRACK1_NOTE_COL
sta COLRAM + (TRACK1_ROW * 40)
lda #$18 ; X
sta SCREEN + (TRACK2_ROW * 40)
lda #TRACK2_NOTE_COL
sta COLRAM + (TRACK2_ROW * 40)
lda #$03 ; C
sta SCREEN + (TRACK3_ROW * 40)
lda #TRACK3_NOTE_COL
sta COLRAM + (TRACK3_ROW * 40)
ldx #0
draw_instr:
lda instr_text,x
beq draw_instr_done
sta SCREEN + (23 * 40) + 6,x
lda #TRACK_LINE_COL
sta COLRAM + (23 * 40) + 6,x
inx
bne draw_instr
draw_instr_done:
rts
title_text:
!scr "sid symphony"
!byte 0
instr_text:
!scr "custom graphics active"
!byte 0
; ----------------------------------------------------------------------------
; Initialize SID
; ----------------------------------------------------------------------------
init_sid:
ldx #$18
lda #0
clear_sid:
sta SID,x
dex
bpl clear_sid
lda #$0F
sta SID_VOLUME
lda #$00
sta SID_V1_FREQ_LO
lda #VOICE1_FREQ
sta SID_V1_FREQ_HI
lda #PULSE_WIDTH
sta SID_V1_PWHI
lda #VOICE_AD
sta SID_V1_AD
lda #VOICE_SR
sta SID_V1_SR
lda #$00
sta SID_V2_FREQ_LO
lda #VOICE2_FREQ
sta SID_V2_FREQ_HI
lda #PULSE_WIDTH
sta SID_V2_PWHI
lda #VOICE_AD
sta SID_V2_AD
lda #VOICE_SR
sta SID_V2_SR
lda #$00
sta SID_V3_FREQ_LO
lda #VOICE3_FREQ
sta SID_V3_FREQ_HI
lda #PULSE_WIDTH
sta SID_V3_PWHI
lda #VOICE_AD
sta SID_V3_AD
lda #VOICE_SR
sta SID_V3_SR
rts
; ----------------------------------------------------------------------------
; Reset Track Colours
; ----------------------------------------------------------------------------
reset_track_colours:
ldx #0
lda #TRACK_LINE_COL
reset_t1:
sta COLRAM + (TRACK1_ROW * 40),x
inx
cpx #38
bne reset_t1
ldx #0
reset_t2:
sta COLRAM + (TRACK2_ROW * 40),x
inx
cpx #38
bne reset_t2
ldx #0
reset_t3:
sta COLRAM + (TRACK3_ROW * 40),x
inx
cpx #38
bne reset_t3
; 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
; ----------------------------------------------------------------------------
play_voice1:
lda #VOICE1_WAVE
ora #$01
sta SID_V1_CTRL
rts
play_voice2:
lda #VOICE2_WAVE
ora #$01
sta SID_V2_CTRL
rts
play_voice3:
lda #VOICE3_WAVE
ora #$01
sta SID_V3_CTRL
rts
; ----------------------------------------------------------------------------
; Flash Tracks
; ----------------------------------------------------------------------------
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
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
Try This: Thicker Track Lines
; Three-pixel thick track:
lda #%00000000 ; Row 0
sta CHARSET + (CHAR_TRACK * 8) + 0
lda #%00000000 ; Row 1
sta CHARSET + (CHAR_TRACK * 8) + 1
lda #%11111111 ; Row 2 - top of line
sta CHARSET + (CHAR_TRACK * 8) + 2
lda #%11111111 ; Row 3 - middle
sta CHARSET + (CHAR_TRACK * 8) + 3
lda #%11111111 ; Row 4 - bottom
sta CHARSET + (CHAR_TRACK * 8) + 4
lda #%00000000 ; Row 5
sta CHARSET + (CHAR_TRACK * 8) + 5
Try This: Dotted Track Lines
; Dotted track (alternating pixels):
lda #%00000000
sta CHARSET + (CHAR_TRACK * 8) + 0
sta CHARSET + (CHAR_TRACK * 8) + 1
sta CHARSET + (CHAR_TRACK * 8) + 2
lda #%10101010 ; Alternating pattern
sta CHARSET + (CHAR_TRACK * 8) + 3
lda #%01010101 ; Offset pattern
sta CHARSET + (CHAR_TRACK * 8) + 4
lda #%00000000
sta CHARSET + (CHAR_TRACK * 8) + 5
What You’ve Learnt
- Character ROM - Default characters at $D000, read-only
- Memory remapping - Processor port $01 controls what’s visible at certain addresses
- Character definitions - 8 bytes per character, 1 bit per pixel
- VIC-II pointer - $D018 tells the VIC where to find screen and character memory
- Binary pixel design - Use %xxxxxxxx notation to visualise character shapes
What’s Next
In Unit 5, we’ll detect when keypresses actually match notes in the hit zone. Right now, pressing keys plays sounds regardless of timing - we’ll add real hit detection.
What Changed
| 1 | 1 | ; ============================================================================ | |
| 2 | - | ; SID SYMPHONY - Unit 3: Making It Your Own | |
| 2 | + | ; SID SYMPHONY - Unit 4: Custom Graphics | |
| 3 | 3 | ; ============================================================================ | |
| 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. | |
| 4 | + | ; Design custom characters for a polished look. Notes are arrows, tracks are | |
| 5 | + | ; clean lines, hit zones have distinctive markers. The game looks professional. | |
| 7 | 6 | ; | |
| 8 | 7 | ; Controls: Z = Track 1 (high), X = Track 2 (mid), C = Track 3 (low) | |
| 9 | 8 | ; ============================================================================ | |
| 10 | 9 | | |
| 11 | 10 | ; ============================================================================ | |
| 12 | - | ; CUSTOMISATION SECTION - Change these values and reassemble! | |
| 11 | + | ; CUSTOMISATION SECTION | |
| 13 | 12 | ; ============================================================================ | |
| 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 | 13 | | |
| 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) | |
| 14 | + | ; SID Voice Settings | |
| 15 | + | VOICE1_WAVE = $21 ; Sawtooth | |
| 16 | + | VOICE2_WAVE = $41 ; Pulse | |
| 17 | + | VOICE3_WAVE = $11 ; Triangle | |
| 32 | 18 | | |
| 33 | - | ; Pulse width (only affects pulse wave, $41) | |
| 34 | - | PULSE_WIDTH = $08 ; $08 = 50% duty cycle (square wave) | |
| 19 | + | VOICE1_FREQ = $1C ; High pitch | |
| 20 | + | VOICE2_FREQ = $0E ; Mid pitch | |
| 21 | + | VOICE3_FREQ = $07 ; Low pitch | |
| 35 | 22 | | |
| 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 | |
| 23 | + | VOICE_AD = $09 ; Attack=0, Decay=9 | |
| 24 | + | VOICE_SR = $00 ; Sustain=0, Release=0 | |
| 25 | + | PULSE_WIDTH = $08 ; 50% duty cycle | |
| 40 | 26 | | |
| 41 | - | BORDER_COL = 6 ; Blue border | |
| 27 | + | ; Visual Settings | |
| 28 | + | BORDER_COL = 0 ; Black border | |
| 42 | 29 | BG_COL = 0 ; Black background | |
| 43 | 30 | | |
| 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 | |
| 31 | + | TRACK1_NOTE_COL = 10 ; Light red | |
| 32 | + | TRACK2_NOTE_COL = 13 ; Light green | |
| 33 | + | TRACK3_NOTE_COL = 14 ; Light blue | |
| 47 | 34 | | |
| 48 | - | TRACK_LINE_COL = 11 ; Dark grey track lines | |
| 49 | - | HIT_ZONE_COL = 7 ; Yellow hit zone | |
| 35 | + | TRACK_LINE_COL = 11 ; Dark grey | |
| 36 | + | HIT_ZONE_COL = 7 ; Yellow | |
| 50 | 37 | | |
| 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 | |
| 38 | + | FLASH1_COL = 2 ; Red | |
| 39 | + | FLASH2_COL = 5 ; Green | |
| 40 | + | FLASH3_COL = 6 ; Blue | |
| 55 | 41 | | |
| 56 | 42 | ; ============================================================================ | |
| 57 | - | ; END OF CUSTOMISATION - Code below uses the settings above | |
| 43 | + | ; MEMORY MAP | |
| 58 | 44 | ; ============================================================================ | |
| 59 | 45 | | |
| 60 | - | ; ---------------------------------------------------------------------------- | |
| 61 | - | ; Memory Addresses | |
| 62 | - | ; ---------------------------------------------------------------------------- | |
| 46 | + | SCREEN = $0400 ; Screen memory | |
| 47 | + | COLRAM = $D800 ; Colour RAM | |
| 48 | + | BORDER = $D020 ; Border colour | |
| 49 | + | BGCOL = $D021 ; Background colour | |
| 50 | + | CHARPTR = $D018 ; Character memory pointer | |
| 63 | 51 | | |
| 64 | - | SCREEN = $0400 | |
| 65 | - | COLRAM = $D800 | |
| 66 | - | BORDER = $D020 | |
| 67 | - | BGCOL = $D021 | |
| 52 | + | CHARSET = $3000 ; Custom character set location | |
| 68 | 53 | | |
| 69 | 54 | ; SID registers | |
| 70 | 55 | SID = $D400 | |
| 71 | 56 | SID_V1_FREQ_LO = $D400 | |
| 72 | 57 | SID_V1_FREQ_HI = $D401 | |
| 73 | - | SID_V1_PWLO = $D402 | |
| 74 | 58 | SID_V1_PWHI = $D403 | |
| 75 | 59 | SID_V1_CTRL = $D404 | |
| 76 | 60 | SID_V1_AD = $D405 | |
| ... | |||
| 78 | 62 | | |
| 79 | 63 | SID_V2_FREQ_LO = $D407 | |
| 80 | 64 | SID_V2_FREQ_HI = $D408 | |
| 81 | - | SID_V2_PWLO = $D409 | |
| 82 | 65 | SID_V2_PWHI = $D40A | |
| 83 | 66 | SID_V2_CTRL = $D40B | |
| 84 | 67 | SID_V2_AD = $D40C | |
| ... | |||
| 86 | 69 | | |
| 87 | 70 | SID_V3_FREQ_LO = $D40E | |
| 88 | 71 | SID_V3_FREQ_HI = $D40F | |
| 89 | - | SID_V3_PWLO = $D410 | |
| 90 | 72 | SID_V3_PWHI = $D411 | |
| 91 | 73 | SID_V3_CTRL = $D412 | |
| 92 | 74 | SID_V3_AD = $D413 | |
| ... | |||
| 103 | 85 | TRACK2_ROW = 12 | |
| 104 | 86 | TRACK3_ROW = 16 | |
| 105 | 87 | | |
| 106 | - | ; Hit zone column | |
| 88 | + | ; Hit zone | |
| 107 | 89 | HIT_ZONE_COLUMN = 3 | |
| 90 | + | | |
| 91 | + | ; Custom character codes (we'll define these) | |
| 92 | + | CHAR_NOTE = 128 ; Filled arrow/circle for notes | |
| 93 | + | CHAR_TRACK = 129 ; Thin horizontal line for tracks | |
| 94 | + | CHAR_HITZONE = 130 ; Vertical bar for hit zone | |
| 95 | + | CHAR_SPACE = 32 ; Space | |
| 108 | 96 | | |
| 109 | 97 | ; Note settings | |
| 110 | - | NOTE_CHAR = $57 ; Filled dot character | |
| 111 | - | TRACK_CHAR = $2D ; Minus character | |
| 112 | 98 | MAX_NOTES = 8 | |
| 113 | 99 | NOTE_SPAWN_COL = 37 | |
| 114 | 100 | | |
| ... | |||
| 146 | 132 | * = $0810 | |
| 147 | 133 | | |
| 148 | 134 | start: | |
| 135 | + | jsr copy_charset ; Copy ROM charset and add custom chars | |
| 149 | 136 | jsr init_screen | |
| 150 | 137 | jsr init_sid | |
| 151 | 138 | jsr init_notes | |
| ... | |||
| 181 | 168 | jsr check_keys | |
| 182 | 169 | | |
| 183 | 170 | jmp main_loop | |
| 171 | + | | |
| 172 | + | ; ---------------------------------------------------------------------------- | |
| 173 | + | ; Copy Character Set from ROM to RAM | |
| 174 | + | ; ---------------------------------------------------------------------------- | |
| 175 | + | ; Copies the standard character ROM to $3000, then adds custom characters | |
| 176 | + | | |
| 177 | + | copy_charset: | |
| 178 | + | ; Disable interrupts during ROM access | |
| 179 | + | sei | |
| 180 | + | | |
| 181 | + | ; Make character ROM visible at $D000 | |
| 182 | + | lda $01 | |
| 183 | + | pha ; Save processor port | |
| 184 | + | and #$FB ; Clear bit 2 (CHAREN) | |
| 185 | + | sta $01 ; Character ROM now at $D000 | |
| 186 | + | | |
| 187 | + | ; Copy 2KB (256 characters * 8 bytes) | |
| 188 | + | ldx #0 | |
| 189 | + | copy_loop: | |
| 190 | + | lda $D000,x | |
| 191 | + | sta CHARSET,x | |
| 192 | + | lda $D100,x | |
| 193 | + | sta CHARSET+$100,x | |
| 194 | + | lda $D200,x | |
| 195 | + | sta CHARSET+$200,x | |
| 196 | + | lda $D300,x | |
| 197 | + | sta CHARSET+$300,x | |
| 198 | + | lda $D400,x | |
| 199 | + | sta CHARSET+$400,x | |
| 200 | + | lda $D500,x | |
| 201 | + | sta CHARSET+$500,x | |
| 202 | + | lda $D600,x | |
| 203 | + | sta CHARSET+$600,x | |
| 204 | + | lda $D700,x | |
| 205 | + | sta CHARSET+$700,x | |
| 206 | + | inx | |
| 207 | + | bne copy_loop | |
| 208 | + | | |
| 209 | + | ; Restore processor port | |
| 210 | + | pla | |
| 211 | + | sta $01 | |
| 212 | + | | |
| 213 | + | ; Re-enable interrupts | |
| 214 | + | cli | |
| 215 | + | | |
| 216 | + | ; Define our custom characters | |
| 217 | + | jsr define_custom_chars | |
| 218 | + | | |
| 219 | + | ; Point VIC-II to our charset at $3000 | |
| 220 | + | ; Screen at $0400 = %0001 in upper nibble | |
| 221 | + | ; Charset at $3000 = %110 in bits 1-3 = $0C | |
| 222 | + | lda #$1C | |
| 223 | + | sta CHARPTR | |
| 224 | + | | |
| 225 | + | rts | |
| 226 | + | | |
| 227 | + | ; ---------------------------------------------------------------------------- | |
| 228 | + | ; Define Custom Characters | |
| 229 | + | ; ---------------------------------------------------------------------------- | |
| 230 | + | ; Creates note, track, and hit zone graphics | |
| 231 | + | | |
| 232 | + | define_custom_chars: | |
| 233 | + | ; Character 128: Note (filled chevron pointing left) | |
| 234 | + | lda #%00000110 ; Row 0 | |
| 235 | + | sta CHARSET + (CHAR_NOTE * 8) + 0 | |
| 236 | + | lda #%00011110 ; Row 1 | |
| 237 | + | sta CHARSET + (CHAR_NOTE * 8) + 1 | |
| 238 | + | lda #%01111110 ; Row 2 | |
| 239 | + | sta CHARSET + (CHAR_NOTE * 8) + 2 | |
| 240 | + | lda #%11111110 ; Row 3 | |
| 241 | + | sta CHARSET + (CHAR_NOTE * 8) + 3 | |
| 242 | + | lda #%11111110 ; Row 4 | |
| 243 | + | sta CHARSET + (CHAR_NOTE * 8) + 4 | |
| 244 | + | lda #%01111110 ; Row 5 | |
| 245 | + | sta CHARSET + (CHAR_NOTE * 8) + 5 | |
| 246 | + | lda #%00011110 ; Row 6 | |
| 247 | + | sta CHARSET + (CHAR_NOTE * 8) + 6 | |
| 248 | + | lda #%00000110 ; Row 7 | |
| 249 | + | sta CHARSET + (CHAR_NOTE * 8) + 7 | |
| 250 | + | | |
| 251 | + | ; Character 129: Track line (centered horizontal line) | |
| 252 | + | lda #%00000000 ; Row 0 | |
| 253 | + | sta CHARSET + (CHAR_TRACK * 8) + 0 | |
| 254 | + | lda #%00000000 ; Row 1 | |
| 255 | + | sta CHARSET + (CHAR_TRACK * 8) + 1 | |
| 256 | + | lda #%00000000 ; Row 2 | |
| 257 | + | sta CHARSET + (CHAR_TRACK * 8) + 2 | |
| 258 | + | lda #%11111111 ; Row 3 - the line | |
| 259 | + | sta CHARSET + (CHAR_TRACK * 8) + 3 | |
| 260 | + | lda #%11111111 ; Row 4 - the line | |
| 261 | + | sta CHARSET + (CHAR_TRACK * 8) + 4 | |
| 262 | + | lda #%00000000 ; Row 5 | |
| 263 | + | sta CHARSET + (CHAR_TRACK * 8) + 5 | |
| 264 | + | lda #%00000000 ; Row 6 | |
| 265 | + | sta CHARSET + (CHAR_TRACK * 8) + 6 | |
| 266 | + | lda #%00000000 ; Row 7 | |
| 267 | + | sta CHARSET + (CHAR_TRACK * 8) + 7 | |
| 268 | + | | |
| 269 | + | ; Character 130: Hit zone (vertical bars) | |
| 270 | + | lda #%01100110 ; Row 0 | |
| 271 | + | sta CHARSET + (CHAR_HITZONE * 8) + 0 | |
| 272 | + | lda #%01100110 ; Row 1 | |
| 273 | + | sta CHARSET + (CHAR_HITZONE * 8) + 1 | |
| 274 | + | lda #%01100110 ; Row 2 | |
| 275 | + | sta CHARSET + (CHAR_HITZONE * 8) + 2 | |
| 276 | + | lda #%01100110 ; Row 3 | |
| 277 | + | sta CHARSET + (CHAR_HITZONE * 8) + 3 | |
| 278 | + | lda #%01100110 ; Row 4 | |
| 279 | + | sta CHARSET + (CHAR_HITZONE * 8) + 4 | |
| 280 | + | lda #%01100110 ; Row 5 | |
| 281 | + | sta CHARSET + (CHAR_HITZONE * 8) + 5 | |
| 282 | + | lda #%01100110 ; Row 6 | |
| 283 | + | sta CHARSET + (CHAR_HITZONE * 8) + 6 | |
| 284 | + | lda #%01100110 ; Row 7 | |
| 285 | + | sta CHARSET + (CHAR_HITZONE * 8) + 7 | |
| 286 | + | | |
| 287 | + | rts | |
| 184 | 288 | | |
| 185 | 289 | ; ---------------------------------------------------------------------------- | |
| 186 | 290 | ; Initialize Notes | |
| ... | |||
| 298 | 402 | rts | |
| 299 | 403 | | |
| 300 | 404 | ; ---------------------------------------------------------------------------- | |
| 301 | - | ; Draw Note | |
| 405 | + | ; Draw Note - Uses custom note character | |
| 302 | 406 | ; ---------------------------------------------------------------------------- | |
| 303 | 407 | | |
| 304 | 408 | draw_note: | |
| ... | |||
| 321 | 425 | sta ZP_PTR_HI | |
| 322 | 426 | | |
| 323 | 427 | ldy #0 | |
| 324 | - | lda #NOTE_CHAR | |
| 428 | + | lda #CHAR_NOTE ; Custom note character | |
| 325 | 429 | sta (ZP_PTR),y | |
| 326 | 430 | | |
| 327 | 431 | lda note_col,x | |
| ... | |||
| 345 | 449 | sta ZP_PTR_HI | |
| 346 | 450 | | |
| 347 | 451 | ldy #0 | |
| 348 | - | lda #NOTE_CHAR | |
| 452 | + | lda #CHAR_NOTE | |
| 349 | 453 | sta (ZP_PTR),y | |
| 350 | 454 | | |
| 351 | 455 | lda note_col,x | |
| ... | |||
| 369 | 473 | sta ZP_PTR_HI | |
| 370 | 474 | | |
| 371 | 475 | ldy #0 | |
| 372 | - | lda #NOTE_CHAR | |
| 476 | + | lda #CHAR_NOTE | |
| 373 | 477 | sta (ZP_PTR),y | |
| 374 | 478 | | |
| 375 | 479 | lda note_col,x | |
| ... | |||
| 384 | 488 | rts | |
| 385 | 489 | | |
| 386 | 490 | ; ---------------------------------------------------------------------------- | |
| 387 | - | ; Erase Note | |
| 491 | + | ; Erase Note - Restores track line character | |
| 388 | 492 | ; ---------------------------------------------------------------------------- | |
| 389 | 493 | | |
| 390 | 494 | erase_note: | |
| ... | |||
| 407 | 511 | sta ZP_PTR_HI | |
| 408 | 512 | | |
| 409 | 513 | ldy #0 | |
| 410 | - | lda #TRACK_CHAR | |
| 514 | + | lda #CHAR_TRACK ; Custom track character | |
| 411 | 515 | sta (ZP_PTR),y | |
| 412 | 516 | | |
| 413 | 517 | lda note_col,x | |
| ... | |||
| 431 | 535 | sta ZP_PTR_HI | |
| 432 | 536 | | |
| 433 | 537 | ldy #0 | |
| 434 | - | lda #TRACK_CHAR | |
| 538 | + | lda #CHAR_TRACK | |
| 435 | 539 | sta (ZP_PTR),y | |
| 436 | 540 | | |
| 437 | 541 | lda note_col,x | |
| ... | |||
| 455 | 559 | sta ZP_PTR_HI | |
| 456 | 560 | | |
| 457 | 561 | ldy #0 | |
| 458 | - | lda #TRACK_CHAR | |
| 562 | + | lda #CHAR_TRACK | |
| 459 | 563 | sta (ZP_PTR),y | |
| 460 | 564 | | |
| 461 | 565 | lda note_col,x | |
| ... | |||
| 479 | 583 | lda #BG_COL | |
| 480 | 584 | sta BGCOL | |
| 481 | 585 | | |
| 586 | + | ; Clear screen with spaces | |
| 482 | 587 | ldx #0 | |
| 483 | - | lda #$20 | |
| 588 | + | lda #CHAR_SPACE | |
| 484 | 589 | clr_screen: | |
| 485 | 590 | sta SCREEN,x | |
| 486 | 591 | sta SCREEN+$100,x | |
| ... | |||
| 489 | 594 | inx | |
| 490 | 595 | bne clr_screen | |
| 491 | 596 | | |
| 597 | + | ; Set all colours to track line colour | |
| 492 | 598 | ldx #0 | |
| 493 | 599 | lda #TRACK_LINE_COL | |
| 494 | 600 | clr_colour: | |
| ... | |||
| 506 | 612 | rts | |
| 507 | 613 | | |
| 508 | 614 | ; ---------------------------------------------------------------------------- | |
| 509 | - | ; Draw Tracks | |
| 615 | + | ; Draw Tracks - Uses custom track character | |
| 510 | 616 | ; ---------------------------------------------------------------------------- | |
| 511 | 617 | | |
| 512 | 618 | draw_tracks: | |
| 619 | + | ; Track 1 | |
| 620 | + | lda #CHAR_TRACK | |
| 513 | 621 | ldx #0 | |
| 514 | - | lda #TRACK_CHAR | |
| 515 | 622 | draw_t1: | |
| 516 | 623 | sta SCREEN + (TRACK1_ROW * 40),x | |
| 517 | 624 | inx | |
| 518 | 625 | cpx #38 | |
| 519 | 626 | bne draw_t1 | |
| 520 | 627 | | |
| 628 | + | ; Track 2 | |
| 629 | + | lda #CHAR_TRACK | |
| 521 | 630 | ldx #0 | |
| 522 | 631 | draw_t2: | |
| 523 | 632 | sta SCREEN + (TRACK2_ROW * 40),x | |
| ... | |||
| 525 | 634 | cpx #38 | |
| 526 | 635 | bne draw_t2 | |
| 527 | 636 | | |
| 637 | + | ; Track 3 | |
| 638 | + | lda #CHAR_TRACK | |
| 528 | 639 | ldx #0 | |
| 529 | 640 | draw_t3: | |
| 530 | 641 | sta SCREEN + (TRACK3_ROW * 40),x | |
| ... | |||
| 535 | 646 | rts | |
| 536 | 647 | | |
| 537 | 648 | ; ---------------------------------------------------------------------------- | |
| 538 | - | ; Draw Hit Zones | |
| 649 | + | ; Draw Hit Zones - Uses custom hit zone character | |
| 539 | 650 | ; ---------------------------------------------------------------------------- | |
| 540 | 651 | | |
| 541 | 652 | draw_hit_zones: | |
| 542 | - | lda #$7D | |
| 653 | + | lda #CHAR_HITZONE ; Custom hit zone character | |
| 543 | 654 | | |
| 544 | - | sta SCREEN + (TRACK1_ROW * 40) + HIT_ZONE_COLUMN | |
| 655 | + | ; Draw hit zone bars spanning all three tracks | |
| 656 | + | sta SCREEN + ((TRACK1_ROW-2) * 40) + HIT_ZONE_COLUMN | |
| 545 | 657 | sta SCREEN + ((TRACK1_ROW-1) * 40) + HIT_ZONE_COLUMN | |
| 658 | + | sta SCREEN + (TRACK1_ROW * 40) + HIT_ZONE_COLUMN | |
| 546 | 659 | sta SCREEN + ((TRACK1_ROW+1) * 40) + HIT_ZONE_COLUMN | |
| 547 | 660 | | |
| 548 | - | sta SCREEN + (TRACK2_ROW * 40) + HIT_ZONE_COLUMN | |
| 549 | 661 | sta SCREEN + ((TRACK2_ROW-1) * 40) + HIT_ZONE_COLUMN | |
| 662 | + | sta SCREEN + (TRACK2_ROW * 40) + HIT_ZONE_COLUMN | |
| 550 | 663 | sta SCREEN + ((TRACK2_ROW+1) * 40) + HIT_ZONE_COLUMN | |
| 551 | 664 | | |
| 552 | - | sta SCREEN + (TRACK3_ROW * 40) + HIT_ZONE_COLUMN | |
| 553 | 665 | sta SCREEN + ((TRACK3_ROW-1) * 40) + HIT_ZONE_COLUMN | |
| 666 | + | sta SCREEN + (TRACK3_ROW * 40) + HIT_ZONE_COLUMN | |
| 554 | 667 | sta SCREEN + ((TRACK3_ROW+1) * 40) + HIT_ZONE_COLUMN | |
| 668 | + | sta SCREEN + ((TRACK3_ROW+2) * 40) + HIT_ZONE_COLUMN | |
| 555 | 669 | | |
| 670 | + | ; Colour the hit zones | |
| 556 | 671 | lda #HIT_ZONE_COL | |
| 557 | - | sta COLRAM + (TRACK1_ROW * 40) + HIT_ZONE_COLUMN | |
| 672 | + | sta COLRAM + ((TRACK1_ROW-2) * 40) + HIT_ZONE_COLUMN | |
| 558 | 673 | sta COLRAM + ((TRACK1_ROW-1) * 40) + HIT_ZONE_COLUMN | |
| 674 | + | sta COLRAM + (TRACK1_ROW * 40) + HIT_ZONE_COLUMN | |
| 559 | 675 | sta COLRAM + ((TRACK1_ROW+1) * 40) + HIT_ZONE_COLUMN | |
| 560 | 676 | | |
| 561 | - | sta COLRAM + (TRACK2_ROW * 40) + HIT_ZONE_COLUMN | |
| 562 | 677 | sta COLRAM + ((TRACK2_ROW-1) * 40) + HIT_ZONE_COLUMN | |
| 678 | + | sta COLRAM + (TRACK2_ROW * 40) + HIT_ZONE_COLUMN | |
| 563 | 679 | sta COLRAM + ((TRACK2_ROW+1) * 40) + HIT_ZONE_COLUMN | |
| 564 | 680 | | |
| 565 | - | sta COLRAM + (TRACK3_ROW * 40) + HIT_ZONE_COLUMN | |
| 566 | 681 | sta COLRAM + ((TRACK3_ROW-1) * 40) + HIT_ZONE_COLUMN | |
| 682 | + | sta COLRAM + (TRACK3_ROW * 40) + HIT_ZONE_COLUMN | |
| 567 | 683 | sta COLRAM + ((TRACK3_ROW+1) * 40) + HIT_ZONE_COLUMN | |
| 684 | + | sta COLRAM + ((TRACK3_ROW+2) * 40) + HIT_ZONE_COLUMN | |
| 568 | 685 | | |
| 569 | 686 | rts | |
| 570 | 687 | | |
| ... | |||
| 584 | 701 | bne draw_title | |
| 585 | 702 | draw_title_done: | |
| 586 | 703 | | |
| 587 | - | lda #$1A ; Z | |
| 704 | + | ; Track labels - use standard characters for letters | |
| 705 | + | lda #$1A ; Z (screen code) | |
| 588 | 706 | sta SCREEN + (TRACK1_ROW * 40) | |
| 589 | 707 | lda #TRACK1_NOTE_COL | |
| 590 | 708 | sta COLRAM + (TRACK1_ROW * 40) | |
| ... | |||
| 603 | 721 | draw_instr: | |
| 604 | 722 | lda instr_text,x | |
| 605 | 723 | beq draw_instr_done | |
| 606 | - | sta SCREEN + (23 * 40) + 8,x | |
| 724 | + | sta SCREEN + (23 * 40) + 6,x | |
| 607 | 725 | lda #TRACK_LINE_COL | |
| 608 | - | sta COLRAM + (23 * 40) + 8,x | |
| 726 | + | sta COLRAM + (23 * 40) + 6,x | |
| 609 | 727 | inx | |
| 610 | 728 | bne draw_instr | |
| 611 | 729 | draw_instr_done: | |
| ... | |||
| 617 | 735 | !byte 0 | |
| 618 | 736 | | |
| 619 | 737 | instr_text: | |
| 620 | - | !scr "customise me!" | |
| 738 | + | !scr "custom graphics active" | |
| 621 | 739 | !byte 0 | |
| 622 | 740 | | |
| 623 | 741 | ; ---------------------------------------------------------------------------- | |
| 624 | - | ; Initialize SID - Uses customisation constants | |
| 742 | + | ; Initialize SID | |
| 625 | 743 | ; ---------------------------------------------------------------------------- | |
| 626 | 744 | | |
| 627 | 745 | init_sid: | |
| ... | |||
| 635 | 753 | lda #$0F | |
| 636 | 754 | sta SID_VOLUME | |
| 637 | 755 | | |
| 638 | - | ; Voice 1 - uses VOICE1_* constants | |
| 639 | 756 | lda #$00 | |
| 640 | 757 | sta SID_V1_FREQ_LO | |
| 641 | 758 | lda #VOICE1_FREQ | |
| ... | |||
| 647 | 764 | lda #VOICE_SR | |
| 648 | 765 | sta SID_V1_SR | |
| 649 | 766 | | |
| 650 | - | ; Voice 2 - uses VOICE2_* constants | |
| 651 | 767 | lda #$00 | |
| 652 | 768 | sta SID_V2_FREQ_LO | |
| 653 | 769 | lda #VOICE2_FREQ | |
| ... | |||
| 659 | 775 | lda #VOICE_SR | |
| 660 | 776 | sta SID_V2_SR | |
| 661 | 777 | | |
| 662 | - | ; Voice 3 - uses VOICE3_* constants | |
| 663 | 778 | lda #$00 | |
| 664 | 779 | sta SID_V3_FREQ_LO | |
| 665 | 780 | lda #VOICE3_FREQ | |
| ... | |||
| 771 | 886 | rts | |
| 772 | 887 | | |
| 773 | 888 | ; ---------------------------------------------------------------------------- | |
| 774 | - | ; Play Voices - Uses customisation waveforms | |
| 889 | + | ; Play Voices | |
| 775 | 890 | ; ---------------------------------------------------------------------------- | |
| 776 | 891 | | |
| 777 | 892 | play_voice1: | |
| 778 | 893 | lda #VOICE1_WAVE | |
| 779 | - | ora #$01 ; Add gate bit | |
| 894 | + | ora #$01 | |
| 780 | 895 | sta SID_V1_CTRL | |
| 781 | 896 | rts | |
| 782 | 897 | | |
| ... | |||
| 793 | 908 | rts | |
| 794 | 909 | | |
| 795 | 910 | ; ---------------------------------------------------------------------------- | |
| 796 | - | ; Flash Tracks - Uses customisation colours | |
| 911 | + | ; Flash Tracks | |
| 797 | 912 | ; ---------------------------------------------------------------------------- | |
| 798 | 913 | | |
| 799 | 914 | flash_track1: | |
| ... | |||
| 804 | 919 | inx | |
| 805 | 920 | cpx #38 | |
| 806 | 921 | bne flash_t1_loop | |
| 807 | - | lda #1 ; White label | |
| 922 | + | lda #1 | |
| 808 | 923 | sta COLRAM + (TRACK1_ROW * 40) | |
| 809 | 924 | rts | |
| 810 | 925 | |