After a couple of weeks of breaking my brain on the Atari 2600 game I’ve been working on, I’ve finally made some significant progress. I now have a gradient sky and grass background, the castle, a player-controlled ballista, and the ability for the ballista to shoot an arrow upwards into the sky when the player presses the joystick button!
After my last post on the game, I spent three weeks working on a general purpose “kernel” (the part of the program responsible for drawing what you see on the screen) that would do everything I wanted in one little package. It took too long for me to realize that this was never going to happen. There is just not enough processing power for it in the Atari 2600.
The visible image displayed by the Atari is divided into 192 horizontal lines (called “scanlines”). This means the Atari effectively outputs a 192p resolution image — a far cry from the 1080p resolution many TVs have today.
Each scanline is drawn from the left to the right. In old CRT televisions, an electron beam is shot at the screen from the inside of the tube. This beam is what actually illuminates the screen with colors.
After the right edge of the screen is reached, the electron beam moves back to the left and down in order to start drawing the next scanline.
During each scanline, the Atari programmer has 76 “cycles” worth of processor time to execute commands which will tell the TV what to draw on that specific line. Individual instructions (like adding two numbers, storing a value into memory, reading a value from memory, etc.) typically take between 2 and 7 cycles each to execute. At best you can perform 38 2-cycle-long instructions per line. In reality, however, doing something useful will require that you run instructions that take more than 2 cycles to run.
As if being starved of processing power wasn’t enough, you actually only get 22 of those 76 cycles to work with before the graphics for that line start getting drawn! (Technically, this is the time that the electron beam is not drawing as it moves from the right edge of the screen back to the left edge in order to start drawing the next scanline). If you try to tell the Atari to draw something on the left edge of the screen by the 23rd cycle in the scanline, it’ll be too late and the TV won’t draw what you wanted. Because of this, your program always has a few steps ahead of the TV’s drawing beam in telling the TV what it’s supposed to draw.
It typically takes a minimum of 21 cycles to execute the necessary instructions to prepare the “playfield graphic” for the current scanline:
lda PFData0,y ; Load playfield graphics 1 to draw (takes 4 cycles) sta PF0 ; Set playfield graphics 1 to draw (takes 3 cycles) lda PFData1,y ; Load playfield graphics 2 to draw (takes 4 cycles) sta PF1 ; Set playfield graphics 2 to draw (takes 3 cycles) lda PFData2,y ; Load playfield graphics 3 to draw (takes 4 cycles) sta PF2 ; Set playfield graphics 3 to draw (takes 3 cycles)
The very next instruction after that will finish on at least the 23rd cycle of the current scanline, which means that it’s too late to get the TV to draw anything else directly along the left edge of the screen.
In my program, this background graphic is the castle. On a scanline where any part of the castle appears I can’t draw anything else along the left edge of the screen. Anything I want to draw on the same scanline (like the player-controlled ballista) will have to be positioned a little ways from the left edge if I want it to appear.
By a fortuitous happenstance, I had originally planned for the ballista’s movement to be limited to within the castle — the player could never move the ballista past the towers to the left and right. Thanks to this limited movement range, I had enough time after setting the castle playfield graphics to also set the ballista graphics before the ballista itself ever needed to be draw.
Drawing the ballista currently takes about another 24 cycles in my code. This brings me to a total of 45 out of the 76 cycles available for the current scanline. At this point, the TV has drawn half the scanline, so I can’t draw anything more on the left half of the screen. If I want to draw a second object on the same line, it will always have to appear on the right half of the screen. This isn’t acceptable with my current goals for the game.
What most Atari programmers end up doing is to halve the vertical resolution of their game in order to double the amount of processing power they have per line. This works due to various technicalities with the Atari hardware which can cause it to draw every other scanline in the exact same way as the one before it (this technique is typically called a two-line kernel). The side effect is that the vertical resolution of the game is now reduced to 81p.
This double-scanline processing gives you more time to properly draw another object on the same line, but it still wasn’t quite enough for what I originally wanted. This is due to the fact that in order to draw all the objects I wanted I would have to reuse the limited amount of sprites (i.e. animated objects) available in the Atari.
Re-using a sprite meant I would have to re-position it in the middle of the screen. Re-positioning a sprite while the screen is drawing results in a thin black horizontal line appearing on the left edge of the screen. These black lines can be seen on many professionally released Atari games from back in the day, like Vanguard (pictured below), and was the only way to get the Atari to draw more than two complex shapes on the screen at a time:
While attempting to create a general purpose kernel, I decided it would be smart to really think about what I actually needed the program to do. I took a look at all the possible things I wanted to have happen on the screen, and from this I determined how many things would need to be drawn on the same line, and where on the screen they would be drawn.
I took a screenshot of my game and, using a graphics editor, added some fake graphics to it to mock up a game screen with all the possible things that could appear onscreen being drawn at once (e.g. the ballista, an arrow, two dragons, fireballs, the castle, etc.). The game would never be in this state, but it gave me a starting point for dividing up the entire screen into multiple horizontal sections. These sections defined points where the program would have to draw different things. For example, the player-controlled ballista will not be able to move vertically, so I divided the part of the screen where it would be drawn into its own section.
After splitting the screen into several sections, I then focused on looking at how many things could possibly be drawn simultaneously within the same horizontal section. I made a couple of game design decisions at this point which would limit the amount of work I would ever have to possibly do within one horizontal section. For example, I decided that if two dragons were onscreen at the same time, they would always appear at two different, non-overlapping altitudes, and only the lower dragon would ever shoot fireballs. This meant that I would never have to worry about drawing either two dragons or a dragon and a fireball on the same line. This helped a bit, but my ideas were still too much for a general purpose kernel.
During all this time, I was also researching how other people were able to draw multiple sprites onscreen at once. I came across a forum post somewhere which mentioned that some people write multiple kernels to handle this, with each kernel being responsible for drawing only a certain portion of the screen.
With this new approach in mind, I took my program, threw away most of what I had written, and started over again almost from scratch. This time, instead of using a single general purpose kernel which would be responsible for drawing the screen, I created multiple kernels, each one of which would be responsible for only drawing a small section of the screen. I made the program change the background color within each section to help visualize everything:
This seemed like a promising new start.
With this done, I decided to implement the arrow sprite, which is what the player would be shooting up at enemies. The arrow sprite would only ever need to be drawn in the sections above the one in which the ballista would be drawn, so I modified only the kernels responsible for those sections, making them call a subroutine which would draw the arrow.
I got this working in fairly short order with the arrow moving from the lower-middle of the screen to the top. The arrow would then looping to the bottom and repeat the animation indefinitely. In the image below you can see the arrow being drawn near the top-left corner of the screen.
Next, I tweaked the program so that the arrow would no longer loop and, instead, would only rise to the top of the screen once. In effect, I made the program automatically shoot the arrow once when the program started.
After this I hooked up the joystick button input to the arrow drawing logic and made it so the player would control the shooting of the arrow. Every time the player pressed the joystick button, a new arrow would fire up toward the top of the screen.
Then I decided to re-implement the code which controlled the ballista’s horizontal movement, and made it so that the arrow would be fired from whatever horizontal position the ballista happened to be at. I didn’t actually get the ballista to draw at this point, but I was now able to control when and where the arrow would be fired.
With the arrow graphics logic pretty much completed, I then added in code to draw the ballista. At this point, things got a little screwy:
My code was taking too long to draw the ballista, so it ended up getting stretched out and drawn incorrectly. I was also unnecessarily re-positioning the ballista to the same point multiple times on ever frame, resulting in all the black lines to the left of the ballista. I cleaned up my code a bit and resolved these issues.
Adding in the castle graphics screwed everything up again, and it took a little longer to resolve all that (I neglected to take a screenshot of it, but, trust me, it was a mess). Once I got the castle drawing correctly, however, a new problem appeared: whenever an arrow was being shot, the section it was currently in would expand in height if that section also had some of the castle drawn within it. This took further refinement of the code to resolve.
Eventually I got things drawing mostly the way I wanted with only some minor glitches, such as the uneven bottom of the left half of the rear castle. I’m living with that for now and might try to fix it later.
One thing I’ve certainly learned about programming for the Atari 2600 is that “the perfect is the enemy of the good”. If you try to get things perfect, you’ll a) never reach that point, and b) miss out on things being “good enough”.
Again, you can see the acceptance of small imperfections in professional games, such as with the black lines on the left edge of the screen in Vanguard (pictured earlier), or the dip in the left edge of the ground in Space Invaders:
In any case, it might be time to try getting the ballista in my game to appear in multiple colors and/or display an animation whenever the player fires an arrow. We’ll see what the next few weeks will bring.