Sunday, June 14, 2009

Primary ColorForth Chapter 2

First some housekeeping:

Since Starting Forth is based on traditional Forth, there are some examples that won't work out of the box on ColorForth. Most of the required words can be easily implemented such as spaces back in chapter 1. Some words already exist but are not available in interactive mode because they have only been defined in the macro wordlist. I will define definitions needed for the Starting Forth examples as we go along. Alternatively here are the new definitions together.

/mod ab-qr /mod ;
mod ab-r mod ;
swap ab-ba swap ;
over ab-aba over ;
rot abc-bca push swap pop swap ;
2swap abcd-cdab push rot rot pop rot rot ;
2dup ab-abab over over ;
2over abcd-abcdab push push 2dup pop pop 2swap ;
2drop ab- drop drop ;

Forth Arithmetic -- Calculator Style

Starting Forth identifies the four fundamental arithmetic instructions in traditional Forth are +, -, *, and /. ColorForth has +, * and /. You might be wondering what happened to -. It is a macro that returns the one's complement (the same as invert in ANS Forth). This corresponds to the way - is used in Charles Moore's MISC processors since MuP21. You can still subtract by adding the negative. For constants just use the negative value. For variables or calculated results you can get the negative using NEGATE.

Starting Forth then gives several examples of postfix math. Note as mentioned in chapter 2, in ColorForth you don't need to use
. in interactive mode except as a shortcut for drop. You can also clear the entire stack using c. ColorForth continuously displays the parameter stack in the lower left of the screen. Try the following:

17 5 +
7 8 *
3 4 +
500 -300 +
5 6 *
20 4 /
c


An example of adding multiple numbers:

17 20 + 132 + 3 + 9 + .

Converting infix expressions to postfix (quizzie 2-a)

Here is a little trick I learned as a computer science undergraduate. Fully parenthesize your expression and move all operators to the end of the corresponding parenthesis. Examples:

c(a + b) => (c*(a+b)) => (c(a b)+)* => c a b + *

a*b/100 => ((a * b)/100) => ((a b)*100)/ => a b * 100 /

Try the following on your own:

(3a - b)/4 + c

(a + 1)/4

x(7x + 5)

Forth Arithmetic -- Definition Style

The next examples are for converting from yards or feet to inches. We know 1 yard = 36 inches and 1 foot = 12 inches. Here is the conversion code:

yards-inches 36 * ;
feet-inches 12 * ;

Usage example:

10 yards-inches
2 feet-inches

If we always want results to be in inches, we can define:

yards 36 * ;
feet 12 * ;
inches ;

to get the phrase

10 yards 2 feet + 9 inches +

Now perhaps you want to be able to use singular or plural words for better readability. Starting Forth used the following code:

: yard yards ;
: foot feet ;
: inch ;

That has the undesiable effect of adding an extra word call just for the sake of readability. ColorForth can do the same without the overhead using fall through factoring. The same definition can have multiple entry points. So in ColorForth you can define:

yard
yards 36 * ;
foot
feet 12 * ;
inch
inches ;

Note that yard and yards, foot and feet, inch and inches all point to the same memory location.

Now let's consider the earlier example of adding 5 numbers together.

17 20 + 132 + 3 + 9 +

The same thing could be accomplished by moving the
+'s to the end.

17 20 132 3 9 + + + +


Changing this to a definition we get:

5sum + + + + ;

Now say if in the same program sometimes we needed to add 5 numbers together and sometimes we needed to add 3? We could use fall through factoring here.

5sum + +
3sum + + ;

This time
5sum and 3sum point to different locations in memory. If you call 5sum it does the first 2 additions and then "falls through" to the remaining 2 additions in 3sum. If you call 3sum it just does the required 2 additions and returns.

An equation for another definition:

(a + b) * c

In postfix:

a b + c *

Alternatively we could say:

c * (a + b)

In postfix:

c a b + *

from which we can write the definition:

solution + * ;

It's a good time to point out that in Forth it's sometimes better to re-write an equation to make it easier to manipulate on the parameter stack. Here is the same code as a word problem:

If a jet plane flies at an average air speed of 600 mph and if it flies with a tail wind of 25 mph, how far will it travel in five hours?

