I am writing a program to get an integer from the user, and then print out all the numbers from 0 up to the number. My code gets the input fine, but when printing it out, it prints continuously in what seems to be an endless loop. Here is my code:
SECTION .data ; Constant variable declaration
len EQU 32 ; Constant length
msg db "Enter a number: ", 0 ; Input message
msglen EQU $-msg ; Input message length
SECTION .bss ; Uninitialised data declaration
other resd len ; Output counter that is incremented
data resd len ; Input data buffer
SECTION .text ; Main program initialiser
GLOBAL _start ; Linker entry point declaration
_start: ; Entry point
nop ; This keeps the debugger happy :)
Msg: ; This section prints out the message
mov eax, 4 ; }
mov ebx, 1 ; }
mov ecx, msg ; } System_write call
mov edx, msglen ; }
int 80h ; }
input: ; This section gets the integer from the user
mov eax, 3 ; }
mov ebx, 0 ; }
mov ecx, data ; } System_read call
mov edx, len ; }
int 80h ; }
ASCIIAdj:
mov ebp, 48 ; This line sets the counter to '0' ASCII
setup: ; This section adjusts the counter
mov [other], ebp ; Increment counter
loop: ; This section loops, printing out from zero to the number given
mov eax, 4 ; }
mov ebx, 1 ; }
mov ecx, other ; } System_write call
mov edx, len ; }
int 80h ; }
mov eax, 1 ; Move 1 to eax
add ebp, eax ; Add eax to ebp (essentially increment ebp)
mov eax, other ; move other to eax
mov ebx, data ; move data to ebx
cmp eax, ebx ; compare them
jne setup ; If they are not the same, go back to the setup to increment other
exit: ; Exits the program
mov eax, 1 ; }
mov ebx, 0 ; } System_exit call
int 80h ; }
Why does it loop continuously? I have incremented the counter, and compared the input and the counter, so why doesn't it break?
Thanks in advance
EDIT:
Expected Output:
Enter a number: 6
0123456
General Semantics of the program:
Display "Enter a number: "
Read in an integer less than 32 bytes in size.
Set a counter variable to the ASCII value of zero
Loop:
Display the character, adding 1 to it, and checking to see if it is equal to the value inputted.
If it is equal, goto the exit section and exit
Else loop.
This is digging waaaay back into the deep dark recesses of my memory, but I think you want
mov eax, [other] ; move other to eax
mov ebx, [data] ; move data to ebx
Note the brackets, which are missing in your code. You are loading the addresses of other and data into eax and ebx, not the values contained there.
Related
I'm starting to learn x86 assembly and tried to write a piece of code that will take input as i/o redirection and write it back as uppercase letters by i/o redirection to a file, but it seems to always cause segmentation fault although I tried to End it by a an exit system call and No output is written to the file.
READ: mov eax, 3 ; choose sys_read
mov ebx, 0 ; file descriptor stdin
mov ecx, Buffer ; pass address of buffer
mov edx, BUFFER_LENGTH ; set buffer length
int 80h ; call read()
cmp eax, 0 ; check if there is error reading
jb EXIT ; if -1 exit
je EXIT ; if 0 exit
mov esi, eax ; safe keeping number of bytes_read
dec esi ; adjust offset
mov ebp, Buffer ; Store address of Buffer in ebp
add ebp, esi ; ebp Now point to it's buffer end
; Now Start The Loop and Change Characters Needed
LOOP: cmp byte[ebp], 61h ; Check if it's equal to 'a'
jb NEXT ; GO to Next Character
cmp byte[ebp], 7Ah ; Check if it's equal to 'z'
ja NEXT ; Go to next Character
sub byte[ebp], 20h ; ELSE convert to uppercase then go to next automatically
NEXT: dec esi ; Decrement esi 'Counter'
dec ebp ; Decryment ebp "to point to previous Character"
jnz LOOP ; Go To the loop again to check Next Character until ZERO
; Once Reached Zero The ZF flag is set and write complete as Normal
WRITE: mov edx, eax ; pass how many bytes to be written
mov eax, 4 ; specify sys_write call
mov ebx, 1 ; specify file descriptor
mov ecx, Buffer ; pass buffer address "changed letter"
int 80h ; make write call
jmp READ ; Go to read again to read NEXT Chunk
EXIT: mov eax, 1 ; specify sys_exit
mov ebx, 0 ; specify return value
int 80h ; make sys_exit call
I am getting a segmentation fault from this simple starting program.
I am using Ubuntu 16.10 and kdbg for debugging. Affter reaching starting __int 80h__, it stops moving to the next line.
section .bss ; section containing uninitialized data
BUFFLEN equ 1024 ; length of buffer
Buff: resb BUFFLEN ; text buffer itself
section .data ; section containing initialzed data
section .text ; secttion containing code
global _start ; linker needs to find the entry point!
_start:
nop ; this no-op keeps gdb happy
; read buffer full of text form stdin:
read:
mov eax, 3 ; specify sys_read call
mov ebx, 0 ; specify file descriptor 0 : standard input
mov ecx, Buff ; pass offset of the buffer to read to
mov edx, BUFFLEN ; pass number of bytes to be read at one pass
int 80h ; call sys_read to fill the buffer
mov esi,eax ; copy sys_read return value for safekeeping
cmp eax, 0 ; if eax = 0 , sys_read reached EOF on stdin
je Done ; jump if Equal ( to o, form compare)
; set up the register for the process buffer step:
mov ecx, esi ; place the number of bytes read into ecx
mov ebp, Buff ; pace address of buffer into ebp
dec ebp ; adjust the count to offset
; go through the buffer and cnvert lowercase to uppercase characters:
Scan:
cmp byte [ebp+ecx], 61h ; test input char agaisnst lowercase 'a'
jb Next ; if Below 'a' in ASCII, not lowercase
cmp byte [ebp+ecx], 7Ah ; test against lowercase 'z'
ja Next
sub byte [ebx+ecx], 20h ; subtract 20h to give uppercase..
Next:
dec ecx ; Decrement counter
jnz Scan ; if characters reamin, loop back
; Write the buffer full of processed text to stdout:
Write:
mov eax,4 ; Specify sys_write call
mov ebx, 1 ; Specify file descriptor 1 : stdout
mov ecx, Buff ; pass the offset of the buffer
mov edx, esi ; pass the # of bytes of data in the buffer
int 80h ; make sys_write kernel call
jmp read ; loop back and load another buffer full
Done:
mov eax, 1 ; Code for Exit sys_call
mov ebx, 0 ; return code of Zero
int 80h
I used these commands:
nasm -f elf -g -F stabs uppercaser1.asm
ld -m elf_i386 -o uppercaser1 uppercaser1.o
./uppercaser < inputflie
I think this code is generally public so I am posting with that in mind.
You should use the code as a guide to understanding what may be wrong in your code; however, it does not conform to any coding standard so it is pointless just to copy and paste and turn it in as an assignment; assuming that is the case.
You will never increase your assembly programming skills by merely doing a copy/paste.
lsb_release -a
...
Description: Ubuntu 16.04.3 LTS
nasm -f elf32 -g uppercase1.s -o uppercase1.o && ld -m elf_i386 uppercase1.o -o uppercase1
section .bss
Buff resb 1
section .data
section .text
global _start
_start:
nop
Read:
mov eax, 3 ; read syscall
mov ebx, 0 ; stdin
mov ecx, Buff ; pass address of the buffer to read to
mov edx, 1 ; read one char or one byte
int 0x80 ;
cmp eax, 0 ; if syscall returns returns 0
je Exit ;
cmp byte [Buff], 0x61 ; lower case a
jb Write ; jump if byte is below a in ASCII chart
cmp byte [Buff], 0x7a ; lower case z
ja Write ; jump if byte is above z in ASCII chart
sub byte [Buff], 0x20 ; changes the value in the buffer to an uppercase char
Write:
mov eax, 4 ; write syscall
mov ebx, 1 ; stdout
mov ecx, Buff ; what to print
mov edx, 1 ; length is one byte - each char is a byte
int 0x80
jmp Read ; go back to Read
Exit:
mov eax, 1
mov ebx, 0
int 0x80
Sample output:
david#ubuntuserver00A:~/asm$ ./uppercase1 < uppercase1.s
SECTION .BSS
BUFF RESB 1
SECTION .DATA
SECTION .TEXT
GLOBAL _START
_START:
NOP
READ:
MOV EAX, 3 ; READ SYSCALL
MOV EBX, 0 ; STDIN
MOV ECX, BUFF ; PASS ADDRESS OF THE BUFFER TO READ TO
MOV EDX, 1 ; READ ONE CHAR OR ONE BYTE
INT 0X80 ;
CMP EAX, 0 ; IF SYSCALL RETURNS RETURNS 0
JE EXIT ;
CMP BYTE [BUFF], 0X61 ; LOWER CASE A
JB WRITE ; JUMP IF BYTE IS BELOW A IN ASCII CHART
CMP BYTE [BUFF], 0X7A ; LOWER CASE Z
JA WRITE ; JUMP IF BYTE IS ABOVE Z IN ASCII CHART
SUB BYTE [BUFF], 0X20 ; CHANGES THE VALUE IN THE BUFFER TO AN UPPERCASE CHAR
WRITE:
MOV EAX, 4 ; WRITE SYSCALL
MOV EBX, 1 ; STDOUT
MOV ECX, BUFF ; WHAT TO PRINT
MOV EDX, 1 ; LENGTH IS ONE BYTE - EACH CHAR IS A BYTE
INT 0X80
JMP READ ; GO BACK TO READ
EXIT:
MOV EAX, 1
MOV EBX, 0
INT 0X80
I'm trying to learn assembly. I wanted to write a simple program that counted to 20 and printed out the numbers. I know you have to subtract ascii '0' from a ascii representation of a number to turn it into it's digit, but my implementation just refuses to work. I still get 123456789:;<=>?#ABCD
Here is my code.
section .bss
num resb 1
section .text
global _start
_start:
mov eax, '1'
mov ebx, 1 ; Filehandler 1 = stdout
mov ecx, 20 ; The number we're counting to
mov edx, 1 ; Size of a number in bytes
l1:
mov [num], eax ; Put eax into the value of num
mov eax, 4 ; Put 4 into eax (write)
push ecx ; Save ecx on the stack
mov ecx, num ; print num
int 0x80 ; Do the print
pop ecx ; Bring ecx back from the stack
mov eax, [num] ; Put the value of num into eax
sub eax, '0' ; Convert to digit
inc eax ; Increment eax
add eax, '0' ; Convert back to ascii
loop l1
mov eax,1 ; System call number (sys_exit)
int 0x80 ; Call kernel
Can anyone see what the problem is? I'm totally hitting a brick wall. I'm using nasm to compile and ld to link.
I'm trying to learn assembly with NASM on 64 bit Linux.
I managed to make a program that reads two numbers and adds them. The first thing I realized was that the program will only work with one-digit numbers (and results):
; Calculator
SECTION .data
msg1 db "Enter the first number: "
msg1len equ $-msg1
msg2 db "Enter the second number: "
msg2len equ $-msg2
msg3 db "The result is: "
msg3len equ $-msg3
SECTION .bss
num1 resb 1
num2 resb 1
result resb 1
SECTION .text
global main
main:
; Ask for the first number
mov EAX,4
mov EBX,1
mov ECX,msg1
mov EDX,msg1len
int 0x80
; Read the first number
mov EAX,3
mov EBX,1
mov ECX,num1
mov EDX,2
int 0x80
; Ask for the second number
mov EAX,4
mov EBX,1
mov ECX,msg2
mov EDX,msg2len
int 0x80
; Read the second number
mov EAX,3
mov EBX,1
mov ECX,num2
mov EDX,2
int 0x80
; Prepare to announce the result
mov EAX,4
mov EBX,1
mov ECX,msg3
mov EDX,msg3len
int 0x80
; Do the sum
; Store read values to EAX and EBX
mov EAX,[num1]
mov EBX,[num2]
; From ASCII to decimal
sub EAX,'0'
sub EBX,'0'
; Add
add EAX,EBX
; Convert back to EAX
add EAX,'0'
; Save the result back to the variable
mov [result],EAX
; Print result
mov EAX,4
mov EBX,1
mov ECX,result
mov EDX,1
int 0x80
As you can see, I reserve one byte for the first number, another for the second, and one more for the result. This isn't very flexible. I would like to make additions with numbers of any size.
How should I approach this?
First of all you are generating a 32-bit program, not a 64-bit program. This is no problem as Linux 64-bit can run 32-bit programs if they are either statically linked (this is the case for you) or the 32-bit shared libraries are installed.
Your program contains a real bug: You are reading and writing the "EAX" register from a 1-byte field in RAM:
mov EAX, [num1]
This will normally work on little-endian computers (x86). However if the byte you want to read is at the end of the last memory page of your program you'll get a bus error.
Even more critical is the write command:
mov [result], EAX
This command will overwrite 3 bytes of memory following the "result" variable. If you extend your program by additional bytes:
num1 resb 1
num2 resb 1
result resb 1
newVariable1 resb 1
You'll overwrite these variables! To correct your program you must use the AL (and BL) register instead of the complete EAX register:
mov AL, [num1]
mov BL, [num2]
...
mov [result], AL
Another finding in your program is: You are reading from file handle #1. This is the standard output. Your program should read from file handle #0 (standard input):
mov EAX, 3 ; read
mov EBX, 0 ; standard input
...
int 0x80
But now the answer to the actual question:
The C library functions (e.g. fgets()) use buffered input. Doing it like this would be a bit to complicated for the beginning so reading one byte at a time could be a possibility.
Thinking the way "how would I solve this problem using a high-level language like C". If you don't use libraries in your assembler program you can only use system calls (section 2 man pages) as functions (e.g. you cannot use "fgets()" but only "read()").
In your case a C program reading a number from standard input could look like this:
int num1;
char c;
...
num1 = 0;
while(1)
{
if(read(0,&c,1)!=1) break;
if(c=='\r' || c=='\n') break;
num1 = 10*num1 + c - '0';
}
Now you may think about the assembler code (I typically use GNU assembler, which has another syntax, so maybe this code contains some bugs):
c resb 1
num1 resb 4
...
; Set "num1" to 0
mov EAX, 0
mov [num1], EAX
; Here our while-loop starts
next_digit:
; Read one character
mov EAX, 3
mov EBX, 0
mov ECX, c
mov EDX, 1
int 0x80
; Check for the end-of-input
cmp EAX, 1
jnz end_of_loop
; This will cause EBX to be 0.
; When modifying the BL register the
; low 8 bits of EBX are modified.
; The high 24 bits remain 0.
; So clearing the EBX register before
; reading an 8-bit number into BL is
; a method for converting an 8-bit
; number to a 32-bit number!
xor EBX, EBX
; Load the character read into BL
; Check for "\r" or "\n" as input
mov BL, [c]
cmp BL, 10
jz end_of_loop
cmp BL, 13
jz end_of_loop
; read "num1" into EAX
mov EAX, [num1]
; Multiply "num1" with 10
mov ECX, 10
mul ECX
; Add one digit
sub EBX, '0'
add EAX, EBX
; write "num1" back
mov [num1], EAX
; Do the while loop again
jmp next_digit
; The end of the loop...
end_of_loop:
; Done
Writing decimal numbers with more digits is more difficult!
How come this program is not printing out to the screen, am I missing something on the INT 80 command?
section .bss
section .data
hello: db "Hello World",0xa ;10 is EOL
section .text
global _start
_start:
mov ecx, 0; ; int i = 0;
loop:
mov dl, byte [hello + ecx] ; while(data[i] != EOF) {
cmp dl, 0xa ;
je exit ;
mov ebx, ecx ; store conetents of i (ecx)
; Print single character
mov eax, 4 ; set sys_write syscall
mov ecx, byte [hello + ebx] ; ...
mov edx, 1 ; move one byte at a time
int 0x80 ;
inc ebx ; i++
mov ecx, ebx ; move ebx back to ecx
jmp loop ;
exit:
mov eax, 0x01 ; 0x01 = syscall for exit
int 0x80 ;
ADDITION
My Makefile:
sandbox: sandbox.o
ld -o sandbox sandbox.o
sandbox.o: sandbox.asm
nasm -f elf -g -F stabs sandbox.asm -l sandbox.lst
Modified Code:
section .bss
section .data
hello: db "Hello World",0xa ;10 is EOL
section .text
global _start
_start:
mov ecx, 0; ; int i = 0;
while:
mov dl, byte [hello + ecx] ; while(data[i] != EOF) {
cmp dl, 0xa ;
je exit ;
mov ebx, ecx ; store conetents of i (ecx)
; Print single character
mov eax, 4 ; set sys_write syscall
mov cl, byte [hello + ebx] ; ...
mov edx, 1 ; move one byte at a time
int 0x80 ;
inc ebx ; i++
mov ecx, ebx ; move ebx back to ecx
jmp while ;
exit:
mov eax, 0x01 ; 0x01 = syscall for exit
int 0x80 ;
One of the reasons it's not printing is because ebx is supposed to hold the value 1 to specify stdin, and another is because sys_write takes a pointer (the address of your string) as an argument, not an actual character value.
Anyway, let me show you a simpler way of structuring your program:
section .data
SYS_EXIT equ 1
SYS_WRITE equ 4
STDOUT equ 1
TRAP equ 0x80
NUL equ 0
hello: db "Hello World",0xA,NUL ; 0xA is linefeed, terminate with NUL
section .text
global _start
_start:
nop ; for good old gdb
mov ecx, hello ; ecx is the char* to be passed to sys_write
read:
cmp byte[ecx], NUL ; NUL indicates the end of the string
je exit ; if reached the NUL terminator, exit
; setup the registers for a sys_write call
mov eax, SYS_WRITE ; syscall number for sys_write
mov ebx, STDOUT ; print to stdout
mov edx, 1 ; write 1 char at a time
int TRAP; ; execute the syscall
inc ecx ; increment the pointer to the next char
jmp read ; loop back to read
exit:
mov eax, SYS_EXIT ; load the syscall number for sys_exit
mov ebx, 0 ; return a code of 0
int TRAP ; execute the syscall
It can be simpler to NUL terminate your string as I did, or you could also do $-hello to get it's length at compile time. I also set the registers up for sys_write at each iteration in the loop (as you do), since sys_write doesn't preserve all the registers.
I don't know how you got your code to assemble, but it doesn't assemble over here for a couple of very good reasons.
You cannot use loop as a label name because that name is reserved for the loop instruction.
Your line 20's instruction mov ecx, byte [hello + ebx] doesn't assemble either because the source and destination operands' sizes don't match (byte vs dword). Possible changes:
mov cl, byte [hello + ebx]
mov ecx, dword [hello + ebx]
movzx ecx, byte [hello + ebx]
Was the above not the actual code you had?