Jump to content
IGNORED

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


Lee Stewart

Recommended Posts

  • 4 weeks later...

I've been trying to tidy up the fbForth 2.0 Manual for an update release. I keep finding something that needs work, as well as getting sidetracked by other interesting developments on this forum. Right now I am working on the DIR routine that starts in block 36 of FBLOCKS. It gives the wrong information for “program” files as compared to various disk managers. I would like to display the program size in bytes, but the DSR's catalog “file” does not have that information or, at least, is not required to have it. It can be calculated from the number of sectors and the EOF offset, which are in each file’s FDR. I would need to read the FDIR and use it to retrieve all of the FDRs to get that information. That is essentially what the DSR does when the catalog file is requested. I think I will work on it for a couple of days before I decide which way to go. I’m glad we had this little talk. :ponder:

 

...lee

Link to comment
Share on other sites

Re DIR : I have a version that works using the catalog “file” of the DSR. It does not show the size of a PROGRAM file because that information is not in the catalog “file”:

post-29677-0-51928600-1434286565_thumb.gif

I am still inclined to write a similar word that will show the program size in bytes in the “B/R” column, just as all of the disk managers do. It is simple enough to write. The only problem I see is that it will be limited to floppy directories because I will be using DSR subprogram >10 (read/write sector) to get the information, whereas the above DIR should work with hard drives, as well, I think. I am not sure what to expect with floppy subdirectories, however.

 

If I proceed with the second word, what should I call it? DIR2 ? CAT ? CATALOG ? The reason I do not want to call it by the same name is that it will be invoked by “ 2 CAT ” to get a listing for DSK2. DIR is invoked by “ DIR DSK2. ” for the same listing.

 

How important is it to be able to get the program/binary-image size from a directory listing in Forth? That is, after all, easily obtained from a disk manager.

 

...lee

Edited by Lee Stewart
Link to comment
Share on other sites

I have completed a working CAT word that does sector reads of a disk to get its information from the VIB, FDIR and each file’s FDR. It displays the byte size of “program” files in a separate column from the bytes/record column used for other files:

post-29677-0-24698500-1434563060_thumb.gif

 

The Forth code for CAT is below. It is divided into seven blocks intended to be pasted into contiguous blocks in a blocks file. It can then be loaded from the starting block. The code below occupies so many blocks because it is extensively commented and (hopefully) more readable than it will be when (if) I include this in the next FBLOCKS file. Then it will be compacted into much less readable code and stripped of most of the comments.

 

 

 

