将计算出字母表中两个字母的距离

will work out how far apart two letters are in the alphabet

然后程序会计算出两个字母的位置之间的差异 字母表,并将其显示到屏幕上。例如,“a”和“e”之间的差值是 4。 “d”和“b”之间的差异是 2。“c”和“c”之间的差异是 0。 这就是我所做的,但我不知道如何计算字母之间的差距

SECTION .bss
SECTION .data
tMsg: db "fist letter",10
tLen: equ $-tMsg
Input: times 100 db 0
ok: db "ok"
oklen: equ $-ok
yMsg: db "Second letter",10
yLen: equ $-yMsg
SECTION .text 
global _start 

   _start:
   nop
   mov eax,4
   mov ebx,1
   mov ecx,tMsg
   mov edx,tLen
   int 80H

   mov eax,3
   mov ebx,0
   mov ecx,Input
   mov edx,100
   int 80H

   mov ax, [Input]
   mov bx, [ok]
   cmp ax, bx
   je y
   mov eax,1
   mov ebx,0
   int 80H

   y:
   mov eax,4
   mov ebx,1
   mov ecx,yMsg
   mov edx,yLen


   int 80H
   mov eax,1
   mov ebx,0
   int 80H

如果您仍然卡住,您的问题要求您确定两个字符之间的距离。这带来了许多您必须实施的检查。尽管您的问题对是否需要同时处理大写和小写字母距离保持沉默,但除非您将所有内容都转换为一种情况或另一种情况,否则您将需要确定两个字符是否具有相同的大小写以使字母表之间的距离这两个字符有效。

由于涉及到两个字符,你需要一种方法来保存第一个字符的大小写,以便与第二个字符的大小写进行比较。在这里,在所有需要简单状态的情况下,只使用一个字节(标志)来存储状态就和其他任何东西一样简单。例如,如果 ASCII 字符不是字母字符,则保存 0 的字节,如果字符是大写字符,则保存 1 ,如果字符是小写字符,则保存 2 (或您喜欢的任何一致方案) )

这样,当您完成比较和测试后,您可以简单地比较两个标志是否相等。如果它们相等,您可以继续从另一个中减去一个以获得距离(如果需要交换),然后输出将数字转换为 ASCII 数字输出的数字。

判断字符是否为大写字符,类似于C中的isupper(),只需要一个简短的函数:

; check if character isupper()
; parameters:
;   ecx - address holding character
; returns;
;   eax - 0 (false), 1 (true)
_isupr:
    
    mov eax, 1                  ; set return true
    
    cmp byte[ecx], 'A'          ; compare with 'A'
    jge _chkZ                   ; char >= 'A'
    
    mov eax, 0                  ; set return false
    ret
    
  _chkZ:
    cmp byte[ecx], 'Z'          ; compare with 'Z'
    jle _rtnupr                 ; <= is uppercase
    
    mov eax, 0                  ; set return false
    
  _rtnupr:
    ret

您可以通过多种方式处理您需要的本地数组和值的存储。您可以从当前堆栈指针中减去以在堆栈上创建临时存储,或者以更易读的方式创建标签以存储在未初始化的段 (.bss) 中并将标签用作变量名。您初始化的变量进入 .data 段。例如,程序的存储可以是:

section .bss

    buf     resb 32             ; general buffer, used by _prnuint32
    bufa    resb 8              ; storage for first letter line
    bufb    resb 8              ; storage for second letter line
    lena    resb 4              ; length of first letter line
    lenb    resb 4              ; length of second letter line
    nch     resb 1              ; number of digit characters in _prnuint32
    ais     resb 1              ; what 1st char is, 0-notalpha, 1-upper, 2-lower
    bis     resb 1              ; same for 2nd char

与其使用散布在系统调用设置中的数字,不如声明初始化标签,例如stdinstdout 而不是使用 01 使事情更具可读性:

section .data

    bufsz:  equ 32
    babsz:  equ 8
    tmsg:   db "first letter : "
    tlen:   equ $-tmsg
    ymsg:   db "second letter: "
    ylen:   equ $-ymsg
    dmsg:   db "char distance: "
    dlen:   equ $-dmsg
    emsg:   db "error: not alpha or same case", 0xa
    elen:   equ $-emsg
    nl:     db 0xa
    stdin:  equ 0
    stdout: equ 1
    read:   equ 3
    write:  equ 4
    exit:   equ 1

