]> git.sur5r.net Git - cc65/blob - libsrc/lynx/ser/lynx-comlynx.s
Create static drivers directly from source files.
[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   INSTALL
29         .addr   UNINSTALL
30         .addr   OPEN
31         .addr   CLOSE
32         .addr   GET
33         .addr   PUT
34         .addr   STATUS
35         .addr   IOCTL
36         .addr   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 ; INSTALL: Is called after the driver is loaded into memory.
58 ;
59 ; Must return an SER_ERR_xx code in a/x.
60
61 INSTALL:
62         ; Set up IRQ vector ?
63
64 ;----------------------------------------------------------------------------
65 ; 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 UNINSTALL:
70
71 ;----------------------------------------------------------------------------
72 ; CLOSE: Close the port and disable interrupts. Called without parameters.
73 ; Must return an SER_ERR_xx code in a/x.
74
75 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 ; 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 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 ; 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 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 ; PUT: Output character in A.
272 ; Must return an SER_ERR_xx code in a/x.
273
274 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 ; STATUS: Return the status in the variable pointed to by ptr1.
305 ; Must return an SER_ERR_xx code in a/x.
306
307 STATUS:
308         ldy     SerialStat
309         ldx     #$00
310         sta     (ptr1,x)
311         txa                     ; Return code = 0
312         rts
313
314 ;----------------------------------------------------------------------------
315 ; 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 IOCTL:
320         lda     #<SER_ERR_INV_IOCTL
321         ldx     #>SER_ERR_INV_IOCTL
322         rts
323
324 ;----------------------------------------------------------------------------
325 ; 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 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
414