汇编语言(王爽第三版)实验13

标签:
汇编王爽it教育 |
分类: 汇编语言(王爽第三版)实验 |
实验13 编写、应用中断例程
(1)编写并安装int 7ch中断例程,功能为显示一个用0结尾的字符串,中断例程安装在0:200处。
assume cs:code
data segment
db 'Welcome to masm!', 0 data ends
code segment
start:
mov dh, 10 所在行数:11行;
所在列数:11列 mov dl, 10 ;
字符属性 mov cl, 2 ;
mov ax, data
mov ds, ax
入口参数ds:si指向字符串data mov si, 0 ;
调用9号子程序,显示字符串 int 7ch ;
mov ax, 4c00H
int 21H code ends
end start
程序分析:这个与我们原来的show_str程序基本类似。稍加将程序改动就可以使用了。不太懂的看看实验10.
assume cs:code code segment
start:
中断例程的安装程序 ;7cH
mov ax, cs
mov ds, ax
将ds:si指向源地址(show_str的机器码) mov si, offset show_str ;
mov ax, 0000H
mov es, ax
将es:di指向目的地址(0:200H向量表中) mov di, 200H ;
设置传输长度 mov cx, offset show_strend - offset show_str ;
传输方向为正 cld ;
字节传输 rep movsb ;
设置中断向量表,使7cH条目中断向量指向0000:200H ;
mov ax, 0000H
mov es, ax
mov word ptr es:[7cH*4], 200H
mov word ptr es:[7cH*4+2], 0000H
mov ax, 4c00H
int 21H ;-------
;装载的例程:7cH
;功能:int 7cH实现按行和列及字符属性显示字符串功能
;入口参数:入口参数:dh-行数、dl-列数、cl-字符属性,ds:si指向data字符串
;返回值:无
;-------
show_str:
push dx
push cx
将子程序用到的寄存器入栈 push si ;
mov ax, 0b800H
设置显示缓冲区内存段 mov es, ax ;
(ax)= 0,防止高位不为零 mov ax, 0 ;
字节/行 mov al, 160 ;0a0H- 160
相对于0b800:0000第dh行偏移量 mul dh ;
将第(dh)行的偏移地址送入bx,bx代表行偏移 mov bx, ax ;
mov ax, 0
列的标准偏移量是2个字节 mov al, 2 ;
同一行列的偏移量,尽量使用乘法,(al)=列偏移 mul dl ;
最终获得偏移地址(bx)=506H add bx, ax ;
将di作为每个字符的偏移量 mov di,0 ;
将字符属性写入al中 mov al, cl ;
将cx高8位设置为0 mov ch, 0 ;
将字符串单个字符读入cl中 show: mov cl, ds:[si] ;
判断字符串是否为零。 jcxz ok ;
在显示缓冲区中写入字符 mov es:[bx+di+0], cl ;
在显示缓冲区中写入字符属性 mov es:[bx+di+1], al ;
add di, 2
inc si
jmp short show
字符串字符为0,结尾 ok: pop si ;
pop dx
恢复寄存器 pop cx ;
iret
show_strend:nop
代码段结尾,便于计算7cH例程的长度。 ; code ends
end start
http://s3/mw690/006LW9dSzy7aA5Uqxii02&690
(2)编写并安装int 7ch中断例程,功能为完成loop指令的功能。
程序分析:在这章节中有详细的介绍,只不过没有形成装载程序,在我的讲义中有详细介绍。这里只列出代码:
测试调用代码如下(假定是zzz.asm):
assume cs:code
code segment
start:
mov ax, 0b800H
mov es, ax
mov di, 160*12
s – offset se mov bx, offset
mov cx, 80
s: mov byte ptr es:[di], '!'
add di, 2
int 7cH
se: nop
mov ax, 4c00H
int 21H code ends
end start
程序分析:
(1)编写一个7cH的中断例程,实现loop语句的功能。通过以上实例,我们发现int 7cH实现的功能就是loop指令的功能。如果(cx)不等于0,跳转到s标号继续执行。并且调用一次int 7cH,(cx)=(cx)-1(每次).
(2)7cH的入口参数:cx、bx(我们规定的),cx代表计数器,bx代表了相对位移。因为中断例程不是返回到操作系统,故它必然包含iret指令,返回到调用那一点。
(3)回忆loop指令,它的相对位移是:从标号s(按照本例子来说)- loop指令下面那个指令的首地址(也就是se标号的地址)。由于int 7cH就等价于loop指令,那么它入口参数也需要相对的位移,这个相对位移就是(bx)=offset s – offset se。这里se标号与前面2个例程用途不一样了,它不用作计算例程的长度了,它代表了se标号的地址,用于计算相对位移了。
(4)我们来看看例程中,怎样实现将(cx)=(cx)-1(每次);跳转到s标号处,直到(cx)=0。跳转到s标号,肯定是修改了ip了。至于cs也应该修改,虽然它在一个段内。产生的结果是:(ip)=s标号的ip,(cs)=s标号的cs。
(5)在调用7cH例程时,cs和ip都压栈了,cs值是s标号的cs,ip的值应该是标号se的偏移地址。那么在栈中,有了s标号的cs了,偏移地址是se的偏移地址。怎样把计算出s的偏移地址,这个例程的任务就完成了。
(6)我们可以利用iret指令。回忆iret指令,(执行iret指令,用汇编描述的过程是:pop ip;pop cs;popf,与我们引发中断过程中CPU执行的入栈过程(pushf;push cs;push ip)正好是相对应的。)popf弹栈状态寄存器值,我们不讨论了。这里主要讨论ip和cs,cs肯定是s标号的cs了,我们现在想法把栈中的ip值(栈中的值为se标号的值)修改为offset se +(bx)那么它肯定就是s标号的地址值。最后在弹栈,实现(cs)=s标号的cs,(ip)=s标号的ip了。
(7)回忆栈结构,此例中是使用的系统自动的栈(这个我喜欢,但也不要蒙圈了。)ss代表栈的段地址。bp默认是偏移地址,也就是说ss:[bp]就代表了栈空间的bp单元。sp是栈顶的指针。在8086中,一个栈的基本单元是2个字节的。以上分析都有了清晰的认识后,我们来看看这个例程代码:
(8)例程代码:
lp:
lpret: pop bp
【1】因为ss和bp是配套使用的,[bp]代表ss栈中单元,bp也就是ss的偏移地址。push bp目的是保护bp的值,因为下面将会用到bp变量。
【2】mov bp, sp将栈顶指针sp送入到bp;此时栈中的数据有(从栈顶开始依次向下),bp的值(刚压入的),ip值(int 7cH调用时的,应该是se标号的值),cs值(int 7cH调用时的,应该和s标号的值一样的),flag值(int 7cH调用时的,标志寄存器的值,这个我们不考虑)。【3】dec cx; 我们为了实现loop的功能,在例程中应该是执行一次循环(调用一次7cH,):cx的值就减1。
【4】jcxz
lpret
【5】add [bp+2],
bx
【6】pop
bp
【7】iret
【8】为毛有个lp:标号(老婆??)?代表了这个例程的名字吧,没错的!本来标号就是伪指令。
装载程序代码如下:
assume cs:code
code segment
start:
中断例程的安装程序 ;7cH
mov ax, cs
mov ds, ax
将ds:si指向源地址(captial的机器码) mov si, offset lp ;
mov ax, 0000H
mov es, ax
将es:di指向目的地址(0:200H向量表中) mov di, 200H ;
设置传输长度 mov cx, offset lpend - offset lp ;
传输方向为正 cld ;
字节传输 rep movsb ;
设置中断向量表,使7cH条目中断向量指向0000:200H ;
mov ax, 0000H
mov es, ax
mov word ptr es:[7cH*4], 200H
mov word ptr es:[7cH*4+2], 0000H
mov ax, 4c00H
int 21H ;-------
;装载的例程:7cH
;功能:int 7cH实现和loop指令相同的功能
;入口参数:cx计数器、bx相对地址偏移量
;返回值:无
;-------
lp:
将bp这个ss栈的偏址保存 push bp ;
将当前栈顶指针值送入到bp mov bp, sp ;
调用一次7cH,(cx)-1 dec cx ;
与(cx)值判断,如果为0,跳转到lpret标号 jcxz lpret ;
修改ss栈中的从栈顶向下第2个单元的值 add [bp+2], bx ; lpret:
恢复bp值 pop bp ;
返回到调用处。 iret ;
lpend:
nop 代码段结尾,便于计算7cH例程的长度。; code ends
end start
程序测试:
【1】编译、连接eee.asm后生成eee.exe文件,然后执行它,将这个例程装载到内存0:200H处,并修改中断向量。
【2】编译、连接zzz.asm,生成EXE文件后,执行它,测试是否达到要求,结果如下:
http://s1/mw690/006LW9dSzy7aA64xWgg50&690(3)下面的程序,分别在屏幕的第2、4、6、8行显示4句英文诗,补全程序。
assume cs:code
code segment
start :
ok:
code ends
end start
程序分析:
【1】首先我们发现4个字符串都定义在了code段中了,并且都以$结尾。int 21H显示字符串需要以$结尾
【2】不要被大量的标号迷惑,它们更清楚的指向了内存单元的地址。
【3】注意一共调用了3次中断例程。置光标、显示字符串、安全退出程序。
实验结果如下:在屏幕的第3行0列开始,显示字符串。