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