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]
,然后它将调整 si
和 di
,或者通过添加(当 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
您好,我正在尝试逐个字符地从用户那里获取一个字符串,然后将该字符串的反向存储在一个未初始化的变量中。在我想显示那个未初始化的变量之后。我知道还有其他方法可以使用但我想使用字符串操作来这样做我认为 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]
,然后它将调整 si
和 di
,或者通过添加(当 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