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

Tasm: Trying to copy a string in reverse order to another string and display it using string operations

您好,我正在尝试逐个字符地从用户那里获取一个字符串,然后将该字符串的反向存储在一个未初始化的变量中。在我想显示那个未初始化的变量之后。我知道还有其他方法可以使用但我想使用字符串操作来这样做我认为 std 指令可以用来以相反的顺序遍历字符串。我知道 cld 用于设置从左到右的顺序。我也查了教科书,说用std前先pushf,用后popf。如果是这样,那么 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            

您的代码显示了几个问题,但让我们关注 "string" 说明。

关于DF(方向标志)

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

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

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

关于movs反转字符串

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

所以你不能做到++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