Go to Triangle Digital Support Home Page TDS2020F TECHNICAL MANUAL
Programming
H8/500 structured Forth assembler
Live website search
Enter key words
 

H8/500 STRUCTURED FORTH ASSEMBLER

WHEN TO USE ASSEMBLER

The symbolic assembler is built in to the TDS2020F. It is used to construct Forth words that 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 that 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. You'll spend most time writing high-level language but the program will run at close to machine code speed.

Both foreground programs and interrupt code can be written in assembler. See INTERRUPTS USING ASSEMBLER LANGUAGE, page 166, for more information on the latter.

SYNTAX RULES

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 all Forth assemblers, Reverse Polish notation is used to construct operands. In addition some of the instructions in the Programming Manual are substituted by more than one TDS2020F instruction. For example the move instruction is represented by MOVI, MOVO, & MOVIM, . On this computer you must therefore keep a close eye on this section while reading the Programming Manual.

Following the notation introduced in the Programming Manual, the form of most TDS2020F instructions is as shown in the first line. However the instructions MOVIM, and CMPIM, (move and compare immediate with memory) and bit-oriented instructions have the second form:

 

Sz   <EAs>   Rd   Operator

E.g. B 2 @R3 R4 MOVI,

Sz   <EAs>   imm   Operator

E.g. W $FFFE )) 1234 ## MOVIM,

 

In the first example B calls for byte size, 2 @R3 is the effective address which is two bytes after the address pointed to by R3 and R4 represents the destination. MOVI, is the MOVe In instruction so that this instruction will move into R4 the byte at the address two bytes after the address in R3.

Some instructions need all 3 operands shown above, some need fewer or none. The Programming Manual and the examples below will help you decide in each case.

The three operand fields must have the following forms. Note that RO will compile without showing an error but will not work. Be sure to type R0 for register 0.

 

SIZE FIELD Sz

W

Word Mode. Use of W is optional-it will be assumed if not used.

B

Byte Mode.

 

EFFECTIVE ADDRESS <EAs>

Rx

The effective address is register Rx, where Rx represents R0 to R7.

offset @Rx

The effective address is that contained in register Rx but with the offset added to it. This combines the three modes @Rn   @(d:8,Rn) and @(d:16,Rn) described in the Programming Manual. The assembler puts in the most efficient one. Note that simple indirect addressing must be specified with a zero offset, i.e. use 0 @Rx not just Rx .

@-Rx

Register Rx is pre-decremented by 1 or 2 depending on byte or word mode (always 2 for R7) and the effective address is the resulting number in Rx.

@Rx+

The effective address is the number in Rx. Rx is post-incremented by 1 or 2 depending on byte or word mode (always 2 for R7, the stack pointer).

address ))

Effective address is that given (direct addressing). This combines the modes @aa:8 and @aa:16 described in the Programming Manual. The assembler puts in the most efficient one.

number ##

Effective address is a literal (immediate). This combines the modes #xx:8 and #xx:16 described in the Programming Manual. The assembler puts in the most efficient one.

 

FINAL OPERAND

Rd

One of the registers R0 to R7 (most instructions)

number ##

A literal is needed for bit oriented instructions (to define which bit) and for move and compare immediate with memory. In the latter case the assembler chooses which of two instructions to use depending on the size of the immediate data.

 

Register 7 is the microprocessor stack pointer, which is the same as the parameter stack. The effective addresses @-R7 and @R7+ are used to push and pull from the stack. 0 @R7 will reference the top item on the stack, 2 @R7 the second item and so on (each Forth item on the stack is two bytes).

At the end of this section the OP-CODE SUMMARY, page 159, has a lot of examples showing practical implementations of these operand forms. The WORD LIST, page 356, has more detailed commentary on each one.

USING CONSTANTS

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.

 

$FFFE CONSTANT PORT7 \ Define address of port 7

CODE INPUT ( - n ) \ Input byte on port 7

   \   and put to stack

   W R3 CLR,   \ ensure top byte is 0

   B PORT7 )) R3 MOVI, \ get byte from port 7

   W @-R7 R3 MOVO,   \ push to stack

   END-CODE

STRUCTURED CONTROL CONSTRUCTS

Assembler code can be written in a structured manner that 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 longhand, 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:

 

BEGIN, . EQ UNTIL,

Loop until Z true. This assembles a BNE op-code.

EQ IF, . ELSE, . THEN,

