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!
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.