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

I made another (slightly longer) video on Forth!

287 views
Skip to first unread message

Hans Bezemer

unread,
May 31, 2022, 12:27:14 PM5/31/22
to
It's here - https://www.youtube.com/watch?v=hpw__rmBisU

The last one was so much fun to make, I decided to do a few
other ones. Consider this one to be some foundation I can
build on - without having to repeat myself too much in future
videos.

Hans Bezemer

Zbig

unread,
May 31, 2022, 12:35:56 PM5/31/22
to
01:45 -- but you know: Forth's stack works on the rule „last in — first out”, not „first in, first out”. Or am I wrong?

Hans Bezemer

unread,
May 31, 2022, 1:06:16 PM5/31/22
to
On Tuesday, May 31, 2022 at 6:35:56 PM UTC+2, Zbig wrote:
> > It's here - https://www.youtube.com/watch?v=hpw__rmBisU
> 01:45 -- but you know: Forth's stack works on the rule „last in — first out”, not „first in, first out”. Or am I wrong?
No, you're not. I pulled it and I'm uploading an updated version.

Hans Bezemer

Hans Bezemer

unread,
May 31, 2022, 1:36:46 PM5/31/22
to
Second attempt: https://www.youtube.com/watch?v=pnOhRPgFZDI

Hans Bezemer

dxforth

unread,
Jun 2, 2022, 8:56:32 PM6/2/22
to
On 1/06/2022 03:36, Hans Bezemer wrote:
>
> Second attempt: https://www.youtube.com/watch?v=pnOhRPgFZDI

The section on return stack was unexpected - especially for an intro.
Found myself stopping, reversing and going through it again bit by bit.
It's detail I rarely consider; wonder what a beginner would make of it.
Not saying RS function shouldn't be mentioned (SF did IIRC) but wishing
it were paced slower. But then antipodeans prefer everything slower...

Hans Bezemer

unread,
Jun 3, 2022, 5:16:26 AM6/3/22
to
On Friday, June 3, 2022 at 2:56:32 AM UTC+2, dxforth wrote:
> The section on return stack was unexpected - especially for an intro.
> Found myself stopping, reversing and going through it again bit by bit.
It's a stack as well - and I find its mechanism both simple and effective.
That's why I included it.

> It's detail I rarely consider; wonder what a beginner would make of it.
> Not saying RS function shouldn't be mentioned (SF did IIRC) but wishing
> it were paced slower. But then antipodeans prefer everything slower...
As a matter of fact, SF did in great length. It even went so far to do quite
tricky manipulations with it to change program flow. I refrained from that for
two reasons. First its a "new" concept to those watching it and second,
the implications of return stack manipulations are quite mind boggling
for newbies. Heck, when I did my co-routine stuff, I found it quite mind
boggling.

Hans Bezemer

dxforth

unread,
Jun 3, 2022, 10:34:39 PM6/3/22
to
What was in SF was probably tricky (or long-winded) enough that I skipped
over it :) OTOH it makes little sense to tell newcomers forth has a
'return stack' without explaining what it's for. That you did caught me
by surprise as so few intros do.

Is return stack a "new" concept? I've no idea when I first heard about
subroutine calls and stacks but I'm sure it was long before I'd heard of
forth. Perhaps it's different for newcomers today (much like their music).

Marcel Hendrix

unread,
Jun 4, 2022, 12:22:59 AM6/4/22
to
On Saturday, June 4, 2022 at 4:34:39 AM UTC+2, dxforth wrote:
[..]
> Is return stack a "new" concept? I've no idea when I first heard about
> subroutine calls and stacks but I'm sure it was long before I'd heard of
> forth. Perhaps it's different for newcomers today (much like their music).

I vividly remember that Forth's implementation techniques were
making understanding it very difficult for me. That was mainly
because it was not explained clearly (or not in a way I could easily
understand). Authors used non-portable aspects of the language
all the time, and it was never explained in depth what the actual
technical advantages of all these different and mind-boggling
tricks (threading methods) were. (At that time it was not yet
possible to Google your way to the heart of even the most esoteric
and difficult concepts.)

At that time I was mainly fascinated by the possibility to use
assembly language from within Forth, and the light came on
(slowly) when I realized that the language essentially boiled
down to generating call-return pairs. It is, for instance, very
easy to explain CREATE DOES> in terms of a call to a
subroutine that uses the return stack pointer to subsequently
load data from the caller's code segment.

-marcel

dxforth

unread,
Jun 4, 2022, 1:36:39 AM6/4/22
to
I was thinking more fundamentally i.e. forth has a return stack
for the same reason CPUs typically had one. The latter also had
the equivalent of >R ... R> for storing temps! I gather early
processors didn't have a return stack - which may have made it
"new" at the time.



Marcel Hendrix

unread,
Jun 4, 2022, 3:38:33 AM6/4/22
to
On Saturday, June 4, 2022 at 7:36:39 AM UTC+2, dxforth wrote:
> On 4/06/2022 14:22, Marcel Hendrix wrote:
> > On Saturday, June 4, 2022 at 4:34:39 AM UTC+2, dxforth wrote:
> > [..]
> >> Is return stack a "new" concept? I've no idea when I first heard about
> >> subroutine calls and stacks but I'm sure it was long before I'd heard of
> >> forth. Perhaps it's different for newcomers today (much like their music).
[..]
> > At that time I was mainly fascinated by the possibility to use
> > assembly language from within Forth, and the light came on
> > (slowly) when I realized that the language essentially boiled
> > down to generating call-return pairs. It is, for instance, very
> > easy to explain CREATE DOES> in terms of a call to a
> > subroutine that uses the return stack pointer to subsequently
> > load data from the caller's code segment.
> I was thinking more fundamentally i.e. forth has a return stack
> for the same reason CPUs typically had one. The latter also had
> the equivalent of >R ... R> for storing temps! I gather early
> processors didn't have a return stack - which may have made it
> "new" at the time.

I am not sure what you mean. Are you hinting that classic Forth
emulated something like the 'link register' the 68000 (and Knuth's
MIX) had?
In iForth I could, instead of CALL-ing primitive A, load the target
return address in register x and then JMP to A. Primitive A simply
jumps to (x) instead of doing a RET. Maybe I should try that...

-marcel

Anton Ertl

unread,
Jun 4, 2022, 5:06:51 AM6/4/22
to
Marcel Hendrix <m...@iae.nl> writes:
>Are you hinting that classic Forth
>emulated something like the 'link register' the 68000 (and Knuth's
>MIX) had?

