The Super Mario Bros jump engine.

The following code was taken from the smb comented disassembly on GitHub. It shows how indirect jumps are used in Super Mario Bros.

#
GameOverMode:
lda OperMode_Task
jsr JumpEngine
.dw SetupGameOver
.dw ScreenRoutines
.dw RunGameOver
...
AreaParserTasks:
jsr JumpEngine
.dw IncrementColumnPos
.dw RenderAreaGraphics
.dw RenderAreaGraphics
.dw AreaParserCore
.dw IncrementColumnPos
.dw RenderAreaGraphics
.dw RenderAreaGraphics
.dw AreaParserCore
...
GameRoutines:
lda GameEngineSubroutine  ;run routine based on number (a few of these routines are
jsr JumpEngine            ;merely placeholders as conditions for other routines)
.dw Entrance_GameTimerSetup
.dw Vine_AutoClimb
.dw SideExitPipeEntry
.dw VerticalPipeEntry
.dw FlagpoleSlide
.dw PlayerEndLevel
.dw PlayerLoseLife
.dw PlayerEntrance
.dw PlayerCtrlRoutine
.dw PlayerChangeSize
.dw PlayerInjuryBlink
.dw PlayerDeath
.dw PlayerFireFlower

And so on, with more indirect addresses…
And the JumpEngine routine:

#
;------------------------------------------------------------------------
;$04 - address low to jump address
;$05 - address high to jump address
;$06 - jump address low
;$07 - jump address high
JumpEngine:
asl          ;shift bit from contents of A
tay
pla          ;pull saved return address from stack
sta $04      ;save to indirect
pla
sta $05
iny
lda ($04),y  ;load pointer from indirect
sta $06      ;note that if an RTS is performed in next routine
iny          ;it will return to the execution before the sub
lda ($04),y  ;that called this routine
sta $07
jmp ($06)    ;jump to the address we loaded

Thus a parameter is set in A and then we call the jump engine. It will go to the caller address plus an index from A, hence calling a routine dynamicly depending on the state of the game.
I do not know how to emulate this. What upernes would do here is disassemble the routine call and then get lost with the routine addresses data below. And the generated code would take his base address on the stack from the recompiled code address at an offset very different from the original nes. And it will add to it the data from the original PRG rom at the wrong address. And it would not work.

Solutions:

  • The jump engine routine could be specified in the indirect jump files. And every data following the call be disassembled. And this specific structure kept in the source at the same @ in order to be able to jump like this.
  • At least the routines could be at their original addresses.
  • Another solution would be to keep the original code running without changing anything but the read and writes to IO ports. They would be replaced with BRK and two bytes indicating the port, read or write direction, and A, X or Y register. The BRK vector would point to an area out of the original rom where the io routines would be called in native mode from another bank. And therefore the original routines would work with this indirect jump mecanism.

The solution is not obvious. For now it means that the indirect jumps do not work because when it reads an address on the snes using the stack it will not be the corresponding nes address. It would be the recompiled code address.

I will discuss that on the nesdev forum to see if anyone has an idea.