nasm infinite loop with FPU - nasm

i'm trying to create a small nasm program which do this operation in floating point
while(input <= 10^5) do
begin
input = input * 10
i = i - 1
end
the equivilant program in nasm is as following
section .data
input: resd 1
n10: dd 0x41200000 ; 10
_start:
mov eax, 0x43480000 ; eax = 200
mov dword [input], eax ; input = eax = 200
mov edx, 0x49742400 ; 10^5
; %begin
mov ecx, 0 ; i = 0
jmp alpha
alpha:
cmp [input], edx ; input <= 10^5
jle _while
jmp log2
_while:
fld dword [input] ; input
fmul dword [n10] ; input * 10
fst dword [input] ; input = input
dec ecx ; i = i - 1
jmp alpha
the _while loop is iterating infinitely
ecx / i gards always the same value = 0 (it is sepposed to be 0) and doesn't decrement

This works for me (tested in DosBox):
org 0x100
bits 16
_start:
mov dword [input], __float32__(99.0)
mov edx, __float32__(10000.0)
mov ecx, 0 ; i = 0
jmp alpha
alpha:
cmp [input],edx ; input <= 10^5
jle _while
jmp log2
_while:
fld dword [input] ; input
fmul dword [n10] ; input * 10
fstp dword [input] ; input = input
inc ecx ; i = i - 1
jmp alpha
log2:
; print the value of cl
mov dl,cl
add dl,'0'
mov ah,2
int 21h
; Exit to DOS
mov ah,0x4c
int 21h
n10: dd 10.0
input: resd 1
Note the bits 16 which tells nasm that 16-bit operands are the default and that instructions using 32-bit operands should be prefixed. Without this your code would be seen as gibberish if you try to execute it in a real-mode environment.It's possible that you'll need to use bits 32 instead, depending on your target environment.
Also note the use of floating-point literals rather than hex values (you had a typo in your code where you compared against 10^6 instead of 10^5).

Related

Segmentation fault in my assembly program

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

Iterate over strings in assembly (NASM)

I am trying to count the length of the string argv[1] in NASM assembly language. I think I'm on the right track. I have moved the address of argv[1] to register eax and now I want to move through it byte by byte and compare to the null string terminator.
Everytime I run the code it segfaults on the null comparison. Am I not getting the memory indexing correct?
*Disclaimer: This is a small part of a large homework assignment.
segment .bss
N: resd 1 ;counter for size of argv[1]
segment .text
global asm_main
asm_main:
enter 0,0 ;setup
pusha ;save all registers
mov eax, dword [ebp+8] ;argc to eax
mov ebx, dword [ebp+12] ; address of argv to ebx
mov eax, dword [ebx+4] ; address of argv[1] to eax
mov [N], dword 0 ; N = 0
.loop:
add eax, [N] ; advance index by N
cmp eax, dword 0 ; check for end of string
je .endloop ; break out of the loop if we're done
add [N], dword 1 ; N++
jmp .loop ; loop back for next char
.endloop:
popa
mov eax, 0
leave
ret
After a few hints and the help of gdb, the loop now looks like this:
mov [N], dword 0 ; N = 0
.loop:
cmp [eax], byte 0 ; check for end of string
je .endloop
add eax, dword 1 ; advance index by 1 byte
add [N], dword 1 ; N++
jmp .loop
.endloop:
Using N to increment the index was silly. I needed to increment by 1.

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.

Tower of Hanoi in assembly x86 using arrays

