
\ ------------------------------------------------------------------------------
\  Mecrisp-Ternary
\ ------------------------------------------------------------------------------

Mecrisp-Ternary is an experiment to explore Forth on a balanced ternary based architecture.

To my knowledge, there is no prior art for Forth on base-3 architectures, so I set sails to find out and explore. If you want to dive in, I assume you are already deeply familiar with the Forth language, and with the behaviour of low-level arithmetic and logic on a binary two-complement machine. As seasoned programmers, we are mostly thinking in terms of binary machines using two-complements arithmetic, and quite a lot of tricks we know depend on this. Lets start with a small intro into the balanced ternary number system, its properties, and how logic may be implemented based on these.

Mecrisp-Ternary is released as open source under GNU GPLv3.

Dr. Matthias Koch, May 2026


View this file with a monospace font.

\ ------------------------------------------------------------------------------
\  Balanced ternary numbers
\ ------------------------------------------------------------------------------

                Balanced
   Decimal:     Ternary:

     -14   =  -+++

     -13   =   ---
     -12   =   --0
     -11   =   --+
     -10   =   -0-
      -9   =   -00
      -8   =   -0+
      -7   =   -+-
      -6   =   -+0
      -5   =   -++

      -4   =   0--    \
      -3   =   0-0     \
      -2   =   0-+      \
                         \
      -1   =   00-  \     \
       0   =   000   | 1   | 2 Trits range
       1   =   00+  /     /
                         /
       2   =   0+-      /
       3   =   0+0     /
       4   =   0++    /

       5   =   +--
       6   =   +-0
       7   =   +-+
       8   =   +0-
       9   =   +00
      10   =   +0+
      11   =   ++-
      12   =   ++0
      13   =   +++

      14   =  +---

A few notable characteristics of balanced ternary numbers include:

* Every number is implicitly signed. There are no unsigned numbers in balanced ternary at all.

* The sign of a number is given by its most significant trit different from "0". There is no dedicated place for the sign as in binary two-complement numbers.

* Zero is neither positive nor negative, and there is exactly one zero.

* For a given set of trits, they can represent exactly the same range of positive and negative values. Changing the sign is simply done by exchanging all "+" and "-" trits. There is no singular "most negative" number without a positive representation like in binary two-complement arithmetic.

* When truncated in a fixed-width register, arithmetic overflow happens from the most positive to the most negative value, and vice versa. Carry and overflow are identical in balanced ternary.

* Shifting a balanced ternary number one place to the left is the same as multiplying with 3, and shifting one place to the right is the same as dividing by 3. In balanced ternary, a right shift is exactly the same as symmetric division by powers of three, unlike binary in which right-shifting negative two-complement numbers to divide by powers of two gives rounding artifacts.


The numerical ranges for registers n trits wide are, in comparison with signed binary wide enough to hold at least the same range:

n Trits  +-(3^n - 1)/2             Bits  Range for signed binary
                                     to fit n Trits:

   1         -1  1                   2        -2  1
                                  (  3        -4  3      )
   2         -4  4                   4        -8  7
   3        -13  13                  5       -16  15
                                  (  6       -32  31     )
   4        -40  40                  7       -64  63
   5       -121  121                 8      -128  127
                                  (  9      -256  255    )
   6       -364  364                10      -512  511
                                  ( 11     -1024 1023    )
   7      -1093  1093               12     -2048 2047
   8      -3280  3280               13     -4096 4095
                                  ( 14     -8192 8191    )
   9      -9841  9841               15    -16384 16383
  10     -29524  29524              16    -32768 32767
                                  ( 17    -65536 65535   )
  11     -88573  88573              18   -131072 131071
                                  ( 19   -262144 262143  )
  12    -265720  265720             20   -524288 524287


\ ------------------------------------------------------------------------------
\  Logic on balanced ternary machines
\ ------------------------------------------------------------------------------

