【问题标题】:X86 NASM Assembly converting lower to upper and upper to lowercase charactersX86 NASM 程序集将小写字符转换为大写字符和大写字符转换为小写字符
【发布时间】:2014-03-30 06:30:44
【问题描述】:

由于我对汇编还很陌生,所以如果用户在汇编中输入大写字母或反之亦然,我应该如何从小写转换为大写,我有几个问题。这是我目前所拥有的:

section .data
Enter db "Enter: "
Enter_Len equ $-Enter

Output db "Output: "
Output_Len equ $-Output

Thanks db "Thanks!"
Thanks_Len equ $-Thanks

Loop_Iter dd 0 ; Loop counter

section .bss
In_Buffer resb 2
In_Buffer_Len equ $-In_Buffer

section .text
global _start

_start:
    ; Print Enter message
    mov eax, 4 ; sys_write
    mov ebx, 1
    mov ecx, Enter
    mov edx, Enter_Len
    int 80h

    ; Read input
    mov eax, 3 ; sys_read
    mov ebx, 0
    mov ecx, In_Buffer
    mov edx, In_Buffer_Len
    int 80h

所以基本上,如果我是正确的,我的 edx 包含输入的字符串。现在出现了从小写转换为大写和从大写转换为小写的困境。由于我对此完全陌生,因此根本不知道该怎么做。任何帮助将不胜感激:)

【问题讨论】:

标签: assembly x86 nasm


【解决方案1】:

小技巧:如果他们只输入个字母,你可以用 0x20 异或他们输入的字母来交换他们的大小写。

然后,如果他们可以输入多个字母,您只需检查每个字母以查看其是否按字母顺序进行异或运算。例如,您可以通过测试来查看它是否位于“a”到“z”或“A”到“Z”范围内。

或者,您可以将每个字母映射到一个包含 256 个元素的表中,该表按照您想要的方式映射字符(例如,这通常是诸如 toupper 之类的函数的实现方式)。

【讨论】:

【解决方案2】:

如果您只支持 ASCII,那么您可以使用 OR 0x20 强制小写

  or   eax, 0x20

同样,您可以通过清除该位将字母转换为大写:

  and  eax, 0xBF   ; or use ~0x20

正如 nneonneo 提到的,可以使用 XOR 指令交换字符大小写:

  xor  eax, 0x20

仅当eax 介于“a”和“z”或“A”和“Z”之间时才有效,因此您必须进行比较并确保您在该范围内:

  cmp  eax, 'a'
  jl   .not-lower
  cmp  eax, 'z'
  jg   .not-lower
  or   eax, 0x20
.not-lower:

我使用了 nasm 语法。您可能需要确保 jl 和 jg 也是正确的...

如果您需要转换任何国际字符,那么这要复杂得多,除非您可以调用接受 Unicode 字符的 libc tolower() 或 toupper() 函数。


作为一个公平的问题:它为什么会起作用? (空白问)

ASCII 字符(也称为 ISO-8859-1)的基本大写字符定义在 0x41 和 0x5A 之间,小写字符定义在 0x61 和 0x7A 之间。

要将 4 强制为 6 并将 5 强制为 7,请强制设置位 5 (0x20)。

要转为大写,你做相反的事情,你删除第 5 位,所以它变成零。

【讨论】:

  • 您的 NASM 和 AT&T 语法的混合出现了问题。您缺少直接操作数上的 $ 。无论如何,OP 的代码都是 Intel/NASM 语法,所以这是明智的。反正你可以写and eax, ~0x20让代码更清晰。
  • 好的,我用 nasm 进行了测试以确保并进行了更正。我没有尝试运行代码,所以我不知道jl 和jg 是否真的正确。对于0xBF,我们也经常使用0x5F...不是~0x20。
  • 刚刚注意到:~0x20 适合符号扩展的 imm8,但 0xBF 会产生 and r32, imm32(因为它在 [-128 .. 127] 范围之外)。所以~0x20 效率更高,比如0xFFFFFFBF。使用0x5F 屏蔽到 7 位 ASCII 也会很有效。我认为你的分支看起来不错。我可能将标签命名为 .not_lowercase,以避免与 jl 混淆。
  • 为什么它会使用那些特定的十六进制值?你是怎么得到它们的?
  • @kuhaku 好吧...我说“第 5 位”。您通常从零开始计算位......它总是有点灰色区域,我知道......所以“第 5 位”是第 6 位。 8-) en.wikipedia.org/wiki/Bit_Test
