home *** CD-ROM | disk | FTP | other *** search
-
- Linking with C and Assembler
-
-
-
-
-
-
- This Appendix is intended for assembly-language
- and C programmers and details linkage with
- these languages, together with the memory maps
- and register usage of compiled programs. It is
- also for users with some knowledge of assembly
- language who wish to use the debugger.
-
-
- Code Generation
-
- A BASIC source program is converted into true
- machine-code, there are no P-codes or
- interpretative run-times. The code produced
- distinguishes between the program area and the
- data area. The program area is what is written
- to the disk or to memory and is position
- dependent code, relocated either by the
- AmigaDOS loader in the case of disk files or
- the code generator itself with programs
- compiled directly to memory. All program code
- executes in user mode and is compatible with
- 68010, 68020, 68030 and 68040 processors.
- Figure F-1 shows the overall memory map of a
- compiled program. Note that the heap and the
- machine stack are not shown here.
-
-
- Register Usage
-
- Several registers are committed to special
- purposes within a compiled program. These are:
-
-
- A3 - Library Pointer
-
- In order to minimise the space and time taken
- for run-time library calls, register A3 is
- dedicated to point to either a run-time jump
- block (if using the shared library( or to the
- run-time library code hunk itself, with a $8000
- offset to allow a maximum of 64k for the whole
- library.
- Library calls from within the compiled code are
- of the form
-
-
- JSR -offset(A3)
-
- Library calls from within the library itself do
- not use A3, they use BSR statements and are
- resolved by the code generator during
- compilation.
-
-
- A4 - Local Variable Stack Frame
-
- At the very beginning of functions and sub
- programs a LINK instruction is done to allocate
- space on the stack for local variables (and
- function results) and to establish a register
- that can be used for accessing parameters. Only
- space for numeric variables are immediately
- allocated using LINK; arrays and string
- descriptors are allocated afterwards using a
- library call. For example a sub-program which
- has one local integer variable starts with the
- instruction
-
-
- LINK #-2,A4
-
- Functions and sub programs finish with a
- corresponding
-
-
- UNLK A4
- RTS
-
-
-
- A5 - Data Area Pointer
-
- The startup code of a compiled program sets up
- A5 to point to the data area of the program
- which of course is always RAM. At the start of
- the area are the run-time globals, followed by
- the descriptor table (descriptors are described
- later). Next is the global variable area, used
- for storing numeric variables. There is a 32k
- limit on the total size of these globals, but
- is would take a massive unstructured BASIC
- program to require such a number of globals.
- The static arrays follow this; there is no 32K
- limit on these, but those within this array are
- accessed more quickly.
-
-
- A6 - Maths Stack
-
- This is a special stack used for storing
- intermediate results of numeric calculations.
-
-
- A7 - Machine Stack
-
- The regular machine stack used for return
- addresses and local variables (using A4). It is
- left at the place provided by the calling
- program unless the stack is not as large as the
- MINSTACK option value when it will be allocated
- its own memory.
-
-
- D7- Top of Stack
-
- This register is used to hold the current value
- from an intermediate expression, be it a single
- precision numeric value (in the ST version), a
- long or short integer. For strings D7 is used
- to hold a pointer to the descriptor for that
- string.
-
-
- D6 - Top of Stack Extension
-
- This register is used in conjunction with D7 to
- hold long results of type double on the ST
- version.
-
- Code Hunk 1
-
-
-
-
- Compiled Program
-
-
-
- Code Hunk 2 or hbasic2.library
-
-
-
-
- Run-time Library
-
-
-
- Global Area A5
-
-
- Run-time Globals
- Descriptor Table
- Global Variables
-
- Static Arrays
-
- Maths Stack
-
-
- The Heap and Descriptors
-
- Crucial to the operation of MaxonBASIC compiled
- programs is the area of memory known as the
- heap. BASIC is one of two common languages (the
- other is LISP) that requires dynamic garbage
- collection. Owing to the way strings in the
- language work it is necessary to allocate
- memory for them as required, then, when it runs
- out, to re-use all the memory no longer needed.
- This re-allocation is known as garbage
- collection. Many compilers and even some
- interpreters do not garbage collect and, as a
- result, certain operations can cause out-of-
- memory errors even though there is a lot of
- unused memory left.
- MaxonBASIC has a very advanced memory
- management system (which is hidden from the
- user normally) whereby any memory allocation
- request can cause a garbage collect to occur in
- order to satisfy the request. There are two
- schemes, the old heap (KEEP) option and the new
- dynamic heap option.
- With the Old Heap, the heap itself is a single
- large block of memory from which allocations
- for string variables and arrays take their
- memory. At the bottom of the heap (low memory)
- are all the string variables, while at the top
- are all the arrays. Strings work their way
- upwards, while arrays grow downwards. Should
- they ever meet, a garbage collect occurs which
- deletes all unused strings and moves existing
- ones around as required. It is important to
- note that arrays never move in memory as a
- result of a garbage collect; a dynamic array
- can only move when you REDIM or ERASE any other
- array; static arrays don´t move at all.
- With the new dynamic heap, arrays are stored in
- their own areas of memory. Large dynamic arrays
- have their memory individually allocated from
- the operating system and small dynamic array
- allocations are pooled together. Initially
- strings are allocated on a heap like the one
- used by the Old Heap scheme but if this fills
- up further mini-heaps may be allocated.
- As items on the heap are liable, without
- warning, to move about, ordinary pointers are
- useless.
- For this reason strings and arrays are accessed
- via descriptors, which exist normally in the
- global area and which themselves contain actual
- pointers to the data on the heap. As the memory
- manager knows where all the descriptors are it
- can update their pointers when a garbage
- collect occurs.
- Incidentally, the garbage collector itself is
- very fast; for example it can compact around
- 350k of fragmented heap in under 2 seconds on a
- standard 8MHz 68000, so there is never any
- noticeable delay in the running of a program
- should it have to garbage collect.
-
-
- Memory Formats
-
-
-
-
- Single-precision Floating Point
-
- These use the Motorola Fast Floating Point
- (FFP) format. The format is unusual by most
- standards, but was designed solely with the
- 68000 architecture in mind, and is as a result
- very fast. It is the same format as used by ST
- BASIC, so random access files created with the
- interpreter should be directly usable within
- the compiler.
- The sign bit is 0 for positive numbers and 1
- for negative numbers. The mantissa has an
- implied binary point at bit 32 and thus ranges
- in value from 0.5 to <1.0. The exponent is held
- in excess -64. The number zero is represented
- with 32 bits of zero.
-
-
- Double-precision Floating Point
-
- These use the IEEE format double-precision
- floats, each occupying 8 bytes, i.e. two longs.
- The sign bit is 0 for positive numbers and 1
- for negative numbers. The mantissa has an
- assumed bit of 1; if present it would be at bit
- 52. The exponent is held in excess-1023. The
- number zero is represented with 64 bits of
- zero.
-
-
- Linkable code
-
- Although this version of BASIC has many
- powerful features and extensions, occasionally
- there is a command or function that is not part
- of standard BASIC. Additionally, a specific
- task can be very time critical and need all the
- speed it can get. For these reasons, MaxonBASIC
- has the ability to call C or assembly language
- subroutines.
- BASIC has a much more complex environment and
- initialisation procedure than C, so it is only
- possible for a BASIC program to call C
- functions; it is not possible for C programs to
- call BASIC code. In addition there are certain
- conventions which make this process more
- complex, so calling assembly-language code
- allows much more flexibility.
-
-
- External Definition
-
- In order to call C or assembly-language the
- compiler allows a program to use a special sort
- of DECLARE statement to indicate that a
- function or sub-program is written in C or
- assembler. Using this will automatically cause
- the compiler to produce linkable (rather than
- executable) code. If you haven´t used the
- Output to (TO) compiler option, the name of the
- output file with have .o added to it (minus the
- .bas extension).
-
-
- The DECLAREÉCDECL statement
-
- The special form of the DECLARE statement is as
- follows:
-
-
- DECLARE {SUB|FUNCTION} subname CDECL
- [ALIAS "externalname"]
- [(param_list)]
-
- The ALIAS clause is optional. The string
- following indicates the exact external name for
- the function. If an ALIAS clause is not
- specified the name (without any type specifier)
- is made lower-case and an under-score (_)
- character inserted at the front. This is
- suitable for linking with SAS/C compilers. Thus
- a function called cname% or a sub-program
- called CNAME will both use the name _cname, if
- there is no ALIAS clause.
- The parameter list is like that of a BASIC sub-
- program or function. That is, it specifies the
- types of the parameters, how many parameters
- there are, and whether the parameters are
- passed by value or reference. Normally the
- BYVAL keyword should be used to indicate that
- the parameter is passed by value (or VAL for
- compatibility with MaxonBASICÊ1).
- It is possible that the name of your external
- routine will clash with a symbol in the BASIC
- run-time system. None of the BASIC routines
- start with an underscore character so we
- recommend their use on your external names to
- avoid possible clashes.
- The use of such a DECLARE statement will
- automatically produce linkable code without the
- need for a compiler option. Compiling a
- linkable program to memory will give an error
- message
-
-
- Calling C Functions
-
- C and assembler functions are called in a very
- similar way to the way standard BASIC sub-
- programs are called.
- If you are using C, call the function or sub-
- program in the same way as you would in C. Make
- sure that the types of the parameters in the
- BASIC DECLARE statement match the sizes of the
- parameters in the C code (taking particular
- care with integer sizes). The parameters should
- be in the same order in both languages.
- If a parameter is a pointer and you normally
- use the C & operator when calling the function,
- omit the BYVAL keyword in the parameter list.
- The address of the BASIC variable will then be
- passed to the C code.
-
-
- Calling Assembly Language Functions
-
- For assembly language, any parameters are
- coerced to the appropriate type and pushed on
- the machine stack in reverse order (i.e. C
- order). Registers A3-A6 must be preserved and
- any return value should be in D0 (or D0 and D1
- if a double, D0=high longword). A string return
- value should be in D0 and be a pointer to a
- null-terminated string.
- You will need to ensure that your routine
- ´looks like´ a C function. This involves
- reading parameters from the stack and using an
- RTS to return. You must leave the parameters on
- the stack. The code should be assembled to
- linkable code and the necessary routines
- exported using XDEF. We recommend that you use
- the ALIAS facility in the DECLARE statement;
- otherwise you must use case sensitive code and
- ensure that the names you export are lower case
- and start with an underscore.
-
-
- Parameters
-
- If numeric parameters are passed by value then
- their value (16-, 32- or 64-bits) is pushed on
- the stack. Double-precision parameters are
- passed in MaxonBASIC 3 order as required by
- SAS/C; not in the same order as when using the
- BASIC library.
- If variable parameters are passed then the
- address of the variable is pushed.
- Strings and arrays are passed as the address of
- their descriptor. The descriptor format is
- private and subject to change so utility
- routines are provided to convert them into a
- more useful form.
-
-
- Linking
-
- You should end up with a linkable file from the
- BASIC compiler and a linkable file from the C
- compiler or assembler. They should be linked
- together using an Amiga format linker such as
- Slink, Blink or even Alink. You may also need
- to specify C compiler libraries if your C
- functions have referred to any.
- With SAS/C 6 you should use the compiler
- options:
-
-
- nosdata noscode nostkchk parameters=stack
-
- which forces 32-bit variable references, 32-bit
- code and disables stack checking, and ensures
- that parameters are passed on the stack. If
- when you link there are undefined symbols this
- normally means your C is calling on a library
- routine. You will then have to specify the
- required C library when you link again, but
- take care that the library code does not assume
- any register values (e.g. for base-relative
- globals) or initialisation code (e.g. the Unix
- file system).
-
-
- C-callable Utility Routines
-
- All utility routines may be called from
- assembly-language but only a few may be called
- from C, due to register requirements. The C-
- callable routines can be found in the file
- hbcutil.o on disk 2 which should be linked with
- your other code. You must generate stand-alone
- code to be able to use these routines.
-
- long getbasicstr(d,&length)
- long d; long length;
-
- This should be passed a string descriptor
- address and the address of where the length
- should be stored (note that strings may be
- >64k). The return result is the address of the
- string which will not be null-terminated and
- you are not allowed to write to the string
- area.
-
- long getbasicarray(d,dims,&length)
- long d; short dims; long length;
-
- This should be passed an array descriptor, the
- expected number of dimensions and the address
- of where the length should be stored. The
- return result is the address of the start of
- the array contents, guaranteed to be even. The
- length will be the total length in bytes taken
- up by the array elements. You are allowed to
- write to the array area.
-
-
- Assembler utility routines
-
- The file hbasm.i in the linking directory
- contains various definitions for use by code
- called from BASIC. Your code has to decide
- whether it is being used by a stand-alone
- program or a shared-library program; if it is
- the latter than the symbol HBLIB should be
- defined before INCLUDEing hbasm.i.
- Most BASIC runtimes need register A5; this is
- why C code cannot call them. If using a shared-
- library program then A3 is also required. The
- macro CALLBAS is provided to call a BASIC
- runtime. Available routines are as follows:
-
-
-
-
- takes: A0 the array descriptor
- D0.W the number of
- dimensions the passed
- array should have
- returns: A2 the address of the
- first element in the
- array
- D4.L the total length in
- bytes taken up by the
- array elements
- register D0,D4,
- s used: A1,A2
-
-
-
-
- takes: A0 the string descriptor
- returns: A1 the address of the
- string
- D4.L the length of the
- string
- register A0,A1,D4
- s used:
-
-
-
-
- takes: A0 the string descriptor
- of a string-variable;
- the descriptor of a
- string value will
- have no effect if
- passed to make_string
- A1 the address of your
- copy of the string
- D4.L the length of the
- string to be created
- register D0-D4,
- s used: A0-A2
- This call allows a string parameter passed by
-
- variable to be safely modified by the caller. A
-
- value string parameter passed to make_string
-
- will have no effect.
-
-
-
-
-
- takes: D0.L bytes required
- D1.L type of memory (cf.
- AllocMem)
- returns A0 allocated area
- register D0/D1/A1
- s used:
- This is a safe way to allocate any type of
-
- memory and chain it to BASICs memory list, so
-
- it will be de-allocated on program termination
-
- (if not already de-allocated).
-
-
-
-
-
- takes: A0 result from
- safe_malloc
- register D0-D1/A0-
- s used: A1
-
-
- Re-Entrant Code
-
- If you wish a compiled program to remain Pure
- (in the AmigaDOS sense) then it is your
- responsibility for your external routines to
- follow the necessary rules. We´ve done our bitÉ
-
-
- Accessing BASIC global variables from assembly
-
- Using the Export Variables names option from
- the Advanced Options requester (or via the
- EXPORTS option) the names of your global
- variables will be exported as absolute symbols
- so that you can access them via the global
- register. As the BASIC variable terminators
- (%&!#$) are not allowed as parts of normal
- assembly language names, the terminators are
- changed to I, L, F, D and S respectively. The
- names are prefixed with _ for normal variable
- and @ for arrays. The variable names are forced
- to upper case. Conversely sub-program and
- function names are forced to lower case.
- Here are some BASIC names are the corresponding
- name to use in assembly language:
-
- i% _II
- fred& _FREDL
- mary! _MARYF
- john%() @JOHNI
- MARY!() @MARYF
-
-
- Linked Examples
-
- Here is an artificially simple example of the
- process, starting with the BASIC program which
- uses an external function to compare two short
- integers:
-
-
- DECLARE FUNCTION cequal% CDECL (BYVAL p1%,BYVAL
- p2%)
- PRINT cequal%(1,2),cequal%(10,10)
-
- Now the C function:
-
-
- short cequal(a,b)
- short a,b
- {
- return (a==b)
- }
-
- and the equivalent (Devpac) assembly-language:
-
-
- opt l+
- xdef _cequal
- _cequal move.w 4(sp),d0
- cmp.w 6(sp),d0
- seq d0
- ext.w d0
- rts
-
-