How should I work with dynamically-sized input in NASM Assembly? - linux

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!

Related

Assembly won't print digits

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.

Finding number is Even/Odd in assembly

I'm trying to find whether a given number (Input by user) is even or odd.
I'm simply applying AND operation on binary digits of a no. with 1, If the number is odd then operation will result 0 and we will Output Number is odd, otherwise we will output Number is even.
Although logic seems simple, But it's not working in the below code. I'm not getting where is the problem in the code. Can anybody tell me where is the problem
section .data
userMsg db 'Please enter a number'
lenuserMsg equ $ - userMsg
even_msg db 'Even Number!'
len1 equ $ - even_msg
odd_msg db 'Odd Number!'
len2 equ $ - odd_msg
section .bss
num resb 5 ;Reserved 5 Bytes for Input
section .text
global _start ;must be declared for linker (gcc)
_start:
;User Prompt
mov ebx, 1 ;file descriptor (stdout)
mov ecx, userMsg ;message to write 'Please enter a number'
mov edx, lenuserMsg ;message length
mov eax, 4 ;system call number (sys_write)
int 0x80 ;call kernel
;Taking user input
mov ebx, 0 ;(stdin)
mov ecx, num
mov edx, 5 ;i/p length
mov eax, 3 ;system call number (sys_read)
int 0x80 ;call kernel
mov ax, [num]
and ax, 1
jz evnn ;Jump on Even
;Printing No. is Odd
mov ebx, 1 ;file descriptor (stdout)
mov ecx, odd_msg ;message to write 'Odd Number!'
mov edx, len2 ;message length
mov eax, 4 ;system call number (sys_write)
int 0x80 ;call kernel
jmp outprog ;Jump to exit
;Printing No. is Even
evnn:
mov ebx, 1 ;file descriptor (stdout)
mov ecx, even_msg ;message to write 'Even Number!'
mov edx, len1 ;message length
mov eax, 4 ;system call number (sys_write)
int 0x80 ;call kernel
;Exit
outprog:
mov eax, 1 ;system call number (sys_exit)
int 0x80 ;call kernel
Just focus on the real problem at hand, shall we? If say an ASCII char is put in AL register, just turn it into a digit and the rest should just be natural. In computing (binary numbers and systems), integers oddness or evenness is determined by the bit 0. If it is 1, it is an odd number. If it is 0, it is an even number. (I am surprised that nobody has specifically put enough emphasis on this thus far).
... ;OS puts a char in AL.
sub al,30h ;turn an ASCII char to one integer digit
shr al,1 ;Lets see how the flags responds below
jc .odd ;CF is set if the first bit (right-most, bit 0) is 1.
;do Even things
;skip pass .odd
.odd:
;do Odd things
Your code does not work because when you ask the user for a number, you read in an ASCII encoded string. You will need to call atoi (ASCII to INT) first to convert the string to a "real" number as computers see it. atoi is included in glibc.
extern atoi
push eax ; pointer to your string to be converted, eg '123'
call atoi
; now eax contains your number, 123
You can also do a bit test on the least significant bit (bit 0) to find out if it is even or odd:
mov al, 01000_1101b
bt al, 0 ; copies the bit to the Carry Flag
jc its_odd ; jump if CF==1
; else - it's even (CF==0)
What BT does, it copies the bit to CF and you can do conditional jumps based on that.
mov ax, [num] loads the first 2 digits of the user's input string, and you're testing the first one. So you're actually testing whether the first character's ASCII code is even.
2 is a factor of 10, so you only need to test the low bit of the last decimal digit to determine if a base-10 number is even or odd.
And since the ASCII code for '0' is 0x30, you can just test the low bit of the last ASCII character of the string.
You don't need to call atoi() unless you need to test n % 3 or some other modulus that isn't a factor of 10. (i.e. you can test n % 2, n % 5, and n % 10 by looking at only the last digit). Note that you can't just test the low 2 bit of the low decimal digit to check for a multiple of 4, because 10 is not a multiple of 4. e.g. 100%4 = 0, but 30%4 = 2.
So, given a pointer + length, you can use TEST byte [last_char], 1 / jnz odd. e.g. after your sys_read, you have a pointer to the string in ECX, and the return value (byte count) in EAX.
;Taking user input
mov ebx, 0 ;(stdin)
mov ecx, num
mov edx, 5 ;i/p length
mov eax, 3 ;system call number (sys_read)
int 0x80 ;call kernel
; Now we make the unsafe assumption that input ended with a newline
; so the last decimal digit is at num+eax-1.
; now do anything that is common to both the odd and even branches,
; instead of duplicating that in each branch.
Then comes the actual test for odd/even: Just one test&branch on the last ASCII digit:
; We still have num in ECX, because int 0x80 doesn't clobber any regs (except for eax with the return value).
test byte [ecx + eax - 1], 1
jnz odd
`section .bss
num resb 1
section .data
msg1 db'enter a number',0xa
len1 equ $-msg1
msg2 db' is even',0xa
len2 equ $-msg2
msg3 db'is odd',0xa
len3 equ $-msg3
section .text
global _start
_start:
mov edx,len1
mov ecx,msg1
mov ebx,1
mov eax,4
int 80h
mov ecx,num
mov ebx,0
mov eax,3
int 80h
mov al,[num]
add al,30h
and al,1
jz iseven
jmp isodd
isodd:
mov edx,len3
mov ecx,msg3
mov ebx,1
mov eax,4
int 80h
jmp exit
iseven:
mov edx,len2
mov ecx,msg2
mov ebx,1
mov eax,4
int 80h
jmp exit
exit:
mov eax,1
int 80h`

