Post subject: Romance of the Three Kingdoms II
Jigwally
He/Him
Active player (418)
Joined: 3/11/2012
Posts: 119
Working on a save corruption route for this game now. Like L'Empereur it uses linked lists which are abusable. Unfortunately there doesn't seem to be a specific RAM address that triggers the ending like in L'Empereur, so in order to trigger the ending I had to perform a very rudimentary ACE. Again I used a mid-frame reset to create a General pointing to controller input & using that began writing code (in the Koei bytecode) to a section of the zero page that seems to be unused. Then, by making an officer @ $0006 and sending it to an empty province, this causes the game to treat it as the only entry in a linked list which means setting it to 0x0000. This is the address for the virtual machine's program counter, so once you do this the game will begin processing all of the values on the zero page as code. Eventually it will get to the code you've written & perform the function call for the ending. Very unoptimized for now (5 1/2 minutes), due to the way the game handles appending linked lists I had to do some very specific setups to write to zero page and not crash the game. Link to video
Alyosha
He/Him
Editor, Expert player (3536)
Joined: 11/30/2014
Posts: 2732
Location: US
Cool! I'm impressed how quickly you are able to work through these games and find these solutions.
Jigwally
He/Him
Active player (418)
Joined: 3/11/2012
Posts: 119
There is a lot of shared code, particularly all of the Koei games have the built-in virtual machine + a standard C library in their Root page, so it is a lot faster going now that I worked most of it out already. I can do this same trick of blanking the program counter to read the zero page as code in L'Empereur, but I haven't worked out a way of writing a payload there yet. This + L'Empereur are probably by far the easiest Koei games to pull this off because of they make the most use of linked lists which allow for direct writing to specific RAM addresses. The other games still have corruptible pointers in the save data but not nearly as much control over what I can do with them. At least Uncharted Waters lets you enter in custom names + starting stats so those are potential payloads.
Alyosha
He/Him
Editor, Expert player (3536)
Joined: 11/30/2014
Posts: 2732
Location: US
Jigwally wrote:
There is a lot of shared code, particularly all of the Koei games have the built-in virtual machine + a standard C library in their Root page, so it is a lot faster going now that I worked most of it out already.
how did you figure that out? Is this information available somewhere? I'd be interested to see a write up on it.
Patashu
He/Him
Joined: 10/2/2005
Posts: 4017
Alyosha wrote:
Jigwally wrote:
There is a lot of shared code, particularly all of the Koei games have the built-in virtual machine + a standard C library in their Root page, so it is a lot faster going now that I worked most of it out already.
how did you figure that out? Is this information available somewhere? I'd be interested to see a write up on it.
https://forums.nesdev.com/viewtopic.php?f=2&t=15931
My Chiptune music, made in Famitracker: http://soundcloud.com/patashu My twitch. I stream mostly shmups & rhythm games http://twitch.tv/patashu My youtube, again shmups and rhythm games and misc stuff: http://youtube.com/user/patashu
Alyosha
He/Him
Editor, Expert player (3536)
Joined: 11/30/2014
Posts: 2732
Location: US
Patashu wrote:
https://forums.nesdev.com/viewtopic.php?f=2&t=15931
Fascinating, thanks for the link.
Jigwally
He/Him
Active player (418)
Joined: 3/11/2012
Posts: 119
Sorry for sleeping on this but I got around to writing a bootstrap for total control. It's extremely unoptimized so it doesn't happen until 9:32. I can shave that down by a lot but you have to jump through some very specific hoops to write the data without crashes. The bootstrap I write to the zero page is in the Koei VM bytecode and writes 2 bytes every 2 frames:
0029:AC A3 FC / Wait for interrupt (update PAD1/PAD2)
002C:AA 6E 00 / Push PAD1/PAD2 to stack (Destination)
002F:AC A3 FC / Wait for interrupt (update PAD1/PAD2)
0032:A4 6E 00 / Load PAD1/PAD2 (Value)
0035:B1       / Pop pointer from stack, store 2-byte value to it
0036:00 00 00
On the first iteration, write this value to close the loop:
0036:D6 29 00 / Jump to $0029
Next I need to use this to write a more optimized loop in 6502. https://www.youtube.com/watch?v=U1nRBZ7I-XI http://tasvideos.org/userfiles/info/60215123580741291
Jigwally
He/Him
Active player (418)
Joined: 3/11/2012
Posts: 119
Since I can't write my own pattern tables I started on a method of drawing images using the existing CHR-ROM. Link to video
Alyosha
He/Him
Editor, Expert player (3536)
Joined: 11/30/2014
Posts: 2732
Location: US
That's pretty cool, is this still using the VM?
Jigwally
He/Him
Active player (418)
Joined: 3/11/2012
Posts: 119
The loop that writes the tile/pattern data for the graphic is in 6502 but most of the rest is in the VM bytecode using the standard sysops for nametable printing + palette change. First I used the bootstrap to write the data below & a pointer to the image data destination to $50 and then overwrote the jump address to go to the bottom function. $6000 = destination of tile table $6340 = destination of pattern table
Function $667E -> Uses PAD1 to print the above data. $667E-667F are overwritten with the last 2 bytes, then $6680 is overwritten with 0x60 to exit.

