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,
On Sunday, February 12, 2023 at 9:36:35 PM UTC-5, SpainHackForth wrote:
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,
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)
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.
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
On 13/02/2023 2:16 pm, Brian Fox wrote:
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)
More forths should support READ-CHAR WRITE-CHAR. It's a logical
extension when
working at the byte/char level.
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.
Backtrace:vm_write<<<
Backtrace:vm_write<<<
I tried this but still no go:
...
On 14/02/2023 12:39 am, SpainHackForth wrote:
I tried this but still no go:
...
Here's a buffered output routine. PUTC writes a byte to file.
Remember to CLOSE-OUTPUT when done to flush the buffer.
[ Aside : Calling a buffer a buffer is a bad idea because its
also a word in the block wordset. ]
On Monday, February 13, 2023 at 5:03:23 PM UTC+1, dxforth wrote:
On 14/02/2023 12:39 am, SpainHackForth wrote:
I tried this but still no go:
...
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!
: vm_write ( n -- ) vm_c,lit n ;
On 14/02/2023 4:22 am, SpainHackForth wrote:Hi DX,
: 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.
I’m looking for a good tutorial on how to implement a cross compiler in gforth for my target system.
On Tuesday, February 14, 2023 at 5:00:39 PM UTC-5, SpainHackForth wrote:Oops.
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
---
On Wednesday, February 15, 2023 at 9:52:41 AM UTC-5, Brian Fox wrote:Thanks I will have a closer look.
On Tuesday, February 14, 2023 at 5:00:39 PM UTC-5, SpainHackForth wrote:
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 memoryOops.
---
Of course one needs to define the TDP variable first.
VARIABLE TDP
On Tuesday, February 14, 2023 at 5:00:39 PM UTC-5, SpainHackForth wrote:
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.
Have fun!Agreed!
IMHO a Forth cross compiler begins with versions of dictionaryI 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
management words and a dictionary pointer variable that operate on a
memory area separate from the main dictionary.
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>
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.
Sysop: | Keyop |
---|---|
Location: | Huddersfield, West Yorkshire, UK |
Users: | 475 |
Nodes: | 16 (2 / 14) |
Uptime: | 20:08:10 |
Calls: | 9,487 |
Calls today: | 6 |
Files: | 13,617 |
Messages: | 6,121,093 |