Skip to content

Commit 84b17eb

Browse files
committed
Write Reliable Communication, protocol description, SioPacket
- fix heading depth: Reliable Communication, SioPacket same as Sio
1 parent ffe2f46 commit 84b17eb

File tree

1 file changed

+85
-32
lines changed

1 file changed

+85
-32
lines changed

src/part2/serial-link.md

Lines changed: 85 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -239,24 +239,62 @@ The state of the CPU registers that `SioPortEnd` modifies are preserved by *push
239239
If 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 -->
260298
There's one more thing our protocol needs: some way to get both devices on the same page and kick things off.
261299
We need a *handshake* that must be completed before doing anything else.
262300
This 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
267305
If 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

278323
At 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

320373
The 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

Comments
 (0)