My Second Plot Routine

Flaws in the First Plot Routine

My first plot routine was plotting pixel by pixel on the Dragon 32. Whilst you might be able to run a few  thousands of instructions per 50th of a second for arcade speed, you quickly burn up instructions in tight loops to plot objects, and there are 8,000 bytes of information in a 320x200 bitmap screen. Rebuilding the entire picture every frame was not an option back then.
 
Every byte on the screen shows 8 consecutive pixels, so firstly you want to be getting all 8 pixels right in one go (or two goes, as it often turns out), not plot them one at a time. Doing screen address calculations per pixel is highly inefficient, there's a multiply in there, or at least a table look-up. You can also do clipping in X and Y dimensions en-masse. So, having done it completely the wrong way first time, I set about converting Steve's Spectrum plot routine.

Code Design

When designing any routine, you should focus on the output, what you want to get out of it. In the case of a plot routine it's getting blocks of data onto the screen. Next you need to consider the input data, i.e. the graphics. Now if you can design your own input format then it's best to design your input format to be as close to your output format as possible. 
 
I didn't know Z80 assembler, just that it had a lot more registers than 6809 or 6502, so it would not be possible to do a line-for-line conversion from Z80. We could reasonably convert assembler to Z80 with some efficiency, but I hadn't written anything yet! My conversion process was that Steve would give me an overview of the Spectrum game, routine by routine, and where I needed it I would get a detailed explanation of data formats and what the routines would do. We had both been taught Jackson Structured Design, so we had a common methodology and were able to draw diagrams and understand a common terminology.
 
I also didn't remember until this week(!) that the Spectrum screen bitmap layout was different from that on the Dragon 32, and Steve's first plot routine was very character-focussed. His input format was to break the bigger graphics into 8x8 pixel blocks, i.e. characters, and plot those. Every character takes 8 bytes on the screen and are consecutive in memory, moving down the screen, but then to move down to the next character below we have to add an extra displacement. Likely this was the same on the C64, not so the Dragon 32. The Dragon 32 bitmap was more linear, not really broken down into characters. It only comes down to which counters go to make up which bits of the address for the graphics chip to fetch the data, but did make a big difference to the construction of the plot routine. 
 
We tended to work from the top left of the object, just as the C64 hardware sprites worked. So we calculated the address  on the screen of the top left point of the object, as a starting point. There are good reasons, in hindsight, to work from the middle of an object. It's just as easy to incorporate X and Y offsets back to the top left as it is to check collisions by adding X and Y offsets to the centre. It's also useful for background map positioning to know the centre position. Rainbow Islands tended to need to know what's under the object's centre feet position, and what's just ahead of the feet position in whichever direction it's going.
 

World Co-ordinates.

The screen pixels on the 8-bit machines go from 0 to 319 left to right and 0 to 199 top to bottom. You might reasonably want objects to exist beyond the screen edges, so we'd run a bigger co-ordinate system for the world, or space. For a wraparound co-ordinate system it's smart to use a power of 2, say 512 pixels in X and Y. That gives us a 9-bit number and we can just remove the top 7 bits from a 16-bit number by ANDing them out, there's no need to check the numeric range and adjust, which would take more instructions and be slower.
  For 3D Space Wars the view is first person, so the player view defines what you see and generates the offset to add to the world co-ordinates to make screen o-ordinates. The player view position is similarly ANDed to keep the world co-ordinates from 0 to 511. This effectively gives the illusion of full rotation in X and Y dimensions and if you keep going down you'll see the same view again as it goes from 510 to 511 and back to 0.
 

Neat Alignment

The initial phase of the plot routine might be to just map one test input block onto an exact character position. That's the simplest form and you can check that you don't plot anything off the edges of the screen. You just work out the address to start your display and OR 8 bytes of data to consecutive bytes on the Spectrum, or 8 bytes 40 bytes apart on the Dragon 32. It was at this point I realised that the 8-bytes a character rule doesn't have as much relevance to the Dragon 32. The object can be split up into 8 pixel wide stripes of any depth. After 1 game I wrote a graphics editor that that was marked up in 8x8 character blocks, but I realised that I could make the plot routine more efficient by not breaking the vertical slices into 8's. I had less registers to do this too.
 

Not-So-Neat Alignment

For objects that don't happen to be plotted on the 8-pixel boundary horizontally they took much longer to plot. Firstly you have to rotate the 8-bit graphics between 1 and 7 pixels to the right, making them become 16-bits wide and then you have to plot two consecutive bytes onto the screen. It takes more than twice as long to do, and happens 7/8 of the time. Some early games not pressed for space might have considered storing pre-rotated graphics. We saw this on the Atari ST for backgrounds too, and was, I believe the basis of Spectrum Uridium.
 

