YASM/NASM x86 程序集中直接括号与方括号的基本用法

Basic use of immediates vs. square brackets in YASM/NASM x86 assembly

提问人:InvalidBrainException 提问时间:4/28/2012 最后编辑:Peter CordesInvalidBrainException 更新时间:7/29/2019 访问量:27136

问:

假设我声明了以下内容:

section .bss
buffer    resb     1

这些说明如下:section .text

mov    al, 5                    ; mov-immediate
mov    [buffer], al             ; store
mov    bl, [buffer]             ; load
mov    cl, buffer               ; mov-immediate?

我理解 bl 将包含值 5,而 cl 将包含变量的内存地址是否正确?buffer

我对两者之间的区别感到困惑

  • 将直接项移动到寄存器中,
  • 将寄存器移入即时寄存器(输入什么,数据还是地址?)和
  • 将直接服务器移动到不带括号的寄存器中
    • 例如,vsmov cl, buffermov cl, [buffer]

更新:在阅读了回复后,我认为以下摘要是准确的:

  • mov edi, array将第 0 个数组索引的内存地址放在 中。即标签地址。edi
  • mov byte [edi], 3将 VALUE 3 放入数组的第 0 个索引中
  • 之后,现在包含数组的第 3 个索引的内存地址add edi, 3edi
  • mov al, [array]将第 0 个索引处的 DATA 加载到 中。al
  • mov al, [array+3]将第三个索引处的 DATA 加载到 中。al
  • mov [al], [array]无效,因为 x86 无法对 2 个显式内存操作数进行编码,并且因为只有 8 位,即使在 16 位寻址模式下也无法使用。引用内存位置的内容。(x86 寻址模式)al
  • mov array, 3无效,因为你不能说“嘿,我不喜欢存储的偏移量,所以我称之为 3”。immediate 只能是源操作数。array
  • mov byte [array], 3将值 3 放入数组的第零个索引(第一个字节)中。需要字节说明符来避免带有内存、即时操作数的指令的字节/字/dword 之间的歧义。否则,这将是组装时错误(操作数大小不明确)。

请提及其中任何一项是否为假。(编者注:我修复了语法错误/歧义,因此有效的语法实际上是有效的 NASM 语法。并链接了其他问答以获取详细信息)

程序集 x86 nasm 内存地址 yasm

评论

0赞 legends2k 9/13/2014
x86 asm 中的括号是什么意思的可能重复?
0赞 ecm 7/25/2019
array resb 0在标签“array”处保留一个零长度空间。例如,如果要在 .bss 部分中使用 10 个字节条目数组,则应指定 .resb 10

答:

27赞 mtvec 4/28/2012 #1

方括号本质上类似于取消引用运算符(例如,在 C 中)。*

所以,像这样的东西

mov REG, x

将 的值移动到 ,而xREG

mov REG, [x]

将指向的内存位置的值移动到 。请注意,如果是标签,则其值是该标签的地址。xREGx

至于你的问题:

我是否正确地理解 bl 将包含值 5 和 cl 是否包含变量缓冲区的内存地址?

是的,你是对的。但请注意,由于只有 8 位宽,因此它只包含地址的最低有效字节。CLbuffer

评论