The 68000 has no link register. It's JSR instruction writes the
return address to memory, and the RTS instruction reads the return
address from memory. The memory is pointed to by the hardware stack
pointer (IIRC A7). Just like on the 8086.

RISC architectures have link registers, either one of the GPRs
(Aarch64, ARM, MIPS, Alpha, SPARC, RISC-V), or a special-purpose
register (PowerPC). For the GPRs-using architectures, in indirect
call instructions, you can choose which GPR to store the return
address to, but for direct calls the register is a specific one (in
order to leave more room in the instruction for the target address).

AFAIK the IBM S/360 also has a link register.

>In iForth I could, instead of CALL-ing primitive A, load the target
>return address in register x and then JMP to A. Primitive A simply
>jumps to (x) instead of doing a RET. Maybe I should try that...

You do that already for calling colon definitions, so if you think
that's the way to go, you might just as well use it for primitives.

But which primitives do you have in mind? I looked at some candidates
(+, MOVE), and they look like they follow the same calling-convention
as colon definitions.

- anton
--
M. Anton Ertl http://www.complang.tuwien.ac.at/anton/home.html
comp.lang.forth FAQs: http://www.complang.tuwien.ac.at/forth/faq/toc.html
New standard: https://forth-standard.org/
EuroForth 2022: http://www.euroforth.org/ef22/cfp.html

Hans Bezemer

unread,
Jun 4, 2022, 6:33:52 AM6/4/22
to
On Saturday, June 4, 2022 at 11:06:51 AM UTC+2, Anton Ertl wrote:
> Marcel Hendrix <m...@iae.nl> writes:
> >Are you hinting that classic Forth
> >emulated something like the 'link register' the 68000 (and Knuth's
> >MIX) had?
> The 68000 has no link register. It's JSR instruction writes the
> return address to memory, and the RTS instruction reads the return
> address from memory. The memory is pointed to by the hardware stack
> pointer (IIRC A7). Just like on the 8086.
>
> RISC architectures have link registers, either one of the GPRs
> (Aarch64, ARM, MIPS, Alpha, SPARC, RISC-V), or a special-purpose
> register (PowerPC). For the GPRs-using architectures, in indirect
> call instructions, you can choose which GPR to store the return
> address to, but for direct calls the register is a specific one (in
> order to leave more room in the instruction for the target address).
>
> AFAIK the IBM S/360 also has a link register.
> >In iForth I could, instead of CALL-ing primitive A, load the target
> >return address in register x and then JMP to A. Primitive A simply
> >jumps to (x) instead of doing a RET. Maybe I should try that...
> You do that already for calling colon definitions, so if you think
> that's the way to go, you might just as well use it for primitives.
>
> But which primitives do you have in mind? I looked at some candidates

Personally, I've always treated 4tHs return stack in the way I knew
from the Z80 - which I was quite familiar with back in the days. And that
concept fitted SF quite nicely.

https://riptutorial.com/assembly/example/17507/zilog-z80-stack

HB

Marcel Hendrix

unread,
Jun 4, 2022, 6:38:28 AM6/4/22
to
On Saturday, June 4, 2022 at 11:06:51 AM UTC+2, Anton Ertl wrote:
> Marcel Hendrix <m...@iae.nl> writes:
> >Are you hinting that classic Forth
> >emulated something like the 'link register' the 68000 (and Knuth's
> >MIX) had?
> The 68000 has no link register. It's JSR instruction writes the
> return address to memory, and the RTS instruction reads the return
> address from memory. The memory is pointed to by the hardware stack
> pointer (IIRC A7). Just like on the 8086.

Indeed, 68k has a link instruction, not a link register.

> >In iForth I could, instead of CALL-ing primitive A, load the target
> >return address in register x and then JMP to A. Primitive A simply
> >jumps to (x) instead of doing a RET. Maybe I should try that...
> You do that already for calling colon definitions, so if you think
> that's the way to go, you might just as well use it for primitives.
>
> But which primitives do you have in mind? I looked at some candidates
> (+, MOVE), and they look like they follow the same calling-convention
> as colon definitions.

The problem is that iForth uses the machine stack as the Forth data stack.
Therefore the CALL instruction can't be used without saving RTOS first,
and restoring RTOS before a return. That overhead (for non-inlined words)
goes away with a dedicated link register, but then famous control-flow
modifying tricks like R> DROP would stop working. I assume that these
archaic techniques will be made non-standard at some time, but probably
mxForth (iForth with R-D stacks switched) arrives sooner than that.

-marcel

Hans Bezemer

unread,
Jun 4, 2022, 7:52:58 AM6/4/22
to
On Saturday, June 4, 2022 at 12:38:28 PM UTC+2, Marcel Hendrix wrote:
> The problem is that iForth uses the machine stack as the Forth data stack.
> Therefore the CALL instruction can't be used without saving RTOS first,
> and restoring RTOS before a return. That overhead (for non-inlined words)
> goes away with a dedicated link register, but then famous control-flow
> modifying tricks like R> DROP would stop working. I assume that these
> archaic techniques will be made non-standard at some time, but probably
> mxForth (iForth with R-D stacks switched) arrives sooner than that.

It's quite courageous to use the machine stack - but I can understand why
you did that and kudos for pulling it off!

I agree that the "old techniques" are dirty and its use should be discouraged.
Although you can pull them off in 4tH, it might pose problems if you don't
know exactly what you're doing because tail call optimization can come in
the way.

On the other hand, some things (like coroutines) can't be done (at least in 4tH)
without it. Still, if I have to weigh them both in, I prefer the clean approach much
more than some fancy thingies.

Hans Bezemer

Anton Ertl

unread,
Jun 4, 2022, 12:03:43 PM6/4/22
to
Marcel Hendrix <m...@iae.nl> writes:
>Indeed, 68k has a link instruction, not a link register.