Hi every one I am trying to do a tower of Hanoi in assembly x86 but I am trying to use arrays. So this code gets a number from user as a parameter in Linux, then error checks a bunch of stuff. So now I just want to make the algorithm which use the three arrays i made (start, end, temp) and output them step by step. If someone can help it would be greatly appreciated. `
%include "asm_io.inc"
segment .data ; where all the predefined variables are stored
aofr: db "Argument out of Range", 0 ; define aofr as a String "Argument out of Range"
ia: db "Incorrect Argument", 0 ; define ia as a String "Incorrect Argument"
tma: db "Too many Arguments", 0 ; define tma as a String "Too many Arguments"
hantowNumber dd 0 ; define hantowNumber this is what the size of the tower will be stored in
start: dd 0,0,0,0,0,0,0,0,9 ; this array is where all the rings start at
end: dd 0,0,0,0,0,0,0,0,9 ; this array is where all the rings end up at
temp: dd 0,0,0,0,0,0,0,0,9 ; this array is used to help move the rings
test: dd 0,0,0,0,0,0,0,0,9
; The next couple lines define the strings to show the pegs and what they look like
towerName: db " Tower 1 Tower 2 Tower 3 ", 10, 0
lastLineRow: db "XXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXX ", 10, 0
buffer: db " ", 0
fmt:db "%d",10,0
segment .bss ; where all the input variables are stored
segment .text
global asm_main ; run the main function
extern printf
asm_main:
enter 0,0 ; setup routine
pusha
mov edx, dword 0 ; set edx to zero this is where the hantowNumber is saved for now
mov ecx, dword[ebp+8] ; ecx has how many arguments are given
mov eax, dword[ebp+12] ; save the first argument in eax
add eax, 4 ; move the pointer to the main argument
mov ebx, dword[eax] ; save the number into ebx
push ebx ; reserve ebx
push ecx ; reserve ecx
cmp ecx, dword 2 ; compare if there are more the one argument given
jg TmA ; if more then one argument is given then jump Too many Argument (TmA)
mov ecx, 0 ; ecx = 0
movzx eax, byte[ebx+ecx] ; eax is the first character number from the inputed number
sub eax, 48 ; subtract 48 to get the actual number/letter/symbol
cmp eax, 10 ; check if eax is less then 10
jg IA ; if eax is greater then 10 then it is a letter or symbol
string_To_int: ; change String to int procedure
add edx, eax ; put the number in edx
inc ecx ; increase counter (ecx)
movzx eax, byte[ebx+ecx] ; move the next number in eax
cmp eax, 0 ; if eax = 0 then there are no more numbers
mov [hantowNumber], edx ; change hantowNumber to what ever is in edx
je rangeCheck ; go to rangeCheck to check if between 2-8
sub eax, 48 ; subtract 48 to get the actual number/letter/symbol
cmp eax, 10 ; check if eax is less then 10
jg IA ; if eax is greater then 10 then it is a letter or symbol
imul edx, 10 ; multiply edx by 10 so the next number can be added to the end
jmp string_To_int ; jump back to string_To_int if not done
rangeCheck: ; check the range of the number
cmp edx, dword 2 ; compare edx with 2
jl AofR ; if hantowNumber (edx) < 2 then go to Argument out of Range (AofR)
cmp edx, dword 8 ; compare edx with 8
jg AofR ; if hantowNumber (edx) > 8 then go to Argument out of Range (AofR)
mov ecx, [hantowNumber] ; move the number enterd by user in ecx
mov esi, 28 ; esi == 28 for stack pointer counter
setStart: ; set the first array the starting peg
mov [start+esi], ecx ; put ecx into the array
sub esi, 4 ; take one away from stack pointer conter
dec ecx ; take one away from ecx so the next number can go in to the array
cmp ecx, 0 ; compare ecx with 0
jne setStart ; if ecx != 0 then go to setStart loop
; This is the section where the algoritham should go for tower of hanoi
mov ecx, [hantowNumber]
towerAlgorithm:
cmp ecx, 0
jg Exit ; jump to Exit at the end of the program
dec ecx
IA:
mov eax, ia ; put the string in eax
push eax ; reserve eax
call print_string ; output the string that is in eax
call print_nl ; print a new line after the output
pop eax ; put eax back to normal
add esp, 4 ; takes 4 from stack
jmp Exit ; jump to Exit at the end of the program
AofR:
mov eax, aofr ; put the string in eax
push eax ; reserve eax
call print_string ; output the string that is in eax
call print_nl ; print a new line after the output
pop eax ; put eax back to normal
add esp, 4 ; takes 4 from stack
jmp Exit ; jump to Exit at the end of the program
TmA:
mov eax, tma ; put the string in eax
push eax ; reserve eax
call print_string ; output the string that is in eax
call print_nl ; print a new line after the output
pop eax ; put eax back to normal
add esp, 4 ; takes 4 from stack
jmp Exit ; jump to Exit at the end of the program
Exit: ; ends the program when it jumps to here
add esp, 9 ; takes 8 from stack
popa
mov eax, 0 ; return back to C
leave
ret
haha I'm doing the exact same assignment and stuck on the algorithm however though when running your code it seems to identify "too many arguments" even though only one argument is provided, consider this algorithm when dealing with arguments(don't forget ./ is considered the "first argument" since it is the zeroth argument provided):
enter 0,0
pusha
; address of 1st argument is on stack at address ebp+12
; address of 2nd arg = address of 1st arg + 4
mov eax, dword [ebp+12] ;eax = address of 1st arg
add eax, 4 ;eax = address of 2nd arg
mov ebx, dword [eax] ;ebx = 2nd arg, it is pointer to string
mov eax, 0 ;clear the register
mov al, [ebx] ;it moves only 1 byte
sub eax, '0' ;now eax contains the numeric value of the firstcharacter of string

Printing an Int (or Int to String)

I am looking for a way to print an integer in assembler (the compiler I am using is NASM on Linux), however, after doing some research, I have not been able to find a truly viable solution. I was able to find a description for a basic algorithm to serve this purpose, and based on that I developed this code:
global _start
section .bss
digit: resb 16
count: resb 16
i: resb 16
section .data
section .text
_start:
mov dword[i], 108eh ; i = 4238
mov dword[count], 1
L01:
mov eax, dword[i]
cdq
mov ecx, 0Ah
div ecx
mov dword[digit], edx
add dword[digit], 30h ; add 48 to digit to make it an ASCII char
call write_digit
inc dword[count]
mov eax, dword[i]
cdq
mov ecx, 0Ah
div ecx
mov dword[i], eax
cmp dword[i], 0Ah
jg L01
add dword[i], 48 ; add 48 to i to make it an ASCII char
mov eax, 4 ; system call #4 = sys_write
mov ebx, 1 ; file descriptor 1 = stdout
mov ecx, i ; store *address* of i into ecx
mov edx, 16 ; byte size of 16
int 80h
jmp exit
exit:
mov eax, 01h ; exit()
xor ebx, ebx ; errno
int 80h
write_digit:
mov eax, 4 ; system call #4 = sys_write
mov ebx, 1 ; file descriptor 1 = stdout
mov ecx, digit ; store *address* of digit into ecx
mov edx, 16 ; byte size of 16
int 80h
ret
C# version of what I want to achieve (for clarity):
static string int2string(int i)
{
Stack<char> stack = new Stack<char>();
string s = "";
do
{
stack.Push((char)((i % 10) + 48));
i = i / 10;
} while (i > 10);
stack.Push((char)(i + 48));
foreach (char c in stack)
{
s += c;
}
return s;
}
The issue is that it outputs the characters in reverse, so for 4238, the output is 8324. At first, I thought that I could use the x86 stack to solve this problem, push the digits in, and pop them out and print them at the end, however when I tried implementing that feature, it flopped and I could no longer get an output.
As a result, I am a little bit perplexed about how I can implement a stack in to this algorithm in order to accomplish my goal, aka printing an integer. I would also be interested in a simpler/better solution if one is available (as it's one of my first assembler programs).
One approach is to use recursion. In this case you divide the number by 10 (getting a quotient and a remainder) and then call yourself with the quotient as the number to display; and then display the digit corresponding to the remainder.
An example of this would be:
;Input
; eax = number to display
section .data
const10: dd 10
section .text
printNumber:
push eax
push edx
xor edx,edx ;edx:eax = number
div dword [const10] ;eax = quotient, edx = remainder
test eax,eax ;Is quotient zero?
je .l1 ; yes, don't display it
call printNumber ;Display the quotient
.l1:
lea eax,[edx+'0']
call printCharacter ;Display the remainder
pop edx
pop eax
ret
Another approach is to avoid recursion by changing the divisor. An example of this would be:
;Input
; eax = number to display
section .data
divisorTable:
dd 1000000000
dd 100000000
dd 10000000
dd 1000000
dd 100000
dd 10000
dd 1000
dd 100
dd 10
dd 1
dd 0
section .text
printNumber:
push eax
push ebx
push edx
mov ebx,divisorTable
.nextDigit:
xor edx,edx ;edx:eax = number
div dword [ebx] ;eax = quotient, edx = remainder
add eax,'0'
call printCharacter ;Display the quotient
mov eax,edx ;eax = remainder
add ebx,4 ;ebx = address of next divisor
cmp dword [ebx],0 ;Have all divisors been done?
jne .nextDigit
pop edx
pop ebx
pop eax
ret
This example doesn't suppress leading zeros, but that would be easy to add.
I think that maybe implementing a stack is not the best way to do this (and I really think you could figure out how to do that, saying as how pop is just a mov and a decrement of sp, so you can really set up a stack anywhere you like by just allocating memory for it and setting one of your registers as your new 'stack pointer').
I think this code could be made clearer and more modular if you actually allocated memory for a c-style null delimited string, then create a function to convert the int to string, by the same algorithm you use, then pass the result to another function capable of printing those strings. It will avoid some of the spaghetti code syndrome you are suffering from, and fix your problem to boot. If you want me to demonstrate, just ask, but if you wrote the thing above, I think you can figure out how with the more split up process.
; Input
; EAX = pointer to the int to convert
; EDI = address of the result
; Output:
; None
int_to_string:
xor ebx, ebx ; clear the ebx, I will use as counter for stack pushes
.push_chars:
xor edx, edx ; clear edx
mov ecx, 10 ; ecx is divisor, devide by 10
div ecx ; devide edx by ecx, result in eax remainder in edx
add edx, 0x30 ; add 0x30 to edx convert int => ascii
push edx ; push result to stack
inc ebx ; increment my stack push counter
test eax, eax ; is eax 0?
jnz .push_chars ; if eax not 0 repeat
.pop_chars:
pop eax ; pop result from stack into eax
stosb ; store contents of eax in at the address of num which is in EDI
dec ebx ; decrement my stack push counter
cmp ebx, 0 ; check if stack push counter is 0
jg .pop_chars ; not 0 repeat
mov eax, 0x0a
stosb ; add line feed
ret ; return to main
; eax = number to stringify/output
; edi = location of buffer
intToString:
push edx
push ecx
push edi
push ebp
mov ebp, esp
mov ecx, 10
.pushDigits:
xor edx, edx ; zero-extend eax
div ecx ; divide by 10; now edx = next digit
add edx, 30h ; decimal value + 30h => ascii digit
push edx ; push the whole dword, cause that's how x86 rolls
test eax, eax ; leading zeros suck
jnz .pushDigits
.popDigits:
pop eax
stosb ; don't write the whole dword, just the low byte
cmp esp, ebp ; if esp==ebp, we've popped all the digits
jne .popDigits
xor eax, eax ; add trailing nul
stosb
mov eax, edi
pop ebp
pop edi
pop ecx
pop edx
sub eax, edi ; return number of bytes written
ret

Resources