previous page next page reference home emBASIC home page

When it comes to special solutions, it might often be desireable to extend emBASIC by custom written functions. This can easily be done if you are proficient in the C language and have access to the development CD for the underlying operating system - mCAT. emBASIC is prepared for C plug-ins that can be used for demanding regulator functions, exotic mathematics, access to specialized hardware etc.

mCAT's extension mechanism is called Shared Library or ShLib. You can write such a ShLib to put all your extension functions into, then compile and download it to the target flash. With the next reset, emBASIC scans memory to find your ShLib and immediately presents your custom functions just as if they were built-in system functions. Writing a ShLib to extend emBASIC requires that you obey some guidelines such that emBASIC knows where to find the function prototype and the code and how to transfer the arguments.

This will walk you through an example of two simple functions, one calculating the length of a string using StrLen, a built-in function of the operating system, and the other to return the square of an integer argument.
A third function described in the source files calculates a moving average to show the implications of RAM usage and initialization. We want to be able to call the functions like

lgth = STRLEN("emBASIC is now even more powerful!")
avg = MATHFLTAVR(currentsample)

Start by creating a working directory for the project in your ..\mcat.2xx\cc source directory, lets assume it's ..\mcat.210\cc\embasext. Edit an mCAT target file to tell the system where to store your extension and name it like the directory: embasext.trg. If you have other system level extensions, you'll know where to store it, if not, the addresses 0x900000 in Flash-EPROM and 0x402000 in RAM used in this example are fairly safe places. A length of 0x1000 or 4K will certainly do for some standard functions. Our first two functions don't need any RAM, so it's ok to use any address for the RAM, the third uses a few bytes. To find out about available RAM, look at the "Memory block" section of emBASICs startup message: Everything between 402000 and the first value given there is quite safe.

Next create a file and call it module.c. You also can name it after your grandfathers dog if you prefer, just not the same as the project, as a file named like the project (i.e. embasext.c) will be created by the ShLib utility MIC. In the text, start with including from the current directory embasic.h (declaring the emBASIC datatype definitions for parameter passing and the structure of the function argument prototypes), common.h and wrapper.h generated by the MIC tool as explained later. Further, include vtype.h and io.h from the standard include directory.

Now the easy part: Writing the function that is called when the emBASIC expression containing our function is evaluated. To keep the sample concise we used an mCAT library function for the first and omitted any range checking on the second function.

int StrLen(char *st)
    return strlen(st);

int MathSqrInt(int i)
   return i*i;

emBASIC needs to know the data type of the arguments its function has to pass. Hence set up a constant array of type emb_arg_proto_t (as defined in emBASIC.h) for each function and assign it the data types enumerated there. The names of the arrays are of the form <emBASICfunctionname>_arg[<no_of_args>].

static const emb_arg_proto_t strlen_arg[1] = {
static const emb_arg_proto_t sqrint_arg[1] = {

The following prototype structures have to be created for emBASIC's method table. emBASIC reads this table on startup and adds references to your C functions from this table to its parsing subsystem. The structure exfun_t defined in embasic.h consists of:

  • Name of the function (any case, max. 20 characters)
  • Pointer to the function's body
  • emBASIC's data type of the return value
  • Number of arguments
  • Pointer to the argument data type array of the function

This is how it looks for our example:

static const embfunc_t T[] = {
   {"StrLen",     &StrLen,     INT_T, 1, strlen_arg},
   {"MathSqrInt", &MathSqrInt, INT_T, 1, sqrint_arg},
   { NULL,        NULL,        0,     0, NULL}

As can be seen, the last entry is required to be a NULL entry.

Finally, specify the ShLib entry function that returns a pointer to our method table "T":

void* SYS_FDECL _emBASextGetTable() {
   return &T;

Save module.c.

Now edit the "Library Definition File" we named emBASext.ldf for our example. Only set the LIBID and VERSION, the NAME that gets copied into the libraries IMD.


 . . .

The LIBDEF lists all the functions we want to create. Start with the only required function GetTable that is used by emBASIC as an entry function to find our table

LIBDEF emBASext # Mandatory entry for emBASIC: GetTable
   GetTable ? void* ();

If you only want to call the functions you create here from emBASIC, you are done with the ldf file and can skip to the MAKEFILE. However, if you want to use the functions from other C-tasks under mCAT, you have to make them accessible with more LIBDEFs. Library definitions can be grouped into families of similar functions. The family name is a prefix to the functions. In our example above the function "GetTable" of the emBASext family gets finally called as emBASextGetTable(). Hence we group our own functions into string and math and declare our functions StrLen and MathSqrInt as follows:

   Len ? @CDECL int (char *st)
   RESERVED #;; more string functions

   SqrInt ? @CDECL int (int i)
   RESERVED #;; more math functions

It's a good practice to leave some space for later extensions. Now your functions can be called from other mCAT tasks as ShLib calls, too.

As the last step, configure the MAKEFILE for this project:
Set PROJECT to embasext, memory to the entries in the target file embasic.trg
and make references to your source file in the OBJFILES line and the line after wrapper.rel - like:

PROJECT = embasext

TARGET = embasext

OBJFILES = $(PROJECT).rel wrapper.rel module.rel
 . . .
 . . .
wrapper.rel: wrapper.c $(INCFILES)
module.rel:  module.c $(INCFILES)

Open a COMMAND window from your project directory and type vmake -r
to start the compilation. First the MIC tool will interpret your ldf instructions and, after generating the necessary files do a compile ending in the hexfile embasext.shx that you can download using BAPImon or wLGO to the target Flash EPROM. Please DELETE the flash page if it isn't virgin at that time, see mCAT documentation 'hardware reference' to find out the Flash EEPROM page associated with your memory setting and delete it with DELPAGE <pageno>, followed by RESET from SYSMON (In BAPImon just click the page's check box in the SHX-download dialog).

RESET your target controller and emBASIC should start up with the commands integrated. To find out, get the Shell on either AW or a terminal and !EXIT to immediate mode. Then enter !show shlibs:

main# exit
# !Show ShLibs

0021 Sample/emBASext 009001c6 INT StrLen(STRING)
0021 Sample/emBASext 009001d1 INT MathSqrInt(INT)
0021 Sample/emBASext 009001d9 LONG MathFltAvr(INT)
1 shared modules loaded, 3 functions exported
# PRINT MathSqrInt(StrLen("Hello"))

And you can now use the functions just as if they were built in.

If the !Show shlibs command doesn't show anything, check the download memory. Enter BYE in immediate mode to leave emBASIC to SYSMON, then enter dump <addr> . If you don't see something starting AA 55 and showing the name you assigned to ARG NAME= in the .ldf then check the download. If this looks o.k. then you got to compare your C and ldf files to our sample to find out. Here's a ZIP of the project.

2+>dump 900000
00900000 AA 55 21 01 90 00 00 00 00 00 00 00 00 00 00 00 * .U!.............
00900010 00 00 0D 7F 51 41 00 02 FF 31 2E 30 30 00 53 61 * ....QA...1.00.Sa
00900020 6D 70 6C 65 2F 65 6D 42 41 53 65 78 74 00 00 4F * mple/emBASext..O