IIRC LINK and UNLK deal with stack frames (but are not quite as
complicated as 8086's ENTER and LEAVE).

>The problem is that iForth uses the machine stack as the Forth data stack.
>Therefore the CALL instruction can't be used without saving RTOS first,
>and restoring RTOS before a return. That overhead (for non-inlined words)
>goes away with a dedicated link register, but then famous control-flow
>modifying tricks like R> DROP would stop working. I assume that these
>archaic techniques will be made non-standard at some time

If you mean the Forth standards (Forth-79 etc.), I don't know if they
ever were standard, but they certainly were not in Forth-94. Already
before Forth-94 there were systems such as F-PC where these techniques
didn't work (at least not with "r> drop exit" to return to the
caller's caller), and it does not work in general in SwiftForth
(thanks to tail-call optimization) and in VFX (thanks to inlining):

VFX Forth for Linux IA32 Version: 4.72 [build 0555]
: foo r> drop ; ok
: bar foo ; ok
: flip bar ." end flip." ; ok
: flop flip ." end flop." ; ok
flop end flop. ok

SwiftForth i386-Linux 3.11.0 23-Feb-2021
: foo r> drop ; ok
: bar foo ; ok
: flip bar ." end flip." ; ok
: flop flip ." end flop." ; ok
flop end flop. ok

iforth-5.1-mini:
FORTH> : foo r> drop ; ok
FORTH> : bar foo ; ok
FORTH> : flip bar ." end flip." ; ok
FORTH> : flop flip ." end flop." ; ok
FORTH> flop end flip. ok

That's surprising; so it already does not work in the classical way in
iForth.

Gforth performs no inlining and no tail-call optimization (yet):
: foo r> drop ; ok
: bar foo ; ok
: flip bar ." end flip." ; ok
: flop flip ." end flop." ; ok
flop end flip.end flop. ok

There have been efforts by Gordon Charlton and Michael Gassanenko to
standardize return-address manipulation, but nothing ended up in
Forth-2012. If you are interested in this functionality, they made
some proposals (not formal Forth-200x proposals, though).

>mxForth (iForth with R-D stacks switched)

Given that you tried both, why did you decide to use RSP as data stack
pointer for iForth. Everybody else used RSP for the return stack, and
it looks like the better choice to me, especially because it results
in a better branch prediction for EXIT.

Hans Bezemer

unread,
Jun 4, 2022, 3:46:54 PM6/4/22
to
On Saturday, June 4, 2022 at 6:03:43 PM UTC+2, Anton Ertl wrote:
> There have been efforts by Gordon Charlton and Michael Gassanenko to
> standardize return-address manipulation, but nothing ended up in
> Forth-2012. If you are interested in this functionality, they made
> some proposals (not formal Forth-200x proposals, though).
I remember this guy! His original paper was
http://www.forth.org.ru/~mlg/ef94/ef94-2-paper.txt

I found this quite compelling, but never fully grasped his concept.
It was remarkable similar to my YIELD library:

\ The implementation of backtracking may be briefly described as following:

\ 1) the residue of the caller procedure threaded code is called continuation;
\ 2) a success is a call of the continuation;
\ 3) a failure is a return from the continuation. (To perform failure a
\ procedure compiled into the continuation should exit both its threaded
\ code and the continuation threaded code).

\ To call the residue of the caller threaded code the callee may execute
\ the code SUCCESS. To perform failure, i.e. exit the code fragment
\ which called the procedure, the callee may perform the code FAIL.
\ Note that since the top return stack item contains the address of
\ continuation, exiting to this address is scarcely meaningful.

\ For example:

\ : 1-10
\ 1
\ BEGIN
\ DUP SUCCESS
\ 1+
\ DUP 11 =
\ UNTIL
\ DROP FAIL
\ ;

\ : X 1-10 . ;

\ Execution of the word X will print numbers from 1 to 10.

\ The technique described above cannot work if we use the return stack to hold
\ temporary data!!

Hans Bezemer

Marcel Hendrix

unread,
Jun 4, 2022, 7:54:51 PM6/4/22
to
On Saturday, June 4, 2022 at 6:03:43 PM UTC+2, Anton Ertl wrote:
> Marcel Hendrix <m...@iae.nl> writes:
[..]
> Gforth performs no inlining and no tail-call optimization (yet):
> : foo r> drop ; ok
> : bar foo ; ok
> : flip bar ." end flip." ; ok
> : flop flip ." end flop." ; ok
> flop end flip.end flop. ok

In iForth one has to suppress both inlining and tail-call optimizations:

FORTH> : foo r> drop [ -OPT ] ; ok
FORTH> : bar foo [ -OPT ] ; ok
FORTH> : flip bar ." end flip." ; ok
FORTH> : flop flip ." end flop." ; ok
FORTH> flop end flip.end flop. ok

> >mxForth (iForth with R-D stacks switched)
> Given that you tried both, why did you decide to use RSP as data stack
> pointer for iForth. Everybody else used RSP for the return stack, and
> it looks like the better choice to me, especially because it results
> in a better branch prediction for EXIT.

Initially (32-bit era), I was struggling for free registers and didn't want
to give one up for TOS. Also, I didn't like the looks of that optimization.
By the time I started to measure performance in earnest, the code
generator had become so complex that I couldn't change it easily
without years of re-testing. Instead, I invested the time in writing the
64bit version of iForth, with mxForth as a prototype. mxForth was
much faster than iForth64, but the latter could be ported
extremely quickly from iForth32 while the former would have been
a complete rewrite (there were also metacompiler issues). I chose
to get on and concentrate on actually using iForth for my other interests.
As I seem to get closer and closer to the physical limits with circuit
simulation, I may want to pick up mxForth again at some point.

-marcel

dxforth

unread,
Jun 5, 2022, 12:27:59 AM6/5/22
to
On 5/06/2022 01:31, Anton Ertl wrote:
> ...
> If you mean the Forth standards (Forth-79 etc.), I don't know if they
> ever were standard, but they certainly were not in Forth-94. Already
> before Forth-94 there were systems such as F-PC where these techniques
> didn't work (at least not with "r> drop exit" to return to the
> caller's caller), and it does not work in general in SwiftForth
> (thanks to tail-call optimization) and in VFX (thanks to inlining):

Did they try? It's not clear to me Forth-94 TC ever considered it -
unless a reader wishes to read it into their statement:

'A program shall not access values on the return stack that it did
not place there'

It's almost biblical.

"The tree of knowledge of good and evil, thou shalt not eat of it."

R> DROP EXIT may not qualify as a beginner's technique, nor easy to debug,
nor frequently used etc but apparently it was known and applied:

From: fi...@ecst.csuchico.edu (Kevin Haddock)
Subject: Re: More Forth-in-C opinions...
Date: 18 Jun 1993 15:38:45 GMT
NNTP-Posting-Host: cscihp.ecst.csuchico.edu

In article <1vs5iv$s...@nz12.rz.uni-karlsruhe.de> DA...@ifk20.mach.uni-karlsruhe.de (Heribert Dahms) writes:
>In <1vpg81...@charnel.ecst.csuchico.edu> fi...@ecst.csuchico.edu writes:
>
>: [...]
>R> DROP EXIT is explicitely forbidden by dpANS Forth !

