view docs/ccguide/basic09.appendix @ 1331:0a3500c747de

Fixed ANOTHER bug
author boisy
date Sun, 07 Sep 2003 00:53:51 +0000
parents 7fb3d02e04b0
children
line wrap: on
line source

<appendix>
<title>Interfacing to Basic09</title>
<para>
The object code generated by the Microware C Compiler can be made
callable from the BASIC09 "RUN" statement. Certain portions of a
BASIC09 program written in C can have a drmatic effect on execution
speed. To effectively utilize this feature, one must be familiar
with both C and BASIC09 internal data representation and procedure
calling protocol.
</para>
<para>
C type "int" and BASIC09 type "INTEGER" are identical; both are two
byte two's complement integers. C type "char" and BASIC09 type
"BYTE" and "BOOLEAN" are also identical. Keep in mind that C will
sign-extend characters for comparisons yielding the range -128 to
127.
</para>
<para>
BASIC09 strings are terminated by 0xff (255). C strings are
terminated by 0x00 (0). If the BASIC09 string is of maximum length,
the terminator is not present. Therefore, string length as well as
terminator checks must be performed on BASIC09 strings when
processing them with C functions.
</para>
<para>
The floating point format used by C and BASIC09 are not directly
compatible. Since both use a binary floating point format it is
possible to convert BASIC09 reals to C doubles and vice-versa.
</para>
<para>
Multi-dimensional arrays are stored by BASIC09 in a different manner
than C. Multi-dimensional arrays are stored by BASIC09 in a column-wise
manner; C stores them row-wise. Consider the following example:
</para>
<screen>
BASIC09 matrix:  DIM array(5,3):INTEGER
The elements in consecutive memory locations (read left to
right, line by line) are stored as:
(1,1),(2,1),(3,1),(4,1),(5,1)
(1,2),(2,2),(3,2),(4,2),(5,2)
(1,3),(2,3),(3,3),(4,3),(5,3)

C matrix:  int array[5][3];
(1,1),(1,2),(1,3)
(2,1),(2,2),(2,3)
(3,1),(3,2),(3,3)
(4,1),(4,2),(4,3)
(5,1),(5,2),(5,3)
</screen>
<para>
Therefore to access BASIC09 matrix elements in C, the subscripts
must be transposed. To access element array(4,2) in BASIC09 use
array[2][4] in C.
</para>
<para>
The details on interfacing BASIC09 to C are best described by
example. The remainder of this appendix is a mini tutorial
demonstrating the process starting with simple examples and working
up to more complex ones.
</para>

<section>
<title>Example 1 - Simple Integer Aritmetic Case</title>
<para>
This first example illustrates a simple case. Write a C function to
add an integer value to three integer variables.
<screen>
build bt1.c
? addints(cnt,value,s1,arg1,s2,arg2,s2,arg3,s4)
? int *value,*arg1,*arg2,*arg3;
? {
?     *arg1 += *value;
?     *arg2 += *value;
?     *arg3 += *value;
? }
?
</screen>
</para>
<para>
That's the C function. The name of the function is "addints". The
name is information for C and c.link; BASIC09 will not know anything
about the name. Page 9-13 of the BASIC09 Reference manual describes
how BASIC09 passes parameters to machine language modules. Since
BASIC09 and C pass parameters in a similar fashion, it is easy to
access BASIC09 values. The first parameter on the BASIC09 stack is
a two-byte count of the number of following parameter pairs. Each
pair consists of an address and size of value. For most C
functions, the parameter count and pair size is not used. The
address, however, is the useful piece of information. The address
is declared in the C function to always be a "pointer to..." type.
BASIC09 always passes addresses to procedures, even for constant
values. The arguments cnt, s1, s2, s3 and s4 are just place holders
to indicate the presence of the parameter count and argument sizes
on the stack. These can be used to check validity of the passed
arguments if desired.
</para>
<para>
The line "int *value,*arg1,*arg2,*arg3" declares the parameters (in
this case all "pointers to int"), so the compiler will generate the
correct code to access the BASIC09 values. The remaining lines
increment each arg by the passed value. Notice that a simple
arithmetic operation is performed here (addition), so C will not
have to call a library function to do the operation.
</para>
<para>
To compile this function, the following C compiler command line is
used:
<informalexample>
<para>
cc2 bt1.c -rs
</para>
</informalexample>
Cc2 uses the Level-Two compiler. Replace cc2 with cc1 if you are
using the Level-One compiler. The -r option causes the compiler to
leave bt1.r as output, ready to be linked. The -s option suppresses
the call to the stack-checking function. Since we will be making a
module for BASIC09, cstart.r will not be used. Therefore, no
initialized data, static data, or stack checking is allowed. More
on this later.
</para>
<para>
The bt1.r file must now be converted to a loadable module that
BASIC09 can link to by using a special linking technique as follows:
<informalexample>
<para>
c.link bt1.r -b=addints -o=addints
</para>
</informalexample>
This command tells the linker to read bt1.r as input. The option
"-b=addints" tells the linker to make the output file a module that
BASIC09 can link to and that the function "addints" is to be the
entrypoint in the module. You may give many input files to c.link
in this mode. It resolves references in the normal fashion. The
name given to the "-b=" option indicates which of the functions is
to be entered directly by the BASIC09 RUN command. The option
"-o=addints" says what the name of the output file is to be, in this
case "addints". This name should be the name used in the BASIC09
RUN command to call the C procedure. The name given in "-o="
option is the name of the procedure to RUN. The "-b=" option is
merely information to the linker so it can fill in the correct
module entrypoint offset.
</para>

