为什么输出没有错误?

时间:2017-10-30 14:04:15

标签: assembly tasm emu8086

我读过一些关于
的早期帖子 ' ptr做什么?'
' []的作用是什么?'
但我发现下面的问题没有任何帮助?

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'

1 个答案:

答案 0 :(得分:2)

在汇编语言中,语法取决于特定的汇编程序。 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值{{1} }。默认情况下,段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值,等于word0x002E),因此使用了不同的操作码'.',并且立即值需要两个字节C7。这个将覆盖地址2E 00的字节为ds:0x1004的内存,2E的字节为ds:0x1005

因此,如果地址00(我的示例中为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.\0d. $" (带点)和'h'(带零)。

'i'

这个零被再次覆盖到美元符号(DOS 0x1000: 41 52 49 46 2E 24 64 2E 20 24 ; "ARIF.$d. $" 服务int 21h的字符串终结符)。

一般来说,松散的语法不是问题,因为你不能构建自己的指令,汇编器会猜测现有指令中的哪一个适合,并编译你所拥有的任何表达式。没有关于x86的指令,例如ah=9在两个不同的内存位置存储相同的值,或者mov [address1] and [address2], value会在mov [address]+2处为内存值添加两个({1}}与[{1}}相关的address可能与add r/m,imm variants有关,具体取决于数据大小。)

因此add [address], 2只能是内存地址mov msg+1,...,x86指令集中没有其他有意义的可能性。数据大小msg + 1从标签byte之后使用的db指令中扣除,这是MASM和emu8086汇编程序的专长,大多数其他汇编程序不会链接任何已定义的带有指令的标签(符号),即没有"类型"常见汇编程序中的符号。对于那些msg:可能以语法错误结束的人,但不是因为左侧有问题,但他们不知道mov msg+1,'R'值应该有多大(多少字节)。

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

汇编程序也不会生成两个指令而不是单个(异常可能是特殊的"伪指令"在某些汇编程序中,它们被编译为多个本机指令,但这在x86汇编中并不常见)

一旦你知道目标CPU指令集,存在哪些指令,你就可以轻松地从模糊的语法中猜出,将产生哪个目标指令。

或者您可以轻松检入调试器反汇编窗口,因为反汇编程序将只使用单一语法来处理特定指令,而不知道源格式不明确。

mov byte [msg+2],...

它将写入两个字节,即MASM指定的 mov word ptr [msg + 4], '.' ; Confused again as writing on word memory specified by msg+4, '.' Will it write ascii on the two bytes-> . 。但该值仅为WORD PTR。但是'.' = 0x2E完全有效,即使是16位值,只是用零扩展到0x2E,这是汇编程序用于此行的值。

将来,如果您不确定,如何组装特定内容,以及它将如何处理CPU /内存状态,只需使用emu8086调试器即可。如果你在这种情况下,你会在反汇编窗口中看到0x002E的所有变体都编译到了原始msg+x内存上逐字节的内存地址。此外,如果你打开一些内存视图(我希望emu8086有一个,我不会使用它)在msg地址,你可以看到每次写入内存,它如何改变原始值,以及如何msg正常,因为你不确定。在调试器中观察通常比在堆栈溢出时阅读这些长答案容易得多......

关于PTR的作用:In assembly, what does `PTR` stand for? ...并没有很好地解释它,因为很难解释它,整个" BYTE PTR"是MASM使用的术语,它不是将它解析为BYTE然后对BYTE做一些PTR,但它会将其解析为" BYTE PTR"并且好像,他想要解决字节"。它就像单个关键字,但有空格。