colorForth Instructions

Here are the 33 c18 instructions, with words about each and examples of their use.

ColorForth mnemonics/syntax are sometimes different from T18. My opinion is that they're better.

There are some tricky things you can do, particularly with or and -.

Registers are:

Jump Instructions

Jump to address in register P, I or R

00 ;

Jump thru R (destructive)
The word ; is Forth syntax to end a definition. Here it doesn't necessarily mean end, but return
  • Return from subroutine (return to caller):
    ;
  • Computed jump - address in T:
    push ;
    Useful for a vectored jump into a table

01 ;:

Jump thru R, save P in R
Sometimes called co-routine jump, it returns to caller while saving the current address. This can be repeated to
  • Interleave 2 threads

02 jump

Jump thru I
The opcode is not used explicitly. The compiler generates it
The address is in the instruction, either 10, 8 or 3 bits depending on slot
  • Tail recursion:
    The compiler will turn a call followed by ; into a jump to save time and space
  • Implement else:
    if . . else . . . then
  • Construct a table of jumps
    Often more efficient than conditional jumps

03 call

Jump thru I, push current address to R
The opcode is not used explicitly. Referencing any defined word generates a call
  • Subroutine call:
    word . . . ;
    word

04 unext

Jump to slot 0
Discards the address left by for. Very efficient loop since no instruction fetchs required.
  • Delay loop:
    for unext
  • Multiply:
    for +* unext
  • Shift:
    for 2* unext
    for 2/ unext
  • Move data to/from ports
    for @+ !b unext
    for @b !+ unext
    for @p+ !+ unext
    for @+ !p+ unext

05 next

If R is non-zero, jump thru I and decrement R. Otherwise pop R
For expects loop-count (-1) on stack and is defined as:
push begin
Begin fills the current instruction word with . so that the loop begins in the next word. Leaves address on compiler stack for next to use
  • Count-down loop:
    for . . . . next
    Prefer unext if only 3 instructions in loop
  • Search for match:
    for @+ over or if drop swap next no match then match
    Swap is executed at compile time to access the address for next
  • /mod for begin over over . + -if drop 2* swap next ; then over or or - 2* - next ;
    Divide operation: trial subtract and shift in either 0 or 1

06 if

Jump thru I if T is zero
If leaves its address on compiler stack for then to complete. Then may abort if address won't fit in slot
  • Test for non-zero (true):
    if . . . then
  • Test for unequal:
    or if . . . then

07 -if

Jump thru I if T is positive
  • Absolute value:
    abs -if - 1 . + then ;
  • Maximum:
    max - over . + - -if drop ; then + ;
  • Minimum:
    min - over . + - -if + ; then drop ;

Memory Instructions

Fetch or store thru registers A, B, P.
@ and ! are Forth abbreviations for 'fetch' and 'store'. Fetch pushes the data stack; store pops it

08 @p+

Fetch thru P, increment P
Increment suppressed when executing from port
  • Fetch a number:
    123
    Uses 1 slot plus 1 word
  • Fetch 4 numbers:
    100 200 300 400
    Uses 4 slots plus 4 words
  • Name a constant:
    pi 314 ;
    Uses 2 slots plus 1 word
  • Add a number:
    1+ 1 . + ;
    Uses 4 slots plus 1 word

09 @+

Fetch thru A, increment A
Increment suppressed when referencing port
  • Check-sum memory:
    0 63 for @+ . + unext

0a @b

Fetch thru B
  • Read status:
    io b! @b

0b @

Fetch thru A
  • Read status:
    io a! @

0c !p+

Store thru P, increment P
  • Send status to port from which you're executing instructions:
    @b !p+
  • Make a constant (x) into a variable:
    x! @p+ drop !p+ ;
    x 100 ;
    Avoids using an address register

0d !+

Store thru A, increment A
  • Fill memory from port:
    63 for @b !+ unext
  • Fill memory executing from port:
    63 for @p+ !+ unext

0e !b

Store thru B
Be careful to distinguish !b from b!
  • Set 2 I/O pins
    30003 !b
  • Reset 3 I/O pins
    2000a !b

