Linux Nasm Assembly Print All Numbers from Zero to 100

NASM - how to print a number using int 080h?

If you want to print a number in ASCII string, you have to convert the number to a string.

This will work for nonnegative number:

    ; dummy to tell the program the end of data
push -1
; set the number to convert
xor eax, eax
mov al, [lett]
; convert the number to string (sequence of character)
convert_loop:
xor edx, edx
mov ebx, 10
div ebx
add edx, '0'
push edx
test eax, eax
jnz convert_loop
; print the converted string
print_loop:
cmp dword [esp], 0
jl print_loop_end ; break when -1 is found
mov eax, 4
mov ebx, 1
mov ecx, esp
mov edx, 1
int 080h
pop eax ; move on next character
jmp print_loop
print_loop_end:
pop eax ; clean -1

UPDATE: Another version without using push / pop instruction:

section .bss
; 32-bit unsigned integer won't be longer than 10 digits in decimal
strtonum_convert_buffer resb 12

section .text

; dummy to tell the program the end of data
mov ecx, strtonum_convert_buffer
mov byte [ecx], 0
; set the number to convert
xor eax, eax
mov al, [lett]
; convert the number to string (sequence of character)
convert_loop:
xor edx, edx
mov ebx, 10
div ebx
add edx, '0'
inc ecx
mov [ecx], dl
test eax, eax
jnz convert_loop
; print the converted string
print_loop:
cmp byte [ecx], 0
je print_loop_end ; break when 0 is found
mov eax, 4
mov ebx, 1
; there is already the pointer to the character on ecx
mov edx, 1
int 080h
dec ecx ; move on next character
jmp print_loop
print_loop_end:

linux nasm assembly find number of digits held in variable

For numbers in the range 0..100, I'd just compare at the boundaries, with pseudo-assembler like:

    mov ax, [counter]

mov cx, 3 ; default length
cmp ax, 100 ; >= 100, use 3
bge done

dec cx ; set length to 2
cmp val, 10 ; >= 10, use 2
bge done

dec cx ; set length to 1

done:
; cx now holds the digit count.

That will actually handle up to 999 but you could also add more condition checks before the 100 one if you wanted to expand the range.

linux nasm assembly dwtoa

Well, ya gotta write it... or borrow one somebody's already written. The latter is easier (the C library has functions like this, and it's easy to call from asm), but the former is more "fun". (if you like that kind of thing - hey, some people do crossword puzzles)

The div instruction is very slow. There's a better way to do it based on multiplying by the reciprocal and "back multiplying". It's quite complicated. We'll wait for div. :)

div ebx

If we had arranged to have our number, say 1234, in eax and 10 in ebx, we would now have 123 in eax and 4 in edx (ebx remains unchanged). Actually, we'd want to have 0 in edx before the div...

xor edx, edx

div ebx

As you know, we can convert the number 4 to the character '4' by adding the character '0' (or 48 decimal or 30h). Now we've got something we can print! But we're not quite ready to print it yet - we're getting the digits backwards. There are several ways to deal with this. I think the simplest is to push 'em on the stack and pop 'em off in the correct order. Another way is to go ahead and put 'em in the buffer backwards and do a "string reverse" at the end. Another way is to start at the "end" of the buffer and work toward the front (decrement your index into the buffer after each character instead of incrementing it). This can mean that you're not quite at the beginning of the buffer when you run out of digits. We can use that to our advantage - right justified numbers look good if you're going to print 'em in a column. You could fill with leading zeros, too (the character '0', not the number 0) if you think that looks good (I don't).

In any case, we've got '4' stashed away safely. Loop back and div again (making edx zero first!). Now we've got 12 in eax and 3 in edx. Do something with the 3 and go back to div again. 1 in eax and 2 in edx. Again, and eax is zero (edx is 1) - at that point we're done! We can skip the last div if we compare eax to 9 - if it's less, we can get our final (first to be printed) digit from al instead of dl. Simpler to do it the same way every time...

; mov eax, the number
; mov edi, the buffer (at least resb 10, please)
; call dwtoa
; mov edx, eax ; count
; mov ecx, buffer
; print it

dwtoa:
xor ecx, ecx ; for a counter
mov ebx, 10
pushloop:
xor edx, edx ; or mov edx, 0
div ebx
add edx, '0'
push edx
inc ecx ; count it
test eax, eax ; or cmp eax, 0
jnz pushloop
mov eax, ecx ; we'll return the count in eax
poploop:
pop edx
mov [edi], dl
inc edi
loop poploop
ret

That's off the top of my head (not cut-and-pasted), and may have errors. It's pretty sloppy - trashes registers that C would like preserved - doesn't return a zero-terminated string as C would like... but we're not using C so we don't care! :)

Feel free to improve it to your taste, or try a different method.

Unless you've got one, you'll want a "atoi" (or "atodw" to use the same naming convention), to convert the text the user enters to a number. Same idea, but we subtract '0' from the character, multiply the "result so far" by ten, and add in the new digit... until done.

;-------------------
; atoi - converts string to (unsigned!) integer
; expects: buffer in edx
; returns: number in eax
atoi:
xor eax, eax ; clear "result"
.top:
movzx ecx, byte [edx]
inc edx

cmp ecx, byte 0
jz .done
cmp ecx, byte 10
jz .done

cmp ecx, byte '0'
jb .invalid
cmp ecx, byte '9'
ja .invalid

; we have a valid character - multiply
; result-so-far by 10, subtract '0'
; from the character to convert it to
; a number, and add it to result.

lea eax, [eax + eax * 4]
lea eax, [eax * 2 + ecx - '0']

jmp short .top
.invalid:
stc
.done:
ret
;--------------


That one's cut-and-pasted so "should" work. It, too, could be improved. Uses an "interesting" way to multiply by ten and add in the new character converted to a number. At this point, the "work" of your program consists of:
add eax, 1
Should give you something to work with, anyway. Have fun! :)



Related Topics



Leave a reply



Submit