]> git.sur5r.net Git - cc65/blob - libsrc/lynx/ser/lynx-comlynx.s
Added SER_ prefix. Whitespace cleanup
[cc65] / libsrc / lynx / ser / lynx-comlynx.s
1 ;
2 ; Serial driver for the Atari Lynx ComLynx port.
3 ;
4 ; Karri Kaksonen, 17.09.2009
5 ;
6
7         .include        "lynx.inc"
8         .include        "zeropage.inc"
9         .include        "ser-kernel.inc"
10         .include        "ser-error.inc"
11
12         .macpack        module
13
14
15 ; ------------------------------------------------------------------------
16 ; Header. Includes jump table
17
18         module_header   _lynx_comlynx_ser
19
20         ; Driver signature
21         .byte   $73, $65, $72           ; "ser"
22         .byte   SER_API_VERSION         ; Serial API version number
23
24         ; Library reference
25         .addr   $0000
26
27         ; Jump table
28         .addr   SER_INSTALL
29         .addr   SER_UNINSTALL
30         .addr   SER_OPEN
31         .addr   SER_CLOSE
32         .addr   SER_GET
33         .addr   SER_PUT
34         .addr   SER_STATUS
35         .addr   SER_IOCTL
36         .addr   SER_IRQ
37
38 ;----------------------------------------------------------------------------
39 ; Global variables
40 ;
41
42         .bss
43
44 TxBuffer:       .res    256
45 RxBuffer:       .res    256
46 RxPtrIn:        .res    1
47 RxPtrOut:       .res    1
48 TxPtrIn:        .res    1
49 TxPtrOut:       .res    1
50 contrl:         .res    1
51 SerialStat:     .res    1
52 TxDone:         .res    1
53
54         .code
55
56 ;----------------------------------------------------------------------------
57 ; SER_INSTALL: Is called after the driver is loaded into memory.
58 ;
59 ; Must return an SER_ERR_xx code in a/x.
60
61 SER_INSTALL:
62         ; Set up IRQ vector ?
63
64 ;----------------------------------------------------------------------------
65 ; SER_UNINSTALL: Is called before the driver is removed from memory.
66 ; No return code required (the driver is removed from memory on return).
67 ;
68
69 SER_UNINSTALL:
70
71 ;----------------------------------------------------------------------------
72 ; SER_CLOSE: Close the port and disable interrupts. Called without parameters.
73 ; Must return an SER_ERR_xx code in a/x.
74
75 SER_CLOSE:
76         ; Disable interrupts
77         ; Done, return an error code
78         lda     #<SER_ERR_OK
79         ldx     #>SER_ERR_OK
80         rts
81
82 ;----------------------------------------------------------------------------
83 ; SER_OPEN: A pointer to a ser_params structure is passed in ptr1.
84 ;
85 ; The Lynx has only two correct serial data formats:
86 ; 8 bits, parity mark, 1 stop bit
87 ; 8 bits, parity space, 1 stop bit
88 ;
89 ; It also has two wrong formats;
90 ; 8 bits, even parity, 1 stop bit
91 ; 8 bits, odd parity, 1 stop bit
92 ;
93 ; Unfortunately the parity bit includes itself in the calculation making
94 ; parity not compatible with the rest of the world.
95 ;
96 ; We can only specify a few baud rates.
97 ; Lynx has two non-standard speeds 31250 and 62500 which are
98 ; frequently used in games.
99 ;
100 ; The receiver will always read the parity and report parity errors.
101 ;
102 ; Must return an SER_ERR_xx code in a/x.
103
104 SER_OPEN:
105         stz     RxPtrIn
106         stz     RxPtrOut
107         stz     TxPtrIn
108         stz     TxPtrOut
109
110         ; clock = 8 * 15625
111         lda     #%00011000
112         sta     TIM4CTLA
113         ldy     #SER_PARAMS::BAUDRATE
114         lda     (ptr1),y
115
116         ldx     #1
117         cmp     #SER_BAUD_62500
118         beq     setbaudrate
119
120         ldx     #2
121         cmp     #SER_BAUD_31250
122         beq     setbaudrate
123
124         ldx     #12
125         cmp     #SER_BAUD_9600
126         beq     setbaudrate
127
128         ldx     #25
129         cmp     #SER_BAUD_4800
130         beq     setbaudrate
131
132         ldx     #51
133         cmp     #SER_BAUD_2400
134         beq     setbaudrate
135
136         ldx     #103
137         cmp     #SER_BAUD_1200
138         beq     setbaudrate
139
140         ldx     #207
141         cmp     #SER_BAUD_600
142         beq     setbaudrate
143
144         ; clock = 6 * 15625
145         ldx     #%00011010
146         stx     TIM4CTLA
147
148         ldx     #12
149         cmp     #SER_BAUD_7200
150         beq     setbaudrate
151
152         ldx     #25
153         cmp     #SER_BAUD_3600
154         beq     setbaudrate
155
156         ldx     #207
157         stx     TIM4BKUP
158
159         ; clock = 4 * 15625
160         ldx     #%00011100
161         cmp     #SER_BAUD_300
162         beq     setprescaler
163
164         ; clock = 6 * 15625
165         ldx     #%00011110
166         cmp     #SER_BAUD_150
167         beq     setprescaler
168
169         ; clock = 1 * 15625
170         ldx     #%00011111
171         stx     TIM4CTLA
172         cmp     #SER_BAUD_75
173         beq     baudsuccess
174
175         ldx     #141
176         cmp     #SER_BAUD_110
177         beq     setbaudrate
178
179         ; clock = 2 * 15625
180         ldx     #%00011010
181         stx     TIM4CTLA
182         ldx     #68
183         cmp     #SER_BAUD_1800
184         beq     setbaudrate
185
186         ; clock = 6 * 15625
187         ldx     #%00011110
188         stx     TIM4CTLA
189         ldx     #231
190         cmp     #SER_BAUD_134_5
191         beq     setbaudrate
192
193         lda     #<SER_ERR_BAUD_UNAVAIL
194         ldx     #>SER_ERR_BAUD_UNAVAIL
195         rts
196 setprescaler:
197         stx     TIM4CTLA
198         bra     baudsuccess
199 setbaudrate:
200         stx     TIM4BKUP
201 baudsuccess:
202         ldx     #TxOpenColl|ParEven
203         stx     contrl
204         ldy     #SER_PARAMS::DATABITS   ; Databits
205         lda     (ptr1),y
206         cmp     #SER_BITS_8
207         bne     invparameter
208         ldy     #SER_PARAMS::STOPBITS   ; Stopbits
209         lda     (ptr1),y
210         cmp     #SER_STOP_1
211         bne     invparameter
212         ldy     #SER_PARAMS::PARITY     ; Parity
213         lda     (ptr1),y
214         cmp     #SER_PAR_NONE
215         beq     invparameter
216         cmp     #SER_PAR_MARK
217         beq     checkhs
218         cmp     #SER_PAR_SPACE
219         bne     @L0
220         ldx     #TxOpenColl
221         stx     contrl
222         bra     checkhs
223 @L0:
224         ldx     #TxParEnable|TxOpenColl|ParEven
225         stx     contrl
226         cmp     #SER_PAR_EVEN
227         beq     checkhs
228         ldx     #TxParEnable|TxOpenColl
229         stx     contrl
230 checkhs:
231         ldx     contrl
232         stx     SERCTL
233         ldy     #SER_PARAMS::HANDSHAKE  ; Handshake
234         lda     (ptr1),y
235         cmp     #SER_HS_NONE
236         bne     invparameter
237         lda     SERDAT
238         lda     contrl
239         ora     #RxIntEnable|ResetErr
240         sta     SERCTL
241         lda     #<SER_ERR_OK
242         ldx     #>SER_ERR_OK
243         rts
244 invparameter:
245         lda     #<SER_ERR_INIT_FAILED
246         ldx     #>SER_ERR_INIT_FAILED
247         rts
248
249 ;----------------------------------------------------------------------------
250 ; SER_GET: Will fetch a character from the receive buffer and store it into the
251 ; variable pointed to by ptr1. If no data is available, SER_ERR_NO_DATA is
252 ; returned.
253
254 SER_GET:
255         lda     RxPtrIn
256         cmp     RxPtrOut
257         bne     GetByte
258         lda     #<SER_ERR_NO_DATA
259         ldx     #>SER_ERR_NO_DATA
260         rts
261 GetByte:
262         ldy     RxPtrOut
263         lda     RxBuffer,y
264         inc     RxPtrOut
265         ldx     #$00
266         sta     (ptr1,x)
267         txa                     ; Return code = 0
268         rts
269
270 ;----------------------------------------------------------------------------
271 ; SER_PUT: Output character in A.
272 ; Must return an SER_ERR_xx code in a/x.
273
274 SER_PUT:
275         tax
276         lda     TxPtrIn
277         ina
278         cmp     TxPtrOut
279         bne     PutByte
280         lda     #<SER_ERR_OVERFLOW
281         ldx     #>SER_ERR_OVERFLOW
282         rts
283 PutByte:
284         ldy     TxPtrIn
285         txa
286         sta     TxBuffer,y
287         inc     TxPtrIn
288
289         bit     TxDone
290         bmi     @L1
291         php
292         sei
293         lda     contrl
294         ora     #TxIntEnable|ResetErr
295         sta     SERCTL       ; Allow TX-IRQ to hang RX-IRQ
296         sta     TxDone
297         plp
298 @L1:
299         lda     #<SER_ERR_OK
300         tax
301         rts
302
303 ;----------------------------------------------------------------------------
304 ; SER_STATUS: Return the status in the variable pointed to by ptr1.
305 ; Must return an SER_ERR_xx code in a/x.
306
307 SER_STATUS:
308         ldy     SerialStat
309         ldx     #$00
310         sta     (ptr1,x)
311         txa                     ; Return code = 0
312         rts
313
314 ;----------------------------------------------------------------------------
315 ; SER_IOCTL: Driver defined entry point. The wrapper will pass a pointer to ioctl
316 ; specific data in ptr1, and the ioctl code in A.
317 ; Must return an SER_ERR_xx code in a/x.
318
319 SER_IOCTL:
320         lda     #<SER_ERR_INV_IOCTL
321         ldx     #>SER_ERR_INV_IOCTL
322         rts
323
324 ;----------------------------------------------------------------------------
325 ; SER_IRQ: Called from the builtin runtime IRQ handler as a subroutine. All
326 ; registers are already saved, no parameters are passed, but the carry flag
327 ; is clear on entry. The routine must return with carry set if the interrupt
328 ; was handled, otherwise with carry clear.
329 ;
330 ; Both the Tx and Rx interrupts are level sensitive instead of edge sensitive.
331 ; Due to this bug you have to disable the interrupt before clearing it.
332
333 SER_IRQ:
334         lda     INTSET          ; Poll all pending interrupts
335         and     #SERIAL_INTERRUPT
336         bne     @L0
337         clc
338         rts
339 @L0:
340         bit     TxDone
341         bmi     @tx_irq     ; Transmit in progress
342         ldx     SERDAT
343         lda     SERCTL
344         and     #RxParityErr|RxOverrun|RxFrameErr|RxBreak
345         beq     @rx_irq
346         tsb     SerialStat  ; Save error condition
347         bit     #RxBreak
348         beq     @noBreak
349         stz     TxPtrIn     ; Break received - drop buffers
350         stz     TxPtrOut
351         stz     RxPtrIn
352         stz     RxPtrOut
353 @noBreak:
354         lda     contrl
355         ora     #RxIntEnable|ResetErr
356         sta     SERCTL
357         lda     #$10
358         sta     INTRST
359         bra     @IRQexit
360 @rx_irq:
361         lda     contrl
362         ora     #RxIntEnable|ResetErr
363         sta     SERCTL
364         txa
365         ldx     RxPtrIn
366         sta     RxBuffer,x
367         txa
368         inx
369
370 @cont0:
371         cpx     RxPtrOut
372         beq     @1
373         stx     RxPtrIn
374         lda     #SERIAL_INTERRUPT
375         sta     INTRST
376         bra     @IRQexit
377
378 @1:
379         sta     RxPtrIn
380         lda     #$80
381         tsb     SerialStat
382 @tx_irq:
383         ldx     TxPtrOut    ; Has all bytes been sent?
384         cpx     TxPtrIn
385         beq     @allSent
386
387         lda     TxBuffer,x  ; Send next byte
388         sta     SERDAT
389         inc     TxPtrOut
390
391 @exit1:
392         lda     contrl
393         ora     #TxIntEnable|ResetErr
394         sta     SERCTL
395         lda     #SERIAL_INTERRUPT
396         sta     INTRST
397         bra     @IRQexit
398
399 @allSent:
400         lda     SERCTL       ; All bytes sent
401         bit     #TxEmpty
402         beq     @exit1
403         bvs     @exit1
404         stz     TxDone
405         lda     contrl
406         ora     #RxIntEnable|ResetErr
407         sta     SERCTL
408
409         lda     #SERIAL_INTERRUPT
410         sta     INTRST
411 @IRQexit:
412         clc
413         rts