Sky Jaguar

A vertical shooter in 16 KB, with a single 1,920-row stage that repeats, a landscape stored as run-length scripts, and a giant enemy whose body is drawn by the background itself.

Konami · © Konami 1984 · An RC-721 16 KB cartridge · MSX1 · page 1 · Dump 58abc5ae…

There is not one illustration or capture here. The wordmark, the landscape and the whole stage are drawn from the bytes of the ROM, by running in Python the same decompressors the Z80 runs, and checked byte for byte against the emulator's VRAM. The listing and the numbers come from the binary and are reproducible with make.

The cartridge in numbers

100%of the binary explained
464routines identified
1,920rows in the only stage
7,086bytes of code
9,298bytes of data
0bytes unidentified

What turned up when we took it apart

It does not carry Konami's hidden mark, and that was checked properly

At the end of many cartridges Konami hid its catalogue number and the title in katakana; Manuel Pazos (@ManuelPazosMSX) found it, and the block lives at offset 0x3FF0. This one does not have it, and not for want of looking: all 16,384 positions were scanned with a finder first validated against four other cartridges of the same family that do carry it, which is the only way to trust a negative. The dump shows why: the ROM runs full of data to 0x7FFB, with four bytes of filler left. No room.

One 1,920-row stage, and the scroll comes out of a table

There are no levels: there is one stage that repeats, and all that climbs is the difficulty (0xE1D3, capped at 2). The landscape is compressed: 244 bytes choose, every eight rows, which of twenty strips to draw. And the fine scroll is not computed, it is stored: every strip has eight versions, the same one started zero to seven rows lower, indexed by 2·(position and 7).

The giant enemy's body is drawn by the background

It appears twice in the stage, in two halves, and its body is not sprites: landscape strip types 18 and 19 are the only ones in the map that use characters 0xB0–0xEC, and each appears exactly once. On top go the sprites of the parts you shoot. The game starts it three steps before it comes into view, to give its graphics time to decompress.

The aiming table has two entries swapped

0x6917 holds nine firing directions, a quarter turn at radius 320. Seven of the nine match to the bit against 320·sin and 320·cos; entries 3 and 5 hold exactly what the other should. This is not rounding, it is an erratum, and two of the nine enemy firing angles point at the mirror of their intended one.

Two jumps that land mid-instruction, deliberately

At 0x6684 a jr c lands on the last byte of the ld bc,0x0300 at 0x6686. That byte is 0x03, and alone it executes as inc bc: with carry BC ends at 0xFE01, without it the whole ld runs. One byte saved over a jp. At 0x69A1 the same trick is played with the 03 of a ld (hl),3.

The same script both paints and erases

Of the two in-house decompressors, the name-table one (0x43D8) has a second door at 0x43F3: entering there it writes zeros instead of the data. So the cartridge does not keep one script to put each thing on screen and another to take it off — just one.

What the cartridge draws

Under each picture is where it comes from and what is on it.

<b>The whole stage</b>: all 1,920 rows, cut into eight columns of 240 and set side by side. It comes from running the cartridge's own PASO_DE_FONDO (0x5837) in Python over the 1,920 positions. That it is right is not an impression: the mosaic was compared cell by cell against real screens dumped from openMSX, with zero differences across 11,592 cells
The whole stage: all 1,920 rows, cut into eight columns of 240 and set side by side. It comes from running the cartridge's own PASO_DE_FONDO (0x5837) in Python over the 1,920 positions. That it is right is not an impression: the mosaic was compared cell by cell against real screens dumped from openMSX, with zero differences across 11,592 cells
The first of the two giant enemies, over the sea. The striking part is that <b>this body is not sprites</b>: the background paints it, with a landscape strip that appears exactly once in the whole map
The first of the two giant enemies, over the sea. The striking part is that this body is not sprites: the background paints it, with a landscape strip that appears exactly once in the whole map
The second giant, with its own characters. The game starts assembling it three steps before it comes up from the bottom
The second giant, with its own characters. The game starts assembling it three steps before it comes up from the bottom
The title screen, built with the cartridge's own steps: CARGA_TITULO (0x434D) decompresses the wordmark's pieces and PINTA_ROTULO (0x4374) places them in the name table
The title screen, built with the cartridge's own steps: CARGA_TITULO (0x434D) decompresses the wordmark's pieces and PINTA_ROTULO (0x4374) places them in the name table
A gameplay screen with its panel, at the position in the stage with the most distinct characters at once
A gameplay screen with its panel, at the position in the stage with the most distinct characters at once
The 104 characters the whole landscape is drawn with (0x40–0xA7), decompressed from 0x6DCA with their colours from 0x702B
The 104 characters the whole landscape is drawn with (0x40–0xA7), decompressed from 0x6DCA with their colours from 0x702B
The sixteen characters 0x00–0x0F, which <b>have no pattern at all</b>: 0x4B71 gives each one a solid colour, and they are the sea and the flatlands
The sixteen characters 0x00–0x0F, which have no pattern at all: 0x4B71 gives each one a solid colour, and they are the sea and the flatlands
The cartridge's 48 sprites of 16×16, from the script at 0x4F1B. That is 1,536 bytes to VRAM: the plane, the enemies, the shots and the explosions
The cartridge's 48 sprites of 16×16, from the script at 0x4F1B. That is 1,536 bytes to VRAM: the plane, the enemies, the shots and the explosions
The font everything is written with, from the 0x10–0x59 range
The font everything is written with, from the 0x10–0x59 range