Print integer to console in x86 assembly
Asked Answered
M

5

9

When I add two values in 16 bit assembly, what is the best way to print the result to console?

At the moment I have this code:

;;---CODE START---;;
mov ax, 1   ;put 1 into ax
add ax, 2   ; add 2 to ax current value
mov ah,2  ; 2 is the function number of output char in the DOS Services.
mov dl, ax ; DL takes the value.
int 21h    ; calls DOS Services

mov ah,4Ch   ; 4Ch is the function number for exit program in DOS Services.
int 21h      ; function 4Ch doesn't care about anything in the registers.
;;---CODE END---;;

I think that dl value should be in ASCII code, but I'm not sure how to convert ax value after addition into ASCII.

Mousseline answered 22/11, 2010 at 10:50 Comment(1)
Displaying numbers with DOS has more complicated answers that include dealing with 32-bit integers using only 16-bit registers. Sep's new answer here is just the simplest form that's good for 1-digit or 16-bit integers, storing into the end of a buffer.Cerecloth
C
11

You basically want to divide by 10, print the remainder (one digit), and then repeat with the quotient.

    ; assume number is in eax
    mov ecx, 10

loophere:
    mov edx, 0
    div ecx

    ; now eax <-- eax/10
    ;     edx <-- eax % 10

    ; print edx
    ; this is one digit, which we have to convert to ASCII
    ; the print routine uses edx and eax, so let's push eax
    ; onto the stack. we clear edx at the beginning of the
    ; loop anyway, so we don't care if we much around with it

    push eax

    ; convert dl to ascii
    add dl, '0'

    mov ah,2  ; 2 is the function number of output char in the DOS Services.
    int 21h    ; calls DOS Services

    ; now restore eax
    pop eax

    ; if eax is zero, we can quit

    cmp eax, 0
    jnz loophere

As a side note, you have a bug in your code right here:

mov ax, 1   ;put 1 into ax
add ax, 2   ; add 2 to ax current value
mov ah,2  ; 2 is the function number of output char in the DOS Services.
mov dl, ax ; DL takes the value.

You put 2 in ah, and then you put ax in dl. You're basically junking ax before printing it.

You also have a size mismatch since dl is 8 bits wide and ax is 16 bits wide.

What you should do is flip the last two lines and fix the size mismatch:

mov ax, 1   ;put 1 into ax
add ax, 2   ; add 2 to ax current value

mov dl, al ; DL takes the value.
mov ah,2  ; 2 is the function number of output char in the DOS Services.
Choreography answered 22/11, 2010 at 13:9 Comment(6)
I could be wrong, but I believe your code prints the numbers in reverse. E.g.: 123 prints 321Oshinski
This prints in reverse order, least-significant digit first. Store into a buffer like *p-- = digit, starting at the end and working backwards, to make a string. Or see Displaying numbers with DOSCerecloth
@PeterCordes: I think that this is just what sidon wrote in his answer below: https://mcmap.net/q/14179/-print-integer-to-console-in-x86-assemblyChoreography
Yes. I think I noticed the same thing after commenting here and moving on to look at the other answers. This one is accepted and significantly upvoted so it should still get fixed, so I decided to leave that comment in place.Cerecloth
@PeterCordes, this answer appears to use 32-bit registers, whereas the question is tagged 16-bit, what do you think?Pietism
@ErikEidt: 32-bit registers are accessible in 16-bit modes on a 386 or later. The question doesn't specify 8086, and some future readers might not have the same restriction. So that's not a showstopper, unlike printing numbers backwards which is why I downvoted it.Cerecloth
A
7

Just fixing the order of @Nathan Fellman 's code

PrintNumber proc
    mov cx, 0
    mov bx, 10
