Thanks for the support everyone. Work has been eating all my time lately, so I haven't had a chance to post anything.
I've been working on notes for the GCC port, and found a few minor bugs, so no patches yet. Sorry.
I do however have some notes and more sample code that people have been asking for.
Primitive Data Types
====================
These are four primitive data types supported by the TMS9900 patch. These types may be stored in registers. Larger data types must be stored in memory, and accessed via pointer dereference.
Name Size in bytes
----- -------------
char 1
int 2
short 2
long 4
Byte quantities are stored in the high byte of a register, this is to accomodate the byte-oriented instructions. Also, this allows tests on signed quantities to behave as expected. Conversion to and from larger types should be rare, so it is best to take advantage of the mechanisms the hardware provides.
Two byte quantities are stored in a single register. This is the most convenient data type to use, as the word-oriented instructions like INC, INCT, DEC, or DECT may be used to improve size and performace of compiled code. The "int" type is intended to be the same size as the machine word, so it is defined as a two-byte quantity.
Four byte quantities are stored in consecutive registers in big-endian format. The lower numbered register contains the most significant bytes word, and the higher numbered regiter contains the least significant bytes.
It is important to realize that not all 32-bit operations are supported by the compiler. This is intentional. Some 32-bit operations, like division, are quite involved on the TMS9900, and if supported by the compiler would result in large pieces of inlined code. It is more efficient for these operations to be handled by an external library. Only simple operations, like addition, subtraction and conditional checks are inlined by the compiler.
Register Usage
==============
The registers are primarily separated into volatile and non-volatile groups. The volatile registers are not preserved when a function is called, and values stored there may be destroyed as a result. Non-volatile registers are preserved across function calls. Care must be taken to preserve and restore the values stored in non-volatile registers if they are to be used.
A certain number of registers are assigned special functions by the hardware. The volatility of these registers has been chosen to reflect the most convenient usage model.
Since the TMS9900 only support two-argument instructions, most interesting calculations will involve many intermediate registers. This has resulted in the decision to classify a large number of registers as volatile. Additionally, since the registers live in memory, non-volatile values can usually be stored in memory without much penalty.
In order to reduce the overhead of a function call, it is desired that the return values and as many arguments as possible are passed by register. These argument register must be contiguous to allow the possibility of 32-bit arguments to be used.
Finally, to better remember the volatility of each register, there should be a line with all volatile registers on one side, and all non-volatile registers on the other.
The register convention has been chosen to try to find a best fit for these constraints and desires.
R0 - Volatile, Bit shift count
R1 - Volatile, Argument 1, return value 1
R2 - Volatile, Argument 2, return value 2
R3 - Volatile, Argument 3
R4 - Volatile, Argument 4
R5 - Volatile, Argument 5
R6 - Volatile, Argument 6
R7 - Volatile, Argument pointer
R8 - Volatile, Frame pointer
R9 - Preserved across BL calls
R10 (SP) - Preserved across BL calls, Stack pointer
R11 (LR) - Preserved across BL calls, Return address after BL
R12 (CB) - Volatile, CRU base
R13 (LW) - Preserved across BL calls, Old workspace register after BLWP
R14 (LP) - Preserved across BL calls, Old program counter after BLWP
R15 (LS) - Preserved across BL calls, Old status register after BLWP
16-bit values are returned on R1, 32-bit valuesa are returned on R1,R2.
Up to six arguments may be passed by register using R1 through R6. If not used for arguments, these are available as volatile registers.
The argument pointer in R7 is not always used. If used, it points to the start of arguments passed on the stack. However, the compiler can usually calculate the location relative to the stack pointer, freeing R7 for general use.
The frame pointer in R8 is not always used. If used, it points to the start of local values stored on the stack. However, the compiler can usually calculate the location relative to the stack pointer, freeing R8 for general use.
The stack pointer is stored in R10. By definition, this is a non-volatile register. Called functions, if they manipulate the stack, must restore the stack pointer to the original value before it returns.
The use of the remaining registers all have special uses defined by the hardware.
R0 is used by the shift instructions, and my be called at any depth in the call tree. It must be volatile.
The CRU base register is assigned to R12. This is volatile since all CRU uses will have to be in an assembly module. Since CRU operations are awkward enough as it is, why compound the confusion by bringing the stack into the picture?
R11, R13, R14 and R15 store values required to return from a BL or BLWP call. These values must be preserved across function calls. If not used for a return from BL or BLWP, these are treated as non-volatile registers.