Sixteen kilobytes on page 1, without a single bank switch. Everything the game does fits between 0x4000 and 0x7FFF.
The first sixteen bytes are the header the BIOS reads: the AB signature and four pointers. Only INIT has a value (0x4081); STATEMENT, DEVICE and TEXT are all zero. So the cartridge adds no commands to BASIC and does not declare itself as a device: it boots and keeps the machine.
INIT sets up house and leaves:
jp to 0x4010;From then on the main program and the interrupt split the work. The interrupt carries the clock, the controls and anything that has to go at screen rate; the main program, the game's state machine.
Working RAM is 0xE000-0xE3FE, with the stack at the very top. It is laid out in zones, and each one has its explanation in the listing:
| range | what it is |
|---|---|
| 0xE000-0xE001 | frame counter and wait in units of 32 |
| 0xE002-0xE017 | the state of the game: who plays, with what, round and event |
| 0xE019-0xE01C | the menu: where the arrow is and which line is picked |
| 0xE01D-0xE01E | the interrupt's two semaphores |
| 0xE020-0xE02E | the state of the attempt and the event, bit by bit |
| 0xE040-0xE04B | the four world records, three BCD bytes each |
| 0xE051-0xE052 | this round's mark to beat |
| 0xE060-0xE09F | the scoreboards as painted |
| 0xE0A0-0xE0FF | the two athletes: speed, angle, mark and pose |
| 0xE120-0xE15F | four 16-byte actor records |
| 0xE160-0xE190 | the sound player's three channels |
| 0xE200-0xE22F | the state of the distance events |
| 0xE230-0xE2CF | the scratch area where figures are assembled before upload |
They deserve their own section, because they are the reason this does not fall apart.
The second one is the fine trick. Setting an address in the VDP is two writes to the control port, and if the interrupt slips in between them —and it writes to the VDP too— whatever comes next gets written in the wrong place. Rather than forbid the interrupt, you let it through and check afterwards.
SCREEN 2, with its three thirds:
| table | address |
|---|---|
| names | 0x3800 |
| patterns | 0x2000 |
| colour | 0x0000 |
| sprite attributes | 0x3B00 |
| sprite patterns | 0x1800, 16x16 |
The eight VDP registers come from registros_del_vdp (0x4E73).
In the listing, VRAM addresses appear with bit 14 set (0x4000 added) when they are about to be written, which is how the VDP wants them: that is why 0x7800 is the name table and 0x5800 the sprite patterns. Worth keeping in mind when reading a dump, or the numbers will not add up.
A pattern that repeats and that misleads when reading the listing: there are tables whose recorded address falls two bytes ahead of the first useful slot, because the index starts at one and the code advances 2*index before reading.
| table | what the code says | where it really starts |
|---|---|---|
| event labels | 0x4F33 | 0x4F35 |
| event screens | 0x4F3B | 0x4F3D |
| event scoreboards | 0x4F43 | 0x4F45 |
| melody pointers | 0x6D26 | 0x6D28 |
| actor records | 0x7B44 | 0x7B46 |
It is not a mistake: slot zero is never used. And the label table's slot zero does double duty: the two bytes at 0x4F33 are the FF FF that closes the last menu message. Two bytes with two jobs.
The four actor records live back to back at 0xE120, 0xE130, 0xE140 and 0xE150, sixteen bytes each. And that is no accident: bit 2 of a record makes that actor bump the counter of the one 0x11 bytes behind it, and bit 3 the one 0x0F ahead.
That is how the scenery drags itself along in a chain with nobody keeping a list of who pushes whom: the relationship is the distance between the records.
Konami's hidden mark is not there. Many cartridges from the house hide their RC-7xx catalogue number and the title in katakana at the end of the ROM, behind the filler; it was Manuel Pazos (@ManuelPazosMSX) who found that out. Here the last useful byte is sound player code and only one 0xFF of filler is left. It was checked with tools/marca_konami.py, which in the same run does find it in another cartridge from the house, so the method works.