In binary logic, there are 2^3=8 commutative out of 2^4=16 possible diadic operators in total, including 2^2=4 possible monadic operators. Ternary has much more! In balanced ternary, there are 3^3=27 possible monadic logic operators, and 3^6=729 possible commutative didadic operators out of 3^9=19683 diadic logic operators in total, including the monadic ones. Out of these, Mecrisp-Ternary offers a selection of especially useful ones:

           "rotate "rotate
             down"   up"

      not    3rdn   3rup

-      +      +      0
0      0      -      +
+      -      0      -

Note that NOT also fulfills the role of NEGATE, changing the sign of a number, and does not alter "0" states. The operators "rotate down" and "rotate up" have no binary equivalents, as these would be identical to NOT in binary, and therefore are unique to ternary logic. They allow to cycle through logic states, including changing "0" states.

                            and     or
    3any    3con    3equ    3min    3max    3mul    3sum
    - 0 +   - 0 +   - 0 +   - 0 +   - 0 +   - 0 +   - 0 +

-   - - 0   - 0 0   + - -   - - -   - 0 +   + 0 -   + - 0
0   - 0 +   0 0 0   - + -   - 0 0   0 0 +   0 0 0   - 0 +
+   0 + +   0 0 +   - - +   - 0 +   + + +   - 0 +   0 + -

-                                           - 0 +
0                                           0 0 0
+                                           + 0 -

                                            3mul not
                                            xor

In standard Forth, there is the convention of using the values with all bits set for TRUE=-1 and all bits cleared for FALSE=0 as "well-formed flags". With three possibilities, three well-formed flags with all trits the same are available, and could be mapped to logic states in different ways. For Mecrisp-Ternary, I selected the following convention (assuming a 12 trits wide machine):

   TRUE  =  &++++++++++++  =  0 3RUP  =   265720
   ZERO  =  &000000000000  =  0       =        0
  FALSE  =  &------------  =  0 3RDN  =  -265720

Working with well-formed TRUE and FALSE flags defined this way, the classic effects of boolean operators are given by these ternary operators:

  NOT    NOT
  AND    3MIN
   OR    3MAX
  XOR    3MUL NOT

But note these equivalents have completely different effects as soon as "0" trits are involved. Feel free to try new tricks, but keep in mind most well-known tricks for binary machines would need a different implementation on balanced ternary machines, and unfortunately, there is very few literature available.

By the way, for masking and combining trits, the combination of 3mul and 3any is quite useful:

  &+++++++++00+ 3mul    Apply mask
  &000000000-+0 3any    ... and set trits

Usually, Forth uses the number base prefixes %1101 for binary, #13 for decimal, and $D for hexadecimal. I additionally picked &+++ as a new prefix to denote balanced ternary numbers in Mecrisp-Ternary.

One more especially notheworthy difference is in the behaviour of conditional control flow structures. Usually, in a standard binary Forth, the control structures are compiled using exactly one type of conditional jump only, often named 0BRANCH which is jumping/branching on zero, which is identical to a well-formed false flag on binary machines, and this behaviour is also often relied upon to count down to zero in a BEGIN ... UNTIL structure. For Mecrisp-Ternary, this is replaced by a "jump if zero or negative" to both support well-formed FALSE flags and the common use case of counting down to zero. I just picked this convention for Mecrisp-Ternary. Other choices for the combinations of well-formed flags, logic operators and conditional jumps might be explored as well - this is so far uncharted territory for Forth and will cause deep changes in behaviour and logic.

To start thinking in ternary, best idea is to have the truth tables handy on a paper "cheat sheet" next to your keyboard. When writing Mecrisp-Ternary, I found the resources given in "The Ternary Manifesto" by Douglas W. Jones https://homepage.cs.uiowa.edu/~dwjones/ternary/ very helpful, and I recommend you to read it for a more thorough introduction.

\ ------------------------------------------------------------------------------
\  Notes on the instruction set
\ ------------------------------------------------------------------------------

