实验1 用机器指令和汇编指令编程
实验 1
实验任务 1
e 命令:
使用 e 命令,从 1000:0 地址开始写入机器码,并修改 cs、ip 寄存器的值,使用 r 命令观察当前各寄存器的值与当前要读取、执行的指令。

使用 t 命令单步执行,观察各寄存器值的变化:
-
单步执行 mov ax,4E20;ax 寄存器值变为 4E20,ip = ip + 3

-
单步执行 add ax,1413;ax 寄存器值变为 6236,ip = ip + 3

-
单步执行 mov bx,2000;bx 寄存器值变为 2000,ip = ip + 3

-
单步执行 add ax,bx;ax 寄存器值变为 8236,ip = ip + 3

a 命令:
使用 a 命令以汇编指令的形式在内存中写入机器指令,并修改 cs、ip 寄存器的值,使用 r 命令观察当前各寄存器的值与当前要读取、执行的指令

使用 t 命令单步执行,观察各寄存器值的变化:
-
单步执行 mov ax,4D20;ax 寄存器值变为 4D20,ip = ip + 3

-
单步执行 add ax,1413;ax 寄存器值变为 6236,ip = ip + 3

-
单步执行 mov bx,2000;bx 寄存器值变为 2000,ip = ip + 3

-
单步执行 add ax,bx;ax 寄存器值变为 8236,ip = ip + 3

实验任务 2
使用 a 指令向从 2000:0 开始的内存单元中写入 3 条指令,计算 2 的 8 次方

修改 cs 与 ip 的值并开始单步执行,前三条指令的功能为:
- 将 1 送入 ax
- 将 ax 内的值与 ax 内的值相加,将结果送入 ax
- 跳转到 2000:0003

进行 8 次加法操作后 ax 的值为 2 的 8 次方的值:0100H,即 256。

实验任务 3
使用 d 命令查看内存 FFF00H ~ FFFFFH 内存单元的值

在右下角可以发现生产日期为 92.01.01,并尝试对其进行修改

再次查看所在区间段,发现日期并未被成功修改

原因是内存 FFF00H ~ FFFFFH 所在区域为 ROM 地址空间,只能读出但无法写入信息,向其写入数据的操作是无效的。
实验任务 4
进行如下操作:

发现屏幕上出现了 4 个图像,原因为 B810:0000 为显存地址空间,修改显存内容即会改变屏幕上显示的内容。对语句地址与值进行修改后可以得到不同的结果:

实验 2
实验任务 1
使用 e 命令修改内存单元 0022:0 ~ 0022:7,使用 a 命令将下续程序段写入内存

理论分析:
mov ax,2200
mov ds,ax
mov ax,2200
mov ss,ax
mov sp,0100
mov ax,[0] ; ax = 5150
add ax,[2] ; ax = A4A2
mov bx,[4] ; bx = 5554
add bx,[6] ; bx = ACAA
push ax ; sp = 00FE; 修改的内存单元的地址是 2200:00FE,内容是 A4A2
push bx ; sp = 00FC; 修改的内存单元的地址是 2200:00FC,内容是 ACAA
pop ax ; sp = 00FE; ax = ACAA
pop bx ; sp = 0100; bx = A4A2
push [4] ; sp = 00FE; 修改的内存单元的地址是 2200:00FE,内容是 5554
push [6] ; sp = 00FC; 修改的内存单元的地址是 2200:00FC,内容是 5756
单步执行:
-
将 0022H 送入 ax
-
将 ax 内的值送入 ds 作为内存单元的段地址
-
将 2200H 送入 ax
-
将 ax 内的值送入 ss 作为栈顶段地址
(由于 8086CPU 的硬件设计,我们无法将数据直接送入段寄存器,需要进行中转)

