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