C64 Workshop Part 4

Collision Detection and Sound

The C64 has hardware collision detection built right into the VIC-II chip, and the legendary SID sound chip can create amazing music and sound effects. Let’s learn how to detect when sprites hit things and make some noise!

You can use your sprite and background from Parts 2 & 3 if applicable!

Objective

Create a program that when a sprite collides with something, a sound effect is played.

Requirements

Bonus


Hardware Collision Detection

The VIC-II chip automatically tracks sprite collisions so you don’t need to write complex overlap checking code! Two collision registers tell you exactly which sprites hit something:

Sprite-to-Background Collision: Address 53278 ($D01E)

Sprite-to-Sprite Collision: Address 53279 ($D01F)


The SID Sound Chip

The SID (Sound Interface Device) has three independent voices that can play simultaneously. Each voice has its own frequency, waveform, and envelope controls. Think of them like three instruments in a band—you can play a melody on voice 1, a bass line on voice 2, and sound effects on voice 3, all at the same time.

Each voice can produce four waveforms: triangle (smooth, flute-like), sawtooth (bright, brassy), pulse (hollow, clarinet-like), and noise (percussion, explosions).

To make a sound, you need three steps:

  1. Set the volume (register 54296) - controls all voices
  2. Set the frequency/pitch (registers vary by voice)
  3. Turn on the waveform and gate (register varies by voice)

SID Base Address: 54272 ($D400)

Voice Register Layout:

Each voice has the same register pattern, offset by 7 bytes.

Key Registers (Voice 1):

Think of frequency like a 16-bit number split across two bytes. Higher numbers = higher pitch.


Resources / Reference

Reading Collision Registers

Always save the collision value to a variable first, because reading the register clears it:

10 C=PEEK(53278): REM SAVE COLLISION VALUE
20 IF C AND 1 THEN PRINT "SPRITE 0 HIT!"
30 IF C AND 2 THEN PRINT "SPRITE 1 HIT!"
40 IF C AND 4 THEN PRINT "SPRITE 2 HIT!"

The AND operator tests individual bits:

  • AND 1 = bit 0 (sprite 0)
  • AND 2 = bit 1 (sprite 1)
  • AND 4 = bit 2 (sprite 2)
  • AND 8 = bit 3 (sprite 3), etc.

Why save first? If you do IF PEEK(53278) AND 1 twice, the second read will always be 0 because the first read cleared the register!

Playing a Simple Sound

Basic sound effect:

10 S=54272: REM SID BASE
20 POKE S+24,15: REM MAX VOLUME
30 POKE S+0,50: POKE S+1,100: REM FREQUENCY
40 POKE S+4,17: REM TRIANGLE WAVE, GATE ON
50 FOR D=1 TO 50: NEXT D
60 POKE S+4,16: REM GATE OFF

How it works:

  • Line 20: Sets volume to maximum (15)
  • Line 30: Sets frequency (low byte 50, high byte 100)
  • Line 40: Bit 4 (value 16) = triangle wave, bit 0 (value 1) = gate on. 16+1=17
  • Line 60: Gate off (bit 0 = 0) to stop sound

SID Waveforms

Register 54276 controls the waveform type and gate. The gate (bit 0) starts/stops the sound:

Waveform Bit Value Binary Gate On Binary
Triangle 4 16 00010000 17 00010001
Sawtooth 5 32 00100000 33 00100001
Pulse 6 64 01000000 65 01000001
Noise 7 128 10000000 129 10000001

The “Gate On” value is waveform value + 1 (sets bit 0).

10 POKE 54276,33: REM SAWTOOTH (32) + GATE (1)
20 FOR D=1 TO 100: NEXT D
30 POKE 54276,32: REM GATE OFF (BIT 0 = 0)

Why turn gate off? The ADSR envelope only triggers when gate goes from 0→1. If you leave it on and try to play another note, it won’t retrigger.

Musical Note Frequencies

The frequency is stored as a 16-bit number split across two registers. The high byte goes in one register, the low byte in another. The formula is: Frequency = (High × 256) + Low

Middle octave note values:

Note Low High Note Low High Note Low High
C 25 68 E 102 85 G# 222 107
C# 143 72 F 110 90 A 26 114
D 162 76 F# 142 95 A# 117 120
D# 205 80 G 179 101 B 230 127

Example - Play middle C:

10 POKE 54272,25: POKE 54273,68
20 POKE 54276,17: REM TRIANGLE, GATE ON

Changing octaves: The table shows middle octave values. To shift octaves, calculate the full 16-bit frequency, multiply or divide by 2, then split it back into low/high bytes. For example, middle C = (68×256)+25 = 17,433. One octave higher: 17,433×2 = 34,866. Split: high = 34,866÷256 = 136, low = 34,866 MOD 256 = 50. This only works for transposing the SAME note up or down octaves.

ADSR Envelope

