[comp.lang.forth] File handling tips

trolfs@vax1.tcd.ie (Thomas) (03/22/89)

 I bought Forth for my computer (commodore 64) to replace the BASIC and was 
immediately won over to it. Nobody I know is interseted in Forth and so I
have had to read up on it in books and learn by trial and error. I now feel 
I have a good understanding of Forth and want to develop my system so I can 
do some serious programing.

 One of the things I'm not happy with is the disk operations. My disk drives
are slow and INDEXing takes forever. What I want to do is develop file
handling primitives and directory(s) structuring (ie not have to INDEX).

 Maybe someone out there has some ideas about this. 

 I would be gratefully for any tips as I have too many ideas and they're 
probably bad.

 Thanks 

-- 

                                Thomas Rolfs.
                                                 (trolfs@vax1.tcd.ie)
                                                  Dublin,
                                                  Ireland.

LEFF@PITTVMS.BITNET (03/24/89)

I also work with FORTH on the Commodore 64 and currently
use a public domain system called BLAZIN' FORTH written
by Scott Balantyne--it is comprehensive and very fast.
The disk is accessed by blocks rather than files--the 83
standard is followed quite closely but there are plenty
of built in words to provide turtle graphics, SID control
and new string handling words.  The program is available
on both Q-Link and compuserve--terrific package!

pmy@vivaldi.acc.Virginia.EDU (Pete Yadlowsky) (03/25/89)

