@@ -239,24 +239,62 @@ The state of the CPU registers that `SioPortEnd` modifies are preserved by *push
239
239
If we didn't do this, the interrupted code would likely break when the registers are suddenly modified out of nowhere.
240
240
241
241
242
- ### A little protocol
242
+ ## Interval
243
243
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.
246
247
247
- /// Everything is packets.
248
- /// Packets: small chunks of data with rudimentary error detection
248
+ :::tip 🤖 Take a break!
249
249
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
254
251
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.\,
259
253
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 -->
260
298
There's one more thing our protocol needs: some way to get both devices on the same page and kick things off.
261
299
We need a * handshake* that must be completed before doing anything else.
262
300
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
267
305
If an unexpected value is received, or something goes wrong with the transfer, that handshake attempt is aborted.
268
306
269
307
270
- ### /// SioPacket
308
+ ## SioPacket
271
309
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.
274
313
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
+ ```
277
322
278
323
At the top of ` sio.asm ` define some constants:
279
324
280
325
``` rgbasm,linenos,start={{#line_no_of "" ../../unbricked/serial-link/sio.asm:sio-packet-defs}}
281
326
{{#include ../../unbricked/serial-link/sio.asm:sio-packet-defs}}
282
327
```
283
328
284
- /// function to call to start building a new packet :
329
+ ` SioPacketTxPrepare ` creates a new empty packet in the Tx buffer :
285
330
286
331
``` rgbasm,linenos,start={{#line_no_of "" ../../unbricked/serial-link/sio.asm:sio-packet-prepare}}
287
332
{{#include ../../unbricked/serial-link/sio.asm:sio-packet-prepare}}
288
333
```
289
334
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 ` .
293
337
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 :
296
340
297
341
``` rgbasm,linenos,start={{#line_no_of "" ../../unbricked/serial-link/sio.asm:sio-packet-finalise}}
298
342
{{#include ../../unbricked/serial-link/sio.asm:sio-packet-finalise}}
299
343
```
300
344
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.
302
349
303
- /// a function to perform the checksum test when receiving a packet, ` SioPacketRxCheck ` :
350
+
351
+ Implement the packet integrity test for received packets:
304
352
305
353
``` rgbasm,linenos,start={{#line_no_of "" ../../unbricked/serial-link/sio.asm:sio-packet-check}}
306
354
{{#include ../../unbricked/serial-link/sio.asm:sio-packet-check}}
307
355
```
308
356
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.
311
361
312
- /// Finally, implement the checksum:
362
+ Finally, implement the checksum:
313
363
314
364
``` rgbasm,linenos,start={{#line_no_of "" ../../unbricked/serial-link/sio.asm:sio-checksum}}
315
365
{{#include ../../unbricked/serial-link/sio.asm:sio-checksum}}
316
366
```
317
367
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
+
318
371
::: tip
319
372
320
373
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.
322
375
323
376
:::
324
377
@@ -327,8 +380,8 @@ It's probably not very suitable for real-world projects.
327
380
328
381
/// Because we have an extra file (sio.asm) to compile now, the build commands will look a little different:
329
382
``` 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
332
385
$ rgblink -o unbricked.gb main.o sio.o
333
386
$ rgbfix -v -p 0xFF unbricked.gb
334
387
```
0 commit comments