Search code examples
assemblytasm

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


Hi i am trying to get a string from a user character by character then store the reverse of that string in an uninitialized variable. After I want to display that uninitialized variable. I know there are other ways of doing this using but I want to do so using string operations I thought the std instruction could've been used to traverse the string in reverse order. I know cld is used to set the order from left to right. I also checked the textbook and it said to pushf before using std and popf after use. If that is the case then where should the push and pop be placed.

  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            

Solution

  • Your code shows several problems, but let's focus on the "string" instructions.

    About DF (direction flag)

    The cld will set DF in the FLAGS register to zero. The std will set DF to one.

    The purpose of pushf advice is to preserve the original DF value, i.e. the pushf + popf enclosing pair should be around your whole operation, where you are modifying the DF, to preserve the original value, and if your code is the only one running, and not being called from external functions, you can decide to not care about original DF.

    In x86 calling conventions it is often decided that DF is expected to be zero, then you don't need to preserve original value, you just have to clear the DF after every part of code, which did need DF=1, before calling some other subroutine. This convention usually works well, as you need DF=1 only in rare cases.

    About movs to reverse string

    The movs[b/w/d] will load value from [ds:si] and store it to [es:di], and then it will adjust both si and di, either by adding (when DF=0) element size, or subtracting (DF=1) element size.

    So you can not make it to do ++si and --di, that would require to flip the DF in the middle of the instruction. movsb is thus not suitable for your needs.

    Besides that you are loading di with the address of beginning of the buffer, so even if the movsb would do what you want, you would overwrite the userExp buffer instead of writing result into bwUserExp buffer.

    You can use the string instructions for your task like this:

                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
    

    As you can see, it's not the prettiest piece of code, and look unreasonably convoluted, the string instructions are not a good fit for your task, you should rather do the task without them, like this:

                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
    

    About other problems of your code

                    ...
                    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
    

    This allows the user to enter for example ~ (126) as valid character, because 126 >= 97. Also I always frown upon signed-math branches used together with ASCII characters, as I think about ASCII characters as unsigned, but technically that doesn't change anything in your case, as you are interested into 97..122 range only, so entering some DOS character with code above 128 (not being regular ASCII any way) being treated as negative value is OK for you.

    You can fix+simplify the logic of this like:

                    ...
                    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 ...
    

    And

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

    ... while this works, you can do the simpler and more performant:

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