汇编语言笔记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

QQ截图20200606094619

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

0x01 编写供应用程序调用的中断例程

QQ截图20200606094935

​ 一样的还是分为三个部分:(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		

QQ截图20200606100402

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

QQ截图20200606100824

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

QQ截图20200606102841

​ 上述代码运行正确

0x02 对int iret和栈的深入理解

QQ截图20200606103643

​ 如果我们把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

QQ截图20200606105739

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

QQ截图20200606110218

QQ截图20200606110240

​ (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
		

QQ截图20200606114315

​ 上述代码运行正确

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自己的中断例程又装入内存,并建立相应的中断向量

QQ截图20200606150448

​ (1)不能,8086cpu上电后执行的第一条指令的地址是固定的

​ (2)不能 int 19h 中断例程用来装载操作系统,DOS还未被装载,更不可能由DOS提供

BIOS中断例程应用

​ 举例 int 10h是由BIOS提供的中断例程(其中断例程的入口地址F000:12C0),其中包括了多个和屏幕输出相关的子程序。

QQ截图20200606151048

​ 一般来说,一个供程序员调用的中断例程中往往包括多个子程序,由传递参数来决定执行哪一个子程序,在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

QQ截图20200606153002

​ 上述代码运行正确

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

QQ截图20200606153902

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