Firebird : 1987

Cockneys Die!!!

After Odin's demise, I was left with decisions to make. After months of running on financial fumes to complete Sidewize, I had no significant monetary reserves; not to put too fine a point on it, I was skint. I did have an idea for a follow-up to Sidewize however, and I didn't feel inclined to go into a full-time employed position again. There were a couple of local options. Gary Bracey @ Ocean pitched Colin and me on the Platoon movie license, but we declined because they wanted us to move to Manchester. I didn't drive, commuting was out of the question, we couldn't consider this option on the terms offered. We talked to Elite in Birmingham, who immediately offered us a deal when they saw the Sidewize demo, but like Ocean, they wanted to do licensed titles, and that didn't appeal at the time (the schedules for licensed titles tend to have zero leeway, which is bad enough if you're *not* being paid on milestones).

A local company, Reptile Industries (which would go on to morph into Memory Expansion Systems, making Amiga memory expansions) was run by John Robinson and Dave McGee who were working on a Spectrum game called Anaconda. They wanted Colin and me to work on a project for them, but when asked about a contract for the work they had in mind, I was told, "Sure, send us one." Clever lads. Unsurprisingly this never turned into anything concrete and I don't think Anaconda ever shipped either (but it can be seen here). Their memory expansion company was much more successful, and I did do a couple of things for them a few years later.

In the end, Colin and I hooked up with Tony Beckwith, a producer at Firebird, and signed on to develop Crosswize (a sequel to Sidewize). I'd dealt with Tony during the dying days of Odin (he was the Firebird producer assigned to Odin). Tony visited Liverpool for the first time in his life during this period at the end of Odin, coming up from London Euston station by train into Lime Street station in Liverpool. He was, I think, nervous to be in Liverpool (I've found that cities are often preceded by an undeserved bad reputation, and that was the case here). I think Tony was particularly struck by the huge white graffiti emblazoned on the wall as the train approached Lime Street, "COCKNEYS DIE!!!" To a Liverpudlian, he was a Cockney alright (despite the probable geographical inaccuracy of the label). Tony was a straightforward guy and it made perfect sense for Colin and me to hook up with him/Firebird. I think Firebird imagined they might recoup part of the investment they had made in Odin by working with ex-Odin people. That's speculation, however.

Crosswize


I was happy with the general gameplay in Sidewize, but the whole thing was somewhat repetitive, and I knew the Spectrum could be pushed to do more elaborate scrolling (rather than black "space" or endlessly repeating blocks). With Crosswize, I decided that the general premise would remain the same, but there needed to be much more variety. I realized that it would not be possible to run the whole game @ 50 fps if the design moved in this direction.

What I decided to do was drop the game frame rate to 25fps. Looking at early tests, this looked smooth on TV screens, and it afforded much more time to run game logic with more elaborate graphics. At 25fps, I had one video field to update all game logic and game model, and one video field to draw the graphics, which were precalculated on the logic phase where possible.

The general format of the game was the same, a sideways scroller with waves of enemies. With the new elaborate scrolling backdrops I had in mind, it made sense to incorporate the backdrop into the gameplay (rather than have it purely as decoration). The background was made collidable (it damaged your player if you hit it) and certain baddies were keyed to the background, to make them a bit more dynamic (though I did pull off an animated backdrop on the final level).

All the sprites were pre-shifted (like Sidewize) and I used the stack to pull bitmap and mask data from memory. Since the whole of the active playing area was cleared by virtue of drawing the backdrop, there was no need for any dirty rectangle handling (which made the logic cleaner).

The scrolling code itself could be looked at in two ways: it was either a testament to the power of self-modifying code, or else it was a complex real-time compiler. The reader can decide. The backgrounds were composed of 16x16 pixel blocks, pre-shifted and carefully drawn so that various combinations of blocks could be placed adjacently on screen (the blocks are shifted into each other). In order to draw the graphics, three things were needed:

1. Source bitmap data.
2. Map data (which would define how the bitmaps were placed on the screen).
3. Code to read the map data and draw the appropriate graphics on the screen.

