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.
Introduction #
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..
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 Equates
Synopsis #
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.
InitPosLoop: 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:
Client: halt 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
Whitepaper #
The Calcnet2.2 whitepaper can be found here and provides more technical detail to interfacing with Calcnet: https://www.ticalc.org/archives/files/fileinfo/433/43304.html