If we define:

flight-dist + * ;

we could enter
5 600 25 flight-dist and get 3125 on the top of the stack.

Try it with different values, including head winds (negative values).


The Division Operators

The basic division in ColorForth (and traditional Forth as well) is /. This is an interger operator which discards the remainder.

22 5 /

This leaves 4 on the stack. There are two other division operators,
mod and /mod. By default they are in the macro wordset and only available during compilation. However they can be compiled to the Forth wordset.

mod ab-r mod ; returns the remainder
/mod ab-qr /mod ; returns quotient and remainder

Testing
/mod interactively: 22 4 /mod leaves 2 5 on the stack.

We can define a word quarters that takes a number of quarters and calculates the dollars and leftover quarters.

126 load q 0 quarters ones 0 ones
quarters 4 /mod . q .s . ones .s ;
go show blank text 22 quarters ;

Test
mod interactively: 22 4 mod leaves 2 on the stack.

Stack Manipulation

Swap

As the name suggests, this word takes the top too numbers on the stack and swaps them. Again this word is only in the macro wordlist. To use it interactively:

swap swap ;

Testing this interactively:
1 2 swap leaves 2 1 on the stack.

Dup

Dup takes the top number on the stack and duplicates it.
2 dup leaves 2 2 on the stack.

You can use dup to square a number.
4 dup * leaves 16 on the stack.

Over

Over is defined only in the macro wordlist. To use it interactively define:

over over ;

Over takes the second item on the stack and copies it to the top of the stack.

Testing it interactively,
1 2 over leaves 1 2 1 on the stack.

Thus the expression

a * (a + b)

can be written as:

over * +

Rot

Rot is in traditional Forth but is not by default defined in ColorForth. However defining it is simple. It requires a the words
push and pop. These words manipulate the second stack called the "return stack". Starting Forth doesn't cover this until another chapter. Basically push takes the top number from the parameter stack and moves it to the return stack. pop does the opposite. Using them rot is defined as:

rot push swap pop swap ;

Rot takes the 3rd item on the stack and rotates it to the top.

Testing it interactively:
1 2 3 rot leaves 2 3 1 on the stack.

Say if you wanted to evaluate the expression
ab - bc. First factor out the b's.

b*(a - c)

Now starting with
c b a on the stack we can use rot - *.

Drop

Drop, as its name suggests, drops the top item on the stack. Testing interactively:
1 2 drop leaves 1 on the stack.

Note: Look ma! No .s. Starting Forth mentions at this point that .s can be used to display the stack non destructively. But ColorForth shows the stack in interactive mode at the bottom left of the screen. Thus .s is not needed and not defined. Hence I can use .s is ColorForth for printing strings.

Stack Manipulation and Math Definitions (Quizzie 2-c)

1. Write a phrase which flips three items on the stack, leaving the middle number in the middle; that is:

a b c ==> c b a

Answer:
swap rot

2. Write a phrase that does what
over does, without using over.

Answer:
swap dup rot rot

3. Write a definition called
-rot, which rotates the top three stack items in the opposite direction from rot; that is:

a b c ==> c a b


Answer: -rot rot rot ;

Write definitions for the following:

4. (n+1) / n

Answer: 2c4 dup 1 + swap / ;

5. x(7x + 5)

Answer: 2c5 dup 7 * 5 + * ;

6.
9a2 - ba

a(9a - b)

Answer:
2c6 over 9 * swap - * ;

Playing Doubles

Many early Forths ran on 16 bit computers. As such "double" words, which allowed 32 bit math, were very important. ColorForth was initially written for a 32 bit computer and thus does not implement double words. However double stack manipulation words are not difficult to write.

2swap abcd-cdab push rot rot pop rot rot ;
2dup ab-abab over over ;
2over abcd-abcdab push push 2dup pop pop 2swap ;
2drop ab- drop drop ;

Problems -- Chapter 2

1. What's the difference between DUP DUP and 2DUP?

2. Write a phrase which will reverse the order of the top four items on the stack; that is,

( 1 2 3 4 -- 4 3 2 1 )

Answer:
reverse swap 2swap swap ;