Mecrisp-Ternary is also an experiment to create an instruction set and a Forth implementation which are carefully crafted to run unchanged with different possible machine widths starting at 10 trits, with no theoretical upper limit. For default, it is set to a machine width of 12 trits. The width can be changed in the source code of the instruction set simulator, currently up to a maximum of 15 trits, limited only by the data types used in the simulator.

To achieve this feat, I created a word-addressed (well, in terms of Forth, thats a cell-addressed) design. Therefore, memory addresses stay the same regardless of the current machine width. In balanced ternary, all numbers are inherently signed, and no sign-extension is necessary. As long as the information fits into the width provided, a zero-extended copy represents the same number. With all instructions starting from the least significant trit, also the instruction set stays the same when changing the machine width.

      Minimum                                 Least significant
       width                                  trit
        v                                      v
 Trit   |                                        |
 11  10 | 09  08  07  06  05  04  03  02  01  00 |
        |                                        |
    ... |     ...    Destination address   -   - |  Jump if TOS is zero or negative
    ... |     ...    Destination address   0   - |  Jump always
    ... |     ...    Destination address   +   - |  Call
        |                                        |
    ... |     ...                     Literal  0 |  Literal
        |                                        |
  0   0 | Instruction IO  Mem D-Stack R-Stack  + |  ALU
        |                                        |

While the ALU opcodes are fixed width and will simply be zero-extended, the addressable memory space effortlessly grows with the machine width. As the Forth nucleus will reside starting at address 0, there is no need to encode larger addresses when preparing it. Literals are encoded with one trit less than necessary for a given machine width, and will be compiled followed with either a 3RUP or 3RDN if necessary to achieve full range. Note that after running the nucleus on a specific width to compile additional Forth source code, the resulting memory image will (most probably) not work on a different width anymore depending on the literals used and the results of constant folding optimisation. Inside the nucleus, the code is carefully crafted to only use literals that can be zero-extended without issues.

The "ALU" opcode format includes two trits controlling return stack and subroutine return, two trits controlling data stack, one trit each for memory and IO access, and three trits for 27 different ALU instructions. Using memory mapped IO, it would be possible to reduce the minimum machine width to 9 trits, but the resulting 7 trit executable address range would be too small to accommodate Forth. Therefore a minimum width of 10 trits featuring 8 trits of executable address range seems to be a natural choice. Adventurous explorers may want to add more trits to the 3 trits wide instruction selector to add custom features.

Minutiae of instruction set encoding are probably due to change while the Verilog implementation for FPGAs is in progress.

\ ------------------------------------------------------------------------------
\  How to compile and run
\ ------------------------------------------------------------------------------

The instruction set simulator is written in C, and the Forth nucleus is prepared using Gforth. With the nucleus running in the instruction set simulator, additional Forth definitions are compiled by piping Forth source code into the terminal.

You probably have gcc or clang already installed on your system, and don't forget to add Gforth now. The resulting Gforth output nucleus.hex is also included for reference purposes, but this way you cannot change anything in the nucleus for yourself.

With the prerequisites in place, all you have to do is to run ./emulate and -fingers crossed- you'll end up in a running Mecrisp-Ternary instance.


\ ------------------------------------------------------------------------------
\                    Glossary of Forth definitions
\ ------------------------------------------------------------------------------


\ ------------------------------------------------------------------------------
\  Terminal-IO
\ ------------------------------------------------------------------------------

        emit?           ( -- flag ) Ready to send a character?
        key?            ( -- flag ) Checks if a key is waiting
        key             ( -- char ) Waits for and fetches the pressed key
        emit            ( char -- ) Emits a character

        pause           ( -- ) Trampoline for pause
        nop             ( -- ) No operation, pauses default action

\ ------------------------------------------------------------------------------
\  Stack Jugglers
\ ------------------------------------------------------------------------------