In article <8903241905.AA06041@jade.berkeley.edu> Forth Interest Group International List <FIGI-L%SCFVM.bitnet@jade.berkeley.edu> writes:
>--it [ Blazin' Forth ]is comprehensive and very fast.
>The disk is accessed by blocks rather than files--

A bit off the original topic, but the traditional block file system
is something I've always wondered about. I can't imagine why, today,
anyone would prefer numbered, fixed-record 'screens' over conventional
named files. This is not a flame, I'm just very curious. Would
some of you in the block camp care to comment on this? I'd
especially like to hear from those who have used both systems.

	- Pete

Peter M. Yadlowsky		| "Pay no attention to that man
Academic Computing Center	|	behind the curtain!"
University of Virginia		|
pmy@Virginia.EDU		|

LEFF@PITTVMS.BITNET (03/25/89)

In BLAZIN' FORTH both data and words are kept on fixed
length blocks--obviously some storage space is wasted
but there is a certain elegance to the idea--multiple
screens can be kept in the traditional block buffers
and each can be edited and re-compiled in a flash.  I've
never been able to write a word longer than a standard block--my
blocks usually hold between 2 to 10 words in a certain "theme."
My system allows me to allocate 0 to about 30 data and
program blocks for very easy access.  The disadvantage
of the block system is the unorthodox use of the disk
media--the disk directory shows NO files present on the
Commodore system. Also, if you want to transfer a set of
blocks you have to write them into a sequential ASCII file.
Perhaps its old fashioned, but I've always enjoyed the
block/buffer system.

oster@dewey.soe.berkeley.edu (David Phillip Oster) (03/25/89)

One advantage of traditional "blocks" over files:

When your program crashes during LOAD, it is easier to fix it up and
continue, rather than FORGETing everything and restarting the load:

One pseudo-advantage:

Since Forth is a postfix language, the only reasonable way of pretty
printing is to make the structures line up on the RIGHT edge, not the LEFT
as in prefix languages:


: Example ( - )
        TEST1 IF
     TEST2 IF
  TEST3 IF
   BODY
     ENDIF
        ENDIF
          ENDIF
;

(The colon lines up on the left edge, because IT is prefix.)

With a blocks file, this doesn't cost any more space than any other way of
writing the word.  In a text file, you must fight your frugal impulses to
be sparing of those white space characters.

koopman@a.gp.cs.cmu.edu (Philip Koopman) (03/27/89)

In article <1270@hudson.acc.virginia.edu>, pmy@vivaldi.acc.Virginia.EDU (Pete Yadlowsky) writes:
> A bit off the original topic, but the traditional block file system
> is something I've always wondered about. I can't imagine why, today,
> anyone would prefer numbered, fixed-record 'screens' over conventional
> named files. This is not a flame, I'm just very curious. Would
> some of you in the block camp care to comment on this? I'd
> especially like to hear from those who have used both systems.

I have used both blocks and text files.  Blocks are handy because you
can compile in small segments.  Using a text editor block designate command
such as Wordstar/Turbo Editor <ctl>K<ctl>B  <ctl>K<ctl>K to do this is
too tedious for me, and loses the natural segmentation of the program
as well as the handy INDEX capability.

But, blocks are really obnoxious on a hard disk because they
waste so much space.  A couple hundred K here, a couple hundred K
there, and pretty soon you're talking real disk space!  Also, if
your word definition (especially an assembly language routine) doesn't
fit in a few lines, you've got problems.  Adding a block in the middle
of a program is a pain, even with an EXPAND type word.  I also find that
blocks constrain my commenting (and no, shadow blocks don't do it for me --
they're either too small are too big, but never just right).

Pure text files are a lose, because you give up a lot of the flexibility
in debugging, and get back into long compile-test-edit cycles.

However, there is one text file technique that I like a lot.
I have seen elements of it in many places, but the first commercial
system I ran across that did it in a really slick way was from
Steve Pelc in England (Modular Programming Environment, or some such).

Just add form-feed characters to a standard text file wherever you
want to put a break in your code.  Software can interpret the form
feeds as variable-length block boundaries.  Compiling individual
blocks works, normal text editors can handle the files, INDEX
works, and you save space (many text editors automatically use tab characters
to reduce wasted space for indenting).  And, copying the file to
a printer produces a page break at every variable length screen boundary.


  Phil Koopman                koopman@greyhound.ece.cmu.edu   Arpanet
  5551 Beacon St.
  Pittsburgh, PA  15217    
PhD student at CMU and sometime consultant to Harris Semiconductor.
-- 

jax@well.UUCP (Jack J. Woehr) (03/28/89)

In article <1270@hudson.acc.virginia.edu> pmy@vivaldi.acc.Virginia.EDU.acc.Virginia.EDU (Pete Yadlowsky) writes:
>In article <8903241905.AA06041@jade.berkeley.edu> Forth Interest Group International List <FIGI-L%SCFVM.bitnet@jade.berkeley.edu> writes:
>>--it [ Blazin' Forth ]is comprehensive and very fast.
>>The disk is accessed by blocks rather than files--
>
>A bit off the original topic, but the traditional block file system
>is something I've always wondered about. I can't imagine why, today,
>anyone would prefer numbered, fixed-record 'screens' over conventional
>named files. This is not a flame, I'm just very curious. Would
>some of you in the block camp care to comment on this? I'd
>especially like to hear from those who have used both systems.
>
>	- Pete
>
	Pete, I use and have used both.

	Two points, briefly:

	1) BLOCK is Forth's one-size-fits-all interface to virtual
memory. It can be used (inefficiently) for program file storage. Long ago
we shoulda written a text editor that uses BLOCKs transparently, to get
more efficient source code storage. *B*U*T* ... BLOCK can be used for
ANYTHING, storing binary data, your laundry list, etc. Please do not
confuse the fact that the lazy Forth programmer uses BLOCK for source
code unformatted, wasting all those precious ascii hex 20s! BLOCK is
a primal mass storage interface that makes virtual memory access uniform
from minimicros to mainframes.

	2) Source code files in BLOCKS are ENTIRELY portable to ANY Forth
system. Unlike host operating systems, some of which use CRs some LFs
some BOTH CR & LF, BLOCK is unformatted. A BLOCK file from my Novix
Forth chip is readable on my VAX running a 32-bit JSR Forth.

{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}
{}                                                                        {}
{} jax@well     ." Sysop, Realtime Control and Forth Board"      FIG      {}
{} jax@chariot  ." (303) 278-0364 3/12/2400 8-n-1 24 hrs."     Chapter    {}
{} JAX on GEnie       ." Tell them JAX sent you!"             Coordinator {}
{}                                                                        {}
{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}

jax@well.UUCP (Jack J. Woehr) (03/28/89)

In article <37859@vax1.tcd.ie> trolfs@vax1.tcd.ie (Thomas) writes:
>
>have had to read up on it in books and learn by trial and error. I now feel 
>I have a good understanding of Forth and want to develop my system so I can 
>do some serious programing.
>
> One of the things I'm not happy with is the disk operations. My disk drives
>are slow and INDEXing takes forever. What I want to do is develop file
>handling primitives and directory(s) structuring (ie not have to INDEX).
>
> Maybe someone out there has some ideas about this. 
>
> I would be gratefully for any tips as I have too many ideas and they're 
>probably bad.
>
	Probably NOT bad, Thomas. Try anything. That is what Forth is
for ... it's your personal language, once you are done extending the dictionary.

	One idea is to reserve a few screens on each disk for some kind of
directory structure.  On my TRS-80 I always used to load certain extensions
to the system that enabled me to access machine readable directory information 
which allowed me to load source by program names. Try anything once, if you
are unhappy, erase the disk!

	For more ideas, check back issues of FORTH Dimensions magazine. You ARE a FIG member, aren't you? :-)

{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}
{}                                                                        {}
{} jax@well     ." Sysop, Realtime Control and Forth Board"      FIG      {}
{} jax@chariot  ." (303) 278-0364 3/12/2400 8-n-1 24 hrs."     Chapter    {}
{} JAX on GEnie       ." Tell them JAX sent you!"             Coordinator {}
{}                                                                        {}
{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}

gamber@cosmo.UUCP (Johannes Teich) (03/29/89)

In a message of <21 Mar 89 23:13 GMT>, Thomas Rolfs (trolfs@vax1.tcd.ie) writes:
 
 > I bought Forth for my computer (commodore 64) to replace the BASIC and
 > was immediately won over to it. Nobody I know is interested in Forth
 > and so I have had to read up on it in books and learn by trial and error.
 
Nobody is interested - well, that's our fate. :-)
 
 > One of the things I'm not happy with is the disk operations. My disk
 > drives are slow and INDEXing takes forever. What I want to do is develop
 > file handling primitives and directory(s) structuring
 
As I don't have a C64 (only Z80 and 80286 and 68000), I cannot say anything
about it. Are you using a file system? If so, then you have a good chance
to speed the machine up dramatically by writing to disk directly, like
'classical' Forth, just block by block, similar to writing to RAM.
 
By the way, if I had a C64, I surely would buy volksForth (or ultraForth),
offered by Forth-Gesellschaft, Germany. But I'm afraid there is only a
German manual. (I have got volksForth for Atari-ST and MS-DOS.)
 
I'm amazed by the fact that neither Doublin nor Ireland is represented
in the worldwide FidoNet, where I try to keep the discussion about Forth
alive, at least in Germany. Anyway, I am watching 'comp.lang.forth' as well.
 
cu Johannes Teich                  UUCP:          unido!cosmo!gamber
                                          D mcvax!unido!cosmo!gamber \
(gamber - that's me)               Fido:                2:507/414.20      

rat@madnix.UUCP (David Douthitt) (04/02/89)

David Phillip Oster (oster@dewey.soe.berkeley.edu.UUCP) writes:
|
| One advantage of traditional "blocks" over files:
| 
| When your program crashes during LOAD, it is easier to fix it up and
| continue, rather than FORGETing everything and restarting the load:

This is not necessarily so.  Why not put the blocks into files?
I believe that the Macintosh MacForth does this - I know Mad Apple Forth does.
This way, everything still looks like blocks to the user, but can either
be a standard text file or an "all-text" file just like the blocks are.

| Since Forth is a postfix language, the only reasonable way of pretty
| printing is to make the structures line up on the RIGHT edge, not the LEFT
| as in prefix languages:

     [... stuff deleted ...]

I would disagree - but then no one follows my style of indentation anyway.
I have my form and you have yours.

      [david]

-- 
======== David Douthitt :::: Madison, WI :::: The Stainless Steel Rat ========
FidoNet: 1:121/2 ::::: WittiNet: "Curiouser and curiouser, said Alice." ::::::
UseNet:  ...{rutgers|ucbvax|harvard}!uwvax!astroatc!nicmad!madnix!rat
ArpaNet: madnix!rat@cs.wisc.edu        {decvax|att}!

ZMLEB@SCFVM.BITNET (Lee Brotzman) (04/04/89)

>
>David Phillip Oster (oster@dewey.soe.berkeley.edu.UUCP) writes:
>|
>| One advantage of traditional "blocks" over files:
>|
>| When your program crashes during LOAD, it is easier to fix it up and
>| continue, rather than FORGETing everything and restarting the load:
>
>This is not necessarily so.  Why not put the blocks into files?
>I believe that the Macintosh MacForth does this - I know Mad Apple Forth does.
>This way, everything still looks like blocks to the user, but can either
>be a standard text file or an "all-text" file just like the blocks are.
>
   I don't know about MacForth, but, in my experience, block-oriented Forth
systems that run under host operating systems usually implement blocks as
1024-byte fixed-length records.  There are no intervening end-of-line
characters or other record terminators.  They are not at all like standard
text files, as evidenced by trying to print them and getting a run-on
mish-mash.
   The reason I use blockish files while using Forth under MS-DOS, or Apple
ProDOS, or VAX VMS (our Forth system is implemented under all three plus a
few others) is for fast access to any part of the code.  When an error occurs
I type WHERE and the editor puts my cursor at the point of the error.  Or, if
I leave the editor, I type RESUME and I'm right back where I left off.  It's
a simple matter to vector the system words to invoke these functions
automatically, I just haven't found it important enough to do.
   The overhead is exactly two integers:  block number and offset into the
block.  The buffer is already in Forth's memory space, so no additional reads
are required to position the cursor.
   This is just like the Turbo-xxxx compiler environments, the only difference
being that I had it before Borland was incorporated.
   On the other hand, I am not a BLOCK fanatic.  I just like them, and they
work for me.  If others prefer text files, great.
>
>--
>======== David Douthitt :::: Madison, WI :::: The Stainless Steel Rat ========
>FidoNet: 1:121/2 ::::: WittiNet: "Curiouser and curiouser, said Alice." ::::::
>UseNet:  ...{rutgers|ucbvax|harvard}!uwvax!astroatc!nicmad!madnix!rat
>ArpaNet: madnix!rat@cs.wisc.edu        {decvax|att}!

-- Lee Brotzman (FIGI-L Moderator)
-- NASA doesn't know what I'm saying.  Let's keep it that way.
-- Astronomical Data Center.  NASA Goddard Space Flight Center.
-- BITNET:  ZMLEB@SCFVM      Internet: zmleb@scfvm.gsfc.nasa.gov
-- SPAN:    CHAMP::BROTZMAN  GEnie:    L.BROTZMAN

cs472226@umbc5.umbc.edu (David Wood (CS472226)) (04/05/89)

ZMLEB@SCFVM.BITNET (Lee Brotzman) writes:
>>David Phillip Oster (oster@dewey.soe.berkeley.edu.UUCP) writes:
>>|
>>| One advantage of traditional "blocks" over files:
>>|
>>| When your program crashes during LOAD, it is easier to fix it up and
>>| continue, rather than FORGETing everything and restarting the load:

 (((stuff about MacForth structuring deleted)))

>   I don't know about MacForth, but, in my experience, block-oriented Forth
>systems that run under host operating systems usually implement blocks as
>1024-byte fixed-length records.  There are no intervening end-of-line
>characters or other record terminators.  They are not at all like standard
>text files, as evidenced by trying to print them and getting a run-on
>mish-mash.
>   The reason I use blockish files while using Forth under MS-DOS, or Apple
>ProDOS, or VAX VMS (our Forth system is implemented under all three plus a
>few others) is for fast access to any part of the code.  When an error occurs
>I type WHERE and the editor puts my cursor at the point of the error.  Or, if
>I leave the editor, I type RESUME and I'm right back where I left off.  It's
>a simple matter to vector the system words to invoke these functions
>automatically, I just haven't found it important enough to do.

   There are a few other reasons for using the BLOCK-oriented file under
a host operating system:
   1> Some FORTHs (at least GS16Forth that I know of) will allow you to
restrict the size of your dictionary, declare a startup word, and save
the system image to disk. The result: An independent application which
you can run from whatever shell you please. If the system uses its OWN
operating system, you can't save it and run it from your standard
operating system.
   2> With the more frequent introduction of mass storage devices, the
typical block arrangement becomes impractical. A 1024-byte block was
just ducky for a 5&1/4" diskette which would hold 100-200K, but with the
3&1/2"s (which hold 800K+) and hard disks (which hold some number of
megs multiplied by ten), searching for screens unless you programmed in
a directory screen in some prominent location becomes a real pain.
   3> Not all FORTHOSes have communications systems, so they can't
really send or receive files too well. By using a file storage protocol
that Whatever-Term 4.19 can read, you can exchange code with other
people. (By the way... How goes the call for comp.forth.binaries?)
   4> It may be only as fast as the native operating system accesses it,
but considering the advantages above, it's worth it to sacrifice a
little speed to get the features.

   And now for something completely different...
   I'm looking for string-handling words, including variables,
constants, LEFT$, RIGHT$, MID$, etc... Could anyone tell me where to get
a collection like that? Are there any books with collections of routines
in FORTH that I should know about but don't because I'm lazy?

                                                 -David Wood
   Help! This account turns into a pumpkin in May! Where and how else
can I stay in touch with UseNet?

rat@madnix.UUCP (David Douthitt) (04/10/89)

I wrote:
|
| Why not put the blocks into files? I believe that the Macintosh MacForth
| does this - I know Mad Apple Forth does.
| This way, everything still looks like blocks to the user, but can either
| be a standard text file or an "all-text" file just like the blocks are.

Lee Brotzman (ZMLEB@SCFVM.BITNET) replied:
|    I don't know about MacForth, but, in my experience, block-oriented Forth
| systems that run under host operating systems usually implement blocks as
| 1024-byte fixed-length records.  There are no intervening end-of-line
| characters or other record terminators.  They are not at all like standard
| text files, as evidenced by trying to print them and getting a run-on
| mish-mash.

I think you misunderstood me.  As I see it, there are three possible formats
here:
        1. 1K Blocks, no filesystem
        2. 1K Blocks stored as 1024-byte records in a file.
        3. 1K Blocks stored as blocks of variable-length lines
           (that is, a block is made up of 16 variable-length text lines;
            does this sound like a text file?  It should...)
        4. Freeform "streaming" text file - no 1K limits.

Personally, I think both versions #2 and #3 show promise for a file-based
block system.  For Apple Prodos, it's rather simple to read 16 lines
terminated either at character 64 or at a newline.  The only problem would be
reading a text line with 64 characters and a newline character.  Of course,
that is not really a problem since Forth needs column 64 clear to keep the
end of one line from connecting to the beginning of the next.  The neat
part is it would handle formats 2 and 3 above without change.

What do you think?

      [david]

-- 
======== David Douthitt :::: Madison, WI :::: The Stainless Steel Rat ========
FidoNet: 1:121/2 ::::: WittiNet: "Curiouser and curiouser, said Alice." ::::::
UseNet:  ...{rutgers|ucbvax|harvard}!uwvax!astroatc!nicmad!madnix!rat
ArpaNet: madnix!rat@cs.wisc.edu        {decvax|att}!

A-PIRARD@BLIULG11.BITNET (Andre' PIRARD) (04/12/89)

I've been reading the discussion about "file systems" with much
interest. I feel the more fundamental question is what to use
blocks for. Blocks are a very efficient means to implement some
sort of virtual storage indeed and, because of this, can be used
to contain anything of the so-called binary format. Thus, trying
to split blocks contents into varying length lines or like things
is irrelevant. On the other hand, having them hold Forth source
code was done because nothing else was available. It is one of
the worst aspects of Forth because of the difficulty to maintain
source code and the waste of disk space (about three time what is
really needed). It even influenced some people to recommend
source code horizontal layout as being good program layout style
practices.

The rational approach is to build the tools to solve the needs,
not to try turning the needs into what the tools can provide.
If source code is best held in files of variable length records,
then let us build the tools to maintain it that way. That is a
what I would call "conventional" files system, a means to
interpret these files and possibly a Forth-built-in editor if the
system does not provide editing source code without quitting
Forth.
I explained Comforth's file system in a former note. The way it
interprets a file is a recursive "FLOAD filename" being somewhat
different from LOAD in that it is externally driven (one cannot
have interpreted text jump from one line to another as LOADed
text can, or skip the rest of a block) but it does not make much
of a difference for usual source code.
A Forth-hosted editor can simply hold a file in storage if it is
not too large. The obvious advantage is that it can be
interpreted from there (we call it SLOAD, S for storage) and even
on exiting the editor to be reentered with the cursor at the
point of an interpretation error. This is much like having
shrunken source blocks in storage except that all of a set must
be in at a time (but it take 3 times less space) and that one
knows where was what at the time of a system crash.
Once a decent files system is available, communicating data
to/from other programs is a simple matter.
As to implementing blocks themselves in order to comply with the
standard for whatever else they can be put to good use, a
conventional file system makes it a straightforward optional
superset as shown in the file below. It loads in each Comforth
implementation in a system independent manner.

Andr .

\ AMP 06mar88 Comforth-83 V5.3  Copyright DIALOGIC 1988

CR .( FORTH Blocks support) CR

( This file should be loaded on top of the development system to allow access
( to block files. Then the word SET#BUFF should be executed and the new system
( saved with FORTHGEN. The number of buffers will only be effective when the
( new system is loaded.

FORTH+ DECIMAL

( --------------------- )
( CONSTANTS & VARIABLES )
( --------------------- )

VARIABLE     RECENT ( Most recently used buffer pointer )
     FILE     BLFCB ( BLOCKS File Control Block )
0 HEXA FFFF BLFCB 2!  BLFCB 2+ RECENT ! ( Force write error if not open )
VARIABLE      BLFOP ( True if file open )
         BLFOP OFF
1024 CONSTANT B/BUF ( Bytes per buffer, 83-standard imposed )
: FIRST >FIRST @ ;  ( First buffer address )
: LIMIT >LIMIT @ ;  ( Last buffer end )
64 CONSTANT   C/L   ( Editor: Characters per line )

NAUV DUP 2+ 'B NAUV ! USER SCR    ( Editor: Current screen number )
NAUV DUP 2+ 'B NAUV ! USER OFFSET ( Blocks file offset )
NAUV DUP 2+ 'B NAUV ! USER R#     ( Editor: Current character in screen )

( -------------------------- )
( INTERNAL SUPPORT FUNCTIONS )
( -------------------------- )

: MASK ( BLKNUM' -- BLKNUM : REMOVE UPDATE BIT )
  HEXA 7FFF AND ;

: ?LOADING ( -- : Check for LOADing state )
  BLK @ 0= ERROR" Use ONLY when loading" ;

: ?DISK ( -- : Check for disk error, including not open )
  BLFOP @ IF BLFCB IOERR ELSE TRUE THEN ABORT" BLOCKS file I/O error" ;

( ------------------------ )
( SYSTEM INTERFACE AND LRU )
( ------------------------ )

: R/W ( ADDR BLOCK-NUM 1/0 -- : READ/WRITE A BLOCK TO ADDR )
  >R ?DISK            ( NOT OPEN ERROR )
  B/BUF UM*           ( STARTING BYTE IN FILE )
  ROT B/BUF BOUNDS DO ( OVER STORAGE SEGMENTS )
    2DUP BLFCB POINT  ( POSITION FILE )
    I BPS             ( STORAGE ADDRESS/LENGTH )
    0 DPICK IF ( READ )
      BLFCB INDATA IF BLFCB READ DROP ELSE BLANK THEN
    ELSE BLFCB WRITE THEN
    ?DISK
    BPS 0 D+      ( INCREMENT FILE BYTE POINTER )
  BPS +LOOP
  2DROP R> DROP ;

: BEST ( BLKNUM -- ADDR FLAG : LRU BUFFER REUSED, TRUE IF TO BE READ )
  >R
  RECENT DUP @ ( PREVIOUS AND CURRENT IN SEARCH )
  BEGIN
    DUP 2- @ MASK R@ <> ( BLKNUM NOT FOUND )
    OVER @ AND          ( NOR CHAIN END )
  WHILE
    NIP DUP @
  REPEAT
  DUP @ ROT !  RECENT @ OVER !  DUP RECENT ! ( UNCHAIN, RECHAIN AT TOP )
  DUP 2+ SWAP 2- @ DUP MASK R@ <>
  IF
    DUP 0< IF 2DUP MASK 0 R/W THEN ( NOT BLKNUM, WRITE IF UPDATED )
    DROP R@ OVER 4 - !
    TRUE
  ELSE DROP FALSE THEN
  R> DROP ;

( -------------- )
( STANDARD WORDS )
( -------------- )

: EMPTY-BUFFERS ( -- : MARK ALL BUFFERS UNUSED )
  0  LIMIT FIRST 2+
  DO
    I ! I            ( CHAINING )
    HEXA 7FFF I 2- ! ( INVALIDATE BLOCK NUM )
  [ B/BUF 4 + ] LITERAL +LOOP
  RECENT ! ;

: SAVE-BUFFERS ( -- : WRITE BUFFERS TO DISK, KEEPING CONTENTS )
  RECENT
  BEGIN  @ DUP WHILE
    DUP 2+ OVER 2- @ DUP 0<  ( UPDATED )
    IF MASK 2DUP 0 R/W SWAP 4 - !
    ELSE 2DROP THEN
  REPEAT
  DROP ;

: FLUSH ( -- : SAME WITHOUT KEEPING )
  SAVE-BUFFERS EMPTY-BUFFERS ;

: BUFFER ( BLOCK -- ADDR : OBTAIN NEXT AVAILABLE BUFFER, ASSIGN IT TO BLOCK )
  BEST DROP ;

: BLOCK ( BLOCK -- ADDR : BUFFER ADDR CONTAINING BLOCK )
  >R RECENT @ DUP 2- @ MASK R@ =
  IF 2+
  ELSE DROP R@ BEST IF DUP R@ 1 R/W THEN
  THEN R> DROP ;

: UPDATE ( -- : MARK BUFFER AS CHANGED )
  RECENT @ 2- DUP @ HEXA 8000 OR SWAP ! ;

: LOAD ( BLOCK -- : START LOADING FROM BLOCK )
  BLK @ >R   >IN @ >R   #TIB @ >R   L>IN @ >R
  BLK !  >IN OFF  B/BUF #TIB !
  INTERPRET
  R> L>IN !  R> #TIB !  R> >IN !  R> BLK ! ;

( ---------------- )
( EXTENDED SUPPORT )
( ---------------- )

: -->  ( -- : CONTINUE LOADING NEXT BLOCK )
  ?LOADING >IN OFF BLK 1+! ; IMMEDIATE

: \ ( Appropriate for blocks )
  BLK @
  IF  >IN @ NEGATE  C/L MOD  >IN +!
  ELSE [COMPILE] \ THEN ; IMMEDIATE

: (LINE) ( LINE BLOCK -- ADDR COUNT )
  >R C/L B/BUF */MOD R> + BLOCK + C/L ;

: .LINE ( LINE BLOCK -- : PRINT LINE )
  (LINE) -TRAILING TYPE ;

: LIST ( BLOCK -- : LIST BLOCK CONTENTS )
  DECIMAL CR DUP SCR ! ." SCR # " .
  B/BUF C/L / 0 DO
    CR I 3 .R SPACE I SCR @ .LINE
    NOHOLD NOT ?LEAVE
  LOOP
  CR ;

: INDEX ( BLOCK1 BLOCK2 -- : PRINT FIRST LINE FROM EACH SCREEN )
  CR 1+ SWAP
  DO
    CR I 4 .R SPACE 0 I .LINE
    NOHOLD NOT ?LEAVE
  LOOP ;

: TRIAD ( BLOCK -- : PRINT 3 SCREENS STARTING FROM BLOCK )
  CR 3 OVER + SWAP
  DO
    CR I LIST NOHOLD NOT ?LEAVE
  LOOP CR ;

: .BUF ( LIST RESIDENT BUFFERS STATUS )
  RECENT
  BEGIN  @ DUP WHILE
    DUP 2- @ DUP MASK .
    DUP 0< IF ." Updated " THEN
    HEXA 7FFF = IF ." Free " THEN
  REPEAT
  DROP ;

( ------------ )
( OPEN / CLOSE )
( ------------ )

: (SRCE) ( -- ADDR : DETERMINE INPUT STREAM ADDRESS )
  BLK @ ?DUP IF BLOCK ELSE TIB THEN ;

: BCLOSE ( -- FLAG : CLOSE BLOCKS FILE )
  FALSE BLFOP @ IF ( OPEN )
    FLUSH
    BLFOP OFF
    BLFCB CLOSE OR
  THEN ;

: NOBLOCKS
  BCLOSE CLOSED? ;

: (USING) ( FNA FNL --- FLAG : SWITCH WORK FILES )
  BCLOSE IF 2DROP 2 EXIT THEN
  LIMIT FIRST - 0 B/BUF UM/MOD 1 < ABORT" Too little buffers" DROP
  EMPTY-BUFFERS
  BLFCB OPENU IF 1 EXIT THEN
  BLFCB MUTEIOERR
  BLFOP ON
  FALSE ;

: USING ( <FILENAME> : DITTO )
  "TOKEN (USING) DUP CASE
  2 OF CLOSED? ENDOF
  1 OF OPEN?   ENDOF
    DROP ENDCASE ;

( ------------ )
( PATCH SYSTEM )
( ------------ )

' (SRCE) >SOURCE PRESET

' NOBLOCKS 'B BYE !

: SET#BUFF ( N -- : SET THE NUMBER OF BUFFERS TO N )
  [ B/BUF 4 + ] LITERAL UM* DROP ( BUFFER AREA SIZE )
  NEGATE -BSIZE PRESET ;         ( SET BOOT-UP LITERAL )

( S0 SN WSAVE <FILENAME> WILL SAVE SCREENS S0 TO SN INCLUSIVE )
( TO THE HOST FILE "FILE" WITH TRAILING BLANKS REMOVED )
( --> AND ;S SHOULD THEN BE REMOVED IN ORDER TO ALLOW FLOAD )
: WSAVE ( START END <FILENAME> -- )
  "TOKEN
  FCBSIZE RESERVE
  DUP OPENO OPEN?
  SWAP 1+ ROT
  DO 16 0
    DO I 64 * J BLOCK + 64 -TRAILING 2 PICK WRITE DUP PUTEOR LOOP
  LOOP
  DUP PUTEOF CLOSE FCBSIZE FREE CLOSED? ;

CR .( Now set 'num' the number of buffers,
CR .( and save your new system.
CR .( Type :
CR .(   'num' SET#BUFF
CR .(   FLOAD FORTHGEN.F83
CR .(   FORTHGEN <filename>
CR .(   BYE
CR .( and execute your new system.