Reputation: 11
I'm trying to create a triangle of dots on the screen by taking a user-entered value (to vary the size of the resulting triangle) and using it to write decrementing lines of dots.
Here is the code:
section .data
global _start
char db ' '
prompt_text db "Enter triangle size (2-99) "
prompt_length equ $-prompt_text
section .bss
tri_size resb 3
tri_size_length equ $-tri_size
section .text
_start:
call prompt
call insert_size
mov rax, [tri_size]
outer_loop:
mov rbx, [tri_size]
inner_loop:
call dot
dec bx
cmp bx, 0
jg inner_loop
call linefeed
call dec_length
dec ax
cmp ax, 0
jne outer_loop
call linefeed
call exit
prompt:
mov rax, 4
mov rbx, 1
mov rcx, prompt_text
mov rdx, prompt_length
int 80h
ret
insert_size:
mov rax, 3
mov rbx, 0
mov rcx, [tri_size]
mov rdx, tri_size_length
int 80h
ret
dot:
mov [char], byte '.'
call print_char
ret
linefeed:
mov [char], byte 10
call print_char
ret
print_char:
push rax
push rbx
push rcx
push rdx
mov rax, 4
mov rbx, 1
mov rcx, char
mov rdx, 1
int 80h
pop rdx
pop rcx
pop rbx
pop rax
ret
dec_length:
push rax
push rbx
push rcx
push rdx
mov rax, [tri_size]
dec ax
mov [tri_size], rax
pop rdx
pop rcx
pop rbx
pop rax
ret
exit:
mov rax, 1
mov rbx, 0
int 80h
Problem:
I've noticed that 32768 is hexadecimal for 10000000_00000000, but other than that I'm completely stuck and I'd REALLY appreciate any help!
P.S. I'm using x84-64 linux and assembling with YASM
Upvotes: 1
Views: 1485
Reputation: 39905
There are two problems in your code, both when you read the input. First, the fix. Then, an explanation of the current results.
insert_size:
mov rax, 3
mov rbx, 0
mov rcx, [tri_size] ; issue 1
mov rdx, tri_size_length
int 80h
ret ; issue 2 (sort of)
First, rcx
should contain the address of a buffer, but you are getting the contents of tri_size
, not its address. Since tri_size
is in the bss section, it is initialized with 0s, so you are telling the OS to read into a NULL buffer. If you were to check the result of the system call, you would see an error code because of this.
Second, when you read input, you are reading a string, not a number. If you want to use it as a number, you need to convert it first. Here is the code with both issues fixed:
insert_size:
mov rax, 3
mov rbx, 0
mov rcx, tri_size ; 1
mov rdx, tri_size_length
int 80h
mov dh, 0 ; 2
mov ah, 0
mov dl, [tri_size] ; 3
mov ah, [tri_size+1]
sub dl, '0' ; 4
cmp al, '0' ; 5
jb done
cmp al, '9' ; 6
ja done
imul dx, 10 ; 7
sub al, '0' ; 8
add dx, ax ; 9
done:
mov [tri_size], dx ; 10
ret
The first issue is an easy fix, just remove the brackets to get the address instead of the contents. The second is more complicated. First, we are going to use 16 bit registers but only read into 8 bits, so step 2 puts 0 in the high byte for each. Then, we read the first two bytes of the string. Next, we convert the first character from a character to a number. Since the digits in ASCII are sequential, we can do this by subtracting the character '0'. Note that this assumes the first character is a valid digit.
We cannot assume the second character is a valid digit, since there might have been only one entered. Therefore, steps 5 and 6 check to see if it is less than '0' and greater than '9', respectively, and jump to the end if either is true. If we get past both, then the second character is a digit. This means the first digit should be in the 10's place, so we multiply it by 10. Then we convert the second character to a number and add it to the first. Now that both characters have been tested, we can store the result back where it is expected and return.
As explained in the fix, you are passing a NULL buffer, so it returns an error. Your tri_size
variable is never changed, so it still contains 0. This means both of your counters start at 0. Since you don't check for this, the first dot is printed and bx
is decremented, resulting in -1. Since -1 is not greater than 0, the inner loop exits, a newline is printed, your counter and ax
are decremented, resulting in -1. This is not zero, so it loops back to the outer loop. This happens 32768 times, until your counter gets to -32768.
When you decrement bx
at this point, it becomes -32769, but this number is not representable using 16-bit 2's complement. Instead, it overflows, and the number you get is 32767. This is greater than 0, so you continue in the inner loop until it gets to 0, printing a total of 32768 dots. After the inner loop exits and the newline is printed, the counter and ax
are both decremented, resulting in 32767. From this point on, your program works as if the input was 32767, meaning you get a triangle from there to 0 dots.
Interestingly, if your inner loop tested if bx
was equal to 0 instead of greater than, you would simply get a triangle from 65536 dots to 0, with no 1-dot lines before it.
Upvotes: 2