Search code examples
assemblysyntaxmasm32

Can't figure out how to print a number in MASM32


I hate asking such basic questions. It makes it look like I am being lazy! But I have spent hours looking over documentation, and for whatever reason, I can't get my head spun around straight on this small point.

I want to print the character "4" to the screen. I can do it as a string, but not from an ascii value.

Here is the working code:

include c:\masm32\include\masm32rt.inc
.data
    num4 db "4", 10,0
.code
start:
    invoke StdOut, addr num4
    inkey
    invoke ExitProcess, 0
end start

I just wanted to take a baby step from there and print ascii character 52 (which is "4"). Here's my best attempt so far:

include c:\masm32\include\masm32rt.inc
.data
.code
start:
    myvar db 52
    invoke StdOut, myvar
    inkey
    invoke ExitProcess, 0
end start

It assembles and links without trouble, but then crashes when I run it. I know that it doesn't have the 0 character at the end, but invoke StdOut, myvar,0 has too many arguments for StdOut.

My eventual goal is to be able to print a multiple digit number, as described by Alexey Frunze here:

x86 assembly (masm32) - how to split multi-digit data into individual characters

But since I am having so much trouble with syntax, I am taking baby steps. I found this, but it doesn't explain how to do the add 48 part syntactically:

x86 assembly - how to show the integer 2, not the second ASCII character

Please help me get past these opening hurdles, and thank you!


Solution

  • First, myvar db 52 is on the wrong place. When the program starts, the computer comes to db 52 and treats that as an instruction. Second, the 0 value (don't say character) is not an argument for StdOut and has to be at the end of the data. StdOut needs as argument a pointer to a zero-terminated string. You cannot give it a direct value, the function would take that as pointer nevertheless. BTW: Consider that StdOut is a function of MASM32 and not a function of the Windows kernel. Your program should look like:

    include c:\masm32\include\masm32rt.inc
    .data
        myvar db 52, 0
    .code
    start:
        invoke StdOut, ADDR myvar
        inkey
        invoke ExitProcess, 0
    end start
    

    You have first to build a string before outputting it with 'StdOut'. If you don't have a string but a number, you have to convert it to a string (keywords for Google: "assembly convert integer to ascii"). The trick is to repeatedly divide the number by 10 and store the remainder. Another trick is to use a MASM32-macro.

    INCLUDELIB C:\masm32\lib\masm32.lib
    INCLUDE C:\masm32\include\masm32rt.inc
    
    .DATA
        decimalstr db 16 DUP (0)
        myvar db 52
    
    .CODE
    
    start PROC
    
        movzx eax, myvar         ; Load an 8-bit-byte into a 32-bit-register
        lea edi, decimalstr      ; Load the address of decimalstr
        call EAX_to_DEC
        invoke StdOut, addr decimalstr
    
        movzx eax, myvar
        printf ("\nAnd the lazy MASM32 way: %u\n",eax)
    
        invoke ExitProcess, 0
    start ENDP
    
    EAX_to_DEC PROC             ; ARG: EDI pointer to string buffer
        mov ebx, 10             ; Divisor = 10
        xor ecx, ecx            ; ECX=0 (digit counter)
      @@:                       ; First Loop: store the remainders
        xor edx, edx
        div ebx                 ; EDX:EAX / EBX = EAX remainder EDX
        push dx                 ; push the digit in DL (LIFO)
        add cl,1                ; = inc cl (digit counter)
        or  eax, eax            ; AX == 0?
        jnz @B                  ; no: once more (jump to the first @@ above)
      @@:                       ; Second loop: load the remainders in reversed order
        pop ax                  ; get back pushed digits
        or al, 00110000b        ; to ASCII
        stosb                   ; Store AL to [EDI] (EDI is a pointer to a buffer)
        loop @B                 ; until there are no digits left
        mov byte ptr [edi], 0   ; ASCIIZ terminator (0)
        ret                     ; RET: EDI pointer to ASCIIZ-string
    EAX_to_DEC ENDP
    
    END start
    

    Also take a look here.