在68K汇编中,有多个可以改变PC寄存器的指令:
jmp
该指令在之前的章节已经介绍,该指令可以把目的操作数传递到PC寄存器,实现程序的流程控制。
bra
该指令的作用与jmp几乎相同,同样可以把目的操作数传递到PC寄存器,实现程序的流程控制。但是这两个指令有一定的区别,他们的区别是,bra比jmp占用的存储空间更少,运行更快。但是,bra虽然有这些优势,但bra的寻址范围有一定的限制。所以,bra不能像jmp一样,可以跳到程序中的任何位置。bra的寻址范围最大为$10000,因为bra可能向前或向后跳转,所以,在向前跳和向后跳分别可以跳转的最大距离为$8000。我们可以用编译器进行测试:
*向后跳转
ORG $10000 *程序存储地址
bra.w $18000 *从10000跳转到18000不会出现编译错误
ORG $20000 *程序存储地址
bra.w $28002 *从20000跳转到28002时会出现编译错误
*向前跳转
ORG $30000 *程序存储地址
bra.w $28002 *从30000跳转到28002不会出现编译错误
ORG $40000 *程序存储地址
bra.w $38000 *从40000跳转到38000时会出现编译错误
出现的错误指示为
ERROR:Branch instruction displacement is out of range or invalid
分支指令位移超出范围或无效
所以,虽然bra有速度快的优势,但有的时候,我们也需要按照条件来选择使用jmp还是bra
jsr & rts
jsr -跳到子程序指令,该指令可以把目的操作数传递到PC寄存器,并把下一句指令的地址传递到"堆栈"。
我们要理解该指令的作用,先要理解堆栈(stack),堆栈是一块可以用来临时存储重要信息的内存空间,你可以指定 m68k 的堆栈的地址在哪,也可以随时地修改堆栈的地址。堆栈的操作有入栈和出栈,入栈是指把数据传递到栈的最顶处,而出栈是读取栈顶中的数据。
在M68K中,地址寄存器A7被用作栈顶的指针,我们可以通过对A7进行设置来指定栈顶的地址:
movea.l #$10000,a7
*或
movea.l #$10000,SP
在栈里面,数据是反向存储的,我们可以把数据传递到栈的指令是
movea.l #$10000,a7
move.w #$12,d0
move.l #$ff001234,d1
move.w d0, -(sp) *入栈
move.l d1, -(sp) *入栈
move.l (sp)+,d3 *出栈
move.w (sp)+,d4 *出栈
这里我们需要注意,当入栈时,栈指针会先减少操作长度的值,再把数据传递到栈中。如:move.w d0,-(sp),执行时,sp寄存器的值会先减少.w长度,也就是2个字节,这时,sp的值为$10000 - $2 = $FFFE,然后才会把数据传递到sp所指向的地址。而出栈正好相反,出栈时会先读取栈顶的数据,然后才会把sp的值增加对应操作长度的值。在操作栈时,我们还需要尊守先入后出,后入先出的规则,就是说,我们要读取栈的数据时,必须先读取最后传递到栈的数据。
所以,jsr指令会把下一句指令的地址入栈,那么就需要一句指令来执行出栈操作
rts -从子程序返回,该指令会从栈里读取返回地址,并把读取到的地址传递到PC寄存器
我们用一段程序来测试jsr和rts的作用原理:
ORG $100
START:
move.w #$01,d0
jsr $180
move.w #$04,d3
move.w #$05,d4
ORG $180
move.b #$02,d1
move.b #$03,d2
rts
END START
bsr & rts
bsr与jsr指令的关系,和jmp和bra的关系是一样的,bsr与jsr指令的作用基本相同,不同之处和bra指令一样,bsr指令的寻址范围是有限制的。这个限制和bra是一样的。