News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

Calling Assembly From Matlab

Started by ASMManiac, February 07, 2012, 05:18:24 PM

Previous topic - Next topic

ASMManiac

I am trying to link an assembly program to a C++ mex file for use in matlab.  Has anyone had success doing this?
I would like to call it as mex test.cpp asmHelper.asm

I think I might need to assemble the ".asm" file first and then try to link it to the test.cpp file, but I'm not sure how to do that.

jj2007

I have done it but only in 32-bit assembler, sorry.

ASMManiac

What's the process for doing it in 32 bit?
Right now I'm trying to compile the asm with system('ml64 asmFile.asm');  where 'ml64 is in my current folder.
My plan is to then do mex cFile.cpp asmFile.obj

jj2007

You probably know this link: http://www.mathworks.it/help/techdoc/matlab_external/f24338.html#f24571

Right now I have no MatLab installation at hand, and I did the test months ago (it worked perfectly).
Below my cookbook, "as is".

One trick is to use dummy routines in the assembler part to cheat the linker - in case you need mxCreateDoubleMatrix etc inside the assembler part..

Create a library that contains 'fake' routines that mimic the behaviour of MatLab mex functions:
mxCreateDoubleMatrix PROTO C :DWORD,  :DWORD,  :DWORD
mxGetPr PROTO C :DWORD
mxDestroyArray PROTO C :DWORD
mexPrintf PROTO C :DWORD
ExternDef a2mMinRows:DWORD
ExternDef a2mRows:DWORD
ExternDef a2mCols:DWORD
ExternDef a2mBytes:DWORD
  These routines can be used from MasmBasic (see ATM.asc) but:
  1. they must be defined in the module header:
LibMod ATM
include \masm32\MasmBasic\MasmBasic.inc
mxCreateDoubleMatrix PROTO C :DWORD,  :DWORD,  :DWORD
... etc
  2. when assembling the library, ; OPT_DebugL /debug MbMexLib.lib must be used.
- mexPrintf with one arg uses from MB the Print function; you can use invoke mexPrintf, Cat$("abc"+Str$(123)+"def"+Lf$) to display
  messages. Note the Lf$: MatLab will interpret CrLf$ as two linefeeds. While your algo is running, mexPrintf normally does not
  show anything; however, you can add mexEvalString("pause(.001);") after mexPrintf in assembler to force immediate printing.
- full program for compiling in MatLab with e.g.  mex MexC :
//to read in a file from MatLab, use MyMatrix=MexC('\mypath\myfile.txt');    << semicolon means "do not display in MatLab command window"
//to get the number of rows, use size(MyMatrix,1)    << no semicolon = display as ans
//to get the number of columns, use size(MyMatrix,2)

#include "mex.h"
#include "matrix.h"
#include "stdafx.h"
#include "stdio.h"

#pragma comment(lib, "\\masm32\\MasmBasic\\matlab\\ATM.lib")

extern int __stdcall AsciiToMatrix(char * AscFile);
extern int __stdcall MbInt3Box(char * MsgText);
int a2mRows; //returns # of rows effectively read
int a2mMinRows; //insert a value to force allocation of a minimum # of rows, e.g. 100000
int a2test;

int String;
int buflen=1000;


void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
//Displays a 'want int 3' MsgBox: Launch Olly, attach to MatLab, F9, Yes, ..., detach!!
//MbInt3Box("Assembler says hi");

if(nrhs>=1) {
if (nrhs==2) {
a2mMinRows=mxGetScalar(prhs[1]);
mexPrintf("\na2mMinRows=%d\n", a2mMinRows);
}
String = mxMalloc(buflen);
mxGetString(prhs[0], String,buflen); //mexPrintf("\nstring passed=%s\n", String);
plhs[0] = AsciiToMatrix(String);
plhs[1] = mxCreateDoubleScalar(a2mRows);
mexPrintf(" %d rows read\n", a2mRows);
nlhs=2; // we return one matrix and the # of rows read
}
else
{
mexPrintf("\nno filename passed!\n");
}
}

- passing integers to the mexFunction, e.g. as arg2 ( mexc('MyFile.dat', 111000); ):
int a2mMinRows; //insert a value to force allocation of a minimum # of rows, e.g. 100000

