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:

```forth
SCR # 1
  0 ( #FN SIO CONSTANTS 1 )
  1
  2 BASE @ HEX
  3
  4 VOCABULARY FUJINET
  5
  6 0300 CONSTANT DDEVIC                  0301 CONSTANT DUNIT
  7 0302 CONSTANT DCOMND                  0303 CONSTANT DSTATS
  8 0304 CONSTANT DBUF                    0306 CONSTANT DTIMLO
  9 0307 CONSTANT DRESVD                  0308 CONSTANT DBYT
10 030A CONSTANT DAUX1                   030B CONSTANT DAUX2
11 030A CONSTANT DAUX
12
13 E459 CONSTANT SIOV
14
15 -->
```

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.

```forth
SCR # 70
  0 ( FLH - Data Structures )
  1
  2 : HOST-ARRAY ( 8 entries 32 bytes )
  3       <BUILDS 256 ALLOT      ( 256 Bytes )
  4       DOES>   SWAP 32 * + ;  ( each entry 32 bytes )
  5
  6 ( Create a HOST-ARRAY called FLHBUF )
  7
  8 HOST-ARRAY FLHBUF
  9
10
11
12
13
14
15 -->
```

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.

```
SCR # 71
  0 ( FLH - Get Host List )
  1
  2 BASE @   HEX
  3
  4 : (FLH)      ( -- )
  5         70 DDEVIC C!
  6         01 DUNIT  C!
  7         F4 DCOMND C!
  8         40 DSTATS C!
  9   0 FLHBUF DBUF   !
10         0F DTIMLO C!
11       0100 DBYT   !
12       0000 DAUX   !
13        (DO-SIOV) ;
14
15 BASE ! -->
```

(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: ( https://github.com/FujiNetWIFI/fujinet- … Host-Slots )
* 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:

```forth
0 FLHBUF 256 CDUMP

3000 SD--------------
3010 ----------------
3020 irata.online----
3030 ----------------
3040 fujinet.online--
3050 ----------------
3060 fujinet.pl------
3070 ----------------
3080 ----------------
3090 ----------------
30A0 ----------------
30B0 ----------------
30C0 ----------------
30D0 ----------------
30E0 ----------------
30F0 ----------------
```

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.

The result looks like this:

```forth
FLH
0 : SD
1 : fujinet.online
2 : atari-apps.irata.online
3 : fujinet.pl
4 : TMA-2
5 : RASPBERRYPI
6 :
7 :
ok
```

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.

-Thom

I am the systems guy behind IRATA.ONLINE. http://irata.online/