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