Jump to content
IGNORED

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


Lee Stewart

Recommended Posts

I need to do more work on ASM>CODE to make it more useful. Right now, it emits the proper code; but, it cannot be used to load the relevant machine-coded words without adding , after each hex number (machine code) to compile said numbers into the definition (see notes added to post #1175).

 

...lee

Link to comment
Share on other sites

I get excited about little victories in Forth coding; but, they are lonely victories because there is simply not a lot of interest in Forth, here. Sometimes that is a little discouraging; but, for the most part, the fun I have developing the fbForth system in high-level Forth and TMS9900 Assembler is all I need. Anyway, my current little victory has to do with making it possible to use the same words as TurboForth for incorporating machine code in the body of an fbForth definition, viz., CODE: and ;CODE . The reason I thought I could not use ;CODE was that fbForth already has such a word for another purpose and I feel the need to keep that use for compatibility with TI Forth.

 

I have managed to define ;CODE as a dual use word. That is, it can now be used for both CODE: ... ;CODE as well as its original use in writing defining words with : DEFINING-WORD <BUILDS … ;CODE ... NEXT, . The discriminator turns out to be the compilation state of the word being defined. CODE: ... ;CODE is executing during the definition; whereas, : DEFINING-WORD <BUILDS … ;CODE ... NEXT, is compiling from : until after ;CODE . The commented definitions of CODE: and ;CODE follow:

 

 

( Machine coded ALC words..LES22DEC2015)
HEX
: CODE: ( -- )
?EXEC ( check that we are not compiling)
CREATE SMUDGE ( create word header and make word visible)
BEGIN ( infinite loop that is exited by ;CODE)
IN @ ( save our place in the input stream, IS)
BL WORD ( get next token to HERE)
0 0 HERE (NUMBER) ( see if it's a number)
C@ BL - IF ( conversion failed? see if it's a word)
DROP DROP ( discard double number)
IN ! ( restore IN for ' )
[COMPILE] ' CFA EXECUTE ( execute found word)
ELSE ( successful conversion)
DROP , ( make it a 16-bit number and compile it)
DROP ( drop previous value of IN)
THEN
AGAIN ; ( get next number in input stream)
( ;CODE is now a synonym for DOES>ASM: and terminator for CODE:)
: ;CODE ( -- )
STATE @ IF ( are we compiling?)
[COMPILE] DOES>ASM: ( invoke DOES>ASM: )
ELSE ( are we executing?)
045F , ( compile B *NEXT )
R> R> DROP >R ( do not return to CODE:)
THEN ; IMMEDIATE

 

 

Basically, CODE: is an interpreter with an infinite loop, just like INTERPRET , the Forth outer (text) interpreter. Whereas INTERPRET first checks whether the next token is a Forth word, CODE: first tries to convert the next token to a number. If CODE: fails number conversion, it looks up the word and executes it if it is found. The only way out of CODE: 's infinite loop is to execute ;CODE , which removes the return to CODE: from the return stack.

 

The only problem I have now is that I must whittle down some fbForth system code if I am ever to fit these two words into ROM! They require about 100 bytes. In the meantime, they can be loaded each time they are needed. I think I will modify ASM>CODE to presume their use. The resulting word definitions will load much faster than the current CODE ... NEXT, solution (from TI Forth) because each and every number after CODE causes INTERPRET to look through more than 400 words before it gives up and converts it to a number. Then, of course, there is the need for , after each number to compile the number into the definition.

 

...lee

Link to comment
Share on other sites

Posting the code for CODE: ... ;CODE in post #1178 was a little premature. It will only work for a single line. | :) As soon as the definition goes to another line, CODE: loses control of the input and execution of ;CODE puts fbForth into the weeds! :-o The following version works for multiple lines as well as when loaded from a blocks file:

 

( Machine coded ALC words revised..LES07APR2016)
HEX
: getCODE ( -- )
BEGIN ( infinite loop that is exited by ;CODE)
IN @ ( save our place in the input stream, IS)
BL WORD ( get next token to HERE)
0 0 HERE (NUMBER) ( see if it's a number)
C@ BL - IF ( conversion failed? see if it's a word)
DROP DROP ( discard double number)
IN ! ( restore IN for ' )
[COMPILE] ' ( get word's PFA)
CFA EXECUTE ( no; execute found word)
ELSE ( successful conversion)
DROP , ( make it a 16-bit number and compile it)
DROP ( drop previous value of IN)
THEN
AGAIN ; ( get next number in input stream)
: CODE: ( -- C0DEh)
?EXEC ( check that we are not compiling)
CREATE SMUDGE ( create word header and make word visible)
C0DE ( leave magic number, C0DEh, on stack for ;CODE)
BEGIN
getCODE ( get code until ;CODE)
BLK @ 0= IF
." ok:" DEPTH .
CR QUERY
THEN
AGAIN ; IMMEDIATE
( ;CODE is now a synonym for DOES>ASM: and terminator for CODE:)
: ;CODE ( -- )
STATE @ IF ( are we compiling?)
[COMPILE] DOES>ASM: ( invoke DOES>ASM: )
ELSE ( are we executing?)
DEPTH IF ( DEPTH > 0?)
C0DE = IF ( magic number, C0DEh, on stack?)
045F , ( compile B *NEXT )
R> R> DROP DROP ( do not return to getCODE or CODE:)
ELSE
0 ERROR ( ERROR!! not paired with CODE:)
THEN
ELSE
0 ERROR ( ERROR!! not paired with CODE:)
THEN
THEN
; IMMEDIATE
DECIMAL

In the above code, getCODE acts like INTERPRET , as explained in my previous post for CODE: . The infinite loop in CODE: now acts like QUIT and LOAD . Because ;CODE 's execution now occurs two levels down, it must pop two return addresses from the return stack to exit CODE: . Multi-line CODE: ... ;CODE definitions entered from the terminal or loaded from a blocks file now compile successfully. The code for these words is now up to 160 bytes, which makes it less likely I will find enough room to shoehorn it into ROM space. One way I might get away with it would be to remove the default font from ROM and load it from a font file as is done now for user fonts. This would save 1024 bytes! :cool: The obvious question is, “Should I do it?”

 

...lee

 

[EDIT: Corrected the above code to have ;CODE check that CODE: was how ;CODE was executed. Otherwise, ;CODE issued at the command line will hang the system. Also, the penultimate line of ;CODE was not dropping the correct addresses from the return stack!]

Edited by Lee Stewart
Link to comment
Share on other sites

That depends on how much you need that 1024 bytes, Lee. I'd say if you need more than half of that space, do it for sure--otherwise, you could go either way with it and it won't cause much in the way of problems, especially since the User fonts are already being loaded that way. . .

Link to comment
Share on other sites

I personally like that the fbForth cartridge doesn't require any of the other files distributed with it. The blocks file I use is usually on the HDX1. drive/pc. It is nice that I can get into forth without loading anything from disk.

 

From what I can tell of the CODE: and ;CODE words, they optimize compiling words that are mostly composed of numbers as in machine code.

 

So I would be cautious of trading an always used thing for a sometimes used thing.

 

I'm hoping to get more time for fbForth after the new year.

Link to comment
Share on other sites

That depends on how much you need that 1024 bytes, Lee. I'd say if you need more than half of that space, do it for sure--otherwise, you could go either way with it and it won't cause much in the way of problems, especially since the User fonts are already being loaded that way. . .

 

I would probably only need about 20% right now. And, I agree about fonts. If I go that route, I should probably try to load the default font from the same disk where the user loads FBLOCKS if s/he holds down a key for a drive other than DSK1. In the event the font file cannot be found, lowercase would be the classic, small-caps font in console GROM #0.

 

It might be better as a BLOCK that the user can load should he/she need it.

 

What “might be better as a BLOCK that the user can load,” the default font (would only require one block!—H-m-m-m...) or the CODE: ... ;CODE words?

 

...lee

Link to comment
Share on other sites

I personally like that the fbForth cartridge doesn't require any of the other files distributed with it. The blocks file I use is usually on the HDX1. drive/pc. It is nice that I can get into forth without loading anything from disk.

 

Good point.

 

From what I can tell of the CODE: and ;CODE words, they optimize compiling words that are mostly composed of numbers as in machine code.

 

That is true. I could certainly manage machine-code loading, such as the CRU words in block #5 of FBLOCKS, by including a conditional load of a block with the requisite CODE: ... ;CODE words. In Classic99, block #5 takes just over 7 seconds to load. Coded with CODE: and ;CODE , it takes only 2.5 seconds. Though nearly 3 times faster, the slower time is not very significant. It would only really matter with multi-block loads of blocks dense with machine code—like @Willsy's double-precision-integer-math library for TurboForth.

 

...lee

Link to comment
Share on other sites

If you try to stuff every word definition into the Forth kernel, you end up with surplusage that most users will only infrequently use at best. Users should have the option of NOT having every word loaded at start-up but still have the facility to load the infrequently used ones from FBLOCKS if needed.

 

The current 4 ROMs is at the limit for the HSGPL, for example, and anything above that size would make FbForth a cartridge-only effort. That might be a bit esoteric to most but it would be a minor consideration to me.

Link to comment
Share on other sites

One name that I know was in there was Mike Bunyard. If you look at the schematic for the sidecar 32K device, his name is in the designer's position (I recognize the signature). Those schematics also identify other team members, as different people were in different roles, depending on which item it was. If you look at all of the documents on WHT, you will eventually be able to identify other people too. The initial set MLB (which shows up in many of the documents), is Michael L. Bunyard. I know that a guy named Pete Jaden was also on the 99/8 team with Mike Bunyard. Mike mentioned several other folks that were involved back then, but I failed to write them down during our long phone conversations. . .

Link to comment
Share on other sites

  • 2 weeks later...

I have not been doing much Forth, lately; so, I thought I would revisit the CODE: ... ;CODE words of post #1180 to see whether they needed any improvement. The only problem I see (so far) is with ;CODE when pasting Forth code using it into an emulator at the command line. If an error occurs after CODE: and the closing ;CODE is not on the same line, the later execution of ;CODE will send fbForth into the weeds.

 

This is not a problem with loading the same code from a blocks file because the error will cause the load to abort before ;CODE can be executed.

 

It should also not be a problem on the command line because the programmer will see the error and should see that it is pointless to continue with the ALC definition until s/he has fixed it. If ;CODE gets typed on the same line as the code that will cause the error, the line’s interpretation will be aborted before ;CODE can get executed.

 

The additional code to trap the pasting error for an emulator is 22 bytes (currently, the code is 160 bytes). Since the problem should not occur on the real iron, I am wondering whether I should bother with it.

 

...lee

Link to comment
Share on other sites

I personally like that the fbForth cartridge doesn't require any of the other files distributed with it. The blocks file I use is usually on the HDX1. drive/pc. It is nice that I can get into forth without loading anything from disk.

 

I am thinking seriously of putting the default font in a block in FBLOCKS. If I go that route, I will, upon failure to find FBLOCKS, load the console font with its small caps for lowercase as shown here

 

post-29677-0-54694100-1452728990_thumb.png

 

...lee

  • Like 1
Link to comment
Share on other sites

If you try to stuff every word definition into the Forth kernel, you end up with surplusage that most users will only infrequently use at best. Users should have the option of NOT having every word loaded at start-up but still have the facility to load the infrequently used ones from FBLOCKS if needed.

 

The current 4 ROMs is at the limit for the HSGPL, for example, and anything above that size would make FbForth a cartridge-only effort. That might be a bit esoteric to most but it would be a minor consideration to me.

 

The advantage to having as many words in the kernel as possible is that there is more room in CPU RAM for user words. It is certainly true that a given user might not need, say, the floating point library; but, for those who do, it would mean losing RAM space because it was left out of the ROM kernel.

 

The disadvantage to having so many words in the kernel is the increased search time during interpretation at the command line or when loading blocks. The only way I can think of to mitigate this situation for those who do not need the full dictionary is to devise a system of compartmentalizing the dictionary such that optional segments can be linked in and out of the search space. I am not sure it is worth the effort, but ... :ponder:

 

...lee

Link to comment
Share on other sites

Just put them in vocabularies. So, if you had the float library in ROM, for example, it would be invisible until you invoke the float vocabulary.

 

I did think of that. And, that is probably the best way to do it. The only problem with using vocabularies is their order. If they are set up to all be connected to the FORTH vocabulary, they will not be directly available without invoking each vocabulary in turn. Let us say that we define the following vocabularies: FLOAT , for the floating point (FP) library and GRAPH , for the graphics primitives. If we need any FP operations while in the GRAPH vocabulary, FLOAT would need to be invoked for the FP operation(s) and then GRAPH , as soon as a continuation of graphics commands is needed. That might get messy.

 

They could, of course, be chained; but then, the vocabulary order chosen by Yours Truly might be inconvenient for any given user.

 

I would also need to rewrite BSAVE and BLOAD to explicitly save/restore the state of all cartridge-resident vocabularies.

 

...food for thought, certainly. :ponder:

 

...lee

Link to comment
Share on other sites

  • 2 months later...

I am thinking seriously of putting the default font in a block in FBLOCKS. If I go that route, I will, upon failure to find FBLOCKS, load the console font with its small caps for lowercase as shown here

attachicon.gifSmCaps.png

That is a seriously good alternative :)

 

We’re on the same wavelength this week! I was just going through code to see where I could rearrange things to make room in bank 0 for the additional code fields and parameter fields I will need.

 

Once I free up enough space, I need to work out the best way to manage the default font.

 

If I expect it to be in the current blocks file in, say, block #5, it would need to be in block #5 in every blocks file the user invokes—much as the requirement in TI Forth for system messages to be in blocks #4 and $5 for any disk in DSK1. I don’t like to hardwire block numbers into ROM code (except for block #1), so I would probably set up a user variable for this purpose. Because I don’t need the first 256 bytes of the current default font, I could set up a validation code at the start of the block and fail over to the console font if is not found.

 

Or—I could use a default font file on the boot disk—DEF_FONT, or some such.

 

Or—?

 

...lee

Link to comment
Share on other sites

We’re on the same wavelength this week! I was just going through code to see where I could rearrange things to make room in bank 0 for the additional code fields and parameter fields I will need.

 

Once I free up enough space, I need to work out the best way to manage the default font.

 

If I expect it to be in the current blocks file in, say, block #5, it would need to be in block #5 in every blocks file the user invokes—much as the requirement in TI Forth for system messages to be in blocks #4 and $5 for any disk in DSK1. I don’t like to hardwire block numbers into ROM code (except for block #1), so I would probably set up a user variable for this purpose. Because I don’t need the first 256 bytes of the current default font, I could set up a validation code at the start of the block and fail over to the console font if is not found.

 

Or—I could use a default font file on the boot disk—DEF_FONT, or some such.

 

Or—?

 

...lee

A user variable wouldn't be set until after getting into block #1 so I'm not sure how that would be used. A word that loads the font from a specified block, would be just as easy to call. Would that user variable be preserved through a cold or something? Or used during the call to graphics or text?

 

I like the idea of checking the block for a header as a little validation.

 

-M@

Link to comment
Share on other sites

A user variable wouldn't be set until after getting into block #1 so I'm not sure how that would be used. A word that loads the font from a specified block, would be just as easy to call. Would that user variable be preserved through a cold or something? Or used during the call to graphics or text?

 

I like the idea of checking the block for a header as a little validation.

 

-M@

Actually, a user variable that is defined in the kernel (which is how I would do it) usually has a default value assigned before ever loading a block. I will set it up like the VDP mode colors to survive COLD .

 

FNT is called for every change in VDP mode so, yes, this particular user variable would get used.

 

...lee

Link to comment
Share on other sites

Actually, a user variable that is defined in the kernel (which is how I would do it) usually has a default value assigned before ever loading a block. I will set it up like the VDP mode colors to survive COLD .

 

FNT is called for every change in VDP mode so, yes, this particular user variable would get used.

 

...lee

Nice! I get now.

  1. If I'm using your fblocks, I just don't worry about it.
  2. If I'm creating an standalone blocks file, and I have have the font block in block #5, I don't have to do anything...
  3. If I want the font block in block #2 or something, I just set the user variable in block #1, then set my graphics mode like I already do and away we go.
  4. If I don't want the runtime cost of loading from disk, (maybe I change graphics modes frequently) I just set the user variable to block #1 ( not a font block ) and we'll get the console font when changing graphics modes.

In development workflow, where I switch between the editor in text80, and mode 2 graphics, it might be nice to be able to effectively disable the behavior of FNT, maybe if the user variable can be set to indicate no font change, or explicitly set to load console font, behavior can be more deliberate. The last one doesn't eliminate the need to fallback if a valid block number is specified. Just offering an idea: maybe -1 is disables the behavior of FNT, and 0 explicitly loads the console font, and any other value attempts to load from the block file, falling back on console font if the block isn't a font block.

 

-M@

Link to comment
Share on other sites

I had another interesting idea, I expect everyone has thought of before, but with carts like the ubergrom, fbForth could be in the eeprom, and what if there was a bit in the rom that could be set so it would bload a range of blocks from GROM in the ubergrom atmega chip?

 

I'm not (hopefully) proposing any major change in structure to fbForth. It could continue to own the ROM and ROM banking.

 

The ubergrom seems like nice development aid. Given that it has GRAM space that could be used during development, something like having GSAVE tools on the fblocks tool set that would be like a BSAVE to GRAM, to test with. I haven't thought this through too deeply, but large things could be built using BSAVE swapping techniques Lee outlined in the fbForth manual, and all loaded fast off of cartridge. I imagine the GRAM space would be handy during development, but the GROM space would make more sense to solidify the code into. The GRAM could be useful for an individual app too.

 

I suppose something similar could be done from from extended banks on the ROM side itself, but I imagine that is complicated by the bank switching already in fbForth.

 

I imagine the only change required to fbForth would be a setting in the rom that can be set indicating to bload block 1 or a range of blocks from a grom address instead of from disk. Then the loaded block(s) can take control. ( maybe bload for block 1 isn't right, cause you problably want to execute some word(s) automatically. ) But hopefully I've communicated the concept.

 

Just an idea.

 

-M@

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   1 member

×
×
  • Create New...