Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

VARIABLE & CONSTANT

5 views
Skip to first unread message

MarkWills

unread,
Dec 30, 2009, 7:27:32 AM12/30/09
to
Today I re-factored VARIABLE and CONSTANT to work 'properly'.

Previously, when you entered:

10 CONSTANT 10

CONSTANT would create a word (using CREATE) that looked like this:

<dictionary entry> DOCOL LIT 10 EXIT

VARIABLE would create a word like this (again, using CREATE)

<dictionary entry> DOCOL LIT XXXX EXIT

XXXX is the address of the word immediately after EXIT. As you can
see, it works, but is hacky and rather un-sophisticated.

I reviewed earlier posts from Elizabeth, and also checked some notes
that I had made, and (re) learned that VARIABLE and CONSTANT are
'special' words. They are special words with 'instance behaviours' (if
I have got my wording correct ;-)

My previous versions of these words had no 'instance behaviour' - they
are just statically compiled stand-alone words: LIT 10 etc for 10
CONSTANT

I have now done some code-surgery, and believe I am much closer to a
proper implementation. I have:

* Taken what was CREATE and called it HEADER (it just makes a
dictionary entry, updates LATEST and HERE and that's it)

* Re-written CREATE to use HEADER and then append what I am calling an
instance behaviour. I note that the default instance behaviour of
children of CREATE is to push the PFA of the child word AT RUN TIME
(child run-time, that is). So now, my CREATE looks like this:

; CREATE ( -- ) new word: ( -- pfa )
; Creates a new entry in the dictionary.
; The default run-time characteristic of the new word is such that
when
; executed, the word leaves its PFA on the stack.
creath data headrh,>6
text 'CREATE'
create data docol
data word,header ; create header
data compile,crtib ; compile instance behaviour
data exit

; this is the 'run time' code (the instance behaviour) for all words
defined
; using CREATE. The default behaviour of all words created with CREATE
is to
; push it's PFA to the stack.
crtib data $+2
inct stack ; make space on the stack
mov r6,*stack ; current pc address to stack (CFA)
inct *stack ; add 2, to point to PFA
b *next

So now, when you do CREATE FRED you get this in memory:

<FRED> CRTIB XXXX

Just a call to CRTIB (create-instance-behaviour) that returns the
address of XXXX. I chose to implement the CRTIB in assembler for added
zest.

It is interesting to note that the default behaviour of children of
CREATE is suitable for use with VARIABLES, since it pushes an address
not a value. So, I have re-written VARIABLE to use CREATE:

VARIABLE
; creates a word that holds a variable. The address of the variable is
pushed
; at runtime. Immediate. Only use from the command line.
varh data consth,>8008
text 'VARIABLE'
var data docol,create,lit,2,allot,exit
; VARIABLE just uses the instance behaviour of CREATE since it is
; suitable for a variable.
; HERE is advanced by 2 (2 ALLOT) so that the next word to be
; defined isn't compiled into the variables space.

I'm very happy with this new approach. I believe this is the approach
that Elizabeth and others were advocating last summer. I abandoned it,
writing it off as too hard. Yet today I implemented it in 10 minutes.

I believe the approach is the similar for CREATE..DOES>

When a word is created with CREATE, DOES just has to patch the pointer
that points to the child-of-creates instance behaviour.

So, CREATE FRED

<FRED> INSTANCE_POINTER PFA

DOES has to patch that INSTANCE_POINTER with a pointer to the code
that it has compiled (the code that follows DOES)

That's how I think it works. Perhaps I'll post on a new thread
regarding this. Just wanted to share my 'breakthrough'.

Regards

Mark

MarkWills

unread,
Dec 30, 2009, 7:29:10 AM12/30/09
to
Woops, typo in my post above.

10 CONSTANT 10

should of course read

10 CONSTANT TEN

Mark

MarkWills

unread,
Dec 30, 2009, 7:39:07 AM12/30/09
to
I should add that I have changed CONSTANT to use a similar approach.

My code for CONSTANT is

;[ CONSTANT ( val -- )
; creates a constant that returns the value val. The name of the
constant is
; taken from the TIB. Immediate. Only use from the command line.
consth data doesh,>8008
text 'CONSTANT'
const data docol
data word,header ; create new dictionary entry
data compile,docon ; compile constant instance behaviour
data comma ; compile value from stack
data exit

; instance behaviour for all words created with CONSTANT
docon data $+2
mov r6,r0 ; get pc (still pointing to CFA)
inct r0 ; point to PFA
inct stack ; advance sack pointer
mov *r0,*stack ; place CONTENTS of PFA on stack
b *next
;]

So now variables and constants are much faster because they call
assembly coded instance behaviours.

After extensive testing it works beautifully and is much faster.

Mark

BF

unread,
Dec 30, 2009, 11:15:40 AM12/30/09
to

I think you are really making good progress on your Forth system
Mark. I have not had time to touch Forth for a while. :-(

Since you mentioned "much faster" I wonder if you have ever considered
caching the top of the data stack in a register. You have a good
number of registers on that old TMS9900. That can cause a good speed
increase on some processors, with only a little extra complication...
(easy for me to say) :-)

BF

MarkWills

unread,
Dec 30, 2009, 3:12:58 PM12/30/09
to

Hi, thanks for the encouraging words!

Actually, it's a valid comment, but I think in the case of the 9900
with it's unique (as far as I know) architecture, it wouldn't make
much difference.

As you are probably aware, the 9900 and it's brethren (9995 etc) has
what is called a 'memory-to-memory' architecture. The 9900 family only
has three "real" on chip registers:

PC (program counter)
ST (status register)
WP (workspace pointer)

The 'registers' (R1, R2 etc) actually live in external RAM. This
technique was innovative at the time, but never seemed to catch on
with other manufacturers. One nice thing is that is easy to do context
switches. Just change the workspace pointer, and instantly all your
registers take on new values. Consequently, it was felt there was no
need for a stack on the 9900, so it is stackless. You can branch to a
subroutine and the subroutine can have it's own register set, and upon
return to the calling routine, the registers are instantly restored,
purely by changing the Workspace Pointer.

Anyway, getting back to your point: If I 'cache' the TOS in a
register, it is *actually* still just sitting in RAM! Fetching it
still generates external memory access cycles. The way my code is
written, R4 tracks the top of stack (TOS). So, to get TOS say, in R0:

MOV *R4,R0 ; get TOS in R0

Thats a two byte instruction (single fetch on a 16 bit bus) and also a
fetch to get the contents of R4 from ram, and a write to write it back
to R0 (which is, of course, in RAM!)

If TOS were 'cached' in a register, lets say, R8:

MOV R8,R0 ; get TOS in R0

It's still a two byte instruction, with exactly the same bus cycle
signature!

In fact, caching the TOS might even be a little slower, (but not much)
since I would have to add code to move the top-most stack item into a
register:

MOV *R4,R8 ; move tos into r8 for easy access

It's actually pointless due to the memory-memory-architecture. As far
as I can see, there seems to be no benefit.

It's a shame the 9900 wasn't more popular. It's got a lovely simple
instruction set (only 69 instructions), and (AFAIK) no bugs. Solid
chip. The 9995 is much easier to build hardware with, however, since
it only requires a single phase clock, and has an 8 bit data bus (so
makes wire-wrap projects easier!). Despite having an 8 bit bus,
requiring two memory cycles each for read and write, the 9995 is
significantly faster than the 9900.

My next hardware project will probably be based on one of the Hitachi
or perhaps Motorola micro-controllers. (Shame, I have some 68Ks lying
around, but they're obsolete, not sure if there is anything on the
market these days based around the original 68K instruction set, which
was very nice).

Regards

Mark

Coos Haak

unread,
Dec 30, 2009, 7:42:39 PM12/30/09
to
Op Wed, 30 Dec 2009 12:12:58 -0800 (PST) schreef MarkWills:

I don't know the instruction set, but wat about the word +
(assuming stack, R4, grows down, TOS in R8)
Non-caching variant Caching in R8
MOV *R4,R8 ADD *R4,R8
INCT R4 INCT R4
ADD *R4,R8
MOV R8,*R4

assuming add from memory to register, otherwise:
MOV *R4,R8 MOV *R4,R9
INCT R4 INCT R4
MOV *R4,R9 ADD R9,R8
ADD R9,R8
MOV R8,*R4

DUP:
MOV *R4,R8 DECT R4
DECT R4 MOV R8,*R4
MOV R8,*R4

DROP is longer:
INCT R4 MOV *R4,R8
INCT R4
--
Coos

CHForth, 16 bit DOS applications
http://home.hccnet.nl/j.j.haak/forth.html

Stephen Pelc

unread,
Dec 30, 2009, 8:52:48 PM12/30/09
to
On Wed, 30 Dec 2009 12:12:58 -0800 (PST), MarkWills
<markrob...@yahoo.co.uk> wrote:

>It's a shame the 9900 wasn't more popular. It's got a lovely simple
>instruction set (only 69 instructions), and (AFAIK) no bugs. Solid
>chip. The 9995 is much easier to build hardware with, however, since
>it only requires a single phase clock, and has an 8 bit data bus (so
>makes wire-wrap projects easier!). Despite having an 8 bit bus,
>requiring two memory cycles each for read and write, the 9995 is
>significantly faster than the 9900.

And we still have a few 99000s (105?) in our parts bins.

The downside was that all architectures that relied on a multi-phase
clock got creamed by the 8086/88. The original 9900 needed a 48MHz
crystal to get a 3MHz CPU clock - ugh!

>My next hardware project will probably be based on one of the Hitachi
>or perhaps Motorola micro-controllers. (Shame, I have some 68Ks lying
>around, but they're obsolete, not sure if there is anything on the
>market these days based around the original 68K instruction set, which
>was very nice).

If you go straight to ARM32 (very nice instruction set), you'll only
be one or two generations behind the leading edge.

Stephen


--
Stephen Pelc, steph...@mpeforth.com
MicroProcessor Engineering Ltd - More Real, Less Time
133 Hill Lane, Southampton SO15 5AF, England
tel: +44 (0)23 8063 1441, fax: +44 (0)23 8033 9691
web: http://www.mpeforth.com - free VFX Forth downloads

Bruce McFarling

unread,
Dec 30, 2009, 11:07:44 PM12/30/09
to
On Dec 30, 7:27 am, MarkWills <markrobertwi...@yahoo.co.uk> wrote:
> It is interesting to note that the default behaviour of children of
> CREATE is suitable for use with VARIABLES, since it pushes an address
> not a value.

Yes, that is the classic fig-Forth and Forth-79 and earlier. Assuming
byte addressing and 16-bit cells,

: VARIABLE ( "name" -- ) CREATE 2 ALLOT ;

With DOES> one can:

: CONSTANT ( "name" x -- ) CREATE HERE 2 ALLOT ! DOES> @ ;

but a {DOCONSTANT} is more common since its normally much faster.

> So, I have re-written VARIABLE to use CREATE:

> VARIABLE
> ; creates a word that holds a variable. The address of the variable is
> pushed at runtime. Immediate. Only use from the command line.

VARIABLE should NOT be immediate - first, it if was only used at the
command line, immediacy does not matter, and if compiling it SHOULD be
compiled rather than execute at that point in time.

Elizabeth D Rather

unread,
Dec 30, 2009, 11:49:42 PM12/30/09
to
Bruce McFarling wrote:
> On Dec 30, 7:27 am, MarkWills <markrobertwi...@yahoo.co.uk> wrote:
>> It is interesting to note that the default behaviour of children of
>> CREATE is suitable for use with VARIABLES, since it pushes an address
>> not a value.
>
> Yes, that is the classic fig-Forth and Forth-79 and earlier. Assuming
> byte addressing and 16-bit cells,
>
> : VARIABLE ( "name" -- ) CREATE 2 ALLOT ;
>
> With DOES> one can:
>
> : CONSTANT ( "name" x -- ) CREATE HERE 2 ALLOT ! DOES> @ ;
>
> but a {DOCONSTANT} is more common since its normally much faster.

Correct. Replace the '2' with 1 CELLS and you're independent of cell size.

>> So, I have re-written VARIABLE to use CREATE:
>
>> VARIABLE
>> ; creates a word that holds a variable. The address of the variable is
>> pushed at runtime. Immediate. Only use from the command line.
>
> VARIABLE should NOT be immediate - first, it if was only used at the
> command line, immediacy does not matter, and if compiling it SHOULD be
> compiled rather than execute at that point in time.

Absolutely right. Defining words in general must not be IMMEDIATE.

Cheers,
Elizabeth

--
==================================================
Elizabeth D. Rather (US & Canada) 800-55-FORTH
FORTH Inc. +1 310.999.6784
5959 West Century Blvd. Suite 700
Los Angeles, CA 90045
http://www.forth.com

"Forth-based products and Services for real-time
applications since 1973."
==================================================

Albert van der Horst

unread,
Dec 31, 2009, 12:02:28 PM12/31/09
to
In article <b51ac214-02ac-4c8d...@m25g2000yqc.googlegroups.com>,
MarkWills <markrob...@yahoo.co.uk> wrote:
>On Dec 30, 4:15=A0pm, BF <brian....@rogers.com> wrote:
<SNIP>

>
>Hi, thanks for the encouraging words!
>
>Actually, it's a valid comment, but I think in the case of the 9900
>with it's unique (as far as I know) architecture, it wouldn't make
>much difference.
>
>As you are probably aware, the 9900 and it's brethren (9995 etc) has
>what is called a 'memory-to-memory' architecture. The 9900 family only
>has three "real" on chip registers:
>
>PC (program counter)
>ST (status register)
>WP (workspace pointer)
>
>The 'registers' (R1, R2 etc) actually live in external RAM. This
>technique was innovative at the time, but never seemed to catch on
>with other manufacturers.

It did, with the transputer.

>
>Regards
>
>Mark

Groetjes Albert

--
--
Albert van der Horst, UTRECHT,THE NETHERLANDS
Economic growth -- being exponential -- ultimately falters.
albert@spe&ar&c.xs4all.nl &=n http://home.hccnet.nl/a.w.m.van.der.horst

MarkWills

unread,
Dec 31, 2009, 12:41:07 PM12/31/09
to
On Dec 31, 5:02 pm, Albert van der Horst <alb...@spenarnc.xs4all.nl>
wrote:
> In article <b51ac214-02ac-4c8d-99af-ec4fa19db...@m25g2000yqc.googlegroups.com>,

Hi Albert

That's true, although the Transputer of course died out as the speed
of 'conventional' processors improved, eventually removing the speed
advantage of the parallel transputer. Of course, the new parallel
programming paradigm was also a hurdle to overcome for programmers
used to the traditional 'single threaded' programming techniques.
Occam was very nice. I did some programming on a T800 IIRC in approx
1993. It was nice.

Now, we seem to have come full circle, with Intel et al offering
multiple cores on a single chip, (Although TI were doing that with the
C80 in the 90's) and of course Chuck Moore and co with the parallel
Forth chips.

It's a shame the Transputer is no longer with us. It was a great
design, and British too ;-)

Regards

Mark

0 new messages