Discussion:
Having fun with embedded Forth in BASIC
Add Reply
Hans Bezemer
2025-05-03 16:01:56 UTC
Reply
Permalink
I watched this ZX Spectrum episode on YT:


Forth inside a BASIC program? Sounds cool. And then it dawned upon me.
Mu uBasic/4tH interpreter has a native data stack - with commands like
PUSH, POP() and TOS(). And it doubles as a parameter stack - meaning,
whatever you put on the parameter stack is visible in the data stack.

So I tried to amuse myself a bit with that idea today. I came up with a
150 lines uBasic/4tH program that transpiles a Basic program with
embedded Forth into a true Basic program - like this:

$ ubasic ubforth.bas forth.bas basic.bas
Transpiling "forth.bas" to "basic.bas" <<
19 lines processed. Done.

0 OK, 0:5520

This is the source:

Print "This is still Basic"

forth{
: square dup * ;

: printstr
begin
dup len
while
dup 0 peek emit 1 chop
repeat cr ." The length is " len . ." now!" cr
;

." The square of 25 =" 25 square 2 spaces . cr cr
s" This is the end!" printstr cr
}basic

Print "This was Forth"
Print FN(_square(16))

BTW, it is a dialect of course - but it is understandable for most of
you, I guess. And this the generated Basic code (abridged):

Print "This is still Basic"


If 0 Then
_square
Proc _dup : Proc _mul
Return
EndIf

If 0 Then
_printstr

Do
Proc _dup : Proc _len

While Pop()
Proc _dup : Push 0 : Proc _peek : Proc _emit : Push 1 : Proc _chop

Loop
Proc _cr : Print "The length is "; : Proc _len : Proc _dot : Print
"now!"; : Proc _cr

Return
EndIf
Print "The square of 25 ="; : Push 25 : Proc _square : Push 2 : Proc
_spaces : Proc _dot : Proc _cr : Proc _cr
Push "This is the end!" : Proc _printstr : Proc _cr

Print "This was Forth"
Print FN(_square(16))

End

Now - you won't hear me say this thing is serious, but it is actually fun.

Hans Bezemer
Hans Bezemer
2025-05-04 11:12:19 UTC
Reply
Permalink
On 03-05-2025 18:01, Hans Bezemer wrote:
Okay, I'm done. I extended the vocabulary considerably, so now comments
are supported, ticking definitions, values are supported (TO, +TO),
strings, BEGIN..WHILE..UNTIL|REPEAT, IF..ELSE..THEN, EXECUTE.

Notoriously missing are (still) DO..LOOP. I also decided to "hide" the
code in REM statements, first: because those strange contraptions in the
'80-ies did the same and second, you can still execute the program as is.

Little example:

If Line(_Guesses) = 0 Then Print "Please transpile this program first
using \quBForth.bas\q." : End

Push Rnd(10)+1