-
将以 ds 为段地址,0 为偏移地址的内存单元的数据送入 ax,即将 5150H 送入 ax
-
将以 ds 为段地址,2 为偏移地址的内存单元的数据加给 ax,即将 5352H 加给 ax,ax 值变为 A4A2H
-
将以 ds 为段地址,4 为偏移地址的内存单元的数据送入 bx,即将 5554H 送入bx
-
将以 ds 为段地址,6 为偏移地址的内存单元的数据加给 bx,即将 5756H 加给 bx,bx 值变为 ACAAH
(字单元由两个地址连续的内存单元组成,高地址内存单元中存放字型数据的高位字节,低地址内存单元中存放字型数据的低位字节,其起始地址为低位字节地址)

-
sp = sp - 2,以当前栈顶元素前面的单元为新的栈顶,将 ax 寄存器内的值送入栈顶内存单元,即 sp 变为 00FEH,A4A2H 入栈
-
sp = sp - 2,以当前栈顶元素前面的单元为新的栈顶,将 bx 寄存器内的值送入栈顶内存单元,即 sp 变为 00FCH,ACAAH 入栈
-
将栈顶 内存单元的值送入 ax 寄存器,sp = sp + 2,以当前栈顶后面的单元为新的栈顶,即 ACAAH 出栈进入 ax,sp 变为 00FEH
-
将栈顶 内存单元的值送入 bx 寄存器,sp = sp + 2,以当前栈顶后面的单元为新的栈顶,即 A4A2H 出栈进入 bx,sp 变为 0100H

-
sp = sp - 2,以当前栈顶元素前面的单元为新的栈顶,将以 ds 为段地址,4 为偏移地址的内存单元的数据入栈, 即 sp 变为 00FEH,将 5554H 送入栈顶内存单元
-
sp = sp - 2,以当前栈顶元素前面的单元为新的栈顶,将以 ds 为段地址,6 为偏移地址的内存单元的数据入栈,即 sp 变为 00FCH,将 5756H 送入栈顶内存单元

实际结果与理论分析一致。
实验任务 2
使用 a 命令将程序段写入内存,使用 e 指令设置 2000:0 ~ 2000:000F 内存内的值置为 0,使用 d 命令观察此内存区域段内的值。

单步执行:
-
将 2000H 送入 ax,ip 变为 0103H;查看 2000:0 ~ 2000:0010 内存单元内的值
-
将 ax 的值送入 ss 作为栈顶段地址,段地址变为 2000H
-
将 10 送入 sp 作为栈顶的偏移地址,为 0010H,ip 变为 0108H,查看 2000:0 ~ 2000:0010 内存单元内的值

-
执行 mov ax,3123H,将 3123H 送入 ax,ip 变为 010BH,查看 2000:0 ~ 2000:0010 内存单元内的值

-
执行 push ax,此时 sp = sp - 2,以当前栈顶元素前面的单元为新的栈顶,将 ax 内的值入栈,ip 变为 010CH,查看 2000:0 ~ 2000:0010 内存单元内的值

-
执行 mov ax, 3366H,将 3366H 送入 ax,ip 变为 010FH,查看 2000:0 ~ 2000:0010 内存单元内的值

-
执行 push ax,此时 sp = sp - 2,以当前栈顶元素前面的单元为新的栈顶,将 ax 内的值入栈,ip 变为 0110H,查看 2000:0 ~ 2000:0010 内存单元内的值

