1. The start
One month ago I wanted to start developing a game in 6502 assembly (for fun and not profit). I had never done anything like it and it seemed like a good way to learn more!
The beginnings were quite hard and I kept a little bit of code lying around for weeks before managing to shape it into something that was capable of making a little sprite move around. This was the first commit of that game. I had to write a C version of the algorithm to get it right, and at that point it was not even capable of merging the sprite with the background. At first, it was very frustrating and I felt incompetent, but the feeling faded away fast after I managed that difficult (to me) first step.
I hand-coded the sprite from a LibreOffice Calc sheet for this first result. For smooth horizontal moving of a sprite on Apple II, one has to create 7 versions of the sprite, each offset by one pixel, as one (monochrome) HGR pixel is a bit and there are seven pixels per byte. This was really tedious and unmanageable:

So I went on my first side-quest and coded a sprite generator in C. It takes a png as input and outputs a ca65 assembler file with the data:


2. Sprite moving cleanups
After this, I went to fix my sprite blitting code so that it can save the background, mask the sprite on the background, and restore the background. This gave more satisfying results moving the plane around.
I then implemented collision detection (via hitboxes defined in assembly), and the vents, which float the plane up and are the main game mechanism. These were the first implementation and will need to be refined later.
After trying a few different things, I settled on a format for the levels. Everything is defined in one file, like this level0.s: the sprites, their location etc, the blockers (obstacles drawn on the background), and vents.
In Glider, one can defend oneself using rubber bands. I wanted to implement that, and did it in three steps. First, adding the sprite and the firing mechanism; then, adding a hitbox check between the rubber band and the “enemy” – in this case, the balloon, but in the end it would be multiple objects that could be destroyed by a band, so I added a “destroyable” property in the sprites definitions.
As I made that last step too quickly, I had duplicated the hitbox-checking code and I was disappointed by that, so I factorized both checks in the next commit. Little did I notice that I kept half the plane’s X and Y coordinates hardcoded in the check algorithm, which meant that I introduced a bug where the enemy would only be destroyed if the plane was still at the same height. I fixed that a few days later.
3. Game elements
At that point, I started adding more “general” things to the game. It needed a dashboard so that the player could see their score, remaining number of lives, a basic way to go to the next level, etc. The dashboard brought the need to print characters on the graphics screen, so it came with a basic font renderer. At that point, I tried the game on real hardware, and noticed that my plane’s sprite was “tearing” when it was high up in the screen. As the game loop starts at the beginning of vertical blanking, elements at the top of the screen have to be painted before the CRT’s ray is back to the top-left corner – a thing so important back then that it has a name, “racing the beam“. Re-ordering sprite drawing so that the plane is done first fixed the problem.
4. The sound
At that point I wanted to add sound, because silent games are less fun. I experimented a bit with busy looping the speaker, but the results were not good, the effects seemed fake, and it was a lot of manual work and testing different values etc. I took a side-quest that resulted in a good enough sound sampling playback. This takes quite a bit of memory, and I am limited to very short samples if I want the rendering interruption to be un-noticeable, but I does sound good. I made it a general solution so I can reuse it in other projects if necessary, and wrote an article about it: A simple way to play sound samples on the Apple II. I’m quite happy about it as it works as well on 6502 as it does on 65c02, and provides the developer choices on the size-versus-quality spectrum.
5. More game
I started adding more levels and more elements, and at some point, felt the need to have an inter-level screen, showing the player how they did. That required to extend my 5×5 pixels font to add every ASCII characters, as it was so far limited to displaying numbers. At that point, I had been “working” with Milen, @circfruit@fosstodon.org, so that the game would have a Bulgarian translation.

