实验目的:
- 进一步学习汇编语言基本指令使用方法;
- 学习DEBUG调试工具的基本使用方法;掌握debug调试命令a、u、r、d、t、g等
- 逐条观察程序指令的执行过程,理解指令的功能,理解程序自动、顺序地执行的概念,实践其过程(重点)。
实验题目:
- 编写、输入一个32位二进制数相加程序,逐条执行指令,观察指令执行情况。
- 使用已经过汇编和连接的小程序(t3-2.exe),将其装入内存,逐条执行指令,观察执行过程和结果。在实验过程中,使用E命令修改XXX和YYY单元中的数据,再重新计算其结果。
实验过程:
1.编写,输入一个32位的二进制数相加程序,我编写的程序如下:
DATA SEGMENT
NUMH1 DW 0001H ;被加数1的高位
NUML1 DW 1234H ;被加数1的低位
NUMH2 DW 0002H ;被加数2的高位
NUML2 DW 0FFFFH ;被加数2的低位
ANSH DW ? ;结果的高位
ANSL DW ? ;结果的低位
DATA ENDS
CODE SEGMENT
ASSUME CS:CODE, DS:DATA
START: MOV AX,DATA
MOV DS,AX
MOV AX,NUML1
ADD AX,NUML2 ;低位相加
MOV ANSL,AX ;存储低位结果
MOV AX,NUMH1
ADC AX,NUMH2 ;高位和CF相加,确保进位
MOV ANSH,AX ;存储高位结果
MOV AH,4CH
INT 21H ;程序结束
CODE ENDS
END START
代码说明:因为要求的是32位的加法,但是我们用的寄存器都是16位的,所以需要我们用两个DW类型的数据来存一个32位的数据,另外在进行加法的过程中,我选择的是先ADD加低位,再ADC加高位,确保进位,之后用ANSH,ANSL来存储最后的结果的高低位。
代码流程图:
程序调试过程:
生成obj文件和exe文件
1.开始debug写好的程序(前面masm,link后面不需要加后缀,但是这里需要加EXE后缀,否则会报错,因为文件夹中存在同名文件,没有后缀无法区分)同时使用u命令将程序反汇编,观察其汇编指令的与源程序的对应关系,观察每一条指令的代码、存放地址、指令代码长度等;(重要):
2.使用r命令读出各寄存器中的数值,从CS:IP寄存器中的数值确定程序当前应执行的指令;(结合计算机硬件知识加以理解):
可以发现,CS:IP中的数值就是之前反汇编之后程序给出的地址后面的指令内容,当前执行的指令在空间中的076B:0000,内容为B86A07,即MOV AX,076A。
3.开始逐条执行程序:
使用t命令逐行执行程序:
这一步的目的是将AX的值赋给DS,结合上一步获取数据段地址,实现DATA数据段中内容的获取。
这里CS:IP的值为076B:0003 内容为8ED8 执行的操作为MOV DS,AX
在下图中看到,这里DS的值变为076A。
这一步的目的是给AX赋一个被加数的低位的值。这里选用的是NUML1,在空间中的地址为DS:[0002]。
这里CS:IP的值为076B:0005 内容为A10200 执行的操作为MOV AX,[0002]
在下图中看到,这里看到AX的值变为1234H
这一步的目的是将两个被加数的低位相加,使用的是ADD命令,因为不需要考虑进位的影响。
在下图中看到AX的数值已经改变为1234H+0FFFFH=1233H(只看低位)
这里CS:IP的值为076B:0008 内容为03060600 执行的操作为ADD AX,[0006]
另外此时的标志位出现变化,因为在执行ADD操作的时候出现了进位,所以CF标志位从NC变成CY。
这一步的目的是把上一步低位相加的结果给到我们之前在数据段中声明的ANSL,这里他的地址为DS:000A。
这里CS:IP的值为076B:000C 内容为A30A00 执行的操作为MOV [000A],AX
这一步的目的是将之前被加数1的高位赋值给到AX。
这里CS:IP的值为076B:000F 内容为A10000,执行的操作为MOV AX,[0000]
从下图中可以看到AX的内容变为0001H。没有其他变化。
这一步的目的是将两个被加数的高位和CF一起相加,完成32位加法的同时实现了进位。因为需要CF参与计算,所以这里使用的是ADC指令。
这里CS:IP的值为076B:0012 内容为13060400 执行的操作为ADC AX,[0004]
从下图可以看出,AX的数值为0001+0002+CF(1)=0004H。
此时CF标志位使用过后从CY变回NC。
这一步的目的是把高位相加的结果给到数据段中已经声明了的ANSH中,这里他的地址为DS:0008。
这里CS:IP的值为076B:0016 内容为A30800 执行的操作为MOV [0008],AX
程序执行到MOV AH,4CH,程序结束。
4.调用D命令,查看最后的结果:
其中ANSH的地址为DS:0008,也就是0004H
ANSL的地址为DS:000A,也就是1233H
程序实现了32位数加法:00011234H+0002FFFFH=00041233H
2.使用已经过汇编和连接的小程序(t3-2.exe),将其装入内存,逐条执行指令,观察执行过程和结果。在实验过程中,使用E命令修改XXX和YYY单元中的数据,再重新计算其结果。
装入内存:
逐条执行指令:
第一条指令的状态需要使用r命令来观察,这里的目的是将数据段的地址赋值给AX。
这里CS:IP的值为076B:0000 内容为B86A07 执行的操作为MOV AX,076A
下图中可以看到AX的数据变为了076A。
这一步的目的是将AX的值赋给DS,结合上一步获取数据段地址,实现DATA数据段中内容的获取。
这里CS:IP的值为076B:0003 内容为8ED8 执行的操作为MOV DS,AX
在下图中看到,这里DS的值变为076A。
这一步的目的是将XXX的值赋给AX,在空间中XXX的地址为DS:0000
这里CS:IP的值为076B:0005 内容为A10000 执行的操作为MOV AX,[0000]
在下图中看到,这里AX的数值变为1234H
这一步的目的是将DX清零,所以选择的是DX和自己做异或操作。
这里CS:IP的值为076B:0008 内容为33D2 执行的操作为XOR DX,DX
因为初始时候DX的数值就是0000,所以没有发生变化。如果DX之前的状态数值非0,这一步后会清零。这里标志位也发生变化,ZF标志位变为ZR,PF标志位变为PE,这是因为标志位看运算的结果,DX为0000,这两个标志位对应结果。
这一步的目的是实现AX+AX->AX的操作,将AX的数值大小扩大二倍。
这里CS:IP的值为076B:000A 内容为03C0 执行的操作为ADD AX,AX
在下图中看到,这里AX的值变为2468H。
这一步的目的是将判断上一步AX*2的操作是否出现了进位,使用ADC指令进行DX+0+CF的操作,如果进位的话DX加一,如果没有DX不变。
这里CS:IP的值为076B:000C 内容为83D200 执行的操作为ADC DX,+00
上一步的操作没有出现进位,所以在下图中DX的数值没有发生改变。
这一步的目的是实现AX+AX->AX的操作,将AX的数值大小扩大二倍。
这里CS:IP的值为076B:000F 内容为03C0 执行的操作为ADD AX,AX
在下图中看到,这里AX的值变为48D0H。
这一步的目的是将判断上一步AX*2的操作是否出现了进位,使用ADC指令进行DX+0+CF的操作,如果进位的话DX加一,如果没有DX不变。
这里CS:IP的值为076B:0011 内容为83D200 执行的操作为ADC DX,+00
上一步的操作没有出现进位,所以在下图中DX的数值没有发生改变。
这一步的目的是实现AX+AX->AX的操作,将AX的数值大小扩大二倍。
这里CS:IP的值为076B:0014 内容为03C0 执行的操作为ADD AX,AX
在下图中看到,这里AX的值变为91A0H。
这一步的目的是将判断上一步AX*2的操作是否出现了进位,使用ADC指令进行DX+0+CF的操作,如果进位的话DX加一,如果没有DX不变。
这里CS:IP的值为076B:0016 内容为83D200 执行的操作为ADC DX,+00
上一步的操作没有出现进位,所以在下图中DX的数值没有发生改变。
这一步的目的是实现AX+AX->AX的操作,将AX的数值大小扩大二倍。
这里CS:IP的值为076B:0019 内容为03C0 执行的操作为ADD AX,AX
在下图中看到,这里AX的值变为2340H。出现进位的情况,标志位中的CF标志位从NC变为了CY
这一步的目的是将判断上一步AX*2的操作是否出现了进位,使用ADC指令进行DX+0+CF的操作,如果进位的话DX加一,如果没有DX不变。
这里CS:IP的值为076B:001B 内容为83D200 执行的操作为ADC DX,+00
上一步的操作出现了进位,CF=1,所以在下图中DX的数值变为了0001H。
这一步的目的是实现AX+YYY->AX的操作。
这里CS:IP的值为076B:001E 内容为03060200 执行的操作为ADD AX,[0002]
在下图中看到,这里AX的值变为79B8H。
这一步的目的是将判断上一步AX+YYY->AX的操作是否出现了进位,使用ADC指令进行DX+0+CF的操作,如果进位的话DX加一,如果没有DX不变。
这里CS:IP的值为076B:0022 内容为83D200 执行的操作为ADC DX,+00
上一步的操作没有出现进位,所以在下图中DX的数值没有发生改变。
这一步的目的是将计算结果的低位存到数据段定义的ZZZ的低位中,ZZZ在空间中的地址为:DS:0004
这里CS:IP的值为076B:0025 内容为A30400,执行的操作为MOV [0004],AX
这一步的目的是将计算结果的高位存到数据段定义的ZZZ的高位中,也就是ZZZ+2,在空间中的地址为:DS:0006。
这里CS:IP的值为076B:0028 内容为89160600,执行的操作为MOV [0006],AX
到这里程序结束。
调用D命令查看最后的结果:
T3-2程序的功能为:
XXX乘以2的四次方+YYY的结果赋值给ZZZ,最后的结果为000179B8H
使用E命令修改XXX和YYY单元中的数据,再重新计算其结果。
需要注意的是,这里需要先使用t命令进行两步,把数据段的地址给到DS之后再进行这样的操作。这里我们把XXX的数值改为1111,YYY的数值改为2222,t3-2程序所计算的结果就是1111*16+2222,最后计算的结果为:00013332H
这里红框里的就是修改过的XXX的数值,黄框里的是修改过的YYY的数值,蓝框里的是最后运算的结果。
思考题
- 程序中的指令、数据是如何在存储器中存储的?
- 如何理解程序执行过程的顺序性?
- 如何理解标志位寄存器中的数据?
答:1.对于指令的存储,可以通过u指令来进行反汇编查看指令的存储地址以及相应的机器码。可以看到在该程序中代码段地址为076BH,并且从偏移地址0000H开始存储代码段的命令。其中第三列就是每一个指令的机器码。
另外,可以通过D命令对机器码进行查看。
数据存储在DS段,数据高低字节分别存储在高低地址。
2.程序执行过程在代码通过masm编译之后就已经在CS段中确定了,我们通过u命令将程序反汇编之后可以观察到程序执行的汇编指令所在的空间地址,按照地址顺序执行程序。
3.标志位寄存器的值遵循以下表格:
标志位寄存器再本次实验中所祈祷的作用是用来存储相关指令的某些执行结果,比如在两个代码中都涉及到的先ADD再ADC,就是利用了标志位寄存器中的CF实现了进位。其它标志位例如PF和ZF同样发生了变化,表明低八位的1的个数发生了变化或者一次运算的结果是0000,但是在该汇编程序中并没有起到具体切实的作用。