Single-Jugglers:

        dup             ( x1 -- x1 x1 )
        over            ( x1 x2 -- x1 x2 x1 )
        third           ( x1 x2 x3 -- x1 x2 x3 x1 )
        tuck            ( x1 x2 -- x2 x1 x2 )
        swap            ( x1 x2 -- x2 x1 )
        drop            ( x1 -- )
        nip             ( x1 x2 -- x2 )
        rot             ( x1 x2 x3 -- x2 x3 x1 )
        -rot            ( x1 x2 x3 -- x3 x1 x2 )
        ?dup            ( x -- x | x x ) Duplicate if x is positive
        depth           ( -- n ) Gives number of single-cell stack items

        >r              ( x -- ) (R: -- x )
        r>              ( -- x ) (R: x -- )
        r@              ( -- x ) (R: x -- x )
        rdrop           (  --  ) (R: x -- )
        rdepth          ( -- n ) Number of items on return stack

Double-Jugglers:

        2dup            ( x1 x2 -- x1 x2 x1 x2 )
        2over           ( x1 x2 x3 x4 -- x1 x2 x3 x4 x1 x2 )
        2swap           ( x1 x2 x3 x4 -- x3 x4 x1 x2 )
        2drop           ( x1 x2 -- )

\ ------------------------------------------------------------------------------
\  Logic
\ ------------------------------------------------------------------------------

        not             ( x1 -- x2 )    Flip all trits
        3rdn            ( x1 -- x2 )    Rotate trits up
        3rup            ( x1 -- x2 )    Rotate trits down

        3any            ( x1 x2 -- x3 ) Any
        3con            ( x1 x2 -- x3 ) Consensus
        3equ            ( x1 x2 -- x3 ) Equal
        3min            ( x1 x2 -- x3 ) Minimum
        3max            ( x1 x2 -- x3 ) Maximum
        3mul            ( x1 x2 -- x3 ) Multiplication
        3sum            ( x1 x2 -- x3 ) Sum

        xor             ( x1 x2 -- x3 ) Same as 3mul not
        or              ( x1 x2 -- x3 ) Same as 3max
        and             ( x1 x2 -- x3 ) Same as 3min

        true            ( --  ...+++ )  True-Flag
        zero            ( --  ...000 )  Zero-Flag
        false           ( --  ...--- ) False-Flag

        rshift          ( x1 n -- x2 ) Right-shift of n trit-places
        lshift          ( x1 n -- x2 )  Left-shift of n trit-places

        WIDTH           ( -- n ) Current machine width

\ ------------------------------------------------------------------------------
\  Arithmetic
\ ------------------------------------------------------------------------------

Single:

        +               ( n1 n2 -- n3 ) Addition
        +carry          ( n1 n2 -- n3 ) Carry of addition
        -               ( n1 n2 -- n3 ) Subtraction
        negate          ( n1 -- n2 ) Flip sign, same as not
        abs             ( n -- |n| ) Absolute value of n

        3sign           ( n -- true | zero | false )
                        Turn sign of n into a well-formed flag

        1+              ( n -- n+1 ) Same as 1 +
        1-              ( n -- n-1 ) Same as 1 -
        3*              ( n -- 3*n ) Same as 1 lshift
        3/              ( n -- n/3 ) Same as 1 rshift

        min             ( n1 n2 -- n1|n2 ) Keeps smaller of top two items
        max             ( n1 n2 -- n1|n2 ) Keeps greater of top two items

        *               ( n1 n2 -- n3 ) n1 * n2 = n3
        /               ( n1 n2 -- n3 ) n1 / n2 = n3
        mod             ( n1 n2 -- n3 ) n1 % n2 = n3
        /mod            ( n1 n2 -- n3 n4 ) n1 / n2 = n4 remainder n3

Mixed:

        s>d             ( n -- d ) Makes a single number double length
        m+              ( d1 n -- d2 ) Addition of a double with a single
        m*              ( n1 n2 -- d ) n1 * n2 = d

