Discussion:
Unable to write to file on GFORTH.
(too old to reply)
SpainHackForth
2023-02-13 02:36:34 UTC
Permalink
Hi all,

I have been trying to create a simple compiler for a VM I wrote. I'm writing the compiler on GForth.
Here is the offending code.

https://gist.github.com/jemo07/18a47eeb0bd99ffb350bd78002d8498e

For what ever reason, I can't seem to figure out how to write data off the stack onto a file. Maybe that is my problem, that I need to extract the data first ? Just kind of thinking out loud as I'm off the bed.

Here is how I was attempting to use the code:
s" bytecode.out" open-output ok
10 vm_c,lit ,, ok
20 vm_c,lit ,, ok
VM_UM_PLUS vm_c, , ok
fd-out write-line throw
:454: File I/O exception

Then:

s" bytecode.out" open-output ok
10 vm_c,lit ,, ok
20 vm_c,lit ,, ok
VM_UM_PLUS vm_c, , ok
fd-out write-line ok
fd-out write-file
:460: Invalid memory address
fd-out >>>write-file<<<
Backtrace:

You can see here what looks like success but the file, although create, it's always empty.

s" bytecode.out" open-output ok
10 vm_c,lit ,, ok
20 vm_c,lit ,, ok
VM_UM_PLUS vm_c, , ok
fd-out write-file ok
fd-out close-file throw ok

Please advice on what am I getting wrong.

Thanks,
Brian Fox
2023-02-13 03:16:15 UTC
Permalink
Post by SpainHackForth
Hi all,
I have been trying to create a simple compiler for a VM I wrote. I'm writing the compiler on GForth.
Here is the offending code.
https://gist.github.com/jemo07/18a47eeb0bd99ffb350bd78002d8498e
For what ever reason, I can't seem to figure out how to write data off the stack onto a file. Maybe that is my problem, that I need to extract the data first ? Just kind of thinking out loud as I'm off the bed.
s" bytecode.out" open-output ok
10 vm_c,lit ,, ok
20 vm_c,lit ,, ok
VM_UM_PLUS vm_c, , ok
fd-out write-line throw
:454: File I/O exception
s" bytecode.out" open-output ok
10 vm_c,lit ,, ok
20 vm_c,lit ,, ok
VM_UM_PLUS vm_c, , ok
fd-out write-line ok
fd-out write-file
:460: Invalid memory address
fd-out >>>write-file<<<
You can see here what looks like success but the file, although create, it's always empty.
s" bytecode.out" open-output ok
10 vm_c,lit ,, ok
20 vm_c,lit ,, ok
VM_UM_PLUS vm_c, , ok
fd-out write-file ok
fd-out close-file throw ok
Please advice on what am I getting wrong.
Thanks,
write-line has this stack diagram:

WRITE-LINE ( c-addr u fileid -- ior )
So you don't write the data from the stack per se.
The data needs to be in memory somewhere and you provide the address and the size.

Same with write-file
WRITE-FILE ( c-addr u fileid -- ior )

