Skip to content

Commit add7190

Browse files
committed
Add core serial IO implementation
1 parent 34b165c commit add7190

File tree

1 file changed

+231
-0
lines changed

1 file changed

+231
-0
lines changed

unbricked/serial-link/sio.asm

+231
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
INCLUDE "hardware.inc"
2+
3+
; ::::::::::::::::::::::::::::::::::::::
4+
; :: ::
5+
; :: ______. ::
6+
; :: _ |````` || ::
7+
; :: _/ \__@_ |[- - ]|| ::
8+
; :: / `--<[|]= |[ m ]|| ::
9+
; :: \ .______ | ```` || ::
10+
; :: / !| `````| | + oo|| ::
11+
; :: ( ||[ ^u^]| | .. #|| ::
12+
; :: `-<[|]=|[ ]| `______// ::
13+
; :: || ```` | ::
14+
; :: || + oo| ::
15+
; :: || .. #| ::
16+
; :: !|______/ ::
17+
; :: ::
18+
; :: ::
19+
; ::::::::::::::::::::::::::::::::::::::
20+
;
21+
; This concludes the physical portion of the tutorial.
22+
; (I'm sure we can all agree that this was worth whatever it cost.)
23+
24+
25+
; Duration of timeout period in ticks. (for externally clocked device)
26+
DEF SIO_TIMEOUT_TICKS EQU 240
27+
; Duration of 'catchup' delay period in ticks. (for internally clocked device)
28+
DEF SIO_CATCHUP_TICKS EQU 1
29+
30+
DEF SIO_CONFIG_INTCLK EQU SCF_SOURCE
31+
DEF SIO_CONFIG_DEFAULT EQU 0
32+
EXPORT SIO_CONFIG_INTCLK
33+
34+
; SioStatus transfer state enum
35+
RSRESET
36+
DEF SIO_IDLE RB 1
37+
DEF SIO_XFER_START RB 1
38+
DEF SIO_XFER_STARTED RB 1
39+
DEF SIO_XFER_COMPLETED RB 1
40+
DEF SIO_XFER_FAILED RB 1
41+
EXPORT SIO_IDLE, SIO_XFER_START, SIO_XFER_STARTED, SIO_XFER_COMPLETED, SIO_XFER_FAILED
42+
43+
44+
SECTION "SioCore State", WRAM0
45+
; Sio config flags
46+
wSioConfig:: db
47+
; Sio state machine current state
48+
wSioState:: db
49+
; Source address of next value to transmit
50+
wSioTxPtr:: dw
51+
; Destination address of next received value
52+
wSioRxPtr:: dw
53+
; Number of transfers to perform (bytes to transfer)
54+
wSioCount:: db
55+
56+
; Timer state (as ticks remaining, expires at zero) for timeouts and delays.
57+
wTimer: db
58+
59+
60+
SECTION "SioCore Impl", ROM0
61+
62+
SioInit::
63+
ld a, SIO_CONFIG_DEFAULT
64+
ld [wSioConfig], a
65+
ld a, SIO_IDLE
66+
ld [wSioState], a
67+
xor a
68+
ld [wTimer], a
69+
ld a, $FF ; using FFFF as 'null'/unset
70+
ld [wSioTxPtr + 0], a
71+
ld [wSioTxPtr + 1], a
72+
ld [wSioRxPtr + 0], a
73+
ld [wSioRxPtr + 1], a
74+
xor a
75+
ld [wSioCount], a
76+
77+
; enable serial interrupt
78+
ldh a, [rIE]
79+
or IEF_SERIAL
80+
ldh [rIE], a
81+
ret
82+
83+
84+
SioTick::
85+
ld a, [wSioState]
86+
cp SIO_XFER_START
87+
jr z, SioProcessQueue
88+
cp SIO_XFER_STARTED
89+
jr z, .xfer_started
90+
cp SIO_XFER_COMPLETED
91+
jr z, SioProcessQueue
92+
; treat anything else as failed/error and do nothing
93+
ret
94+
.xfer_started:
95+
; update timeout on external clock
96+
ldh a, [rSC]
97+
and SCF_SOURCE
98+
ret nz
99+
ld a, [wTimer]
100+
and a
101+
ret z ; timer == 0, timeout disabled
102+
dec a
103+
ld [wTimer], a
104+
jr z, SioAbortTransfer
105+
ret
106+
107+
108+
SioProcessQueue:
109+
; if SioCount > 0: start next transfer
110+
ld a, [wSioCount]
111+
and a
112+
ret z
113+
114+
; FALLTHROUGH
115+
116+
SioStartNextTransfer:
117+
; delay on internal clock device
118+
ldh a, [rSC]
119+
bit SCB_SOURCE, a
120+
jr z, .no_delay
121+
ld a, [wTimer]
122+
and a
123+
jr z, .no_delay
124+
dec a
125+
ld [wTimer], a
126+
ret
127+
.no_delay
128+
129+
; get next value and start transfer
130+
ld hl, wSioTxPtr
131+
ld a, [hl+]
132+
ld h, [hl]
133+
ld l, a
134+
ld b, [hl]
135+
call SioStartTransfer
136+
137+
ret
138+
139+
140+
; Start a serial port transfer immediately.
141+
; @param B: data byte to send
142+
; @mut: AF
143+
SioStartTransfer::
144+
; set the clock source (do this first & separately from starting the transfer!)
145+
ld a, [wSioConfig]
146+
and SCF_SOURCE ; the sio config byte uses the same bit for the clock source
147+
ldh [rSC], a
148+
; load the value to send
149+
ld a, b
150+
ldh [rSB], a
151+
; start the transfer
152+
ldh a, [rSC]
153+
or SCF_START
154+
ldh [rSC], a
155+
156+
; reset timeout (on externally clocked device)
157+
bit SCB_SOURCE, a
158+
jr nz, :+
159+
ld a, SIO_TIMEOUT_TICKS
160+
ld [wTimer], a
161+
:
162+
ld a, SIO_XFER_STARTED
163+
ld [wSioState], a
164+
ret
165+
166+
167+
SioAbortTransfer::
168+
ld a, SIO_XFER_FAILED
169+
ld [wSioState], a
170+
ldh a, [rSC]
171+
res SCB_START, a
172+
ldh [rSC], a
173+
ret
174+
175+
176+
SioSerialInterruptHandler:
177+
push af
178+
push hl
179+
180+
; check that we were expecting a transfer
181+
ld a, [wSioState]
182+
cp SIO_XFER_STARTED
183+
ret nz
184+
185+
; store the received value
186+
ld hl, wSioRxPtr
187+
ld a, [hl+]
188+
ld h, [hl]
189+
ld l, a
190+
ldh a, [rSB]
191+
ld [hl+], a
192+
; store the updated Rx pointer
193+
ld a, l
194+
ld [wSioRxPtr + 0], a
195+
ld a, h
196+
ld [wSioRxPtr + 1], a
197+
198+
; update the Tx pointer
199+
ld hl, wSioTxPtr
200+
inc [hl] ; inc low byte
201+
jr nz, :+
202+
; inc high byte if overflow
203+
inc hl
204+
inc [hl]
205+
:
206+
207+
; update transfer count
208+
ld hl, wSioCount
209+
dec [hl]
210+
211+
; set transfer state to 'completed'
212+
ld a, SIO_XFER_COMPLETED
213+
ld [wSioState], a
214+
215+
ldh a, [rSC]
216+
and SCF_SOURCE
217+
jr z, :+
218+
; reset delay timer on internal clock
219+
ld a, SIO_CATCHUP_TICKS
220+
ld [wTimer], a
221+
:
222+
223+
pop hl
224+
pop af
225+
reti
226+
227+
228+
section "Serial Interrupt", rom0[$58]
229+
SerialInterrupt:
230+
jp SioSerialInterruptHandler
231+

0 commit comments

Comments
 (0)