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