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