Jump to content
IGNORED

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


Lee Stewart

Recommended Posts

Brief aside... When the TI Forth assembler package is loaded, Im assuming the assembly code will utilize Forth's workspace registers... Did you have to modify any plumbing to allow for access in this area?

 

Additionally, IIRC, the assembly mnemonic "C" caused conflicts with the pre-existing Forth vocabulary. Any chance you modified or COULD modify this in the source to allow for C to be used in the assembler vocabulary?

Link to comment
Share on other sites

Brief aside... When the TI Forth assembler package is loaded, Im assuming the assembly code will utilize Forth's workspace registers...

 

Yes, but you can set up your own workspace.

 

Did you have to modify any plumbing to allow for access in this area?

 

No special plumbing. I haven't yet tested the Forth Assembler—will soon. It should work the same as it does in TI Forth because it's basically the same code. The only changes I've made are to extend the Forth register convenience words to all registers R0 – R15 and to implement what @Willsy did in TurboForth regarding the easier-to-use Wycove mnemonics.

 

Additionally, IIRC, the assembly mnemonic "C" caused conflicts with the pre-existing Forth vocabulary. Any chance you modified or COULD modify this in the source to allow for C to be used in the assembler vocabulary?

 

The "C" (compare words) TMS9900 Assembler mnemonic is implemented in the Forth Assembler vocabulary as C, . There is really no conflict with the Forth vocabulary's C, ("C comma" or "byte compile") unless you want to use "byte compile" in the Assembler vocabulary, i.e., after CODE or ;CODE , in which case you can define another word in the Forth vocabulary that duplicates the function of C, such as : CC, C, ; . I cannot for the life of me, however, think of a situation where you would need it or couldn't use another simple construction to effect the same operation, i.e., compiling the low-order byte of a 16-bit cell to the dictionary. If you have the Forth Assembler loaded and you want to use the Forth vocabulary's C, normally, you only need to ensure that Forth is the context vocabulary (executing FORTH will do it) for that definition to be the one found by the interpreter.

 

...lee

Link to comment
Share on other sites

I'm trying to use my read/write blocks logic to handle reading from FBCHARS and FBMSGS while in the middle of interpreting a block from another blocks file, usually FBLOCKS, the system blocks file, but it could be any, including the terminal/console. I figure that I need to save the name of the current blocks file just before the switch is encountered, as well as the block number and the offset into the block. Once I'm through with the diversion, I would then "reopen" the interrupted file and restore the correct block and offset. Of course, the current blocks file may not actually be taking part in the above, but it still needs to be restored to "current" status because the interlopers must be made "current" to work with the referenced read/write logic. Restoring the correct block will resolve the matter: If it's block #0, the interpreter was operating on the terminal input block, TIB, and it knows how to deal with that eventuality. I have written 2 words that save ( SAVBFL ) and restore ( RSTBFL ) the current blocks file. I am trying to economize on the code as much as possible, so I am wondering whether including the save/restore for BLK , the currently interpreting block, and IN , the offset into that block of the next character to be interpreted will have any adverse effect if the interpreter is not involved. I'm thinking not, but I'm not sure. I will certainly test it—this rambling is just my out-loud thinking; but, who knows, maybe I'll get some useful suggestions that haven't yet occurred to me!

 

...lee

Link to comment
Share on other sites

Additionally, IIRC, the assembly mnemonic "C" caused conflicts with the pre-existing Forth vocabulary. Any chance you modified or COULD modify this in the source to allow for C to be used in the assembler vocabulary?

 

 

That's a TurboForth quirk. fbForth/TI Forth aren't affected by this.

 

There is a convention when coding assembly in Forth to place a comma at the end of each assembly mnemonic:

 

LI,

MOV,

SWPB,

 

The reason for this is related to the word , ("comma") in Forth, which takes a value from the stack, and writes it to the current compilation address (called HERE in TF).

 

Try this is TF under classic99:

 

Start it up. Hold ENTER to bypass the disk boot.

 

