本章的内容相对简单,但需要学习的知识点很多,也需要我们耐心去学习和理解一些细节内容。
不过,既然是做读书笔记,那只需要记录一下重点需要理解和记忆的内容,有些知识点原书上就有,可以一笔带过。
标志寄存器,说的是一种特殊的寄存器,它可以实现的功能是:
1、用来存储相关指令的某些执行结果。
2、用来为CPU执行相关指令提供行为依据。
3、用来控制CPU的相关工作方式。
这种寄存器称为“标志寄存器”,其中存储的信息通常被称为程序状态字(PSW)。而PSW,也就是Program status word的缩写。
但王爽老师的书中,有很多看似不太重要、但对理解和记忆有帮助的信息却没有讲出来。
比如这个“标志寄存器”,英文名叫作Flags Register,而其存储的信息称为PSW,但这一点却没有介绍清楚,往往导致初学者误认为“标志寄存器”就是PSW。
在8086CPU中标志寄存器有16位,其结构如下图所示:
从右到左,标志寄存器(FLAG)的第0、2、4、6、7、8、9、10、11位具有特殊的含义,一共9个标志位。
而在本章,一共介绍了CF、PF、ZF、SF、OF、DF 6个标志位的具体用法,并且介绍了与之相关的adc、sbb、cmp,以及串传送指令(movsb、movsw)、pushf、popf等指令。
在8086CPU的指令集中,有的指令执行是影响标志寄存器的,比如:add、sub、mul、div、inc、or、and 等。
有些指令对标志寄存器没有影响,比如:mov、push、pop等。
一、ZF标志(zero flag)
ZF位于FLAG寄存器第6位,英文全称是zero flag。它记录相关指令执行后,如果结果为0,那么ZF=1;如果结果不为0,那么ZF=0。
例如:
mov ax,1
and ax,0
执行后,结果为0,则ZF=1,表示结果为0。以此类推。
二、PF标志(parity flag)
PF位于FLAG寄存器第2位,英文全称是parity flag。parity的就是“奇偶校验”的意思,从字面意义上讲,是在相关指令执行后,CPU将判断其结果的所有bit位中1的个数是否为偶数。如果1的个数为偶数,PF=1;如果为奇数,那么PF=0。
例如:
mov al,1
add al,10
执行后,结果为00001011B,其中1的个数为3个(奇数),则PF=0。以此类推。
三、SF标志(sign flag)
SF位于FLAG寄存器第7位,英文全称是sign flag,也就是符号标志的意思,以此判断数据是有符号,还是无符号的。
对于二进制数据,计算机既可以将它当作无符号的数据来运算,也可以当作有符号的数据来运算,比如:
00000001B,可以看作无符号数1,也可以看作有符号数+1
10000001B,可以看作无符号数129,也可以看作有符号数-127
为什么10000001B可以看作有符号数-127呢?这就涉及计算机补码的知识。
计算机中的整数数据分成有符号数和无符号数。无符号数是用所有的数据位表示数值,有符号数则是用最高位表示数据的正负性,其余位表示数值。
例如:
对于八位数无符号数,所有8位均是数据位,编码 00000000~11111111 表示0~255。
而八位有符号数,从右到左其最高位代表符号,其他7位表示数据。最高位的符号位是0代表正数,是1代表负数。
对于有符号数,当最高位为 0 时,代表正数,低 7位就是这个正数的编码,如0111 1111 。最高位1 表示负数,低 7位111 1111 表示其数值 127,而最高位和低7位联合起来,1111 1111 表示-1。
而每一个代表正数的二进制数据,按位取反后加1后得到的二进制数据,也就是补码,代表这个正数相对应的负数。
比如有符号数127,其二进制为:01111111,此时SF=0。按位取反后,就是1000000,再加1,就是10000001。10000001就是-127的补码,而此时SF=1。
对于有符号的十进制数,转化为二进制数的方式是:
1、如果是正数,保持十进制数不变,直接转化为二进制数。
2、如果是负数,首先将负数采取绝对值操作,转化为正数,然后转化为二制数;接下来,将这个二进制数,按位取反加1,就得到了负数的二进制数。这个二进制数,就是负数的补码。
需注意的是:
1、正数的补码和原码相同,而负数的补码是在相对应的正数的反码的末位加1。
2、如果将数据当作无符号数来运算,SF的值无意义。
3、如果指令进行的是有符号数运算,如果SF=1,那么结果为负。
4、如果指令进行的是有符号数运算,如果SF=0,那么结果为非负。
例如:
mov al,10000001B
add al,1
执行后,结果为10000010B。此时,SF=1,表示:如果指令进行的是有符号数运算,结果为负。以此类推。
需注意的是:
某些指令将影响标志寄存器中的多个标志位。比如指令sub al,al执行后,ZF、PF、SF等标志位都要受到影响,它们分别是:1、1、0
四、CF标志(carry flag)
CF位于FLAG寄存器第0位,英文全称是carry flag。carry就是“进位”的意思。
需注意的是:
1、CF标志仅用于无符号数的运算。
2、在进行无符号数运算的时候,记录了运算结果的最高有效位向更高位的进位值,或者从更高位借位值。
3、对于位数为N的无符号数来说,其对应的二进制信息的最高位,即第N-1位,就是它的最高有效位。
比如,有一个8位的无符号二进制数据:00011100
从右到左第0位到第7位都是代表数值(无符号),最高有效位是第7位。如果在运算的过程中,向假想的更高位产生了进位,或者产生了借位,那么CF=1。
比如产生进位的例子:
mov al,98H
add al,al;执行后:(al)=30H,CF=1,CF记录了从最高有效位向更高位的进位值
add al,al;执行后:(al)=60H,CF=0,CE记录了从最高有效位向更高位的进位值
这是原书第216页的描述。现在用二进制演算一下,以帮助理解:
1、无符号数98H,对应的二进制是:10011000,当执行第一个add al,al之后:98H+98H = 100110000。此时结果已由8位二进制数据,进位成了9位二进制数据。从右到左,最高位为1,而高位1后面的00110000,就是16进制数30H,而CF则记录了最高位的1。所以,结果为30H,CF=1。
2、无符号数30H,对应的二进制是:00110000,当执行第二个add a,al之后:
30H+30H = 01100000,此时结果仍然是8位二进制数据,没有产生进位,所以CF=0。
又比如产生借位的例子:
mov al,97H
sub al,98H;执行后:(al)=FFH,CF=1,CF 记录了向更高位的借位值;
sub al,al;执行后:(al)=0,CF=0,CF 记录了向更高位的借位值
用二进制演算一下,以帮助理解:
1、无符号数97H,对应的二进制是10010111;无符号数98H,对应的二进制是10011000。在执行sub al,98H时,10010111(97H)减去10011000(98H),10011000(97H)需要向假想的更高位借一个1,构成110010111,也就是197H,才能完成对10011000(98H)的减法运算。此时,CF=1。
2、在执行sub al,al时,其结果为0,既不产生进位,也不产生借位,故CF=0。
五、OF标志(overflow flag)
OF位于FLAG寄存器第11位,英文全称是overflow flag。overflow就是“溢出”的意思。当进行有符号运算时,计算结果产生了溢出,就会影响到OF标志。
对于8位有符号数据,机器所能表示的范围就是-128~127;对于16位有符号数据,机器所能表示的范围是-32768~32767,如果运算结果超出了机器所能表达的范围,将产生溢出。
下面,来分析一下原书217页所给出的两个例子。
例1:
mov al,98
add,al,99
对于有符号运算,执行后将产生溢出。
1、十进制98,对应的二进制是01100010;十进制99,对应的二进制是01100011。
2、98+99=197,对应的二进制是11000101,而11000101则代表了有符号数-59。因为59的二进制是00111011,按位取反加1后就是11000101,这也就是-59的补码。
因此,在有符号运算过程中,98+99不是等于197,而是等于-59。这样的结果让人无法接受,其原因,就是实际的结果197超出了8位有符号数的范围:-128~127,在8位寄存器al中存放不下,产生了溢出。
在这个例子中,对于无符号运算,98+99没有进位,CF=0;而对于有符号运算,98+99发生了溢出,OF=1。
例2:
mov al,0F0H;0F0H,为有符号数-16 的补码
add al,088H;88H,为有符号数-120的补码
对于有符号运算,执行后将产生溢出。
1、16进制0F0H,对应的二进制是11110000,其原码就是按位取反加1,也就是00010000,对应的十进制是16。也就是说,作为有符号数F0H代表的就是-16。
2、16进制88H,对应的二进制是10001000,其原码就是按位取反加1,也就是01111000,对应的十进制是120,也就是说,作为有符号数88H,代表的是-120。
3、0F0H和88H通过add指令运算,假想中按照人的十进制运算规则:-16+(-120),结果应该是-136,而-136的补码,也就是136(二进制10001000)按位取反加1,也就是01111000,对应的十进制是120,也就是16进制78H。也就是说,这个结果作为有符号数,代表的是120。
因此,在有符号运算过程中,0F0H+88H不是等于-136,而是等于120。显然,这样的结果也是无法让人接受的。
其原因,就是实际的结果-136,超出了8位有符号数的范围:-128~127,在8位寄存器al中存放不下,产生了溢出。
需注意的是:
1、对于无符号运算,0F0H+88H有进位,即11110000+10001000= 101111000,CF=1。
2、对于有符号运算,0F0H+88H发生溢出(结果为-136,超出了机器对于8位有符号数据的表达范围-128~127),OF=1。
结合前面学习的CF标志,需注意的是:
1、OF是对有符号运算有意义的标志位。
3、对于有符号数运算,CPU用OF来记录是否产生了溢出,还要用SF位来记录结果的符号。
2、CF是对无符号运算有意义的标志位。
4、对于无符号数运算,CPU用CF来记录是否产生了进位。
5、CF和OF所表示的进位和溢出,分别对应了无符号数和有符号数运算,它们之间没有任何关系。
那么,CPU是如何判断在计算中,是进行无符号运算还是有符号运算的呢?
答案是,CPU本身并不作区分。也就是说,一个数字在计算的时候,既可以作为无符号数处理,也可以作为有符号数处理。关键在于程序员如何去看待结果:
1、CPU的运算结果将同时影响进位标志CF与溢出标志OF。
2、如果程序员认为这是无符号数运算,则只考虑进位标志CF,而忽略溢出标志OF。
3、反之,如果程序员认为这是有符号数运算,则只考虑溢出标志OF,而忽略进位标志CF。
六、ADC指令
adc是带进位加法指令,它利用了CF位上记录的进位值。
指令格式:adc 操作对象1,操作对象2
功能:操作对象1=操作对象1+操作对象2+CF
比如指令 adc ax,bx 实现的功能是:(ax)=(ax)+(bx)+ CF
例如:
mov ax,2
mov bx,1
sub bx,ax
adc ax,1
执行后,(ax)=4。在第三步,sub bx,ax中,由于bx的值小于ax,因此需要产生借位,使CF=1。
故在第四步时,adc ax,1,就相当于计算:(ax)+1+CF=2+1+1=4。
又例如:
mov al,98H
add al,al
adc al,3
执行后,(ax)=34H。
1、98H对应的二进制是10011000,在执行add al,al之后,其结果为100110000,这是一个9位的二进制,产生了进位,CF=1。
2、其结果100110000,在8位的寄存器AL,其有效位是00110000,对应的16进制就是30H。
3、在执行adc al,3时,相当于计算(ax)+1+CF=30H+3+1=34H。
简单地说,adc指令比add指令多加了一个CF位的值。那为什么要用到adc这个指令呢?因为用adc指令,可以进行更大位数的相加。
原书第220页提供了这样一个例子:用两个16位寄存器AX和BX进行198H+183H的运算:
在王爽老师的讲解中:“可以看出,加法可以分两步来进行:①低位相加;②高位相加再加上低位相加产生的进位值”,其实列出的竖式并没有很好解释这个原理。下面,通过二进制演算逐一讲解:
1、AX中保存的16进制198H,对应的二进制是00000001 10011000;其低8位是10011000,高8位则是00000001。
2、BX中保存的16进制183H,对应的二进制是00000001 10000011;其低8位是10000011,高8位则是00000001 。
3、当进行add,al,bl时,即10011000+10000011 = 0001 00011011,此时al的结果,是 00011011,也就是16进制1B,并且产生了一个进位值0001,故CF=1。
3、当进行adc ah,bh时,先进行ah+bh运算,即00000001+00000001 = 00000010,再加上CF的值1,其结果就等于 00000011(也就是16进制03)。
4、运算结束,AL=00011011,AH=00000011,两个8位寄存器联合起来就是00000011 00011011。也就是最终的结果,16进制的031B。
有这个分析,就不难理解后面的例子:
1、这两个数的低16位,分别是1000H和1EF0H。
2、这两个数的次高16位,分别是F000H和1000H。
3、这两个数的最后高16位,分别是1EH和20H。
因此,后面的代码就是:
mov ax,001EH
mov bx,0F000H
mov cx,1000H
add cx,1EF0H
adc bx,1000H
adc ax,0020H
这段代码,后面连用了2个adc运算,最终实现了1EF0001000H和2010001EF0H这样两个大数的相加。
七、SBB指令
sbb 是带借位减法指令,它利用了 CF 位上记录的借位值。
指令格式:sbb 操作对象1,操作对象2
功能:操作对象1=操作对象1-操作对象 2-CF
比如指令 sbb ax,bx 实现的功能是:(ax)=(ax)-(bx)-CF
有了对adc指令的理解,sbb指令也就很容易理解。
例如,计算003E1000H-00202000H:
mov bx,1000H
mov ax,003EH
sub bx,2000H
sbb ax,0020H
1、先通过sub bx,2000H,将两个数的低位1000H和2000H相减,产生CF的借位值1。
2、再通过sbb ax,0020H,将两个数的高位003EH和0020H相减,再减去CF的值1。
八、CMP指令
cmp 是比较指令,cmp 的功能相当于减法指令,只是不保存结果。cmp指令执行后,将对标志寄存器产生影响。其他相关指令通过识别这些被影响的标志寄存器位来得知比较结果。
cmp 指令格式:cmp 操作对象1,操作对象2
功能:计算操作对象1-操作对象2,但并不保存结果,仅仅根据计算结果对标志寄存器进行设置。
比如,指令 cmp ax,ax,做(ax)-(ax)的运算,结果为 0,但并不在 ax 中保存,仅影响flag 的相关各位。指令执行后:zf=1,pf=1,sf=0,cf=0,of=0。
这里需注意的是:结果为0,也就意味着所有bit位中1的个数也为0,而0在数学中是定义为偶数的,所以PF值为1。
此外,原书第223页,介绍了执行cmp ax,bx后,相关标志位的值:
反之,指令 cmp ax,bx 的逻辑含义是比较 ax 和 bx 中的值,如果执行后:
需注意的是:
同 add、sub 指令一样,CPU 在执行 cmp 指令的时候,也包含两种含义:进行无符号数运算和进行有符号数运算。所以利用cmp指令可以对无符号数进行比较,也可以对有符号数进行比较。
1、如果比较的是两个无符号数
如下表格所示:
执行cmp指令 | ZF值 | CF值 |
目的操作数 < 源操作数 | 0 | 1 |
目的操作数 > 源操作数 | 0 | 0 |
目的操作数 = 源操作数 | 1 | 0 |
这个表格理解起来相对简单:
1)当目的操作数小于源操作数时,由于两数不相等,结果不为0,故ZF为0;并且由于产生了借位,故CF值为1
2)当目的操作数大于源操作数时,由于两数不相等,结果不为0,故ZF为0;并且没有产生借位,故CF值为0
3)当目的操作数等于源操作数时,由于两数相等,结果为0,故ZF为1;并且没有产生借位,故CF值为0。
2、如果比较的是两个有符号数
原书的通篇解释比较复杂,但经过理解,归纳一下:
1)当目的操作数 < 源操作数时的情况有:
① SF = 1 且 OF = 0
② SF = 0 且 OF = 1
2)目的操作数 > 源操作数时的情况有:
① SF = 1 且 OF = 1
② SF = 0 且 OF = 0
3)目的操作数 = 源操作数时的情况:
SF = 0 且 OF = 0
但其实只要关注SF、OF的关系与ZF的值,就可以简化理解。如下表格所示:
执行cmp指令 | 相关标志位的关系 |
目的操作数 < 源操作数 | SF ≠ OF |
目的操作数 > 源操作数 | SF = OF |
目的操作数 = 源操作数 | ZF = 1 |
也就是说:
1)当目的操作数小于源操作数时,SF和OF不相等
2)当目的操作数大于源操作数时,SF等于OF(都为1或0)
3)当目的操作数等于源操作数时,由于两数相等,结果为0,故ZF为1。
cmp指令主要用于,根据比较结果配合一些条件转移指令:
cmp指令可以同时对无符号数比较和有符号数的比较,然后根据相应的结果进行条件转移。
在原书中,只是重点介绍了无符号数的比较结果导致的条件转移,而根据有符号数的比较结果进行条件转移,原书一笔带过,因为基本原理相同,只是后者检测了不同的标志位:
1、根据无符号数的比较结果进行条件转移,CPU检测ZF、CF的值。
2、根据有符号数的比较结果进行条件转移,CPU检测SF、OF和ZF的值。
这两点,笔记前面的两个表格有详细说明。
而对于je、jne等条件转移的含义,原书也有更详细的说明:
而在原书第227页中,重点以je指令为例,介绍了这些转移指令的工作原理。
比如,je指令检测的是ZF的位置,不管je指令前面是什么指令,只要CPU执行je指令之前ZF=1,那么就会发生转移:
mov ax,0
add ax,0
je s
inc ax
S:inc ax
执行后,(ax)=1。add ax,0 使得 zf-1。
需注意的是:
1、je指令将进行转移。此处的 je指令检测到的 ZF=1,不是由 cmp等比较指令设置的,而是由 add 指令设置的,并不具有“两数相等”的含义。
2、但无论“ZF=1”的含义如何,是什么指令设置的,只要是ZF=1,就可以使得je指令发生转移。
3、对于 jne、jb、jnb、ja、jna 等指令和 cmp 指令配合使用的思想和 je 相同。
4、在联合应用它们的时候,不必再考虑 cmp 指令对相关标志位的影响和 je 等指令对相关标志位的检测。因为相关的标志位,只是为 cmp 和 je 等指令传递比较结果。
5、可以直接考虑cmp 和ie 等指令配合使用时,表现出来的逻辑含义。它们在联合使用的时候表现出来的功能有些像高级语言中的IF语句。
九、DF标志(direction Flags)和串传送指令
DF位于FLAG寄存器第10位,英文全称是direction Flags。direction就是“方向”的意思。DF标志主要是配合串传送指令movsb、movsw的执行,控制每次操作后si、di的增减。
df = 0 ,每次操作后si、di递增
df = 1,每次操作后si、di递减
(一)MOVSB指令
movsb 的功能是将 ds:si 指向的内存单元中的字节送入 es:di 中,然后根据标志寄存器 df位的值,将 si 和 di 递增或递减。
格式:movsb
功能:执行 movsb 指令相当于进行下面几步操作。
1、((es)*16+(di))=((ds)*16+(si))
2、如果 df=0 则:
(si)=(si)+1
(di)=(di)+1
如果 df=1 则:
(si)=(si)-1
(di)=(di)-1
在原书的第231页,王爽老师给出了一个并不存在的指令,只是用于描述movsb指令的功能:
mov es:[di] byte ptr ds:[si]
如果 df=0:
inc si
inc di
如果 df=1:
dec si
dec di
需注意的是:
1、SI这个寄存器,全名是Source Index,也就是源变址寄存器,用于存放源操作数的偏移地址,并且SI的内容有自动修改的功能。
2、DI这个寄存器,全名是Destination Index,也就是目的变址寄存器 ,用于存放目的操作数的偏移地址,并且DI的内容有自动修改的功能。
3、SI、DI可以和BX、BP联用。它们和BX连用时,段地址默认在DS中;和BP联用时,段地址默认在SS中。它们也可以单独使用,单独使用时,段地址默认在DS中,想要越段使用,加上段前缀即可。
4、在串指令操作中,一般来说,SI和DS联用,确定源地址;DI和ES(附加段寄存器)联用,确定传送的目的地址;在这种使用过程中,SI和DI具有自加和自减功能。
(二)MOVSW指令
格式:movsw
movsw 的功能是将 ds:si 指向的内存字单元中的字送入 es:di 中,然后根据标志寄存器df 位的值,将 si 和 di 递增 2 或递减 2。
同样,王爽老师给出了一个并不存在的指令,只是用于描述movsw指令的功能:
mov es:ldil ,word ptr ds:[si]
如果 df=0:
add si,2
add di,2
如果 df=1:
sub si,2
sub di,2
需注意的是:
1、movsb传送的是字节单元的内容,而movsw传送的是字单元的内容。所以两个指令所影响的SI、DI的增减量不一样,一个是1,另一个是2。
2、一般来说,movsb和movsw都和rep配合使用,格式为:rep movsb和 rep movsw 。
而以rep movsb为例,用汇编的语法来描述其功能就是:
s:movsb
loop s
可见,rep的作用是根据CX的值,重复执行后面的串传送指令,每执行一次movsb指令SI和DI都会递增或递减,指向后一个单元或者前一个单元。
同样,rep movsw也相当于:
s:movsw
loops
而标志寄存器(FLAG)的DF位则决定了串传送指令执行后,SI和DI改变的方向。8086CPU提供了下面两条指令对DF进行设置:
1、cld指令:将标志寄存器的DF位置0,也就是所谓的“正向传送”。
2、std指令:将标志寄存器的DF位置1,也就是所谓的“逆向传送”。
需注意的是:
1、在设置好数据(按字节或字)的源地址和目标地址后,用CLD 和 串传送指令(movsb或movsw)进行正向传送的时候,SI要指向源地址的第一个单元,DI要指向目标地址的第一个单元。
2、在设置好数据(按字节或字)的源地址和目标地址后,用STD 和 串传送指令(movsb或movsw)进行逆向传送的时候,SI要指向源地址的最后一个单元,DI要指向目标地址的最后一个单元。
关于这两点,原书第232页和第233页有详细介绍,这里不再赘述。
而串传送指令movsb和movsw,在编写和安装中断程序的时候用得比较多,在原书的第12章、13章、14章、15章涉及得比较多,所以应重点理解。
十、PUSHF和POPF指令
这两个指令是本章中最简单,最容易理解的两个指令。
pushf的功能是将标志寄存器的值压栈,而popf是从栈中弹出数据,送入标志寄存器。这两个指令为直接访问标志寄存器提供了一种方法。
这里,重点分析一下原书第233页,检测点11.4,以加深理解:
下面的程序执行后:(ax)=?
mov ax,0
push ax
popf
mov ax 0fff0h
add ax,0010h
pushf
pop ax
and al,11000101B
and ah,00001000B
1、通过第一条mov指令,使AX寄存器里的16位数据全部为0。
2、执行push ax之后,也就是将AX的数据0压入栈中。
3、执行popf,就是将栈中弹出数据0,将标志寄存器里面的所有标志(前面提到的CF、PF、ZF、SF、OF、DF等标志)归零。
4、经过mov ax 0fff0h和add ax,0010h两条指令的操作结果,二进制为1 0000 0000 0000 0000,很明显,这个值已经超出了AX的数据容纳范围(16位),达到了17位。此时AX中的内容为 0000 0000 0000 0000,也就是0H。
5、对于无符号数运算,0fff0H对应的二进制数据是1111 1111 1111 0000 ,0010H对应的二进制数据是0000 0000 0001 0000;这两个数的所有位数值都是有效数据(最高位不代表符号),0fff0H与0010H相加,产生了进位,因此CF=1。
6、对于有符号数运算,0fff0h对应的二进制数据是1111 1111 1111 0000,其最高位是符号位,1代表负数;1111 1111 1111 0000代表的是-10h;因此与0010H相加,结果为0H,没发生溢出(或者说,结果0H没有超出机器能表示的16位范围-32768~32767),因此OF=0。
7、而结果是0H,因此ZF=1;而0H作为非负数,所以SF=0;0H的二进制是0000 0000 0000 0000,其中1的个数为0,是偶数,因此PF=1。因此,此时FLAG中相关标志的值:CF=1,OF=0,ZF=1,SF=0,PF=1。
8、在标志寄存器中,CF位于第0位,PF位于第2位,ZF位于第6位。因此,此时的标示寄存器的内容为:
0000 0000 0100 0101,也就是45H。
9、在执行pushf指令后,将标志寄存器的值45H压栈。
10、在执行pop ax指令后,AX获得了弹出的数据45H。
11、执行and al,11000101B指令,AL的内容是AX的低8位数据,也就是0100 0101,其结果为0100 0101,也就是45H。
12、执行and ah,00001000B指令,AH的内容是AX的高8位数据,也就是0000 0000 ,其结果为0000 0000。
13、因此,最后的结果:AX = 0000 0000 0100 0101,也就是45H。
十一、标志寄存器在Debug中的表示
这个知识点没什么好说的,直接贴出原书第234页的内容以作参考:
最后,贴出实验11 编写子程序的代码:
===========
assume cs:codesg
datasg segment
db "Beginner's All-purpose Symbolic Instruction Code.",0
datasg ends
codesg segment
begin: mov ax,datasg
mov ds,ax
mov si,0
call letterc
mov ax,4c00h
int 21h
letterc:
mov al,[si]
mov ch,0
mov cl,al
jcxz ok
cmp byte ptr [si],27H;比较是否为单引号
je notchar
cmp byte ptr [si],20H;比较是否为空格
je notchar
cmp byte ptr [si],2DH;比较是否为横杠号
je notchar
cmp byte ptr [si],2EH;比较是否为减号
je notchar
and al,11011111b
mov [si],al
inc si
jmp short letterc
notchar: inc si
jmp short letterc
ok:ret
codesg ends
end begin
===========
实验11很简单,没什么可说的,一看就明白。
小结一下学习本章的体会:
1、本章的信息量很大,特别是补码和原码的关系、无符号数与有符号数的操作、进位和溢出的原理,很多细节都需要逐步去推敲和理解才能掌握相关知识。
2、和学习王爽老师写的其他章节一样,很多坑得自己去填,如果图省事跳过一些内容,就会造成知识点的断档,导致后面的学习无法推进,所以必须多复习几遍,所谓“慢就是快”。
3、这一章很多内容,是12章以后内容的基础,特别是串传送指令(movsb和movsw)需要深入理解。