zuloloxi / mecrisp-ice

http://mecrisp.sourceforge.net/ Mecrisp-Ice is an enhanced version of Swapforth and the J1a stack processor by James Bowman, featuring three MSP430 style IO ports, a tick counter, constant folding, inlining and tail-call optimisations
BSD 3-Clause "New" or "Revised" License
27 stars 4 forks source link

Mecrisp-Ice - A conveniently enhanced fork of Swapforth by James Bowman

It makes your Lattice Icestick FPGA development board to behave just like a microcontroller.

The original Swapforth and J1a processor implementation can be found here: http://www.excamera.com/sphinx/article-j1a-swapforth.html

I would not have been possible without the first open-source FPGA toolchain: http://www.clifford.at/icestorm/

Many, many thanks to James Bowman and the whole Icestorm team !

http://mecrisp.sourceforge.net/ m-atthias@users.sf.net


HIC SUNT DRACONES - HIC SUNT DRACONES - HIC SUNT DRACONES

This is the first experimental release, so beware: Monsters may be inside !


Shortly after Clifford Wolff, Mathias Lasser and Cotton Seed managed to build the first working completely open-source FPGA toolchain for the iCE40-HX1K, James Bowman started porting his elegant J1 processor design.

I have been involved in testing and enhanced the resulting monumental work with stronger optimisations and a handfull of features for convenience.


What you need:


Which board for you ?

If you just wish to play with a stack processor, if you have small ideas, if you desire to have IrDA communication, if you wish a cheap, cool looking board, if you are fine without tinkering with additional logic

--> HX1K Icestick is your choice.

If you wish to experiment with VGA video, if you like 7-segment displays, if you need buttons on your board

--> HX1K Nandland Go should be the right for you.

If you have large ideas in Forth, if you need a lot of IO pins, if you wish to wire in additional peripherals, opcodes or whatever you imagine, if you love to have single cycle multiplier and barrel shifters

--> HX8K breakout board will give you a nice time.


Basic usage, same for HX1K and HX8K:

Flash the binary: iceprog -b j1a.bin

Open a terminal: picocom -b 115200 /dev/ttyUSB$n --imap lfcrlf,crcrlf --omap delbs,crlf --send-cmd "ascii-xfr -s -l 30 -n"

Toggle DTR line to Reset by pressing Crtl+A Ctrl+T Crtl+A Ctrl+T

You should get a welcome message.

If you hard crash it, iceprog -t or a power cycle should recover.


Some notes for Forth users new to this target and its peripheral possibilities:

The Icestick has 8 kb of initialised RAM which is mapped from address $0000 to $1FFF and a SPI flash to store the FPGA bitstream and additional user generated memory images.

Data and return stack are 16 elements deep. James implemented them as part of the CPU itself, so they are not accessible through memory.

Pinout and board layout:

   /--------------------------------------
  /                 Header 1:  76543210-+
 /            ________              ++
/            |        |      R      --

[USB] FT | FPGA | R G R 73 Ir [USB] DI | | R 62 DA \ |____| 51 \ 40 \ Header 2: 01234567-+ --------------------------------------

Numbers are bit numbers

There is a separate IO address space, with those registers available:

 Addr  Bit READ            WRITE

 0001  0   PMOD in
 0002  1   PMOD out        PMOD out
 0004  2   PMOD dir        PMOD dir
 0008  3   misc.out        misc.out

 0010  4   header 1 in
 0020  5   header 1 out    header 1 out
 0040  6   header 1 dir    header 1 dir
 0080  7                   eint

 0100  8   header 2 in
 0200  9   header 2 out    header 2 out
 0400  10  header 2 dir    header 2 dir
 0800  11                  dint

 1000  12  UART RX         UART TX
 2000  13  misc.in
 4000  14  ticks           clear ticks
 8000  15

Contents of misc.out and misc.in:

Bitmask Bit misc.out misc.in

 0001    0  SPI CS          UART Ready to Transmit
 0002    1  SPI MOSI        UART Character received
 0004    2  SPI SCK         SPI MISO
 0008    3  IrDA-TXD        IrDA-RXD
 0010    4  IrDA-Sleep      RTS
 0020    5  CTS             Random
 0040    6  Green LED
 0080    7  Red LED
 0100    8  Red LED
 0200    9  Red LED
 0400   10  Red LED

If you are already familiar with MSP430 chips, you will know this IO port style:

You can set two registers at once if you OR together their addresses. 255 $440 io! should set all header pins as outputs.

