chuck moore is running colorforth as a process under windows these days because it's gotten too hard to write the damn device drivers

before that, though, he was using ram as his 'block file' (i think having reduced from 1024-byte blocks to 256-byte blocks) and only using the disk to make changes persistent. also he still recompiles programs from source every time he switches context from one program to another; the program he's switching from gets discarded, and the one he's switching to gets recompiled

i remember having to defragment my floppy under the p-system in order to make a big enough piece of free space for the file i wanted to copy onto it. i have the vague memory that cp/m supported non-contiguous files; hdos certainly did

while i don't intend to question your assertion that forth users at the time maintained their tables of contents blocks manually, it seems like you could implement a fat-like directory-maintenance system in less than one screen of code. like cp/m, you could maintain file sizes in blocks rather than in bytes (but 1024-byte blocks, of course); like some dec tape formats (?), you could use four bytes for the filename with a weird encoding (forth's native base 36 instead of dec radix-50). suppose you have a double-sided disk with 40 tracks of 9 512-byte sectors per side; that's 720 in total, or 360 forth blocks. you can't fit a block number into one byte, so the simplest thing is to use two. so if you want to make fat-style linked lists, you need 720 bytes of block numbers, for the following block number. that leaves you 304 bytes for filenames and starting block numbers; if filenames are 4 bytes and starting block numbers are 2 bytes you have room for 50 files, which is probably okay

so i feel like maintaining a table of contents automatically would have cost about 1% of the disk space (1 block for the code and 1 block for the directory and allocation table), so it would have been a good idea

the interface might look something like

open ( d u -- flag ) create file named d, size u blocks, returning 0 on success
extent ( d -- u ) return the number of blocks in file d, or 0 if nonexistent
delete ( d -- ) delete file named d if it exists
filename ( c-addr u -- d ) convert ascii filename to a double-precision number
.filename ( d -- ) print the filename d
file ( u -- d ) return the name of the uth file in the directory or 0 if none
maxfiles ( -- 50 ) return the maximum possible file index to pass to file
index ( d u1 -- u2 ) return the block number for block u of file d

and from there you can use the usual forth block words to read or modify the contents of the files, such as block, update, load, and save-buffers. things like refill and thru, which presuppose semantics for the sequencing of block numbers, might benefit from file-aware versions. for example, you might supplement thru with a word old which loads each block of a file, whatever sequence they're in on disk:

2variable oldfile
: old ( d -- ) 2dup oldfile 2! extent 0 ?do oldfile 2@ i index load loop ;

or i guess that, if you can trust the code being loaded to not leave crap on the stack between blocks, it might be justifiable to just write

: old ( d -- ) 2dup extent 0 ?do 2dup i index load loop 2drop ;

you might also want to list the directory of files:

: stat ( d -- ) 2dup or if 2dup .filename ." " extent . cr else 2drop then ;
: ls-l ( -- ) maxfiles 0 do i file stat loop ;