在8086汇编编程中,我们只能将数据加载到段寄存器中,首先将其加载到通用寄存器中,然后我们必须将它从这个通用寄存器移到段寄存器中。
为什么我们不能直接加载它?有没有被允许的特殊原因?
mov ax,5000H
和mov ax,[5000H]
之间有什么区别? [5000h]
是否表示内存位置5000h中的内容?
答案 0 :(得分:13)
请记住,汇编语言(任何汇编语言)的语法只是编写机器代码的人类可读方式。您可以在机器代码中执行的操作规则取决于处理器电子设备的设计方式,而不取决于汇编语法可以轻松支持的内容。
所以,只是因为看起来你可以写mov DS, [5000h]
而且从概念上来看似乎没有理由你不应该这样做,它真的是“有没有机制处理器可以从内存位置的内容加载段寄存器吗?“
在8086组装的情况下,我认为原因很简单,工程师只是没有创建一条电路,可以将信号从存储器I / O数据线馈送到写入段寄存器的线路
为什么呢?我有几个理论,但没有权威知识。
最可能的原因只是简化设计:需要额外的布线和门才能做到这一点,这是一个不常见的操作(这是70年代),它不值得芯片中的房地产。这并不奇怪; 8086已经超出允许任何正常寄存器连接到ALU(算术逻辑单元),允许任何寄存器用作累加器。我敢肯定这样做并不便宜。当时大多数处理器只允许一个寄存器( 累加器)用于此目的。
允许从存储器读取中写入段寄存器也可能导致几个奇怪的边缘情况很难在电路中找到。毕竟,要写入的段寄存器可能用于寻址源操作数。
就括号而言,您是对的。假设记忆位置5000h包含数字4321h。 mov ax, 5000h
将值5000h置于ax中,而mov ax, [5000h]
将4321h从内存加载到ax中。本质上,括号的作用类似于C中的*
指针解引用运算符。
只是为了强调汇编是机器代码可以做的理想抽象这一事实,你应该注意到这两个变量不是具有不同参数的相同指令,而是完全不同的操作码。他们可以使用 - 比如 - MOV
作为第一个和MVD
(MoVe Direct寻址内存)用于第二个操作码,但他们必须已经确定括号语法更容易让程序员记住。
答案 1 :(得分:4)
段寄存器与通用寄存器不同(在硬件级别上)。当然,正如Mike W在评论中所说的那样,只有英特尔开发人员知道你不能直接将直接值移入段寄存器的确切原因。但我想,这是因为设计很简单。请注意,此选择不会影响处理器性能,因为段寄存器操作非常少见。因此,一条指令越多,一条指令就越不重要。
在x86汇编语法的所有合理实现中,mov reg, something
将立即数something
移动到寄存器reg
。例如:
NamedConst = 1234h
SomeLabel:
mov edx, 1234h ; moves the number 1234h to the register edx
mov eax, SomeLabel ; moves the value (address) of SomeLabel to eax
mov ecx, NamedConst ; moves the value (1234h in this case) to ecx
关闭方括号中的数字意味着具有此地址的内存内容将移动到寄存器:
SomeLabel dd 1234h, 5678h, 9abch
mov eax, [SomeLabel+4] ; moves 5678h to eax
mov ebx, dword [100h] ; moves double word memory content from the
; address 100h in the data segment (DS) to ebx.
答案 2 :(得分:3)
x86机器代码只有一个用于移动到Sreg的操作码。该操作码是
8E /r
mov Sreg, r/m16
,允许注册或内存来源(但不是即时)。
与其他答案中的某些声明相反, mov ds, [5000h]
运行正常,假设地址5000h
的16个字节为您所在的模式保留了有用的分段值。(实模式,它们直接用作数字与受保护,其中Sreg值是索引LDT / GDT的选择器)。
x86总是使用不同的操作码作为指令的直接形式(使用常量编码作为机器代码的一部分)与寄存器/内存源版本。例如add eax, 123
汇总到add eax, ecx
的不同操作码。但是add eax, [esi]
与add r, r/m32
的操作码add eax, ecx
相同,只是一个不同的ModR / M字节。
NASM列表,来自nasm sreg.asm -l/dev/stdout
,以16位模式组装平面二进制文件并生成列表。
我手动编辑将字节分成opcode modrm extra
。这些都是单字节操作码(在ModRM字节的/ r字段中没有额外的操作码位借用空间),因此只需查看第一个字节以查看它是什么指令,并注意两个指令共享相同的操作码。
address machine code source ; comments
1 00000000 BE 0050 mov si, 5000h ; mov si, imm16
2 00000003 A1 0050 mov ax, [5000h] ; special encoding for AX, no modrm
3 00000006 8B 36 0050 mov si, [5000h] ; mov r16, r/m16 disp16
4 0000000A 89 C6 mov si, ax ; mov r/m16, r16
5
6 0000000C 8E 1E 0050 mov ds, [5000h] ; mov Sreg, r/m16
7 00000010 8E D8 mov ds, ax ; mov Sreg, r/m16
8
9 mov ds, 5000h
9 ****************** error: invalid combination of opcode and operands
支持mov Sreg, imm16
编码需要单独的操作码。这将需要8086的额外晶体管进行解码,并且它将耗尽更多的操作码编码空间,为将来的扩展留下更少的空间。我不确定8086 ISA的架构师认为哪些更重要。
请注意,8086具有特殊的mov AL/AX, moffs
操作码,从绝对地址加载累加器时可保存1个字节。但它无法为mov
提供一个操作码 - 立即给Sreg?这个设计决定很有道理。您多久需要重新加载一个段寄存器?很少,在真正的大型程序中,它通常不会有一个常数(我认为)。但是在使用静态数据的代码中,您可能正在将累加器加载/存储到循环内的固定地址。 (8086的代码获取非常弱,所以代码大小=大部分时间的速度)。
另请注意,只需一条额外指令(如mov Sreg, r/m16
)即可将mov ax, 4321h
用于汇编时常数。但是如果我们只有mov Sreg, imm16
,那么运行时变量段值将需要自修改代码。 (所以显然你不会遗漏r/m16
源版本。)我的观点是,如果你只有一个,它肯定会成为注册/内存源版本。
答案 3 :(得分:0)
我记得在当天回顾原因。我没有在我面前的那份文件,所以请原谅我挥手。
从内存位置或常量加载段寄存器涉及内存周期。如果内存对齐混乱,读取16位值可能需要两个内存周期。在两个周期之间,段寄存器的值无效。现在假设您正在弄乱堆栈段寄存器并发生中断:这是您的手推车;享受骑行!