If the Z flag is set it does everything between IF, and ELSE, . IF, assembles a BNE op-code and ELSE, assembles a BRA.

 

Only half of the usual tests are defined, the others are obtained by use of N . This does not result in generation of any extra code:

 

EQ N IF, . THEN,

If the Z flag is not set it executes the code between IF, and THEN,

 

Every UNTIL, WHILE, or IF, must be preceded by a word, perhaps qualified by N related to the condition of the status register. All of these are permitted:

 

CS

Continue if carry set

EQ

Continue if equal to zero

LS

Continue if less than or same

LE

Continue if less than or equal to zero

LT

Continue if less than zero

MI

Continue if minus

VS

Continue if overflow bit set

 

 

CS N

Continue if carry not set

EQ N

Continue if not equal to zero

LS N

Continue if higher

LE N

Continue if greater than zero

LT N

Continue if greater than or equal to zero

MI N

Continue if plus

VS N

Continue if overflow bit clear

 

Each relates to the result of the previous assembler code operation that 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. It means that no branch will be taken if the preceding test 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.

In ANS Forth, control structures need not be neatly nested. You can have multiple WHILEs between BEGIN and REPEAT for example, although in that case there must be an external matching THEN for each extra WHILE . See CONTROL STRUCTURES, page 136, for details and flow diagrams.

Exactly the same applies to H8/532 assembler loops and this can be very useful where you need to exit a loop on more than one condition. Any other way would waste execution time and presumably you're using assembler because you need that to be a minimum.

This example follows the last of the three flow diagrams. The word TEST will decrement both input parameters n1 and n2 and will terminate when either is zero. If n2 reaches zero WHILE, is used to jump out of the loop to ELSE, . If n1 reaches zero the loop is left at UNTIL, and the code between UNTIL, and ELSE, is then executed. The extra code after the loop adds 10 or 20 to the lower parameter to help identify which exit from the loop was used.

 

CODE TEST ( n1 n2 - n3 n4 )

   BEGIN,

   0 @R7   DEC,

   EQ N WHILE,

   2 @R7   DEC,

   EQ UNTIL,

   2 @R7 R3   MOVI,

   10 ## R3   ADD,

   2 @R7 R3   MOVO,

   ELSE,

   2 @R7 R3   MOVI,

   20 ## R3   ADD,

   2 @R7 R3   MOVO,

   THEN,

   END-CODE

SUBROUTINES

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,

 

Since there is no return to Forth the jump compiled by END-CODE would be wasted. E.g.

 

CODE 3X \ A subroutine

   R3 R4 MOVI,   R3 SHAL,   R4 R3 ADD,   RTS,

CODE 1.5X \ Word which calls it

   @R7+ R3 MOVI,   ' 3X >BODY BSR,   R3 SHAR,

   @-R7 R3 MOVO,

   END-CODE

 

This is a contrived example-usually the situation is more complex. 3X is a subroutine that multiples register 3 by 3. 1.5X is a Forth word that multiplies the top of stack by 1.5. The top of stack is pulled to R3, multiplied by 3 using the subroutine then divided by 2 and afterwards put back to the stack. Note how ' 3X >BODY 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.

FORTH RE-ENTRY POINTS

The word END-CODE compiles in-line code to get back to Forth but you can also jump to NEXT (a Forth constant). Some additional speed is sometimes achieved using an alternative re-entry point which also pushes R3 to the stack; it can be coded as NEXT 2- . The example on page 153 could be rewritten:

 

CODE INPUT ( - n ) \ Input byte on port 7

   \   and put to stack

   W R3 CLR,

   B PORT7 )) R3 MOVI,

   NEXT 2- JMP,

MIXED FORTH AND ASSEMBLER

In the TDS2020F 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   0 @R7 DEC,

   A>F   ." again"   .S ;

 

When using them you must always start off with a colon definition, you cannot 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.

FORTH REGISTERS

Registers R5 R6 must be saved then restored at the end if needed in a CODE word. In interrupts save and restore any of R2 R3 R4 R5 and R6 which will be used. Register allocation of the Forth is:

 

R0

R1

Only used in compiling, free at run time

R2

R3

Used as scratchpad by many Forth words

R4

 

Forth W register

R5

 

Forth IP inner interpreter pointer

R6

 

Return stack pointer

R7

 

Machine and parameter stack pointer

 

The pointer to the base of the user variables area is at an address returned by STATUS 2- .