然后为了读取您的字符输入,您将有,例如

    mov     eax, write          ; prompt for 1st letter
    mov     ebx, stdout
    mov     ecx, tmsg
    mov     edx, tlen
    int 80h                     ; __NR_write
    
    mov     eax, read           ; read 1st letter line
    mov     ebx, stdin
    mov     ecx, bufa
    mov     edx, babsz
    int 80h                     ; __NR_read
    
    mov     [lena], eax         ; save no. of character in line

然后检查字符输入的大小写,你可以这样做:

    call    _isupr              ; check if uppercase
    cmp     eax, 1              ; check return 0-false, 1-true
    jne chkalwr                 ; if not, branch to check lowercase
    
    mov     byte[ais], 1        ; set uppercase flag for 1st letter
    
    jmp getb                    ; branch to get 2nd letter
    
  chkalwr:
    call    _islwr              ; check if lowercase
    cmp     eax, 1              ; check return
    jne notalpha                ; 1st letter not alpha char, display error
    
    mov     byte[ais], 2        ; set lowercase flag for 1st char

notalpha: 标签只是一个块,用于在字符不是字母字符或两个字符之间的大小写不匹配时输出错误:

  notalpha:                     ; show not alpha or not same case error
    mov     eax, write
    mov     ebx, stdout
    mov     ecx, emsg
    mov     edx, elen
    int 80h                     ; __NR_write
    
    mov     ebx, 1              ; set EXIT_FAILURE

完成两个字符的输入和分类后,现在需要验证两个字符是否大小写相同,如果是则需要计算字符之间的距离(必要时交换,或使用绝对值) value) 最后处理它们之间的距离从数值到 ASCII 数字的转换输出。您可以执行类似以下操作:

  chkboth:
    mov     al, byte[ais]       ; load flags into al, bl
    mov     bl, byte[bis]
    cmp     al, bl              ; compare flags equal, else not same case
    jne notalpha
    
    mov     eax, write          ; display distance output
    mov     ebx, stdout
    mov     ecx, dmsg
    mov     edx, dlen
    int 80h                     ; __NR_write
    
    mov     al, byte[bufa]      ; load chars into al, bl
    mov     bl, byte[bufb]
    cmp     al, bl              ; chars equal, zero difference
    
    jns getdiff                 ; 1st char >= 2nd char
    
    push    eax                 ; swap chars
    push    ebx
    pop     eax
    pop     ebx
    
  getdiff:
    sub     eax, ebx            ; subtract 2nd char from 1st char
    call _prnuint32             ; output difference
    
    xor     ebx, ebx            ; set EXIT_SUCCESS
    jmp     done

把它放在一起,包括下面的_prnuint32函数,用于转换和输出字符之间的数字距离,你会得到:

section .bss

    buf     resb 32             ; general buffer, used by _prnuint32
    bufa    resb 8              ; storage for first letter line
    bufb    resb 8              ; storage for second letter line
    lena    resb 4              ; length of first letter line
    lenb    resb 4              ; length of second letter line
    nch     resb 1              ; number of digit characters in _prnuint32
    ais     resb 1              ; what 1st char is, 0-notalpha, 1-upper, 2-lower
    bis     resb 1              ; same for 2nd char

section .data

    bufsz:  equ 32
    babsz:  equ 8
    tmsg:   db "first letter : "
    tlen:   equ $-tmsg
    ymsg:   db "second letter: "
    ylen:   equ $-ymsg
    dmsg:   db "char distance: "
    dlen:   equ $-dmsg
    emsg:   db "error: not alpha or same case", 0xa
    elen:   equ $-emsg
    nl:     db 0xa
    stdin:  equ 0
    stdout: equ 1
    read:   equ 3
    write:  equ 4
    exit:   equ 1

section .text

    global _start:
