👾 One Byte at a Time: The Story of a C64 Game
When I first sat down in front of a Commodore 64 again — decades after the 8-bit wars had ended — it felt a bit like uncovering an ancient map.
Not just any map, but one drawn with memory addresses, sprites, and sound waves.
A blueprint for magic.
This time, though, I wasn’t just a kid mashing keys to load LOAD"*",8,1
.
I was an adventurer. A creator.
And the code you’re about to see?
It’s not just a list of instructions — it’s the story of a game being born.
Chapter 1: The Memory Map
Every epic needs a world.
On the Commodore 64, that world is the memory.
The game begins with the Zero Page — the bustling harbor where the CPU keeps its most precious cargo for fast access.
Above it, the Screen Memory at $0400
is like the marketplace, where characters chatter and colors flash.
Then, towering high at $C000
, we find the Upper RAM Area, a sort of castle stronghold where scores and secret variables are tucked safely away.
And overlooking it all, like a mighty sorcerer, stands the VIC-II chip at $D000
— controlling the sprites, the colors, and the dance of pixels across the screen.
Memory wasn’t just storage.
It was territory.
And every address we defined — from SPRITE0_X
to SIDV1_FRQL
— was another landmark on our growing map.
Chapter 2: Sprites Awake
With the world prepared, it was time to breathe life into our heroes: the sprites.
Imagine the screen as a blank theater stage.
The paddles (our players), the ball, and the scoreboard sprites were summoned by setting up their pointers:
lda #$80
sta SPRITE0_PTR
lda #$20
sta SPRITE0_X
...
The paddles lined up like eager actors, rehearsing their moves.
The ball, small but full of mischief, waited center stage.
And somewhere above, the scoreboard began blinking into existence, ready to tally the victories.
Each sprite wasn’t just a graphic.
It was a character, with a position, a color, and even a personality.
The paddles moved swiftly and surely.
The ball darted unpredictably.
This was more than data.
It was the heartbeat of the game.
Chapter 3: The Battle of Joysticks
The players entered through the gates of CIA_PORTA
and CIA_PORTB
— the joysticks.
They spoke in simple signals:
- Pressed down?
AND #$02
- Pressed up?
AND #$01
Each command translated directly into motion, the players pushing their paddles up and down the battlefield.
But in true gladiatorial style, there was a limit.
Move too high, and the sprite would be clamped at $32
.
Too low, and $CF
would stop you.
Balance was enforced by code and gravity alike.
Chapter 4: Game Loop, the Pulse of Life
At the heart of it all, a simple but relentless drumbeat:
GAME_LOOP:
jsr PLAYER_MOV
jsr BALL_MOV
jsr SLOWDOWN
jsr GOAL_CHECK
jsr SCORE_REND
jsr WIN_CHECK
jsr COLL_CHECK
jmp GAME_LOOP
The main loop.
A tireless conductor, orchestrating the dance of the paddles, the furious chase of the ball, the cries of victory or defeat.
Every jump back to GAME_LOOP
was another heartbeat.
Another second of gameplay, woven from thousands of tiny electrical impulses across the 6510 CPU.
And if something went wrong — a goal scored, a collision detected — the code responded like a living creature.
Chapter 5: Sound, Light, and Glory
Of course, no battle is complete without music.
Whenever a collision happened or a point was scored, a triumphant sound erupted from the legendary SID chip:
SOUND:
sty SIDV1_FRQH
lda #$20
sta SIDV1_FRQL
lda #$11
sta SIDV1_CTRL
The SID didn’t just beep.
It roared.
It cried.
It sang the song of battle.
Meanwhile, the border colors blinked furiously during resets, a visual fireworks display controlled by careful writes to BORDER_COL
and BGND_COL
.
The game wasn’t just seen.
It was felt.
Chapter 6: Victory (and Reset)
Every goal pushed the players toward their destiny.
Score 10 points, and — like a cycle of life and death — the game didn’t just end.
It restarted.
The paddles lined up again.
The ball floated gently to its center position.
A new battle was born.
Because in the world of the Commodore 64, the story never truly ended.
It simply looped forever.
💬 Epilogue: Why This Matters
You might look at this mass of labels, .byte
declarations, and bitwise operations and think:
“This is too low-level. Too hard.”
But when you see it with the eyes of a storyteller, something else appears:
A world built from nothing.
A symphony played by electrons.
A game where every line of code is another verse in a timeless epic.
On a machine from 1982, we built a living universe — one memory address at a time.
And the beauty is, you can too.
Eager to play now?
Try it on the emulator or on a real C64, here is the assembled code.
And here is the full source code, 500 lines of Assembler Code.