Chapter 12
High-Level Language
Interface

Modified
© Ray Wisman

Download

C++ and Assembler project

 

12.3    Linking to C/C++ in Protected Mode

C++ and assembler routines can be used interchangeably, one calling the other, as long as the interfacing protocols continue to be followed.

Caller

  1. Save any critical registers.
  2. Push actual parameters onto stack, right to left order.
  3. Call function.
  4. Remove parameters from stack.
  5. Restore any critical registers.

Callee

  1. Save eBp register.
  2. Access parameters.
  3. Restore eBp and eSp.
  4. Return.

12.3.2    Calling C and C++ Functions

C/C++ to Assembler

To call an Assembler function from C/C++:

Define the prototype as extern in the C/C++ program:

extern "C" int addem(int p1, int p2, int p3);

Call Assembler function from C/C++:

int total = addem( 10, 15, 25 );

In Assembler, must define memory model as C, and function name as public:

.586
.model flat, C

.code
addem proc public

 

Below is a simple example where C++ calls an assembler function and prints the result.

C++
#include <iostream>
using namespace std;

int main()  {

   int total = addem( 10, 15, 25 );

   cout << "Total = " << total << endl;

   return 0;
}

int addem(int p1, int p2, int p3) {

   return p1 + p2 + p3;

}

 

C++ and Assembler

#include <iostream>
using
namespace std;

extern "C" int addem(int p1, int p2, int p3);

int main()  {

   int total = addem( 10, 15, 25 );

   cout << "Total = " << total;

   return 0;
}

.586
.model flat, C

.code
addem proc public, p1:DWORD, p2:DWORD, p3:DWORD
    mov    eax, p1    ; first argument
    add    eax, p2    ; second argument
    add    eax, p3    ; third argument
    ret                   
addem endp
end

Question 1 - Give the assembler and C++ for double written in Assembler:

C++
#include <iostream>
using
namespace std;

void main()  {
   int total = double( 10 );
   cout <<
total;
}

int double(int x) {
   return x*2;
}

Question 2 - Give the assembler and C++ for double written in Assembler:

C++
#include <iostream>
using namespace std;

void main()  {
   int x = 10;
   cout << double( x );
}

int double(int &x) {
   return x*2;
}

Visual Studio

The Irvine text has a Web site with Visual Studio projects, the one above can be downloaded.

 

Assembler to C/C++

C/C++ function that can be called from Assembler:

In C/C++, specify as extern:

extern "C" print( int x, int y) {

  cout << x << " " << y << "\n";

}

In Assembler, specify as proto near C:

print proto near C, x : dword, y : dword

In Assembler, invoke:

invoke print, p1, p2
C++ and Assembler
#include <iostream>
using namespace std;

extern "C" int printASM(int p1, int p2);

void main() {
  printASM( 10, 15); 
}

extern "C" void print( int x, int y) {
  cout << x << " " << y;
}
.586
.model flat, C

print	proto near C, x : dword, y : dword

.code

printASM proc public, p1:DWORD, p2:DWORD
    invoke	print, p1, p2
    ret                   
printASM endp
end

Question 3 - Give the assembler and C++ for double written in Assembler:

C++
#include <iostream>
using namespace std;

void main()  {
   double(10);
}

