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

Can't understand this

42 views
Skip to first unread message

Zbiggy

unread,
May 18, 2011, 8:13:38 PM5/18/11
to
I'm trying to analyse an example program (creation of double-linked list)
from the book "Forth Applications" (by S.D. Roberts, chapter 4).
Unfortunately, the description is rather spartan, and IMHO unclear.

There is a listing for simple double-linked list management system, which
at the very beginning has strange definition:

: PAD PAD RECLEN + ;

( RECLEN is a "record size", defined earlier as "32 CONSTANT RECLEN")

I can't understand, what a rationale can be behind it. Moving permanently
the address, at which PAD can be "seen"? Why? What for?

Maybe someone more experienced could tell me, why is that? There are then
several operations "relative to pad address" - but what the difference can
it make, whether we will use PAD starting at its "original" address, or we
move all of our operation on PAD contents "32 bytes up"? It's still PAD area,
anyway... looks like first 32 bytes should be "left untouched" by the
application, but why?


Below is complete listing, which I typed in from the book - partially
ANSIzed already (it was most probably prepared for some sort of FIG-dialect,
and for 8-bit machine).

\ =================================================
\ (incomplete) ANSization:
: open-scr ; \ needs completion
: close-scr ; \ needs completion
: 2+ 2 + ;
: 2- 2 - ;
: cls page ;
: not 0= ;
\ ascii replaced by [char]
\ name changes: compare -> (comp)
\ search -> search_list

\ redefined pad address <- _WHY_?

1 constant first
1024 constant b/buf
32 constant reclen

variable pjl
variable pjr
variable pj

