我读过一些关于
的早期帖子
' 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'
答案 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 06
是MOV [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
值,等于word
(0x002E
),因此使用了不同的操作码'.'
,并且立即值需要两个字节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"并且好像,他想要解决字节"。它就像单个关键字,但有空格。