Combining C and Assembler

Honors Computer Systems Organization (Prof. Grishman)

Section and page numbers, unless otherwise stated, are to Paul Carter's PC Assembly Language

32-bit processors

The 80386 and later machines are upward-compatible 32-bit processors

Addressing

The 8086 already supported 1 MB of memory (sec. 1.2.6, p. 8) The 80386 introduced a 32-bit (4 GB) address space and 32-bit protected mode addressing (sec. 1.2.8, p. 9) In assembler, one must specify which segment generated code / data should be placed into

Linker and object modules

All operating systems provide a mechanism for combining the output of separate compilations or assemblies

NASM

The Dewar Assembler doesn't produce object files ... it only produces executable (.com) files.  So we need to move to a 'real' assembler.  NASM, the Netwide Assembler, is a freely-available, widely used x86 assembler.  Through its home page you can download both the code (for Windows or Linux) and extensive documentation.

Unfortunately, NASM syntax has some differences from DAS, MASM (the Microsoft assembler), and other assemblers.  Most of these are described in section 2.2 of the NASM manual. The main differences are:

If you have downloaded the Windows version of the assembler, nasmw, to a directory and created a file z.asm in that directory, you can assemble the file with
nasmw -f win32 z.asm
This will produce an object file z.obj.  (NASM has many other command-line options.)

Paul Carter provides some basic printing and debugging functions for use when running C and NASM together (section 1.3.6, p. 16).

Combining Visual C++ and NASM

In order to run a program consisting of a C main program and a function coded in assembly language, fun.asm, all residing in directory D:
  1. Create a project with the C program and the fun.asm file in directory D
  2. Put a copy of asm_io.inc in directory D [if using Carter's functions]
  3. If nasmw is in this directory, assemble fun.asm with nasmw -f win32 fun.asm;  this produces file fun.obj in the same directory
  4. Put a copy of asm_io.obj in that directory [if using Carter's functions]
  5. In Visual C++,

  6.    Select from menu Project / Project Settings
       Select link tab
       Select category 'input'
       Add asm_io.obj and fun.obj to the list of object/library modules
  7. Build project
  8. Execute project

Declaring external symbols

If you want your C program to be able to reference your assembly-coded function, you must declare the function name global in the assembly code, and put a '_' in front of its name (sec. 4.7.2, p. 78).  So if the function is referenced as fun() in the C program, the assembly code should begin
segment .text
        global _fun
_fun:   first instruction of function

Passing parameters and returning values

If your function returns an integer value, it is returned in EAX (sec. 4.7.5, p. 79).

The C calling convention assumes that EBX, ESI, EDI, EBP (and the stack --- ESP) are preserved across function calls (sec. 4.7.1, p. 77).  The PUSHA and POPA instructions (available since the 80186) can be used to save all 8 registers.

So a 'skeleton' for a function which keeps no variables in memory (no .data segment) would be

segment .text
        global  _fun
_fun:   enter 0,0
        pusha
        ... code for function ...
        popa
        mov   eax, returned value
        leave
        end

Parameters:  C calling convention

Local variables