]> git.sur5r.net Git - cc65/blob - libsrc/plus4/plus4-stdser.s
CLOSE is actually the same as INSTALL/UNINSTALL
[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 ; and:
138 ;
139 ; CLOSE: Close the port, disable interrupts and flush the buffer. Called
140 ; without parameters. Must return an error code in a/x.
141 ;
142
143 INSTALL:
144 UNINSTALL:
145 CLOSE:
146
147 ; Deactivate DTR and disable 6551 interrupts
148
149         lda     #%00001010
150         sta     ACIA_CMD
151
152 ; Done, return an error code
153
154         lda     #<SER_ERR_OK
155         tax                     ; A is zero
156         rts
157
158 ;----------------------------------------------------------------------------
159 ; PARAMS routine. A pointer to a ser_params structure is passed in ptr1.
160 ; Must return an SER_ERR_xx code in a/x.
161
162 OPEN:
163
164 ; Check if the handshake setting is valid
165
166         ldy     #SER_PARAMS::HANDSHAKE  ; Handshake
167         lda     (ptr1),y
168         cmp     #SER_HS_HW              ; This is all we support
169         bne     InvParam
170
171 ; Initialize buffers
172
173         jsr     InitBuffers
174
175 ; Set the value for the control register, which contains stop bits, word
176 ; length and the baud rate.
177
178         ldy     #SER_PARAMS::BAUDRATE
179         lda     (ptr1),y                ; Baudrate index
180         tay
181         lda     BaudTable,y             ; Get 6551 value
182         bmi     InvBaud                 ; Branch if rate not supported
183         sta     tmp1
184
185         ldy     #SER_PARAMS::DATABITS   ; Databits
186         lda     (ptr1),y
187         tay
188         lda     BitTable,y
189         ora     tmp1
190         sta     tmp1
191
192         ldy     #SER_PARAMS::STOPBITS   ; Stopbits
193         lda     (ptr1),y
194         tay
195         lda     StopTable,y
196         ora     tmp1
197         ora     #%00010000              ; Receiver clock source = baudrate
198         sta     ACIA_CTRL
199
200 ; Set the value for the command register. We remember the base value in
201 ; RtsOff, since we will have to manipulate ACIA_CMD often.
202
203         ldy     #SER_PARAMS::PARITY     ; Parity
204         lda     (ptr1),y
205         tay
206         lda     ParityTable,y
207         ora     #%00000001              ; DTR active
208         sta     RtsOff
209         ora     #%00001000              ; Enable receive interrupts
210         sta     ACIA_CMD
211
212 ; Done
213
214         lda     #<SER_ERR_OK
215         tax                             ; A is zero
216         rts
217
218 ; Invalid parameter
219
220 InvParam:
221         lda     #<SER_ERR_INIT_FAILED
222         ldx     #>SER_ERR_INIT_FAILED
223         rts
224
225 ; Baud rate not available
226
227 InvBaud:
228         lda     #<SER_ERR_BAUD_UNAVAIL
229         ldx     #>SER_ERR_BAUD_UNAVAIL
230         rts
231
232 ;----------------------------------------------------------------------------
233 ; GET: Will fetch a character from the receive buffer and store it into the
234 ; variable pointer to by ptr1. If no data is available, SER_ERR_NO_DATA is
235 ; return.
236 ;
237
238 GET:    ldx     SendFreeCnt             ; Send data if necessary
239         inx                             ; X == $FF?
240         beq     @L1
241         lda     #$00
242         jsr     TryToSend
243
244 ; Check for buffer empty
245
246 @L1:    lda     RecvFreeCnt             ; (25)
247         cmp     #$ff
248         bne     @L2
249         lda     #<SER_ERR_NO_DATA
250         ldx     #>SER_ERR_NO_DATA
251         rts
252
253 ; Check for flow stopped & enough free: release flow control
254
255 @L2:    ldx     Stopped                 ; (34)
256         beq     @L3
257         cmp     #63
258         bcc     @L3
259         lda     #$00
260         sta     Stopped
261         lda     RtsOff
262         ora     #%00001000
263         sta     ACIA_CMD
264
265 ; Get byte from buffer
266
267 @L3:    ldx     RecvHead                ; (41)
268         lda     RecvBuf,x
269         inc     RecvHead
270         inc     RecvFreeCnt
271         ldx     #$00                    ; (59)
272         sta     (ptr1,x)
273         txa                             ; Return code = 0
274         rts
275
276 ;----------------------------------------------------------------------------
277 ; PUT: Output character in A.
278 ; Must return an error code in a/x.
279 ;
280
281 PUT:
282
283 ; Try to send
284
285         ldx     SendFreeCnt
286         inx                             ; X = $ff?
287         beq     @L2
288         pha
289         lda     #$00
290         jsr     TryToSend
291         pla
292
293 ; Put byte into send buffer & send
294
295 @L2:    ldx     SendFreeCnt
296         bne     @L3
297         lda     #<SER_ERR_OVERFLOW      ; X is already zero
298         rts
299
300 @L3:    ldx     SendTail
301         sta     SendBuf,x
302         inc     SendTail
303         dec     SendFreeCnt
304         lda     #$ff
305         jsr     TryToSend
306         lda     #<SER_ERR_OK
307         tax
308         rts
309
310 ;----------------------------------------------------------------------------
311 ; STATUS: Return the status in the variable pointed to by ptr1.
312 ; Must return an error code in a/x.
313 ;
314
315 STATUS: lda     ACIA_STATUS
316         ldx     #0
317         sta     (ptr1,x)
318         txa                             ; SER_ERR_OK
319         rts
320
321 ;----------------------------------------------------------------------------
322 ; IOCTL: Driver defined entry point. The wrapper will pass a pointer to ioctl
323 ; specific data in ptr1, and the ioctl code in A.
324 ; Must return an error code in a/x.
325 ;
326
327 IOCTL:  lda     #<SER_ERR_INV_IOCTL     ; We don't support ioclts for now
328         ldx     #>SER_ERR_INV_IOCTL
329         rts                             ; Run into IRQ instead
330
331 ;----------------------------------------------------------------------------
332 ; IRQ: Called from the builtin runtime IRQ handler as a subroutine. All
333 ; registers are already save, no parameters are passed, but the carry flag
334 ; is clear on entry. The routine must return with carry set if the interrupt
335 ; was handled, otherwise with carry clear.
336 ;
337
338 IRQ:    lda     ACIA_STATUS     ; Check ACIA status for receive interrupt
339         and     #$08
340         beq     @L9             ; Jump if no ACIA interrupt (carry still clear)
341         lda     ACIA_DATA       ; Get byte from ACIA
342         ldx     RecvFreeCnt     ; Check if we have free space left
343         beq     @L1             ; Jump if no space in receive buffer
344         ldy     RecvTail        ; Load buffer pointer
345         sta     RecvBuf,y       ; Store received byte in buffer
346         inc     RecvTail        ; Increment buffer pointer
347         dec     RecvFreeCnt     ; Decrement free space counter
348         cpx     #33             ; Check for buffer space low
349         bcc     @L1             ; Assert flow control if buffer space low
350         rts                     ; Return with carry set (interrupt handled)
351
352 ; Assert flow control if buffer space too low
353
354 @L1:    lda     RtsOff
355         sta     ACIA_CMD
356         sta     Stopped
357         sec                     ; Interrupt handled
358 @L9:    rts
359
360 ;----------------------------------------------------------------------------
361 ; Try to send a byte. Internal routine. A = TryHard
362
363 .proc   TryToSend
364
365         sta     tmp1            ; Remember tryHard flag
366 @L0:    lda     SendFreeCnt
367         cmp     #$ff
368         beq     @L3             ; Bail out
369
370 ; Check for flow stopped
371
372 @L1:    lda     Stopped
373         bne     @L3             ; Bail out
374
375 ; Check that swiftlink is ready to send
376
377 @L2:    lda     ACIA_STATUS
378         and     #$10
379         bne     @L4
380         bit     tmp1            ;keep trying if must try hard
381         bmi     @L0
382 @L3:    rts
383
384 ; Send byte and try again
385
386 @L4:    ldx     SendHead
387         lda     SendBuf,x
388         sta     ACIA_DATA
389         inc     SendHead
390         inc     SendFreeCnt
391         jmp     @L0
392
393 .endproc
394
395
396 ;----------------------------------------------------------------------------
397 ; Initialize buffers
398
399 InitBuffers:
400         ldx     #0
401         stx     Stopped
402         stx     RecvHead
403         stx     RecvTail
404         stx     SendHead
405         stx     SendTail
406         dex                             ; X = 255
407         stx     RecvFreeCnt
408         stx     SendFreeCnt
409         rts
410