【问题标题】:What is the "simplest" way to reverse case a string in Assembly 8086 [duplicate]在Assembly 8086中反转字符串的“最简单”方法是什么[重复]
【发布时间】:2021-03-16 02:24:41
【问题描述】:

目前,我创建了一个程序,允许我输入字符串并将其反转。它做我想做的事,我认为这是最好的方式。现在我想反转大小写的意思,当有人进入 Hello World 时,它会输出 DLORw OLLEh

我不确定解决此问题的最佳方法是什么。我已经查看了平台上的其他解决方案,但在尝试使一切井井有条时遇到了一些困难。如果有任何改进我当前代码的建议,我也很感激

下面是我目前的代码:

include 'emu8086.inc'
 
org 100h  


CALL STRING_INPUT 
RET

STRING_INPUT PROC
     
    PRINT "Enter a string to reverse it: "   
    LEA DI, buffer ; put string inside so we can read it later
    MOV DX, buffSize

    CALL GET_STRING
    PRINTN  
   
    MOV SI, DI 
  
    MOV CX, 0h ; character count of string
    
STRING_INPUT ENDP 

STRING_REVERSE PROC 
    ReadInput:
        ;check for last character 
        MOV AX, [SI]  
        CMP AL, 0
        JE CreateReverse  
     
        PUSH [SI] ; push inside stack 
      
        ; count each character
        INC SI  
        INC CX    
        JMP ReadInput  
         
    CreateReverse: ; 
        MOV SI, DI ; set input again 
        MOV AX, CX ; store length of input
      
        BuildString:    
            CMP CX,0  
            JE PrintReversedInput  
      
            POP DX    
            MOV DH, 0  
            MOV [SI], DX ;set si to the reversed string character
            INC SI       
            DEC CX  
            JMP BuildString
                   
    PrintReversedInput:  
        MOV SI, DI
        PRINT "Reversed String: "  
        CALL PRINT_STRING    
        RET
    
STRING_REVERSE ENDP 
 
buffer DB 20 DUP (?)  ;set input max size for get_string
buffSize = $-buffer

DEFINE_GET_STRING
DEFINE_PRINT_STRING

END

【问题讨论】:

  • ASCII 被排列成大小写字母相差 32 个位置。因此,您可以通过将字母与常量 32 进行异或来反转字母的大小写。请注意,您可能需要首先检查该字符是否实际上是字母。
  • 最简单的方法可能是使用查找表,部分原因是代码可以将所有字符视为相同(不关心字符是否为字母)。
  • @Brendan 是否有任何资源显示如何使用查找表?似乎无法在线找到 x86 的任何资源
  • @NateEldredge 是否有必要检查字符是否为字母?例如,如果用户输入中有空格,这意味着字符数也将与常数 32 异或?我忽略了用户可以输入数字的事实,让我们假设用户是值得信赖的哈哈
  • 是的,这是必要的。例如,如果有空格,则与 32 进行异或运算会将其更改为 NUL,这肯定不会打印为空格。

标签: assembly x86 emu8086


【解决方案1】:

如果有任何改进我当前代码的建议,我也很感激

让我们从这个开始。

  • 你为什么让你的proc's 掉入对方?要么创建 2 个 call,要么创建一个 proc
  • 如果数据已经从寄存器中可用,为什么还要从内存位置获得push?在代码中:将push [si] 更改为push ax
  • 当一个字符存储在一个字节中时,为什么要写一个字?在代码中:将mov [si], dx 更改为mov [si], dl
  • 为什么在设置参数寄存器和调用 PRINT_STRING 之间允许宏调用?您是否知道 PRINT 宏不会破坏 SI

接下来是我的重写,它弥补了上述问题,还删除了一些多余的指令等等......

include 'emu8086.inc'
 
org 100h  

CALL STRING_INPUT 
CALL STRING_REVERSE
PRINT "Reversed String: "
LEA  SI, buffer
CALL PRINT_STRING
RET                      ; Exit to DOS

STRING_INPUT PROC
    PRINT "Enter a string to reverse it: "   
    LEA  DI, buffer
    MOV  DX, buffSize
    CALL GET_STRING
    PRINTN
    RET
STRING_INPUT ENDP