3. Write a definition called 3DUP which will duplicate the top three numbers on the stack; for example,

Answer:
3dup abc-abcabc dup 2over rot ;

Write definitions for the following infix equations, given the stack effects shown:
4. a2 + ab + c ( c a b -- result )

a(a + b) + c

Answer:
prob4 over + * + ;

5. (a-b) / (a+b) ( a b -- result ) [answer]

Answer:
prob5 2dup - -rot + / ;

6. Write a set of words to compute prison sentences for hardened criminals such that the judge can enter:

CONVICTED-OF ARSON HOMICIDE TAX-EVASION ok
WILL-SERVE 35 years ok

or any series of crime beginning with the word CONVICTED-OF and ending with WILL-SERVE. Use these sentences

HOMICIDE 20 years
ARSON 10 years
BOOKMAKING 2 years
TAX-EVASION 5 years

Answer:

126 load years 0 years
convicted-of 0 ;
homicide 20 + ;
arson 10 + ;
bookmaking 2 + ;
tax-evasion 5 + ;
will-serve . years .s ;
ok show blank text convicted-of homicide arson tax-evasion will-serve ;

7. You're the inventory programmer at Maria's Egg Ranch. Define a word called EGG.CARTONS which expects on the stack the total number of eggs laid by the chickens today and prints out the number of cartons that can be filled with a dozen each, as well as the number of left-over eggs.

126 load cartons 0 cartons eggs 0 eggs
egg.cartons e-cr 12 /mod . cartons .s . eggs .s ;
ok show blank text 55 egg.cartons ;

Fall through factoring

The idea is that you can have multiple entry points to the same word. Here's an example. Say if you needed code for shifting right twice (4 *) and shifting right3 times (8 *). You could code that without factoring as:

4* 2* 2* ;
8* 2* 2* 2* ;

You could also code it using traditional factoring as:

4* 2* 2* ;
8* 2* 4* ;

Using fall through factoring that becomes:

8* 2*
4* 2* 2* ;

With fall through factoring 8* doesn't have the overhead of having to call 4*, but you still get the code density benefit of having 4* factored out. Fall through factoring can also be used to increase code readability without added overhead. Consider the example from Starting Forth chapter 2.

: YARDS 36 * ;
: FEET 12 * ;
: INCHES ;

: YARD YARDS ;
: FOOT FEET ;
: INCH INCHES ;

The only reason for yard, foot and inch is readability. Here is the same result using fall through factoring.

yard
yards 36 * ;
foot
feet 12 * ;
inch
inches ;

Note that there is now no overhead for yard, foot, or inch being their own separate words. Each word is a separate label for the same code address. In traditional Forth the words yard, foot and inch all have the overhead of making a call to their plural versions. Some traditional Forths have a word alias that can give a similar result as this last example, but that's not standard.

Labels:

Saturday, June 13, 2009

Typing ColorForth 4.1

Well I thought I had done my last post on "typing ColorForth" at version 4.0. That version does everything I want. However it doesn't quite do it the way I wanted it to. I took code I found on the web that types comment text to the screen, modified it to use yellow "immediate" mode for accessing variables, and then factored the word ".s" (stype in the original version) using inline macros. At least I thought I was using inline macros. The reason is that I wanted to make it clear what .s does. Again the code from the original stype.

stype 1 + dup @ dup 15 and select stype ;

Anyone who understands ColorForth's tail recursion feature can tell what
stype ; does. And select is explained in the shadow block comments. But what does 1 + dup @ dup 15 and do? Not obvious is it? 1 + dup @ dup increments then retrieves from the string address. 15 and gets the tag value. You can factor that out those two phrases as "nextw" and "tag", but that doesn't do anything for code density in this case because the words are only used once yet you still have the penalty (albeit small) of the extra call.

So I thought about trying to inline it using
cyan words. My thought was that I could define the words inside the macro wordlist and then use them as cyan words in the normal forth wordlist and everything would be inlined. I'd get the readability improvement from factoring without the overhead.

On retrospect I believe my understanding of cyan words was not right.
Cyan should be used inside macro wordlist and not the forth wordlist. I believe the result of using cyan for a word defined as a macro is that a call was inserted instead of inline code.