[...]
R> DROP EXIT in most of the polyFORTH code I've seen is about as common
as the 'break' statement in C (and is roughly the equivalent).

Don't flame me, I'm just making an observation!

-Kevin

If Forth is about getting results using the simplest possible means then IMO
it should have been included. I came upon it unwittingly when coding the
example I describe here:

http://dxforth.mirrors.minimaltype.com/unnest.html

none albert

unread,
Jun 5, 2022, 3:23:24 AM6/5/22
to
In article <2022Jun...@mips.complang.tuwien.ac.at>,
Anton Ertl <an...@mips.complang.tuwien.ac.at> wrote:
<SNIP>
>
>Given that you tried both, why did you decide to use RSP as data stack
>pointer for iForth. Everybody else used RSP for the return stack, and
>it looks like the better choice to me, especially because it results
>in a better branch prediction for EXIT.

8086 fig-Forth and all their derivatives uses the real data stack.
16 bit models fig-Forth uses segments to their advantage to break
the 64 kbyte limit. That is not easily changed.
iForth and for that matter ciforth never changed that.

I have found an effective DLL activation on MS-Windows in
32/64 ciforth. I'm not sure how good this works if EXSP is not the
real data stack.

>
>- anton

Groetjes Albert
--
"in our communism country Viet Nam, people are forced to be
alive and in the western country like US, people are free to
die from Covid 19 lol" duc ha
albert@spe&ar&c.xs4all.nl &=n http://home.hccnet.nl/a.w.m.van.der.horst

Anton Ertl

unread,
Jun 5, 2022, 4:58:47 AM6/5/22
to
dxforth <dxf...@gmail.com> writes:
>On 5/06/2022 01:31, Anton Ertl wrote:
>> ...
>> If you mean the Forth standards (Forth-79 etc.), I don't know if they
>> ever were standard, but they certainly were not in Forth-94. Already
>> before Forth-94 there were systems such as F-PC where these techniques
>> didn't work (at least not with "r> drop exit" to return to the
>> caller's caller), and it does not work in general in SwiftForth
>> (thanks to tail-call optimization) and in VFX (thanks to inlining):
>
>Did they try?

Did who try?

>It's not clear to me Forth-94 TC ever considered it -

Given that return-address manipulation is a well-known technique and
not extremely rare (as can also be seen by the repeated discussions
about the topic here, and by Marcel Hendrix' assumption that it is
standardized), I expect that they considered it, and decided against
standardizing it.

>unless a reader wishes to read it into their statement:
>
> 'A program shall not access values on the return stack that it did
> not place there'

More exactly (from 3.2.3.3):

|A program shall not access values on the return stack (using R@, R>,
|2R@, 2R> or NR>) that it did not place there using >R, 2>R or N>R;

>It's almost biblical.

The use of "shall" is normal standards language. Forth-94 usually
uses a different style: It describes how a standard system behaves
when processing a standard program; and basically anything outside
this description is non-standard: If a program relies on a behaviour
that's not guaranteed in the standard, it is non-standard; and if a
system behaves in a way incompatible with the requirements of the
standard, it is non-standard. I usually prefer that style.

All the restrictions spelled out explicitly with "shall not" and
"shall" in 3.2.3.3 are consequences of (from 3.1.5.2 System-execution
types):

|These data types denote zero or more items on the return stack.

Apparently the Forth-94 committee felt it necessary to spell out these
consequences explicitly in the face of well-known return-address
manipulation (and maybe also loop-control parameter manipulation,
although that seems to be much less popular) techniques.

>If Forth is about getting results using the simplest possible means then IMO
>it should have been included.

Somehow people manage to get results without return-address
manipulation (or are happy enough with system-specific ways to perform
them) or F-PC, iForth, SwiftForth, VFX, cmForth, machineForth, and
colorForth would have supported R> DROP EXIT by default; they don't
(at least not with the same results as you see on, say fig-Forth).
Given the lack of common practice these days, I expect that that ship
has sailed, but if you feel like it, you can try proposing support for
that feature.

Alternatively, you can implement that feature in your system (it
probably is already there) and document it as a feature.

Anton Ertl

unread,
Jun 5, 2022, 5:13:32 AM6/5/22
to
albert@cherry.(none) (albert) writes:
>8086 fig-Forth and all their derivatives uses the real data stack.

You mean they use SP as the data stack pointer.

>16 bit models fig-Forth uses segments to their advantage to break
>the 64 kbyte limit.

I don't see that in the fig-Forth listing, and I doubt that played a
role for that question. If they had used SP for the return stack
instead of the data stack, they would have broken the limit, too, and
by a similar (small) amount (typical data and return stack depths are <30).

I expect that the much more relevant issue for 8086 fig-Forth is that
there are many more data-stack accesses than return-stack accesses,
and thus making use of POP and PUSH saved code space and (on the 8086
and 8088) execution time.

>I have found an effective DLL activation on MS-Windows in
>32/64 ciforth. I'm not sure how good this works if EXSP is not the
>real data stack.

VFX and SwiftForth seem to have no problems.

Anton Ertl

unread,
Jun 5, 2022, 5:43:12 AM6/5/22
to
Marcel Hendrix <m...@iae.nl> writes:
>Initially (32-bit era), I was struggling for free registers and didn't want
>to give one up for TOS.

We also have this problem on the 386 (as has everybody else).
However, after many years I noticed that keeping the TOS in a register
does not really increase register pressure. The reason is that you
keep the TOS in a register between the end of one word and the start
of the next; there are a few cases to consider:

* for data-stack words, the TOS can be

- alive throughout the a data-stack word (e.g., +), so if you do not
keep it in a register, you memory-pop it at the start of the word and
memory-push it at the end. Keeping it alive across words does not
increase register pressure.

- dead at the start of a data-stack word (e.g., R@). So if you keep the
TOS in a register, you memory-push it at the start, and are in the
same situation wrt. register pressure as if you had not kept the
TOS in a register.

- dead at the end of a data-stack word (e.g., !). If you keep the
TOS in a register, you have to memory-pop it at the end, but
before that, you are in the same situation wrt. register pressure
as if you had not kept the TOS in a register.

* So the only words where keeping the TOS in a register makes a real
difference to register pressure are those that don't access the data
stack at all, such as ;S/EXIT or F+. However, for these words
general-purpose register pressure tends not to be a problem.

This is from the perspective of a simple Forth system, but even with
an analytic compiler (like iForth, lxf and VFX), the reasoning is
similar, only at the level of colon definitions rather than
primitives.

