环境:我在
MS-DOS 6.22
中使用VirtualBox
虚拟机。
任务:借助位于
debug.exe
中的内置 C:\DOS\DEBUG.EXE
程序,我想将一些指令写入内存。详细进度如下图所示:
下面是我手动编写的屏幕截图的文本版本:
C:\>debug
-a 1000:0
1000:0000 mov ax,ffff
1000:0003 mov ds,ax
1000:0005 mov ax,2200
^error
1000:0006
-u 1000:0 6
1000:0000 b8ffff mov ax,ffff
1000:0003 8ed8 mov ds,ax
1000:0005 a5 movsw
1000:0006 42 inc dx
-q
同样令人困惑的是,错误指令被转向同一内存地址中的
movsw
。
我试图使用
a
中的debug.exe
命令将这些命令写入内存:
mov ax,ffff
mov ds,ax
mov ax,2200
mov ss,ax
mov sp,0100
mov ax,[0]
mov bx,[2]
push ax
push bx
pop ax
pop bx
后来我打算使用其他命令来执行指令并观察系统的行为,以达到学习Assembly的目的。
但是,我卡在了输入
mov ax,2200
指令的步骤,程序在a
处提示错误。我不知道为什么会发生这种情况。
我在VirtualBox中安装了一台新的Windows2000虚拟机,并在
cmd
中做了同样的事情(输入debug.exe
并使用a
插入指令)。它成功了!那么问题可能与我使用的MS-DOS 6.22
虚拟机有关?
但是,我仍然很好奇
MS-DOS 6.22
可能出了什么问题,因为这种情况对我来说似乎很不寻常。会不会和debug.exe
有关?或者是否有我可能不知道的在内存中设置指令的特定规则?
你似乎随意选择了地址
1000:0
,但它不属于你,你也不知道它可能用来做什么。 事实上,它恰好包含一些 DEBUG 自己的内部数据,因此覆盖它会导致其汇编器行为异常。
不要这样做。如果您只使用
a
而不给出地址,您将组装到为您预留的内存中,并且可以安全地写入。
具体发生的情况如下。 我们可以按照DEBUG的源代码进行操作。
(源代码适用于 MS-DOS 4.0 而不是 6.22,但是当我使用 Bochs 逐步执行 6.22 时,相关部分的反汇编与 4.0 源代码匹配,因此它显然没有改变。错误行为实际上是我的测试与您的测试略有不同,可能是因为 DEBUG 加载到不同的地址,但是您的问题中有足够的信息来推断发生了什么你。)
您覆盖的字节提供了提示:
CXZ\0JNB
。 我们位于 DEBUG 的助记符字符串列表中,特别是here。 您的第一条指令 MOV AX, FFFF
是三个字节长,因此它会覆盖 CXZ
。 这不会造成任何有形的伤害。 但是,您的第二条指令 MOV DS, AX
会覆盖 1000:3
处的空字节,这是 JCXZ
和 JNB
之间的分隔符。 现在伤害已经造成了。
匹配助记符的汇编代码是这里。 它扫描字符串列表来搜索匹配项,每次失败后递增 CX。 因此,当它最终找到匹配项时,CX 就是列表中匹配助记词的索引。 该列表与
OPTAB
表的顺序相同,其中包含指向解析指令操作数的函数的指针。
当您覆盖
JCXZ
后面的空字节时,您实际上将 JCXZ
和 JNB
合并为一个助记词,现在该列表与 OPTAB
不同步。 因此,当我们尝试解析第三条指令 MOV
(按字母顺序排列在 JCXZ
和 JNB
之后)时,我们仅在传递它们时递增 CX 一次。 这意味着当我们到达列表中的 MOV
时,CX 增加的次数太少了。 因此,当我们使用该值(现在在 BX 中)作为 OPTAB
的索引时,它指向的不是 MOV
的 正确条目,而是指向 前一个。 你猜对了,MOVSW
(或如评论所说的 MOVW
)。
因此,我们组装了
MOVSW
的操作码,这就是您在转储中看到的内容。MOVSW
不接受 任何操作数,因此 AX, 2200
行的其余部分不会被解析为 MOV
的操作数,而是解析为新指令。 由于 AX
不是有效的指令助记符,因此我们收到错误。