R0 R1 R2 and R3 can be used in your assembler words without saving them, provided they are not part of an interrupt. Also R4 can be used if the value of W is not needed in the word you are defining.

Note that R0 and R1 are usually free for you to use at once inside interrupts without having to stack them. This is done to allow shortest possible interrupt response times. The only words using R0 and R1 (and their dependents) are the following, often not used at run-time:

 

R0:

FIND

R1:

FIND CMOVE CMOVE>

 

In interrupts you must stack and restore other registers if you must use them. Do not alter R7, the stack pointer.

OP-CODE SUMMARY & EXAMPLES

This is a list of assembler operators in TDS2020F with the appropriate reference to the Programming Manual where you can get full details of what the instruction does and its legal modes. Following each one are examples of valid TDS2020F syntax. They are only examples; the Programming Manual defines what operands each operator needs. The description of the general form under SYNTAX RULES, page 151, shows you how each operand should be constructed for TDS2020F and also the order of operands.

 

Operator

Manual

Examples of valid instructions

 

 

 

 

 

2DEC,

ADD:Q

R2 2DEC,   B $FF00 )) 2DEC,

 

2INC,

ADD:Q

R2 2INC,   B $FF00 )) 2INC,

 

ADD,

ADD

B R2 R3 ADD,   $FF00 )) R3 ADD,

 

 

 

@R7+ R3 ADD,

 

ADDS,

ADDS

B R2 R3 ADDS,

 

ADDX,

ADDX

W $FF00 )) R3 ADDX,

 

AND,

AND

0 @R2 R3 AND,

 

ANDC,

ANDC

W $0300 ## SR ANDC,

 

BCLR,

BCLR

R2 R3 BCLR,

 

BCLRI,

BCLR

B $FF8E )) 2 ## BCLRI,

 

BNOT,

BNOT

R2 R3 BNOT,

 

BNOTI,

BNOT

B $FF8E )) 2 ## BNOTI,

 

BRA,

BRA

NEXT BRA,

 

BSET,

BSET

R2 R3 BSET,

 

BSETI,

BSET

B $FF8E )) 2 ## BSETI,

 

BSR,

BSR

CODE 3X   R3 R4 MOVI,   R3 SHAL,

 

 

 

   R4 R3 ADD,   RTS,

 

 

 

CODE 1.5X   @R7+ R3 MOVI,

 

 

 

   ' 3X BSR,   >BODY   R3 SHAR,

 

 

 

   @-R7 R3 MOVO,   END-CODE

 

BTST,

BTST

R2 R3 BTST,

 

BTSTI,

BTST

B $FF8E )) 2 ## BTSTI,

 

CLR,

CLR

B R3 CLR,   $FF00 )) CLR,

 

 

 

0 @R7 CLR,

 

CMP,

CMP

B R2 R3 CMP,   $FF00 )) R3 CMP,

 

 

 

4 @R7 R3 CMP,

 

CMPIM,

CMP

$FF00 )) 1234 ## CPMIM,

 

DADD,

DADD

B R2 R3 DADD,

must be byte size

DEC,

ADD:Q

R2 DEC,   B $FF00 )) DEC,

 

DIVXU,

DIVXU

CODE UM/MOD

 

 

 

   @R7+ R4 MOVI,   @R7+ R2 MOVI,

 

 

 

   @R7+ R3 MOVI,   R4 R2 DIVXU,

 

 

 

   @-R7 R2 MOVO,   @-R7 R3 MOVO,

 

 

 

   END-CODE

 

DSUB,

DSUB

B R2 R3 DSUB,

must be byte size

EXTS,

EXTS

B R2 EXTS,

must be byte size

EXTU,

EXTU

B R2 EXTU,

must be byte size

INC,

ADD:Q

R2 INC,   B $FF00 )) INC,

 

JMP,

JMP

0 @R2 JMP,   8 @R2 JMP,

 

 

 

$9000 JMP,

 

JSR,

JSR

0 @R2 JSR, $9000 JSR,

 

LDC,

LDC

CODE E@   @R7+ R3 MOVI,   B R3 EPR LDC,

 

 

 

   @R7+ R4 MOVI,   0 @R4 R3 MOVI,

 

 

 

   @-R7 R3 MOVO,   B 0 ## EPR LDC,

 

 

 

   END-CODE

 

LDM,

LDM

7 LDM,   appropriate bits are set in the operand byte

 

LINKFP,

LINKFP

B 8 LINKFP,

 

MOVFPE,

MOVFPE

