Skip to content

Commit ffe2f46

Browse files
committed
Explain SioPortEnd & Serial interrupt handler.
1 parent b3f572d commit ffe2f46

File tree

1 file changed

+10
-47
lines changed

1 file changed

+10
-47
lines changed

src/part2/serial-link.md

Lines changed: 10 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -218,39 +218,11 @@ The last part of the core implementation handles the end of each byte transfer:
218218
{{#include ../../unbricked/serial-link/sio.asm:sio-port-end}}
219219
```
220220

221-
---
222-
223-
**TODO:** walkthrough SioPortEnd
224-
225-
this one is a little bit more involved...
226-
227-
- check that Sio is in the **ACTIVE** state before continuing
228-
- use `ld a, [hl+]` to access `wSioState` and advance `hl` to `wSioCount`
229-
- update `wSioCount` using `dec [hl]`
230-
- which you might not have seen before?
231-
- this works out a bit faster than reading number into `a`, decrementing it, storing it again
232-
233-
- NOTE: at this point we are avoiding using opcodes that set the zero flag as we want to check the result of decrementing `wSioCount` shortly.
234-
235-
- construct a buffer Rx pointer using `wSioBufferOffset`
236-
- load the value from wram into the `l` register
237-
- load the `h` register with the constant high byte of the buffer Rx address space
238-
239-
- grab the received value from `rSB` and copy it to the buffer Rx
240-
- we need to increment the buffer offset ...
241-
- `hl` is incremented here but we know only `l` will be affected because of the buffer alignment
242-
- the updated buffer pointer is stored
243-
244-
- now we check the transfer count remaining
245-
- the `z` flag was updated by the `dec` instruction earlier -- none of the instructions in between modify the flags.
246-
247-
- if the count is more than zero (i.e. more bytes to transfer) start the next byte transfer
248-
- construct a buffer Tx pointer in `hl` by setting `h` to the high byte of the buffer Tx address. keep `l`, which has the updated buffer position.
249-
- load the next tx value into `rSB` and activate the serial port!
250-
251-
- otherwise the count is zero, we just completed the final byte transfer, so set `SIO_DONE` and return.
252-
253-
---
221+
`SioPortEnd` starts by checking that a transfer was started (the `SIO_ACTIVE` state).
222+
We're receiving a byte, so the transfer counter (`wSioCount`) is reduced by one.
223+
The received value is copied from the serial port (`rSB`) to Sio's buffer (`wSioBufferRx`).
224+
If there are still bytes to transfer (transfer counter is greater than zero) the next value is loaded from `wSioBufferTx` and the transfer is started by `SioPortStart`.
225+
Otherwise, if the transfer counter is zero, enter the `SIO_DONE` state.
254226

255227
`SioPortEnd` must be called once after each byte transfer.
256228
To do this we'll use the serial interrupt:
@@ -259,21 +231,12 @@ To do this we'll use the serial interrupt:
259231
{{#include ../../unbricked/serial-link/sio.asm:sio-serial-interrupt-vector}}
260232
```
261233

262-
All this short routine really *does* is `call SioPortEnd`.
263-
But because this is an interrupt handler we have to do a little dance to prevent *bad things* from happening.
264-
We need to preserve the values in the registers that will be modified by `SioPortEnd`, `af` and `hl`.
265-
/// make sure the registers are in the same state as when the interrupt occured so that when the interrupted code is resumed, it can continue as if nothing happened.
266-
*The stack* is the perfect way to do this.
267-
`push` copies the value from the register to the top of the stack and `pop` takes the top value off of the stack and moves it to the register.
268-
Note that a stack is a first-in first-out (FIFO) container, so we push `af` then `hl` -- leaving `hl` on the top -- and pop `hl` then `af`.
234+
This is an interrupt handler -- a special piece of code that gets called by the CPU under certain conditions.
235+
The serial interrupt occurs when the serial port completes a transfer.
269236

270-
:::tip We interrupt this broadcast to briefly explain what interrupts are
271-
272-
An interrupt is a way to run a certain piece of code when an external event occurs.
273-
Interrupts are requested by *peripherals* (hardware connected to the CPU) and the CPU literally interrupts whatever it was doing to go an execute some different code instead.
274-
In this case, the event is the serial port counting to eight (meaning a whole byte was transferred), and the code that will be executed is whatever is at memory address `$58` -- the routine above.
275-
276-
:::
237+
All this code does is invoke `SioPortEnd` safely.
238+
The state of the CPU registers that `SioPortEnd` modifies are preserved by *pushing* them to the stack before the `call`, then restored by *popping* them off the stack afterwards.
239+
If we didn't do this, the interrupted code would likely break when the registers are suddenly modified out of nowhere.
277240

278241

279242
### A little protocol

0 commit comments

Comments
 (0)