Double:

        d+              ( d1 d2 -- d3 ) Double addition
        d-              ( d1 d2 -- d3 ) Double subtraction
        dnegate         ( d1 -- d2 ) Flip sign of double number
        dabs            ( d -- |d| ) Absolute value of d

        d3*             ( d -- d*3 ) Double shift  left for one place
        d3/             ( d -- d/3 ) Double shift right for one place

        d/mod           ( d1 d2 -- d3 d4 ) d1 / d2 = d4 remainder d3

For internal usage:

        divstep         ( -- ) Internal part of d/mod
        dividend        ( -- addr ) Double variable
        remainder       ( -- addr ) Double variable
        divisor         ( -- addr ) Double variable
        quotient        ( -- addr ) Double variable
        lowhigh         ( -- addr ) Double variable
        one             ( -- addr ) Variable

\ ------------------------------------------------------------------------------
\  Comparisons
\ ------------------------------------------------------------------------------

Single comparisons:

        =               ( n1 n2 -- flag )
        <>              ( n1 n2 -- flag )
        <               ( n1 n2 -- flag )
        >               ( n1 n2 -- flag )
        <=              ( n1 n2 -- flag )
        >=              ( n1 n2 -- flag )

        0=              ( n -- flag )
        0<>             ( n -- flag )
        0>              ( n -- flag )
        0<              ( n -- flag )

        within          ( n1 n2 n3 -- flag )  Check if n1 is within n2 and n3

Double comparisons:

        d=              ( d1 d2 -- flag )
        d<>             ( d1 d2 -- flag )
        d<              ( d1 d2 -- flag )
        d>              ( d1 d2 -- flag )

        d0=             ( d -- flag )
        d0<>            ( d -- flag )
        d0<             ( d -- flag )
        d0>             ( d -- flag )

\ ------------------------------------------------------------------------------
\  Tools for number input
\ ------------------------------------------------------------------------------

        digit           ( c -- n true | c false )
                        Try to convert a character to a number
                        using the current base.

        number          ( addr len -- 0 )
                                   -- n 1 )
                                   -- n-low n-high 2 )
                        Tries to convert a string to a number

\ ------------------------------------------------------------------------------
\  Number base
\ ------------------------------------------------------------------------------

        binary          ( -- ) Sets base to 2
        ternary         ( -- ) Sets base to -3
        decimal         ( -- ) Sets base to 10
        hex             ( -- ) Sets base to 16
        base            ( -- addr ) Base variable address

\ ------------------------------------------------------------------------------
\  Memory access
\ ------------------------------------------------------------------------------

        cmove           ( addr1 addr2 n -- ) Moves backward
        cmove>          ( addr1 addr2 n -- ) Moves forward
        move            ( addr1 addr2 n -- ) Moves n cells in memory any direction
        fill            ( addr n x ) Fill n cells of memory with value x

        constant  name  ( n -- )  Makes a single constant.
        variable  name  ( n -- )  Makes an initialized single variable

        2constant name  ( d -- ) Makes a double constant.
        2variable name  ( d -- ) Makes an initialized double variable

        buffer:         ( n -- ) Create an uninitialised buffer of n cells

        @               ( addr -- n ) Fetches single number from memory
        !               ( n addr -- ) Stores single number in memory
        +!              ( n addr -- ) Add to memory location

        2@              ( addr -- d ) Fetches double number from memory
        2!              ( d addr -- ) Stores double number in memory

IO address space:

        io@             ( addr -- x ) Fetches from IO register
        io!             ( x addr -- ) Stores  into IO register

\ ------------------------------------------------------------------------------
\  Strings and beautiful output
\ ------------------------------------------------------------------------------

String routines:

        type            ( addr length -- )
                        Prints a string

        rtype           ( addr length n -- )
                        Prints a string right aligned in a field n characters wide

        ltype           ( addr length n -- )
                        Prints a string  left aligned in a field n characters wide

        s" Hello"       Compiles a string and
                        ( -- addr length )
                        gives back its address and length when executed

        ." Hello"       Compiles a string and
                        ( -- )
                        prints it when executed

        ( Comment )     Ignore Comment
        \ Comment       Comment to end of line

        cr              ( -- ) Emits line feed
        bl              ( -- 32 ) ASCII code for Space
        space           ( -- ) Emits space
        spaces          ( n -- ) Emits n spaces if n is positive

        accept          ( addr maxlength -- length ) Read input into a string

