Pong Wars on the Commodore 64 - Imran Nazar
Back to Articles
15th Jul 2026<br>This post was first promised in early 2024[0]Pongwars on the C64, @Two9A, Feb 22nd 2024, but it got so long, I added a table of contents. That might be a first...
Introduction
Before we get started
Digression: Building a C64 disk image
The game loop: Movement calculations
Digression the second: Representing negative numbers
After movement comes positioning
An element of chance
The playing field
Keeping score
Digression the third: Binary long division
Rendering the score
Future considerations
Footnotes and citations
One day early in 2024, I was idly flipping through Mastodon when I came across this toot by Koen van Gilst[1]Pongwars recreated in JavaScript, @vnglst, Jan 2024:
Figure 1: The toot by @vnglst that sparked the descent into madness currently under discussion
This is a depiction of an eternal battle between the "day" ball and the "night" ball, to reverse the colour of the opposite ball's field. Many were transfixed, judging by the thousands of faves; I was one of them, and the thought arose unbidden:
If van Gilst saw this online and had to recreate it in JavaScript, and now I've seen it online, I have to recreate it on the Commodore 64, right?
Is such a thing even possible?
"Yes it is!" Well, it seems like it should be. This is a relatively simple animation, with a random element thrown in at the point the balls contact a surface that makes them bounce; defining "a surface that makes them bounce" seems like the tricky part.
So we need to do some planning. And as the first step, there's some defining:
We have the ball (two of them) which moves smoothly in straight lines;
There's a blockfield : the 20x20 block area over which the balls travel;
The blockfield has a boundary outside of which the balls can't travel, and they bounce off the boundary;
And there are two sides whose edges also cause the ball to bounce, but the block that gets hit reverses sides.
This is all complicated by the fact that, despite a lifelong interest in hacking at the C64, I'd never to this point written anything substantial in assembly for the machine. Looks like we'll be learning as we go.
On most machines of the era video was output to a CRT, as explained in my megathread on the 1541 disk drive from a couple of years back[2]"Why Was the Commodore 64 Disk Drive So Slow?", Imran Nazar, Dec 2024. After each frame of video output comes a short vertical blanking period, during which the video chip is not making any demands on memory; we'll be looking to try to limit any calculations needed for this animation, so they're completed within the blanking period and we're not unduly affecting other things that may be happening on the machine during the frame rendering period.
On the Commodore 64, the BASIC interpreter is the main thing that might be running during that period, and it provides some helpful things for this effect. One of those is a hook that gets triggered at the bottom of every frame[3]"Raster interrupt", C64 Wiki, updated Mar 2024, allowing custom code to be called every frame while keeping BASIC running. So we can plan out something like this for the code:
Figure 2: Flow chart for the main loop
Before we get started
So let's look at the initialisation portion: we'll be keeping an internal representation of the state of the blockfield, but there's also the on-screen blockfield to set up. The C64 has a 40x25 area of characters in its default text mode (each character being an 8x8 pixel square), and there are two areas of memory of interest here:
The screen RAM sits at $0400 by default, and is a thousand-byte area where the character values are stored;
The color RAM at $D800 is a thousand nybbles[A]A nybble is four bits, as opposed to an 8-bit byte. where each location can represent one of the C64's 16 colours.
Immediately after screen RAM sit the pointers to sprite data, but we're not setting up any sprites (yet), so a little shortcut is to write 1024 bytes to screen. This writes past the end of the screen, but makes the code much simpler:
SCRRAM = $0400<br>BLOCKPTR = $03 ; A block of two bytes not used by BASIC
init:<br>subroutine
; Set up a pointer to screen RAM, starting at the top left<br>; The 65xx CPUs are little-endian, so low byte first<br>lda #sta BLOCKPTR<br>lda #>SCRRAM<br>sta BLOCKPTR + 1
; We'll be writing SPACE (character 32), not char 0 which is @<br>lda #32
; Four lots of 256, for 1024 writes<br>ldx #4<br>ldy #0
.blank:<br>; The inner loop: runs from Y=0 through 255, 254, ...0 again<br>sta (BLOCKPTR), y<br>dey<br>bne .blank
; The outer loop: punt the pointer along by 256 each go-around<br>inc BLOCKPTR + 1<br>dex<br>bne .blank
(As an aside, BLOCKPTR is set to a value which is "not used by BASIC", but how do we know that? It turns out there's a detailed map of memory used by the C64, by Joe Forster of the STA demo group[4]Commodore 64 memory map, STA, last updated probably a while ago as the page is HTML 4.01, which states locations...