I am writing a program to print out all the numbers from zero to 100. The only reason I am doing this is to test out printing out multiple digit numbers.
The problem th
You need to convert the number to an ASCII digit(s) in order to print to terminal. Now I won't give you my dwtoa, that will take the fun out of learning, but you could do something like this:
sys_exit equ 1
sys_write equ 4
stdout equ 1
SECTION .bss
lpBuffer resb 4
SECTION .text
GLOBAL _start
_start:
xor esi, esi
.NextNum:
call PrintNum
inc esi
cmp esi, 100
jna .NextNum
.Exit:
mov eax, sys_exit
xor ebx, ebx
int 80h
;~ #####################################################################
PrintNum:
push lpBuffer
push esi
call dwtoa
mov edi, lpBuffer
call GetStrlen
inc edx
mov ecx, lpBuffer
mov eax, sys_write
mov ebx, stdout
int 80H
ret
;~ #####################################################################
GetStrlen:
push ebx
xor ecx, ecx
not ecx
xor eax, eax
cld
repne scasb
mov byte [edi - 1], 10
not ecx
pop ebx
lea edx, [ecx - 1]
ret
Notice, I use things like sys_exit, sys_write, stdout, instead of hard coded numbers. Makes code a bit more self documenting.
This is my function to print the digits on stdout. It's in AT&T sorry ;)
movl <your decimal here>, %eax
xor %ecx, %ecx # the counter
movl $10, %ebx
loop:
xor %edx, %edx
div %ebx # isolate the last digit, remainder in edx
add $48, %dx # '0' is 48 in ascii, result is the ascii equivalent
shl $8, %dx # move the ascii byte to %dh
pushw %dx # puch ascii code on the stack
inc %esp # point to the ascii byte! (discard %dl)
inc %ecx # count the digits
cmp $0, %eax
jnz loop
movl $4, %eax # write()
movl $1, %ebx # stdout
movl %ecx, %edx # now edx holds the number of digits
movl %esp, %ecx # load the address of string array
int $0x80 # the string array is on top of the stack
Cheers!
EDIT: it's not an "error" per se, but just misdirection for the casual reader to access a counter and strlen as one byte variables and in other places compare the contents to 32-bit variables...
add BYTE[ecx], NUL
this perhaps adds NUL
terminator to ecx
, but I suppose it should append the terminator.
That could happen at place [ecx+1]
.
Anyway the handling of the variables and pointers is very unconventional in your code...
First: the kernel functions that 'output' stuff, assume that ecx contains the address of a string. There isn't a string allocated anywhere. If the string would just fit into the eight bytes reserved to counter at counter resb 8
, and the counter would contain characters: '1'
,'3'
,'\0'
then the approach would work. And this reveals the second thing: printf deals with strings, which encode single digits 0-9 to values 48-57. Space e.g. in this ASCII system encodes to 32 (decimal) while \NUL is ascii zero.
So, what is needed:
option 1
Initialize your counter
to a string
counter db '0','0','0','0','0','0','0','1'
length dq 1
Ascii zero is not needed to terminate the string, because as I understood, it's given to the print function
Then one can give the real pointer to the string as
lea ecx, counter // get the address of counter string
add ecx, 7 // this is the last character
Also one can increase the counter as a string one digit at a time:
loop:
mov al,[ecx] // assuming ecx still points to last character
inc al
mov [ecx],al
cmp al, '9'
jle string ok
mov al, '0'
mov [ecx],al
dec ecx
jmp loop
ok: // here the counter has been increased correctly
option 2
Increase the counter as a 32-bit integer Convert the integer to string one digit at a time with the following algorithm:
digits = 0;
string_ptr = &my_string[32]; // move barely outside the string
do {
last_digit = a % 10 + '0'; // calculate the last digit and convert to ASCII
a = a / 10;
*--string_ptr = last_digit; // write the last digit
digits++; // count the number of digits
} while (a);
// because we predecrement string_ptr, that value also contains the exact
// start of the first character in the printable string. And digits contains the length.
To produce some good looking result, one has to still add line feeds. That can be handled separately or just appended to the original string -- and ensure they are never written over, so they can be used in all cases.