Sixteen kilobytes in the MSX's page 1, from 0x4000 to 0x7FFF. The game takes up 8,115 bytes: from 0x5FB3 onwards there are 8,269 bytes of 0xFF in a row, chip padding that no instruction ever reads.
41 42 5F 40 00 00 00 00 00 00 00 00 00 00 00 00
A B INIT=0x405F
AB is the signature the BIOS looks for at boot, and behind it come four pointers. This cartridge declares only the first one, INIT; the other three — STATEMENT, DEVICE and TEXT — are zero.
0x405F does this, in order:
jp 0x4034 in the H.KEYI hook (0xFD9A), the one the BIOS calls on every interrupt.jr $ (0x409E) and never comes out.From then on the main thread does nothing. The whole game runs inside the interrupt, which is the usual arrangement in Konami cartridges of this period.
SCREEN 2, with the register table at 0x46BE:
| table | address |
|---|---|
| names | 0x3800 |
| patterns | 0x2000, in three thirds of 0x800 |
| colours | 0x0000, in three thirds as well |
| sprite attributes | 0x3B00 |
| sprite patterns | 0x1800, 16×16 |
In SCREEN 2 the character generator is three independent tables, one per third of the screen, which is why nearly every load in the cartridge repeats the same copy three times adding 0x800. The exception is the big title lettering (0x5766), loaded into the first third only: that is where its two rows fall.
Almost nothing is copied raw. There are two formats, and both turn up all over the cartridge.
Format A, read by 0x472E: two destination bytes in VRAM and then the tile numbers, with 0xFE to change destination and 0xFF to finish. Every text label uses it.
It has a nice touch: the same list serves to draw and to erase. Entering at 0x472A instead of 0x472E, the only difference is that C holds 0x00 rather than 0xFF, and down in the loop there is an and c that turns every tile into a zero. The menu's blinking is the two entry points of one routine over one list.
Format B, read by 0x4969: a stream of commands where bit 7 separates the two cases. With the bit set, the low seven say how many literal bytes follow; without it, how many times the next byte repeats. 0x00 closes the block and 0xFF means another destination follows. The Konami logo, the table colours and the whole table plan are compressed with it.
The 48 glyphs at 0x4136 are loaded into tiles 0x30 to 0x5F, and those numbers are exactly the ASCII codes of the characters they draw: the digits, a few symbols and the capitals.

That is why the cartridge's text lists spell the words out literally, in ASCII, with no conversion table in between. In the slot where ASCII has the backslash, the bracket and the underscores (0x5C to 0x5F) sit four drawings of its own: the two menu cursor tiles, a diamond and a filled ball, which are the ones that draw the goes and the spare balls on the scoreboard.

The 59 table tiles are copied verbatim from 0x4A70, but their colours come compressed separately at 0x4C40. And there is a deliberate overlap there: the first eight bytes of the colour block are also the last eight of the pattern copy. Konami spliced the two tables to save eight bytes.
There is a second overlap, and a stranger one. The sprite patterns are copied from 0x4B30, which sits inside the table tiles: the balls and the cloth drawings are the same bytes read two ways.
![]()
288 bytes are copied, enough for nine 16×16 sprites, but only eight are real drawings: the cue ball, the six numbered ones and the aiming dot. The ninth overruns the end and lands on the table's compressed colours. Nothing uses it.

