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