Another example of using the #FujiNet from Forth is to do things like view the host or disk slots currently in use. This example focuses on the FLH word, which will display the available host slots.
We start with a set of constants that map to memory locations that SIO uses. These make the code easier to read, and are used by all of the words in the FUJINET vocabulary in Forth:
We also need screen 3, which provides the (DO-SIOV) word that actually tells the Atari to perform the SIO operation.
```forth SCR # 3 0 ( SIOV ) 1 2 CODE (DO-SIOV) 3 4 XSAVE STX, ( SAVE X REGISTER ) 5 SIOV JSR, 6 XSAVE LDX, ( REST X REGISTER ) 7 NEXT JMP, ( RET TO INTERPRET ) 8 9 10 11 12 13 14 15 --> ```
We need a place in memory to store the results of our host list, as well as a way to easily retrieve each host slot from this memory. We can do this by specifying a word that gives us these properties, HOST-ARRAY. HOST-ARRAY allocates a 256 byte area of memory, when we create it, and when we use it, it will return the offset for the host slot for which we ask. This is possible because Forth can define words that do one thing when being created, and another while being used. This is a concept called meta-programming. The <BUILDS word allows the programmer to specify what words are to be executed when the definition is compiled, or "built", and DOES> allows the programmer to specify what words are executed when the word is encountered.
HOST-ARRAY is defined as a compiler word, and it is immediately used to create a HOST-ARRAY called FLHBUF, which is where the host list will be stored.
the <BUILDS section is straightforward enough, it creates a 256 byte area in which data can be stored. This divides into 8 slots, each holding 32 characters.
The DOES> section, is a bit more involved, as DOES> puts an address which corresponds to the first available byte of data onto the stack, and we then swap the stack around slightly (because it is currently backwards) so that we can multiply that address by (32 * n) with n being the parameter passed in.
This creates a data type that works like this:
```forth 0 FLHBUF . 16304 ( the address in memory of the first host slot ) 1 FLHBUF . 16336 ( the address in memory of the second host slot ) 2 FLHBUF . 16368 ( the third entry ... and so on ) ```
So we have a data type that does all the heavy lifting for us when we want to start reading it.
With this, we can now create a word, (FLH) that reads the host list into our newly created buffer.
(FLH) uses the FN SIO constants defined above to make the code easier to read, putting all of the values we need into the device control block (DCB) table in memory.
* DDEVIC is set to $70, which is the SIO device ID for the #FujiNet control device * DUNIT is set to $01, because we want to refer specifically to device $70 * DCOMND is set to $F4, which is the Get Host Slot List command defined here: ( ->link<- ) * DSTATS is $40, which specifies that the Atari is expected to get a payload of data from the FujiNet. * DBUF is set to the address of the very top of FLHBUF. Since all of the host slots in FLHBUF are contiguous, we just need to specify the top of the buffer, and we can read them all in at once. * DTIMLO is set to $0F, a standard value meaning to wait 15 seconds for an answer. * DBYT is set to $0100, which is 256 in hex, meaning we are expecting a payload of 256 bytes from the FujiNet. * DAUX is set to $0000. It isn't used.
Once these values are set, the (DO-SIOV) word is used to perform the SIO operation.
The (FLH) word does nothing to the stack and outputs nothing. It is purely a procedural word that is needed to get the host list and put it into our buffer.
If we were to peek into the buffer we would see something like this:
the dashes here represent the null character, and on the Atari they would be represented by the heart character. Since the FujiNet firmware is implemented in C, the strings are null terminated, and the most straightforward way to display the data is to simply not display the null character. We can do this via a conditional that only uses the EMIT word to display the character if it is not null. Here we make a word HEMIT, that does just that:
```forth SCR # 72 0 ( FLH - HEMIT word ) 1 2 3 4 ( EMIT, but ignore null chars ) 5 6 : HEMIT ( n -- ) 7 DUP ( because 0= consumes it ) 8 0= IF ( is char a null? ) 9 DROP ( yes, drop it. ) 10 ELSE ( otherwise ... ) 11 EMIT ( Display it. ) 12 THEN ; ( Done.) 13 14 15 --> ```
HEMIT, like EMIT expects a single byte number. If that number is 0, nothing is displayed.
```forth HEMIT 65 Aok HEMIT 66 Bok HEMIT 0 ok ```
Upon entering HEMIT, we immediately DUPlicate the number coming in, because 0= will consume it and replace it with a 1 if it matches, or a 0 if it doesn't. IF will consume the 0 or the 1. If the value is 1, then we were passed in a NULL, and the number we previously duplicated is DROPped (if you make a mess, clean it up.), otherwise, we pass that duplicated number to EMIT, which displays it.
With the HEMIT word in hand, we now have everything we need to create a word, .FLH" that will display the name of one host slot. It takes one parameter, the host slot number we wish to display.
```forth SCR # 73 0 ( FLH - Display Host slot n ) 1 2 0 VARIABLE HOSTSLOT ( Temporary variable for slot ) 3 4 5 : .FLH" ( n -- ) 6 HOSTSLOT C! ( save n ) 7 32 0 DO 8 HOSTSLOT C@ FLHBUF ( Beginning of Hostslot ) 9 I + ( next character ) 10 C@ ( Get Character ) 11 HEMIT ( And display it.) 12 LOOP ; ( Done. ) 13 14 15 --> ```
To make this code easier to read (and not have to resort to return stack juggling), a variable called HOSTSLOT is defined, and is set to the parameter passed in. We then set up a loop that first gets the address of the host slot, adds the loop index to it, gets the byte number at that address, and then passes it to HEMIT for printing, then looping back until all 32 characters are printed.
This produces a word that acts like the following:
```forth (FLH) ok 0 .FLH" SD ok 1 .FLH" irata.online ok 2 .FLH" fujinet.online ok ```
...and so on.
It then becomes a trivial matter to write a simple word that does this for all 8 slots:
```forth SCR # 74 0 ( FLH - Display all Host Slots ) 1 2 3 4 : FLH ( -- ) 5 CR ( start on new line ) 6 (FLH) ( Get host slots ) 7 8 0 DO ( 0 to 7 ) 8 I U. ." : " ( show slot # ) 9 I .FLH" ( Display host slot I ) 10 CR ( CR ) 11 LOOP ; ( done. ) 12 13 14 15 ;S ```
The FLH word starts by using the CR word to make sure that output starts on the next line, otherwise the output would immediately start on the same line as FLH. The (FLH) word is called to fetch the host list, and a new loop of 8 iterations is done. Inside this loop, the I index variable is used by the U. word to display the current slot number, followed by an inline ." : " to print a colon afterwards. I is then also used as the input for .FLH" to display the value of that host slot. We then make sure to use the CR word again, to move the cursor to the next line, and terminate the loop.
At the end of the day we have a word, FLH, built on previous words to output a host list. But this is more than a command, this is a word that is now part of the language. This means it can not only be used interactively, but it can be used from within other words:
```forth : MYHOSTLIST ." This is my host list: " CR FLH ; ```
And even the individual words that make up FLH can be used in other words. In essence, the language is extended with a set of words that can do meaningful things with the FujiNet.
In the same way, other words can be added, extending the capabilities of the environment. This is part of what attracted some people to Forth, its unique ability to solve problems by extending the language.
Thomas, the above Fig-Forth screens seem to come from a ready floppy image with more FujiNet tools. You are using screens 1, 3, 70... all with the "load next screen arrow".
I can type in the selected screens but is the full ATR available somewhere perhaps? Is there a Fig-Forth floppy image available, with more FujiNet-related code (in my dreams - Forth words to manage similar operations as can be done with the N: device in Basic).