Tutorials:Calcnet 2.2 For The Simple Minded

From Doors CS, Doors CSE, and Doors CE Wiki
Jump to: navigation, search

Note: This is not a finished tutorial

Although it has completed sections, this is not the final guide. Please feel free to read it and make comments. You can contact me on IRC via irc.efnet.org #cemetech user Geekboy.


Well, I was reading Kerm’s white papers on CALCnet and needless to say got overly confused to the point where I was getting headaches…but I now understand it and am going to make it a bit simpler and more to the point without all the technical know-how.

To start off I will explain more or less how CALCnet sends and receives data. By this I mean where and how it accesses it and what you as an end user need to do to use it. If you want to know the technical stuff then you can read the white pages.

Now for simplicity's sake in this tutorial, (I am assuming you are using Brass I have no idea if this works elsewhere) I am going to start referring to these hex equates as the following..

CALCnet Equates
Equate Address Description
MyID $86EE Your Calculator's ID
IncomingID $86F3 The ID of the calculator that sent you the data
IncomingSize $86F8 The size of the data that was received
IncomingData $86FA The data received
OutgoingID $87FA The ID of the calculator you are sending to
OutgoingSize $87FF The size of the data being sent
OutgoingData $8801 The data being sent


CALCnet sends data in an up to 256 byte buffer at ‘OutgoingData‘ by using an interrupt to handle it. All you need to do is write the data to be sent there, put the receiver's ID in ‘OutgoingID’, and then put the size of the data you are sending (in bytes) in ‘OutgoingSize’. After you do all of this you set the high bit of ‘OutgoingSize+1’ to tell CALCnet data is ready to be sent in which it takes over.

For receiving data, when the data is sent to your calculator it is automatically written to the ‘IncomingData’ buffer and the size of the received data is written to ‘IncomingSize’. Along with this the ID of the sending calculator is written to ‘IncomingID’. After everything is received and verified the high bit of ‘IncomingSize+1’ is set.

How this information is used (or rather, what to do with it)

Well, I could start on how we use the info above to make programs send data back and forth, but that would be pointless if we don’t first talk about how the network is setup. And who is sending what data to who, and where the data is coming from.

For this we have a few options:

  • Peer to Peer
  • Server and Client

However, we also have to consider that we are not working with a powerful computer so we tend to mix these two concepts.

In peer to peer networks the calculators are aware of each other. This allows them to talk to each other directly without needing a server.

In the Server and Client configuration the client calculators only know of one other person on the network - the server. The server knows all of the calculators on the network. This then forwards the messages to the appropriate peer on the network. A real-life example would be this wiki, or indeed any website. The website could have any number of users at a given moment on different pages and doing different things. The server, however, knows who is browsing that website while you do not directly. It is also important to understand the concept of getting the right message delivered to the right person. If you click on a link, you expect to go to that page and not some other page.

So what do we do to make this easier on the network and its clients so that one calculator does not have to deal with the entire data load for all the calculators is designate a single server calculator, or "manager". This manager calculator connects with all of the network's client and then sends out their IDs to all the other calculators. This then enables the calculators to talk in a peer to peer fashion, because they know each other's individual IDs. Making the connections strictly peer to peer will be faster and more efficient down the road.

Putting The Ideas Into Code

We will be following this flow chart as it is the general way to initialize a set of CALCnet clients and distribute the IDs to every client involved:

 call Cn2_Setup     ; this initializes CALCnet's interrupt

That is pretty simple. One call and CALCnet is active. You don't have to do anything more.

Now we need to decide who is going to be the server and who are the clients. The way that this has been done in the past is to show a simple screen which allows you to assign your calculator a number - 1 for the server, and 2-9 for the clients.

 call Cn2_GetK ; This gets the key being held and returns it in A 
 cp sk1        ; subtract the value for 1 from the output of Cn2_GetK
 jp z,Server   ; if we are #1 we are a server and so we jump to the server branch
 cp sk2        ; subtract the value for 2 from a
 jp z,client   ; if we are not the server we are a client so we jump to that code

; if you intend on having more clients you can repeat this setup over and over
; and it will work just the same

 halt  ; power management and let the CALCnet interrupt fire
 jr InitPosLoop  ; go back to the start of the loop and repeat

Okay, now that we know who is who we will start with the easier of the two - the client. So to start we wait for a frame to come in:

 ld a,(incomingsize) ; load a with the incoming flag byte
 bit 7,a             ; if the msb is set we have a frame 
 jp z,Client         ; if not set we do not so we start over

Now for the case of the tutorial we are assuming we have a decent amount of free memory at the pointer in the location 'FreeMem' and the label 'FreeMemP' points to the start of this location

di          ; disable interupts so we don't accidentally get data over written
 ld de,(freemem)     ; point the destination to free ram
 ld bc,5             ; out id is five bytes long
 ld hl,IncomingID    ; we want to copy the ID
 ldir                ; and there it goes
 inc de                ; e was already inc'd to point to the last item in the ID we are saving 
 ld (freemem),de       ; so we inc it again to once again point to free ram and save it
 ld de,OutGoingId      ; load de with the id that sent frames are sent to
 ld hl,incomingID      ; load hl with the servers' ID
 ld bc,5               ; it's five bytes long so we set bc to that
 ldir                  ; copy it over
 ld a,1                ; load a with 1
 ld (OutgoingSize),a ; load that into the size portion of the size word
 set 7,(OutgoingSize+1) ; mark the frame ready to be sent 
 res 7,(IncomingSize+1)  ; acknowledge that the received frame has been recieved
 ei     ; enable interrupts so that CALCnet can do its thing

Now that we have the incoming frame handled and a ACK (acknowledgement) sent back to the server it is time for us to wait for other IDs to be received, and then save them somewhere. In the case of this example the IDs will be saved in the same fashion as above. The only difference is we are saving more than one.

   halt                ; let calcnet do its thing
   bit 7,(Outgoingsize+1) ; if the bit is reset
   jp nz,-_         ; continue else check again until sent
   halt    ; calcnet do its thing
   bit 7,(incomingsize+1)  ; check to see if we have a frame
   jp z,-_       ; if we don't continue check again

So there we make sure we have a new frame. Now we check to see if the frame we have received is from the server:

 ld de,freememp      ; this points to the saved ID
 ld hl,incomingid    ; the id for the frame we just got
 ld b,5             ; they are five bytes long, remember?
 ld      a,(de)     ; Load a with the byte of the saved ID
 cp      (hl)       ; compare it to the received ID          
 jr      nz, wrong  ; if it's not a match quit        
 inc     hl         ; inc both for the next loop
 inc     de
 djnz -_            ; repeat until all the IDs are checked

Now that we know it's the same person we can save the new IDs. The first byte in the received frame is how many IDs are included. This is immediately followed by a length of 5 bytes that are the IDs:

 ld a,(incomingdata)  ; load the amount of entries into a
 ld b,a
 add a,a
 add a,a
 add a,b     ; multiply by five
 ld c,b      
 ld b,0      ; load into c and clear b for ldir
 ld de,(freemem)
 ld hl,Incomingdata+1
 ldir        ; copy the entries over



The Calcnet2.2 whitepaper can be found here and provides more technical detail to interfacing with Calcnet: