@@ -239,24 +239,62 @@ The state of the CPU registers that `SioPortEnd` modifies are preserved by *push
239239If we didn't do this, the interrupted code would likely break when the registers are suddenly modified out of nowhere.
240240
241241
242- ### A little protocol
242+ ## Interval
243243
244- Before diving into (more) implementation, let's take a minute to describe a * protocol* .
245- A protocol is a set of rules that govern communication.
244+ So far we've written a bunch of code that, unfortunately, doesn't do anything on its own.
245+ <sup ><em >It works though, I promise!</em ></sup >
246+ The good news is that Sio -- the code that interfaces directly with the serial port -- is complete.
246247
247- /// Everything is packets.
248- /// Packets: small chunks of data with rudimentary error detection
248+ :::tip 🤖 Take a break!
249249
250- For reliable data transfer, alternate between two message types:
251- protocol metadata in * SYNC* packets, and application data in * DATA* packets.
252- DATA packets are required to include a sequential message ID -- the rest is up to the application.
253- SYNC packets include the ID of the most recently received DATA packet.
250+ Suggested break enrichment activity: CONSUME REFRESHMENT
254251
255- Packet integrity can be tested on the receiving end using a checksum.
256- Damaged packets of either type are discarded.
257- Successful delivery of a DATA packet is acknowledged when its ID is included in a SYNC packet.
258- The sender should retransmit the packet if successful delivery is not acknowledged.
252+ Naturally, yours, &c.\,
259253
254+ A. Hughman
255+
256+ :::
257+
258+
259+ ## Reliable Communication
260+
261+ Sio by itself offers very little in terms of * reliability* .
262+ For our purposes, reliability is all about dealing with errors.
263+ The errors that we're concerned with are data replication errors -- any case where the data transmitted is not replicated correctly in the receiver.
264+
265+ <!-- Link/checksum -->
266+ The first step is detection.
267+ The receiver needs to test the integrity of every incoming data packet, before doing anything else with it.
268+ We'll use a checksum for this:
269+ - The sender calculates a checksum of the outgoing packet and the result is transmitted as part of the packet transfer.
270+ - The receiver preforms the same calculation and compares the result with the value from the sender.
271+ - If the values match, the packet is intact.
272+
273+ <!-- Link/protocol -->
274+ With the packet integrity checksum, the receiving end can detect packet data corruption and discard packets that don't pass the test.
275+ When a packet is not delivered successfully, it should be transmitted again by the sender.
276+ Unfortunately, the sender has no idea if the packet it sent was delivered intact.
277+
278+ To keep the sender in the loop, and manage retransmission, we need a * protocol* -- a set of rules that govern communication.
279+ The protocol follows the principle:
280+ > The sender of a packet will assume the transfer failed, * unless the receiver reports success* .
281+
282+ Let's define two classes of packet:
283+ - ** Application Messages:** critical data that must be delivered, retransmit if delivery failed
284+ - contains application-specific data
285+ - ** Protocol Metadata:** do not retransmit (always send the latest state)
286+ - contains link state information (including last packet received)
287+
288+
289+ :::tip Corruption? In my Game Boy?
290+
291+ Yep, there's any number of possible causes of transfer data replication errors when working with the Game Boy serial port.
292+ Some examples include: old or damaged hardware, luck, cosmic interference, and user actions (hostile and accidental).
293+
294+ :::
295+
296+
297+ <!-- Link/handshake -->
260298There's one more thing our protocol needs: some way to get both devices on the same page and kick things off.
261299We need a * handshake* that must be completed before doing anything else.
262300This is a simple sequence that checks that there is a connection and tests that the connection is working.
@@ -267,58 +305,73 @@ In each exchange, each peer sends a number associated with its role and expects
267305If an unexpected value is received, or something goes wrong with the transfer, that handshake attempt is aborted.
268306
269307
270- ### /// SioPacket
308+ ## SioPacket
271309
272- We'll implement some functions to facilitate constructing, sending, receiving, and checking packets.
273- The packet functions will operate on the existing serial data buffers.
310+ SioPacket is a thin layer over Sio buffer transfers.
311+ - The most important addition is a checksum based integrity test.
312+ - Several convenience routines are also provided.
274313
275- The packets follow a simple structure: starting with a header containing a magic number and the packet checksum, followed by the payload data.
276- The magic number is a constant that marks the start of a packet.
314+ Packets fill a Sio buffer with the following structure:
315+ ``` rgbasm
316+ PacketLayout:
317+ .start_mark: db ; The constant SIO_PACKET_START.
318+ .checksum: db ; Packet checksum, set before transmission.
319+ .data: ds SIO_BUFFER_SIZE - 2 ; Packet data (user defined).
320+ ; Unused space in .data is filled with SIO_PACKET_END.
321+ ```
277322
278323At the top of ` sio.asm ` define some constants:
279324
280325``` rgbasm,linenos,start={{#line_no_of "" ../../unbricked/serial-link/sio.asm:sio-packet-defs}}
281326{{#include ../../unbricked/serial-link/sio.asm:sio-packet-defs}}
282327```
283328
284- /// function to call to start building a new packet :
329+ ` SioPacketTxPrepare ` creates a new empty packet in the Tx buffer :
285330
286331``` rgbasm,linenos,start={{#line_no_of "" ../../unbricked/serial-link/sio.asm:sio-packet-prepare}}
287332{{#include ../../unbricked/serial-link/sio.asm:sio-packet-prepare}}
288333```
289334
290- /// returns packet data pointer in ` hl `
291-
292- /// After calling ` SioPacketTxPrepare ` , the payload data can be added to the packet.
335+ - The checksum is set to zero for the initial checksum calculation.
336+ - The data section is cleared by filling it with the constant ` SIO_PACKET_END ` .
293337
294- Once the desired data has been copied to the packet , the checksum needs to be calculated before the packet can be transferred .
295- We call this * finalising * the packet and this is implemented in ` SioPacketTxFinalise ` :
338+ After calling ` SioPacketTxPrepare ` , the payload data can be written to the packet.
339+ Then, the function ` SioPacketTxFinalise ` should be called :
296340
297341``` rgbasm,linenos,start={{#line_no_of "" ../../unbricked/serial-link/sio.asm:sio-packet-finalise}}
298342{{#include ../../unbricked/serial-link/sio.asm:sio-packet-finalise}}
299343```
300344
301- /// call ` SioPacketChecksum ` to calculate the checksum and write the result to the packet.
345+ - Call ` SioPacketChecksum ` to calculate the packet checksum.
346+ - It's important that the value of the checksum field is zero when performing this initial checksum calculation.
347+ - Write the correct checksum to the packet header.
348+ - Start the transfer.
302349
303- /// a function to perform the checksum test when receiving a packet, ` SioPacketRxCheck ` :
350+
351+ Implement the packet integrity test for received packets:
304352
305353``` rgbasm,linenos,start={{#line_no_of "" ../../unbricked/serial-link/sio.asm:sio-packet-check}}
306354{{#include ../../unbricked/serial-link/sio.asm:sio-packet-check}}
307355```
308356
309- /// Checks that the packet begins with the magic number ` SIO_PACKET_START ` , before checking the checksum.
310- /// For convenience, a pointer to the start of packet data is returned in ` hl ` .
357+ - Check that the packet begins with the magic number ` SIO_PACKET_START ` .
358+ - Calculate the checksum of the received data.
359+ - This includes the packet checksum calculated by the sender.
360+ - The result of this calculation will be zero if the data is the same as it was when sent.
311361
312- /// Finally, implement the checksum:
362+ Finally, implement the checksum:
313363
314364``` rgbasm,linenos,start={{#line_no_of "" ../../unbricked/serial-link/sio.asm:sio-checksum}}
315365{{#include ../../unbricked/serial-link/sio.asm:sio-checksum}}
316366```
317367
368+ - start with the size of the buffer (effectively -1 for each byte summed)
369+ - subtract each byte in the buffer from the sum
370+
318371::: tip
319372
320373The checksum implemented here has been kept very simple for this tutorial.
321- It's probably not very suitable for real-world projects.
374+ It's probably worth looking into better solutions for real-world projects.
322375
323376:::
324377
@@ -327,8 +380,8 @@ It's probably not very suitable for real-world projects.
327380
328381/// Because we have an extra file (sio.asm) to compile now, the build commands will look a little different:
329382``` console
330- $ rgbasm -L - o sio.o sio.asm
331- $ rgbasm -L - o main.o main.asm
383+ $ rgbasm -o sio.o sio.asm
384+ $ rgbasm -o main.o main.asm
332385$ rgblink -o unbricked.gb main.o sio.o
333386$ rgbfix -v -p 0xFF unbricked.gb
334387```
0 commit comments