今天主要总结int指令所引起的中断,int的一般用法是int n,这个n就是指中断类型码
如果对中断概念一点没有请进入: https://blog.csdn.net/qq_37232329/article/details/79876197
废话不多说先贴第一个程序,这个例子是王爽老师的《汇编语言》第三版p253页13.2上的,极品8086汇编教程,值得购买:)但这个例子在书上是分离的,如果是和我一样用DOSBox来模拟16位环境那么这个例子没办法运行,所以我把它组合了一下:
assume cs:code
code segment
start:
;安装中断例程
mov ax, 0
mov es, ax
mov ax, cs
mov ds, ax
mov si, offset intstart
mov di, 200h
mov cx, offset intend - intstart
cld
rep movsb
;把中断例程入口放入中断向量表中的7ch位置
mov word ptr es:[7ch * 4], 200h ;
mov word ptr es:[7ch * 4 + 2], 0
int 7ch ;引发中断例程,即在中断向量表中找到中断例程入口
mov ax, 4c00h
int 21h
;这里是7ch中断例程
intstart:
mov ax, 3456
mul ax
add ax, ax
adc dx, dx
iret ;跳转回去之前还会有cs,ip出栈,标志寄存器出栈的过程,这是和ret(f)的区别
intend:
nop
code ends
end start
我真的不大喜欢打注释,因为我觉得会影响美观.... 对审美极度洁癖,但是为了方便我还是打了,为自己的良心感到抱歉。
这里注释说的很清楚了,稍稍说下,这里主要分三段中断例程的代码,中断例程的安装和中断例程的入口放入中断向量表。
在提醒一下iret == $(pop IP; pop CS; popf),popf不知道就别看这个了。
继续贴,同样我也把分开的组合起来的:
assume cs:code
code segment
start:
;安装中断例程
mov ax, 0
mov es, ax
mov di, 200h
mov ax, cs
mov ds, ax
mov si, offset intgo ;中断例程的代码开始点
mov cx, offset intend - offset intgo ;中断例程的代码条数
cld ;正向
rep movsb ;这不是sb这是movsb,以字节为单位不断传输
;把中断例程入口放入中断向量表
mov word ptr es:[7ch * 4], 200h
mov word ptr es:[7ch * 4 + 2], 0
int 7ch ;引发7ch号中断例程
mov ax, 4c00h
int 21h
;中断例程代码
intgo: ;有这个的目的是为了不让这串字符串被其他覆盖,所以放置在中断例程里
jmp short intstart
db 'welcome to masm!', 0
intstart:
mov si, 202h ;跳过jmp short intstart这句,本来偏移位置在200h的
inLoop: ;这个看不明白说明前面基础不好,这里是小写变大写的过程
mov cl, es:[si]
mov ch, 0
jcxz back
;下面四句为了防止空格和感叹号被按位和
cmp cl, 97
jb notAlpha
cmp cl, 122
ja notAlpha
and cl, 11011111b
mov es:[si], cl
notAlpha:
inc si
jmp short inLoop
back:
iret
intend:
nop
code ends
end start
到这里,总结一下,这两个例子的都用了7ch这一个中断类型码,而且代码类型显然换汤不换药都是类似的,都是那么三段
接下来这个例子书上没有写全,在256页上,我把这个代码补全了稍稍改了下可以运行,可对照书看:
assume cs:code
code segment
start:
;这段没什么好说的,就是安装中断例程的过程
mov ax, 0
mov es, ax
mov ax, cs
mov ds, ax
mov di, 200h
mov si, offset lp
mov cx, offset lpend - offset lp
cld
rep movsb
;把中断例程入口放入中断向量表
mov word ptr es:[7ch * 4], 200h
mov word ptr es:[7ch * 4 + 2], 0
;一些准备工作
mov ax, 0b800h
mov es, ax
mov di, 160 * 12
mov bx, offset s - offset se
mov cx, 80
s: ;int 7ch执行后会跳到这里
mov byte ptr es:[di], '?'
mov byte ptr es:[di + 1], 2
add di, 2
int 7ch ;int 7ch在这里执行的是loop的功能
se:
nop
;中断例程代码
lp:
push bp
mov bp, sp ;这里建议看一下我另外一篇博客-->x86汇编的高级过程
dec cx
jcxz lpret
add [bp + 2], bx ;这边代码下面的文字会详细说
lpret:
pop bp
iret
lpend: ;这里的主要目的是为了方便截取这整段代码即:offset lpend - offset lp
nop
code ends
end start
我个人认为这段代码主要要注意两个地方,首先不要关注这段代码的意思,而是关注整体。书上代码给的就一部分,主要的一段是这样的:
lp:
push bp
mov bp, sp
dec cx
jcxz lpret
add [bp + 2], bx
lpret:
pop bp
iret
很明显,这是7ch中断例程的代码也就是loop功能的实现,可以和我更改后的代码作比较,如下:
lp:
push bp
mov bp, sp
dec cx
jcxz lpret
add [bp + 2], bx
lpret:
pop bp
iret
lpend:
nop
唯一的差别就是我下面多了lpend段,一开始看这本书的时候我也是直接把树上的代码直接上去了,然后安装时的cx表示成了offset lpret - offset lp,但是debug调试的时候,我总是发现后面没代码了,就像这样:
在add [BP + 02], BX后就没了,这是为什么,我那时候一直没弄明白,后来我才发现了是因为offset lpret - offset lp并没有把后面的pop bp和iret囊括进去,于是为了解决这个问题就在后面又加了一个段以此来囊括整个中断代码。
结果:
就成功了。
第二个要注意的地方是这里:
lp:
push bp
mov bp, sp
dec cx
jcxz lpret
add [bp + 2], bx ;这里,哥
lpret:
pop bp
iret
这句是什么意思? 首先我们得看看关于这个int到底是怎么回事:
int 7ch执行后它干了这么几件事情:
1.pushf
2.IF=0 TF=0
3.push cs; push ip
4.(IP)=[7ch * 4], (CS)=[7ch * 4 + 2]
也就是说栈中的情况应该是这样子的:
--------------------------->地址变大方向
我们知道bp,ip之类的是16位寄存器所以挪动以此要加2字节,BP前面已经被赋值为SP了,所以[BP + 2]就相当于[SP + 2],也就是在IP的位置,这个ip就是中断例程程序结束后跳回的位置也就是int 7ch下一条语句的偏移地址,而我们用它加了BX,BX又是什么? BX是mov bx, offset s - offset se,注意这个结果是一个负数,所以add [bp +2], bx其实就是ss:[bp+2]内存中的值减去了offset s - offset se这段偏移地址的差,结果正好就是s段的起始!也就实现了loop的功能!
x32汇编高级过程: http://......
有了这个基础之后我们来实现jmp near ptr的功能,我们知道jmp short ptr是短跳转,最多就8位也就是-128~127,正向最远127字节,负向最远128字节,而loop也相当于短跳转,现在是段间跳转的中断形式,也就是16位-32768~32767。贴代码:
assume cs:code
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 nearp
mov cx, offset nearpend - offset nearp
cld
rep movsb
;将中断例程入口放入中断向量表
mov word ptr es:[7ch * 4], 200h
mov word ptr es:[7ch * 4 + 2], 0
;将字符投入显存中即B8000~B8FFF这32KB的大小
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
jne no ;因为jne也是属于短跳转没办法逾越127所以要下面那句来辅助跳转至ok
jmp near ptr ok ;辅助跳转语句
no:
mov al, [si]
mov es:[di], al
inc si
add di, 2
mov bx, offset s - offset ok
db 128 dup (0) ;为了打肿脸充胖子强行插入的128字节
int 7ch
ok:
mov ax, 4c00h
int 21h
;中断例程代码
nearp:
push bp
mov bp, sp
add [bp + 2], bx
pop bp
iret
nearpend:
nop
code ends
end start
有了前面的基础,看这里应该不会很困难,唯一需要注意的就是因为里面插入了128字节所以je(书上是用je)没办法使用,所以这里用jne外加一个段间转移来替代
------------------------------------------------------------分割线--------------------------------------------------------------
下面介绍的是关于DOS和BIOS所提供的中断例程:
关于BIOS的作用请看:
https://blog.csdn.net/qq_37232329/article/details/79845307
https://blog.csdn.net/qq_37232329/article/details/79867463
BIOS主要提供外中断,内中断等中断例程以及对硬件IO操作的中断例程总之就是与硬件相关的中断例程
BIOS与DOS中断例程的安装过程:
1.开机后CPU加电便初始化0FFFFH:0,从这个地址执行程序,0FFFF:0处有一条跳转指令,CPU执行后便去执行BIOS中的硬件系统检测与初始化程序
2.初始化程序建立BIOS所支持的中断向量,即把BIOS提供的中断例程入口登记在中断向量表中,这些是固化在ROM中的一直存在于内存(如果BIOS坏掉了,计算机没办法打开操作系统)
3.硬件系统自检加电和初始化完成后便调用INT 19H进行操作系统的引导,即BIOS的工作已经完成,把控制权交给了优先权最高的启动设备的启动程序,一般都是第一个扇区。
4.DOS启动后,除了完成其他工作,还把它所提供的中断例程装入内存并建立相应的中断向量
注意: BIOS与DOS所提供的中断例程都是以ah来传递内部子程序编号的
BIOS提供的中断例程:int 10h中的2号与9号,上代码:
assume cs:code
code segment
start:
mov ah, 2 ;2号子程序,作用是放置光标位置
mov bh, 0 ;0页
mov dh, 5 ;5行
mov dl, 12 ;12列
int 10h
mov ah, 9 ;9号子程序,作用是显示字符
mov al, 'a' ;al放置要显示的字符
mov bl, 11001010b ;bl放置颜色属性
mov bh, 0 ;页码
mov cx, 3 ;重复的个数
int 10h
mov ax, 4c00h
int 21h
code ends
end start
颜色属性:
DOS提供的中断例程:int 21h中的9号与4ch号,上代码:
assume cs:code
data segment
db 'Welcome to masm', '$'
data ends
code segment
start:
mov ah, 2 ;2号
mov bh, 0 ;页
mov dh, 5 ;行
mov dl, 12 ;列
int 10h ;这是BIOS提供的中断例程
mov ax, data
mov ds, ax ;段地址
mov dx, 0 ;首地址偏移地址即ds:dx
mov ah, 9 ;9号
int 21h ;DOS提供的中断例程作用是显示字符
mov ax, 4c00h ;ah=4ch,4ch号功能是程序返回,即返回值为al
int 21h ;;DOS提供的中断例程作用是程序返回
code ends
end start
上面两个无需解释
那些实验其实就是要求写出前面例子的完整表达(书上是不全的,只给了一小部分), 上面代码已经贴出
(完)
接下去的外中断篇复习总结传送门:https://blog.csdn.net/qq_37232329/article/details/79922994