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