Strange behaviour writing random number generator in assembly - linux

For a project recently I've been working on translating an RNG from Numerical Recipes in C to assembly language. I have tried many different methods of compiling and disassembling the C code but all lead to the same output, 7 good random numbers followed by a string of 2,147,483,648.0:
Output:
0.214112
0.581177
0.848338
0.028559
0.996681
0.228658
0.067130
2147483648.0
2147483648.0
2147483648.0
2147483648.0
2147483648.0
2147483648.0
2147483648.0
2147483648.0
2147483648.0
2147483648.0
2147483648.0
2147483648.0
2147483648.0
This is interesting because its IM + 1 and 2^31 but I don't know where its coming from or why. If someone can see whats wrong with my code or even better fix it for me ;) That would be much appreciated.
This is the C code that I'm trying to translate:
// This is the C Random Number Generator
float ran0(long *idum) {
long k;
float ans;
*idum ^= MASK;
k= (*idum) / IQ;
*idum= IA * (*idum - k * IQ) - IR * k;
if (*idum < 0) *idum += IM;
ans= AM * (*idum);
*idum ^= MASK;
return ans;
}
This is the assembly code (I know its a lot but its pretty modular and I think its fairly well commented):
[global _start]
section .data
counter dd 0 ; This is the counter for our loop in _start
seed dd 0 ; This holds the seed for ran0
ranNum dd 0 ; This holds the return from ran0 (after its pulled off the FPU stack)
; Constants for ran0
IA dd 16807
IM dd 2147483647
AM dq 0x3E00000000200000
IQ dd 127773
IR dd 2836
MASK dd 123459876
; Lengths of the output strings for itoa and ftoa
intLen equ 12 ; This tells it to only print itoaOut
floatLen equ 24 ; This includes both itoaOut and ftoaOut
section .bss
; Output strings for itoa and ftoa
; Both functions use itoaOut for their outputs, but ftoa stores the float part in ftoaOut
itoaOut resb 12
ftoaOut resb 12
section .text
_start:
push ebp
mov ebp, esp
; This gets the system time, which we use as a "random" seed
sub esp, 0x04
mov eax, 13
mov ebx, esp
int 0x80
mov eax, [esp]
add esp, 0x04
; Init ran0 (running it once seems to make it more random)
sub esp, 0x04
push eax
call ran0
pop eax
fstp dword [esp] ; The random number is returned on the top of the FPU stack
add esp, 0x04
mov [counter], dword 0
mov [seed], eax
.startLoop:
; Uses the value in seed to call ran0
mov eax, dword [seed]
push eax
call ran0
pop eax
mov [seed], eax
fstp dword [ranNum] ; Puts the value returned from ran0 into ranNum
; Uses the random number to call ftoa which converts it to a string
mov eax, dword [ranNum]
push eax
call ftoa
pop eax
;;;;;;;;
; Print the result
; To print a float, point it to itoaOut and tell it to print a longer length
mov eax, 4
mov ebx, 1
mov ecx, itoaOut ; This points to the string we want to print
mov edx, floatLen ; This tells it the length of the string
int 0x80
add [counter], dword 1
cmp [counter], dword 20
jl .startLoop
leave
mov eax, 1
mov ebx, 0
int 0x80
ran0: ; ran0(int seed): returns st0
push ebp
mov ebp, esp
sub esp, 0x08
; *idum ^= MASK
mov eax, dword [ebp+0x08]
xor eax, dword [MASK]
mov [ebp+0x08], eax
; k= (*idum) / IQ
mov eax, dword [ebp+0x08]
mov ebx, dword [IQ]
mov edx, 0
div ebx
mov [esp+0x04], eax
; *idum= IA * (*idum - k * IQ) - IR * k
mov eax, dword [esp+0x04]
mov ebx, dword [IQ]
imul ebx
mov edx, [ebp+0x08]
sub edx, eax
mov eax, dword [IA]
imul edx
mov ecx, dword [IR]
mov edx, [esp+0x04]
imul edx, ecx
sub eax, edx
mov [ebp+0x08], eax
; if (*idum < 0) *idum += IM
mov eax, [ebp+0x08]
cmp eax, 0
jge .endIfRAN
mov eax, [ebp+0x08]
add eax, dword [IM]
mov [ebp+0x08], eax
.endIfRAN:
; ans= AM * (*idum)
fild dword [ebp+0x08]
fld qword [AM]
fmulp st1, st0
fstp dword [esp]
; *idum ^= MASK
mov eax, dword [ebp+0x08]
xor eax, dword [MASK]
mov [ebp+0x08], eax
fld dword [esp]
add esp, 0x08
leave
ret
itoa: ; itoa(int number): returns itoaOut
push ebp
mov ebp, esp
; Set itoaOut to 0
mov edi, itoaOut
mov ecx, 0
.ftoaLoopCLEAR:
mov [edi], byte 0
inc edi
inc ecx
cmp ecx, 12
jl .ftoaLoopCLEAR
mov edi, itoaOut
mov [edi+0x0b], byte 0x0a
add edi, 0x0a
mov eax, [ebp+0x08] ; Puts the argument (int number) into eax
mov ebx, 10 ; This is what we divide eax by
; This loop writes digits to itoaOut backwards
.itoaLoop:
mov edx, 0
div ebx ; eax= eax/ebx edx= remainder
or edx, 0x30 ; or the remainder with 30 to make a valid ascii char
mov [edi], dl ; Put the char in the output string (itoaOut)
dec edi
cmp edi, itoaOut ; If edi is not at the beginning of itoaOut, write another digit
jge .itoaLoop ; otherwise quit
leave
ret
ftoa: ; ftoa(flt number): returns itoaOut
push ebp
mov ebp, esp
sub esp, 12
; Set both itoaOut and ftoaOut to 0
mov edi, itoaOut
mov esi, ftoaOut
mov ecx, 0
.ftoaLoopCLEAR:
mov [edi], byte 0
mov [esi], byte 0
inc edi
inc esi
inc ecx
cmp ecx, 12
jl .ftoaLoopCLEAR
fld dword [ebp+0x08] ; Load 'number' into st0
fisttp dword [esp+0x08] ; Set [esp+0x08] to the int part of 'number'
fld dword [ebp+0x08] ; Load 'number' into st0
fisub dword [esp+0x08] ; st0= st0 - [esp+0x08]
fstp dword [esp+0x04] ; Set [esp+0x04] to the float part of 'number'
; Digits are written to itoaOut backwards because it's easy to
; repetitively isolate the least significant digit of a number
mov edi, itoaOut ; Set the destination to itoaOut
add edi, 0x0b ; Set edi to point to the end of itoaOut
mov eax, [esp+0x08] ; Set eax to the integer part of the float
mov ebx, 0x0a ; Set ebx to 10, this will be the divisor
mov ecx, 0 ; ecx functions as a character counter throughout the code
.ftoaLoopINT:
mov edx, 0x00 ; Set edx to 0, it will collect the remainder
div ebx ; eax= eax/ebx, edx= remainder
or edx, 0x30 ; Tag the remainder as an ascii number character
mov [edi], dl ; Put the ascii number character into the string (itoaOut)
dec edi ; Set edi to point to the previous character
inc ecx ; Add one to ecx to count ont more character
cmp eax, 0x00 ; If eax > 0, jump back to the beginning to write the next number
jg .ftoaLoopINT
; Digits are written to ftoaOut forwards by
; multiplying by 10 and repetitively truncating to get the next digit
mov edi, ftoaOut ; Set the destination to ftoaOut
fld dword [esp+0x04] ; Load the float part of 'number' into st0
mov [edi], byte "." ; Place the decimal at the first byte of ftoaOut
inc edi ; Set edi to the second byte of ftoaOut
mov [esp], dword 10 ; Set variable to 10
.ftoaLoopFLT:
fimul dword [esp] ; st0= st0 * [esp]
fld st0 ; Copy st0 to st1
fisttp dword [esp+0x08] ; Truncate and send st0 to [esp+0x08], now st0=st1
fisub dword [esp+0x08] ; st0= st0 - [esp+0x08]
mov edx, [esp+0x08] ; mov [esp+0x08] into edx
or edx, 0x30 ; Translate the digit in edx into an ascii number character
mov [edi], dl ; Put the ascii number character into [edi] (ftoaOut)
inc edi ; Point edi to the next character in ftoaOut
inc ecx ; Add one to ecx to count ont more character
cmp ecx, 7 ; A 32 bit float can generally hold no more than 7 digits
jl .ftoaLoopFLT
mov [edi], byte 0x0a ; append '\n' to the end
; In the end, itoaOut holds the int part and ftoaOut holds the float part
add esp, 12
leave
ret
The code in ran0 is based off of the dissasembled gcc code.
Thank you. :)