My issue w/ continually polling PAD1 to print data was that when the interrupt happened in the middle of the input getting polled, the results got screwed up. My workaround involves having input on PAD2 the entire time this loop runs. This loop never polls PAD2 input, so if $6F ever becomes nonzero this is a "flag" that the interrupt happened and that the value in $6E can't be trusted, in which case the game takes the input again.

667E LDY #$00
6680 TYA
6681 JSR $F9B9   ; Poll PAD1 input (updates in $6E)
6684 LDA $6F     ; Check PAD2 input (should be zero)
6686 BEQ $668E
6688 STY $6F     ; If PAD2 was updated, reset to zero and retake PAD1 input
668A BNE $6680   ; (Functional JMP)
668C LDA $6E
668E STA ($50),Y
6690 INC $50     ; Increment pointer
6692 BNE $66A0
6694 INC $51
6696 BNE $6680   ; (Functional JMP)
Palette Set @ 6698
I don't think the BG color I choose here matters since I have to run another function to actually get it to change.

30 10 00 0F 30 10 0F 00 30 00 10 0F 30 00 0F 10
Function $66A8 -> Sets palette BG to white
I don't 100% understand this but w/ trial & error I got it to work this way.

66A8 LDA PPU_STATUS
66AB LDA #$3F
66AD STA PPU_ADDR
66B0 LDA #$00
66B2 STA PPU_ADDR
66B5 LDA #$30
66B7 STA PPU_DATA
66BA JMP $F3AC
(66BD-66C6) [obsoleted code I didn't remove yet]
Function $66C7 (Written in VM bytecode)

66C7 AC 0B FA    / Call $FA0B (Clear sprites)
66CA AC 7E 66    / Call $667E (Print image data)
66CD AC A8 66    / Call $66A8 (Set nametable BG to white)
66D0 8E 98 66    / Push #$6698
66D3 8D 10       / Push #$10
66D5 60          / Push #$00
66D6 E9 81 D6 06 / Call $D681 (Update nametable palettes)
66DA 8E 40 63    / Push #$6340 (pattern table)
66DD 8E 00 60    / Push #$6000 (tile table)
66E0 8D 1A       / Push #$1A (height of 27 tiles)
66E2 8D 20       / Push #$20 (width of 32 tiles)
66E4 62          / Push #$02 (y-pos 2)
66E5 60          / Push #$00 (x-pos 0)
66E6 68
66E7 E9 E6 EE 10 / Call $EEE6 -> Sysop 0x08 (Print graphic to nametable)
66EB D6 EB 88 / Jump $66EB (Loop)