I came across a C64 BASIC game that ran pretty slow, so I challenged myself to see how much I could speed it up.
Today’s C64 BASIC optimisations are around making a pong-style bat and ball game from Tom‘s online tutorial play a bit better.
You can see the before and after below, with only minor alterations on the original to get it to run outside of Tom’s interactive tutorial on regular C64 and emulators:
Before and after optimising the BASIC codeOriginal Code
If you look closely you can see there are a couple of issues with the original code as it was developed to run within Tom’s embedded BASIC interpreter, not on a C64.
First, the C64 starts addressing screen memory at 1024, so to make this work any of the screen pokes need 1024 added.
Secondly, in C64 BASIC we can not poke using CHR$, so that would need to be changed also.
Regardless, the code was of course written with clarity in mind rather than prioritising speed. This means we have some opportunities to make it run better, even under interpreted BASIC.
My Version
Remember anywhere I use {} these are for my online developer environment or a desktop IDE that understands these tokens, otherwise you need to replace them with the appropriate key on the C64 keyboard, and {13} is the Return key.
What I did in brief:
- Roughly halved the line count.
- Removed nearly all unnecessary GOTO statements.
- Reduced frame delays by removing as much processing overhead as possible.
How the Optimised Version Works and Why It’s Faster
If you compare the two listings, the program logic is almost identical, but the rewritten version executes more smoothly, takes less memory, and is easier to follow.
In the main, my goal was to reduce the amount of work the BASIC interpreter must do per frame, by cutting redundant calculations, and by replacing the slow multiple character-by-character operations for the paddle with the built-in PRINT command.
Easy line-count reductions:
Check the variable initialisation:
Each line forces the interpreter to look up, parse, and execute a statement. Combining them onto one line like this:
saves several interpreter passes. On a Commodore 64, even small reductions like this can add up, especially inside loops or setup sections that run often. It also makes it clear at a glance which variables form the game’s initial state.
Using Strings for Complex Graphics
Originally, the paddle was drawn by six separate POKE statements, each involving math and conversions:
This means the interpreter has to calculate and update each memory location individually. The improved version builds a string once that contains both the paddle graphics and cursor control codes:
Then it simply sets and prints that string at a specific cursor row position using a built-in ROM routine:
The system call (SYS 58732) positions the cursor using the screen editor routine, which is written in machine code and runs far faster than any BASIC equivalent. By letting the computer handle cursor motion and character drawing, we save both code and CPU time.
Streamlining the Game Loop
The old loop contained many lines like:
I combined and simplified them to give the same result:
The logic is the same, in that it reverses ball direction when hitting a boundary, but we use short-circuit conditions (OR) and simple arithmetic (DX=DX*-1) instead of separate checks and assignments.
Multiplying by -1 to reverse direction is both more compact and faster. The reduced branching means the interpreter does fewer scans for line numbers, which can be one of the slowest parts of BASIC execution.
Quicker Collision Detection
The paddle collision now checks the entire range of possibilities in one expression:
This replaces multiple individual comparisons and jumps. It’s more efficient and also easier to read. One glance shows that the ball only passes the paddle safely if it’s within the range P to P+4.
You will also notice the weird double IF. A quirk of C64 BASIC is IF THEN IF executes a bit faster than IF AND, possibly because after the first check it can drop out right away, avoiding the next check.
Smarter Screen Drawing
In the earlier version, the code directly erased and redrew the ball every frame using calculations in multiple places. The revision tracks both the old and new screen locations:
First it erases the old position (OB), then calculates and draws the new one (B). This method avoids repeating the multiplication and addition that converts coordinates to screen memory each time and prevents ghosting because it always erases exactly the previous position.
Faster Input and Bounds Control
Rather than checking the ASCII value of each keypress (ASC(K$)), the optimised listing compares directly with strings:
Direct string comparison is simpler and slightly faster. Storing OP (old paddle position) allows an immediate correction if the player tries to move off-screen:
This single condition replaces a chain of limit checks, still keeping the paddle constrained to the visible area.
Friendlier Game Over Condition
The old “Game Over” routine dropped to BASIC. The new version clears the screen and immediately prompts for replay:
Optimisation Process
The key ideas applied here are broadly useful for any C64 BASIC program:
- Reduce line numbers and branches. Each new line requires the interpreter to search the program listing so fewer lines mean less searching.
- Cache repeated calculations with variables. Store results like screen addresses and reuse them instead of recalculating.
- Use built-in ROM features where possible. Printing strings or calling ROM routines (SYS commands) can outperform BASIC loops dramatically.
- Combine statements on one line. The colon (:) lets you group short statements, improving flow and performance, especially when part of an IF condition.
- Keep logic compact. Logical operators and arithmetic sign changes can often replace multiple IF and GOTO statements.
- Think about readability as well as speed. Well-structured code is easier to read therefore easier to optimise and debug later.
Taking it Further
We are not done, though, because a great improvement can be made by going from interpreted BASIC to a compiled version! Remember XC-BASIC? In my next post I will show how we can take the working C64 BASIC version and with minimal changes get it to compile with XC, and what a speed difference that makes …
.png)
![Someone built a CRT case for the Switch 2 [video]](https://www.youtube.com/img/desktop/supported_browsers/edgium.png)