Related

Learning assembly. how to make code faster

I started to learn assembly some days ago and i write my first ever piece of code using user input, string functions, passing arguments by stack or by register etc...
I have some questions. Do you have some advices to make my code faster. For example, in my atoi function, i know that imul is time consuming. Maybe, there are enormous mistakes but as far as i know, many things to improve for sure. So my main question is : are there fatal errors in this first code and my second is : any type to refactoring code with faster instructions
SYS_READ equ 3
SYS_WRITE equ 4
STDIN equ 0
STDOUT equ 1
%macro printm 2
mov eax, SYS_WRITE
mov ebx, STDOUT
mov ecx, %1
mov edx, %2
int 0x80
%endmacro
%macro prolog 0
push ebp,
mov ebp, esp
%endmacro
%macro epilog 0
mov esp, ebp
pop ebp
%endmacro
section .text
global _start
_start:
; first check if our strlen proc works
push dword msgbegin
call strlen
add esp, byte 4
cmp eax, lenbegin
je .suite ; it works, we continue
; exiting prog if the len computed in rax != lenbegin
mov eax, 1
int 0x80
.suite:
; check if strcpy works printing res (msgbegin -> srcdst)
push dword lenbegin
push dword msgbegin
push dword strdst
call strcpy
add esp, byte 12
push dword lenbegin
push dword strdst
call print
add esp, byte 8
; first input
printm msgbinp1, leninp1
mov eax, SYS_READ
mov ebx, STDIN
mov ecx, num1
mov edx, 2
int 0x80
printm msgbinp2, leninp2
mov eax, SYS_READ
mov ebx, STDIN
mov ecx, num2
mov edx, 2
int 0x80
printm msgbinp3, leninp3
mov eax, SYS_READ
mov ebx, STDIN
mov ecx, bignum
mov edx, 4
int 0x80
mov edx, bignum
call atoi
cmp eax, 123
je .success ; exit if bignum != 123
mov eax, 1
int 0x80
.success:
; need to strip line feed from bignum
printm bignum, 4
printm msgoutp, lenoutp
; now we compute the sum
mov eax, [num1]
sub eax, '0'
mov ebx, [num2]
sub ebx, '0'
add eax, ebx
add eax, '0'
mov [sum], eax
printm msgres, lenres
; we print it
printm sum, 1
; exiting the programm
mov eax, 1
int 0x80
print:
push ebp
mov ebp, esp
mov eax, 4
mov ebx, 1
mov ecx, [ebp + 8]
mov edx, [ebp + 12]
int 0x80
mov esp, ebp
pop ebp
ret
strcpy:
push ebp
mov ebp, esp
mov ecx, [ebp + 16]
mov esi, [ebp + 12]
mov edi, [ebp + 8]
rep movsb
mov esp, ebp
pop ebp
ret
strlen:
push ebp
mov ebp, esp
push edi
push ecx
mov edi, [ebp + 8]
sub ecx, ecx
sub al, al
not ecx
cld
repne scasb
not ecx
lea eax, [ecx] ; keep null term in size
pop ecx
pop edi
mov esp, ebp
pop ebp
ret
atoi:
xor eax, eax ; zero a "result so far"
.top:
movzx ecx, byte [edx] ; get a character
inc edx ; ready for next one
cmp ecx, '0' ; valid?
jb .done
cmp ecx, '9'
ja .done
sub ecx, '0' ; "convert" character to number
imul eax, 10 ; multiply "result so far" by ten
add eax, ecx ; add in current digit
jmp .top ; until done
.done:
ret
section .data
msgbegin db "hello everyone !", 0xa, 0
lenbegin equ $ - msgbegin
msgbinp1 db "Enter a digit : ", 0xa, 0
leninp1 equ $ - msgbinp1
msgbinp2 db "Enter second digit : ", 0xa, 0
leninp2 equ $ - msgbinp2
msgbinp3 db "Enter third digit : ", 0xa, 0
leninp3 equ $ - msgbinp3
msgoutp db "is equal to 123 !", 0xa, 0
lenoutp equ $ - msgoutp
msgres db "sum of x and y is ", 0xa, 0
lenres equ $ - msgres
strdst times lenbegin db 0
segment .bss
sum resb 1
num1 resb 2
num2 resb 2
bignum resd 4
Thanks you. I started reading the doc but i'm not sure that i understood key concepts.

