第十一章 标志寄存器
11.0 概述
作用
存储部分指令的部分执行结果
作为部分指令的执行参数
用来控制CPU的相关工作方式
8086CPU标志寄存器
有十六个位
存储的信息,称为程序状态字(PSW)
标志寄存器(flag),是按位起作用的
其他数据寄存器,是整体起作用的
结构
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
OF DF IF TF SF ZF AF PF CF
使用指令时,注意指令对标志寄存器的影响
11.1 ZF标志
Zero Flag
变化指令
算术逻辑运算指令
add、sub、mul、div
inc、or、and
变化规律
(ZF) = 指令结果==0
结果为0吗?
11.2 PF标志
Parity Flag
(PF) = 指令结果的二进制1位数量==偶数
结果为偶数个1吗?
11.3 SF标志
Sign Flag
(SF) = 指令结果最高位==1
结果为负吗?
11.4 CF标志
Carry Flag
用于无符号运算
(CF) = 指令结果包含进位或借位
加法中可能出现进位
减法中可能出现借位
结果有进位借位吗
11.5 OF标志
Overflow Flag
用于有符号运算
溢出现象,运算结果超出机器表示范围
8位,-128~127
16位,-32768~32767
加法溢出
正极大+正极大,过大而负
负极小+负极小,过小而正
减法溢出
正极大-负极小,过大而负
负极小-正极大,过小而正
(OF) = 指令结果发生溢出
结果有溢出吗
例子1
mov al, 0f0h
add al, 88h
看作无符号运算,有进位(CF)=1
看作有符号运算,有溢出(OF)=1
例子2
mov al, 0f0h
add al, 78h
看作无符号运算,有进位(CF)=1
看作有符号运算,无溢出(OF)=0
11.6 adc指令
名称,带进位加法
格式,adc p1, p2
行为,p1 = p1 + p2 + cf
目的,用于实现任意位数的加法
低位的普通相加,或者cf为0的带进位加法
高位的带进位加法
例子
; name, add128
; function, two 128 bits data add
; parameters
; p1, ds:si, low byte low address
; p2, ds:di, low byte low address
add128:
push ax
push cx
push si
push di
sub ax, ax ; set cf=0
mov cx, 8
add128_s:
mov ax, [si]
adc ax, [di]
mov [si], ax
inc si
inc si
inc di
inc di
loop add128_s
pop di
pop si
pop cx
pop ax
ret
11.7 adc指令
名称,带借位减法
格式,sbb p1, p2
行为,p1 = p1 - p2 - cf
目的,用于实现任意位数的减法
低位的普通相减,或者cf为0的带借位减法
高位的带借位减法
11.8 cmp指令
比较指令,相当于不存结果的减法指令,会影响标志寄存器
cmp p1, p2
看作无符号数比较
即无符号数减法
zf = 1 p1 == p2
zf = 0 p1 != p2
cf = 1 p1 < p2
cf = 0 p1 >= p2
cf = 0 and zf = 0 p1 > p2
cf = 1 or zf = 1 p1 <= p2
看作有符号数比较
即有符号数减法
of = 0 and sf = 0 p1 >= p2
of = 0 and sf = 1 p1 < p2
of = 1 and sf = 0 p1 < p2
of = 1 and sf = 1 p1 > p2
11.9 检测比较结果的条件转移指令
条件转移指令
属于短转移,[-128, 127]
行为,条件触发时转移,即修改IP
常与影响条件的指令配合使用,如cmp、sub、add等
jcxz,检查cx是否为0
基于无符号比较结果的条件转移指令
je == zf=1 equal
jne != zf=0 not equal
jb < cf=1 below
jnb >= cf=0 not below
ja > cf=0 and zf=0 above
jna <= cf=1 or zf=1 not above
基于有符号比较结果的条件转移指令
je == zf=1 equal
jne != zf=0 not equal
jl < (of=0 and sf=1) or (of=1 and sf=0) less
jnl >= of=0 and sf=0 not less
jg > of=1 and sf=1 greater
jng <= not greater
检查单个标志位
jz zf=1
jnz zf=0
js sf=1
jns sf=0
jo of=1
jno of=0
jp pf=1
jnp pf=0
jc cf=1
jnc cf=0
编程时注意转移语义
逻辑语义,
条件C成立,则执行A
C语言
与自然描述一致
以执行为描述中心
if (C) A;
汇编语言
与自然描述相反
以转移为描述中心
反条件成立,则转移
jmp !C
A
例子,统计data段中数值为8的字节的个数
data segment
db 8,11,8,1,8,5,63,38
data ends
code segment
start:
mov ax, data
mov ds, ax
mov bx, 0
mov ax, 0
mov cx, 0
s: cmp byte ptr [bx], 8
jne next
inc ax
next:
inc bx
loop s
mov ax, 4c00h
int 21h
code ends
11.10 DF标志和串传输标志
rep movsb
move serial bytes
从ds:[si]到es:[di],DF方向传输cx个字节
相当于
s: mov byte ptr es:[di], ds:[si]
若DF为0
inc si
inc di
若DF为1
dec si
dec di
loop s
rep movsw
move serial words
从ds:[si]到es:[di],DF方向传输cx个字
相当于
s: mov word ptr es:[di], ds:[si]
若DF为0
add si, 2
add di, 2
若DF为1
sub si, 2
sub di, 2
loop s
DF
指定si和di的增长方向,0正1负
std指令,set df,df=1
cld指令,clear df,df=0
例子,复制字符串到随后空间中
data segment
db 'welcome to masm!'
db 16 dup (0)
data ends
code segment
mov ax, data
mov ds, ax
mov es, ax
mov si, 0
mov di, 16
mov cx, 16
cld
rep movsb
code ends
11.11 pushf和popf
pushf,标志寄存器入栈,push flag
popf,出栈到标志寄存器,pop flag
11.12 标志寄存器在Debug中的表示
对应关系
xx xx xx xx xx xx xx xx
OF DF SF ZF PF CF
标志表示
标志 1 0
of OV NV
sf NG PL
zf ZR NZ
pf PE PO
cf CY NC
df DN UP
11.13 实验11 编写子程序
assume cs:code
data segment
db "Beginner's All-purpose Symbolic Instruction Code.", 0
data ends
code segment
begin:
mov ax, data
mov ds, ax
mov si, 0
call letterc
mov ax, 4c00h
int 21h
letterc:
push cx
push si
mov ch, 0
letterc_s:
mov cl, [si]
jcxz letterc_ret
and cl, 11011111b
mov [si], cl
inc si
loop letterc_s
letterc_ret:
pop si
push cx
ret
code ends
end begin
第十二章 内中断
12.0 概述
CPU的中断能力
CPU执行完一条指令后,若检测到一种特殊信息(外部发送或内部产生),则立即对其进行处理
这种特殊信息,称为中断信息
中断信息,是一种逻辑上的说法
它是对硬件产生的事件的同一描述
中断信息,要求CPU立即进行处理,并提供必要的参数
中断信息,可来自CPU外部和内部
中断,暂停当前,处理突发
12.1 内中断的产生
CPU发生如下情况时,将产生对应的中断信息
除法错误,如商溢出 0
单步执行 1
执行into指令 4
执行int指令 参数决定
8086CPU用中断类型码标识中断信息的类型和来源
中断类型码,为一个字节,可表示256种中断信息来源
中断源,中断信息的来源,即产生中断信息的事件
12.2 中断处理程序
用来处理中断信息的程序,称为中断处理程序
一般而言,中断类型不同,中断处理程序不同
12.3 中断向量表
中断向量表
中断向量的列表
中断处理程序入口地址的列表
CPU通过中断类型码N,定位到中断处理程序入口地址的存储位置(4*N),获取入口地址
中断类型码,0~255
中断向量表,0~1023(3ff)
在一个中断向量中,低字存偏移地址,高字存段地址
12.4 中断过程
获得中断类型码
标志寄存器入栈
TF、IF置为0
CS和IP入栈
使用中断向量,设置IP(4*N)和CS(4*N+2)
12.5 中断处理程序和iret指令
由于CPU随时可以执行中断请求,故中断向量表和中断处理程序需要一直存储在内存中
中断处理程序的编写,与子程序编写类似
保存用到的寄存器
处理中断
恢复用到的寄存器
iret
IP和CS出栈
pop IP
pop CS
标志寄存器出栈
popf
12.6 除法错误中断的处理
除法溢出
代码
mov ax, 1000h
mov bh, 1
div bh
显示信息
Your program caused a divide overflow error.
If the program persists, contact your program vendor.
12.7 编程处理0号中断
编写中断处理程序
显示字符串 "overflow!"
安装中断处理程序
程序存放空间
由于我们在操作系统上使用计算机,所有硬件资源都在操作系统管理之下
使用内存资源,需要向操作系统申请
但过多讨论内存申请,将偏离主题
我们学习汇编的目的,在于获得底层编程的体验,尽可能的直接面向硬件资源
8086一共支持256个中断类型,内存0~3ffh用于存放中断向量表,但许多空间都是空的
中断向量表是PC系统最重要的内存区,DOS系统和其他应用程序不会随意使用这段空间
可以使用200h~2ffh这段空间
中断向量为空
操作系统和其他应用程序不会使用
设置中断向量表
将程序的入口地址,放入中断向量表中
根据中断类型码,定位表中的项空间
偏移地址在低字,段地址在高字
程序框架
assume cs:code
code segment
start:
do0安装程序
设置中断向量表
mov ax, 4c00h
int 21h
d0:
显示字符串"overflow!"
mov ax, 4c00h
int 21h
code ends
end start
12.8 安装
"-"是编译器可识别的运算符号,用来进行两个常数的减法
代码
start:
; d0安装程序
mov ax, cs
mov ds, ax
mov si, offset do0
mov ax, 0
mov es, ax
mov di, 200h
mov cx, offset do0_end - offset do0
cld
rep movsb
设置中断向量表
mov ax, 4c00h
int 21h
do0:
显示字符串"overflow!"
mov ax, 4c00h
int 21h
do0_end:
nop
12.9 do0
中断程序,包含指令和数据,需要一起并长期存放在内存中
在当前情况下,可以将数据放入中断程序中
代码
do0:
jmp short do0_start
db 'overflow!'
do0_start:
mov ax, cs
mov ds, ax
mov si, 202h
mov ax, 0b800h
mov es, ax
mov di, 12*160+36*2
mov cx, 9
do0_s:
mov al ds:[si]
mov es:[di], al
add di, 2
inc si
do0_s
mov ax, 4c00h
int 21h
do0_end:
nop
12.10 设置中断向量
代码
mov ax, 0
mov es, ax
mov word ptr es:[4*0], 200h
mov word ptr es:[4*0+2], 0
12.11 单步中断
CPU执行完一条指令后,若检查到TF为1,则产生单步中断,中断类型码为1
注意中断过程中,TF和IF被置为0,避免了中断递归、
Debug利用单步中断实现T命令
Debug提供了单步中断的中断处理程序
功能为显示寄存器内容,等待输入
执行t命令时,将TF置为1,使得CPU执行完单条指令后引发单步中断
12.12 相应中断的特殊情况
在有些情况下,即便CPU检测到中断,也不会响应
中断过程中需要向栈中保存数据,为了保证栈顶地址的一直有效性
向ss段寄存器传送数据后,会继续执行后一条指令,不会被中断
向ss段寄存器传送数据后,应该是传送sp偏移地址
12.13 编写0号中断的处理程序
第十三章 int指令
13.0 概述
当有需要立即处理的事情时,将产生中断信息,CPU检测到中断信息后,将引发中断过程
中断信息可来自CPU的内部和外部
int指令引发的是内中断
13.1 int指令
格式,int N
行为,引发中断过程,中断类型码为N,后转去执行中断处理程序
中断处理程序,简称为中断例程
int指令与call指令
相同,调用一段程序
不同
call,普通函数调用,属于静态绑定,一般用于调用内部子程序
int,虚函数调用,属于动态绑定,一般用于调用外部程序
属于变化度分离,程序的调用和实现
13.2 编写供应用程序调用的中断例程
编写、安装中断7ch的中断例程
功能,求一个字的平方
参数,ax
返回值,ax存放结果低16位,dx存放结果高16位
例子,求2*3456^2
mov ax, 3456
int 7ch
add ax, ax
adc dx, dx
mov ax, 4x00h
int 21h
代码,int7c.asm
assume cs:code
code segment
start:
; 安装程序
mov ax, cs
mov ds, ax
mov si, offset do0
mov ax, 0
mov es, ax
mov di, 200h
mov cx, offset handler_end - offset handler
cld
rep movsb
mov ax, 0
mov es, ax
mov word ptr es:[4*7ch], 200h
mov word ptr es:[4*7ch+2], 0
mov ax, 4c00h
int 21h
handler:
mul ax
iret
handler_end:
nop
code ends
end start
13.3 对int、iret和栈的深入理解
用7ch中断例程完成loop指令的功能
应用代码,在屏幕打印80个'!'
mov ax, 0b800h
mov es, ax
mov di, 160*12
mov bx, offset s - offset se
mov cx, 80
s: mov byte ptr es:[di], '!'
add di, 2
int 7ch
mov ax, 4c00h
int 21h
中断例程
lp: dec cx
jcxz lp_ret
push sp
mov bp, sp
add [bp+2], bx
pop sp
lp_ret:
iret
13.4 BIOS和DOS所提供的中断例程
在主板的ROM中,存放着一套程序,称为BIOS,包括
硬件系统的检测和初始化程序
外部中断和内部中断的中断例程
用于对硬件设备进行I/O操作的中断例程
其他和硬件系统相关的中断例程
操作系统DOS也提供了中断例程,DOS的中断例程就是操作系统向程序员提供的编程资源
BIOS和DOS提供的中断例程中包含了许多子程序,为编程提供常用功能
和硬件设备相关的中断例程中,一般都调用了BIOS的中断例程
13.5 BIOS和DOS中断例程的安装过程
1. 开机后,CPU加电,初始化(CS)=FFFF,(IP)=0,自动从FFFF:0开始执行程序
FFFF:0处是一条跳转指令,转去执行BIOS中的硬件系统检测和初始化程序
2. 初始化程序,将建立BIOS支持的中断向量
注意,安装BIOS中断例程时,只需要设置中断向量表,因为中断例程固化在ROM中,一直处于内存中
3. 硬件系统检测和初始化程序完成后,调用int 19h进行操作系统的引导
从此计算机交由操作系统控制
4. DOS启动后,会将支持的中断例程装入内存,并设置中断向量表
13.6 BIOS中断例程应用
int 10h中断例程
BIOS提供
包含多个子程序,与屏幕输出有关
设置光标
mov ah, 2 ; sub program id, set cursor
mov bh, 0 ; page index
mov dh, 5 ; row index
mov dl, 12 ; column index
int 10h
在光标处显示字符
mov ah, 9
mov al, 'a' ; symbol which will be display
mov bl, 7 ; property code
mov bh, 0 ; page index
mov cx, 3 ; repeat count
int 10h
13.7 DOS中断例程应用
int 21h中断例程,DOS提供
程序返回
mov ah, 4c ; program return
mov al, 0 ; return value
int 21h
在光标处显示字符串
ds:dx指向$结尾的字符串
mov ah, 9
int 21h
13.8 编写、应用中断例程
第十四章 端口
14.0 概述
和CPU通过总线相连的芯片种类
存储器芯片,RAM、ROM等
接口卡上接口芯片,它们会控制接口卡工作
主板上的接口芯片,用于控制部分外设
其他芯片,用来存储相关的系统信息,或进行相关的输入输出处理
在后三种芯片中,都有一对可由CPU读写的寄存器
这些成对的寄存器
都和CPU总线相连,通过芯片进行连接的
被CPU读写时,都是通过控制线向其芯片发送端口读写命令的
从CPU的角度,将这些寄存器都当作端口,并进行统一的编址,建立了一个统一的端口地址空间
CPU可直接访问的位置
CPU内部寄存器
内存地址空间
端口地址空间
14.1 端口的读写
端口所在芯片和CPU通过总线相连,端口地址通过地址总线来传输
在PC系统中,CPU最多定位64KB个不同端口,端口地址范围为[0, 65535]
内存读操作
CPU在地址线上写地址信息
CPU在控制线上写内存读命令,选中存储器芯片
存储器芯片向数据线上写数据
端口读操作
CPU在地址线上写地址信息
CPU在控制线上写端口读命令,选中端口芯片
端口芯片向数据线上写数据
端口访问命令
读,in al/ax, idata/dx
端口号
[0~255],使用立即数
[256~65535],使用dx
写,out idata/dx, al/ax
14.2 CMOS RAM芯片
包含一个实时钟和一个RAM(128字节)
通过电池供电,关机后信息不丢失
RAM使用
实时钟占用[0~0dh],保存时间信息
其余大部分单元用于保存系统配置信息
供系统启动时BIOS读取
BIOS提供程序配置CMOS RAM中的系统信息
有两个端口
地址端口,70h
数据端口,71h
使用步骤
写地址端口
读写数据端口
14.3 shl和shr指令
shl 寄存器/内存单元, 1/cl
逻辑左移,低位补0,移出位存于CF
数据左移,相当于*2
shr 寄存器/内存单元, 1/cl
逻辑右移,高位补0,移出位存于CF
数据右移,相当于/2
14.4 CMOS RAM中存储的时间信息
位置,秒0分2时4日7月8年9
格式,BCD码
15.4 实验14 访问CMOS RAM
assume cs:code
data segment
db 'yy/mm/dd hh:MM:ss$'
d_pos:
db 9,8,7,4,2,0
data ends
stack segment stack
db 160 dup (0)
stack ends
code segment
start:
mov ax, data
mov ds, ax
mov bx, 0
mov si, offset d_pos
mov cx, 6
s:
mov al, [si]
out 70h, al
in al, 71h
call convert
add bx, 3
inc si
loop s
mov ah, 2
mov bh, 0
mov dh, 12
mov dl, 30
int 10h
mov dx, 0
mov ah, 9
int 21h
mov ax, 4c00h
int 21h
convert:
push ax
push cx
mov ah, al
mov cl, 4
shr ah, cl
and al, 0fh
mov byte ptr [bx], ah
mov byte ptr [bx+1], al
pop cx
pop ax
ret
code ends
end start
第十五章 外中断
15.1 接口芯片和端口
PC系统的接口卡和主板上,有各种的外设接口芯片
这些外设接口芯片中有一些寄存器,CPU将这些寄存器当作端口进行访问
15.2 外中断信息
外中断源
可屏蔽中断
CPU可不响应的外中断
CPU检测到可屏蔽中断时
若IF为1,则响应
若IF为0,则不响应
可屏蔽中断引发的中断过程
与内中断引发的中断过程类似
仅"取中断类型码N"的实现不同
可屏蔽中断信息,来自于CPU外部,中断类型码通过数据总线传入CPU
内中断的中断类型码,在CPU内部产生
中断过程中将IF置为0,是为了禁止可屏蔽中断
在设置中断向量表时,也需要禁止可屏蔽中断
避免在仅设置了段地址时,出现可屏蔽中断,导致CPU执行错误指令
IF设置
sti,设置IF为1
cli,设置IF为0
不可屏蔽中断
CPU必须响应的外中断
不可屏蔽中断的中断类型码,固定为2,故中断过程不需要取中断类型码
几乎所有外设引发的外中断,都是可屏蔽中断
不可屏蔽中断是在系统中有必须处理的紧急情况
15.3 PC机键盘的处理过程
键盘的输入
键盘上的键,相当于一个开关,键盘中有一个芯片,会对所有键的开关状态进行扫描
按下一个键,芯片产生一个扫描码,称为通码,扫描码被送入主板上相关接口芯片的寄存器中,端口号为60h
松开一个键,芯片产生一个扫描码,称为断码
扫描码,一个字节长
同一键,通码+80h=断码
引发9号中断
键盘的输入到达60h端口后,相关的芯片会向CPU发送9号可屏蔽中断的中断信息
执行9号中断例程
主要工作
读取60h端口的扫描码
扫描码
是字符键,将扫描码和字符码送入内存中BIOS键盘缓冲区
BIOS键盘缓冲区,可存储15个键盘输入
一个键盘输入,为一个字,高位字节为扫描码,低位字节为字符码
是控制键(Ctrl)或切换键(CapsLock),将其转变为状态字节写入内存
40:17存储键盘状态字节,记录了控制键和切换键的状态
状态字节的位心理系
0,右shirt,按下为1
1,左shirt,按下为1
2,Ctrl,按下为1
3,Alt,按下为1
4,ScrollLock,Scroll灯亮为1
5,Numlock,Num灯亮为1
6,CapsLock,Caps灯亮为1
7,Insert,为1表示删除状态
对键盘系统进行控制,如发送应答信息
15.4 编写int9中断例程
不完整地编写键盘中断例程,因为需要处理一些键盘细节,只需在自定义的中断例程中调用BIOS的中断例程
任务
在屏幕中依次显示"a"~"z"
可以让人看清
在显示过程中,按下Esc键,则改变显示颜色
代码
assume cs:code
stack segment stack
db 128 dup (0)
stack ends
data segment
db 4 dup (0)
data ends
code segment
start:
; modify int9 handler
mov ax, 0
mov es, ax
mov ax, data
mov ds, ax
mov ax, es:[9*4]
mov ds:[0], ax
mov ax, es:[9*4+2]
mov ds:[2], ax
cli
mov word ptr es:[9*4], offset int9
mov es:[9*4+2], cs
sti
; display symbol
mov ax, 0b800h
mov es, ax
mov cx, 26
mov bl, 'a'
mov ax, 0
mov dx, 100h
s: mov es:[12*160+40*2], bl
inc bl
call delay
loop s
; recover int9 handler
mov ax, 0
mov es, ax
cli
mov ax, ds:[0]
mov es:[9*4], ax
mov ax, ds:[2]
mov es:[9*4+2], ax
sti
mov ax, 4c00h
int 21h
delay:
push ax
push dx
push cx
delay_s:
sub ax, 1
sbb dx, 0
mov cx, ax
or cx, dx
jcxz delay_ret
jmp short delay_s
delay_ret:
pop cx
pop dx
pop ax
ret
int9:
push ax
push es
in al, 60h
pushf
call dword ptr ds:[0]
cmp al, 1
jne int9_ret
mov ax, 0b800h
mov es, ax
inc byte ptr es:[12*160+40*2+1]
int9_ret:
pop es
pop ax
iret
code ends
end start
15.5 安装新的int9中断例程
任务
按F1,改变当前屏幕颜色
代码
assume cs:code
stack segment stack
db 128 dup (0)
stack ends
code segment
start:
mov ax, cs
mov ds, ax
mov si, offset int9
mov ax, 0
mov es, ax
mov di, 204h
mov cx, offset int9_end - offset int9
cld
rep movsb
mov ax, es:[9*4]
mov es:[200h], ax
mov ax, es:[9*4+2]
mov es:[202h], ax
cli
mov word ptr es:[9*4], 204h
mov word ptr es:[9*4+2], 0
sti
mov ax, 4c00h
int 21h
int9:
push ax
push es
push bx
push cx
in al, 60h
pushf
call dword ptr cs:[200h]
cmp al, 3bh
jne int9_ret
mov ax, 0b800h
mov es, ax
mov bx, 1
mov cx, 80*25
int9_s:
inc byte ptr es:[bx]
add bx, 2
loop int9_s
int9_ret:
pop cx
pop bx
pop es
pop ax
iret
int9_end:
nop
code ends
end start
15.6 安装新的int9中断例程
15.7 指令系统总结
数据传送指令
mov, push, pop, pushf, popf, xchg
算术运算指令
add, sub, adc, sbb, inc, dec, cmp, imul, idiv, aaa
逻辑指令
and, or, not, xor, test, shl, shr, sal, sar, rol, ror, rcl, rcr
转移指令
jmp
jcxz, je, jb, ja, jnb, jna
loop
call, ret, retf
int, iret
处理机控制指令
cld, std
cli, sti
nop, clc, cmc, stc, hlt, wait, esc, lock
串处理指令
movsb, movsw, cmps, scas, lods, stos
和rep, repe, repne等前缀指令配合使用