【笔记】8086汇编-标志寄存器

前言:本篇仅仅是笔记,整理于2017/9/4,结构可能会略有混乱,见谅。

-引入:

–这是一类特殊的寄存器,具有以下3中作用:

—1)用来存储相关指令的某些执行结果

—2)用来为CPU执行相关指令提供行为依据

—3)用来控制CPU的相关工作方式

-在8086CPU中,标志寄存器有16位,其中存储的信息通常被称为程序状态字(PSW, program state words)

-本章将标志寄存器简称为flag(flags register)

-标志寄存器的特点是:按照位来起作用,每一位有不同的含义

-在8086CPU中,1、3、5、12、13、14、15位是没有被使用的,因而无含义;而0、2、4、6、7、8、9、10、11位是有特殊意义的。

-【11.1  ZF(zero flag)】

–flag的第6位是ZF,零标志位。它记录相关指令执行后,其结果是否为0。为是,则ZF为1,反之ZF为0。

–可以记忆为它包含了“结果为0”这一命题,ZF的数值表示了真假,一般1为真,0意假。

–运算指令会直接影响这个寄存器。【在8086CPU的指令集中,有的指令会直接影响标志寄存器,比如:add、sub、mul、div、inc、or、and等,它们大多都是运算指令(进行逻辑或算术运算);而有的指令的执行对flag没有影响,比如mov、push、pop等,它们大多是传送指令。】

–【在使用一条指令的时候,要注意这条指令会对标志寄存器的哪些位造成影响】

-【11.2  PF(parity flag)】

–第2位

–判断执行结果的1的个数是否为偶数。是为1,否为0。

-【11.3  SF(sign flag)】

–第7位,符号标志位

–判断条件:结果为负?(是1,否0)【判断最高位是否为1】

–【重要,关于补码实用技巧】对于有符号数:取反后加1就可以实现正负数转换,要注意,取反后加1与减1后取反得到的结果完全相同,所以我们为了使用时不混淆,只要记住取反+1就可以了。

–它的取值首先与数据是否含符号有关,其次又与运算结果有关。

–【一定要注意,传送指令不改变以上三个位的值,比如:mov ax,0虽然让ax变成0,但是ZF、PF、SF的值保持不变】

-【11.4  CF(carry flag)】

–flag的第0位是CF,进位标志位。

–记录无符号数是否发生进位/借位。

–在进行无符号数运算时,它记录了运算结果的最高有效位向更高位的进位值,或从更高位的借位值。

–CF记录的是进位或借位这一动作,只要有借位或进位,则记录为1,反之则记录为0。

-【11.5  OF(overflow flag)】

–第11位,溢出标志位。

–记录有符号数运算是否发生溢出。

–【注意区分CF和OF,CF针对无符号数,OF针对有符号数。】

–【计算机分不清是否是有符号数运算,所以它将CF和OF都进行记录,而对于程序员,则可以根据需要来选择性观看(观看CF意味着它是无符号数,观看OF兼且SF意味着它是有符号数)(计算机需要两种都记录,将未知类型问题转化为条件讨论,即同时研究两个条件导致的结果)】

-【事实上,计算机没有区分有无符号数,它的运算法则唯一,永远是逢二进一,这会产生这样一个结果:假设是8位寄存器,(这句话是错误的)当6号位改变时,(错误)就会将OF改变为1,而当7号位产生进位/借位时,就会将CF改变为1,虽然这只是在8位寄存器情况下的讨论,但是对于不同大小的寄存器,这个原理是类似的。】

-要注意的是:并不是第6位改变,OF就改变,比如:FFF0h+10h会使第6号位改变,但不溢出,因为这里仅仅是-1+16,没有溢出,所以要注意溢出发生的情况:1)正+正=负;2)负+负=正。

-【11.6  adc指令】

–adc是带进位加法指令,利用了CF位上记录的进位值

–语法:adc 操作对象1,操作对象2

–功能:操作对象1 = 操作对象1 + 操作对象2 + CF

–【十六进制与二进制对应关系:1=1,2=10,3=11,4=100,5=101,6=110,7=111,8=1000,9=1001,a=1010,b=1011,c=1100,d=1101,e=1110,f=1111,在这里,我们发现,当十六进制数最高位是8~f时,代表着对应二进制数最高位是1】

–这样的相加模式关键是用来进行低位到高位的进位,比如,我们想让ax与bx相加,这样我们可以使用add al,bl与adc ah,bh,这样可以达到同样地效果。因此我们可以使用多个寄存器来表示位,比如我们可以将ax表示为最低16位,bx表示为次高16位,dx表示位最高16位。

–有时要注意CF的置0。

–子程序编写,128位数相加,对应test49,其实现与教材要求不同,需要将两个数连续存放。

—【注意:在改变si的值时,一定不要使用add si,2因为这样会改变CF的值,而应当使用不会改变CF值的inc指令(inc与loop都不会改变CF的值)(注意:并不是说inc不改变任何值,inc会改变包括但不限于ZF的值)】

