XeO3-CPC

Wednesday, November 15, 2006

Arrrgh

Scrolling is nearly working, but I think my logic was a bit screwy and I'm not drawing the right bits at the right time. Keeping in mind what's currently displayed where is making my brain hurt. Hopefully the end result will be worth it.

Fingers crossed for some video footage sometime this weekend (time permitting as I'm going to the video game history thing at the Science museum too!)

Friday, November 10, 2006

Scrolling Theory

Ok, so we have two video screens, one which will be located in bank 2 (paged in at #8000) and the other in bank 3 (#c000) which will contain roughly the same but shifted left by one pixel. And we'll scroll these using a combination of CRTC register 3 (for a 2 pixel shift) and standard address offsetting using CRTC registers 12 and 13 to perform the main scroll.

So, what does that look like in pseudo code?

Frame 1
reset CRTC register 3
select the screen at #8000
draw a shifted column of bytes at #c000 + offset + screen width

Frame 2
select the screen at #c000
draw an unshifted column at #8000 + offset + screen width

Frame 3
move the display using CRTC register 3
select the screen at #8000
draw a shifted column of bytes at #c001 + offset + screen width

Frame 4
select the screen at #c000
draw an unshifted column at #8001 + offset + screen width

Frame 5
Increment the hardware offset
reset CRTC register 3
select the screen at #8000
draw a shifted column of bytes at #c000 + offset + screen width

and then repeat from Frame 2 onwards

Note that in each case we're always writing to the unselected video memory, so that we don't need to worry about tearing.

Tuesday, November 07, 2006

Pages and pages of flipping pixels

Ok, time for a brief aside from scrolling and a quick look at the CPC's pixel format and a nifty Z80 technique.

In Mode 0 we have 2 pixels encoded within each byte of display memory, but not in the worlds most straightforward format (as demonstrated by the diagram showing the bit mappings.)

Now what happens if we want to flip the left and right pixels over? No simple combination of rotates is going to manage it, so instead we'd have to resort to some basic arithmetic. And so, our naive Z80 routine looks something like this:

LD C, %01010101  ; [2]  This is our pixel mask

LD A, (HL) ; [2] Fetch the byte to flip
AND C ; [1] Strip out the right pixel
RLCA ; [1] Shift it left
LD B,A ; [1]
LD A,C ; [1]
RLCA ; [1] Shift the mask
AND (HL) ; [2] Strip out the left pixel
RRCA ; [1]
OR B ; [1] Recombine the pixels


So that's a massive 11 NOPS per pixel pair (ignoring the setup cost). It's glacially slow. Damn. Clearly we can't afford to spend anything like that long, but pre-shifting all our graphics will surely be prohibitively expensive? Time for a rethink. Given that there are 256 possible combinations of pixels, all we need is a 256 byte table that maps any given byte on to it's flipped alternative. The Z80s 16-bit register pairs make this very efficient, as long as we page align the data. A page is a block of 256 bytes which share a common high byte (eg 0xFEnn), allowing us to perform an index lookup by direct manipulation of the least significant register.

So our improved code looks more like:
LD D,page        ; [2]  Initialise the lookup table pointer

LD E,(HL) ; [2] Fetch byte
LD A,(DE) ; [2] Flip the pixels


That's a mere 4 NOPs per pixel pair ignoring setup costs. A significant enough saving that we can easily justify the extra memory it requires. And it has the added advantage that by using a different lookup table we can apply special effects to our drawing, which may come in handy later on.

As an aside to the aside, Z80 coders not familiar with the CPC may well be wondering whats with all the odd timing info and the mention of NOPs. I'll make a proper post on it before I post too much more code, but for now just accept that 1 NOP = 1us and gives us a much more accurate instruction timing on the CPC.

Monday, November 06, 2006

Give with one hand, take with the other....

So, we've got our ultra smooth scrolling at a cost of barely any cycles. We're laughing right, this is going to be a piece of cake? Well, no. All those savings come at a price and that price is a big increase in complexity.

A static bitmap display has some nice properties, locating the next byte to the right is a simple INC HL instruction (or even an INC L if we're careful) which makes drawing nice and easy. Moving vertically is slightly more tricky, but it's consistent and thus not too much of a strain.

When we start hardware scrolling the screen, things get a bit messy. The display memory starts to wrap around and we can no longer rely on simple calculations, even moving one byte to the right now potentially involves significant maths and we can say goodbye to all those precious cycles. That's bad, so it's necessary to look closer at how the wrap affects screen addressing.

Essentially, it divides the screen into at most three separate areas; a top and bottom area which we know will be internally contiguous and a single character high row on which we will have to deal with the address wrap. All of our drawing routines need to be aware of this, using high speed instructions where possible and falling back on slower code when we don't get a choice.

As if that weren't enough, recall that to get pixel perfect precision we introduced a second buffered display. This is drawn shifted across by a single pixel to compensate for the lack of finesse we achieved in hardware. This means that for absolutely everything we draw, be it sprites or background tiles we need to be able to draw it with pixel precision. And naturally the CPC's pixel format isn't exactly designed to make that easy.

Kinda makes nicking that crushingly slow Speccy routine look tempting, doesn't it?

The CPC can't scroll

The CPC is rubbish at scrolling.

There I said it. Because it's true. You have a 4Mhz CPU and a 16KB bitmapped display and no hardware assistance whatsoever. Even contemplating writing a scroller on it is plain stupid. A brief look at most existing titles will only serve to confirm this. It is a well established fact in retro gaming circles that you don't play the CPC version of games that scroll, because the other machines (even the humble Speccy) will generally do a better job.

Well, actually that's only almost true. There is a tiny bit of hardware assistance. The CRTC is wired up in what is perhaps best described as a funky way, allowing us some hardware assisted scrolling. Sadly it's not enormously useful on it's own, as it moves in chunks of 4 pixels at a time (in the low-res, high colour Mode 0). That means that at 50fps, objects would enter and leave the screen in under 1 second, a challenge for even the most hardened shoot-em-up fan.

It's at this point that 99% of CPC games opt for the, "we'll just use the software scroller from the Speccy version and run suckily slowly". Great for getting finished in time for payday, but not so good gameplaywise. Thankfully over the years, demo coders have perfected the art of pushing the CPC hardware a little further. With a bit more CRTC based jiggery pokery we can shift the position of the screen on the monitor left by 2 pixels. Normally that would look odd, but if we stretch the display to eliminate the left and right borders, we now have smooth 2 pixel scrolling at 50 fps in a handful of CPU cycles. Combine that with a page flipped display and we can have ultra smooth pixel perfect scrolling at almost no cost.

Sounds too good to be true? Well....

Intros

So, this is the unofficial home of XeO3 for the CPC. Or rather the official home of the unofficial version of XeO3. Which may become official. Or not.

Ahem.

I'm looking at targetting a CPC6128 version, because getting a decent framerate and smooth scrolling on just a 464 is probably not possible. If all goes well, it'll probably be followed up by a full on hardcore 6128+ version, but only time will tell. Like the other versions the game will vary slightly to be best optimised for the CPC hardware, but with hopefully enough echoes of the other versions to make it fit in.

I'm still getting my head round the scrolling routines, as it's going to be a bit of a challenge. Technical stuff to follow later hopefully.