在此次实验中,前三行汇编指令用于设定栈顶段地址与栈顶的偏移地址。由于 8086CPU 的硬件设计,我们无法将数据直接送入段寄存器,所以需要使用 mov ax,2000 进行中转,在使用 mov ss,ax 来设置栈顶段地址;mov sp,10 设置栈顶的偏移地址为 10H。由于中断机制,Debug的 T 命令在执行修改寄存器 ss 的指令时,下一条指令会紧接着被执行,因此我们未在 T 指令的单步执行中看见 mov sp,10 指令,但它确实被执行了。
此外,通过单步调试我们可以观察到,在执行完前 3 条指令后,在后续的 4 条指令每一条指令被执行之后,2000:0 ~ 2000:000F 内存单元内的值都会发生变化。在对各条指令执行之后对 2000:0 ~ 2000:000F 内存单元内的值进行观察,可以发现,其中部分字单元的值与 ax、cs、ip 寄存器的值一致,于是,做出以下假设:
-
在执行完 mov ss,ax 与 mov sp,10 后,2000:0006 为 ax 寄存器的地址,2000:000A 为 ip 寄存器的地址,2000:000C 为 cs 寄存器的地址,2000:0010 为栈顶地址。

-
单步执行 mov ax,3123 后,ax 的值变为 3123,ip 增为 010B,内存地址内的值也做出如下变化:

可知,内存地址内的变化与先前的假设一致。 -
push ax 执行后,sp 改变为 000E,栈顶元素会覆盖原先 000E ~ 000F 内存空间内的数值,但此时通过观察我们发现 2000:0 ~ 2000:000F 内存单元内的值发生了如下变化:

对比推测可以得知,为了使内存单元内的值不被覆盖,ax、ip、cs 寄存器所在的 0006 ~ 000F 内存单元整体前移,为栈顶元素留出了一个字单元的空间。移动后,ip 的值也做了同步的修改,变为 010C,与假设的一致。 -
mov ax,3366 执行后,ax 的值变为 3377,ip 变为 010F,2000:0 ~ 2000:000F 内存地址内的值做出如下变化:

与提出的假设的一致。 -
push ax 执行后,sp 改变为 000C,栈顶元素会覆盖原先 000C ~ 000D 内存空间内的数值,可通过观察发现 2000:0 ~ 2000:000F 内存单元内的值再次发生了变化:

与先前一致,ax、ip、cs 寄存器所在的 0004 ~ 000D 内存再次整体前移,为栈顶元素留出一个字单元的空间,ip 寄存器的值也修改为 0110。与最开始的假设一致。
到此,我们可以得出这样的假设:在由于栈顶的移动而出现内存单元的冲突时,8086CPU 给出的解决方法是将出现冲突的部分整体前移,来避免插入栈顶元素造成的冲突。
实验总结
实验收获:
-
语句使用:
- a 命令后接内存地址,用于以汇编指令的新是向指定内存中写入机器指令,默认地址为 cs:ip
- d 命令用于查看某一内存地址范围内的值
- e 命令用于向指定内存地址中写入机器码,默认地址为 cs:ip
- g 命令用于执行指定地址范围内的机器指令
- r 命令用于查看修改寄存器的值
- u 命令用于反汇编
- t 命令可以后接地址与数字,用于单步执行执行内存地址后数条机器指令
-
8086PC 机内存地址空间分布:
- 00000 ~ 9FFFF 为主存储器地址空间
- A0000 ~ BFFFF 为显存地址空间
- C0000 ~ FFFFF 为各类 ROM 地址空间PC
-
内存中字的存储方式:
- 在内存中存储时,由于内存但愿是字节单元,则一个字要用两个地址联系的内存单元来存放,这个字的低位字节存放在低地址单元中,高位字节存放在高地址单元中,且字的起始地址为低位字节地址。
-
push 与 pop 语句栈顶的移动方式
- push 时 sp 先改变,将指向的单元作为新栈顶,再将内容送入 ss:sp 所指的内存单元
- pop 时先将 ss:sp 指向的内存单元处的内容送出,后改变 sp,将指向的单元作为新栈顶
尚存问题:
- 在实验 2 的实验任务 2 中,2000:0 ~ 2000:000F 内存单元内的值在前 3 条指令执行完之后才发生变化,先前未发生变化的原因不清楚,即不清楚寄存器地址变化的原因
- 实验 2 实验任务 2 中,不清楚值为 01A3 的字单元有何作用

浙公网安备 33010602011771号