: pad pad reclen + ;
: @pa ( -- n ) first block 2+ @ ;
: !pa ( -- n ) first block 2+ ! update ;
: #index ( n -- a ) reclen b/buf */mod first + block + ;
: @pl ( n -- n' ) #index 26 + @ ;
: @pr ( n -- n' ) #index 28 + @ ;
: @n ( -- n ) #index @ ;
: string ( -- n ) create 1 max 255 min dup c, 0 c, allot does> 1+ ;
: s! ( -- ala ) drop dup 2- c@ rot min dup >r over r> swap 1- c! cmove ;

40 string a$ s" ltest" a$ count s!

: f_open begin cls ." File: (" a$ count type ." ) "
pad 1+ 40 expect span @ dup 0= not
if pad c! pad count a$ count s! else drop then
a$ open-scr not until ;

: f_close flush close-scr ;
: !npl ( -- n ) pad 26 + ! ;
: !npr ( -- n ) pad 28 + ! ;
: !pl ( -- n n' ) #index 26 + ! update ;
: !pr ( -- n n' ) #index 28 + ! update ;
: !n ( -- n ) 0 #index ! update ;
: +n 1 0 #index +! update ;
: @nr ( n -- n' ) #index 30 + @ ;
: !nr @n pad 30 + ! ;

: (comp) ( a a' n -- f ) ( f -- 0+ )
over + swap do count i c@ - ?dup
if swap 0= leave then loop if 0 then ;
: comp ( n -- f ) #index pad 25 (comp) ;

: !mem !nr pad @n #index reclen cmove update +n ;
: empty-list 0 !pa 1 !n ;
: ?f# ( -- f ) 0 @pa 0= if !mem 1+ dup !pa 0 1 !pl 0 1 !pr then ;
: @ptrs ( f -- f ) dup 2dup @pl pjl ! @pr pjr ! pj ! ;
: @mem ( -- f ) #index pad reclen cmove ;

: (>lst) @pa
begin @ptrs comp dup 0=
if ." Already in list" drop 0 1
else 0< if pjr @ 0= if ( new element is largest )
0 !npr @n pj @ !pr pj @ !npl 1 1
else ( continue search )
pjr @ 0 then
else pjl @ 0= if ( new element is smallest )
0 !npl @pa !npr @n dup @pa !pl !pa 1 1
else ( insert ) pj @ @pl !npl
pjl @ @pr !npr @n pjl @ !pr @n
pj @ !pl 1 1
then
then
then until if !mem then ;

: padc pad reclen 32 fill ;
: >pad padc 13 word count pad swap cmove ;
: new >pad ?f# not if (>lst) then ;
: .rec ( -- n ) cr @mem pad 19 type
cr pad 26 + @ . pad 28 + @ . pad 30 + @ . cr ;
: print @pa begin dup 0= not
if dup .rec @pr dup then 0= until drop ;
: .mes2 ." end of list" cr ;

: (y/n) ." ( /n) " key 78 = if 0 else 1 then ;
: <-> ( -- n ) ." p n x "
begin key dup [char] p =
if drop dup @pl 0= not
if @pl else .mes2 then 1
else dup [char] n =
if drop dup @pr 0= not
if @pr else .mes2 then 1
else [char] x =
if drop 0 else 1 then
then
then
while dup .rec repeat ;

: .mes1 ." not in list " ;
: (search) ( -- n ) @pa
begin dup comp 0= if dup .rec 1
else dup @pr 0= if .mes1 1
else @pr 0 then
then
until ;
: search_list >pad (search) ." skim through" (y/n)
if <-> else drop then ;

: (delete) ( -- n ) dup dup @pr 0=
if @pl 0 swap !pr
else dup @pl 0=
if @pr dup 0 swap !pl !pa
else dup @pl over @pr !pl
dup @pr swap @pl !pr
then
then dup 0 swap !pr 0 swap !pl ;

: delete >pad (search) (y/n) if (delete) else drop then ;

\ =================================================

Part of description:

To enter a new keyword enter: NEW <KEYWORD>
To search: SEARCH <KEYWORD>
To erase: DELETE <KEYWORD>

The word #INDEX calculates the address of the N-th record. The length of an
element of this list is 32 bytes: 26 of these bytes are for keyword, other
six bytes are three pointers: PL & PR used for chaining, and pointer N,
which points at the memory location where the information belonging to the
keyword can be found.

The first 32 bytes in screen 1 contain the number N. That is the number for
teh next entry. The two bytes following that contain the pointer PA, which
points at the smallest element of the list. The list is empty, if N equals 1
and PA equals 0.

For comparison of the keywords command COMPARE is used. The word to be
compared to is in PAD. The keyword is taken from the list. The word !MEM
stores an element at the end of the list after it has been chained.
EMPTY-LIST creates an empty list.


---------
Well, that's all I could type today; trying to understand, how it's working,
I'm somewhat stuck at the very beginning with that PAD address increment. Why
is that? Maybe I'm missing something, but I can't see a reason.
--
It's easy: all you need is Forth!

Elizabeth D Rather

unread,
May 18, 2011, 10:09:32 PM5/18/11
to
On 5/18/11 2:13 PM, Zbiggy wrote:
> I'm trying to analyse an example program (creation of double-linked list)
> from the book "Forth Applications" (by S.D. Roberts, chapter 4).
> Unfortunately, the description is rather spartan, and IMHO unclear.
>
> There is a listing for simple double-linked list management system, which
> at the very beginning has strange definition:
>
> : PAD PAD RECLEN + ;
>
> ( RECLEN is a "record size", defined earlier as "32 CONSTANT RECLEN")
>
> I can't understand, what a rationale can be behind it. Moving permanently
> the address, at which PAD can be "seen"? Why? What for?

Looking briefly at the code, I don't see any reason for adding an offset
to the original PAD. Maybe his system uses the first part of PAD for
something.

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."
==================================================

BruceMcF

unread,
May 18, 2011, 11:23:21 PM5/18/11
to
On May 18, 8:13 pm, Zbiggy <zbigniew2011REM...@gmail.REMOVE.com>
wrote:

> Well, that's all I could type today; trying to understand,
> how it's working, I'm somewhat stuck at the very beginning
> with that PAD address increment. Why is that? Maybe I'm
> missing something, but I can't see a reason.

It preserve this from being trashed by a later use of PAD in a classic
system with PAD to the end of the space available for the dictionary,
that definition makes sense, but only at the end of the wordset.

I'd either see if:

CREATE recpad reclen CHARS ALLOT

... and replace recpad with all uses of PAD works. Or just delete the
redefinition of PAD and document it as using the first reclen CHAR of
PAD.

Zbiggy

unread,
May 19, 2011, 5:41:25 AM5/19/11
to
In comp.lang.forth, BruceMcF wrote:

> It preserve this from being trashed by a later use of PAD in a classic
> system with PAD to the end of the space available for the dictionary,
> that definition makes sense, but only at the end of the wordset.

But how this can occur? In "Starting Forth" I've found there:

#v+
Since the pad's beginning address is defined relative to the last dictionary
entry, it moves every time you add a new definition or execute FORGET or
MARKER. This arrangement proves safe, however, because the pad is never used
when any of these events are occurring.
#v-

Besides: how accessing PAD only 32 bytes "higher" could preserve it in any
way, if in the system something will go so terribly wrong, that the PAD can
be trashed?

And even worse: let's assume, the author was using memory-constrained system
(Spectrum 16k, or - say - even Commodore VC-20, with 3,5 KB RAM ;). If he
moved the "access window" for the PAD, he could trash parameter stack this
way, which is supposed to be some distance "above". But that distance has
been "shortened" for that 32 bytes.

So... what's the point?

> I'd either see if:
>
> CREATE recpad reclen CHARS ALLOT
>
> ... and replace recpad with all uses of PAD works. Or just delete the
> redefinition of PAD and document it as using the first reclen CHAR of
> PAD.

Then i think, maybe it's forgotten definition, which should have been removed.

Zbiggy

unread,
May 19, 2011, 5:46:57 AM5/19/11
to
In comp.lang.forth, Elizabeth D Rather wrote:

> Looking briefly at the code, I don't see any reason for adding an offset
> to the original PAD. Maybe his system uses the first part of PAD for
> something.

And did you use - or see - such "PAD access window translation" anytime
at all? Still trying to figure out, what the author was going to... :]

BruceMcF

unread,
May 19, 2011, 10:14:41 AM5/19/11
to
On May 19, 5:41 am, Zbiggy <zbigniew2011REM...@gmail.REMOVE.com>
wrote:

> In comp.lang.forth, BruceMcF wrote:
> > It preserve this from being trashed by a later use of PAD in
> > a classic system with PAD to the end of the space available
> > for the dictionary, that definition makes sense, but only at
> > the end of the wordset.

> But how this can occur? In "Starting Forth" I've found there:

> #v+
> Since the pad's beginning address is defined relative
> to the last dictionary entry, it moves every time you
> add a new definition or execute FORGET or MARKER. This
> arrangement proves safe, however, because the pad is
> never used when any of these events are occurring.
> #v-

In between defining or FORGET/MARKER, you might want to use these
words defined here, and also use some other words that use the pad for
some other use. In that case, since this uses 32 chars of pad
(assuming 1chars=1), redefining *PAD* to add 32 to the original PAD
would allow the later set of definitions and this definition to share
the PAD.

Or else, if there was a previous use of PAD, which only used a fixed
amount of it, then adding that fixed amount would allow this use and
that use to share the pad.

But redefining pad to add the amount of pad that *this* wordset uses,
*up front* ~ that suggests that author was attempting one of those two
but hadn't thought it through. Since PAD would normally provide 64
chars of space, unnecessarily adding 32 not cause any *problems*, so
the mistake would not show up in testing unless the effort to share
pad was itself being directly tested.

> Besides: how accessing PAD only 32 bytes "higher" could
> preserve it in any way, if in the system something will go
> so terribly wrong, that the PAD can be trashed?

This is not about pad being "trashed". In this wordset, there are
multiple words that use pad to communicate persistent information.
Some other set of words might use pad the same way. If you tried to
used both sets of words mixed together, they would interfere with each
other. Defining pad *after* the first in order to preserve the space
used by one set of words from interference with a set of words defined
later would work.

> And even worse: let's assume, the author was using memory-constrained system (Spectrum 16k, or - say - even Commodore VC-20, with 3,5 KB RAM ;). If he moved the "access window" for the PAD, he could trash parameter stack this way, which is supposed to be some distance "above". But that distance has been "shortened" for that 32 bytes.

In general, yes ~ you cannot use this gimmick to indefinitely stretch
out the use of PAD in small address space Forth systems. OTOH, small
address space Forth systems only have room for a limited number of
wordsets at one time, so they would not have many distinct wordsets
trying to use this gimmick.

> > I'd either see if:

> > CREATE recpad reclen CHARS ALLOT

> > ... and replace recpad with all uses of PAD works. Or just delete the
> > redefinition of PAD and document it as using the first reclen CHAR of
> > PAD.

> Then i think, maybe it's forgotten definition, which should have
> been removed.

Either that, or it was added in the wrong position in the "partial
ANSification" by someone who misunderstood the ANS prohibition against
*system* words using the pad, and was trying to write around it.

User tools certainly can use the pad if they want to, they just ought
to document that they do so.

Brad

unread,
May 19, 2011, 11:14:36 AM5/19/11
to
On May 18, 5:13 pm, Zbiggy <zbigniew2011REM...@gmail.REMOVE.com>
wrote:

> : @pa ( -- n )   first block 2+ @ ;
> : !pa ( -- n )   first block 2+ ! update ;

2+ needs to be replaced with CELL+, unless you plan to only run it on
a 16-bit Forth.

-Brad

Elizabeth D Rather

unread,
May 19, 2011, 1:58:59 PM5/19/11
to
On 5/19/11 4:14 AM, BruceMcF wrote:
> On May 19, 5:41 am, Zbiggy<zbigniew2011REM...@gmail.REMOVE.com>
> wrote:
>> In comp.lang.forth, BruceMcF wrote:
>>> It preserve this from being trashed by a later use of PAD in
>>> a classic system with PAD to the end of the space available
>>> for the dictionary, that definition makes sense, but only at
>>> the end of the wordset.
>
>> But how this can occur? In "Starting Forth" I've found there:
>
>> #v+
>> Since the pad's beginning address is defined relative
>> to the last dictionary entry, it moves every time you
>> add a new definition or execute FORGET or MARKER. This
>> arrangement proves safe, however, because the pad is
>> never used when any of these events are occurring.
>> #v-

It's possible that additional application words (such as words that make
use of this linked list facility) might have uses for PAD as data. In
which case, Bruce's hypothesis that the redefinition should go at the
end of this code rather than the beginning makes sense.

> In between defining or FORGET/MARKER, you might want to use these
> words defined here, and also use some other words that use the pad for
> some other use. In that case, since this uses 32 chars of pad
> (assuming 1chars=1), redefining *PAD* to add 32 to the original PAD
> would allow the later set of definitions and this definition to share
> the PAD.
>
> Or else, if there was a previous use of PAD, which only used a fixed
> amount of it, then adding that fixed amount would allow this use and
> that use to share the pad.
>
> But redefining pad to add the amount of pad that *this* wordset uses,
> *up front* ~ that suggests that author was attempting one of those two
> but hadn't thought it through. Since PAD would normally provide 64
> chars of space, unnecessarily adding 32 not cause any *problems*, so
> the mistake would not show up in testing unless the effort to share
> pad was itself being directly tested.

...


>
>>> I'd either see if:
>
>>> CREATE recpad reclen CHARS ALLOT
>
>>> ... and replace recpad with all uses of PAD works. Or just delete the
>>> redefinition of PAD and document it as using the first reclen CHAR of
>>> PAD.

I see no reason to define a static buffer for this application.

>> Then i think, maybe it's forgotten definition, which should have
>> been removed.
>
> Either that, or it was added in the wrong position in the "partial
> ANSification" by someone who misunderstood the ANS prohibition against
> *system* words using the pad, and was trying to write around it.
>
> User tools certainly can use the pad if they want to, they just ought
> to document that they do so.

I think this analysis is correct. The author of the code clearly had
only a very foggy notion of ANS. For example, defining 2+ and 2- makes
no sense and has nothing to do with ANS Forth, which includes an
optional 2* and 2/ to enable people to make code definitions for these
words using shifts for speed. Also, most systems that define 'ascii'
make it state-smart, so it would not be equivalent to [char] which is
compile-only.

So, that definition of PAD is bogus, and should either go at the end (if
you *know* you're going to write code that uses PAD for a different
purpose) or go away altogether. And if you want to clean things up
further, get rid of that whole "ANSization" section, renaming 'cls' and
'not' in the code if necessary for compatibility with whatever system
you're running on.

Zbiggy

unread,
May 19, 2011, 3:23:22 PM5/19/11
to
In comp.lang.forth, Elizabeth D Rather wrote:

> I think this analysis is correct. The author of the code clearly had
> only a very foggy notion of ANS. For example, defining 2+ and 2- makes
> no sense and has nothing to do with ANS Forth, which includes an
> optional 2* and 2/ to enable people to make code definitions for these
> words using shifts for speed. Also, most systems that define 'ascii'
> make it state-smart, so it would not be equivalent to [char] which is
> compile-only.

Actually all modifications described as "ANSization" are mine :D - but since
I've found in the original listing such things, as "2+", then "at first look"
I've just defined equivalent words, instead of changing it; yes, Brad's right,
that most probably it should be changed to cell+/cell-. In many old listings
(well, screens rather...) I can see that "2+/2-".

> So, that definition of PAD is bogus, and should either go at the end (if
> you *know* you're going to write code that uses PAD for a different
> purpose) or go away altogether. And if you want to clean things up
> further, get rid of that whole "ANSization" section, renaming 'cls' and
> 'not' in the code if necessary for compatibility with whatever system
> you're running on.

I kept that (of LMI-Forth origin probably) words just because I'm going to
try the code on my C-64 as well :) But only after functionality of all the
words will be quite clear to me.

Hugh Aguilar

unread,
May 19, 2011, 8:40:15 PM5/19/11
to
On May 18, 6:13 pm, Zbiggy <zbigniew2011REM...@gmail.REMOVE.com>
wrote:

> I'm trying to analyse an example program (creation of double-linked list)
> from the book "Forth Applications" (by S.D. Roberts, chapter 4).
> Unfortunately, the description is rather spartan, and IMHO unclear.
> ...

> To enter a new keyword enter:    NEW <KEYWORD>
> To search:                    SEARCH <KEYWORD>
> To erase:                     DELETE <KEYWORD>

Code like that is pretty "spartan" and not really worth studying.

See my list.4th in my novice package for a full implementation. This
is actually a singly-linked list, but it could fairly easily be
changed to be doubly-linked. It would be a good idea to make the
doubly-linked version compatible with the singly-linked version so
that a program using one could switch over to using the other without
having to be modified. I may do this later myself --- it is an
exercise for the novice right now. I think that doubly-linked lists
are slower than singly-linked lists unless you want to traverse
backwards. I do have FIND-PRIOR for finding the node prior to a
specific node, and it is plenty fast with the singly-linked lists.
Finding the tail is theoretically slower with singly-linked lists, but
is quite fast in practice because you are just searching for a nil
link. Finding specific elements is slow with either kind of list,
which is a big part of why I wrote the association data type. I
considered rewriting the slide-rule program to use associations rather
than lists, but never got around to doing this --- the program is
plenty fast as it is, even though it is working with lists of up to
15,000 nodes.

Zbiggy

unread,
May 20, 2011, 1:14:00 PM5/20/11
to
In comp.lang.forth, Hugh Aguilar wrote:

> Code like that is pretty "spartan" and not really worth studying.
>
> See my list.4th in my novice package for a full implementation.

Of course, I'm going to study your package - but I decided to examine that
examples at the moment, exactly because they are shorter. The problem was,
that they are not well documented (e.g. stack effects diagrams are somewhat
messy, I've got to re-create it by myself).

But even in such case one could learn something new; take a look at this
interesting use of "count" in string comparison example - now it seems
obvious, but most probably I wouldn't invent it by myself, not very soon
anyway:

: (comp) ( a a' n -- f ) ( f: -0+ )


over + swap
do count i c@ - ?dup if swap 0= leave then loop
if 0 then ;

--

Hugh Aguilar

unread,
May 22, 2011, 1:59:04 AM5/22/11
to
On May 20, 11:14 am, Zbiggy <zbigniew2011REM...@gmail.REMOVE.com>
wrote:

> In comp.lang.forth, Hugh Aguilar wrote:
>
> > Code like that is pretty "spartan" and not really worth studying.
>
> > See my list.4th in my novice package for a full implementation.
>
> Of course, I'm going to study your package - but I decided to examine that
> examples at the moment, exactly because they are shorter. The problem was,
> that they are not well documented (e.g. stack effects diagrams are somewhat
> messy, I've got to re-create it by myself).

If you are just starting out in Forth, you may be better off to study
a simple implementation of data structures, rather than my full-blown
code --- but you don't necessarily want to use those simple
implementations in your application programs, because they are too
simple --- use the full-blown implementations in order to simplify
your application code. For example, look in the slide-rule program for
how I use EACH many many times --- imagine how much more complicated
the slide-rule program would be if I had to manually write out the
BEGIN ... WHILE ... etc. code for every loop traversal throughout the
program.

Mostly, my novice package is designed to provide data structures and
other low-level support code to the novice so he can begin writing
applications, rather than be required to first implement these data
structures himself. The underlying code in my novice package is in
many cases too advanced for most novices, and the novice would just
get bogged down in studying it. People can use the novice package
without understanding how it works. Later on when the novice is ready
to begin writing his own data structures, he is going to have to
figure out the underlying workings of the novice package (mostly
the :NAME definer, which is pretty much basis for everything). By that
time however, he will have some experience writing applications in
Forth, and he will be ready to tackle the more advanced stuff.

I recommend that people learn Forth by writing applications in Forth,
rather than by implementing data structures and low-level support code
in Forth that might someday be useful in some application. Writing
applications is a lot more fun than writing support code (especially
if the application is of interest itself, and not just a boring
homework assignment), and people learn best when they are having fun.

Also, I don't recommend writing a Forth system before gaining some
experience writing Forth applications. Mark Willis is doing this, and
he is learning pretty well, but I would have preferred that he first
write an application or two --- that might have given him a more
rounded perspective on Forth.

> But even in such case one could learn something new; take a look at this
> interesting use of "count" in string comparison example - now it seems
> obvious, but most probably I wouldn't invent it by myself, not very soon
> anyway:
>
> : (comp) ( a a' n -- f ) ( f: -0+ )
>   over + swap
>   do count i c@ - ?dup if swap 0= leave then loop
>   if 0 then ;
>
> --
> It's easy: all you need is Forth!

COUNT can be used for stepping through char arrays, but I don't
recommend this because it makes for difficult-to-read code. People
look at a function such as (COMP) above and the COUNT is confusing.
What is being counted? I would recommend using these words instead:

: C@++ ( adr -- next-adr c )
count ;

: W@++ ( adr -- next-adr n )
dup cell+ swap @ ;

: D@++ ( adr -- next-adr d )
dup cell+ cell+ swap 2@ ;

This gives you a consistent naming convention for stepping through
arrays of chars, words, doubles, etc.. The ++ affix is reminiscent of
C's post-increment, which most people are familiar with --- this
should improve readability compared to using COUNT in a way different
from what it was designed for (although you are actually using COUNT
internally).

Good luck in your effort to learn Forth --- feel free to ask
questions; I will answer them as best as I'm able.

Hugh Aguilar

unread,
May 23, 2011, 12:58:07 AM5/23/11
to
On May 20, 11:14 am, Zbiggy <zbigniew2011REM...@gmail.REMOVE.com>
wrote:
> But even in such case one could learn something new; take a look at this
> interesting use of "count" in string comparison example - now it seems
> obvious, but most probably I wouldn't invent it by myself, not very soon
> anyway:
>
> : (comp) ( a a' n -- f ) ( f: -0+ )
>   over + swap
>   do count i c@ - ?dup if swap 0= leave then loop
>   if 0 then ;

With threaded Forth systems, there is a huge difference in speed
between functions written in assembly-language (primitives) and
functions written in Forth. The function COUNT is presumably a
primitive, so there is a big speed advantage to using it rather than a
Forth version such as:

: C@++ ( adr -- next-adr n )
dup char+ swap c@ ;

This speed difference is still apparent in non-optimizing Forth
compilers such as SwiftForth:

: C@++ ( adr -- next-adr n )
dup char+ swap c@ ; ok
see c@++
46E8FF 4 # EBP SUB 83ED04
46E902 EBX 0 [EBP] MOV 895D00
46E905 EBX INC 43
46E906 0 [EBP] EAX MOV 8B4500
46E909 EBX 0 [EBP] MOV 895D00
46E90C EAX EBX MOV 8BD8
46E90E EAX EAX XOR 31C0
46E910 0 [EBX] AL MOV 8A03
46E912 EAX EBX MOV 8BD8
46E914 RET C3 ok
see count
404B4F EAX EAX XOR 31C0
404B51 0 [EBX] AL MOV 8A03
404B53 EBX INC 43
404B54 4 # EBP SUB 83ED04
404B57 EBX 0 [EBP] MOV 895D00
404B5A EAX EBX MOV 8BD8
404B5C RET C3 ok

A lot of published Forth code was originally written for Forth Inc.
systems (either PolyForth or SwiftForth), and uses a lot of weird
contortions to overcome the gross inefficiency of those systems. This
is why you see people doing things like using COUNT for stepping
through a char array, despite the readability issue. You also see
people writing gigantic functions, rather than factoring their code
into small functions, because the indirect-threading of PolyForth put
such a huge speed cost on function calls (with small functions, more
time could be spent in NEXT than in the code itself). This is also
true of modern Gforth code, which also has a grossly inefficient
threading scheme and a huge difference in speed between calling a
primitive function written in C and a high-level function written in
Forth.

I would really recommend against studying any Forth code that was
originally written for PolyForth, SwiftForth or Gforth, because it
tends to be unreadable and generally awful code.

My novice package was written primarily for SwiftForth. I don't use
very many weird contortions to boost speed though. Mostly, I get
around the speed problem of SwiftForth by providing a lot of code
written in assembly language. I have a compiler switch that the user
sets to indicate either SwiftForth or generic Forth. This is an
example:

SwiftForth? [if]

icode exchange ( adrX adrY size -- )
0 [ebp] eax mov w [ebp] ecx mov
begin non-zero? while
0 [eax] push
0 [ecx] edx mov edx 0 [eax] mov
0 [ecx] pop
w # eax add w # ecx add
w # ebx sub repeat
w 2 * [ebp] ebx mov
w 3 * # ebp add ret end-code

\ the size of the record must be a multiple of W

[else]

macro: exchange ( adrX adrY size -- )
begin dup while
over @ fourth @
fourth ! fourth !
rot w + rot w + rot w -
repeat
3drop ;

\ the size of the record must be a multiple of W

[then]

Alex McDonald

unread,
May 23, 2011, 4:29:41 AM5/23/11
to
On May 23, 5:58 am, Hugh Aguilar <hughaguila...@yahoo.com> wrote:

<extract>


>
> I would really recommend against studying any Forth code that was
> originally written for PolyForth, SwiftForth or Gforth, because it
> tends to be unreadable and generally awful code.
>
> My novice package was written primarily for SwiftForth.

< />

The juxtaposition of the sentences may be unintended, but the
conclusion is inescapable.

BruceMcF

unread,
May 23, 2011, 9:40:57 AM5/23/11
to
On May 19, 3:23 pm, Zbiggy <zbigniew2011REM...@gmail.REMOVE.com>
wrote:

> I kept that (of LMI-Forth origin probably) words just because
> I'm going to try the code on my C-64 as well :) But only
> after functionality of all the words will be quite clear to me.

With the Forth94 system for the C64 VolksForth64 or with some early
80's vintage system?

Brad

unread,
May 23, 2011, 1:16:17 PM5/23/11
to
On May 22, 9:58 pm, Hugh Aguilar <hughaguila...@yahoo.com> wrote:
> The function COUNT is presumably a
> primitive, so there is a big speed advantage to using it rather than a
> Forth version such as:
>
> : C@++  ( adr -- next-adr n )
>     dup char+  swap c@ ;
>
> This speed difference is still apparent in non-optimizing Forth
> compilers such as SwiftForth:

Methinks you write to the implementation way too much. The classic
priority in programming is get it working, then speed up the
bottlenecks. I don't understand your focus on implementation timings
that can shift with the wind.

-Brad

Hugh Aguilar

unread,
May 24, 2011, 1:03:52 AM5/24/11
to

The exact opposite is true. I was being critical of the "weird
contortions" that Forthers go through to boost the speed of their
programs. This includes using COUNT for stepping through char arrays
because COUNT is a "primitive" and runs faster than a high-level Forth
equivalent. Also, a lot of people are aware that threaded systems are
very inefficient at running high-level code. Anton Ertl has stated
that Gforth was designed to call primitive functions quickly, and to
call Forth functions much slower. Because of this, the programmers of
these badly designed systems tend to eschew factoring. They write
gigantic functions which are mostly composed of primitives, rather
than factor their code into many small Forth functions. They are
"writing for the implementation" --- at the expense of readability ---
this is a big part of why Forth code has a reputation for being hard
to read.

By comparison, I mostly wrote my novice package to be readable. Later
on when it was discovered to be slow (not a surprise), I rewrote a lot
of the low-level code in assembly-language for SwiftForth. This
assembly-language work wasn't done until after the code was working.
Actually, it wasn't done until after I had a large program (slide-rule.
4th) written using the novice package as a basis --- at that time I
was better able to determine which parts of the novice package were
being used the most and were the best candidates for assembly-
language.

For the most part, I factor quite a lot. My slide-rule program is
intended to be a showcase for good Forth style. At least part of my
motivation in writing the program was to provide a good example of
Forth style for novices to learn from --- there are way to many
examples of ugly Forth code floating around, most of which was written
that way in a desperate effort to obtain reasonable speed on a bad
compiler.

Ray St. Marie

unread,
May 24, 2011, 7:04:57 AM5/24/11
to
On May 18, 6:13 pm, Zbiggy <zbigniew2011REM...@gmail.REMOVE.com>
wrote:
> I'm trying to analyse an example program (creation of double-linked list)
> from the book "Forth Applications" (by S.D. Roberts, chapter 4).
> Unfortunately, the description is rather spartan, and IMHO unclear.
>
> There is a listing for simple double-linked list management system, which
> at the very beginning has strange definition:
>
> : PAD PAD RECLEN + ;
>

There is no such definition in my copy of "Forth Applications",
chapter 4, Data Structures. Copyright 1985 by S. D. Roberts and Elcomp
Publishing.

Just check again to be sure I do not make a fool of myself ( ahem ) an
d I find that there are two listings in the chapter the first
pertaining to linked lists and the second to balanced binary trees and
in either case PAD is not redefined and there is no such definition as
the one you provide here above.

We, of course, could have divergent versions of the book.

Ray

Mark Wills

unread,
May 24, 2011, 10:20:14 AM5/24/11
to

Same here. I can find no reference to that definition in chapter 4.

My book says "First edition - First Printing" on the inside front
cover. It's a red cover, with BITFIRE written at the bottom.

Mark

Zbiggy

unread,
May 25, 2011, 1:16:40 PM5/25/11
to
In comp.lang.forth, Ray St. Marie wrote:

> There is no such definition in my copy of "Forth Applications",
> chapter 4, Data Structures. Copyright 1985 by S. D. Roberts and Elcomp
> Publishing.
>
> Just check again to be sure I do not make a fool of myself ( ahem )

Chapter 4, page 31. Yes, it's still there. :)

Zbiggy

unread,
May 25, 2011, 1:19:54 PM5/25/11
to
In comp.lang.forth, Mark Wills wrote:

> My book says "First edition - First Printing" on the inside front
> cover. It's a red cover, with BITFIRE written at the bottom.

My has white cover, with red letters "BITFIRE" (and red stripes) at the
bottom. But it hasn't any "First edition" remark. Maybe the listing has been
revised?

It is exactly this one:
http://www.amazon.com/FORTH-Applications-Ready-programs-Bitfire/dp/0911827005

Zbiggy

unread,
May 25, 2011, 1:26:54 PM5/25/11
to
In comp.lang.forth, Hugh Aguilar wrote:

> I recommend that people learn Forth by writing applications in Forth,
> rather than by implementing data structures and low-level support code
> in Forth that might someday be useful in some application. Writing
> applications is a lot more fun than writing support code (especially
> if the application is of interest itself, and not just a boring
> homework assignment), and people learn best when they are having fun.

You're right: I need practice.

> Also, I don't recommend writing a Forth system before gaining some
> experience writing Forth applications.

Of course I'm aware about my current level of Forth-knowledge. :)
No, I'm not going to write my own Forth tomorrow, although I'm trying slowly
to explore ForthOS.

> COUNT can be used for stepping through char arrays, but I don't
> recommend this because it makes for difficult-to-read code. People
> look at a function such as (COMP) above and the COUNT is confusing.

> What is being counted? I would recommend using these words instead: [..]

I agree. At first look, I couldn't understand, what that COUNT is supposed
to make there - but when I made step-by-step analysis... but yes: clear
naming would make it easier.

> Good luck in your effort to learn Forth --- feel free to ask
> questions; I will answer them as best as I'm able.

Surely I'll have several questions to ask.

Zbiggy

unread,
May 25, 2011, 1:28:26 PM5/25/11
to
In comp.lang.forth, BruceMcF wrote:

> With the Forth94 system for the C64 VolksForth64 or with some early
> 80's vintage system?

I gathered entire collection of Forths for C-64 - but at the moment most
of my attempts I'm making using Super-forth 64.

BruceMcF

unread,
May 25, 2011, 2:14:50 PM5/25/11
to
On May 25, 1:28 pm, Zbiggy <zbigniew2011REM...@gmail.REMOVE.com>
wrote:

> In comp.lang.forth, BruceMcF wrote:
> > With the Forth94 system for the C64 VolksForth64 or with some early
> > 80's vintage system

> I gathered entire collection of Forths for C-64 - but at the moment most


> of my attempts I'm making using Super-forth 64.

Volkforth64 can be handy because its ANS Forth, though AFAIU they ran
out of steam before implementing the file wordset.

Zbiggy

unread,
May 25, 2011, 2:21:49 PM5/25/11
to
In comp.lang.forth, BruceMcF wrote:

> Volkforth64 can be handy because its ANS Forth, though AFAIU they ran
> out of steam before implementing the file wordset.

Yes, got it too - although most probably Durex Forth is fully ANSized:
http://noname.c64.org/csdb/release/?id=75456

Well, I've got just to make a comparison.

BruceMcF

unread,
May 25, 2011, 8:29:04 PM5/25/11
to
On May 25, 2:21 pm, Zbiggy <zbigniew2011REM...@gmail.REMOVE.com>
wrote:

> In comp.lang.forth, BruceMcF wrote:
> > Volkforth64 can be handy because its ANS Forth, though AFAIU they ran
> > out of steam before implementing the file wordset.
>
> Yes, got it too - although most probably Durex Forth is fully ANSized:http://noname.c64.org/csdb/release/?id=75456

Does not seem to be.

0 new messages