【解决方案3】:

好的,但是您的字符串不在edx 中,它在[ecx](或[In_Buffer])中(而且它只是一个有用的字符)。获取单个字符...

mov al, [ecx]

在 HLL 中,您执行“如果有条件,则执行此代码”。您可能想知道 CPU 是如何知道是否执行代码的。我们真正做的是(HLL 为您做这件事)是“如果没有条件,则跳过此代码”(到标签)。试试看,你会明白的。

无论您的代码采用何种路径,都可以干净地退出。你没有显示这个,但我假设你这样做了。

我刚刚在sys_readhere上发布了一些信息。

这是一个完全不同的程序(添加两个数字 - “十六进制”数字),但关于 sys_read 的部分可能会让你感兴趣...

【讨论】:

    【解决方案4】:

    这是我一起编写的一个 NASM 程序,它可以翻转字符串的大小写,您基本上需要遍历字符串并检查 ascii 中的每个字符的边界,然后添加或减去 0x20 以更改大小写(即ascii 中上下之间的距离)。您可以使用 Linux ascii 命令查看 ascii 值表。

    文件:flipcase.asm

    section     .text
    global      _start                 ; Entry point for linker (ld)
    
      ; Linker entry point                                
    _start:                                                         
        mov     rcx,len                ; Place length of message into rcx
        mov     rbp,msg                ; Place address of our msg into rbp    
        dec     rbp                    ; Adjust count to offset
    
      ; Go through the buffer and convert lowercase to uppercase characters:
    upperScan:
        cmp byte [rbp+rcx],0x41        ; Test input char against uppercase 'A'                 
        jb lowerScan                   ; Not uppercase Ascii < 0x41 ('A') - jump below
        cmp byte [rbp+rcx],0x5A        ; Test input char against uppercase 'Z' 
        ja lowerScan                   ; Not uppercase Ascii > 0x5A ('Z') - jump above  
         ; At this point, we have a uppercase character
        add byte [rbp+rcx],0x20        ; Add 0x20 to get the lowercase Ascii value
        jmp Next                       ; Done, jump to next
    
    lowerScan:
        cmp byte [rbp+rcx],0x61        ; Test input char against lowercase                 
        jb Next                        ; Not lowercase Ascii < 0x61 ('a') - jump below
        cmp byte [rbp+rcx],0x7A        ; Test input char against lowercase 'z'
        ja Next                        ; Not lowercase Ascii > 0x7A ('z') - jump below  
         ; At this point, we have a lowercase char
        sub byte [rbp+rcx],0x20        ; Subtract 0x20 to get the uppercase Ascii value
         ; Fall through to next        
    
    Next:   
        dec rcx                        ; Decrement counter
        jnz upperScan                  ; If characters remain, loop back
    
      ; Write the buffer full of processed text to stdout:
    Write:        
        mov     rbx,1                  ; File descriptor 1 (stdout)    
        mov     rax,4                  ; System call number (sys_write)
        mov     rcx,msg                ; Message to write        
        mov     rdx,len                ; Length of message to write
        int     0x80                   ; Call kernel interrupt
        mov     rax,1                  ; System call number (sys_exit)
        int     0x80                   ; Call kernel
    
    section     .data
    
    msg     db  'hELLO, wwwoRLD!',0xa  ; Our dear string
    len     equ $ - msg                ; Length of our dear string
    

    然后就可以编译运行了:
    $&gt; nasm -felf64 flipcase.asm &amp;&amp; ld -melf_x86_64 -o flipcase flipcase.o &amp;&amp; ./flipcase

    【讨论】:

    【解决方案5】:

    Jeff Duntemann 写了一本书,名为《Assembly Language Step by Step Programming with linux..》,第 275-277 页很好地涵盖了这个主题。

    他使用代码sub byte [ebp+ecx], 20h 显示 然后您可以将小写更改为大写,请注意缓冲区正在使用 1024 字节,这是一种更快更好的方法,然后位于第 268-269 页的上一个示例中,其中缓冲区只有 8 位时间。

    【讨论】:

    • 为什么020h 很特别?他们是怎么得到的?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-18
    • 2016-02-24
    • 1970-01-01
    • 2018-09-07
    • 2015-06-05
    • 1970-01-01
    相关资源
    最近更新 更多