Printing current pid in assembly [duplicate]

I'm a novice Assembly x86 Learner, and i want to add two numbers (5+5) and print the result on the screen.
here is my code:
global _start
section .text
_start:
mov eax, 5
mov ebx, 5
add eax, ebx
push eax
mov eax, 4 ; call the write syscall
mov ebx, 1 ; STDOUT
pop ecx ; Result
mov edx, 0x1
int 0x80
; Exit
mov eax, 0x1
xor ebx, ebx
int 0x80
Correct me please
Another approach to convert an unsigned integer to a string and write it:
section .text
global _start
_start:
mov eax, 1234567890
mov ebx, 5
add eax, ebx
; Convert EAX to ASCII and store it onto the stack
sub esp, 16 ; reserve space on the stack
mov ecx, 10
mov ebx, 16
.L1:
xor edx, edx ; Don't forget it!
div ecx ; Extract the last decimal digit
or dl, 0x30 ; Convert remainder to ASCII
sub ebx, 1
mov [esp+ebx], dl ; Store remainder on the stack (reverse order)
test eax, eax ; Until there is nothing left to divide
jnz .L1
mov eax, 4 ; SYS_WRITE
lea ecx, [esp+ebx] ; Pointer to the first ASCII digit
mov edx, 16
sub edx, ebx ; Count of digits
mov ebx, 1 ; STDOUT
int 0x80 ; Call 32-bit Linux
add esp, 16 ; Restore the stack
mov eax, 1 ; SYS_EXIT
xor ebx, ebx ; Return value
int 0x80 ; Call 32-bit Linux