Now I'm not sure if this is right either. But there is a better way to get the results I want. One of the key features of ColorForth is fall through factoring. The idea is that you can have multiple entry points to the same word. Using fall through factoring I can rewrite
.s as this:

select jump rest done done done done done done done done norm cap caps done done done done ;
.s
nextw 1 + dup @ dup
tag f and select .s ;

I can now document nextw and tag in the corresponding shadow block. Again, the entire code.

+first 0 +rest 0
type space fffffff0 and unpack +first @ + emit
rest unpack if +rest @ + emit rest ; then drop drop ;
done drop drop pop drop ;
norm 0 +first ! 0 +rest ! type ;
cap 48 +first ! 0 +rest ! type ;
caps 48 +first ! 48 +rest ! type ;
select jump rest done done done done done done done done norm cap caps done done done done ;
.S
nextw 1 + dup @ dup
tag f and select .s ;

And the shadow block:

type w display one 32-bit word of text
rest w finish typing a word
done aw drop addr and word and return from stype
norm normal text
cap text starts with a capital
caps all caps
select wt handle word according to tag
.s a display contiguous white text
nextw a get next word and increment address
tag n get tag field from word. used by select.

The usage remains the same. "Hello World" in 2 lines of ColorForth.

126 load hello 0 Hello World, I speak ColorForth
ok show blank text hello .s ;

