【SMB源码解析系列】——001.JumpEngine函数

时间:2023-03-10 04:38:05
【SMB源码解析系列】——001.JumpEngine函数

在SMB的源码中大概有不到20处看起来很奇怪的指令,它的格式是通过jsr指令调用一个名为JumpEngine的函数,其后并不是跟随某些后续的逻辑指令,而是通过.dw定义了一系列16位地址。

【SMB源码解析系列】——001.JumpEngine函数

我们可以看到在jsr指令之前,还会将某个值写入A寄存器。

JumpEngine函数本身代码并不长:

【SMB源码解析系列】——001.JumpEngine函数

让我们来看看这段函数到底做了什么:

1.将A寄存器的值乘以2,然后写入Y寄存器;

2.从栈中取出2字节数据,分别写入$04、$05,然后将Y寄存器的值+1;

3.通过"后索引间接寻址"方式,将($04),y中的数据写入到$06中,又将($04),y+1处的数据写入$07中;

4.通过"间接寻址"方式,跳转至($0006)处执行代码。

原本我们如果在966行处调用了jsr指令(此时会先将指令所在地址$8234+2=$8236推入栈中),按正常流程应该在跳转至JumpEngine之后某处调用一次rts指令(把栈中的$8236取出并+1得到$8237),然后程序会跳转至$8237处继续执行。

但在第二步,我们将栈中的信息手动取出($8236),存入了$04、$05,并将Y寄存器+1,如果OperMode_Task的值是0的话,此时$04、$05中保存的值是$8236,Y寄存器的值为0*2+1=1,lda ($04),y就相当于是把$8236+1=$8237处的数据取出来写入$06,接着又将($04),y+1也就是$8238处的值取出来,写入$07。

这时我们就发现,968行通过.dw定义的16位地址InitializeGame已经被写入$06、$07中了。

最后通过唯一的一个间接寻址指令jmp ($0006)我们就跳转到了InitializeGame继续执行代码。

程序中的其他地方,可能会修改OperMode_Task的值为1,这样第二步Y寄存器的值最终为1*2+1=3,第三步就是把$8239,$8240处的值(ScreenRoutines)取出来了。目前看来这种方式最长可以通过.dw定义100多个函数地址~

SMB中就是依靠JumpEngine函数,让程序按照某个顺序逐一执行,序列化的完成了整个游戏逻辑的控制。不过我觉得这更像是在射击CPU硬件逻辑时就设计好的一个"语法糖",因为这个函数的思路和指令的实现逻辑结合的实在是太完美无缺了,当然这只是我的个人臆想罢了。