It was thus said that the Great woodelf once stated:
Sean Conner wrote:
It was thus said that the Great Tony Duell once
stated:
As regards having spare time, it's amazing how
complicated some of my
repairs can be...But replacing a capcitor isn't one of them
Maybe for you it's easy. Possibly not for me. And possibly for the
person requesting a new Apple /// power supply (I've lost track of who that
was). To put it into perspective you might understand, can you implement a
Forth (or Forth-like) system in two hours? I did that the other night
because I was rather bored and it was more interesting than I what I was
supposed to be doing [1]. If you can't, why not? It's not that
complicated.
-spc (then again, I'm a software guy)
[1] Hanging out online with some friends. Well, I was doing that as I
was implementing the Forth-like system [2].
[2] A rather minimal system, and different enough [3] that it probably
couldn't be classified as Forth. Forth-like, yes, but not Forth.
[3] Managed to get execellent code density (it's a TIL [4]), and I
managed to do away with !, @ and most of the stack noise words (NIP,
TUCK, OVER, etc).
[4] Threaded Interpreted Langauge
So just what are the primitives and just what is the CPU?
You don't hear about [4] spoken about any more.
I like the control structure of Forth but not the fact that it
hard to address conventional data structures.
One version is in C (reference implementation) and I coded up most of it
in (severely untested) 6809 Assembly. The "tokens" are 16 bit values, and
are broken up as:
$0000 - $00FF - push literal values between -128 .. 127 onto the
data stack
$0100 - $01FF - ALU primitives. Bits 4..7 define the operation:
0 +
1 -
2 *
3 /
4 mod
5 and
6 or
7 xor
8 <
9 <=
A =
B <>
C >=
D >
E - (operands reversed)
F / (operands reversed)
Bits 2..3 define the index into the data
stack for the second operand
00 - ustack[1]
01 - ustack[2]
10 - ustack[3]
11 - ustack[4]
Bits 0..1 define the index into the data
statck for the first operand and
destination
00 - ustack[0]
01 - ustack[1]
10 - ustack[2]
11 - ustack[3]
If the dest index is 0, and the src index is 0 (top
two elements on the data stack) then the top of the
stack is popped and the result goes into the new top
of stack, otherwise, nothing is popped from the
stack. So, $0100 would act as "+" does normally in
Forth, but something like $0101 is
ustack[0] = ustack[0] + ustack[2]
or in Forth
ROT OVER OVER + >R -ROT DROP R>
(I might have gotten ROT and -ROT confused)
This doesn't get rid of all the stack noise words,
but I suspect it would get rid of a lot of them.
$0200 - $02FF - rest of the primitives in the system, typical
Forth type words.
$0300 - $7FFF - secondary definitions. Value shifted left one bit
is the address of the "word" to interpret.
$8000 - $BFFF - Mask off two high bits, shift left, address of
the value to push onto the data stack. This means
that all VARIABLES have to live within the first 32K
of memory (I was writing this with an 8-bit CPU in
mind). This is the same as:
X @
$C000 - $FFFF - Mask off two high bits, shift left, address to
store top of stack. This is:
X !
I also took a different approach to storing the code. Instead of the
traditional headers mixed with code, I split the headers off into their own
separate part of memory. So something like:
VARIABLE x
: foo 3 * 5 + ;
: bar x foo -> x ;
Would end up in memory like (addresses made up for example purposes only):
2000 variable X
3000 0003 ; 3
3002 0120 ; *
3004 0005 ; 5
3006 0100 ; +
3007 0200 ; ;
3008 9000 ; x
300A 1800 ; foo (address 3000)
300C D000 ; -> x
300E 0200 ; ;
5000 [1][x][_][_]
5004 [previous word]
5006 [1000]
5008 [3][f][o][o]
500C [5000]
500E [1800]
5010 [3][b][a][r]
5014 [500C]
5016 [1804]
Over the long term, not only would this format save space, but it would be
trivial to chop off the headers to "freeze" the application from futher
change, and leave more data space for processing.
-spc (I was struck with the idea while reading an old Byte book on
implementing TILs)