汇编原理自我总结(四)
第十三章 int指令
13.1 int 指令
int指令的格式:int n(n为中断码),供能是引发中断过程。
执行过程如下:
- 取中断类型码n;
- 标志寄存器入栈,TF = 0,IF = 0;
- CS,IP入栈;
- (IP)=(n*4),(CS)= (n*4+2);
从此处转去执行n号中断处理程序。
编程的时候,可以用int指令调用这些处理程序,这些处理程序之后叫中断例程。
13.2 编写供应用程序调用的中断例程
编程1:编写,安装中断7ch的中断例程。
功能:求一word型数据的平方
参数:ax
结果:dx,ax中分别存放高位和低位
assume cs:code
code segment
start:
;安装
mov ax,cs
mov ds,ax
mov si,offset sqr
mov ax,0
mov es,ax
mov di,0200h
mov cx,offset sqrend-offset sqr
cld
rep movsb
;中断向量表
mov ax,0
mov es,ax
mov word ptr es:[7cH*4],0
mov word ptr es:[7ch*4+2],0200h
mov ax,4c00h
int 21h
sqr:
mul ax
iret
sqrend:
nop
code ends
end start
编写2:编写,安装7ch的中断例程
功能:将一个全是字母,以0为结尾的字符串转化为大写字母。
参数:ds:si指向字符串的首地址
assume cs:code
data segment
db 'conversation',0
data ends
code segment
start:
;安装
mov ax,cs
mov ds,ax
mov si,offset conversation
mov ax,0
mov es,ax
mov di,0200h
mov cx,offset conversationend-offset conversation
cld
rep movsb
mov ax,0
mov es,ax
mov word ptr es:[7ch*4],0
mov word ptr es:[7ch*4+2],0200h
mov ax,4c00h
int 21h
conversation:
push cx
push si
s:
mov cl,[si]
mov ch,0
jcxz ok
and byte ptr [si],11011111b
inc si
jmp short s
ok:
pop si
pop cx
iret
conversationend:
nop
code ends
end start
13.3 对int,iret和栈的深入理解
问题:用7ch中断例程完成loop指令的功能。
参数:ax,次数;dx,位移;
应用举例:
assume cs:code
code segment
start:
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
se: nop
mov ax,4c00h
int 21h
code ends
end start
lp: push bp
mov bp,sp
dec cx
jcxz lpret
add [bp+2],bx
lpret:
pop bp
iret
用7ch中断例程完成jmp near ptr s指令的功能,用bx向中断例程传送转移位移。
应用举例:在屏幕的前12行,显示data段中以0结尾的字符串。
assume cs:code
data segment
db 'conversation',0
data ends
code segment
start:
mov ax,data
mov ds,ax
mov si,0
mov ax,0b800h
mov es,ax
mov di,12*160
s: cmp byte ptr [si],0
je ok
mov al,[si]
mov es:[di],al
inc si
add di,2
mov bx,offset s-offset ok
int 7ch
ok: mov ax,4c00h
int 21h
code ends
end start
jnmp:
push bp
mov bp,sp
cmp bx,0
jna jnmp1
add [bp],bx
pop bp
iret
jnmp1:
sub [bx],bx
pop bp
iret
13.4 BIOS和DOS所提供的中断例程
在系统板的ROM中存放着一套程序,称为BIOS(Basic Input/Output System),包含以下几部分内容:
- 硬件系统的检测和初始化程序;
- 外部中断和内部中断的中断例程;
- 用于对硬件设备进行I/O操作的中断例程;
- 其他和硬件系统相关的中断例程。
操作系统DOS也提供了中断例程,从操作系统的角度来看,DOS的中断例程就是系统向程序员提供的编程资源。
和硬件设备相关的DOS中断例程中,一般都调用了BIOS的中断例程。
13.5 BIOS和DOS中断例程的安装过程
- 开机后,CPU一加电,初始化(CS)= 0ffffh,(IP)= 0,自动从FFFF:0单元开始执行程序。FFFF:0处有一条转跳指令,CPU执行该指令后,转去执行BIOS中硬件系统检测和初始化程序。
- 初始化程序将建立BIOS所支持的中断向量,即将BIOS提供的中断例程的入口地址登记在中断向量表中。注意,对于BIOS所提供的中断例程,只需将入口地址登记在中断向量表中即可,因为它们是固化到ROM中的程序,一直在内存中存在。
- 硬件系统检测和初始化完成后,调用int 19h进行操作系统的引导。从此将计算机交由操作系统控制。
- DOS启动后,除完成其他工作外,还将它提供的中断例程装入内存,并建立相应的中断向量。
13.6 BIOS中断例程应用
int 10h:一般来说,一个供程序员调用的中断例程中往往包括多个子程序,中段例程内部用传递进来的参数来决定执行哪一个子程序。BIOS和DOS提供的中断例程,都用ah来传递内部子程序的编号。
assume cs:code
code segment
mov ah,2 ;置光标
mov bh,0 ;第0页
mov dh,5 ;dh中放行号
mov dl,12 ;dl中放列号
int 10h
mov ah,9 ;在光标位置显示字符
mov al,'a' ;字符
mov bl,11001010b ;颜色属性
mov bh,0 ;第0页
mov cx,3
int 10h
mov ax,4c00h
int 21h
code ends
end
13.7 DOS中断例程应用
int 21h:该中断例程是DOS提供的中断例程,其中包含了DOS提供给程序员在编程时调用的子程序。
程序返回功能:
mov ah,4ch ;程序返回
mov al,0 ;返回值
int 21h
显示字符串功能:
ds:dx 指向字符串 ;要显示的字符串需用”$“作为结束符
mov ah,9 ;功能号9,表示在光标位置显示字符串
int 21h
assume cs:code
data segment
db 'Welcome to masm','$'
data ends
code segment
start:
mov ah,2
mov bh,0
mov dh,5
mov dl,12
int 10h
mov ax,data
mov ds,ax
mov dx,0
mov ah,9
int 21h
mov ax,4c00h
int 21h
code ends
end start
实验十三
-
编写安装 int 7ch 中断例程,功能为显示一个用0结束的字符串,中断例程安装在0:200处。
参数:(dh)=行号,(dl)=列号,(cl)=颜色,ds:si指向字符串首地址。
assume cs:code data segment start: mov ax,cs mov ds,ax mov si,offset show mov ax,0 mov es,ax mov di,0200h mov cx,offset showend-offset show cld rep movsb mov ax,0 mov es,ax mov es:[7ch*4],0200h mov es:[7ch*4+2],0 mov ax,4c00h int 21h show: mov ax,0b800h mov es,ax mov al,dh mov dh,160 mul dh add ax,2*dl mov di,ax mov al,cl s: mov cl,[si] mov ch,0 jcxz ok mov es:[di],cl inc di mov es:[di],al inc di inc si jmp short s ok: iret showend: nop code ends end start
测试:
assume cs:code data segment db 'welcome to masm!',0 data ends code segment start: mov dh,10 mov dl,10 mov cl.2 mov ax,data mov ds,ax mov si,0 int 7ch mov ax,4c00h int 21h code ends end start
-
编写并安装int 7ch中断例程,功能为完成loop指令的功能。
参数:(cx)=循环次数,(bx)=位移。
assume cs:code code segment start: mov ax,cs mov ds,ax mov si,offset lp mov ax,0 mov es,ax mov di,0200h mov cx,offset lpend-offset lp cld rep movsb mov ax,0 mov es,ax mov word ptr es[7ch*4],0200h mov word ptr es[7ch*4+2],0 mov ax,4c00h int 21h lp: push bp mov bp,sp dec cx jcxz ok add [bp+2],bx ok: pop bp iret lpend:nop code ends end start
测验:
assume cs:code code segment start: 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 se:nop mov ax,4c00h int 21h code ends end start
-
补全程序,分别在屏幕的第2、4、6、8行显示4句英文诗。
assume cs:code code segment s1: db 'Good,better,best,',','$' s2: db 'Never let it rest,',','$' s3: db 'Till good is better,',',','$' s4: db 'And better,best.',',','$' s: dw offset s1,offset s2,offset s3,offset s4 row: db 2,4,6,8 start: mov ax,cs mov ds.ax mov bx,offset s mov si,offset row mov cx,4 ok: mov bh,0 mov dh,ds:[si] ; mov dl,0 mov ah,2 int 10h mov dx,ds:[bx] ; mov ah,9 int 21h add bx,2 ; inc si ; loop ok mov ax,4c00h int 21h code ends end start
第十四章 端口
在PC机系统中,和CPU通过总线相连的芯片除存储器外,还有以下3种芯片:
- 各种接口卡(显卡,网卡等)上的接口芯片,它们控制接口卡进行工作;
- 主板上的接口芯片,CPU通过它们对部分外设进行访问;
- 其他芯片,用来存储相关的系统信息,或进行相关的输入输出处理。
在这些芯片中,都有一组可以由CPU读写的寄存器。这些寄存器,它们在物理上可处于不同的芯片中,但是它们在以下两点相同:
- 都和CPU的总线相连,当然这种连接是通过它们所在的芯片进行的;
- CPU对它们进行读或写的时候都通过控制线向它们所在的芯片发出端口读写命令。
从CPU的角度,将这些寄存器都当作端口,对它们进行统一编址,从而建立了一个统一的端口地址空间。每一个端口在地址空间中都有一个地址。
CPU可以直接读写以下3个地方的数据。
- CPU内部的寄存器;
- 内存单元;
- 端口;
14.1 端口的读写
在访问端口的时候,CPU通过地址来定位端口。因为端口所在的芯片和CPU通过总线相连,所以,端口地址和内存地址一样,通过地址总线来传送。在PC系统中,CPU最多可以定位64KB个不同的端口。则端口地址范围为0~65535。
对端口的读写不能用mov 、push、pop等内存读写命令。端口的读写只有两条:in和out,分别用于从端口读取数据和往端口写入数据。
访问端口:
in al,60h ;从60h号端口读入一个字节
执行时与总线相关的操作如下。
- CPU通过地址线将地址信息60h发出;
- CPU通过控制线发出端口读命令,选中端口所在的芯片,并通知它,将要从中读取数据;
- 端口所在的芯片将60h端口中的数据通过数据线送入CPU。
注意,在in和out指令中,只能使用ax或al来存放从端口中读入的数据或要发送到端口中的数据。访问8位端口时用al,访问16位端口时用ax。
对0~255以内的端口进行读写时:
int al,20h
out 20h,al
对256~65535的端口进行读写时,端口号放在dx中:
mov dx,3f8h
in al,dx
out dx,al
14.2 CMOS RAM芯片
该芯片的特征如下:
- 包含一个实时钟和一个有128个存储单元的RAM存储器(早期的计算机为64个字节)。
- 该芯片靠电池供电。所以,关机后其内部的实时钟仍可正常工作,RAM中的信息不丢失。
- 128个字节的RAM中,内部实时钟占用0~0dh单元来保存时间信息,其余大部分单元用于保存系统配置信息。供系统启动时BIOS程序读取。BIOS也提供了相关的程序,使我们可以在开机的时候配置CMOS RAM中的系统信息。
- 该芯片内部有两个端口,端口地址为70h和71h。CPU通过这两个端口来读写CMOS RAM。
- 70h为地址端口,存放要访问的CMOS RAM单元的地址;71h为数据端口,存放从选定的CMOS RAM单元中读取的数据,或要写入到其中的数据。可见,CPU对CMOS RAM的读写分两步进行,比如,读CMOS RAM的2号单元:
将2送入端口70h;
从端口71h读出2号单元的内容
14.3 shl和shr指令
逻辑移位指令。shl:逻辑左移指令:
- 将一个寄存器或内存单元中的数据向左移位;
- 将最后移出的一位写入CF中;
- 最低位用0补充。
指令:
mov al,01001000b
shl al,1 ;将al中的数据左移一位
执行后,(al)=10010000b,CF=0。
如果移动位数大于1时,必须将移动位数放在cl中。
mov al,01010001b
mov cl,3
shl al,cl
shr是逻辑右移指令,它和shl所进行的操作刚好相反。右移一位空出来的位用0填充,逻辑右移嘛。算术右移是用最高位填充。
mov al,10000001b
shr al,1
执行后,(al)=0100000b,CF=1。
编程:用加法和移位指令计算(ax)=(ax)*10。
assume cs:code
code segment
start:
mov ax,520h
mov dx,ax
shl ax,1
mov cl,3
shl dx,cl
add ax,dx;(ax)*10 = (ax)*2+(ax)*8
mov ax,4c00h
int 21h
code ends
end start
14.4 CMOS RAM中存储的时间信息
在CMOS RAM中,存放着当前的时间:年、月、日、时、分、秒。这6个信息的长度都为1个字节,存放单元为:
秒:0 分:2 时:4 日:7 月:8 年:9
这些数据以BCD码的方式存放。
10: 0 1 2 3 4 5 6 7 8 9
BCD码:0000 0001 0010 0011 0100 0101 0110 0111 1000 1001
数值26的BCD码为:0010 0110。
一个字节可表示两个bcd码。则CMOS RAM存储时间信息的单元中,存储了用两个BCD码表示的十进制数,高4位的BCD码表示十位,低4位的表示个位。比如:00010100b表示14。
编程,在屏幕中间显示当前的月份。
assume cs:code
code segment
start:
mov al,8
out 70h,al
in al,71h
mov ah,al
mov cl,4
shr ah,cl
and al,00001111b
add al,30h
add ah,30h
mov bx,0b800h
mov es,bx
mov byte ptr es:[160*12+40*2],ah
mov byte ptr es:[160*12+40*2+2],al
mov ax,4c00h
int 21h
code ends
end start
实验十四
编程,以“年/月/日 时:分:秒”的格式,显示当前的日期、时间。
assume cs:code
stack segment
db 16 dup (0)
stack ends
code segment
start:
mov ax,0b800h
mov es,ax
mov si,160*12+33*2
mov ax,9
mov cx,3
s:
push cx
push ax
out 70h,al
in al,71h
mov ah,al
mov cl,4
shr ah,cl
and al,00001111b
add ah,30h
add al,30h
mov es:[si],ah
add si,2
mov es:[si],al
add si,2
pop ax
pop cx
cmp ax,7
je over
mov byte ptr es:[si],'/'
add si,2
sub ax,1
loop s
over:
mov byte ptr es:[si],' '
add si,2
mov cx,3
mov ax,4
s0:
push cx
push ax
mov dx,cx
out 70h,al
in al,71h
mov ah,al
mov cx,4
shr ah,cl
and al,00001111b
add ah,30h
add al,30h
mov es:[si],ah
add si,2
mov es:[si],al
add si,2
pop ax
pop cx
cmp ax,0
je over0
mov byte ptr es:[si],':'
add si,2
sub ax,2
loop s0
over0:
mov ax,4c00h
int 21h
code ends
end start
第十五章 外中断
要及时处理外设的输入,就要解决两个问题:外设的输入随时可能发生,CPU如何得知?CPU从何处得到外设的输入?
15.1 接口芯片和端口
外设的输入与输出(诸如CPU的控制信息)不直接送入内存和CPU,而是送入相应的接口芯片的端口中;CPU的输入与输出也是送到该芯片的端口中。
可见,CPU通过端口和外部设备进行联系。
15.2 外中断信息
CPU如何知道外设随时可能发给它的信息呢?提供中断机制来满足这种需要。这种中断信息来自外部,由相关的芯片给向CPU发出相应的中断信息。CPU在执行完当前的指令后,可以检测到发送过来的中断信息,引发中断过程,处理外设的输入。
在PC系统中,外中断源一共有以下两类:
- 可屏蔽中断
- 不可屏蔽中断
可屏蔽中断是CPU可以不响应的外中断,具体响应与否要看标志寄存器·的IF位的设置,当CPU检测到可屏蔽中断信息时,如果IF=1,则CPU在执行完当前指令后响应中断,引发中断过程;如果IF=0,则不响应可屏蔽中断。
可屏蔽中断所引发的中断过程,除了在取得中断码的实现上有所不同外,基本上和内中断的中断过程相同。外部中断的中断码通过数据总线送入CPU;而内中断的中断类型码是在CPU内部产生的。
在中断过程中将IF设置为0的原因就是在进入中断处理程序后,禁止其他的可屏蔽中断。
当然,如果在处理程序中需要处理可屏蔽中断,可以用指令将IF置1。8086CPU提供的设置IF的指令如下:
sti ;设置IF=1
cli ;设置IF=0
不可屏蔽中断是CPU必须响应的外中断。当检测到此类中断信息时,则在执行完当前指令后,立即响应,引发中断过程。
对于8086CPU,不可中断的中断类型码固定为2,所以中断过程中,不需要取中断类型码。则不可屏蔽中断的中断过程为:
- 标志寄存器入栈,IF=0,IF=0;
- CS、IP入栈;
- (IP)=(8),(CS)=(0AH)。
几乎所有由外设引发的外中断,都是可屏蔽中断。
15.3 PC机键盘的处理过程
键盘输入: 键盘上的每一个键相当于一个开关,键盘中有一个芯片对键盘上的每一个键的开关状态进行扫描。
按下一个键时,开关接通,该芯片产生一个扫描码,说明了按下的键的位置,扫描码被送入主板上相关接口芯片的寄存器中,该寄存器的端口地址为60h。这个扫描码被称为通码。
松开按下的键时,产生断码,也被送入相同的端口地址中。
扫描码的长度为一个字节,通码的第7为0,断码的第7位为1,即:
断码 = 通码 + 80h
引发9号中断: 键盘的输入到达60h端口时,芯片向CPU发出中断类型码为9的中断信息。CPU检测中断信息后,看IF的值,是否响应。若响应,执行int 9中断例程。
执行int 9中断例程: 由BIOS提供int 9中断例程,用来进行基本的键盘输入处理,主要的工作如下:
- 读出60h端口的扫描码;
- 如果是字符键的扫描码,将该码和它所对应的字符码(ASCII码)送入内存中的BIOS键盘缓冲区;如果是控制键(如Ctrl)和切换键(如CapsLock)的码,则将其转变为状态字节写入内存中存储状态字节的单元;
- 对键盘系统进行相关的控制,比如,向相关芯片发出应答信息。
BIOS键盘缓冲区是系统启动后,BIOS用于存放int 9中断例程所接收的键盘输入的内存区。该内存区可以存储15个键盘输入,因为int 9中断例程除了接收扫描码外,还要产生和码对应的字符码,所以在BIOS键盘缓冲区中,一个键盘输入用一个字单元存放,高位字节存放扫描码,低位字节存放字符码。
0040:17单元存储键盘状态字节,记录了控制键和切换键的状态。
15.4 编写int 9中断例程
编程:在屏幕中间依次显示“a"~“z”,并可以让人看清,在显示的过程中,按下Esc键后。改变显示的颜色。
assume cs:code
stack segment
db 128 dup (0)
stack ends
data segment
dw 0,0
data ends
code segment
start:
mov ax,stack
mov ss,ax
mov sp,128
mov ax,data
mov ds,ax
mov ax,0
mov es,ax
push es:[9*4]
pop ds:[0]
push es:[9*4+2]
pop ds:[2] ;将原来的int 9中断例程入口地址保存
mov word ptr es:[9*4],offset int9
mov es:[9*4+2],cs ;设置新的中断地址
mov ax,0b800h
mov es,ax
mov ah,'a'
s:
mov es:[160*12+40*2],ah
call delay
inc ah
cmp ah,'z'
jna s
mov ax,4c00h
int 21h
delay:
push ax
push dx
mov dx,10h
mov ax,0
s1:
sub ax,1
sbb dx,0
cmp ax,0
jne s1
cmp dx,0
jne s1
pop dx
pop ax
ret
;---------------以下为新的int 9中断例程-----------------
int9:
push ax
push bx
push es
int al,60h
pushf
;pushf
;pop bx
;and bh,11111100b
;push bx
;popf
call dword ptr ds:[0]
cmp al,1
jne int9ret
mov ax,0b800h
mov es,ax
inc byte ptr es:[160*12+40*2+1]
int9ret:
pop es
pop bx
pop ax
iret
code ends
end start
15.5 安装新的int 9中断例程
任务:安装一个新的int 9中断例程。
功能:在DOS下,按F1键后改变当前屏幕的显示颜色。
assume cs:code
stack segment
db 128 dup (0)
stack ends
code segment
start:
mov ax,stack
mov ss,ax
mov sp,128
mov ax,0
mov ds,ax
push ds:[9*4]
pop ds:[200h]
push ds:[9*4+2]
pop ds:[202h]
;安装
mov ax,cs
mov ds,ax
mov si,offset int9
mov ax,0
mov es,ax
mov di,204h
mov cx,offset int9end-offset int9
cld
rep movsb
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
mov ax,0
mov ds,ax
in al,60h
pushf
call dword ptr ds:[200h]
cmp al,3bh
jne int9over
mov ax,0b800h
mov es,ax
mov bx,1
mov cx,2000
s:
inc byte ptr es:[bx]
add bx,2
loop s
int9over:
pop cx
pop bx
pop es
pop ax
iret
int9end:
nop
code ends
end start
et:
pop es
pop bx
pop ax
iret
code ends
end start