_start:
    
    mov     byte[ais], 0        ; zero flags
    mov     byte[bis], 0
    
    mov     eax, write          ; prompt for 1st letter
    mov     ebx, stdout
    mov     ecx, tmsg
    mov     edx, tlen
    int 80h                     ; __NR_write
    
    mov     eax, read           ; read 1st letter line
    mov     ebx, stdin
    mov     ecx, bufa
    mov     edx, babsz
    int 80h                     ; __NR_read
    
    mov     [lena], eax         ; save no. of character in line
    
    call    _isupr              ; check if uppercase
    cmp     eax, 1              ; check return 0-false, 1-true
    jne chkalwr                 ; if not, branch to check lowercase
    
    mov     byte[ais], 1        ; set uppercase flag for 1st letter
    
    jmp getb                    ; branch to get 2nd letter
    
  chkalwr:
    call    _islwr              ; check if lowercase
    cmp     eax, 1              ; check return
    jne notalpha                ; 1st letter not alpha char, display error
    
    mov     byte[ais], 2        ; set lowercase flag for 1st char
    
  getb:
    mov     eax, write          ; prompt for 2nd letter
    mov     ebx, stdout
    mov     ecx, ymsg
    mov     edx, ylen
    int 80h                     ; __NR_write
    
    mov     eax, read           ; read 2nd letter line
    mov     ebx, stdin
    mov     ecx, bufb
    mov     edx, babsz
    int 80h                     ; __NR_read
    
    mov     [lenb], eax         ; save no. of character in line
    
    call    _isupr              ; same checks for 2nd character
    cmp     eax, 1
    jne chkblwr
    
    mov     byte[bis], 1
    
    jmp chkboth
    
  chkblwr:
    call    _islwr
    cmp     eax, 1
    jne notalpha
    
    mov     byte[bis], 2
    
  chkboth:
    mov     al, byte[ais]       ; load flags into al, bl
    mov     bl, byte[bis]
    cmp     al, bl              ; compare flags equal, else not same case
    jne notalpha
    
    mov     eax, write          ; display distance output
    mov     ebx, stdout
    mov     ecx, dmsg
    mov     edx, dlen
    int 80h                     ; __NR_write
    
    mov     al, byte[bufa]      ; load chars into al, bl
    mov     bl, byte[bufb]
    cmp     al, bl              ; chars equal, zero difference
    
    jns getdiff                 ; 1st char >= 2nd char
    
    push    eax                 ; swap chars
    push    ebx
    pop     eax
    pop     ebx
    
  getdiff:
    sub     eax, ebx            ; subtract 2nd char from 1st char
    call _prnuint32             ; output difference
    
    xor     ebx, ebx            ; set EXIT_SUCCESS
    jmp     done
    
  notalpha:                     ; show not alpha or not same case error
    mov     eax, write
    mov     ebx, stdout
    mov     ecx, emsg
    mov     edx, elen
    int 80h                     ; __NR_write
    
    mov     ebx, 1              ; set EXIT_FAILURE
    
  done:
    mov     eax, exit           ; __NR_exit
    int 80h
    
; print unsigned 32-bit number to stdout
; arguments:
;   eax - number to output
; returns:
;   none
_prnuint32:
    mov     byte[nch], 0        ; zero nch counter
    
    mov     ecx, 0xa            ; base 10  (and newline)
    lea     esi, [buf + 31]     ; load address of last char in buf
    mov     [esi], cl           ; put newline in buf
    inc     byte[nch]           ; increment char count in buf

_todigit:                       ; do {
    xor     edx, edx            ; zero remainder register
    div     ecx                 ; edx=remainder = low digit = 0..9.  eax/=10
    
    or      edx, '0'            ; convert to ASCII
    dec     esi                 ; backup to next char in buf 
    mov     [esi], dl           ; copy ASCII digit to buf
    inc     byte[nch]           ; increment char count in buf

    test    eax, eax            ; } while (eax);
    jnz     _todigit

    mov     eax, 4              ; __NR_write from /usr/include/asm/unistd_32.h
    mov     ebx, 1              ; fd = STDOUT_FILENO
    mov     ecx, esi            ; copy address in esi to ecx (addr of 1st digit)
                                ;  subtracting to find length.
    mov     dl, byte[nch]       ; length, including the \n
    int     80h                 ; write(1, string,  digits + 1)

    ret

; check if character islower()
; parameters:
;   ecx - address holding character
; returns;
;   eax - 0 (false), 1 (true)
_islwr:
    
    mov eax, 1                  ; set return true
    
    cmp byte[ecx], 'a'          ; compare with 'a'
    jge _chkz                   ; char >= 'a'
    
    mov eax, 0                  ; set return false
    ret
    
  _chkz:
    cmp byte[ecx], 'z'          ; compare with 'z'
    jle _rtnlwr                 ; <= is lowercase
    
    mov eax, 0                  ; set return false
    
  _rtnlwr:
    ret


; check if character isupper()
; parameters:
;   ecx - address holding character
; returns;
;   eax - 0 (false), 1 (true)
_isupr:
    
    mov eax, 1                  ; set return true
    
    cmp byte[ecx], 'A'          ; compare with 'A'
    jge _chkZ                   ; char >= 'A'
    
    mov eax, 0                  ; set return false
    ret
    
  _chkZ:
    cmp byte[ecx], 'Z'          ; compare with 'Z'
    jle _rtnupr                 ; <= is uppercase
    
    mov eax, 0                  ; set return false
    
  _rtnupr:
    ret

