Last updated: January 6, 2007
Evolve 4.0 - The KFORTH Language
VOLVE    4.0

The KFORTH Language

Table of Contents:


Introduction:

So you want to learn KFORTH, do you? Well lets start with a simple example.

First launch the KFORTH Interpreter from the Evolve program. You'll see this dialog:

Now lets write a program to subtract two years. In the KFORTH SOURCE CODE pane enter the following KFORTH program:


{ 2006  1968 - }

All KFORTH instructions must be enclosed within curly braces. This is called a "code block". Code blocks are assigned numbers starting with 0. Inside of the curly braces goes "the code". This consists of numbers and instructions (using in postfix notation).

Now press the [Compile] button. And this is what you will see in the upper pane:

main:
{
     2006  1968 -
}

The compiled version looks very similar to what we typed it. The first thing to note is the label main: was added in front our code block. In KFORTH the first code block (code block 0) is where program execution starts. That is why the disassmbler inserted the label main:.

Now click on [RUN]. The answer '38' will appear in the data stack pane. You can also single-step through this example. To single step, first press [Reset].

Lets add another code block to this example.

Your neighbor is building a BBQ, to measure for the foundation he needs to know what 'x' is (I know, lame example, but I'm hungry right now). You want to help him so he'll invite you over for BBQ. You realize that this is a perfect job for KFORTH! We will add a new code block to our example, which when executed will compute 'x':

Here, type this:

	{ 2006 1968 - }
	{ 98 dup * 311 dup * + sqrt }

When you compile this program, the disassembly will be shown as:

main:
{
	2006 1968 -
}

row1:
{
	98 dup * 311 dup * + sqrt
}

The disassembler gives every code block a label. The first one, as we already saw is called main: subsequent code blocks are labeled row1, row2, row3 and so on...

Now click on [Run]. Whoooa! What the fuck? The data stack only shows the same value '38'? What's wrong?

The problem is KFORTH automatically executes code block 0, but all other code blocks must be explictly called, using the call instruction.

Let's try this again.... Now we will add '1 call' to the end of the first code block, like so:

	{ 2006 1968 - 1 call }
	{ 98 dup * 311 dup * + sqrt }

Now click on [Compile] and then [Run].

Hopefully, when the program terminates there are two items on the stack. '326' and '38'. The '326' is the answer to our "find x?" problem. Since KFORTH only uses integers the answer has been rounded to the nearest whole number.

This example shows how code blocks are treated as executable functions. The call instructions pops a number from the top of the data stack and treats it as a code block number, and calls that code block.

When humans write KFORTH they should use labels, so here is the same program with labels added:

main:
	{ 2006 1968 - findx call }

findx:
	{ 98 dup * 311 dup * + sqrt }

When your [Compile] this, your labels are lost and the disassembler picks labels like: "main, row1, row2, row3, ...". Anyway, here is what you'll see in the disassembly pane:

main:
{
	2006 1968 - 1 call
}

row1:
{
	98 dup * 311 dup * + sqrt
}

Also notice the label before the call instruction is 1 not row1. This is because the disassembler cannot reliably regenerate labels INSIDE of code blocks (I don't want to go into the reasons why, but you could calculate the code block number rather than hard code a value).


Labels

A label is any text followed by a colon ':'. A label can consist of any characters except: whitespace, colon ':', semi-colon ';' , or curly braces '{', '}'. Also, a label cannot clash with the name of an existing instruction.

To define a label simply follow it by a colon (without spaces). The value of the label is the next code block number to be assigned (code block numbers are assigned startig at 0 and then incrementing by 1 for each new code block).

To use a label, just enter its name (do not use colon). The compiler will convert this label reference to the code block number.


Case Insensitive

Instructions and labels are case insensitive. So "MAKE-SPORE" is the same as "Make-Spore".


Registers

The KFORTH computing model includes ten registers R0 through R9. These can be used to store/retrieve temporary values. For example,

main:
{	2006 1968 - R1!
	findx call R5!
	R1 R5 + R9!
}

findx:
{
	98 dup * 311 dup * + sqrt
}

This is the same example as before, but we have added 'R5!' and 'R1!'. These instructions take the value on top of the data stack and stores it into the indicated register.

The line R1 R5 + R9! fetches the value of R1 and R5 and adds these values and then writes the result to register R9.

Registers are not used by any KFORTH instructions, so they can be managed and used by the user without worrying about conflicts.


Comments

Use the semi-colon ; to insert a comment into a KFORTH program. The rest of the line after the semi-colon is ignored.


; my first KFORTH program
main:
{
	2006 1968 - R1! ; compute difference between 2006 and 1968
	findx call R5!  ; call the findx routine and store in result in R5.
	R1 R5 + R9!
}

; my first KFORTH subroutine
findx:
{
	98 dup * 311 dup * + sqrt
}


Stack Underflow

It can be expected that a mutated KFORTH program would attempt instructions without having enough data on the data stack. How does KFORTH handle this?

KFORTH's policy for stack underflow is to treat the operation as a NO-OP. Meaning the program just continues to the next instruction without removing anything from the stack.

For example, this code calls the plus (+) instruction but only 1 data item is on the stack.

main:
{
	200 + 3 *
}

The '+' operation needs two values on the stack. Since this is not the case, the stack underflow policy is to simply proceed to the next instruction. When the '*' operation is executed it finds two values on the data stack, so it is able to perform its job (computes 600, replacing 200 and 3 on the data stack).


Undefined Functions

In addition to stack underflow, sometimes the value to an instruction (a function) are not defined. For example division by 0 and trying to take the square root of a negative number. Therefore, div mod, and /mod will act as NO-OP instructions when the second operand is zero. And when the argument to sqrt is negative the instruction leaves the stack unchanged.


Flow Control

We already know about the call instruction, which transfers control to a new code block. What else can we do? KFORTH includes the following instructions for flow control:


call

We have already seen this instruction at work. It's the unconditional form of subroutine call. Below we explore if and ifelse which are conditional subroutine call instructions.

You may be wondering what "call" does with a bogus code block number? For example, "-900 call", or "555 call". When call instruction (and if, ifelse) is used with a bogus code block number, it removes the values from the stack, as if this were a normal instruction, but it won't call any code block. Conceptually, we can think of a KFORTH program as having empty rows for every possible integer (except the ones that have been defined).

main:
{
	5000 call
}

row1: { }
row2: { }
row3: { }
row4: { }
row5: { }
row6: { }
...
row9999999: { }


if

Here's an example of using the IF instruction.

main:
{
	521 R1!
	R1 dup 500 >= blah if
}

blah:
{
	500 -
}

This code checks if R1 is greater than 500 and if true, calls the subroutine blah. Blah will subtract 500. The 'dup' instruction creates a 2nd copy of R1 on the data stack.


Nested Code Blocks:

There is another human friendly way to write this same thing. It is called nested code blocks. Here is how to re-write this example,

main:
{
	521 R1!
	R1 dup 500 >= { 500 - } if
}

We nested the code block that use to be blah, directly into main. If you [Compile], you'll notice that the nesting is removed. This reveals how the nesting actually works. Nesting code blocks is just for human convienience.

When compiled it maps to the orginal version we wrote that used the code block blah, see:

main:
{
	521 R1! R1 dup 500 >= 1 if 
}

row1:
{
	500 - 
}

Nesting can be of unlimited depth. But, again this nesting is a lexical feature, and does not imply and special powers in the KFORTH language. It is simply a way to make writing KFORTH programs simpler.


ifelse

This instruction calls one of two code blocks depening on if the condition is true or false.

main:
{
	499 R1!
	R1 dup 500 >=
		{ 500 - R1! }
		{ 2 / R1! }  ifelse
}

First notice that the KFORTH compiler is free-format, allowing you to space your KFORTH programs any way which you desire.

This is what the code will be converted into by the compiler:

main:
{
	499 R1! R1 dup 500 >= 1 2 ifelse 
}

row1:
{
	500 - R1! 
}

row2:
{
	2 / R1! 
}

Which does the same as this C code:

/* C code, not KFORTH */
void main()
{
	int R1;

	R1 = 499;
	if( R1 >= 500 ) {
		R1 = R1 - 500;
	} else {
		R1 = R1 / 2;
	}
}

The ifelse instruction requires three arguments to be on the data stack. If this is not the case, then the stack underflow policy is to just treat the ifelse as a no-op.


?loop

As this awsome diagram illustrates, the ?loop instruction loops! It is a form of controlled goto. It does not do a subroutine call, instead we simply branch back to the beginning of the current code block (if the value on top of the stack is non-zero). Otherwise we continue to the next instruction.

main:
{
	{ ...stuff... } call 1 ?loop
}

The instructions 1 ?loop causes execution to return to the start of the code block, therefore this will loop forever.


?exit

This diagram shows how the exit instruction works. The ?exit instruction conditionally exits (branches to the end of the code block) the current code block. If the top of the data stack is non-zero then we jump to the end of the code-block. Otherwise we continue to the next instruction. As with ?loop, this instruction does not involve any subroutine calling. It is a branch.

These are the only branching instructions in KFORTH.


DATA TYPES

KFORTH has the most elaborate and rich data types in the whole history of computer languages!!!! Bwhahahahaha! Just kidding. KFORTH only has one data type: a 64-bit signed integer.

That's it. There are no strings, booleans, arrays, etc...

a 64-bit number is quite large. Here is the min/max range of this data type:
-9,223,372,036,854,775,808 to +9,223,372,036,854,775,807

Only decimal notation is accepted. Numeric literals may be preceeded by a minus (-) sign to indicate a negative value.


KFORTH INSTRUCTION REFERENCE

Here are all the core KFORTH instructions. (the instructions that control organisms/cells are listed here).

The USAGE column uses forth notation to document these instructions. This notation attempts to show the data stack before and after the call to the instruction. For example,

	(a b c -- n )
The stuff before -- is the state of the data stack before calling the instruction. And the stuff after -- is state of the data stack AFTER this instruction finishes.

In this example, the instruction takes three arguments a, b, and c. After the instruction executes those three arguments are replaced with a single value n (which is the result).

INSTRUCTION USAGE DESCRIPTION
call ( code-block -- ) Remove top item from the stack. This value is a 'code-block' number which will be called. If code block is invalid, the request is ignored. In disassembly row123 is code-block 123, and so on.

if ( expr code-block -- ) Removes two items from top of stack. If expr is non-zero then code-block is called. Otherwise, execution continues on the next instruction.

ifelse ( expr true-block false-block -- ) removes three items from top of stack. If expr is non-zero then call true-block, else call false-block.

?loop ( n -- ) Remove 1 item from the stack. If value is non-zero jump to the start of the current code block. Otherwise continue with the next instruction after '?loop'.

?exit ( n -- ) Remove 1 item from the stack. If non-zero then exit current code block.

pop ( n -- ) Remove item from stack and discard it.

dup ( a -- a a ) Duplicate item on top of the stack.

swap ( a b -- b a ) Swap top two elements on the stack.

over ( a b -- a b a ) Copy second item from the stack.

rot ( a b c -- b c a ) Rotate third item to top.

pick ( ... v3 v2 v1 n -- ... v3 v2 v1 vn ) Grab arbitrary item on the stack. n is the item to grab. n numbering begins with 1.

?dup ( n -- n n | 0 ) Duplicate top element if non-zero.

-rot ( a b c -- c a b ) Rotate top to third position.

2swap ( a b c d -- c d a b ) Swap pairs.

2over ( a b c d -- a b c d a b) Leapfrog pair.

2dup ( a b -- a b a b ) Dupicate pair.

2pop ( a b -- ) Remove pair.

nip ( a b -- b ) Remove 2nd item from stack.

tuck ( a b -- b a b) Copy top item to third position.

1+ ( n -- n+1 ) Add 1 to the item on top of the stack.

1- ( n -- n-1 ) Subtract 1 from item on top of the stack.

2+ ( n -- n+2 ) Add 2 to item on top of the stack

2- ( n -- n-2 ) Subtract 2 from the item on top of the stack.

2/ ( n -- n/2 ) Half value.

2* ( n -- n*2 ) Double value.

abs ( n -- abs(n) ) Absolute value of n.

sqrt ( n -- sqrt(n) ) Square root. n must be positive.

+ ( a b -- a+b ) Addition top to elements on stack.

- ( a b -- a-b ) Subtraction first item on stack from 2nd.

* ( a b -- a*b ) Multiply.

/ ( a b -- a/b ) Divide.

mod ( a b -- a%b ) Modulos.

/mod ( a b -- a%b a/b ) Divide and modulos.

negate ( n -- -n ) negate top item on stack.

2negate ( a b -- -a -b ) negate top two items on stack. (useful for computing a "flee" direction to evade something).

= ( a b -- EQ(a,b) ) Equal to.

<> ( a b -- NE(a,b) ) Not equal to.

< ( a b -- LT(a,b) ) Less than.

> ( a b -- GT(a,b) ) Greater than.

<= ( a b -- LE(a,b) ) Less than or equal to.

>= ( a b -- GE(a,b) ) Greater than or equal to.

0= ( n -- EQ(n,0) ) Is element on top of the stack equal to 0?

or ( a b -- a|b ) Bitwise OR. Can be used as a logical OR operator too, because KFORTH boolean operators return 1 and 0.

and ( a b -- a&b ) Bitwise AND. Can be used a a logical AND operator too, because KFORTH boolean operators return 1 and 0.

not ( n -- !n ) Logical NOT.

invert ( n -- ~n ) Invert bits (Bitwise NOT).

xor ( a b -- a^b ) XOR function.

min ( a b -- min(a,b) ) Minimum value.

max ( a b -- max(a,b) ) Remove 2 items from stack and replace with maximum value.

CB ( -- CB ) Pushes the current code block number on the data stack. Can be used to implement "relative" code block addressing.

Rn ( n -- Rn ) Pushes register Rn on the data stack. If n is not 0-9 the request is ignored.

R0 ( -- R0 ) Pushes register R0 on the data stack

R1 ( -- R1 ) Pushes register R1 on the data stack

R2 ( -- R2 ) Pushes register R2 on the data stack

R3 ( -- R3 ) Pushes register R3 on the data stack

R4 ( -- R4 ) Pushes register R4 on the data stack

R5 ( -- R5 ) Pushes register R5 on the data stack

R6 ( -- R6 ) Pushes register R6 on the data stack

R7 ( -- R7 ) Pushes register R7 on the data stack

R8 ( -- R8 ) Pushes register R8 on the data stack

R9 ( -- R9 ) Pushes register R9 on the data stack

Rn! ( val n -- ) Removes 2 items from the data stack 'n' is the register and 'val' is the value to store into register Rn. If 'n' is not 0-9 then this instruction ignores the request.

R0! ( val -- ) Removes 1 item 'val' from the data stack and stores 'val' into register R0

R1! ( val -- ) Removes 1 item 'val' from the data stack and stores 'val' into register R1

R2! ( val -- ) Removes 1 item 'val' from the data stack and stores 'val' into register R2

R3! ( val -- ) Removes 1 item 'val' from the data stack and stores 'val' into register R3

R4! ( val -- ) Removes 1 item 'val' from the data stack and stores 'val' into register R4

R5! ( val -- ) Removes 1 item 'val' from the data stack and stores 'val' into register R5

R6! ( val -- ) Removes 1 item 'val' from the data stack and stores 'val' into register R6

R7! ( val -- ) Removes 1 item 'val' from the data stack and stores 'val' into register R7

R8! ( val -- ) Removes 1 item 'val' from the data stack and stores 'val' into register R8

R9! ( val -- ) Removes 1 item 'val' from the data stack and stores 'val' into register R9

SIGN ( n -- SIGN(n) ) Compute sign of 'n'. If n is negative, SIGN will return -1. if n is greater than 0, SIGN will return 1. If n is 0, SIGN will return 0.

PACK2 ( a b -- n ) Combine two 32-bit integers 'a' and 'b' into a single value 'n'.

UNPACK2 ( n -- a b ) Extract two 32-bit integers 'a' and 'b' from a single value 'n'.

PACK4 ( a b c d -- n ) Combine four 16-bit integers 'a', 'b', 'c', and 'd' into a single value 'n'.

UNPACK4 ( n -- a b c d ) Extract four 16-bit integers 'a', 'b', 'c', and 'd' from a single value 'n'.


Example Code 1

main:
{
  
    3 cube call
    99 cube call

   ; this line computes (9^3)^3
   9 cube call cube call

}

;
; cube the value on top of the stack
;
cube:
{
    dup dup * *
}


Example Code 2

main:
{
	20 fact call
}

;
; Recursive factorial algorithm:
; ( n -- factorial(n) )
;
fact:
{
	dup 0 =
		{ pop 1 }
		{ dup 1 - fact call * } ifelse
}


Example Code 3

;
; compute: (R0 * 8 / R3 + R1) * R2
;
; Where:
;   R0 = 10
;   R1 = 20
;   R2 = 5
;   R3 = 2
;
main:
{
    10 R0!
    20 R1!
    5  R2!
    2 R3!

   R0 8 * R3 / R1 + R2 *
}


Example Code 4

main:
{
	20 fact call
}

;
; non-recursive factorial algorithm:
; ( n -- factorial(n) )
;
fact:
{
    1 swap
    {
        dup 0 = ?exit
        dup 1- -rot
        * swap
        1 ?loop
    } call

    pop
}


Example Code 5

;
; use code blocks as a form of array/table lookup.
; This will fetch the pair (5, 6) from the "table":
;
main:
{
    mytable 3 + call
}


mytable:
   { 2 3 }		; index 0
   { 1 1 }		; index 1
   { 3 4 }		; index 2
   { 5 6 }		; index 3
   { 10 5 }	; index 4


Example Code 6

;
; Code blocks can also be used as a cheap
; way to implement named contants.
; (see PI).
;
main: {
	ugly call 100 511 897 + + +
}

ugly: {
    987 PI call +
}

; a constant, as a code block
PI: { 3 }


Example Code 7

;
; look_n_fertilize
; ----------------
;
; USAGE: (x y e -- r)
;
; DESCRIPTION:
;	This subroutine looks for a spore in direction (x, y).
;	If a spore is found, then ferilize it with a second
;	spore with energy 'e'
;
; RETURNS
;	0 is no spore found, or could not create a spore
;	1 is returned on success
;
; HOW TO CALL:
;	1 0 5 look_n_fertilize call
; 
look_n_fertilize:
{
	-rot 2dup look-spore
		{ rot make-spore } { 2pop pop 0 } ifelse
}


Example Code 8

main:
{
    -100 19 normalize call
    -40 -234 normalize call
}

;
;  ( x y -- NORM(x) NORM(y) )
;
; normalized a pair of values:
;
normalize:
{
         norm1 call swap norm1 call swap
}

;
; (n -- NORM(n))
;
; Compute this function:
;
;	n < 0	NORM(n) = -1
;	n = 0	NORM(n) = 0
;	n > 0	NORM(n) = 1
;
norm1:
{
	dup 0 > { pop 1 } { 0= { 0 } { -1 } ifelse } ifelse
}


Example Code 9

See an example of Towers of Hanoi in KFORTH.