MC-Basic C-Interface

From SoftMC-Wiki
Jump to: navigation, search


The softMC offers users an option to incorporate applications written in C or C++ into an MC-Basic task. You can write your algorithm in C/C++, compile it with a GNU compiler, download it into a target program and call the code from a program written in MC-Basic or directly from the command line.

The parameters can be passed by value as well as by reference. This function returns a value that can be processed by an MC-Basic application.

MC-Basic provides a facility to load a user object-file into the RAM of the softMC. This file is relocatable and must include information about global symbols for the softMC to find the C-function. The object module may consist of any number of functions. The only restriction is RAM limit.

Object Files

Object files (extension “.o”), contain compiled C-programs and are stored in the Flash disk. You can SEND object files to the controller, load files into RAM with OLOAD and unload them with OUNLOAD. If OLOAD/OUNLOAD fails for any reason, details are found in the OLOAD.ERR file.


For the MC-Basic language translator to match function parameters, provide a C-function prototype file (PROTO.PRO). There is also an option to wrire prototypes in library files, within the declarations section, instead of using the PROTO.PRO file. It is important to understand that matching between the provided softMC prototype and actual C implementation cannot be tested by the translator. It is your responsibility to keep consistency between the function prototype and C implementation.

To speedup translation of the prototype file, PROTO.PRO is copied into RAM at startup and at every OLOAD. When PROTO.PRO is modified and sent to the controller, issue an OLOAD or reboot the softMC to refresh the prototypes in the RAM.

The translator is case-insensitive, while object module loader is case-sensitive, so write the name of a C function in capital letters within the C code. softMC prototypes of C functions and C function calls through the softMC are not case sensitive.

Parameters can be passed “by value” and “by reference”

Few object files may be incrementally linked together.

The general syntax for the softMC prototype of a C-function is:

IMPORT_C <Function_Name> ({AS <type>}, {BYVAL AS {<type>}) {AS <type>}

A parameter passed by value has the BYVAL prefix.

A parameter passed by reference has no prefix.

Prototypes do not include parameter names.

A C function prototype with no parameters is written with empty parentheses:

IMPORT_C <Function_Name> ( ) {AS <type>}

A C function prototype with no returned value (a void function) is written as:

IMPORT_C <Function_Name> ({AS <type>}, {BYVAL AS {<type>})

A C-function accepts any combination of double-, long- , string-, joint- and location-type parameters. The parameters may be passed “by value” or “by reference”.

The returned value may be long, double, string, joint, location or none (for C functions with void returned value).

Examples of prototypes and implementation:

MC-Basic Prototype
C Implementation
import_c cFunc_LV As Long int CFUNC_LV(void);
import_c cFunc_DV As Double double CFUNC_DV(void);
import_c cFunc_SV As String SYS_STRING* CFUNC_SV(void);
import_c cFunc_PV As Joint Of XYZR SYS_POINT* CFUNC_PV(void);
import_c cFunc_GPV As Generic Location SYS_POINT* CFUNC_GPV(void);
import_c cFunc_VV() void CFUNC_VV(void);
import_c cFunc_LL(ByVal As Long) As Long int CFUNC_LL(int L);
import_c cFunc_DRD(As Double) As Double double CFUNC_DRD(double* D);
import_c cFunc_SRS(As String) As String SYS_STRING* CFUNC_SRS(SYS_STRING** S);
import_c cFunc_PRP(As Joint Of XY) As Location Of XYZ SYS_POINT* CFUNC_PRP(SYS_STRING** P);
import_c cFunc_GPRGP(As Generic Location) As Generic Joint SYS_POINT* CFUNC_GPRGP(SYS_STRING** P);
import_c cFunc_SS(ByVal As String) As String SYS_STRING* CFUNC_SS(SYS_STRING* S);
import_c cFunc_PGP(ByVal As Generic Joint) As Location Of XYZ SYS_POINT* CFUNC_PP(SYS_POINT* P);
import_c cFunc_GPP(ByVal As Location Of XY) As Generic Joint SYS_POINT* CFUNC_GPGP(SYS_POINT* P);
Import_c cFunc_LADAD([*] as Long, [*] As Double) As Double Double CFUNC_LADAD(Long* L, Double* D);
Import_c cFunc_SAV([*] as String) void CFUNC_SAV(SYS_STRING** SA);
Import_c cFunc_GPAPAS([*] As Generic Joint, [*] As Location Of XY) As String SYS_STRING* CFUNC_GPAPAS(SYS_POINT** PA1, SYS_POINT** PA2);

Parameters can be of type long, double, string, joint or location; in any order, up to 32 parameters.

Only one-dimensional arrays are allowed as C-functions arguments.

C-functions without parameters or returned values are called using a different syntax than their MC-Basic counterparts.

Example for calling C-functions from MC-Basic:

No Parameters:
' C code: int CFUNC_LV(void) 

No Returned Value:
' C code: void CFUNC_VV(par1 as int)

Example of PROTO.PRO:

'No Parametrs
import_c CFUNC_LV() As Long
import_c CFUNC_DV() As Double
import_c CFUNC_SV() As String
import_c CFUNC_PV() As Location Of XYZ
import_c CFUNC_GPV() As Generic Joint
import_c CFUNC_VV()
'A Single "By Value" Parameter
import_c CFUNC_LL(ByVal As Long) As Long
import_c CFUNC_DD(ByVal As Double) As Double
import_c CFUNC_SS(ByVal As String) As String
import_c CFUNC_PP(ByVal As Joint of XY) As Location Of XYZ
import_c CFUNC_GPP(ByVal As Generic Location) As Generic Joint
'A Single "By Reference" Parameter
import_c CFUNC_LRL(As Long) As Long
import_c CFUNC_DRD(As Double) As Double
import_c CFUNC_SRS(As String) As String
import_c CFUNC_PRP(As Joint of XY) As Location Of XYZ
import_c CFUNC_GPRP(As Generic Location) As Generic Joint
'Multiple "By Value" Parameters
import_c CFUNC_LVALP(ByVal As double, ByVal As Long, ByVal As String) As Long
import_c CFUNC_VVALP(ByVal As String, ByVal As double, ByVal AS Long)
'Multiple "By Reference" Parameters
import_c CFUNC_DREFP(As String, As Long, As Double) As Double
import_c CFUNC_VREFP(As Long, As Double, As String)
' Mixed Parameters
import_c CFUNC_VMIXP(ByVal As Long,As Double,ByVal As String,As String,As Long,ByVal As double) AS Long

Example of test.c:

/* No Parameters */
int CFUNC_LV(void)
return 1;
double CFUNC_DV(void)
return 2.2;
char* CFUNC_SV(void)
return "SV";
void CFUNC_VV(void)
int fd = open("/tyCo/1",2,0);
/* A Single "By Value" Parameter */
int CFUNC_LL(int L)
L = L + 1;
return L;
double CFUNC_DD(double D)
D = D + 2.2;
return D;
return S;
/* A Single "By Reference" Parameter */
int CFUNC_LRL(int* L)
*L = *L + 3;
return *L;
double CFUNC_DRD(double* D)
*D = *D + 4.4;
return *D;
return *S;
/* Multiple Parameters */
/* By Value Parameters */
int CFUNC_LVALP(double D, int L, SYS_STRING* S)
return (D + L + atoi(S)); 
void CFUNC_VVALP(SYS_STRING* S, double D, int L)
int fd = open("/tyCo/1",2,0);
fdprintf(fd,"Original Values:%d, %f, %s\r\n", L, D, S);
L = 10;
D = 22.2;
fdprintf(fd,"New Value:%d, %f, %s\r\n", L, D, S);
/* By Reference Parameters */
double CFUNC_DREFP(SYS_STRING** S, int* L, double* D)
return (*D + *L + atof(*S)); 

Using MC-Basic Strings in C Functions

In C functions code, there must a distinction between local “internal” strings, used only within function block, and “external” strings, originated form MC-basic code (parameters), or returned to MC-basic code (returned values). While regular ANSI strings can be used as “internal” strings, “external” strings are MC string data structure.

The String Library

This library provides functions that enable access to various fields of the string data structutre. Therefore, C function files should include a link to“”.

int str_GetType (SYS_STRING* string)
Returns the string’s type: 0 (no type, used for string values and unassigned returned values), 1 (ASCII-8 type) or 2 (UTF-8 type). Returns ERROR (-1) if string parameter is NULL.

int str_ GetMemSize (SYS_STRING* string)
Returns the amount of bytes allocated for the string. Returns ERROR (-1) if string parameter is NULL.

int str_GetNumBytes (SYS_STRING* string)
Returns the actual length of the string in bytes, not including a null-terminator. This size might be smaller than memory size. Returns ERROR (-1) if string parameter is NULL.

int str_GetNumSymbols (SYS_STRING* string)
Returns the number of Unicode symbols within the string, not including the null-terminator. This value should be identical to str_GetNumBytes() value (see above) in ASCII-8 strings, and might be smaller than str_GetNumBytes() value in UTF-8 strings. Returns ERROR (-1) if string parameter is NULL.

unsigned char* str_GetData (SYS_STRING* string)
Returns the string itself. The string is null-terminated, but may include more null characters in other locations.

int str_Strcpy (SYS_STRING* dest, const char* source, int size)
This function replaces the ANSI strcpy function, which copies “size” bytes from “source” string into the “dest” string data structure without using a null-terminator. str_Strcpy() also updates the string size field, and calculates the new number of symbols according to string type. Returns ERROR (-1) if value of size parameter is larger than size of memory allocated for the string, or if one of the pointer parameters is NULL. Returns OK (0) for successful string copy.

SYS_STRING* str_GetString (unsigned char* data, int data_size, int type)
This function creates a new string data structure, with”type” string type. It first allocates a new string data structure, and then allocates “size” bytes through malloc() for the “data” string. Then, str_Strcpy() (see above) is called to copy “data” string to the new string data structure. Returns the address of the new string data structure, or NULL if memory allocation or str_Strcpy() fails.

Querying String Parameters

In the new string data structures of MC, strings’ data cannot be applied directly as in ANSI strings. Therefore, string’s data should be applied through str_GetData(<string_data_ structure>). All other fields of string data structure should be addressed likewise, using the appropriated str_Get… function from String library (see above).

#include “"
By Value Parameters:

… CFUNC1(…, SYS_STRING* ValStr, …)
	char Cstring[100];  /* Local ANSI string */
	strcpy(Cstring, str_GetData(ValStr));

By Reference Parameters:

… CFUNC2(…, SYS_STRING** RefStr, …)
	printf(“%s\n”, str_GetData(*RefStr));

By Reference Array Parameters:

… CFUNC3(…, SYS_STRING** RefStrArr, …)
	char CStringArr[10][60];  /* Local ANSI string */

	if (strcmp(str_GetData(RefStrArr[2]),CstringArr[2])      

Assignment of String Parameters

The str_StrCpy() function (see above) was designed for assignmnet of a string parameters. If str_StrCpy() returns ERROR (-1), an error message should be raised, since the amount of memory allocated for the string is probably not enough for the new string. Memory release and realloction of MC string parameters within C code should be avoided, since it might cause memory management problems.

#include “"
… CFUNC6(…, SYS_STRING** MCString, …) /* MC string */	
  char CString[20]; /* Local ANSI string */

  if( str_Strcpy(*MCString,Cstring,strlen(Cstring)!=OK) )
    printf("Not enough memory in string parameter\n")

Strings as Returned-Values

A returned string cannot be an ANSI string, but must be a string data structure. The returned string data structure must be newly allocated through str_GetString() function (see above).

#include “"
 char CString[20]; /* Local ANSI string */
 int size;

 size = strlen(Cstring);

 return str_GetString(Cstring, size, ASCII8_STRING_TYPE);

Special Considerations

Motion and System properties can only be passed by value.

KILLTASK will not unblock a task locked inside the C-function. An endless block on a semaphore, message queue, etc. inside a C-function prevent sa task from correct being killed. If C-function has some blocking operation system call (e.g., puting a task to sleep, taking a semaphore, passing messages through a message queue, IO operation, etc.), this may interfere with killing the user task.

When an object module is loaded several times without unloading, all its instances are kept in the RAM, while the system symbol table has references only to the resent instance. MC-Basic applications may refer to the obsolete version of some modified object file.

OUNLOAD does not check to see if the object file is in use. It is your responsibility to ensure that the object is not unloaded as long as there tasks and libraries that refer to the object.

Consider following example, both the program TASK1.PRG and TASK2.PRG use the same function from object module.

-->oload my_obj.o
-->load TASK1.PRG
-->send my_obj.o ‘ send updated version of object file
-->oload my_obj.o
-->load TASK2.PRG

In this example, TASK1.PRG references the oldest instance of my_obj.o while TASK2.PRG references the recent version.

See Also