]> git.sur5r.net Git - cc65/blob - libsrc/atmos/ser/atmos-acia.s
Added library reference ser_libref to SER interface.
[cc65] / libsrc / atmos / ser / atmos-acia.s
1 ;
2 ; Serial driver for the Telestrat integrated serial controller and the
3 ; Atmos with a serial add-on.
4 ;
5 ; Stefan Haubenthal, 2012-03-05
6 ;
7 ; The driver is based on the cc65 rs232 module, which in turn is based on
8 ; Craig Bruce device driver for the Switftlink/Turbo-232.
9 ;
10 ; SwiftLink/Turbo-232 v0.90 device driver, by Craig Bruce, 14-Apr-1998.
11 ;
12 ; This software is Public Domain.  It is in Buddy assembler format.
13 ;
14 ; This device driver uses the SwiftLink RS-232 Serial Cartridge, available from
15 ; Creative Micro Designs, Inc, and also supports the extensions of the Turbo232
16 ; Serial Cartridge.  Both devices are based on the 6551 ACIA chip.  It also
17 ; supports the "hacked" SwiftLink with a 1.8432 MHz crystal.
18 ;
19 ; The code assumes that the kernal + I/O are in context.  On the C128, call
20 ; it from Bank 15.  On the C64, don't flip out the Kernal unless a suitable
21 ; NMI catcher is put into the RAM under then Kernal.  For the SuperCPU, the
22 ; interrupt handling assumes that the 65816 is in 6502-emulation mode.
23 ;
24
25         .include        "zeropage.inc"
26         .include        "ser-kernel.inc"
27         .include        "ser-error.inc"
28
29 ; ------------------------------------------------------------------------
30 ; Header. Includes jump table
31
32         .segment        "JUMPTABLE"
33
34         ; Driver signature
35         .byte   $73, $65, $72           ; "ser"
36         .byte   SER_API_VERSION         ; Serial API version number
37
38         ; Library reference
39         .addr   $0000
40
41         ; Jump table
42         .addr   INSTALL
43         .addr   UNINSTALL
44         .addr   OPEN
45         .addr   CLOSE
46         .addr   GET
47         .addr   PUT
48         .addr   STATUS
49         .addr   IOCTL
50         .addr   IRQ
51
52 ;----------------------------------------------------------------------------
53 ; I/O definitions
54
55 ACIA            = $031C
56 ACIA_DATA       = ACIA+0        ; Data register
57 ACIA_STATUS     = ACIA+1        ; Status register
58 ACIA_CMD        = ACIA+2        ; Command register
59 ACIA_CTRL       = ACIA+3        ; Control register
60
61 ;----------------------------------------------------------------------------
62 ; Global variables
63
64         .bss
65
66 RecvHead:       .res    1       ; Head of receive buffer
67 RecvTail:       .res    1       ; Tail of receive buffer
68 RecvFreeCnt:    .res    1       ; Number of bytes in receive buffer
69 SendHead:       .res    1       ; Head of send buffer
70 SendTail:       .res    1       ; Tail of send buffer
71 SendFreeCnt:    .res    1       ; Number of bytes in send buffer
72
73 Stopped:        .res    1       ; Flow-stopped flag
74 RtsOff:         .res    1       ;
75
76 RecvBuf:        .res    256     ; Receive buffers: 256 bytes
77 SendBuf:        .res    256     ; Send buffers: 256 bytes
78
79 Index:          .res    1       ; I/O register index
80
81         .rodata
82
83         ; Tables used to translate RS232 params into register values
84 BaudTable:                      ; bit7 = 1 means setting is invalid
85         .byte   $FF             ; SER_BAUD_45_5
86         .byte   $01             ; SER_BAUD_50
87         .byte   $02             ; SER_BAUD_75
88         .byte   $03             ; SER_BAUD_110
89         .byte   $04             ; SER_BAUD_134_5
90         .byte   $05             ; SER_BAUD_150
91         .byte   $06             ; SER_BAUD_300
92         .byte   $07             ; SER_BAUD_600
93         .byte   $08             ; SER_BAUD_1200
94         .byte   $09             ; SER_BAUD_1800
95         .byte   $0A             ; SER_BAUD_2400
96         .byte   $0B             ; SER_BAUD_3600
97         .byte   $0C             ; SER_BAUD_4800
98         .byte   $0D             ; SER_BAUD_7200
99         .byte   $0E             ; SER_BAUD_9600
100         .byte   $0F             ; SER_BAUD_19200
101         .byte   $FF             ; SER_BAUD_38400
102         .byte   $FF             ; SER_BAUD_57600
103         .byte   $FF             ; SER_BAUD_115200
104         .byte   $FF             ; SER_BAUD_230400
105 BitTable:
106         .byte   $60             ; SER_BITS_5
107         .byte   $40             ; SER_BITS_6
108         .byte   $20             ; SER_BITS_7
109         .byte   $00             ; SER_BITS_8
110 StopTable:
111         .byte   $00             ; SER_STOP_1
112         .byte   $80             ; SER_STOP_2
113 ParityTable:
114         .byte   $00             ; SER_PAR_NONE
115         .byte   $20             ; SER_PAR_ODD
116         .byte   $60             ; SER_PAR_EVEN
117         .byte   $A0             ; SER_PAR_MARK
118         .byte   $E0             ; SER_PAR_SPACE
119
120         .code
121
122 ;----------------------------------------------------------------------------
123 ; INSTALL: Is called after the driver is loaded into memory. If possible,
124 ; check if the hardware is present. Must return an SER_ERR_xx code in a/x.
125 ;
126 ; Since we don't have to manage the IRQ vector on the Telestrat/Atmos, this is
127 ; actually the same as:
128 ;
129 ; UNINSTALL: Is called before the driver is removed from memory.
130 ; No return code required (the driver is removed from memory on return).
131 ;
132 ; and:
133 ;
134 ; CLOSE: Close the port and disable interrupts. Called without parameters.
135 ; Must return an SER_ERR_xx code in a/x.
136
137 INSTALL:
138 UNINSTALL:
139 CLOSE:
140         ldx     Index           ; Check for open port
141         beq     :+
142
143         ; Deactivate DTR and disable 6551 interrupts
144         lda     #%00001010
145         sta     ACIA_CMD,x
146
147         ; Done, return an error code
148 :       lda     #<SER_ERR_OK
149         tax                     ; A is zero
150         stx     Index           ; Mark port as closed
151         rts
152
153 ;----------------------------------------------------------------------------
154 ; OPEN: A pointer to a ser_params structure is passed in ptr1.
155 ; Must return an SER_ERR_xx code in a/x.
156
157 OPEN:
158         ; Check if the handshake setting is valid
159         ldy     #SER_PARAMS::HANDSHAKE  ; Handshake
160         lda     (ptr1),y
161         cmp     #SER_HS_HW              ; This is all we support
162         bne     InvParam
163
164         ; Initialize buffers
165         ldy     #$00
166         sty     Stopped
167         sty     RecvHead
168         sty     RecvTail
169         sty     SendHead
170         sty     SendTail
171         dey                             ; Y = 255
172         sty     RecvFreeCnt
173         sty     SendFreeCnt
174
175         ; Set the value for the control register, which contains stop bits,
176         ; word length and the baud rate.
177         ldy     #SER_PARAMS::BAUDRATE
178         lda     (ptr1),y                ; Baudrate index
179         tay
180         lda     BaudTable,y             ; Get 6551 value
181         bmi     InvBaud                 ; Branch if rate not supported
182         sta     tmp1
183
184         ldy     #SER_PARAMS::DATABITS   ; Databits
185         lda     (ptr1),y
186         tay
187         lda     BitTable,y
188         ora     tmp1
189         sta     tmp1
190
191         ldy     #SER_PARAMS::STOPBITS   ; Stopbits
192         lda     (ptr1),y
193         tay
194         lda     StopTable,y
195         ora     tmp1
196         ora     #%00010000              ; Receiver clock source = baudrate
197         sta     ACIA_CTRL
198
199         ; Set the value for the command register. We remember the base value
200         ; in RtsOff, since we will have to manipulate ACIA_CMD often.
201         ldy     #SER_PARAMS::PARITY     ; Parity
202         lda     (ptr1),y
203         tay
204         lda     ParityTable,y
205         ora     #%00000001              ; DTR active
206         sta     RtsOff
207         ora     #%00001000              ; Enable receive interrupts
208         sta     ACIA_CMD
209
210         ; Done
211         stx     Index                   ; Mark port as open
212         lda     #<SER_ERR_OK
213         tax                             ; A is zero
214         rts
215
216         ; Invalid parameter
217 InvParam:lda    #<SER_ERR_INIT_FAILED
218         ldx     #>SER_ERR_INIT_FAILED
219         rts
220
221         ; Baud rate not available
222 InvBaud:lda     #<SER_ERR_BAUD_UNAVAIL
223         ldx     #>SER_ERR_BAUD_UNAVAIL
224         rts
225
226 ;----------------------------------------------------------------------------
227 ; GET: Will fetch a character from the receive buffer and store it into the
228 ; variable pointed to by ptr1. If no data is available, SER_ERR_NO_DATA is
229 ; returned.
230
231 GET:
232         ldy     SendFreeCnt     ; Send data if necessary
233         iny                     ; Y == $FF?
234         beq     :+
235         lda     #$00            ; TryHard = false
236         jsr     TryToSend
237
238         ; Check for buffer empty
239 :       lda     RecvFreeCnt     ; (25)
240         cmp     #$FF
241         bne     :+
242         lda     #<SER_ERR_NO_DATA
243         ldx     #>SER_ERR_NO_DATA
244         rts
245
246         ; Check for flow stopped & enough free: release flow control
247 :       ldy     Stopped         ; (34)
248         beq     :+
249         cmp     #63
250         bcc     :+
251         lda     #$00
252         sta     Stopped
253         lda     RtsOff
254         ora     #%00001000
255         sta     ACIA_CMD
256
257         ; Get byte from buffer
258 :       ldy     RecvHead        ; (41)
259         lda     RecvBuf,y
260         inc     RecvHead
261         inc     RecvFreeCnt
262         ldx     #$00            ; (59)
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         ; Try to send
273         ldy     SendFreeCnt
274         iny                     ; Y = $FF?
275         beq     :+
276         pha
277         lda     #$00            ; TryHard = false
278         jsr     TryToSend
279         pla
280
281         ; Put byte into send buffer & send
282 :       ldy     SendFreeCnt
283         bne     :+
284         lda     #<SER_ERR_OVERFLOW
285         ldx     #>SER_ERR_OVERFLOW
286         rts
287
288 :       ldy     SendTail
289         sta     SendBuf,y
290         inc     SendTail
291         dec     SendFreeCnt
292         lda     #$FF            ; TryHard = true
293         jsr     TryToSend
294         lda     #<SER_ERR_OK
295         tax
296         rts
297
298 ;----------------------------------------------------------------------------
299 ; STATUS: Return the status in the variable pointed to by ptr1.
300 ; Must return an SER_ERR_xx code in a/x.
301
302 STATUS:
303         lda     ACIA_STATUS
304         ldx     #$00
305         sta     (ptr1,x)
306         txa                     ; SER_ERR_OK
307         rts
308
309 ;----------------------------------------------------------------------------
310 ; IOCTL: Driver defined entry point. The wrapper will pass a pointer to ioctl
311 ; specific data in ptr1, and the ioctl code in A.
312 ; Must return an SER_ERR_xx code in a/x.
313
314 IOCTL:
315         lda     #<SER_ERR_INV_IOCTL
316         ldx     #>SER_ERR_INV_IOCTL
317         rts
318
319 ;----------------------------------------------------------------------------
320 ; IRQ: Called from the builtin runtime IRQ handler as a subroutine. All
321 ; registers are already saved, no parameters are passed, but the carry flag
322 ; is clear on entry. The routine must return with carry set if the interrupt
323 ; was handled, otherwise with carry clear.
324
325 IRQ:
326         ldx     Index           ; Check for open port
327         beq     Done
328         lda     ACIA_STATUS,x   ; Check ACIA status for receive interrupt
329         and     #$08
330         beq     Done            ; Jump if no ACIA interrupt
331         lda     ACIA_DATA,x     ; Get byte from ACIA
332         ldy     RecvFreeCnt     ; Check if we have free space left
333         beq     Flow            ; Jump if no space in receive buffer
334         ldy     RecvTail        ; Load buffer pointer
335         sta     RecvBuf,y       ; Store received byte in buffer
336         inc     RecvTail        ; Increment buffer pointer
337         dec     RecvFreeCnt     ; Decrement free space counter
338         ldy     RecvFreeCnt     ; Check for buffer space low
339         cpy     #33
340         bcc     Flow            ; Assert flow control if buffer space low
341         rts                     ; Interrupt handled (carry already set)
342
343         ; Assert flow control if buffer space too low
344 Flow:   lda     RtsOff
345         sta     ACIA_CMD,x
346         sta     Stopped
347         sec                     ; Interrupt handled
348 Done:   rts
349
350 ;----------------------------------------------------------------------------
351 ; Try to send a byte. Internal routine. A = TryHard
352
353 TryToSend:
354         sta     tmp1            ; Remember tryHard flag
355 Again:  lda     SendFreeCnt
356         cmp     #$FF
357         beq     Quit            ; Bail out
358
359         ; Check for flow stopped
360         lda     Stopped
361         bne     Quit            ; Bail out
362
363         ; Check that ACIA is ready to send
364         lda     ACIA_STATUS
365         and     #$10
366         bne     Send
367         bit     tmp1            ; Keep trying if must try hard
368         bmi     Again
369 Quit:   rts
370
371         ; Send byte and try again
372 Send:   ldy     SendHead
373         lda     SendBuf,y
374         sta     ACIA_DATA
375         inc     SendHead
376         inc     SendFreeCnt
377         jmp     Again