Related
I am trying to create a random letter generator for my string and I've been given a C code and will have to convert it into Assembly language for my program. I'm doing this in x86-64 bit NASM assembly language. I'm supposed to be using only system calls and not C/C++ function calls.
Here's the C/C++ code that I've got to convert:
int genran(int x,int y)
{
int a = 0;
a = a + x * 1103515245 + 12345;
return (unsigned int)(a / 65536) % (y + 1);
}
I am new to Assembly and any help is appreciated, here's what I've got so far. I know its somewhat wrong but I will be working on improving it:
section .data
string db "The random string is generated below: "
len_string equ $-string
a dd 0
x dd ?
y dd ?
rem dd 0
section .bss
string_buff resb 21 ;Our string's length is 20 characters
section .txt
global main
main:
mov rax, 1
mov rdi, 1
mov rsi, string
mov rdx, len_string
syscall
mov rax, a
mov rbx, x ;we have to come up with a value of x?
mul rbx, 1103515245
add rbx, 12345
mov rax, rbx
div rax, 65536
mov rdx, y ;we have to come up with a value of y?
add rdx, 1
;mod rax, rdx
;ret
exit:
mov rax, 60
xor rdi, rdi
syscall
mov rax, rbx
div rax, 65536
Please read the instructions; this doesn't exist. div is an opcode that only takes one argument. The code should look like
mov rax, rbx
xor rdx, rdx ; Div takes rdx:rax as an implicit 128 bit argument.
mov rcx, 65536
div rcx
At this skill level I don't recommend trying to use read or write system calls. I very much instead recommend calling the C standard library until you get the hang of it. read and write have too many gotchas. You will want to write them once and include them in a mini-library any more assembly work you do.
We can skip read by taking argument off the command line as follows:
mov rbx, [rsp + 16] ; argv[1]
Unless I'm very much mistaken, the first thing on the stack is argc, and then argv[0], then argv[1], ...
Edit: This is similar to this: Reset a string variable to print multitple user inputs in a loop (NASM Assembly). But it is not the same issue.
From the other post, I was able to prevent additional characters from being printed. However, I still cannot prevent those additional characters from being read when the program goes back to the point in which it asks the user for input.
I'm creating a program that asks an user for input, and then prints it. Afterwards, it asks the user to enter 'y' if they want to print another text, or press anything else to close the program.
My issue is that if the user enters more characters than expected, those extra characters don't go away, and when the program goes back to ask the user for input, there's no chance to enter input because the program takes the remaining characters from the last time it received input.
For example:
The user is asked to enter text to print, and they enter: "Heyyyyyyyyyyyyyyyyyyyyyyyyyyyyy"
Leftover is "yyy"
At this point, the program should ask the user to enter 'y' to repeat the process, or anything else to close the program.
Output:
Heyyyyyyyyyyyyyyyyyyyyyyyyyy
Wanna try again? If yes, enter y. If not, enter anything else to close the program
Enter your text:
Output: yy
Wanna try again? If yes, enter y. If not, enter anything else to close the program.
And only now it asks for user input again
Since "yyy" is still in buffer, the user doesn't get a chance to actually enter input in this case.
What can I do to fix this issue? I've just started to learn about this recently, so I'd appreciate any help.
Thanks.
This is what I've done
prompt db "Type your text here.", 0h
retry db "Wanna try again? If yes, enter y. If not, enter anything else to close the program"
section .bss
text resb 50
choice resb 2
section .text
global _start
_start:
mov rax, 1 ;Just asking the user to enter input
mov rdi, 1
mov rsi, prompt
mov rdx, 21
syscall
mov rax, 0 ;Getting input and saving it on var text
mov rdi, 0
mov rsi, text
mov rdx, 50
syscall
mov r8, rax ;This is what I added to prevent additional characters
;from being printed
mov rax, 1 ;Printing the user input
mov rdi, 1
mov rsi, text
mov rdx, r8
syscall
mov rax, 1 ;Asking if user wants to try again
mov rdi, 1
mov rsi, retry
mov rdx, 83
syscall
mov rax, 0 ;Getting input and saving it on var choice
mov rdi, 0
mov rsi, choice
mov rdx, 2
syscall
mov r8b, [choice] ;If choice is different from y, go to end and close the program. Otherwhise, go back to start.
cmp byte r8b, 'y'
jne end
jmp _start
end:
mov rax, 60
mov rdi, 0
syscall
The simple way to clear stdin is to check if the 2nd char in choice is the '\n' (0xa). If it isn't, then characters remain in stdin unread. You already know how to read from stdin, so in that case, just read stdin until the '\n' is read1, e.g.
mov rax, 0 ;Getting input and saving it on var choice
mov rdi, 0
mov rsi, choice
mov rdx, 2
syscall
cmp byte [choice], 'y' ; check 1st byte of choice, no need for r8b
jne end
cmp byte [choice + 1], 0xa ; is 2nd char '\n' (if yes done, jump start)
je _start
empty: ; chars remain in stdin unread
mov rax, 0 ; read 1-char from stdin into choice
mov rdi, 0
mov rsi, choice
mov rdx, 1
syscall
cmp byte [choice], 0xa ; check if char '\n'?
jne empty ; if not, repeat
jmp _start
Beyond that, you should determine your prompt lengths when you declare them, e.g.
prompt db "Type your text here. ", 0h
plen equ $-prompt
retry db "Try again (y/n)? ", 0h
rlen equ $-retry
That way you do not have to hardcode lengths in case you change your prompts, e.g.
_start:
mov rax, 1 ;Just asking the user to enter input
mov rdi, 1
mov rsi, prompt
mov rdx, plen
syscall
mov rax, 0 ;Getting input and saving it on var text
mov rdi, 0
mov rsi, text
mov rdx, 50
syscall
mov r8, rax
mov rax, 1 ;Printing the user input
mov rdi, 1
mov rsi, text
mov rdx, r8
syscall
mov rax, 1 ;Asking if user wants to try again
mov rdi, 1
mov rsi, retry
mov rdx, rlen
syscall
If you put it altogether, you can do:
prompt db "Type your text here. ", 0h
plen equ $-prompt
retry db "Try again (y/n)? ", 0h
rlen equ $-retry
section .bss
text resb 50
choice resb 2
section .text
global _start
_start:
mov rax, 1 ;Just asking the user to enter input
mov rdi, 1
mov rsi, prompt
mov rdx, plen
syscall
mov rax, 0 ;Getting input and saving it on var text
mov rdi, 0
mov rsi, text
mov rdx, 50
syscall
mov r8, rax
mov rax, 1 ;Printing the user input
mov rdi, 1
mov rsi, text
mov rdx, r8
syscall
mov rax, 1 ;Asking if user wants to try again
mov rdi, 1
mov rsi, retry
mov rdx, rlen
syscall
mov rax, 0 ;Getting input and saving it on var choice
mov rdi, 0
mov rsi, choice
mov rdx, 2
syscall
cmp byte [choice], 'y' ; check 1st byte of choice, no need for r8b
jne end
cmp byte [choice + 1], 0xa ; is 2nd char '\n' (if yes done, jump start)
je _start
empty: ; chars remain in stdin unread
mov rax, 0 ; read 1-char from stdin into choice
mov rdi, 0
mov rsi, choice
mov rdx, 1
syscall
cmp byte [choice], 0xa ; check if char '\n'?
jne empty ; if not, repeat
jmp _start
end:
mov rax, 60
mov rdi, 0
syscall
Example Use/Output
$ ./bin/emptystdin
Type your text here. abc
abc
Try again (y/n)? y
Type your text here. def
def
Try again (y/n)? yes please!
Type your text here. geh
geh
Try again (y/n)? yyyyyyyyyyyyyeeeeeeeeeesssssssss!!!!
Type your text here. ijk
ijk
Try again (y/n)? n
Now even a cat stepping on the keyboard at your (y/n)? prompt won't cause problems. There are probably more elegant ways to handle this that would be more efficient that repetitive reads, with syscall, but this will handle the issue.
Additional Considerations And Error-Checks
As mentioned above, the simplistic reading and checking of a character-at-a-time isn't a very efficient approach, though it is conceptually the easiest extension without making other changes. #PeterCordes makes a number of good points in the comments below related to approaches that are more efficient and more importantly about error conditions that can arise that should be protected against as well.
For starters when you are looking for information on the individual system call use, Anatomy of a system call, part 1 provides a bit of background on approaching their use supplemented by the Linux manual page, for read man 2 read for details on the parameter types and return type and values.
The original solution above does not address what happens if the user generates a manual end-of-file by pressing Ctrl+d or if a read error actually occurs. It simply addressed the user-input and emptying stdin question asked. With any user-input, before you use the value, you must validate that the input succeeded by checking the return. (not just for the yes/no input, but all inputs). For purposes here, you can consider zero input (manual end-of-file) or a negative return (read error) as a failed input.
To check whether you have at least one valid character of input, you can simply check the return (read returns the number of characters read, sys_read placing that value in rax after the syscall). A zero or negative value indicating no input was received. A check could be:
cmp rax, 0 ; check for 0 bytes read or error
jle error
You can write a short diagnostic to the user and then handle the error as wanted, this example simply exits after outputting a diagnostic, e.g.
readerr db 0xa, "eof or read error", 0xa, 0x0
rderrsz equ $-readerr
...
; your call to read here
cmp rax, 0 ; check for 0 bytes read or error
jle error
...
error:
mov rax, 1 ; output the readerr string and jmp to end
mov rdi, 1
mov rsi, readerr
mov rdx, rderrsz
syscall
jmp end
Now moving on to a more efficient manner for emptying stdin. The biggest hindrance indicate in the original answer was the repeated system calls to sys_read to read one character at a time reusing your 2-byte choice buffer. The obvious solution is to make choice bigger, or just use stack space to read more characters each time. (you can look at the comments for a couple of approaches) Here, for example we will increase choice to 128-bytes which in the case of the anticipate "y\n" input will only make use of two of those bytes, but in the case of an excessively long input will read 128-bytes at a time until the '\n' is found. For setup you have:
choicesz equ 128
...
section .bss
text resb 50
choice resb 128
Now after you ask for (y/n)? your read would be:
mov rax, 0 ; Getting input and saving it on var choice
mov rdi, 0
mov rsi, choice
mov rdx, choicesz
syscall
cmp rax, 0 ; check for 0 bytes read (eof) or error
jle error
cmp byte [choice], 'y' ; check 1st byte of choice, no need for r8b
jne end ; not a 'y', doesn't matter what's in stdin, end
Now there are two conditions to check. First, compare the number of characters read with your buffer size choicesz and if the number of characters read is less than choicesz, no characters remain unread in stdin. Second, if the return equals the buffer size, you may or may not have characters remaining in stdin. You need to check the last character in the buffer to see if it is the '\n' to indicate whether you have read all the input. If the last character is other than the '\n' characters remain unread (unless the user just happened to generate a manual end-of-file at the 128th character) You can check as:
empty:
cmp eax, choicesz ; compare chars read and buffer size
jb _start ; buffer not full - nothing remains in stdin
cmp byte [choice + choicesz - 1], 0xa ; if full - check if last byte \n, done
je _start
mov rax, 0 ; fill choice again from stdin and repeat checks
mov rdi, 0
mov rsi, choice
mov rdx, choicesz
syscall
cmp rax, 0 ; check for 0 bytes read or error
jle error
jmp empty
(note: as noted above, there is a further case to cover, not covered here, such as where the user enters valid input, but then generates a manual end-of-file instead of just pressing Enter after the 128th character (or a multiple of 128). There you can't just look for a '\n' it doesn't exist, and if there are no more chacters and call sys_read again, it will block wating on input. Conceivably you will need to use a non-blocking read and putback of a single character to break that ambiguity -- that is left to you)
A comlete example with the improvements would be:
prompt db "Type your text here. ", 0x0
plen equ $-prompt
retry db "Try again (y/n)? ", 0x0
rlen equ $-retry
textsz equ 50
choicesz equ 128
readerr db 0xa, "eof or read error", 0xa, 0x0
rderrsz equ $-readerr
section .bss
text resb 50
choice resb 128
section .text
global _start
_start:
mov rax, 1 ; Just asking the user to enter input
mov rdi, 1
mov rsi, prompt
mov rdx, plen
syscall
mov rax, 0 ; Getting input and saving it on var text
mov rdi, 0
mov rsi, text
mov rdx, textsz
syscall
cmp rax, 0 ; check for 0 bytes read or error
jle error
mov r8, rax
mov rax, 1 ; Printing the user input
mov rdi, 1
mov rsi, text
mov rdx, r8
syscall
mov rax, 1 ; Asking if user wants to try again
mov rdi, 1
mov rsi, retry
mov rdx, rlen
syscall
mov rax, 0 ; Getting input and saving it on var choice
mov rdi, 0
mov rsi, choice
mov rdx, choicesz
syscall
cmp rax, 0 ; check for 0 bytes read (eof) or error
jle error
cmp byte [choice], 'y' ; check 1st byte of choice, no need for r8b
jne end ; not a 'y', doesn't matter what's in stdin, end
empty:
cmp eax, choicesz ; compare chars read and buffer size
jb _start ; buffer not full - nothing remains in stdin
cmp byte [choice + choicesz - 1], 0xa ; if full - check if last byte \n, done
je _start
mov rax, 0 ; fill choice again from stdin and repeat checks
mov rdi, 0
mov rsi, choice
mov rdx, choicesz
syscall
cmp rax, 0 ; check for 0 bytes read or error
jle error
jmp empty
end:
mov rax, 60
mov rdi, 0
syscall
error:
mov rax, 1 ; output the readerr string and jmp to end
mov rdi, 1
mov rsi, readerr
mov rdx, rderrsz
syscall
jmp end
There are surely more efficient ways to optimize this, but for purposes of discussion of "How do I empty stdin?", this second approach with the buffer size used alieviates the repetitive calls to sys_read to read one character at-a-time is a good step forward. "How do it completely optimize the check?" is a whole separate question.
Let me know if you have further questions.
Footnotes:
1. In this circumstance where the user is typing input, the user generates a '\n' by pressing Enter, allowing you to check for the '\n' as the final character in emptying stdin. The user can also generate a manual end-of-file by pressing Ctrl+d so the '\n' isn't guaranteed. There are many still other ways stdin can be filled, such as redirecting a file as input where there should be a ending '\n' to be POSIX compliant, there too that isn't a guarantee.
I'm trying to input into my program... All it does is run through and print a '0' to the screen. I'm pretty sure that the PRINTDECI function works, I made it a while ago and it works. Do I just have to loop over the input code and only exit when I enter a certain value? I'm not sure how I would do that... Unless it's by ACSII values which might suck.... Anyways, here's my code (Yasm(nasm clone), Intel Syntax):
GLOBAL _start
SECTION .text
PRINTDECI:
LEA R9,[NUMBER + 18] ; last character of buffer
MOV R10,R9 ; copy the last character address
MOV RBX,10 ; base10 divisor
DIV_BY_10:
XOR RDX,RDX ; zero rdx for div
DIV RBX ; rax:rdx = rax / rbx
ADD RDX,0x30 ; convert binary digit to ascii
TEST RAX,RAX ; if rax == 0 exit DIV_BY_10
JZ CHECK_BUFFER
MOV byte [R9],DL ; save remainder
SUB R9,1 ; decrement the buffer address
JMP DIV_BY_10
CHECK_BUFFER:
MOV byte [R9],DL
SUB R9,1
CMP R9,R10 ; if the buffer has data print it
JNE PRINT_BUFFER
MOV byte [R9],'0' ; place the default zero into the empty buffer
SUB R9,1
PRINT_BUFFER:
ADD R9,1 ; address of last digit saved to buffer
SUB R10,R9 ; end address minus start address
ADD R10,1 ; R10 = length of number
MOV RAX,1 ; NR_write
MOV RDI,1 ; stdout
MOV RSI,R9 ; number buffer address
MOV RDX,R10 ; string length
SYSCALL
RET
_start:
MOV RCX, SCORE ;Input into Score
MOV RDX, SCORELEN
MOV RAX, 3
MOV RBX, 0
SYSCALL
MOV RAX, [SCORE]
PUSH RAX ;Print Score
CALL PRINTDECI
POP RAX
MOV RAX,60 ;Kill the Code
MOV RDI,0
SYSCALL
SECTION .bss
SCORE: RESQ 1
SCORELEN EQU $-SCORE
Thanks for any help!
- Kyle
As a side note, the pointer in RCX goes to a insanely large number according to DDD... So I'm thinking I have to get it to pause and wait for me to type, but I have no idea how to do that...
The 'setup' to call syscall 0 (READ) on x86_64 system is:
#xenon:~$ syscalls_lookup read
read:
rax = 0 (0x0)
rdi = unsigned int fd
rsi = char *buf
rdx = size_t count
So your _start code should be something like:
_start:
mov rax, 0 ; READ
mov rdi, 0 ; stdin
mov rsi, SCORE ; buffer
mov rdx, SCORELEN ; length
syscall
The register conventions and syscall numbers for x86_64 are COMPLETELY different than those for i386.
Some conceptual issues you seem to have:
READ does not do ANY interpretation on what you type, you seem to be expecting it to let you type a number (say, 57) and have it return the value 57. Nope. It'll return '5', '7', 'ENTER', 'GARBAGE'... Your SCORELEN is probably 8 (length of resq 1), so you'll read, AT MOST, 8 bytes. or Characters, if you wish to call them that. And unless you type the EOF char (^D), you'll need to type those 8 characters before the READ call will return to your code.
You have to convert the characters you receive into a value... You can do it the easy way and link with ATOI() in the C library, or write your own parser to convert the characters into a value by addition and multiplication (it's not hard, see code below).
Used below, here as a reference:
#xenon:~$ syscalls_lookup write
write:
rax = 1 (0x1)
rdi = unsigned int fd
rsi = const char *buf
rdx = size_t count
Ugh.... So many... I'll just rewrite bits:
global _start
section .text
PRINTDECI:
; input is in RAX
lea r9, [NUMBER + NUMBERLEN - 1 ] ; + space for \n
mov r10, r9 ; save end position for later
mov [r9], '\n' ; store \n at end
dec r9
mov rbx, 10 ; base10 divisor
DIV_BY_10:
xor rdx, rdx ; zero rdx for div
div rbx : rax = rdx:rax / rbx, rdx = remainder
or dl, 0x30 ; make REMAINDER a digit
mov [r9], dl
dec r9
or rax, rax
jnz DIV_BY_10
PRINT_BUFFER:
sub r10, r9 ; get length (r10 - r9)
inc r9 ; make r9 point to initial character
mov rax, 1 ; WRITE (1)
mov rdi, 1 ; stdout
mov rsi, r9 ; first character in buffer
mov rdx, r10 ; length
syscall
ret
MAKEVALUE:
; RAX points to buffer
mov r9, rax ; save pointer
xor rcx, rcx ; zero value storage
MAKELOOP:
mov al, [r9] ; get a character
or al, al ; set flags
jz MAKEDONE ; zero byte? we're done!
and rax, 0x0f ; strip off high nybble and zero rest of RAX (we're lazy!)
add rcx, rcx ; value = value * 2
mov rdx, rcx ; save it
add rcx, rcx ; value = value * 4
add rcx, rcx ; value = value * 8
add rcx, rdx ; value = value * 8 + value * 2 (== value * 10)
add rcx, rax ; add new digit
jmp MAKELOOP ; do it again
MAKEDONE:
mov rax, rcx ; put value in RAX to return
ret
_start:
mov rax, 0 ; READ (0)
mov rdi, 0 ; stdin
mov rsi, SCORE ; buffer
mov rdx, SCORELEN ; length
syscall
; RAX contains HOW MANY CHARS we read!
; -OR-, -1 to indicate error, really
; should check for that, but that's for
; you to do later... right? (if RAX==-1,
; you'll get a segfault, just so you know!)
add rax, SCORE ; get position of last byte
movb [rax], 0 ; force a terminator at end
mov rax, SCORE ; point to beginning of buffer
call MAKEVALUE ; convert from ASCII to a value
; RAX now should have the VALUE of the string of characters
; we input above. (well, hopefully, right?)
mov [VALUE], rax ; store it, because we can!
; it's stored... pretend it's later... we need value of VALUE!
mov rax, [VALUE] ; get the VALUE
call PRINTDECI ; convert and display value
; all done!
mov rax, 60 ; EXIT (60/0x3C)
mov rdi, 0 ; exit code = 0
syscall
section .bss
SCORE: resb 11 ; 10 chars + zero terminator
SCORELEN equ $-SCORE
NUMBER: resb 19 ; 18 chars + CR terminator
NUMBERLEN equ $-NUMBER
I'm going to say that this should work first time, it's off-the-cuff for me, haven't tested it, but it should be good. We read up to 10 chars, terminate it with a zero, convert to a value, then convert to ascii and write it out.
To be more proper, you should save registers to the stack in each subroutine, well, certain ones, and really, only if you're going to interface with libraries... doing things yourself lets you have all the freedom you want to play with the registers, you just have to remember what you put where!
Yes, someone is going to say "why didn't you just multiply by 10 instead of weird adding?" ... uh... because it's easier on the registers and I don't have to set it all up in rdx:rax. Besides, it's just as readable and understandable, especially with the comments. Roll with it! This isn't a competition, it's learning!
Machine code is fun! Gotta juggle all the eggs in your head though... no help from the compiler here!
Technically, you should check return result (RAX) of the syscalls for READ and WRITE, handle errors appropriately, yadda yadda yadda.... learn to use your debugger (gdb or whatever).
Hope this helps.
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:
On running the program, I want the user-inputted number to be used for the number of dots on the first line. However, when I type any number, loads of lines are printed with a single dot on each, then after about a second, a line is printed which contains 32768 dots. This is followed by a line with 32767 dots etc. The number of dots on each line continue to decrease until the line which has 1 dot.
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
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)
The Fix
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.
The Explanation
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.
I am making a program where the user enters a number, and it prints out all the numbers from zero up to the number. It compiles fine, links fine, and returns no errors when it runs, and yet it prints out absolutely nothing. Here is the code:
SECTION .data
len EQU 32
SECTION .bss
other resd len
data resd len
SECTION .text
GLOBAL _start
_start:
nop
input: ; This section gets the integer from the user
mov eax, 3 ; }
mov ebx, 1 ; }
mov ecx, data ; } System_read call
mov edx, len ; }
int 80h ; }
mov ebp, 1
setup: ; This section sets up the registers ready for looping
mov [other], ebp
loop: ; This section loops, printing out from zero to the number given
mov eax, 4
mov ebx, 1
mov ecx, [other]
mov edx, len
int 80h
exit: ; Exits the program
mov eax, 1 ; }
mov ebx, 0 ; } System_exit call
int 80h ; }
When I step through it on KDBG, it returns a few errors; it receives an interrupt and a segmentation fault, although I can't tell where. I'm not sure why though, because when I run it in Geany, it returns a 0 value at the end and runs without error. Why does it not work?
Thanks in advance
NOTE: This code does not loop. It is not finished yet. All it should do here is print out the number 1.
When you go to print, you are calling mov ecx, [other]. This looks at the address that's stored in other and follows that address to get whatever is stored there. The problem is that this system call is expecting an address in ecx, not a value.
If you call mov ecx, other instead, then ecx will have the address of other and it will be able to go to that address and print what's there.
You have another problem here: when you print the number stored in other, it will translate it into the ascii value. So, for example, when you try to print a 1, instead of printing the number 1, it will print ascii 1 (which happens to be a start of heading character; nothing you want to print). Add '0' (the character '0') if you want to print numbers.
EDIT: One more thing, when you read, you are passing 1 into ebx. 1 is STDOUT. What you want is STDIN which is 0.