Jump to content
IGNORED

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


Lee Stewart

Recommended Posts

Lee, you could potentially keep things the same--if you put an E/A grom in your cartridge using an ÜberGROM board. . .bonus there is that the cart then can become a dual-use item if you leave the option to boot it into the E/A. Gazoo's GPL loader could also be modified to help here, as it then guarantees the boot bank of the cartridge by setting it during the GPL power-up routines. I may be off-base here, but I hope the idea is of use.

 

Thanks, Jim. That's an option I hadn't considered. I still think I want to keep it to a 32KB – 64KB ROM; but, it certainly is worth considering. Thanks again. :)

 

...lee

Link to comment
Share on other sites

Regarding using MG's Univeral GPLLNK/DSRLNK (see following spoiler), I do not see where it checks >8350 for any error when DSRLNK is called with >A instead of >8.

 

 

 

*--------------------------------------------------------------------*
* GPLLNK- A universal GPLLNK - 6/21/85 - MG                          *
* This routine will work with any GROM library slot since it is      *
* indexed off of R13 in the GPLWS.  (It does require Mem Expansion)  *
* This GPLLNK does NOT require a module to be plugged into the       *
* GROM port so it will work with the Editor/Assembler,               *
* Mini Memory (with Mem Expansion), Extended Basic, the Myarc        *
* CALL LR("DSKx.xxx") or the CorComp Disk Manager Loaders.           *
* It saves and restores the current GROM Address in case you want    *
* to return back to GROM for Basic or Extended Basic CALL LINKs      *
* or to return to the loading module.                                *
*                                                                    *
*    ENTER: The same way as the E/A GPLLNK, i.e., BLWP @GPLLNK       *
*                                                 DATA >34           *
*                                                                    *
*    NOTES: Do Not REF GPLLNK when using this routine in your code.  *
*                                                                    *
* 70 Bytes - including the GPLLNK Workspace                          *
*--------------------------------------------------------------------*

GPLWS  EQU  >83E0               GPL workspace
GR4    EQU  GPLWS+8             GPL workspace R4
GR6    EQU  GPLWS+12            GPL workspace R6
STKPNT EQU  >8373               GPL Stack pointer
LDGADD EQU  >60                 Load & ECUTE GROM address entry point
XTAB27 EQU  >200E               Low Mem XML table location 27
GETSTK EQU  >166C              
                               
GPLLNK DATA GLNKWS      R7      Set up BLWP Vectors
       DATA GLINK1      R8     
                               
RTNAD  DATA XMLRTN      R9      address where GPL XML returns to us
GXMLAD DATA >176C       R10     GROM Address for GPL XML (0F 27 Opcode)
       DATA >50         R11     Initialized to >50 where PUTSTK address resides
GLNKWS EQU  $->18               GPLLNK's workspace of which only
       BSS  >08         R12-R15 ...registers R7 through R15 are used

GLINK1 MOV  *R11,@GR4           Put PUTSTK Address into R4 of GPL WS
       MOV  *R14+,@GR6          Put GPL Routine Address in R6 of GPL WS
       MOV  @XTAB27,R12         Save the value at >200E
       MOV  R9,@XTAB27          Put XMLRTN Address into >200E
       LWPI GPLWS               Load GPL WS
       BL   *R4                 Save current GROM Address on stack
       MOV  @GXMLAD,@>8302(R4)  Push GPL XML Add on stack for GPL ReTurn
       INCT @STKPNT             Adjust the stack pointer
       B    @LDGADD             Execute our GPL Routine

XMLRTN MOV  @GETSTK,R4          Get GETSTK pointer
       BL   *R4                 Restore GROM address off the stack
       LWPI GLNKWS              Load our WS
       MOV  R12,@XTAB27         Restore >200E
       RTWP                     All Done - Return to Caller