Counted string routines:

        count           ( cstr-addr -- addr length )
                        Convert counted string into addr-length string

Pictured numerical output:

        [char] *        Compiles code of following char
                        ( -- char ) when executed

        char *          ( -- char ) gives code of following char
        hold            ( char -- ) Adds character to pictured number
                                    output buffer from the front

        .digit          ( n -- char ) Convert a single-digit number
                                      to a character

        sign            ( n -- ) Add a minus sign to pictured number
                                 output buffer, if n is negative

        #S              ( d -- 0 0 ) Add all remaining digits
                        from the double length number to output buffer
        #               ( d1 -- d2 ) Add one digit from the
                        double length number to output buffer
        #>              ( d -- addr len )
                        Drops double-length number and finishes
                        pictured numeric output ready for type
        <#              ( -- ) Prepare pictured number output buffer
        .               ( n -- ) Print single number
        d.              ( d -- ) Print double number

         .r             ( n width -- ) Print        number right aligned
        d.r             ( d width -- ) Print double number right aligned

         .l             ( n width -- ) Print        number  left aligned
        d.l             ( d width -- ) Print double number  left aligned

        buf0            ( -- addr ) Start of number output buffer
        buf             ( -- addr ) End   of number output buffer
        hld             ( -- addr ) Variable with current position

\ ------------------------------------------------------------------------------
\  User input and its interpretation
\ ------------------------------------------------------------------------------

        tib             ( -- addr ) Input buffer
        pad             ( -- addr ) Location to hold temporary data

        refill          ( -- ? ) Refill input buffer, return true if successful
        source!         ( addr len -- ) Change source
        source          ( -- addr len ) Current source
        >in             ( -- addr ) Variable with current offset into source

        /string         ( addr1 n1 n -- addr2 nn ) Cut n leading characters
        parse-name      ( -- addr len ) Get next token from input buffer
        parse           ( char -- addr len )
                        Cuts anything delimited by char out of input buffer

        evaluate        ( any addr len -- any ) Interpret given string
        quit            ( many -- ) (R: many -- ) Resets Stacks
        abort           ( many -- ) (R: many -- ) Print ? and quit

\ ------------------------------------------------------------------------------
\  Dictionary expansion
\ ------------------------------------------------------------------------------

        dp              ( -- addr ) Dictionary pointer variable
        allot           ( n -- ) Advances dictionary pointer
        here            ( -- addr ) Gives current position in Dictionary

        ,               ( n -- ) Appends a single number to dictionary

        cornerstone name    Create a permanent dictionary wayback point
        new                 Core wayback point

        s,              ( addr len -- ) Inserts a string without runtime
        sliteral        ( addr len -- ) Inserts a string with runtime

        literal         ( n -- ) Compiles a literal

        compile,        ( addr -- ) Compiles a call to a subroutine

        forth           ( -- addr ) Variable with entry point for dictionary

        ahead           ( -- addr ) Prepare a forward jump

\ ------------------------------------------------------------------------------
\  Flags and inventory
\ ------------------------------------------------------------------------------

        immediate       ( -- ) Makes current definition immediate
        foldable        ( n -- ) Current word becomes foldable with n constants

        sfind           ( addr len -- addr len false | xt flags true )
                               Searches for a string in Dictionary.

      flags>immediate   ( x -- false|true ) Check for immediateness
      flags>foldability ( x -- n )          Check for foldability

