汇编语言笔记13int指令
汇编语言笔记13int指令
0x00 int指令
int指令 可以引起中断,其格式为int n n即为对应中断类型码,中断过程如下:
(1)取中断类型码n
(2)标志寄存器入栈,IF=0 TF=0
(3)CS IP入栈
(4)IP=4 * N CS= 4* N+2
然后转入n号中断的中断处理程序,可以用int 指令调用任何一个中断的中断处理程序,例如:
assume cs:code
code segment
start: mov ax,0b800h
mov es,ax
mov byte ptr es:[12*160+4*2],'!'
int 0
code ends
end start

运行结果如图,第一个黑色的’!’是我们程序里写的 第二个绿字则是之前我们另一个程序安装的0号中断的中断处理程序显式的overflow,说明中断处理程序安装后没有随着原程序结束而释放,而且int 0指令成功调用了对应的中断处理程序
0x01 编写供应用程序调用的中断例程

一样的还是分为三个部分:(1)编写对应的中断处理程序 (2)写入内存固定地方(一般选0000:0200h) (3)设置中断向量表中对应的表项
assume cs:code,ss:stack
code segment
start: mov ax,cs
mov ds,ax
mov si,offset do7c
mov ax,0
mov es,ax
mov di,0200h
mov cx,offset do7c_end-offset do7c
cld
rep movsb
mov word ptr es:[7ch*4],0200h
mov word ptr es:[7ch*4+2],0;至此安装完成
mov ax,1000
int 7ch;调用
mov ax,4c00h
int 21h
do7c: mul ax
iret
do7c_end:nop
code ends
end start

上述代码运行正确,另外 我在上一篇笔记中 对iret的用法 认识错误 ,iret指令在中断处理程序结尾调用时,相当于执行 pop ip pop cs popf, 中断处理程序结尾用mov ax,4c00h int 21h 这种就直接返回到dos 不用再用iret

assume cs:code,ss:stack
stack segment
db 64 dup (0)
stack ends
data segment
db 'welcometomasm',0
data ends
code segment
start: mov ax,stack
mov ss,ax
mov sp,40h
mov ax,cs
mov ds,ax
mov si,offset do7c
mov ax,0
mov es,ax
mov di,200h
mov cx,offset do7c_e-offset do7c
cld
rep movsb
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
do7c: push si
push cx
s: mov cl,ds:[si]
mov ch,0
jcxz ok
and cl,11011111b
mov ds:[si],cl
inc si
jmp short s
ok: pop cx
pop si
iret
do7c_e: nop
code ends
end start

上述代码运行正确
0x02 对int iret和栈的深入理解

如果我们把loop 指令看成一个函数 那么这个函数的传入 参数有 cx(循环的次数)ds:si 跳转地址
ds不用传入 因为进入中断处理程序是push 标志寄存器 push cs push ip ds可以从栈中的cs里读,那么si 呢 我们可以设置标号 通过标号的偏移地址来读
assume cs:code
code segment
start: mov ax,cs
mov ds,ax
mov si,offset do7c
mov ax,0
mov es,ax
mov di,200h
mov cx,offset do7c_e-offset do7c
rep movsb
mov word ptr es:[7ch*4],200h
mov word ptr es:[7ch*4+2],0;安装好了
mov cx,80
mov ax,0b800h
mov ds,ax
mov si,0
s0: mov ds:[12*160+si],'!'
mov ds:[12*160+si+1],2
add si,2
int 7ch;如果cx 不等于0 跳转到s0 否则继续下去
s1: mov ax,4c00h
int 21h
do7c: push bp
push ax;栈顶分别是 ax bp ip cs flag
mov bp,sp
dec cx
cmp cx,0
je fin
mov ax,ss:[bp+4]
sub ax,offset s1-offset s0
mov ss:[bp+4],ax
fin: pop ax
pop bp
iret
do7c_e: nop
code ends
end start

上述代码运行正确,获得跳转的偏移地址很简单,因为iret 会pop ip pop cs popf 所以只要在iret之前改掉栈中原来保存的ip就好,改成多少呢? 原来ip 是指向s1的 我们要跳到s0 所以原ip要减小offset s1-offset s0


(1)上面跳转最大位移是max(offset s,ffffh-0ffset s),因为只能修改ip
(2)
assume cs:code
data segment
db 'conversation',0
code segment
start: mov ax,cs
mov ds,ax
mov si,offset do7c
mov ax,0
mov es,ax
mov di,200h
mov cx,offset do7c_e-offset do7c
rep movsb
mov word ptr es:[7ch*4],200h
mov word ptr es:[7ch*4+2],0;安装完成
mov ax,data
mov ds,ax
mov ax,0b800h
mov es,ax
mov si,0
mov di,0
s0: cmp byte ptr ds:[si],0
je ok
mov al,ds:[si]
mov es:[2000+di],al
mov byte ptr es:[2000+di+1],2
inc si
add di,2
mov bx,offset s0-offset ok
int 7ch;跳转到标号s0
ok: mov ax,4c00h
int 21h
do7c: push bp
push ax;栈顶 ax bp ip cs 标志
mov bp,sp
mov ax,[bp+4]
add ax,bx;ax=ax+bx=offset ok+offset s0-offset ok =offset s0
mov [bp+4],ax
pop ax
pop bp
iret
do7c_e: nop
code ends
end start