Further Complications

As an object starts to slide off the side of the screen either to the left or the right (fortunately nothing was ever big enough to clip off both sides), we may need to skip over the left section of the data, or stop before we get to the right-most vertical stripes. Additionally we may have to clip off the top or the bottom and skip data per vertical stripe or finish the stripe early and move to the next. Typically the plot routine splits up into different X clipping configurations early on, and then within each of those it has to set up the plot loops according to Y clipping. Being the most oft-called routine in the game it's important to make the plot routine as efficient as possible.
 
I'd probably not have been able to write such a big routine without an assembler, it likely took me a good 2 weeks to get it right with one.  In order to reduce the plot overhead slightly I put a border of graphics round the game screen. That actually made it easier to see if the clipping was working because any incorrect plotting would show up as overwriting my border. You really don't want to be plotting accidentally above or below the screen, you might hit something important, like your own code.
 
Initially it's a good plan to put one object under joystick (or later mouse) control. You can drag it to the edges of the screen, and across the edges to see that the clipping is working. It was interesting to see what happens before we got the routine working properly. Then you have to push the object to and across the 4 corners as those cases are also all different. Sometimes the object would distort, or disappear early. This plog routine was the one that I spent the longest on, but it teaches you about how the screen works. We only logically ORed the data onto the screen because we were plotting onto a predominantly blank screen. We were also writing directly onto the display screen, not onto a back buffer. Hence you do see some flickering.
 

Unplotting

Once we got the plot routine written, we created its sibling un-plot routine. This removes a graphic from the screen. To reduce the flickering as much as possible we would go through the objects one by one and remove each one and re-plot it in its new position as quickly as possible. This differs from 16-bit strategies as we started later to have enough memory for back buffers and were plotting graphics cleanly over others with masks, behaving more like hardware sprites.
 

Space ships

We drew the enemy space ships from more or less front-on in about 8 different sizes from a couple of pixels across to about 24 x 24 pixels. This gives the illusion that the ships are coming towards you, though it was only an animation from smallest to largest. We also increased the X and Y speeds over time, and then if the ship goes off screen it "re-generates" itself at its smallest back in the middle-ish of the screen as if it's a new one coming in to attack. With the wraparound co-ordinate system simulating the player ship turning round in the X and Y directions it gives the illusion of flying through space.
 

Illusions

That's what our old games were doing a lot of the time, creating the illusion of reality, removing the mundane processes to create something that is fun. We weren't trying to create photo-realistic images, but it was an aim to add little semi-realistic touches like shadows, fragments, glowing lights, accelerations and gravity. Just beware that if you get it really wrong it will be noticed.  
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

My First Plot Routine

ST Software Begins

In 1983, Steve Turner started to write Spectrum games for a living. He took the courageous step to give up his IT job and start working from home. We played in a band together, and used to tour the local ale houses on a Friday evening together in search of good beer and arcade machines. Thus I knew how he was getting along, and that he had successfully written a game: "3D Space Wars", and found a publisher. Shortly afterwards he started receiving royalty cheques and was writing his second game, provisionally called "War of the Worlds". We got a letter from Jeff Wayne's people later on, so it was changed to "Seiddab Attack".
 
 

The Interview

Things were going well and one August Friday evening Steve came round and, knowing that I wasn't happy in my job, he asked me if I'd like to work for him. That was the easiest question I ever had to answer, and the shortest interview I never prepared for. I handed in my notice the next week, and during my month's notice period I thought I'd better get acquainted with 6809 assembler.
 
 

Tandy Co-Co

I bought a book on 6809 assembler programming, quite a sensible sized paperback, and I must have bought a book on the Dragon 32, or maybe actually the Tandy Co-Co, on which the Dragon 32 was based. We used to have a Tandy shop in nearby Colchester, which was useful for accessories and games. 
  

Lunar Lander

I didn't have too much idea about how fast home computers ran, or know anything about how graphics got onto the screen on a Dragon. I had written a Lunar Lander type game that only really needed to plot one small object on the screen, and it appeared to run at a reasonable pace in BASIC. The object plotting was done by a BASIC function, so I wasn't doing that part of the task. I at least learned that since we weren't clearing the screen and starting from scratch every frame, you had to un-plot your object from its old position and plot it in its new position. We also weren't using back buffers nor frame-syncing in those days. Actually, to clear an entire bitmap of 8K might take you a tenth of a second or more without doing anything else.
 