-【11.7  sbb指令(borrow 借位)】

–语法:sbb argument1,argument2

–功能:argument1 = argument – argument2 – CF

-【11.8  cmp(compare)指令】

–cmp是比较指令

–语法:cmp argument1,argument2

–功能:计算argument1 – argument2,但是但是不保存结果,仅仅根据结果对标志寄存器进行设置。

–对于程序员,使用起来可以理解为减法,这样是在遗忘比较结果与两个参数关系之间的对应情况时使用,我们要知道,这个指令实际上是反应了两个参数的大小关系吗,比如:如果(ax)>(bx),cmp ax,bx会使CF=0(没有借位),并且ZF=0(不为0)。

–以上在无符号运算时是十分准确的,但是如果对于有符号数,我们无法根据sf=1说明argument1<argument2,因为在有符号且位数有限时,一个正数减去一个负数,可能得到一个负数 (由于溢出),这时sf=1,但arguement1>argument2。

—以上,我们发现只OF也是需要观察的,这样才能判断是否发生溢出,以此来获得正确结果。

—我们要注意:正数相加溢出一定是负数,负数相加溢出一定是正数。

—所以:

—-1)如果sf=1,而of=0,由于of=0没有溢出,所以sf表示的正负是真正意义上的正负,所以这时,a1<a2

—-2)如果sf=0,of=0,则a1>a2

—-3)如果sf=1,of=1,则a1>a2【相当于结果相反】

—-4)如果sf=0,of=1,则a1<a2

–无论有无溢出,zf的判断是绝对的,zf=0则a1≠a2,zf=1则a1=a2。

–8086CPU这种工作机制的设计思想,对于很多处理机来说是普遍的,我们应当留意这种设计思想。

-【11.9  检测比较结果的条件转移指令】

–所有条件转移指令(如jcxz【jump cx zero】)的转移位移都是[-128,127]

–还有许多的条件转移指令,它们都是针对标志寄存器位的,并且,我们常常将cmp指令与它们配合使用。

–对于无符号数,我们常常观察zf、cf,使用:je[jump equal],jne[jump not equal],jb[jump below],jnb,ja[jump above],jna。

–【编程思想】【等价转化问题来简化代码】在编写程序时,我们不一定要完全遵循要求,比如,要求我们判断一段内存中等于8的数据的个数,这时,我们并不需要严格地来判断这个数据是否等于(je)8,而可以判断其不等于(jne)8,我们可以在jne指令下一行进行统计数据的增加,再下面的指令加上标号,这样我们可以将jne指令设定为跳转到这个标号下,也就是说,如果不等于8,我们直接跳过“累计增加”这一操作,直接进行下一次循环,这样的编写有时能够减少我们的代码量。

–对于有符号数的条件转移指令,原理与无符号的相同,只是检测了不同的标志位,我们这里仅仅是讨论cmp、标志寄存器相关位、条件转移指令三者配合应用的原理,这个原理具有普遍性【在使用时关注其逻辑上的对等关系(如je表示cmp的等于),又明白原理是对标志位的检测】,其它的指令可以查看相关的指令手册。

–在使用cmp时,如果要访问内存单元,记得要限定读取类型(如:byte、word、dword)

–我们在这里可以用一个逆转思想:不是想如果什么条件会执行什么,而是想如果不满足某一条件就跳出【重要设计思路】,这样我们可以在主线上设计好全部内容,在相应内容设计不满足条件时的跳出,将地址指向这一部分的末尾(比如:有时可能是循环体的最后)。

-【11.10  DF(Direction flag)标志和串传送指令】

–第10位,方向标志位。

–决定si与di的变化。

—当df=0,一次传送后,si、di会增加

—当df=1,依次传送后,si、di会减少

–指令:movsb【movs byte】、movsw【movs word】

–作用将字(byte)节型(字(word)型)数据从ds:si处传送到es:di处,之后进行si、di数值的改变(改变方式由df值决定)。

—常常配合rep(意为repeat)使用:rep movsb、rep movsw。【注意不要与我们临时用来表示寄存器的reg混淆】

—-作用:根据cx中存放的次数,循环传送

–设置DF值:CLD(clear DF)将DF设置为0,STD(set DF)将DF设置为1。

-【11.11  pushf(push flag)和popf(pop flag)】

–作用:将标志寄存器的值入栈,或将栈中的数据弹入标志寄存器。

–意义:为直接访问标志寄存器提供了一种方法。

-【11.12  标志寄存器在Debug中的表示】

【备注:以下圆括号内单词仅仅为猜测】

–of:OV(overflow)/NV(not overflow)【斜杠左边代表1,右边代表0】

–sf:NG(negative sign)/PL(plus sign)

–zf:ZR(zero)/NZ(not zero)

–pf:PE(parity even)/PO(parity odd)

–cf:CY(carry)/NC(not carry)

–df:DN(down)/UP(up)

-【实验11  编写子程序】

–记得要定义栈段。

-【注意】

–串传送前记得要设置DF