So something like this might work assuming FD-OUT is the file handle.
(don't use Gforth much)

HERE 10 vm_c, lit , , ( -- caddr )
HERE OVER - ( -- caddr u )
FD-OUT WRITE-LINE ( -- ior)
dxforth
2023-02-13 05:45:19 UTC
Permalink
Post by Brian Fox
Post by SpainHackForth
Hi all,
I have been trying to create a simple compiler for a VM I wrote. I'm writing the compiler on GForth.
Here is the offending code.
https://gist.github.com/jemo07/18a47eeb0bd99ffb350bd78002d8498e
For what ever reason, I can't seem to figure out how to write data off the stack onto a file. Maybe that is my problem, that I need to extract the data first ? Just kind of thinking out loud as I'm off the bed.
s" bytecode.out" open-output ok
10 vm_c,lit ,, ok
20 vm_c,lit ,, ok
VM_UM_PLUS vm_c, , ok
fd-out write-line throw
:454: File I/O exception
s" bytecode.out" open-output ok
10 vm_c,lit ,, ok
20 vm_c,lit ,, ok
VM_UM_PLUS vm_c, , ok
fd-out write-line ok
fd-out write-file
:460: Invalid memory address
fd-out >>>write-file<<<
You can see here what looks like success but the file, although create, it's always empty.
s" bytecode.out" open-output ok
10 vm_c,lit ,, ok
20 vm_c,lit ,, ok
VM_UM_PLUS vm_c, , ok
fd-out write-file ok
fd-out close-file throw ok
Please advice on what am I getting wrong.
Thanks,
WRITE-LINE ( c-addr u fileid -- ior )
So you don't write the data from the stack per se.
The data needs to be in memory somewhere and you provide the address and the size.
Same with write-file
WRITE-FILE ( c-addr u fileid -- ior )
So something like this might work assuming FD-OUT is the file handle.
(don't use Gforth much)
HERE 10 vm_c, lit , , ( -- caddr )
HERE OVER - ( -- caddr u )
FD-OUT WRITE-LINE ( -- ior)
More forths should support READ-CHAR WRITE-CHAR. It's a logical extension when
working at the byte/char level.
Ron AARON
2023-02-13 13:33:39 UTC
Permalink
Post by Brian Fox
WRITE-LINE ( c-addr u fileid -- ior )
So you don't write the data from the stack per se.
The data needs to be in memory somewhere and you provide the address and the size.
Same with write-file
WRITE-FILE ( c-addr u fileid -- ior )
So something like this might work assuming FD-OUT is the file handle.
(don't use Gforth much)
HERE 10 vm_c, lit ,  ,  ( -- caddr )
HERE OVER -              ( -- caddr u )
FD-OUT WRITE-LINE ( -- ior)
More forths should support READ-CHAR WRITE-CHAR.  It's a logical
extension when
working at the byte/char level.
8th has f:getc which returns one Unicode character from the file. No
equivalnt f:putc exists.
Anton Ertl
2023-02-13 06:41:35 UTC
Permalink
Post by SpainHackForth
You can see here what looks like success but the file, although create, it's always empty.
s" bytecode.out" open-output ok
10 vm_c,lit ,, ok
20 vm_c,lit ,, ok
VM_UM_PLUS vm_c, , ok
fd-out write-file ok
fd-out close-file throw ok
Please advice on what am I getting wrong.
There are a number of words here that don't come from Gforth:
OPEN-OUTPUT VM_C,LIT ,, VM_C, FD-OUT

OPEN-OUTPUT and FD-OUT may have the same definitions as in
<https://gforth.org/manual/Files-Tutorial.html#Create-file-for-output>.
I can only guess what the others do. From the layout my guess would
be that you don't pass the c-addr and u parameters to WRITE-FILE, but
that will normally result in a stack underflow.

Anyway, here's an example of writing some bytes:

0 Value fd-out
: open-output ( addr u -- ) w/o create-file throw to fd-out ;
s" test.out" open-output
create foo
'a' c,
'b' c,
'c' c,
foo here over - fd-out write-file throw
fd-out close-file throw

- 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: https://euro.theforth.net
SpainHackForth
2023-02-13 10:32:05 UTC
Permalink
Post by Anton Ertl
OPEN-OUTPUT and FD-OUT may have the same definitions as in
<https://gforth.org/manual/Files-Tutorial.html#Create-file-for-output>.
I can only guess what the others do. From the layout my guess would
be that you don't pass the c-addr and u parameters to WRITE-FILE, but
that will normally result in a stack underflow.
0 Value fd-out
: open-output ( addr u -- ) w/o create-file throw to fd-out ;
s" test.out" open-output
create foo
'a' c,
'b' c,
'c' c,
foo here over - fd-out write-file throw
fd-out close-file throw
- anton
Thank you for taking the time Anton et all!
Here are the definitions for the words that I'm using.
: open-output ( addr u -- ) w/o create-file throw to fd-out ;
\ Compile a word into the bytecode
: vm_c, ( n -- ) [char] , emit ;
\ Compile a word and its argument into the bytecode
: vm_c,lit ( n -- ) vm_c, vm_c, n ;

Here is an interactive view of the outputs:

Gforth 0.7.3, Copyright (C) 1995-2008 Free Software Foundation, Inc.
Gforth comes with ABSOLUTELY NO WARRANTY; for details type `license'
Type `bye' to exit
ok
ok
s" bytecode.out" open-output ok
.S <0> ok
create foo ok
.S <0> ok
10 vm_c,lit ,, ok
.S <2> 10 140542381430136 ok
foo here over - fd-out write-file throw ok
.S <2> 10 140542381430136 ok
fd-out close-file throw ok
.S <2> 10 140542381430136 ok

As you can see, the stack does not get consumed. Note that the double-commas are not a word I'm typing, as in 10 vm_c,lit and after I hit enter the ' ,, ' appear.
SpainHackForth
2023-02-13 13:39:35 UTC
Permalink
Post by Anton Ertl
OPEN-OUTPUT and FD-OUT may have the same definitions as in
<https://gforth.org/manual/Files-Tutorial.html#Create-file-for-output>.
I can only guess what the others do. From the layout my guess would
be that you don't pass the c-addr and u parameters to WRITE-FILE, but
that will normally result in a stack underflow.
Ok so I'm getting closer, had to redefine some of the words,

vm_create ok
10 vm_c,lit ,l ok
.S <1> 10 ok
20 vm_c,lit ,l ok
.S <2> 10 20 ok
VM_UM_PLUS vm_c, , ok
.S <1> 30 ok
vm_write
:22: File I/O exception
Post by Anton Ertl
vm_write<<<
Backtrace:
$7FDC2D2A9E30 throw

here are the offending words.

\ Compile a word into the bytecode
: vm_c, ( n -- ) [char] , emit ;

\ Compile a word and its argument into the bytecode
: vm_c,lit ( n -- ) [char] , emit [char] lit emit ;

\ Output the generated bytecode to a file
: vm_create ( -- ) s" bytecode.out" open-output ;
: vm_write ( n -- )
fd-out here over -
fd-out write-file throw ;
: vm_close ( -- ) close-file throw ;

as you can see, somehow I'm not sending the data to the memory buffer, but rater executing the code on the machine.

I tried this but still no go:

create buffer redefined buffer ok
: write-to-buffer ( n -- ) buffer c, ; ok
: write-to-file ( -- ) buffer fd-out write-file throw ; ok
: vm_c,lit ( n -- ) [char] lit emit write-to-buffer ; redefined vm_c,lit ok
: vm_write ( -- ) write-to-file ; redefined vm_write ok

vm_write
:26: File I/O exception
Post by Anton Ertl
vm_write<<<
Backtrace:
$7F3CE486CEF0 throw
$7F3CE486CF90 write-to-file
dxforth
2023-02-13 16:03:20 UTC
Permalink
...
Here's a buffered output routine. PUTC writes a byte to file.
Remember to CLOSE-OUTPUT when done to flush the buffer.

-1 value FD-OUT

64 constant #OBUF
create OBUF #obuf allot
variable OSIZ variable OPTR

\ reset output buffer
: /OBUF ( -- ) obuf optr ! 0 osiz ! ;

: (flushwrite) ( -- ior )
obuf osiz @ fd-out write-file /obuf ;

: OPEN-OUTPUT ( adr len -- )
r/w create-file throw to fd-out /obuf ;

\ errors not reported
: CLOSE-OUTPUT ( -- )
fd-out dup 0> if (flushwrite) drop then
close-file drop -1 to fd-out ;

\ Write char to buffered output
: PUTC ( char -- )
osiz @ #obuf = if (flushwrite) throw then
optr @ c! 1 optr +! 1 osiz +! ;
SpainHackForth
2023-02-13 17:22:32 UTC
Permalink
...
Here's a buffered output routine. PUTC writes a byte to file.
Remember to CLOSE-OUTPUT when done to flush the buffer.
Fantastic FX,

Here is my implementation based on your code:

: VM_DROP DROP ;
: VM_EXIT bye ;
: VM_BRANCH branch ;
: VM_DUP dup ;
: VM_FETCH @ ;
: VM_LIT lit ;
: VM_R_FROM r> drop ;
: VM_R_FETCH r@ ;
: VM_TO_R >r ;
: VM_STORE ! ;
\ : VM_ENTER ['] ENTER ;
: VM_OVER over ;
: VM_AND and ;
: VM_ZERO_LESS 0= ;
: VM_SWAP swap ;
: VM_COND_BRANCH ( flag dest -- ) dup 0= if swap branch then drop ;
: VM_UM_PLUS + ;

64 constant #OutBuff
create OutBuff #OutBuff allot
variable BUFSIZE
variable BUFPOS
variable n
variable fd-out
0 Value fd-in
0 Value fd-out
\ Reset the output buffer
: /OutBuff ( -- ) OutBuff BUFPOS ! 0 BUFSIZE ! ;

\ Flush the buffer to the output file
: WFLUSH ( -- ior )
OutBuff BUFSIZE @ fd-out write-file /OutBuff ;

\ Open a file for writing
: OPEN-OUTPUT ( adr u -- ) r/w create-file throw to fd-out /OutBuff ;

\ Close the file, flushing the buffer if necessary
: CLOSE-OUTPUT ( -- )
fd-out dup 0> if WFLUSH drop then
close-file drop ;

\ Write a character to the buffer
: WBUF ( char -- )
BUFSIZE @ #OutBuff = if WFLUSH throw then
BUFPOS @ c! 1 BUFPOS +! 1 BUFSIZE +! ;

\ Compile a word into the bytecode
: vm_c, ( n -- ) WBUF ;

\ Compile a word and its argument into the bytecode
: vm_c,lit ( n -- ) [char] lit emit WBUF ;

\ Output the generated bytecode to a file
: vm_create ( -- ) s" bytecode.out" open-output ;
: vm_write ( n -- ) vm_c,lit n ;
: vm_close ( -- ) close-output ;

I can now see something is been written, but I now have to solve what that is, as the file is created and it grow, but a cat does not show the expected values. Cheers!
NN
2023-02-13 17:37:41 UTC
Permalink
Post by SpainHackForth
...
Here's a buffered output routine. PUTC writes a byte to file.
Remember to CLOSE-OUTPUT when done to flush the buffer.
Fantastic FX,
: VM_DROP DROP ;
: VM_EXIT bye ;
: VM_BRANCH branch ;
: VM_DUP dup ;
: VM_LIT lit ;
: VM_R_FROM r> drop ;
: VM_TO_R >r ;
: VM_STORE ! ;
\ : VM_ENTER ['] ENTER ;
: VM_OVER over ;
: VM_AND and ;
: VM_ZERO_LESS 0= ;
: VM_SWAP swap ;
: VM_COND_BRANCH ( flag dest -- ) dup 0= if swap branch then drop ;
: VM_UM_PLUS + ;
64 constant #OutBuff
create OutBuff #OutBuff allot
variable BUFSIZE
variable BUFPOS
variable n
variable fd-out
0 Value fd-in
0 Value fd-out
\ Reset the output buffer
: /OutBuff ( -- ) OutBuff BUFPOS ! 0 BUFSIZE ! ;
\ Flush the buffer to the output file
: WFLUSH ( -- ior )
\ Open a file for writing
: OPEN-OUTPUT ( adr u -- ) r/w create-file throw to fd-out /OutBuff ;
\ Close the file, flushing the buffer if necessary
: CLOSE-OUTPUT ( -- )
fd-out dup 0> if WFLUSH drop then
close-file drop ;
\ Write a character to the buffer
: WBUF ( char -- )
\ Compile a word into the bytecode
: vm_c, ( n -- ) WBUF ;
\ Compile a word and its argument into the bytecode
: vm_c,lit ( n -- ) [char] lit emit WBUF ;
\ Output the generated bytecode to a file
: vm_create ( -- ) s" bytecode.out" open-output ;
: vm_write ( n -- ) vm_c,lit n ;
: vm_close ( -- ) close-output ;
I can now see something is been written, but I now have to solve what that is, as the file is created and it grow, but a cat does not show the expected values. Cheers!
0 [if] --------------------------------------------------------

Here is a list of standard words used for manipulating files.
https://forth-standard.org/standard/file


[ Aside : Calling a buffer a buffer is a bad idea because its
also a word in the block wordset. ]

If you can write your byte-code into an area of memory , then you can write it out one one go.

Here is an example code

-------------------------------------------------------- [then]

require random.fs

( create a buffer )
create buf1 511 chars allot

: fillbuf ( -- )
511 0 do
64 random 32 + buf1 i + c!
loop

( insert cr to break up a long line)
( just for visual purposes and not needed )

511 0 do
10 buf1 i + c!
128 random +loop
;

: showdata ( -- )
buf1 511 type ;

fillbuf showdata

0 [if] --------------------------------------------------------


You will need :

(1) Filename
(2) File-id
(3) Buffer addr and length

Here is an example code

-------------------------------------------------------- [then]

0 value file-id

variable file-name 50 chars allot
s" testdata.txt" file-name place

: (wo) ( -- )
file-name count w/o create-file throw to file-id
buf1 511 file-id write-file throw
file-id close-file throw

0 to file-id ;

: wo ( -- )
['] (wo) catch if
cr ." <ERR> While writing"
else
cr ." No errors"
then ;

wo

cr cr cr

bye

0 [if] --------------------------------------------------------

You should be able to check file "testdata.txt" using notepad.

-------------------------------------------------------- [then]
SpainHackForth
2023-02-13 18:06:43 UTC
Permalink
Post by NN
[ Aside : Calling a buffer a buffer is a bad idea because its
also a word in the block wordset. ]
Hello NN,

I agree, I was trying something similar, but I could not get it to write to the file, but instead it wrote it to the stack.
dxforth
2023-02-14 00:46:20 UTC
Permalink
Post by SpainHackForth
: vm_write ( n -- ) vm_c,lit n ;
Action doesn't match stack comment. After taking value n from the
stack and writing it to file, it then takes the address of variable
n and places it on the stack. I doubt that's what you want.
SpainHackForth
2023-02-14 22:00:37 UTC
Permalink
Post by SpainHackForth
: vm_write ( n -- ) vm_c,lit n ;
Action doesn't match stack comment. After taking value n from the
stack and writing it to file, it then takes the address of variable
n and places it on the stack. I doubt that's what you want.
Hi DX,

correct, I was expecting to take the value of n and writing that to the file.
here was my original attempt to getting this done.

\ Compile a word into the bytecode
: vm_c, ( n -- ) [char] , emit ;

\ Compile a word and its argument into the bytecode
: vm_c,lit ( n -- ) [char] , emit [char] lit emit ;

My original idea was that while in Forth, I could create a DSL to write just the byte code.

I’m looking for a good tutorial on how to implement a cross compiler in gforth for my target system.

Here is a link for the VM, https://github.com/jemo07/fastVM-16bit

I’m in the middle of writing a minimal forth based on a 2009 discussion here on the minimal words needed to bootstrap a forth. Then add the rest of the words in forth. It will be slow, but I’m already working on another idea of a more robust VM. It will also have a circular stack, this way you can just take a value with a fetch 0/-x and access any value on the stack based on the TOS pointer.

here is what I have so far in C https://godbolt.org/z/MbfvfsP1c
Brian Fox
2023-02-15 14:52:39 UTC
Permalink
Post by SpainHackForth
I’m looking for a good tutorial on how to implement a cross compiler in gforth for my target system.
I wrote my own cross compiler some years back and I didn't find a paper that put it all together.
I had to pick my way through other peoples code to get some concepts in my head.
There are a lot of variations in how people do cross-compilers in Forth. No surprise there.

IMHO a Forth cross compiler begins with versions of dictionary management words and
a dictionary pointer variable that operate on a memory area separate from the main dictionary.

From what have seen here, you are trying to compile your target code into the HOST Forth
dictionary. That is probably a harder way to do it.

Consider this code that creates a way to manage a Target dictionary memory area in the same
way that Forth manages its dictionary. The names have a 'T' prefix as a reminder of their
purpose. They could be made to look normal with DEFER words or a "TARGET" wordlist
but that's not necessary if you don't mind this look.
---
\ Target versions of HERE and ALLOT
: THERE ( -- Taddr) TDP @ ; \ end of TARGET dictionary
: TALLOT ( n -- ) TDP +! ; \ allot bytes in the target dictionary

\ Target versions for storing
: TC! ( n Taddr --) THERE + C! ;
: T! ( n Taddr --) THERE + ! ;

\ Target versions for fetching
: TC@ ( n Taddr --) THERE + C@ ;
: T@ ( n Taddr --) THERE + @ ;

\ integer and byte "Target" compilers
: T, ( n -- ) THERE ! 2 TALLOT ; \ compile a cell in target
: TC, ( c -- ) THERE C! 1 TALLOT ; \ compile a byte in target

CREATE TARGET-SEG HEX FFFF ALLOT \ 64K bytes for target image

TARGET-SEG TDP ! \ dictionary now starts at TARGET-SEG memory
---

Once you have these word, you build a Forth style Assembler that uses
TARGET memory words. It's just bytes compiled into memory
after all. :-) This becomes your cross-assembler.

Once you have an Assembler, you can write your Forth compiler
in the assembler that your just wrote.

Once the program image is complete, just save the
entire image to a file. You may need to add a header depending on the usage.


Chuck Moore is reputed to have said something like:

"I now had an interpreter which could interpret assembler, which could
assemble a compiler, which could compile an interpreter."

GForth is your 1st level interpreter. You build the rest and your done.
---

Have fun!
Brian Fox
2023-02-15 16:08:06 UTC
Permalink
Post by Brian Fox
Post by SpainHackForth
I’m looking for a good tutorial on how to implement a cross compiler in gforth for my target system.
I wrote my own cross compiler some years back and I didn't find a paper that put it all together.
I had to pick my way through other peoples code to get some concepts in my head.
There are a lot of variations in how people do cross-compilers in Forth. No surprise there.
IMHO a Forth cross compiler begins with versions of dictionary management words and
a dictionary pointer variable that operate on a memory area separate from the main dictionary.
From what have seen here, you are trying to compile your target code into the HOST Forth
dictionary. That is probably a harder way to do it.
Consider this code that creates a way to manage a Target dictionary memory area in the same
way that Forth manages its dictionary. The names have a 'T' prefix as a reminder of their
purpose. They could be made to look normal with DEFER words or a "TARGET" wordlist
but that's not necessary if you don't mind this look.
---
\ Target versions of HERE and ALLOT
: TALLOT ( n -- ) TDP +! ; \ allot bytes in the target dictionary
\ Target versions for storing
: TC! ( n Taddr --) THERE + C! ;
: T! ( n Taddr --) THERE + ! ;
\ Target versions for fetching
\ integer and byte "Target" compilers
: T, ( n -- ) THERE ! 2 TALLOT ; \ compile a cell in target
: TC, ( c -- ) THERE C! 1 TALLOT ; \ compile a byte in target
CREATE TARGET-SEG HEX FFFF ALLOT \ 64K bytes for target image
TARGET-SEG TDP ! \ dictionary now starts at TARGET-SEG memory
---
Oops.
Of course one needs to define the TDP variable first.

VARIABLE TDP
SpainHackForth
2023-02-16 06:53:01 UTC
Permalink
Post by Brian Fox
Post by Brian Fox
Post by SpainHackForth
I’m looking for a good tutorial on how to implement a cross compiler in gforth for my target system.
I wrote my own cross compiler some years back and I didn't find a paper that put it all together.
I had to pick my way through other peoples code to get some concepts in my head.
There are a lot of variations in how people do cross-compilers in Forth. No surprise there.
IMHO a Forth cross compiler begins with versions of dictionary management words and
a dictionary pointer variable that operate on a memory area separate from the main dictionary.
From what have seen here, you are trying to compile your target code into the HOST Forth
dictionary. That is probably a harder way to do it.
Consider this code that creates a way to manage a Target dictionary memory area in the same
way that Forth manages its dictionary. The names have a 'T' prefix as a reminder of their
purpose. They could be made to look normal with DEFER words or a "TARGET" wordlist
but that's not necessary if you don't mind this look.
---
\ Target versions of HERE and ALLOT
: TALLOT ( n -- ) TDP +! ; \ allot bytes in the target dictionary
\ Target versions for storing
: TC! ( n Taddr --) THERE + C! ;
: T! ( n Taddr --) THERE + ! ;
\ Target versions for fetching
\ integer and byte "Target" compilers
: T, ( n -- ) THERE ! 2 TALLOT ; \ compile a cell in target
: TC, ( c -- ) THERE C! 1 TALLOT ; \ compile a byte in target
CREATE TARGET-SEG HEX FFFF ALLOT \ 64K bytes for target image
TARGET-SEG TDP ! \ dictionary now starts at TARGET-SEG memory
---
Oops.
Of course one needs to define the TDP variable first.
VARIABLE TDP
Thanks I will have a closer look.
none) (albert
2023-02-16 13:17:44 UTC
Permalink
Post by Brian Fox
I’m looking for a good tutorial on how to implement a cross compiler in gforth for my target system.
I wrote my own cross compiler some years back and I didn't find a paper that put it all together.
I had to pick my way through other peoples code to get some concepts in my head.
There are a lot of variations in how people do cross-compilers in Forth. No surprise there.
IMHO a Forth cross compiler begins with versions of dictionary
management words and a dictionary pointer variable that operate on a
memory area separate from the main dictionary.
I would think that you have to start with a decision whether to have an
indirect threaded, direct threaded or subroutine threaded Forth (or whatever)
The next decision is the layout of a dictionary entry. I'm an advocate
for an uniform data structure (same for constant's, variable's, create/does
words, defer words), but that can waste some memory (empty fields).
If it is non uniform, you have to carefully design how definitions are
chained and take care of any exceptions.
Then there is the important decision, actually imposed, if the Forth runs
stand alone or hosted on an operating system. In the latter case you can
choose for a single executable, or a kind of loader, that supposedly
yield some benefits for portability.
Then you have to choose registers for the Forth program counter,
data stack pointer, return stack pointer, and possibly more, e.g.
floating point stack, locals stack.
A decision that affects all code definitions is whether you hold
the top of stack in memory in a register.

Designing a Forth from scratch can be a waste of time.
E.g. for ciforth I started from fig-Forth and there was minor
mile stones, e.g. 32 bits, ANSI-compatible.
The regression test was valid through a few thousands of iterations.

<SNIP>
Post by Brian Fox
Have fun!
Agreed!
Having you own Forth is fun, but never forget to do something useful
with it. Otherwise you end up adding futile enhancements.

Groetjes Albert
--
Don't praise the day before the evening. One swallow doesn't make spring.
You must not say "hey" before you have crossed the bridge. Don't sell the
hide of the bear until you shot it. Better one bird in the hand than ten in
the air. First gain is a cat spinning. - the Wise from Antrim -
Brian Fox
2023-02-16 14:25:34 UTC
Permalink
Post by none) (albert
Post by Brian Fox
IMHO a Forth cross compiler begins with versions of dictionary
management words and a dictionary pointer variable that operate on a
memory area separate from the main dictionary.
I would think that you have to start with a decision whether to have an
indirect threaded, direct threaded or subroutine threaded Forth (or whatever)
The next decision is the layout of a dictionary entry. I'm an advocate
for an uniform data structure (same for constant's, variable's, create/does
words, defer words), but that can waste some memory (empty fields).
If it is non uniform, you have to carefully design how definitions are
chained and take care of any exceptions.
Then there is the important decision, actually imposed, if the Forth runs
stand alone or hosted on an operating system. In the latter case you can
choose for a single executable, or a kind of loader, that supposedly
yield some benefits for portability.
Then you have to choose registers for the Forth program counter,
data stack pointer, return stack pointer, and possibly more, e.g.
floating point stack, locals stack.
A decision that affects all code definitions is whether you hold
the top of stack in memory in a register.
Designing a Forth from scratch can be a waste of time.
E.g. for ciforth I started from fig-Forth and there was minor
mile stones, e.g. 32 bits, ANSI-compatible.
The regression test was valid through a few thousands of iterations.
<SNIP>
Post by Brian Fox
Have fun!
Agreed!
Having you own Forth is fun, but never forget to do something useful
with it. Otherwise you end up adding futile enhancements.
Yes all those decisions are required as well.
Unfortunately CiForth doesn't have a version for TMS9900. :-)))

I actually did not do all the work. I stood on the work of Brad Rodriguez and
used his Camel Forth as a stepping stone. Brad wrote the early versions in
Assembler but all the comments for hi-level words are clear Forth definitions.
I also used work from 1984 by engineers who wrote TI-Forth.

So I did what I said _and_ what you recommended:
- converted the TI-Forth Assembler to a cross-assembler for a PC Forth.
- Made the internal decisions (ITC, TOS in register, memory location...)
- wrote the primitives in the cross-assembler
- Wrote the cross compiler
- compiled Brad's comments as source code.
Eventually it worked.

My project was a "bucket list". I always wanted to build a compiler that built
a compiler The lessons learned were invaluable for me but of no
value to many others although I have found 9900 Forth allies in retro computing
circles. (albeit it is a very short list) :-)

Loading...