Display contents of register

hi i need help displaying contents of a register.my code is below.i have been able to display values of the data register but i want to display flag states. eg 1 or 0. and it would be helpful if to display also the contents of other registers like esi,ebp.
my code is not printing the states of the flags ..what am i missing
section .text
global _start ;must be declared for using gcc
_start : ;tell linker entry point
mov eax,msg ; moves message "rubi" to eax register
mov [reg],eax ; moves message from eax to reg variable
mov edx, 8 ;message length
mov ecx, [reg];message to write
mov ebx, 1 ;file descriptor (stdout)
mov eax, 4 ;system call number (sys_write)
int 0x80 ;call kernel
mov eax, 100
mov ebx, 100
cmp ebx,eax
pushf
pop dword eax
mov [save_flags],eax
mov edx, 8 ;message length
mov ecx,[save_flags] ;message to write
mov ebx, 1 ;file descriptor (stdout)
mov eax, 4 ;system call number (sys_write)
int 0x80
mov eax, 1 ;system call number (sys_exit)
int 0x80 ;call kernel
section .data
msg db "rubi",10
section .bss
reg resb 100
save_flags resw 100
I'm not going for anything fancy here since this appears to be a homework assignment (two people have asked the same question today). This code should be made as a function, and it can have its performance enhanced. Since I don't get an honorary degree or an A in the class it doesn't make sense to me to offer the best solution, but one you can work from:
BITS_TO_DISPLAY equ 32 ; Number of least significant bits to display (1-32)
section .text
global _start ; must be declared for using gcc
_start : ; tell linker entry point
mov edx, msg_len ; message length
mov ecx, msg ; message to write
mov ebx, 1 ; file descriptor (stdout)
mov eax, 4 ; system call number (sys_write)
int 0x80 ; call kernel
mov eax, 100
mov ebx, 100
cmp ebx,eax
pushf
pop dword eax
; Convert binary to string by shifting the right most bit off EAX into
; the carry flag (CF) and convert the bit into a '0' or '1' and place
; in the save_flags buffer in reverse order. Nul terminate the string
; in the event you ever wish to use printf to print it
mov ecx, BITS_TO_DISPLAY ; Number of bits of EAX register to display
mov byte [save_flags+ecx], 0 ; Nul terminate binary string in case we use printf
bin2ascii:
xor bl, bl ; BL = 0
shr eax, 1 ; Shift right most bit into carry flag
adc bl, '0' ; bl = bl + '0' + Carry Flag
mov [save_flags-1+ecx], bl ; Place '0'/'1' into string buffer in reverse order
dec ecx
jnz bin2ascii ; Loop until all bits processed
mov edx, BITS_TO_DISPLAY ; message length
mov ecx, save_flags ; address of binary string to write
mov ebx, 1 ; file descriptor (stdout)
mov eax, 4 ; system call number (sys_write)
int 0x80
mov eax, 1 ;system call number (sys_exit)
int 0x80 ;call kernel
section .data
msg db "rubi",10
msg_len equ $ - msg
section .bss
save_flags resb BITS_TO_DISPLAY+1 ; Add one byte for nul terminator in case we use printf
The idea behind this code is that we continually shift the bits (using the SHR instruction) in the EAX register to the right one bit at a time. The bit that gets shifted out of the register gets placed in the carry flag (CF). We can use ADC to add the value of the carry flag (0/1) to ASCII '0' to get an ASCII value of '0` and '1'. We place these bytes into destination buffer in reverse order since we are moving from right to left through the bits.
BITS_TO_DISPLAY can be set between 1 and 32 (since this is 32-bit code). If you are interested in the bottom 8 bits of a register set it to 8. If you want to display all the bits of a 32-bit register, specify 32.
Note that you can pop directly into memory.
And if you want to binary dump register and flag data with write(2), your system call needs to pass a pointer to the buffer, not the data itself. Use a mov-immediate to get the address into the register, rather than doing a load. Or lea to use a RIP-relative addressing mode. Or pass a pointer to where it's sitting on the stack, instead of copying it to a global!
mov edx, 8 ;message length
mov ecx,[save_flags] ;message to write ;;;;;;; <<<--- problem
mov ebx, 1 ;file descriptor (stdout)
mov eax, 4 ;system call number (sys_write)
int 0x80
Passing a bad address to write(2) won't cause your program to receive a SIGSEGV, like it would if you used that address in user-space. Instead, write will return EFAULT. And you're not checking the return status from your system calls, so your code doesn't notice.
mov eax,msg ; moves message "rubi" to eax register
mov [reg],eax ; moves message from eax to reg variable
mov ecx, [reg];
This is silly. You should just mov ecx, msg to get the address of msg into ecx, rather than bouncing it through memory.
Are you building for 64bit? I see you're using 8 bytes for a message length. If so, you should be using the 64bit function call ABI (with syscall, not int 0x80). The system-call numbers are different. See the table in one of the links at x86. The 32bit ABI can only accept 32bit pointers. You will have a problem if you try to pass a pointer that has any of the high32 bits set.
You're probably also going to want to format the number into a string, unless you want to pipe your program's output into hexdump.