Inputs can ORed together the same way and give an ORed result.

You can detect short-circuited pins - for example if a pin is set to output and low, but shortened to Vcc, then the OUT register will read back low, as set by you, but the IN register will read high for that pin.

Misc.out and misc.in are a mixed back of wires and flags available.

No fear to destroy the IrDA transceiver with too long pulses as it turns off the IR LED itself when IrDA-TXD is high for more than 50 us.

The UART data register is for both incoming and outgoing data, a read from it will clear the "Character received" flag and you should only write to it when "Ready to Transmit" is set.

Ticks contains a 16 bit cycle counter that counts up with 48 MHz and you can set it to any value you wish by writing to it.

Memory location $1FFE is an interrupt vector for the ticks counter overflow. You can place an opcode there, perhaps ALU Exit ($608C) or a JMP to a handler. Interrupts can be enabled with eint and disabled with dint.


Notes on HX8K:

This feels very similiar to Mecrisp-Ice on HX1K, just with a few differences:

The peripheral map is the same, but the IN, OUT and DIR registers are up to 16 bits wide, with the following connections:

$0001 Port A IN $0010 Port B IN $0100 Port C IN $0002 Port A OUT $0020 Port B OUT $0200 Port C OUT $0004 Port A DIR $0040 Port B DIR $0400 Port C DIR

                 Red LEDs
                 76543210

1.2V 3.3V oo oo oo 1.2V B13 oo oo oo A12 B12 oo ____ oo oo GND GND oo | | oo oo A11 B11 oo | FPGA | oo oo A10 B10 oo | | oo oo A9 B9 oo |____| oo oo GND GND oo oo oo A8 B8 oo oo oo A7 B7 oo oo oo A6 B6 oo Jumper config: oo oo GND GND oo oo oo A5 B5 oo | || oo oo A4 B4 oo oo oo A3 B3 oo Green oo oo GND GND oo LED oo oo A2 B2 oo FT oo oo A1 B1 oo DI oo oo A0 B0 oo oo oo GND GND oo [USB] oo oo [USB]

Port C is currently unconnected, you can wire the bits as you wish in the constraints PCF file.

Ticks contains a 16 bit cycle counter that counts up with 36 MHz and you can set it to any value you wish by writing to it.

Memory location $3FFE is an interrupt vector for the ticks counter overflow. You can place an opcode there, perhaps ALU Exit ($608C) or a JMP to a handler. Interrupts can be enabled with eint and disabled with dint.


Notes on Nandland Go:

S4 is Reset button, as DTR line is not connected on this board.

 Addr  Bit READ            WRITE

 0001  0   PMOD in
 0002  1   PMOD out        PMOD out
 0004  2   PMOD dir        PMOD dir
 0008  3   misc.out        misc.out

 0010  4                   VGA Colours
 0020  5                   VGA HSYNC
 0040  6                   VGA VSYNC
 0080  7   Segments        Segments

 0100  8
 0200  9
 0400  10
 0800  11

 1000  12  UART RX         UART TX
 2000  13  misc.in
 4000  14  ticks           clear ticks
 8000  15

Contents of misc.out and misc.in:

Bitmask Bit misc.out misc.in

 0001    0  SPI CS          UART Ready to Transmit
 0002    1  SPI MOSI        UART Character received
 0004    2  SPI SCK         SPI MISO
 0008    3  Green LED 1     Random
 0010    4  Green LED 2     S1
 0020    5  Green LED 3     S2
 0040    6  Green LED 4     S3 

Note that you can OR together the VGA lines.

VSYNC HSYNC Blue 2 Blue 1 Blue 0 Green 2 Green 1 Green 0 Red 2 Red 1 Red 0

$10: 256 128 64 32 16 8 4 2 1 $20: 512 $40: 1024

So by writing IO Register $60, you can set HSYNC and VSYNC independently from the colour register bits. If you prefer to have all VGA signals in one register, use IO port $70.

Ticks contains a 16 bit cycle counter that counts up with 25 MHz and you can set it to any value you wish by writing to it.

Memory location $1FFE is an interrupt vector for the ticks counter overflow. You can place an opcode there, perhaps ALU Exit ($608C) or a JMP to a handler. Interrupts can be enabled with eint and disabled with dint.


Special features included in Mecrisp-Ice:

You can save the complete current memory contents into the SPI flash sectors 1 to 63. Sector 0 contains the FPGA bitstream, sector 1 will be loaded automatically on startup if you save a memory image into this.

sector# load sector# save sector# erase

