News for hackers. Stuff that matters.
Re:Konilo: A personal computing system in Forth by whartung on Saturday March 02, 2024 @04:29PM (#39575936)
It's interesting to contrast a "Forth OS" with a what might be considered a more conventional one. And by this I'm talking about early small machine systems like CP/M and its contemporaries.

At a fundamental level, you had a kernel of some kind, and free space within which programs could be loaded and executed. With a Forth, you could get that with about 6K of total overhead.

One of the big contrasts is that with Forth, at the time, programs were loaded from source code, whereas everyone else used binary code. But another interesting aspect was that in Forth, when you were done with a program, you had to free up the space, if necessary, to run another program. In something like CP/M, when a program was exited, the OS reclaimed the space, and, to run it again, the program would have to be reloaded from storage.

The next big difference was the file system (or lack thereof). Forth does not have a file system, it simply organizes storage as a sequence of 1K blocks. It was common to have "table of contents" screens to track where programs were stored on disk, these all had to be manually maintained.

CP/M had a much better file system, but its interesting to contrast it with the UCSD P-System, which had a very crude file system. While it indeed had named files, it suffered from requiring them to be contiguous, and really only allowing one file to be written at a time (not exactly true, but only one file could grow freely at a time -- the file "at the end"). However, reorganizing, and compacting free space was a routine task under the P-System.

The Forth system being rock simple was quite easy to make a mistake and lose data, for example if you were relocating source code to make new space, you could easily overwrite something accidentally, since you were responsible for the actual disk locations of the operation.

The final nice feature of the Forth system, speaking of disk blocks, was the crude virtual memory system. Here, disk blocks were arbitrarily mapped to memory buffers, with a simple LRU system, and dirty buffers automatically being flushed back to disk. This made persistent memory mechanics quite easy to use.

Of course, for most micro users this was not as hazardous, as most of the work was done at the "floppy" level in contrast to working on a hard disk with 5000 screens. But that was the reality of native Forth systems of the day, and folks just worked with it, and leveraged it to get work done.

The idea of those basic ideas being scaled up to a modern machine with GB of RAM and enormous hard disks is an interesting curiosity. It certainly gives you a lot of room to be sloppy. Such a system wouldn't happen, the frugal nature of those early Forth systems was no longer required, so adding complexity and space to make the userland more friendly is worth the investment.

Re:Konilo: A personal computing system in Forth by alexisread on Saturday March 02, 2024 @05:56PM (#39576544)
Sloppy, potentially, but what about an OO Forth with types and delimited continuations? That might tidy up many of the large scale footguns.

Bear in mind that a small kernel would be handy for fitting into L1 caches and GPU units.

Re:Konilo: A personal computing system in Forth by Karrot_Kream on Saturday March 02, 2024 @06:36PM (#39576866)
This is an interesting idea but I'm not sure fitting in L1 caches would work that well for threaded Forths. Most CPUs align on word boundaries based on their pipeline size and unless you organize your Forth around both the L1 size and the instruction pipeline, you're gonna get huge pipeline stalls and pay branch predictor penalties. Also with modern processors you're gonna pay a huge hit with stack operations as opposed to using registers.

As far as using OO Forth, you can also just use specific words that offer a safe interface over more dangerous raw memory words. Old Forths probably worked directly with the filesystem more because of code size limitations than any real abstraction penalty.

Re:Konilo: A personal computing system in Forth by alexisread on Saturday March 02, 2024 @06:59PM (#39576997)
The forth base I was thinking of is Freeforth, which is an STC Forth that assigns registers to the top two stack elements to avoid SWAP instructions. I take your point about caches, but surely a Forth like this that optimises the words during compile (inlining etc) would help?

I guess that this hypothetical stc Forth would have to have abstracted I/O and registers- any spare ones outside the stack management could be used for data passing, maybe as the top of the data stack.

Of note, this cache friendliness would dictate the kernel wordset, so choosing it would be dependent on your cpu targets.

Really the OO is in support of vocabulary modules and data spaces. Freeforth has a cross compiler environment to MSP430 cpus - as a module this would make hygienically writing to new targets easy.

Re:Konilo: A personal computing system in Forth by Karrot_Kream on Saturday March 02, 2024 @07:53PM (#39577331)
I took a look at Freeforth. It's a pretty innovative Forth.

> which is an STC Forth that assigns registers to the top two stack elements to avoid SWAP instructions. I take your point about caches, but surely a Forth like this that optimises the words during compile (inlining etc) would help?

Certainly. The way Freeforth seems to be doing things is by storing the swapped state and by testing against that to make a branch. I'm curious how this works with branch predictor penalties, but if swaps are done in loops then it might actually work out very favorably. You need to be careful that your code doesn't use too much ROT or DUP but as long as it's mostly SWAP oriented it should be okay.

> I guess that this hypothetical stc Forth would have to have abstracted I/O and registers- any spare ones outside the stack management could be used for data passing, maybe as the top of the data stack.

Forth implementations are notoriously architecture specific for exactly this reason and the differences in registers and stack semantics between architectures. A lot of recreational Forths target abstract VMs exactly to be able to target a single architecture and not care much about performance, but if you want to adapt Freeforth to different architectures you may need to make deep changes. I'm not sure how FASM works but maybe FASM decreases some of the pain.

Re:Konilo: A personal computing system in Forth by kragen on Saturday March 02, 2024 @06:11PM (#39576667)
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 ;
Re:Konilo: A personal computing system in Forth by kragen on Sunday March 03, 2024 @12:22AM (#39578641)
on further consideration, i think that interface requires about one and a half blocks of code to implement. this interface would be more useful and have a simpler implementation

    open   ( d -- flag )    create file d, size 0 blocks, returning 0 if ok
    extent ( d -- u )       return the number of blocks in file d, or -1 if none
    delete ( d -- )         delete file named d if it exists
    extend ( d -- flag )    append a block to file d, returning 0 if ok
    filename ( c-addr u -- d )  convert ASCII filename to a double number
    .filename ( d -- )      print the filename d
    file   ( u -- d )       return name of the uth file in the directory or 0
    maxfiles ( -- 50 )      return the maximum valid file index
    index  ( d u1 -- u2 )   return the block number for block u1 of file d
Re:Konilo: A personal computing system in Forth by abecedarius on Sunday March 03, 2024 @01:06PM (#39582816)
In the Forth golden age there was a convention of grouping blocks in triads -- you could print one per page. E.g. when I proposed to rewrite the "full-screen-style" editor at FORTH, Inc., they were like "if you think you can get it down from 4 blocks to 3, then go ahead" so it wouldn't be wasting 2/3 of a triad. (You might remember this, it was the more-conventional editor they didn't use, contributed by a customer.)

So maybe make this filesystem work on triad units: an index fits in a byte, more space for longer filenames. Not sure what to do with the extra block in the filesystem triad.

Re:Konilo: A personal computing system in Forth by kragen on Sunday March 03, 2024 @04:12PM (#39584592)
that makes sense; i had seen forth printed that way but had forgotten about it. i think the code was printed side by side with the shadow screens (the comments), requiring 132 columns (4 of padding i suppose)

i think that, if you want to put the metadata into multiple blocks, you need to have some control over the order of buffer saving. one way to do this would be to have two copies of the metadata that you alternate between, which would be desirable anyway in case of power loss during metadata writes. maybe once every 30 seconds, say. after your last modification to replica tweedledee and before your first modification to replica tweedledum, you would call save-buffers, ensuring that briefly tweedledee is also consistent on disk (tweedledum already having been consistent), even if buffer evictions later put tweedledee into a torn state

then the question is how, upon startup, to know whether to use tweedledee or tweedledum? maybe an update serial number at the beginning and end of the block that has to match?

using a cluster size larger than the forth block size seems like it might complicate the calling interface somewhat

glad to hear from you!

Re:Konilo: A personal computing system in Forth by abecedarius on Sunday March 03, 2024 @04:41PM (#39584775)
Hope you're well!

I wasn't proposing keeping file metadata in multiple blocks -- just, might as well consider if any functionality might be worth adding in the remaining block of the filesystem triad, if it's gonna be a triad (source code, metadata, empty). (This makes the larger minimum size of these triads a bit of an attractive nuisance from a "Forth Zen" pov...)

Re:Konilo: A personal computing system in Forth by kragen on Sunday March 03, 2024 @07:48PM (#39585950)
oh, yeah, that makes sense. you could probably fit most of the standard cp/m or unix filesystem utilities into the remainder; cp, rm, mv, cat, head, and so on. maybe nsweep

i'm, eh, not dead