6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Sat May 11, 2024 11:20 pm

All times are UTC




Post new topic Reply to topic  [ 25 posts ]  Go to page Previous  1, 2
Author Message
PostPosted: Sun Feb 24, 2019 2:41 pm 
Offline

Joined: Tue Jan 07, 2014 8:40 am
Posts: 91
MichaelM wrote:
Since I think that S will be the PSP, the following code should provide the same functionality. Since I can't at the moment see how to support the same behavior for a DTC Forth implementation, your suggestion of additional instructions using W has high merit.
Code:
EXEC            .wrd  $+2               
                pla.w            ; siz pla
                jmp (0,A)        ; oax jmp (abs,X)               

Won't work. Consider if you EXECUTE a secondary word. Both ENT and IENT expect the address of the secondary word in W. This won't do that.

For DTC, you need to do w <= (psp++); pc <= w
For ITC, you need to do w <= (psp++); pc <= (w)

The only way I can see to do that with your current instruction set is (for ITC)
siz pla, siz pha.x, plw, jmp (0,A)
where I assume that you have a pha.x instruction (to push A as 16 bits onto the return stack), and that jmp (0,A) does pc <= (a). For DTC that last instruction would have to be whatever transfers A or W directly into the PC.


Quote:
I thought that the ip-relative instructions might provide the means by which CONSTANT and VARIABLE might be implemented using pointers embedded in the threads. For example, if a pointer to a CONSTANT follows the pointer (or the actual constant) to CONSTANT, then the following instruction sequence would put the value of the constant onto the parameter stack:
Code:
CONST           .wrd  $+2               
                lda.w  (0,I++)  ; isz lda 0,I++
                pha.w           ; siz pla
                inxt            ; ind nxt               
(If the constant is embedded directly in the thread, then simply leave off the ind prefix for the lda 0,I++ base instruction, and if the constant is a byte instead of a word, leave off the siz prefix as well. When a byte is loaded, the upper 8 bits are loaded with 0x00.)


Here's the problem: how do you EXECUTE a CONSTANT?

_________________
Because there are never enough Forth implementations: http://www.camelforth.com


Top
 Profile  
Reply with quote  
PostPosted: Sun Feb 24, 2019 3:59 pm 
Offline
User avatar

Joined: Mon Apr 23, 2012 12:28 am
Posts: 760
Location: Huntsville, AL
Brad:

Thanks for your reply.

I am a bit confused since the model of EXECUTE from figFORTH appears to use the top word of the PS, i.e. addressed by X, as the address of the word to be executed.
Quote:
For ITC, you need to do w <= (psp++); pc <= (w)
Your pseudo code for the required operation seems to be saying that as well. If TOS of A is treated as analogous to W, then the code fragment I presented above seems to be perform the same actions as those you describe. The default stack for pull A is the parameter stack.(The default stack for standard pull / push operations is the system stack.) The operations that code performs is: A <= (++psp); pc <= (A).

