汇编语言学习笔记
《汇编语言》--王爽
前言
学习汇编目的:充分获得底层编程体验;深刻理解机器运行程序的机理。
原则:没有通过监测点不要向下学习;没有完成当前实验不要向下学习。
第一章 基础知识
有三类指令组成汇编语言:汇编指令;伪指令;其他符号。8bit = 1byte = 一个存储单元有n根地址线,则可以寻址2的n次方个内存单元。
1.1节 --1.10节 小结
(1)汇编指令是机器指令的助记符,同机器指令一一对应。
(2)每一种cpu都有自己的汇编指令集。
(3)cpu可以直接使用的信息在存储器中存放。
(4)在存储器中指令和数据没有任何区别,都是二进制信息。
(5)存储单元从零开始顺序编号。
(6)一个存储单元可以存储8个bit,即八位二进制数。
(7)每一个cpu芯片都有许多管脚,这些管脚和总线相连。也可以说,这些管脚引出总线。一个cpu可以引出的三种总线的宽度标志了这个cpu不同方面的性能。地址总线的宽度决定了cpu的寻址能力;数据总线的宽度决定了cpu与其他器件进行数据传送时的一次数据传送量;控制总线的宽度决定了cpu对系统中其他器件的控制能力。
监测点:1KB的存储器有1024个存储单元?存储单元的编号从0到1023.
内存地址空间:
最终运行程序的是cpu,我们用汇编编程时,必须要从cpu的角度思考问题。对cpu来讲,系统中的所有存储器中的存储单元都处于一个统一的逻辑存储器中,它的容量受cpu寻址能力的限制。这个逻辑存储器即是我们所说的内存地址空间。
第二章 寄存器(cpu的工作原理)
mov ax, 2
add ax, ax
add ax, ax
add ax, ax
(1)cpu中的相关部件提供两个16位的地址,一个称为段地址,另一个称为偏移地址;
(2)段地址和偏移地址通过内部总线送人一个称为地址加法器的部件;
(3)地址加法器将两个16位地址合成为一个20位的物理地址;
(4)地址加法器通过内部总线将20位物理地址送人输入输出控制电路;
(5)输入输出控制电路将20位物理地址送上地址总线;
(6)20位物理地址被地址总线传送到存储器;
段地址*16+偏移地址 = 物理地址 的本质含义
内存并没有分段,段的划分来自cpu。
以后编程时可以根据需要,将若干地址连续的内存单元看做一个段,用段地址*16定位段的起始地址,用偏移地址定位段的内存单元。
一个段的起始地址一定是16的倍数,一个段的最大长度为64kB。当然也没有办法定义一个起始地址不是16的倍数的段。
cpu可以用不同的段地址和偏移地址形成同一物理地址。
在8086cpu加电启动或复位后cs和ip被设置为cs = f000h,ip = ffffh,即ffff0h单元中的指令是8086pc机开机后执行的第一条指令。(??应该是fffffh吧??)
cpu将cs:ip指向的内存单元看做指令。
在cpu中,程序员能够用指令读写的部件只有寄存器,程序员可以通过改变寄存器中的内容实现对cpu的控制。
mov指令称为传送指令,cpu中大部分寄存器的值都可以通过mov指令改变。
除了cs:ip 8086没给他这样的功能。
cs:ip可以用转移指令来改变。jmp
jmp cs:ip 用指令给出的段地址修改cs,偏移地址修改ip
jmp 某一寄存器 的功能为:用寄存器中的值修改ip
jmp ax 含义类似于mov ip,ax
段地址在8086pc机的段寄存器存放。当8086cpu要访问内存时,由段寄存器提供内存单元的段地址。8086cpu有四个段寄存器,其中cs用来存放指令的段地址。
cs存放指令的段地址,ip存放指令的偏移地址。
8086机中,任意时刻,cpu将cs:ip指向的内容当作指令执行。
8086cpu的工作过程:
1.从cs:ip指向内存单元读取指令,读取的指令进入指令缓冲器;
2.ip指向下一条指令
3.执行
8086提供转移指令修改cs:ip的内容
debug的使用
查看,修改cpu中寄存器的内容:r命令
查看内存中的命令:d命令
修改内存中的内容:e命令(可以写入数据,指令,在内存中,它们实际上没有区别)
将内存中的命令解释为机器指令和相应的汇编指令:u命令
执行cs:ip指向的内存单元处的命令:t命令
以汇编指令的形式向内存中写入指令:a命令
第三章 寄存器(内存访问)
3.1内存中字的存储
高八位存放在高字节中,低八位存放在低字节中
3.2DS和【address】
3.3字传送
3.4MOV ADD SUB
3.5数据段
3.6栈
栈是一种具有特殊的访问方式的存储空间。它的特殊性就在于,最后进入这个空间的数据,最先出去。
栈有两个基本的操作:入栈和出栈。
栈的这种操作规则被称为:lifo(last in first out,后进先出)。
cpu如何知道10000H--1000fH这段空间被当作栈使用?
push pop指令在执行时必须知道哪个单元是栈顶单元,可是如何知道呢?
栈顶的段地址存放在ss中,偏移地址存放在sp中。任意时刻,ss:sp指向栈顶单元。
push和pop指令执行时,cpu从ss和sp中得到栈顶的地址。
入栈时,栈顶从高地址向低地址方向增长。
如何定义一个栈的大小?
mov ax, 1000h
mov ss, ax
mov sp, 0010h ;则栈顶为1000f 栈底为 10000
******************************************
mov ax, 1000h
mov ds, ax
mov ax, 2266h
mov [0], ax
************************
mov ax,1000h
mov ss, ax
mov sp, 0002h
mov ax, 2266h
push ax
栈顶的变化范围最大为:0--ffffh
栈的综述
(1)8086cpu提供了栈操作机制,方案如下:
在ss、sp中存放栈顶的段地址和偏移地址;
提供入栈和出栈指令,他们根据ss、sp指示的地址,按照栈的方式访问内存单元。
(2)push指令的执行步骤:1)sp=sp-2;2)向ss:sp指向的字单元送人数据;
(3)pop指令的执行步骤:1)从ss:sp指向的字单元中读取数据;2)sp=sp+2
(4)任意时刻,ss:sp指向栈顶元素。
(5)8086cpu只记录栈顶,栈空间的大小我们要自己管理。
(6)用栈来暂存以后需要恢复的寄存器的内容时,寄存器出栈的顺序要和入栈的顺序相反。
(7)push、pop指令实质上是一种内存传送指令,注意他们的灵活运用。
一个栈段最大可以设为64k
段的综述
我们可以将一段内存定义为一个段,用一个段地址指示段,用偏移地址访问段内的单元。这完全是我们的安排。
用一个段存放代码,即代码段
用一个段存放数据,即数据段
用一个段当栈,即栈段
对于数据段,把段地址放在ds中,用mov,add,sub等访问内存单元的指令时,cpu就将我们定义的数据段内容当做数据来访问;
对于代码段,把段地址放在cs:ip中,用mov,add,sub等访问内存单元的指令时,cpu就将执行我们定义的代码段的指令;
对于栈段,把段地址放在ss:sp中,用push pop 等访问内存单元的指令时,cpu就将其当作堆栈来访问;
debug的t命令在执行修改器ss的指令时,下一条指令也紧接着被执行了。
3.7栈超界问题
8086cpu不保证我们对栈的操作不会超界。也就是说,8086cpu只知道栈顶在何处而不知道我们安排的栈空间有多大,这点就好像,cpu只知道当前要执行的指令在何处,而不知道要执行的指令有多少。从这两点我们可以看出cpu的工作机理,它只考虑当前情况:当前的栈在何处,当前要执行的指令是哪一条。
对于超界问题我们可以做到就是小心。
第五章【bx】和loop指令
我们完整的描述一个内存单元,需要两种信息:
(1)内存单元的地址;(2)内存单元存放数据的类型;
inc bx的含义是bx中的内容加一
[bx]寄存器bx中所包含的地址中存放的内容
[bx]表示一个内存单元,他的偏移地址在bx中。
loop指令执行的时候,要进行两步操作:
1;(cx)=(cx)- 1;
2;判断cx中的值,不为零则转至标号处执行。
“通常”我们用loop实现循环功能,cx中存放循环次数。
用cx和loop指令相配合实现循环功能的三个要点:
(1)再cx中存放循环次数;
(2)loop指令中的标号所标识地址要在前面;
(3)要循环执行的程序段,要写在标号和loop指令的中间。
在汇编源程序中,数据不能以字母开头。
所以对于大于9fffh的数,均在前面加上0。
mov cx,11
s: add ax,ax
loop s
assume cs:code
code segment
..
mov ax,4c00h
int 21h
code ends
end
ffff:6单元是一个字节单元,ax是一个十六位寄存器,数据长度不一样,如何赋值?
注意我们说的是“赋值”,就是说,让ax中的数据的值(数据的大小)和ffff:0006单元中的数据的值(数据的大小)相等。八位数据01h和16位数据0001h的数据长度不一样,但他们的值是相等的。
设ffff:0006单元中的的数据是xxh,若要ax中的值和ffff:0006单元中的值相等,ax中的数据应为00xxh。所以,若实现ffff:0006单元向ax赋值,我们应该令(ah)=0,(al)=(ffff6H).
若希望程序能从cs:0012处执行,可以用g命令。“g 0012”。他表示程序执行到0012处。
若希望程序能跳出循环,用p命令
5.4 debug和汇编编译器masm对指令的不同处理。
在汇编源程序中,mov al,[0]会被编译器解释成为:mov al,0
所以要这样表达:mov bx,0
mov al,[bx]
或者 mov al,ds:[0]
第一,我们在汇编源程序中,如果用指令访问一个内存单元,则在指令中必须用[...]来表示内存单元,如果在[]里用一个常量idata直接给出内存单元的偏移地址,就要在[]的前面显示地给出段地址所在的段寄存器。
第二,如果在[]里用寄存器,比如bx,间接给出内存单元的偏移地址,则段寄存器默认在ds中。
第六章 包含多个段的程序
程序取得所需段的方法有两种:
一是在加载程序的时候为程序分配,再就是程序在执行的过程中向系统申请。
dw的含义是定义字型数据即define word 字型数据间以逗号隔开。
程序运行的时候cs存放代码段的段地址,所以我们可以从cs中得到它们的段地址。
dw定义的数据处于代码段的最开始,所以偏移地址为零。
dw 0123h,5604h,1234h
start :指令
end start
我们在程序的第一条指令的前面加了一个标号start:并在end的后面再次加入。
end除了通知编译器程序结束外,还可以通知编译器程序的入口在什么地方。在单任务系统中,可执行文件中的程序执行如下:
(1)由其他的程序(debug、command或其他程序)将可执行文件中的程序加载入内存;
(2)设置cs:ip指向程序的第一条要执行的指令(即程序的入口),从而使程序得以运行;
(3)程序运行结束后,返回到加载者;
描述信息
可执行文件由描述信息和程序组成,程序来源于源程序中的汇编指令和定义的数据;描述信息则主要是编译连接程序对原程序中相关伪指令进行处理所得到的信息。
6.2 在代码中使用栈
在程序中通过定义数据来取得一段空间,然后将这段空间当作栈空间来使用。
例如将cs:16 -- cs:31的内存空间当作栈来用,初始状态下栈为空,所以ss:sp要指向栈底,则设置ss:sp指向cs:32.
6.3将数据代码栈放入不同的段
用assume将定义的具有一定用途的段跟寄存器联系起来
第七章 更灵活的定位内存地址的方法
7.1 and 和 or 指令
(1)and指令:逻辑与指令,按位进行与运算。
mov al ,01100011B
and al ,00111011B
执行后:al=00100011B
通过该运算可将操作对象的相应位设为0,其他位不变。
(2)or指令:逻辑或指令,按位进行或运算。
通过该运算可将操作对象的相应位设为1,其他位不变。
7.2 关于ASCII码
所谓编码方案,就是一套规则,它约定了用什么样的信息来表示现实对象。
小写字母的ASCII码值,比大写字母的ASCII码值大20H。
就ASCII码的二进制形式来看,除第五位外,大写字母和小写字母的ASCII码值都一样。大写字母第五位为0,小写字母第五位为1.
运用and和or指令的给特定位赋值功能即可实现大小写字母之间的转换。
7.5 [bx+idata]
类似于[bx]的一种更灵活的指明内存单元的方式。
[bx+idata]表示一个内存单元,它的偏移地址为(bx)+idata (bx中的数值加上idata。
也可以写成
mov ax,[200+bx]
mov ax,200[bx]
mov ax,[bx].200
7.6 用[bx+idata]的方式进行数组的处理
7.7 SI和DI
SI和DI是8086cpu中和bx功能相近的寄存器。区别是SI和DI不能够分成两个八位寄存器来使用。
codesg segment
start: mov ax,datasg
mov ds,ax
mov si,0
mov di,16
mov cx,8 ;di si 为16位寄存器只需循环
s: mov ax,[si]
mov [di],ax ;八次即可将16个字节传送完毕
add si,2
add di,2
loop s
mov ax,4c00h
int 21h
codesg ends
end start
si = di - 16
[di] = 16[si] = [si +16] = [si].16
7.8【bx+si】和【bx+di】
[bx+si]表示一个内存单元,偏移地址为bx中的数值加上si中的数值,是一种更加灵活的寻址方式。
7.9【bx+si+idata】和【bx+di+idata】
mov ax,【bx+di+idata】
7.10不同的寻址方式的灵活运用
(1)【idata】用一个常量来表示地址,可用于直接定位一个内存单元;
(2)【bx】用一个变量来表示内存地址,可用于间接定位一个内存单元
(3)【bx+idata】用一个变量加一个常量来表示内存地址,可在一个起始地址的基础上用变量间接定位一个内存单元
(4)【bx+si】用两个变量表示地址
(5)【bx+si+idata】用两个变量和一个常量来表示地址
第八章 数据处理的两个基本问题
(1)处理的数据在什么地方?
(2)要处理的数据有多长?
我们定义的描述性符号:reg和sreg 分别表示寄存器和段寄存器
reg:ax,bx,cx,dx,ah,al,bh,bl,ch,cl,dh,dl,sp,bp,si,di;
sreg:cs,ds,ss,es;
8.1 bx,si,di,bp
(1)这四个寄存器可以用在[...]中来进行内存单元的寻址。
而下面的指令是错误的
mov ax,[cx]
mov ax,[ax]
mov ax,[dx]
mov ax,[ds]
(2)可单个出现,或只能以四种组合出现:bx和si;bx和di;bp和si,bp和di。
(3)只要在[]中使用寄存器bp,而指令中没有显性的给出段地址,段地址就默认在ss中。
8.2
机器中数据处理的指令大致可分为三类:读取,写入,运算。
所要处理的数据可以在三个地方:cpu内部,内存,端口。
8.3汇编语言中数据位置的表达
1.立即数
直接包含在机器指令中的数据(执行前在cpu的指令缓冲器中)
mov ax,1
add bx,2000h
or bx,00010000b
mov al,‘a’
2.寄存器
3.段地址(SA)和偏移地址(EA)
mov ax,[0]
mov ax,[di]
mov ax,[bx+8]
mov ax,[bx+si]
mov ax,[bx+si+8]
段地址默认在ds中
mov ax,[bp]
mov ax,[bp+8]
mov ax,[bp+si]
mov ax,[bp+si+8]
段地址默认在ss中
8.4 寻址方式
直接寻址
寄存器间接寻址
寄存器相对寻址
基址变址寻址
相对基址变址寻址
8.5 指令要处理的数据有多长
指令可以处理两种尺寸的数据,byte和word。
汇编语言中数据处理方法
(1)通过寄存器名指明指令进行的是字操作
(2)在没有寄存器名存在的情况下用操作符X ptr指明内存单元的长度 X在汇编指令中为word或byte
mov word ptr ds:[0],1
inc byte ptr ds:[0],1
(3)有些指令默认了访问的是字单元还是字节单元如 push [1000H] 只进行字操作。
8.6 寻址方式的综合应用
8.7 div指令
div 是除法指令
(1)除数 :有八位和十六位两种,在一个寄存器或内存单元中。
(2)被除数:默认存放在ax或ax和dx中,如果除数是八位,被除数则为十六位,默认在ax中存放;如果除数是十六位,被除数则为三十二位,默认dx中存放高十六位,ax中存放低十六位。
(3)结果:除数是八位,则al存放除法的商,ah存放除法操作的余数。除数是十六位,则ax存放除数的商,dx存放除法操作的余数。
div byte ptr ds:[0] 被除数在ax中。商在al中,余数在ah中。
div word ptr es:[0] 被除数高十六位在dx中,低十六位在ax中;商在ax中,余数在dx中。
8.8 伪指令dd
db == define byte 占一个字节
dw == define word 占一个字
dd == define double word 占两个字
8.9 dup
操作符由编译器识别处理的符号。
与db dw dd 配合使用,来进行数据的重复。
db 3 dup(0) = db 0,0,0
db 3 dup(0,1,2) = db 0,1,2,0,1,2,0,1,2
dx 重复的次数 dup (重复的数据)
第九章 转移指令
可以修改ip,或同时修改cs和ip的指令统称为转移指令。
可以控制cpu执行内存中某处代码的指令。
段内转移:只修改ip时 jmp ax 修改范围-128 127
段间转移:同时修改cs:ip时 jmp 1000:0 修改范围-32768 32767
无条件转移指令 jmp
条件转移指令
循环指令
过程
中断
9.1操作符 offset
offset 作用 取得标号的偏移地址
start:mov ax,offset start ;相当于mov ax,0
s:mov ax,offset s
9.2jmp指令
9.3依据位移进行转移的jmp指令
jmp short 标号
实现段内短转移,对ip的修改范围是-128 127
cpu在执行jmp指令的时候并不需要转移的目的地址
需要的是所需偏移的数值
指令“jmp short 标号”的功能为:(ip)=(ip)+8位位移
(1)八位位移 = “标号”处的地址 - jmp指令后的第一个字节的地址
(2)short指明此处的位移为八位位移。
(3)八位位移的范围为-128 127,用补码表示;
(4)八位位移由编译程序在编译时算出。
还有一种jmp near ptr 标号 实现段内近转移 (ip)= (ip)+16位位移
(1)16位位移 = “标号”处的地址 - jmp指令后的第一个字节的地址
(2)near ptr 指明此处的位移为16位位移,进行的是段内近转移;
(3)16位位移的范围为-32768 32767,用补码表示。
(4)16位位移由编译程序在编译时算出。
9.4 转移的目的地址在指令中的jmp指令
jmp far ptr标号 实现的是段间转移,又称为远转移。
(cs)=标号所在段的段地址;(ip)=标号在段中的偏移地址
9.5转移地址在寄存器中的jmp指令
jmp 16位寄存器 (ip)=(16位寄存器)
9.6转移地址在内存中的jmp指令
分为两种格式:1,jmp word ptr 内存单元的地址(段内转移)
功能:内存单元地址开始存放着的一个字是转移的目的偏移地址
2,jmp dword ptr 内存单元地址(段间转移)
功能:内存单元开始处存放两个字,高地址的字是转移的目的段地址,地地址处是转移的目的偏移地址。
9.7 jcxz指令
有条件转移指令,所有的有条件转移指令都是短转移,在对应的机器码中包含转移的位移,而不是目的地址。
指令格式:j cx z 标号 (如果(cx)=0,转移到标号处执行)
9.8 loop指令
loop指令为循环指令,所有的循环指令都是短转移。
9.9 根据位移进行转移的意义
9.10 编译器对转移位移超界的检测
jmp 2000:0100 的转移指令,是在debug中使用的汇编指令,汇编编译器并不认识。如果在源程序中使用,编译时也会报错。
10.1 ret和retf
ret指令用栈中的数据,修改ip的内容,从而实现近转移;
retf指令用栈中的数据,修改cs和ip的内容,从而实现远转移。
10.2 call
两步操作:
(1)将当前的ip或cs和ip压入栈中
(2)转移
不能进行短转移
10.3 依据位移进行转移的call指令
call 标号(将当前的ip压栈后,转到标号处执行)
call 标号 相当于
push ip
jmp near ptr 标号
10.4 转移的目的地址在指令中的call指令
call far ptr 标号 实现的是段间转移
call far ptr 标号
相当于
push cs
push ip
jmp far ptr 标号
10.5 转移地址在寄存器中的call指令
call 16位寄存器
push ip
jmp 16位寄存器
10.7 call和ret的配合使用
配合使用实现子程序的机制
call指令执行以后,ip已指向下一指令,然后call指令将此ip存入栈中,再jmp
call指令转去执行子程序前,call指令后面的指令的地址将存储在栈中,所以可以在子程序的后面使用ret指令,用栈中的数据设置ip的值,从而转到call指令后面的代码处继续执行。
10.8 mul指令
mul是乘法指令
(1)两个相乘的数:要么都是八位,要么都是十六位。
如果都是八位,一个存放在AH中,另一个放在八位寄存器或内存字节单元中;如果是16位,一个默认在AX中,另一个放在十六位寄存器或内存单元中。
(2)结果:如果是八位乘法,结果默认放在Ax中;如果是十六位乘法,结果高位默认在DX中存放,低位默认在Ax 中存放。
mul reg mul 内存单元
内存单元可以用不同的寻址方式给出,比如:
mul byte ptr ds:[0]
(ax) = (al)*((ds)*16+0)
mul word ptr [bx+si+8]
含义: (ax) = (ax)*((ds)*16+(bx)+(si)+8)结果的低十六位
(dx) = (ax)*((ds)*16+(bx)+(si)+8)结果的高十六位
10.9模块化程序设计
10.10参数和结果传递的问题
10.11批量数据的传递
capital:and byte ptr[si],11011111b
inc si
loop capital
ret
10.12 寄存器冲突的问题
主程序要使用cx记录循环次数,可是子程序也使用了cx,在执行子程序的时候,cx中保存的循环计数值被改变,使得主程序的循环出错。
子程序中使用的寄存器,很可能在主程序中也要使用,造成了寄存器使用上的冲突。
以后编写子程序的标准框架是:
子程序开始:子程序中使用的寄存器入栈
子程序内容
子程序中使用的寄存器出栈
ret、retf
第十一章 标志寄存器
(1)用来存储相关指令的某些执行结果;
(2)用来为cpu执行相关指令提供行为依据;
(3)用来控制cpu的相关工作方式。
8086cpu中标志寄存器有十六位其中存储的信息被称为程序状态字(psw)。
flag和其他寄存器不一样,其他寄存器是用来存放数据的,都是整个寄存器具有一个含义。而flag寄存器是按位起作用的,也就是说,它的每一位都有专门的含义。
11.1 zf标志
第六位,零标志位。执行相关指令后,如果结果为零,那么zf为1.
add、sub、mul、div、inc、or、and等,大都是运算指令执行后对标志寄存器有影响。mov、push、pop等对标志寄存器没有影响。
11.2 pf标志
第二位,奇偶标志位。指令执行后其结果中的所有二进制中一的个数是否为偶数。如果1的个数为偶数,pf = 1,如果为奇数,那么pf = 0。
11.3 sf标志
第七位,符号标志位。指令执行后,结果为负,sf=1,如果非负,sf=0.
计算机中通常用补码来表示有符号数据。计算机中一个数可以看做有符号数也可以看做无符号数。
10000001B,可以看做无符号数129,也可以看做有符号数-127.
对于同一个二进制数据,计算机可以将他看做无符号数来运算,也可以看做有符号数来运算。
补码就是反码加一。
不管我们如何看待cpu在执行add等指令时,就已经包含了两种含义,也将得到同一种信息来记录的两种结果。关键在于我们的程序需要哪一种结果。
sf标志,就是cpu对有符号数运算结果的一种记录,它记录数据的正负。在我们将数据当作有符号数来运算的时候,可以通过他来得知结果的正负。如果我们将结果当作无符号数来计算,sf的值将没有意义。
11.4 cf标志
第零位,进位标志位。,它记录了运算结果有效位向更高位的进位。
11.5 of标志
第十一位,溢出标志位。在进行有符号数运算的时候,如果结果超出了机器所能表示的范围称为溢出。
cf是对无符号数运算有意义的标志位,of是对有符号数运算有意义的标志位。
两者之间没有关系。
11.6 adc指令
adc是带进位加法指令,它利用了cf位上记录的进位值。
ADC AX,BX (ax)= (ax)+(bx)+ cf
11.7 sbb指令
sbb是带借位减法指令,它利用了cf位上记录的借位值。
11.8 cmp指令
cmp是比较指令,cmp的功能相当于减法指令,只是不保存结果。cmp指令执行后,将对标志寄存器产生影响。
在进行无符号数比较时,cmp ax,bx
ZF = 1 ;(AX) = (BX)
ZF = 0 ;(AX) != (BX)
CF = 1 ;(AX) < (BX)
CF = 0 ;(AX) >=(BX)
CF = 0 && ZF = 0;(AX) > (BX)
CF = 1 || ZF = 1;(AX) <=(BX)
在进行有符号数比较时:cmp ah,bh
(1)SF = 1 && OF = 0
说明没有溢出,逻辑上真正的结果的正负 = 实际结果的正负
因为SF = 1,实际结果为负,所以逻辑上真正的结果为负,所以(AH)< (BH)
(2)SF = 1 && OF = 1
说明有溢出,逻辑上真正的结果的正负 != 实际结果的正负;
因为SF = 1,实际结果为负,而又有溢出,这说明是由于溢出导致了实际结果为负,如果因为溢出导致了实际结果为负,那么逻辑上真正的结果必然为正。
所以SF = 1 && OF = 1说明(AH)>(BH)
(3)SF = 0 && OF = 1
因为溢出导致实际结果为正,则逻辑结果必然为负;
所以(AH)<(BH)
(4)SF = 0 && OF = 0
没有溢出逻辑结果 = 实际结果 所以(AH) >= (BH)
11.9 检测比较结果的条件转移指令
“条件转移指令”根据条件决定是否修改IP
jcxz即为一条条件转移指令,jmp cx zero 如果cx=0就修改ip否则什么也不做。所有条件转移指令的转移位移都是【-128,127】。
如call和ret配合使用一般,系统提供了一些转移指令与cmp配合使用。
je = jmp equal 等于则转移 检查标志位 zf = 1
jne = jmp not equal 不等于则转移 zf = 0
jb = jmp below 低于则转移 cf = 1
jnb = jmp not below 不低于则转移 cf = 0
ja = jmp above 高于则转移 cf = 0 && zf = 0
jna = jmp not above 不高于则转移 cf = 1 || zf = 1
11.10 DF标志和串传送指令
第十位,方向标志位。在串处理指令中,控制每次操作后SI,DI的增减。
DF = 0;每次操作后SI,DI递增
DF = 1;每次操作后SI,SI递减
串传送指令: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
MOVSW 可以传送一个字
MOVSB和MOVSW进行的是串传送操作中的一个步骤,一般都要和rep配合使用,格式如下:
rep movsb
相当于:
s:movsb
loop s
rep的作用是根据cx的值,重复执行后面的串传送指令。
8086cpu提供了下面两条指令对DF位进行设置:
CLD:将DF位置0
STD:将DF位置1
1.传送的原始地址
2.传送的目的地址
3.传送的长度
4.传送的方向
11.11 pushf和popf
压入/弹出标志寄存器中的数据
为访问标志寄存器提供了一种方法。
11.12 标志寄存器在DEBUG中的表示
第十二章 内中断
12.1 内中断的产生
cpu内部发生下面情况时,将产生相应的中断信息:
(1)除法错误(2)单步执行(3)执行int0命令(4)执行int命令
中断类型码0 1 4 n 为一个字节型数据
12.2 中断处理程序
cpu如何通过八位的中断类型码得到中断处理程序的段地址和偏移地址?
12.3 中断向量表
答:通过中断向量表
中断向量表就是中断处理程序入口地址的列表。保存在内存中。
cpu如何找到中断向量表?
对于8086中断向量表指定存在内存地址0处。从0000:0000到0000:03e8单元。共1000个字节。中断向量表中一个表项存放一个中断向量,占两个字。分别存放段地址和偏移地址。
12.4 中断过程
(1)从中断信息中取得中断类型码
(2)标志寄存器的值入栈
(3)设置标志寄存器的第八位TF和第九位IF的值为0
(4)CS的内容入栈
(5)IP的值入栈
(6)从内存地址为中断类型码*4和中断类型码*4 + 2 的两个字单元中读取中断处理程序的入口地址设置IP和CS
12.5 中断处理程序
(1)保存用到的寄存器
(2)处理中断。
(3)恢复用到的寄存器
(4)用IRET指令返回。
12.6 除法错误中断的处理
12.7 编程处理0号中断
12.8 安装
第十三章 int指令
13.1 int指令
int指令的最终功能和call指令相似,都是调用一段程序。
13.2 编写供应用程序调用的中断例程
编写、安装中断7ch的中断例程,功能:求一word型数据的平方。
(AX) = 要计算的数据。
DX,AX存放结果的高十六位和低十六位。
MOV WORD PTR ES:[7CH*4],200H
MOV WORD PTR ES:[7CH*4+2],0
MOV CX,OFFSET SQREND-OFFSET SQR
CLD
REP MOVSB
int指令和iret指令配合使用与call指令和ret指令配合使用具有相似的思路。
13.3 对int、iret和栈的深入理解
*********************************
13.4 BIOS和DOS所提供的中断例程
BIOS主要包含以下内容:
1、硬件系统的检测和初始化程序
2、外部中断和内部中断的中断例程
3、用于对硬件设备进行I/O操作的中断例程
4、其他和硬件系统相关的中断例程
13.5 BIOS和DOS中断例程的安装过程
1.开机后,cpu一加点,初始化(cs)= 0FFFFH,(IP)= 0,自动从FFFF:0单元开始执行程序。FFFF:0处有一条跳转指令,cpu执行该程序后,转去执行BIOS中的硬件检测和初始化程序。
2.初始化程序将建立BIOS所支持的中断向量,即将BIOS提供的中断例程的入口登记在中断向量表中。注意,对于BIOS所提供的中断例程,只需将入口地址登记在中断向量表中即可,因为它们是固化到ROM中的内容,一直在内存中存在。
3.硬件系统检测和初始化完成后,调用int 19h进行操作系统的引导。从此将计算机交给操作系统控制。
4.DOS启动后,除完成其他工作外,还将它提供的中断例程装入内存,并建立相应的中断向量。
13.6 BIOS中断例程应用
int 10h 中断例程是BIOS提供的中断例程,其中包含多个和屏幕输出有关的子程序。
一个供程序员调用的中断例程往往包含多个子程序,中断例程内部用传递进来的参数来决定执行那一个子程序。BIOS和DOS提供的中断例程,都用AH来传递内部参数
13.7 DOS中断例程应用
例如 我们一直在使用的4ch号子程序,即程序返回功能。
mov ah 4ch
mov al 0
int 21h
========================================================================
DOS功能调用 int 21h
(1)键盘输入
1) 1号调用——从键盘输入单个字符
调用格式: MOV AH,1
INT 21H
功能: 等待从键盘输入一个字符并送入AL。
执行时系统将扫描键盘,等待有健按下,一旦有健按下,就将其字符的ASCII码读
入,先检查是否Ctrl-Break,若是,退出命令执行;否则将ASCII码送AL,同时将
该字符送显示器显示。
2) 10号调用——从键盘输入字符串
功能: 从键盘接收字符串送入内存的输入缓冲区,同时送显示器显示。
调用前要求: 先定义一个输入缓冲区
MAXLEN DB 100 ;第1个字节指出缓冲区能容纳的字符个数,即缓冲区长度,不能为0
ACLEN DB ? ;第2个字节保留,以存放实际输入的字符个数
STRING DB 100 DUP(?) ;第3个字节开始存放从键盘输入的字符串。
调用格式: LEA DX,MAXLEN(缓冲区首偏移地址)
MOV AH,10
INT 21H
(2)显示输出
1) 2号调用——在显示器上显示输出单个字符
调用格式: MOV DL,待显示字符的ASCII码
MOV AH,2
INT 21H
功能:将DL中的字符送显示器显示。
【例】显示输出大写字母A
MOV DL,41H ;或写为 MOV DL,'A'
MOV AH,2
INT 21H
2) 9号调用——在显示器上显示输出字符串
调用格式: LEA DX,字符串首偏移地址
MOV AH,9
INT 21H
功能:将当前数据区中DS:DX所指向的以'$'结尾的字符串送显示器显示。
【例】在显示器上显示字符串“YOU ARE SUCESSFUL!”
DATA SEGMENT
STRING DB ' YOU ARE SUCESSFUL! $ '
DATA ENDS
CODE SEGMENT
… …
MOV AX,DATA
MOV DS,AX
LEA DX,STRING
MOV AH,9
INT 21H
… …
CODE ENDS
说明:若希望显示字符串后,光标可自动回车换行,可在定义字符串时作如下更改:
STRING DB ' YOU ARE SUCESSFUL! ' ,0AH,0DH,' $ ' ;在字符串结束前加回车换行的ASCII码0AH,0DH