There is a variable "init" which is executed after loading an image.

So if you wish to create a turnkey application, prepare everything in RAM and type:

' fireworks init ! 1 save

Replace fireworks with whatever you wish to be executed on startup. Nothing is executed if init contains a zero, which is the default.

If you are not sure if it will work properly, save to any other sector and load manually. The init mechanism works the same on all sectors.

On HX8K: Sectors 0, 1 and 2 contain the bitstream. Sector 3 will be loaded automatically if available.

The Nandland Go has a smaller SPI Flash with four sectors only:

0: Bitstream 1: Autoload 2: Free 3: Free


For your convenience, some introspection features are preloaded for default.

Things to try:

0 8192 dump insight see +

They will be removed by typing "new" so that you have more space available.


Inline optimisations and constant folding are included !


Main differences to Mecrisp and Mecrisp-Stellaris:

Unlike Mecrisp and Mecrisp-Stellaris, this is running completely in RAM memory. So there is no need for special memory handling !

You can use CREATE with DOES> and if you need a buffer, just CREATE ... ALLOT it.

By the way: If you save a memory image, the last contents of the variables are saved during the process and reloaded with the image. Variables are initialised, but there is no logic to reinitialise them on Reset to the values assigned at compilation.


If you wish to build the whole package yourself:

... and their dependencies.

In a nutshell, you need to type:

compile iceprog -b j1a.bin

That's all.

;------------------------------------------------------------------------------ Here comes a word list, -- Glossary -- with short descriptions of all currently included words: View it with fixed-width font ! ;------------------------------------------------------------------------------

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

;------------------------------------------------------------------------------ ; Stack Jugglers ;------------------------------------------------------------------------------

Single-Jugglers:

    depth           ( -- +n ) Gives number of single-cell stack items.
    rdepth          ( -- +n ) HX8K only: Number of items on return stack.
    nip             ( x1 x2 -- x2 )
    drop            ( x -- )
    rot             ( x1 x2 x3 -- x2 x3 x1 )
    -rot            ( x1 x2 x3 -- x3 x1 x2 )
    swap            ( x1 x2 -- x2 x1 )
    tuck            ( x1 x2 -- x2 x1 x2 )
    over            ( x1 x2 -- x1 x2 x1 )
    ?dup            ( x -- 0 | x x )
    dup             ( x -- x x )

    >r              ( x -- ) (R: -- x )
    r>              ( -- x ) (R: x -- )
    r@              ( -- x ) (R: x -- x )
    rdrop           (  --  ) (R: x -- )

Double-Jugglers: They perform the same for double numbers.

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

;------------------------------------------------------------------------------ ; Logic ;------------------------------------------------------------------------------

    arshift         ( x1 u -- x2 ) Arithmetric right-shift of u bit-places
    rshift          ( x1 u -- x2 ) Logical right-shift of u bit-places
    lshift          ( x1 u -- x2 ) Logical  left-shift of u bit-places
    invert          ( x1 -- x2 )   Invert all bits
    not             ( x1 -- x2 )   Invert all bits = Bitwise not
    xor             ( x1 x2 -- x3 ) Bitwise Exclusive-OR
    or              ( x1 x2 -- x3 ) Bitwise OR
    and             ( x1 x2 -- x3 ) Bitwise AND
    false           ( --  0 ) False-Flag
    true            ( -- -1 ) True-Flag

;------------------------------------------------------------------------------ ; Arithmetic for single numbers ;------------------------------------------------------------------------------

    /mod            ( n1 n2 -- n3 n4 ) n1 / n2 = n4 rem n3
    mod             ( n1 n2 -- n3 ) n1 / n2 = remainder n3
    /               ( n1 n2 -- n3 ) n1 / n2 = n3
    *               ( u1|n1 u2|n2 -- u3|n3 ) 16*16 = 16 Multiplication
    min             ( n1 n2 -- n1|n2 ) Keeps smaller of top two items
    max             ( n1 n2 -- n1|n2 ) Keeps greater of top two items
    umin            ( u1 u2 -- u1|u2 ) Keeps unsigned smaller
    umax            ( u1 u2 -- u1|u2 ) Keeps unsigned greater
    1-              ( u1|n1 -- u2|n2 ) Subtracts one, optimized
    1+              ( u1|n1 -- u2|n2 ) Adds one, optimized

    2*              ( n1 -- n2 ) Arithmetric  left-shift
    2/              ( n1 -- n2 ) Arithmetric right-shift
    abs             ( n -- u ) Absolute value
    negate          ( n1 -- n2 ) Negate
    sgn             ( u1 n1 -- n2 ) Give u1 the sign of n2
    -               ( u1|n1 u2|n2 -- u3|n3 ) Subtraction
    +               ( u1|n1 u2|n2 -- u3|n3 ) Addition

