Using the BCMD interface for Chipmunk BASIC

Updated 8-31-96

Report discrepancies to dmcnutt@macnauchtan.com

Header lines from the Chipmunk BASIC source code

These are all you really need. They can be copied, at least with Netscape, and pasted into your header file.

typedef pascal long (* bcmdfunc)(bcmdData *);
typedef struct bcmdPBlock
   {
   long        param;      /* parameter, int or char* */
   long        paramType;  /* parameter type */
   void        *varPtr;    /* variable Ptr (or NULL) */
   long        reserved;   /* reserved, set to 0 */
   } bcmdPBlock;
typedef struct bcmdData
   {
   long        selector;   /* bcmd selector, int or char* */
   long        selType;    /* 0 for int, 1 for char* */
   long        version;    /* >= 0x3470 ( +1 for PPC ) */
   ProcPtr     callbackPP; /* unimplemented as of 3.4.7 (NULL) */
   bcmdPBlock  parms[1];   /* up to 3 (or 4???) */
   } bcmdData;

Generalities

BCMD's are like CODE resources. Chipmunk accesses them using GetNamedResource(). You can put them in the Chipmunk application or you can

open "filename" ( no file number) for data input

which will make that file the topmost resource file. Closing it requires a '>new' command. There can be no close #n command since there is no file number assigned. BCMDs are compiled as type pascal long with a single argument of type bcmdData*, resource type 'BCMD'.

Note the typedef line at the beginning of the header lines above. The BCMD should return a long integer but as of version 3.4.7 it is not used. Negative values will oneday be treated as error conditions.

Chipmunk Basic always stores numeric variables and arrays in locked blocks which will not move with memory manager calls.

All strings in Chipmunk BASIC are C-strings. Strings, as seen by BCMDS, are always assigned 256 byte blocks of memory unless they are in a CLASS. There are no Pascal length bytes present even though some BASIC commands seem to indicate otherwise by truncating to a 254 char maximum.

Two dimensional arrays count the second index to maximum before the first index is incremented. Three and four dimensional arrays similarly increment from right to left.

Pointing to the name of an array without specifing an element is allowed but Chipmunk BASIC, and all other BASICs, treat the reference as a different non-array variable which has nothing to do with the array. That is not what you want! Some early versions of Micro$oft BASIC used the format array() or array(n) where n represented the number of dimensions in the array like 3 for array(0,0,0). ChipBasic does not recognize that syntax.

An array CAN be passed by reference as an argument to a subroutine within a class and the subroutine DOES get the pointer you want, not a pointer to a new variable as in the previous paragraph. If you pass this pointer on to a BCMD you will have to know the dimensionality of the array so that the right number of zeros can be included in the call. ChipBasic reports a "missing comma" if you send array(0) when you should have sent array(0,0).

In short, if you need to change values in an array:


DIM     array(10, 10)
...
SUB BASICsubr(array)    ' Use just the name of the array here
    CALL "BCMDsubr", dummy, array    ' Error - defines new variable array
    CALL "BCMDsubr", dummy, array(0)    ' Error - missing comma
    CALL "BCMDsubr", dummy, array()    ' Error
    CALL "BCMDsubr", dummy, array(0,0)    ' This works
END SUB
...
CALL BASICsubr(array) ' Call with just the name of the array

The dummy variable is required because only arguments after the first have addresses passed to the BCMD. There's nothing lost because you probably need one call-by-value argument anyway. The number in array(0,0) is converted to a long integer and passed by value in param. The address of array(0,0), which is also the address of the complete array is also passed in varptr and can be used for returning data.

The dimensions of an array as declared in the DIM statement are not passed to the BCMD. DIM statements are different from C or FORTRAN in that the number in the DIM statement is the maximum value of the index taken as a cardinal. Since the minimum value is always zero Chipmunk BASIC assigns what can appear to be one too many memory cells. To get the equivalent of array[2,2] in C you code DIM array(1,1) in BASIC. If an unknown user has written DIM array(2,2) your BCMD must allow for 3 cells as it calculates pointers for cells within an array.

Floating point values and arrays are stored as IEEE doubles on PPC machines and as Apple SANE 10 byte extended on 68k machines. An exception is made for floating point values within CLASSes where everything is IEEE double because class data may be sent to external storage from which it must be readable by any machine. The Symantec C compiler offers two kinds of "double" floating point. To get IEEE doubles instead of Apple 10 byte extended, the default, you do:

#pragma options(double_8)

Judicious use of this pragma will allow your code to handle both class and normal variables. Selecting double_8 does not prevent declaring a type extended which will still be 10 byte SANE. My universal header file contains the pragma and these type definitions:


typedef double BasicDbl, *BasicDblPtr; /* for typed values */
typedef short BasicInt, *BasicIntPtr;
#if GENERATINGPOWERPC
     typedef double BasicFlt, *BasicFltPtr;
#else
     typedef extended BasicFlt, *BasicFltPtr;
#endif

How the arguments are handled

Within a BASIC program BCMDs are called by a statement of the form:

CALL "name", arg1, arg2, arg3, arg4

where "name" is the resource name of the BCMD. All or none of the arguments may be present. More arguments will probably be allowed in later versions of Chipmunk BASIC. The arguments are not passed to the BCMD in accordance with Pascal standards; rather the BCMD is entered with a single argument which is a pointer to the bcmdData structure shown above.

A programmer of BCMDs needs to know what will be found in the structures. The tabulation below is intended to cover all possibilities. For simplicity I will refer to the various items defined in the header by their short names prepended with an assumed pointer a-> when they are part of a bcmdData structure and b-> when they are part of a bcmdPBlock substructure in finished C code.

Arg1 is different from the rest. It is limited to a single 32 bit integer, a long, and is generally passed by value assuming no change by the BCMD.

A BCMD can ascertain the version of the interpreter and the type of machine it's running on from a->version. The decimal digits of the version number are each placed in three separate four bit nibbles in a->version. The rightmost (least significant) nibble is set to 0 for a 68k machine and to 1 for a power PC. Testing for odd or even returns the machine. Testing for >= 0x3260 for instance checks for a recent enough version of Chipmunk BASIC. Returning the value to Chipmunk BASIC in a variable will result in a confusing mixture of hex and decimal.

Callbacks are not yet implemented so a->callbackPP will be zero for now. If you need a feature consider sending ChipBasic an AppleEvent from the BCMD.

Arg2, arg3, argX and more are passed by extending the bcmdData block with instances of bcmdPBlock. I will designate items within one of these blocks as b->.

The general rules for b->paramType are:


 0 = A long int is being passed
 1 = A c-string pointer is being passed
 0x11 = pointer to a c-string (The contents of what's in param taken as an
address)
 0x20 = int (var or array) pointer. (short*)
 0x30 = floating (var or array) pointer. (extended* for 68k, double* for PPC)
 0x100 may be added to any of the above to indicate that the item
            is an array or an element of an array.

The general rules for b->varPtr are:
b->varPtr is always a pointer if it is not NULL. For strings it can be a pointer-pointer

Colophon

If you found this useful or if you have suggestions or comments, I'd like to hear from you. Doug McNutt, The MacNauchtan Laboratory, Colorado Springs, dmcnutt@macnauchtan.com I'm an analog hardware guy who feels out of his element.