User Files from OnehundredthCoin

Upload All User Files

#637971270692437190 - The Legend of Zelda "Subframe Inputs" in 44 seconds

In 00:00.00 (0 frames), -1 rerecords
Game: The Legend of Zelda (NES)
4 comments, 0 views, 23 downloads
Uploaded 8/26/2022 4:11 PM by OnehundredthCoin (6 files)
VBlank count is definitely not accurate. Apologies for uploading this 3 times, the website was giving me an error since the file was missing VBlankCount. I assumed it didn't go through.
This is far from optimal.
"What's with the 18 seconds of nothing on the file name screen?"
So, the way this TAS works is by sending in non-matching inputs over and over again with the SubNesHawk core (bizhawk version 2.8) to create precise amounts of lag. And it needs to be incredibly precise. At the end of any frame, there are the following instructions.
STA $2000
"STA $2000" Enables the Non Maskable Interrupt, so another frame can begin after that point. "STA $FF" stores a copy of the data in $2000 at $FF. "RTI" pulls 3 bytes off the stack, and returns to an infinite loop at address $E45B. "JMP $E45B" is the infinite loop. It just keeps jumping to itself. I need to create lag so precise, that it starts a new frame after "STA $2000" but before "RTI". I think this is around a 5 CPU cycle window.
Keep in mind, whenever I stall for additional inputs, it's not adding by a single CPU cycle, it adds 218 cycles. To time it just right, I need to stall for more than just one frame. If I time it just right, a new frame begins in that 5 cycle window, adding 3 more bytes to the stack. This sets it up so if I hit the "RTI" instruction on a later frame, it will immediately follow it up with a second RTI instruction. I can repeat this process as long as I want. What does this achieve?
Well, if I do it enough times I can make the stack overflow. The "Register Name" selection on the file name screen has an interesting property. Your inputs are pushed to the stack, and aren't overwritten before the end of the frame. On the frame you initially select this option, a value of $34 is pushed to the stack one byte higher than where your inputs will get stored. By overflowing the stack, writing these desired bytes, and letting the giant tower of RTI instructions all execute, you could return to address $xy34, where "xy" is whatever buttons you hold down on controller 1. The names of the save files are stored at $0638, so I could use this to begin executing from $0634 by holding down "06" which is a combination of Down and Left.
Slight issue, $0634 is set to a value of 00 if the middle file doesn't exist yet. So I need to set up that save file, return to the menu, and re-enter the file name screen. With a value of 00 it executes a BRK instruction, which jumps to the programmable interrupt. Zelda doesn't use such an interrupt, so it jumps to some code that leads to another BRK. an infinite loop. If $0634 has a value of "01", however, it's an ORA instruction, and I don't have to worry about it.
"What are you writing with the file names?"
The code I can write is extremely limited. the bytes I can write are 00 - 24, 28 - 2C, 63, and 64. Not the most useful bytes, but let's see what we can do with them. We have the ASL instructions, which let's us multiply a byte by 2. ("Arithmetic Shift Left" also tosses bit 7 into the carry flag. Also any number greater than 255 will get truncated to a single byte.) This can be used to change some of the bytes in the payload to more useful ones.
I write: ASL $06
ASL $06
ASL $06 (Address $06 starts at a value of 0x12. shifting it thrice gives it a value of 0x90, which is "BCC" or Branch on Carry Clear.)
ASL $07 (Address $07 starts at a value of 0xFF. shifting it gives it a value of 0xFE. combined with the branch instruction, if executed this causes an infinite loop, since 0xFE is -2. it branches back 2 bytes to the start of the branch instruction.)
ASL $0622,X (X = 0x25, this shifts address $0647)
ASL $0622,X (this also shifts address $0647)
ASL $60,X (The number 60 isn't a byte that I can write. It starts as 0x18, but the two previous instructions shift it to a value of 0x60. the offset of X makes it shift address $85, which is the vertical position of the cursor for selecting characters to name the file.)
CLC (Clear the Carry flag just in case)
JSR $0006 (Jump to that infinite loop branch I made)
In short, this creates an infinite loop, moves the cursor for selecting letters to name a file with, and then jumps to that loop.
With the cursor offset, I can now grab characters from outside the bounds of the table.
"Hold up. Why are you jumping to an infinite loop?"
Remember how the game jumps to an infinite loop after regularly executing "RTI"? I'm unable to jump back there with the bytes I have to work with, so I need to create my own loop. This is the game's way of "Spinning" while we wait for the next frame.
"What about the changes you make to the save file with the new characters?"
This time, I have more bytes to work with. However, I am unable to change the name of file 2, since that file already exists. I'll need to work around it. I write:
LDA #$13 (A = 0x13)
STA $12 (Store A in $12. this wins the game)
INX (X is now 0x26)
ASL $0622,X (X = 0x26, this shifts address $0648)
ASL $0622,X (this also shifts address $0648)
ASL $60,X (This is leftover from the previous payload. it has no use in the second payload)
JMP $0648 (I can't write "JMP" so I needed to shift something else into this value. it started as 0x13. With "JMP" set up, it creates an infinite loop.)
In short, this wins the game and then loops infinitely.
"Okay... why did you run payload 1 on the name register screen, but payload 2 in game?"
Unfortunately, the code for the credits isn't loaded on the name register screen, so I had to start the game. Also, due to underflowing the stack, the game will crash after registering the new names. It does however save them, so I can simply reset the game and start from there. After loading in, I press the start button to pause and begin subframe mashing again. Once the stack is about to overflow, I press UP+A on controller 2 to bring up a menu that has the same properties as the name register menu. That's right, it pushes my inputs to the stack and never overwrites it! Once again I can jump to $0634 and execute the new file names.
This wins the game from the pause screen.
"You mentioned this is far from optimal. What can be improved?"
One glaring issue with this TAS is the several seconds of subframe mashing. First off, I can begin mashing earlier. If I subframe mash while writing the file names, or while the level is loading for payload 2, I can execute the code earlier. Second, I'm still figuring out how to optimize the subframe mash.
Right now, I have a LUA script simply alternate inputs over and over until it lands a new frame inside the 5 cycle window. This is inconsistent. Some times it takes 4 frames, other times around 20. Sometimes I have chains of 4 frames multiple times in a row, other times 20 frames multiple times in a row. It's likely due to executing the "STA $FF" (the last instruction before the next frame) on a different cycle than other times it works. Either that, or due to how a frame can take 29780 or 29781 CPU cycles before the next one begins. I've got some research to do. I imagine I might be able to save time by stalling longer than 20 frames if it can end up in another large chain of 4 frame successes. I'd also like to cut out a payload entirely, but I've been unable to win the game without offsetting the cursor. One idea I had was using link's position to write a branch-loop, but I'm unable to jump to his position with the limited 24 bytes I have to work with. I'll keep looking for ideas though.
Anyway, the combination of what was written in the above paragraph and the obvious lack of entertainment during the periods of subframe mashing is why this is currently a user file and not being submitted for publishing yet.

#637956051181818139 - The Legend of Zelda Subframe Crash on title screen

In 00:00.00 (0 frames), 592 rerecords
Game: The Legend of Zelda (NES)
2 comments, 0 views, 22 downloads
Uploaded 8/9/2022 1:25 AM by OnehundredthCoin (6 files)
If this works on console, Bizhawk is terrifyingly accurate.
Done in Bizhawk 2.8, SubNesHawk core.
So, TLoZ has the entire game loop inside an interrupt, with the exception of spinning. Of course, the Non Maskable Interrupt is disabled for this entire duration until the moment before spinning. However, there is a very brief 5 CPU cycle window where the NMI can happen before the RTI, so if timed correctly you can nest an interrupt inside the interrupt. This requires subframe button mashing for a ridiculous amount of time even just to line it up for a single nested interrupt, but should you nest too many, the stack overflows! We can then resolve 84 RTIs in a row, eventually pulling off some "garbage" as a return address. This leads to a BRK, which leads to a BRK, which leads to a BRK... and the infinite loop might as well be a game crash, since the NMI isn't going to run anymore.
You could probably make this happen somewhere other than the title screen to begin executing code somewhere else, but it needs more experimenting.
This was achieved through a crummy LUA script that just kept adding a new input, stalling for 7 frames, and seeing if another NMI happened before the RTI. It could probably be improved, since I was specifically checking for an exact address being pushed to the stack, when there are actually 2 different addresses that could be pushed between enabling the NMI and executing the RTI.
Hilariously, since the entire code is inside the NMI, when I need to perfectly time the NMI to happen before the RTI, the entire frame happens, so the game continues to play slowly whenever I can nest another NMI.

#637864080729078524 - Gimmick! "Subframe Inputs" in 00:00.25

In 00:00.25 (15 frames), 360 rerecords
Game: Gimmick! (NES)
5 comments, 171 views, 26 downloads
Uploaded 4/24/2022 2:41 PM by OnehundredthCoin (6 files)
Use the Bizhawk SubNesHawk Core to watch. (Or the youtube link)
This has not been console verified yet, so if anybody wants to give it a go, it would be very appreciated. But you should know this run involves executing uninitialized RAM as code, (using bizhawk's 00 00 00 00 FF FF FF FF pattern) so keep that in mind. I'm hesitant to submit a Subframe Input TAS without console verification.
In this TAS, I complete the Game "Gimmick" by utilizing subframe inputs, not unlike my TAS of Super Mario Bros. 3. More info on the technical details if verified, but I'll explain a general idea of how the "route" works.
At the moment I execute RAM as code, the game has uninitialized RAM from address $F1 through $F4. This is a bit problematic for verification, but it's also the key idea of how this run works. Controller data is stored at $F5 through $F8, in pretty much the same manner as SMB3.
Circling back to the uninitialized RAM, bizhawk's "00 00 00 00 FF FF FF FF" pattern for RAM on console boot leaves address $F2 with "00" and $F4 with "FF". Both of those bytes get executed, and "FF" is a 3 byte instruction. That limits the number of "controller bytes" from 4 to 2. (Those are also the only uninitialized bytes that get executed. Gimmick! Clears the first 240 bytes of the zero page, leaving $F0 - $FF uninitialized, but by the time I execute code, the only uninitialized bytes are $F1 through $F4)
On the first frame with inputs, I 'stall' for 58 inputs, then using both controllers, I write STY $00F4. This replaces the "FF" at address $F4 with "08", as that's what was held in the Y register. This creates a PHP instruction, but what's more useful is that it's only 1 byte long. Now I have the full 4 "controller bytes"
I reset the console, and recall that address $F4 is uninitialized. That means when resetting, Gimmick doesn't clear the 08. I might go back and try to improve this TAS, but the subframe mashing to execute code seems to only work on the first frame with inputs. I need to strategically write JSR $E0F0, or 20 F0 E0 written as bytes. I need to hold down E0 (A + B + Select) on controller 1, while the new inputs are 20 (Select) so I use the first possible frame after resetting to hold down A + B. Then I reset again.
On the third and final loop, I repeat the same subframe mashing as before, and hold down the buttons that write JSR $E0F0, which wins the game when executed.
If I could find a way to offset the code so I can execute 3 controller bytes while also lining up the 20 F0 E0 for loop 2, I could save 5 frames. Likewise, if I can execute RAM after the first frame with inputs, it's likely I could cut out the second reset, saving time.

#637810967022224841 - Kirby's Adventure DPCM (Failed Console Verification, Works in Emulator)

In 00:04.00 (240 frames), 294 rerecords
Game: Kirby's Adventure (NES)
5 comments, 163 views, 33 downloads
Uploaded 2/22/2022 3:18 AM by OnehundredthCoin (6 files)
This is an unbelievably sloppy TAS that I made back in December to test if the DPCM audio bug exploit could be done in Kirby's Adventure. Unfortunately, after many tests for Console Verification, there has been no luck. I imagine the culprit is the DPCM audio bug, as unlike Super Mario Bros. 3, the title screen has DPCM audio.
I honestly don't know all the details of the DPCM audio bug or how emulating it works, but my hypothesis is that the bug is inconsistent on console, and so emulating here isn't "inaccurate", but the results may vary.
Anyway, the trick that made this TAS work happened by mistake, and reproducing it on purpose has been exceedingly difficult. I manipulated the stack in such a perfect manner to pop a value of the stack, store it in X, and use X as an offset that ends up storing a NOP at the JMP instruction that rests inside the programmable interrupt. From there, the programmable interrupt happens, and I use the bytes that appear after where the JMP was to write code, as that's where the controller data is stored.
Again, this TAS is crude and sloppy, but until I can get it working on console (or figure out why it doesn't work), I have no desire to attempt improving it.
Rerecord count is inaccurate. VBlankCount is also probably inaccurate. I don't know why, but the .bk2 file didn't include VBlankCount.

#75436230045449211 - Super Mario Bros. 3 "Game End Glitch" in 00:00:00.316

In 00:00.31 (19 frames), 0 rerecords
Game: Super Mario Bros. 3 (NES)
4 comments, 2494 views, 144 downloads
Uploaded 11/10/2021 6:36 AM by OnehundredthCoin (6 files)
Run created in Bizhawk 2.6.3 (with some odd export issue, so I just pasted the inputs in version 2.3.2 and exported it there?) Before I submit this run, I'm going to be taking the time to write up a huge explanation, and probably make a video explaining how this run works.
The credits work perfectly fine without any visual bugs.
Uploaded as a user file so it can be console verified before submission.
Huge thanks to Bigbass for verifying the run.
The final run had 3183 rerecords, I'll fix the metadata before the submission.

#75291642453080158 - Super Mario Bros. 3 "Game End Glitch" in 0:00:00.399

In 00:00.25 (15 frames), 13882 rerecords
Game: Super Mario Bros. 3 (NES)
10 comments, 871 views, 75 downloads
Uploaded 11/3/2021 6:19 PM by OnehundredthCoin (6 files)
I'm going to make a much better writeup of this, but for now I'm just uploading the .bk2
This should beat the current TAS by 23 frames.
Keep in mind this run was made in version 2.3.2 with the SubNesHawk core. I believe this version has a bug with IRQ timing? I don't know all the details, but it might not work on later versions of the emulator. This should not affect the run on console, so I've been told.