Measuring the patterns themselves, a ball's drawing takes ten pixels across by twelve down. With the square pixel of a modern monitor —the top row— that is an oval, and it shows.
Ten and twelve are screen pixels. The VDP's register 1 comes from 0x46BE and holds 0xE2: bit 1 set asks for 16×16 sprites, and bit 0 clear leaves MAG = 0, no magnification. Magnified, every pattern pixel would cover two on screen and the figures above would have to be doubled.
The bottom row is the same drawing with each pixel painted five across by four down, which is the ratio usually attributed to an MSX pixel on a television of the period. The 10×12 becomes 50×48: round, to within 4 %.
The ratio that would make the balls exactly circular is 12/10, that is 6:5, which falls between the two figures usually quoted for the MSX pixel. That one does come from the cartridge; what an MSX pixel is really worth depends on the television, and the binary does not say.
The picture is drawn by tools/graficos.py from the bytes of the ROM.
No. The physics works 1:1, and you can point at it:
busca_choques (0x4F25) does its first sieve with cp 00Ch on the Y difference and the very same cp 00Ch on the X one: twelve pixels on both axes;resuelve_el_choque (0x4F63) confirms with Pythagoras, and treats both components with the same instructions —the same four-bit shift, the same squaring— before adding them;calcula_el_tiro (0x4CA8) gets the distance to the aiming point the same way, dY and dX squared and the root;saca_la_pendiente_del_vector (0x5430) divides both components by 32 and compares, favouring neither;So the balls look round and behave as if the space were square. In practice that means a ball covers more real distance per pixel vertically than horizontally, and a 45° angle in the code does not look like 45° on screen. It is the solution of the period, and it is the cheap one: correcting the ratio would cost a multiplication per axis on every collision.
It was pointed out by theNestruo in this repository's first issue.
The TMS9918 has thirty-two sprites in its attribute table —here at VRAM 0x3B00, which is what R5 = 0x76 asks for— but it only draws four per scanline. From the fifth on it drops them on that line and raises its fifth-sprite flag. X plays no part: a sprite counts on a line if that line falls inside its box, and with 16×16 and MAG = 0 the box is sixteen lines, Y+1 to Y+16. Sixteen, not the twelve a ball's drawing measures.
Billiards uses eight of the thirty-two entries: the cue ball, the six numbered ones and the sight.
![]()
The eight starting positions are at 0x461F, four bytes each (Y, X, pattern, colour). Counting whole boxes:
| lines | sprites | which |
|---|---|---|
| 97-104 | 1 | the 4 |
| 105-112 | 2 | the 2 and the 4 |
| 113-120 | 4 | the cue ball, the 1, the 2 and the 5 |
| 121-128 | 4 | the cue ball, the 1, the 3 and the 5 |
| 129-136 | 3 | the 3, the 6 and the sight |
| 137-144 | 2 | the 6 and the sight |
Four at most, never five. And by a hair: the box of the 4, which starts at Y=0x60, ends on line 112, exactly where the boxes of the three that start at Y=0x70 begin. One pixel breaks the arrangement: the 3, which starts at Y=0x78, moved a single line up already puts five sprites on line 120.
In the picture each ball carries its 16×16 box —the dashed square, which is what the VDP looks at— and the column on the right counts, line by line, how many sprites touch it, with the figure beside it; the lines where the count reaches four come out in red. It is drawn by tools/sprites_por_linea.py from the bytes of the ROM, and the same program hangs off make sanity: if any of these figures stopped coming out of the binary, the make would go red.
A ball's centre can only sit between Y=0x3E and Y=0xA1 —the two sub in mueve_una_bola, the sub 03Eh at 0x4ED4 and the sub 064h at 0x4ED6— so the boxes of the seven balls live in 115 lines, from 63 to 177. And between the seven they measure 7 × 16 = 112. Three lines to spare: two boxes overlapping is not the exception, it is what happens all the time. With five overlapping, the VDP draws four and swallows the fifth.
The cartridge does not avoid it. What it does is make sure the one that is lost is not always the same one. refresca_los_sprites (0x463F) dumps the eight entries to VRAM in two groups of four, and bit 0 of the frame counter decides which group goes first:
avanza_el_juego bumps it with the inc (hl) at 0x433A, once per interrupt;rra at 0x4651 carries that bit 0 into the carry flag and picks the half it starts with;rr c at 0x465E pulls it out again for the second half, which is the other one.On an even frame the order in the attribute table is cue-1-2-3-4-5-6-sight; on an odd one, 4-5-6-sight-cue-1-2-3. Since the VDP keeps the first four on the list that touch the line, the one that is lost changes every frame.
An example. If the cue ball, the 1 and the 2 —first group— share a line with the 4 and the 5 —second group—, the even frame draws cue, 1, 2 and 4, and swallows the 5; the odd one draws 4, 5, cue and 1, and swallows the 2. Neither disappears outright: both flicker, at 25 Hz on a PAL MSX and 30 Hz on an NTSC one.
The cartridge never looks at the fifth-sprite flag. It reads the VDP status register three times —at 0x4035, 0x405A and 0x409A— and throws the value away all three: it is there to acknowledge the interrupt, not to find out what was lost.
The table is not stored as a screen of 768 tiles. At 0x4C59 there is a strip of groups, and each group is a number followed by a format B block: the number says how many consecutive rows carry that same drawing. The routine that unrolls it (0x4A39) draws the block and steps down 32 bytes at a time from row 7, reloading the pointer on each pass because the interpreter leaves it at the end. A zero closes the plan.
Seven groups covering sixteen rows, from 7 to 22, in 79 bytes.
Manuel Pazos found that Konami cartridges of this period hide their RC-7xx catalogue number and the game's title in katakana at the end of the ROM. This one does not: there is not a single RC-7 in ASCII in the 16 KB, and the end of the chip is 0xFF all the way.
The RC-706 this cartridge is known by comes from the catalogue, not from the binary.