IA32 使用字符串基元转换字符串的汇编

问题描述 投票:0回答:1

我是一名正在上课的学生,我在这个项目上遇到了真正的麻烦。现在,我不仅仅是在寻找答案,因为我理所当然地想了解正在发生的事情,但我已经用尽了我所有的资源,现在我在这里。首先,我的程序应该采用用户生成的数字字符串(最多 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 中,但存储在程序的十六进制内存中。

assembly x86
1个回答
0
投票

您声称

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
(2) 相当于
mov edx, 0
但更优先。
(3) 您可以相信方向标志 (DF) 是明确的。无需使用
cld
,尤其是不要在循环中重复使用
(4)
dec
后面跟着
jnz
loop
好得多。
(5) 您仍然可以使用
mov [edi], edx
后接
add edi, 4


现在的技巧是告诉机器当它到达没有数字的点时何时停止迭代

如果从 ["0","9"] 转换为 [0,9] 的

sub al, '0'
实际上没有产生 [0,9],那么你可以认为已经到达了数字的末尾。
如果 bytesRead 值有可能为零,那么您不应该进入 _convLoop 循环,但可能仍将数字零返回到 inArray

  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`
© www.soinside.com 2019 - 2024. All rights reserved.