我正在努力学习x86程序集,并希望能够继续从事涉及多个人的项目。
这是一个(相对的)简单的子例程,它是在Intel程序集中编写的,它将一个字符串复制到另一个字符串的末尾。我将其命名为strglue,以避免与C库函数strcat (因为此方法的驱动程序是C程序)和MASM宏CatStr发生冲突。
我想知道我是否能对以下内容有一些了解:
.386
.MODEL FLAT, C
.DATA
.CODE
; Glue together (concatenate) two strings
; PARAMS:
; [ESP+4] - Pointer to first character in null-terminated destination string
; [ESP+8] - Pointer to first character in null-terminated source string
; USES:
; EAX: Holds the destination string during execution
; ECX: Holds the source string during execution:
; EDX: Lower 8 bits (DL) holds characters being copied from source to destination
; OUTPUT:
; [ESP+4] - Pointer to first character in null-terminated concatenation of destination string + source string (excluding the null-terminator from the destination string)
strglue PROC
; Step 1: Get a pointer to the null-terminator character in the destination string
MOV EAX, DWORD PTR [ESP+4]
ENDOFDEST:
INC EAX
CMP BYTE PTR [EAX], 0
JNZ ENDOFDEST
; Step 2: Copy the source string into the pointer to the end of dest, overwriting the null-terminator
MOV ECX, DWORD PTR [ESP+8]
STRCATLOOP:
MOV DL, BYTE PTR [ECX]
LEA EAX, [EAX+1]
MOV BYTE PTR [EAX-1], DL
LEA ECX, [ECX+1]
; If ZF=0 the null terminator in the source string has been reached; stop looping
TEST DL, DL
JNZ STRCATLOOP
RET
strglue ENDP
END发布于 2016-04-23 14:52:56
在我看来,这有点像我希望从编译器那里看到的代码--可以工作的代码,但完全忽略了传统的x86寄存器用法,以及x86为工作提供的特殊指令。
传统上,使用edi指向目标字符串,使用esi指向源字符串。您可以使用repnz scasb查找第一个字符串的结尾,使用lodsb/stosb将字节从源复制到目标(实际上还有一个movsb来复制字节,但它不允许您在复制时检查字符串的结尾)。
请注意,这取决于方向标志为0,但我所知道的每个编译器都是这样的(也取决于您对它的保留方式)。
xor eax, eax ; set al to value we're looking for, 0 in this case.
lea ecx, [eax-1] ; set ecx to 0ffffffffh
mov edi, [esp+4] ; find end of dest string
repnz scasb
mov esi, [esp+8] ; we've found the end of the destination, now do the copy
dec edi
copy_loop:
lodsb
stosb
test al, al
jnz copy_loop
ret这有点短。在正常情况下,我们可以预期任何一个处理器都是内存绑定的,因此速度可能不是特别重要(但如果我们确实关心,它将取决于所使用的确切处理器-这在旧处理器上可能工作得更好,但在较新的处理器上可能会慢一点)。
就可读性而言,这两种代码都相当容易阅读,但我认为我的代码更符合为x86编写代码的人希望看到寄存器被使用的方式。就缩进而言,对于是否缩进汇编语言似乎没有多少一致意见。您的缩进当然不会伤害任何东西,但是它不像高级语言,在这种语言中,没有缩进的代码显然是一个问题。我写上面的代码的方式(指令缩进一个停止,标签(S)在左边边缘)几乎肯定是更常见的。
我应该补充一点,我在发表这个话题时有点犹豫--虽然我认为我曾经很好地写过汇编语言,但我已经多年没有写到足够多的东西了。我确实期待@Edward在这方面发表评论(虽然我不确定他是否还会编写更多的程序集代码)。
发布于 2016-04-23 10:18:29
密码看起来没问题。但是有一些说明,movsb和rep,它们基本上可以代替您的第二步。您可以在第一步之后使用[esp + 8],然后执行以下操作:
sub eax, dword ptr [esp + 8] ;get the length of the string
mov ecx, eax ;move the length of the string into ecx
mov esi, dword ptr [esp + 8] ;move the source address into esi您将再次使用原始步骤1获取目标的空终止符,并执行以下操作:
mov edi, eax ;move address of the destination null-terminator to edi, you could of course also replace eax with edi in your step 1
inc ecx ;don't forget to copy the null-terminator
rep movsb ;movsb: move the value at esi to the address at edi, increment esi and edi
;rep: decrement ecx and do the following instruction again if nonzero对于文档,查找调用约定。您可以注意所使用的调用约定,但除此之外,您不需要显式地注意使用eax、ecx和edx,因为它们在大多数约定中都是参数。除此之外,我认为除了最后的评论之外,文档不是太冗长,也不是太少。
还请注意,您使用[esp + 4]和[esp + 8]作为参数,最好遵循约定,使用ecx和edx或堆栈。此外,您应该将指向堆栈的数据指针push,而不是在固定地址中返回数据。
缩进更多的是个人的事情,除了一些东西,比如不要使用硬标签(而且你没有使用它们,所以这很好)。大多数情况下,使用带有4个空格的缩进。唯一的一件事是,我个人离开跳转指令在循环结束,以及缩进。
https://codereview.stackexchange.com/questions/126495
复制相似问题