vedran
vedran

Reputation: 1118

How to call printf from a C function that I call in the MASM assembler?

I have a C file called C.c and for now in that file I have one function that compares two numbers and saves the greater number to the register eax.

I need help with creating the second function that will call printf inside and print some text. How to add support for printf in C that I can call from MASM assembler?

My masm file:

    TITLE MASM Template (main.asm)
.386
.model FLAT
.stack 4096

WriteString PROTO stdcall
ReadChar PROTO stdcall
Crlf PROTO stdcall
ExitProcess PROTO stdcall:DWORD
greater PROTO C :DWORD, :DWORD


.data
PorukaZaIspis db "Poruka za ispis", 0

.code
_main PROC

push 8
push 3
call greater


call Crlf
mov edx, OFFSET PorukaZaIspis
call WriteString
call ReadChar
Invoke ExitProcess, 0
_main ENDP
END _main

END

My C.c file:

int greater(int a, int b) {
if ( a > b)
return a;
else 
return b;
}

Upvotes: 1

Views: 7061

Answers (2)

Adrian Alan Bennett
Adrian Alan Bennett

Reputation: 11

I just worked this one out starting with the tutorial/console/demo1 that came with MASM for x86. It says to compile using the console option which is in the drop down 'Project' menu. It works good, and I think it near the minimum to get started:

.486                                    ; create 32 bit code
.model flat, stdcall                    ; 32 bit memory model
option casemap :none                    ; case sensitive


include \masm32\include\windows.inc     ; always first
include \masm32\macros\macros.asm       ; MASM support macros

include \masm32\include\masm32.inc
include \masm32\include\gdi32.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
include \masm32\include\msvcrt.inc

includelib \masm32\lib\masm32.lib
includelib \masm32\lib\gdi32.lib
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\msvcrt.lib

.code 

start:  

print chr$("Hey, this actually works.",13,10)

mov         eax, 0
mov         ebx, 0
printf("%d\t %d\t\n", eax, ebx);

mov         eax, 100
mov         ebx, 100
printf("%d\t %d\t\n", eax, ebx);

mov         eax, 100
mov         ebx, 100
add eax, ebx
printf("%d\t %d\t\n", eax, ebx)

exit
end start

I am using printf here to print from the registers. I found out from this that printf itself effects the registers EAX, ECX, and EDX, as the add will not give a proper result if eax isn't reloaded before the printf statement passing it. (You can also see this when single-stepping in a debugger and watching register values change).

This will print from the eax and ebx registers, but you can print whatever you want following the C syntax. The important thing is to include these two lines:

include \masm32\include\msvcrt.inc
includelib \masm32\lib\msvcrt.lib

You need to include both of them; the .inc defines the printf() C-style macro, so that syntax will not work without it.

rkhb's answer on this question shows how you can declare printf manually and call it with standard MASM INVOKE like any other function, or even pushing args yourself and doing a call printf without MASM doing any magic. In that case you just need the .lib.

Upvotes: 1

rkhb
rkhb

Reputation: 14409

You need to link to an appropriate library (e.g. msvcrt.lib) and you need to know the exported names of the functions. To detect the names I use dumbinGUI.

The calling conventions of C-functions is called "cdecl". The arguments are pushed onto the stack and the stack have to be adjusted after the call. You can let MASM do that job by using INVOKE, if you declare the function as PROTO C.

Example:

test.asm:

.686
.model FLAT

INCLUDELIB msvcrt.lib
printf PROTO C, :VARARG
exit PROTO C, :DWORD

; Functions in C.c:
greater PROTO C :DWORD, :DWORD          ; Declaration of greater (int,int)
hello PROTO C                           ; Declaration of hello (void)

.data
fmt db "%s %u", 10, 0                   ; '10': printf of msvcrt.dll doesn't accept "\n"
PorukaZaIspis db "Message from ASM: ", 0

.code
_main PROC
    invoke greater, 8, 3
    call output                         ; "Message from ASM ..."
    invoke hello                        ; "Message from C ..."
    invoke exit, 0
_main ENDP

output PROC                             ; ARG: EAX
    invoke printf, OFFSET fmt, OFFSET PorukaZaIspis, eax
    ret
output ENDP

END _main

Let's add an output function to the C file:

C.c:

#include <stdio.h>

void hello ()
{
    puts ("Message from C: hello");
}

int greater(int a, int b)
{
    if ( a > b)
        return a;
    else
        return b;
}

Play with following batch file:

@ECHO OFF
SET VS_PATH=<Full\Path\to\Visual Studio\e.g.\C:\Program Files\Microsoft Visual Studio 10.0>

SET PATH=%VS_PATH%\VC\bin
SET LIB=%VS_PATH%\VC\lib
SET INCLUDE=%VS_PATH%\VC\include

SET CALLER=test.asm
SET CALLEE=C.c
SET TARGET=test.exe

echo cl_first
del %TARGET%
call :cl_first %CALLER% %CALLEE% %TARGET%
if exist %TARGET% %TARGET%

echo.
echo ml_first
del %TARGET%
call :ml_first %CALLER% %CALLEE% %TARGET%
if exist %TARGET% %TARGET%

goto :eof

:cl_first
cl /nologo /c %2
ml /nologo /Fe%3 %1 %~n2.obj /link /nologo
goto :eof

:ml_first
ml /nologo /c %1
cl /nologo /Fe%3 %~n1.obj %~n2.c /link /nologo
goto :eof

Upvotes: 2

Related Questions