Jump to content
tschak909

Making binding for FORTH.

Recommended Posts

Given that #FujiNet can be wholly used over SIO, it should be possible to make bindings that can work in any FORTH.

 

Problem is, the part of my brain that used to be able to grok FORTH seems to have decomposed too much for me to remember enough to do it. sigh.

 

e.g. it should be simple enough to make an easy set of constants for the DCB

 

BASE @ HEX

( CONSTANTS )

0300 CONSTANT DDEVIC
0301 CONSTANT DUNIT
0302 CONSTANT DCOMND
0303 CONSTANT DSTATS
0304 CONSTANT DBUF
0306 CONSTANT DTIMLO
0307 CONSTANT DRESVD
0308 CONSTANT DBYT
030A CONSTANT DAUX
030A CONSTANT DAUX1
030B CONSTANT DAUX2

BASE @ ;S

but I am really blanking on best practices for fixed length string buffers.

 

e.g. the OPEN command:

https://github.com/FujiNetWIFI/fujinet-platformio/wiki/N%3A-SIO-Command-'O'---Open

 

Needs DBUF pointing to a 256 byte area containing a devicespec, with

* DDEVIC being $71

* DUNIT being anything from 1 to 8.

* DBYT set to 256

* DAUX1 set to the file open mode (4, 8, 12, whatever)

* DAUX2 set to the desired translation mode

 

I initially tried to do something like

: DEVICESPEC 256 ALLOT

to get a 256 byte buffer, but the word became unresolvable.

 

sigh. I forgot all of this stuff, and honestly, I'm wondering if anyone actually gives a shit, because I am busting my ass trying to make all of this available so people can build on it, and I feel like I'm building a city with nobody in it. fucking hell...

 

-Thom

Share this post


Link to post
Share on other sites
51 minutes ago, tschak909 said:

Given that #FujiNet can be wholly used over SIO, it should be possible to make bindings that can work in any FORTH.

 

Problem is, the part of my brain that used to be able to grok FORTH seems to have decomposed too much for me to remember enough to do it. sigh.

 

e.g. it should be simple enough to make an easy set of constants for the DCB

 

BASE @ HEX

( CONSTANTS )

0300 CONSTANT DDEVIC
0301 CONSTANT DUNIT
0302 CONSTANT DCOMND
0303 CONSTANT DSTATS
0304 CONSTANT DBUF
0306 CONSTANT DTIMLO
0307 CONSTANT DRESVD
0308 CONSTANT DBYT
030A CONSTANT DAUX
030A CONSTANT DAUX1
030B CONSTANT DAUX2

BASE @ ;S

but I am really blanking on best practices for fixed length string buffers.

 

e.g. the OPEN command:

https://github.com/FujiNetWIFI/fujinet-platformio/wiki/N%3A-SIO-Command-'O'---Open

 

Needs DBUF pointing to a 256 byte area containing a devicespec, with

* DDEVIC being $71

* DUNIT being anything from 1 to 8.

* DBYT set to 256

* DAUX1 set to the file open mode (4, 8, 12, whatever)

* DAUX2 set to the desired translation mode

 

I initially tried to do something like

: DEVICESPEC 256 ALLOT

to get a 256 byte buffer, but the word became unresolvable.

 

sigh. I forgot all of this stuff, and honestly, I'm wondering if anyone actually gives a shit, because I am busting my ass trying to make all of this available so people can build on it, and I feel like I'm building a city with nobody in it. fucking hell...

 

-Thom

The top Forth experts seems to hang out in the Ti-99 subforum... 

Share this post


Link to post
Share on other sites

Forth is so bizarre, but also very cool from what I've seen. Someday I really want to take a deep dive and try to learn it, because I find it fascinating. Good luck with the project Thom, I wish I could help, but it's way beyond my gray matter presently.

Share this post


Link to post
Share on other sites
11 hours ago, tschak909 said:

I forgot all of this stuff, and honestly, I'm wondering if anyone actually gives a shit, because I am busting my ass trying to make all of this available so people can build on it, and I feel like I'm building a city with nobody in it. fucking hell...

 

Well, be glad you have a part of your brain that can grok Forth, that's better than me.

 

But seriously, dude you're heading for burnout.  Wait for some excited FORTH programmer to show up asking for a way to use Fujinet, and then build them the bindings (or show them how to do it).  In the meantime, do the things that (a) people are asking for and (b) make you happy.  There's nothing wrong in wanting some gratification for your efforts, and creating bindings for non-existent FORTH programmers won't do that.

  • Like 3

Share this post


Link to post
Share on other sites

Sorry, no.

 