<para>
Enter the following BASIC09 program:
<programlisting>
PROCEDURE btest
DIM i,j,k:INTEGER
i=1
j=132
k=-1033
RUN addints(4,i,j,k)
PRINT i,j,k
END
</programlisting>
When this procedure is RUN, it should print:
<screen>
5       136     -1029
</screen>
indicating that our C function worked!
</para>
</section>

<section>
<title>Example 2 - More Complex Integer Aritmetic Case</title>
<para>
The next example shows how static memeory can be used. Take the
C function from the previous example and modify it to add the number
of times it has been entered to the increment:
</para>
<screen>
buld bt2.c
? static int entcnt;
?
? addints(cnt,cmem,cmemsiz,value,s1,arg1,s2,arg2,s2,arg3,s4)
? char *cmem;
? int *value,*arg1,*arg2,*arg3;
? {
? #asm
?  ldy 6,s base of static area
? #endasm
?     int j = *value + entcnt++;
?
?     *arg1 += j;
?     *arg2 += j;
?     *arg3 += j;
? }
?
</screen>
<para>
This example differs from the first in a number of ways. The line
"static in entcnt" defines an integer value name entcnt global to
bt2.c. The parameter cmem and the line "char *cmen" indicate a
character array. The array will be used in the C function for
global/static storage. C accesses non-auto and non-register
variables indexed off the Y register. cstart.r normally takes care
of setting this up. Since cstart.r will not be used for this
BASIC09-callable function, we have to take measures to make sure the
Y register points to a valid and sufficiently large area of memory.
The line "ldy 6,s" is assembly language code embedded in C source
that loads the Y register with the first parameter passed by
BASIC09. If the first parameter in the BASIC09 RUN statement is an
array, and the "ldy 6,s" is placed <emphasis>immediately</emphasis> after the
"{" opening the function body, the offset will always be "6,s". Note the line
beginning "int j = ...". This line uses an initializer which, in
this case, is allowed because <varname>j</varname> is of class "auto".
No classes but "auto" and "register" can be initialized in BASIC09-callable C
functions.
</para>
<para>
To compile this function, the following C compiler command line is
used:
<screen>
cc2 bt2.c -rs
</screen>
Again, the -r option leaves bt2.r as output and the -s option
suppresses stack checking.
</para>
<para>
Normally, the linker considers it to be an error if the "-b=" option
appears and the final linked module requires a data memory
allocation. In our case here, we require a data memory allocation
and we will provide the code to make sure everything is set up
correctly. The "-t" linker option causes the linker to print the
total data memory requirement so we can allow for it rather than
complaining about it. Our linker command line is:
<screen>
c.link bt2.r -o=addints -b=addints -r
</screen>
</para>
<para>
The linker will respond with "BASIC09 static data size is 2 bytes".
We must make sure <varname>cmem</varname> points to at least 2 bytes of memory. The
memory should be zeroed to conform to C specifications.
</para>
<para>
Enter the following BASIC09 program:
</para>
<screen>
PROCEDURE btest
DIM i,j,k,n;INTEGER
DIM cmem(10):INTEGER
FOR i=1 TO 10
     cmem(i)=0
