Traditionally, Forth has been used on systems without an operating system29, and in particular without a file system. Forth provides a mechanism, called blocks, for accessing mass storage on such systems.
A block is a 1024-byte data area, which can be used to hold data or Forth source code. No structure is imposed on the contents of the block. A block is identified by its number; blocks are numbered contiguously from 1 to an implementation-defined maximum.
A typical system that used blocks but no operating system might use a single floppy-disk drive for mass storage, with the disks formatted to provide 256-byte sectors. Blocks would be implemented by assigning the first four sectors of the disk to block 1, the second four sectors to block 2 and so on, up to the limit of the capacity of the disk. The disk would not contain any file system information, just the set of blocks.
On systems that do provide file services, blocks are typically implemented by storing a sequence of blocks within a single blocks file. The size of the blocks file will be an exact multiple of 1024 bytes, corresponding to the number of blocks it contains. This is the mechanism that Gforth uses.
Only one blocks file can be open at a time. If you use block words without having specified a blocks file, Gforth defaults to the blocks file blocks.fb. Gforth uses the Forth search path when attempting to locate a blocks file (see Source Search Paths).
When you access blocks, Gforth uses a number of block buffers as intermediate storage. The behaviour of the block buffers is analogous to that of a cache. Each block buffer has three states:
Initially, all block buffers are unassigned. In order to access a block, the block (specified by its block number) must be assigned to a block buffer.
The assignment of a block to a block buffer is performed by
block or buffer. Use block when you wish to
modify the existing contents of a block. Use buffer don’t care
about the existing contents of the block and just want to overwrite
it.
Once a block has been assigned to a block buffer using block or
buffer, that block buffer becomes the current block
buffer. Data may only be manipulated (read or written) within the
current block buffer.
When the contents of the current block buffer has been modified, it is
necessary, before calling block or buffer again, to
either abandon the changes (by doing nothing) or mark the block as
changed (assigned-dirty), using update. Using update does
not change the blocks file; it simply changes a block buffer’s state to
assigned-dirty. The block will be written implicitly when it’s
buffer is needed for another block, or explicitly by flush or
save-buffers.
Flush writes all assigned-dirty blocks back to the
blocks file on disk. Leaving Gforth with bye also performs a
flush.
When a block is used for storing source code, it is traditional to
display the contents as 16 lines each of 64 characters (with
list). A block provides a single, continuous stream of input
(for example, it acts as a single parse area) – there are no
end-of-line characters within a block, and no end-of-file character at
the end of a block. There are two consequences of this:
\ – comment to end of line – requires special
treatment; in the context of a block it causes all characters until the
end of the current 64-character “line” to be ignored.
In Gforth, when you use block with a non-existent block number,
the current blocks file will be extended to the appropriate size and the
block buffer will be initialised with spaces.
Gforth includes a simple block editor (type use blocked.fb 0 list
for details) but doesn’t encourage the use of blocks; the mechanism is
only provided for backward compatibility.
Common techniques that are used when working with blocks include:
thru commands which load the whole of the application.
See Frank Sergeant’s Pygmy Forth to see just how well blocks can be integrated into a Forth programming environment.
C-addr u is the name of a file; open-blocks opens
this file as the current blocks file.
Open file as the current blocks file.
Return the file-id of the current blocks file. If no blocks file has been opened, use blocks.fb as the default blocks file.
User variable containing the number of the first block (default
since 0.5.0: 0). Block files created with Gforth versions before
0.5.0 have the offset 1. If you use these files you can: 1
offset !; or add 1 to every block number used; or prepend 1024
characters to the file.
The contents of block u are found at addr (and the
following 1023 bytes). Addr is valid until there is
another call to block or buffer (possibly inside
another block-access word). If the block is not yet in a
buffer, block reads it from mass storage.
Addr (and the following 1023 bytes) are the buffer of
block u; this memory area is initialized arbitrarily.
Addr is valid until there is another call to block
or buffer (possibly inside another block-access word).
The subtle difference between buffer and block
mean that you should only use buffer if you don’t care
about the previous contents of block u.
Mark the state of the current block buffer as assigned-dirty.
If and only if there is a buffer for block u and it has
been updated, return true.
Transfer the contents of each updated block buffer to
mass storage, then mark all block buffers as assigned-clean.
Mark all block buffers as unassigned; if any had been marked as
assigned-dirty (by update), the changes to those blocks
will be lost.
Perform the functions of save-buffers then
empty-buffers.
Display block u as 16 numbered lines, each of 64 characters.
User variable containing the block number of the block most
recently processed by list.
Text-interpret block u. Block 0 cannot be loaded.
load the blocks n1 up to and including n2 in sequence.
Used within a block to load the block specified as the current block + n.
Used within a block to load the range of blocks specified as the current block + n1 up to and including the current block + n2.
If this symbol is encountered whilst loading block n,
discard the remainder of the block and load block n+1. Used
for chaining multiple blocks together as a single loadable
unit. Not recommended, because it destroys the independence of
loading. Use thru (which is standard) or +thru
instead.
Use within a block that is to be processed by load. Save
the current blocks file specification, open the blocks file
specified by a-addr u and load block 1 from that
file (which may in turn chain or load other blocks). Finally,
close the blocks file and restore the original blocks file.