Five Finger Punch
[Q] How long have you all been writing demos and for which platforms?
[-V-] Me and my brother had sort of a demo group on the ZX Spectrum. That must have been in 1986 perhaps. The next platform was the Amiga 500, where we made some half-decent (for the time) demos. I didn’t do anything demo scene-related after 1993 or so. Then in 2014 Morbid wanted to go to the Datastorm demo party and I wanted to go if we had a demo to show, so we founded Five Finger Punch and made a demo to release at the party. We have since then released demos for Amiga, ZX Spectrum, Spectravideo, ABC80, and of course the Laser 200. Our next demo will be released within a few months, for a platform nobody would expect!
[Q] What computers have you all been developing on over the years?
[-V-] The first one must have been the ABC80 at some course, or the ABC800 I tried to program at my dad’s work. Or the ZX81 that we borrowed home for a while. But the first one I owned was a Spectrum 48. I did some things on C64 as well. Then Amiga of course. Later on, apart from the above, also professionally on PC and mobile phones. My day job is at Resolution Games where we’re working on visual computing devices from Oculus, Magic Leap, Vive, Microsoft, Sony, Google, and Apple.
I found the Laser 200 at a retro computer fair in September 2017 (https://www.instagram.com/p/BZq3aHMAH2I). Before that, I didn’t even know it existed.
[Q] The mode(0) rotoscoping is very cool. How was that coded?
[-V-] The source image is 16×16 characters for a total of 256 bytes. Starting at the top left, I calculate the coordinates in the source image to copy to the screen. Then add a delta to the source coordinates. Proceed to the character to the right etc, and repeat 32 times to fill a whole row. Then add a vertical delta to the source coordinates and continue with the next 32 characters, and so on. Part of the trick is to work with fixed point numbers to be able to represent fractions. The coordinates use 4 bits for the whole part of the number and 4 bits for the decimals (or rather, “binimals”) so numbers can be represented with a precision of 1/16, which is enough for this resolution. The hardest part was optimizing it to be fast enough to render before the display started, as you get garbage on the screen if you access the video ram while the display is updating. I didn’t quite succeed, which is why there is some noise at the top of the screen.
[Q] I also love the mode(0) sinus spinning lines. It looks simple but is very effective!
[-V-] Yeah, it looks simpler than it is maybe, because the resolution of a colour is 2×2 pixels in semigraphics mode (because it’s basically a character with a certain bit pattern and color) so different colours can’t always be next to each other. Morbid did that part too.
[Q] The one that blows my mind the most is the demo part with large mode(0) words with FLD. I had a Commodore 64 back in the day and there were many demos with this, but I never thought I’d see that on the VZ computer. How hard was that to program and how was it done?
[-V-] While experimenting with multi-channel music output, I stumbled upon how to trick the graphics chip into stretching the image which is what makes this possible. The VZ only has a single raster interrupt, at the start of the bottom border, so the code has to execute a known number of cycles before starting to fiddle with the graphics chip’s settings. Then every scanline, if you change between graphics mode 1 and 0, that scanline will be repeated, so a single character row which normally is 12 scanlines tall can be stretched to any height.
The “Kefrens bars” at the end uses the same hardware trick but in a more advanced way. It’s set up so that the top scanline is repeated across the whole screen, but for every displayed scanline, a new part is added (and the CPU has to do that while the screen is displaying the border, so the timing took some work to get right).
[Q] The sinus plasma-like waves is amazing. I can’t imagine how to develop something like that. Please tell us a bit about it.
[-V-] The thing that maybe makes this a bit harder than it looks is again that you can only have one foreground color within a 2×2 pixel block. But mostly the problem was again to render this quickly enough to not get garbage on the screen if you update too slowly. In this part I render to a backing screen in normal RAM, and copy to VRAM while the display isn’t showing pixels.
[Q] Finally, what language was the demo programmed in? Did you use an IDE?
[-V-] We use Z80 assembly, assembled using Vasm. We use different text editors. I use Vim when I code assembly. We use GNU Make and Python scripts to build the code and other assets like images, and also the final wav file that can be played to run on the Laser.
[Q] I hope to see more of your work in the future (plus maybe more VZ demos?)
[-V-] The VZ is a pretty nice machine for demos, despite being so limited. Maybe we’ll return to it at some point in the future. I’m sure we can come up with more ideas for it.
[-M-] Some background:
[Q] The first sinus-wave mode(0) demo part. How on earth was that programmed? I can’t imagine the logic behind it. It looks very cool but out of my abilities 🙂
[-M-] That part is called Helix.
I precalculate two sinetables before the effect starts.
During the effect I wait for vertical blanking by polling $6800. Then I quickly copy from a backbuffer to the screen. This is done with an unrolled loop of ldi:s.
I then clear the backbuffer. This is done with an unrolled loop of ld (hl),a; inc l
And finally fill the backbuffer with graphics for the next frame.
This is done in the following way:
Loop over all even rows.
Figure out the current pixels color
Grab a value from the sine table
Use that sine value as the x position
Write the color to the backbuffer using or.
Increase pointers into sine table
Repeat for all odd rows.
I use a few tricks to make it faster, separate code paths for odd row/even row/left pixel/right pixel and self modifying code for some of the variables.
[Q] The split-screen sinus wave in mode(1) looks amazing. That must have taken some time to get the timing correct. There is an old VZ game called Escape River which has the top 4 lines in mode(0) and the rest of the screen in mode(1). A similar concept. Have you seen it? Can you give us any information about how you did this part?
[-M-] That part is called Ykaros.
I precalculate one sinetable before the effect starts.
During the effect I wait for vertical blanking by polling $6800.
For the transition I draw one new line of the screen every frame, when the whole effect is visible I still draw one line, but have modified the code so that it draws to rom instead of the screen. This is just to not mess up the timing.
I then draw to the screen as fast as I can. This time I point the stack pointer to my buffer. The buffer is a list of pointers to graphics to draw and where to draw them. So the code is basically an unrolled loop of pop hl pop de ldi ldi ldi.
Then I generate new data into the buffers. Just reading sine values for the left and right movement and then calculate screen address and source graphics address. I use some self modifying code here for some variables.
And finally the rastersync stuff. It was added just a few hours before the compo deadline. Since I didn’t have the hardware there really wasn’t much timing stuff I could do but I wanted to do at least something, so this was a last minute hack. Although I had been planning to add it. Martin (vilcans) had already figured out the timing of the vz200, so all I had to do was to keep the above routines at constant speed for each frame. Then grab a sine value from the table and wait the required number of cycles and flip the background color.
[Q] I also love the mode(0) sinus spinning lines. It looks simple but is very effective! Any info about this one?
[-M-] This one is called Screw
Same principle as the others.
I precalc two sine tables before the effect starts.
During the effect i wait for vertical blanking by polling $6800.
I then copy the entire screen from a back buffer to the screen, while also clearing the back buffer.
I then calculate new data for the back buffer. I essentially have five blocks of “graphics” per row that I move in a sine wave. I have separate graphics for the left/right case. Copying is done with a few unrolled ldi. Some self modifying code is used for a few variable, which really isn’t needed. This effect does not use all raster time so it does not really need to be optimized.
And finally, here is the demo in all its glory: