]> git.sur5r.net Git - cc65/blob - libsrc/c64/c64-swlink.s
New EMD driver for the ISEPIC cartridge written and contributed by
[cc65] / libsrc / c64 / c64-swlink.s
1 ;
2 ; Serial driver for the C64 using a Swiftlink or Turbo-232 cartridge.
3 ;
4 ; Ullrich von Bassewitz, 2003-04-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        "ser-kernel.inc"
26         .include        "ser-error.inc"
27         .include        "c64.inc"
28
29
30 ; ------------------------------------------------------------------------
31 ; Header. Includes jump table
32
33 .segment        "JUMPTABLE"
34
35 ; Driver signature
36
37         .byte   $73, $65, $72           ; "ser"
38         .byte   SER_API_VERSION         ; Serial API version number
39
40 ; Jump table.
41
42         .word   INSTALL
43         .word   UNINSTALL
44         .word   OPEN
45         .word   CLOSE
46         .word   GET
47         .word   PUT
48         .word   STATUS
49         .word   IOCTL
50         .word   IRQ
51
52 ;----------------------------------------------------------------------------
53 ; I/O definitions
54
55 ACIA            = $DE00
56 ACIA_DATA       = ACIA+0        ; Data register
57 ACIA_STATUS     = ACIA+1        ; Status register
58 ACIA_CMD        = ACIA+2        ; Command register
59 ACIA_CTRL       = ACIA+3        ; Control register
60
61 ;----------------------------------------------------------------------------
62 ;
63 ; Global variables
64 ;
65
66 ; We reuse the RS232 zero page variables for the driver, since the ROM
67 ; routines cannot be used together with this driver.
68 RecvHead        = $B5           ; Head of receive buffer
69 RecvTail        = $BD           ; Tail of receive buffer
70 RecvFreeCnt     = $F7           ; Number of bytes in receive buffer
71 SendHead        = $F8           ; Head of send buffer
72 SendTail        = $F9           ; Tail of send buffer
73 SendFreeCnt     = $FA           ; Number of bytes free in send buffer
74
75 .bss
76 Stopped:        .res    1       ; Flow-stopped flag
77 RtsOff:         .res    1       ;
78
79 ; Send and receive buffers: 256 bytes each
80 RecvBuf:        .res    256
81 SendBuf:        .res    256
82
83 .data
84 NmiContinue:    .byte   $4c     ; JMP instruction for NMI save -- continue
85 NmiSave:        .res    2       ; normal NMI handler
86
87 .rodata
88
89 ; Tables used to translate RS232 params into register values
90
91 BaudTable:                      ; bit7 = 1 means setting is invalid
92         .byte   $FF             ; SER_BAUD_45_5
93         .byte   $FF             ; SER_BAUD_50
94         .byte   $FF             ; SER_BAUD_75
95         .byte   $FF             ; SER_BAUD_110
96         .byte   $FF             ; SER_BAUD_134_5
97         .byte   $02             ; SER_BAUD_150
98         .byte   $05             ; SER_BAUD_300
99         .byte   $06             ; SER_BAUD_600
100         .byte   $07             ; SER_BAUD_1200
101         .byte   $FF             ; SER_BAUD_1800
102         .byte   $08             ; SER_BAUD_2400
103         .byte   $09             ; SER_BAUD_3600
104         .byte   $0A             ; SER_BAUD_4800
105         .byte   $0B             ; SER_BAUD_7200
106         .byte   $0C             ; SER_BAUD_9600
107         .byte   $0E             ; SER_BAUD_19200
108         .byte   $0F             ; SER_BAUD_38400
109         .byte   $FF             ; SER_BAUD_57600
110         .byte   $FF             ; SER_BAUD_115200
111         .byte   $FF             ; SER_BAUD_230400
112
113 BitTable:
114         .byte   $60             ; SER_BITS_5
115         .byte   $40             ; SER_BITS_6
116         .byte   $20             ; SER_BITS_7
117         .byte   $00             ; SER_BITS_8
118
119 StopTable:
120         .byte   $00             ; SER_STOP_1
121         .byte   $80             ; SER_STOP_2
122
123 ParityTable:
124         .byte   $00             ; SER_PAR_NONE
125         .byte   $20             ; SER_PAR_ODD
126         .byte   $60             ; SER_PAR_EVEN
127         .byte   $A0             ; SER_PAR_MARK
128         .byte   $E0             ; SER_PAR_SPACE
129
130 .code
131
132 ;----------------------------------------------------------------------------
133 ; INSTALL routine. Is called after the driver is loaded into memory. If
134 ; possible, check if the hardware is present.
135 ; Must return an SER_ERR_xx code in a/x.
136
137 INSTALL:
138
139 ; Deactivate DTR and disable 6551 interrupts
140
141         lda     #%00001010
142         sta     ACIA_CMD
143
144 ; Set up the nmi vector
145
146         lda     NMIVec
147         ldy     NMIVec+1
148         sta     NmiSave+0
149         sty     NmiSave+1
150         lda     #<NmiHandler
151         ldy     #>NmiHandler
152 SetNMI: sta     NMIVec
153         sty     NMIVec+1
154
155 ; Done, return an error code
156
157         lda     #<SER_ERR_OK
158         tax                     ; A is zero
159         rts
160
161 ;----------------------------------------------------------------------------
162 ; UNINSTALL routine. Is called before the driver is removed from memory.
163 ; Must return an SER_ERR_xx code in a/x.
164
165 UNINSTALL:
166
167 ; Stop interrupts, drop DTR
168
169         lda     #%00001010
170         sta     ACIA_CMD
171
172 ; Restore NMI vector and return OK
173
174         lda     NmiSave+0
175         ldy     NmiSave+1
176         jmp     SetNMI
177
178 ;----------------------------------------------------------------------------
179 ; PARAMS routine. A pointer to a ser_params structure is passed in ptr1.
180 ; Must return an SER_ERR_xx code in a/x.
181
182 OPEN:
183
184 ; Check if the handshake setting is valid
185
186         ldy     #SER_PARAMS::HANDSHAKE  ; Handshake
187         lda     (ptr1),y
188         cmp     #SER_HS_HW              ; This is all we support
189         bne     InvParam
190
191 ; Initialize buffers
192
193         jsr     InitBuffers
194
195 ; Set the value for the control register, which contains stop bits, word
196 ; length and the baud rate.
197
198         ldy     #SER_PARAMS::BAUDRATE
199         lda     (ptr1),y                ; Baudrate index
200         tay
201         lda     BaudTable,y             ; Get 6551 value
202         bmi     InvBaud                 ; Branch if rate not supported
203         sta     tmp1
204
205         ldy     #SER_PARAMS::DATABITS   ; Databits
206         lda     (ptr1),y
207         tay
208         lda     BitTable,y
209         ora     tmp1
210         sta     tmp1
211
212         ldy     #SER_PARAMS::STOPBITS   ; Stopbits
213         lda     (ptr1),y
214         tay
215         lda     StopTable,y
216         ora     tmp1
217         ora     #%00010000              ; Receiver clock source = baudrate
218         sta     ACIA_CTRL
219
220 ; Set the value for the command register. We remember the base value in
221 ; RtsOff, since we will have to manipulate ACIA_CMD often.
222
223         ldy     #SER_PARAMS::PARITY     ; Parity
224         lda     (ptr1),y
225         tay
226         lda     ParityTable,y
227         ora     #%00000001              ; DTR active
228         sta     RtsOff
229         ora     #%00001000              ; Enable receive interrupts
230         sta     ACIA_CMD
231
232 ; Done
233
234         lda     #<SER_ERR_OK
235         tax                             ; A is zero
236         rts
237
238 ; Invalid parameter
239
240 InvParam:
241         lda     #<SER_ERR_INIT_FAILED
242         ldx     #>SER_ERR_INIT_FAILED
243         rts
244
245 ; Baud rate not available
246
247 InvBaud:
248         lda     #<SER_ERR_BAUD_UNAVAIL
249         ldx     #>SER_ERR_BAUD_UNAVAIL
250         rts
251
252 ;----------------------------------------------------------------------------
253 ; CLOSE: Close the port, disable interrupts and flush the buffer. Called
254 ; without parameters. Must return an error code in a/x.
255 ;
256
257 CLOSE:
258
259 ; Stop interrupts, drop DTR
260
261         lda     #%00001010
262         sta     ACIA_CMD
263
264 ; Initalize buffers. Returns zero in a
265
266         jsr     InitBuffers
267
268 ; Return OK
269
270         lda     #<SER_ERR_OK
271         tax                             ; A is zero
272         rts
273
274 ;----------------------------------------------------------------------------
275 ; GET: Will fetch a character from the receive buffer and store it into the
276 ; variable pointer to by ptr1. If no data is available, SER_ERR_NO_DATA is
277 ; return.
278 ;
279
280 GET:    ldx     SendFreeCnt             ; Send data if necessary
281         inx                             ; X == $FF?
282         beq     @L1
283         lda     #$00
284         jsr     TryToSend
285
286 ; Check for buffer empty
287
288 @L1:    lda     RecvFreeCnt             ; (25)
289         cmp     #$ff
290         bne     @L2
291         lda     #<SER_ERR_NO_DATA
292         ldx     #>SER_ERR_NO_DATA
293         rts
294
295 ; Check for flow stopped & enough free: release flow control
296
297 @L2:    ldx     Stopped                 ; (34)
298         beq     @L3
299         cmp     #63
300         bcc     @L3
301         lda     #$00
302         sta     Stopped
303         lda     RtsOff
304         ora     #%00001000
305         sta     ACIA_CMD
306
307 ; Get byte from buffer
308
309 @L3:    ldx     RecvHead                ; (41)
310         lda     RecvBuf,x
311         inc     RecvHead
312         inc     RecvFreeCnt
313         ldx     #$00                    ; (59)
314         sta     (ptr1,x)
315         txa                             ; Return code = 0
316         rts
317
318 ;----------------------------------------------------------------------------
319 ; PUT: Output character in A.
320 ; Must return an error code in a/x.
321 ;
322
323 PUT:
324
325 ; Try to send
326
327         ldx     SendFreeCnt
328         inx                             ; X = $ff?
329         beq     @L2
330         pha
331         lda     #$00
332         jsr     TryToSend
333         pla
334
335 ; Put byte into send buffer & send
336
337 @L2:    ldx     SendFreeCnt
338         bne     @L3
339         lda     #<SER_ERR_OVERFLOW      ; X is already zero
340         rts
341
342 @L3:    ldx     SendTail
343         sta     SendBuf,x
344         inc     SendTail
345         dec     SendFreeCnt
346         lda     #$ff
347         jsr     TryToSend
348         lda     #<SER_ERR_OK
349         tax
350         rts
351
352 ;----------------------------------------------------------------------------
353 ; STATUS: Return the status in the variable pointed to by ptr1.
354 ; Must return an error code in a/x.
355 ;
356
357 STATUS: lda     ACIA_STATUS
358         ldx     #0
359         sta     (ptr1,x)
360         txa                             ; SER_ERR_OK
361         rts
362
363 ;----------------------------------------------------------------------------
364 ; IOCTL: Driver defined entry point. The wrapper will pass a pointer to ioctl
365 ; specific data in ptr1, and the ioctl code in A.
366 ; Must return an error code in a/x.
367 ;
368
369 IOCTL:  lda     #<SER_ERR_INV_IOCTL     ; We don't support ioclts for now
370         ldx     #>SER_ERR_INV_IOCTL
371        rts
372
373 ;----------------------------------------------------------------------------
374 ; IRQ: Not used on the C64
375 ;
376
377 IRQ     = $0000
378
379 ;----------------------------------------------------------------------------
380 ;
381 ; NMI handler
382 ; C128 NMI overhead=76 cycles: int=7, maxLatency=6, ROMenter=33, ROMexit=30
383 ; C64  NMI overhead=76 cycles: int=7, maxLatency=6, ROMenter=34, ROMexit=29
384 ;
385 ; timing: normal=76+43+9=128 cycles, assertFlow=76+52+9=137 cycles
386 ;
387 ; C128 @ 115.2k: 177 cycles avail (fast)
388 ; C64  @  57.6k: 177 cycles avail, worstAvail=177-43? = 134
389 ; SCPU @ 230.4k: 868 cycles avail: for a joke!
390 ;
391
392 NmiHandler:
393         pha
394         lda     ACIA_STATUS     ;(4) ;status ;check for byte received
395         and     #$08            ;(2)
396         beq     @L9             ;(2*)
397         cld
398         txa
399         pha
400         tya
401         pha
402 @L1:    lda     ACIA_DATA       ;(4)  data  ;get byte and put into receive buffer
403         ldy     RecvTail        ;(4)
404         ldx     RecvFreeCnt     ;(4)
405         beq     @L3             ;(2*) Jump if no space in receive buffer
406         sta     RecvBuf,y       ;(5)
407         inc     RecvTail        ;(6)
408         dec     RecvFreeCnt     ;(6)
409         cpx     #33             ;(2)  check for buffer space low
410         bcc     @L2             ;(2*)
411         jmp     NMIEXIT         ;(3)
412
413 ; Assert flow control
414
415 @L2:    lda     RtsOff          ;(3) assert flow control if buffer space too low
416         sta     ACIA_CMD        ;(4) command
417         sta     Stopped         ;(3)
418 @L3:    jmp     NMIEXIT         ;(3)
419
420 @L9:    pla
421         jmp     NmiContinue
422
423 ;----------------------------------------------------------------------------
424 ; Try to send a byte. Internal routine. A = TryHard
425
426 .proc   TryToSend
427
428         sta     tmp1            ; Remember tryHard flag
429 @L0:    lda     SendFreeCnt
430         cmp     #$ff
431         beq     @L3             ; Bail out
432
433 ; Check for flow stopped
434
435 @L1:    lda     Stopped
436         bne     @L3             ; Bail out
437
438 ; Check that swiftlink is ready to send
439
440 @L2:    lda     ACIA_STATUS
441         and     #$10
442         bne     @L4
443         bit     tmp1            ;keep trying if must try hard
444         bmi     @L0
445 @L3:    rts
446
447 ; Send byte and try again
448
449 @L4:    ldx     SendHead
450         lda     SendBuf,x
451         sta     ACIA_DATA
452         inc     SendHead
453         inc     SendFreeCnt
454         jmp     @L0
455
456 .endproc
457
458
459 ;----------------------------------------------------------------------------
460 ; Initialize buffers
461
462 InitBuffers:
463         ldx     #0
464         stx     Stopped
465         stx     RecvHead
466         stx     RecvTail
467         stx     SendHead
468         stx     SendTail
469         dex                             ; X = 255
470         stx     RecvFreeCnt
471         stx     SendFreeCnt
472         rts
473