Binary representation in processor's registers in Nasm

I would like to ask about process of put instructions into registers. For example: we want to overwrite count '50' into EBX (in ASCII '50' is count '2').
EBX consists of 32 bits. When we put '50' into it, it will be arranged as binary represent, yes? (0000000 | 00000000 | 00000000 | 00110010). Have a right? What happens with bits, when we place a string into register?
EAX holds 32 bits which Intel calls "integer". The programmer - and sometimes the assembler - decides how to interpret these bits. If you load EAX with the number 50 (not the string '50')
mov eax, 50
the assembler decides to generate a machine instruction that loads the 50 in a manner, that you can read it as number 50 in a binary system:
00000000000000000000000000110010
Try out, what the assembler does if you feed it with a string:
GLOBAL _start
SECTION .bss
outstr resb 40
SECTION .data
_start:
mov eax, 'Four' ; Load EAX with a string
call int2bin ; Convert it to a binary string in outstr
mov byte [edi], 10 ; Add a line feed
inc edi ; Increment the pointer
mov eax, 4 ; SYS_WRITE
mov ebx, 1 ; STDOUT
mov ecx, outstr ; Pointer to output buffer
mov edx, edi ; Count of bytes to send:
sub edx, outstr ; EDX = EDI (offset returned from int2bin) - offset of output buffer
int 0x80 ; Call kernel
mov eax, 1 ; SYS_EXIT
xor ebx, ebx ; Returncode: 0 (ok)
int 0x80 ; Call kernel
int2bin: ; Converts an integer in EAX to a binary string in outstr
mov edi, outstr ; Pointer to a string
mov ecx, 32 ; Loop counter
.LL1:
test cl, 0b111 ; CL%8 = 0 ?
jnz .F ; No: skip the next instructions
mov Byte [edi], ' ' ; Store a space
inc edi ; and increment the pointer
.F:
shl eax, 1 ; The leftmost bit into carry flag
setc dl ; Carry flag into DL
or dl, '0' ; Convert it to ASCII
mov [edi], dl ; Store it to outstr
inc edi ; Increment the pointer
loop .LL1 ; Loop ECX times
mov byte [edi], 0 ; Null termination if needed as C string (not needed here)
ret
Output:
01110010 01110101 01101111 01000110
NASM stored it backwards in EAX. The ASCII of leftmost character is stored in the rightmost byte of EAX, the second-to-last character is to be found in the second byte, and so on. Better to see when those bytes are printed as ASCII characters:
GLOBAL _start
SECTION .bss
outstr resb 40
SECTION .data
_start:
mov eax, 'Four' ; Load EAX with a string
call int2str ; Convert it to a binary string in outstr
mov byte [edi], 10 ; Add a line feed
inc edi ; Increment the pointer
mov eax, 4 ; SYS_WRITE
mov ebx, 1 ; STDOUT
mov ecx, outstr ; Pointer to output buffer
mov edx, edi ; Count of bytes to send:
sub edx, outstr ; EDX = EDI (offset returned from int2bin) - offset of output buffer
int 0x80 ; Call kernel
mov eax, 1 ; SYS_EXIT
xor ebx, ebx ; Returncode: 0 (ok)
int 0x80 ; Call kernel
int2str: ; Converts an integer in EAX to an ASCII string in outstr
mov edi, outstr ; Pointer to a string
mov ecx, 4 ; Loop counter
.LL1:
rol eax, 8
mov [edi], al ; Store it to outstr
inc edi ; Increment the pointer
loop .LL1 ; Loop ECX times
mov byte [edi], 0 ; Null termination if needed as C string (not needed here)
ret
Output:
ruoF
Both programs above show EAX in big endian order. This is the order you are familiar with looking at decimal numbers. The most significant digit is left and the least significant digit is right. However, EAX would be saved in memory or disk in little endian order, starting the sequence from the right with the least significant byte. Looking at the memory with a disassembler or debugger you would see 'F','o','u','r' as well as you had defined it in a .data section with db 'Four'. Therefore you'll get no difference when you load a register with a string, save it to memory and call the write routine of the kernel:
GLOBAL _start
SECTION .bss
outstr resb 40
SECTION .data
_start:
mov eax, 'Hell' ; Load EAX with the first part of the string
mov ebx, 'o wo' ; Load EBX with the second part
mov ecx, 'rld!' ; Load ECX with the third part
mov dword [outstr], eax ; Store the first part in outstr (little endian)
mov dword [outstr+4], ebx ; Append the second part
mov dword [outstr+8], ecx ; Append the third part
mov eax, 4 ; SYS_WRITE
mov ebx, 1 ; STDOUT
mov ecx, outstr ; Pointer to output buffer
mov edx, (3*4) ; Count of bytes to send (3 DWORD à 4 bytes)
int 0x80 ; Call kernel
mov eax, 1 ; SYS_EXIT
xor ebx, ebx ; Returncode: 0 (ok)
int 0x80 ; Call kernel
Output:
Hello world!
Please note: This behavior is made by the NASM programmers. Other assemblers might have a different behavior.