>Also, I didn't like the looks of that optimization.
>By the time I started to measure performance in earnest, the code
>generator had become so complex that I couldn't change it easily
>without years of re-testing. Instead, I invested the time in writing the
>64bit version of iForth, with mxForth as a prototype. mxForth was
>much faster than iForth64, but the latter could be ported
>extremely quickly from iForth32 while the former would have been
>a complete rewrite (there were also metacompiler issues). I chose
>to get on and concentrate on actually using iForth for my other interests.
>As I seem to get closer and closer to the physical limits with circuit
>simulation, I may want to pick up mxForth again at some point.

Interesting, thanks. I guess that, with aggressive inlining, returns
are rare enough that the misprediction disadvantage plays a small
role.

dxforth

unread,
Jun 5, 2022, 7:34:29 AM6/5/22
to
An 8080 has three 16-bit registers and 8-bit accumulator. An ITC
NEXT will use all of them:

NEXT LDAX B
INX B
MOV L,A
LDAX B
INX B
MOV H,A
MOV E,M
INX H
MOV D,M
XCHG
PCHL

It might be possible to keep IP in memory but that will only slow down
NEXT. ISTM keeping TOS in a register is predicated on having enough free
ones to begin with. IOW TOS in register is not the highest priority.

Anton Ertl

unread,
Jun 5, 2022, 10:01:55 AM6/5/22
to
dxforth <dxf...@gmail.com> writes:
>An 8080 has three 16-bit registers and 8-bit accumulator. An ITC
>NEXT will use all of them:
>
>NEXT LDAX B
> INX B
> MOV L,A
> LDAX B
> INX B
> MOV H,A
> MOV E,M
> INX H
> MOV D,M
> XCHG
> PCHL
>
>It might be possible to keep IP in memory but that will only slow down
>NEXT. ISTM keeping TOS in a register is predicated on having enough free
>ones to begin with. IOW TOS in register is not the highest priority.

IP certainly has a higher priority, and if all the registers you have
are needed in NEXT, then obviously the considerations above don't
apply, because they are predicated on being able to keep the TOS in a
register without slowing down NEXT.

Anyway, we were discussing the 80386, not the 8080. Even if we take
the in-between 8086, I see in "Inside F83" the following register
allocation:

BX=W
SP=SP
BP=RP
SI=IP

AX, CX, DX, and DI are scratch registers

NEXT destroys AX, so let's leave that alone.

One might use DI as TOS, allowing to replace F83's

CODE @ ( addr -- n )
BX POP
0 [BX] PUSH
NEXT
END-CODE

by

CODE @ ( addr -- n )
DI 0 [DI] MOV
NEXT
END-CODE

and (I expanded the 1PUSH makro to make the cost of 1PUSH obvious):