上述代码运行正确
0x03 BIOS和DOS所提供的中断例程
BIOS(basic Input Output System),是主板ROM中存放的程序,BIOS包含以下几部分内容
(1)硬件系统的检测与初始化程序
(2)外部中断和内部中断的中断例程
(3)用于对硬件设备进行I/O操作的中断例程
(4)其他和硬件系统设备相关的中断例程
操作系统DOS也提供中断例程,但是从操作系统的角度来看,DOS的中断例程就是操作系统向程序员提供的编程资源
DOS和BIOS在所提供的中断例程中包含许多子程序,可以用int指令直接调用
和硬件设备相关的DOS中单例程一般调用了BIOS的中断例程
BIOS和DOS中断例程的安装过程
(1)开机后,cpu加电,初始化(CS)=0FFFFH,(IP)=0,自动从FFFF:0单元开始执行程序。FFFF:0处有一条跳转指令,跳转去执行BIOS硬件中的硬件系统检测和初始化程序
(2)初始化程序将建立BIOS所支持的中断向量,即将BIOS提供的中断例程的入口地址登记在中断向量表中。注意,对于BIOS所支持的中断例程,只需将入口地址登记在中断向量表中即可,因为他们是固化到ROM中的程序,一直在内存中存在
(3)硬件系统检测和初始化完成后,调用int 19h进行操作系统的引导,从此将计算机交由操作系统控制
(4)DOS启动后,除完成其他的工作外,将DOS自己的中断例程又装入内存,并建立相应的中断向量

(1)不能,8086cpu上电后执行的第一条指令的地址是固定的
(2)不能 int 19h 中断例程用来装载操作系统,DOS还未被装载,更不可能由DOS提供
BIOS中断例程应用
举例 int 10h是由BIOS提供的中断例程(其中断例程的入口地址F000:12C0),其中包括了多个和屏幕输出相关的子程序。

一般来说,一个供程序员调用的中断例程中往往包括多个子程序,由传递参数来决定执行哪一个子程序,在BIOS和DOS提供的中断例程中,都用AH来传递内部子程序的编号
下面来看一下int 10h 的设置光标位置功能
assume cs:code
code segment
start: mov ah,2;置光标子功能
mov bh,0;第0页
mov dh,12;行号
mov dl,0;列号
int 10h
mov ah,4ch;程序返回子功能
int 21h;
code ends
end start
该程序执行后光标确实到12行闪烁了,但是好像不是第0列(尴尬,光标截图不到,截图就消失了)
(ah)=2表示调用第10h号中断例程的2号子程序,功能为设置光标位置,dh为行号,dl为列号(80*25字符模式下:行0-24 列0-79) bh为页号。
页号的含义,内存地址空间B8000H到BFFFFH 共32KB空间,是80*25彩色字符模式的显式缓冲区,一屏内容在缓冲区栈4000字节,所以显式缓冲区分8页,显示屏可以显式任意一页的内容,通常情况下,显式第0页的内容,也就是说,通常B8000H到B8F9FH的4000个字节的内容将出现在显示器上
int 10h中断例程的9号子功能: 在光标位置处显式字符串的功能
assume cs:code
code segment
start: mov ah,9;子功能号
mov al,'a';要显示的字符,一次只能一个
mov bl,7;字符颜色
mov bh,0;第0页
mov cx,3;字符重复个数
int 10h
mov ah,4ch
int 21h
code ends
end start

上述代码运行正确
DOS中断例程应用
int 21h中断例程是由DOS提供的中断例程,也是包含了多个子程序,前面我们一直使用的是int 21h的 4ch号功能,即程序返回功能:
mov ah,4ch
mov al,0
int 21h
int 21h的9号功能 也是在光标处显式字符串的功能:
assume cs:code
data segment
db 'Welcome to masm','$'
data ends
code segment
start: mov ah,2;置光标;BIOS的10h中断例程
mov bh,0;第0页
mov dh,12;行号
mov dl,12;列号
int 10h;
mov ax,data
mov ds,ax;ds:dx存放要显示的字符串的首地址,以'$'结束
mov dx,0
mov ah,9
int 21h
mov ax,4c00h
int 21h
code ends
end start

上述代码运行正确(但实际上光标的位置和显式的字符串开头并不很对应,可能是DOSBOX模拟的问题把)