void print(int x){
   cout << x;

void double(int x) {
   print( x*2 );
}

 

Note

Alternatively to invoke, actual parameters can be pushed onto the stack manually, right-to-left as in:

.586
.model flat, C

print	proto near C, x : dword, y : dword

.code

printASM proc public, p1:DWORD, p2:DWORD
    push	p2
    push	p1
    call	print
    add	esp, 8
    ret                   
printASM endp
end

 

BinarySearch example

Note: eBp is restored automatically prior to ret instruction. 

C++ and Assembler
 .586
.model flat, C

.code							; int BinarySearch (int A[], int key, int lo, int hi) {

BinarySearch proc	public, A:ptr dword, key:dword, lo:dword, hi:dword		
  mov	eAx, lo
  .IF	eAx > hi						; if (lo > hi) return -1;
	mov	eAx, -1
	ret
  .ENDIF
  mov	eAx, lo						; int mid=(lo+hi)/2;
  add	eAx, hi
  mov	eCx, 2
  mov	eDx, 0
  div	eCx
  mov	eBx, A
  mov	eSi, key
  .IF	eSi == [eBx+eAx*4]				; if (key == A[mid]) return mid;
	ret
  .ENDIF
  .IF	eSi > [eBx+eAx*4]					; if (key > A[mid]) 
	inc    eAx
	invoke BinarySearch, A, key, eAx, hi	;         return BinarySearch(A, key, mid+1, hi);
  .ELSE
	dec	 eAx
	invoke BinarySearch, A, key, lo, eAx	; else    return BinarySearch(A, key, lo, mid-1);
  .ENDIF
  ret
BinarySearch endp
end
#include <iostream>
using namespace std;

extern "C" int BinarySearch (int A[], int key, int lo, int hi);

int X[] = {2, 4, 5, 7, 8, 9, 12, 14, 17, 19, 22, 25, 27, 28, 33};

void main () {
	cout << BinarySearch(X, 8, 0, 14);
}

 

Registers

High-level language routines generally do not save registers, it is up to the caller to save any registers prior to the call and restore the registers after the call. For example, the following saves and restores eAx:

.586
.model flat, C

print	proto near C, x : dword, y : dword

.code

printASM proc public, p1:DWORD, p2:DWORD
    mov	eBx, 5
    push	eBx

    push	p2
    push	p1
    call	print
    add	esp, 8

    pop	eBx
    ret                   
printASM endp
end

Question 4 - Give the C++ and Assembler for function Fib.

#include <iostream>
using namespace std;
int Fib(int n) {
    if (n == 1) return 1;
    if (n == 2) return 1;
    return Fib(n-1) + Fib(n-2);
}	
void main(void) {
    cout << Fib(5);
}

 

C Functions - File Copy

The standard library for the C language contains many useful functions for file handling, running other process, math, etc.

These functions can be accessed by defining the PROTO for the C function and then INVOKE the function. 

The assembler program should include the following:

main proc public

      ret

main  endp

      end

The following Command Prompt example illustrates by copying a file to the screen.

If testing, the C program should be named file.C  with a C extension.

Copy the MSVCRT.LIB, by default located at:

Set Visual C environment variables:

The Assembly program below can be assembled with the MSVCRT.LIB in the same directory by:

The C program copies file.C while the Assembler program copies file.asm to the console screen. The commands necessary are:

  1. Copy the following Assembler program and save as file.asm.
  2. copy C:"\Program Files\Microsoft Visual Studio 9.0\VC\MSVCRT.LIB"
  3. C:"\Program Files\Microsoft Visual Studio 9.0\VC\vcvarsall.bat"
  4. ml /coff /Zi file.asm /link MSVCRT.LIB /subsystem:console
  5. file
Copy file to console screen

int *fopen( const char *filename, 
            const char *mode );
int getc( int *stream );
int putchar( int c );


char filename[7] = "file.c";
char mode[2] = "r";


void main(void) {
	int *fp = fopen(filename, mode);
	int c;

	while ((c=getc(fp)) != -1)
		putchar(c);
}
.586
.model small, c

fopen	  proto	near C, 
        filename:near ptr dword, mode:near ptr dword
getc 	  proto	near C, fileptr : dword
putchar proto	near C, char : dword

.data
filename	byte	"file.asm",0
mode		byte	"r",0

.code

main	proc	public
	local	 fp : dword

	invoke fopen, addr filename, addr mode
	mov	 fp, eAx
@@while:
	invoke getc, fp
	cmp	 Al, -1
	jne	 @@do
	jmp	 @@endwhile
   @@do:
	invoke putchar, eAx
	jmp	 @@while
@@endwhile:

	ret
	
main	endp
	end