CODE + (S n1 n2 -- sum )
BX POP AX POP BX AX ADD AX PUSH NEXT END-CODE
CODE NEGATE (S n -- n' )
AX POP AX NEG AX PUSH NEXT END-CODE

by

CODE + (S n1 n2 -- sum )
AX POP AX DI ADD NEXT END-CODE
CODE NEGATE (S n -- n' )
DI NEG NEXT END-CODE

Shorter and faster.

However, I have to admit that my argument why keeping the TOS in a
register does not increase register pressure does not hold in these
cases, because in these cases only registers AX and BX are used within
a word that NEXT uses anyway.

However, if you consider the direct-threaded NEXT of gforth-fast 0.6.2
(compiled with gcc-2.95), the only register involved is IP (in ebx):

add ebx , # 4
jmp dword ptr -4 [ebx]

(This is in a setup (primitive-centric hybrid direct/indirect threaded
code) where words called with NEXT don't need W. In this setup the
argument certainly is true.

dxforth

unread,
Jun 5, 2022, 11:25:13 PM6/5/22
to
On 5/06/2022 23:33, Anton Ertl wrote:
> ...
> Anyway, we were discussing the 80386, not the 8080. Even if we take
> the in-between 8086, I see in "Inside F83" the following register
> allocation:
>
> BX=W
> SP=SP
> BP=RP
> SI=IP
>
> AX, CX, DX, and DI are scratch registers
>
> NEXT destroys AX, so let's leave that alone.
>
> One might use DI as TOS, allowing to replace F83's
> ...

I could have gone with TOS in register when DX-Forth moved from
8080 to 8086 but it was never a serious consideration - perhaps
because I'd be looking at 15% speed improvement max and I was
already using an inline NEXT. Some years later I did create a
kernel with TOS in BX just to see what it would take. I recall
it wasn't particularly hard. However to make the change at that
point in time, invalidating anything that used assembler, I felt
was more trouble than worth. I'd only do it were I starting a
new forth and even then the gain would be marginal.

dxforth

unread,
Jun 6, 2022, 1:09:02 AM6/6/22
to
On 5/06/2022 18:05, Anton Ertl wrote:
> dxforth <dxf...@gmail.com> writes:
>>On 5/06/2022 01:31, Anton Ertl wrote:
>>> ...
>>> If you mean the Forth standards (Forth-79 etc.), I don't know if they
>>> ever were standard, but they certainly were not in Forth-94. Already
>>> before Forth-94 there were systems such as F-PC where these techniques
>>> didn't work (at least not with "r> drop exit" to return to the
>>> caller's caller), and it does not work in general in SwiftForth
>>> (thanks to tail-call optimization) and in VFX (thanks to inlining):
>>
>>Did they try?
>
> Did who try?

The authors of those implementations. The reason for something not
working can be as simple as it was never considered.

>
>>It's not clear to me Forth-94 TC ever considered it -
>
> Given that return-address manipulation is a well-known technique and
> not extremely rare (as can also be seen by the repeated discussions
> about the topic here, and by Marcel Hendrix' assumption that it is
> standardized), I expect that they considered it, and decided against
> standardizing it.

With no explanation? It doesn't seem likely to me but perhaps I expect
too much.

>>unless a reader wishes to read it into their statement:
>>
>> 'A program shall not access values on the return stack that it did
>> not place there'
>
> More exactly (from 3.2.3.3):
>
> |A program shall not access values on the return stack (using R@, R>,
> |2R@, 2R> or NR>) that it did not place there using >R, 2>R or N>R;
>
>>It's almost biblical.
>
> The use of "shall" is normal standards language. Forth-94 usually
> uses a different style: It describes how a standard system behaves
> when processing a standard program; and basically anything outside
> this description is non-standard: If a program relies on a behaviour
> that's not guaranteed in the standard, it is non-standard; and if a
> system behaves in a way incompatible with the requirements of the
> standard, it is non-standard. I usually prefer that style.

"shall" is a commandment. It presumes authority and its acceptance.
On whose authority did ANS take away the ability of users to manipulate
the return stack? Not Moore's AFAIK.

> ...
> Given the lack of common practice these days, I expect that that ship
> has sailed, but if you feel like it, you can try proposing support for
> that feature.

Does the Pope ask Catholics to send him proposals?

> Alternatively, you can implement that feature in your system (it
> probably is already there) and document it as a feature.

As can anyone who sees it as a basic entitlement of Forth.

Anton Ertl

unread,
Jun 6, 2022, 5:32:24 AM6/6/22
to
dxforth <dxf...@gmail.com> writes:
>15% speed improvement max

Maybe on the original 8086. I recently measured it again
<2022May2...@mips.complang.tuwien.ac.at>, and on modern CPUs the
speed difference is about a factor of 2:

Results are in cycles (except the "instructions" line), gforth-fast
keeps the TOS in a register, and gforth keeps it in memory.

|gforth-fast gforth
|3,460,908,004 4,737,564,166 instructions (all)
|2,269,234,025 4,104,828,209 Goldmont (2016 efficiency)
|1,714,076,717 3,495,857,543 Sandy Bridge (2011)
|1,584,890,277 3,419,614,950 Zen (2017)
|1,342,756,453 2,706,876,924 Zen2 (2019)
|1,110,655,337 2,424,136,035 Zen3 (2020)
|1,189,653,919 2,106,179,585 Rocket Lake (2021)

Anton Ertl

unread,
Jun 6, 2022, 6:12:33 AM6/6/22
to
dxforth <dxf...@gmail.com> writes:
>On 5/06/2022 18:05, Anton Ertl wrote:
>> dxforth <dxf...@gmail.com> writes:
>>>On 5/06/2022 01:31, Anton Ertl wrote:
>>>> ...
>>>> If you mean the Forth standards (Forth-79 etc.), I don't know if they
>>>> ever were standard, but they certainly were not in Forth-94. Already
>>>> before Forth-94 there were systems such as F-PC where these techniques
>>>> didn't work (at least not with "r> drop exit" to return to the
>>>> caller's caller), and it does not work in general in SwiftForth
>>>> (thanks to tail-call optimization) and in VFX (thanks to inlining):
>>>
>>>Did they try?
>>
>> Did who try?
>
>The authors of those implementations. The reason for something not
>working can be as simple as it was never considered.

It's possible that no customer ever reported problems with
return-address-manipulating code, and therefore the implementors never
considered the issue; in that case one might conclude that this
feature is used so rarely that it would be a bad idea to slow down
their systems in order to support that feature.

However, I believe that they have had reports from customers, have
considered it, and decided that inlining or tail-call optimization are
enabled by default, and they give the programmers ways to disable
these optimizations, so the programmers can get return-address
manipulations to work (at a cost in performance).

>> The use of "shall" is normal standards language. Forth-94 usually
>> uses a different style: It describes how a standard system behaves
>> when processing a standard program; and basically anything outside
>> this description is non-standard: If a program relies on a behaviour
>> that's not guaranteed in the standard, it is non-standard; and if a
>> system behaves in a way incompatible with the requirements of the
>> standard, it is non-standard. I usually prefer that style.
>
>"shall" is a commandment. It presumes authority and its acceptance.

Maybe that's what standards bodies had in mind when they introduced
that language.

>On whose authority did ANS take away the ability of users to manipulate
>the return stack? Not Moore's AFAIK.

Did they take anything away? Was return-address manipulation
standardized in Forth-83?

Concerning Moore, cmForth uses tail-call optimization (see page 101 of
"Footsteps in an Empty Valley"), so Moore took "away the ability of
users to manipulate the return stack" in cmForth. He continued in
this away-taking in machineForth and colorForth.

>> Given the lack of common practice these days, I expect that that ship
>> has sailed, but if you feel like it, you can try proposing support for
>> that feature.
>
>Does the Pope ask Catholics to send him proposals?

Why should that be relevant?

Hans Bezemer

unread,
Jun 6, 2022, 7:56:14 AM6/6/22
to
On Monday, June 6, 2022 at 7:09:02 AM UTC+2, dxforth wrote:
> > The use of "shall" is normal standards language. Forth-94 usually
> > uses a different style: It describes how a standard system behaves
> > when processing a standard program; and basically anything outside
> > this description is non-standard: If a program relies on a behaviour
> > that's not guaranteed in the standard, it is non-standard; and if a
> > system behaves in a way incompatible with the requirements of the
> > standard, it is non-standard. I usually prefer that style.
> "shall" is a commandment. It presumes authority and its acceptance.
> On whose authority did ANS take away the ability of users to manipulate
> the return stack? Not Moore's AFAIK.

Instead of bickering - which has been a useless exercise to me since my
early youth - maybe the standards people should know their RFCs. In particular
RFC 2119 (https://datatracker.ietf.org/doc/html/rfc2119), which says:

1. MUST This word, or the terms "REQUIRED" or "SHALL", mean that the
definition is an absolute requirement of the specification.

3. SHOULD This word, or the adjective "RECOMMENDED", mean that there
may exist valid reasons in particular circumstances to ignore a
particular item, but the full implications must be understood and
carefully weighed before choosing a different course.

5. MAY This word, or the adjective "OPTIONAL", mean that an item is
truly optional. One vendor may choose to include the item because a
particular marketplace requires it or because the vendor feels that
it enhances the product while another vendor may omit the same item.
An implementation which does not include a particular option MUST be
prepared to interoperate with another implementation which does
include the option, though perhaps with reduced functionality. In the
same vein an implementation which does include a particular option
MUST be prepared to interoperate with another implementation which
does not include the option (except, of course, for the feature the
option provides.)

I consider these definitions to be solid enough to end this dispute.

Hans Bezemer

dxforth

unread,
Jun 6, 2022, 12:16:22 PM6/6/22
to
On 6/06/2022 19:34, Anton Ertl wrote:
> dxforth <dxf...@gmail.com> writes:
>>On 5/06/2022 18:05, Anton Ertl wrote:
>>> dxforth <dxf...@gmail.com> writes:
>>>>On 5/06/2022 01:31, Anton Ertl wrote:
>>>>> ...
>>>>> If you mean the Forth standards (Forth-79 etc.), I don't know if they
>>>>> ever were standard, but they certainly were not in Forth-94. Already
>>>>> before Forth-94 there were systems such as F-PC where these techniques
>>>>> didn't work (at least not with "r> drop exit" to return to the
>>>>> caller's caller), and it does not work in general in SwiftForth
>>>>> (thanks to tail-call optimization) and in VFX (thanks to inlining):
>>>>
>>>>Did they try?
>>>
>>> Did who try?
>>
>>The authors of those implementations. The reason for something not
>>working can be as simple as it was never considered.
>
> It's possible that no customer ever reported problems with
> return-address-manipulating code, and therefore the implementors never
> considered the issue; in that case one might conclude that this
> feature is used so rarely that it would be a bad idea to slow down
> their systems in order to support that feature.

Issue was known, discussed and solutions proposed in forums between at
least 1992 and 1994:

https://groups.google.com/g/comp.lang.forth/c/jPXS2dq5KY0/m/jTX2v2ejSCoJ

https://groups.google.com/g/comp.lang.forth/c/CvTCOEGuQ50/m/phmHu3SV2DAJ

Apparently Mitch Bradley didn't like it because he saw it as encroaching
on his CATCH/THROW.

> However, I believe that they have had reports from customers, have
> considered it, and decided that inlining or tail-call optimization are
> enabled by default, and they give the programmers ways to disable
> these optimizations, so the programmers can get return-address
> manipulations to work (at a cost in performance).

I'm puzzled by the claim an optimizing compiler wouldn't know how to
handle a colon definition with UNNEST or ABANDON in it. When I write a
discrete definition I expect the compiler to respect that and optimize
only when safe to do so. The presence of those words should signal
don't inline this definition.

>>> Given the lack of common practice these days, I expect that that ship
>>> has sailed, but if you feel like it, you can try proposing support for
>>> that feature.

It's left out of ANS and there's an injunction to not remove from the
return stack what the program didn't put there. If such practice isn't
common today that's entirely on ANS and 200x.

>>
>>Does the Pope ask Catholics to send him proposals?
>
> Why should that be relevant?

Same top-down business model?

Hans Bezemer

unread,
Jun 6, 2022, 1:37:18 PM6/6/22
to
On Monday, June 6, 2022 at 6:16:22 PM UTC+2, dxforth wrote:
> I'm puzzled by the claim an optimizing compiler wouldn't know how to
> handle a colon definition with UNNEST or ABANDON in it. When I write a
> discrete definition I expect the compiler to respect that and optimize
> only when safe to do so. The presence of those words should signal
> don't inline this definition.
I'm not. Take this example:

: TEST ( bla bla) ABANDON ( more bla bla) ;

I can't speak for ALL implementations, but 4tH would compile this
as RDROP. And so far - all is well. The 4tH optimizer is capable of setting
a hold on "back optimization" - which means everything BEFORE RDROP
is no longer available for optimization.

The problem however, is further down the line. If the last word before
";" is a user defined word, it will get tail call optimization - unless ";" is
explicitly flagged by [FORCE] not to be optimized.

So unless I set a flag or something, the compiler is blissfully unaware
that ABANDON has been executed.

Hans Bezemer

Anton Ertl

unread,
Jun 6, 2022, 1:54:06 PM6/6/22
to
dxforth <dxf...@gmail.com> writes:
>On 6/06/2022 19:34, Anton Ertl wrote:
>> However, I believe that they have had reports from customers, have
>> considered it, and decided that inlining or tail-call optimization are
>> enabled by default, and they give the programmers ways to disable
>> these optimizations, so the programmers can get return-address
>> manipulations to work (at a cost in performance).
>
>I'm puzzled by the claim an optimizing compiler wouldn't know how to
>handle a colon definition with UNNEST or ABANDON in it.

I checked gforth, iForth, lxf, sf, vfxlin, and none of them has a word
called ABANDON. Only gforth-0.7.3 has a word called UNNEST; it's a
variable and probably not what you have in mind.

They are also not in fig-Forth, Forth-79, or Forth-83. Why should any
Forth system, optimizing or not, know how to handle UNNEST or ABANDON?

As for stuff like using R>, >R, etc. for return address manipulation,
Bernd Paysan has discussed how a sophisticated compiler might not just
notice this and disable inlining or tail-call optimization in that
case, but he also discussed how an even more sophisticated compiler
could optimize this by performing data-flow analysis of the return
address, and then inlining and rearranging the code. However, do we
really want to spend that much effort on this idiom?

In the meantime, even for unsophisticated compilers like gforth-itc,
return address manipulation interacts badly with locals.

: foo r> drop ;
: bar 1 {: a :} foo ;
: flip 2 {: b :} bar b . ;
flip

On current gforth-itc this results in:

flip 1
*the terminal*:4:5: error: Invalid memory address
flip>>><<<

On SwiftForth this seems to result in an endless loop, with a
Segmentation fault on Ctrl-C.

On vfxlin this results in a segmentation violation right away.

Even if I disable inlining and optimizations in VFX with

unoptimised
no inlining
-sin

I get the same result. BTW, in looking for these settings, I found a
section "return stack modifiers" in the VFX manual, which demonstrates
that return-address manipulation has been considered in VFX.

You may prefer return-address manipulation over locals, but these
systems implement locals, and return-address manipulation does not
work well with that, even without optimization.

>When I write a
>discrete definition I expect the compiler to respect that and optimize
>only when safe to do so. The presence of those words should signal
>don't inline this definition.

They certainly won't inline the definition, because the presence of
these words results in a compilation error.

In the example above, disabling the optimizations does not change
anything in VFX.

However, for the earlier example:

: foo r> drop ;
: bar foo ;
: flip bar ." end flip." ;
: flop flip ." end flop." ;
flop

Disabling optimizations in VFX as shown above results in the same
output as that produced by Gforth.

As for the idea that the optimizer should not change the behaviour, we
have three different goals:

same behaviour as without optimization
performance
compiler simplicity

Choose any two. In general I value the first goal higher, but in the
present case return-address manipulation is something that breaks in
the presence of locals even without optimization. So for a compiler
that implements locals, is the first goal really worth abandoning one
of the others for?

>>>> Given the lack of common practice these days, I expect that that ship
>>>> has sailed, but if you feel like it, you can try proposing support for
>>>> that feature.
>
>It's left out of ANS and there's an injunction to not remove from the
>return stack what the program didn't put there. If such practice isn't
>common today that's entirely on ANS and 200x.

You assign much more power to the standard than it has. E.g.,
Forth-94 specified that standard programs shall not rely on 1 chars =
1, nor on 2s-complement arithmetic, and yet these continued to be
common practice, and eventually were standardized.

If return-address manipulation had been similarly common practice
before Forth-94, we would not have seen F-PC, which broke the common
idioms for that; we would still have seen cmForth, which broke that;
and after Forth-94, systems implementors would not have dared to break
it, and we probably would have standardized it by now.

>>>Does the Pope ask Catholics to send him proposals?
>>
>> Why should that be relevant?
>
>Same top-down business model?

I don't know about the "business model" of the Catholic church, but
for Forth-200x the process is that anyone can make a proposal, and
that things that are not proposed by anyone are not standardized.
Things that are proposed may or may not be standardized. I have given
you my guess about the chances of a proposal for return-address
manipulation being accepted, but my guesses have been wrong before.

Marcel Hendrix

unread,
Jun 6, 2022, 8:05:50 PM6/6/22
to
On Monday, June 6, 2022 at 7:54:06 PM UTC+2, Anton Ertl wrote:
> dxforth <dxf...@gmail.com> writes:
> >On 6/06/2022 19:34, Anton Ertl wrote:
[..]
> In the meantime, even for unsophisticated compilers like gforth-itc,
> return address manipulation interacts badly with locals.
>
> : foo r> drop ;
> : bar 1 {: a :} foo ;
> : flip 2 {: b :} bar b . ;
> flip
>
> On current gforth-itc this results in:
>
> flip 1
> *the terminal*:4:5: error: Invalid memory address
> flip>>><<<

The same on iForth, minus the exception:

FORTH> : foo r> drop [ -opt ] ; ok
FORTH> : bar 1 local a foo [ -opt ] ; ok
FORTH> : flip 2 local b bar b . ; ok
FORTH> flip 1 ok

I had to disassemble flip to see what it does and why it prints 1
and not 2. The code also shows that the locals stack depth is increased
( b is not removed ).

FORTH> see flip
Flags: ANSI
$0133DCC0 : flip
$0133DCCA mov rbx, 2 d#
$0133DCD1 lea rsi, [rsi #-16 +] qword
$0133DCD5 mov [rsi] qword, rbx
$0133DCD8 lea rbp, [rbp -8 +] qword
$0133DCDC mov [rbp 0 +] qword, $0133DCE9 d#
$0133DCE4 jmp bar+10 ( $0133DC8A ) offset NEAR
$0133DCE9 push [rsi] qword
$0133DCEB lea rbp, [rbp -8 +] qword
$0133DCEF mov [rbp 0 +] qword, $0133DCFC d#
$0133DCF7 jmp .+10 ( $0124A102 ) offset NEAR
$0133DCFC add rsi, #16 b#
$0133DD00 ;
FORTH> see bar
Flags: ANSI
$0133DC80 : bar
$0133DC8A mov rbx, 1 d#
$0133DC91 lea rsi, [rsi #-16 +] qword
$0133DC95 mov [rsi] qword, rbx
$0133DC98 lea rbp, [rbp -8 +] qword
$0133DC9C mov [rbp 0 +] qword, $0133DCA9 d#
$0133DCA4 jmp foo+10 ( $0133DC4A ) offset NEAR
$0133DCA9 add rsi, #16 b#
$0133DCAD ;
FORTH> see foo
Flags: ANSI
$0133DC40 : foo
$0133DC4A mov rbx, [rbp 0 +] qword
$0133DC4E lea rbp, [rbp 8 +] qword
$0133DC52 ;

-marcel

David Jones

unread,
Jun 6, 2022, 8:58:11 PM6/6/22
to
On Tuesday, May 31, 2022 at 10:36:46 AM UTC-7, the.bee...@gmail.com wrote:
> On Tuesday, May 31, 2022 at 7:06:16 PM UTC+2, Hans Bezemer wrote:
> > On Tuesday, May 31, 2022 at 6:35:56 PM UTC+2, Zbig wrote:
> > > 01:45 -- but you know: Forth's stack works on the rule „last in — first out”, not „first in, first out”. Or am I wrong?
> > No, you're not. I pulled it and I'm uploading an updated version.
> Second attempt: https://www.youtube.com/watch?v=pnOhRPgFZDI
>
> Hans Bezemer
I am a New-Comer To Forth. I found your Video Very Clear, Thanks. Also, the Return Stack Animation was "Obvious After Watching"!"

dxforth

unread,
Jun 6, 2022, 10:52:39 PM6/6/22
to
Compiled you mean? I can get by with having UNNEST/ABANDON a synonym of
RDROP. Others may need to make it immediate, set a header flag, whatever.
If it were an ANS/200x mandate folks would find a way. Currently they
have the option to implement or not according to their convictions. It
doesn't get better than that.

dxforth

unread,
Jun 7, 2022, 12:01:43 AM6/7/22
to
On 7/06/2022 10:05, Marcel Hendrix wrote:
> On Monday, June 6, 2022 at 7:54:06 PM UTC+2, Anton Ertl wrote:
>> dxforth <dxf...@gmail.com> writes:
>> >On 6/06/2022 19:34, Anton Ertl wrote:
> [..]
>> In the meantime, even for unsophisticated compilers like gforth-itc,
>> return address manipulation interacts badly with locals.
>>
>> : foo r> drop ;
>> : bar 1 {: a :} foo ;
>> : flip 2 {: b :} bar b . ;
>> flip
>>
>> On current gforth-itc this results in:
>>
>> flip 1
>> *the terminal*:4:5: error: Invalid memory address
>> flip>>><<<
>
> The same on iForth, minus the exception:
>
> FORTH> : foo r> drop [ -opt ] ; ok
> FORTH> : bar 1 local a foo [ -opt ] ; ok
> FORTH> : flip 2 local b bar b . ; ok
> FORTH> flip 1 ok

Moore gave his opinion on locals:

"I remain adamant that local variables are not only useless, they are
harmful."

Thanks for the demonstration.

S Jack

unread,
Jun 7, 2022, 11:02:01 AM6/7/22
to

: foo r> drop ;
: bar foo ." END BAR. " ;
: flip bar ." end flip. " ;
: flop flip ." end flop. " ;
i. flop ==> end flip. end flop.

0 val a
: foo r> drop ;
: bar 0= if foo fi 666 to a ;
: flip 2 to a bar a . ;
i. true flip ==> 666
i. false flip ==> 2
--
me

S Jack

unread,
Jun 7, 2022, 11:20:20 AM6/7/22
to
: foo r> drop ;
: boo ." BOO " ;
: hoo ." HOO " ;
{ ' foo ' hoo ' boo begin enter again }
i. e ==> BOO HOO

--
me
0 new messages