Re: I feel stupid... "Invalid combination of opcode and operand", was, now is FORTH question




Rod Pemberton wrote:
"Jean-François Michaud" <spamtrap@xxxxxxxxxx> wrote in message
news:1155913132.070839.133370@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Could you be more
specific? I can supply code in more managable bits on the forum.

I can start with this:

: QUIT RS! BEGIN QUERY AGAIN ;

RS! clears the return stack
BEGIN AGAIN is an infinite loop
QUERY checks to see if a key has been pressed (mine is set up using
interrupts)


Okay, let me see what I can figure out....(I'm writing this as I review your
posts and other stuff.)


My summary of your posts so far:
You're using ITC, indirect token threaded code (vs. DTC,STC,TTC).
You're using the standard FORTH dual stacks: data and address.
You're using a four register CPU "virtual machine":
TOS in ebx - top of stack - first (data) stack element
PSP in ebp - parameter stack pointer - pointer to (data) stack, except TOS
RSP in edi - return stack pointer - pointer to address stack
IP in esi - interpreter pointer
It seems that you're not using two other common ITC registers: W-working
pointer or X-indirect working pointer.


A+ :o). Except for one detail. My Forth isn't tokenized; simply
Indirect threaded. Tokenization is somewhat similar to compression.
Each definition is replaced by a concise symbol. Whenever a concise
symbol is encountered, it is matched against a lookup table. This
allows for extremely compact code representations. Also, I do use a
working pointer but its impossible for you to tell from what I showed
you. My use of the "W" register is implicit rather then explicit.
Instead of using a CPU register I hook up an execution tree dynamically
in a very special high level definition.

Description of your NEXT, EXIT, and DOCOLON, described somewhat in terms of
C:
1) NEXT performs an indirect non-returning function call, i.e., calls a
pointer to a pointer to a non-returning function (or indirect non-local
goto...).

NEXT simply calls up the next word to execute in the sequence of
addresses.

: ABC A (addr0) B (addr2) C (addr3) ;

if A, B and C are core words (written in assembly), they will each
execute next at the end of their definition. The only bits of code that
actually get executed to obtain a transformation are core definitions
written in assembly. High level Forth definitions merely organize the
sequence of operations.

so in A's definition

: A .... blah blah assembly blah .... NEXT

NEXT looks into the IP register the addr that B points to and executes
the code found at the beginning of B's definition (if B is a high level
definition then DOCOLON_ASM will be executed otherwise, we have a core
definition and the assembly code will be executed immediately and will
be terminated by NEXT which will take us to C.

2) EXIT performs a function return, using a return address saved on the
address stack. This roughly the same as the called function's epilog in C.

Somewhat; except the process is much simpler on the Forth virtual
machine. There is no context to save and restore and no parameters to
deal with (if there are parameters, they are already on the parameter
stack).

3) DOCOLON performs the function entry, saving the return address on the
address stack and updates the current address (i.e., interpreter pointer).
This is roughly the same as the called function's prolog in C and the
non-virtual cpu's instruction pointer adjustment.

Same comment. No context to save and restore except the word you are
coming from at a higher level. DOCOLON_ASM only says "I'm a high level
Forth definition. I contain a sequence of memory addresses. Execute the
code contained in the word following DOCOLON_ASM in this definition.


Questions:
1) Why is NEXT missing the "header" or "frame structure" that DOCOLON and

NEXT is simply a bit of code that gets called over and over. There is
no need to call it explicitly while writing high level code although it
could be set up that way (I don't see any use for it though) so it is
only set up as being a macro. DOCOLON on the other hand can be used to
create words that create words. In such a case, it is necessary to be
able to refer to DOCOLON to incorporate it into new definitions (don't
worry about this just yet though). EXIT is refered to at the end of
every high level Forth definition and must therefore be part of the
dictionary (hence the header).

EXIT have?
2) Why does the "header" or "frame structure" of DOCOLON have an extra
field: ".byte 0x00" ?

All my headers are padded so that each logical entry is aligned with
memory boundaries. It is simpler to navigate through the header with
code when the logical concepts don't cross over memory boundaries (less
calculations involved). I could be set up a different way, but I find
this more convenient.

3) When using DOCOLON, NEXT, EXIT which is called first? If NEXT, what is
NEXT inited to? Perhaps, DOCOLON?

In a high level Forth definition, DOCOLON_ASM is always called first
and EXIT is always called last. Here is the compiled version of the
source code for QUIT:

Looking at the code I noticed the definition is actually like this
instead of what I wrote earlier. This is what the source code looks
like:

: QUIT BEGIN RS! QUERY AGAIN ; (instead of : QUIT
RS! BEGIN QUERY AGAIN ;)

And this is what the compiled code looks like after getting parsed by
the interpreter (this one is actually hand compiled by me in the
assembler but the end result would be logically identical if compiled
by the outer interpreter).

..align 4
#_______________________________
#
# This is the outer interpreter.
#_______________________________
#
# ( -- ) : QUIT BEGIN RS! QUERY AGAIN ;
#
QUIT_ADDR:
.byte 0x00
.byte 0x04
.ascii "QUIT"
.word 0x0000
.long EVALUATE_ADDR # This links to the previous
word in the dictionary (unidirectional linked list)

QUIT:

.long DOCOLON_ASM # This first entry points to
code to execute. This is this high level Forth word's signature

BEGIN_QUIT:

.long RS_STORE # Only points to
RS_STORE
.long QUERY # Only points to
QUERY

AGAIN_QUIT:
.long BRANCH, BEGIN_QUIT # Basic looping construct

.long EXIT # Exit terminates
this definition by popping the return address from the stack so that
execution can continue where we left off.

Regards
Jean-Francois Michaud

.



Relevant Pages

  • Re: Stack pointer at program start?
    ... details and / or the context your program is running in. ... And if yes, does it mean that every time I execute this program, the stack ... pointer slips 4 byte higher and higher, or does the stack pointer "forget" ...
    (alt.lang.asm)
  • Re: Wanted - example program to execute stack
    ... The bytes 0xc3 and 0x0 are placed onto the stack. ... fnptr is a pointer to a function. ... they will execute fine. ...
    (comp.lang.c)
  • Re: level 2 postscript debugger steps through procs and files
    ... a proc or file or just execute it. ... with the stack, maybe) then enter will quit again. ... db: Step def ... currentdict end 3 1 roll def begin ...
    (comp.lang.postscript)
  • Re: Sabotaged PaXtest (was: Re: Patch 4/6 randomize the stack pointer)
    ... > that once an attacker can execute his own code on the target system ... > technique that can find out the libc randomization without having to ... > learn the stack one. ... components and reduce an N*M attack to N+M. ...
    (Linux-Kernel)
  • Re: Status of my Forth interpreter in C
    ... The stacks are currently large ... The name field is the only dictionary header field which is ... type, without casting, ditto for pointer to integer. ... execute() is a modified version of next. ...
    (comp.lang.forth)