Library of functions for MineOS


I would like this to eventually became a repository of functions that can be used by other programs. If the word is useful for the user and not other programs, it should go to the Utilities instead.

Operators

Some additional stack operations.
: DIG \ a b c -- a b c a
\ analogous to OVER
  2OVER ;
: BURY \ a b c -- c a b c
\ analogous to TUCK
  -ROT DIG ;
: RUST \ a b c -- b c
\ analogous to NIP
  ROT DROP ;
: 2SWAP \ a b c d -- c d a b
  >R -ROT R> -ROT ;
: 2OVER \ a b c d -- a b c d a b
\ (original 2OVER is incorrect, this is correct)
  >R >R 2DUP R> -ROT R> -ROT ;

Direct to address increment/decrement (useful for counters).
: 1+! \ addr --
\ Increment counter at address addr
  DUP @ 1+ SWAP ! ;
: 1-! \ addr --
\ Decrement counter at address addr
  DUP @ 1- SWAP ! ;

Find the highest bit set.
: LOG2 \ x -- y
\ Compute the highest bit position of x into y, return -1 if 0
\ Examples: 0->-1, 1->0, 2->1, 3->1, 4->2, 7->2, 8->3, 65535->15
  -1 BEGIN SWAP ?DUP WHILE 1 U>> SWAP 1+ REPEAT ;

Check if a number is within a range.
: WITHIN \ x a b -- bool
\ Check if a <= x < b
  -ROT OVER <= -ROT > AND ;

Test a number against a series of values returning true if any match. The last parameter is the count of test values.
( n n1 n2 ... nc -- f )
:  IN
   SP@ SWAP 1+ 2* +
   BEGIN SP@  OVER CELL -  < WHILE
      TUCK @ = IF
         SP!
         DROP
         TRUE
         EXIT
      THEN
   REPEAT
   2DROP
   FALSE
   ;
 
\ example
pString C@ BL 13 10 9 4 IN IF
   \ is white char
THEN

Check sign of given number, returns either 0 if input value was 0 or 1 resp. -1 in respect to sign.
: SIGN \ n1 -- n
DUP 0= IF EXIT THEN 0< IF -1 ELSE 1 THEN ;

Return absolute value.
: ABS \ n1 -- n
DUP 0< IF NEGATE THEN ;

Simple double word (32 bit) equality - useful for sortron item identifiers.
\ d d -- f
: M= ROT = -ROT = AND ;
 
\ d d -- f
: M<> M= 0= ;
More 32 bit operators.
Most of the following words are constructed from op codes, so the FORTH compiler cannot be used. They must be injected.
Each double word value parameter should be little endian (low high).
\ double addition M+
\ d d -- d
HEADER M+
250 ,C 104 ,C 24 ,C 99 ,C 2 ,C 131 ,C 2 ,C 138 ,C
99 ,C 0 ,C 131 ,C 0 ,C 2 ,C
 
\ double subtraction M-
\ d d -- d
HEADER M-
163 ,C 6 ,C 56 ,C 227 ,C 2 ,C 131 ,C 6 ,C 163 ,C
4 ,C 227 ,C 0 ,C 131 ,C 4 ,C 250 ,C 250 ,C 2 ,C
 
\ unsigned double relational UM?
\ d d -- n
HEADER UM?
104 ,C 195 ,C 2 ,C 144 ,C 16 ,C 208 ,C 22 ,C 104 ,C
250 ,C 195 ,C 0 ,C 144 ,C 10 ,C 208 ,C 16 ,C 104 ,C
244 ,C 0 ,C 0 ,C 128 ,C 14 ,C 104 ,C 104 ,C 104 ,C
244 ,C 1 ,C 0 ,C 128 ,C 6 ,C 104 ,C 104 ,C 104 ,C
244 ,C 255 ,C 255 ,C 2 ,C
 
