PDA

View Full Version : Implementing my own interpreter?


les@windstream
05-03-01, 02:11 PM
I am working on an application where an NMIN-0121 will be attached to a serial connection. Based on the text that comes across the interface, the code will set one of the I/O port values. To do this I need to turn off the interpreter and implement my own using WORD, FIND, and EVALUATE and some defined control words. I'm assuming I can turn the interpreter off using ";S" and turn it on again with "INTERPRET"; is that the case? Also, is the PAD area available for application use, or is it used internally for storing info from the serial interface. I thought data from the serial port was moved into the TIB region, but found some indication that terminal entry bytes are going through the PAD region as well. Just trying to understand what's available. Thanks.

les@windstream
05-03-01, 05:53 PM
Follow up on my last questions, after a correction: I should have said "EXECUTE" not "EVALUATE". Wrong FORTH...

What part of the executable does "FIND" return? And what part of the address does "EXECUTE" require? There are several different regions in the executable storage area, and I'm enough of a newbie to not know which one EXECUTE may be looking for. (I think that could be different depending on the details of the FORTH, right?)

Thanks, in advance for any help.

les@windstream
05-03-01, 07:09 PM
For the record, and answering my own question from above, FIND appears to return the pfaptr address, and EXECUTE reguires the cfa. This is different than some FORTHs and not what the ANS spec says. That's not a problem; just requires a bit more documenting what is really happening. Now on to the next question:

What does QUERY do, and what's the correct invocation of it? I'm still finding all of my entries to the NMIN-0121A show up in the PAD, and not in the TIB area. QUERY is supposed to "store input characters into the TIB". However, I haven't been able to figure out how it does that. Suggestions and guidance would be appreciated. Thanks.

Also, using the ";S" does not halt the interpreter, at least not globally. So, what's the correct way to allow my interpreter to run and not the main interpreter so that I can have some kind of a parser that ignores words I don't care about in the input stream and handles the ones I want it to respond to - when not connected to a development terminal at least? That's why I'm trying QUERY, but could use more guidance as to how it is supposed to work.

rob
05-04-01, 05:53 AM
For more details refer to the online glossary at:

http://www.ee.ualberta.ca/~rchapman/MFwebsite/V35/Alphabetical/Brief/index.html

PAD is used for number conversion like when you use . <dot>.