NASM addition program

I am a developer who uses high level languages, learning assembly language in my spare time. Please see the NASM program below:
section .data
section .bss
section .text
global main
main:
mov eax,21
mov ebx,9
add eax,ebx
mov ecx,eax
mov eax,4
mov ebx,1
mov edx,4
int 0x80
push ebp
mov ebp,esp
mov esp,ebp
pop ebp
ret
Here are the commands I use:
ian#ubuntu:~/Desktop/NASM/Program4$ nasm -f elf -o asm.o SystemCalls.asm
ian#ubuntu:~/Desktop/NASM/Program4$ gcc -o program asm.o
ian#ubuntu:~/Desktop/NASM/Program4$ ./program
I don't get any errors, however nothing is printed to the terminal. I used the following link to ensure the registers contained the correct values: http://docs.cs.up.ac.za/programming/asm/derick_tut/syscalls.html
You'll have to convert the integer value to a string to be able to print it with sys_write (syscall 4). The conversion could be done like this (untested):
; Converts the integer value in EAX to a string in
; decimal representation.
; Returns a pointer to the resulting string in EAX.
int_to_string:
mov byte [buffer+9],0 ; add a string terminator at the end of the buffer
lea esi,[buffer+9]
mov ebx,10 ; divisor
int_to_string_loop:
xor edx,edx ; clear edx prior to dividing edx:eax by ebx
div ebx ; EAX /= 10
add dl,'0' ; take the remainder of the division and convert it from 0..9 -> '0'..'9'
dec esi ; store it in the buffer
mov [esi],dl
test eax,eax
jnz int_to_string_loop ; repeat until EAX==0
mov eax,esi
ret
buffer: resb 10
programming in assembly requires a knowledge of ASCII codes and a some basic conversion routines. example: hexadecimal to decimal, decimal to hexadecimal are good routines to keep somewhere on some storage.
No registers can be printed as they are, you have to convert (a lot).
To be a bit more helpfull:
ASCII 0 prints nothing but some text editors (kate in kde linux) will show something on screen (a square or ...). In higher level language like C and C++ is it used to indicate NULL pointers and end of strings.
Usefull to calculate string lengths too.
10 is end of line. depending Linux or Windows there will be a carriage return (Linux) too or not (Windows/Dos).
13 is carriage return
1B is the ESC key (Linux users will now more about this)
255 is a hard return, I never knew why it is good for but it must have its purpose.
check http://www.asciitable.com/ for the entire list.
Convert the integer value to a string.
Here i have used macros pack and unpack to convert integers to string and macro unpack to do the vice-versa
%macro write 2
mov eax, 4
mov ebx, 1
mov ecx, %1
mov edx, %2
int 80h
%endmacro
%macro read 2
mov eax,3
mov ebx,0
mov ecx,%1
mov edx,%2
int 80h
%endmacro
%macro pack 3 ; 1-> string ,2->length ,3->variable
mov esi, %1
mov ebx,0
%%l1:
cmp byte [esi], 10
je %%exit
imul ebx,10
movzx edx,byte [esi]
sub edx,'0'
add ebx,edx
inc esi
jmp %%l1
%%exit:
mov [%3],ebx
%endmacro
%macro unpack 3 ; 1-> string ,2->length ,3->variable
mov esi, %1
mov ebx,0
movzx eax, byte[%3]
mov byte[%2],0
cmp eax, 0
jne %%l1
mov byte[%2],1
push eax
jmp %%exit2
%%l1:
mov ecx,10
mov edx,0
div ecx
add edx,'0'
push edx
inc byte[%2]
cmp eax, 0
je %%exit2
jmp %%l1
%%exit2:
movzx ecx,byte[%2]
%%l2:
pop edx
mov [esi],dl
inc esi
loop %%l2
%endmacro
section .data ; data section
msg1: db "First number : " ;
len1: equ $-msg1 ;
msg2: db "Second number : " ;
len2: equ $-msg2 ;
msg3: db "Sum : " ;
len3: equ $-msg3 ;
ln: db 10
lnl: equ $-ln
var1: resb 10
var2: resb 10
str1: resb 10
str2: resb 10
ans: resb 10
ansvar: resb 10
ansl: db ''
l1: db ''
l2: db ''
section.text ;code
global _start
_start:
write msg1,len1
read str1,10
pack str1,l1,var1
write msg2,len2
read str2,10
pack str2,l2,var2
mov al,[var1]
add al,[var2]
mov [ansvar],al
unpack ans,ansl,ansvar
write msg3,len3
write ans,10
write ln,lnl
mov ebx,0 ; exit code, 0=normal
mov eax,1 ; exit command to kernel
int 0x80 ; interrupt 80 hex, call kernel
To assembler, link and run:
nasm -f elf add.asm
ld -s -o add add.o
./add

Resources