That required, obviously, to extend the cyrillic font too, and that was a bit bothersome, and also, it suddenly felt stupid when I realized we would need larger characters, and we were basically going to ship a copy of the character ROM. So I ditched that, and redid the inter-level screen in text mode.
6. Scale for size
After making five levels, I realized that having all the levels data inside the main binary was making it bigger and bigger. I reworked the linker configuration so that each level file would contain the level’s background, but also the levels’ specific data – blockers, vents, sprites. The memory layout of Glider was now looking like this when launching the game:
0x00 – 0x400 | Not for Glider |
0x400 – 0x800 | Text screen memory |
0x800 – 0xC00 | File I/O buffer |
0xC00 – 0x2000 | Nothing |
0x2000 – 0x4100 | Nothing |
0x4100 – … | Glider’s main binary |
And after loading the main binary, the first thing to do is open a “LOWCODE” file containing more of the program, and load it at 0xC00 up to 0x2000. Then open a level file, and load it at 0x2000 up to 0x4100. That leaves 256 possible bytes of code after the level’s background for the level-specific data, which is more than enough. That made each level file weigh in between 8192 and 8448 bytes. It was fine until I arrived at seven levels, at which point I realized that I could only ship eight or nine levels on a single floppy disk. It bothered me at that time.
7. Counting time
In the original game, the clocks are bonuses that gives the player points, but also the player gets bonus points when they complete a level fast enough, under a certain amount of time. I wanted to add that mechanism, but how to count seconds on an Apple II, which has no real-time clock unless an after-market card is installed ? It is actually not too hard when your game already uses the vertical blanking signal to trigger drawing the screen: the VBL happens regularly and at a fixed rate: either 60Hz on US models, or 50Hz on European models. So one second passes every 60 or 50 blankings. There is no simple way to determine that value from a specific memory location or anything, so the solution to that is to count cycles! Wait for vertical blanking. Start counting. Stop counting at the next VBL. If you waited about 17000 cycles, you’re at 60Hz! About 20000? 50Hz.
8. Shipping more levels
8 or 9 levels did not seem enough, as I had ideas for a few more, but I had to make them fit. I started investigating solutions and figured that LZ4 decompression is a simple enough algorithm that a 1MHz 6502 can tackle it efficiently, and even that cc65, my compiler of choice, ships with one in its runtime!
Out of principle, I went on a little side quest to optimize cc65’s LZ4 decompressor, squeezed 27 bytes and 15% cycles out of it, and adapted my build chain and code to load LZ4-compressed data. This made my levels files shrink by 50-80%, and theoretically, allows me the fill my floppy with as much as 25-35 levels!
It even is faster to load 3kB and decompress it than to load 8kB, so this was a double win! An annoyance I had at first is that the LZ4 decompressor requires one to load the full compressed data before decompressing it, which meant that I had to do with a 6kB temporary buffer, which is a lot of space. I reworked that to read the compressed data at 0x2000, figure out the compressed size from the header, move it to the far end of the available space do avoid the decompression overwriting the end of the compressed data, and decompress it. This is a bit more costly in terms of time, but not nearly enough to be noticeably annoying.
9. More generalization
With that much levels possible, it seemed very linear to go left-to-right from one level to another one. As the original Glider had, I wanted to have stairs, leading other ways in the house, and I wanted to be able to visit the house, not just run through it. So, I implemented dynamic exits instead of hardcoding the right edge of the screen to lead to level N+1: each level would now implement an arbitrary number of exit zones, each pointing the game engine to the level to load when exiting some place. I added exits on the left on levels, to be able to go back to previous levels. Of course, each level’s time bonus would only be counted once. Then I implemented levels with stairs.
10. Wrapping up
I made 14 levels. We took a long week-end off at a friend of my spouse, and there we had nature and snow, so I took a picture of the landscape, and used it as the last level’s background for memories. I put up a beta version and some folks tested it, and a nice person, Japhy Riddle, even sent an Apple II sprite my way to replace a Macintosh SE. I put it in the first level. It’s now time to release!
I hope that my game is good enough to respect the work of John Calhoun and that he will be okay with the name I chose, “Glider for Apple II”, along with a nod to his work in the splash screen. I tried to make it clear it’s not the same game or author, and I want to thank him there for providing me hours of entertainment as a child playing his game, and more hours of joy rewriting it for the retro computer I love.
Also I found out with joy that John seems like a cool guy, and he open-sourced Glider a few years ago.
If you want even more details of the Glider for Apple II’s implementation journey, I have documented it on a very long Mastodon thread.
You’re also very welcome to head to the main Glider for Apple II page, download the floppy image and try it yourself!