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