我阅读了一些关于
“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”??
在汇编中,语法取决于特定的汇编程序。Emu8086 主要遵循 MASM 方言,它在规则上相当宽松,并允许多个不同的选项(具有相同的输出)。
如果你习惯了一些高级编程语言,这可能会让人感到困惑,为什么语法不是一成不变的,以及如何在 asm 中忍受这种混乱。
但是对于 asm 程序员来说,这很少是一个问题,因为在汇编中你不会用运算符和不同的值构建一些运行时表达式,来自源的指令通常是 1:1 映射到 CPU 指令之一,具有确切的参数和选项CPU中存在的特定指令。
x86 上的MOV
指令本身有点混乱,因为它是用于许多不同指令操作码的单个助记符“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值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
用 byte2E
和ds:0x1005
byte覆盖地址处的内存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'
(带零)。
0x1000: 41 52 49 46 2E 24 64 2E 20 24 ; "ARIF.$d. $"
并且该零被再次覆盖为美元符号(DOSint 21h
服务的字符串终止符ah=9
)。
通常,宽松的语法不是问题,因为您无法构建自己的指令,汇编程序会猜测现有指令中的哪一个适合,并编译您拥有的任何表达式。x86 上没有任何指令,例如mov [address1] and [address2], value
在两个不同的内存位置存储相同的值,或者mov [address]+2
将两个值添加到内存值中address
(这可能取决于变体中的add [address], 2
一个,具体取决于数据大小)。add r/m,imm
所以mov msg+1,...
只能是 memory address msg + 1
,x86 指令集中没有其他有意义的可能性。并且数据大小byte
从db
label 之后使用的指令中扣除msg:
,这是 MASM 和 emu8086 汇编程序的特长,大多数其他汇编程序不会将任何定义的标签(符号)与其后使用的指令链接,即没有符号的“类型”在常见的汇编程序中。对于那些mov msg+1,'R'
可能以语法错误结束的人,但不是因为左侧有问题,而是他们不知道该'R'
值应该有多大(多少字节)。
我个人最喜欢的 NASM 会报告另一个错误,因为它需要在内存访问周围加上括号,所以只有在 NASM 中才mov [msg+2],...
有效(在 MASM 中允许使用像 "byte ptr" 这样的大小修饰符,但没有 "ptr": mov byte [msg+2],...
。但在 MASM 中/emu8086 您使用的所有变体都是具有相同含义的有效语法,产生 16b 偏移的内存引用。
汇编器也不会产生两条指令而不是一条指令(例外情况可能是某些汇编器中的特殊“伪指令”,它们被编译为几条本机指令,但这在 x86 汇编中并不常见)。
一旦你知道了目标 CPU 指令集,哪些指令确实存在,你就可以很容易地从模糊的语法中猜测将产生哪条目标指令。
或者您可以轻松地检查调试器反汇编窗口,因为反汇编器将只使用单一的语法方式来处理特定指令,而不知道源格式的歧义。
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
MASM 中指定的内容。但价值仅为'.' = 0x2E
. 但是0x2E
即使作为 16 位值也完全有效,只需用零扩展到0x002E
,这就是汇编程序为此行使用的值。
将来,如果您不确定特定事物的组装方式以及它对 CPU/内存状态的影响,请使用 emu8086 调试器。如果你在这种情况下愿意,你会在反汇编窗口中看到所有这些变体都msg+x
编译为内存地址,逐字节覆盖原始msg
内存。此外,如果您在msg
地址处打开一些内存视图(我希望 emu8086 有一个,我不使用它),您可以观察每次写入内存,它如何更改原始值以及它是如何WORD PTR
工作的,因为您没有当然。在调试器中观看通常比在堆栈溢出时阅读这些长答案要容易得多......
关于 PTR 的作用:在汇编中,`PTR` 代表什么?... 不好解释,因为很难解释清楚,整个“BYTE PTR”是 MASM 使用的术语,它不是将其解析为 BYTE 然后对 BYTE 执行某些 PTR 操作,但它会解析它作为“BYTE PTR”,就像“好吧,他想寻址字节”。这就像单个关键字,但有空格。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句