Shape your sounds with Attack, Decay, Sustain, Release:

  • 54277: Attack/Decay (high nibble = attack, low nibble = decay)
  • 54278: Sustain/Release (high nibble = sustain, low nibble = release)
10 POKE 54277,9: REM ATTACK=0, DECAY=9
20 POKE 54278,0: REM SUSTAIN=0, RELEASE=0

Higher attack = slower fade-in. Higher decay = slower fade-out.

Multi-Coloured Sprites

Regular sprites use 1 bit per pixel (on/off = 24 pixels wide). Multi-coloured sprites use 2 bits per pixel (4 colour choices = 12 pixels wide).

Pixel bit patterns:

  • 00 = Transparent
  • 01 = Multi-colour 0 (register 53285) - shared by all sprites
  • 10 = Sprite individual colour (register 53287-53294) - unique per sprite
  • 11 = Multi-colour 1 (register 53286) - shared by all sprites

Enable multi-colour for sprite 0:

10 POKE 53276,1: REM BIT 0 ON
20 POKE 53285,6: REM MULTI-COLOUR 0 = BLUE
30 POKE 53286,3: REM MULTI-COLOUR 1 = CYAN
40 POKE 53287,2: REM SPRITE COLOUR = RED

How sprite data changes:

Normal sprite byte: 10101010 = 8 pixels (alternating on/off)

Multi-colour byte: 10101010 = 4 pixels read as 10, 10, 10, 10 = four red pixels (using sprite individual colour)

Another example: 11111111 = 11, 11, 11, 11 = four cyan pixels (multi-colour 1)

Mixed: 00011011 = 00, 01, 10, 11 = transparent, blue, red, cyan (one of each!)

Multi-Colour Sprite Editor

Click pixels to cycle through colours (colour 1/2/3/transparent). Click colour swatches to pick from C64 palette:

Multi-Colour 0 (01):
Sprite Colour (10):
Multi-Colour 1 (11):


Collision and Multi-Colour Registers

Register Address Purpose
Sprite-Background Collision 53278 Bit N = sprite N hit background
Sprite-Sprite Collision 53279 Bit N = sprite N hit another sprite
Multi-Colour Enable 53276 Bit N = sprite N uses multi-colour
Multi-Colour 0 53285 Shared colour for 01 bits
Multi-Colour 1 53286 Shared colour for 11 bits

SID Sound Chip Registers

Register Address Purpose
Frequency Low (Voice 1) 54272 Low byte of pitch
Frequency High (Voice 1) 54273 High byte of pitch
Waveform/Gate (Voice 1) 54276 Bit 0=gate, bits 4-7=waveform
Attack/Decay (Voice 1) 54277 High nibble=attack, low=decay
Sustain/Release (Voice 1) 54278 High nibble=sustain, low=release
Master Volume 54296 Volume level (0-15)

Note: Voice 2 registers start at 54279, Voice 3 at 54286.


Example: Collision Detection with Sound

This minimal example shows a sprite moving automatically that detects collision and plays a sound:

10 REM SETUP SPRITE
20 POKE 2040,128: POKE 53269,1: REM POINTER AND ENABLE
30 POKE 53287,1: REM WHITE
40 GOSUB 1000: REM LOAD SPRITE DATA
50 REM DRAW OBSTACLE
60 FOR I=1024 TO 1063: POKE I,160: NEXT
70 REM SETUP SID
80 POKE 54296,15: REM MAX VOLUME
90 X=100: Y=100: DX=1: DY=1
100 REM MAIN LOOP
110 X=X+DX: Y=Y+DY
120 IF X<24 OR X>320 THEN DX=-DX
130 IF Y<50 OR Y>229 THEN DY=-DY
140 POKE 53248,X: POKE 53249,Y
150 C=PEEK(53278): REM SAVE COLLISION
160 IF C AND 1 THEN GOSUB 300
170 GOTO 110
300 REM COLLISION!
310 POKE 53287,2: REM RED
320 POKE 54272,50: POKE 54273,100
330 POKE 54276,129: REM NOISE, GATE ON
340 FOR D=1 TO 30: NEXT D
350 POKE 54276,128: REM GATE OFF
360 POKE 53287,1: REM WHITE
370 RETURN
1000 REM SPRITE DATA AT 8192
1010 FOR I=0 TO 62: READ A: POKE 8192+I,A: NEXT
1020 RETURN
1030 DATA 0,126,0,1,255,128,3,255,192
1040 DATA 7,231,224,15,255,240,31,255,248
1050 DATA 31,255,248,15,255,240,7,231,224
1060 DATA 3,255,192,1,255,128,0,126,0
1070 DATA 0,0,0,0,0,0,0,0,0
1080 DATA 0,0,0,0,0,0,0,0,0
1090 DATA 0,0,0,0,0,0,0,0,0

Key lines:


Tips for Success


Previous: Part 3 - Sprites and Joystick Control | Back to Home