在过程调用和操作 ecx 寄存器之前保存 ECX 寄存器
Posted
技术标签:
【中文标题】在过程调用和操作 ecx 寄存器之前保存 ECX 寄存器【英文标题】:Saving ECX registers before procedure calls and manipulatation of ecx register 【发布时间】:2019-06-08 06:41:54 【问题描述】:我在尝试将 ecx 寄存器保存在堆栈上然后在过程调用中将 ecx 寄存器用于循环时遇到了一些麻烦。我从数字中获得的输出受主程序中的 ecx 约束,而不是程序。问题的本质是在 Masm 中获取字符串,然后使用 ascii 图表表将其更改为数字,然后将这些新数字放入数组中。但是当我从用户那里输入数字时,它会受到主循环的约束,例如,如果我的 ecx 为 3,那么它将只取我输入的前三个数字。首先代码是主要的,然后是两个宏的程序。任何帮助将不胜感激。
我尝试将 ecx 寄存器推入程序内部,然后在程序结束后将其弹出,但这只会弄乱我在主程序中的循环。我还尝试在过程调用开始时使用 pushad 保存所有寄存器,然后在代码末尾使用 popad 将它们全部弹出。那也没用。
.data
prompt1 byte "Welcome to Low level I/O programming , Assignment6.asm, I am your Programmer Jackson Miller :)",0
prompt2 byte "Ths will prompt you for 10 unsigned integears, make sure they can fit into a 32 bit register. After you enter 10 raw ints, I will display the list, sum, and average value",0
prompt3 byte " Please enter a unsigned integear: ",0
prompt4 byte " Invalid Entry",0
input byte 200 dup(0)
list dword 20 dup(0)
num dword ?
temp dword ?
test1 byte "How many times it get here",0
main PROC
push offset prompt1
push offset prompt2
call introduction
mov ecx,10
mov edi, offset list
mov ebx,0
mov edx,0
fillnumbers:
push ecx
push edx
displaystring prompt3
push offset input
call readval
pop edx
mov [edi+edx],eax
add edx,4
pop ecx
loop fillnumbers
mov esi, offset list
mov ecx,10
mov ebx,0
displayints:
mov eax, [esi+ebx]
call writedec
add ebx,4
loop displayints
exit ; exit to operating system
main ENDP
readval PROC
push ebp
mov ebp,esp
retry:
mov edx, [ebp+8]
getstring edx
mov esi,edx
mov ecx,0
check:
lodsb
cmp ax,0
je done
cmp ax,57
jle good
jmp notgood
good:
cmp ax,48
jge doublegood
jmp notgood
doublegood:
sub ax,48
mov ebx,10
xchg eax,ecx
mul ebx
add ecx,eax
mov eax,0
jmp check
notgood:
mov edx,offset prompt4
call writestring
call crlf
mov edx, offset prompt3
call writestring
jmp retry
done:
mov eax,ecx
call writedec
pop ebp
ret 4
readval ENDP
displaystring MACRO input
push edx
mov edx, offset input
call writestring
pop edx
ENDM
getstring MACRO buffer
push edx
mov edx,buffer
call readstring
pop edx
ENDM
【问题讨论】:
您说的是writedec
过程,对吧?不幸的是,您没有发布writedec
程序...
首先不要使用loop
,如果您希望ECX 用于循环内的其他内容。使用呼叫保留寄存器。 (在 Irvine32 的调用约定中,所有寄存器都是调用保留的,返回值除外。)
@PeterCordes 我没有完全理解这个问题:在哪个时刻ecx
没有预期的价值?
【参考方案1】:
Irvine32 ReadString
根据the documentation 有 2 个参数:
在您使用getstring
宏调用它时,ECX 会保存main
的循环计数器,因此您的最大长度字符限制会随着每次外循环迭代而减少。
为您的循环计数器选择一个不同的寄存器,例如 EDI 或 EBX,这样您就可以将 ECX 设置为您的缓冲区长度,或者让 readval
销毁 ECX。
loop
指令在除 AMD Bulldozer/Ryzen 之外的所有现代 CPU 上都很慢,因此您不应该首先使用它,除非您正在优化代码大小。但是,如果您是,并且,在 ECX 中使用递减计数器实际上很方便,那么当然可以。否则以不同的方式循环,例如dec edi
/ jnz top_of_loop
。
其他错误:
lodsb
/ cmp ax,0
:你没有将 EAX 归零,lodsb
有点像 mov al, [esi]
/ inc esi
。所以 AX 的高字节在这里可能是非零的。如果您正在寻找零终止符,那么检查它是没有意义的。
ReadString 返回EAX
= 输入字符串的大小,因此在实践中(除了非常长的输入)只有 EAX 的低字节非零,lodsb
替换它。因此,您的代码恰好适用于正常输入。
不过,通过检查第一个非数字而不是分别检查 0 和非数字要容易得多。您可以将该 ReadString 返回值用作递减计数器,但如果您正在检查其他非数字输入以结束循环,
... get string input
xor eax, eax
jmp first_iteration_starts_here
top_of_loop:
imul eax, 10
add eax, edx
first_iteration_starts_here:
movzx edx, [esi]
inc esi
sub edx, '0'
;; EDX = an integer from 0 .. 9 else out of bounds
cmp edx, 9
jbe top_of_loop ; *unsigned* compare catches low characters that wrapped, too
; EDX = some non-digit character minus '0'
; total in EAX.
ret
您正在编写 32 位代码,因此您应该使用 2 or 3-operand imul
for integer multiply,除非您确实想要高半结果。您不必为使用不方便且效率较低的 1 操作数 mul
而受苦。
【讨论】:
以上是关于在过程调用和操作 ecx 寄存器之前保存 ECX 寄存器的主要内容,如果未能解决你的问题,请参考以下文章
汇编里的"PUSH"和"POP"起了啥功能,,谁能用通俗点的话告诉我吗??
有没有一种简单的方法可以在 AT&T 汇编中像这样将两个寄存器相乘:%eax * %ebx = %ecx