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