X86 NASM Assembly преобразует нижние и верхние и нижние символы

Как я довольно новичок в сборке, у меня есть несколько вопросов относительно того, как я должен преобразовывать из строчной буквы в верхний регистр, если пользователь вводит прописную букву или наоборот в сборке. вот что я до сих пор:

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 содержит введенную строку. Теперь возникает дилемма преобразования от нижнего к верхнему и верхнему к нижнему. Поскольку я совершенно новичок в этом, буквально не знаю, что делать. Любая помощь будет высоко оценена:)

Ответ 1

Если вы поддерживаете только ASCII, вы можете принудительно ввести строчный код с помощью OR 0x20

  or   eax, 0x20

Аналогично, вы можете преобразовать букву в верхний регистр, очистив этот бит:

  and  eax, 0xBF   ; or use ~0x20

И как упоминалось выше, случай символа может быть заменен с помощью команды 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 тоже верны...

Если вам нужно преобразовать любой международный символ, то это намного сложнее, если вы не можете вызвать функцию libc tolower() или toupper(), которая принимает символы Unicode.


Как справедливый вопрос: зачем это работает? (спросил кухаку)

Символы ASCII (также ISO-8859-1) имеют основные символы верхнего регистра, определенные между 0x41 и 0x5A и строчными буквами между 0x61 и 0x7A.

Чтобы заставить 4 в 6 и 5 в 7, вы устанавливаете бит 5 (0x20).

Чтобы перейти в верхний регистр, вы делаете обратное, вы удаляете бит 5, чтобы он становился равным нулю.

Ответ 2

Хорошо, но ваша строка не находится в edx, она находится в [ecx] (или [In_Buffer]) (и это только один полезный символ). Чтобы получить один символ...

mov al, [ecx]

В HLL вы делаете "если какое-то условие, выполните этот код". Вы можете задаться вопросом, как CPU знает, следует ли выполнять код или нет. То, что мы действительно делаем (HLLs делают это для вас), это "если НЕ условие, пропустите этот код" (на ярлык). Поэкспериментируйте с этим, вы поймете это.

Выйти чисто, независимо от того, какой путь ваш код. Вы не показываете это, но я предполагаю, что вы это делаете.

Я только что разместил информацию о sys_read здесь.

Это для совершенно другой программы (добавление двух чисел - "шестнадцатеричных" чисел), но часть о sys_read может вас заинтересовать...

Ответ 3

Симпатичный трюк: если они печатают только буквы, вы можете XOR их входные буквы с 0x20, чтобы обменять их.

Затем, если они могут печатать больше, чем буквы, вам просто нужно проверить каждую букву, чтобы увидеть, является ли она алфавитной, прежде чем XORing. Вы можете сделать это с помощью теста, чтобы определить, находится ли он в диапазонах от 'a' до 'z' или 'A' до 'Z', например.

В качестве альтернативы вы можете просто сопоставить каждую букву с таблицей из 256 элементов, которая отображает символы так, как вы их хотите (обычно такие функции, как toupper, реализованы).

Ответ 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

Затем вы можете скомпилировать и запустить его с помощью:
$> nasm -felf64 flipcase.asm && ld -melf_x86_64 -o flipcase flipcase.o && ./flipcase

Ответ 5

Jeff Duntemann написал книгу под названием "Язык поэтапного программирования ассемблера" с linux.., который очень хорошо освещает эту тему на стр. 275 - 277.

там он показывает, используя код sub byte [ebp+ecx], 20h вы можете изменить нижний регистр на верхний регистр, обратите внимание, что буфер использует 1024 байта, что является более быстрым и лучшим способом сделать это, чем предыдущий пример, расположенный на стр. 268-269, где буфер имеет только 8 бит на время.