*--------------------------------------------------------------------*
* DSRLNK - A Universal Device Service Routine Link - MG              *
*      (Uses console GROM 0's DSRLNK routine)                        *
*      (Do not REF DSRLNK or GPLLNK when using these routines)       *
*      (This DSRLNK will also handle Subprograms and CS1, CS2)       *
*                                                                    *
*      ENTER: The same way as the E/A DSRLNK, i.e., BLWP @DSRLNK     *
*                                                   DATA 8           *
*                                                                    *
*      NOTES: Must be used with a GPLLNK routine                     *
*             Returns ERRORs the same as the E/A DSRLNK              *
*             EQ bit set on return if error                          *
*             ERROR CODE in caller's MSB of Register 0 on return     *
*                                                                    *
* 186 Bytes total - including GPLLNK, DSRLNK and both Workspaces     *
*--------------------------------------------------------------------*

PUTSTK EQU  >50                     Push GROM Add to stack pointer
TYPE   EQU  >836D                   DSRLNK Type byte for GPL DSRLNK
NAMLEN EQU  >8356                   Device name length pointer in VDP PAB
VWA    EQU  >8C02                   VDP Write Address location
VRD    EQU  >8800                   VDP Read Data byte location
GR4LB  EQU  >83E9                   GPL Workspace R4 Lower byte
GSTAT  EQU  >837C                   GPL Status byte location
                                    
DSRLNK DATA DSRWS,DLINK1            Set BLWP Vectors

DSRWS  EQU  $                       Start of DSRLNK workspace
DR3LB  EQU  $+7                     lower byte of DSRLNK workspace R3
DLINK1 MOV  R12,R12         R0      Have we already looked up the LINK address?
       JNE  DLINK3          R1      YES!  Skip lookup routine
*<<-------------------------------------------------------------------------->>*
* This section of code is only executed once to find the GROM address          *
* for the GPL DSRNK - which is placed at DSRADD and R12 is set to >2000        *
* to indicate that the address is found and to be used as a mask for EQ & CND  *
*------------------------------------------------------------------------------*
       LWPI GPLWS           R2,R3   else load GPL workspace
       MOV  @PUTSTK,R4      R4,R5   Store current GROM address on the stack
       BL   *R4             R6
       LI   R4,>11          R7,R8   Load R4 with address of LINK routine vector
       MOVB R4,@>402(R13)   R9,R10  Set up GROM with address for vector

***les*** Note on above instruction:
***les***    1. R13 of GPLWS has >9800=GRMRD (GROM Read Data)
***les***    2. >402 added to GRMRD yields >9C02=GRMWA (GROM Write Address)

       JMP  DLINK2          R11     Jump around R12-R15
       DATA 0               R12     contains >2000 flag when set
       DATA 0,0,0           R13-R15 contains WS, PC & ST for RTWP
DLINK2 MOVB @GR4LB,@>402(R13)       Finish setting up GROM address
       MOV  @GETSTK,R5              Take some time & set up GETSTK pointer
       MOVB *R13,@DSRAD1            Get the GPL DSR LINK vector
       INCT @DSRADD                 Adjust it to get past GPL FETCH instruction
       BL   *R5                     Restore the GROM address off the stack
       LWPI DSRWS                   Reload DSRLNK workspace
       LI   R12,>2000               Set flag to signify DSRLNK address is set
*<<-------------------------------------------------------------------------->>*
DLINK3 INC  R14                     Adjust R14 to point to caller's DSR Type byte
       MOVB *R14+,@TYPE             Move it into >836D for GPL DSRLNK
       MOV  @NAMLEN,R3              Save VDP address of Name Length
       AI   R3,-8                   Adjust it to point to PAB Flag byte
       BLWP @GPLLNK                 Execute DSR LINK
DSRADD BYTE >03                     High byte of GPL DSRLNK address
DSRAD1 BYTE >00                     Lower byte of GPL DSRLNK address
*----Error Check & Report to Caller's R0 and EQU bit-------------------------
       MOVB @DR3LB,@VWA             Set up LSB of VDP Add for Error Flag
       MOVB R3,@VWA                 Set up MSB of VDP Add for Error Flag
       SZCB R12,R15                 Clear EQ bit for Error Report
       MOVB @VRD,R3                 Get PAB Error Flag
       SRL  R3,5                    Adjust it to 0-7 error code
       MOVB R3,*R13                 Put it into Caller's R0 (msb)
       JNE  SETEQ                   If it's not zero, set EQ bit
       COC  @GSTAT,R12              Else, test CND bit for Link Error (00)
       JNE  DSREND                  No Error, Just return
SETEQ  SOCB R12,R15                 Error, so set Caller's EQ bit
DSREND RTWP                         All Done - Return to Caller 

 

 

 

The DSRLNK coded by Paolo Bagnaresi et al. is basically the same as what is used in fbForth except for the explicit checking of >8350 if the >A (subroutine) route is taken. This seems to be what sets it apart from the DSRLNK in fbForth, TI Forth and the above MG routine. I suppose I can use whatever is easiest to include in fbForth 2.0 as long as I include the >8350 check or, at the very least, include a caveat to users. In all fairness to the TI programmers, they did not actually code a word to use DSRLNK for subroutines (>A), so the Forth programmer would be on her/his own there, anyway. Since I am trying to be as compatible as possible with TI Forth, I probably don't need to bother with it, either—except, possibly, in a discussion about how the Forth programmer might use DSRLNK >A if they really needed it. Sorry for rambling—just thinking out loud....

 

...lee

Edited by Lee Stewart
Link to comment
Share on other sites

How do I set up ALC in a ROM bank so that it can later be copied from ROM to RAM? Can I use DORG within the ROM space (>6000 – >7FFF) prior to the block of code I want to move? I understand how @Willsy did it in TurboForth for small blocks of code by using EQUates; but, I need to move a sizable block to low RAM and would prefer to avoid using a relative EQUate for every label after the first.

 

...lee

Link to comment
Share on other sites

One way (perhaps the only way) I see to use DORG in ROM space to work out copying a large block of code (~2600 bytes!) to RAM is to copy the relevant code into ROM space preceded by DORGing the starting RAM address. Then, I could follow that code with a copy without any labels except one at the beginning and one at the end to facilitate copying. The only problem is to work out a consistent way to blank all the labels automagically. I suppose I could write a sed (or similar utility) script to do my bidding. Any other suggestions?

 

...lee

Link to comment
Share on other sites

I hit upon a way to use EQUates that is somewhat less painful for large blocks of ALC than @Willsy's method. The following snippet is from TurboForth ALC and that is followed by how I would do it now:

 

@Willsy's—

 

docol   equ >8320                   ; address of this routine in PAD
toRAM   dect rstack                 ; make space on return stack
        mov pc,*rstack              ; save PC to return stack
        mov r6,pc                   ; place in PC and drop down to NEXT

;NEXT
; loads the next CFA and branches to the address in the CFA.
_next   equ docol+6                 ; address of this routine in PAD
        mov *pc+,r6                 ; get CFA in r6
        mov *r6+,r7                 ; get contents of CFA
        b *r7                       ; execute it

;EXIT 
; exits from a FORTH high level word (i.e. a word entered with DOCOL)
exit    equ _next+6                 ; address of this routine in PAD
        data exit+2                 ; called by NEXT, so needs a pointer
        mov *rstack+,pc             ; place saved PC into PC & pop return stack
        b *next                     ; do next instruction

 

 

 

Mine—

 

docol   equ >8320                   ; address of this routine in PAD
toRAM   dect rstack                 ; make space on return stack
        mov pc,*rstack              ; save PC to return stack
        mov r6,pc                   ; place in PC and drop down to NEXT

;NEXT
; loads the next CFA and branches to the address in the CFA.
_next   equ docol+$-toRAM           ; address of this routine in PAD
        mov *pc+,r6                 ; get CFA in r6
        mov *r6+,r7                 ; get contents of CFA
        b *r7                       ; execute it

;EXIT 
; exits from a FORTH high level word (i.e. a word entered with DOCOL)
exit    equ docol+$-toRAM           ; address of this routine in PAD
        data exit+2                 ; called by NEXT, so needs a pointer
        mov *rstack+,pc             ; place saved PC into PC & pop return stack
        b *next                     ; do next instruction 

 

 

 

Note lines 8 and 15 in each snippet. In my modification, every label after the first is EQUated to docol+$-toRAM, which correctly tracks the addresses. This works and is only tedious initially. I can test it before hoisting to ROM by simply manipulating the docol EQUate, if necessary.

 

...lee

  • Like 2
Link to comment
Share on other sites

Making headway! :-o I may have a first run at an fbForth 2.0 cartridge very soon! Just as soon as I can reduce the dictionary footprint by a few hundred more bytes to do a test, I should be able to do my first proof of concept. Woo-hoo!

 

...lee

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

I'm trying to figure out which words in the resident dictionary are good candidates for moving their payload to other ROM banks with stubs in bank0. I figure words involved with interpretation could probably be moved without a big performance hit—words like (FIND) and ENCLOSE , perhaps. I am looking particularly at the words that involve the most code—I'll post a list soon. Any suggestions?

 

...lee

Link to comment
Share on other sites

This was an issue for me, too. In the end, I kept (what I felt were) the most commonly used words, and/or short words in bank 0. That amounted to:

  • The most commonly used stack used stack words;
  • The most commonly used math words;
  • Run-time looping e.g. (DO) (LOOP) et al;
  • Anything that was implemented in Forth.

In later releases that got changed, as I moved some common stack and math words to pad ram for added whizziness!

Link to comment
Share on other sites

...

In later releases that got changed, as I moved some common stack and math words to pad ram for added whizziness!

 

How did you handle the problem with location >8370? Your currently available source does not have that fixed. I'm guessing you just use two copy loops—one that ends before >836E and a second that begins at >837E.

 

...lee

Link to comment
Share on other sites

Another thought :idea:

 

Currently, the headers in fbForth from EXECUTETASK consume 3KB of space in the resident dictionary, with an average of ~10 bytes/word (min: 4 bytes; max: 16 bytes). If I put the headers in another bank, I could recover that 3KB in bank 0 for the extra words I want to put there. I would need to re-write all the words that search the linked list of words; but, I think that is pretty small. It would increase the time for compilation of new words; but, that is a slow process, anyway. Execution won't be any slower except for words that involve word searches. I wonder if that is too big a sacrifice.... :ponder:

 

...lee

Link to comment
Share on other sites

It depends on what you want to hoist into its place, Lee. If it is something important, I would say do it. If it is just a nice to have, and that item would work fine outside of bank 0, then it really just depends on how much work you want to put into it.

Link to comment
Share on other sites

 

Checked this evening. Looks like the source is up-to-date. The code to restore scratch-pad does indeed write two sections to pad, but it's a single routine.

 

Scroll to the bottom of 1-06-Blocks

 

Not by my read. There's only one copy of the block of code between toRAM and copyend. The code between _lit and _dup is written right over >836E – >837E, destroying the integrity of the DSR buffers in high VRAM if >8370 was other than >37D7, which is the case for the nanoPEB and CF7+. That DATA statement needs to be bypassed so that >8370 remains as it was before the copy. EDIT: I was hitting a cached copy. :dunce: I see the fix, now.

 

...lee

Edited by Lee Stewart
Link to comment
Share on other sites

During an exchange of PMs with @Willsy on the subject of splitting the Forth headers out to another bank for added space in bank 0, I think I am convinced of its viability. Consequently, I will try to work out what I need to do with the dictionary searching/manipulating words by modifying the headers of a handful of words at the beginning of the dictionary. This will be truly like trying to keep several balls in the air at once. Should be fun! |:) I will report back, periodically.

 

