The names of instructions are easily changed and continually debated. These are the opcodes and my current preference for names:

## Jumps | ## Memory | ## Arithmetic | ## Register | ||||
---|---|---|---|---|---|---|---|

## 00 | ## ; | ## 08 | ## @p | ## 10 | ## +* | ## 18 | ## dup |

## 01 | ## ex | ## 09 | ## @+ | ## 11 | ## 2* | ## 19 | ## pop |

## 02 | ## jump | ## 0a | ## @b | ## 12 | ## 2/ | ## 1a | ## over |

## 03 | ## call | ## 0b | ## @ | ## 13 | ## - | ## 1b | ## a |

## 04 | ## unext | ## 0c | ## !p | ## 14 | ## + | ## 1c | ## . |

## 05 | ## next | ## 0d | ## !+ | ## 15 | ## and | ## 1d | ## push |

## 06 | ## if | ## 0e | ## !b | ## 16 | ## or | ## 1e | ## b! |

## 07 | ## -if | ## 0f | ## ! | ## 17 | ## drop | ## 1f | ## a! |

Registers are:

- R - top of return stack
- A - address/scratch
- B - address
- P - program counter
- I - instruction word
- T - top of data stack
- S - second number on data stack

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

Execute. Sometimes called co-routine jump, it returns to caller while saving the current address. This can be repeated to

- Interleave 2 threads
- Computed call - address in T:

push ex

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

The opcode is not used explicitly. Referencing any defined word generates a call

- Subroutine call:

word . . . ;

word

- If the low-order bit is 0 it's called unext:

If R is non-zero, jump to slot 0 and decrement R. Otherwise pop R and execute the next slot.

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

- Delay loop:
- If the low-order bit is 1:

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

- Count-down loop:

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

- Absolute value:

abs -if - 1 . + then ; - Maximum:

max - over . + - -if drop ; then + ; - Minimum:

min - over . + - -if + ; then drop ;

@ and ! are Forth abbreviations for 'fetch' and 'store'. Fetch pushes the data stack; store pops it

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

Increment suppressed when referencing port

- Check-sum memory:

0 63 for @+ . + unext

- Read status:

io b! @b

- Read status:

io a! @

- 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

- Fill memory from port:

63 for @b !+ unext - Fill memory executing from port:

63 for @p !+ unext

Be careful to distinguish !b from b!

- Set 2 I/O pins

30003 !b - Reset 3 I/O pins

2000a !b

- Set first I/O pin

30000 !

- Integer multiply: 36-bit result, buried multiplicand

* nm-nhl a! 0 17 for +* unext a ;

unext provides the delay between +*s for carry to propagate. No delay is needed for the initial add to 0 - Short integer multiply: 9-bits x 9-bits yields 18-bits

* nm-p a! 0 8 for +* unext drop drop a ;

If the multiplicand (S) was shifted 9 places left the result is right-justified

- Shift 1 into bit 0:

- 2* - - 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

- Divide by 4:

2/ 2/

- 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 - - Construct -2:

dup or - 2* - Construct +1:

dup or - 2* -

Same number of slots as literal 1

- Add:

. +

Dot required if stack has just changed. Otherwise carry can propagate only 9 bits

Requires bit 9 of P be set

- Set P9:

200 org arithmetic . . . ;

arithmetic

P9 reset upon return from arithmetic - Add:

. + - Return and clear carry: 1 if carry 1; 0 if carry 0

dup dup or dup + - Return and preserve carry: 0 if carry 1; -1 if carry 0

dup dup - . + - Set carry:

dup or - dup + (trashes stack)

- Mask low-order byte:

ff and - Mask sign bit:

20000 and - Return zero if T was a power of 2:

dup -1 . + and

- 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 - swap:

over push over or or pop

- Discard R:

pop drop - nip (discard S):

push drop pop

Uses a return stack location

- 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 * *

- 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 ;

- Fetch an increment:

over . + - Working copy of two numbers:

over over - Swap T and S (leaving S behind)

- Retrieve something saved
- Read address for decrementing:

a over . +

- Fill unused slots
- Delay:

!b . !b - Delay before add:

. + - Delay loops:

for . unext

for . . unext

for . . . unext

- Set loop count
- Expose deeper stack:

push over - nip:

push drop pop

Uses a return stack location - swap:

over push push drop pop pop

Uses 2 return stack locations - Decrement non-zero number:

push here 1 + next pop - clip:

push max pop min

Be careful to distinguish b! and !b

- Set @b !b address

- Set @ ! address
- Save top of stack
- swap:

push a! pop a