Can't find bad memory indicated by valgrind

I'm having trouble with valgrind.
I wrote an assembly program that simulates a game as part of an assignment and I only allocated dynamic memory once for a 2d array called drone_stats. I'm not getting any memory leaks since I did free all memory , but I am getting a bunch of other errors. namely:
Use of uninitialised value of size 4
Uninitialised value was created by a stack allocation
and they all come from this file:
global N
global R
global K
global d
global seed
global drone_stats
global resume
global CORS
global CODEP
global SPP
global seed_dword
global numco
global random
global quit
CODEP equ 0
SPP equ 4
section .rodata
format_int: db "%d"
format_float: db "%f"
section .bss
N: resb 4 ; number of drones
R: resb 4 ; number of rounds per elimination
K: resb 4 ; number of steps per print
d: resb 4 ; max distance away from target that allows destruction
seed: resb 4 ; intial seed for random number
drone_stats: resb 4 ; pointer to an array of pointer, each pointing the an array of the following format:
; [activated: boolean, id: int, x_pos: float, y_pos: float, speed: float, angle: float, score: int]
; drone_i_arr: [[drone_stats]+ drone_id(i)]
SPMAIN: resb 4
SPT: resb 4
CURR: resb 4
STKSZ equ 16*1024
STK1: resb STKSZ
STK2: resb STKSZ
STK3: resb STKSZ
STK4: resb STKSZ
section .data
numco: dd 4
PRINTER: dd f_printer
dd STK1+STKSZ
DRONE: dd f_drone
dd STK2+STKSZ
TARGET: dd f_target
dd STK3+STKSZ
SCHEDULER: dd f_scheduler
dd STK4+STKSZ
CORS: dd PRINTER ; id 0
dd DRONE ; id 1
dd TARGET ; id 2
dd SCHEDULER ; id 3
temp_macro: dd 4
; macro to free memory allocated by param
%macro free_array 1
pushad
push %1
call free
add esp, 4
popad
%endmacro
; macro to allocate %1 blocks of %2 bytes each , and return a pointer to allocated memory in eax
%macro allocate 2
pushad
push %2
push %1
call calloc
add esp, 8
mov [temp_macro], eax
popad
mov eax, [temp_macro]
%endmacro
; macro to read an input from the user according to format %2 and store in variable %1
%macro read_formatted 3 ; sscanf(%3, %2, %1)
pushad
push dword %1
push %2
push %3
call sscanf
add esp, 12
popad
%endmacro
; macro to initialize a single co-routine with id %1
%macro init_co 1
pushad
mov ebx, %1
mov ebx, [4*ebx+CORS]
mov eax, [ebx+CODEP]
mov [SPT], esp
mov esp, [ebx+SPP]
push eax ; push into co-routine's stack
pushfd
pushad
mov [ebx+SPP], esp
mov esp, [SPT]
popad
%endmacro
;macro to generate a number in range of 0 - %1 and to store it's value in %2
%macro generate_random 2
push dword %1
call calc_range
add esp, 4
mov dword [%2], eax
%endmacro
;============================================================::CODE::================================================================
section .text
;funcs
global main
extern f_drone
extern f_printer
extern f_target
extern f_scheduler
extern calc_range
extern calloc
extern free
extern sscanf
;vars
extern target_x
extern target_y
;================================::AUXILIARY FUNCTIONS::===================================
resume: ; save state of current co-routine
pushfd
pushad
mov edx, dword [CURR]
mov dword [edx + SPP], esp ; save current ESP
do_resume: ; load ESP for resumed co-routine
mov esp, dword [ebx + SPP]
mov dword [CURR], ebx ; get the current co routine
popad ; restore resumed co-routine state
popfd
ret ; "return" to resumed co-routine
;--------------------------------
; void quit()
; function to execute a syscall exit right after freeing all memory allocated by the program
quit:
push ebp
mov ebp, esp
mov eax, [drone_stats]
mov ecx, dword [N]
.loop:
mov ebx, [eax]
free_array ebx
add eax, 4
loop .loop
mov eax, [drone_stats]
free_array eax
mov eax, 1
mov ebx, 0
int 0x80
mov esp, ebp
pop ebp
;--------------------------------
; void random()
;
random:
push ebp ; function prologue
mov ebp, esp
pushad
mov edx, 16
seed_loop:
mov ebx,0
mov eax,0
mov ecx,0
mov bx, word [seed]
mov cx, 1
and cx, bx ; check if the 16 bit is '1' or '0'
shr bx, 2 ; get the 14 bit
mov ax, 1
and ax, bx ;check the 14 but
xor cx, ax
shr bx, 1 ; get the 13 bit
mov ax,1
and ax, bx ; check the 13 bit
xor cx, ax
shr bx,2 ; get the 11 bit
mov ax,1
and ax, bx ; check 11 bit
xor cx , ax
mov ax , word [seed]
shr ax, 1
shl cx, 15 ; move result to msb
or ax, cx
mov word [seed], ax
dec edx
cmp edx ,0
jne seed_loop
popad
mov esp, ebp ; function epilogue
pop ebp
ret
main:
push ebp ; function prologue
mov ebp, esp
pushad
; get command-line arguments: <N> <R> <K> <d> <seed>
mov ecx , dword [ebp+8] ; ecx = argc
mov ecx, dword [ebp+12] ; ecx points to argv[0] = program name
mov ebx , dword [ecx+4] ; ebx is a pointer to N(num_drones)
read_formatted N, format_int, ebx
mov ebx , dword [ecx+8] ; ebx is a pointer to R(number of rounds before elimination, Round = N steps)
read_formatted R, format_int, ebx
mov ebx , dword [ecx+12] ; ebx is a pointer to K(number of steps before printing the board)
read_formatted K, format_int, ebx
mov ebx , dword [ecx+16] ; ebx is a pointer to d(the maximum distance)
read_formatted d, format_float, ebx
mov ebx , dword [ecx+20] ; ebx is a pointer to seed(LFSR initialization value)
read_formatted seed, format_int, ebx
; initialize all co-routines
mov ecx, [numco]
xor ebx, ebx ; id of current co-routine
.init_cos:
init_co ebx
inc ebx
loop .init_cos
; intialize target
generate_random 100, target_x ; x: random [0~100]
generate_random 100, target_y ; y: random [0~100]
; allocate an array of pointers. each pointer will point to an array of size 7 that holds all the stats of a single drone:
; step1: allocate an array of N (num of drones) pointers
; step2: allocate N arrays of size 7 and have each one pointed to by a corresponding pointer in the first array
allocate dword [N], 4
mov [drone_stats], eax
mov ecx, [N]
mov ebx, [drone_stats]
.loop_create:
allocate 7, 4 ; allocate an array of size 7 to hold the stats of a drone
mov [ebx], eax ; ebx = drone_stats + i*4, that's a pointer to newly allocated memory for each drone
add ebx, 4
loop .loop_create
; initialize drones
mov ecx, dword [N]
mov edx, dword [drone_stats]
xor edi, edi
; initial stats of drone : [mode: on, id: edi, x: random, y: random, speed: random, angle: random, score: 0]
.loop_init:
mov ebx, [edx] ; array of i'th drone
mov dword [ebx], 1 ; mode: ON
add ebx, 4
mov dword [ebx], edi ; id: edi
add ebx, 4
generate_random 100, ebx ; x: random [0~100]
add ebx, 4
generate_random 100, ebx ; y: random [0~100]
add ebx, 4
generate_random 100, ebx ; speed: random [0~100]
add ebx, 4
generate_random 360, ebx ; angle: random [0~360]
add ebx, 4
mov dword [ebx], 0 ; score: 0
add edx, 4 ; next drone
inc edi ; next id
loop .loop_init
;; printing the board before playing
mov ebx, [CORS]
jmp do_resume
startCo:
pushad
mov [SPMAIN], esp
mov ebx, [CORS +12]
jmp do_resume
popad
mov esp, ebp
pop ebp
ret
Can anyone point to the problem? Thanks ahead!

