笔记:
1、Debug程序是什么:
是DOS、Windows都提供的实模式(8086 方式)程序的调试工具。使用它,可以查看CPU各种寄存器中的内容、内存的情况和在机器码级跟踪程序的运行。
2、Debug命令:
Debug命令 | 功能 |
---|---|
R命令 | 查看、改变CPU寄存器的内容 |
D命令 | 查看内存中的内容 |
E命令 | 改写内存中的内容 |
U命令 | 将内存中的机器指令翻译成汇编指令 |
T命令 | 执行一条机器指令 |
A命令 | 以汇编指令的格式在内存中写入一条机器指令 |
3、如何使用Debug程序
在WIN10系统中,已经不支持在命令窗口(CMD)中打开Debug程序了,需要利用DOSBox模拟器来运行Debug,具体操作可以看这几篇博客:
DOSBox的安装及使用_学无止境****的博客-CSDN博客_dosbox
windows10 如何使用 debug_霜序0.2℃的博客-CSDN博客_win10debug
汇编语言学习-DOSBox-MASM-安装及使用教程_feezxe的博客-CSDN博客_汇编语言安装教程
因为发现其他实验所需的程序比较难找,所以实验所需的相关环境我已上传到CSDN(本次实验暂时只要使用debug):
汇编语言实验环境(包括DOSBox,Debug,MASM等)-其它文档类资源-CSDN下载
debug.exe:调试工具
DOSBox0.74-3-win32-installer.exe:DOS模拟器
edit.com:编辑器
exe2bin.exe:转换exe文件到二进制工具
LINK.EXE:链接工具
MASM.EXE:汇编工具
在这里记录一下我下载DOSBox和使用debug的过程:
(1)在DOSBox官网下载DOSBox软件:
(2)安装DOSBox:
(3)安装完成后打开DOSBox:
(会弹出两个窗口)
(4)在电脑某处创建文件夹,下载上述实验所需的相关环境:
我将实验所需的程序下载到了"D:\Assembly",注意应在磁盘的根目录(C盘c:\,D盘d:\均可)下新建文件夹(文件夹名可以任取),将程序放入该文件夹中(为了下一步的挂载)。
另外其实本次实验1需要用到的只有debug程序,所以仅下载debug程序也是可以的。不过为了方便起见,我还是将其他的程序都下载了。
(5)在DOSBox中挂载目录"D:\Assembly"
关于挂载的定义:Linux挂载_sut_uestc的博客-CSDN博客_linux挂载
在DOSBox中输入挂载命令:(此处为手动挂载,如果再次打开DOSBox,后还需重新挂载)
mount d d:\Assembly
d:
因为debug程序在这个被挂载的文件夹中,所以之后就可以使用debug程序了,运行结果如下图:
4、用R命令查看、改变CPU寄存器的内容
我们已经知道了AX、BX、CX、DX、CS、IP这6个寄存器,现在来看一下它们之中的内容。其他寄存器如SP、BP、SI、DI、DS、ES、SS、标志寄存器等我们先不予理会。
注意CS和IP的值,CS=073F,IP=0100,也就是说,内存073F:0100处的指令为CPU当前要读取、执行的指令。 在所有寄存器的下方,Debug还列出了CS:IP所指向的内存单元所存放的机器码,并将它翻译为汇编指令。可以看到,CS:IP所指向的内存单元为073F:0100,此处存放的机器码为0000,对应的汇编指令为ADD [BX+SI],AL(这条指令的含义我们还不知道,先不必深究)
Debug输出的右下角还有一个信息:“DS:0000=CD”,我们以后会进行说明,这里同样不必深究。
若要修改一个寄存器中的值,比如AX中的值,可以用R命令后加寄存器名来进行,输入“r ax”后按Enter键,将出现“: ”,作为输入提示,在后面输入要写入的数据后按回车键,即完成了对AX中内容的修改(这里修改AX寄存器中的内容为22FFH)。若想看一下修改的结果,可以再用R命令查看。
用R命令将CS修改为FF00H,IP修改为200,则CS:IP指向ff00:0200,此处存放的机器码为
【这条实验操作失败了,因为变换CS:IP后对应的机器码还是0000,对应的汇编指令也没有改变,可能是DOSBox的问题】
5、用Debug的D命令查看内存中的内容
要查看内存10000H处的内容,首先将这个地址表示为段地址:偏移地址的格式,可以是1000:0,然后用“d 1000:0”列出1000:0处的内容,如下图所示:
- 使用“d 段地址:偏移地址”的格式,Debug将列出从指定内存单元开始的128个内存单元的内容。
- 在使用d 1000:0后,Debug列出了1000:0~1000:7F中的内容(即内存地址单元的后8位二进制数为0000 0000~0111 1111,共2^7=128种可能的寻址结果,所以是128个内存单元)
使用D命令,Debug将输出3部分内容,如上图所示:
(1)中间是从指定地址开始的128个内存单元的内容,即128个字节(每行16个字节,共8行,16*8=128),用十六进制的格式输出,每行的输出从16的整数倍的地址开始,最多输出16个单元的内容。
- 注意在每行的中间有一个”-“,它将每行的输出分为两部分,这样便于查看。
- 内存1000:0~1000:F中的内容都在第一行,“-”前是1000:0~1000:7的8个单元,后面是1000:8~1000:F的8个单元。
- 内存1000:10~1000:1F中的内容都在第二行,“-”前面是1000:10~100:17的八个单元,后面是1000:18~1000:1F的8个单元。
- 比如,想要从图中找出1000:6B单元中的内容,可以从1000:60找到行,“-”前面是1000:60~1000:67的8个单元,“-”后面是1000:68~1000:6F的8个单元,这样我们就可以从1000:68单元向后数3个单元,找到1000:6B单元。
(2)左边是每行的起始地址。
(3)右边是每个内存单元中的数据对应的可显示的ASCII码字符。没有对应可显示的ASCII字符,Debug就用“.”来代替。
我们看到的内存中的内容,在不同计算机中是不一样的,也可能每次用Debug看到的内容都不相同,因为我们用Debug看到的是原来就在内存中的内容,这些内容受随时都有可能变化的系统环境的影响。
我们使用d 1000:9查看1000:9处的内容,如下图所示。Debug从1000:9开始显示,一直到1000:88,一共是128个字节。第一行中的1000:0~1000:8单元中的内容(共9个字节)不显示。最后一行,比上图多了1000:80的行中的9个字节数据。
在一进入Debug后,用D命令直接查看,将列出Debug预设的地址处的内容,如下图所示:
在使用“d 段地址:偏移地址”之后,接着使用D命令,可以列出后续的(128个内存单元的)内容,如下图所示:
也可以指定D命令的查看范围,此时采用“d 段地址:起始偏移地址 结尾偏移地址” 的格式。比如要看1000:0~1000:9中的内容,可以用"d 1000:0 9"实现,如下图所示:
如果只想查看内存单元10000H中的内容,可以用下图中的任何一种方法看到,因为图中的所有“段地址:偏移地址”都表示了10000H这一物理地址。
6、用Debug的E命令改写内存中的内容
比如,要将内存1000:0~1000:9单元中的内容分别写为0、1、2、3、4、5、6、7、8、9,可以用“e 起始地址 数据 数据 数据……”的格式来进行。如下图所示。
- 先用D命令查看1000:0~1000:f单元的内容;
- 再用E命令修改从1000:0开始的10个单元的内容
- 最后用D命令查看1000:0~1000:f中内容的变化
可以用E命令以提问的方式来逐个地修改从某一地址开始的内存单元中的内容。
① 输入 e 1000:10,按Enter键
② Debug显示起始地址1000:0010,和第一单元(即1000:0010单元)的原始内容00,然后光标停在“.”的后面提示输入想要写入的数据:
此时可以有两个选择:
其一为输入数据(我们输入的是ff),然后按空格键,即用输入的数据改写当前的内存单元;
其二为不输入数据,直接按空格键,则不对当前内存单元进行改写。
③ 当前单元处理完成后(不论是改写或没有改写,只要按了空格键,就表示处理完成),Debug将接着显示下一个内存单元的原始内容,并提示进行修改,我们可以用同样的方法处理。
④ 所有希望改写的内存单元改写完毕后,按Enter键,E命令操作结束。
可以用E命令向内存中写入字符,比如,用E命令从内存1000:0开始写入数值1、字符“a”、数值2、字符“b”、数值3、字符“c”,可采用下图所示的方法进行:
从上图可以看出, Debug对E命令的执行结果,向1000:0、1000:2、1000:4单元写入数值1、2、3,向1000:1、1000:3、1000:5单元中写入字符“a”、“b”、“c”的ASCII码值:61H、62H、63H。
也可以用E命令向内存中写入字符串,比如,用E命令从内存1000:0开始写入:数值1、字符串“a+b”、数值2、字符串“c++”、数值3、字符串“IBM”,如下图所示:
7、用E命令向内存中写入机器码,用U命令查看内存中机器码的含义,用T命令执行内存中的机器码
机器码 | 对应的汇编指令 |
---|---|
b80100 | mov ax, 0001 |
b90200 | mov cx, 0002 |
01c8 | add ax, cx |
输入指令如下图所示:
- 首先用E命令向从1000:0开始的内存单元中写入了8个字节的机器码(b8 01 00 b9 02 00 01 c8);
- 然后用D命令查看内存1000:0~1000:1f中(0 0000~1 1111,共2^5=64个内存单元)的数据(从数据的角度看一下写入的内容);
- 最后用U命令查看从1000:0开始的内存单元中的机器指令和它们所对应的汇编指令。
U命令的显示输出分为3部分,每一条机器指令的地址、机器指令、机器指令所对应的汇编指令。我们可以看到:
1000:0处存放的是写入的机器码b8 01 00所组成的机器指令,对应的汇编指令是mov ax,1;
1000:3处存放的是写入的机器码b9 02 00所组成的机器指令,对应的汇编指令是mov cx,2;
1000:6处存放的是写入的机器码01 c8所组成的机器指令,对应的汇编指令是add ax,cx;
1000:8处存放的是内存中的机器码00 00所组成的机器指令,对应的汇编指令是add [bx+si],al
由此,我们可以再次看到内存中的数据和代码没有任何区别,关键在于如何解释。
使用Debug的T命令可以执行一条或者多条指令,简单地使用T命令,可以执行CS:IP指向的指令。
- 首先用E命令向从1000:0开始的内存单元中写入了8个字节的机器码(b8 01 00 b9 02 00 01 c8);
- 然后用R命令查看CPU中寄存器的状态,可以看到,CS=073FH、IP=0100H,指向内存073F:0100;若要用T命令控制CPU执行我们写到的1000:0指令,必须先让CS:IP指向1000:0;
- 接着用R命令修改CS、IP中的内容,使CS:IP指向1000:0。
- 完成上面的步骤后,就可以使用T命令来执行我们写入的指令了(此时,CS:IP指向我们的指令所在的内存单元)。执行T命令后,CPU执行CS:IP指向的指令,则1000:0处的指令b8 01 00(mov ax,0001)得到执行。
- 指令执行后,Debug显示输出CPU中寄存器的状态。
上述操作结果如下图所示:
注意,指令执行后,AX中的内容被改写为1,IP改变为IP+3(因为mov ax,0001的指令长度为3个字节),CS:IP指向下一条指令。
接着上图,我们可以继续使用T命令执行下面的指令,注意每条指令执行后,CPU相关寄存器内容的变化。
操作如下图所示:
8、用Debug的A命令以汇编指令的形式在内存中写入机器指令
- 首先用A命令,以汇编语言向从1000:0开始的内存单元中写入了几条指令。在给出的起始地址后直接按Enter键表示操作结束。
- 用D命令查看A命令的执行结果。
- 操作结果如下图所示:
简单地使用A指令,是从系统预设的地址 073F:0100 开始输入指令,如下图所示:
此时,存到对应内存中的机器码为:
再用T命令让CPU执行当前CS:IP指向的内存中(073F:0100)存着的指令。每次输入一条T指令,CPU执行一条对应的汇编语句。注意图中相应寄存器内容的变化:
总结:
本次实验需要用到的命令
查看、修改CPU中寄存器的内容:R命令
查看内存中的内容 :D命令
修改内存中的内容:E命令(可以写入数据、指令,在内存中,它们实际上没有区别)
将内存中的内容解释为机器指令和对应的汇编指令:U命令
执行CS:IP指向的内存单元处的指令:T命令
以汇编的形式向内存中写入指令:A命令
实验任务:
(1)使用Debug,将下面的程序段写入内存,逐条执行,观察每条指令执行后CPU中相关寄存器中内容的变化。
机器码 | 汇编指令 | |
---|---|---|
1 | b8 20 4e | mov ax,4E20H |
2 | 05 16 14 | add ax,1416H |
3 | bb 00 20 | mov bx, 2000H |
4 | 01 d8 | add ax,bx |
5 | 89 c3 | mov bx,ax |
6 | 01 d8 | add ax,bx |
7 | b8 1a 00 | mov ax,001AH |
8 | bb 26 00 | mov bx,0026H |
9 | 00 d8 | add al,bl |
10 | 00 dc | add ah,bl |
11 | 00 c7 | add bh,al |
12 | b4 00 | mov ah,0 |
13 | 00 d8 | add al,bl |
14 | 04 9c | add al,9CH |
提示,可用E命令和A命令以两种方式将指令写入内存。 注意用T命令执行时,CS:IP的指向。
答:使用E命令是将机器码写入内存,使用A命令是以汇编指令的形式向内存中写入指令。使用A指令较为方便,所以这里使用A命令来实现将指令写入起始地址为1000:0的内存单元。
Step1:将要执行的指令,用A命令写入内存:
Step2: 用U命令查看从1000:0开始的内存单元中的机器指令和它们对应的汇编指令(检查一下刚才输入的有没有错误):
Step3:(若要用T命令控制CPU执行我们写到的1000:0的指令,必须先让CS:IP指向1000:0)先用R命令查看各寄存器存储的内容,再用R命令修改CS、IP的内容,使CS:IP指向1000:0。
Step4:用一次T命令,CPU执行当前CS:IP指向的指令,指令执行后CS:IP指向下一条指令:
1、
2、
3、
4、
5、
6、
7、
8、
9、
10、
11、
12、
13、
14、
从第14个汇编指令的执行可以看出,AL=66H,66H+9CH= 102H,但是因为AL是独立的8位寄存器(在AX的低8位,即0~7位),因此1不会进到AX的高8位中。所以最后运算的结果AL=02H
注:
上述汇编指令中提到的AH、AL是16位寄存器AX的高8位(8位~15位)和低8位(0位~7位),见书2.1节。AH和AL是可以独立使用的8位寄存器,CPU在执行指令时认为AH、AL是两个不相关的寄存器。
同理BH、BL。
(2)将下面的3条指令写入从2000:0开始的内存单元中,利用3条指令计算2的8次方
mov ax,1
add ax,ax
jmp 2000:0003
答:
Step1:用A命令,将上面的3条指令写入从2000:0开始的内存单元中
jmp 2000:0003的意思是让CS:IP重新指向2000:0003处,而这里此时存储的指令是"add ax,ax"
Step2:用R命令,使CS:IP指向2000:0
Step3:用T命令执行CS:IP指向的内存单元2000:0中存储的命令
使用3次T命令后,下一条待执行的指令变为“add ax,ax”,是因为执行的第3条指令“jmp 2000:0003”使CS:IP重新指向2000:0003位置了,那里存储的指令为“add ax,ax”
从此以后,每执行2次T命令,相当于执行一次“add ax,ax”操作(实际上是执行"add ax,ax"和“jmp 2000:0”的操作)
Step4:再执行13或14次T命令,来实现计算2^8=256=0100H的操作
1、第1~4次T命令
2、第5~8次T命令
3、第9~12次T命令
4、第13~14次T命令,此时AX寄存器中的值为0100H=256=2^8
注:
mov指令不能用于设置CS、IP的值,mov指令被称为传送指令
jmp指令可以用来修改CS、IP的值,jmp指令被称为转移指令
“jmp 段地址:偏移地址”指令的功能为:用指令中给出的段地址修改CS,偏移地址修改IP
“jmp 某一合法寄存器”指令的功能为:用寄存器中的值修改IP
当AX中的初始值为0001时,每执行1次add ax,ax操作,AX中值发生的变化如下表所示,可以发现一些规律:
执行次数 AX中的值(十进制) AX中的值(十六进制) 1 1+1=2=2^1 0002 2 2+2=4=2^2 0004 3 4+4=8=2^3 0008 4 8+8=16=2^4 0010 5 16+16=32=2^5 0020 6 32+32=64=2^6 0040 7 64+64=128=2^7 0080 8 128+128=256=2^8 0100
(3) 查看内存中的内容.
PC机主板上的ROM中有一个生产日期,在内存FFF00H~FFFFFH中的某几个单元中,找到生产日期并且改变它。
提示,如果读者对实验的结果感到疑惑,请阅读第1章中的1.15节。
答:
Step1:用D命令查看内存中的内容:
指定D命令的查看范围,采用“d 段地址:起始偏移地址 结尾偏移地址”的格式
使用“d 段地址:偏移地址”的格式,将列出从指定内存单元开始的128个内存单元的内容,接着使用d命令,可以列出后续的128个内存单元的内容。
可以看出,在DOSBox模拟器中,生产日期存在FFF0:00F5~FFF0:00FC(即FFFF5H~FFFFCH)的内存单元中,生产日期是01/01/92,对应内存单元中的内容为30 31 2F 30 31 2F 39 32。
Step2:尝试改变生产日期
使用E命令来改写内存FFF0:000FC中的内容32变为31,希望右边对应可显示的ASCII码字符变为1,从而将生产日期改写为01/01/91
我们发现,使用E命令后再使用D命令查看内存FFF0:00FC中的内容并没有改变,CPU向内存地址为FFF0:000FC的内存单元写入数据的操作是没有结果的,FFF0:000FC单元中的内容不会被改变,FFF0:00FC单元实际上就是ROM存储器中的一个单元。
注:
只读存储器(ROM)只能读取不能写入,关机后其中的内容不丢失。
(4)向内存从B8100H开始的单元中填写数据,如:
-e b810:0000 01 01 02 02 03 03 04 04
请读者先填写不同的数据,观察产生的现象;再改变填写的地址,观察产生的现象。
提示,如果读者对实验的结果感到疑惑,请阅读第1章中的1.15节。
答:
Step1:先填写不同的数据,输入刚刚生产日期ASCII码对应的内存单元内容,在窗口右上方生成了一个0/1的图案:
Step2:填写题目要求的数据
Step3:改变填写的地址,可以发现显示的内容与上图有不同
注:
可以得知内存地址空间B8100H处是显存地址空间,向这里的内存单元中写入数据,这个数据就被写入显存中,然后会被显卡输出到显示器上。
本博文是我在学习《汇编语言》王爽第三版时,一边学习一边做的笔记,可能会存在一些错误,欢迎大家来讨论和指正。