Type HERE $.

(you'll get a value shown in HEX)

 

Open the Classic99 debugger, and on the CPU memory page, set the address you want to look at to the hex number that TF gave you.

 

Now type

 

$DEAD , $FACE ,

 

(remember the spaces!)

 

Now look in the debugger.

 

Now type HERE $.

 

You'll see that HERE has advanced by 4 bytes, since you wrote 4 bytes to memory.

 

Now type:

 

: TEST 1 2 3 ;

 

Look in the debugger. DEAD FACE is still there, because you "compiled" them to memory using comma.

 

To put it another way (this is getting long-winded, sorry) - the comma word caused a write to memory and always causes a write to memory.

 

Now, in the assembler, assembler mnemonics are assembled/compiled to memory at the point at which they are encountered - remember, the arguments to assembly mnemonics precede the assembler instruction when doing assembly code in Forth. So the comma is simply a reminder that your instruction will be assembled at that point. Just a convention.

 

Now, TF doesn't support vocabularies, unlike TI/fbForth, so in TF the word C, which compiles a byte to memory (it's not a POKE!) would clash with the assembly instruction C, which is the mmnemonic for compare.

 

So, in TF I renamed the assembler word C, to CMP - which I actually think is much clearer than TI's crappy "C" mnemonic anway.

 

Phew!

 

Sorry for the hijack, Lee. Back to the fbForth stuff.

 

Mark

Link to comment
Share on other sites

I'm trying to use my read/write blocks logic to handle reading from FBCHARS and FBMSGS while in the middle of interpreting a block from another blocks file, usually FBLOCKS, the system blocks file, but it could be any, including the terminal/console.

You're going to get that eventuality at boot-time aren't you, because, AIUI at boot time the FBCHARS block will be called by a line of code in FBLOCKS.

I figure that I need to save the name of the current blocks file just before the switch is encountered, as well as the block number and the offset into the block.

Yes, if you’re going to re-load the resuming block via BLOCK. This will already be taken care of by WORD (probably) in the original TI Forth code – WORD will check BLOCK and call it if BLK !=0.

Once I'm through with the diversion, I would then "reopen" the interrupted file and restore the correct block and offset.

Probably don’t need to re-open it, as I reckon BLOCK will open the file, do it’s thang, then close it again. So you just need to restore the “context” (filename et al, as you described).

I am trying to economize on the code as much as possible, so I am wondering whether including the save/restore for BLK , the currently interpreting block, and IN , the offset into that block of the next character to be interpreted will have any adverse effect if the interpreter is not involved. I'm thinking not, but I'm not sure. I will certainly test it—this rambling is just my out-loud thinking; but, who knows, maybe I'll get some useful suggestions that haven't yet occurred to me!

I think you’re probably right. However, not saving them in the event that original block was 0 (i.e. terminal) means extra code both in SAVBFL and RSTBFL to determine *what* to save, and of course, what to restore. So what you save at runtime in terms of stack or heap space, you pay for in the size of your executable!

Link to comment
Share on other sites

... So, in TF I renamed the assembler word C, to CMP - which I actually think is much clearer than TI's crappy "C" mnemonic anyway.

 

I agree. The only thing keeping me from doing the same is the mountain <tongue firmly planted in cheek> of code I would orphan. I suppose it wouldn't hurt to define CMP, to accommodate those familiar with TurboForth's "compare" word; but, I don't think I should eliminate the fbForth Assembler's C, definition.

 

Phew!

 

Sorry for the hijack, Lee. Back to the fbForth stuff.

 

Mark

 

No problem! Virtually everything you said obtains for fbForth and TI Forth, as well. They are, after all, basically the same Assembler—yours with enhancements, some of which I lifted for fbForth. One enhancement you made that I will probably include in fbForth's version is the ASM: ... ;ASM pair for CODE ... NEXT, , which I would retain so as not to orphan the "you know what".

 