0f !

Store thru A
  • Set first I/O pin
    30000 !

ALU Instructions

Unary and binary operations:

10 +*

Multiply step: add S to T if A0=1 then shift T and A right
  • 4-bit multiply:
    . +* . +* . +* . +*
    If the multiplicand (in T) was shifted left 4 places, the result is aligned
  • n-bit multiply:
    push . begin +* unext
    This construction is required to provide a . for the first +*; unext provides delay for the others
  • Unsigned integer multiply:
    * a! 17 push . begin +* unext drop a ;
    18-bit result

11 2*

Shift T left
  • Multiply by 3:
    dup 2* . +
  • Multiply by 5:
    dup 2* 2* . +
  • Multiply by 6:
    2* dup 2* . +
  • Multiply by 7:
    dup 2* dup 2* . + . +
  • Test right-port read-status:
    @b 2* -if
  • Test down-port read-status (after right-port):
    2* 2* -if

12 2/

Shift T right; sign fill
  • Divide by 4:
    2/ 2/

13 -

One's complement T
  • Test for positive:
    - -if
  • Negate:
    - 1 . +
  • Subtract T from S:
    - . +
    This result is 1 too small. It can be corrected during additional arithmetic
  • Subtract S from T:
    - . + -
  • Subtract T from S:
    push - pop . + -
  • Construct -1:
    dup or -
    dup dup - . +
  • Construct -2:
    dup or - 2*
  • Construct +1:
    dup or - 2* -
    Same number of slots as literal 1

14 +

Add S to T (discard S)
  • Add:
    . +
    Dot required if stack has just changed. Otherwise carry can propagate only 9 bits

14 +

Add S to T with carry
Requires bit 9 of P be set
  • Set P9:
    200 org arithmetic . . . ;
    arithmetic
    P9 reset upon return from arithmetic
  • Add:
    . +
  • Clear and save carry:
    dup or . +
  • Return -1 if carry zero; 0 if carry set. Cleverly adds number to complement:
    dup dup - . +
  • Set carry:
    dup or - dup . +

15 and

Bit-wise and of S and T
  • Mask low-order byte:
    ff and
  • Mask sign bit:
    20000 and
  • Return zero if T was a power of 2:
    dup -1 . + and

16 or

Bit-wise exclusive-or of S and T
  • One's complement T:
    3ffff or
    Not useful since it uses 5 slots instead of 1 ( - )
  • Change sign bit:
    20000 or
  • Inclusive-or:
    over - and or
  • nip:
    over or or
    Uses a stack location

17 drop

Discard T
  • Discard R:
    pop drop
  • nip (discard S):
    push drop pop
    Uses a return stack location

Stack Instructions

18 dup

Create a working copy of T
  • construct a 0 (discards T):
    dup or
  • Preserve the stack while using it destructively:
    dup dup or
  • Square a number:
    dup *
  • Cube a number:
    dup dup * *

19 pop

Fetch R (destructive)
  • Retrieve something saved
  • Retrieve a loop index:
    pop dup push
  • Discard loop index and exit loop:
    pop drop ;
  • Discard a return address. Return to the caller's caller:
    pop drop ;

1a over

Fetch S (non-destructive)
  • Fetch an increment:
    over . +
  • Working copy of two numbers:
    over over
  • Swap T and S (leaving S behind)

1b a

Fetch A (non-destructive)
  • Retrieve something saved
  • Read address for decrementing:
    a over . +

1c .

Do nothing
  • Fill unused slots
  • Delay:
    b! . b!
  • Delay before add:
    . +
  • Delay loops:
    for . unext
    for . . unext
    for . . . unext

1d push

Push T into R
  • Set loop count
  • Expose deeper stack:
    push over
  • nip:
    push drop pop
    Uses a return stack location
  • Decrement non-zero number:
    push here 1 + next pop
  • clip:
    push max pop min

1e b!

Store into B
Be careful to distinguish b! and !b
  • Set b@ b! address

1f a!

Store into A
  • Set @ ! address
  • Save top of stack
  • Clean swap:
    push a! pop a