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