汇编实验16 编写包含多个功能子程序的中断例程——浅谈直接地址表
来源:互联网 发布:软考中级数据库工程师 编辑:程序博客网 时间:2024/05/21 11:15
汇编实验16 编写包含多个功能子程序的中断例程——浅谈直接地址表
这是王爽《汇编语言(第三版)》的第16个实验,本章的内容就是介绍了一种编程技巧——直接定址表,可以认为是一种以空间换时间的编程策略,相对于算法竞赛中的“打表法”,可以使程序变得更加简洁优美,避免过于繁琐的分支结构。
好吧,好话就说到这里。本次实验如果要照搬以前的套路,肯定是失败的!具体来说,如果要照搬书中代码(稍微做些修改),再把中断例程安装到0:200h之后的内存空间中,那么别用直接定址表,那是无效的,程序一定跳转到天边去了。为什么会这样,请继续看下去。
任务
安装一个新的int 0中断例程,为显示输出提供以下功能的子程序:
- 清屏
- 设置前景色
- 设置背景色
- 向上滚动一行
入口参数说明:
用寄存器ah传递功能号:
- 0表示清屏
- 1表示设置前景色
- 2表示设置背景色
- 3表示向上滚动一行
对于1、2号功能,用al传递颜色值,(al) = 0,1,2,…,7
预备知识
标号
有这样一类标号,它不但代表内存单元地址,而且还隐含长度信息,即在此标号下的内存单元是字节(byte)单元,还是字(word)单元,还是双字(dword)单元。
例如
data segments dd 2a db 1,2,3,4,5,6,7,8b dw 0data endsassume ds:data
这里的标号a,b,s之后没有冒号,他们同时描述了内存单元地址和单元长度。标号a描述了的地址ds:4
,从这个地址开始(程序中使用标号a的)内存单元都是字节单元。标号b描述了地址ds:12
,从这个地址开始 (程序中使用标号b的)内存单元都是字单元。标号s描述了地址ds:0
,从这个地址开始 (程序中使用标号c的)内存单元都是双字单元。
举几个例子:
mov ax,b
相对于mov ax,ds:[12]
mov b, 2
相对于mov word ptr ds:[12],2
inc b
相对于inc word ptr ds:[12]
这些指令中,标号b代表一个内存单元,地址为ds:12
,长度为2个字节。
指令mov al,b
是错误的,因为al是8位寄存器,而b代表字单元。
mov al,a[si]
相当于mov al,ds:[4+si]
mov al,a[3]
相当于mov al,ds:[4+3]
mov al,a[bx+si+3]
相当于mov al,ds:[4+bx+si+3]
要想在某一个段中使用标号访问数据, 必须要使用伪指令assum将标号所在的段和一个段寄存器联系起来,否则编译器无法确定标号的段地址。这里的标号在data段中,我把data段作为数据段使用,因此用伪指令assume ds:data
将告诉编译器标号所在的段地址。
再比如
data segmenta db 1,2,3,4,5,6,7,8b dw 0c dw a,b;相当于 c dw offset a,offset bd dd a,b;相当于 d dw offset a,seg a,offset b,seg adata ends
这里seg
操作符是取得某一标号的段地址。
陷阱
下面给出setscreen子程序(但还不是中断处理程序,用ret返回主程序,而不是iret,注意一下)这里用了直接定址表的技巧,但是这种技巧有一些注意点,我们不能像之前一样依葫芦画瓢地完成实验任务。
;*************************************************************;子程序setsreen;功能:(1)清屏(2)设置前景色(3)设置背景色(4)向上滚动一行;参数:;(1)ah寄存器传递功能号: 0表示清屏; 1表示设置前景色; 2表示设置背景色; 3表示向上滚动一行;(2)对于1、2号功能,用al寄存器传送颜色,al=0,1,2,...,7;**********************************************************setscreen: jmp short set table dw func0,func1,func2,func3set: push bx cmp ah,3 ja sret mov bl,ah mov bh,0 add bx,bx call word ptr table[bx]sret: pop bx ret;------------------------------------------------------;0号功能:清屏func0: push bx push cx push es mov bx,0b800h mov es,bx mov bx,0 mov cx,2000func0s: mov byte ptr es:[bx],' ' add bx,2 loop func0s pop es pop cx pop bx ret;------------------------------------------------------;1号功能:设置前景色func1: push bx push cx push es mov bx,0b800h mov es,bx mov bx,1 mov cx,2000func1s: and byte ptr es:[bx],11111000b or es:[bx],al add bx,2 loop func1s pop es pop cx pop bx ret;------------------------------------------------------;2号功能:设置背景色func2: push bx push cx push es mov cl,4 shl al,cl mov bx,0b800h mov es,bx mov bx,1 mov cx,2000func2s: and byte ptr es:[bx],10001111b or es:[bx],al add bx,2 loop func2s pop es pop cx pop bx ret;------------------------------------------------------;3号功能:向上滚动一行func3: push cx push si push di push es push ds mov si,0b800h mov es,si mov ds,si mov si,160 mov di,0 cld;书中给出的程序用一个循环来处理第n+1行复制到第n行,虽然思路清晰;但是过于复杂,由于si和di总是相差一行(160字节),直接进行如下的串处理;就可以了。 mov cx,23*160 rep movsb mov cx,80 mov si,0func3s1: mov byte ptr [160*24+si],' ' add si,2 loop func3s1 pop ds pop es pop di pop si pop cx ret
如果这段代码被安装到0:200h处的内存单元中,直接地址表就会失效!。原因就出在标号上,编译器会将标号翻译为在当前代码段中相应的偏移地址,也就是说在只有当前的代码段,地址表的定位是正确的。但是如果把这段(表现为二进制数据形式的)指令复制到另外一块内存区域,每条指令在内存中的位置发生巨大变化,原来的直接定址表就不适用了,它提供的偏移地址和现在的子程序位置不对应了,程序的跳转就会失控。
实现
如果我们一定要用直接定址表,那该怎么办呢?很简单,直接修改中断向量表即可。代码如下:
mov word ptr ds:[0*4],offset setscreenmov word ptr ds:[0*4+2],cs
也就是说,我们不能将子程序安装到特定位置。这样的结果是,一旦主程序执行完毕,我们写的新的中断程序也就完成使命,不能一直在内存中逗留,最后被其它数据覆盖。
下面只给出主程序:
assume cs:code,ss:stackstack segment db 256 dup(0) stack endscode segmentmain: mov ax,stack mov ss,ax mov sp,256 mov ax,0 mov ds,ax ;保存原来的0号中断向量 push word ptr ds:[0*4] push word ptr ds:[0*4+2] ;修改0号中断向量 mov word ptr ds:[0*4],offset setscreen mov word ptr ds:[0*4+2],cs mov ax,0204h int 0 mov ax,0102h int 0 ;恢复0号中断向量 pop word ptr ds:[0*4+2] pop word ptr ds:[0*4] int 0 mov ax,4c00h int 21h;------------------------------------------------------setscreen:;子程序部分略;------------------------------------------------------code ends end main
如果一定要安装都内存空间中的安全区域0:200h中,就不应该使用直接地址表。子程序的主体部分可以这么写:
setsreen: cmp al,0 je do0 cmp al,1 je do1 cmp al,2 je do2 cmp al,3 je do3 jmp short sretdo0: call func0 jmp short sretdo1: call func1 jmp short sretdo2: call func2 jmp short sretdo3: call func3sret: iret
但这样做实在是有些难看。
总结
两个星期紧张的期末复习过去了,也终于放假了。此中有无数的槽点不吐不快,可笑的是我恰恰是个懒人,懒得吐槽。之前由于复习,学习的进程中断了,正好假期里我有的是时间。马上王爽的书我也要看完了,前面的内容却已经变得模糊,不像刚开始学的时候那么清晰,没关系,学习的过程是充实的,我已经很满足了,知识总是随着时间慢慢流失(如果不经常使用的话),还好汇编并非我的饭碗(再说不会的时候可以翻书嘛,知道书上哪里有这个东西就行)。学完王爽的汇编教材后,我也不打算在复习梳理一遍(一个字,懒),毕竟我要把注意力转向数据结构和算法的学习上去了(也不是说就完全抛下汇编语言了)。汇编语言还是很有趣,很重要的。我已经买了《x86汇编语言:从实模式到保护模式》这本书,作为我的第二本汇编教材。说实话,我对汇编语言的要求也不高,能读懂就行,同时通过汇编对计算机体系结构有一个侧面的认识和理解就心满意足了。一句话,就是看看热闹,摸摸门道,好好玩一玩就行(打个不恰当的比方,如果把自己精通的高级语言比作正式的配偶,学汇编语言就是在外面找个小情人……这么说的话,我现在还没有老婆……笑)。
- 汇编实验16 编写包含多个功能子程序的中断例程——浅谈直接地址表
- 王爽 汇编 实验16 编写包含多个功能子程序的中断例程
- 王爽《汇编语言》实验16——编写包含多个功能子程序的中断例程
- 王爽《汇编语言》实验17——编写包含多个功能子程序的中断例程
- [Hb-XVI] 编写包含多个功能子程序的中断例程(直接定址表)
- 汇编语言----实验16--编写包含多个功能子程序的中断例程
- ***汇编语言 实验16 编写包含多个功能子程序的中断例程
- 实验16 编写包含多个功能子程序的中断例程
- 编写包含多个功能子程序的中断例程
- 编写包含多个功能子程序的中断例程
- 实验十六_编写包含多个功能子程序的中断例程
- 王爽《汇编语言》实验16:包含多个功能子程序的中断例程 解答
- 多个功能子程序的中断例程测试程序
- 汇编,编写并安装int 7ch中断例程,功能为完成loop指令的功能
- 王爽《汇编语言》实验13——编写、应用中断例程
- 王爽 汇编 实验15:安装新的int9中断例程
- 汇编实验15:安装新的int 9中断例程
- 实验16---多功能子程序中断例程:清屏,设置前景色,设置背景色,向上滚动一行
- Maven
- 2017.1.13【初中部 】普及组模拟赛C组 excel 电子表格 题解
- mysql sql语句大全
- 13.2 连接到世界银行
- 递归和动态规划专题(二)----剑指offer+左程云算法
- 汇编实验16 编写包含多个功能子程序的中断例程——浅谈直接地址表
- open() close() 函数的使用
- 49个常用sql语句
- 设子数组A[0:k]和A[k+1:N-1]已排好序(0≤K≤N-1)。试设计一个合并这2个子数组为排好序的数组A[0:N-1]的算法。
- Android studio录屏按钮不能用怎么办
- 素数筛选
- 有向图的十字链表存储表式
- visual studio code jupyter错误!No kernel specs found
- 聊聊高并发之隔离术