;------------------------------------------------------------------------------ ; Arithmetic involving double numbers ;------------------------------------------------------------------------------

    um*             ( u1 u2 -- ud )    16*16 = 32 Multiplication

    um/mod          ( ud u1 -- u2 u3 ) ud / u1 = u3 remainder u2

    m+              ( d1 n -- d2 ) Addition of a double with a single
    m*              ( n1 n2 -- d )     n1 * n2 = d
    fm/mod          ( d n1 -- n2 n3 )  d / n1 = n3 remainder r2 floored
    sm/rem          ( d n1 -- n2 n3 )  d / n1 = n3 remainder r2 symmetric

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

    d2*             ( d1 -- d2 ) Arithmetric  left-shift

    dabs            ( d -- ud ) Absolute value
    dnegate         ( d1 -- d2 ) Negate
    d-              ( ud1|d1 ud2|d2 -- ud3|d3 ) Subtraction
    d+              ( ud1|d1 ud2|d2 -- ud3|d3 ) Addition
    s>d             ( n -- d ) Makes a signed single number double length

;------------------------------------------------------------------------------ ; Comparisions ;------------------------------------------------------------------------------

    u<=             ( u1 u2 -- flag )   Unsigned comparisions
    u>=             ( u1 u2 -- flag )
    u>              ( u1 u2 -- flag )
    u<              ( u1 u2 -- flag )
    <=              ( n1 n2 -- flag )   Signed comparisions
    >=              ( n1 n2 -- flag )
    >               ( n1 n2 -- flag )
    <               ( n1 n2 -- flag )
    0>              ( n -- flag )       Positive ?
    0<              ( n -- flag )       Negative ?
    0<>             ( x -- flag )
    0=              ( x -- flag )
    <>              ( x1 x2 -- flag )
    =               ( x1 x2 -- flag )

    d0=             ( d -- flag )

    within          ( x1 x2 x3 -- ? )   Check if x1 is within x2 and x3.