Although that implementation may work for an ITC implementation of EXECUTE, I don't have a corresponding way to implement the DTC EXECUTE: w <= (++psp); pc <= w. I will have to look for a way to implement a DTC EXECUTE, and then an ITC EXECUTE that only differs by having the internal IND flag set.
Quote:
Here's the problem: how do you EXECUTE a CONSTANT?
Well, from that comment / statement, it appears I have a poor understanding of the concepts used for implementing CONSTANT and VARIABLE, and probably several other critical elements of a Forth implementation as well. :( Can I get back to you on this after I've had a chance to read the figForth source for a more comprehensive understanding than I am clearly demonstrating at this time?

_________________
Michael A.


Top
 Profile  
Reply with quote  
PostPosted: Sun Feb 24, 2019 4:17 pm 
Offline

Joined: Tue Jan 07, 2014 8:40 am
Posts: 91
Michael, the difference between my EXECUTE pseudo-code and your proposed implementation is that mine leaves the address of the word being executed in W. This is important if the word being executed is a secondary or a "defined word" (CONSTANT, VARIABLE, etc.). It's not sufficient to simply have that address in A.

Certainly, get back to me at your convenience. I should be notified of replies to this topic; if all else fails I get notified of private messages sent to me here.

_________________
Because there are never enough Forth implementations: http://www.camelforth.com


Top
 Profile  
Reply with quote  
PostPosted: Sun Feb 24, 2019 5:42 pm 
Offline
User avatar

Joined: Sun Jun 30, 2013 10:26 pm
Posts: 1929
Location: Sacramento, CA, USA
With both of you drawing attention to the EXECUTE word, I hope that I'm not causing any additional topic drift with a direct question about the mechanics of that word.

My limited understanding is that EXECUTE simply allows us to execute a word whose address isn't generally known until run-time. It's kind of like the old "ON X GOSUB aaa,bbb,ccc" structure in BASIC, right? So, in my untested DTC Forth, I simply pop what is assumed to be a valid code field address off the data stack and shove it into PC, without touching W (which I don't use) or IP (which should be pointing to the word following EXECUTE's address inside the secondary that called EXECUTE).

If the word being EXECUTE'd is a primitive, then its NEXT will simply direct the VM back to the secondary and proceed. If the EXECUTE'd word is also a secondary, then IP will be pushed on the return stack with its JSR ENTER, but should eventually get popped off by the final EXIT of the executed secondary, which would still direct the VM back to the address following EXECUTE inside the original secondary.

If the word being executed is a defined word, then its JSR douser or JSR dovar or JSR docon pushes IP so the VM knows how to return back to the original secondary.

Am I making any sense, or do I need some major help, or am I floating somewhere in between?

_________________
Got a kilobyte lying fallow in your 65xx's memory map? Sprinkle some VTL02C on it and see how it grows on you!

Mike B. (about me) (learning how to github)


Last edited by barrym95838 on Sun Feb 24, 2019 6:21 pm, edited 1 time in total.

Top
 Profile  
Reply with quote  
PostPosted: Sun Feb 24, 2019 6:19 pm 
Offline
User avatar

Joined: Mon Apr 23, 2012 12:28 am
Posts: 760
Location: Huntsville, AL
Brad:

Again thanks for the reply. I am a bit slow on the uptake. I was editing my previous post to include a "Never Mind". IOW, your M65C02A code fragment finally clicked, and I came to understand why you wrote it the way you did. However, I was taking too long to add that comment to my previous post because I could not see how that implementation of EXECUTE would be able to execute a secondary like CONSTANT or VARIABLE, so I canceled.

I think that I can see how your code fragment works for an ITC Forth:
    (a) If the target is a secondary, then ITC EXECUTE will jump into the code for ENTER / DOCOL. The secondary will terminate with an ITC EXIT / SEMIS, and the VM will continue with the IP popped from the RS;
    (b) If the target is a primitive, then ITC EXECUTE will jump directly into the code of the primitive. The primitive will terminate with an ITC NEXT, i.e. an inxt instruction, and the VM will continue with the word pointed to by IP.

I am thinking that for a DTC Forth it will work in a similar manner:
    (a) If the target is a secondary, then DTC EXECUTE will jump into a secondary whose first instruction is either the ent instruction or a jsr ENTER. The secondary will terminate with an DTC EXIT / SEMIS, and the VM will continue with the IP popped from the RS;
    (b) If the target is a primitive, then DTC EXECUTE will jump directly into the code of the primitive. The primitive will terminate with an DTC NEXT, i.e. a nxt instruction, and the VM will continue with the word pointed to by IP.

To have an efficient EXECUTE, I need to find a way to implement an exe instruction that performs the operations you describe in the code fragment you provided above. Given my incorrect understanding of this operation, I have filled the opcode space and defined basic operations that will make implementing such an instruction a bit more difficult. It will be somewhat of a challenge, but since the implementation is supposed to be flexible, i.e. microprogrammed, I suppose that I will just have to make the change. It would be a bit of a bummer if ENTER, NEXT, EXIT are efficient, and EXECUTE is significantly less efficient.

My original thinking on the ip-relative instructions was that the constants and pointers to variables would be embedded directly in the VM instruction stream, i.e. thread. This thinking is somewhat out of step with the standard implementations. Although the ip-relative instructions may not be as applicable as I originally envisioned for Forth, the register indirect addressing mode represented has certainly proven useful in the implementation of certain operations in my Pascal compiler.

Although jmp (abs,X) can be coerced to work for ITC EXECUTE with A as the index value, it appears that one instruction missing from the M65C02A's repertoire is a register indirect jump, W could be used for such an instruction, and would likely benefit not just Forth but other languages as well. What form that instruction may take remains to be determined, but there are currently 4 unused column 2 opcodes that have not been defined. I was reserving them for defining various signed/unsigned multiplication / division instructions, but those operations could be implemented using the co-processor instruction, 0x02.

One final note is that I probably should use the IND flag to control whether or not the W register is adjusted during the ENTER instruction. Currently, W is incremented by 1 during each push operation, which leaves it pointing to offset 2 of the current word. That operation could be conditional on whether the IND flag is set or not. If not set, as in a DTC Forth, then W would not be incremented. If set as in an ITC Forth, then W would be doubly incremented and point to first word in the parameter field. I think this modification would then make W behave in a manner more consistent with expectations for DTC and ITC Forth implementations.

Michael:

Not drifting at all. I essentially had the same idea as you describe above in your post. I feel I am a bit more of a newbie on this subject, since the only Forth that I've done is getting the figForth model from the forum's archives running on my previous 6502/65C02 core, and typing in some examples from Leo Brodie's "Thinking Forth". Getting a Forth VM running is a far cry from truly understanding the nuances of the VM like Brad has pointed out above.

I think now would be a good time to clear up any misconceptions about how the Forth VM should operate that either one of us may harbor.

_________________
Michael A.


Top
 Profile  
Reply with quote  
PostPosted: Sun Feb 24, 2019 6:26 pm 
Offline
User avatar

Joined: Sun Jun 30, 2013 10:26 pm
Posts: 1929
Location: Sacramento, CA, USA
Michael, I believe that EXECUTE is the Forth VM's version of a native EXE machine instruction, so they are similar in some ways but different in others.

_________________
Got a kilobyte lying fallow in your 65xx's memory map? Sprinkle some VTL02C on it and see how it grows on you!

Mike B. (about me) (learning how to github)


Top
 Profile  
Reply with quote  
PostPosted: Sun Feb 24, 2019 6:38 pm 
Offline

Joined: Tue Jan 07, 2014 8:40 am
Posts: 91
Michael, I composed this before reading your latest reply.

Perhaps some pseudocode examples will help explain CONSTANT and VARIABLE. These assume that when a word is executed (either through the normal thread interpretation, or by EXECUTE) that the address of the word (CFA) is in the W register. As before, I'm using [ xxx ] to indicate a cell (or cells) in memory.

Primitives
DTC: [ machine code ... ]
ITC: [ $+2 ] [ machine code ... ]

Secondaries
DTC: [ ENT ] [ address ] [ address ] [ address ] [ addr of EXIT ]
ITC: [ addr of ENTER ] [ address ] [ address ] [ address ] [ addr of EXIT ]

where DTC ENT instruction does (rsp--) <= ip; ip <= w+2; w <= (ip++); pc <= w
and ITC ENTER does (rsp--) <= ip; ip <= w+2; w <= (ip++); pc <= (w)
and ITC EXIT does ip <= (++rsp); w <= (ip++); pc <= (w)
and DTC EXIT does ip <= (++rsp); w <= (ip++); pc <= w

Constants
DTC: [ JMP DOCONST ] [ value ]
ITC: [ DOCONST ] [ value ]

where DTC DOCONST does z = w+3; push TOS; TOS = (z)
and ITC DOCONST does z = w+2; push TOS; TOS = (z)
Here I am assuming that "z" is an available address register. You could use w for this. I'm also assuming that JMP DOCONST is 3 bytes. You could create a new machine instruction for this, and eliminate the JMP.

Variables (classic implementation)
DTC: [ JMP DOVAR ] [ value ]
ITC: [ DOVAR ] [ value ]
where DTC DOVAR does push TOS; TOS = w+3
and ITC DOVAR does push TOS; TOS = w+2

Variables are the same as constants, except that constants fetch the value, and variables simply return the address of the value. Again, you could create a new machine instruction to replace JMP DOVAR for DTC. (ITC, by its nature, must begin each word with the address of a machine code fragment.)

The real fun is CREATE...DOES>. But that's for another day.

_________________
Because there are never enough Forth implementations: http://www.camelforth.com


Top
 Profile  
Reply with quote  
PostPosted: Sun Feb 24, 2019 6:44 pm 
Offline

Joined: Tue Jan 07, 2014 8:40 am
Posts: 91
MichaelM wrote:
I think that I can see how your code fragment works for an ITC Forth:
    (a) If the target is a secondary, then ITC EXECUTE will jump into the code for ENTER / DOCOL. The secondary will terminate with an ITC EXIT / SEMIS, and the VM will continue with the IP popped from the RS;
    (b) If the target is a primitive, then ITC EXECUTE will jump directly into the code of the primitive. The primitive will terminate with an ITC NEXT, i.e. an inxt instruction, and the VM will continue with the word pointed to by IP.

I am thinking that for a DTC Forth it will work in a similar manner:
    (a) If the target is a secondary, then DTC EXECUTE will jump into a secondary whose first instruction is either the ent instruction or a jsr ENTER. The secondary will terminate with an DTC EXIT / SEMIS, and the VM will continue with the IP popped from the RS;
    (b) If the target is a primitive, then DTC EXECUTE will jump directly into the code of the primitive. The primitive will terminate with an DTC NEXT, i.e. a nxt instruction, and the VM will continue with the word pointed to by IP.

That all appears correct.


Quote:
My original thinking on the ip-relative instructions was that the constants and pointers to variables would be embedded directly in the VM instruction stream, i.e. thread. This thinking is somewhat out of step with the standard implementations.

It is acceptable to have constants and variable pointers embedded in the instruction stream; several Forths do this kind of micro-optimization. However, it is still required that a constant or variable also be an executable Forth word. One way this can be done is to make constants and variables "state smart" -- when encountered during compilation, they embed code into the instruction stream, but when encountered outside of compilation, the "normal" word is executed to return the constant value or variable pointer. But that's getting into advanced topics.

_________________
Because there are never enough Forth implementations: http://www.camelforth.com


Top
 Profile  
Reply with quote  
PostPosted: Sun Feb 24, 2019 6:51 pm 
Offline

Joined: Tue Jan 07, 2014 8:40 am
Posts: 91
barrym95838 wrote:
My limited understanding is that EXECUTE simply allows us to execute a word whose address isn't generally known until run-time. It's kind of like the old "ON X GOSUB aaa,bbb,ccc" structure in BASIC, right? So, in my untested DTC Forth, I simply pop what is assumed to be a valid code field address off the data stack and shove it into PC, without touching W (which I don't use) or IP (which should be pointing to the word following EXECUTE's address inside the secondary that called EXECUTE).

If the word being EXECUTE'd is a primitive, then its NEXT will simply direct the VM back to the secondary and proceed. If the EXECUTE'd word is also a secondary, then IP will be pushed on the return stack with its JSR ENTER, but should eventually get popped off by the final EXIT of the executed secondary, which would still direct the VM back to the address following EXECUTE inside the original secondary.

That appears to be all correct. (Though I can't vouch for the ON X GOSUB analogy, because I forgot most of my BASIC years ago.)

Quote:
If the word being executed is a defined word, then its JSR douser or JSR dovar or JSR docon pushes IP so the VM knows how to return back to the original secondary.

Generally, douser/dovar/docon don't need to alter IP -- they tend to be written in machine code -- so they don't need to push it or restore it. Words defined with CREATE...DOES> do need to save and restore IP, because DOES> will direct the VM to a new thread for interpretation (and that thread will end with EXIT, like any secondary).

_________________
Because there are never enough Forth implementations: http://www.camelforth.com


Top
 Profile  
Reply with quote  
PostPosted: Sun Feb 24, 2019 7:40 pm 
Offline
User avatar

Joined: Mon Apr 23, 2012 12:28 am
Posts: 760
Location: Huntsville, AL
Brad:

Thanks for the reply. I certainly appreciate the time you've taken to help me resolve my misconceptions regarding EXECUTE, ENTER, EXIT, and NEXT for both ITC and DTC Forth VMs. I will study the pseudo code that you provided above for CONSTANT and VARIABLE.
Quote:
One way this can be done is to make constants and variables "state smart" -- when encountered during compilation, they embed code into the instruction stream, but when encountered outside of compilation, the "normal" word is executed to return the constant value or variable pointer. But that's getting into advanced topics.
At this point, that would certainly not be particularly fruitful; I'm too much of a newbie to understand these things, much less undertake to develop them. :D At this point, trying to follow what's going on recently in some of the other Forth threads here has been pointless.

_________________
Michael A.


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 25 posts ]  Go to page Previous  1, 2

All times are UTC


Who is online

Users browsing this forum: No registered users and 4 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to: