为什么在汇编语言中执行操作时我们必须为字符串添加-2?

时间:2017-10-31 16:37:04

标签: assembly dos x86-16

Data Segment
    str1 db 'MADAME','$' 
    strlen1 dw $-str1  ;calculating the length of the string
  strrev db 20 dup(' ')
  s1 db 'String is:','$'
  NEWLINE DB 10,13,"$"
  str_palin db 'String is Palindrome.','$'
  str_not_palin db 'String is not Palindrome.','$'
Data Ends

Code Segment
  Assume cs:code, ds:data

  Begin:

    mov ax, data
    mov ds, ax
    mov es, ax
    mov cx, strlen1
    add cx, -2

    lea si, str1
    lea di, strrev

    add si, strlen1
    add si, -2
     mov ah, 09h
     lea dx, s1
     int 21h
     mov ah, 09h
     lea dx, str1
     int 21h
     MOV AH,09H
        LEA DX,NEWLINE
        INT 21H
    L1:
       mov al, [si]
       mov [di], al
       dec si
       inc di
       loop L1
       mov al, [si]
       mov [di], al
       inc di
       mov dl, '$'
       mov [di], dl
       mov cx, strlen1

    Palin_Check:
       lea si, str1
       lea di, strrev
       repe cmpsb
       jne Not_Palin

    Palin:
       mov ah, 09h
       lea dx, str_palin
       int 21h
       jmp Exit

    Not_Palin:
       mov ah, 09h
       lea dx, str_not_palin
       int 21h

    Exit:
       mov ax, 4c00h
       int 21h
Code Ends
End Begin

1 个答案:

答案 0 :(得分:2)

添加-2(add cx, -2

的第一个实例

考虑

mov cx, strlen1
add cx, -2        <-- Can be avoided totally

以及

L1:
 mov al, [si]
 mov [di], al
 dec si
 inc di
 loop L1
 mov al, [si]     <-- Should stay inside the loop
 mov [di], al     <-- Should stay inside the loop
 inc di           <-- Should stay inside the loop

由于 strlen1 的定义方式(strlen1 dw $-str1add cx, -2(为什么这不仅仅是sub cx, 2?)没有给出正确的长度串。你得到的太少了。之后因此,你的 L1 循环必须附加3条额外的指令!

添加-2(add si, -2

的第二个实例
lea si, str1
add si, strlen1
add si, -2

再次,为什么更喜欢add si, -2而不是更具可读性sub si, 2? 由于 strlen1 的定义方式(strlen1 dw $-str1),add si, strlen1会使SI落后于终止$字符。
减去1会使SI终止$字符,因此后面字符串的最后一个字符。
减去2将使SI位于字符串的最后一个字符。

建议

如果您重新定义 strlen1 以使其不包含终止$字符,则上述大部分问题都不会存在。当人们谈论字符串的长度时,他们很少在计数中包含任何终止字符。这样的字符(无论是$还是零)并不是字符串

part
strlen1 dw $ - str1 - 1  ;Length of the string

在上下文中查看所有内容:

 mov  ah, 09h
 mov  dx, s1
 int  21h
 mov  ah, 09h
 mov  dx, str1
 int  21h
 mov  ah, 09h
 mov  dx, NEWLINE
 int  21h

 cld                 ;To be absolutely safe
 mov  cx, strlen1    ;The improved definition! db 'MADAME','$' => 5
 mov  di, strrev
 mov  si, str1
 add  si, cx         ;Now points behind the last character ('E')
L1:
 dec  si
 mov  al, [si]
 stosb               ;Equivalent to "mov [di], al" "inc di"
 dec  cx
 jnz  L1
 mov  byte ptr [di], '$'

请注意以下细节:

  • 我已将显示在屏幕上的代码与执行反转的代码完全分开。
  • dec si指令放在之前阅读[SI](我们称之为预先递减),之前可以删除一条指令循环从 L1 开始。
  • 我已经lea替换了每个mov。结果相同,但代码缩短了1个字节。每一次。
  • 我已使用等效代码loop dec cx替换了慢速jnz L1指令。
  • 我已将mov [di], al inc di指令替换为仅1个等效指令stosb。我可以这样做,因为设置了ES寄存器并且我清除了方向标志(DF)。您的repe cmpsb也取决于DF = 0。
  • 我已经用1条指令mov byte ptr [di], '$'替换了编写新$终结符的指令对。