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
Your code shows several problems, but let's focus on the "string" instructions.
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.
movs
to reverse stringThe 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
...
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