]> git.sur5r.net Git - cc65/blob - libsrc/cbm510/ser/cbm510-std.s
Merge pull request #14 from groessler/something_to_pull
[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 ; 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 ; and:
133 ;
134 ; CLOSE: Close the port, disable interrupts and flush the buffer. Called
135 ; without parameters. Must return an error code in a/x.
136 ;
137
138 INSTALL:
139 UNINSTALL:
140 CLOSE:
141
142 ; Deactivate DTR and disable 6551 interrupts
143
144         lda     #%00001010
145         jsr     write_cmd
146
147 ; Done, return an error code
148
149         lda     #<SER_ERR_OK
150         tax                     ; A is zero
151         rts
152
153 ;----------------------------------------------------------------------------
154 ; PARAMS routine. A pointer to a ser_params structure is passed in ptr1.
155 ; Must return an SER_ERR_xx code in a/x.
156
157 OPEN:
158
159 ; Check if the handshake setting is valid
160
161         ldy     #SER_PARAMS::HANDSHAKE  ; Handshake
162         lda     (ptr1),y
163         cmp     #SER_HS_HW              ; This is all we support
164         bne     InvParam
165
166 ; Initialize buffers
167
168         ldx     #0
169         stx     Stopped
170         stx     RecvHead
171         stx     RecvTail
172         stx     SendHead
173         stx     SendTail
174         dex                             ; X = 255
175         stx     RecvFreeCnt
176         stx     SendFreeCnt
177
178 ; Set the value for the control register, which contains stop bits, word
179 ; length and the baud rate.
180
181         ldy     #SER_PARAMS::BAUDRATE
182         lda     (ptr1),y                ; Baudrate index
183         tay
184         lda     BaudTable,y             ; Get 6551 value
185         bmi     InvBaud                 ; Branch if rate not supported
186         sta     tmp1
187
188         ldy     #SER_PARAMS::DATABITS   ; Databits
189         lda     (ptr1),y
190         tay
191         lda     BitTable,y
192         ora     tmp1
193         sta     tmp1
194
195         ldy     #SER_PARAMS::STOPBITS   ; Stopbits
196         lda     (ptr1),y
197         tay
198         lda     StopTable,y
199         ora     tmp1
200         ora     #%00010000              ; Receiver clock source = baudrate
201         ldy     #ACIA::CTRL
202         jsr     write
203
204 ; Set the value for the command register. We remember the base value in
205 ; RtsOff, since we will have to manipulate ACIA_CMD often.
206
207         ldy     #SER_PARAMS::PARITY     ; Parity
208         lda     (ptr1),y
209         tay
210         lda     ParityTable,y
211         ora     #%00000001              ; DTR active
212         sta     RtsOff
213         ora     #%00001000              ; Enable receive interrupts
214         jsr     write_cmd
215
216 ; Done
217
218         lda     #<SER_ERR_OK
219         tax                             ; A is zero
220         rts
221
222 ; Invalid parameter
223
224 InvParam:
225         lda     #<SER_ERR_INIT_FAILED
226         ldx     #>SER_ERR_INIT_FAILED
227         rts
228
229 ; Baud rate not available
230
231 InvBaud:
232         lda     #<SER_ERR_BAUD_UNAVAIL
233         ldx     #>SER_ERR_BAUD_UNAVAIL
234         rts
235
236 ;----------------------------------------------------------------------------
237 ; GET: Will fetch a character from the receive buffer and store it into the
238 ; variable pointer to by ptr1. If no data is available, SER_ERR_NO_DATA is
239 ; return.
240 ;
241
242 GET:    ldx     SendFreeCnt             ; Send data if necessary
243         inx                             ; X == $FF?
244         beq     @L1
245         lda     #$00
246         jsr     TryToSend
247
248 ; Check for buffer empty
249
250 @L1:    lda     RecvFreeCnt
251         cmp     #$ff
252         bne     @L2
253         lda     #<SER_ERR_NO_DATA
254         ldx     #>SER_ERR_NO_DATA
255         rts
256
257 ; Check for flow stopped & enough free: release flow control
258
259 @L2:    ldx     Stopped
260         beq     @L3
261         cmp     #63
262         bcc     @L3
263         lda     #$00
264         sta     Stopped
265         lda     RtsOff
266         ora     #%00001000
267         jsr     write_cmd
268
269 ; Get byte from buffer
270
271 @L3:    ldx     RecvHead
272         lda     RecvBuf,x
273         inc     RecvHead
274         inc     RecvFreeCnt
275         ldx     #$00
276         sta     (ptr1,x)
277         txa                             ; Return code = 0
278         rts
279
280 ;----------------------------------------------------------------------------
281 ; PUT: Output character in A.
282 ; Must return an error code in a/x.
283 ;
284
285 PUT:
286
287 ; Try to send
288
289         ldx     SendFreeCnt
290         inx                             ; X = $ff?
291         beq     @L2
292         pha
293         lda     #$00
294         jsr     TryToSend
295         pla
296
297 ; Put byte into send buffer & send
298
299 @L2:    ldx     SendFreeCnt
300         bne     @L3
301         lda     #<SER_ERR_OVERFLOW      ; X is already zero
302         rts
303
304 @L3:    ldx     SendTail
305         sta     SendBuf,x
306         inc     SendTail
307         dec     SendFreeCnt
308         lda     #$ff
309         jsr     TryToSend
310         lda     #<SER_ERR_OK
311         tax
312         rts
313
314 ;----------------------------------------------------------------------------
315 ; STATUS: Return the status in the variable pointed to by ptr1.
316 ; Must return an error code in a/x.
317 ;
318
319 STATUS: lda     #$0F
320         sta     IndReg
321         ldy     #ACIA::STATUS
322         lda     (acia),y
323         ldx     #0
324         sta     (ptr1,x)
325         lda     IndReg
326         sta     ExecReg
327         txa                             ; SER_ERR_OK
328         rts
329
330 ;----------------------------------------------------------------------------
331 ; IOCTL: Driver defined entry point. The wrapper will pass a pointer to ioctl
332 ; specific data in ptr1, and the ioctl code in A.
333 ; Must return an error code in a/x.
334 ;
335
336 IOCTL:  lda     #<SER_ERR_INV_IOCTL     ; We don't support ioclts for now
337         ldx     #>SER_ERR_INV_IOCTL
338         rts
339
340 ;----------------------------------------------------------------------------
341 ; IRQ: Called from the builtin runtime IRQ handler as a subroutine. All
342 ; registers are already save, no parameters are passed, but the carry flag
343 ; is clear on entry. The routine must return with carry set if the interrupt
344 ; was handled, otherwise with carry clear.
345 ;
346
347 IRQ:    lda     #$0F
348         sta     IndReg          ; Switch to the system bank
349         ldy     #ACIA::STATUS
350         lda     (acia),y        ; Check ACIA status for receive interrupt
351         and     #$08
352         beq     @L9             ; Jump if no ACIA interrupt (carry still clear)
353         ldy     #ACIA::DATA
354         lda     (acia),y        ; Get byte from ACIA
355         ldx     RecvFreeCnt     ; Check if we have free space left
356         beq     @L1             ; Jump if no space in receive buffer
357         ldy     RecvTail        ; Load buffer pointer
358         sta     RecvBuf,y       ; Store received byte in buffer
359         inc     RecvTail        ; Increment buffer pointer
360         dec     RecvFreeCnt     ; Decrement free space counter
361         cpx     #33             ; Check for buffer space low
362         bcs     @L9             ; Assert flow control if buffer space low
363
364 ; Assert flow control if buffer space too low
365
366 @L1:    lda     RtsOff
367         ldy     #ACIA::CMD
368         sta     (acia),y
369         sta     Stopped
370         sec                     ; Interrupt handled
371
372 ; Done, switch back to the execution segment
373
374 @L9:    lda     ExecReg
375         sta     IndReg
376         rts
377
378 ;----------------------------------------------------------------------------
379 ; Try to send a byte. Internal routine. A = TryHard
380
381 .proc   TryToSend
382
383         sta     tmp1            ; Remember tryHard flag
384         lda     #$0F
385         sta     IndReg          ; Switch to the system bank
386 @L0:    lda     SendFreeCnt
387         cmp     #$ff
388         beq     @L3             ; Bail out
389
390 ; Check for flow stopped
391
392 @L1:    lda     Stopped
393         bne     @L3             ; Bail out
394
395 ; Check that swiftlink is ready to send
396
397 @L2:    ldy     #ACIA::STATUS
398         lda     (acia),y
399         and     #$10
400         bne     @L4
401         bit     tmp1            ; Keep trying if must try hard
402         bmi     @L0
403
404 ; Switch back the bank and return
405
406 @L3:    lda     ExecReg
407         sta     IndReg
408         rts
409
410 ; Send byte and try again
411
412 @L4:    ldx     SendHead
413         lda     SendBuf,x
414         ldy     #ACIA::DATA
415         sta     (acia),y
416         inc     SendHead
417         inc     SendFreeCnt
418         jmp     @L0
419
420 .endproc
421
422
423 ;----------------------------------------------------------------------------
424 ; Write to the ACIA changing the indirect segment. Offset is in Y, value in A.
425
426 write_cmd:
427         ldy     #ACIA::CMD
428 write:  pha
429         lda     #$0F
430         sta     IndReg
431         pla
432         sta     (acia),y
433         lda     ExecReg
434         sta     IndReg
435         rts
436