[转译][马基 杰斯特(MarkeyJester) 摩托罗拉68000 入门教程] 陆 - 条件分支 | 1. CCR (状态字寄存器)
注意:本文经过原作者授权转译,转载请标明出处
原文地址:http://mrjester.hapisan.com/04_MC68/Sect06Part01/Index.html
条件允许建议阅读原文,网上非中文资料还是较多,当作锻炼英文岂不美哉
翻译若有不足之处欢迎批评指正
译文:
"千万别和地位远高于你或远低于你的人做朋友,那样的友情不会给你带来好运" ---- 考底利耶 (Chanakya),古印度政治家,哲学家,摩揭陀国孔雀王朝大臣
简介
大部分的处理器 (CPU) 都会有某种形式的状态字寄存器
(CCR
, Conditional Code Register) 来存放一些状态,这些状态可以帮助我们了解到:"这个字节
现在变成00
了吗?","这个字
现在是个负数吗?","这个字节
比另一个字节
大还是小?"
状态字寄存器
(CCR) 的目的就是能够让 CPU 在不同的状态下执行不同的动作
简单的例子
这里我们来康康一个非常简单的有两条新指令的例子以便于你能更好地了解:
cmp.b #$20, d0
beq.s ValueIs20
move.b d0, d1
ValueIs20:
这两条指令分别是CMP
和BEQ
:其中CMP
表示CoMPare
(比较),在这个例子里表示比较字节
20
与d0
中的字节
- 如果
d0
中的字节是45
,于是他们不相等,结果是一个非零结果
(不相等
) - 如果
d0
中的字节是20
,于是他们相等,结果是一个零结果
(相等
)
接下来一个指令BEQ
表示Branch on Equal
(在相等时分支),如果CMP
的结果是零结果
(相等),那么 m68k 就会分支到标记
ValueIs20:
处继续执行。如果CMP
的结果是非零结果
(不相等),那么 m68k 只是会继续执行下一条指令move.b d0,d1
,它不会去做分支动作
我会为这些新的指令在适当的时候做更多的解释。目前来说关键点在于,你要明白 m68k 是可以在某个字节
,字
或是长字
符合某些状态或是条件的时候,分支/跳转到其他的地方执行指令
标志 (Flag)
CCR
本身只有一个字节
大小,二进制状态下是这样的:
- - - X N Z V C
0 0 0 0 0 0 0 0
如你所见,m68k 有5
个状态标志 (flag)
- C - Carry (进位)
- V - oVerflow (溢出)
- Z - Zero (零结果)
- N - Negative (负结果)
- X - eXtended (扩展,在一些其他的 cpu 中,该位的功能包含在
C
标志中)
m68k 的大多数指令可能会根据执行结果来设置(1),擦除(0),翻转,或是根本不理会这些状态标志
进位 (Carry)
举个例子,我们把一个字节
加到一个寄存器里:
addi.b #$20, d0
现在我们假定d0
里的内容在执行前是000000FE
,那么你应当知道FE + 04 = 102
,你也应该知道其中的02
会被保存到d0
里,此时d0
的内容变成了00000002
,而102
里面那个1
的二进制
的最后一位 0001 会被保存到进位
状态标志里,因为它超出了一个字节
的范围,所以它是进位 (Carried over) 了,于是我们把进位
状态表示设置为1
如果d0
的内容在执行前是00000090
的话,90 + 04 = 94
,d0
的内容会变成00000094
,而此时进位是0
,于是进位
状态表示会变成0
同样对于某些指令比如LSR
(逻辑右移) 来说,任何被移出寄存器或是内存空间范围的位
,都会被移到进位
状态标志里面
溢出 (Overflow)
假设d0
的内容是0000007D
:
addi.b #$04, d0
在指令执行后,d0
的内容会是00000081
,此时,如果你把这个字节
当作有符号数
,7D
是个正数,而81
却是个负数
,这种从正数
变成负数
的情况就会把溢出
状态标志设置为1
详细来说,7D
+ 04
= 0081
,正数 + 正数 = 正数。但是只有0081
的一个字节会被保存,也就是说结果是81
,是个负数,这在数学上看来是不合逻辑的,因为数学上不会出现一个正数 + 正数 = 负数,所以溢出
状态标志会被设置为1
同样的,如果出现了负数 + 负数 = 正数的情况,也会把溢出
状态标志设置为1
零结果 (Zero)
这个是最简单的了,假设d0
的内容是0024000C
:
subi.b #$0C, d0
这条指令执行后d0
的内容会变成 00240000,因为0C
- 0C
= 00
,得到的字节
的结果是0
,所以零结果
状态标志会被设置为1
,而当然如果得到的字节
的结果不是0
,那么零结果
状态标志会被设置为0
负结果 (Negative)
这一个也很简单,假设d0
的内容是00049044
:
addi.w #$0100, d0
9044
+ 0100
= 9144
d0
中的字
是个负数,所以负结果
状态标志会被设置为1
,而假如指令结果是正数,那么负结果
状态标志会被设置为0
扩展 (Extended)
这个和进位
(C) 状态标志一样的,它会在进位发生的时候也被设置或是擦除
然而,并不是所有的修改进位
状态标志的指令都会修改扩展
状态标志。这是基于这样一种思路:当你使用一条同时修改进位
和扩展
状态标志的指令之后,再去使用一条只会修改进位
状态标志的指令,于是扩展
(X) 状态标志就会保留之前的那次进位的状态
综述
再康康这一串指令:
addi.b #$20, d0
beq.s ValueIsNull
move.b d0, d1
ValueIsNull:
现在我们假定d0
在所有指令执行之前是0048F0F0
,ADD
指令会把字节``20
加到d0
里,于是 20 + F0 = 110,当然只有10
会被保存到d0
里,于是d0
的内容是 0048F010
现在我们来考虑一下CCR
,首先是C
和X
(进位
和扩展
) 状态标志,他们都会被设置为1
,因为110
已经能超出了一个字节
,所以超出的那个1
的二进制的最后一位 0001会被放到C
和X
里
接下来,考虑下Z
(零结果
) 状态标志,由于指令结果的字节
是10
,不是0
,所以Z
里应该是0
然后是V
(溢出
) 状态标志,因为计算等式是 正数 + 负数 = 正数 (20
+ F0
= 10
),这很OK,所以V
里是0
最后康康N
(负结果
) 状态标志,由于结果是10
,是个正数,所以N
的内容也是0
(非负)
此时CCR
的内容是:
- - - X N Z V C
0 0 0 1 0 0 0 1
接下来一条指令beq.s ValueIsNull
(相等时则分支) 会去检查Z
(零结果
) 状态标志:"Z
被设置为1
了吗?",没有,于是表示上次的结果是非零的 (不相等),所以,m68k 此时不会跳转到分支,而是会继续执行下面的move.b d0,d1
指令
说明手册
因为所有的指令去修改不同的状态标志的原因各不相同,你不能仅仅凭着上面综述的例子来臆测所有的可能性,我强烈建议大家去找一本 m68k 的说明手册,它会比较详细的告诉你哪些指令会修改哪些状态标志
目录
上一篇:[转译][马基 杰斯特(MarkeyJester) 摩托罗拉68000 入门教程] 伍 - 程序流程控制 | 6. BSR (分支到子过程) 指令
下一篇:[转译][马基 杰斯特(MarkeyJester) 摩托罗拉68000 入门教程] 陆 - 条件分支 | 2. CMP, TST & BTST (测试) 指令