Not one capture here. Every picture is drawn from the bytes of the ROM, by running in Python the very same scripts, mirrors and figure readers the Z80 runs, and then checked byte for byte against openMSX's VRAM: nine screens dumped from the emulator come out with zero differences in colour (6,144 bytes), patterns (6,144) and sprite patterns (1,792), and the title screen matches 768 of 768 tiles as well. The listing and the numbers are reproducible with make, and reassembling gives back the ROM byte for byte.
Asked by theNestruo in issue #1, where he said from memory that it took a particular movement at a particular point. That is exactly what it turned out to be, down to the word movement. The table at 0x507A was here all along with a note saying nobody knew what it was for.
The steaming bowl that makes you invulnerable for a while does not come out at random. At the start of a round 0x5027 copies into 0xE300 the pair that belongs to it from a table of eight at 0x507A, two bytes per round: row and column. That table was already in the listing, with nobody knowing what it was for.
Then, one frame in two, 0x73FD asks whether the player is there. But it does not look at where the figure is: it looks at 0xE12C, the FOURTH of the four boxes built by 0x684A -the strike box, the very one 0x53B3 uses to decide whether the player reaches the rival-. It has to fall inside an 11x11 window starting two beyond that pair.
And that fourth box does not exist in every frame. Of the ten drawings at 0x6C83 only 1, 3, 5 and 6 carry it, and those are the four attacks; in the other six the script has 0x80, 0x684A leaves the box at zero and the and a at 0x65C6 knocks it out. So standing on the spot is not enough: you have to strike there. That is the particular movement at the particular point.
The eight pairs (row, column): (0x7E,0x80) (0x8E,0xE0) (0x68,0xD8) (0x8E,0x03) (0x9E,0x90) (0x68,0x10) (0x68,0x80) (0x8E,0x80). Only in phase 3 of the round -0x733E demands (0xE107) = 3- and with a single player (0x732F).
What it gives: (0xE29E) = 0xA8, counting down by one every other frame: six and a half seconds at 50 Hz. While it is not zero the player's blow does not count (0x53F4), the rival throws nothing (0x5588), whatever was already flying gets caught instead of hitting (0x566F), the slots cannot be broken (0x7734), nothing touches the player (0x798C) and the figure blinks (0x6C1E). It also adds 500 points. And once per round only: when it runs out 0x7496 sets (0xE261) = 1 and scene 0 stops asking.
Checked in openMSX: during the demo (0xE300) = (0x7E, 0x80), the first pair of the table. Moving the spot onto the player makes the bowl drop, and touching it sets 0xE29E to 0xA8, counting down to zero.
The very first thing INIT does, before it even installs the interrupt hook, is call 0xBF6C. There sits a slot scan: it walks the four primary slots from 0xFCC1, descends into subslots when bit 7 asks for it, switches page 1 of each with ENASLT and takes two 16-byte sums, one from 0x5300 and one from 0x6700.
It compares them against the two pairs written at 0xBFD9: 5A 47 and 23 70. Those are the two builds of the original Yie Ar Kung-Fu (RC-725), recognised separately. Running those two sums over the other ROMs in the series matches none of them, so the identification is not a guess.
If it finds it, (0xE450) = 1. The only place in the game that reads that flag is 0x74A3, and it also demands level 3 or later (0xE053), a single player (bit 5 of 0xE002) and phase 3.
What it gives, measured. It shows up when both energy bars are nearly empty -the player's below 9 and the rival's below 13, out of 0x24 full-: a 4x3 tile sign (0x7525, row 6 column 14) and a drink that comes down, the sprite from the four bytes at 0x74F1. Taking it jumps to 0x738C with B = 1, and when the 0x10 rest frames run out 0x7356 puts the player's bar back to 0x24. The rival's is left alone and nobody loses a life. In openMSX the bars go from (0x08, 0x0C) to (0x24, 0x0C).
And it has nothing to do with the soup: they are two separate items in two separate attributes -0xE250 for the soup, 0xE254 for the drink- and the neighbour flag does not appear anywhere along the soup's path.
0x597F builds the fight screen from forty scripts pointed at by forty pointers spread over six contiguous tables, 0x592F to 0x597E. But only half of it is drawn: 0x5A4D copies three stretches of the pattern table onto themselves through vuelve_los_bits, and the right half of the scenery is the same drawings reversed.
That explains an oddity you can read straight off the code: colour is written twice and the pattern once. The mirror never touches colour. And the arithmetic checks out on all three bands: 0x0560-0x0260 = 0x300 and the other two 0x3C0, exactly the offset of each copy.
The screens where enemies arrive in a row are not drawn anywhere. The strip at 0x5BE8 for that scenery carries eight nibbles in four bytes; each nibble picks one of the thirty figures at 0x5C64, and the eight are painted in a row, four columns apart. Even ones come from the low nibble and odd ones from the high nibble, which is what the bit 0,b at 0x5B9F says.
0x67F5 and 0x67A7 read the same figure format except for the 0xE0..0xEF order, and there they differ in both ways at once: the first repeats the byte that follows and takes two bytes, the second writes zeros and takes one. Mixing them up does not blow up the listing: it gives a figure that almost fits and overruns by two or three bytes.
Both live in tools/formatos.py, and what proves it is that the thirty wave figures only fit the first and the rivals' only the second.
Every scenery has its rival, and they hang off two tables of eight indexed the same way: 0x6922 for the frames and 0x7FDB for the behaviour. Each block is 22 pointers closing at 44 bytes, and the 168 stretches between consecutive entries tile with no gap and no overlap.
The odd frame is the even one facing the other way: the bit 0,a at 0x677C follows a two-byte pointer instead of reading the drawing, and the same bit negates x at 0x686A. The eight rivals are YEN-PEI, LAN-FANG, PO CHIN, WEN-HU, WEI-CHIN, MEI-LING, HAN-CHEN and LI-JEN (0x4497); the one you play is LEE YOUNG (0x447B).
0x43B8 keeps the first ten key presses since power-on and compares them against the ten bytes at 0x43ED: 01 04 04 02 02 02 08 08 08 08. With the PSG bit layout that reads up once, left twice, down three times and right four, a 1-2-3-4. If it matches, (0xE055) goes from 3 lives to 0x95.
One LEE YOUNG frame is four header sprites plus up to eight [y][x][pattern] triples, hanging off the twenty pointers at 0x6C83 -ten drawings in the even entries and ten two-byte redirections in the odd ones-.
And there is the saving: facing one way, the eight sprites use patterns 0 to 7, which 0x6BE6 has just uploaded to 0x1800 following the frame's own scripts; facing the other, the triples carry pattern numbers that land in the mirrored bank left by espeja_sprites (0x490C). One drawing, two directions, only one half stored.
The last fifteen bytes of the cartridge, closing at 0xBFFF, carry RC-737 and the title in katakana written backwards. It is the mark Manuel Pazos found in Konami's cartridges, and this one has it.
There is also a second header at 0x4010, with its own AB and the bytes 07 37: the one the Konami Game Master reads from the other slot.
Five routines -0x47B0, 0x4875, 0x488A, 0x544F and 0x6EB5- are named by no call, no jp and no table. They are declared as entry points so they come out as code rather than data, and the reason for each is written down in src/yiear2.entries.
pon_la_figura_en_la_pantalla (0x66F1) steps down 32 tiles row - 1 times, because of the dec a at 0x66FB. A figure asked for at row 4 starts at row 3, and row 0 is the exception because the jr z at 0x66F9 skips the loop entirely. Without that, the title logo lands four rows too low and three columns too far left.
Under each picture is where it comes from and what is on it.
cp 003h at 0x5A7A.