Not one capture here. Every picture is drawn from the bytes of the ROM, by running in Python the same two script interpreters the Z80 runs. The listing and the numbers are reproducible with make, and reassembling gives back the ROM byte for byte.
In SCREEN 2 the usual layout puts patterns at 0x0000 and colours at 0x2000. This cartridge does it the other way round, and you cannot see that in any address: you see it in the registers. tabla_registros_vdp (0x45C0) holds 02 E2 0E 7F 07 76 03 E4, and in SCREEN 2 registers 3 and 4 are not an address but base plus mask: bit 7 of R3 clear puts colour at 0x0000, and bit 2 of R4 set puts patterns at 0x2000.
The code settles it: 0x4831 draws the wordmark with ld hl,06300h, and 0x6300 masked to fourteen bits is 0x2300, inside the pattern table. Read it the usual way and the shapes come out right and the colours come out striped.
X is not one byte, nor two: it is three. 0x73F6 adds the velocity with add hl,de over 0xE138/0xE139 and carries into 0xE13A with adc a,c. The lowest byte is the fraction, in 1/256 of a pixel, and the top two are exactly the ones lee_celda_de_sala takes as the whole-pixel X when it looks up a cell.
It needs that much X because a room is wider than the screen: the even-numbered rooms are 64 columns, 512 pixels, which will not fit in a byte. When the explorer walks off the side, task 9 does ld (0e139h),bc and changes the high byte by one: a 256-pixel jump, one whole screen.
Five entity loops do ld hl,<closer> / push hl before dispatching, so the ret of whatever they dispatch to does not return to the caller: it falls into the closer, which bumps the index and goes round again. A static tracer cannot follow that, because the address travels through the stack as if it were data.
That is why 87 bytes were showing as "unidentified". All five closers have the same shape -ld hl,<index> / inc (hl) / cp (hl) / jp nz,<body>- and the body they jump to is exactly the label that pushed them.
0x4501 is nineteen bytes that decode as another variant of drawing a script across the three thirds. 0x4542 is ten bytes that are the counterpart of prepara_escritura_vdp for READING VRAM: it calls SETRD and takes the read port from 0x0007, just as the other takes the write port from 0x0006.
Neither is called, and that is not an impression: the byte pairs 01 45 and 42 45 appear nowhere in the 16,384, checked byte by byte. The second one says something about how the game is built: this cartridge never reads VRAM back. It writes and forgets.
The game carries six parallel entity tables and each has its own accessor. You do not have to guess how big an entry is: it is in the multiplication. 0x6A12 does i, 3i, 7i with B carrying the powers of two, so stride 7; 0x65A5 reaches 9i; 0x5AF6 joins the same chain halfway and reaches 17i; and 0x73D3 does 2i, 6i, 22i.
Reading the level descriptor byte by byte says what each one holds: stride 7 the exit doors and the revolving doors, stride 9 the jewels and the trap walls, stride 17 the knives already thrown and stride 22 the mummies. The stride-7 and stride-9 lists share an index, but they are not one entity split in two: they are the doors and the jewels, and what they share is a counter variable.
actualiza_tabla_de_sprites always writes the same four sprites into the attribute table, but each frame it starts from a different one of a four-entry ring, rotated by a counter at 0xE061.
It is not decoration. On the MSX1 only four sprites per scanline are shown and the lowest-numbered ones win; rotating the write order means the one that drops out changes every frame, so all four flicker evenly instead of the same one always vanishing.
The rooms came out black on black until we found who sets the colour of the wall and floor tiles. It is not prepara_sala_nueva: it is columna_decorativa (0x6DA0), and the script it uses depends on the group of four levels -0x6DC4 plus 9 per group, with the group taken from (level-1) >> 2-.
That is why the first four rooms are ochre stone and the next ones change colour with the same brick artwork: the only thing that changes is eight bytes of colour.
Two routines write into the cartridge’s own address space, and from ROM neither does anything. They are not dead code: they are copy protection, and doing nothing is the whole point. A pirated cartridge is a copy in RAM, and there the writes land.
0x403E smashes the first byte of task 1 with a pop hl + ret; 0x409C writes DE over 0x43C0, which is the operand of the jp nc at 0x43BF. Manuel Pazos identified both in his 2009 disassembly; this project had one written up as a failed patch and the other as a fill address.
At 0x5817 our ROM has the three separate comparisons the second build replaces with two subtractions, and the second build’s signature appears nowhere in the 16,384 bytes. What is disassembled here is version 1, bugs included: throwing a knife while the door opens corrupts its tiles, throwing it against an object passes through it, and two trap walls sit in the wrong place.
The pop de at 0x6B0E restores the pointer to the band that has just been unpacked, so the cp 030h two instructions later tests THAT band, not the next one: the 0x3x band is drawn and ends the list. Read the other way round it loses one band per room.
The fix is not an opinion: the room buffer was compared against the RAM of a real machine for all fifteen pyramids, 31,680 cells with no difference, and the drawn screens against its VRAM - name, pattern, colour and sprite tables - also with zero.
The cartridge’s labels are not in ASCII, and not in a private font with a lookup table either: they are ASCII shifted down by 0x20. The bytes 2B 2F 2E 21 2D 29 plus 0x20 give KONAMI, and the same sum gives SCORE, HI, REST, PUSH SPACE KEY, PLAY START, GAME OVER, SOFTWARE and PYRAMID. 0x00 is the space and 0x1A the copyright sign.
Konami hid its catalogue number and the title in katakana at the end of many cartridges; Manuel Pazos (@ManuelPazosMSX) found it. This one has it, ending at 0x7FFF: RC-727 and OU KE NO TA NI, the game’s Japanese title, 王家の谷, the Valley of the Kings.
Under each picture is where it comes from and what is on it.
rotulo_horizontal (0x76A7) writes one end tile and fills sideways, dropping a row and growing one cell each time round until it passes VRAM 0x3A80prepara_sala_nueva (0x4FC9) leaves it. The wall tiles are loaded but still colourless: columna_decorativa supplies that afterwards, and it is what gives each group of four rooms its own stone