void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
if (nrhs==2) {
a2mMinRows=mxGetScalar(prhs[1]);
mexPrintf("\na2mMinRows=%d\n", a2mMinRows);
}


- save matrix:
mm=mexc('D:\masm32\MasmBasic\matlab\BE_09_20110502_S080.prn_c_0-0-618808-27662919.dat');
ATM: mxCreateDoubleMatrix[106054, 1, mxREAL]
........... 100000 rows read
save('BE_100000', 'mm', '-v6')

DRX

I have done this before.  I don't have the code right before me (I may be able to bring this up later, if necessary), but as I remember, the basic idea was to create a dummy function in C that was declared as external, which was linked with the .asm function after assembly.

For 64-bit Matlab, you may also want to check out the info about calling a .dll from Matlab on Win x64 at the following link:
https://skydrive.live.com/?cid=9078D7D212BB1B48&id=9078D7D212BB1B48!107&sc=documents
Basically, Matlab doesn't follow the x64 ABI, so it has to thunk to call x64 .dlls.

GTG — I will check back later.  I hope this helps.

jj2007

Quote from: ASMManiac on February 07, 2012, 06:05:41 PM
My plan is to then do mex cFile.cpp asmFile.obj

If I remember well, that's what I did. The problem is that you will need one or more of the mxWhatever functions inside the assembly code - and you can't use them from assembler. But you can create a function with the same name in an assembler library, which does basically the same (e.g. create an array using GlobalAlloc). So you can use that one for testing the assembler part "standalone" with ml ..., link myasmcode.obj mymexlib.obj, and when you are ready, the mex cFile.cpp asmFile.obj command uses the "real" mxWhatever.

HTH, jj

ASMManiac

I was able to figure it out.
I put in ml64.exe and link.exe in the folder with the .asm file.

I call system('ml64 Transpose16x16A.asm');
Then mex Transpose16x16.cpp Transpose16x16A.obj

You need to define the function like this in your c++ code:

#ifdef __cplusplus
extern "C" {
#endif
void Transpose16x16A(char* a, char* b, int nRep);
#ifdef __cplusplus
}
#endif

