How to talk to the FujiNet in CP/M. (Wanna help with lang bindings?)

Post your CP/M & TDOS questions here
Post Reply
tschak909
Posts: 29
Joined: Thu Jul 15, 2021 5:10 pm

How to talk to the FujiNet in CP/M. (Wanna help with lang bindings?)

Post by tschak909 » Thu Jan 13, 2022 2:18 pm

#FujiNet #ColecoAdam #CPM I can work with somebody to build bindings for their favorite language, e.g. Turbo PASCAL. Anyone want to jump in?

All FujiNet networking functions can be done with writes to the correct AdamNet DCB, we just need a few bits of info, here's the data structure for a DCB in C:

Code: Select all

typedef struct _dcb
{
  unsigned char status;      // 1
  void *buf;                 // 2 
  unsigned short len;        // 2 
  unsigned long block;       // 4
  unsigned char unit;        // 1
  unsigned char reserved0;   // 1 
  unsigned char reserved1;   // 1
  unsigned char reserved2;   // 1
  unsigned char reserved3;   // 1
  unsigned char reserved4;   // 1
  unsigned char reserved5;   // 1
  unsigned char dev;         // 1
  unsigned short max_len;    // 2
  unsigned char type;        // 1
  unsigned char dev_status;  // 1
} DCB;                       // 21 bytes total
And these exist contiguously immediately after the 4 bytes that comprise the PCB. One DCB for each device found on the AdamNet.

Notable entries:

* status - This does double duty, as both the status and command byte. You write the command, the Adam does the operation, and writes back a status sometime later.
* buf - 16 bit memory address in Z80 space for the target operation
* len - Length of I/O operation relative to the buffer.
* block - Used by block devices to denote the desired 32-bit block address.
* unit - logical unit number specified.
* dev - The Adamnet node number for this device. (1 = keyboard, 2 = printer, etc.)
* max_len - The maximum size of payload allowed for this device
* type - Device type, 0 = character, 1 = block
* dev_status - The 8-bit status byte returned in the last byte of a status packet.

So all we have to do for a typical program is to:

* Define a data structure that nicely maps a DCB
* Find the DCB we want in memory by comparing against the dev entry until we find it.
* Write to that section of memory any time we want something to happen for FujiNet
* Periodically poll status to see when that operation is considered complete by the 6801.

For MBASIC, I was able to do this with the following code:

Code: Select all

64000 REM FUJINET ROUTINES
64010 REM ----------------
64011 REM DCB FUNCS
64012 REM ----------------
64020 DCBLEN=21:DCBEGIN=&HFEC4:DCBNUM=&HFEC3
64021 NET$=STRING$(255,0)
64022 STAT=1:WRI=3:REA=4
64030 DEF FN DCB(X)=X*DCBLEN+DCBEGIN
64040 DEF FN DCBSTATUS(X)=PEEK(FNDCB(X))
64050 DEF FN DCBCMD(X)=FNDCB(X)
64060 DEF FN DCBBUFL(X)=FNDCB(X)+1
64061 DEF FN DCBBUFH(X)=FNDCB(X)+2
64070 DEF FN DCBDEVSTATUS(X)=PEEK(FNDCB(X)+20)
64080 DEF FN DCBDEV(X)=PEEK(FNDCB(X)+16)
64081 DEF FN DCBLENL(X)=FNDCB(X)+3
64082 DEF FN DCBLENH(X)=FNDCB(X)+4
64090 DEF FN DCBNUM(X)=PEEK(DCBNUM)
The above sets up a nice little data structure that can easily traverse through the DCBs, finding them relative to the PCB at $FEC0.

You can then find the correct DCB with the following code:

Code: Select all

64093 REM --------
64100 REM FIND DCB
64101 REM --------
64110 FOR X=0 TO FNDCBNUM(0)-1
64120 IF FNDCBDEV(X)=9 THEN NET=X:RETURN
64130 NEXT X
64140 RETURN
Where we look for the DCB that matches device ID 9, or the first network device, and if found, set NET to it.

We can then send network operations with the following common code:

Code: Select all

64150 REM --------------------
64200 REM DO FUJINET OPERATION
64210 REM --------------------
64220 GOSUB 64100:REM FIND DCB
64230 POKE FNDCBBUFL(NET),PEEK(VARPTR(NET$)+1)
64240 POKE FNDCBBUFH(NET),PEEK(VARPTR(NET$)+2)
64250 POKE FNDCBLENL(NET),PEEK(VARPTR(NET$))
64260 POKE FNDCBLENH(NET),0
64270 POKE FNDCBCMD(NET),CMD
64280 IF FNDCBSTATUS(NET)<&H80 THEN GOTO 64280
64290 RETURN
Because of the functions defined above, finding the correct addresses in memory is very easy, and we simply POKE the operation we want to do, taking special care to make sure that the very last byte written is the command (into the status/cmd byte!).

After this, it's a simple matter of checking the status byte for the last bit set, to indicate that the command has completed.

After this, doing FujiNet commands is as simple as:

Code: Select all

NET$="O"+CHR$(12)+CHR$(0)+"N:TCP://192.168.1.10:8080/":CMD=WRI:GOSUB 64200:REM OPEN
NET$="WTHIS STRING GETS WRITTEN OUT."+CHR$(13):CMD=WRI:GOSUB 64200 REM WRITE
NET$="C":CMD=WRI:GOSUB 64200:REM CLOSE
NET$=STRING$(255,0):CMD=REA:GOSUB 64200:REM READ ANYTHING WAITING INTO NET$
and so on. Does this make sense?

-Thom

User avatar
Wmaalouli
Posts: 155
Joined: Sat Jul 20, 2019 2:09 pm

Re: How to talk to the FujiNet in CP/M. (Wanna help with lang bindings?)

Post by Wmaalouli » Fri Jan 14, 2022 9:19 am

This looks pretty simple to port to Turbo Pascal based on your Basic example using record structures.
Once I have a Fujinet device for the Adam, I might want to mess with this a bit.
Are these available yet for purchase by the way?

tschak909
Posts: 29
Joined: Thu Jul 15, 2021 5:10 pm

Re: How to talk to the FujiNet in CP/M. (Wanna help with lang bindings?)

Post by tschak909 » Fri Jan 14, 2022 9:36 pm

Not yet. John Lundy will be making production units available, once the firmware reaches 1.0. Anyone who wants to build them, can already, as we've released the hardware information, and the firmware is public.

Anyone who wants to work on firmware can get a beta unit, as I can really use the help.

-Thom

Post Reply