Assembly NASM x86 - Simple Stack Project

I'm writing a subroutine to simply reprint decimal numbers as strings using the stack, but not getting the values I expected. When I run it through the debugger I see that I can't get the value from esi into al. I suspect that I'm not allowed to use esi in the manner that I am, but I'm not sure on another way I can do this. Also, I am not allowed to push the elements I'm storing in edx onto the stack.
Subroutine code:
%define STDIN 0
%define STDOUT 1
%define SYSCALL_EXIT 1
%define SYSCALL_READ 3
%define SYSCALL_WRITE 4
%define BUFLEN 256
SECTION .bss ; uninitialized data section
src_str: resb BUFLEN ; buffer for backwards number
dec_str: resb BUFLEN ; number will be converted and put in this buffer
rlen: resb 4 ; length
SECTION .text ; code begins here
global prt_dec
; Begin subroutine
prt_dec:
push eax
push ebx
push ecx
push edx
push esi
push edi
mov eax, [esp + 28] ; store the decimal number 4 bytes each for each push, plus the eip
mov esi, src_str ; point esi to the backwards string buffer
mov edi, dec_str ; point edi to the new buffer
mov ebx, 10 ; stores the constant 10 in ebx
div_loop:
mov edx, 0 ; clear out edx
div ebx ; divide the number by 10
add edx, '0' ; convert from decimal to char
mov [esi], edx ; store char in output buffer
inc esi ; move to next spot in output buffer
inc ecx ; keep track of how many chars are added
cmp eax, 0 ; is there anything left to divide into?
jne div_loop ; if so, continue the loop
output_loop:
add esi, ecx ; move 1 element beyond the end of the buffer
mov al, [esi - 1] ; move the last element in the buffer into al
mov [edi], al ; move it into the first position of the converted output buffer
inc edi ; move to the next position of the converted output buffer
dec ecx ; decrement to move backwards through the output buffer
cmp ecx, 0 ; if it doesn't equal 0, continue loop
jne output_loop
print:
mov eax, SYSCALL_WRITE ; write out string
mov ebx, STDOUT
mov ecx, dec_str
mov edx, 0
mov edx, rlen
int 080h
pop_end:
pop edi ; move the saved values back into their original registers
pop esi
pop edx
pop ecx
pop ebx
pop eax
ret
; End subroutine
Driver:
%define STDIN 0
%define STDOUT 1
%define SYSCALL_EXIT 1
%define SYSCALL_READ 3
%define SYSCALL_WRITE 4
SECTION .data ; initialized data section
lf: db 10 ; just a linefeed
msg1: db " plus "
len1 equ $ - msg1
msg2: db " minus "
len2 equ $ - msg2
msg3: db " equals "
len3 equ $ - msg3
SECTION .text ; Code section.
global _start ; let loader see entry point
extern prt_dec
_start:
mov ebx, 17
mov edx, 214123
mov edi, 2223187809
mov ebp, 1555544444
push dword 24
call prt_dec
add esp, 4
call prt_lf
push dword 0xFFFFFFFF
call prt_dec
add esp, 4
call prt_lf
push 3413151
call prt_dec
add esp, 4
call prt_lf
push ebx
call prt_dec
add esp, 4
call prt_lf
push edx
call prt_dec
add esp, 4
call prt_lf
push edi
call prt_dec
add esp, 4
call prt_lf
push ebp
call prt_dec
add esp, 4
call prt_lf
push 2
call prt_dec
add esp, 4
mov eax, SYSCALL_WRITE ; write message
mov ebx, STDOUT
mov ecx, msg1
mov edx, len1
int 080h
push 3
call prt_dec
add esp, 4
mov eax, SYSCALL_WRITE ; write message
mov ebx, STDOUT
mov ecx, msg3
mov edx, len3
int 080h
push 5
call prt_dec
add esp, 4
call prt_lf
push 7
call prt_dec
add esp, 4
mov eax, SYSCALL_WRITE ; write message
mov ebx, STDOUT
mov ecx, msg2
mov edx, len2
int 080h
push 4
call prt_dec
add esp, 4
mov eax, SYSCALL_WRITE ; write message
mov ebx, STDOUT
mov ecx, msg3
mov edx, len3
int 080h
push 3
call prt_dec
add esp, 4
call prt_lf
; final exit
;
exit: mov EAX, SYSCALL_EXIT ; exit function
mov EBX, 0 ; exit code, 0=normal
int 080h ; ask kernel to take over
; A subroutine to print a LF, all registers are preserved
prt_lf:
push eax
push ebx
push ecx
push edx
mov eax, SYSCALL_WRITE ; write message
mov ebx, STDOUT
mov ecx, lf
mov edx, 1 ; LF is a single character
int 080h
pop edx
pop ecx
pop ebx
pop eax
ret
Fixes I had on mind (asterisk denotes lines I did touch), hopefully it will be clear from comments what I did:
...
div_loop:
* xor edx, edx ; clear out edx
div ebx ; divide the number by 10
* add dl, '0' ; convert from decimal to char
* mov [esi], dl ; store char in output buffer
inc esi ; move to next spot in output buffer
inc ecx ; keep track of how many chars are added
* test eax,eax ; is there anything left to divide into?
* jnz div_loop ; if so, continue the loop
* ; (jnz is same instruction as jne, but in this context I like "zero" more)
* mov [rlen], ecx ; store number of characters into variable
output_loop:
* ; esi already points beyond last digit, as product of div_loop (removed add)
* dec esi ; point to last/previous digit
mov al, [esi] ; move the char from the div_loop buffer into al
mov [edi], al ; move it into the first position of the converted output buffer
inc edi ; move to the next position of the converted output buffer
dec ecx ; decrement to move backwards through the output buffer
* jnz output_loop ; if it doesn't equal 0, continue loop
print:
mov eax, SYSCALL_WRITE ; write out string
mov ebx, STDOUT
mov ecx, dec_str
* mov edx, [rlen] ; read the number of digits from variable
int 080h
...

