The MSP430 assembler in the IRTC will allow you to do most that a conventional assembler will, and something's that it may not. It is a single pass interpreted assembler with conditional statements, such as
IF...ELSE...THEN
BEGIN...UNTIL
BEGIN...WHILE...REPEAT
BEGIN...AGAIN etc.
The syntax of the assembler is not that of Forth, Reverse Polish or Postfix notation but standard Prefix. This means that the source looks like standard assembly but with space delimiters. e.g.
MOV #0123h,R7 becomes
MOV # $0123 , R7
As all assemblers this checks the validity of the addressing mode for the instruction, and the size of the data. This is accomplished by flags that are set to indicate the various addressing modes.
Note: The only forward referencing available in the assembler is with the structures above.
The conditionals require a flag test, CS, 0=, 0<, 0>, >=, before their use. Each may be followed by a NOT to produce the inverse of the test. e.g.
0= IF - branch if non zero.
0= NOT IF - branch if zero.
The MSP430 register use is described in the TLM-Forth section. Here is a résumé of that usage:
R5 - Data Stack Pointer
R7 - Top of the Data Stack
SP - Return Stack Pointer
R4 - UP, Task Area
R8 - N, ScratchFile Use
Because of the way Forth and the Assembler work it is possible to define Macros for use in the assembly process. If we make a Host definition which contains assembler words, when interpreted at compile time, the definition will execute these words. This will place the assembler code directly into the Target. An example of this technique is:
XASM DEFINITIONS
MACRO: PUSHT ADD # 2 , DP
MOV TOS , 0 (DP)
MACRO;
IN-META
Using this technique it is possible to construct repetitive code for insertion in your machine code programs
Note: You may also produce the same result with an H: definition and M[ .. ]M e.g.
H: PUSHT M[ ADD # 2 , DP MOV TOS , 0 (DP) ]M ;
Two other macros are pre-compiled to assist with stack control.
PUSHT places the TOS contents onto the data stack with the following code;
ADD # 2 , DP
MOV TOS , 0 (DP)
POPT gets the data stack into TOS whith the following;
MOV @DP , TOS
If you wish to manipulate the stack you should do so with these macros. If you write in-line code and use these macros the optimiser will try to elliminate unnecessary stack use.
The MSP430 has many and varied addressing modes. By no means all are used in the TLM, but many are. To understand their use it is strongly recommended that you read the TI data book for a full explanation. Here we will consider the addressing mnemonic used in the IRTC assembler.
The same syntax that appears in the TI data book has been used for register identification. A upper case 'R' for a register, 0-15. For convenience and improved readability the registers, are pre-defined, R4, PC and SR etc.
The pre-defined versions are much more readable. Also all the I/O registers and the flags within them are predefined by name e.g.
BIS # CAP , & CCTL0
Will enable the capture on TimerA
Note: When using the I/O Registers in a high level definition they must be preceeded by I/O. e.g.
$51 I/O P1OUT C!
Note: When using the Register flags in a high level definition they must be preceeded by FLAG to generate a mask e.g.
FLAG CAP I/0 CCTL0 ON
To differentiate between destination and source operands a space delimited comma is used e.g.
ADD # 2 , TOS
BEWARE: of the use of a Forth , while in the assembler as very odd results will occur.
The operand value used by the instruction is the value supplied by the operand field itself. The hashmark (#), optional, is used to distinguish data from an absolute address.
Examples:
ADD # 04 , R6
Adds 4 to the value originally contained in register R6.
ADD UP , TOS
Adds UP TO the top stack item.
In this mode a register is be addressed by using its absolute address in the register file.
Example:
ROL R4
Rotates the contents of register number 4 left one bit.
In this mode the address of the data does not appear in the instruction, but is located in a register.
Example:
MOV . B @TOS , TOS
This is equivalent to a C@ in Forth, R7 being the top stack item holds the address of the data byte.
The .B indicates a byte not a word transfer.
The indirect working register acts as a base or starting value to which is added an immediate offset, to point to the data. The offset value is the immediate value given in the instruction while the index value is given by the contents of a register.
Example:
MOV R10 , 5 (R4)
If R4 contains 55 then the contents of 60 (55+5) will be loaded with the contents of register 10.
Here the destination or source addresses are given by the contents of a register, which are then post-incremented.
Example:
MOV @R5+ , TOS
In IRTC R5 contains the Data Stack pointer, DP. The above example is equivalent to a DROP.
This mode addresses the specific location within the data memory directly. It only needs the absolute address value.
Example:
MOV R7 , COUNTER
The data memory location COUNTER is loaded with the contents of the register 7.
The program execution continues at the address contained in the instruction.
Example:
JMP ' # PAUSE
Jumps directly to PAUSE. The ' is used in Forth to find the CFA of the following word. If the word was created by a LABEL the ' is not required.
CALL ' # STOP
Calls the subroutine STOP.
The program execution is continued at the address contained in register.
Examples:
BR N
Jumps to the address in N.
CALL N
Calls the address in N.
The offset, -2048 to 2047, is held in the instruction causing the execution to continue from the Program Counter plus the signed offset.
Example:
JMP ' TEST
Jumps to TEST relative from the location of the instruction.
To define a Forth code word the assembler must be invoked to allow the instruction words to be found. Three words will do this; CODE, LABEL and INTERRUPT.
CODE creates a Header for the word following and causes the assembled machine code to be run when the word is executed. This is just like a Forth : definition only in code.
LABEL creates a Header for the following word, that only leaves the address of the assembled machine code on the Host stack when the word is executed. This is often used as a reference for jumps or branches within a CODE definition.
All assembler definitions must end with END-CODE or C;. These words terminate the assembler and test the Host stack for irregularities. CODE definitions will usually end with RET.
As MSP430 Forth is subroutine threaded and produces machine code, it is possible to put code fragments into a high level definition. The code is encompassed with C[ and ]C, for example;
: TEST C[ ADD # 4 , TOS ]C ;
This adds 4 to the item on top of the stack.
If you are only using assembler code you may still use TLM to test your routines.
TLM expects the CFA it receives to be that of a subroutine. If you have used CODE definitions for your routines these may be executed by name from the command line. If they are defined by LABELs then a stub is required e.g.
CODE TST JMP ' <name> C;
This assumes <name> is a routine ending in a return opcode and that it does not corrupt the TLM file space or stack.
To test parts of your code it may be necessary to insert return opcodes into the code at strategic points and then create stubs to test that section.
Interrupts in the MSP430 are done via vectors in the last 32 bytes of the program memory space. To enable these vectors to be changed during development the TLM has these locations programmed to jump to locations at the start of the development code RAM. These RAM locations may then be programmed with an absolute jump address to the interrupt routine. This adds about 0.5uSecs to the interrupt latency at 8MHz. The Forth word VECTOR! takes a vector address, and the interrupt code address to automatically create the jump in the RAM location.
The word INTERRUPT may be used to define an interrupt routine instead of CODE or LABEL. The associated interrupt vector address must preceed INTERRUPT and it will create a jump vector to the routine in the code vectors. This will only take effect when the whole image is REPROGRAMed.
Use: $0FFEC INTERRUPT <name> \ TMRA0 vector
save registers if required
....... interrupt code
restore registers
END-INTERRUPT
During development the above interrupt would be coded as;
CODE <name>
\ $0FFEC INTERRUPT <name>
....... interrupt code
END-INTERRUPT
: SET-TMRA-INT TMRA0-VEC ['] <name> VECTOR! ;
The SET-TIMA-INT is run during the initialisation to set the RAM vector. The interrupt may then be tested and when operational the CODE <name> replaced by the $0FFEC INTERRUPT <name>.
To assist with code definitions in particular, memory dump utilities DUMP and RDUMP
DUMP is for the code memory e.g.
$0000 50 DUMP
The Data memory may be dumped by BDUMP. This dumps the area defined as byte values rather than words.