Previous Post: Infinity is More Than Ten, Right?
1983. Like all games written for the early Apple II computer, the original Dino Eggs -- with all the features I had designed into it -- needed to fit into a limited space. Exactly 48K of RAM memory.
How much is 48K?
Today’s home computer – or smartphone, tablet, or micro SD card – can easily contain 8 Gig of memory. This is over 160,000 times more than 48K.
(That does not count the additional and separate caches of memory that today's devices rely on, like video memory. Nor does it count the additional hard drive space available to laptop and desktop computers – a resource unavailable to the early Apple II’s.)
How little is 48K?
If you type text into Windows Notepad, you'll exceed 48 K in about 20 screen fulls.
If you record CD-quality audio, you'll exceed 48K in a half-second.
Video? Forget about it.
All of these calculations refer to uncompressed data. And that's part of the point. Nothing was compressed or dynamic within the Apple II. With a few exceptions, every byte was written to remain a whole byte and stayed where you put it. If you wanted a byte to mean more than 8 bits, you needed to write your own self-modifying code, or game-specific data compression/extraction routines.
The review of Dino Eggs published in Softalk magazine -- Thank you, Al Tommervik and Margot Comstock! -- was based on an early development disk of the game that used an extra 16K of memory within the Apple II referred to as “Language Card” memory.
Softalk’s game reviewer mistakenly assumed this meant that the commercial version of Dino Eggs would require 64K of RAM, and said so in his review.
My game publisher was livid with anger and fear. At that time, for an Apple II product to require 64K was practically a death sentence for sales.
So, how did I squeeze Dino Eggs into 48K? Reviewing my source code thirty-two years later, here’s what strikes me:
- All of the horizontally-moving creatures in Dino Eggs (the proto-snakes and proto-pedes) share code. They differ primarily in the bitmaps called, and whether I moved them every frame (for the faster proto-pedes) or every other frame (for the slower snakes). As with the Baby Dinos and Spiders, I could afford only three of them being on the screen at the same time.
As revealed by an early sketch of Dino Eggs in my previous post, I conceived of the game's horizontal creatures as "roaches." On screen, they became proto-snakes and proto-pedes. In the code, they remained roaches.
- My core graphic routine was a central chunk of self-modifying code. Before moving or copying any graphics, I wrote bytes ahead to set the source and destination of the move – and the operation codes (the verbs) needed to accomplish what I wanted. That is -- Is this operation an erase (STZ), a copy-over (STA), a copy-onto (ORA), does it use a graphic mask (AND), etc. This approach saved memory, and also made the core graphic loops faster.
"DEPG3" was the behind-the-scenes hi-res page on which all my source graphics were stored. Even though it was never directly visible on the Apple II hi-res pages, DEPG3 used the same pixel-addressing scheme as the Apple's hi-res pages 1 and 2. This saved time and code when moving graphics around from page to page during the game.
- The Apple II hi-res graphic screens used an arcane addressing system that left 8 bytes of memory unused at the far edge of each $100 (256) bytes. I used those 8 bytes for scratch space. I just had to make sure that when I erased or moved the graphic screen data, I didn’t overwrite anything I needed in these scratch spaces.
- In the 6502 code, the jump op code (JMP) took three bytes, but could take you anywhere within the 48K. In contrast, conditional branch codes (like BNE) took only two bytes, but could branch only so far away in the code. So, whenever I could, I used branches to take the code where it needed to go, even if that meant “piggy-backing” a chain of branches to get from point A to point B. Yes, that’s right, sloppy “spaghetti” code, but it fit.
In lines 1047 and 1050, the code branches (BNE, BCC) to the pre-existing jump (JMP) in line 1040 rather than branching around a newly-entered jump, thus saving three bytes per branch. Also, the assembler could accept only so many labels per file of source code -- so "ATLVL-3" is a make-shift way of labeling line 1040.
- Similarly, the return-from-subroutine op code (RTS) took one byte that I could sometimes avoid using. When I could, I branched to an already-existing RTS – even if that RTS was written as part of some other subroutine. The chip didn’t know the difference. A byte saved is a byte earned.
One step at a time. Effectively, there was only "one thing going on" within the Apple II at any given time. There was nothing to hand anything off to. Even when you called the floppy disk read/write routines, you couldn’t do anything else until they were done and handed control back to you. There was nothing happening "on the side" or in parallel. There was no way of telling anything “Let me know when that happens.”
If you were giving the speaker a little "tick", you weren't updating the screen, or doing anything else. Any interleafing of routines had to be written into the code.
Speed. The Apple II clocked in at 1 Megahertz (basically a million cycles per second). Roughly speaking, computer speeds today are 3,000 times faster (if you compare them cycle to cycle) – or 24,000 times faster (if you compare the number of bits processed per second).
So, here’s an odd picture. While 160,000 running copies of Dino Eggs could fit inside today’s typical computer’s 8 Gig of RAM, the same computer’s typical processor could not keep all of those instances running at original speed. It could manage to run only 24,000 of them.
Only 24,000 instances of Dino Eggs running within one computer at the same time? Pathetic!
These 24 mini-screens represent 0.1% of the instances of Dino Eggs that – in gross theory – a typical contemporary computer could keep running simultaneously at original speed.
Pixels. Animation was made possible by hires-page flipping. If you poked byte $C054, the Apple II displayed memory locations $2000 to $3FFF as a grid of 280 by 192 pixels on the screen. If you poked byte $C055, it displayed memory locations $4000 to $5FFF in the same format. This was the powerful curtain behind which we programmers could construct our magic.
The fundamental challenge was: How many of the “backstage” pixels could you change before having to flip the pages – thus making the “backstage” page into the visible page – while keeping the frame rate (animation speed) sufficiently fast?
There was no native support for sprites or collision detection. Every pixel was just a pixel that you turned on or off. The system has no special awareness of any pixels being “in front” or “behind” or being part of a moving figure.
Odd-numbered pixels could be green or orange. Even-numbered pixels could be violet or blue. Any two adjacent pixels (of any color) could create white or black. Only seven bits of each byte were visible -- an odd number -- thus any given byte value would display as different colors in different columns on the screen.
All this made for an odd set of graphic handcuffs. And yet, amazingly, it worked. I have found that technical limitations – even strange, arbitrary technical limitations like those described above – often don't ruin the aesthetic results, but can actually enhance them.
Sometimes the smaller the keyhole – the greater the effort in squeezing things through that keyhole – the higher the quality as a result.
I felt very comfortable with my ability to illustrate and animate using those huge chunky 8-bit 6-color pixels. Gradually over the years – as evolving technology gave me more and more pixels and more and more colors to work with – I became less comfortable with my abilities.
Around 1963. My earliest encounter with a computer was an indirect one. I was about 7 years old and visiting the Chicago Museum of Science and Industry with my father. The museum had dedicated a large room to the display of a computer that could play Tic-Tac-Toe with visitors. The size of the room was necessitated by both the size of the computer and the size of the crowds it attracted.
In my memory, it plays like a dream sequence from an episode of Mad Men. A large sweaty roomful of dress-suited men wearing fedoras, the air heavy with testosterone and Old Spice. Each man in the swarm, my father among them, pressing politely yet assertively forward to get his chance at the single computerized console of nine fat pushbuttons. Each man convinced that he can beat the computer at Tic-Tac-Toe .
But, of course, none of them could. The strategy for not losing a game of Tic-Tac-Toe is a fairly simple one. If you know the strategy, a tie is the worst that can happen. That’s what the computer had been programmed to do. Not lose.
But all this was so new, the challenge fascinated and obsessed every male in that room. Even at the age of seven, I could feel the energy swirling from male to male around me:
Surely I can beat it...!
No, surely I can beat it...!
No, surely I can...!
I didn’t have a chance (or the perspective at that age) to examine the computer’s visual display – the crowd was so thick, I don’t think I ever got closer than 25 feet to it -- but it no doubt involved individually-wired 60-watt incandescent light bulbs, theatrical gel color filters, a hand-built wooden frame, and lots of black felt and dark curtain.
But I will never forget the urgent press of the men in that room. That energy remains at the essence of our industry.
Fundamentally -- what we do doesn’t have anything to do with video resolution, or the number of pixels. It doesn’t have anything to do with speed, or hand-eye coordination. It is about framing a story. It is about framing a challenge.
"Surely I can beat this thing...!"
Fast forward to 2013. Thirty years after I wrote the original Dino Eggs, I have a chance to revive and expand the game.
What in the world could something like “Dino Eggs” be now?
To Be Continued...
Next Post: Labor of Love