I would also like to design a clever, similar code pair for ;CODE ... NEXT, . The only ideas that come to mind are ;ASM: ... ;ASM , ASM:DOES> ... ;ASM , or, perhaps, DOES>ASM: ... ;ASM , which would then be used in the following construction:

 

: NEW_DEFINING_WORD

<BUILDS

<fbForth assembly mnemonics>

DOES>ASM:

<fbForth assembly mnemonics>

;ASM

 

However, if I were to do that, I would need to define ;ASM in the kernel to allow its use with machine code without needing to load the fbForth Assembler, as I've done with CODE and ;CODE . I should do that with NEXT, , anyway. In fact, I've been contemplating an improvement to the fbForth Assembler that would save and restore the context vocabulary between CODE ... NEXT, and ;CODE ... NEXT, because both CODE and ;CODE change the context vocabulary to ASSEMBLER so that the Assembler code works; but, nothing restores the context vocabulary unless the programmer does it explicitly (not quite true—see Post #114). This problem becomes apparent in two situations:

  1. The C, conflict mentioned above with the Assembler loaded will manifest itself when you have finished defining new words with CODE or ;CODE . You would expect that your next use of C, would pop one cell from the stack, store the low-order byte at HERE and advance the dictionary pointer one byte. Surprise! You just executed the Assembler's C, , which pops two cells from the stack, stores them and the TMS 9900's "C" instruction at HERE , advancing the dictionary pointer 2, 4 or 6 bytes, depending on the comparison.
  2. If you happen to be defining words with CODE or ;CODE in a vocabulary that is parallel to the Assembler vocabulary and decide to reuse a word in the Forth vocabulary (not that unlikely—If you wanted to define, say, a calculator with infix notation, you might want to redefine the operators, + , - , ...), the reverse of (1) will bite you because the interpreter will search for the word first in the Assembler vocabulary and then in the Forth vocabulary, where the word will be found and executed before it can be found in the current (your new) vocabulary. This is due to the fact that -FIND searches the context vocabulary first, then the current vocabulary.

Gee, I'm glad we had this little talk! :-o :P

 

...lee

Link to comment
Share on other sites

You're going to get that eventuality at boot-time aren't you, because, AIUI at boot time the FBCHARS block will be called by a line of code in FBLOCKS.

 

I may also do it in the ALC because, now that I'm using a single object file, I can use the resident dictionary, where the block read/write words reside. This may not be good programming practice, but I still may do it. :P

 

Yes, if you’re going to re-load the resuming block via BLOCK. This will already be taken care of by WORD (probably) in the original TI Forth code – WORD will check BLOCK and call it if BLK !=0.

 

In fbForth and TI Forth, WORD checks BLK to decide whether to call BLOCK or fetch the TIB address. It then uses IN to get the next space-delimited string. I think both BLK and IN will have changed in either event.

 

Probably don’t need to re-open it, as I reckon BLOCK will open the file, do it’s thang, then close it again. So you just need to restore the “context” (filename et al, as you described).

 

What he said! That's why I put "reopen" in quotes. The level-2 routines I'm using do not really open/close files. Your terminology is better.

 

I think you’re probably right. However, not saving them in the event that original block was 0 (i.e. terminal) means extra code both in SAVBFL and RSTBFL to determine *what* to save, and of course, what to restore. So what you save at runtime in terms of stack or heap space, you pay for in the size of your executable!

 

I still think I might need BLK and IN restored

 

...lee

Link to comment
Share on other sites

Phew! :-o I think I'm finally to the point where I don't need to modify the kernel any more. HERE is 170 bytes greater than TI Forth, but with more functionality. If I revamp the startup menu, HERE will actually wind up at a lower address!

 

And now, onto some serious rewriting and testing of the system blocks file. Of course, I also need to do more extensive testing of my additions to the kernel; but, it does seem to be working with separate message and character-set files. When I'm done, the system will have 4 files: FBFORTH (compressed object file), FBLOCKS (system blocks file), FBCHARS (true lowercase for text mode and 64-column editor) and FBMSGS (system messages file).

 

...lee

Link to comment
Share on other sites

I need some advice from any Forthers out there for the following changes I am making to the system blocks for fbForth as I import them from TI Forth:

  1. The string-store and screen-copy words loaded by -SCOPY that are for copying one or more blocks to another location are (!") , !" , SCOPY , SMOVE , { DTEST ,FORTH-COPY ,DISK-HEAD }. fbForth has no real use for the words in braces, so I won't be using them. This question has to do with the four words listed first. I am basically replacing SCOPY and SMOVE with CPYBLK , a word that functions exactly the way it does in @Willsy's TurboForth. Even were I to keep them, they would not function in the manner to which TI Forthers are accustomed—they would necessarily be limited to a single blocks file. Consequently, I am inclined to jettison them. The string-store words are, however, potentially very useful. They store a string at an address without a preceding character-count byte. TI created them to be used by DISK-HEAD , which we are not using in fbForth. I do not need it for CPYBLK . I'm torn.
     
  2. This one's primarily for @Willsy—TI Forth's BSAVE starts with a FLUSH . I am inclined to start with EMPTY-BUFFERS because of the potential for a user to change blocks files before executing BSAVE ; but, I noticed that, in TurboForth, you did neither.
     
  3. At startup, TI Forth defines 21 menu-related words to make it easier for users to load optional parts of the system. This consumes 444 bytes of dictionary space! For fbForth, I am inclined to define a handful of menu-related words to aid the user, much as @Willsy did for TurboForth. What do you think?

...lee

Link to comment
Share on other sites

This one's primarily for @Willsy—TI Forth's BSAVE starts with a FLUSH . I am inclined to either start with EMPTY-BUFFERS because of the potential for a user to change blocks files before executing BSAVE ; but, I noticed that, in TurboForth, you did neither.

 

Then that's an omission on my part, and EMPTY-BUFFERS shall be shoe-horned into BSAVE tout-suite!

 

At startup, TI Forth defines 21 menu-related words to make it easier for users to load optional parts of the system. This consumes 444 bytes of dictionary space! For fbForth, I am inclined to define a handful of menu-related words to aid the user, much as @Willsy did for TurboForth. What do you think?

 

If it were me, I would probably go down the TF route. It's actually quite handy, I type UTILS all the time to get a list of the available utilities and their block numbers.

 

For those that aren't familiar, Here's TF's screen after it boots (with the utilities disk in drive 1):

 

post-24932-0-30061900-1371329067_thumb.png

 

...and when you type UTILS you are presented with the following:

 

post-24932-0-67770700-1371329093_thumb.png

 

Lee is proposing a similar system. I think it's a good idea. Users will tend to customise their Forth environment anyway to load what they want at boot-up - that's the beauty of Forth - you can set the environment to suit your own needs, so loading 20 odd words at start-up is probably a waste of effort, as people will just tailor the boot sequence to their own needs, so go minimal!

Link to comment
Share on other sites

Just to clarify something re TF (it's probably the same with fbForth): The intention of the utilities disk is that it's a tool-kit. When you start your own project, you would create an appropriately sized blocks file on a new disk, and using CPYBLK from the utilities disk you would then copy the utilities (e.g. assembler, memory dump, local variables etc) to your new disk, and that becomes your project disk.

Link to comment
Share on other sites

... In fact, I've been contemplating an improvement to the fbForth Assembler that would save and restore the context vocabulary between CODE ... NEXT, and ;CODE ... NEXT, because both CODE and ;CODE change the context vocabulary to ASSEMBLER so that the Assembler code works; but, nothing restores the context vocabulary unless the programmer does it explicitly. ...

 

H-m-m-m—That is not quite right. Actually, : sets CONTEXT to CURRENT before calling CREATE . So, if the very next thing you did was to start a colon definition, all would likely be well. Perhaps, what I should do is to define NEXT, to set CONTEXT to CURRENT as the last thing it does. I cannot really think of a situation, where CONTEXT and CURRENT would not be the same when you start defining new words. It's just that CODE and ;CODE change CONTEXT to ensure finding Assembler words for the definition and I just think NEXT, should rectify the situation!

 

...lee

Link to comment
Share on other sites

I need someone looking over my shoulder here—particularly, @Willsy—to see whether I might be missing anything:

 

My synonym for CODE is ASM: . Here is the code for both—

 

: CODE

?EXEC CREATE SMUDGE LATEST PFA DUP CFA !

[COMPILE] ASSEMBLER ;

 

: ASM: CODE ;

 

ASM: appears to be working the same way CODE does.

 

My synonym for ;CODE is DOES>ASM: . Here is the code for both—

 

 

 

: ;CODE

?CSP COMPILE (;CODE) SMUDGE

[COMPILE] [ [COMPILE] ASSEMBLER ; IMMEDIATE

 

: DOES>ASM: ;CODE ; IMMEDIATE

 

Again, DOES>ASM: appears to be working the same way ;CODE does.

 

Anybody spot anything I should be worried about?

 

...lee

Link to comment
Share on other sites

@Willsy...

 

Can I get away with this assembly code for ;ASM in the source for fbForth?:

 

*

*** NEXT, *** 1st word in ASSEMBLER vocabulary

DATA FORTHP <--Link field points to PNF of FORTH

ASM001 DATA >854E,>4558,>54AC <--Name field

NEXTC DATA $+2 <--Code field

NEXTP LI TEMP0,>045F <--parameter field

MOV @>12(U),TEMP1 >12 is offset into USER variable table for DP (contains HERE )

MOV TEMP0,*TEMP1+

MOV TEMP1,@>12(U)

MOV @>4A(U),@>48(U) >4A and >48 are offsets into USER variable table for CURRENT and CONTEXT, respectively

B *NEXT

*

*** ;ASM *** 2nd and last word in ASSEMBLER vocabulary; points to NEXT,

* ...pointed to by ASSEMBLER as the last word defined in the

* ...ASSEMBLER vocabulary in the kernel

DATA ASM001 <--Link field

ASM002 DATA >843B,>4153,>4DA0 <--Name field

SASM DATA $+2 <--Code field

JMP NEXTP <--parameter field

 

...lee

Link to comment
Share on other sites

Lee,

 

The code looks okay to me - like you, I can't really think of a scenario where you would be writing code, and yet want CURRENT and CONTEXT to be pointing at different vocabularies.

 

Thanks, Mark...

 

Now, it's pretty much down to whipping FBLOCKS into shape. Then, I will post the package with a rudimentary document describing new and changed words. Then, y'all can have at it to see if you can break it! :P

 

...lee

Link to comment
Share on other sites

Almost ready to let y'all take a look at it; but, first had to fix a couple of things! One was a change in the program, which was easy to fix. The other has me stymied. I want to release a 90KB DSK image, but TI99dir won't copy the last file. It tells me the disk is full when I know it is not. The final file will leave only 2 sectors free, but it should work. Classic99 won't do it and I don't have the other emulators up and running properly, yet. Now what?!

 

...lee

Link to comment
Share on other sites

Are you sure about your sector count? Remember that there are two reserved sectors on the disk (0 and 1), plus every file must use a sector for its descriptor...

 

Yeah, I'm absolutely sure. I accounted for the VIB, FDIR and the 4 FDRs in the total. I managed it with Michael's TIImageTool. Apparently, Fred's TI99Dir doesn't like to fragment files, which is what is required to use up that much space.

 

...lee

  • Like 1
Link to comment
Share on other sites

I will post the beta version (v0.9) of fbForth tomorrow. I just need to put together a quick and dirty README file to document the critical changes from TI Forth. The system is working fine in Classic99 in FIAD mode. As seen from the previous post, I managed to put together a 90KB SSSD disk image of the system. I could only get the disk image to work on Win994a. Neither TI994w nor MESS liked it at all. I will try it on the real iron in a day or two with my nanoPEB.

 

...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...