1赞 Alexey Frunze 4/28/2012
cl将仅包含地址的 8 个最低有效位。
0赞 mtvec 4/28/2012
@Alex:是的,我刚刚意识到:-)
0赞 Robert Houghton 7/14/2019
当你说“如果 X 是一个标签,它的值就是该标签的地址”时,这是否意味着如果 X 的地址为 0x1234那么它作为标签时的值也是0x1234的?
0赞 Peter Cordes 7/15/2019
@InstructionPointer:在 MASM 语法中,是一个负载,与 相同。在 NASM 语法中,是标签地址的 mov-immediate 。(MASM 语法 mov eax, OFFSET varmov eax, [var]' 以避免歧义。如果你想要地址,没有可移植的语法可以明确地表示地址。(除了在 x86-64 PIC 代码中,假设 NASM 语法是最佳选择。除了 GAS .intel_syntax 需要相对于当前位置的地址......mov eax, var[var]mov eax, var). If you want a load, always use lea rdi, [var]default rellea rdi, [var + rip]
20赞 byrondrossos 4/28/2012 #2

确实,你的想法是正确的。也就是说,bl 将包含 5 和 cl 缓冲区的内存地址(实际上标签缓冲区本身就是一个内存地址)。


现在,让我解释一下您提到的操作之间的差异:

  • 可以使用 将直接数据移动到寄存器中。可能令人困惑的是,标签(例如缓冲区)本身就是包含地址的即时值。mov reg,imm

  • 您不能真正将寄存器移动到即时寄存器中,因为即时值是常量,例如 或 。您可以做的是将寄存器移动到常量指向的位置。你可以像.2FF1Ahmov [const], reg

  • 您还可以使用间接寻址,例如提供的 reg1 指向有效位置,它会将 reg1 指向的值传输到 reg2。mov reg2,[reg1]


因此,将缓冲区的地址移动到 cl(它可能会也可能不会给出正确的地址,因为 cl 只有一个字节长),而将获得实际值。mov cl, buffermov cl, [buffer]

总结

  • 使用 [a] 时,将引用 a 指向的位置的值。例如,如果 a 是 ,则 [a] 是指 RAM 中的地址 F5B1。F5B1
  • 标签是地址,即像 .F5B1
  • 存储在寄存器中的值不必作为 [reg] 引用,因为寄存器没有地址。事实上,寄存器可以被认为是即时值。

评论

1赞 Coldblackice 12/16/2012
我不精通装配,但在你顶部的中间要点,最后一个示例装配线不应该是相反的吗?即,?mov [const], reg
0赞 Peter Cordes 10/22/2021
@Coldblackice:是存储到内存的地址。这是一个奇怪的名称选择,但所有符号地址都是链接时间常量。例如,您可以在某个地方(例如在数据部分)发出一个 4 字节的绝对地址。或者可以是数字地址,例如,如果您正在编写处理传统硬件(如固定内存地址的 VGA 内存)的代码。例如,在 DS 中使用正确的值。mov [const], regconstdd constconstmov [0x0], ax
6赞 Alexey Frunze 4/28/2012 #3

你明白了。但是,有几个细节值得牢记:

  1. 地址可以而且通常大于 8 位可以容纳的地址(8 位、16 位、32 位、64 位)。因此,很可能不等于变量的地址。它只有地址的最低有效 8 位。clcxecxrcxclbuffer
  2. 如果存在可以抢占上述代码和/或访问的中断例程或线程,则 中的值可能与 5 不同。中断例程损坏实际上可能会影响任何寄存器,因为它们无法保留寄存器值。bufferbl

评论

0赞 byrondrossos 4/28/2012
不要忘记 ECX 仅在 386 或更高版本中可用,而 RCX 在 x86-64 中可用。
0赞 Alexey Frunze 4/28/2012
@byrondrossos:正确。x86 涵盖了 i80386。
4赞 Dirk Wolfgang Glomp 5/5/2013 #4

对于所有使用即时值作为操作数的指令,用于将值写入 ram 位置(或在其中进行计算),我们必须指定要访问的字节数。因为我们的集合无法知道我们是否只想访问一个字节、一个单词一个多重词,例如,如果直接值是较低的值,如以下说明所示。

array db 0FFh, 0FFh, 0FFh, 0FFh
mov byte [array], 3

结果:

array db 03h, 0FFh, 0FFh, 0FFh

....

mov word [array], 3

结果:

array db 03h, 00h, 0FFh, 0FFh

....

mov dword [array], 3

结果:

array db 03h, 00h, 00h, 00h

匕首