Sunday, April 29, 2018

Making Lemmings on the ZX Spectrum Next

So..... I was thinking on the choices I've been making while writing Lemmings, to get where I have so far, and thought it might be nice to write it all down. I've been a huge fan of development diaries, ever since I was a teenager in fact. Reading Andrew Braybrook's development of Morpheus on the C64 in ZZap64 was the highlight of the month for me. I didn't just learn a lot from them, but I was inspired while reading them. So, here we go.....

When the ZX Spectrum Next was launched on Kickstarter I got really excited, I've always been a fan of the Spectrum, and the thought of a faster one with some extra "toys" get me unreasonably excited. I - like everyone else, watched the various videos Jim, Victor and Henrique posted and relished the possibilities that they presented. I really, really wanted to get in on the act as well. I tried to find a TBBlue board on ebay, only to find out, I'd just missed one, and as they don't come up very often, I figured I'd need to take another tack. I looked out my old Spectrum emulator and got it all working again - it was a little old, and then started to add in the hardware that was available on the TBBlue. I was able to get a hold of the sprite demo that was shown on one of the videos, and so decided to tackle that first. I decompiled it to examine it more easily, then realised I'd need an assembler...

Damn. Yet more tools work. I spent a few weeks adding a Z80 assembler to my SNasm assembler - which was mainly a 6502/65c02/65816 assembler. This required me to rejig things, and I spent a long time assembling a test file, and comparing it with the binary output of another assembler to validate the results. I could have used other tools/assemblers, but experience tells me it's worth investing in your own tools if you can afford to. Things usually end up going faster in the long run.

Once I had the sprite example in a state I could mess around with, I was able to get the sprite test running in my emulator.

After this I added hacked a Layer 2 bitmap demo by Jim Bagley, and commented so I could figure out how it all worked (you can see it here: ). Once I'd figured that all out, I added Layer 2 into CSpect, along with scrolling. Now I had a good solid base from which to actually start playing.

This has obviously been an on-going task, as hardware is added, I've been adding it into the emulator. I released it a while back as several folk would themselves in the same position as I was, and this meant the whole "write a game" thing kept getting put back.

Once the 28Mhz mode appeared, I suddenly started to wonder if it could actually handle Lemmings. Not like it did before with only 20 lemmings at a horrible frame rate, but a full blown Amiga style one running at full speed.. With Layer 2 and 256 colour stuff, it was possible to look like it, but moving around 256 colour graphics takes a lot of grunt and 28Mhz might just manage it.

I started by loading in a 320K bitmap into memory, and drawing it to the screen.

So.... this was promising. Using just the CPU it was copying the screen over at a reasonable rate, but I'd need to try and get some lemmings on screen first, to see how I really stood.

Before being able to do this, I first needed to get some graphics.... Now, this is where things get a little complicated. If I ever wanted to release this, then I'd have to come up with a way to get the graphics without actually distributing them. Sony owns Lemmings. There's no getting away from that. They do occasionally seem a little lax in chasing down folk using the old levels and graphics, but that's not something I wish to test - they have far more money than I do! I've seen web clones that have been around for over a decade now, a clone on the Windows store, some on iPhone versions, and a GameBoy DS homebrew version, but still.... if i can avoid that, I'd be much happier.

Still all that said.... I did see someone who made a clone take a different approach. They provided a tool that converted Windows Lemmings assets, into what he needed. This was ideal. it means people could buy the content legally, then convert it into what I needed, and I wouldn't ever need to ship with copyrighted content. Sweet.

Now, while I worked on the original, I have had no idea how Windows Lemmings works, or what the format of the files it uses. Fortunately, Windows Lemmings has been out for a while, and it's been hacked to bits, and documented pretty well over the years.

After much Googling, I eventually managed to find the various documents, and although they were a little tricky to follow at times I was all set. The sprite format had a funny compressed method, basically a byte-run compression variant and that took a little bit of time to figure out even using the docs. The code below shows what I ended up with, along with some examples of the compression. As you can see, it can start with a "skip" which helps move over the blank data at the start of a sprite scanline.

for (int y = 0; y < dataHeight; y++)
                int x = 0;
                // Basic compression
                //  0x84 0xAA 0xBB 0xCC 0xDD 0x80
                //  represents four bytes image data(AA, BB, CC, DD) starting at offset 0.
                //  0x03 0x85 0xAA 0xBB 0xCC 0xDD 0xEE 0x80
                //  represents 5 bytes image data(AA, BB, CC, DD, EE) starting at offset 3.
                //  Each line can have many "sub" lines
                //  05 84 30 2C 2C 2C ## 02 84 30 30 2C 2C 80
                // 7F 0B 94 1A 0B 18...
                // The offset here is not 0x7f, but 0x7f + 0x0b, the following 0x94 is the start character for a data line of 0x14 elements.
                while (buffer[_offset] != 0x80)
                    int len = buffer[_offset++];
                    if ((len & 0x80) == 0)
                        x += len;
                        len = buffer[_offset++];
                    if (len == 0x7f)
                        len += buffer[_offset++];
                    int counter = len & 0x7f;
                    while (counter > 0)
                        spr[x, y] = pal[buffer[_offset++]];

Still, I eventually got all this sussed out and was able to save out all the Lemmings into a usable format, including a sprite pointer table at the start of it. Here's more or less what was saved out.

Once i did that... it was now time to actually draw a lemming. This was done using one of the new Z80 instructions that have added to the Next: LDIX. This loads from (HL) and stores in (DE), but only if (HL) wasn't the same a what's in the A register. This new instruction is ideal for sprites, as it lets you drop out pixels on demand. As you see from above, I use Magenta/Pink as transparent so simply set A to be this colour, then I did a simple Z80 Y by X loop around one of these sprites and got the image below - the while bar is a timing bar, showing how fast (or slow) the function was.

So... this was disappointingly slow. A single Lemming was taking 5 scan lines to draw. Scale that up to 100, include some "loop" and processing time and we'd be looking at at least 2 frames (probably 3) to draw 100 lemmings. Since the Amiga version ran in 3 frames, this wasn't good news. I decided I needed to scale this up to get a better idea of where I stood with this, so I did a 100 lemming test next

As I suspected, this wasn't great news... so it was time for Plan B.


Unknown said...

Good read. All it was missing was some photos of you working in the office with pizza boxes and cans of coke lying around. (I always enjoyed those photos in commodore format of the Creatures 2 development diaries). Looking forward to the next update, and Plan B...

Mike van der Lee said...

Thanks for the write-up, Mike. A quite enjoyable read :)

In the article, you mention: "...I've been adding it into the emulator. I released it a while back..." - where can we download it?

Mike said...

There's a link on the right....

Mike van der Lee said...

I swear I looked yesterday and didn't see it.. [wipes his glasses clean].

Thanks :)

IncaIB said...