( Catalog program that uses VIB, FDIR and FDRs..LES 16JUN2014)
0 CLOAD CAT   BASE->R  DECIMAL ." loading CAT catalog program"
( initial value of Buf1 is 2-byte PAB value = 272 = 0110h)
272 VARIABLE Buf1 254 ALLOT  ( CPU RAM buffer for VIB and FDIR)
0 VARIABLE Buf2 18 ALLOT   ( CPU RAM buffer for FDRs)
0 VARIABLE Total        ( sectors + FDRs used by files)
0 VARIABLE FCount       ( file count)
0 VARIABLE LC           ( screen line count)
0 VARIABLE bpr          ( bytes/record)
0 VARIABLE sect         ( # of sectors)
0 VARIABLE prot         ( protection)
HEX 
1154 CONSTANT VBuf   ( sector buffer in VRAM after 2-byte PAB)
0B10 VARIABLE Tabs 1C22 ,    ( tabs at 11, 16, 28, 34)
Buf1 VBuf 2- 2 VMBW     ( write PAB)
                                    R->BASE  -->

BASE->R HEX                            ." ."
: RdErr?  ( err --- )    ( report any I/O error)
   -DUP 
      IF   CR ." Disk I/O error " 
      BASE->R [COMPILE] HEX . R->BASE ABORT
   THEN   ;
: DSRLNK10 0A 0E SYSTEM    ( call DSR subprogram)
   8350 C@ RdErr?   ;      ( abort if error)
: getBuf ( bufadr count --- )  ( get sector info to RAM)
   VBuf ROT ROT VMBR    ;
: getSect ( sect# --- )
   8350 !         ( sect# to transfer block)
   VBuf 834E !    ( VRAM address to transfer block) 
   VBuf 2- 8356 ! ( write subroutine pointer)
   DSRLNK10       ( get sector sect# to VBuf)
;                         R->BASE    -->

BASE->R DECIMAL                         ." ."
: Tab ( n --- ) 
    Tabs + C@ CURPOS @ SCRN_WIDTH @ / GOTOXY   ;                      
: getFree   ( --- n )   ( get number of free sectors from VIB)
   0 Buf2 !       ( initialize free-sector count)
   Buf1 56 +      ( beginning of allocation bitmap)
   DUP 200 + SWAP DO    ( loop through allocation bitmap)
      I @         ( get next 16 bits)
      65535 XOR     ( now, 1=free & 0=used)
      -DUP IF     ( process if at least one bit set)
         16 0 DO     ( bit-shift loop)
            DUP 1 AND Buf2 +! ( free-sector count + LSb)
            1 SRL             ( get next bit)
         LOOP    DROP     ( drop leftover number)
      THEN
   2 +LOOP   Buf2 @  ;   ( # free sectors to stack) R->BASE -->

BASE->R DECIMAL                            ." ."
: Head1 ( --- ) 
    ." ---------- ---- ------- --- ----- -" CR   ;
: Head ( --- )  
    ." Name       Size Type    B/R Bytes P" CR
    Head1   ;
HEX
: DskInfo   ( dsk# --- )   ( Display disk information)
   SWPB 1+ 834C !    ( dsk# + read-sector to transfer block)
   0 getSect  ( read sector 0 = VIB)
   Buf1 100 getBuf   ( get all of sector 0 to Buf1)
   CR Buf1 0A ." Disk Name: " TYPE CR 
   ." Total: " Buf1 0A + @ 2- DUP U.  2 SPACES 
   ." Free: "  getFree DUP U.  2 SPACES         
   ." Used: " - U. CR 
;                                       R->BASE  -->

BASE->R DECIMAL                           ." ."
: Ftype ( --- )  ( Display file type and record size)
   Buf2 17 + C@ bpr !  ( get bytes/record)
   Buf2 12 + C@ DUP 8 AND prot ! 247 AND ( get status flags)
   CASE 0 OF ." DIS/FIX" ENDOF
      128 OF ." DIS/VAR" ENDOF
        2 OF ." INT/FIX" ENDOF       
      130 OF ." INT/VAR" ENDOF
        1 OF ." PROGRAM" sect @ 256 *   ( bytes in file)
            Buf2 16 + C@   ( get EOF offset in last sector)
            -DUP IF        ( handle non-zero)
               + 256 -     ( correct total byte-count)
            THEN   0 bpr !    ( do not display later)
            2 Tab 5 U.R    ( display byte size of file)
          ENDOF    ." ???????" 0 bpr !
   ENDCASE   bpr @ -DUP IF 4 U.R THEN   ;    R->BASE -->

BASE->R DECIMAL                                 ." ."
: DoCAT ( --- )   ( Catalog each file in directory)
   0 LC !  0 Total !  0 FCount !  Head 
   1 getSect Buf1 256 getBuf Buf1 ( read FDIR; set to 1st FDR)
   BEGIN   LC @ 20 MOD 19 =  IF KEY DROP CR Head THEN  DUP @ 
   -DUP WHILE 
      getSect Buf2 20 getBuf   ( get next FDR) 
      Buf2 10 TYPE         ( display filename)
      Buf2 14 + @ DUP sect !   ( get # sectors in file; store)
      1+ DUP  0 Tab 4 U.R  ( + FDR sector; display)
      Total +!             ( update total sectors used)
      1 Tab Ftype          ( handle file type; get protection)
      prot @ IF 3 Tab ." Y" THEN CR ( display protection)
      1 LC +!  1 FCount +!   2+  ( accounting; inc FDR pointer)
   REPEAT    DROP  Head1  FCount @ . 
   ." files" 0 Tab Total @ 4 U.R ."  sectors" CR ; R->BASE -->                                             

                                          ." ."
: CAT ( dsk# --- )   ( list disk catalog)
    ( Get the catalog and display it)
    BASE->R [COMPILE] DECIMAL
    DskInfo     ( display disk info)
    DoCAT       ( display file list)
    R->BASE
;
CR 
." n CAT - Catalogs a disk. n = disk #." CR
." E.g., 1 CAT catalogs DSK1." CR             ;S 

 

 

 

The code above does the job; but, I am sure it can be streamlined. Feel free to take a whack at it. :)

 

...lee

Link to comment
Share on other sites

For completeness, here is the code for DIR (not split into block-sized chunks as is the code for CAT in the last post):

 

 

 

DECIMAL
0 VARIABLE CatRec 36 ALLOT    ( CPU RAM Catalog record buffer)
PABS @ CatRec OVER 70 + FILE Cat    ( Catalog file reference)
0 VARIABLE Total        ( sectors + FDRs used by files)
0 VARIABLE FCount       ( file count)
0 VARIABLE LC           ( screen line count)
0 VARIABLE bpr          ( bytes/record)
0 VARIABLE sect         ( # of sectors)
0 VARIABLE prot         ( protection)
HEX 
0B10 VARIABLE Tabs 1C00 ,    ( tabs at 11, 16, 28)
DECIMAL

: Tab ( n --- ) 
    Tabs + C@ CURPOS @ SCRN_WIDTH @ / GOTOXY 
;                      
( Convert Radix-100 number in CatRec to 16-bit integer)
: @R100 ( i --- v ) 
    9 * CatRec DUP C@ + 2+ + 
    PAD 8 CMOVE             ( in case on odd boundary)
    PAD F@ F->S
;                                           
( Display disk information)
: DskInfo  
    RD DROP CR 
    CatRec COUNT ." Disk Name: " TYPE CR 
    ." Total: " 1 @R100 DUP U.  2 SPACES 
    ." Free: "  2 @R100 DUP U.  2 SPACES         
    ." Used: " - U. CR 
;
( Display file type and record size)
: Ftype ( ftype --- )  
    2 @R100 bpr !
    CASE                                       
        1 OF ." DIS/FIX" ENDOF
        2 OF ." DIS/VAR" ENDOF
        3 OF ." INT/FIX" ENDOF       
        4 OF ." INT/VAR" ENDOF
        5 OF ." PROGRAM" 0 bpr ! ENDOF
        ." ???????" 0 bpr !
    ENDCASE
    bpr @ -DUP IF 4 U.R THEN
;
: Head1 ( --- ) 
    ." ---------- ---- ------- --- -" CR 
;
: Head ( --- )  
    ." Name       Size Type    B/R P" CR
    Head1 
;
( Catalog each file in directory)
: DoDIR ( --- ) 
    0 LC !  0 Total !  0 FCount !  Head 
    BEGIN      
        LC @ 20 MOD 19 = 
        IF 
            KEY DROP CR Head 
        THEN 
        RD DROP
   ( debug CatRec 38 DUMP KEY DROP )
        CatRec COUNT DUP              
    WHILE 
        TYPE  
        1 @R100 DUP 1- sect ! DUP  0 Tab 4 U.R  Total +!            
        0 @R100 DUP prot ! ABS 1 Tab Ftype  
        prot @ 0< IF 2 Tab ." Y" THEN CR
        1 LC +!  1 FCount +!
    REPEAT 
    DROP DROP  Head1  
    FCount @ . ." files" 0 Tab Total @ 4 U.R ."  sectors" CR 
;                                             
( Catalog the directory)
: DIR 
    Cat SET-PAB         ( Initialize PAB skeleton)
    INTRNL  FXD  RLTV  INPT  38 REC-LEN ( Init catalog parms)
    ( Get directory name from input stream)
    PAB-ADDR @ 10 + 32 WORD HERE COUNT >R SWAP R VMBW R> N-LEN!
    ( Get the catalog and display it)
    OPN         ( open the catalog)
    DskInfo     ( display disk info)
    DoDIR       ( display file list)
    CLSE        ( close the catalog)
; 

 

 

 

Also, I think I will set up both CAT and DIR load blocks to only load if neither word exists in the CONTEXT vocabulary.

 

...lee

Link to comment
Share on other sites

One more comment about CAT regarding usability: In its current state, CAT can be used in Text , Text80 and Graphics modes, regardless of which of those modes was active at the time CAT was loaded; however, the screen display in Graphics mode is not wide enough to display each line without wrapping. I can change that to a more cryptic representation of file type. Right now, that column is headed “Type” and the various file types are displayed as “PROGRAM”, “DIS/FIX”, “DIS/VAR”, “INT/FIX” and “INT/VAR”. To make it work the same in all three modes, I would need to change the heading to “Typ” and the types to “PRG”, “D/F”, “D/V”, “I/F” and “I/V”. Does anyone object to the change?

 

...lee

Link to comment
Share on other sites

PGM usually the 3 letters used for ProGraM

 

RXB used these as that is what Miller Graphics used for CAT.

 

That was included in the GRAM KRACKER FACTS for the GK XB.

 

Also for most of the Disk Managers.

Edited by RXB
Link to comment
Share on other sites

PGM usually the 3 letters used for ProGraM

 

RXB used these as that is what Miller Graphics used for CAT.

 

That was included in the GRAM KRACKER FACTS for the GK XB.

 

Also for most of the Disk Managers.

 

Thanks Rich, I'll change it to “PGM”.

 

...lee

  • Like 1
Link to comment
Share on other sites

Looking forward to that manual. :)

 

My printer and comb binder are itching to go. :)

 

Thanks again for all your effort and work on fbForth. It is truly a wonderful piece of work.

 

You are too kind, Owen—but, thanks! It has certainly been fun!

 

...lee

Link to comment
Share on other sites

OK—DIR does work in Classic99, though not as expected on disk images unless the TICC DSR is set for the image tested. With the TICC DSR emulation, the “catalog” file has the expected information, viz.,

  • Total sectors excludes the VIB and FDIR, so is 2 less than the actual number of sectors on the disk;
  • Sectors/file includes the file’s FDR, so is 1 more than the number of sectors in the body of the file;
  • A protected file is indicated by a negated file type, e.g., the 9-byte field in the catalog record of a PROGRAM file (in hex with leading byte-count byte and 8-byte, floating-point (FP), file-type number) would be
    • Unprotected PROGRAM: 08 40 05 00 00 00 00 00 00 = 5
    • Protected PROGRAM: 08 BF FB 00 00 00 00 00 00 = -5 (only the first 2 bytes are negated in negative FP numbers on the TI-99/4A)

In the next day or two, I will add CAT to the manual, check for format irregularities and get the updated manual and FBLOCKS to the first post.

 

...lee

Edited by Lee Stewart
Link to comment
Share on other sites

I just updated post #1 with an updated fbForth 2.0 Manual, which corrects some errors and adds DIR and CAT , and an updated FBLOCKS file included in a ZIP file with 2 disk images (90KiB and 400KiB), which contain the updated FBLOCKS. The cartridge binaries for EPROM, Classic99 and MESS are current except for FBLOCKS, which you can update from the FLOCKS ZIP file.

 

Please let me know of any errors or any changes you think I should make to the manual.

 

...lee

Edited by Lee Stewart
Link to comment
Share on other sites

  • 2 weeks later...

While trying to port @Willsy's DarkStar game to fbForth I needed a scroll routine to scroll the screen in any direction like the SCROLL word in TurboForth. The following is my high-level offering:

 

 

 

( SCROLL routines---)   0 CLOAD SCROLL 
CR ." loading SCROLL routines"
0 VARIABLE WRAP     0 VARIABLE PANCR   0 VARIABLE PANW   
0 VARIABLE PANH     0 VARIABLE sWRCH   0 VARIABLE sSRCCH
0 VARIABLE sDSTCH   0 VARIABLE sINC    0 VARIABLE sOUTCTR
0 VARIABLE sINCTR   0 VARIABLE sNXTRC

: PANEL ( c r w h --- )
   PANH ! PANW ! SCRN_WIDTH @ * + PANCR !  ;
: sSETUP ( srcadr dstadr inc octr ictr nxtrc --- )
   sNXTRC !  sINCTR !  sOUTCTR !  sINC !  sDSTCH !  sSRCCH !
;
: SCRDIR  ( dir ---  )
   CASE
    0 OF    ( left scroll)
         PANCR @ 1+                          ( src address)
         PANCR @                             ( dst address)
         1                                   ( increment)
         PANH @                              ( outer counter)
         PANW @ 1-                           ( inner counter)
         SCRN_WIDTH @                        ( next row/col)
         sSETUP   
      ENDOF
    2 OF    ( right scroll)
         PANCR @ PANW @ + 2-                 ( src address)
         DUP 1+                              ( dst address)
         -1                                  ( increment)
         PANH @                              ( outer counter)
         PANW @ 1-                           ( inner counter)
         SCRN_WIDTH @                        ( next row/col)
         sSETUP   
      ENDOF
    4 OF    ( up scroll)
         PANCR @ SCRN_WIDTH @ +              ( src address)
         PANCR @                             ( dst address)
         SCRN_WIDTH @                        ( increment)
         PANW @                              ( outer counter)
         PANH @ 1-                           ( inner counter)
         1                                   ( next row/col)
         sSETUP   
      ENDOF
    6 OF    ( down scroll)
         PANCR @ SCRN_WIDTH @ PANH @ 2- * +  ( src address)
         DUP SCRN_WIDTH @ +                  ( dst address)
         SCRN_WIDTH @ MINUS                  ( increment)
         PANW @                              ( outer counter)
         PANH @ 1-                           ( inner counter)
         1                                   ( next row/col)
         sSETUP   
      ENDOF
   ENDCASE
   WRAP @ 0= IF BL sWRCH ! THEN
;
: SCROLL  ( dir --- )  ( 0=left 2=right 4=up 6=down)
   SCRDIR
   sOUTCTR @ 0 DO
      sSRCCH @ sDSTCH @    ( source, dest addresses to stack)
      WRAP @ IF sDSTCH @ VSBR sWRCH ! THEN   ( save wrap char)
      sINCTR @ 0 DO
         sSRCCH @ VSBR     ( read source char)
         sDSTCH @ VSBW     ( copy to dest)
         sINC @ DUP sSRCCH +! sDSTCH +!   ( inc/dec addresses)
      LOOP
      sWRCH @ sDSTCH @ VSBW      ( copy wrap/blank char)
      sNXTRC @ DUP ROT + sDSTCH !  ( next-round dst address)
      + sSRCCH !                   ( next-round src address)
   LOOP   
; 

 

 

 

It doesn't have checks for bad scroll numbers, yet. And, it is slower than the Forth Assembler version I plan to convert it to. Just thought you might be interested.

 

...lee

  • Like 1
Link to comment
Share on other sites

Should be quite simple to port to fbForth. I used VALUES rather than variables, but that should be quite easy to graft into fbForth.

 

Here's some Forth-83 code which you should be able to port to FIG flavour, if you haven't already :grin:

 

Tested in TF, works okay. YMMV :grin:

 

 

: value ( value "name" -- " )
  constant ; \ a trick, but should work
 
: to ( value "name" -- )
  bl word find if
    state @ if
      \ compiling
      >body compile lit ,
      compile !
    else
      \ interpreting
      >body !
    then
  else
    true abort" TO: Word not found"
  then
; immediate
 

: +to ( value "name" -- )
  bl word find if
    state @ if
      \ compiling
      >body compile lit ,
      compile +!
    else
      \ interpreting
      >body +!
    then
  else
    true abort" +TO: Word not found"
  then
; immediate

:thumbsup:

Link to comment
Share on other sites

  • 2 weeks later...

Should be quite simple to port to fbForth. I used VALUES rather than variables, but that should be quite easy to graft into fbForth.

 

Here's some Forth-83 code which you should be able to port to FIG flavour, if you haven't already :grin:

 

Tested in TF, works okay. YMMV :grin:

 

 

: value ( value "name" -- " )
  constant ; \ a trick, but should work
 
: to ( value "name" -- )
  bl word find if
    state @ if
      \ compiling
      >body compile lit ,
      compile !
    else
      \ interpreting
      >body !
    then
  else
    true abort" TO: Word not found"
  then
; immediate
 

: +to ( value "name" -- )
  bl word find if
    state @ if
      \ compiling
      >body compile lit ,
      compile +!
    else
      \ interpreting
      >body +!
    then
  else
    true abort" +TO: Word not found"
  then
; immediate

 

 

 

:thumbsup:

 

And, here it is in fbForth:

 

 

 

: VALUE ( value IS:name -- )
  CONSTANT ; ( a trick, but should work)
 
: TO ( value IS:name -- )
   -FIND IF
      DROP STATE @ IF   ( compiling)
         COMPILE LIT , 
         COMPILE !
      ELSE              ( interpreting)
         !
      THEN
   ELSE
      0 ERROR
   THEN
;   IMMEDIATE

: +TO ( value IS:name -- )
   -FIND IF
      DROP STATE @ IF   ( compiling)
         COMPILE LIT , 
         COMPILE +!
      ELSE              ( interpreting)
         +!
      THEN
   ELSE
      0 ERROR
   THEN
;   IMMEDIATE 

 

 

 

“IS:” in the stack effects means “input stream:”.

 

BTW, TO and +TO work equally well on variables.

 

...lee

Link to comment
Share on other sites

Years ago in the mid 1980s I coded the word SAY in TI Forth for speaking a single phrase by its speech-synthesizer (SS) address (listed in Section 24.6 of the E/A Manual). Below is a little safer and slightly better definition of SAY that takes a list of phrase addresses along with a count—practically identical to TurboForth's SAY . It waits for the SS to say one phrase (at one address) before going on to the next one. The ASM: ... ;ASM code for spstat is pretty much a copy of @Willsy's TurboForth ROM code for SS status.

 

 

 

HEX
( The body of the following word, starting with its CFA, will be)
( copied to scratchpad RAM at 8348h so it can be executed while the)
( speech synthesizer [SS] is speaking.  The 8-bit memory bus is not)
( available during this time.  This word is not used as is.  Rather,)
( it is modified below.)
ASM: spstat ( -- )   ( get SS status)
   9000 @() 8354 @() MOVB,
   R0 0C SRC,
;ASM

( The following variable begins 6 cells [12 bytes] of machine)
( code from the assembly of the above word.  The CFA is changed)
( to point to the next cell after 8348h, where the following code)
( will be copied by cpystat every time SAY is executed.)
834A VARIABLE spstat D820 , 9000 , 8354 , 0BC0 , 045F ,

( Copy the above 6 cells to scratchpad RAM where getstat EXECUTEs)
( them to get the SS's status to 8354h.)
: cpystat spstat 8348 6 MOVE ;  

( getstat passes the CFA of spstat [now 8348h] to EXECUTE, which)
( dutifully runs the above code of spstat to put the SS's status)
( in 8354h and returns to the address interpreter.  getstat then)
( retrieves that byte, picks off the SS busy bit and pushes it)
( onto the stack.)
: getstat 8348 EXECUTE 8354 @ 8000 AND ;

( SAY needs on the stack 1 or more ROM speech addresses in the SS,)
( with the address of the first word/phrase to be spoken on top)
( and the number of addresses, n, as the topmost cell.)
: SAY ( spaddrN..spaddr1 n --- ) 
   cpystat           ( copy sptat code to scratchpad RAM)
   0 DO              ( loop through n phrases)
      BEGIN          ( ensure SS not busy before next phrase)
         getstat 0= 
      UNTIL 
      4 0 DO         ( write 4 nybbles of phrase's SS address)
         4 SRC       ( get least-significant nybble [LSN] to MSN)
         DUP         ( DUP cell for next go-round)
         F000 AND    ( only want MSN)
         4 SRL       ( move it right one nybble)
         4000 +      ( add address command [40h] to MSB)
         9400 !      ( write byte to SS)
      LOOP           ( process next nybble)
      4000 9400 !    ( write end-of-address [0] nybble)
      5000 9400 !    ( tell the SS to speak the phrase)
      DROP           ( drop excess address from DUP in above loop)
   LOOP ;            ( pop next phrase's address)

 

 

 

Eventually, I will incorporate the above into the fbForth 2.0 kernel. But for now, the above will allow me to replicate @Willsy's DarkStar game in fbForth 2.0. I am almost there! :-o

 

A little later, I will post an easier-to-load version of the above code.

 

...lee

  • Like 1
Link to comment
Share on other sites

Here is the more easily loaded code from the last post:

834A VARIABLE spstat D820 , 9000 , 8354 , 0BC0 , 045F ,
: cpystat spstat 8348 6 MOVE ;  
: getstat 8348 EXECUTE 8354 @ 8000 AND ;
: SAY ( spaddrN..spaddr1 n --- )   cpystat   0 DO  
   BEGIN getstat 0= UNTIL 4 0 DO 4 SRC DUP F000 AND  4 SRL 
   4000 + 9400 ! LOOP 4000 9400 ! 5000 9400 ! DROP  LOOP ;

: FBFORTH  56B3 3A32 2D19 1A42 299F 5 SAY ;

After you load the above, type FBFORTH . :grin:

 

...lee

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