Run Time Libraries


Charm provides a number of run time library modules for use by application developers. The source files for these libraries are held in the charm:lib.src directory which currently contains:

Error library
Dynamic string library
Program library
Runtime library
Output stream library
Input stream library

RISC OS library (manage variable creation, setting, reading and deletion)
Efficient allocation of small memory blocks
Maths library with veneer for ARM assembler floating point maths functions
Debug support
Veneer for VDU calls
Veneer for RISC OS WIMP SWIs (see PRM)

Veneer for toolbox resources
Handling of WIMP events from toolbox reported back by RISC OS

Each library module exports a number of public constants, records and procedures.  Optionally records are used in larger libraries to categorise procedures into logical groupings. Implementations are in a mixture of Charm source code and in-line ARM assembler calls to RISC OS SWIs (software interrupts).

In order to use procedures defined in a library the module needs to be imported. Generally speaking it is necessary to qualify a reference to a library definition with the module name e.g. Maths.random () or Rtl.IN_STR_EOF.

The distributed Charm code uses upper case for constants, camel case for record and module names and lower case with underscore separators for procedure names.

For documentation on the RISC OS Programmers Reference Manuals see which define the SWI calls used to back the implementation of the Charm startup code and libraries see:

http://foundation.riscos.com/Private/manuals/PRMs/

For more information on the toolbox see:

http://foundation.riscos.com/Private/manuals/Toolbox/

Program Startup and Termination


The author of a Charm program must provide a single globally accessible ~start procedure in one of the program modules which is invoked by the Charm startup code after preparing the program environment. The signature of this procedure is similar to other languages and must match either:

    export proc ~start () {}
or:
    export proc ~start (int argc, ref array ref array char argv) {}

Here argc is the number of command line parameters passed to the program, and argv is a reference to an array of strings which contains each parameter. The runtime ensures that the array contains an additional terminating entry with nil content so that the value of argc is not essential to its processing.

Any non-positional command line switches (parameters starting with '-' e.g. -module)  can be extracted and processed using the Rtl.Parse.arguments procedure by passing an array of switch definitions (switches) followed by the number of entries in the array e.g. :-

ref array ref array char za := Rtl.Parse.arguments (argv, switches, 3);

Note that as a result of this call za is a reduced (possibly empty except for nil terminator) array of positional parameters with the position independent switches excised.

The procedure declaration for the Rtl.parse.arguments procedure and the Switch record definition can be found in the system rtl source file :-

export record Switch
{
    int                    type;
    ref array char         name;
    ref proc (ref Value) callback;
}

The first record field type is the switch type, and can take one of the following values :-

  • PT_FLAG flag switch e.g, -nolink
  •   PT_STRING string switch with following parameter e.g. -object outfile
  •   PT_VALUE value switch with following numeric parameter e.g. -mode 23
  •   PT_FLOAT float switch with following floating point parameter e.g. -x 1.234e-3

The second field name is the switch name.

The third field callback is a pointer to a procedure that is called back with the value of the switch.

For example, the command table and associated routines used by the Charm assembler is :-

declare proc p_noobject (ref Rtl.Value);
declare proc p_list     (ref Rtl.Value);
declare proc p_object   (ref Rtl.Value);
array [] Rtl.Switch switches = (
     (Rtl.PT_FLAG  , "noobj" , p_noobject),
     (Rtl.PT_STRING, "list"  , p_list),
     (Rtl.PT_STRING, "obj"   , p_object));

const SWITCHES = size (switches) / size (Rtl.Switch);

proc p_noobject (ref Rtl.Value value)
{
    Data.fp_object := false;
}
proc p_object (ref Rtl.Value value)
{
    Data.fp_object := true;
    object_file := value.string ();
}
proc p_list (ref Rtl.Value value)
{
    Data.fp_list := true;
    list_file := value.string ();
}

The program can be terminated with an exit code by calling the Program.exit procedure e.g.

Program.exit (0);

Any open files are automatically closed before the program finally exits. The return code is available after program termination in the Sys$ReturnCode variable with a value of 0 indicating successful completion.

Miscellaneous Routines


The program run time library divide and modulo routines provide support for division and remainder in the Charm language.  Calls to them are automatically generated by the compiler for the / and mod operators. Similarly the run time library allocate and release procedures provide support for the new and delete Charm keywords.

Charm Register Usage

The Charm language uses the ARM dedicated and semi-dedicated registers r0 - r15 as follows :-

  • r0 - r6 - general purpose registers
  • r7 - r11 - register variables
  • r12 (dp) - data section pointer
  • r13 (sp) - stack pointer
  • r14 (rp) - return link pointer
  • r15 (pc) - program counter

When Charm calls a procedure, the first four parameters are evaluated and stored in registers r0 - r3 respectively.  Additional parameters are pushed on the stack in reverse order e.g.

doit (1, 2, 3, 4, 5, 6);

generates the following code :-

mov r14,#6
str r14,[sp,#-4]!
mov r14,#5
str r14,[sp,#-4]!
mov r3,#4
mov r2,#3
mov r1,#2
mov r0,#1
bl _doit
add sp,sp,#8

It should be clear from this example how to access the parameters from assembly language source code i.e. they are available in registers, and at positive offsets on the stack starting from offset 0 on entry.  It will be necessary to suitably bias the offsets if registers are pushed at the start of the called routine prior to accessing a stacked parameter.

Called Charm procedures and assembly language routines which return a result are expected to place it in register r0 prior to returning control to the caller.

Registers r0 - r6 inclusive may be used in a called routine without restriction i.e. they do not need to be preserved.

Registers r7 - r11 inclusive must be saved if they are used by a Charm procedure or an an assembly language routine, as they may be used to hold values in registers at higher caller levels which are retained by convention over procedure calls.

The Charm assembler will automatically use the data register pointer r12 (dp) to access data variables.  This register should not normally be altered by an assembly language routine.  If it is altered, data variables cannot be accessed, and it "must" be restored before returning control to any Charm caller.

The stack register pointer r13 (sp) may be used as needed by any called routine, provided the stack limit is not exceeded (set to 10K in the RTL).  If it is altered, it must be restored before returning control to the caller.  Larger areas of memory should be allocated statically, or using the heap routines, and not allocated directly on the stack.

The return pointer r14 (rp) contains the return address on routine entry.  This is usually pushed on the stack and restored into the program counter at the end of the routine.  It may be left untouched within short routines, but note that its contents will be altered by calls to other routines or to software interrupts.

The program counter r15 (pc) is dedicated to pointing at instructions to be executed, and has no extra significance within the Charm language.

When dealing with floating point values, Charm uses the in-built floating point emulator (FPE) contained within RISCOS. This has a set of instructions that use floating point registers f0 - f7 inclusive. Like the ARM registers f0 – f3 are used to pass the first four floating point parameters, however none of the registers are reserved and so do not need to be preserved across procedure calls.

If the ARM chip has a VFP unit then the programmer has the option of compiling for the VFP instruction set which is much faster as it is not emulated. In this case the available register set is d0 – d15 double length (64 bit) registers shadowed by s0 – s31 single length (32 bit) registers. There is some loss of precision compared to the FPE which uses extended precision operations (96 bit), but this is more than compensated for by the order of magnitude increase in execution speed.

Make a Free Website with Yola.