home *** CD-ROM | disk | FTP | other *** search
/ ARM Club 3 / TheARMClub_PDCD3.iso / hensa / programming / adhesive_1 / goodies / basicC / doc / Text < prev   
Encoding:
Text File  |  1994-09-29  |  13.4 KB  |  254 lines

  1. basicC and calling APCS routines
  2. written by George Taylor
  3.  
  4. Disclaimer
  5. This information is provided in good faith but the author cannot accept any liability for any loss or damage resulting from the use of this information.
  6.  
  7. Introduction
  8. APCS (ARM Procedure Call Standard) is a mechanism by which procedures compiled from high level languages (e.g. C, Pascal) and those written in assembler can all call each other. The programmers reference manual (volume 4) gives details of APCS. (In fact under RISC-OS we use a variant of APCS known as APCS-R.)
  9.  
  10. Strictly this document does not describe APCS but describes APCS when used in a C like environment under RISC-OS.
  11.  
  12. Most APCS routines will at some point use the Shared C Library. This is so they can perform stack extension when the stack overflows and allocate space using the standard C routine malloc().
  13.  
  14. The Shared C Library needs to keep static data which is local to each program using the Shared C Library. Therefore this data must be kept with the program, not with the Shared C Library. The Shared C Library access this data using some internal information stored at the stack limit (sl) (given by r10 in APCS-R). This means when calling APCS procedures sl (r10) must contain the correct value.
  15.  
  16. C Programs
  17. Compiled languages have no problem with this as the compiler takes care of it all. When the C program initialises it set's up the stack limit and other register using an initialisation procedure within the Shared C Library.
  18.  
  19. Assembler programs
  20. Assembler programs may either setup with the Shared C Library themselves (fairly complex) or if linked with C programs then the C part can call the assembler. Writing APCS conformant routines in assembler means you must follow certain rules though. These rules are explained in the PRM but information relating to the passing of arguments such as floating point values is not given in the PRM. The section ‘Writing APCS routines in assembler’ later in this document attempts to describe how to do this.
  21.  
  22. BASIC programs and the basicC object
  23. BASIC programs can not normally call APCS routines. It is possible to have some BASIC assembler which initialises with the Shared C Library but it is still not possible to directly call the APCS routines from BASIC without this assembler.
  24.  
  25. With the basicC Adhesive object though, it is possible to call APCS routines from BASIC and from BASIC assembler without you having to know how to initialise with the Shared C Library.
  26.  
  27. This allows you to use Shared C Library routines such as malloc() and free() directly from BASIC.
  28. (The only penalty you pay is you must not alter HIMEM or END and therefore cannot use other third party memory allocation methods.)
  29.  
  30. Additionally you can call Adhesive objects which operate in an APCS environment (e.g. objects written in C). Alternatively you can call inline BASIC assembler which is APCS conformant.
  31.  
  32.  
  33.  
  34. BasicC
  35. This Adhesive object (number 142) provides procedures to allow you to call APCS routines from BASIC using a CALL statement. An example of it's use is provided in the ‘Example’ directory.
  36.  
  37. You may distribute the basicC object with your own programs once you have registered with Adhesive.
  38.  
  39. basicC provides the following entry points:
  40.     0       basicC_StringTo0
  41.     1       basicC_StringFrom0
  42.     2       basicC_ToDouble
  43.     3       basicC_FromDouble
  44.     4       basicC_Init    (must be used from CALL statement)
  45.     5       basicC_CCall    (must be used from CALL statement)
  46. Entry points 0-3 perform simple conversions these are documented in the file ‘142.100.Info’ - i.e. the information file supplied with the object.
  47.  
  48. basicC_Init
  49. This routine must be called using a USR statement from BASIC. It returns the address of a branch table which gives access to routines in the Shared C Library. The entries in this branch table are described in the PRM and are also listed in the file ‘tools.ShLib’ supplied with Adhesive. The example provides a procedure to convert from the entry number given in the ‘tools.ShLib’ file to an address in the branch table.
  50.  
  51. This routine initialises with the Shared C Library. You use this call as follows:
  52. 1    Register with Adhesive and request the basicC object.
  53. 2    Drop HIMEM by at least 16K to provide some initial workspace for the Shared C Library. (e.g. HIMEM = HIMEM - 1024*16). Remember not to change HIMEM inside a procedure.
  54. 3    Call basicC_Init (e.g. VectorBase = USR basicC_init).
  55. 4    You are now ready to call APCS routines from BASIC. Do not alter HIMEM, END or your task's wimpslot after making this call.
  56. The Shared C Library is told it can extend your task's wimpslot if it runs out. For example if you allocate large blocks using malloc().
  57.  
  58. basicC_CCall
  59. Performs a C function call. This is done by parsing the passed arguments and building up a APCS conformant stack frame. This call is re-entrant so if you call some C code which then chains a BASIC program which calls some more C code....then this call won’t be the cause of your errors!
  60.  
  61. If you are not using BASIC64 then conversion between double and BASIC 5byte floating point numbers occurs.
  62.  
  63. The syntax is as follows:
  64.  
  65. CALL basicC_CCall, routine%, return, a1,a2,a3,.....,an
  66. Where,
  67. routine%    address to branch to (this must be integer)
  68. return    this is updated to the return value
  69. a1..an    arguments
  70.  
  71. Argument types allowed are:
  72.     Type    Examples        Description
  73.     byte    ?a  array%?3    8bit character, passed to C as integer use for char,int.
  74.  
  75.     integer    b%  word%        32bit word, passed to C as integer
  76.         name%(n)        use for char,int and pointers.
  77.     integer    !c c!3 c%!2    32bit word, passed to C as integer
  78.                 use for char,int and pointers.
  79.  
  80.     integer array    d%()        Array of 32bit words, passed to C as pointer to array data. NB More than one subscript possible, subscripts are same order as in C.
  81.  
  82.     string    name$        String. Converted to NULL terminated string,
  83.                         list$(3)        passed to C as a pointer.
  84.  
  85.     string    $id        String. Converted to NULL terminated string,
  86.         $(id+5)        passed to C as a pointer.
  87.  
  88.     real    name        Floating point value, passed to C as
  89.         name(4)        double (IEEE standard).
  90.  
  91. The additional argument type below can be used under BASIC64.
  92.  
  93.     real array    name()        Passed to C as pointer to double.
  94.  
  95. Return value types allowed:
  96.  
  97.     integer    r% ret%!4        32bit integer, this must be word aligned (e.g. ret%!2 not allowed if ret% word aligned)
  98.                                             Use for C routines which return void,int,char or a pointer.
  99.  
  100.     real    name        Floating point value, converted from double
  101.         name(7)        if not using BASIC64.
  102.  
  103.     string    name$        String. Converted from pointer to NULL
  104.                         list$(7)        terminated string. Use if C function returns char*. String is truncated to 255 characters if necessary. If the C function returns NULL the string is set to be an empty (0 length) string.
  105.  
  106.     string    $ident        String. Converted from pointer to NULL
  107.         $(ident+3        terminated string. Use if C function returns char*.
  108.                                             If the C function returns NULL the string is set to be an empty string ie just a ASCII 13 terminator. You must ensure the space at ident is large enough to hold the string and the ASCII 13 terminator.
  109.  
  110. Note1: There is a fixed size (currently 1K) buffer for string conversion. An error is given if this is exceeded. Strings passed as arguments are only kept during the C call. They are destroyed when the C call returns.
  111. Strings passed to C are always word aligned.
  112.  
  113. Note2: In ordinary BASIC (ie not BASIC64) basic 5byte values are converted to the double format and vice versa by truncation (no rounding). Double values which are too large/small are converted to the largest BASIC 5byte value or 0.
  114.  
  115. Note3: You may omit the return value and all arguments to call a C procedure void foo(void) for example. If you wish to pass arguments you must pass a variable for the return value. If the C routine does not return
  116. a value (ie void) then the return value is undefined.
  117.  
  118. Warning:
  119. When using CALL if you pass an undefined variable BASIC simply creates it. This can easily cause address exceptions and other nice errors if for example you drop the percent on a integer variable so basicC_CCall thinks it is a 5byte real not a 4 byte integer.
  120.  
  121.  
  122. Writing APCS routines in assembler
  123.  
  124. This section describes how to write assembler routines which can be called from C and can call C routines. How the C (and Pascal and hopefully other compilers) pass arguments to procedures is described along with how C structures and Pascal records are organised.
  125.  
  126. C structures (and Pascal records)
  127. Structures can contain several primitive types. These are integer, pointer, character, float/double and arrays of these types. When you define a structure the compiler does not reorder the items, the elements of the structure appear in memory in the order you define them.
  128.  
  129. Each of these types is the following size:
  130.     integer / pointer    32bits word aligned
  131.     float        32bits word aligned
  132.     double        64bits word aligned
  133. Arrays of the above types simply consist of many of the same type one after another.
  134. Characters are the exception. Characters which are next to each other in memory are packed together (byte alignment), the first character will have word alignment. If a single character is ‘sandwiched’ between two integers then it will occupy a whole word. Strings are simply arrays of characters. (In Pascal the packed keyword has no effect on arrays of characters.)
  135.  
  136. Arguments
  137. Arguments to procedures are always passed through registers r0-r3 if possible and then are placed on the stack. An argument of type character is placed in a register on it's own - no packing occurs. Floating point values are passed through integer registers (the APCS standard permits the passing of floating point values in f0-f3 but this C compiler does not do this - partly due to C's variable argument lists). Floating points double values will therefore take up two registers. It is possible for a double to be split between r3 and the stack.
  138.  
  139. Arguments which are placed on the stack are placed such that the last argument is pushed on the stack first. This means the last thing on the stack before the routine is called is the 5th argument or the second half of the 4th argument if the 4th argument it a double.
  140.  
  141. Below is some example code which demonstrates how to perform stack checking and stack extension.
  142.  
  143. ; example of writing assembler for use in APCS environment
  144.  
  145. ; in STM/LDM instructions rx..ry indicates any combinations
  146. ; (0 or more) of x..y may be present
  147.  
  148. ; x$stack_overflow and x$stack_overflow_1 are provided by
  149. ; the run time kernel
  150.  
  151.  
  152. ; this next block identifies this routine
  153. ; should a backtrace be required
  154.  
  155.         =       “procedure name”,0
  156.         ALIGN
  157.         &       &FF0000xx  ; xx is positive offset from this word back to start of string
  158. ; eg  “abcde”,0 needs a value of &FF000008 (remember to include padding from ALIGN)
  159.  
  160. procedure
  161.         MOV     ip,sp
  162.         ; optional instruction to save arguments on entry
  163.         ; STMDB   sp!,{r0..r3 }
  164.         STMDB   sp!,{r4..r9 ,fp,ip,lr,pc}
  165.         SUB     fp,ip,#4
  166.  
  167. ; only one of the following 3 pieces of code should be used next
  168. ; depending on amount of stack used etc
  169.  
  170. ; (1) procedure will use no more than 512 bytes of stack and
  171. ;     will not call an external procedure
  172. ;     (internal routines can be considered as part of this one
  173. ;     provided they use a fixed amount of stack)
  174.  
  175.         ; no code required
  176.         ; guaranteed 512 bytes of space left on stack
  177.  
  178.  
  179. ; (2) procedure will use no more than 256 bytes of stack and
  180. ;     may call an external procedure
  181.  
  182.         CMP     sp,sl
  183.         BLLO    |x$stack_overflow|
  184.  
  185.  
  186. ; (3)  (a) procedure may use more than 512 bytes of stack
  187. ;          (and may or may not call external procedure)
  188. ;      OR
  189. ;      (b) procedure may use more than 256 bytes of stack and
  190. ;          may call external procedure
  191.  
  192.         SUB     ip,sp,#amount_of_stack_space_needed_in_bytes
  193.         CMP     ip,sl
  194.         BLLO    |x$_stack_overflow_1|
  195.         ; typical instruction to drop sp for local space
  196.         SUB     sp,sp,#amount_of_stack_space_needed_in_bytes
  197.         ; more code which uses local space,
  198.         ; always accessed using positive offsets from sp
  199.         ; eg STR r0,[sp,#positive_offset]
  200.  
  201.  
  202. ; main procedure body
  203. ; to access arguments which may have been pushed on stack
  204. ; also access with -ve offsets from fp
  205. ; eg LDR r0,[fp,#negative_offset]
  206.  
  207. ; remember to count stack use of internal subroutines
  208. ; as part of this procedure
  209.         BL      internal_subroutine
  210.  
  211. ; when calling an external procedure,
  212. ; sl,sp must be valid
  213.         BL      external_procedure
  214. ; on return:  r0-r3,ip,lr may be corrupted;   r4-r9,sl,fp are preserved
  215. ;             PSR flags are preserved
  216. ; (lr corrupted implies it may contain anything, not just the return
  217. ;  address setup by the BL)
  218.  
  219.         ; more code
  220.         ; must preserve sl,fp
  221.         ; may corrupt r0-r3,ip,lr
  222.         ; may corrupt r4..r9 but only if they are stacked at entry/exit
  223.  
  224.  
  225. ; when returning sl,fp must be valid
  226.  
  227.         MOV     r0,#return_value
  228. ; return with value in r0
  229.         LDMDB   fp,{r4..r9 ,fp,sp,pc}^
  230.  
  231.  
  232. EXTRA NOTES:
  233.  
  234. In leaf procedures (ones that call no other external procedures)
  235. various optimisations can be made to the entry/exit sequence.
  236.  
  237. eg
  238. procedure
  239.         LDR     r0,[r0,#Offset]
  240.         MOVS    pc,lr
  241.  
  242. It is important that a procedure always returns the flags using lr
  243. (usually stacked) and not the flags on entry. Consider the following
  244. example of a tail-optimised procedure calling an external procedure:
  245.  
  246. procedureA
  247.         CMP     r0,#value
  248.         MOVLO   r1,#constX
  249.         MOVHS   r1,#constY
  250.         B       procedureB
  251. If procedureB was to restore the flags it saw on entry
  252. (not those in lr) then it will return to A’s caller with the
  253. wrong flags.
  254.