Ben I.
Ben I.

Reputation: 1082

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!

Upvotes: 1

Views: 7590

Answers (1)

rkhb
rkhb

Reputation: 14409

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.

Upvotes: 7

Related Questions