]> git.sur5r.net Git - cc65/blob - libsrc/cbm610/ser/cbm610-std.s
Added library reference ser_libref to SER interface.
[cc65] / libsrc / cbm610 / ser / cbm610-std.s
1 ;
2 ; Serial driver for the builtin 6551 ACIA of the Commodore 610.
3 ;
4 ; Ullrich von Bassewitz, 2003-12-18
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        "../extzp.inc"
26         .include        "ser-kernel.inc"
27         .include        "ser-error.inc"
28         .include        "cbm610.inc"
29
30
31 ; ------------------------------------------------------------------------
32 ; Header. Includes jump table
33
34 .segment        "JUMPTABLE"
35
36 ; Driver signature
37
38         .byte   $73, $65, $72           ; "ser"
39         .byte   SER_API_VERSION         ; Serial API version number
40
41 ; Library reference
42
43         .addr   $0000
44
45 ; Jump table
46
47         .word   INSTALL
48         .word   UNINSTALL
49         .word   OPEN
50         .word   CLOSE
51         .word   GET
52         .word   PUT
53         .word   STATUS
54         .word   IOCTL
55         .word   IRQ
56
57 ;----------------------------------------------------------------------------
58 ;
59 ; Global variables
60 ;
61
62 .bss
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 ; Send and receive buffers: 256 bytes each
74 RecvBuf:        .res    256
75 SendBuf:        .res    256
76
77 .rodata
78
79 ; Tables used to translate RS232 params into register values
80
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
103 BitTable:
104         .byte   $60             ; SER_BITS_5
105         .byte   $40             ; SER_BITS_6
106         .byte   $20             ; SER_BITS_7
107         .byte   $00             ; SER_BITS_8
108
109 StopTable:
110         .byte   $00             ; SER_STOP_1
111         .byte   $80             ; SER_STOP_2
112
113 ParityTable:
114         .byte   $00             ; SER_PAR_NONE
115         .byte   $20             ; SER_PAR_ODD
116         .byte   $60             ; SER_PAR_EVEN
117         .byte   $A0             ; SER_PAR_MARK
118         .byte   $E0             ; SER_PAR_SPACE
119
120 .code
121
122 ;----------------------------------------------------------------------------
123 ; INSTALL routine. Is called after the driver is loaded into memory. If
124 ; possible, check if the hardware is present.
125 ; Must return an SER_ERR_xx code in a/x.
126 ;
127 ; Since we don't have to manage the IRQ vector on the Plus/4, this is actually
128 ; the same as:
129 ;
130 ; UNINSTALL routine. Is called before the driver is removed from memory.
131 ; Must return an SER_ERR_xx code in a/x.
132 ;
133 ; and:
134 ;
135 ; CLOSE: Close the port, disable interrupts and flush the buffer. Called
136 ; without parameters. Must return an error code in a/x.
137 ;
138
139 INSTALL:
140 UNINSTALL:
141 CLOSE:
142
143 ; Deactivate DTR and disable 6551 interrupts
144
145         lda     #%00001010
146         jsr     write_cmd
147
148 ; Done, return an error code
149
150         lda     #<SER_ERR_OK
151         tax                     ; A is zero
152         rts
153
154 ;----------------------------------------------------------------------------
155 ; PARAMS routine. A pointer to a ser_params structure is passed in ptr1.
156 ; Must return an SER_ERR_xx code in a/x.
157
158 OPEN:
159
160 ; Check if the handshake setting is valid
161
162         ldy     #SER_PARAMS::HANDSHAKE  ; Handshake
163         lda     (ptr1),y
164         cmp     #SER_HS_HW              ; This is all we support
165         bne     InvParam
166
167 ; Initialize buffers
168
169         ldx     #0
170         stx     Stopped
171         stx     RecvHead
172         stx     RecvTail
173         stx     SendHead
174         stx     SendTail
175         dex                             ; X = 255
176         stx     RecvFreeCnt
177         stx     SendFreeCnt
178
179 ; Set the value for the control register, which contains stop bits, word
180 ; length and the baud rate.
181
182         ldy     #SER_PARAMS::BAUDRATE
183         lda     (ptr1),y                ; Baudrate index
184         tay
185         lda     BaudTable,y             ; Get 6551 value
186         bmi     InvBaud                 ; Branch if rate not supported
187         sta     tmp1
188
189         ldy     #SER_PARAMS::DATABITS   ; Databits
190         lda     (ptr1),y
191         tay
192         lda     BitTable,y
193         ora     tmp1
194         sta     tmp1
195
196         ldy     #SER_PARAMS::STOPBITS   ; Stopbits
197         lda     (ptr1),y
198         tay
199         lda     StopTable,y
200         ora     tmp1
201         ora     #%00010000              ; Receiver clock source = baudrate
202         ldy     #ACIA::CTRL
203         jsr     write
204
205 ; Set the value for the command register. We remember the base value in
206 ; RtsOff, since we will have to manipulate ACIA_CMD often.
207
208         ldy     #SER_PARAMS::PARITY     ; Parity
209         lda     (ptr1),y
210         tay
211         lda     ParityTable,y
212         ora     #%00000001              ; DTR active
213         sta     RtsOff
214         ora     #%00001000              ; Enable receive interrupts
215         jsr     write_cmd
216
217 ; Done
218
219         lda     #<SER_ERR_OK
220         tax                             ; A is zero
221         rts
222
223 ; Invalid parameter
224
225 InvParam:
226         lda     #<SER_ERR_INIT_FAILED
227         ldx     #>SER_ERR_INIT_FAILED
228         rts
229
230 ; Baud rate not available
231
232 InvBaud:
233         lda     #<SER_ERR_BAUD_UNAVAIL
234         ldx     #>SER_ERR_BAUD_UNAVAIL
235         rts
236
237 ;----------------------------------------------------------------------------
238 ; GET: Will fetch a character from the receive buffer and store it into the
239 ; variable pointer to by ptr1. If no data is available, SER_ERR_NO_DATA is
240 ; return.
241 ;
242
243 GET:    ldx     SendFreeCnt             ; Send data if necessary
244         inx                             ; X == $FF?
245         beq     @L1
246         lda     #$00
247         jsr     TryToSend
248
249 ; Check for buffer empty
250
251 @L1:    lda     RecvFreeCnt
252         cmp     #$ff
253         bne     @L2
254         lda     #<SER_ERR_NO_DATA
255         ldx     #>SER_ERR_NO_DATA
256         rts
257
258 ; Check for flow stopped & enough free: release flow control
259
260 @L2:    ldx     Stopped
261         beq     @L3
262         cmp     #63
263         bcc     @L3
264         lda     #$00
265         sta     Stopped
266         lda     RtsOff
267         ora     #%00001000
268         jsr     write_cmd
269
270 ; Get byte from buffer
271
272 @L3:    ldx     RecvHead
273         lda     RecvBuf,x
274         inc     RecvHead
275         inc     RecvFreeCnt
276         ldx     #$00
277         sta     (ptr1,x)
278         txa                             ; Return code = 0
279         rts
280
281 ;----------------------------------------------------------------------------
282 ; PUT: Output character in A.
283 ; Must return an error code in a/x.
284 ;
285
286 PUT:
287
288 ; Try to send
289
290         ldx     SendFreeCnt
291         inx                             ; X = $ff?
292         beq     @L2
293         pha
294         lda     #$00
295         jsr     TryToSend
296         pla
297
298 ; Put byte into send buffer & send
299
300 @L2:    ldx     SendFreeCnt
301         bne     @L3
302         lda     #<SER_ERR_OVERFLOW      ; X is already zero
303         rts
304
305 @L3:    ldx     SendTail
306         sta     SendBuf,x
307         inc     SendTail
308         dec     SendFreeCnt
309         lda     #$ff
310         jsr     TryToSend
311         lda     #<SER_ERR_OK
312         tax
313         rts
314
315 ;----------------------------------------------------------------------------
316 ; STATUS: Return the status in the variable pointed to by ptr1.
317 ; Must return an error code in a/x.
318 ;
319
320 STATUS: lda     #$0F
321         sta     IndReg
322         ldy     #ACIA::STATUS
323         lda     (acia),y
324         ldx     #0
325         sta     (ptr1,x)
326         lda     IndReg
327         sta     ExecReg
328         txa                             ; SER_ERR_OK
329         rts
330
331 ;----------------------------------------------------------------------------
332 ; IOCTL: Driver defined entry point. The wrapper will pass a pointer to ioctl
333 ; specific data in ptr1, and the ioctl code in A.
334 ; Must return an error code in a/x.
335 ;
336
337 IOCTL:  lda     #<SER_ERR_INV_IOCTL     ; We don't support ioclts for now
338         ldx     #>SER_ERR_INV_IOCTL
339         rts
340
341 ;----------------------------------------------------------------------------
342 ; IRQ: Called from the builtin runtime IRQ handler as a subroutine. All
343 ; registers are already save, no parameters are passed, but the carry flag
344 ; is clear on entry. The routine must return with carry set if the interrupt
345 ; was handled, otherwise with carry clear.
346 ;
347
348 IRQ:    lda     #$0F
349         sta     IndReg          ; Switch to the system bank
350         ldy     #ACIA::STATUS
351         lda     (acia),y        ; Check ACIA status for receive interrupt
352         and     #$08
353         beq     @L9             ; Jump if no ACIA interrupt (carry still clear)
354         ldy     #ACIA::DATA
355         lda     (acia),y        ; 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         bcs     @L9             ; Assert flow control if buffer space low
364
365 ; Assert flow control if buffer space too low
366
367 @L1:    lda     RtsOff
368         ldy     #ACIA::CMD
369         sta     (acia),y
370         sta     Stopped
371         sec                     ; Interrupt handled
372
373 ; Done, switch back to the execution segment
374
375 @L9:    lda     ExecReg
376         sta     IndReg
377         rts
378
379 ;----------------------------------------------------------------------------
380 ; Try to send a byte. Internal routine. A = TryHard
381
382 .proc   TryToSend
383
384         sta     tmp1            ; Remember tryHard flag
385         lda     #$0F
386         sta     IndReg          ; Switch to the system bank
387 @L0:    lda     SendFreeCnt
388         cmp     #$ff
389         beq     @L3             ; Bail out
390
391 ; Check for flow stopped
392
393 @L1:    lda     Stopped
394         bne     @L3             ; Bail out
395
396 ; Check that swiftlink is ready to send
397
398 @L2:    ldy     #ACIA::STATUS
399         lda     (acia),y
400         and     #$10
401         bne     @L4
402         bit     tmp1            ; Keep trying if must try hard
403         bmi     @L0
404
405 ; Switch back the bank and return
406
407 @L3:    lda     ExecReg
408         sta     IndReg
409         rts
410
411 ; Send byte and try again
412
413 @L4:    ldx     SendHead
414         lda     SendBuf,x
415         ldy     #ACIA::DATA
416         sta     (acia),y
417         inc     SendHead
418         inc     SendFreeCnt
419         jmp     @L0
420
421 .endproc
422
423
424 ;----------------------------------------------------------------------------
425 ; Write to the ACIA changing the indirect segment. Offset is in Y, value in A.
426
427 write_cmd:
428         ldy     #ACIA::CMD
429 write:  pha
430         lda     #$0F
431         sta     IndReg
432         pla
433         sta     (acia),y
434         lda     ExecReg
435         sta     IndReg
436         rts
437