;------------------------------------------------------------------------------ ; Tools for number input ;------------------------------------------------------------------------------

    number          ( c-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
    decimal         ( -- ) Sets base to 10
    hex             ( -- ) Sets base to 16
    base            ( -- a-addr ) Base variable address

;------------------------------------------------------------------------------ ; Memory access ;------------------------------------------------------------------------------

    cmove           ( c-addr1 c-addr2 u -- ) Moves backward
    cmove>          ( c-addr1 c-addr2 u -- ) Moves forward
    move            ( c-addr1 c-addr2 u -- ) Moves u Bytes in Memory
    fill            ( c-addr u c ) Fill u Bytes of Memory with value c

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

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

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

    c@              ( c-addr -- char ) Fetches byte from memory
    c!              ( char c-addr ) Stores byte in memory

IO memory area:

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

    xor!            ( mask c-addr -- ) Toggle bits
    bic!            ( mask c-addr -- ) Clear BIts
    bis!            ( mask c-addr -- ) Set BIts

;------------------------------------------------------------------------------ ; Strings and beautiful output ;------------------------------------------------------------------------------

String routines:

    type            ( c-addr length -- )
                    Prints a string.

    rtype           ( c-addr length u -- )
                    Prints a string in a field u characters wide.

    s" Hello"       Compiles a string and
                    ( -- c-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          ( c-addr maxlength -- length ) Read input into a string.

Counted string routines:

    count           ( cstr-addr -- c-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.

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

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

    u.r             ( u width -- ) Print      unsigned right aligned
     .r             ( n width -- ) Print        signed right aligned
    d.r             ( d width -- ) Print double signed right aligned

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

Deep insights:

    words           ( -- ) Prints list of defined words.
    .x2             ( c -- ) Prints  8 bit unsigned in hex base
    .x              ( u -- ) Prints 16 bit unsigned in hex base
                             This is independent of number subsystem.

;------------------------------------------------------------------------------ ; User input and its interpretation ;------------------------------------------------------------------------------

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

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

    /string         ( c-addr1 u1 n -- c-addr2 u2 ) Cut n leading characters
    parse-name      ( -- c-addr len ) Get next token from input buffer
    parse           ( char -- c-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 ;------------------------------------------------------------------------------

    align           ( -- ) Aligns dictionary pointer
    aligned         ( c-addr -- a-addr ) Advances to next aligned address
    cell+           ( x -- x+2 ) Add size of one cell
    cells           ( n -- 2*n ) Calculate size of n cells

    allot           ( n -- ) Tries to advance Dictionary Pointer by n bytes
    here            ( -- a-addr|c-addr )
                    Gives current position in Dictionary

    ,               ( u|n -- ) Appends a single number to dictionary
    c,              ( char -- ) Appends a byte to dictionary

    unused          ( -- u ) How many free space is still available ?

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

;------------------------------------------------------------------------------ ; Dictionary expansion (more internal) ;------------------------------------------------------------------------------

    s,              ( c-addr len -- ) Inserts a string with a maximum
                                      of 255 characters without runtime
    sliteral        ( c-addr len -- ) Insert a string with runtime

    literal         ( u|n -- ) Compiles a literal

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

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

    ahead           ( -- a-addr ) Prepare a forward jump

;------------------------------------------------------------------------------ ; Flags and inventory ;------------------------------------------------------------------------------

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

    sfind           ( c-addr len -- c-addr len 0 | a-addr flags )
                           Searches for a string in Dictionary.

;------------------------------------------------------------------------------ ; Compiler essentials ;------------------------------------------------------------------------------

    execute         ( a-addr -- ) Calls subroutine
    recurse         ( -- ) Lets the current definition call itself
    ' name          ( -- a-addr ) Tries to find name in dictionary
                                  gives back executable address
    ['] name        ( -- a-addr)  Tick that compiles the executable address
                                  of found word as literal
    postpone name   ( -- ) Helps compiling immediate words.
    does>           ( -- ) executes: ( -- a-addr )
                           Gives address to where you have stored data.
    create name     ( -- ) Create a definition with default action
    >body           ( a-addr -- a-addr ) Address of data field after create
    state           ( -- a-addr ) Address of state variable
    ]               ( -- ) Switch to compile state
    [               ( -- ) Switch to execute state
    ;               ( -- ) Finishes new definition
    : name          ( -- ) Opens new definition
    :noname         ( -- a-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 as long 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               ( -- u|n ) Gives second loop index
    i               ( -- u|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

;------------------------------------------------------------------------------ ; SPI and low-level flash memory access ;------------------------------------------------------------------------------

    spix            ( c1 -- c2 ) Exchange one byte on SPI
    >spi            ( c -- ) Send one byte to SPI
    spi>            ( -- c ) Receive one byte from SPI
    idle            ( -- ) Set SPI flash to idle state
    spiwe           ( -- ) Write enable on SPI flash
    waitspi         ( -- ) Wait for write or erase to finish

;------------------------------------------------------------------------------ ; Memory images ;------------------------------------------------------------------------------

Sectors from 1 to 63. Sector 1 is automatically loaded after Reset.

    load            ( sector# -- ) Loads an image
    save            ( sector# -- ) Saves an image
    erase           ( sector# -- ) Erase an image

    init            ( -- a-addr ) Variable containing either zero
                                  or the address of a turnkey definition
                                  which is executed automatically

;------------------------------------------------------------------------------ ; Misc hardware ;------------------------------------------------------------------------------

    leds            ( x -- ) Display bitpattern on LEDs
    ms              ( u -- ) Wait u milliseconds

    ticks           ( -- u ) Read current ticks
    now             ( -- )   Clear tick counter
    delay           ( u -- ) Wait until u ticks are reached

    randombit       ( -- 0 | 1 ) Gives a random bit
    random          ( -- x ) Gives a random number

    eint?           ( -- ? ) Are interrupts enabled ?
    eint            ( -- ) Enable ticks counter overflow interrupt
    dint            ( -- ) Disable interrupt

;------------------------------------------------------------------------------ ; Insight tools that are gone after NEW ;------------------------------------------------------------------------------

    .s              ( many -- many ) Prints stack contents

    dump            ( addr len -- ) Dump memory contents

    insight         ( -- ) Complete printout of dictionary structure

    name.           ( a-addr -- ) If this is the code-start of a definition,
                                  try to print its name.
    memstamp        ( a-addr -- ) Show memory location nicely
    disasm-$        ( -- a-addr ) Variable for current disasm position
    disasm-cont     ( -- a-addr ) Variable: Continue up to this position
    disasm-step     ( -- ) Disassemble one more instruction

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

Matthias Koch, Summer 2015