The asm routine is called following the standard c++ x64 calling convention (http://software.intel.com/en-us/articles/introduction-to-x64-assembly/)
The important thing to know is the caller passes parameters in RCX, RDX, R8 and R9  (RCX for first param, RDX for second param, R8 for third param, R9 for fourth param)

You can call the function as any other normal function from c++ code.
char* a, *b;
int nRep;
....  code defining a,b, nRep ...
Transpose16x16A(a,b,nRep);

DRX

I'm glad you got it; that extern dummy C function does the trick all right.

QuoteThe asm routine is called following the standard c++ x64 calling convention (http://software.intel.com/en-us/articles/introduction-to-x64-assembly/)
The important thing to know is the caller passes parameters in RCX, RDX, R8 and R9  (RCX for first param, RDX for second param, R8 for third param, R9 for fourth param)

The link I gave above just points out that Matlab itself doesn't follow the standard x64 calling convention for .dll files; it fixes up calls to an external .dll by passing through a thunk function that puts certain parameters where they should be for the external function.
Specifically, any floats and doubles (but not pointers to floats or double) passed in the first four arguements should be passed in the appropriate xmm registers, not in RCX, RDX, R8, and R9, which is where Matlab puts them before the thunk function corrects the assignments.
The link you give to Intel says this as well under the section Calling Conventions:
QuoteThe rules for interfacing with C/C++ style functions:
• RCX, RDX, R8, R9 are used for integer and pointer arguments in that order left to right.
• XMM0, 1, 2, and 3 are used for floating point arguments. ...

Now mex files are a somewhat different beast, because all access to Matlab variables etc. passes through the mx and mex functions.  However, the mx and mex functions could hide the Matlab internal calling convention through thunks in the .lib functions or possibly their entrance points with Matlab, but I have not looked at any of those.  Have you checked the .mexw64 files you made to see if a thunk occurs (i.e. floats or doubles are moved between xmm registers and the general purpose registers)?  A function like mxGetEps or mxIsFinite that returns or passes doubles (respectively) might expose a thunk.


ASMManiac

Quote from: DRX on February 09, 2012, 03:56:32 AM

Now mex files are a somewhat different beast, because all access to Matlab variables etc. passes through the mx and mex functions.  However, the mx and mex functions could hide the Matlab internal calling convention through thunks in the .lib functions or possibly their entrance points with Matlab, but I have not looked at any of those.  Have you checked the .mexw64 files you made to see if a thunk occurs (i.e. floats or doubles are moved between xmm registers and the general purpose registers)?  A function like mxGetEps or mxIsFinite that returns or passes doubles (respectively) might expose a thunk.

I agree.  I am actually calling the assembly straight from C++ (not trying to call it from MATLAB) and within assembly I am not using any calls to matlab functions.

ASMManiac

How did you print stuff to the MATLAB window from assembly?
I can't call mexPrintf from assembly, and also tried simple printing routines and I got segmentation violation detection error.
I am interested in printing for debug purposes so speed is not important.
The following simple assembly program gave a segmentation violation when called from C++ within a mex file:

.code
printTest proc uses rdx
    mov ah, 02h
    mov dl, "A"
    int 21h
    xor rax,rax

    RET
printTest  endp
end



Quote from: jj2007 on February 08, 2012, 03:05:39 AM
Quote from: ASMManiac on February 07, 2012, 06:05:41 PM
My plan is to then do mex cFile.cpp asmFile.obj

If I remember well, that's what I did. The problem is that you will need one or more of the mxWhatever functions inside the assembly code - and you can't use them from assembler. But you can create a function with the same name in an assembler library, which does basically the same (e.g. create an array using GlobalAlloc). So you can use that one for testing the assembler part "standalone" with ml ..., link myasmcode.obj mymexlib.obj, and when you are ready, the mex cFile.cpp asmFile.obj command uses the "real" mxWhatever.

HTH, jj


jj2007

Quote from: ASMManiac on February 16, 2012, 06:57:25 PM
How did you print stuff to the MATLAB window from assembly?
I can't call mexPrintf from assembly, ...

You need to create a library that mimics the MatLab routines, and can be statically linked to your assembly code for testing. For the version that runs under MatLab then, you link with the real MatLab mxWhatever obj.

The code below works fine with Matlab, but it's 32-bit of course.

include \masm32\MasmBasic\MasmBasic.inc

mxCreateDoubleMatrix PROTO C :DWORD,  :DWORD,  :DWORD
mxGetPr PROTO C :DWORD
mxDestroyArray PROTO C :DWORD
mexEvalString PROTO C :DWORD
mexPrintf PROTO C :DWORD
ExternDef a2mMinRows:DWORD
ExternDef a2mRows:DWORD
ExternDef a2mCols:DWORD
ExternDef a2mBytes:DWORD

.data?
a2mMinRows dd ?
a2mRows dd ?
a2mCols dd ?
a2mBytes dd ?

.code
mexEvalString proc C pText:DWORD ; simply discard the string in MB
  ret
mexEvalString endp

OPTION PROLOGUE:NONE
OPTION EPILOGUE:NONE
mexPrintf proc C pText:DWORD
  push [esp+4] ; pText
  push 1
  call MbPrint
;  Print ; MatLab adds LF$
  retn
mexPrintf endp

mxCreateDoubleMatrix proc C rows, cols, mxType
call GetNumID
xchg eax, ecx
PrintLine Str$("Free ID=%i", ecx), Str$(", requested %i rows and ", [esp+4]), Str$("%i columns", [esp+8])
push ecx
mov eax, [esp+8]
mul dword ptr [esp+12]
push eax
push 8
push ecx
call MbArrayDimStruct
; mov eax, [esp]
; PrintLine Str$("dimmed: %i", eax), Str$(" with %i rows and ", [esp+8]), Str$("%i columns", [esp+12])
pop eax
ret
mxCreateDoubleMatrix endp

OPTION PROLOGUE:PrologueDef
OPTION EPILOGUE:EpilogueDef

mxGetPr proc C ArrPtr
mov eax, ArrPtr
mov eax, [MbArrTable+4*eax]
ret
mxGetPr endp

mxDestroyArray proc C arrID
push arrID
call MbStructErase
ret
mxDestroyArray endp
END