You halt the main interpreter each time you execute a word. So for you to halt the interpreter and get the text you need to use QUERY and parse TIB or you can just get the characters as they are coming in with KEY. ;S is just used to skip to the end of a block. Just like ( at the start of a line.

Rob

RMDumse
05-04-01, 10:37 AM
Originally posted by les@windstream
To do this I need to turn off the interpreter and implement my own using WORD, FIND, ...

The inner interpreter of Forth is very simple. It is basically a endless loop of QUERY INTERPRET .

I'm assuming I can turn the interpreter off using ";S" and turn it on again with "INTERPRET"; is that the case?

I'm not sure what your thinking is on this. INTERPRET stops at the end of a line, and returns control to the calling word. In the inner interpreter that induces another round of QUERY INTERPRET . Control is given to each thing on the line if the state is interpretive, or compilation otherwise.

Also, is the PAD area available for application use, or is it used internally for storing info from the serial interface. I thought data from the serial port was moved into the TIB region, but found some indication that terminal entry bytes are going through the PAD region as well.

Historically, PAD was an area out beyond the TIB where numbers could be manipulated for print out. Above PAD was open, yet-unused, dictionary space. The problem is a bit different in a ROMable system, since unused ROM (PROM) is not very useful in the same sense the unused RAM of mainframe Forths is. So in Max-FORTH, PAD is a RAM area. Numbers are built down in memory from PAD. So it you print a number like 123 the 3 is at PAD-1 the two is at PAD-2 and the 1 is at PAD-3.

I'm trying to remember how PAD is used in interpretation. I think words may be parsed to PAD during interpretation, but I can't remember, and I don't have my reference materials here at hand. As I recall the words are parsed at DP, so they are laid down and can be turned into a "word" if in defining mode.

If you look at the memory map of the system, you will find PAD's location and that a few bytes are saved below it for number formating, and above it as well for conventional PAD use, but that's about as much as I can recall off the top of my head without reference material.

RMDumse
05-04-01, 11:21 AM
Originally posted by les@windstream
Follow up on my last questions, after a correction: I should have said "EXECUTE" not "EVALUATE".

Right. EXECUTE takes the CFA of a word and causes execution control to be passed to it.

What part of the executable does "FIND" return?

In MaxFORTH FIND returns the PFAPTR, rather than the actual CFA. This variation is necessary, since MaxFORTH can run headless. There has to be a link from the head to the code, and the PFAPTR is that link. But it is a oneway link. You can't go (directly at least) from the code section of the word back to its head. So when you FIND a word, you are given the pointer in the head, from which you can get to either the head or the code.

And what part of the address does "EXECUTE" require? There are several different regions in the executable storage area, and I'm enough of a newbie to not know which one EXECUTE may be looking for. (I think that could be different depending on the details of the FORTH, right?)

EXECUTE is looking for the CFA. To convert a PFAPTR to a CFA, just do a fetch @ . Otherwise MaxFORTH is like most Forths. This separated heads and codes for ROMability is the only significant difference.

For the record, and answering my own question from above, FIND appears to return the pfaptr address, and EXECUTE reguires the cfa. This is different than some FORTHs and not what the ANS spec says. That's not a problem; just requires a bit more documenting what is really happening.

True. To get the embeddibility, we had to go beyond the standard. If we'd had a two way link from head to code and code to heads we could have been closer to the standard, but at the time, memory was precious, and we wanted only the smallest simplest code we could get. Hence the PFAPTR in the tossable head was the best solution.

What does QUERY do, and what's the correct invocation of it? I'm still finding all of my entries to the NMIN-0121A show up in the PAD, and not in the TIB area. QUERY is supposed to "store input characters into the TIB". However, I haven't been able to figure out how it does that.

Well, loosely from distant memory... QUERY is TIB CHAR/LINE EXPECT. I don't remember the exact name of CHAR/LINE or if it is fetched or what. But that's the basic idea. QUERY is the highest level terminal communications word in Forth. It means "query the operator for a line of input". You can make your own outer interpreter (I mistakenly called it an inner interpreter above) this way...

: X BEGIN QUERY INTERPRET ." dho!" AGAIN ;

When you execute X you won't notice anything remarkable except every line ends with dho!

Ah! Alan just emailed me a memory map to check. In MaxFORTH the variable UPAD is set to the same place as TIB. Consequently, the reason you are seeing TIB stuff at PAD is they are in the same place! You can move PAD anywhere you like by changing UPAD, but in the tight memory of the NMIN-0021 things were piled up to be as useful as possible in a stand alone situation, with no external memory to direct to.

Also, using the ";S" does not halt the interpreter, at least not globally. So, what's the correct way to allow my interpreter to run and not the main interpreter so that I can have some kind of a parser that ignores words I don't care about in the input stream and handles the ones I want it to respond to - when not connected to a development terminal at least? That's why I'm trying QUERY, but could use more guidance as to how it is supposed to work.

The way back into the regular outer interpreter is by making an error. When the lower level internals of INTERPRET doesn't find something it can understand, it exits through the error processing methods through (a vectorable verstion of) ABORT.

Really the outer interpreter of MaxForth goes from reset vector to STARTUP. STARTUP sets up Forth and calls ABORT, ABORT calls QUIT and QUIT is the endless loop of QUERY INTERPRET . So you can reenter the reqular outer interpreter by calling QUIT if you do want the return stack cleared and don't want the data stack cleaned. You can reenter the reqular outer interpreter by calling ABORT if you do want the return stack and don't the data stack cleared.

What you really need to get where you need to go is the code for INTERPRET. Then you can copy it and change just the error handling. Email nmitech@newmicros.com and ask him to copy out the source for INTERPRET to you.

RMDumse
05-04-01, 01:46 PM
Well, nmitech sent me the source code to sort out, and I have reconstructed the parts of it you need, following,

: STARTUP
CR ." Max-FORTH V3.5E"
ABORT
] SMUDGE ( NO SEMICOLON SORT OF "BRANCH"

: ABORT
FPSP!
FORTH DEFINIONS
QUIT
] SMUDGE ( NO SEMICOLON SORT OF "BRANCH"

: QUIT
BLK CLEAR
COMPILE [
BEGIN
RP!
CR
QUERY
INTER
?STATE NOT
IF
." OK"
THEN
AGAIN
[ SMUDGE ( NO SEMICOLON SORT OF "BRANCH"

: QUERY
TIB @
CSLL
EXPECT
IN CLEAR
BLK CLEAR
SPAN #TIB @!
;

( ...

: QNOTEND
>IN @
BLK @
IF
B/BUF
ELSE
#TIB @
THEN
U<
;

: INTERPRET
BEGIN
QNOTEND ( LOOKS AT TIB/BLK TO SEE HOW FAR IN, DEFINED ABOVE )
WHILE
IF
GETFIND ( DEFINED AS BLANK WORD FIND
?DUP
IF
1 =
?STATE 0= OR
IF
CFA EXECUTE
ELSE
CFA ,
THEN
THEN
ELSE
DROP ( ...

( THEN SOME TEDIOUS NUMBER HANDLING STUFF
( COMPILING A LITERAL, A DOUBLE LITERAL, OR A F.P. LITERAL

THEN
?STACK
REPEAT
( AND SOME BLK HANDLING STUFF
;


This is not exhaustively translated. I haven't downloaded it to check for errors, etc., and I skipped some block and number things I doubt you need. But I think you can see the structure of a new interpret if you wish to build one. The key is the used of FIND and not doing NUMBER which can take you to error processing which you don't want to do. Plus, since you are not wanting to compile, you can pull out the ?STATE stuff and just execute what you like.

Hope that puts you on your way to success.

les@windstream
05-06-01, 02:55 PM
Thanks to all respondents for their help. I've made enough progress to get to my next question:

Now that I'm parsing the input stream and have defined my own words that I would like it to respond to, is there a way to "exclude" everything else but my defined words? The incoming text that will be parsed is not entirely free form, however there's no way for me to explicitly exclude the possibility of an existing FORTH word being in the input stream. In other versions of FORTH it appears that one can define their own word list, and only use that one to search for a match during interpretation. Is that possible in Max-FORTH? Reading the documentation indicates how to create my own vocabulary, but doesn't appear to have a way to exclude other vocabularies. Thanks, again, in advance for suggestions.

RMDumse
05-06-01, 03:55 PM
As I recall, Forth searches until it gets to a zero link. I think you can define a few words, then exclude all others by popping the link structure. Try

0 ' DEEPEST-WORD-YOU-WANT LFA !

If there are other words you want from the language itself, just redefine them after the deepest word of yours you want to keep, and they'll be available too. (Be sure to make the new definition immediate if the old one was also immediate!)

Maybe it would be a good idea to keep the link during debugging so you can put it back and recover the full dictionary.

jya
05-29-01, 03:59 PM
Thank you all for an interesting and informative thread.

Here's a q&d test for immediacy that works on the nmiy0020. I hope it's useful.

: ?IMM ' NFA C@ 40 AND IF ." Yes " ELSE ." No " THEN ;

EXAMPLES:

?IMM + <cr> No OK
?IMM IF <cr> Yes OK

Jerry