Note: This will probably be the last version of Typing ColorForth (at least I hope so). It does (almost*) everything I want it to do as is as efficient and readable as I can make it. To the critics (well meaning I'm sure) who don't understand why I was doing this, please look at the rest of the Primary ColorForth tutorial. Doing all of the examples in Starting Forth by looking up codes and "emitting" them would be an unecessary pain in the butt. And yes, I could just type in "Hello World" on a blank screen, but that would not be flexible enough for what I'm doing.

(* There is one other thing I'd like to do. The current version sticks in an extra space before typing anything. Sometimes this throws the formatting off. But I'm going to quibble about that...for now.)

Labels:

Tuesday, May 26, 2009

Typing ColorForth...versions 3.1 and 4.0

Hello again. Here's yet another update to my experiments with unpacking and displaying packed ColorForth text. I know have it to the place where doing the examples from Starting Forth should be easy. First a fix from version 3.0 based on the fact that ColorForth 2.0 enforces Chuck's admonition for yellow variable calls.

s 0 hello , i speak forth
type fffffff0 and unpack if emit type ; then drop drop ;
print dup @ f and -9 + drop if drop then ; dup @ type space 1 + print ;
greet show text s 1 + print ;

However I found a much better version. This version actually handles caps and mixed case. But it also requires the yellow variable call fix.


+first 0 +rest 0
type space fffffff0 and unpack +first @ + emit
rest unpack if +rest @ + emit rest ; then drop drop ;

done drop drop pop drop ;
norm 0 +first ! 0 +rest ! type ;
cap 48 +first ! 0 +rest ! type ;
caps 48 +first ! 48 +rest ! type ;

select jump rest done done done done done done done done norm cap caps done done done done ;
stype 1 + dup @ dup 15 and select stype ;

Here's a slightly different version that uses macros to (IMO) improve readability and includes an example of usage.

+first 0 +rest 0
type space fffffff0 and unpack +first @ + emit
rest unpack if +rest @ + emit rest ; then drop drop ;

done drop drop pop drop ;
norm 0 +first ! 0 +rest ! type ;
cap 48 +first ! 0 +rest ! type ;
caps 48 +first ! 48 +rest ! type ;

select jump rest done done done done done done done done norm cap caps done done done done ; macro
tag 15 and ;
nextw 1 + dup @ dup ; forth
stype nextw tag select stype ;


Here's what this does and why I think it's better. The words "nextw" and "tag" are put in the "macro" dictionary and then inlined because they as "cyan" words. Cyan words allow you to inline. Normally in ColorForth words are factored to save space. Here no space is saved because "nextw" and "tag" are only used once. But "stype" is more readable because it says exactly what it does. Here's the documentation "shadow block".

String Display
type w display one 32-bit word of text
rest w finish typing a word
done aw drop addr and word and return from stype
norm normal text
cap text starts with a capital
caps all caps
select wt handle word according to tag
nextw a get next word and increment address
stype a display contiguous white text


Using the above, here's the solution to problem 1-1 from Starting Forth.

162 load salute 0 Dear friend 0 Stephanie, thank 0 thanks for the gift 0 bookends.
thanks salute stype friend stype thank stype gift stype ;
ok show text blank white thanks keyboard ;

Note that "162 load" assumes that the "String Display" library is in block 162.

Labels:

A ColorForth 2.0 "gotcha".(Windows version anyway)

Hello all.

I pretty much use a Windows version of ColorForth all the time these days. (Usually ColorForth 2.0 from Chuck Moore in Windows mode, but some Roman Pavlyuk's version.). The reason I'm prefacing my remarks is because I have never used the native version of ColorForth 2.0 and I don't know if this "gotcha" only happens under Windows. There is an easily fixable bug that only happens in Roman's Windows version of ColorForth. It uses absolute addressing from blocks as opposed to indirect addressing. So the code:


0 block @


causes it to crash. The fix is easy. Assume the following code in block 162.


ofs 0
foo block ofs 162 block negate + -1 + + ;
block foo ;

Thankfully ColorForth 2.0 for Windows doesn't have that problem. It uses an offset to calculate blocks out of the box. However there is another "gotcha" when using variables. If you try to address a variable using a green word it doesn't work. You may simply not get the results you want or it might crash. Consider the following example taken from Starting Forth chapter 2.


date 0 month 0 year 0
!date date ! month ! year ! ;

5 26 2009 !date


That doesn't get the expected results. But there's a reason I call this a "gotcha" instead of a bug. Long ago Chuck documented that variables really should be used as yellow words (evaluated at compile time) instead of green words (evaluated at run time). When you think about it, this makes sense. A variable is really just an address and for the most part address are constants. So why wait until run time to find the address of a variable? Just find it at compile time and compile the address constant directly into the word. With that in mind the above code becomes:


date 0 month 0 year 0
!date date ! month ! year ! ;

5 26 2009 !date


So if you have old ColorForth code that uses variables as green words you need to change them to yellow words. Your code will be (slightly) faster and it will work on ColorForth 2.0 for Windows (again, I don't know if the native version even has this "gotcha").

Labels: ,

Sunday, January 25, 2009

SEAForth Matrix Multiplication Example update 1

It's been about 1 year since I posted my initial SEAForth matrix multiplication example. A lot has happened since then including changes to the VentureForth IDE/simulator as well as the commercial release of actual SEAForth chips. That's the good news. The bad news (for me at least) was that the changes were such that it broke my old code. That wouldn't have been so bad if there had been "this has been deprecated" section in the docs, but then I suppose that would have made the transition too easy. ;) Seriously though, some of the changes actually make the code much cleaner. Also I've made the code much more modular thanks to the judicious use of include files.

First I'll go through the changes that I've noticed in the new system and then an overview the new matrix multiplication code.

Change 1 : Node specification

In the original VentureForth you specified the node you were compiling to this way:


12 node !


Now the compiling node is specified this way:


12 {node
...
.../ some VentureForth code
...
node}


It's a lot clearer where compiling to a node begins and ends. This can also lead to a clean modular code pattern that will be discussed later.

Change 2 : Stream Specification

Section 2.3.1 of the VentureForth manual explains streams. Basically it's a way for code and data to be initially distributed to the correct nodes. If you are just learning the SEAForth system as I am you probably aren't ready to create your own custom stream nodepath. But you do need to make sure all of the nodes are initialized. The following code will do that.

19stream

reset

Change 3 : Literal Specification

The original VentureForth compiler would compile literals automatically. Now a "#" is required after every literal. Note that port selection constants are really literals and require a "#" after them also. So:


'r--- b!
7 for @p+ !b unext


becomes:


'r--- # b!
7 # for @p+ !b unext

Toward More Modular Programs

I saw the following code pattern in the "blinktest" sample project:


\ Main program.vf
.
.
19 {node 0 org here =p include module.vf node}
.
.


So I've followed a similar pattern for my code, breaking what was a single monolithic module into 6 smaller (and in my opinion easier to read) modules.

Main module : matrixmult.vf

( $Id: matrixmult.vf,v 1.0 2006-11-2 $ )
\ Matrix multiplication example
\ Parrallel multiplication of a 2 row matrix by a 2 column matrix
\ 1) Data initially is in node 12
\ 2) Row1 is passed to node 18
\ 3) Row2 is passed to node 13
\ 4) Column1 is passed to nodes 18 and 13 simultaneously
\ 5) Multiplications R1*C1 and R2*C1 done simultaneously
\ 6) Column2 is passed to nodes 18 and 13 simultaneously
\ 7) Multiplications R1*C2 and R2*C2 done simultaneously
\ 8) Results R1*C1, R1*C2, R2*C1, R2*C2 are returned to node 12

v.VF +include" c7Dr03/romconfig.f"

12 {node include node12.vf node}
13 {node include node13.vf node}
18 {node include node18.vf node}

19stream

reset

cr


Node 12 code : node12.vf
Note: This is the taskmaster node. It passes the data to nodes 13 and 18 to be processed and the collates the results.



\ ******* node 12 ***************************************
\ Pass row1 to node 13, row2 to node 18
\ and columns 1 and 2 to nodes 13 and 18 simultaneously

0 org here =p

\ Stream R1 to node 13
'r--- # b! \ point reg b to right node. (I/O node 13)
7 # for @p+ !b unext
1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 ,

\ Stream R2 to node 18
| '-d-- # b! . . \ point reg b to down node. (I/O node 18)
7 # for @p+ !b . unext
9 , 10 , 11 , 12 , 13 , 14 , 15 , 16 ,

\ Stream C1 to nodes 13 and 18 simultaneously
'rd-- # b! . . \ point reg b to right and down nodes. (I/O nodes 13 and 18)
0 # !b . . \ dummy write for synch purposes
15 # for @p+ !b . unext
17 , 18 , 19 , 20 , 21 , 22 , 23 , 24 ,
25 , 26 , 27 , 28 , 29 , 30 , 31 , 32 ,

59 # a! . . \ point A register to address 59
'r--- # b! . . \ point reg b to right node. (I/O node 13)
@b !a+ . . \ Get result R1*C1
@b !a+ . . \ Get result R1*C2
'-d-- # b! . . \ point reg b to down node. (I/O node 18)
@b !a+ . . \ Get result R2*c1
@b !a+ . . \ Get result R2*c2


Nodes 13 and 18 basically do the same thing. Get a row of data from node 12, then use that row to calculate a dot product of two columns of data.

Node 13 code : node13.vf

\ ******* node 13 *********************************************
\ Receive row1 then multiply it by columns 1 and 2
\ and return results.

0 org

include 8bitmult.vf

here =p
'r--- # b! . . \ point reg b to right node

include vectormult.vf


Node 18 code : node18.vf

\ ******* node 18 *********************************************
\ Receive row2 then multiply it by columns 1 and 2
\ and return results.

0 org

include 8bitmult.vf

here =p
'-d-- # b! . . \ point reg b to down node

include vectormult.vf


8 bit multiply : 8bitmult.vf

: 8*8 ( n1 n2 -- n1*n2 ) \ 16 bit output
push 2* 2* . \ left shift n1 8 times
2* 2* 2* .
2* 2* 2* .
pop +* +* +*
+* +* +* +*
+* push drop .
pop ;


Vector multiply : vectormult.vf

\ ******* vector multiply *******************************
\ Receive a row then multiply it by columns 1 and 2
\ and return results.

$20 # dup a! push \ point reg a to buffer - leave adr on return stack

7 # for @b !a+ unext

@b drop \ dummy read for synchronization purposes

pop dup a! push \ reset a to start of buffer
dup xor \ initilize TOS to 0
7 # for
@b @a+ 8*8
+ next

dup dup xor \ preserve result from vector mult - initialize TOS to 0

pop a!
7 # for
@b @a+ 8*8
+ next

!b !b \ Send results back to right node

Labels: