Question about the game code
1 year ago
Calgary, AB, Canada
  1. How often is code executed in this game? Is it one line per frame, faster, or slower?
  2. While code is being executed, is it possible to interrupt the subroutine, and begin a new one, will the subroutine finish before the new one starts, or can the game execute multiple subroutines at once?
South Carolina, USA

Super Mario Bros.'s game code runs within the game's non-maskable interrupt (NMI) handler, which the NES calls every frame. Whenever all code in the NMI handler is executed on any given frame, code execution transfers to an infinite loop at address $8057 until the NMI handler is called on the next frame. So Super Mario Bros. essentially runs on a frame-by-frame basis, and tries to execute everything in the NMI handler on every frame unless code execution takes too many CPU cycles, in which case the game will lag on that frame.

The only proper CPU interrupt the game uses is the aforementioned non-maskable interrupt, which unconditionally transfers code execution there each frame. However, the game code frequently uses JMP (jump) and JSR (jump to subroutine) opcodes within the game code to transfer code execution from one subroutine to others. The JMP opcode transfers code execution to the specified address. The JSR opcode does the same, but stores the address minus one of the next operation into the stack so that an RTS (return from subroutine) opcode transfers execution to the stored address plus one. Code execution is strictly linear so multiple subroutines cannot run simultaneously.

Fleabetto, ultra_exists and 5 others like this
Calgary, AB, Canada

Okay, so, on that note, would it be theoretically possible to increment the value at $06D6 twice (from 00, to 01, to 02), by hitting the scroll unlock object twice at the end of 1-2? Essentially, my idea is that you would hit the object, and it would start the subroutine. Then, after it executes the part of the code which increments the value, but before it executes the part of the code which deletes the object, you hit it again, to interrupt the subroutine before it can finish. This would start the subroutine again, and allow a second incrementation before the object is deleted. From here, via clipping through the pipe, and going down the one furthest to the left, you would warp straight to 8-1. Based on how the game handles subroutines and interrupts, could this be done?

Edited by the author 1 year ago
Fleabetto likes this
South Carolina, USA

Unfortunately, that would be impossible. I took a deeper look into the exact handling of NMI in Super Mario Bros. and realized that I forgot to mention the fact that the start of the NMI handler disables interrupts unless the entire sequence of code in the handler is executed. So, in other words, code execution cannot return to the start of the game code unless the entire NMI handler has been ran first, even if that means that the game code must be processed over the course of two frames. This is why lag frames do not impact enemy patterns in speedruns; the console simply uses that lag frame to finish up code execution from the previous frame. So it's impossible for an interrupt to go off in such a way that the instruction to increment $06D6 can execute multiple times, as the game disables interrupts in the event of a lag frame.

Even if interrupts occurred during lag frames, the instruction to increment $06D6 immediately precedes the code to delete the object, meaning there would be a near-impossible timing window for the console to increment $06D6 but not delete the object due to an interrupt.

Fleabetto, Crescendo and 2 others like this
Calgary, AB, Canada

Darn! So close! Thank you for explaining that, I’ve been looking into the “Dream Warp” for like a year now, and I could never figure out why it can’t be incremented twice, it makes sense now. I guess that concludes my journey, but hey, I learned a ton about the game in the process.

Game stats
Followers
7,814
Runs
8,832
Players
1,792
Latest news
Requirements for High-Level Any% Runs

Any% (NTSC) runs below 4:57.000 must now fulfill additional requirements in order to be verified.

  • The run's full session must be included in the submission description.
  • For emulator runs below 4:57.000, some form of input display must be visible for the duration of the run. A hand-cam or input
4 months ago