\ signed double relational M?
\ d d -- n
HEADER M?
104 ,C 195 ,C 2 ,C 48 ,C 16 ,C 208 ,C 22 ,C 104 ,C
250 ,C 195 ,C 0 ,C 48 ,C 10 ,C 208 ,C 16 ,C 104 ,C
244 ,C 0 ,C 0 ,C 128 ,C 14 ,C 104 ,C 104 ,C 104 ,C
244 ,C 1 ,C 0 ,C 128 ,C 6 ,C 104 ,C 104 ,C 104 ,C
244 ,C 255 ,C 255 ,C 2 ,C
 
\ dereference double at addr M@
\ addr -- d
HEADER M@
250 ,C 181 ,C 0 ,C 72 ,C 181 ,C 2 ,C 72 ,C 2 ,C
 
\ write double to addr M!
\ d addr --
HEADER M!
250 ,C 104 ,C 149 ,C 2 ,C 104 ,C 149 ,C 0 ,C 2 ,C
 
\ convert signed single to signed double >M
\ n -- d
HEADER >M
104 ,C 159 ,C 72 ,C 175 ,C 72 ,C 2 ,C
 
\ double is equal M=
\ d d -- f
: M= M? 0= ;
 
\ double not equal M<>
\ d d -- f
: M<> M? 0<> ;
The UM? and M? words are unsigned and signed relational testers. If the first double word value is less than the second they return -1. If greater they return 1, and if equal they return zero. All the relational operators can be constructed from these base operators if desired, or if rarely used these can be used as is. The equality and inequality versions have been defined as the most likely to be used.

Examples of other variants:
: M< M? 0< ;
 
: UM>= UM? 0 >= ;
Hint: To convert an unsigned single value to a double just use zero for the high word. To convert either signed or unsigned double to single just DROP the high word (this will truncate the value if it was greater than a one word range).

Compilation

