实验1 用机器指令和汇编指令编程

实验 1

实验任务 1

e 命令:

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

1

使用 t 命令单步执行,观察各寄存器值的变化:

  • 单步执行 mov ax,4E20;ax 寄存器值变为 4E20,ip = ip + 3
    6

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

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

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

a 命令:

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

5

使用 t 命令单步执行,观察各寄存器值的变化:

  • 单步执行 mov ax,4D20;ax 寄存器值变为 4D20,ip = ip + 3
    6

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

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

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

实验任务 2

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

10

修改 cs 与 ip 的值并开始单步执行,前三条指令的功能为:

  • 将 1 送入 ax
  • 将 ax 内的值与 ax 内的值相加,将结果送入 ax
  • 跳转到 2000:0003

11

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

12

实验任务 3

使用 d 命令查看内存 FFF00H ~ FFFFFH 内存单元的值

13

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

14

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

15

原因是内存 FFF00H ~ FFFFFH 所在区域为 ROM 地址空间,只能读出但无法写入信息,向其写入数据的操作是无效的。

实验任务 4

进行如下操作:

16

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

17

实验 2

实验任务 1

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

18

理论分析:

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 的硬件设计,我们无法将数据直接送入段寄存器,需要进行中转)
    19

  • 将以 ds 为段地址,0 为偏移地址的内存单元的数据送入 ax,即将 5150H 送入 ax

  • 将以 ds 为段地址,2 为偏移地址的内存单元的数据加给 ax,即将 5352H 加给 ax,ax 值变为 A4A2H

  • 将以 ds 为段地址,4 为偏移地址的内存单元的数据送入 bx,即将 5554H 送入bx

  • 将以 ds 为段地址,6 为偏移地址的内存单元的数据加给 bx,即将 5756H 加给 bx,bx 值变为 ACAAH
    (字单元由两个地址连续的内存单元组成,高地址内存单元中存放字型数据的高位字节,低地址内存单元中存放字型数据的低位字节,其起始地址为低位字节地址)
    20

  • sp = sp - 2,以当前栈顶元素前面的单元为新的栈顶,将 ax 寄存器内的值送入栈顶内存单元,即 sp 变为 00FEHA4A2H 入栈

  • sp = sp - 2,以当前栈顶元素前面的单元为新的栈顶,将 bx 寄存器内的值送入栈顶内存单元,即 sp 变为 00FCHACAAH 入栈

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

  • 将栈顶 内存单元的值送入 bx 寄存器,sp = sp + 2,以当前栈顶后面的单元为新的栈顶,即 A4A2H 出栈进入 bx,sp 变为 0100H
    21

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

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

实际结果与理论分析一致。

实验任务 2

       使用 a 命令将程序段写入内存,使用 e 指令设置 2000:0 ~ 2000:000F 内存内的值置为 0,使用 d 命令观察此内存区域段内的值。
24

单步执行:

  • 将 2000H 送入 ax,ip 变为 0103H;查看 2000:0 ~ 2000:0010 内存单元内的值

  • 将 ax 的值送入 ss 作为栈顶段地址,段地址变为 2000H

  • 将 10 送入 sp 作为栈顶的偏移地址,为 0010H,ip 变为 0108H,查看 2000:0 ~ 2000:0010 内存单元内的值
    25

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

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

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

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

       在此次实验中,前三行汇编指令用于设定栈顶段地址与栈顶的偏移地址。由于 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 为栈顶地址。
    31

  • 单步执行 mov ax,3123 后,ax 的值变为 3123,ip 增为 010B,内存地址内的值也做出如下变化:
    32
    可知,内存地址内的变化与先前的假设一致。

  • push ax 执行后,sp 改变为 000E,栈顶元素会覆盖原先 000E ~ 000F 内存空间内的数值,但此时通过观察我们发现 2000:0 ~ 2000:000F 内存单元内的值发生了如下变化:
    33
    对比推测可以得知,为了使内存单元内的值不被覆盖,ax、ip、cs 寄存器所在的 0006 ~ 000F 内存单元整体前移,为栈顶元素留出了一个字单元的空间。移动后,ip 的值也做了同步的修改,变为 010C,与假设的一致。

  • mov ax,3366 执行后,ax 的值变为 3377,ip 变为 010F,2000:0 ~ 2000:000F 内存地址内的值做出如下变化:
    34
    与提出的假设的一致。

  • push ax 执行后,sp 改变为 000C,栈顶元素会覆盖原先 000C ~ 000D 内存空间内的数值,可通过观察发现 2000:0 ~ 2000:000F 内存单元内的值再次发生了变化:
    35
    与先前一致,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 的字单元有何作用
posted @ 2020-10-10 16:57  maranlll  阅读(236)  评论(3)    收藏  举报