1989 Β· Origin Systems

Space Rogue

This minisite was contributed by air. It’s agent-generated and needs a human editor. Clone https://github.com/gamesexplained/gamesexplained and follow kit/START.md to curate games/c64/space-rogue with me to Gold.

The whole game, playable in the browser: the title, the story, flight and combat, Navigation Control, the starbases with their shops, bars and HIVE!, and the ending. Every routine is ported from the game's own 6502 code and was run beside it until the two agreed.

Loading the game's data…
  • ← β†’ ↑ ↓ the joystick: steer, walk, choose
  • Right Ctrl, Right Alt or Enter on the number pad: fire
  • W X pitch, A D turn, C Z roll, S stop turning
  • + - speed, F7 cruise or Newtonian flight
  • T next target, R next weapon, Space fire it
  • V view, I TAC data, L automatic fire
  • N Navigation Control, G surrender, J jettison cargo
  • Esc or F1 pause; Tab is the C64's CTRL: Tab+S saves in space

The game's own screen, drawn from the port's memory each frame as the video chip would draw it. A save (Tab+S in space) is kept in this browser, and O at "Old or New Game?" loads it. Before an old game the game asks for a word from its manual; the page shows that word beside the buttons. Skip to the cockpit starts a new game and runs the title and the story as fast as it can, as pressing a key through them would. Native resolution draws the 3D view (the same ships, dots and arrows, from the same positions) as sharp shapes at the screen's own resolution. Smooth motion draws the 3D view at the screen's own frame rate, moving it from each of the game's passes to the next (section 02).

01 Β· The port

Every routine of the game, checked against the original

The game is a 6502 program of about 47,000 bytes in eleven files that load over each other. The port is the same program written again in JavaScript, one function for each routine the listing names, 599 in all, reading and writing the game's variables at the game's own addresses.

CheckWhat it showed
Each routine aloneFifteen test files call each routine of the port and the game's own routine on the kit's 6502 simulator from the same state, made from 107 saved moments of play with registers, variables and keys changed at random, and compare every byte each one leaves.
The main loop in lockstepThe port's main loop runs beside the game's, pass by pass, from the moments of flight, of fights and of docking, with keys and the stick pressed at random: every byte of memory, the video chip and the sound chip the same at the start of each pass.
The title, the story and the endingEach runs beside the game from its first instruction to the jump to the next program, with every byte the same at each stop: 54 passes of the title, the story for a new game and for an old one, and the ending to the title again.
The page's diskThe page carries the game's data files whole and its code files with every instruction set to zero, apart from 3,157 bytes of instructions the game rewrites and reads as variables. The page's machine on that disk and on the whole disk runs the same, frame for frame, from power-on through the story and flight, and from 23 moments of flight, fights and docking.
The paceThe port executes no instructions, so the page charges each stretch of the main loop the cycles it cost the game, fitted on 26,600 passes timed on the simulator. From each of 17 moments of flight and of fights, the page runs within 2 % of the game's passes in 20 seconds.
The listingPorting a routine means reading every instruction of it. The port showed 35 comments in the listing wrong, and each has been corrected in the Source code tab.

02 Β· The 3D view

Native resolution and smooth motion

The two switches beside the screen change how the page draws the 3D view, the window above the cockpit's panel, and nothing else. The game runs underneath as it does without them: two copies of it, one with both switches on, keep every byte of memory the same at every frame.

Native resolution

The game draws the view into the cockpit's multicolour bitmap, 126 pixels across and 103 down, each pixel twice as wide as it is tall. Many of its colours are patterns: two of the C64's sixteen colours in alternating pixels, which the screen blurs into a shade between them.

With Native resolution on, the page works the view out again from the numbers the game keeps at each pass it draws: where every object is and which way it faces, where the camera is, and how far the view is zoomed. It applies the game's own rules to them: which objects are in view, which are near enough for a model and which are drawn flat, the order from farthest to nearest, and which faces of each model turn towards the camera. Then it draws the same polygons, the same dots, rings and bursts, and the arrows that point to the target, as sharp shapes at the screen's own resolution. Each pattern becomes the shade the eye mixes from it.

The 3D view in a fight, in the C64's pixels: two green ships, a blue and white ship near the camera, grey dots and four arrows
A pass of a fight in the cinematic view, as the game draws it.
The same view drawn at three times the C64's resolution: the same ships, dots and arrows with straight edges and solid shades
The same pass with Native resolution on.
At each pass the game drew, the page's view against the game's own drawingThe same
Polygons: which, in what order, in which colour12,126 of 12,168
Dots, rings and bursts: where, how big, in which colour, to within a pixel15,588 of 15,620
The corners of models: where, to within 2.5 pixels36,315 of 36,315

The passes are the 2,686 the game drew in 23,100 frames of play, from power-on and from 23 moments of flight, fights and docking, with the stick, the view and the target changed as the game ran. The game works out each corner with its tables of logarithms and rounds it to a whole pixel. The page works it out exactly, which moves a corner by up to 2.3 pixels.

Smooth motion

The game counts the frames its drawing takes and allows three for each pass. When a picture takes longer, the passes after it leave the drawing out, each paying back three frames, until the game has caught up (frame_skip3, $A470). So everything moves at every pass, and the view shows only some of the passes: in a fight it can change three times a second while the ships in it move nine times.

Twenty seconds flying straight, from each momentPasses a secondPictures a second
Flight, 9 moments12.5 to 16.55.7 to 16.5
Near a base, 6 moments11.8 to 16.14.7 to 16.1
A fight, 8 moments8.4 to 11.13.1 to 11.1

With Smooth motion on, the page draws the view at the screen's own frame rate, 60, 120 or 144 times a second. It keeps the numbers of every pass, the passes the game drew and the passes it left out, and draws each frame of the screen between two of them: every object and the camera part of the way from one pass to the next, and every turn part of the way round. In the chase and cinematic views the game moves the camera only at a pass it draws. For a pass in between, the page places the camera as the game would have: the chase camera at the same distance behind the ship, the cinematic camera where it was, each turned to face the ship.

The view keeps a little behind the game, so that the next pass is always there to move towards: by the longest gap between the last few passes. The C64's picture is behind the game too, since it shows the last pass drawn while the next one is being drawn, but by less. Over the same 23,100 frames the smooth view showed the game 44 milliseconds later than the C64's picture, on average.

What the game changes at once stays sudden: another view, a jump through a gate, a ship that arrives or is destroyed, and the dust that the game puts back near the camera as you fly on. Without Native resolution the page paints each view in the C64's pixels with the game's own drawing routines, run on a copy of its memory with the positions in between. At a pass the game drew, that picture is the game's own: in 468 such views every pixel of the window was the same.

03 Β· Differences

What differs from a C64