\ ------------------------------------------------------------------------------
\  Compiler essentials
\ ------------------------------------------------------------------------------

        execute         ( addr -- ) Calls subroutine
        recurse         ( -- ) Lets the current definition call itself
        ' name          ( -- addr ) Tries to find name in dictionary
                                      gives back executable address
        ['] name        ( -- addr)  Tick that compiles the executable address
                                      of found word as literal
        postpone name   ( -- ) Helps compiling immediate words
        inline:         ( -- ) Compiles the instruction directly following inline:
                               into the current definition
        does>           ( -- ) executes: ( -- addr )
                               Gives address to where you have stored data
        create name     ( -- ) Create a definition with default action
        >body           ( addr -- addr' ) Address of data field after create
        state           ( -- addr ) Address of state variable
        ]               ( -- ) Switch to compile state
        [               ( -- ) Switch to execute state
        ;               ( -- ) Finishes new definition
        : name          ( -- ) Opens new definition
        :noname         ( -- addr ) Opens new definition without name

\ ------------------------------------------------------------------------------
\  Control structures
\ ------------------------------------------------------------------------------

Decisions:

flag if ... then
flag if ... else ... then

        then            ( -- )           This is the common
        else            ( -- )           flag if ... [else ...] then
        if              ( flag -- )      structure

Case:

n case
     m1   of ... endof
     m2   .. ... .....
    all others
  endcase

        case            ( n -- n )       Begins case structure
        of              ( m -- )         Compares m with n, choose this if n=m
        endof           ( -- )           End of one possibility
        endcase         ( n -- )         Ends case structure, discards n

Indefinite Loops:

begin ... again
begin ... flag until
begin ... flag while ... repeat

        repeat          ( -- ) Finish of a middle-flag-checking loop

        while           ( flag -- ) Check a flag in the middle of a loop

        until           ( flag -- ) begin ... flag until
                                    loops until flag is true
        again           ( -- )  begin ... again
                                is an endless loop
        begin           ( -- )


Definite Loops:

limit index   do ... [one or more leave(s)] ... loop
             ?do ... [one or more leave(s)] ... loop
              do ... [one or more leave(s)] ... n +loop
             ?do ... [one or more leave(s)] ... n +loop


        j               ( -- n ) Gives second loop index
        i               ( -- n ) Gives innermost loop index


        unloop          (R: old-limit old-index -- )
                        Drops innermost loop structure,
                        pops back old loop structures to loop registers

        exit            ( -- ) Returns from current definition

        leave           ( -- ) (R: old-limit old-index -- )
                        Leaves current innermost loop promptly

        +loop           ( u|n -- )
                        (R: unchanged | old-limit old-index -- )
                        Adds number to current loop index register
                        and checks whether to continue or not

        loop            ( -- )
                        (R: unchanged | old-limit old-index -- )
                        Increments current loop index register by one
                        and checks whether to continue or not

        ?do             ( Limit Index -- )
                        (R: unchanged | -- old-limit old-index )
                        Begins a loop if limit and index are not equal

        do              ( Limit Index -- )
                        (R: -- old-limit old-index )
                        Begins a loop

        bounds          ( addr len -- limit index )
                        Calculate values to loop over a string

\ ------------------------------------------------------------------------------
\  Insight into the Forth system
\ ------------------------------------------------------------------------------

        t.              ( n -- ) Prints a balanced ternary number
        .s              ( many -- many ) Prints stack contents
        cycles          ( -- n ) Cycle counter

        words           ( -- ) Prints list of all definitions
        insight         ( -- ) Complete printout of dictionary structure

        name.           ( addr -- ) If this is the code-start of a definition,
                                    try to print its name
        alu.            ( opcode -- ) Try to print name of an ALU instruction

        memstamp        ( addr -- ) Show memory location nicely
        disasm-$        ( -- addr ) Variable for current disasm position
        disasm-cont     ( -- addr ) Variable: Continue up to this position
        disasm-step     ( -- ) Disassemble one more instruction

        seec            ( -- ) Continue to see at disasm-$
        see name        ( -- ) See the definition

\ ------------------------------------------------------------------------------
\  May the ternary Forth be with you :-)
\ ------------------------------------------------------------------------------
f