NEXT i
FOR n=1 TO 5
     i=1
     j=132
     k=-1033
     RUN addints(cmem,4,i,j,k)
     PRINT i,j,k
NEXT n
END
</screen>
<para>
This program is similar to the previous example. Our area for data
memory is a 10-integer array (20 bytes) which is way more than the 2
bytes for this example. It is better to err on the generous side.
Cmem is an integer array for convenience in initializing it to zero
(per C data memory specifications). When the program is run, it
calls addints 5 times with the same data values. Because addints
add the number of times it was called to the value, the i,j,k
values should be 4+number of times called. When run, the program prints:
<screen>
     5       136     -1029
     6       137     -1028
     7       138     -1027
     8       139     -1026
     9       140     -1025
</screen>
Works again!
</para>
</section>

<section>
<title>Example 3 - Simple String Manipulation</title>
<para>
This example shows how to access BASIC09 strings through C
functions. For this example, write the C version of SUBSTR.
</para>
<screen>
build bt3.c
? /* Find substring from BASIC09 string:
?         RUN findstr(A$,B$,findpos)
?    returns in fndpos the position in A$ that B$ was found or
?    0 if not found.  A$ and B$ must be strings, fndpos must be
?    INTEGER.
? */
? findstr(cnt,string,strcnt,srchstr,srchcnt,result);
? char *string,*srchstr;
? int strcnt, srchcnt, *result;
? {
?     *result = finder(string,strcnt,srchstr,srchcnt);
? }
?
? static finder(str,strlen,pat,patlen)
? char *str,*pat;
? int strlen,patlen;
? {
?     int i;
?     for(i=1;strlen-- &gt; 0 &amp;&amp; *str!=0xff; ++i)
?         if(smatch(str++,pat,patlen))
?             return i;
? }
?
? static smatch(str,pat,patlen)
? register char *str,*pat;
? int patlen;
? {
?     while(patlen-- &gt; 0 &amp;&amp; *pat != 0xff)
?         if(*str++ != *pat++)
?             return 0;
?     return 1;
? }
?
</screen>
<para>
Compile this program:
<screen>
    cc2 bt3.c -rs
</screen>
And link it:
<screen>
    c.link bt3.r -o=findstr -b=findstr
</screen>
The BASIC09 test program is:
<screen>
PROCEDURE btest
DIM a,b:STRING[20]
DIM matchpos:INTEGER
LOOP
INPUT "String ",a
INPUT "Match  ",b
RUN findstr(a,b,matchpos)
PRINT "Matched at position ",matchpos
ENDLOOP
</screen>
When this program is run, it should print the position where the
matched string was found in the source string.
</para>
</section>

<section>
<title>Example 4 - Quicksort</title>
<para>
The next example programs demonstrate how one might implement a
quicksort written in C to sort some BASIC09 data.
</para>
<para>
C integer quicksort program:
</para>
<programlisting>
#define swap(a,b) { int t; t=a; a=b; b=t; }

/* qsort to be called by BASIC09:
     dim d(100):INTEGER  any size INTEGER array
     run cqsort(d,100)   calling qsort.
*/

qsort(argcnt,iarray,iasize,icount,icsiz)
int  argcnt,     /* BASIC09 argument count */
     iarrary[],  /* Pointer to BASIC09 integer array */
     iasize,     /* and it's size */
     *icount,    /* Pointer to BASIC09 (sort count) */
     icsiz;      /* Size of integer */
{
     sort(iarray,0,*icount);  /* initial qsort partition */
}

/* standard quicksort algorithm from Horowitz-Sahni */
static sort(a,m,n)
register int *a,m,n;
{
     register i,j,x;
     
     if(m &lt; n) {
          i = m;
	  j = n + 1;
	  x = a[m];
	  for(;;) {
	       do i += 1; while(a[i] &lt; x);  /* left partition */
	       do j -= 1; while(a[j] &gt; x);  /* right partition */
	       if(i &lt; j)
	            swap(a[i],a[j])          /* swap */
		    else break;
	  }
	  swap(a[m],a[j]);
	  sort(a,m,j-1);                     /* sort left */
	  sort(a,j+1,n);                     /* sort right */
     }
}
</programlisting>
<para>
The BASIC09 program is:
</para>
<programlisting>
PROCEDURE sorter
DIM i,n,d(1000):INTEGER
n=1000
i=RND(-(PI))
FOR i=1 to n
d(i):=INT(RND(1000))
NEXT i
PRINT "Before:"
RUN prin(1,n,d)
RUN qsortb(d,n)
PRINT "After:"
RUN prin(1,n,d)
END

