Jump to content
tschak909

Programming FujiNet on Adam CP/M (language bindings)

Recommended Posts

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

 

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:

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:

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:

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:

 

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

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.
Note: Your post will require moderator approval before it will be visible.

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