CODE PC@   @R7+ R2 MOVI,   R3 CLR,

 

 

 

   B 0 @R2 R3 MOVFPE,   @-R7 R3 MOVO,

 

 

 

   END-CODE

 

MOVI,

MOV

B R2 R3 MOVI,   $FF00 )) R3 MOVI,

 

 

 

@R7+ R3 MOVI,   6 ## R2 MOVI,

 

MOVIM,

MOV

@-R7 6 ## MOVIM,

 

 

 

$FF00 )) 1234 ## MOVIM,

 

MOVO,

MOV

$FF00 )) R3 MOVO,   @-R7 R3 MOVO,

 

MOVTPE,

MOVTPE

CODE PC!   @R7+ R2 MOVI,

 

 

 

   @R7+ R3 MOVI,   B 0 @R2 R3 MOVTPE,

 

 

 

   END-CODE

 

MULXU,

MULXU

CODE UM*   0 @R7 R2 MOVI,

 

 

 

   2 @R7 R2 MULXU,   2 @R7 R3 MOVO,

 

 

 

   0 @R7 R2 MOVO,

 

 

 

   END-CODE

 

NEG,

NEG

R2 NEG,   B $FF00 )) NEG,

 

NOP,

NOP

NOP,

 

NOT,

NOT

0 @R7 NOT,   $FF00 )) NOT,

 

OR,

OR

B $FF8E )) R3 OR,

 

ORC,

ORC

B 1 ## CCR ORC,

 

PJMP,

PJMP

0 @R2 PJMP,   HEX F1234. PJMP,

 

PJSR,

PJSR

HEX F1234. PJSR,

 

PRTD,

PRTD

B 8 ## PRTD,   $100 ## PRTD,

 

PRTS,

PRTS

PRTS,

 

ROTL,

ROTL

B $FF00 ROTL,

 

ROTR,

ROTR

R3 ROTR,

 

ROTXL,

ROTXL

B $FF00 ROTXL,

 

ROTXR,

ROTXR

R3 ROTXR,

 

RTD,

RTD

B 8 ## RTD,   $100 ## RTD,

 

RTE,

RTE

RTE,

 

RTS,

RTS

RTS,

 

SCB/EQ,

SCB

$FF00 R3 SCB/EQ,

 

SCB/F,

SCB

CODE REST   @R7+ R3 MOVI,

 

 

 

   HERE R3 SCB/F,   END-CODE

 

SCB/NE,

SCB

$FF00 R3 SCB/NE,

 

SHAL,

SHAL

B $FF00 SHAL,

 

SHAR,

SHAR

$FF00 SHAR,

 

SHLL,

SHLL

R3 SHLL,

 

SHLR,

SHLR

$FF00 SHLR,

 

SLEEP,

SLEEP

SLEEP,

 

STC,

STC

CODE PRIORITY   R3 SR STC,

 

 

 

   F8FF ## R3 AND,   B R3 SWAP,

 

 

 

   @R7+ R3 OR,   B R3 SWAP,

 

 

 

   R3 SR LDC,

 

 

 

   END-CODE

 

STM,

STM

$19 STM,

 

SUB,

SUB

B R2 R3 SUB,   $FF00 )) R3 SUB,

 

SUBS,

SUBS

B R2 R3 SUBS,

 

SUBX,

SUBX

W $FF00 )) R3 SUBX,

 

SWAP,

SWAP

B R3 SWAP,

 

TAS,

TAS

B $FF00 )) TAS,

 

TRAP/VS,

TRAP/VS

TRAP/VS,

 

TRAPA,

TRAPA

4 ## TRAPA,

 

TST,

TST

R2 TST,   0 @R7 TST,

 

UNLINKFP,

UNLINK

UNLINKFP,

 

XCH,

XCH

R2 R3 XCH,

must be word mode

XOR,

XOR

R2 R3 XOR,

 

XORC,

XORC

B 1 ## CCR XORC,

 

TRACE ASSEMBLER SOURCE CODE

Assembler source code on TDS2020F can more easily be debugged by using the tracing tool in library file #TRACE.TDS.

 

Trace of TDS2020F assembler code

When tracing is turned on, all the microprocessor registers and user chosen variables are listed after execution of most instructions. Tracing can be enabled and disabled to allow only the parts of the code of interest to produce register data. This reduces the amount of output that has to be understood.

You need an Update Service subscription to trace TDS2020F assembler code.

Go to Triangle Digital Support Home Page Go to top   Next page