@@loophere:
    mov dx, 0
    div bx                          ;divide by ten

    ; now ax <-- ax/10
    ;     dx <-- ax % 10

    ; print dx
    ; this is one digit, which we have to convert to ASCII
    ; the print routine uses dx and ax, so let's push ax
    ; onto the stack. we clear dx at the beginning of the
    ; loop anyway, so we don't care if we much around with it

    push ax
    add dl, '0'                     ;convert dl to ascii

    pop ax                          ;restore ax
    push dx                         ;digits are in reversed order, must use stack
    inc cx                          ;remember how many digits we pushed to stack
    cmp ax, 0                       ;if ax is zero, we can quit
jnz @@loophere

    ;cx is already set
    mov ah, 2                       ;2 is the function number of output char in the DOS Services.
@@loophere2:
    pop dx                          ;restore digits from last to first
    int 21h                         ;calls DOS Services
    loop @@loophere2

    ret
PrintNumber endp
Aggarwal answered 27/4, 2011 at 23:57 Comment(1)
The push ax / pop ax in the first loop is pointless, and distracting when combined with pushing digits in a loop. add dl, '0' doesn't affect AX. (You moved the int 21h call out of the first loop vs. Nathan's version but didn't take out the save/restore of AX). Two loops with digits on the stack is one way to do this, but storing backwards into a buffer is often good like Sep's answer shows.Cerecloth
F
4

The basic algorithm is:

divide number x by 10, giving quotient q and remainder r
emit r
if q is not zero, set x = q and repeat 

Note that this will yield the digits in the inverse order, so you are probably going to want to replace the "emit" step with something that stores each digit, so that you can later iterate in reverse over the stored digits.

Also, note that to convert a binary number between 0 and 9 (decimal) to ascii, just add the ascii code for '0' (which is 48) to the number.

Faraday answered 22/11, 2010 at 10:56 Comment(0)
B
2
mov dl, ax

This won't work as dl and ax have different bit sizes. What you want to do is create a loop in which you divide the 16 bit value by 10, remember the rest on the stack, and then continue the loop with the integer division result. When you reach a result of 0, clean up the stack digit by digit, adding 48 to the digits to turn them into ASCII digits, then print them.

Bonnes answered 22/11, 2010 at 10:57 Comment(0)
C
1

The accepted answer is wrong in that it displays the result reversed! That's why I write this answer today. My answer demonstrates the better technique of outputting the result in one gulp (via DOS.function 09h).

Your code

mov ah,2  ; 2 is the function number of output char in the DOS Services.
mov dl, ax ; DL takes the value.
int 21h    ; calls DOS Services

Always load the DOS function number in the instruction right before the int 21h instruction.
Always check your sizes: you cannot move from a 16-bit register to an 8-bit register.

I think that dl value should be in ASCII code, but I'm not sure how to convert ax value after addition into ASCII.

Your numbers allow the use of the DOS.PrintCharacter function. To convert the value 3 into the character "3", you just need to add 48 so as to have an ASCII code in DL.

When I add two values in 16-bit assembly, what is the best way to print the result to console?

Your example for a single-digit result

; Sum
  mov  al, 1
  add  al, 2

; Print
  add  al, '0'
  mov  dl, al
  mov  ah, 02h        ; DOS.PrintCharacter
  int  21h

; Exit
  mov  ax, 4C00h      ; DOS.TerminateWithExitcode
  int  21h

Solution for a multi-digit result

; Sum
  mov  ax, 7346
  add  ax, 913

; Print
  mov  bx, Buffer + 5 ; Position of the mandatory '$' terminator
  mov  cx, 10
More:
  xor  dx, dx
  div  cx
  dec  bx
  add  dl, '0'        ; Convert to ASCII
  mov  [bx], dl
  test ax, ax
  jnz  More
  mov  dx, bx
  mov  ah, 09h        ; DOS.PrintString
  int  21h

; Exit
  mov  ax, 4C00h      ; DOS.TerminateWithExitcode
  int  21h

; Storage
Buffer: db '.....$'  

NASM use mov bx, Buffer + 5
MASM use mov bx, OFFSET Buffer + 5

Christine answered 31/12, 2022 at 1:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.