Quote a word during execution, i.e. insert its address as a literal. (This word was added to MineOS as LITERAL)
: ['] \ -- code-addr
\ Put code address of the next word to stack (works only in compile)
  ' (lit) (lit) , , ; IMMEDIATE

Function Compiling

See the Function Compiling page to author word definitions with a function like syntax and named parameters and local variables.

SELECT CASE Syntax

This page contains compiler extension code which allows the use of a SELECT ... CASE|BETWEEN ... SELECT; conditional block syntax.
n SELECT n CASE
   ...
NEXT n CASE
   ...
NEXT n n BETWEEN
   ...
NEXT DEFAULT CASE
   ...
SELECT;

System Hooks

System hooks are custom routines that are run by the system when certain events take place. MineOS does not implement system hooks but the following pages detail patches and code to implement them. They each define a variable that if contains a value other than zero when the event occurs, it is assumed the address of the routine (word) to run and is executed. They can be set with a routine constantly or set and reset as required.

Boot Hook
A boot hook is called by the system after fully booting up. It can be used for extended boot code and/or jumping straight to an application. When this patch is applied on a system is crucial, see the page for details.

Idle hook
An idle hook is called by the system when its not busy doing something else. These are usually used for monitoring and low priority timing routines.

Abort hook
An abort hook is called by the system when ABORT is called, from anywhere. This allows clean up and escape code before control is lost.

Unknown Token Hook
An unknown token hook is called by the system when a token is given that cannot be identified (in intermediate or compile modes), before the “Unknown token: <token>” error. This hook allows custom input handling.

Data and Buffers


Arrays and Structures
This page contains words that create and manage compound data types.

Heap Manager
This page contains a basic heap manager.


Printing

The .B and .W will print hex byte and word, zero padded.
: HEXDIGIT \ n -- c
\ Convert lowest digit of n to character c in base 16
  15 AND DUP 9 > IF 55 ELSE 48 THEN + ;
: .B  \ b --
\ Write out byte b in hex - 2 digits
  DUP HEXDIGIT SWAP 4 U>> HEXDIGIT EMIT EMIT ;
: .W  \ w --
\ Write out 2 bytes w in hex - 4 digits
  DUP 8 U>> .B .B ;

IOX

PULSE word is useful to send a simple signal to something:
: PULSE  \ bits delay --
\ Set output bits for a delay ticks, then reset
  OVER IOXSET TICKS IOXRST ;

These functions can be used to test IOX bits:
: IOXSET? \ bits -- bool
\ Return true if all the selected bits are set
  DUP IOX@ AND = ;
: IOXRST? \ bits -- bool
\ Return true if all the selected bits are reset
  IOX@ AND 0= ;

STOPWATCH allows you to wait for an event in a polling loop.
: STOPWATCH \ timeout condition -- time
\ Measure time until condition word is false
  SWAP 0 BEGIN 2DUP > 3 PICK EXECUTE AND
  WHILE 1+ TICK REPEAT NIP NIP ;

Time

Returns the current CPU tick counter value as little endian double word value. The word is constructed from injected op codes.
\ -- d
HEADER TIME 239 ,C 135 ,C 72 ,C 223 ,C 2 ,C

VALUE and TO

VALUE is used to create a variable that returns its value instead of its address. TO is used to reset a VALUE. VALUE is a synonym for CONSTANT and may be omitted with CONSTANT used instead. VALUE can only be used in intermediate mode, while TO may be used in both intermediate and compile modes but the VALUE must exist at compile time.
\ Create a new value
\ n VALUE token
( n -- )
:  VALUE
   CONSTANT
   ;
 
\ Reset the value of a value
\ n TO token
( n -- )
:  TO
   ' 3 +
   STATE @ IF
      LITERAL (lit) , , LITERAL ! ,
   ELSE
      !
   THEN
   ; IMMEDIATE
 
\ Example usage
10 VALUE TEST
TEST . \ 10
5 TO TEST
TEST . \ 5

Weird stuff

I originally wrote this for my automated probe. Someone might find it useful (e.g. for mining), I dunno.
: SPIRAL \ x y -- x+ y+
\ Return next position in spiral starting at 0,0
2DUP 2DUP - -ROT + 0 >
IF 0< IF SWAP 1+ SWAP ELSE 1- THEN
ELSE 0 > IF SWAP 1- SWAP ELSE 1+ THEN THEN ;

BloodyRain2k's DISKEXEC

https://dl.dropbox.com/u/59546146/diskexec.img

DISKEXEC allows to run a plain text forth code from the disk, this means you can write your sourcecode with your favorite texteditor into a disk's imagefile and then execute that code ingame.

It expects a semi optional parameter which is the disksector at which it will start interpreting, if the stack is empty it will use 0 as startsector itself, otherwise it will start at the given sector.
This is only needed if you for example put a source block at a specific position on your disk behind the system data. Unless you alter your system disk in that way you don't have to bother with this, just make sure your stack is empty or a 0 on top when you run it.

There need to be atleast as much byte free as the source takes up + 256.
So if your source is 1000 byte you need atleast 1256 byte of free memory.
This is due to having to parse the whole source to guarantee that words like ERASE that read from the Terminal Input Buffer aren't missing their parameters.
Due to this it is recommended to either only use it for small source or have atleast one ram extension installed.

Also another thing you have to avoid is loading source that defines large variables, these make the compiled code (namely HERE) expand very fast which could result in an overwritten buffer.
An example would be "VARIABLE moo 256 ALLOT" this would instantly write into the buffered sourcecode and mostlikely corrupt it.

A workaround is splitting up the creation of variables and the following code, the last line of the creation block would just have to contain the diskexec command with the parameter for the following block.

HEX : DISKEXEC DEPTH 0= IF 0 THEN
HERE 100 + SCRATCH ! DISKADDR @ RBP!
200 0 DO 300 80 0 FILL
I 80 * SCRATCH @ + DUP 81 0 FILL
OVER I + 380 ! 4 382 C!
BEGIN 382 C@ 4 = WHILE TICK REPEAT
300 SWAP 80 MOVE
382 C@ FF = 300 C@ 0= OR 37F C@ 0= OR IF LEAVE THEN
LOOP DROP
SCRATCH @ INTERPRET ;