You can play this game online using the JSBeeb emulator here:
How to play
Use Z and X to move the helicopter (green block in the thin version) left and right, and "shift" to thrust upward. Avoid the birds (red blocks) and collect coins (yellow blocks). Each coin you collect on a single trip doubles your bonus.
When you hand on the platform, the bonus is added to your score and zeroed, and you receive a new load of fuel. The aim of the game is simply to get the highest possible score.
If you run out of fuel (indicated by the red bar), you lose the ability to thrust upward, though you can still land if you're nimble enough. If you collide with a bird or miss the platform, you die. Press "space" to start a new game.
How to build
Building the resources requires python 2.7 with pygame. Type:
python gfx.py > gfx.s
Building the binary files requires atasm or equivalent:
atasm -r gfx.s
atasm -r -DHACKADAY_1K=1 -DHACKADAY_2K5=0 copter.s -othin.bin
atasm -r -DHACKADAY_1K=0 -DHACKADAY_2K5=1 copter.s -ofat.bin
How to launch
Load the disk image "copter.ssd" into your hardware or emulator (we recommend BeebEm for this).
If using real hardware, set up the keyboard links to boot into graphics mode 2 (see below).
If using an emulator which doesn't support the keyboard links, change to mode 2 on startup by typing:
To run the "fat" version, type:
To run the "thin" version, type:
BASIC scripts "GOFAT" and "GOTHIN" are provided to automate these steps. At startup, type:
On top of writing data to the display, we need to interact with the hardware in a couple of ways. The OS ROM is off limits, so we have to do this the old-fashioned way.
We poll for vblank by reading bit 1 of the system 6522 VIA interrupt status register at 0xfe4d. The vblank status is cleared by writing to the same register. We clear the vblank status immediately before the main loop to ensure we're in sync from the very first frame, as otherwise we see some flicker early on.
We scan the keyboard by disabling keyboard auto-scan at start of day, and then writing to and reading from the slow databus at address 0xfe4f. The (completely insane) annotated disassembly for Exile here:
was very helpful in figuring out how to do this.
The BBC Micro lacks hardware sprite support, so all our moving objects (birds, coins, the helicopter, the platform) need to be drawn to the framebuffer in software. This is made more exciting by the BBC's odd screen memory layout. Each byte contains two horizontally-adjacent pixels interleaved in bits (6,4,2,0) and (7,5,3,1), and pixel-pair byte addresses look like this:
0x000 0x008 0x010 ..
0x001 0x009 0x011 ..
0x002 0x00a 0x012 ..
0x003 0x00b 0x013 ..
0x004 0x00c 0x014 ..
0x005 0x00d 0x015 ..
0x006 0x00e 0x016 ..
0x007 0x00f 0x017 ..
: : : : : :
Our sprites are all 7*8 pixels in size (allowing us to support odd x coordinates using pre-shifted copies). The function "plot" does the necessary setup and uses one of three kernels ("pad", "store" and "blend") to do the actual writing.
Blend is the most interesting kernel. It uses a per-pixel mask stored in the "spare" senior bit of each pixel (the BBC Micro only supports 8 colors, so only three bits are required for the full palette) to do pixel-accurate masking and hit detection.
Chasing the raster
The game runs in graphics mode 2, whose framebuffer consumes 20K of the 32K of available RAM. Double buffering is out of the question, so to get flicker-free graphics we must carefully manage the position of the raster. In summary:
- The game runs "in a frame" at 50Hz.