Rem Forth
Rem : Guesses ( secret# #times --)
Rem ." Guess a number between 1 and 10" cr
Rem begin \ repeat n times
Rem dup \ check number of guesses
Rem while \ when ok
Rem over
Rem uBasic

Input a : Push a

Rem Forth
Rem over over = if ." Guessed!" cr abort then
Rem ." Too " > if ." small" else ." big" then cr 1 -
Rem repeat \ if guessed, exit
Rem drop ." You didn't guess it, it was " . cr
Rem ; \ error if not guessed
Rem uBasic

Proc _Guesses(3)

I'll see about this DO..LOOP ;-)

Hans Bezemer
Hans Bezemer
2025-05-05 12:25:09 UTC
Reply
Permalink
Post by Hans Bezemer
I'll see about this DO..LOOP ;-)
Well, it's got DO..LOOPs. And LEAVE. And I. And THROW and CATCH. But
still no Return Stack, sorry.

Hans Bezemer

https://sourceforge.net/p/forth-4th/code/HEAD/tree/trunk/4th.src/apps/basic/ubforth.bas
Hans Bezemer
2025-05-10 13:51:10 UTC
Reply
Permalink
Post by Hans Bezemer
http://youtu.be/xyfXxW8UuBM
OKAY!!

I'm done now with my own transpiler, so I took a closer look at my
inspiration, the Currah uSource for the ZX Sinclair Spectrum.

The original hardware is almost impossible to find - but even
virtualized it's not easy to find either. It is supported by the FUSE ZX
Spectrum emulator, but finding the ROM is hard - I'd say: nigh impossible.

Now - The original thing cost initially about £50 - but I wouldn't want
to spend that if you'd buy it for the Forth. First of all - this thing
is *SLOW* - the Jupiter Ace is at least ten times faster. Even the Z80
Spectrum Forths are six times faster (even the "type-in"s).

For benchmarking I use the Fignition benchmark, which is a 8-bit hobby
board that runs Forth. Compare it with a Raspberry.

Jup-Ace FIGn
(fast) FIGn 0.9.8 uBFrt uSrc
------- ----- ----- ----- -----
BM1 0.16 0.02 0.014 0.051 1.4
BM2 0.54 0.09 0.056 0.715 28.3
BM3 7.66 0.41 0.286 3.162 128.0
BM4 6.46 0.47 0.298 1.995 97.0
BM5 6.52 0.51 0.326 2.183 98.0
BM6 7.38 0.64 0.402 2.699 119.0
BM7 12.98 1.27 0.836 3.773 183.0

The benchmarks are increasingly demanding. I'll give the first and the
last one to give you an idea:

10 LET forth=1
20 REM # % BM1
30 REM # CR 83 EMIT
40 REM # 10000 0 DO LOOP
50 REM # 69 EMIT
60 REM # ;
...
190 REM # % BM7
200 REM # CR 83 EMIT 0 BEGIN
210 REM # 1+ DUP 2 / 3 * 4 + 5 -
220 REM # DROP BM5SUB
230 REM # 5 0 DO DUP PUT m LOOP ( 5 0 DO DUP DROP LOOP)
240 REM # DUP 9999 > UNTIL
250 REM # 69 EMIT DROP
260 REM # ;

Yes, you're not dreaming - definitions have to start with a %, not a
colon. That is because the colon sets the ZX Spectrum editor in "keyword
mode". Now - that may be true for a "hash" prefix, but it isn't true for
a "double quote".

Tou see, in order to be recognized as a Forth line, it not only has to
be embedded in a REM statement - it also has to be prefixed (by a "#").
uSource also supports embedded assembler, but this has to be prefixed by
a bang ("!").

The Forth vocabulary doesn't adhere to any standard - not even Forth-79.
But the most infuriating thing is how it is implemented:

* Several words cannot be used within definitions. Like ." - which
restricts the language to sheer processing tasks;

* External (ZX BASIC) variables can only be accessed by GET, PUT and
COPY keywords, that only seem to work *outside* definitions;

* There is no CHAR, CREATE, DOES> or EXECUTE. That makes extending the
language quite difficult;

* You cannot access ZX BASIC arrays or subroutines. It's also impossible
to call Forth routines directly from ZX Basic;

* Error messages are terse - and often beside the point.

In short, I consistently have the feeling you're "working around" all
the issues - Scott McNealy called it "mountain biking" - you're working
very hard, you're very proud of what you have accomplished, but in the
end you're at exactly the same place as you began.

So - given the restrictions, the (lack of) performance and the sheer
frustration of working with it, I can't recommend it. It feels this part
of the device has gotten very little love - in comparison with the
assembler and disassembler functionality.

It's also infuriating the manual sneaks past these restrictions - by
largely not mentioning them. I don't know that much about paging ROMs,
but I think one could have done better.

Hans Bezemer
Hans Bezemer
2025-05-13 10:36:21 UTC
Reply
Permalink
On 10-05-2025 15:51, Hans Bezemer wrote:
*I think we got something here!*
For a vid of my own I'm doing research on a device once called "Currah
uSource". It was never a great success, few were sold - and nowadays
those things are for sale on the net for ridiculous prices (up to $400
for a ₤50 device when it was new). Now, these things are horrible and
bug ridden, but that's not the point here. I wanted also to know how
they performed. You see, the uSource came with a built-in Forth
compiler. So what do you do when you got a Forth compiler? You test it
against other Forth compilers. And this is it (all in seconds):

Aber David
uBFrt uSrc soft vFrth Mill.
----- ----- ----- ----- -----
BM1 0.051 1.4 1.05 1.2 0.5
BM2 0.715 28.3 5.07 3.5 4.0
BM3 3.162 128.0 53.81 49.5 15.2
BM4 1.995 97.0 53.20 49.7 20.0
BM5 2.183 98.0 54.20 50.6 20.1
BM6 2.699 119.0 60.20 57.8 23.7
BM7 3.773 220.0 65.21 62.9 31.0

This is the "Fignition" benchmark - which is an 8-bit Raspberry Pi with
a Forth OS. So this one is excellent for 8-bit computers. These are the
candidates:
1. *uBForth* = my "alternative" to the uSource philosophy - but runs on
an i5 (hors competition);
2. *uSource* = the ZX Spectrum device to be tested;
3. *Abersoft Forth* = one of the best sold, best supported and most
popular Forth compilers on the Spec in the day;
4. *vForth* = a modern Forth for the ZX Spectrum - one of the most
efficient and user friendly ZX Spectrum Forth compilers I've ever seen;
5. *David Millington* = *THIS IS A 1980-ies TYPE-IN!* The parser is very
bad, but the performance is obviously stunning.

Now, the timings on the Spectrum were done using the Speccies internal
clock by defining:
*: .timer 23672 dup @ swap 2+ c@ ;* putting the definition at the start
and end of every routine. The resolution is 1/50th of a second -
subtract those (double) numbers and divide by 5 to get 1/10th of a
second measurements.

This means that Davids compiler manages to outclass some of the best ZX
Spectrum compilers _EVER._ I briefly measured BM7 on ZX Forth (Artic)
and got a 30s-ish result as well - but I'm not doing that again, because
it's a horror to work with. As for the uSource, like the literature
says, it's about three times as fast as ZX BASIC - and that's it. Worse,
what do you think *31416 3 /* would result to? The uSource claims it's
zero. That's how bad it is..
Paul Rubin
2025-05-13 18:55:45 UTC
Reply
Permalink
Post by Hans Bezemer
This is the "Fignition" benchmark - which is an 8-bit Raspberry Pi
with a Forth OS. So this one is excellent for 8-bit computers.
Did you mean 8 bit Arduino? It was apparently built around an
Atmega168. It was apparently slower than the Z80 boards of earlier
times because of its use of external ram and flash.

http://www.doctormonk.com/2012/02/fignition-30-single-board-computer-with.html

https://sites.google.com/site/libby8dev/fignition/performance
Hans Bezemer
2025-05-13 21:22:48 UTC
Reply
Permalink
Post by Paul Rubin
Post by Hans Bezemer
This is the "Fignition" benchmark - which is an 8-bit Raspberry Pi
with a Forth OS. So this one is excellent for 8-bit computers.
Did you mean 8 bit Arduino? It was apparently built around an
Atmega168. It was apparently slower than the Z80 boards of earlier
times because of its use of external ram and flash.
http://www.doctormonk.com/2012/02/fignition-30-single-board-computer-with.html
https://sites.google.com/site/libby8dev/fignition/performance
Well, yeah, that's the Fignition. Can't say too much about the H/W. I
don't have one. But it's fun getting the S/W to run on completely
different platforms. I once ripped a few graphics demos - which produced
these tiny little graphs! Cute!

Hans Bezemer

Loading...