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