Ninjaman 8-way scrolling 50Hz multicolour demo :-)

Here's a demo I've been working on for a while. The goal was to make the best scrolling routine possible on a humble 48K ZX Spectrum.

I was originally planning to make this into a full game, but I don't have time right now TBH. I'd abandoned this project a few months ago, but I figured if I can't make a game out of it, I should at least finish it up as a tech demo and release something.

So here it is. You can download the .TAP file from here. It's non-interactive, so you can just watch it play itself. There's no real gameplay implemented.

It requires a 48K Spectrum - it won't run on 128/+2/+3.
This is not because of a technical restriction, it's just that the timings need to be tweaked for each model and I couldn't be bothered to do that.

YouTube capture:

- 50Hz redraw
- 8-way scrolling
- Multicolour backgrounds
- No colour clash
- 8 enemy sprites and one player sprite on-screen at once

I'll write up some more notes on how it's implemented if anyone's interested.


  • Looks great. Nice work.

    I'm going to have a play with this over the weekend to see how it works.

    1985: ZX Spectrum+ 48K Interface 1 ZX81 16KB ASZMIC/SP ROM Philips 12" B/W TV Epson Dot Matrix Printer ZX Printer Now: Late 2015 iMac 5K 27" 4GHz i7 32GB RAM macOS 10.15.7 1TB Ext SSD USB C Drive Ext 4TB 3TB and 2TB USB 3 Hard Disks Ext USB 3 Blu-Ray iPad R7 32GB iPhone 6s 64GB iOS 14.0.1 Apple TV Gen 2
  • edited October 2015
    Very nice!
    Post edited by majikeyric on
  • Very very nice!
  • Wow!! This actually has a lot of potential!! :)
    Only a Little worj with graphics and animations would make it even better ;)
    Keep it up!
  • Impressive. I was just thinking about tackling something like this on a 128K, but you've done it on a 48K!

    OK, so you're using around half the frame cycle to redraw the pixel part of the screen with scenery, then you've got the equivalent of a bicolour routine running but you're only using it to make it look like whole 8-pixel high attributes are jogging up and down. Classic magic trick that - always under-play your best tricks so they don't spot the bigger scam!

    And I'm guessing the black line through the middle of the platforms is deliberate, to hide the fact that the attributes aren't quite synching up with the 1-pixel vertical movement! Don't worry, I won't tell anyone! (Heh heh!)
    - -
  • Good work, nifty.

    As Joefish alludes to, I found myself looking at it for a little while before realised what the fuss was about - then realised that what appears to be normal speccy attributes and moving vertically! Presumably this engine can do bicolours?
  • This is quite an impressive achievement, well done!

    Is this what you call the proverbial octocolor? :D
  • Very clever..!

    So I guess that's 2/3 of the screen, minus the 1 character square top, bottom, right & left?

    (couldn't be bothered to squint and try to work it out ;) )
  • edited October 2015
    There isn't one row missing top and bottom. It's doing a full 16 characters high. That black row is at the top of the lower third score panel.

    In fact, it seems to be drawing 18 characters high (one more row of blocks than it needs), fetching the screen row addresses from a table and diverting un-needed pixel rows into the ROM space. That may appear wasteful, but makes up for it in simplifying the drawing loop. Though I'd advise diverting it into an unused bit of contended RAM to keep the timing constant.

    It's also drawing a whole 16 pixels more than it needs horizontally, using addressing to do the 8-pixel horizontal step. It draws either one or two characters to the left of what you can see, and none or one to the right.

    Now, what it's doing with the colour is very interesting. It's filling the next two rows just ahead of the raster by PUSHing the same colour, timed so that it collides with the raster just on the right row for that colour to start. And because it's already painted the next row at that point, the raster can carry on showing that same colour on the next character row, before it collides with the next colour change coming up from two rows ahead. Very clever, though it is leaving a single pixel row where the colour steps mid-row. You can occasionally catch it in the middle of a spider; the block design hides it quite well.

    Try saving the black-on-white attributes from 22528,768 in an emulator when you reset the Speccy, then load them in again while the demo's running. Lets you see what's going on in the hidden bits of lots of games. Try it with R-Type for instance.

    Of course, the other great thing with a routine like this (or a multicolour one) is if you hit pause in ZXSpin and bring up the debugger, you can pretty much guarantee you'll find yourself slap-bang in the middle of a critical list of PUSH code, which is a great place to start looking at how it works!
    Post edited by joefish on
    - -
    Thanked by 1p13z
  • Thanks everyone, glad you all liked it! :-)
    Here's a few notes on how it works:

    It's not quite correct to say that the black line across the tile middle is necessary to hide a glitch - if I'd chosen to make each tile a single whole colour, then it would have worked fine. However I thought it looked nicer to have the bottom half be non-bright, and changing attributes requires a full line to do, so you if you want to do that then you need a mostly-black line to mask it. If you didn't want the top/bottom attribute split however, then it'd work fine.

    The engine does not support bicolour. It can only change attributes every 8 lines, but you get to pick the vertical start position, which is what allows vertical scrolling. It may be possible to extend it to get some bicolour for the main player sprite however, given some effort.

    The engine works by splitting a frame up into a number of tasks (perhaps up to 100, I don't have the count to hand). A task might be something like "fill in one line of attributes", "draw one enemy sprite", "draw one row of the player sprite", or "draw one row of tile bitmaps", for example.

    The tasks are organized into a "display list", which is simply a block of machine code with a lot of CALL instructions in. This is the fastest way to dispatch tasks. Because we have 8 different Y-offsets the screen can start at, we need 8 different display lists for each case. The display list is specific to each model of Spectrum, so the loader would need to decompress the appropriate precompiled list for the current hardware. They compress well so that's not a problem.

    I generate the display lists using an offline program running on the PC. Each task is assigned a set of constraints to inform the engine when it needs to run - e.g. the task to write down a row of attributes has a very specific window in which it can execute. Too late, and the ULA will already have read in the old colour attributes and drawn the line. Too early, and it'll overwrite the old attributes before the ULA read them.

    The offline scheduler tries every combination of tasks in order to find which ones can form a valid schedule that satisfies all the constraints. It uses a cycle-accurate Z80 CPU emulation to check each task to see if it can be scheduled at any given point. NOPs are inserted to pad the timing out if a task is scheduled too early.

    Because this is an automated process, it's easy to tweak the timing to account for the various contention differences between the 48/128/+3 models. On my 3GHz PC, it takes maybe less than a minute to solve this. This wouldn't have been possible in the 1980s I think - you'd need some kind of supercomputer to solve the schedule.

    I think trying to do this by manually working out the timing on it would basically be impossible. You can do it for certain cases, like Nirvana does, where each scanline is doing exactly the same timing pattern, but when you start trying to juggle several balls at once then you need something to calculate it for you.
    Thanked by 1p13z
  • So is like a new ENGINE for scrolling games ;)

  • Well done, sir! The fact that you've automated the process makes it even more mind-blowing! :)
  • edited November 2015
    Crikey, that sounds a bit complicated!
    Post edited by joefish on
    - -
  • Just lovely stuff!!
  • this is the... impossible!
  • edited November 2015
    This is very impressive. Is it kind of doing whats done in multicolour demos but just for 8 lines at a time (allowing a pixel offset vertically from normal attr positions), allowing two different ink & paper attr's in a ordinary character square? If so very nifty, and clever.
    Post edited by dmsmith on
  • Any plans on releasing the source code?

    I pondered about what you've done and it's downright crazy amount of work. Would be a pity if it never got any use. | | - make ZX Spectrum choose your own adventure games, no programming needed (release 3)
  • The source code is kinda messy and complicated and probably not suitable for somebody to just drop in and use to write a game with, without a lot of effort on their part.

    But maybe the idea might inspire someone to write something themselves.
  • Excuses! | | - make ZX Spectrum choose your own adventure games, no programming needed (release 3)
  • A while ago I was looking for some handy math plotting program to simplify some programming tasks (I generally plot with libreoffice calc / excel, but that can be cumbersome). I found one nifty one which, unfortunately, had a few little bugs that I believe would be easy to fix.

    So I check, and find that yes, I'm using the latest version, from a couple years ago. The FAQ says that the author doesn't want to open source "yet", as he wants to be in control, but might consider it later on. There was also talk about "messy code" and whatnot, usual excuses. (Here's a revelation: all code is messy).

    Since it's been a couple years since the last version, I mail the author. The email (to an university address) bounces. I google the author's name. I find an eulogy, from a couple years ago.

    The saddest thing? Not the first time something like this has happened.

    This probably won't change your mind, but whatever. Even if nobody can just "drop in and use" your code, it's valuable reference for other people who might attempt the same. | | - make ZX Spectrum choose your own adventure games, no programming needed (release 3)
Sign In or Register to comment.