Gig Rush: a 2020s game, built for 1982 hardware
Every game on this site so far has been about the past. This one is about right now — running on hardware from 1982. Gig Rush is a complete, original single-screen arcade game: you're a delivery rider, your phone battery is both your health bar and your countdown clock, and six lanes of traffic stand between you and your tips. It's 2,630 bytes of hand-assembled Z80, no BASIC anywhere, and it plays start to finish — title screen, scoring, a difficulty curve, game over, high score, restart.
The pitch
Every generation gets the arcade game its anxieties deserve. The 80s had Frogger's hazard-crossing and Pac-Man's endless pursuit; 2020s life apparently deserves dodging traffic on a bike for tips while a battery percentage quietly decides when your shift ends. Gig Rush collects orders from the GRUB HUT at the top of the screen and delivers them to a customer's door at the bottom, with two zones of horizontal traffic in between. Battery drains constantly (it's the clock), crashes take a hard chunk out of it (it's the health bar too), and every delivery tops it back up. Every third delivery, the lanes get faster. It ends, one way or another, when the battery hits zero.
A single screen, on purpose
No scrolling, no second screen — everything happens in fixed horizontal bands, which keeps the whole design mentally tractable and, not coincidentally, is exactly how a lot of the best 1983 games worked:
row 0 HUD (score / hi / orders)
band 0 rows 1-2 top pavement, GRUB HUT door
bands 1-3 rows 3-8 three traffic lanes
band 4 rows 9-10 grass median
bands 5-7 rows 11-16 three more traffic lanes
band 8 rows 17-18 bottom pavement, customer doors appear here
row 19 battery bar
row 21 status message
This bit of bookkeeping mattered more than it looks: my first draft printed the title text at row 3 — which is inside a live traffic lane — and the attract-mode traffic redrew straight through the words every few frames. The fix wasn't clever, just disciplined: keep every piece of text confined to the pavement, grass and message rows that nothing else ever touches.
Traffic as a bitmask, not a sprite list
Each of the six lanes is eight bytes: which band it's in, which direction it moves, how fast, a countdown, and a 32-bit mask — one bit per column across the screen. A car is a run of set bits. Moving the whole lane one column is a single rotate of that 32-bit value:
; cars moving right: rotate the mask right, wrapping the far bit
; back in at the start
ld a,(ix+7)
rra ; old column 31 -> carry
ld a,(ix+4)
rra
ld (ix+4),a
ld a,(ix+5)
rra
ld (ix+5),a
ld a,(ix+6)
rra
ld (ix+6),a
ld a,(ix+7)
rra
ld (ix+7),a
No per-car object, no list to walk, no allocation — the entire lane's worth of traffic moves in four rotate instructions. Redrawing only re-paints the columns that actually changed shape (empty road, or a left/middle/right piece of a car body), and each lane redraws only when its own countdown fires, so faster lanes genuinely animate faster without a separate timer per car.
Getting the car to look like a car
The first pixel-art pass drew the cars as a rounded shape, fully symmetric top and bottom. On screen, six lanes of them didn't read as traffic — they read as a field of gumdrops. The fix was to stop rounding: a flat, squat rectangle with a strip of open road above and below it, and two small wheel nubs underneath, reads as “car, seen from above” far more readily than any amount of rounding did. Shape carries more information than detail at 16×16 pixels.
Built and tested the modern way
Here's the actual 21st-century part, and it's not on the screen at all: this game was never manually poked at in an emulator to see if it worked. It was assembled with a real cross-assembler (the free Pasmo, not hand-counted opcode bytes) and then run through a small custom Z80 interpreter, written in Python, that plays the game headlessly: it boots the ROM-free binary, simulates keypresses through the actual keyboard-matrix ports, and asserts on the results.
# excerpt from the test suite
keys.add((0xFB, 0)) # hold Q (up)
while rd(RIDERB) != 0:
cpu.run_frames(4)
keys.clear()
cpu.run_frames(2)
assert rd(PHASE) == 1, "should have collected the order"
The suite drives a full playthrough — boot to title, start a game,
walk to the restaurant, collect, walk to a customer, deliver, verify the
score and battery math, force a crash and check the penalty and respawn,
drain the battery to zero and confirm game over, confirm the high score
survived, and restart. It caught the traffic-through-text bug and would catch
a regression in five seconds flat, which is a very different development
loop from the one this hardware was born into. The interpreter itself,
z80mini.py, is downloadable below if you'd like to headlessly
test your own Z80 programs the same way.
Play it
- gigrush.tap — ready to run: load it in Fuse or any .tap-capable emulator. Q A O P to move, S to start.
- gigrush.asm
— the full commented source. Build it yourself:
pasmo --tapbas gigrush.asm gigrush.tap - z80mini.py — the headless Z80 test interpreter used to verify this game, dependency-free Python 3. Point it at your own binaries.
Ideas if you fork it: a second customer type worth double points but with a tighter delivery window, a Kempston-joystick control option (see our interface build), or per-lane sound cues so you can hear a fast lane coming before you see it. The whole game is under 2.7KB — there's room in a 48K machine for a great deal more chaos.