Jump to content
IGNORED

fbForth—TI Forth with File-based Block I/O [Post #1 UPDATED: 06/09/2023]


Lee Stewart

Recommended Posts

Gettin' there! :) I am pretty much done with the design of the low-level stuff. And, the high-level stuff is coming along nicely. I just need to quit changing the design in the middle of the process—it requires too many balls in the air at once, risking dropping a few! :grin:

 

I should be ready to start testing in a day or two.

 

...lee

Link to comment
Share on other sites

!!!! I just ran into a problem I wasn't expecting—asm994a v3.10 won't assemble my program without crapping out with no messages before the first pass is done. Windows just says the program stopped working. The source has a fair number of changes from a file that does assemble successfully, but it's only 28 lines longer, 2234 vs. 2206. Now what?!? :mad: Any ideas? I'm stuck.

 

...lee

Link to comment
Share on other sites

God, I hate getting old!!! Some of y'all are probably laughing at me because I went through this exact same problem about a year ago—even started quite a thread on it before I solved it the last time. Those involved in the last conversation will remember that the problem was the use of the label 'ELSE'. Someone said at the time that there are macro capabilities in asm994a that are not spelled out anywhere and that 'ELSE' may be part of it. I had replaced 'ELSE' with 'else' to solve the problem last time. I could probably remove the label because it is not referenced anywhere. I am wondering, though, if there are any silent gotchas hidden elsewhere in the code because there are a lot of labels like 'IF', 'THEN', 'ENDIF', 'BEGIN', 'END', ..., I would expect to be part of such a macro language if it exists within asm994a. Anybody know?

 

...lee

Link to comment
Share on other sites

I was going to shorten the code for MON , the TI Forth word that exits Forth to the operating system, i.e., console ROM reset vector location 0, which ends with the opening selection screen; but, I'm not sure now. The current word fills the lower 8KB of CPU RAM with 4E4Fh (ASCII "NO"), i.e., a string of "NONONONONONONONONONONO..." Anybody know why?

 

...lee

Link to comment
Share on other sites

I was going to shorten the code for MON , the TI Forth word that exits Forth to the operating system, i.e., console ROM reset vector location 0, which ends with the opening selection screen; but, I'm not sure now. The current word fills the lower 8KB of CPU RAM with 4E4Fh (ASCII "NO"), i.e., a string of "NONONONONONONONONONONO..." Anybody know why?

 

...lee

 

Really! That's weird, but quite cool. I wonder if it's some sort of (dumb) copy protection feature. You know, to stop people resetting the console and using something like easybug to examine the memory? I know TI Forth was given away for free, but that wouldn't have been the original intention. IIRC TI Forth was dropped like a hot potato when TI pulled out, and it was posted to the groups as-is.

 

Does seem odd though. If it were me, I'd just do a BLWP @0 and be done with it! That's all TF does. It doesn't close any open files or anything. It just quits.

Link to comment
Share on other sites

God, I hate getting old!!! Some of y'all are probably laughing at me because I went through this exact same problem about a year ago—even started quite a thread on it before I solved it the last time. Those involved in the last conversation will remember that the problem was the use of the label 'ELSE'. Someone said at the time that there are macro capabilities in asm994a that are not spelled out anywhere and that 'ELSE' may be part of it. I had replaced 'ELSE' with 'else' to solve the problem last time. I could probably remove the label because it is not referenced anywhere. I am wondering, though, if there are any silent gotchas hidden elsewhere in the code because there are a lot of labels like 'IF', 'THEN', 'ENDIF', 'BEGIN', 'END', ..., I would expect to be part of such a macro language if it exists within asm994a. Anybody know?

 

...lee

 

There's a lot of undocumented stuff in the assembler that I assume is incomplete. I once spent an afternoon experimenting the macro syntax to see if I could get it to work, but no luck. It also seems to support (or, at least, was intended to) conditional compilation, but again, I couldn't get that to work.

 

If you drag the EXE file into notepad++ or a similar text editor, you'll see lots of text that gives clues away.

 

But yes, I'd avoid labels like begin and end etc. Prepend them with a _ that's what I do.

 

Also, it's worth pointing out, there is no six character label length limit like in the TI assembler. As far as I know, you can have much longer labels. There's a gotcha though if you want to use the output with an option 3 loader, as the object file WILL only save labels to the first six characters (because that's the format of the tagged loader). But if you're on a pure machine code project that is being loaded in as a memory image, you can use longer labels. I'm using 8 characters in some place (I try and keep them short) but I'm sure I could use longer.

 

When you begin to look at cart images give me a shout. I have a simple little program that Tursi wrote for me in (IIRC) 2008/2009 that takes the object file and produces .bin files suitable for use with classic99, Win994a Cart Creator, TI994W etc. That's what I use as part of the TF build process.

 

Mark

Link to comment
Share on other sites

Really! That's weird, but quite cool. I wonder if it's some sort of (dumb) copy protection feature. You know, to stop people resetting the console and using something like easybug to examine the memory? I know TI Forth was given away for free, but that wouldn't have been the original intention. IIRC TI Forth was dropped like a hot potato when TI pulled out, and it was posted to the groups as-is.

 

Does seem odd though. If it were me, I'd just do a BLWP @0 and be done with it! That's all TF does. It doesn't close any open files or anything. It just quits.

 

No need to clear the user interrupt link at 83C4h?

 

...lee

Link to comment
Share on other sites

...

But yes, I'd avoid labels like begin and end etc. Prepend them with a _ that's what I do.

 

I am actually wondering why they were put in at all because none of them appear to be used anywhere in the ALC. I guess I could sprinkle about the prepended '_' to see if I get any unresolved references.

 

...

When you begin to look at cart images give me a shout. I have a simple little program that Tursi wrote for me in (IIRC) 2008/2009 that takes the object file and produces .bin files suitable for use with classic99, Win994a Cart Creator, TI994W etc. That's what I use as part of the TF build process.

 

I think that is what @Tursi passed to me when I was working on some project or other recently—FP package, I think—see? "bit rot" again!

 

...lee

Link to comment
Share on other sites

I assume (haven't checked) that the console ROM does that. The Classic99 debugger should tell us that :-)

 

To be completely safe, you have to clear the interrupt vector before BLWP to 0000. The console doesn't clear the vector until well into the GPL code, which means that a VDP interrupt always happens before it gets there. If the user interrupt doesn't return cleanly, the startup code loses control. (Doesn't happen at power-on because VDP interrupts are disabled in either the VDP or the 9901 or both, I don't remember.)

 

I discovered by accident a long time ago, I was working on a little mini-game that used the interrupt vector to run the main loop (so I didn't need timing code). To save memory, it branched back into the game instead of returning normally, and so when you hit FCTN-=, the game was briefly accelerated then kept running when you let go. ;)

 

Link to comment
Share on other sites

To be completely safe, you have to clear the interrupt vector before BLWP to 0000. The console doesn't clear the vector until well into the GPL code, which means that a VDP interrupt always happens before it gets there. If the user interrupt doesn't return cleanly, the startup code loses control. (Doesn't happen at power-on because VDP interrupts are disabled in either the VDP or the 9901 or both, I don't remember.)

 

I discovered by accident a long time ago, I was working on a little mini-game that used the interrupt vector to run the main loop (so I didn't need timing code). To save memory, it branched back into the game instead of returning normally, and so when you hit FCTN-=, the game was briefly accelerated then kept running when you let go. ;)

 

I just need to CLR @>83C4, right?

 

...lee

Link to comment
Share on other sites

Phew! I'm having fun now—not! I want to include the definition of ASSEMBLER in the Forth kernel, so I figure I must get a clear understanding of how vocabularies work in Forth. I actually have a fair understanding of it, but how exactly to define a vocabulary word in the ALC source is driving me nuts! :-o It's easy in high-level Forth: VOCABULARY XYZ IMMEDIATE . But—doing that in ALC is a bit trickier, to say the least. I will report my progress...

 

...lee

Link to comment
Share on other sites

... of course, looking forward to putting this whole thing into cartridge ROM, I need to start thinking about what parts cannot go there—in this case, the word, ASSEMBLER . It actually may be the only word I'm putting into the fbForth kernel that gets modified after its creation. FORTH , the word at the head of the vocabulary chain, is defined differently. It uses the two user variables immediately ahead of FORTH_LINK for vocabulary linkage instead of the two fields reserved for that purpose when VOCABULARY defines a new vocabulary. :idea: I suppose I could define ASSEMBLER the same way, h-m-m-m ... :ponder:

 

...lee

Link to comment
Share on other sites

Almost there!

 

I think I've figured out the vocabulary problem. I put ASSEMBLER in without much fuss once I got into my thick skull how vocabularies work!

 

I'm just about done with the serious coding and re-coding. I believe the only thing left to do before testing and debugging is to re-code COLD , which will add only a handful of bytes. As it stands, i am only 134 bytes over the default TI Forth system after it boots up and I am 364 bytes under the default TI Forth system with the system synonyms block loaded! Just about anything you want to do with TI Forth requires the system synonyms, which is why I am including them in the kernel of fbForth.

 

...lee

Link to comment
Share on other sites

There's a lot of undocumented stuff in the assembler that I assume is incomplete. I once spent an afternoon experimenting the macro syntax to see if I could get it to work, but no luck. It also seems to support (or, at least, was intended to) conditional compilation, but again, I couldn't get that to work.

 

If you drag the EXE file into notepad++ or a similar text editor, you'll see lots of text that gives clues away.

 

But yes, I'd avoid labels like begin and end etc. Prepend them with a _ that's what I do.

 

Also, it's worth pointing out, there is no six character label length limit like in the TI assembler. As far as I know, you can have much longer labels. There's a gotcha though if you want to use the output with an option 3 loader, as the object file WILL only save labels to the first six characters (because that's the format of the tagged loader). But if you're on a pure machine code project that is being loaded in as a memory image, you can use longer labels. I'm using 8 characters in some place (I try and keep them short) but I'm sure I could use longer.

 

When you begin to look at cart images give me a shout. I have a simple little program that Tursi wrote for me in (IIRC) 2008/2009 that takes the object file and produces .bin files suitable for use with classic99, Win994a Cart Creator, TI994W etc. That's what I use as part of the TF build process.

 

Mark

 

Conditional compilation does work with the mentioned assembler. Here's some code I used in Pitfall. You get the idea:

 

***************************************************************
* Some build directives
********@*****@*********************@**************************
DISK    EQU   0
CART    EQU   1
BANK0   EQU   0
BANK1   EQU   1
BANK2   EQU   0
BANK3   EQU   0

...

  IF BANK1
       LI    R1,>6006              ; Select BANK0
       LI    R2,B0TMGR             ; Jump to TMGR
       B     @GOBANK               ; Switch bank 0 (without page-out)
  ENDIF


Link to comment
Share on other sites

Conditional compilation does work with the mentioned assembler. Here's some code I used in Pitfall. You get the idea:

 

***************************************************************
* Some build directives
********@*****@*********************@**************************
DISK EQU 0
CART EQU 1
BANK0 EQU 0
BANK1 EQU 1
BANK2 EQU 0
BANK3 EQU 0

...

IF BANK1
LI R1,>6006 ; Select BANK0
LI R2,B0TMGR ; Jump to TMGR
B @GOBANK ; Switch bank 0 (without page-out)
ENDIF


 

Thanks for the info. I definitely need to obfuscate the Forth conditional labels. I will do that and re-assemble to see if anything changes.

 

...lee

Link to comment
Share on other sites

COLD is re-coded. I was going to remove two of the three resets of the FORTH vocabulary fields until I realized that it is through COLD that they are set for the first time! The reason I was about to remove the resets of the pseudo name field and the chronological link field is that they are never changed from their initial values. The only field that changes is the vocabulary link field, which changes for every word that is added to the FORTH vocabulary.

 

If anybody cares, I will explain in some detail how vocabularies work in TI Forth and, now, fbForth.

 

...lee

Link to comment
Share on other sites

Yes, I would very much like to know. If I have time I'll implement something similar in TF. In TF it would have to be a loadable option, as the ROMs are jam-packed full!

 

Vocabularies in TI Forth and fbForth—

 

The FORTH Encyclopedia: The complete FORTH Programmer's Manual by Mitch Derick and Linda Baker explains vocabularies very well in their discussion of VOCABULARY , the vocabulary-defining word. There are a few errors in the discussion, one in the text and a couple in Figure VOC-4; but, it is still quite clear. There was apparently a second edition of the book that may well have corrected the above-mentioned errors; but, I can't find it anywhere.

 

I am not sure exactly how you would implement vocabularies in TurboForth without mucking with the cartridge because words that are in the cartridge need to be modified to implement vocabularies. I suppose you could figure out a way to patch them. TI Forth words in the kernel that use vocabulary-related words are VOCABULARY , FORGET , CREATE , -FIND , FORTH , COLD , BOOT , DEFINITIONS , BLOAD , <CLOAD> , LATEST , ABORT and the user variables, CONTEXT , CURRENT , VOC-LINK and FORTH_LINK (unique to TI Forth, I think).

 

CONTEXT points to the vocabulary where dictionary searches begin in that it contains the address of the relevant vocabulary's VLF (see below). You set the context vocabulary by executing the name of the vocabulary. Forth is always the main or topmost vocabulary and CONTEXT is set to it by executing FORTH .

 

CURRENT points to the vocabulary (via its VLF) with which new definitions will be associated. Executing the word DEFINITIONS makes the context vocabulary also the current vocabulary by copying the contents of CONTEXT to CURRENT .

 

Vocabulary name words ( FORTH , ASSEMBLER , EDITOR1 , EDITOR2 ) are special words with two sets of links. They are normal Forth words in that they have a link field (LF), a name field (NF), a code field (CF) and a parameter field (PF). In TI Forth (and, consequently, fbForth), these fields are in the named order. (TurboForth reverses the order of LF and NF.) The second set of links are the vocabulary links and populate the PF. There are only three of them: a vocabulary link field (VLF), a pseudo name field (PNF) and a chronological link field (CLF), in that order. (TurboForth would need to reverse the order of the first two fields.)

 

When VOCABULARY creates a new vocabulary name, it points the new name's VLF to the PNF of the current vocabulary by adding 2 to the contents of CURRENT to get the address of the PNF of the current vocabulary. It creates a PNF, which is always 81A0h, a perfectly legal but impossible-to-find name because it consists of a single space. This is visible when you strip the terminator bits (80h) from both bytes: 0120h (first byte is count=1 and the second is 20h=<space>). It then points the new CLF to the CLF of the most recently defined vocabulary prior to this one by copying the contents of VOC-LINK , which always contains the CLF of the most recently defined vocabulary, thus linking all vocabularies chronologically.

 

There is one problem I have with the defining word VOCABULARY and that is that the programmer must remember to execute IMMEDIATE just after defining the new vocabulary because that is the convention. I am not sure why you would want it otherwise, so I am inclined to set the precedence bit from within the definition of VOCABULARY .

 

Now for the nitty gritty of vocabularies: You will recall that, upon creation, a vocabulary's VLF points to its parent vocabulary's PNF. The reason will obtain shortly. When CREATE creates the header for a new word within a current vocabulary, it gets what the vocabulary's VLF is pointing to via LATEST , which is defined as CURRENT @ @ , and copies that to the new word's LF and copies the address of the new word's NF to the VLF via CURRENT @ ! . This creates a chain for the current vocabulary with the VLF always pointing to the most recently created word in that vocabulary (retrievable by LATEST ) and the first word pointing to the current vocabulary's parent's PNF, thus maintaining the vocabulary search chain.

 

This chaining works because, during a name search, -FIND knows to jump to the LF of the current unmatched word by subtracting 2 from the NFA to find the next word up the chain. It doesn't know that sometimes it's looking at a PNF instead of a true NF. It only knows what to do if the name doesn't match. When the first word in the vocabulary is reached without a match, -FIND continues with the next vocabulary up the chain, going directly from its PNF to the last word in that parent vocabulary and then working up to the head of that vocabulary until it runs out of words and vocabularies. At that point, after exhausting the context vocabulary chain, it starts over with the current vocabulary as the starting point before giving up altogether. In this process, the Forth vocabulary, at the very least, gets searched twice.

 

That's it for now. I'll explain how the Forth vocabulary in TI Forth is handled differently from other vocabularies in a later, shorter post.

 

...lee

Edited by Lee Stewart
  • Like 1
Link to comment
Share on other sites

OK...the Forth vocabulary is not defined using VOCABULARY as is every other vocabulary. Instead, three user variables are used to store the VLF, PNF and CLF for the Forth vocabulary. (See last post for abbreviations.) They are referenced through FORTH_LINK , which stores the CLF=0 to indicate the top of the heap. The other two link fields are in their proper order as the two unnamed user variables immediately preceding FORTH_LINK and are referenced by subtracting 2 and 4 from FORTH_LINK . Besides needing to initialize all three fields at startup and every other time COLD is invoked, COLD also makes a duplicate copy of the last dictionary entry ( TASK ) in the resident dictionary (kernel) to the end of the of the dictionary after a 14-byte pad of zeroes and sets FENCE to the NFA of that last copy of TASK . I can only guess that the reason for this duplication has to do with the BLOAD / BSAVE mechanism—but, I digress.

 

I suppose that the Forth vocabulary may have been handled as described above in anticipation of producing a cartridge, which would require any modifications to be made in RAM. If FORTH were created with VOCABULARY , FORTH 's VLF (1st cell of PF) would be changed frequently—not possible in ROM. I still may change it. It would save 21 bytes—not a lot, but every little bit counts!

 

...lee

  • Like 1
Link to comment
Share on other sites

Thanks Lee, good stuff. I'll copy this to my laptop so that I can digest it offline.

 

Regarding implementation of vocabs on cartridge based systems, I was planning that all ROM supplied words would be in the FORTH vocabulary, but, unlike the de-facto way of doing things, where user code simply extends the FORTH vocab (unless another vocab is created/invoked) all user code would go into a USER vocabulary. The FORTH vocab would be essentially sealed, since it's in ROM.

 

However, I can't remember how I left things... The current build of TF that I have on my laptop has some of the plumbing in there. CURRENT and CONTEXT, and FIND has been modified to search the current and context vocabs. IIRC HEADER (the word that creates dictionary headers and links them to their predecessor) is also modified. (Colon, CREATE, VARIABLE, VALUE, CONSTANT, etc (any defining word) all use HEADER to lay down a dictionary header).

 

I'll have to get back into it as it's the last thing I wanted to get into TF V1.x. I'd already determined that there wasn't room for it all, so I only intend to have the minimal plumbing work in there, so that I can implement the rest in Forth as a loadable extension.

 

We'll see. If it's too much work, then I'll just release it as it is.

 

I also looked at an alternative system to vocabs, called Applications. (Just posting the following out of interest).

 

I was looking at a system where, instead of all words being available to everything, some concept of encapsulation would permit seperate applications to be in memory, rather than one huge Forth dictionary (which is what TF currently is). Dictionary search speed wasn't the driver - FIND in TF is mega quick, but typing WORDS after loading a few applications into memory would result in a screen dump of 500 or more words! A lot of those words are only useful in the context of the application in which they are defined, they're not really useful in a stand-alone context, so it doesn't make sense to list them.

 

Now, it's possible to hide words in TF but I thought the concept of an application would be nice. It would work like this:

 

APPLICATION GAME

: stuff < code > ;

: more-stuff <code> ;

: etc ... ;

: etc ... ;

: RUN ... ;

END-APPLICATION

 

When you type WORDS, you get a list of all the words in the FORTH namespace (all the words in ROM) and also an application list. The word END-APPLICATION links the last defined word in the application to the name of the application. So, in the example above, the word GAME invokes the word RUN. So, I can run GAME by typing:

 

GAME

 

That was the idea. I have some notes and diagrams somewhere but never got around to implementing it. I worked out most of the implentation details IIRC. However, I've done very little Forth this year - I'm kind of burned out so taking some down-time! My guitars have getting some attention instead! :)

  • Like 1
Link to comment
Share on other sites

I'm having some serious trouble with asm994a v3.010. It's giving me DORGed addresses as relocatable!!! I DORGed a label at >832E and it put it at >232E! What am I doing wrong? Is there a workaround? @Willsy? @Tursi? @matthew180? @retroclouds?

 

...lee

 

Sorry for the frantic plea. I got asm994a to cooperate by putting an AORG ahead of it (there was one already immediately following the DORG section)—shouldn't need to do that, but it worked.

 

...lee

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...