《汇编语言》- 读书笔记 - 第13章-int 指令
- 13.1 int 指令
- 13.2 编写供应用程序调用的中断例程
- 中断例程:求一 word 型数据的平方
- 主程序
- 中断处理程序
- 执行效果
- 中断例程:将一个全是字母,以0结尾的字符串,转化为大写
- 主程序
- 中断处理程序
- 执行效果
- 13.3 对 int、iret 和栈的深入理解
- 主程序
- 中断处理程序
- 分析
- 执行效果
- 检测点 13.1
- 13.4 BIOS 和 DOS 所提供的中断例程
- 13.5 BIOS 和 DOS 中断例程的安装过程
- BIOS中断例程的安装:
- DOS中断例程的安装:
- 13.6 BIOS 中断例程应用
- 功能 02H 设置光标位置
- 功能 09H 在光标位置显示字符
- 13.7 DOS 中断例程应用(INT 21H)
- 功能 4CH:返回码方式的终止进程
- 功能 09H:输出一个字符串到标准输出设备上
- 实验 13 编写、应用中断例程
- 参考资料
13.1 int 指令
int n
指令的功能是:引发 n
号中断过程,从而执行 n号中断处理程序
。
系统将一些具有一定功能的子程序,以中断处理程序
的方式提供给应用程序
调用。
我们在编程的时候,可以用 int 指令
调用这些子程序。
当然,也可以自己编写些中断处理程序供别人使用。
以后,我们可以将中断处理程序
简称为中断例程
。
13.2 编写供应用程序调用的中断例程
中断例程:求一 word 型数据的平方
问题一 | 编写、安装中断 7ch 的中断例程。 |
---|---|
功能 | 求一 word 型数据的平方。 |
参数 | (ax )=要计算的数据。 |
返回 | dx 、ax 中存放结果的高 16 位和低 16 位。 |
应用举例 | 求 2*3456^2 = 16C8000h = 23,887,872 |
主程序
assume cs:code
code segment
start: mov ax,3456 ; (ax)=3456
int 7ch ; 调用中断 7ch 的中断例程,计算ax中的数据的平方
; dx:ax 存放结果,将结果乘以2
add ax,ax ; 先加低位
adc dx,dx ; 再加高位(这里用了进位加法)
; 23887872
mov ax,4c00h
int 21h
code ends
end start
中断处理程序
assume cs:code
code segment
start: ; ====================== 安装 ====================
; ------------------- 复制数据 -------------------
mov ax,cs
mov ds,ax
mov si,offset sqr ;设置 ds:si 指向源地址
mov ax,0
mov es,ax
mov di,200h ;设置 es:di 指向目的地址
mov cx,offset sqrend-offset sqr ;设置 cx为传输长度
cld ;设置传输方向为正。movsb中si,di递增
rep movsb ;重复复制数据次数由 cx 控制
; ------------------- 复制数据 -------------------
; ----------------- 设置中断向量 -----------------
mov ax,0
mov es,ax ; 段地址 0
mov word ptr es:[7ch*4],200h ; 设置【中断处理程序】的:偏移地址
mov word ptr es:[7ch*4+2],0 ; 设置【中断处理程序】的:段地址
; ----------------- 设置中断向量 -----------------
; ====================== 安装 ====================
mov ax, 4c00H ; 退出
int 21H
; =======================================================
; --------------------- 中断处理程序 --------------------
; ax 的平方
; -------------------------------------------------------
; 参数:ax 要计算的数据。
; 返回:dx、ax 中存放结果的高 16 位和低 16 位。
; -------------------------------------------------------
sqr: mul ax ; ax * ax 结果 dx=高16位,ax=低16位
iret ; 返回原程序。 pop IP, pop CS, popf
sqrend: nop
; --------------------- 中断处理程序 --------------------
; =======================================================
code ends
end start
- 安装功能跟之前都是一样的,只是改下值就行了。
- 中断处理程序也很简单就是一个乘法就返回了。
执行效果
中断例程:将一个全是字母,以0结尾的字符串,转化为大写
问题二 | 编写、安装中断 7ch 的中断例程。 |
---|---|
功能 | 将一个全是字母,以0结尾的字符串,转化为大写。 |
参数 | ds:si 指向字符串的首地址。 |
应用举例 | 将 data 段中的字符串转化为大写 |
主程序
assume cs:code
data segment
db 'conversation',0
data ends
code segment
start: mov ax,data
mov ds,ax
mov si,0
int 7ch
mov ax,4c00h
int 21h
code ends
end start
中断处理程序
assume cs:code
code segment
start: ; ====================== 安装 ====================
; ------------------- 复制数据 -------------------
mov ax,cs
mov ds,ax
mov si,offset capital ;设置 ds:si 指向源地址
mov ax,0
mov es,ax
mov di,200h ;设置 es:di 指向目的地址
mov cx,offset capitalend-offset capital ;设置 cx为传输长度
cld ;设置传输方向为正。movsb中si,di递增
rep movsb ;重复复制数据次数由 cx 控制
; ---------------- 安装(复制数据) ----------------
; ----------------- 设置中断向量 -----------------
mov ax,0
mov es,ax ; 段地址 0
mov word ptr es:[7ch*4],200h ; 设置【中断处理程序】的:偏移地址
mov word ptr es:[7ch*4+2],0 ; 设置【中断处理程序】的:段地址
; ----------------- 设置中断向量 -----------------
; ====================== 安装 ====================
mov ax, 4c00H ; 退出
int 21H
; =======================================================
; --------------------- 中断处理程序 --------------------
; 将一个全是字母,以0结尾的字符串,转化为大写。
; -------------------------------------------------------
; 参数:ds:si 指向字符串的首地址。
; -------------------------------------------------------
capital: push cx ; 备份寄存器
push si
change: mov cl,[si] ; 取字符
mov ch,0 ; 高位不参与计算,置 0
jcxz ok ; 如果取到 0 跳到 ok 循环结束
and byte ptr [si],11011111b ; 否则:字符转大写
inc si ; si++ 指向下一字符
jmp short change; 跳到开头继续循环
ok: pop si ; 还原寄存器
pop cx
iret ; 返回原程序。 pop IP, pop CS, popf
capitalend: nop
; --------------------- 中断处理程序 --------------------
; =======================================================
code ends
end start
没什么新知识点,都是之前学过的:安装、遍历字符串、字符转大写。
执行效果
13.3 对 int、iret 和栈的深入理解
问题 | 用 7ch 中断例程完成 loop 指令的功能 (模拟 loop s ) |
---|---|
参数 | bx 存 s 到 se 之间的位移 cx 循环次数 |
返回 | 无 |
应用举例 | 在屏幕中间显示80个“!” |
主程序
assume cs:code
code segment
start: mov ax,0b800h
mov es,ax
mov di,160*12 ; 从第16向第一个字符开始(正好一行80个字符)
mov bx,offset s-offset se ; 参数:se到s的【位移】= -9
mov cx,80 ; 参数:循环80次
; ES: 当前偏移量 000Eh = 23 + (-9) = 14
s: mov byte ptr es:[di],'!' ;
add di,2
int 7ch ; 实现 loop s 效果
se: nop ; 当前偏移量 0017h = 23
mov ax,4c00h
int 21h
code ends
end start
- 从
s
到se
共 9 个字节。把它存进bx
,传给中断处理程序,
在中断处理程序中我们就是利用它来算出s
偏移量,来实现循环的。 - 从上面的截图可以看出
mov byte ptr es:[di],'!'
这一句被反编译成了两句。
2.1. 先是ES:
看上去是设置默认数据段
的意思 。
2.2. 因为第二句中MOV BYIE PIR [DI],21
中没有写段地址,只写了[DI]
中断处理程序
assume cs:code
code segment
start: ; ---------------- 安装(复制数据) ----------------
mov ax,cs
mov ds,ax
mov si,offset lp ;设置 ds:si 指向源地址
mov ax,0
mov es,ax
mov di,200h ;设置 es:di 指向目的地址
mov cx,offset lpend-offset lp ;设置 cx为传输长度
cld ;设置传输方向为正。movsb中si,di递增
rep movsb ;重复复制数据次数由 cx 控制
; ---------------- 安装(复制数据) ----------------
; ----------------- 设置中断向量 -----------------
mov ax,0
mov es,ax ; 段地址 0
mov word ptr es:[7ch*4],200h ; 设置【中断处理程序】的:偏移地址
mov word ptr es:[7ch*4+2],0 ; 设置【中断处理程序】的:段地址
; ----------------- 设置中断向量 -----------------
mov ax, 4c00H ; 退出
int 21H ; 触发33号中断
; =======================================================
; --------------------- 中断处理程序 --------------------
; 模拟 loop s 效果
; -------------------------------------------------------
; 参数:bx 下一句指令到 s 之间的位移
; 参数:cx-1 循环次数
; -------------------------------------------------------
lp: push bp ; 备份寄存器
mov bp,sp ; 取栈顶所指的内存位置
dec cx ; 计数 cx = cx-1
jcxz lpret ; cx 为 0 就跳出循环
add [bp+2],bx ; 分析:1
lpret: pop bp ; 还原寄存器
iret ; 分析:2 【重点】
lpend: nop
; --------------------- 中断处理程序 --------------------
; =======================================================
code ends
end start
分析
- 分析1. 上面我们从
sp
拿到了栈顶内存地址存到bp
。
这里我们就直接修改这段内存中的偏移量
了。当前栈中:[bp, ip, cs, flags]
(注意:这时的 bp 是栈顶,跟栈中备份的那个bp值可不是同一个东西了)
所以修改时使用的是[bp+2]
指向中断前备份的IP
。 - 分析2.
iret
是用来返回原程序,继续执行的。它会从栈中恢复中断前保存的寄存器状态,还原了CS:IP
就能实现继续执行中断前的下一条指令了。
pop IP
pop CS
popf
这里是本节的重点了:欺骗善良无知的iret
。
- 在中断触发前,
IP
指向了标号se
处。(在主程序中我已经计算出了se
到s
之间的偏移) 中断过程
中,会备份当前寄存器:pushf, push cs, push ip
。中断处理程序
中:我们在add [bp+2],bx ; 分析:1
这句修改了栈中备份的IP
的值。
所以iret
恢复后其实指向了标号s
,这也就实现了跳回去重新执行的效果。
执行效果
检测点 13.1
《汇编语言》- 读书笔记 - 检测点 13.1
13.4 BIOS 和 DOS 所提供的中断例程
BIOS
(基本输入输出系统)存储在系统板
的ROM
中,包含以下内容:硬件系统
的检测
和初始化
程序外部中断
和内部中断
的中断例程- 用于对
硬件
设备进行I/O操作
的中断例程 - 其他与
硬件
系统相关
的中断例程
- DOS操作系统同样提供了中断例程,这些例程是
操作系统
向程序员
提供的编程接口
。 - BIOS和DOS提供的中断例程包括许多子程序,它们实现了常见的编程功能。
程序员
可以通过int指令
直接调用
:BIOS
或DOS
的中断例程
来完成特定任务。- 与
硬件
设备相关
的DOS中断例程
通常会进一步调用BIOS的中断例程
以执行底层硬件操作。
13.5 BIOS 和 DOS 中断例程的安装过程
在8086架构的早期个人计算机系统中,BIOS和DOS中断例程的安装过程并非由用户程序直接执行安装,而是内置于系统的初始化过程中。以下是一个简化的概述:
BIOS中断例程的安装:
- 开机后,CPU 一加电,自动从
FFFF:0000H
地址处开始执行ROM
中的代码(即BIOS)。
(这个位置存放着一条跳转指令,它会跳转到ROM中的实际BIOS启动代码区域。) - BIOS首先进行硬件自检(POST, Power-On Self-Test),检测和初始化硬件设备。
- BIOS建立中断向量表,将自身的
中断处理程序
的入口地址
填入到中断向量表
对应的内存位置。
(只需要有登记入口地址,因为中断处理程序
本身已固化在BIOS的ROM中。)
DOS中断例程的安装:
- 硬件自检和初始化完成后,调用
int 19h
进行操作系统的引导。
1.1. 在BIOS
对int 19h
中断做出响应时,它首先从硬盘的第一个扇区(即主引导记录MBR)读取512字节的数据到内存特定位置(如0x7C00)。
1.2.MBR
中的前446
字节通常是机器码
构成的操作系统引导程序
(Boot Loader),该引导程序接着会被CPU执行。
1.3. 引导程序的任务是找到并加载实际的操作系统,例如MS-DOS或更现代的操作系统的核心文件,并将控制权转交给操作系统。 - 当DOS启动时,它会接管控制权,并且根据需要重定向某些中断向量,以便调用自己的中断例程来处理特定的服务请求。
- DOS通常会保留一部分中断向量用于调用原始的BIOS服务,同时提供额外的中断服务例程以支持文件操作、磁盘读写等功能。
总之,在8086环境下,BIOS和DOS中断例程的安装
是指在系统启动阶段由固件
和操作系统
自动完成中断向量表的填充和设置,而不是程序员手动安装的过程。
《汇编语言》- 读书笔记 - 检测点 13.2.
13.6 BIOS 中断例程应用
功能 02H 设置光标位置
INT 10H | 显示服务 Video Service |
---|---|
功能02H | 设置光标位置 |
参数 | AH =调用 02H 号子程序BH =显示页码DH =行(Y坐标)DL =列(X坐标) |
返回 | 无 |
assume cs:code
code segment
start: mov ah,2 ; 设置光标位置
mov bh,0 ; 显示页码,第0页
mov dh,5 ; dh 中放行号
mov dl,12 ; dl 中放列号
int 10h
ok: mov ax,4c00h
int 21h
code ends
end start
功能 09H 在光标位置显示字符
INT 10H | 显示服务 Video Service |
---|---|
功能09H | 在当前光标处按指定属性显示字符 |
参数 | AH =功能号 09H AL =字符内容BH =显示页码BL =颜色属性CX =字符重复个数 |
返回 | 无 |
assume cs:code
code segment
start: 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 ; 显示页码
mov cx,20 ; 字符重复个数
int 10h
ok: mov ax,4c00h
int 21h
code ends
end start
13.7 DOS 中断例程应用(INT 21H)
在8086架构下,int 21h
是DOS
操作系统为应用程序
提供的一种系统API
。
通过调用int 21h
并设置不同的AH
寄存器值,程序员可以请求执行多种操作,如:文件操作、设备I/O、内存管理等。
这些中断服务例程相当于DOS系统的函数库,使得应用程序能够与操作系统进行交互和访问底层资源。
功能 4CH:返回码方式的终止进程
INT 21H | DOS 中断例程 |
---|---|
功能 4CH | 终止程序的执行,并可返回一个代码 |
参数 | AH =功能号 4CH AL =返回值 |
返回 | 无 |
mov ah,4ch ; 调用 4ch 号功能
mov al,00h ; 返回值
int 21h
功能 09H:输出一个字符串到标准输出设备上
INT 21H | DOS 中断例程 |
---|---|
功能09H | 输出一个字符串到标准输出设备上。待显示的字符串以$ 作为其结束标志 |
参数 | AH =功能号09H DS:DX =待输出字符的地址 |
返回 | 无 |
assume cs:code
data segment
db 'Welcome to masm','$'
data ends
code segment
start: mov ah,2 ; 设置光标位置
mov bh,0 ; 第0页显示
mov dh,5 ; dh 中放行号
mov dl,12 ; dl 中放列号
int 10h
mov ax,data
mov ds,ax
mov dx,0 ;ds:dx指向字符串的首地址 data:0
mov ah,9
int 21h
ok: mov ax,4c00h
int 21h
code ends
end start
实验 13 编写、应用中断例程
《汇编语言》- 读书笔记 - 第13章-实验13编写、应用中断例程
参考资料
DOS 中断参考手册