【问题标题】:Reversing a string given by the user in Assembly Language反转用户在汇编语言中给出的字符串
【发布时间】:2020-11-27 14:46:17
【问题描述】:

我现在正在开发一个汇编语言程序,用户可以输入他们喜欢的任何单词,然后程序将反转它。所以,如果用户输入HELLO,程序就会输出OLLEH

我决定实现的算法是首先获取字符串的长度并将其存储在length 中。然后我将使用长度在给定的字符串中向后遍历,并将每个字符存储在一个新的内存位置,即REVERSE。我多次测试了我的程序,但它只输出一个字符。我尝试了几个单词,发现输出的字符总是字符串的第二个字符,所以我不确定它是否能够反转单词。

我是否遗漏了什么或者我错误地实现了一个代码块?

注意:GET_STRING[end] 5 在我以.exe 格式运行代码时使用。

以下是我目前正在处理的代码:

%include "io.inc"
section .data
string dd 0x00
end dd 0x00
REVERSE dd 0x00
length db 0

section .text
global CMAIN
CMAIN:
    ;write your code here
    
    PRINT_STRING "Please enter a string: "
    GET_STRING [string], 10
    
    lea esi, [string]
    lea edi, [REVERSE]
    lea ecx, [length]
    
L1:
    mov al, [esi]
    cmp al, 0
    JE FINISH
    JNE INCREMENT
    inc esi
    jmp L1

INCREMENT:
    inc byte[length]
    inc esi
    jmp L1
    

    FINISH: 
    L2:
;points to the current index of the string (i.e. if Hello,it will first point at 'o' which is 5
    mov al, [ecx] 
    cmp cl, 0  ;checks if length == 0
    JE DONE
    JNE DECREMENT
    dec ecx
    jmp L2

DECREMENT:
    mov byte[edi], al ;adds a character from [string]
    dec ecx
    jmp L2
    
DONE:
    PRINT_STRING REVERSE
    GET_STRING[end], 5
    xor eax, eax
    ret

【问题讨论】:

  • 感谢您投入工作以提供详细的问题描述。您的代码中的一些 cmets 表明您认为您的代码在做什么,也可能会有所帮助。没有它们,我只能看到它的作用,很难判断这是否符合您的意图。
  • 感谢您指出这一点!我编辑了我的帖子并在我的代码中添加了 cmets。我希望我的 cmets 能够解释我现在正在尝试实施的内容。
  • JE FINISH 和 JNE INCREMENT 涵盖了这两种可能性,因此无法访问它们之后的 inc esi / jmp L1。您应该能够通过删除它来简化您的逻辑,并且只需让 INCREMENT 案例落入该块。 (实际上,您应该能够将循环安排为只有一个总分支,底部有一个条件。Why are loops always compiled into "do...while" style (tail jump)?)在内存中拥有length 也是没有意义的。你有足够的寄存器。另外,希望输入函数返回一个长度...
  • 另外,dd 0 只占用 4 个字节的空间,因此用户输入的任何长度都将与您在另一个标签后保留的空间重叠。
  • io.inc 是 SASM 中需要的包。 SASM 目前是我用来解决问题所需的汇编程序。我仍在尝试将每个人现在在这里分享的内容融入我的答案,因为我仍然很难迁移到另一种语言。

标签: string assembly character reverse


【解决方案1】:

如果 GET_STRING 给您一个以零结尾的字符串,那么您的 L1 部分将找到该字符串的长度。

这是删除冗余代码后剩下的内容:

    lea   esi, [string]
L1:
    mov   al, [esi]
    cmp   al, 0
    je    FINISH
    inc   byte [length]
    inc   esi
    jmp   L1
FINISH:
lea ecx, [length]

然而在 L2 部分

  • 您正在使用 length 变量的地址,就好像它是字符串本身的地址一样!
  • 您正在从未使用长度加载的寄存器中检查长度 == 0

L2 部分的开头,ESI 寄存器仍然指向终止零。所以你必须先递减,然后获取并存储字符。

如果length变量包含0当然不能复制任何字符。所以先检查这个条件。

    lea   edi, [REVERSE]
    mov   cl, [length]
    test  cl, cl
    jz    DONE
L2:
    dec   esi             ; Decrement
    mov   al, [esi]       ; Fetch
    mov   [edi], al       ; Store
    inc   edi
    dec   cl              ; 1 more character done
    jnz   L2
DONE:
    mov   [edi], cl       ; CL=0 at this point, now zero-terminating the result

如果您保留当前的数据定义,则不要输入超过 3 个字符。 dword dd 只允许 3 个字符和 1 个以零结尾。
或者扩大缓冲区:

string  db 11 dup (0)        ; GET_STRING [string], 10
end     db  6 dup (0)        ; GET_STRING [end], 5
REVERSE db 11 dup (0)
length  db 0

以下是不使用基于内存的 length 变量的版本。字符串的长度存储在ECX中。

    lea   edi, [REVERSE]
    lea   esi, [string]
L1:
    mov   al, [esi]
    inc   esi
    cmp   al, 0
    jne   L1
    lea   ecx, [esi - 1 - String]
    jecxz DONE
; ESI points behind the terminating zero, the fetch uses a -2 offset (avoiding address stall)
L2:
    mov   al, [esi - 2]   ; Fetch
    mov   [edi], al       ; Store
    dec   esi
    inc   edi
    dec   ecx             ; 1 more character done
    jnz   L2
DONE:
    mov   [edi], cl       ; CL=0 at this point, now zero-terminating the result

【讨论】:

  • 感谢您向我解释我出错的部分。您的解释对让我了解寄存器指向的位置非常有帮助。它确实让事情变得更加清晰。
猜你喜欢
  • 2014-06-06
  • 2014-05-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多