So in order to understand multiplexing, we first have to understand how a CRT draws the display. The TV has X and Y positions (as shown below), and the display is created by a “beam” projected from the back of the screen to hit the glass at the from, moving from the top left, right and down to the bottom left

Like this…

Now each line it draw’s is known as a “scanline“, and when the raster fly’s back from right to left (the pink lines), or from the bottom back to the top, these are horizontal and vertical blanking periods. In PAL, the C64 draws 312 scanlines, 320 pixels on screen, with the border filling the rest of the space.

The Horizontal blanking period is when nothing is draw, and there is a “pause” as the beam moves back to the start of the next line. Why is this important? Because it means there’s a tiny bit of time where the TV isn’t drawing anything – during that fly-back time. More importantly though, is the vertical fly-back or Vertical Blanking period (VBlank). This takes a lot longer, but it can also generate a vertical blanking interrupt.

So why does all this matter? Well, each “scanline” it draws, you could for example, wait until that horizontal blank (HBlank) and change something for the next line. Let’s say we set the X-Scroll register on line 0, we then let that draw and during the HBlank at the end of the line, we set the scrolling to 1 and the beam will draw the updated value. This is the core of 8 bit graphics. Draw something and then “chase the raster” (as it’s known), where we do things to the display as the TV draws it.

This is exactly what Shadow of the Beast does for the ground at the bottom. It sets the scroll register, waits a few lines, then sets another one – one that scrolls faster, then does this again and again.

Let’s be specific here. Lets say pixel 0 is red, and pixel 1 is blue – on both lines 0 and 1. If we set the first line to X-Scroll to 0, then we see the red pixel as the first pixel on the line. If we then set X-Scroll to 1 after line 0 has drawn, we’ll see a blue pixel first.

How does this relate to sprites? Below is the C64 screen in relation to sprites.

On the C64, pixel 0,0 is 24,50 for a sprite. So if we position all 8 sprites at Y position 50, then they’ll all sit right under the top border. So what if we wanted to have more than 8 sprites? Well, just like above when we were talking about changing the scroll register on the next line, we could change the sprite locations part way down the screen.

So lets say half way down on raster 156 , we set all the sprite Y coordinates to 229, which would place them all sitting on the bottom border. This moves the sprites from the top – after the raster has drawn them, to below the current raster, ready to be drawn again.

And that’s the core of multiplexing. Setting up sprites, waiting for the raster to finish drawing them, then moving them further down the screen for the raster to draw them again. So for a general multiplexor there are 3 ways of doing this. The first is simple, and what was used in Delta…

Here we have 5 “zones”, where we have Raster Interrupts that reposition the sprites 4 times. First, what is a raster interrupt? Well, simply put, when the beam draw a line, it increments the raster line “number” in the C64, and you can setup an interrupt so that when it hits line “X“, it’ll trigger that interrupt. So if you setup a Raster IRQ (Interrupt Request) at line 64, then you’ll get an IRQ running when the beam is about to draw the screen, just after the border.

So in delta, the VBlank will setup the panel sprites at the top, then a Raster IRQ at (about) line 60, will setup the top sun sprites, moving the sprites down, changing shape, X position etc. It’ll then setup an IRQ at about line 86 – just under the sun sprites, and this will setup the game sprites, and so on. All IRQs are “fixed”, don’t change, and will perform the same task each time.

The next method is “bands” as I used in Ballistix, and Andrew Braybrook use in Morpheus. This method (shown below) simply moves an active sprite from one band into the other as it moves about on the Y axis. This is fairly simple, and again involves having raster IRQs at fixed locations on screen, and as a sprite starts to straddle both bands, the IRQ will setup the sprite twice, then as it goes fully into a band, then only that IRQ will setup the sprite.

So what about the more complex one? The one I used in Blood Money, and Armalyte? This creates Dynamic IRQ’s that will change depending on demand. How do you go about setting this up?

Dynamic IRQ’s” make it sounds like I’m procedurally making up code on the fly etc, but I’m not, I’m just picking the scanline to spawn the IRQ on. It’s exactly the same as the fixed bands above in respect of the code running in the IRQ and setting up some sprites, just the scanline picked will be dynamic.

What we do first is in the VBlank interrupt, we sort all the sprites on “Y”, and then we setup the first 8 filling in the Hardware registers so they are ready for the raster to display. There are lots of ways to sort the sprites, bubble sorts, insertion sorts and so on. Armalyte used the stack to sort, which was very fast, and I later adopted, but Blood Money used an insertion sort. It worked, but it was a little slower. On faster machines, you could probably do a linked list, possibly using a distance hash – like the Y coordinate divided by 8 or something, then mini linked lists off that primary node. That would make insertion really quick, and you’d just have to traverse the chain to find the next sprite.

Once the first 8 are setup, we then look at the 9th sprite, get it’s Y coordinate, and add about 8. Why 8? Well, looking at the image above, you can see sprites “start” at 50, but actually… the RASTER they start on is 64. So we if we added on 14, the raster would occur on the same line the sprite is to be display on – and that’s too late. The C64 isn’t that quick, and as it’ll take a few scanlines to setup a some sprites, we want to generate the IRQ a few scanlines above where the sprite needs to be, so we’ll move back 8 scanlines and generate the IRQ there.

The trick here is to get the code fast enough to set up all the sprites you need, in as little CPU time as possible. This lets you subtract of less off the Y coordinate, and pick a scanlines closer and closer to the sprite itself. This would then let you move sprites closer together.

The next trick is to setup as many sprites in a “batch” as is possible. If you have to generate an IRQ for every sprite, that wastes time, and if you have multiple sprites on the same line – would also break things. But if you can setup a good batch of sprites together, then that cuts down on IRQs, saves CPU time, and gives you more time for the game.

In Blood Money, I didn’t do this well and when I went back to tinker with it, I fixed that and saved a huge chunk of CPU time. Blood Money runs in 3 frames, which is very slow for a C64 shooter, but this was the speed of the Amiga one, and as I moved the player every VBlank, it felt a lot smoother anyway. Fixing the multiplexor almost allows it to run in 2 frames, smoothing it out – though making it too fast to play.

This is the raster IRQ for the “core” multiplexor in Blood Money, and you’ll notice I don’t PHA etc but STA. This is to zero page, so it’s a little quicker, and as they aren’t re-entrant, it’s fine. Aside from that, it’s just setting up the next sprites, and looking to see if I can set up any more.

So… there you go. I hope this explains things a little better, and how multiplexors work. This should work in any system that has sprites, though it will depend on hardware. I’ve used the “fixed” method on the ZX Spectrum Next to do playfields, but I could do any of these methods.

But like the sprites and scrolling, you could also do the same thing for changing colours (colour bars), character sets (swap to a panel character set), bitmap base addresses (allowing split screen scrolling) and so on.

“Chasing the raster” was also used on the original Spectrum, but it waited for the raster to go past a location, then it would draw to it, meaning you’d not see the CPU draw the screen/sprites etc.