PROCEDURE prin
PARAM n,m,d(1000):INTEGER
DIM i:INTEGER
FOR i=n TO m
PRINT d(i); " ";
NEXT i
PRINT
END
</programlisting>
<para>
C string quicksort program:
</para>
<programlisting>
/* qsort to be called by BASIC09:
     dim cmemory:STRING[10] This should be at least as large as
                            the linker says the data size should
                            be.
     dim d(100):INTERGER    Any size INTEGER array.

     run cqsort(cmemory,d,100) calling qsort. Note that the pro-
                            cedure name run in the linked OS-9
                            subroutine module. The module name
                            need not be the name of the C func-
                            tion.
*/

int maxstr;    /* string maximum length */

static strbcmp(str1,str2)            /* basic09 string compare */
register char *str1,*str2;
{
     int maxlen;

     for (maxlen = maxstr; *str1 == *str2 ;++str1)
          if (maxlen-- &gt;0 || *str2++ == 0xff)
               return 0;
     return (*str1 - *str2);
}

cssort(argcnt,stor,storsiz,iaarray,iasize,elemlen,elsiz,
             icount,icsiz)
int argcnt;          /* BASIC09 argument count */
char *stor;         /* Pointer to string (C data storage) */
char iarray[];      /* Pointer to BASIC09 integer array */
int  iasize,          /* and it's size */
     *elemlen,        /* Pointer integer value (string length) */
     elsiz,           /* Size of integer */
     *icount,         /* Pointer to integer (sort count) */
     icsiz;           /* Size of integer */
{
/* The following assembly code loads Y with the first
   arg provided by BASIC09.  This code MUST be the first code
   in the function after the declarations.  This code assumes the
   address of the data area is the first parameter in the BASIC09
   RUN command. */
#asm
 ldy 6,s get addr for C storage
#endasm

/* Use the C library qsort function to do the sort. Our
   own BASIC09 string compare function will compare the strings.
*/

     qsort(iarray,*icount,maxstr=*elemlen,strbcmp);
}

/* define stuff cstart.r normally defines */
#asm
_stkcheck:
 rts dummy stack check function

 vsect
errno: rmb 2 C function system error number
_flacc: rmb 8 C library float/long accumulator
 endsect
#endasm
</programlisting>
<para>
The BASIC09 calling programs:  (words file contains strings to sort)
</para>
<programlisting>
PROCEDURE ssorter
DIM a(200):STRING[20]
DIM cmemory:STRING[20]
DIM i,n:INTEGER
DIM path:INTEGER
OPEN #path,"words":READ

n=100
FOR i=1 to n
INPUT #path,a(i)
NEXT i
CLOSE #path
RUN prin(a,n)
RUN cssort(cmemory,a,20,n)
RUN prin(a,n)
END

PROCEDURE prin
PARAM a(100):STRING[20]; n:INTEGER
DIM i:INTEGER
FOR i=1 TO n
PRINT i; " "; a(i)
NEXT i
PRINT i
END
</programlisting>
</section>

<section>
<title>Example 5 - Floating Point</title>
<para>
The next example shows how to access BASIC09 reals from C functions:
</para>
<programlisting>
flmult(cnt,cmemory,cmemsiz,realarg,realsize)
int cnt;            /* number of arguments */
char *cmemory;      /* pointer to some memory for C use */
double *realarg;    /* pointer to real */
{
#asm
 ldy 6,s get static memory address
#endasm

     double number;

     getbreal(&amp;number,realarg);      /* get the BASIC09 real */
     number *= 2.;                   /* number times two*/
     putbreal(realarg,&amp;number);      /* give back to BASIC09 */

}

/* getreal(creal,breal)
     get a 5-byte real from BASIC09 format to C format */