write number to file using NASM

How do I write a variable to a file using NASM?
For example, if I execute some mathematical operation - how do I write the result of the operation to write a file?
My file results have remained empty.
My code:
%include "io.inc"
section .bss
result db 2
section .data
filename db "Downloads/output.txt", 0
section .text
global CMAIN
CMAIN:
mov eax,5
add eax,17
mov [result],eax
PRINT_DEC 2,[result]
jmp write
write:
mov EAX, 8
mov EBX, filename
mov ECX, 0700
int 0x80
mov EBX, EAX
mov EAX, 4
mov ECX, [result]
int 0x80
mov EAX, 6
int 0x80
mov eax, 1
int 0x80
jmp exit
exit:
xor eax, eax
ret
You have to implement ito (integer to ascii) subsequently len for this manner. This code tested and works properly in Ubuntu.
section .bss
answer resb 64
section .data
filename db "./output.txt", 0
section .text
global main
main:
mov eax,5
add eax,44412
push eax ; Push the new calculated number onto the stack
call itoa
mov EAX, 8
mov EBX, filename
mov ECX, 0x0700
int 0x80
push answer
call len
mov EBX, EAX
mov EAX, 4
mov ECX, answer
movzx EDX, di ; move with extended zero edi. length of the string
int 0x80
mov EAX, 6
int 0x80
mov eax, 1
int 0x80
jmp exit
exit:
xor eax, eax
ret
itoa:
; Recursive function. This is going to convert the integer to the character.
push ebp ; Setup a new stack frame
mov ebp, esp
push eax ; Save the registers
push ebx
push ecx
push edx
mov eax, [ebp + 8] ; eax is going to contain the integer
mov ebx, dword 10 ; This is our "stop" value as well as our value to divide with
mov ecx, answer ; Put a pointer to answer into ecx
push ebx ; Push ebx on the field for our "stop" value
itoa_loop:
cmp eax, ebx ; Compare eax, and ebx
jl itoa_unroll ; Jump if eax is less than ebx (which is 10)
xor edx, edx ; Clear edx
div ebx ; Divide by ebx (10)
push edx ; Push the remainder onto the stack
jmp itoa_loop ; Jump back to the top of the loop
itoa_unroll:
add al, 0x30 ; Add 0x30 to the bottom part of eax to make it an ASCII char
mov [ecx], byte al ; Move the ASCII char into the memory references by ecx
inc ecx ; Increment ecx
pop eax ; Pop the next variable from the stack
cmp eax, ebx ; Compare if eax is ebx
jne itoa_unroll ; If they are not equal, we jump back to the unroll loop
; else we are done, and we execute the next few commands
mov [ecx], byte 0xa ; Add a newline character to the end of the character array
inc ecx ; Increment ecx
mov [ecx], byte 0 ; Add a null byte to ecx, so that when we pass it to our
; len function it will properly give us a length
pop edx ; Restore registers
pop ecx
pop ebx
pop eax
mov esp, ebp
pop ebp
ret
len:
; Returns the length of a string. The string has to be null terminated. Otherwise this function
; will fail miserably.
; Upon return. edi will contain the length of the string.
push ebp ; Save the previous stack pointer. We restore it on return
mov ebp, esp ; We setup a new stack frame
push eax ; Save registers we are going to use. edi returns the length of the string
push ecx
mov ecx, [ebp + 8] ; Move the pointer to eax; we want an offset of one, to jump over the return address
mov edi, 0 ; Set the counter to 0. We are going to increment this each loop
len_loop: ; Just a quick label to jump to
movzx eax, byte [ecx + edi] ; Move the character to eax.
movsx eax, al ; Move al to eax. al is part of eax.
inc di ; Increase di.
cmp eax, 0 ; Compare eax to 0.
jnz len_loop ; If it is not zero, we jump back to len_loop and repeat.
dec di ; Remove one from the count
pop ecx ; Restore registers
pop eax
mov esp, ebp ; Set esp back to what ebp used to be.
pop ebp ; Restore the stack frame
ret ; Return to caller

Resources