首页 > 解决方案 > Tasm:尝试将一个字符串以相反的顺序复制到另一个字符串并使用字符串操作显示它

问题描述

嗨,我正在尝试逐个字符地从用户获取字符串,然后将该字符串的反转存储在未初始化的变量中。在我想显示那个未初始化的变量之后。我知道还有其他方法可以做到这一点,但我想使用字符串操作来做到这一点,我认为 std 指令可以用来以相反的顺序遍历字符串。我知道 cld 用于设置从左到右的顺序。我还检查了教科书,它说在使用 std 和 popf 之前使用 pushf 。如果是这种情况,那么 push 和 pop 应该放在哪里。

  INCLUDE   PCMAC.INC
                .MODEL SMALL
                .386
                .STACK 128
;================================================
                .DATA
prompt1         DB          13, 10,'Enter a character(Press ENTER to end Expression): $'
prompt2         DB          'Are you done ?:    $'
prompt3         DB          13, 10,'Not valid choice try again', 13, 10,'$'     
userExp         DB          50 DUP (?)
bwUserExp       DB          50 DUP (?)      
validity        DB          'Invalid$','Valid$'         
;================================================               
                .CODE
                EXTRN       PutStr : NEAR, GetCh : NEAR, PutCh : NEAR
Main            PROC        NEAR
                mov         ax, @DATA
                mov         ds, ax
                mov         es, ax
                xor         bx, bx              ;Clears bx register
                xor         cx, cx              ;Clears cx register

DispPrompt:     _PutStr     prompt1             ;Displays prompt1 to screen

GetEXP:         _GetCh      al                  ;Gets character from user
                cmp         al, 13              ;Compares character 
                                                ;to Carriage return 
                je          LoadUserExp         ;If equal to the carriage
                                                ;return user jumps to
                                                ;LoadUserExp 
                cmp         al, 97              ;Compares character to a 
                jge         AddtoExp            ;If equal or greater than
                                                ;a user jumps to AddtoExp
                cmp         al, 122             ;Compares character to z 
                jle         AddtoExp            ;If equal or greater than
                                                ;a user jumps to AddtoExp
                jmp         GetEXP              ;Jumps to GetEXP if character 
                                                ;is not any of the matching
                                                ;characters

AddtoExp:       mov         userExp + bx, al    ;Adds character from 
                                                ;al to userExp array    
                inc         bx                  ;increments bx to
                                                ;increment the position
                                                ;in the array
                jmp         GetEXP              ;Jumps to GetEXP

LoadUserExp:    
                mov         si, OFFSET userExp      ;Loads userExp into si
                mov         di, OFFSET bwUserExp    ;Loads bwUserExp into di
                std                                 ;Tells the program to
                                                    ;go from Right to the Left
                mov         cx, bx                  ;Moves bx (the size of the array) into cx
                rep         movsb                   ;Moves the contents of si into di

DispLoop:       
                mov         cx, bx                  ;Moves bx (the size of the array) into cx
                xor         bx, bx                  ;Clears the bx register

DisplayExp:     mov         al, bwUserExp + bx      ;Moves the character 
                                                    ;in position bx into al
                _PutCh      al                      ;Displays the value of
                                                    ;al to the screen
                inc         bx                      ;increments bx to increment 
                                                    ;the position in the array
                dec         cx                      ;Decrements cx 
                jcxz        done                    ;Jumps to done if
                                                    ;cx is zero
                jmp         DisplayExp              ;Jumps to DisplayExp 
                                                    ; if cx is not zero

done:           mov         ax, 4c00h
                int         21h             

Main            ENDP                            
;================================================
END             Main            

标签: assemblytasm

解决方案


您的代码显示了几个问题,但让我们关注“字符串”指令。

关于 DF(方向标志)

将寄存器cld中的 DF 设置FLAGS为零。将stdDF 设置为 1。

建议的目的pushf是保留原始DF值,即pushf + popf封闭对应该围绕您的整个操作,您正在修改 DF,以保留原始值,并且如果您的代码是唯一运行的代码,并且没有被调用外部函数,您可以决定不关心原始 DF。

在 x86 调用约定中,通常决定 DF 预计为零,那么您不需要保留原始值,您只需在代码的每一部分之后清除 DF,这确实需要 DF=1,然​​后再调用一些其他子程序。这种约定通常效果很好,因为只有在极少数情况下才需要 DF=1。

即将movs反转字符串

movs[b/w/d]将从 加载值并将其[ds:si]存储到[es:di],然后通过添加(当 DF=0 时)元素大小或减去(DF=1)元素大小来调整si和。di

因此,您无法做到++si并且--di,这需要在指令中间翻转 DF。movsb因此不适合您的需求。

除此之外,您正在加载di缓冲区的开头地址,因此即使movsb会执行您想要的操作,您也会覆盖userExp缓冲区而不是将结果写入bwUserExp缓冲区。

您可以将字符串指令用于您的任务,如下所示:

            mov         si, OFFSET userExp       ; source buffer
            lea         di, [bx + bwUserExp - 1] ; end(!) of destination buffer
            mov         cx, bx                   ; cx = size of user input
reverse_loop:
            cld                                  ; DF=0
            lodsb                                ; al = one character, ++si
            std                                  ; DF=1
            stosb                                ; store character, --di
            dec         cx
            jnz reverse_loop
            cld                                  ; DF=0 for future use

如您所见,它不是最漂亮的代码,而且看起来非常复杂,字符串指令不适合您的任务,您宁愿在没有它们的情况下完成任务,如下所示:

            mov         si, OFFSET userExp       ; source buffer
            lea         di, [bx + bwUserExp]     ; beyond end of destination buffer
            mov         cx, bx                   ; cx = size of user input
reverse_loop:
            mov         al,[si]
            dec         di
            inc         si
            mov         [di],al
            dec         cx
            jnz reverse_loop
            ; di points at bwUserExp here

关于您的代码的其他问题

                ...
                cmp         al, 97              ;Compares character to a 
                jge         AddtoExp            ;If equal or greater than
                                                ;a user jumps to AddtoExp
                cmp         al, 122             ;Compares character to z 
                jle         AddtoExp            ;If equal or greater than
                                                ;a user jumps to AddtoExp
                jmp         GetEXP              ;Jumps to GetEXP if character 
                                                ;is not any of the matching
                                                ;characters

这允许用户输入例如~(126) 作为有效字符,因为 126 >= 97。此外,我总是不赞成与 ASCII 字符一起使用的有符号数学分支,因为我认为 ASCII 字符是无符号的,但从技术上讲,这并不在您的情况下不要更改任何内容,因为您只对 97..122 范围感兴趣,因此输入一些代码高于 128 的 DOS 字符(无论如何都不是常规 ASCII)被视为负值对您来说是可以的。

您可以修复+简化这样的逻辑:

                ...
                cmp         al, 'a'
                jb          GetEXP              ; ignore characters below 'a'
                cmp         al, 'z'
                ja          GetEXP              ; ignore characters above 'z'
AddtoExp:
                ... valid 'a'..'z' input, add to buffer ...

            dec         cx                      ;Decrements cx 
            jcxz        done                    ;Jumps to done if
                                                ;cx is zero
            jmp         DisplayExp              ;Jumps to DisplayExp 

...虽然这可行,但您可以做更简单、更高效的事情:

            dec         cx                      ;Decrements cx 
            jnz         DisplayExp              ;Jumps to DisplayExp until cx is zero

推荐阅读