有很多方法可以编写不同的片段,与最有效的编写方式相比,这里更容易理解。

例子Use/Output

编译后link代码,例如

nasm -f elf -o ./obj/char_dist_32.o char_dist_32.asm
ld -m elf_i386 -o ./bin/char_dist_32 ./obj/char_dist_32.o

您可以使用您的问题和其他问题中给出的输入进行测试,例如

$ ./bin/char_dist_32
first letter : a
second letter: e
char distance: 4

$ ./bin/char_dist_32
first letter : d
second letter: b
char distance: 2

$ ./bin/char_dist_32
first letter : D
second letter: B
char distance: 2

$ ./bin/char_dist_32
first letter : a
second letter: Z
error: not alpha or same case

检查一下,如果您还有其他问题,请告诉我。

就像abs(c1 - c2)一样简单。您可以减去 ASCII 码; +'a' 部分将抵消。

或者,如果您想要不区分大小写,请使用 c1|=0x20 强制字符先小写。请参阅 What is the idea behind ^= 32, that converts lowercase letters to upper and vice versa?(它还提供了一种有效的方法来 检查 如果输入是字母的并转换为小写字母,从而导致字母表中的 0..25 位置)。

一般情况下,How do I print an integer in Assembly Level Programming without printf from the c library? 涵盖了将数字打印为十进制 ASCII 数字串。如果我们可以假设一个 2 位数,我们可以简化,如果我们不介意打印前导零,则更是如此。


只是为了好玩,让我们看看我们可以把它做的多么简单。好吧,也许并不总是“简单”,而是紧凑和极简主义。需要理解的代码较少,但有些步骤并不是我希望初学者采用的方式。 (例如,同一寄存器不同位置的 2 个字符,并使用 8 位 DIV 的 AH:AL 结果作为字符串。)

对于初学者,我们将把两个字符放在同一行作为同一个 read 系统调用的一部分。 (命令行参数也很简单,但如果不检查 argc 会更简单,如果不提供它们就会崩溃。)

我省略了打印提示,因为那很无聊,如果你愿意,可以这样做。

SYS__exit   equ 1
SYS_read   equ 3
SYS_write  equ 4

section .text          ; optional, already the default section
global _start 
_start:
   sub  esp, 32        ; reserve some stack space.  I only ended up using 4 bytes

   mov  eax, SYS_read
   xor  ebx, ebx       ; ebx=0 = stdin
   mov  ecx, esp
   mov  edx, 4         ; long enough to get 2 chars and a newline with room to spare
   int  0x80           ; read(STDIN_FILENO, stackbuf, 4)

   movzx eax, word [ecx]  ; load both chars into AL and AH
   or    eax, 0x2020      ; force them both to lower case, operating on AL and AH at once
    ;;; if you want to check for non-alphabetic chars, add code here
   sub   al, ah           ; subtract ASCII codes
   jns  .done_abs         ; jump if result wasn't negative
    neg   al               ; AL = abx(al)
   .done_abs:

   movzx eax, al          ; clear AH
   mov   dl, 10
   div   dl               ; AL = diff/10 (leading digit)  AH = diff%10 (least significant digit)
   add   eax, `00\n`      ; 0..9 integer digits to ASCII codes and append a newline
   mov   [ecx], eax       ; store ASCII chars back into the same stack buffer
   
   mov   eax, SYS_write
   mov   ebx, 1           ; stdout
   ;mov  ecx, esp         ; still set
   mov   edx, 3           ; 2-digit number + newline, maybe including leading zero
   int   0x80             ; write(1, stackbuf, 3)

   mov   eax, SYS__exit
   xor   ebx, ebx
   int   0x80            ; _exit(0)

有趣的事实:尽管使用了部分寄存器,这主要避免了部分寄存器停顿。 (除了在 div dl 写入 AX 之后添加到 EAX。)如果您实际上是在优化性能,您将使用乘法逆。 (https://codereview.stackexchange.com/questions/142842/integer-to-ascii-algorithm-x86-assembly)

测试用例:

$ nasm -felf32 -Worphan-labels char-diff.asm &&
 ld -melf_i386 -o char-diff char-diff.o

$ ./char-diff 
ab
01
$ ./char-diff 
Ax
23
$ ./char-diff 
Aa
00
$ ./char-diff 
Xa
23
$ ./char-diff 
xA
23