#1 was pretty simple - blocks of data were 10 bytes (5 reg pairs) wide and 16 lines tall. For #2 and #3, on each scan line, the draw code would POP AF, BC, DE, IX & IY register pairs from the stack to constitute the source bitmap data for that screen line - HL was used and set to zero for blank blocks. Next, after stack pointer swapping magic, per 16-pixel tall block the code would run through a sequence of PUSH instructions, the order of which would determine which of the 5 blocks would be drawn where. The trick was to generate the push instructions in the correct order, which was a case of interpreting the map data appropriately and writing the correct sequence of push instructions into the scan line draw code (into a buffer set up for the purpose, which would be executed as the code falls through).

This operation is complicated by the fact that PUSH IX and PUSH IY are 2-byte opcodes, compared to 1 for the rest, but that is neatly dealt with by allowing for the max sized buffer and inserting a JP instruction in there (faster than JR even though it's one byte longer at 3 rather than 2 bytes) to skip the unused part of the buffer.

The map data was stored as one-byte offsets from the start of a (256-byte) page-aligned baseline. The byte offset was added to the baseline and the code was executed (via JP (HL) - which is misspecified isn't it? I think the mnemonic should be JP HL, but I digress). The bitmap and attributes were updated in a similar way.

The player sprite was drawn after all the background, enemies, and enemy weapons, but before the player weapons, and if any "set" pixels were encountered whilst drawing the player he was deemed to be "hit" (be it background, enemy, or weapon). The player can never collide with his own weapons (because where's the fun in that).

How the Crosswize Screen Update was Organized

I have taken the time to capture (this time using the Retro Virtual Machine 2 emulator because I needed to be able to see what was happening on screen for each captured frame) 8 frames from Crosswize, with the attribute colors set to bright white on black. This effect reveals certain hidden tricks that are done to speed up screen updates and save some memory.

Crosswize Screen Utilization, aka why Steve is not an artist.

There is a lot going on in this image, let me break it down:
  1. The RED areas are normally hidden by setting the attributes colors to black on black. There are 24 hidden pixels on the left side of the screen, and 16 hidden pixels on the right side of the screen. This serves two purposes, first, it masks sprites and background graphics that are moving off of each edge of the screen (so there is no need to code to clip those graphics as they slide off the screen), and second, it simplifies the scrolling background update and helps reduce the amount of shifted versions of each block to 4 rather than 8.
  2. The BLUE area is the status display and is updated when needed, and not every frame.
  3. The GREEN area at the bottom is another hidden area that was used to control the sprite movements! I was so short of memory that I needed to use everything available. so by shaving off 8 pixel-rows at the bottom of the screen I was able to claw back another 256 bytes. I was using the screen to store dynamic program data, which is why it animates.
  4. The grid overlay represents a 16x16 pixel grid, corresponding to the nominal background block size. You might notice that the grid jumps every 4 movements of the background. This is because (as noted above) I'm only storing 4 shifted versions of each background block. Each shift is 2 pixels (the minimum movement step for the backdrop) and so 4 shifts correspond to 8 pixels (one Z80 byte). I am using the Z80 stack to draw the background, and it pushes 16-bit (2-byte) values, but the Z80 has no issue writing 16-bit values to odd memory addresses, so I just offset the draw position by 1 byte (8 pixels) every time the shift count would reach 4. That is what you're seeing with the grid overlay.

What Did the Crosswize Code Look Like?

I've posted a couple of disassemblies (using the fab but $$$ IDA tool - I still cling on to my copy from 2005) of key parts of the game to my Github in a new repo here. There you'll see the code for the scrolling background draw phase (but not the logic to set that up), and the code to draw the main player sprite.

The background draw code is pretty compact, here's an example (this code updates one row of 16x16 pixel blocks):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
draw_backdrop:
        ld    (save_stack), sp  ; store stack away somewhere
        ld    iy, 0
        ld    c, 2
        ld    de, 401Fh         ; last byte of first row

loc_0_8635:
        ld    hl, 0

loc_0_8638:

        nop                     ; SMC -    this is    dec e or ???
        ld    b, 8              ; 8 scan lines

loc_0_863B:
        ld    sp, hl
        ld    hl, 10            ; 5 reg    pairs, 10 bytes
        add    hl, sp
        exx
        pop    af               ; grab the 5 source reg    pairs
        pop    bc
        pop    de
        pop    iy
        pop    ix
        exx
        ex    de, hl
        ld    sp, hl
        ex    de, hl
        exx
        ld    hl, 0             ; blank blocks

loc_0_8650:
        jp    0                 ; self modified jump into push buffer


push_buffer_0:
        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0

loc_0_866F:
        exx
        inc    d                ; step down a line
        djnz    loc_0_863B

        ld    de, 403Fh
        dec    c
        jp    nz, loc_0_8638

The push_buffer_0 label is where the appropriate push instructions were precalculated (compiled). There are 28 bytes to allow for the worst-case scenario of 14 x PUSH IX or PUSH IY, which are 2-byte instructions. The JP 0 ahead of the push buffer jumps into the buffer at the appropriate spot, to allow for the variable-sized push instructions, and evidently, the buffer was filled from the end by the dynamic compiler. This code redraws the entirety of the active playing area every update cycle (so that is 25 times per second). Because of this, there is never a need to track dirty rectangles to erase old sprites, etc. This did open up the door to a good number of particle effects for explosions and the like.

What I'll say about the player sprite drawing code is that it is about 1.6KB in size and is unrolled to the hilt. Whether the speed increase resulting from this unrolling was worth it is an interesting question (the answer to which is lost in the mists of time)! The one thing I'd point out for those curious enough to go and look at the code is that the draw code accumulates collisions with "collidable stuff" (basically anything that is drawn before the player can be collided with) in the alternate A register. This is close to pixel-perfect collision detection.

What does the Crosswize Screen Update Look Like?

As I did with Sidewize, I used Mike Dailly's excellent CSpect ZX Spectrum emulator and extended the plugin I wrote for Sidewize to work with Crosswize. One interesting discovery (which conflicts with my recollection) is that Crosswize is not using the "floating bus" trick for video sync. It appears to be using a regular (well, almost) wait for VBL interrupt approach for sync. So either the version of Crosswize I used was hacked to remove that code, or else I did not use it in the first place. The fact that I do not have an obvious block of solid attribute colors in place on the screen (other than at the very bottom) suggests that my recollections were simply faulty. I did not push the Crosswize modifications to the Github repo yet (I used program code execution triggers this time!) but will do so when I get a chance.

So below are three examples of Crosswize screen update, captured byte-by-byte (but then time-compressed to 15 seconds each), so that you can see the entire playing area is cleared by drawing in the scrolling background, and then all the moving objects are redrawn over that.

[For those interested in the minutiae, the links to the non-time-compressed versions are also here: Example 1Example 2 & Example 3].

The animated backdrop on this level is technically no slower than a non-animating backdrop. It just consumes more memory, so was used sparingly.

The giant yellow "shrimps" are part of the backdrop. The white hatching shrimps are sprites.

It is no faster to have a clear "sky" than to fill the entire screen with the backdrop. There is an example of that later on in the game where you must blast away part of the backdrop in order to proceed.

I Have A VIC 20

In September 1987, two things happened. First (and most importantly) I met the woman who was to become my wife, Elsbeth Jones. "Met" may be a slight exaggeration. One Friday night, when Stuart and I were having a quiet drink in Milo's rock bar in Liverpool, Elsbeth and a friend thought it might be amusing to lob empty beer bottles at us. I was puzzled about the source of the projectiles since, Milo's being a rock bar, it was pitch black. Stuart ended up talking to Elsbeth's friend and I spoke to Elsbeth once or twice. "Oh, you make computer games ..." quoth she, "... I have a Vic 20". But that was as far as it went before I left for a "vacation" in the States. Which was the second thing.

America Calling. Or, If You Were So Skint Why Did You Go To America for 2 Weeks?

Earlier that year, I'd received a phone call from Stefan Walker. He was calling from his parents' house in Nottingham, and he had news: "I'm moving to America with a new job!" Stef elaborated that he had secured a position in the States, working for Microprose in Hunt Valley, near Baltimore. He'd completed his interviews with the UK arm of Microprose which was run by a fellow called Stewart Bell (ask not for whom ...). Stef had passed his interview with flying colors, and he was going west, young man! In the Spring of 1987, Stef moved out to Baltimore to work on the Amstrad CPC version of Sid Meiers's classic game, "Pirates!". The logic behind this is a mystery to me (instead of doing the port to the UK-specific Amstrad in the UK, move someone out to the USA to do it), but Stef was going to the States!

And they needed more programmers! And, I hadn't taken any vacation in 4 years. In September 1987, after (I don't know how!) scraping together the £300 for the plane ticket and with £200 of spending money in my pocket (which was everything I had to my name!), I got on a plane in London and flew to Baltimore, my first time in the USA! A college buddy, Ian, came along, and we planned to sleep on Stef's sofa/floor for 2 weeks of fun. Additionally, I was scheduled to meet with the development folks at Microprose and discuss their open programming positions!

Baltimore was fun. Stef, Ian, and I enjoyed ourselves at a few of the local nightspots (all the while eking out my precious £200 of pocket money). Soon my interview came around, and I duly turned up at the Microprose office at the appointed time. We had a ride over to the Hunt Valley office from Stef's residence in Cockeysville (we didn't have transport, and the bus service was patchy, to say the least). Once at the office, I first met with Steve Meyer, who was head of development at Microprose at the time. It felt like my initial chat went smoothly enough, and I was told that next, I had to meet the newly hired director of development (whose name escapes me). I was looking forward to meeting with him - it turned out that he had recently (within the previous week!) moved over from Liverpool with his family in tow. His first question was, "Have you heard of me?". My answer was that I had heard of his games. He went on to let me know how he'd single-handedly done this, that, and the other, whereas I, he informed me, "had only worked as part of a team, so that is something to which we'll have to give some serious thought". I hadn't single-handedly done enough of the things, let alone all the things. By the time I left, I knew that I didn't have the job.

I made the most of the rest of the trip, took a Greyhound bus to Washington DC, saw the White House and other sights of DC (including walking the wrong route to the Greyhound Station, which in '87 was in a sketchy part of town), went to Sid Meier's bachelor party(!), went to Hammerjack's in downtown Baltimore, visited the Baltimore waterfront, drank a few pitchers of beer at Schaefers in Towson, frequented the Hunt Valley Mall, and startled a few drivers by walking around. This part of America tended not to be pedestrian-friendly, on the whole.

Stef was 20 years old at this time, and this is below the drinking age in the USA, but we invariably found that the combination of an English accent and a borrowed UK driving license (which did not have photos at the time) would suffice.

I decided that, on balance, I liked this America place. I wondered how I might one day be able to move there, but it was back to Liverpool for me, to finish Crosswize.

Unfortunately for Stef, things didn't go smoothly for him at Microprose and they dropped him gracelessly when he returned to the UK for Christmas that year. I don't believe Pirates! Amstrad CPC ever saw the light of day. Edit: Twitter user @jordimontornes points out that it did.

Back to Crosswize

Crosswize development continued to the end of 1987. It was supposed to be completed by the end of the year, so despite my best efforts, it was late. It was really close though. In the meantime, I was looking around for the next thing for once Crosswize was finished and entered into discussion with Denton Designs in Liverpool. But things were about to get very interesting with Crosswize ...

If you enjoy my blog, you can help support it by buying me a coffee!

Comments

Christian said…
This comment just to say that I really enjoy reading your blog entries! Thanks for writing about development back in the 8-bit days :-)
Steve said…
Thanks for reading and commenting! :)

Popular posts from this blog

Nodes of Yesod : ZX Spectrum Next : Update #4

Odin Computer Graphics Part One : 1985

Software Projects : 1984