...lee

Link to comment
Share on other sites

Prototyping the header split-out is coming along nicely. So far, the only snags I've run into are the user variables, CONTEXT and CURRENT , which store the vlfas (vocabulary link field addresses) of the context and current vocabularies, respectively. I can mitigate part of the problem by putting the vocabulary words currently in the resident dictionary ( FORTH and ASSEMBLER ) into RAM. The other part of the problem is that, if the user dereferences one of those vlfas, it will be pointing to the wrong bank because the default bank will be bank 0, not the bank with the headers. LATEST does this with CURRENT @ @ , which could also be a problem. As long as the resulting nfas (name field addresses) are only ever used by dictionary search/manipulation routines, all will be well. I certainly have control of what words in the resident dictionary do with an nfa or lfa (link field address), but none over what a user might decide to do, other than a stern and prominent caveat. I will certainly need to proceed carefully through these murky waters. :-o

 

...lee

Link to comment
Share on other sites

Wouldn't you know it—while slogging through this cartridge project, I found a bug in CREATE ! It really only matters if a user decides to change the maximum width of the names of new word definitions to some value less than 31. I may or may not fix it for fbForth 1.0. I have fixed it for the fbForth 2.0 cartridge version.

 

Otherwise, progress is slow but happening. I'm still a little concerned about CONTEXT and CURRENT . I'm still on the split-header path—we'll see...

 

...lee

Link to comment
Share on other sites

Woohoo! :-o I pretty much have all of the header words working with a handful of headers split out to a location different from the physical location of the code fields and parameter fields. There may be one or two additional words that need to go with the headers, but that's it. Now, it's mostly the tedium of logistics to hoist the code into cartridge space. The most tedious task will be splitting out all 314 of the headers in the resident dictionary. I also need to move (or convert to ALC and move) some of the longer words out of bank 0. Once I do all that, I should be able to plan and execute the conversion—still a little ways off.

 

If all of the above gives me enough additional space in bank 0, I want to hoist all of the file I/O and graphics words into cartridge space. Oh—I almost forgot about the editors—I want to put them in cartridge space, as well.... :P

 

...lee

  • Like 2
Link to comment
Share on other sites

OK, I got the headers all moved, the redundant labels commented out and the threaded list of pfa pointers added! :o I thought this was going to take me a week or two; but, with the help of a few macros in Notepad++, I got it done in a couple of days! Moving right along....

 

...lee

  • Like 1
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...