So, these articles are from some time ago, late April 2018 just as the Next was getting going, and long before the machines – or even the dev boards, were ever made. This also meant as I wrote a lot of this, the machine was changing underneath me, as new features were added – or even removed. But it’s still mostly how I’d write it now, though I do need to refactor it, and perhaps change the rendering to use less banking.
But it’s still an interesting process, even knowing these things – so Let’s Go!
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: http://dailly.blogspot.co.uk/2017/06/zx-spectrum-next-bitmap-example.html ). 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.
h
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++]]; counter--; x++; } } _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.