目录
0. 前言
- 参考材料:《汇编语言》(第4版)王爽 第16章 直接定址表
在做这个实验的时候,笔者也遇到了一些小问题,现在分享出来,希望对正在学习这一部分内容的朋友有所帮助~
1. 实验基础
- 阅读第16章,了解直接定址表的使用逻辑
由于书中已经提供了大部分逻辑的代码(参见p296 16.4 程序入口地址的直接定址表),因此这里就不再赘述
根据书中的逻辑很容易写出如下代码👇:
assume cs:code code segment ; 安装一个新的 int 7ch 中断例程,为显示输出提供如下功能程序。 ;(1)清屏; ;(2)设置前景色; ;(3)设置背景色; ;(4)向上滚动一行。 ; ah传递功能号:0 表示清屏,1 表示设置前景色,2 表示设置背景色,3 表示向上滚动一行,其它数字正常返回; ; 对于 1、2 号功能,用al传送颜色值,0 <= al <= 7 start: ;安装程序 push cs pop ds mov si,offset install mov ax,0 mov es,ax mov di,200H mov cx,offset installend-offset install cld rep movsb ;安全修改向量表 cli mov word ptr es:[7cH*4],200H mov word ptr es:[7cH*4+2],0 sti ;安装程序结束 mov ax,4c00H int 21H ;需安装代码段 install: jmp short set table dw sub0,sub1,sub2,sub3 set: push bx cmp ah,3 ja setret mov bl,ah sub bh,bh shl bx,1 call word ptr table[bx] setret: pop bx iret ;(1)清屏 sub0: push bx push cx push es mov bx,0b800H mov es,bx sub bx,bx mov cx,2000 sub0s: mov byte ptr es:[bx],' ' add bx,2 loop sub0s pop es pop cx pop bx ret ;(2)设置前景色 sub1: push bx push cx push es mov bx,0b800H mov es,bx mov bx,1 mov cx,2000 sub1s: and byte ptr es:[bx],11111000b or es:[bx],al add bx,2 loop sub1s pop es pop cx pop bx ret ;(3)设置背景色 sub2: push ax push bx push cx push es mov bx,0b800H mov es,bx mov bx,1 mov cl,4 shl al,cl mov cx,2000 sub2s: and byte ptr es:[bx],10001111b or es:[bx],al add bx,2 loop sub2s pop es pop cx pop bx pop ax ret ;(4)向上滚动一行 sub3: push bx push cx push ds push es push si push di mov bx,0b800H mov ds,bx mov es,bx mov si,160 sub di,di mov cx,160*24 rep movsb mov cx,80 sub3s: mov byte ptr es:[di]," " add di,2 loop sub3s pop di pop si pop es pop ds pop cx pop bx ret ;需安装代码段结束 installend: nop code ends end start
注意:第15章曾提到过如何较为安全地修改向量表,在这里也出现了相应的应用场景
;安全修改向量表
cli
mov word ptr es:[7cH*4],200H
mov word ptr es:[7cH*4+2],0
sti
如果针对这一部分还有不明白的可以看一下我写的上一篇文章:
汇编语言 第15章 外中断 扩编中断例程的设计思路及注意事项(附书上示例及实验代码+注释)-CSDN博客
2. 发现问题
经过测试,我们发现这段安装程序虽然能够正常运行,但是安装过后并不能实现正确的功能
这里提供一个可以对所有输入进行全面测试的程序,供有需要的朋友直接使用:)
注意:测试程序运行时颜色变化较为强烈,建议将屏幕亮度调低后进行测试
assume cs:code code segment ;对所有输入进行全面测试程序 start: ;全屏字符置绿色 mov ah,1 mov al,2 int 7cH ;等待一段时间后进行其它测试,下同理 call delay ;全屏向上移动一行 mov ah,3 mov al,0 int 7cH call delay ;全屏背景置红色 mov ah,2 mov al,4 int 7cH call delay ;清屏 mov ah,0 mov al,0 int 7cH call delay ;其它输入测试 mov ah,4 mov al,0 int 7cH call delay ;测试程序结束 mov ax,4c00H int 21H ;延迟方法 delay: push ax push dx mov dx,10H decrease: sub ax,1 sbb dx,0 cmp ax,0 jne decrease cmp dx,0 jne decrease pop dx pop ax ret code ends end start
3. 问题分析
- 在进行调试之前我们可以先思考一下,问题可能发生在哪个环节?
既然书中16.4节类似代码可以正常运行,那么我们需要关注的是实验代码与前者区别在何处。
很明显,我们所写的代码中多了一个安装中断例程的步骤,因此我们基本可以确定,问题基本发生在代码转移,即地址改变的过程中。
- 结论:调试过程中我们需要重点关注几条涉及数据标号的指令,我们遇到的问题很可能就发生在编译器对标号的编译环节。
4. 调试排查
我们运行安装程序后进入Debug模式进行调试,可以使用上述测试程序进行单步调试(Debug test.exe)
于是我们注意到了,这里出现了标号编译,对应代码如下:
call word ptr table[bx]
此时CS为0,BX为一个很小的数字,因此CS:BX+2AH显然在0:200H之前,而我们保存的table应当被保存在0:202H处(0:200H处保存了 jmp short set 指令),如下图:
这里我们又发现了一个问题,根据推测这些子程序应当被保存在大于200H的地址处,因此可以判断table中保存的数据不是正确的地址,看来直接修改IP的跳转指令和经过编译后的标号被复制后并不总是指向正确的地址!
5. 解决方案
- 现在已经确定了问题所在:table及四个子程序的标号被复制后指向了错误的地址
由于待复制代码最终会被安装到0:200H处,因此我们可以考虑一开始就将这段代码安装到CS:200H处,这样就可以使得标号被编译时与被复制后的IP地址吻合,以此来防止代码复制后造成的地址混乱。
这里我们可以使用 org指令(origin):
;表示从IP为200H处开始存放代码
org 200H
;需安装代码段
install:
jmp short set
table dw sub0,sub1,sub2,sub3
set:
push bx
cmp ah,3
ja setret
mov bl,ah
sub bh,bh
shl bx,1
call word ptr table[bx]
setret:
pop bx
iret
这是没有使用指令 org 200H 时的编译结果:
这是使用指令 org 200H 后的编译结果:
修改代码后再次进行测试, 一切运行正常!
再次提醒:测试程序运行时颜色变化较为强烈,建议将屏幕亮度调低后进行测试
6. 要点总结
- 注意修改向量表时保证安全性
- 直接修改IP的跳转指令和经过编译后的标号被复制后并不总是指向正确的地址,因此当我们需要复制程序时,我们可以通过org指令提前将待复制程序从复制后的IP处开始编译,保证这些经过编译后的常数复制后的正确性