No Assembler

I also didn't have an assembler at the time. What we did have though was a plethora of magazines which published listings that you typed in yourself. All were written in BASIC, but some used to POKE machine code into higher areas of memory, then pass control to it. That technique was what I was interested in. Little did I know that Steve had written an entire game using that technique. You might think that's not particularly important, but I was going to be converting that game to the Dragon 32, without any assembler source code for reference.
 

Always Save Your Program Before Running It

So, my evening job for the next few nights was to try to write an assembler plot routine on paper. I would then look up the machine code for my instructions, which were in hex, then convert them to decimal, work out the arguments, such as variables in hex, and convert to decimal, and work out the relative branch distances. Having worked out all of the values in decimal, I would type them into a BASIC loader program, SAVE IT, TWICE, and then see if it runs. Naturally it doesn't work properly for quite a while. Depending on how badly it doesn't work, you may be re-booting and hoping your program reloads from tape.
 

Pixel by Pixel

In my naivety, I decided to try and plot a space ship pixel by pixel. I know, totally impractical, but I was optimistic. Since the screen started out blank and black, I provided just a list of the white pixels` positions relative to the centre of the spaceship. So for each pixel I had to calculate the screen position and which bit to set. I actually included a multiplier factor, or at least an addition factor,  so I could make the ship explode by moving the pixels further from the centre point. I believe I did include clipping, which by pixel isn't so tough.  Pixels didn't come back until Morpheus, unless we count the Uridium background stars, and they didn't move, which is actually the clever bit!
 

Relative Code

6809 assembler code supported relative code, which means that provided you avoid any absolute jumps, the code can be loaded at any location and all the branches still work. 6502 and Z80 code weren't usually written relative. The advantages of relative code weren't really relevant until the Amiga, which allowed the operating system to load your code in anywhere that fits and run side-by-side with other programs. I had seen that on our mainframe, which was slightly different as it segregated programs by making them think they were the only program loaded, and faking the addresses so they always looked the same (whether the COBOL code was compiled as relative code, I don't know, they didn't let me see my COBOL programs in machine code, but whether relative or absolute, the faked addresses would still work.
 

Add Some NOPs

The problem with writing relative code the way I was doing it is that if you want to add an instruction in the middle, you have to "stretch" all the branches that might cross over that point. There are no labels or function names as I wasn't using assembler, I was writing in raw machine code. I quickly adopted the strategy of throwing in a few NOPs (or No OPeration, null instructions) to pad out the routines so that if I needed to add an instruction I could remove the corresponding number of NOPs and all the branches stay the same. Similarly if you want to remove an instruction you can replace it with one or more NOPs, and just tidy it up at the end once you've debugged it since NOPs still take a little time to run.
 
 

No Debugger

Naturally I didn't have a debugger, and I was starting with a whole plot routine. Fortunately the list of points helped. You can just give it one point to start with and see if that works before you add more. Computers can always think of new ways to not work, so it's a mental exercise of relating what you see on screen to how your code might be wrong. There's always a way! With one small routine you get to know it well. I was used to debugging code by staring at it and willing the bugs to show themselves. Dry-running, we called it. We are as clever as computers, just not as quick at finding the mistakes!
 

My First Space-Ship 

I can't remember exactly how long it took me to get this working, probably a week or so of evenings. Like any number of tutorials I have seen recently that are not running frame-sync'ed you have little idea how quickly or slowly the screen is being updated if there is no movement. I expect my next step was to add a small BASIC loop to just move the X position of my space-ship and call the plot routine again. At this point the reality of how long this routine was taking to plot my space-ship set in. It was maybe updating 2 or 3 times a second, and I was somewhat disappointed.
 
  There was also another limitation that I wasn't plotting black pixels, my graphic relied on being plotted onto an empty black screen, and my un-plot routine was removing pixels one by one rather than clearing (or restoring) a rectangle. With the explode option, that rectangle would get bigger and at some point clearing the rectangle would be slower. 

Hardware Sprites - Now That's Magic

All this seemed so laborious compared to what I was already seeing in Commodore 64 demos: you just put your graphic data into a particular location in video RAM, POKE a few ports on the graphics chip and the hardware plots a sprite on the screen for you, pretty much for free (data fetches aside). No un-plotting, different colours than the background, very straightforward, and you can slide the background around underneath it and it stays where you put it. It would be about 6 more months before I got a chance to try them out for real.


Next time...A Proper Plot Routine.