MASM中使用MOV助记符load/copy一个字符串到一个内存寄存器时,字符是否倒序存储?
When using the MOV mnemonic to load/copy a string to a memory register in MASM, are the characters stored in reverse order?
我想知道使用MOV指令将字符串复制到寄存器中是否会导致字符串以相反的顺序存储。我了解到,当 MASM 将字符串存储到定义为单词或更高(dw 和更大尺寸)的变量中时,字符串以相反的顺序存储。当我将字符串复制到寄存器时是否会发生同样的事情?
基于这个问题 ( and about ) 我假设如下:
- 当MASM将字符串加载到变量中时,它以相反的顺序加载它,即字符串中的最后一个字符存储在字符串变量的最低内存地址(开头)中。这意味着像这样分配一个变量 str:
str dd "abc"
导致 MASM 将字符串存储为 "cba",这意味着 "c" 位于最低内存地址。
- 将变量定义为
str db "abc"
时,MASM 将 str
视为字符数组。尝试将数组索引与 str
的内存地址匹配,MASM 会将 "a" 存储在 str
. 的最低内存地址处
- 默认情况下,SCAS 和 MOVS 指令从目标字符串的开始(最低)地址开始执行,即存储在 EDI 寄存器中的字符串。在执行之前,它们不会 "pop" 或将 "last in, first out" 规则应用于它们操作的内存地址。
- MASM 始终以相同的方式处理字符数组和字符串到内存寄存器。将字符数组 'a'、'b'、'c' 移动到 EAX 与将 "abc" 移动到 EAX 相同。
当我使用 'a'、'b' 和 'c' 将字节数组 arLetters
传输到双字变量 strLetters
时 MOVSD
,我相信这些字母被反向复制到 strLetters
,即存储为 "cba"。当我使用 mov eax, "abc"
时,字母是否也以相反的顺序存储?
下面的代码将在退出前设置零标志。
.data?
strLetters dd ?,0
.data
arLetters db "abcd"
.code
start:
mov ecx, 4
lea esi, arLetters
lea edi, strLetters
movsd
;This stores the string "dcba" into strLetters.
mov ecx, 4
lea edi, strLetters
mov eax, "dcba"
repnz scasd
jz close
jmp printer
;strLetters is not popped as "abcd" and is compared as "dcba".
printer:
print "No match.",13,10,0
jmp close
close:
push 0
call ExitProcess
end start
我希望字符串 "dcba" 存储在 EAX "as is" - 'd' 在 EAX 的最低内存地址 - 因为 MASM 将字符串移动到寄存器不同于分配字符串到变量。 MASM 将 'a'、'b'、'c' 'd'" 复制到 strLetters 中作为 "dcba" 以确保如果弹出 strLetters,则字符串是 emmitted/released正确的顺序 ("abcd")。如果使用 REP MOVSB
指令代替 MOVSD
,strLetters 将包含 "abcd" 并且将 popped/emmitted 作为 "dcba"。但是,因为使用了 MOVSD
并且 SCAS 或 MOVS 指令在执行前不会弹出字符串,所以上面的代码应该设置零标志,对吗?
不要在 MASM 需要 16 位或更大整数的上下文中使用字符串。 MASM 会将它们转换为整数,这种方式在存储到内存中时会颠倒字符的顺序。由于这会造成混淆,因此最好避免这种情况,并且仅将字符串与 DB 指令一起使用,这会按预期工作。不要使用超过字符的字符串作为立即值。
内存有字节顺序,寄存器没有
寄存器没有地址,谈论寄存器中的字节顺序是没有意义的。在 32 位 x86 CPU 上,像 EAX 这样的通用寄存器保存 32 位整数值。您可以在概念上将 32 位值划分为 4 个字节,但是当它存在于寄存器中时,字节没有有意义的顺序。
只有当内存中存在 32 位值时,组成它们的 4 个字节才有地址,因此才有顺序。由于 x86 CPUs 使用 little-endian byte order 这意味着 4 个字节中最低有效字节是第一个字节。最重要的部分成为最后一个字节。每当 x86 将 16 位或更宽的值加载到内存或从内存中存储时,它都会使用小端字节序。 (一个例外是 MOVBE 指令,它在加载和存储值时专门使用 big-endian 字节顺序。)
所以考虑这个程序:
.MODEL flat
.DATA
db_str DB "abcd"
dd_str DD "abcd"
num DD 1684234849
.CODE
_start:
mov eax, "abcd"
mov ebx, DWORD PTR [db_str]
mov ecx, DWORD PTR [dd_str]
mov edx, 1684234849
mov esi, [num]
int 3
END _start
在组装和链接后,它被转换成类似这样的字节序列:
.text section:
00401000: B8 64 63 62 61 8B 1D 00 30 40 00 8B 0D 04 30 40 ,dcba...0@....0@
00401010: 00 BA 61 62 63 64 8B 35 08 30 40 00 CC .ºabcd.5.0@.I
...
.data section:
00403000: 61 62 63 64 64 63 62 61 61 62 63 64 abcddcbaabcd
(在 Windows 上,.data
部分通常放在内存中的 .text
部分之后。)
DB 和 DD 对待字符串的方式不同
所以我们可以看到 DB 和 DD 指令,即标记为 db_str
和 dd_str
的指令,为同一个字符串 "abcd"
生成了两个不同的字节序列。在第一种情况下,MASM 生成了我们期望的字节序列,61h、62h、63h 和 64h,a
、b
、c
的 ASCII 值,和 d
分别。对于 dd_str
虽然字节顺序是颠倒的。这是因为 DD 指令使用 32 位整数作为操作数,因此必须将字符串转换为 32 位值,并且当转换结果存储在内存中时,MASM 最终会反转字符串中字符的顺序。
在内存中,字符串和数字都是字节
您还会注意到标记为 num
的 DD 指令也生成了与 DB 指令相同的字节序列。事实上,如果不查看源代码,就无法判断前四个字节应该是一个字符串而后四个字节应该是一个数字。如果程序以这种方式使用它们,它们只会变成字符串或数字。
(不太明显的是十进制值1684234849是如何转换为与DB指令生成的相同序列字节的。它已经是一个32位值,只需要通过MASM将其转换为字节序列。不出所料,汇编程序使用与 CPU 相同的小端字节顺序来执行此操作。这意味着第一个字节是 1684234849 的最低有效部分,恰好与 ASCII 字母 [=19= 具有相同的值] (1684234849 % 256 = 97 = 61h) 最后一个字节是数字的最高有效部分,恰好是 d
(1684234849 / 256 / 256 / 256 = 100 = 64h) 的ASCII值。 )
立即数像 DD 一样处理字符串
使用反汇编器更仔细地查看 .text
部分中的值,我们可以看到存储在那里的字节序列在 CPU 执行时将如何解释为指令:
00401000: B8 64 63 62 61 mov eax,61626364h
00401005: 8B 1D 00 30 40 00 mov ebx,dword ptr ds:[00403000h]
0040100B: 8B 0D 04 30 40 00 mov ecx,dword ptr ds:[00403004h]
00401011: BA 61 62 63 64 mov edx,64636261h
00401016: 8B 35 08 30 40 00 mov esi,dword ptr ds:[00403008h]
0040101C: CC int 3
我们在这里可以看到,MASM 以与 dd_str
DD 指令相同的顺序在指令 mov eax, "abcd"
中存储构成立即数的字节。内存中指令的立即数部分的第一个字节是64h,即d
的ASCII值。原因是因为对于 32 位目标寄存器,此 MOV 指令使用 32 位立即数。这意味着 MASM 需要将字符串转换为 32 位整数并最终颠倒字节顺序,就像它对 dd_str
所做的那样。 MASM 还处理作为 mov ecx, 1684234849
的立即数给出的十进制数,其处理方式与使用相同数字的 DD 指令相同。 32 位值已转换为相同的小端表示法。
在内存中,指令也是字节
您还会注意到,反汇编器生成的汇编指令使用十六进制值作为这两条指令的立即数。与 CPU 一样,汇编程序无法知道立即数应该是字符串和十进制数。它们只是程序中的一个字节序列,它所知道的是它们是 32 位立即值(来自操作码 B8h 和 B9h),因此由于没有更好的选择,因此将它们显示为 32 位十六进制值.
寄存器中的值反映内存顺序
通过在调试器下执行程序并在到达断点指令 (int 3
) 后检查寄存器,我们可以看到寄存器中实际结束的内容:
eax=61626364 ebx=64636261 ecx=61626364 edx=64636261 esi=64636261 edi=00000000
eip=0040101c esp=0018ff8c ebp=0018ff94 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246
image00000000_00400000+0x101c:
0040101c cc int 3
现在我们可以看到第一条和第三条指令加载了与其他指令不同的值。这两条指令都涉及 MASM 将字符串转换为 32 位值并最终反转内存中字符顺序的情况。寄存器转储确认内存中字节的颠倒顺序导致不同的值被加载到寄存器中。
但实际上,寄存器没有字节顺序
现在您可能正在查看上面的寄存器转储并认为只有 EAX 和 ECX 的顺序正确,a
的 ASCII 值首先是 61h,然后是 [=22 的 ASCII 值=],最后 64 小时。 MASM 颠倒字符串在内存中的顺序实际上导致它们以正确的顺序加载到寄存器中。但正如我之前所说,寄存器中没有字节顺序。数字 61626364
就是调试器在将值显示为您可以阅读的字符序列时表示值的方式。字符 61
在调试器的表示中排在第一位,因为我们的编号系统将数字的最重要部分放在左侧,并且我们从左到右阅读,因此它成为第一部分。然而,正如我之前所说,x86 CPUs 是小端优先的,这意味着最低有效部分在内存中排在第一位。这意味着内存中的第一个字节成为寄存器中值的最低有效部分,调试器将其显示为数字的最右边的两个十六进制数字,因为这是数字在我们的编号系统中的最低有效部分。
换句话说,因为 x86 CPUs 是小端,最低位在前,但我们的编号系统是大端,最高位在前,十六进制数以字节顺序倒序显示它们实际上是如何存储在内存中的。
简单地复制 "strings" 不会改变他们的顺序
现在还应该清楚,将字符串加载到寄存器只是概念上发生的事情。汇编器将字符串转换为字节序列,当将其加载到 32 位寄存器中时,在内存中将其视为小端 32 位整数。当寄存器中的 32 位值存储在内存中时,32 位值将转换为以小端格式表示值的字节序列。对于 CPU 你的字符串只是一个 32 位整数,它加载并存储在内存中。
所以这意味着如果在示例程序中加载到 EAX 中的值存储到内存中类似 mov [mem], eax
那么存储在 mem
的 4 个字节的顺序将与它们出现在构成 mov eax, "abcd"
的立即数的字节中。这是相同的相反顺序,64h、63h、62h、61h,MASM 将它们放入构成立即数的字节中。
但是为什么呢?我不知道,只是不要那样做
现在我不知道为什么 MASM 在将字符串转换为 32 位整数时颠倒字符串的顺序,但这里的原则是不要将字符串用作立即数或任何其他需要它们的上下文转换为整数。汇编程序在如何将字符串文字转换为整数方面不一致。 (类似的问题发生在 C 编译器如何将 'abcd'
之类的字符文字转换为整数。)
SCASD 和 MOVSD 并不特殊
SCASD 或 MOVSD 指令没有发生任何特殊情况。 SCASD把EDI指向的四个字节当作一个32位的小端值,载入一个未命名的临时寄存器,将临时寄存器与EAX进行比较,然后根据DF标志对EDI加4或减4。 MOVSD 将 ESI 指向的内存中的一个 32 位值加载到一个未命名的临时寄存器中,将 EDI 指向的 32 位内存位置存储到临时寄存器中,然后根据 DF 标志更新 ESI 和 EDI。 (字节顺序对 MOVSD 无关紧要,因为字节永远不会用作 32 位值,但顺序不会改变。)
我不会尝试将 SCASD 或 MOVSD 视为 FIFO 或 LIFO,因为最终这取决于您如何使用它们。 MOVSD 可以像 LIFO 堆栈一样轻松地用作 FIFO 队列实现的一部分。 (将其与 PUSH 和 POP 进行比较,理论上它们可以独立地用于实现 FIFO 或 LIFO 数据结构的一部分,但一起只能用于实现 LIFO 堆栈。)
有关 MASM 工作原理的详细说明,请参阅 。这个答案将它与 NASM 进行了比较,如果您只关心 MASM,这可能会让人感到困惑。
mov ecx, 4
是四个 dwords = 16 个字节,当与 repne scasd
.
一起使用时
更简单的方法是省略 rep
并只使用 scasd
。
或者更简单cmp dword ptr [strLetters], "dcba"
.
如果你查看机器代码中的立即数,如果它在内存中的顺序与数据相同,它将比较相等,因为两者都被视为小端32 位整数。 (因为 x86 指令编码使用 little-endian immediates,匹配 x86 的数据 load/store endianness。)
是的,对于 MASM 显然你确实需要 "dcba"
在使用字符串作为整数常量时获得所需的字节顺序,因为 MASM 将第一个字符视为“最重要的”并将其放在最后一个 32 位立即数。
NASM 和 MASM 在这里 非常 不同。 在 NASM 中,mov dword [mem], 'abcd'
产生'a', 'b', 'c', 'd'
在记忆中。即一次一个字节的内存顺序与源顺序匹配。参见 NASM character constants。多字符常量在 32 位小端立即数中简单地右对齐,字符串字节按源顺序排列。
例如
objdump -d -Mintel disassembly
c7 07 61 62 63 64 mov DWORD PTR [rdi], 0x64636261
NASM 来源:mov dword [rdi], "abcd"
MASM 来源:mov dword ptr [rdi], "dcba"
气体来源:AFAIK 。你可以做类似 $'a' + ('b'<<8) + ...
我同意 Ross 关于避免在 MASM 中使用多字符字符串文字的建议,除非作为 db
. 的操作数立即数,使用 NASM 或 EuroAssembler (https://euroassembler.eu/eadoc/#CharNumbers)
另见 - 在 NASM 中,dd "abcd"
完全等同于 db "abcd"
(因为我使用了 4 个 ASCII 字符,所以没有额外的填充来填充双字).但是 MASM 会在 dd
中破坏你的字符串,就像 dword 立即数一样。
还有,不要用jcc
和jmp
,用一个je close
来判断是否漏电
(你确实避免了 jcc
在 jmp
上的脑死亡习语,这里你的 jz
是理智的,而 jmp
完全是多余的,跳跃到下一条指令。)
我想知道使用MOV指令将字符串复制到寄存器中是否会导致字符串以相反的顺序存储。我了解到,当 MASM 将字符串存储到定义为单词或更高(dw 和更大尺寸)的变量中时,字符串以相反的顺序存储。当我将字符串复制到寄存器时是否会发生同样的事情?
基于这个问题 (
- 当MASM将字符串加载到变量中时,它以相反的顺序加载它,即字符串中的最后一个字符存储在字符串变量的最低内存地址(开头)中。这意味着像这样分配一个变量 str:
str dd "abc"
导致 MASM 将字符串存储为 "cba",这意味着 "c" 位于最低内存地址。 - 将变量定义为
str db "abc"
时,MASM 将str
视为字符数组。尝试将数组索引与str
的内存地址匹配,MASM 会将 "a" 存储在str
. 的最低内存地址处
- 默认情况下,SCAS 和 MOVS 指令从目标字符串的开始(最低)地址开始执行,即存储在 EDI 寄存器中的字符串。在执行之前,它们不会 "pop" 或将 "last in, first out" 规则应用于它们操作的内存地址。
- MASM 始终以相同的方式处理字符数组和字符串到内存寄存器。将字符数组 'a'、'b'、'c' 移动到 EAX 与将 "abc" 移动到 EAX 相同。
当我使用 'a'、'b' 和 'c' 将字节数组 arLetters
传输到双字变量 strLetters
时 MOVSD
,我相信这些字母被反向复制到 strLetters
,即存储为 "cba"。当我使用 mov eax, "abc"
时,字母是否也以相反的顺序存储?
下面的代码将在退出前设置零标志。
.data?
strLetters dd ?,0
.data
arLetters db "abcd"
.code
start:
mov ecx, 4
lea esi, arLetters
lea edi, strLetters
movsd
;This stores the string "dcba" into strLetters.
mov ecx, 4
lea edi, strLetters
mov eax, "dcba"
repnz scasd
jz close
jmp printer
;strLetters is not popped as "abcd" and is compared as "dcba".
printer:
print "No match.",13,10,0
jmp close
close:
push 0
call ExitProcess
end start
我希望字符串 "dcba" 存储在 EAX "as is" - 'd' 在 EAX 的最低内存地址 - 因为 MASM 将字符串移动到寄存器不同于分配字符串到变量。 MASM 将 'a'、'b'、'c' 'd'" 复制到 strLetters 中作为 "dcba" 以确保如果弹出 strLetters,则字符串是 emmitted/released正确的顺序 ("abcd")。如果使用 REP MOVSB
指令代替 MOVSD
,strLetters 将包含 "abcd" 并且将 popped/emmitted 作为 "dcba"。但是,因为使用了 MOVSD
并且 SCAS 或 MOVS 指令在执行前不会弹出字符串,所以上面的代码应该设置零标志,对吗?
不要在 MASM 需要 16 位或更大整数的上下文中使用字符串。 MASM 会将它们转换为整数,这种方式在存储到内存中时会颠倒字符的顺序。由于这会造成混淆,因此最好避免这种情况,并且仅将字符串与 DB 指令一起使用,这会按预期工作。不要使用超过字符的字符串作为立即值。
内存有字节顺序,寄存器没有
寄存器没有地址,谈论寄存器中的字节顺序是没有意义的。在 32 位 x86 CPU 上,像 EAX 这样的通用寄存器保存 32 位整数值。您可以在概念上将 32 位值划分为 4 个字节,但是当它存在于寄存器中时,字节没有有意义的顺序。
只有当内存中存在 32 位值时,组成它们的 4 个字节才有地址,因此才有顺序。由于 x86 CPUs 使用 little-endian byte order 这意味着 4 个字节中最低有效字节是第一个字节。最重要的部分成为最后一个字节。每当 x86 将 16 位或更宽的值加载到内存或从内存中存储时,它都会使用小端字节序。 (一个例外是 MOVBE 指令,它在加载和存储值时专门使用 big-endian 字节顺序。)
所以考虑这个程序:
.MODEL flat
.DATA
db_str DB "abcd"
dd_str DD "abcd"
num DD 1684234849
.CODE
_start:
mov eax, "abcd"
mov ebx, DWORD PTR [db_str]
mov ecx, DWORD PTR [dd_str]
mov edx, 1684234849
mov esi, [num]
int 3
END _start
在组装和链接后,它被转换成类似这样的字节序列:
.text section:
00401000: B8 64 63 62 61 8B 1D 00 30 40 00 8B 0D 04 30 40 ,dcba...0@....0@
00401010: 00 BA 61 62 63 64 8B 35 08 30 40 00 CC .ºabcd.5.0@.I
...
.data section:
00403000: 61 62 63 64 64 63 62 61 61 62 63 64 abcddcbaabcd
(在 Windows 上,.data
部分通常放在内存中的 .text
部分之后。)
DB 和 DD 对待字符串的方式不同
所以我们可以看到 DB 和 DD 指令,即标记为 db_str
和 dd_str
的指令,为同一个字符串 "abcd"
生成了两个不同的字节序列。在第一种情况下,MASM 生成了我们期望的字节序列,61h、62h、63h 和 64h,a
、b
、c
的 ASCII 值,和 d
分别。对于 dd_str
虽然字节顺序是颠倒的。这是因为 DD 指令使用 32 位整数作为操作数,因此必须将字符串转换为 32 位值,并且当转换结果存储在内存中时,MASM 最终会反转字符串中字符的顺序。
在内存中,字符串和数字都是字节
您还会注意到标记为 num
的 DD 指令也生成了与 DB 指令相同的字节序列。事实上,如果不查看源代码,就无法判断前四个字节应该是一个字符串而后四个字节应该是一个数字。如果程序以这种方式使用它们,它们只会变成字符串或数字。
(不太明显的是十进制值1684234849是如何转换为与DB指令生成的相同序列字节的。它已经是一个32位值,只需要通过MASM将其转换为字节序列。不出所料,汇编程序使用与 CPU 相同的小端字节顺序来执行此操作。这意味着第一个字节是 1684234849 的最低有效部分,恰好与 ASCII 字母 [=19= 具有相同的值] (1684234849 % 256 = 97 = 61h) 最后一个字节是数字的最高有效部分,恰好是 d
(1684234849 / 256 / 256 / 256 = 100 = 64h) 的ASCII值。 )
立即数像 DD 一样处理字符串
使用反汇编器更仔细地查看 .text
部分中的值,我们可以看到存储在那里的字节序列在 CPU 执行时将如何解释为指令:
00401000: B8 64 63 62 61 mov eax,61626364h
00401005: 8B 1D 00 30 40 00 mov ebx,dword ptr ds:[00403000h]
0040100B: 8B 0D 04 30 40 00 mov ecx,dword ptr ds:[00403004h]
00401011: BA 61 62 63 64 mov edx,64636261h
00401016: 8B 35 08 30 40 00 mov esi,dword ptr ds:[00403008h]
0040101C: CC int 3
我们在这里可以看到,MASM 以与 dd_str
DD 指令相同的顺序在指令 mov eax, "abcd"
中存储构成立即数的字节。内存中指令的立即数部分的第一个字节是64h,即d
的ASCII值。原因是因为对于 32 位目标寄存器,此 MOV 指令使用 32 位立即数。这意味着 MASM 需要将字符串转换为 32 位整数并最终颠倒字节顺序,就像它对 dd_str
所做的那样。 MASM 还处理作为 mov ecx, 1684234849
的立即数给出的十进制数,其处理方式与使用相同数字的 DD 指令相同。 32 位值已转换为相同的小端表示法。
在内存中,指令也是字节
您还会注意到,反汇编器生成的汇编指令使用十六进制值作为这两条指令的立即数。与 CPU 一样,汇编程序无法知道立即数应该是字符串和十进制数。它们只是程序中的一个字节序列,它所知道的是它们是 32 位立即值(来自操作码 B8h 和 B9h),因此由于没有更好的选择,因此将它们显示为 32 位十六进制值.
寄存器中的值反映内存顺序
通过在调试器下执行程序并在到达断点指令 (int 3
) 后检查寄存器,我们可以看到寄存器中实际结束的内容:
eax=61626364 ebx=64636261 ecx=61626364 edx=64636261 esi=64636261 edi=00000000
eip=0040101c esp=0018ff8c ebp=0018ff94 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246
image00000000_00400000+0x101c:
0040101c cc int 3
现在我们可以看到第一条和第三条指令加载了与其他指令不同的值。这两条指令都涉及 MASM 将字符串转换为 32 位值并最终反转内存中字符顺序的情况。寄存器转储确认内存中字节的颠倒顺序导致不同的值被加载到寄存器中。
但实际上,寄存器没有字节顺序
现在您可能正在查看上面的寄存器转储并认为只有 EAX 和 ECX 的顺序正确,a
的 ASCII 值首先是 61h,然后是 [=22 的 ASCII 值=],最后 64 小时。 MASM 颠倒字符串在内存中的顺序实际上导致它们以正确的顺序加载到寄存器中。但正如我之前所说,寄存器中没有字节顺序。数字 61626364
就是调试器在将值显示为您可以阅读的字符序列时表示值的方式。字符 61
在调试器的表示中排在第一位,因为我们的编号系统将数字的最重要部分放在左侧,并且我们从左到右阅读,因此它成为第一部分。然而,正如我之前所说,x86 CPUs 是小端优先的,这意味着最低有效部分在内存中排在第一位。这意味着内存中的第一个字节成为寄存器中值的最低有效部分,调试器将其显示为数字的最右边的两个十六进制数字,因为这是数字在我们的编号系统中的最低有效部分。
换句话说,因为 x86 CPUs 是小端,最低位在前,但我们的编号系统是大端,最高位在前,十六进制数以字节顺序倒序显示它们实际上是如何存储在内存中的。
简单地复制 "strings" 不会改变他们的顺序
现在还应该清楚,将字符串加载到寄存器只是概念上发生的事情。汇编器将字符串转换为字节序列,当将其加载到 32 位寄存器中时,在内存中将其视为小端 32 位整数。当寄存器中的 32 位值存储在内存中时,32 位值将转换为以小端格式表示值的字节序列。对于 CPU 你的字符串只是一个 32 位整数,它加载并存储在内存中。
所以这意味着如果在示例程序中加载到 EAX 中的值存储到内存中类似 mov [mem], eax
那么存储在 mem
的 4 个字节的顺序将与它们出现在构成 mov eax, "abcd"
的立即数的字节中。这是相同的相反顺序,64h、63h、62h、61h,MASM 将它们放入构成立即数的字节中。
但是为什么呢?我不知道,只是不要那样做
现在我不知道为什么 MASM 在将字符串转换为 32 位整数时颠倒字符串的顺序,但这里的原则是不要将字符串用作立即数或任何其他需要它们的上下文转换为整数。汇编程序在如何将字符串文字转换为整数方面不一致。 (类似的问题发生在 C 编译器如何将 'abcd'
之类的字符文字转换为整数。)
SCASD 和 MOVSD 并不特殊
SCASD 或 MOVSD 指令没有发生任何特殊情况。 SCASD把EDI指向的四个字节当作一个32位的小端值,载入一个未命名的临时寄存器,将临时寄存器与EAX进行比较,然后根据DF标志对EDI加4或减4。 MOVSD 将 ESI 指向的内存中的一个 32 位值加载到一个未命名的临时寄存器中,将 EDI 指向的 32 位内存位置存储到临时寄存器中,然后根据 DF 标志更新 ESI 和 EDI。 (字节顺序对 MOVSD 无关紧要,因为字节永远不会用作 32 位值,但顺序不会改变。)
我不会尝试将 SCASD 或 MOVSD 视为 FIFO 或 LIFO,因为最终这取决于您如何使用它们。 MOVSD 可以像 LIFO 堆栈一样轻松地用作 FIFO 队列实现的一部分。 (将其与 PUSH 和 POP 进行比较,理论上它们可以独立地用于实现 FIFO 或 LIFO 数据结构的一部分,但一起只能用于实现 LIFO 堆栈。)
有关 MASM 工作原理的详细说明,请参阅
mov ecx, 4
是四个 dwords = 16 个字节,当与 repne scasd
.
更简单的方法是省略 rep
并只使用 scasd
。
或者更简单cmp dword ptr [strLetters], "dcba"
.
如果你查看机器代码中的立即数,如果它在内存中的顺序与数据相同,它将比较相等,因为两者都被视为小端32 位整数。 (因为 x86 指令编码使用 little-endian immediates,匹配 x86 的数据 load/store endianness。)
是的,对于 MASM 显然你确实需要 "dcba"
在使用字符串作为整数常量时获得所需的字节顺序,因为 MASM 将第一个字符视为“最重要的”并将其放在最后一个 32 位立即数。
NASM 和 MASM 在这里 非常 不同。 在 NASM 中,mov dword [mem], 'abcd'
产生'a', 'b', 'c', 'd'
在记忆中。即一次一个字节的内存顺序与源顺序匹配。参见 NASM character constants。多字符常量在 32 位小端立即数中简单地右对齐,字符串字节按源顺序排列。
例如
objdump -d -Mintel disassembly
c7 07 61 62 63 64 mov DWORD PTR [rdi], 0x64636261
NASM 来源:mov dword [rdi], "abcd"
MASM 来源:mov dword ptr [rdi], "dcba"
气体来源:AFAIK $'a' + ('b'<<8) + ...
我同意 Ross 关于避免在 MASM 中使用多字符字符串文字的建议,除非作为 db
. 的操作数立即数,使用 NASM 或 EuroAssembler (https://euroassembler.eu/eadoc/#CharNumbers)
另见 dd "abcd"
完全等同于 db "abcd"
(因为我使用了 4 个 ASCII 字符,所以没有额外的填充来填充双字).但是 MASM 会在 dd
中破坏你的字符串,就像 dword 立即数一样。
还有,不要用jcc
和jmp
,用一个je close
来判断是否漏电
(你确实避免了 jcc
在 jmp
上的脑死亡习语,这里你的 jz
是理智的,而 jmp
完全是多余的,跳跃到下一条指令。)