Report discrepancies to dmcnutt@macnauchtan.com
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;
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:
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.
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 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
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.
Note that local variables are passed to basic subroutines by copying. If you use varptr to take the address of a variable which has been passed as an argument, be sure to do it within the subroutine which is about to call the BCMD. Otherwise you may get a stale copy. This is not a problem with dimensioned arrays.
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->.
b->paramType is set to 0x30.
A pointer to the actual variable is placed in b->varPtr. The variable is always IEEE double for PPC's. The variable is 10 byte extended for 68k machines unless it is part of a class or typed array in which case it is IEEE double.
b->varPtr is set to zero. This zero can be used by a BCMD to determine if the number passed was in fact a constant. Be very careful not to attempt to change a value if b->varPtr is zero!
b->paramType is set to 0x30. RHN Is this true?
The value of the element is converted to a long integer and placed in b->param.
b->paramType is set to 0x130.
Floating point arrays are stored in 10 byte extended in 68k machines and 8 byte IEEE doubles on PPC's. Since arrays cannot be members of a type or class the kind can be uniquely determined by examining the machine bit in a->version.
b->varPtr is set to zero.
b->paramType is set to 0. It is not possible to change the value of the constant.
A pointer to the original 16 bit integer variable is placed in b->varPtr.
b->paramType is set to 0x20.
A pointer to the original element is placed in b->varPtr.
b->paramType is set to 0x120.
Integer arrays are 16 bit values designed mostly for storage of such things as digital audio. Integers have no advantage in speed of execution and can in fact delay operation since all operations are performed in floating point. Integer arithmetic is not guaranteed to work as of version 3.4.7. Integer arrays cannot be passed by reference to subroutines within classes. (A nil address is passed.) Replacement operations are reliable as are conversions from floating point to integer.
b->paramType is set to 0.
b->varPtr is undefined.
b->paramType is set to 1. (As of 3.4.5 this was unreliable when the constant string was passed as an argument to a ChipBasic subroutine!)
b->varPtr is undefined and should not be used.
The original string cannot be modified by the BCMD. The BCMD may make any use of the 256 byte space pointed to by b->param. ChipBasic reuses it for other things after the return.
b->paramType is set to 0x11.
b->varPtr is set to a pointer to a pointer to the "real" c-string. This copy, which is not in the same place as the one pointed to by b->param, can be modified by the BCMD up to 255 characters including the terminating null. The pointer-pointer is a little like a MacOS handle except that it is managed by Chipmunk Basic rather than by the memory manager. When string space is condensed the string pointers change but the pointer-pointers do not. You can think of them as pointers into an array of master pointers. Pointer-pointers can be saved and reused like handles but the commands new, load, dim & clear WILL leave you with a dangling (char **) when they collect garbage within ChipBasic.
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