汇编语言 第11章 标志寄存器
8086寄存器总结
ax、bx、cx、dx通用寄存器
除了通用用途,且可分割为byte外(即al ah之类),还有额外用途:
- ax(Accumulator, 累加器)可用于乘法、除法,保存被除数或者某个乘数的值,以及结果,以及程序返回,用累加器进行操作可能需要更少时间。
- bx可以作为指定内存地址,例如 [bx] ,默认代表 ds:[bx]的内存地址, 也可用cs:[bx]、ss:[bx]等。
- cx可用与loop循环次数。
- dx可用于当ax的乘除法位数不够用时辅助, 例如32位除法dx表示被除数高位,或者32位结果的高位。
bx和si、di、bp寻址寄存器
bp与bx不同点在于bp默认是用于栈,ss:[bp], 而bx默认是普通内存地址ds:[bx] 此外bp不是通用寄存器,因此不能分割为byte计算
si、di则是辅助bx和bp做高维数据寻址: [bx+si] [bp+si] [bx+di] [bp+di] [bx+si+di] [bp+si+di]
ss、sp栈寄存器
ss(Stack Segment)是栈的段地址 sp(Stack Pointer)是栈的偏移地址 ss:sp指向栈顶。
注意sp赋值应该是 栈顶元素+2。
关于栈内数据和SP的操作顺序,可以把栈想象成一个弹夹, SP表示弹夹里的弹簧,数据表示子弹, 在push装子弹的过程中,首要要把弹簧压下去,所以先执行 sp-2 当pop弹出子弹的过程中,首先子弹出来,弹簧才会弹上去,所以后执行 sp+2
还有栈可用于call、ret中存储子函数的参数和子函数的指令地址, 注意参数的压栈顺序,而子函数的指令地址最后压栈,故在栈顶端。
8086不确定,现代的子函数存储在堆栈帧中, 所谓堆栈帧就是每个子函数独立一个栈,避免意外造成相互影响。 此外,猜测用栈来存储子函数猜测原因是调用\返回的访问顺序正好与栈的后进先出顺序一致。
cs、IP指令寄存器
cs(Code Segment)是指令的段地址 IP(Instruction Pointer)是指令的偏移地址 cs:IP指向内存之中正在执行的指令。
指令数据都是二进制的机器码,所以cs:IP指向的内存空间被指明为指令,否则都是二进制谁也不知道是指令还是数据。
注意当指令进入指令缓冲器还没有执行时,IP先增加到执行下一个指令,然后再执行。 这在确定jmp偏移量时非常重要。
ds内存数据地址寄存器
ds(Data Segment)是内存数据地址的段地址 ds:[idata]指向地址空间
[idata]可以是常数 [2] 或者寄存器[bx] 或者 组合[bx+di+2]等等。
通常默认情况下,ds:[0]都指向代码空间的最开头, 然后默认100H空间的psp段用于与系统shell交互 接着是cs:[IP]
es寄存器
用途不明,或许是Extra Segment的简称,额外的。 寄存器不够用的时候辅助一下, 目前已知特殊用途是用在串传递中(见下文)从ds:si到es:di
标志寄存器
以上用13个寄存器,8086有14个寄存器,最后一个寄存器是标志寄存器, 用16位,其中有9位用于状态标识,结构如下:
[15] [14] [13] [12] [OF] [DF] [IF] [TF] [SF] [ZF] [5] [AF] [3] [PF] [1] [CF]
ZF 零标志位
Zero Flag
如果某指令执行后其结果为0,则zf变为1,不为0,则zf变为0 例如:
mov ax, 1
sub ax, ax
结构为0,所以zf执行到此处时为 1
add、sub、inc、mul、div、or、and等运算指令会对zf有影响, push、mov、pop等传送指令对zf没有影响。
PF 奇偶标志位
Parity Flag,单词parity有奇偶性的意思,例如奇偶校验(Parity Check)
如果结果所有bit中1的个数是偶数则为1,奇数为0。
SF 正负号标志位
Sign Flag sign英文即正负号,
结果为负,则sf = 1 结果非负,则sf = 0
这个标志位与signed数的最高位用处是一样的
此外,计算机计算无符号数和有符号补码方式是一样的,结果的bits一样,神奇的地方。 所以可以无视到底算无符号还是有符号,只是输出结果时转化一下即可。
CF 进位标志位
Carry Flag,Carry英文:进位
在进行无符号数运算时,它记录运算结果的最高有效位进位值(是否发生进位,类似正溢出),或者从更高有效位借位值(是否发生借位,类似负溢出)。
正溢出的例子:1000 0000B + 1000 0000B = 80H+80H - 100H = 00H, cf = 1
负溢出的例子:97H - 98H,发生向更高位借位,实际197H - 98H = FFH结果, 此时cf = 1
OF 溢出标志位
Overflow Flag
在进行有符号数计算时,结果超过机器表示范围称为溢出。
如果结果溢出,of = 1
CF和OF的区别
CF和OF并非同时变化 例如:
补码正溢出:
64+64 = 0100 0000B + 0100 0000B = 1000 0000B,
对于补码而言已经正溢出,结果为 -128
如果是无符号数,则没有发生借位。
因此补码正溢出不一定会发生对于无符号数的借位,
因为正溢出考虑的是次高位,而无符号借位考虑的是最高位。
&
1111 1111B + 1111 1111B = 1111 1110 = -2
如果是无符号数发生了借位,但是对于补码,没有溢出
总体而言: 对于无符号数,观察借位标志位CF,对于补码,观察溢出标志位OF。CF和OF没任何关系。
利用了标志位的指令
adc指令
adc(add-carry)是带进位加法指令,利用CF记录进位值。
adc ax, bx = (ax) + (bx) + CF
adc的用途
扩展了可计算数的范围,任意整数加法都可计算 例如。add ax,bx 相当于:
add al,bl
adc ah,bh
可以看出从8位数计算扩展到16位数,类似扩展到32位、64位、128位。。。 只是计算复杂度增加了。
sbb指令
sbb即带借位的减法指令,利用CF记录借位值。
sbb ax, bx = (ax) - (bx) - CF
作用于adc类似,扩展了减法范围,实现任意大数减法计算。
cmp指令
cmp是比较指令,cmp相当于减法指令,只是不保存结果。 cmp执行后,将对标志位产生影响。接着,其他指令通过标志位判断比较结果。 cmp通过标志位,可以实现多种大小比较,
对于无符号数,主要通过CF标志位和ZF标志位判断,如下:
以上结果是针对无符号数而言, 对于有符号数,判断等于、不等于根据zf判断,判断大小则考察sf和of
以上结果是根据sf和of的取值,判断大小关系。 如果是 <= 稍微麻烦一点,要么zf =0 或者 sf和of异号。
条件转移指令
对于8086,所有条件转移指令位移在[-128,127]之间。
大多数条件转移指令都检测标志寄存器相关标志位,以此决定是否修改IP。 通常都和cmp指令相配合。就像call和ret相配合一样。
然而,由于cmp既可以比较无符号,也可以比较补码, 因此对于的条件跳转检测zf、cf或者zs、sf、of有所不同。 因此应该是不同的条件跳转指令。
因本章节只介绍了无符号的条件跳转,有符号略。 在没有完整的内容的情况下就不做相关笔记了。
DF标志和串传送指令
DF(Direction Flag)方向标志位,在串处理指令中,控制每次操作后si、di的增减。
df = 0,每次操作后si、di递增; df = 1,每次操作后si、di递减;
串传送指令
格式:movsb(move string byte)
顺带说一下 string 串 和stream 流的区别,故名思意,一串是静态的, 所以静态的文本,字符串就是string。 stream 流 是动态的,所以比如输入输出之类,数据不会长期存在,后续数据会覆盖。 所以stream比较适合针对I/O
功能:相当于以下步骤:
1. ( (es)*16 + (di) ) = ( (ds)*16 + (si) )
2. if df = 0 then (si) = (si)++ ;(di) = (di)++
else (si) = (si)-- ;(di) = (di)--
可以看出,movsb功能是将ds:si指向的内存空间字节送到es:di中,然后根据df标志位的值,将si、di递增或递减1,
movsb是复制byte,而movsw复制word,所以对于es、di 加2 或者 减2
一般movsb/movsw和rep指令配合使用
格式: rep movsb
功能是:
s:movsb
loop s
可见,rep根据cx的值,重复执行串传送指令。
那么如何设置df来控制串复制的方向呢?
cld
指令(clear df),将df设置为0
std
指令(set df),将df设置为1,
例如栈数据复制就适合负方向。或者复制一个数据的末尾片段。
pushf 和 popf
pushf将标志寄存器的值压栈,popf将数据从栈中弹出,送入标志寄存器。
注意8086中可以pushf
然后pop ax
,也可以push ax
然后popf
,
即并没有为标志位寄存器单独开栈空间。
回顾
目前而言,13个寄存器全部涉及, 标志位寄存器中的 OF (Overflow Flag) DF (Direction Flag) SF (Sign Flag) ZF (Zero Flag) PF (Parity Flag) CF (Carry Flag) 已涉及
IF、TF、AF未涉及。(TF、IF用于中断) TF(Trap Flag)用于debug中的单步执行中断指令 IF(Interrupt-enable Flag)用于可屏蔽外中断的屏蔽指令,详见第15章
进阶
此文为第一次学习寄存器的总结,包括AX、BX、CX、DX的用途,BP的用途AF的用途等还没有彻底认识。
而如下level2 文中详解了相关部分,当然,level 2中有些地方不够详尽,例如借位、溢出的区别,比如IF屏蔽的本质,好在能从本章和第15章中得到解答,所以最好两篇文章综合来看。
level 2: [转载]寄存器总结 level 2