People are simultaneously not getting what is actually possible, while basically waiting for me to somehow bring the mountain to them. It's somewhat frustrating.

 

(I did not mean to derail this topic, I will try to keep my commentary to myself)

Share this post


Link to post
Share on other sites

Back to my original question, what is the best way to define a 256 byte character array in the dictionary? Do I need to go all out and implement a <BUILDS DOES> structure?

 

-Thom

 

Share this post


Link to post
Share on other sites

How’s Forth on the Atari anyway? What’s the plus value in learning it over plain assembler, or C?

Share this post


Link to post
Share on other sites
5 minutes ago, Tuxon86 said:

How’s Forth on the Atari anyway? What’s the plus value in learning it over plain assembler, or C?

I really had hoped to keep this thread on topic, but I guess that just isn't happening.

 

It's fine, I'm just trying to provide yet another set of bindings so it can be used with FujiNet.

 

-Thom

  • Like 1

Share this post


Link to post
Share on other sites

Grinding through it

 



SCR # 0 
  0 ( #FUJINET Vocabulary Disk )
  1 
  2 Author: Thom Cherryhomes
  3   <[email protected]>
  4 
  5 This disk contains a vocabulary
  6 for fig-FORTH users which provides
  7 input and output functions to a
  8 #FUJINET network adapter.
  9 
 10 All code is licensed under the
 11 GNU Public License v. 3
 12 
 13 
 14 
 15 


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 -->


SCR # 2 
  0 ( SIO CONSTANTS 2 )
  1 
  2 ( N: DEVICE IDs )
  3 
  4 1 CONSTANT N1:                        2 CONSTANT N2:
  5 3 CONSTANT N3:                        4 CONSTANT N4:
  6 5 CONSTANT N5:                        6 CONSTANT N6:
  7 7 CONSTANT N7:                        8 CONSTANT N8:
  8 
  9 02EA CONSTANT DVSTAT
 10 
 11 
 12 
 13 
 14 
 15 -->





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 -->


SCR # 4 
  0 ( N: CLOSE )
  1 
  2 : NCLOSE                              ( Nx: )
  3 
  4 71   DDEVIC C!                        ( N: DEVICE )
  5       DUNIT C!                        ( FROM STACK )
  6 43   DCOMND C!                        ( 'C' CLOSE )
  7 00   DSTATS C!                        ( NO PAYLOAD )
  8 0000 DBUF !                           ( NO BUFFER )
  9 0F   DTIMLO C!                        ( 15 S TIMEOUT )
 10 0000 DBYT !                          ( NO BYTES )
 11 0000 DAUX !                          ( NO AUX )
 12 
 13 (DO-SIOV) ;
 14 
 15 -->


SCR # 5 
  0 ( N: STATUS )
  1 
  2 02EA CONSTANT BW                      ( BYTES WAITING )
  3 02EC CONSTANT CONN?                   ( CONNECTED ? )
  4 02ED CONSTANT NERR                    ( ERROR CODE )
  5 
  6 : NSTATUS   ( N: -- )
  7 
  8 ( Set up DCB )        53    DCOMND C!
  9 
 10 71   DDEVIC C!        ( n )  DUNIT C!
 11 40   DSTATS C!        DVSTAT DBUF  C!
 12 0F   DTIMLO C!             4 DBYT  C!
 13 (DO-SIOV) ;
 14 
 15 -->





SCR # 6 
  0 ( N: READ/WRITE )
  1 
  2 : (NR/W)  ( buf len N: -- )
  3          71    DDEVIC C!        ( n )  DUNIT C!
  4          (len) DUP DBYT ! DAUX !
  5          (buf) DBUF !
  6          (DO-SIOV ) ;
  7 
  8 : NREAD  ( buf len N: -- )
  9          52 DCOMND C! 40 DSTATS C!
 10         (NR/W) ;
 11 
 12 : NWRITE ( buf len N: -- )
 13          57 DCOMND C! 80 DSTATS C!
 14          (NR/W) ;
 15 


SCR # 7 
  0 ( N: DEVICESPEC BUFFER )
  1 
  2 ( CHAR BUFF  WORD )
  3 : CBUF   <BUILDS ALLOT DOES> ;
  4 
  5 256 CBUF DSPEC  ( a 256 byte devicespec buffer )
  6 
  7 
  8 
  9 
 10 
 11 
 12 
 13 
 14 
 15 -->


SCR # 8 
  0 ( STRING FUNCTIONS )
  1 
  2 ( JUMP OVER INLINE STRING )
  3 : ["] R COUNT DUP 1+ R> + >R ;
  4 
  5 ( INLINE STRING )
  6 : "      ( -- addr len )
  7     22 STATE @ IF
  8         COMPILE ["] WORD HERE [email protected] 1+ ALLOT
  9     ELSE
 10         WORD HERE DUP [email protected] 1+ PAD SWAP CMOVE PAD COUNT
 11     THEN ; IMMEDIATE
 12 
 13 
 14 
 15 -->





SCR # 9 
  0 ( STRING -> BUFFER )
  1 
  2 : "->BUFFER
  3        2DUP + >R
  4        SWAP CMOVE R> 0 SWAP C! ;
  5 
  6 ( String to Devicespec Buffer )
  7 
  8 : ->DSPEC DSPEC "->BUFFER ;
  9 
 10 
 11 
 12 
 13 
 14 
 15 ;S
    

-Thom

  • Like 1

Share this post


Link to post
Share on other sites

Am pointing @cas to this thread. :)

 

(p.s. strings in fig-forth are a pain in the ass. Am really struggling trying to come up with a way to easily set a device spec that can be used EITHER in an immediate mode, OR as part of a compiled definition.)

 

I had wanted to do something like:

 

DEVICESPEC N1:   ok
N1: SET-DEVICESPEC N1://TNFS://RASPBERRYPI/

and then it could be passed to NOPEN.

N1: 0 4 OPEN    ok

-Thom

Share this post


Link to post
Share on other sites
SCR # 0 
  0 ( #FUJINET Vocabulary Disk )
  1 
  2 Author: Thom Cherryhomes
  3   <thom.cherryhomes@gmail.com>
  4 
  5 This disk contains a vocabulary
  6 for fig-FORTH users which provides
  7 input and output functions to a
  8 #FUJINET network adapter.
  9 
 10 All code is licensed under the
 11 GNU Public License v. 3
 12 
 13 
 14 
 15 

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 -->

SCR # 2 
  0 ( SIO CONSTANTS 2 )
  1 
  2 ( N: DEVICE IDs )
  3 
  4 1 CONSTANT N1:                        2 CONSTANT N2:
  5 3 CONSTANT N3:                        4 CONSTANT N4:
  6 5 CONSTANT N5:                        6 CONSTANT N6:
  7 7 CONSTANT N7:                        8 CONSTANT N8:
  8 
  9 02EA CONSTANT DVSTAT
 10 
 11 
 12 
 13 
 14 
 15 -->

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 -->

SCR # 4 
  0 ( N: CLOSE )
  1 
  2 : NCLOSE                              ( Nx: )
  3 
  4 71   DDEVIC C!                        ( N: DEVICE )
  5       DUNIT C!                        ( FROM STACK )
  6 43   DCOMND C!                        ( 'C' CLOSE )
  7 00   DSTATS C!                        ( NO PAYLOAD )
  8 0000 DBUF !                           ( NO BUFFER )
  9 0F   DTIMLO C!                        ( 15 S TIMEOUT )
 10 0000 DBYT !                          ( NO BYTES )
 11 0000 DAUX !                          ( NO AUX )
 12 
 13 (DO-SIOV) ;
 14 
 15 -->

SCR # 5 
  0 ( N: STATUS )
  1 
  2 02EA CONSTANT BW                      ( BYTES WAITING )
  3 02EC CONSTANT CONN?                   ( CONNECTED ? )
  4 02ED CONSTANT NERRCODE                ( ERROR CODE )
  5 
  6 : NSTATUS   ( N: -- )
  7 
  8 ( Set up DCB )        53    DCOMND C!
  9 
 10 71   DDEVIC C!        ( n )  DUNIT C!
 11 40   DSTATS C!        DVSTAT DBUF  C!
 12 0F   DTIMLO C!             4 DBYT  C!
 13 (DO-SIOV) ;
 14 
 15 -->

SCR # 6 
  0 ( N: READ/WRITE )
  1 
  2 : (NR/W)  ( buf len N: -- )
  3          71    DDEVIC C!        ( n )  DUNIT C!
  4          ( len ) DUP DBYT ! DAUX !
  5          ( buf ) DBUF !
  6          (DO-SIOV) ;
  7 
  8 : NREAD  ( buf len N: -- )
  9          52 DCOMND C! 40 DSTATS C!
 10          (NR/W) ;
 11 
 12 : NWRITE ( buf len N: -- )
 13          57 DCOMND C! 80 DSTATS C!
 14          (NR/W) ;
 15 -->

SCR # 7 
  0 ( N: STRING WORDS )
  1 
  2 : ["]
  3       R COUNT DUP 1+ R> + >R ;
  4 
  5 : "  ( start embedded string )
  6      22 STATE @ IF
  7          COMPILE ["] WORD HERE [email protected] 1+ ALLOT
  8      ELSE
  9          WORD HERE DUP [email protected] 1+ PAD SWAP CMOVE PAD COUNT
 10      THEN ; IMMEDIATE
 11 
 12 : <-STRING ( src len dest -- )
 13      2DUP + >R
 14      SWAP CMOVE R> 0 SWAP C! ;
 15 -->

SCR # 8 
  0 ( N: DEVICESPEC WORDS )
  1 
  2 ( BUFFER TO HOLD DEVICESPEC )
  3 0 VARIABLE DSPEC   254 ALLOT
  4 
  5 ( CLEAR DEVICESPEC )
  6 : CL/DSPEC    DSPEC 256 0 FILL ;
  7 
  8 ( SET DEVICESPEC )
  9 : <-DSPEC
 10     CL/DSPEC
 11     DSPEC <-STRING ;
 12 
 13 
 14 
 15 -->

SCR # 9 
  0 ( N: OPEN )
  1 
  2 : NOPEN ( Nx: aux2 aux1 str -- )
  3         <-DSPEC
  4   DSPEC DBUF !
  5         DAUX1 C!
  6         DAUX2 C!
  7         DUNIT C!
  8      4F DCOMND C!
  9      71 DDEVIC C!
 10     100 DBYT !
 11      80 DSTATS C!
 12         (DO-SIOV) ;
 13 
 14 
 15 -->

SCR # 10 
  0 ( N: INTERRUPT ROUTINE )
  1 
  2 ( INTERRUPT FLAG )
  3 
  4 0    VARIABLE TRIP
  5 
  6 ( INTERRUPT ROUTINE )
  7 
  8 CODE (VPRCED)
  9    01 LDA,
 10  TRIP STA,
 11       PLA,
 12       RTI,
 13 
 14 
 15 -->

SCR # 11 
  0 ( N: INTERRUPT PIA WORDS )
  1 
  2 
  3 ( PIA WORDS )
  4 
  5 D302 CONSTANT PACTL
  6 
  7 ( ENABLE PIA INTERRUPT CTL )
  8 
  9 : INTEN
 10     PACTL [email protected]
 11     1 OR
 12     PACTL C! ;
 13 
 14 
 15 -->

SCR # 12 
  0 ( N: INTERRUPT ON/OFF )
  1 
  2 0202 CONSTANT VPRCED
  3 E4C0 CONSTANT DEFINTR
  4 
  5 : VPRCED-ON
  6     (VPRCED) VPRCED ! ;
  7 
  8 : VPRCED-OFF
  9     DEFINTR VPRCED ! ;
 10 
 11 
 12 
 13 
 14 
 15 -->

SCR # 13 
  0 ( N: ERROR )
  1 
  2 : NERR NSTATUS NERRCODE [email protected] ;
  3 
  4 
  5 
  6 
  7 
  8 
  9 
 10 
 11 
 12 
 13 
 14 FORTH DEFINITIONS
 15 BASE ! ;S

The screens thus far.

 

NOPEN and NCLOSE work:

 

WIN_20210402_23_09_52_Pro.thumb.jpg.6e4116127cd7ad4bfe01adf59c1f7f2c.jpg

 

nread and nwrite are not using the target buffers. argh :(

 

Share this post


Link to post
Share on other sites

I am working on N: bindings for FORTH. Here is a small explanation of the NOPEN word:

```forth

SCR # 9 
  0 ( N: OPEN )
  1 
  2 : NOPEN ( aux2 aux1 str Nx: -- )
  3         DUNIT C!                      ( Nx: to DUNIT )
  4         ->DSPEC                       ( string into DSPEC )
  5   DSPEC DBUF !                        ( point DBUF to DSPEC )
  6         DAUX1 C!                      ( set DAUX1 to aux1 )
  7         DAUX2 C!                      ( set DAUX2 to aux2 )
  8      71 DDEVIC C!                     ( DDEVIC = $71 FN N: )
  9      4F DCOMND C!                     ( DCOMND = 'O' )
 10      80 DSTATS C!                     ( DSTATS = $80 -> FN )
 11     100 DBYT !                        ( DBYT = 256 bytes )
 12       F DTIMLO !                      ( DTIMLO = 15 secs )
 13         (DO-SIOV) ;
 14 
 15 -->

```

First, an example of how to use it:

```forth
0 12 " N:TCP://bbs.fozztexx.net:23/" N1: NOPEN   ok
```

NOPEN takes four parameters:

  • the aux2 value (translation mode)
  • the aux1 value (read/write/etc)
  • * The Devicespec (given as an in-line string)
  • * The Unit number (the word N1: is an alias to 1 here)

These values are popped off the stack and immediately assigned into their respective addresses in memory, which correspond to the Device Control Block (DCB) at address $0300 

For most of these, the C! and ! words are used to set their values, and they both take the same parameters:

```
<value> <address> C!
<value> <address> ! 
```

Since these values come off the stack, the C! next to DUNIT gets its value from the next value on the stack.

->DSPEC is a word defined on the previous screen to quickly take the in-line string on the stack and put it in a 256 byte buffer called DSPEC (which is cleared before it's moved)

it's defined here, along witrh DSPEC and CL/DSPEC words to manage its little buffer:

```forth
SCR # 8 
  0 ( N: DEVICESPEC WORDS )
  1 
  2 ( BUFFER TO HOLD DEVICESPEC )
  3 0 VARIABLE DSPEC   254 ALLOT
  4 
  5 ( CLEAR DEVICESPEC )
  6 : CL/DSPEC    DSPEC 256 0 FILL ;
  7 
  8 ( SET DEVICESPEC )
  9 : ->DSPEC
 10     CL/DSPEC
 11     DSPEC <-STRING ;
 12 
 13 
 14 
 15 -->
```

(why 254? variable automatically allots 2 bytes, we just need 254 more for 256.)

 

and <-STRING is part of a small set of string words that define the " and <-STRING words we need to easily make in-line strings and put them into buffers:

```forth
SCR # 7 
  0 ( N: STRING WORDS )
  1 
  2 : ["]
  3       R COUNT DUP 1+ R> + >R ;
  4 
  5 : "  ( start embedded string )
  6      22 STATE @ IF
  7          COMPILE ["] WORD HERE [email protected] 1+ ALLOT
  8      ELSE
  9          WORD HERE DUP [email protected] 1+ PAD SWAP CMOVE PAD COUNT
 10      THEN ; IMMEDIATE
 11 
 12 : <-STRING ( src len dest -- )
 13      2DUP + >R
 14      SWAP CMOVE R> 0 SWAP C! ;
 15 -->
```

Yes, screen 7 looks scary, but to explain as simply as I can, You don't want to interpret strings, you just want to have the interpreter skip over them to the next thing that it can interpret, keeping it in memory long enough to put it into a buffer. That's what the above words do, taking into account that a Forth programmer wants to be able to use in-line strings either compiled into a word, or interactively. That's what the first two words implement. The third word ->STRING takes the address generated by the " word, as well as its length, and moves it into the target buffer.

 

With these words, you have everything you need to implement NOPEN, which needed a way to specify a devicespec string.

 

The only thing left is, what's (DO-SIOV) ?

 

It's a word implemented in assembler which calls the SIO vector (routines) in the Atari, which take the values we've previously put onto the DCB, and does the requested I/O operation.

 

It's implemented like this in Forth Assembler:

```
SCR # 3 
  0 ( SIOV )
  1 
  2 CODE (DO-SIOV)
  3 
  4 XSAVE STX,   ( SAVE X REGISTER )
  5 SIOV JSR,    ( CALL SIOV)
  6 XSAVE LDX,   ( RESTORE X REGISTER )
  7 NEXT JMP,    ( RET TO INTERPRETER )
  8 
  9 
 10 
 11 
 12 
 13 
 14 
 15 -->
```

Since the X register is extensively used by the fig-FORTH interpreter, and SIOV also uses the X register, we need to temporarily save the X register contents into a location that fig-FORTH provides for this purpose. Other than that, we simply call SIOV, restore the X register, and jump back to the interpreter. The CODE word is a special word in Forth that indicates that the following word is for assembler code to be entered using the Forth assembler. Instructions are entered in stack order, with the operand first, followed by 'deposit' words that end in comma indicating that the instruction is to be deposited at the next available byte in memory.

 

Odd? yes.

 

Why am I doing this? To show that the FujiNet can be used anywhere on the Atari, even on systems that do not have a formal file system, such as fig-Forth.

 

-Thom 

Share this post


Link to post
Share on other sites
Posted (edited)

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-platformio/wiki/SIO-Command-%24F4-Read-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 [email protected] FLHBUF ( Beginning of Hostslot )
  9         I +               ( next character )
 10         [email protected]                ( 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
```

 

WIN_20210403_23_50_07_Pro.thumb.jpg.ebe11f979fde19544681ce5c51273eaa.jpg

 

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 ; ok 
```

 

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

Edited by tschak909
  • Like 1

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...

  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...