getbreal(creal,breal)
double *creal,*breal;
{
     register char *cr,*br;   /* setup some char pointers */

     cr = creal;
     br = breal;
#asm
*  At this point U reg contains address of C double
*                  0,s contains address of BASIC09 real
 ldx 0,s get address of B real

 clra clear the C double
 clrb
 std 0,u
 std 2,u
 std 4,u
 stb 6,u
 ldd 0,x
 beq g3 BASIC09 real is zero

 ldd 1,x get hi B mantissa
 and a #$7f clear place for sign
 std 0,u put hi C matissa
 ldd 3,x get lo B mantissa
 andb #$fe mask off sign
 std 2,u put lo C mantissa
 lda 4,x get B sign byte
 lsra shift out sign
 bcc g1
 lda 0,u get C sign byte
 ora #$80 tun on sign
 sta 0,u put C sign byte
g1 lda 0,x get B exponent
 suba #128 excess 128
 sta 7,u put C exponent
g3 clra clear carry
#endasm

}

/* putbreal(breal,creal)
     put C format double into a 5-byte real from BASIC09 */

putbreal(breal,creal)
double *breal,*creal;
{
     register char *cr,*br;   /* setup some pointers */

     cr = creal;
     br = breal;
#asm
*  At this point U reg contains address of C double
*                  0,s contains address of BASIC09 real
 ldx 0,s get address of B real

 lda 7,u get C exponent
 bne p0 not zero?
 clra clear the BASIC09
 clrb real
 std 0,x
 std 2,x
 std 4,x
 bra p3 and exit

p0 ldd 0,u get hi C mantissa
 ora #$80 this bit always on for normalized real
 std 1,x put hi B mantissa
 ldd 2,u get lo C mantissa
 std 3,x put lo B mantissa
 incb round mantissa
 bne p1
 inc 3,x
 bne p1
 inc 2,x
 bne p1
 inc 1,x
p1 andb #$fe turn off sign
 stb 4,x put B sign byte
 lda 0,u get C sign byte
 lsla shift out sign
 bcc p2 bra if positive
 orb #$01 turn on sign
 stb 4,x put B sign byte
p2 lda 7,u get C exponent
 adda #128 less 128
 sta 0,x put B exponent
p3 clra clear carry
#endasm
}


/* replace cstart.r definitions for BASIC09 */
#asm
_stkcheck:
_stkchec:
 rts

 vsect
_flacc: rmb 8
errno: rmb 2
 endsect
#endasm
</programlisting>
<para>
BASIC09 calling program:
</para>
<programlisting>
PROCEDURE btest
DIM a:REAL
DIM i:INTEGER
DIM cmemory:STRING[32]
a=1.
FOR i=1 TO 10
  RUN flmult(cmemory,a)
  PRINT a
NEXT i
END
</programlisting>
</section>

<section>
<title>Example 6 - Matrix Elements</title>
<para>
The last program is an example of accessing BASIC09 matrix elements.
The C program:
</para>
<programlisting>
matmult(cnt,cmemory,cmemsiz,matxaddr,matxsize,scalar,scalsize)
char *cmemory;       /* pointer to some memory for C use */
int matxaddr[5][3];  /* pointer a double dim integer array */
int *scalar;         /* pointer to integer */
{
#asm
 ldy 6,s get static memory address
#endasm

     int i,j;

     for(i=0; i&lt;5; ++i)
          for(j=1; j&lt;3; ++j)
               matxaddr[j][i]  *=  *scalar;  /* multiply by value */
}
#asm
_stkcheck:
_stkchec:
 rts

 vsect
_flacc: rmb 8
errno: rmb 2
 endsect
#endasm
</programlisting>
<para>
BASIC09 calling program:
</para>
<programlisting>
PROCEDURE btest
DIM im(5,3):INTEGER
DIM i,j:INTEGER
DIM cmem:STRING[32]
FOR i=1 TO 5
    FOR j=1 TO 3
        READ im(i,j)
    NEXT j
NEXT i
DATA 11,13,7,3,4,0,5,7,2,8,15,0,0,14,4
FOR i=1 TO 5
    PRINT im(i,1),im(i,2),im(i,3)
NEXT i
PRINT
RUN matmult(cmem,im,64)
FOR i=1 TO 5
    PRINT im(i,1),im(i,2),im(i,3)
NEXT i
END
</programlisting>
</section>
</appendix>