]> git.sur5r.net Git - cc65/blob - libsrc/cbm510/ser/cbm510-std.s
Removed (pretty inconsistently used) tab chars from source code base.
[cc65] / libsrc / cbm510 / ser / cbm510-std.s
1 ;
2 ; Serial driver for the builtin 6551 ACIA of the Commodore 510.
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        "cbm510.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 ; and:
129 ;
130 ; CLOSE: Close the port, disable interrupts and flush the buffer. Called
131 ; without parameters. Must return an error code in a/x.
132 ;
133
134 INSTALL:
135 UNINSTALL:
136 CLOSE:
137
138 ; Deactivate DTR and disable 6551 interrupts
139
140         lda     #%00001010
141         jsr     write_cmd
142
143 ; Done, return an error code
144
145         lda     #<SER_ERR_OK
146         tax                     ; A is zero
147         rts
148
149 ;----------------------------------------------------------------------------
150 ; PARAMS routine. A pointer to a ser_params structure is passed in ptr1.
151 ; Must return an SER_ERR_xx code in a/x.
152
153 OPEN:
154
155 ; Check if the handshake setting is valid
156
157         ldy     #SER_PARAMS::HANDSHAKE  ; Handshake
158         lda     (ptr1),y
159         cmp     #SER_HS_HW              ; This is all we support
160         bne     InvParam
161
162 ; Initialize buffers
163
164         ldx     #0
165         stx     Stopped
166         stx     RecvHead
167         stx     RecvTail
168         stx     SendHead
169         stx     SendTail
170         dex                             ; X = 255
171         stx     RecvFreeCnt
172         stx     SendFreeCnt
173
174 ; Set the value for the control register, which contains stop bits, word
175 ; length and the baud rate.
176
177         ldy     #SER_PARAMS::BAUDRATE
178         lda     (ptr1),y                ; Baudrate index
179         tay
180         lda     BaudTable,y             ; Get 6551 value
181         bmi     InvBaud                 ; Branch if rate not supported
182         sta     tmp1
183
184         ldy     #SER_PARAMS::DATABITS   ; Databits
185         lda     (ptr1),y
186         tay
187         lda     BitTable,y
188         ora     tmp1
189         sta     tmp1
190
191         ldy     #SER_PARAMS::STOPBITS   ; Stopbits
192         lda     (ptr1),y
193         tay
194         lda     StopTable,y
195         ora     tmp1
196         ora     #%00010000              ; Receiver clock source = baudrate
197         ldy     #ACIA::CTRL
198         jsr     write
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         jsr     write_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
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
256         beq     @L3
257         cmp     #63
258         bcc     @L3
259         lda     #$00
260         sta     Stopped
261         lda     RtsOff
262         ora     #%00001000
263         jsr     write_cmd
264
265 ; Get byte from buffer
266
267 @L3:    ldx     RecvHead
268         lda     RecvBuf,x
269         inc     RecvHead
270         inc     RecvFreeCnt
271         ldx     #$00
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     #$0F
316         sta     IndReg
317         ldy     #ACIA::STATUS
318         lda     (acia),y
319         ldx     #0
320         sta     (ptr1,x)
321         lda     IndReg
322         sta     ExecReg
323         txa                             ; SER_ERR_OK
324         rts
325
326 ;----------------------------------------------------------------------------
327 ; IOCTL: Driver defined entry point. The wrapper will pass a pointer to ioctl
328 ; specific data in ptr1, and the ioctl code in A.
329 ; Must return an error code in a/x.
330 ;
331
332 IOCTL:  lda     #<SER_ERR_INV_IOCTL     ; We don't support ioclts for now
333         ldx     #>SER_ERR_INV_IOCTL
334         rts
335
336 ;----------------------------------------------------------------------------
337 ; IRQ: Called from the builtin runtime IRQ handler as a subroutine. All
338 ; registers are already save, no parameters are passed, but the carry flag
339 ; is clear on entry. The routine must return with carry set if the interrupt
340 ; was handled, otherwise with carry clear.
341 ;
342
343 IRQ:    lda     #$0F
344         sta     IndReg          ; Switch to the system bank
345         ldy     #ACIA::STATUS
346         lda     (acia),y        ; Check ACIA status for receive interrupt
347         and     #$08
348         beq     @L9             ; Jump if no ACIA interrupt (carry still clear)
349         ldy     #ACIA::DATA
350         lda     (acia),y        ; Get byte from ACIA
351         ldx     RecvFreeCnt     ; Check if we have free space left
352         beq     @L1             ; Jump if no space in receive buffer
353         ldy     RecvTail        ; Load buffer pointer
354         sta     RecvBuf,y       ; Store received byte in buffer
355         inc     RecvTail        ; Increment buffer pointer
356         dec     RecvFreeCnt     ; Decrement free space counter
357         cpx     #33             ; Check for buffer space low
358         bcs     @L9             ; Assert flow control if buffer space low
359
360 ; Assert flow control if buffer space too low
361
362 @L1:    lda     RtsOff
363         ldy     #ACIA::CMD
364         sta     (acia),y
365         sta     Stopped
366         sec                     ; Interrupt handled
367
368 ; Done, switch back to the execution segment
369
370 @L9:    lda     ExecReg
371         sta     IndReg
372         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         lda     #$0F
381         sta     IndReg          ; Switch to the system bank
382 @L0:    lda     SendFreeCnt
383         cmp     #$ff
384         beq     @L3             ; Bail out
385
386 ; Check for flow stopped
387
388 @L1:    lda     Stopped
389         bne     @L3             ; Bail out
390
391 ; Check that swiftlink is ready to send
392
393 @L2:    ldy     #ACIA::STATUS
394         lda     (acia),y
395         and     #$10
396         bne     @L4
397         bit     tmp1            ; Keep trying if must try hard
398         bmi     @L0
399
400 ; Switch back the bank and return
401
402 @L3:    lda     ExecReg
403         sta     IndReg
404         rts
405
406 ; Send byte and try again
407
408 @L4:    ldx     SendHead
409         lda     SendBuf,x
410         ldy     #ACIA::DATA
411         sta     (acia),y
412         inc     SendHead
413         inc     SendFreeCnt
414         jmp     @L0
415
416 .endproc
417
418
419 ;----------------------------------------------------------------------------
420 ; Write to the ACIA changing the indirect segment. Offset is in Y, value in A.
421
422 write_cmd:
423         ldy     #ACIA::CMD
424 write:  pha
425         lda     #$0F
426         sta     IndReg
427         pla
428         sta     (acia),y
429         lda     ExecReg
430         sta     IndReg
431         rts
432