51 单片机汇编语言:利用 RET 指令实现多路分支
扫描二维码
随时随地手机看文章
有这样一个问题:
已知程序执行前有 A = 02H,SP = 52H,(51H) = FFH,(52H) = FFH。
执行下列程序:
POP DPH
POP DPL
MOV DPTR, #4000H
RL A
MOV B, A
MOVC A, @A + DPTR
PUSH A
MOV A, B
INC A
MOVC A, @A + DPTR
PUSH A
RET
ORG 4000H
DB 10H, 80H, 30H, 50H, 30H, 50H
求程序执行后:
A=( )H、SP=( )H、(51H) =( )H、(52H)=( )H、PC=( )H。
------------------------------
这个问题,在百度知道出现过很多次,搜一下,就会出来几十个链接。
但是回答正确的,确实不多。
有的提问者,好像是说,这是什么学校教材上的习题。
在这个程序中,写错了两条指令:
非法指令:PUSH A
应该写成:PUSH ACC
做为教材来说,这可是一个 BUG 啊,呵呵
这个程序,是利用 RET 指令来进行多路分支。
程序中,有两条查表指令:MOVC A, @A + DPTR,
当 A = 0 时,将从 4000H 处,先后读出 10H、80H;
当 A = 1 时,将从 4000H 处,先后读出 30H、50H;
当 A = 2 时,将从 4000H 处,先后读出 30H、50H。
本题目的条件已经给出:A = 02H,那么就是读出了 30H、50H。
读出数据后,先后压栈,再执行 RET。
RET 指令,就是把堆栈中的两个字节,送到 PC。
那么,PC = 5030H,就是这么来的。
全部的空格,填写如下:
A=(50)H、SP=(50)H、(51H) =(30)H、(52H)=(50)H、PC=(5030)H。
------------------------------
本程序,是个子程序结构,应该用 CALL 指令来调用。
在本程序开始处,用 POP 指令,抛弃了 CALL 自动保存的返回地址。
腾出了两个字节的堆栈空间,又压入两字节数据,然后,再返回。
RET 返回指令,就会用刚刚压入两字节数据,当做返回地址。
本程序,是属于偷梁换柱,还是移花接木 ?
------------------------------
下面逐条解释一下:
POP DPH
POP DPL ;弹出两次,SP = SP - 2 = 50H
MOV DPTR, #4000H
RL A ;乘以2
MOV B, A ;B = 04H
MOVC A, @A + DPTR ;取出第4个字节30H
PUSH ACC ;SP = SP + 1 = 51H, (51H)=30H
MOV A, B
INC A ;A = 05H
MOVC A, @A + DPTR ;取出第5个字节50H
PUSH ACC ;SP = SP + 1 = 52H, (52H)=50H
RET ;子程序返回指令
执行 RET 指令时,是从堆栈中弹出两个字节到 PC 的高、低八位。
即:
(SP) → PCH,然后 SP - 1 → SP;
(SP) → PCL,然后 SP - 1 → SP。
即:
(52H) = 50H → PCH,SP = 51H
(51H) = 30H → PCL,SP = 50H
那么,PC = 5030H,就是这么来的。
------------------------------
进行多路分支,还有简单一点的方法。
如,利用指令:JMP @A + DPTR,就可以。
那么,前面的程序,可以改为:
MOV DPTR, #4000H
RL A
MOV B, A
MOVC A, @A + DPTR
XCH A, B
INC A
MOVC A, @A + DPTR ;先后读出两字节
MOV DPH, A ;把读出数据送到 DPTR
MOV DPL, B
CLR A ;A=0
JMP @A + DPTR ;以 DPTR 内容为目的地,转移
ORG 4000H
DB 10H, 80H, 30H, 50H, 30H, 50H
程序这样写,就不涉及堆栈了,也就没有前一个程序那么复杂的推导。
理解起来,可以轻松些。
这个程序,没有使用 RET 指令,那么,就不要用 CALL 来调用了。
要用 JMP 指令来应用本程序。