书籍电子版 提取码: b62a
第13章:int指令
CPU的中断可以来自内部和外部。
int就是一种重要的内中断
int指令
int n
- 取中断类型码n
- 标志寄存器入栈,TF、IF设置为0
- CS、IP入栈
- IP = 1*n,CS = 1*n+2
编写供应用程序调用的中断例程
1. 求一个数的平方乘2
assume cs:code
code segment
start: mov ax, 0
mov es, ax
mov di, 200h
mov ax, cs
mov ds, ax
mov si, offset sqr
mov cx, offset sqrend-offset sqr
cld
rep movsb
mov ax, 0
mov es, ax
mov word ptr es:[7ch*4], 200h
mov word ptr es:[7ch*4+2], 0
mov ax, 3456
int 7ch
add ax, ax
adc dx, dx
mov ax, 4c00h
int 21h
sqr: mul ax
iret
sqrend: nop
code ends
end start
将求平方的程序写在0:200内存处,再将程序的入口地址放在7ch表项中,使其成为中断7ch的中断例程。
2.将内存地址上的字母小写改大写
assume cs:code, ds:data
data segment
db 'conversation',0
data ends
code segment
start:
mov ax, 0
mov es, ax
mov di, 200h
mov ax, cs
mov ds, ax
mov si, offset interrupt
mov cx, offset interruptend - offset interrupt
cld
rep movsb
mov ax, 0
mov es, ax
mov word ptr es:[7ch*4], 200h
mov word ptr es:[7ch*4+2], 0
mov ax, data
mov ds, ax
mov si, 0
int 7ch
mov ax, 4c00h
int 21h
interrupt:
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
interruptend:
nop
code ends
end start
对int、iret和栈的深入理解
用7ch完成loop指令的功能
assume cs:code
code segment
start:
mov ax, 0
mov es, ax
mov di, 200h
mov ax, cs
mov ds, ax
mov si, offset lp
mov cx, offset lpend - offset lp
cld
rep movsb
mov ax, 0
mov es, ax
mov word ptr es:[7ch*4], 200h
mov word ptr es:[7ch*4 + 2], 0
mov ax, 1800h
mov es, ax
mov di, 0
mov bx, offset s - offset se
mov cx, 80
s:
mov byte ptr es:[di], '!'
inc di
int 7ch
se: nop
mov ax, 4c00h
int 21h
lp:
mov bp, sp
dec cx
jcxz lpret
add ss:[bp], bx
lpret:
iret
lpend:
nop
code ends
end start
可以推测:可以转移的最大位移应该bx的最大值:FFFFH
用7ch完成jmp near ptr s指令
assume cs:code
code segment
start:
mov ax, 0
mov es, ax
mov di, 200h
mov ax, cs
mov ds, ax
mov si, offset jps
mov cx, offset jpend - offset jps
cld
rep movsb
mov ax, 0
mov es, ax
mov word ptr es:[7ch*4], 200h
mov word ptr es:[7ch*4+2], 0
mov bx, offset e - offset s
int 7ch
s: nop
mov ax, 3
add ax, ax
add ax, ax
add ax, ax
add ax, ax
add ax, ax
add ax, ax
e: nop
mov ax, 4c00h
int 21h
jps:
push bp
mov bp, sp
add [bp+2], bx
jlret:
pop bp
iret
jpend: nop
code ends
end start
BIOS和DOS所提供的中断例程
在系统板的ROM中存放着一套程序:BIOS
- 硬件系统的检测和初始化程序
- 外部中断和内部中断例程
- 用于对硬件设备进行I/O操作的中断例程
- 其他和硬件系统相关的中断例程
操作系统DOS也提供了中断例程。DOS就是操作系统向程序员提供的编程资源。
BIOS和DOS的安装过程
BIOS中断例程应用
DOS中断例程应用
int 21h中断例程是DOS提供的中断例程,其中包含了DOS提供给程序员在编程时调用的子程序。
mov ah, 4ch
mov al, 0 ;返回值
int 21h
ah = 4ch表示调用第21h号中断例程的4ch号子程序,功能为程序返回,可以提供返回值作为参数。这里的返回值就是0。
assume cs:code, ds:data
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
实验13编写、应用中断例程
第14章:端口
各种存储器都和CPU的三大总线相连,CPU在操控它们的时候,都把它们当作内存来看待。把它们总地看做一个由若干存储单元组成的逻辑存储器,这个逻辑存储器我们称其为内存地址空间。
和CPU相连的芯片除各种存储器外,还有以下三种:
- 各种接口卡上的接口芯片,它们控制接口卡进行工作
- 主板上的接口芯片,CPU通过它们对部分外设进行访问
- 其他芯片,用来存储相关系统信息,或进行相关的输入输出处理
在这些芯片中,都 有一组可以由CPU读写的寄存器,在物理上,它们可能处于不同的芯片中,但是在以下两点上相同:
- 都和CPU的总线相连,
- CPU对它们进行或写的进候都通过控制线向它们所在的芯片发出端口读写命令
从CPU角度来说,CPU把这些寄存器都当作端口,对它们进行了统一编址,建立了一个统一的地址空间。每一个端口在地址空间中都有一个地址。
CPU可以直接读写以下3个地方的数据:
- CPU内部的寄存器
- 内存单元
- 端口
端口的读写
端口和内存地址一样,都通过地址总线来传送。在PC系统中,CPU最多可以定位64KB个不同的端口。则端口的地址范围为0~65535
-
读端口
in al, 60h
从60h号端口读入一个字节
-
写端口
out 60h, al
往60h号端口写入一个字节
可见这两个指令与其它的指令一样,将最终的目地的紧跟在指令的后面。比如读,读到哪里?读到al。写,写到哪里?写到60h。
in和out指令只能用al和ax来操作,前者8位,后者16位。
CMOS RAM芯片
检测点14.1
shl和shr指令
shl左移
shr右移
位移最后移出的一位写入CF中,低位用0补充
mov al, 01001000b
shl al, 1 ;左移一位
如果移动的位数大于1,将1换成cl。
mov al, 01010001b
mov cl, 3
shl al, cl
执行后:al = 10001000b
检测点14.2
CMOS RAM中存储的时间信息
assume cs:code
data segment
db '/','/',' ',':',':'
data ends
code segment
date: db 9, 8, 7, 4, 2, 0
start:
mov cx, offset start - offset date ;共6个内容
mov bx, offset date
mov di, 160*14
mov si, 0
mov ax, data
mov ds, ax
s:
mov al, cs:[bx]
call invoke
inc bx
loop s
mov ax, 4c00h
int 21h
invoke:
push cx
push ax
push bx
out 70h, al
in al, 71h
mov ah, al ;将8位值给ah
mov cl, 4
shr ah, cl ;右移4位
and al, 00001111b ;高位置0
add ah, 30h
add al, 30h ;加上48(30h)就是对应的数字的ASCII码
mov bx, 0b800h
mov es, bx
mov byte ptr es:[di], ah ;十位
inc di
mov byte ptr es:[di], 02 ;颜色
inc di
mov byte ptr es:[di], al ;个位
inc di
mov byte ptr es:[di], 02
inc di
;填符号
mov dl, ds:[si]
mov byte ptr es:[di], dl
inc di
mov byte ptr es:[di], 02
inc di
inc si
pop bx
pop ax
pop cx
ret
code ends
end start
重点BCD码的转换:
两个BCD码是一个字节,高位是高位数据,低位是低位数据。比如:不超过两位的十进制数,用BCD码来表示十位用高四位,个位用低四位。
mov ah, al
mov cl, 4
shr ah, cl ;执行完al的高四位在ah的低四位
and al, 00001111b
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] ;保存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, 0
mov es, ax
;向量表归位
push ds:[0]
pop es:[9*4]
push ds:[2]
pop es:[9*4+2]
mov ax, 4c00h
int 21h
;延时程序,根据自己CPU来确定循环次数
delay:
push ax
push dx
mov dx, 5h
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
;更新的9号中断
int9:
push ax
push bx
push es
in al, 60h ;读取端口数据
;模拟int,因为int9在向量表中变成别的,原来的在ds[2]:ds[0]中
pushf
pushf
pop bx
and bh, 11111100b
push bx
popf
call dword ptr ds:[0]
;是1就改变颜色,不是就跳出
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
实验14访问CMOS RAM
第15章:外中断
CPU除了可以执行指令外,还可以对外部设备进行控制,接收输入,进行输出。
比如, 按下键盘上的一个键,CPU最终要处理这个键,在使用编辑器时,按下键后,对应键上的内容会显示在显示器上。
接口芯片和端口
外设的输入不直接送入内存和CPU,而是送入相关的接口芯片的端口中,CPU向外设的输出也不是直接送入外设,而是先送入端口中,再由相关的芯片送到外设。
端口在CPU传送过程中起到一个桥梁的作用。
外中断信息
外设的输入随时都有可能发生,CPU怎么及进知道并作出相应的处理呢?
CPU用中断实现了这种可能,原理上等同于原先的内中断, 但是这个中断信息是由外部产生的。
1. 可屏蔽中断
可屏蔽意味着有两种选择:
- IF = 1,响应中断
- IF = 0, 不响应中断
可屏蔽信息来自于CPU外部,中断类型码通过数据总线送入CPU。
sti,设置IF = 1
cli,设置IF = 0
IF置0的作用不言而喻,在进入中断处理程序后,禁止其他可屏蔽中断。
2. 不可屏蔽中断
是CPU必须响应的中断,CPU执行完当前指令后,立即响应,引发中断过程。
对于8086CPU中断类型码是固定的2,所以中断过程中, 不需要取中断类型码。
几乎所有处设引发的中断都是可屏蔽中断。
PC机键盘的处理过程
1. 键盘输入
键盘上的每一个键都相当于一个开发,键盘中会有一个芯片对键盘上每一个键的开关状态进行扫描。当你按下一个键或松开一个键时都会产生一个扫描码,然后扫描码被送入相关接口芯片的寄存器中,该寄存器的端口地址为60h。
按下一个键产生的扫描码称为通码
松开一个键产生的扫描码称为断码
扫描码的长度为一个字节,通码的第7位为0,断码的第7位为1
所以它们之间相差2^7。也就是80h。
断码 = 通码 + 80h
2. 引发9号中断
3. 执行9号中断例程
BIOS提供了int 9中断例程
编写int 9中断例程
- 键盘产生扫描码
- 扫描码送入60上端口
- 引发9号中断
- 执行int 9中断例程处理键盘输入
1,2, 3步都是由硬件完成。所以只能改变第4步的中断处理程序。
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] ;保存9号中断
cli
mov word ptr es:[9*4], offset int9
mov es:[9*4+2], cs ;更改向量表,设置int 9中断例程的段地址和偏移地址
sti
;显示程序
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, 0
mov es, ax
cli
;向量表归位
push ds:[0]
pop es:[9*4]
push ds:[2]
pop es:[9*4+2]
sti
mov ax, 4c00h
int 21h
;延时程序,根据自己CPU来确定循环次数
delay:
push ax
push dx
mov dx, 5h
mov ax, 0
;循环50000h次
s1:
sub ax, 1
sbb dx, 0
cmp ax, 0
jne s1
cmp dx, 0
jne s1
pop dx
pop ax
ret
;更新的9号中断
int9:
push ax
push bx
push es
in al, 60h ;读取端口数据
pushf
call dword ptr ds:[0]
;是1就改变颜色,不是就跳出
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
改变中断的我们能做的就只是改变它的向量表。也可以用模拟的手段模拟一个中断。