If you are still stuck, you question requires that you determine the distance between two characters. That brings with it a number of checks you must implement. Though your question is silent on whether you need to handle both uppercase and lowercase distances, unless you are converting everything to one case or the other, you will need to determine whether both characters are of the same case to make the distance within the alphabet between those two characters valid.
Since two characters are involved, you need a way of saving the case of the first for comparison with the case of the second. Here, and in all cases where a simple state is needed, just using a byte (flag) to store the state is about as simple as anything else. For example, a byte to hold 0
if the ASCII character is not an alpha character, 1
if the character is uppercase and 2
if the character is lowercase (or whatever consistent scheme you like)
That way, when you are done with the comparisons and tests, you can simply compare the two flags for equality. If they are equal, you can proceed to subtract one from the other to get the distance (swapping if necessary) and then output the number converting the number to ASCII digits for output.
To test if the character is an uppercase character, similar to isupper()
in C, a short function is all that is needed:
; 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
You can handle the storage for the local arrays and values you need in a couple of ways. You can either subtract from the current stack pointer to create temporary storage on the stack, or in a slightly more readable way, create labels to storage within the uninitialized segment (.bss) and use the labels as variable names. Your initialized variables go in the .data segment. For example, storage for the program could be:
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
Rather than using numbers sprinkled through your syscall setups, declaring initialized labels for, e.g. stdin
and stdout
instead of using 0
and 1
make things more readable:
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
Then for your reading your character input, you would have, e.g.
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
To then check the case of the character input, you could do:
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
The notalpha:
label just being a block to output an error in case the character isn't an alpha character or the case between the two characters don't match:
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
After you have completed input and classification of both characters, you now need to verify whether both character are of the same case, if so you need to compute the distance between the characters (swapping if necessary, or using an absolute value) and finally handle the conversion of the distance between them from a numeric value to ASCII digits for output. You can do something similar to the following:
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
Putting it altogether and including the _prnuint32
function below for conversion and output of the numeric distance between characters, you would have:
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
There are many ways to write the varying pieces and this is intended to fall more on the easier to follow side than the most efficient way it can be written side.
Example Use/Output
After you compile and link the code, e.g.
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
You can test with the inputs given in your question and others, e.g.
$ ./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
Look things over and let me know if you have further questions.