6301 STRUCTURED FORTH ASSEMBLER
WHEN TO USE ASSEMBLER
The symbolic assembler is built in to the TDS9092. It is used to construct Forth words which execute at machine code speed. These Forth words can be used just as others in building up high level code; The difference is that the words which might otherwise slow execution are now fast, so the entire application can approach machine code speeds.
As a rule of thumb write 90% in Forth and 10% in assembler. Code in assembler those parts where the program spends most of its time.
Both foreground programs and interrupt code can be written in assembler. See INTERRUPTS USING ASSEMBLY LANGUAGE for more information on the latter.
The usual form of an assembler definition is
CODE TEST . (assembler words) . END-CODE
The definition TEST is added to the dictionary and executes in machine code. Note that unlike colon definitions, the processor is in the execute, not the compile, mode between CODE and END-CODE . This means that the full power of the Forth system is available. For example an operand can be calculated by a definition with a complex structure such as a loop, you are not limited to simple additions etc. in the operand field as you would be in most assemblers.
As in most Forth assemblers, Reverse Polish notation is used to construct operands. On this computer you must therefore keep a close eye on this section while reading the 6301 MICROPROCESSOR DATA. Syntax is explained by means of the following examples:
13 A LDA, ( load accumulator A from address
( 13, direct addressing mode will be
( selected by the assembler
8000 B LDA, ( load accumulator B from address
( 8000, extended addressing mode
( will be selected by the assembler
13 ## B ADD, ( add immediate - constant - data 13
( to accumulator B
8000 ## SUBD, ( subtract immediate data 8000 from
( accumulator D - which is A and B
( together, most significant byte A
A ROL, ( rotate accumulator A left. An
( example of an instruction needing
( only the accumulator
E2 ROL, ( rotate left the byte at memory
( location E2
0A ROL, ( rotate left the byte at memory
( location 0A. The zero in front of
( the A ensures that 0A is taken as
( a number, not the defined word A
0 ,X ROL, ( rotate left the byte at the memory
( location pointed to by the X
( register, i.e. no offset. The zero
( is essential
5 ,X NEG, ( negate the byte at the memory
( location five after that pointed
( to by the X register. Offset range
( is 0-255 decimal
$17 $80 ## TIM, ( test address location 17 - which
( is parallel port 6 - against
( immediate data hex 80. The
( processor z flag is set if this
( bit is zero. This is a direct test
( of an input bit
( to digital converter, then
CODE INPUT ( - n ) ( use the name later as an
ATOD B LDA, A CLR, ( operand. An operand does not
PSHB, PSHA, ( have to be a number. In this
END-CODE ( complete example the analog
( value goes to the stack
Available op-codes are derived from those shown in table 18 of the 6301 MICROPROCESSOR DATA. For instance ADDA (add memory to the A register) becomes A ADD, . All of the microprocessor's instructions have Forth op-codes, the WORD LIST has more detailed commentary on each one.
These are the same as equates in many assemblers, you can associate a name with a particular number, whether it is an address, literal or other value. E.g.
$17 CONSTANT PORT6 ( define address of port 6,
CODE INPUT ( - n input byte on port 6
( and put to stack
A CLR, ( ensure top byte is 0
PORT6 B LDA, ( get byte from port 6
PSHD ( push to stack
Assembler code can be written in a structured manner which is very similar to the usual Forth style. These are available:
BEGIN, . UNTIL,
BEGIN, . WHILE, . REPEAT,
IF, . THEN,
IF, . ELSE, . THEN,
Note that the assembler forms have a comma as part of the word. Although this looks like high level language the resulting code is pure machine code just as you would have written it long-hand, no compromises or extra code! The structures can be nested and we recommend indenting each level of structure in the source code of a word defined in assembler. The words IF, WHILE, UNTIL, differ from usual Forth in that they do not expect a true/false flag on the stack, they want a true/false result from a test immediately preceding:
Only half of the usual tests are defined, the others are obtained by use of NOT, . This does not result in generation of any extra code:
Every UNTIL, WHILE, or IF, must be preceded by a word, perhaps qualified by NOT, related to the condition of the status register. All of these are permitted:
Each relates to the result of the previous assembler code operation which affected the status register. To make the operation of the assembler clearer, note that EQ IF, assembles a 'branch if not equal to zero' instruction, meaning no branch will be taken if the result is zero. It will continue into the IF, part of the structure. Otherwise operation will continue from the next ELSE, or THEN, .
Use of these structured constructs does not slow operation in any way at all, the execution speed is the same as for conventional assemblers.
If a piece of assembler code will be used several times it can be put in as a conventional subroutine. Use the structure
CODE . RTS, SMUDGE
Since there is no return to Forth, the jump compiled by END-CODE would be wasted so a simple SMUDGE to make the name findable is included. E.g.
CODE 4X ( subroutine to multiply D register by 4
ASLD, ASLD, RTS, SMUDGE
CODE 12X ( n - 12n Forth word to multiply top of
( stack by 12
PULD, 3 ## LDX, ( set up to loop 3 times
BEGIN, ' 4X BSR, ( branch to subroutine
DEX, EQ UNTIL, PSHD,
This is a contrived example, usually the situation is more complex. Note how ' 4X is used to find the required address.
A subroutine cannot be called directly as a Forth word, you can only branch to it from within a Forth word coded in assembler. Use BSR, if the branch is less than 128 bytes or JSR, if larger. In fact the latter can be used all the time because it makes its own decision whether to put in a JSR or BSR instruction.
Macros come naturally to a Forth assembler without much extra code being necessary. For example a macro which pulls both the A and B registers from the stack as one mnemonic is built into the TDS9092:
: PULD, PULB, PULA, ;
Another example consists of long branches. These should be used when the scope of an IF, . ELSE, . THEN, construct is too large for the usual branches:
: NOT, ( condition - notcondition) 1 XOR, ;
: LIF, ( condition - a
NOT, C, 3 C, 7E C, HERE 0 , ;
: LTHEN, ( a -
HERE SWAP ! ; ( jump to address, not offset
: LELSE, ( a1 - a2) 20 LIF, SWAP LTHEN, ;
All of these definitions are standard features of the TDS9092.
FORTH RE-ENTRY POINTS
The word END-CODE re-enters Forth at NEXT (a Forth constant) but some additional speed is sometimes achieved by using the alternative re-entry point PUSHBA which is defined in the assembler source. The example above could be re-written:
CODE INPUT ( - n ) ( the SMUDGE - normally done
ATOD B LDA, A CLR, ( by END-CODE - is used to
PUSHBA JMP, SMUDGE ( re-toggle the bit in the
( header set by CODE
MIXED FORTH AND ASSEMBLER
In the TDS9092 you can temporarily drop into assembler from inside a Forth word. F>A converts from Forth to assembler and A>F is to go back to Forth. For example this word TEST will display: 3 Hello again 2
: TEST ( Puts 3 on the stack, executes some Forth,
( decrements the 3 in assembler and then
( does more Forth
3 .S ." Hello "
F>A PULX, DEX, PSHX,
A>F ." again" .S ;
When using them you must always start off with a colon definition. You can not use these in the reverse order to go to Forth from inside an assembler word. If you want to do this, start off a Forth word with F>A so that although this is a colon definition, most of it may be in assembler with a bit of high level Forth in the middle. The format is:
: JOB F>A .assembler. A>F .Forth. F>A .assembler. A>F ;
The pair can be used as often as you wish within a single Forth word. This technique is particularly useful for speeding up previously debugged Forth words without having to separate out the assembler part. However the code resulting can look messy unless it is very well documented with comments.
The addresses of the Forth registers W IP RP USP and a 7-byte scratchpad area N used by Forth are returned by these Forth words. The glossary of additional words has full details. The information is useful for breaking the usual rules for good software.
The 7-byte scratchpad can be used within assembler definitions for temporary storage. For example:
CODE A2+B2 ( p q = p2 + q2 where p and q are bytes
PULD, ( q to B register) TBA, ( copy B to A
MUL, N STD, ( multiply and temporarily keep
( result in N
PULD, ( p to B register) TBA, ( copy B to A
MUL, N ADDD, ( multiply and add the two squares
PSHD, ( result to stack
However if the assembler routine is an interrupt, remember on entering it to stack the parts you will need of this scratchpad area.