我是一名正在上课的学生,我在这个项目上遇到了真正的麻烦。现在,我不仅仅是在寻找答案,因为我理所当然地想了解正在发生的事情,但我已经用尽了我所有的资源,现在我在这里。首先,我的程序应该采用用户生成的数字字符串(最多 32 位)。从那里,我需要使用 LODS* 从字符串中提取每个数字,并将其转换为十进制值,然后使用 LODS* 恢复到第二个数组以供将来使用。之后,我需要重新访问该数组并将所有十进制值重新转换为其 ASCII 表示形式并将其打印出来。
现在我的问题是,每当我运行程序时,LODS* 函数和转换例程似乎运行得很好,但是当它遇到 STOS* 函数时,它会存储到程序的实际十六进制内存中,而不是数组。我的教授和他的助教没能成功地让我明白这一点,所以现在我只能任由互联网上的同行摆布。
bytesRead DWORD ?
userVal BYTE 10 DUP(?)
MulVal BYTE 10
inArray DWORD MAXSIZE DUP(?)
outArray DWORD MAXSIZE DUP(?)
.code
main PROC
MOV ECX, VALCOUNT ;VALCOUNT = 10
MOV EBX, 0
PUSH OFFSET Intro
PUSH OFFSET Header
CALL ProgIntro
MOV EDI, OFFSET inArray
_getInts:
PUSH OFFSET promptDec
PUSH OFFSET userVal
PUSH OFFSET bytesRead
CALL ReadVal
LOOP _getInts
ReadVal PROC
PUSH EBP
MOV EBP, ESP
PUSH ECX
MOV EDX, [ebp+16] ;print prompt
CALL WriteString
MOV EDX, [ebp+12] ;set buffer for input
MOV ECX, MAXSIZE
CALL ReadString
MOV bytesRead, EAX
MOV ECX, bytesRead ;set counter to number of digits
MOV ESI, [ebp+12]
MOV BL, 0
_convLoop:
CLD
LODSB
SUB AL, '0'
PUSH EAX
MOV AL, BL
MUL MulVal
MOV BL, AL
POP EAX
ADD BL, AL
STOSB
LOOP _convLoop
ADD EDI, 4
POP ECX
POP EBP
RET 12
ReadVal ENDP
这是我的主要内容和相关程序,供感兴趣的人参考。我期待着看到你们都能向我传授什么智慧。预先感谢。
尝试输入一个简单的字符串“1234”。预期 1234d 存储在 inArray 中,但存储在程序的十六进制内存中。
您声称
LODSB
和转换例程似乎运行得很好,但外表可能具有欺骗性!你是对的,但当它击中STOSB
时就会出错。该 stos
指令旨在将完成的数字存储在 inArray 中(在 EDI 中)。该指令永远不应成为 _convLoop 循环的一部分,而应遵循它。因为任务描述指出需要 32 位数字,所以双字版本 stosd
可以完成这项工作。
在您的评论中说“LODSB 是我可以使用的所有内容,这意味着我正在使用 AL,这意味着 BL 是我可以使用的所有内容”,您在加载的大小(这是一位数字拟合)之间表现出混淆以字节为单位)和存储内容的大小(即适合双字的最终数字)。您需要使用 EBX 而不是 BL,甚至更好的是 EDX,因为它不是“调用保留”寄存器。当然这也意味着你不能继续使用字节大小的乘法。幸运的是,x86 指令集具有
imul
指令,可以轻松地将任何通用寄存器与数字相乘。
call ReadString ; -> EAX (bytesRead)
mov esi, [ebp+8] ; (1) OFFSET bytesRead
mov [esi], eax ; (1)
xor edx, edx ; (2) Number under construction
mov esi, [ebp+12] ; OFFSET userVal
mov ecx, eax
_convLoop:
lodsb ; (3)
sub al, '0' ; Conversion from ["0","9"] to [0,9]
imul edx, 10
movzx eax, al ; Prepare for same-size operands
add edx, eax ; This addition needs same-size operands
dec ecx ; (4)
jnz _convLoop ; (4)
mov eax, edx ; (5)
stosd ; (5) No more need for `ADD EDI, 4`
(1) 由于您通过堆栈传递了
OFFSET bytesRead
,因此您不应该直接使用 bytesRead。mov edx, 0
但更优先。cld
,尤其是不要在循环中重复使用 !dec
后面跟着 jnz
比 loop
好得多。mov [edi], edx
后接 add edi, 4
。
现在的技巧是告诉机器当它到达没有数字的点时何时停止迭代
如果从 ["0","9"] 转换为 [0,9] 的
sub al, '0'
实际上没有产生 [0,9],那么你可以认为已经到达了数字的末尾。 call ReadString ; -> EAX (bytesRead)
mov esi, [ebp+8] ; (1) OFFSET bytesRead
mov [esi], eax ; (1)
xor edx, edx ; (2) Number under construction
test eax, eax
jz _endLoop
mov esi, [ebp+12] ; OFFSET userVal
mov ecx, eax
_convLoop:
lodsb ; (3)
sub al, '0' ; Conversion from ["0","9"] to [0,9]
cmp al, 9
ja _endLoop ; Was not a decimal digit
imul edx, 10
movzx eax, al ; Prepare for same-size operands
add edx, eax ; This addition needs same-size operands
dec ecx ; (4)
jnz _convLoop ; (4)
_endLoop:
mov eax, edx ; (5)
stosd ; (5) No more need for `ADD EDI, 4`