STRING_REVERSE PROC
    XOR  CX, CX          ; character count of string = 0
    LEA  SI, buffer
  ReadInput:
    MOV  AL, [SI]
    CMP  AL, 0
    JE   CreateReverse  
    PUSH AX              ; Don't care about garbage in AH
    INC  SI
    INC  CX
    JMP  ReadInput
  CreateReverse:
    JCXZ DoneReversing
    LEA  SI, buffer
  BuildString:
    POP  DX
    MOV  [SI], DL
    INC  SI
    DEC  CX
    JNZ  BuildString
  DoneReversing:        ; There's no need to zero terminated the string manually
    RET                 ; because Input and Output have the same length and the
STRING_REVERSE ENDP     ; original zero-terminator is still there.
 
buffer DB 20 DUP (?)
buffSize = $-buffer

DEFINE_GET_STRING
DEFINE_PRINT_STRING

END

现在我想反转大小写的意思,当有人进入 Hello World 时,它会输出 DLROw OLLEh

您可以选择在ReadInputBuildString 中进行此转换。我更喜欢后者。

代码:

  BuildString:
    POP  DX              ; OriginalChar

    mov  al, dl          ; All verification is done on a copy of the character!
    or   al, 32          ; [A,Z] become [a,z], [a,z] stays [a,z], others don't care
    sub  al, 'a'         ; If [a,z] it will become [0,25]
    cmp  al, 26
    jnb  Other
    xor  dl, 32          ; Change the case on the OriginalChar: [A,Z] -> [a,z]
  Other:                 ;                                      [a,z] -> [A,Z]

    MOV  [SI], DL
    INC  SI
    DEC  CX
    JNZ  BuildString

您想要“最简单的方法”。嗯,这是非常私人的。

  • 我对@Nate Eldredge 建议的解决方案的实施只需要一些简单的说明,仅此而已。
  • @Brendan 建议的 查找表 解决方案需要的指令更少,但不可忽略强制的 256 字节 ASCII 表。

正如@Peter Cordes 在他的评论中指出的那样,您可以在不以这种方式使用堆栈的情况下编写此逆向操作。您需要的是从末尾开始填充的第二个缓冲区 bufferB。这次 STRING_REVERSE proc 以 SI 注册结果字符串的地址结束,准备打印!

...
PRINT "Reversed String: "
CALL STRING_REVERSE    ; -> SI
CALL PRINT_STRING
...

...
STRING_REVERSE PROC
    LEA  DI, buffer
    LEA  SI, bufferB + 19
    MOV  byte [SI], 0        ; Zero-terminate the output buffer
  ReadInput:
    MOV  DL, [DI]
    CMP  DL, 0
    JE   DoneReversing

    mov  al, dl          ; All verification is done on a copy of the character!
    or   al, 32          ; [A,Z] become [a,z], [a,z] stays [a,z], others don't care
    sub  al, 'a'         ; If [a,z] it will become [0,25]
    cmp  al, 26
    jnb  Other
    xor  dl, 32          ; Change the case on the OriginalChar: [A,Z] -> [a,z]
  Other:                 ;                                      [a,z] -> [A,Z]

    DEC  SI
    MOV  [SI], DL
    INC  DI
    JMP  ReadInput
  DoneReversing:
    RET
STRING_REVERSE ENDP

【讨论】:

  • 人们使用push / pop 来扭转局面是怎么回事?如果您一次读取输入 1 个字节,则可以从数组末尾开始按降序存储它。无论如何,您都将打印它(即将它复制到其他地方),因此字符串在缓冲区中的哪个位置开始并不重要,只要您有一个指向该开始的指针。 (或者如果您的读取函数返回长度,您可以从那里开始并将结果留在缓冲区的开头)。公平地说,OP 已经有了这段代码,但我至少会提到整个事情只需要 1 次通过。
  • @PeterCordes OP 使用的 GET_STRING 一次性检索输入,并且该过程不提供计数。我确实使用第二个缓冲区添加了替代版本。
  • 哦,对了,这需要另一个缓冲区,除非你 strlen 它这样你就可以就地反转,一次做一对字符。 (复制案例翻转代码)。不返回长度的读取函数是愚蠢的。
猜你喜欢
  • 1970-01-01
  • 2012-06-01
  • 2021-06-25
  • 2012-01-07
  • 2017-01-21
  • 2012-08-01
  • 2011-10-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多