DEBUG的用法
在 DOSBox 中使用 DEBUG 工具时,可以使用以下命令及其用法,这些命令主要用于调试汇编语言程序、查看和修改寄存器与内存内容、执行代码等。以下是 DEBUG 的所有常用命令及其详细用法:
1. 进入和退出 DEBUG
- 进入 DEBUG:在 DOSBox 中输入
debug
命令。 - 退出 DEBUG:使用
q
命令退出 DEBUG 并返回到 DOSBox 命令行。
2. 寄存器操作
- 查看所有寄存器:输入
r
,查看所有寄存器的当前值。 - 查看和修改单个寄存器:输入
r <寄存器名>
,例如r ax
,查看 AX 寄存器的值。输入新值后按回车即可修改。
3. 内存操作
- 查看内存内容:
d [起始地址]
:从指定地址开始查看内存内容。d [起始地址] [结束地址]
:查看指定范围内的内存内容。
- 修改内存内容:
e [内存地址]
:修改指定地址的内存内容。e [内存地址] '文本'
:直接输入文本内容。
- 填充内存内容:
f [起始地址] [结束地址] [值1] [值2]...
:用指定值填充内存区域。
4. 汇编指令操作
- 输入汇编指令:
a [地址]
:从指定地址开始输入汇编指令。输入完成后按回车退出。
- 反汇编指令:
u [地址]
:从指定地址开始反汇编指令。u [段地址:偏移地址]
:指定段地址和偏移地址进行反汇编。
5. 程序执行
- 单步执行:
t
:执行当前指令并进入下一步。t [地址]
:从指定地址开始单步执行。
- 连续执行:
g
:从当前地址开始执行程序。g=[地址]
:从指定地址开始执行程序,并设置断点。
- 运行程序至结束:使用
p
命令。
6. 其他功能
- 计算偏移量:
h value1 value2
:计算两个十六进制值的和。
- 保存程序到文件:
p [文件名] [地址]
:将内存中的程序保存到文件。
汇编程序中的注意点
关于下面四个指令,在汇编程序中有不同的含义:
1 | mov al,[0] |
(al)= 0
(al)= ((ds)*16 + 0)
(al)= ((ds)*16 + bx)
(al)= ((ds)*16 + bx)
所以总结得到:
- 如果在”[ ]“里用一个常量idata直接给出内存单元的偏移地址,就要在”[ ]“前面显式的给出段地址所在的寄存器,否则会被解释为常量
- 如果在”[ ]“里面使用寄存器,则默认段地址为ds,可以不用显式的表现
汇编程序的入口
当我们在代码段设置数据时会遇到一个问题,就是程序的入口被设置在代码段
但是这会导致程序无法执行,因为你定义的字节被反编译后可能是未被定义的或者意义不明的指令
所以我们在运行程序时需要手动调节IP值,那么有什么更好的办法呢?
我们可以在汇编代码时事先定义程序的入口:
1 | assume cs:code |
通过这种格式我们可以在start处定义函数的入口,程序将从此开始运行
那么换个角度思考,我们既然可以指定程序的入口,那么我们就可以对程序进行分段
我们将数据,代码,栈分别放入不同的段中
比如下面这个程序
1 | assume cs:code,ds:data,ss:stack |
注意要分清什么是伪指令,什么是汇编指令。CPU如何处理我们定义的段空间,完全是靠程序中具体的汇编指令,这里的伪指令只是将其进行了抽象,这样的分段定义,实际上是基于段寄存器的使用。并非我们想象的直接对内存进行分段
汇编程序转换大小写
首先我们要知道ASCII字符中大小写字母之间有什么样的联系
- 大写字母的十六进制数值比小写字母的十六进制数值小 20H
- 大写字母和小写字母的区别在于小写字母的第五位是1(因为位数从0开始计算)
循环遍历
所以我们可以写出以下程序:
1 | assume cs:code,ds:data |
可以看到我们使用了两个循环,分别将其转换为大小写
数组处理
我们可以用’[bx+idata]’的形式来模拟数组的行为
1 | assume cs:code,ds:data |
这里面的 0[bx]
和
5[bx]
可以分别理解为C语言中的 a[]
和
b[]
,只不过这里体现的是偏移地址
在这里我们有几种等价的表达方式:
1 | [bx + idata] = idata[bx] = [bx].idata |
我们可以用数组的思想去理解
汇编内存寻址的进一步理解
我们依次进行深入:
DS,SS,CS
作为一个段地址,往往用来划分一定的内存区域存放特定的数据
我们可以理解成C语言中,申请了一段空间(空间)
[BX]寄存器间接寻址
通过修改寄存器BX中的值,我们可以进一步索引到段中的某一部分内存的起点
我们可以理解成在这一片空间中划分了一部分作为数组(一维数组)
[BX + idata]寄存器相对寻址
我们以BX确定在段空间的位置后,我们可以用常量去查询指定内存的数值
此时我们可以把常量idata理解成数组的下标(二维数组)
我们可以用以下形式表达:
1 | [bx + idata] = idata[bx] = [bx].idata |
[BX + SI/DI + idata]相对基址变址寻址
我们先用BX确定一部分空间,再用SI/DI中的地址确定在这段空间中的位置,然后用常量去查询指定内存中的数值
此时我们可以把SI/DI中的数值理解成二维数组的首地址,而常量作为数组下标进行索引(三维数组)
当没有常量时我们这样表达:(基址变址寻址)
1 | [bx + si/di] = [bx][si/di] |
有常量时我们这样表达:
1 | [bx + si/di + idata] = idata[bx][si/di] = [bx].idata[si/di] = [bx][si/di].idata |
SI/DI
这两个寄存器是8086CPU中与bx功能相近的寄存器,这两个寄存器不能被分成两个八位的寄存器来使用
通过这些各种各样的表达方式,我们可以根据自己的需求进行各种各样的寻址
新的汇编指令
div指令
使用这个指令时我们需要注意以下几点:
- 除数:有8位和16位两种,在一个reg或者内存单元中
- 被除数:默认放在AX或DX和AX中,被除数的位数是除数的两倍,如果被除数是32位,那么DX存放高十六位,AX存放低十六位
- 结果:如果除数为8位,则AL存储除数操作的商,AH存储除数操作的余数;如果为16位,那么AX存储商,DX存储余数
伪指令 dd
db
,dw
,dd
分别代表三种不同的定义类型
1 | db ;定义字节(byte)类型 |
dup
用来重复定义同一类型的数据
1 | db 重复的次数 dup (重复的字节类型) |
比如定义200个字类型
1 | dw 200 dup (0) |
实验七
答案如下:
1 | assume cs:code |