assembly 8086-为什么我们不能把一个立即数移入段寄存器?

sg3maiej  于 2023-10-19  发布在  其他
关注(0)|答案(3)|浏览(126)

在8086汇编编程中,我们只能通过以下方式将数据加载到段寄存器中:首先将其加载到通用寄存器(或存储器)中,然后我们必须将其从那里移动到段寄存器。
例如,这两个都有效

mov    ax, 5000h
  mov    ds, ax

;;; or if you don't have a spare register, this works in modes other than 64-bit
  push   5000h
  pop    ds

但是mov ds, 5000h不是有效的x86指令。
为什么不能直接从一个immediate加载呢?有什么特殊的理由不允许吗?

uqdfh47h

uqdfh47h1#

请记住,汇编语言(任何汇编)的语法只是一种人类可读的编写机器代码的方式。你能用机器码做什么的规则取决于处理器的电子设备是如何设计的,而不是取决于汇编程序语法能容易地支持什么。
所以,仅仅因为它看起来像你可以写mov DS, 5000h,而且从概念上看,似乎没有理由你不应该这样做,它实际上是关于“是否有一种机制,通过这种机制,处理器可以直接从立即值加载段寄存器?“
在8086组件的情况下,我认为原因很简单,工程师们只是没有创建一个电路,可以从存储器I/O数据线向写入段寄存器的线馈送信号。
为什么?我有几个理论,但没有权威的知识。
最可能的原因是简化设计:这需要额外的布线和门电路来完成,而且这是一种不常见的操作(这是70年代的操作),不值得在芯片中使用真实的资产。这并不奇怪; 8086已经走极端,允许任何正常的寄存器连接到ALU(算术逻辑单元),这允许任何寄存器被用作累加器。我相信这可不便宜。当时大多数处理器只允许一个寄存器(the accumulator)用于此目的。

关于括号,你是正确的。假设内存位置5000 h包含数字4321 h。mov ax, 5000h将值5000 h放入ax,而mov ax, [5000h]将4321 h从内存加载到ax。本质上,方括号的作用就像C中的*指针解引用操作符。

为了强调汇编是机器代码可以做的事情的理想化抽象,你应该注意到这两种变体不是具有不同参数的同一条指令,而是完全不同的操作码。他们可以使用-比如说-MOV作为第一个操作码,MVD(MoVe直接寻址内存)作为第二个操作码,但他们肯定认为括号语法更容易让程序员记住。

tvz2xvvm

tvz2xvvm2#

x86机器码只有一个用于move-to-Sreg的操作码。该操作码是
8E /rmov Sreg, r/m16允许寄存器或内存源(但不是立即)。
与其他答案中的一些说法相反,mov ds, [5000h]运行得很好,假设地址5000h处的2个字节为您所处的模式保存了有用的段值。(真实的模式,直接用作数字,受保护,其中Sreg值是索引LDT / GDT的选择器)。
x86总是使用不同的操作码用于指令的立即形式(具有作为机器代码的一部分编码的常数),寄存器/内存源版本。例如,add eax, 123组装成与add eax, ecx不同的操作码。但是add eax, [esi]add eax, ecx是相同的add r, r/m32操作码,只是不同的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伊萨的架构师认为哪一个更重要。
请注意,8086有特殊的mov AL/AX, moffs操作码,当从绝对地址加载累加器时,可以保存1个字节。但是它不能为mov腾出一个操作码-直接到Sreg?这个设计决策是有道理的。多久需要重新加载一次段寄存器?非常罕见,在真实的大型程序中,它通常不会使用常量(我认为)。但是在使用静态数据的代码中,您可能会将累加器加载/存储到循环中的固定地址。(8086的代码提取能力非常弱,所以大多数时候代码大小=速度)。
还请记住,您可以使用mov Sreg, r/m16来处理时间常数,只需一条额外的指令(如mov ax, 4321h)。但是如果我们只有mov Sreg, imm16,运行时变量段值将需要自修改代码。(所以显然你不会遗漏r/m16源代码版本。)我的观点是,如果你只打算有一个,它肯定会是寄存器/内存源代码版本。

xoshrz7s

xoshrz7s3#

关于段寄存器

段寄存器与通用寄存器不同(在硬件级别上)。当然,正如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.

相关问题