移动字节指针和移动字指针有什么区别?

What is difference between move byte ptr, move word ptr?

我阅读了一些关于
的早期帖子 'What ptr does?'
'What [ ] does?'
但我发现对理解以下问题没有任何帮助?

Title : Program failed to comprehend
.model small
.stack 100h
.data
     Msg db 10,13, 'Zahid. $'   
.code
.startup
     ; Initialising data segment
      mov ax, @data
      mov dx, ax
    ;Before operation displaying message
     mov dx, offset msg
     mov ah,09h
     int 21h

      mov msg , 'A'          ; Writing on memory specified by msg thats OK -> A
      mov msg+1 , 'R'     ; Confusion as writing on memory specified by msg then add 1(not 8bits next address write) -> A
      mov [msg]+2, 'I'     ; confusion: Writing on memory specified by msg then add 2 value to that address write-> I
      mov byte ptr [msg+3] , 'F'      ; Ok with me, writing on byte memory specified by msg+3 -> F 
      mov word ptr [msg + 4], '.'      ; Confused again as writing on word memory specified by msg+4, '.' Will it write ascii on the two bytes-> .
      mov msg[5] , '$'   ; Not confused on this.

   ;Print var
    mov dx, offset msg
    mov ah,09h
    int 21h
 
    ;Exit Code.
    mov ah,04ch
    xor al,al
    int 21h
     
Ends

Output : Zahid. ARIF.

请解释操作,因为我认为它不应该打印 'ARIF'??

在汇编中,语法取决于特定的汇编器。 Emu8086 主要遵循 MASM 方言,在规则上相当宽松,并允许几个不同的 options (with same output).

如果你习惯了一些高级编程语言,这可能会让人感到困惑,为什么语法不是一成不变的,以及如何在 asm 中忍受这种混乱。

但对于 asm 程序员来说,这很少是个问题,因为在汇编中你不会构建一些带有运算符和不同值的运行时表达式,来自源的指令通常 1:1 映射到 CPU 指令,以及存在于 CPU.

中的特定指令的确切参数和选项

x86 上的 MOV instruction 本身有点混乱,因为它是用于许多不同指令操作码的单个助记符 "MOV",但在您的示例中只使用了两条指令:MOV r/m8,imm8 使用操作码 C6 存储字节值,MOV r/m16,imm16 使用操作码 C7 存储字值。在所有情况下,r/m 部分都是通过绝对偏移量进行的内存引用,这是在编译时计算的。

所以如果 msg 是内存地址 0x1000 的符号,那么你问题中的那些行编译为:

; machine code  | disassembled instruction from machine code

C606001041        mov byte [0x1000],0x41

将字节值 0x41 ('A') 存储到地址 ds:0x1000 的内存中。 C6 06MOV [offset16],imm8 指令操作码,00 10 字节是 0x1000 offset16 本身(小端),最后 41 是 imm8 值 0x41.默认情况下,段 ds 将用于计算完整的物理内存地址,因为该指令之前没有段覆盖前缀。

C606011052        mov byte [0x1001],0x52
C606021049        mov byte [0x1002],0x49
C606031046        mov byte [0x1003],0x46
C70604102E00      mov word [0x1004],0x2e
C606051024        mov byte [0x1005],0x24

其余几行都是同样的故事,在特定的内存偏移处写入字节值,在内存中逐字节移动,覆盖其中的每一个。

mov word ptr [msg + 4], '.'的细微差别,它的目标内存地址ds:0x1004与其他行类似,但存储的值是imm16,即word值,等于0x002E'.'),所以使用不同的操作码C7,立即数需要两个字节2E 00。这将用字节 2E 覆盖地址 ds:0x1004 处的内存,并用字节 00.

覆盖地址 ds:0x1005 处的内存

因此,如果地址 msg 处的内存(在我的示例中为 ds:0x1000)位于开头:

0x1000: 0A 0D 5A 61 68 69 64 2E 20 24  ; "\n\rZahid. $"

每执行一次MOV就会变成这样:

0x1000: 41 0D 5A 61 68 69 64 2E 20 24  ; "A\rZahid. $"
0x1000: 41 52 5A 61 68 69 64 2E 20 24  ; "ARZahid. $"
0x1000: 41 52 49 61 68 69 64 2E 20 24  ; "ARIahid. $"
0x1000: 41 52 49 46 68 69 64 2E 20 24  ; "ARIFhid. $"
0x1000: 41 52 49 46 2E 00 64 2E 20 24  ; "ARIF.[=13=]d. $"

这个词确实覆盖了两个字节,'h'(带点)和 'i'(带零)。

0x1000: 41 52 49 46 2E 24 64 2E 20 24  ; "ARIF.$d. $"

并且该零被再次覆盖为美元符号(DOS int 21h 服务 ah=9 的字符串终止符)。

通常宽松的语法不是问题,因为你不能建立自己的指令,汇编程序会猜测现有指令中的哪一个适合,然后将你有的任何表达式编译到其中。 x86 上没有指令,例如 mov [address1] and [address2], value 在两个不同的内存位置存储相同的值,或者 mov [address]+2 会将 address 处的内存值加两个(这可能与 add [address], 2 这是 add r/m,imm variants 中的一个,具体取决于数据大小)。

所以mov msg+1,...只能是内存地址msg + 1,在x86指令集中没有其他有意义的可能性。而数据大小byte是从标签msg:后使用的db指令中扣除的,这是MASM和emu8086汇编器的特长,其他大多数汇编器不会link任何定义的标签(符号),其后使用指令,即普通汇编程序中没有 "types" 符号。对于那些 mov msg+1,'R' 可能以语法错误结尾,但不是因为左侧有问题,而是他们不知道 'R' 值应该有多大(多少字节)。

我个人最喜欢的 NASM 会报告另一个错误,因为它需要围绕内存访问的括号,所以在 NASM 中只有 mov [msg+2],... 是有效的(大小修饰符如"byte ptr" 在 MASM 中允许,但没有 "ptr":mov byte [msg+2],...。但是在 MASM/emu8086 中,您使用的所有变体都是具有相同含义的有效语法,通过 16b 偏移产生内存引用。

汇编器也不会产生两条指令而不是一条指令(在某些汇编器中可能会有特殊的例外情况 "pseudo-instructions",它们被编译为多个本机指令,但这在 x86 汇编中并不常见)。

一旦你知道了目标CPU指令集,确实存在哪些指令,你就可以很容易地从模糊的语法中猜测出将产生哪条目标指令。

或者您可以轻松检查调试器反汇编 window,因为反汇编器将仅对特定指令使用单一语法方式,而不会意识到源格式的歧义。

 mov word ptr [msg + 4], '.'
   ; Confused again as writing on word memory specified by msg+4,
     '.' Will it write ascii on the two bytes-> .

它将写入两个字节,这是MASM 中指定的WORD PTR。但是数值只有'.' = 0x2E。但是 0x2E 即使作为 16 位值也是完全有效的,只需用零扩展到 0x002E,这就是汇编程序在该行中使用的值。

将来,如果您不确定具体的组装方式,以及它会对 CPU/memory 状态做什么,只需使用 emu8086 调试器即可。如果你在这种情况下这样做,你会在反汇编 window 中看到 msg+x 的所有这些变体确实编译到在原始 msg 内存上逐字节移动的内存地址。此外,如果您在 msg 地址打开一些内存视图(我希望 emu8086 有一个,我不使用它),您可以观察每次写入内存,它如何改变原始值,以及 WORD PTR 有效,因为你不确定。在调试器中观察通常比阅读这些关于堆栈溢出的长答案要容易得多...

关于PTR是干什么的: ... 不好解释,不好解释,整个"BYTE PTR"是MASM用的术语,不是解析它作为 BYTE,然后对 BYTE 执行一些 PTR,但它会将其解析为 "BYTE PTR" 并类似于 "okay, he want to address byte"。这就像单个关键字,但带有 space.