]> git.sur5r.net Git - cc65/blob - libsrc/c64/c64-swlink.s
Added first version of swiftlink driver
[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   $00                     ; Serial API version number
39
40 ; Jump table.
41
42         .word   INSTALL
43         .word   UNINSTALL
44         .word   PARAMS
45         .word   GET
46         .word   PUT
47         .word   PAUSE
48         .word   UNPAUSE
49         .word   STATUS
50         .word   IOCTL
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 ACIA_CLOCK      = ACIA+7        ; Turbo232 external baud-rate generator
61
62 ;----------------------------------------------------------------------------
63 ;
64 ; Global variables
65 ;
66
67 .bss
68 DropCnt:        .res    4       ; Number of bytes lost from rx buffer full
69 Stopped:        .res    1       ; Flow-stopped flag
70 RtsOff:         .res    1       ;
71 Errors:         .res    1       ; Number of bytes received in error, low byte
72 RecvHead:       .res    1       ; Head of receive buffer
73 RecvTail:       .res    1       ; Tail of receive buffer
74 RecvFreeCnt:    .res    1       ; Number of bytes in receive buffer
75 SendHead:       .res    1       ; Head of send buffer
76 SendTail:       .res    1       ; Tail of send buffer
77 SendFreeCnt:    .res    1       ; Number of bytes free in send buffer
78 BaudCode:       .res    1       ; Current baud in effect
79
80 ; Send and receive buffers: 256 bytes each
81 RecvBuf:        .res    256
82 SendBuf:        .res    256
83
84 .data
85 NmiContinue:    .byte   $4c     ; JMP instruction for NMI save -- continue
86 NmiSave:        .res    2       ; normal NMI handler
87
88 .rodata
89
90 ; Tables used to translate RS232 params into register values
91
92 BaudTable:                      ; bit7 = 1 means setting is invalid
93         .byte   $FF             ; SER_BAUD_45_5
94         .byte   $FF             ; SER_BAUD_50
95         .byte   $FF             ; SER_BAUD_75
96         .byte   $FF             ; SER_BAUD_110
97         .byte   $FF             ; SER_BAUD_134_5
98         .byte   $FF             ; SER_BAUD_150
99         .byte   $05             ; SER_BAUD_300
100         .byte   $06             ; SER_BAUD_600
101         .byte   $07             ; SER_BAUD_1200
102         .byte   $FF             ; SER_BAUD_1800
103         .byte   $08             ; SER_BAUD_2400
104         .byte   $FF             ; SER_BAUD_3600
105         .byte   $0A             ; SER_BAUD_4800
106         .byte   $FF             ; SER_BAUD_7200
107         .byte   $0C             ; SER_BAUD_9600
108         .byte   $0E             ; SER_BAUD_19200
109         .byte   $0F             ; SER_BAUD_38400
110         .byte   $FF             ; SER_BAUD_57600
111         .byte   $FF             ; SER_BAUD_115200
112         .byte   $FF             ; SER_BAUD_230400
113
114 BitTable:
115         .byte   $60             ; SER_BITS_5
116         .byte   $40             ; SER_BITS_6
117         .byte   $20             ; SER_BITS_7
118         .byte   $00             ; SER_BITS_8
119
120 StopTable:
121         .byte   $00             ; SER_STOP_1
122         .byte   $80             ; SER_STOP_2
123
124 ParityTable:
125         .byte   $00             ; SER_PAR_NONE
126         .byte   $20             ; SER_PAR_ODD
127         .byte   $60             ; SER_PAR_EVEN
128         .byte   $A0             ; SER_PAR_MARK
129         .byte   $E0             ; SER_PAR_SPACE
130
131 HandshakeTable:                 ; bit7 = 1 means that setting is invalid
132         .byte   $FF             ; SER_HS_NONE
133         .byte   $00             ; SER_HS_HW
134         .byte   $FF             ; SER_HS_SW
135
136 ; Delay times: 32 byte-receive times in milliseconds, or 100 max.
137 ; Formula = 320,000 / baud. Invalid values do contain $FF.
138 PauseTimes:
139         .byte   $FF             ; SER_BAUD_45_5
140         .byte   $FF             ; SER_BAUD_50
141         .byte   $FF             ; SER_BAUD_75
142         .byte   $FF             ; SER_BAUD_110
143         .byte   $FF             ; SER_BAUD_134_5
144         .byte   $FF             ; SER_BAUD_150
145         .byte   100             ; SER_BAUD_300
146         .byte   100             ; SER_BAUD_600
147         .byte   100             ; SER_BAUD_1200
148         .byte   $FF             ; SER_BAUD_1800
149         .byte   100             ; SER_BAUD_2400
150         .byte   $FF             ; SER_BAUD_3600
151         .byte   67              ; SER_BAUD_4800
152         .byte   $FF             ; SER_BAUD_7200
153         .byte   34              ; SER_BAUD_9600
154         .byte   17              ; SER_BAUD_19200
155         .byte   9               ; SER_BAUD_38400
156         .byte   $FF             ; SER_BAUD_57600
157         .byte   $FF             ; SER_BAUD_115200
158         .byte   $FF             ; SER_BAUD_230400
159
160
161 .code
162
163 ;----------------------------------------------------------------------------
164 ; INSTALL routine. Is called after the driver is loaded into memory. If
165 ; possible, check if the hardware is present.
166 ; Must return an SER_ERR_xx code in a/x.
167
168 INSTALL:
169
170 ; Initialize buffers & control
171
172 @L4:    lda     #0
173         sta     RecvHead
174         sta     SendHead
175         sta     RecvTail
176         sta     SendTail
177         sta     Errors
178         sta     Stopped
179         lda     #255
180         sta     RecvFreeCnt
181         sta     SendFreeCnt
182
183 ; Set up nmi's
184
185         lda     NMIVec
186         ldy     NMIVec+1
187         sta     NmiSave+0
188         sty     NmiSave+1
189         lda     #<NmiHandler
190         ldy     #>NmiHandler
191         sta     NMIVec
192         sty     NMIVec+1
193
194 ; Set default to 2400-8N1, enable interrupts
195
196         lda     ACIA_DATA
197         lda     ACIA_STATUS
198         lda     #$18
199         sta     ACIA_CTRL
200
201         lda     #$01
202         sta     RtsOff
203         ora     #$08
204         sta     ACIA_CMD
205         lda     #$06
206         sta     BaudCode
207
208 ; Done, return an error code
209
210         lda     #SER_ERR_OK
211         tax
212         rts
213
214 ;----------------------------------------------------------------------------
215 ; UNINSTALL routine. Is called before the driver is removed from memory.
216 ; Must return an SER_ERR_xx code in a/x.
217
218 UNINSTALL:
219
220 ; Stop interrupts, drop DTR
221
222         lda     RtsOff
223         and     #%11100010
224         ora     #%00000010
225         sta     ACIA_CMD
226
227 ; Restore NMI vector
228
229         lda     NmiSave+0
230         ldy     NmiSave+1
231         sta     NMIVec
232         sty     NMIVec+1
233
234 ; Flag uninitialized
235
236         lda     #SER_ERR_OK
237         tax
238         rts
239
240 ;----------------------------------------------------------------------------
241 ; PARAMS routine. A pointer to a ser_params structure is passed in ptr1.
242 ; Must return an SER_ERR_xx code in a/x.
243
244 PARAMS:
245         ldy     #SER_PARAMS_BAUDRATE
246         lda     (ptr1),y                ; Baudrate index
247         tax
248         lda     BaudTable,x             ; Get 6551 value
249         bmi     invalid                 ; Branch if rate not supported
250         sta     tmp1
251
252         iny                             ; Databits
253         lda     (ptr1),y
254         tax
255         lda     BitTable,x
256         ora     tmp1
257         sta     tmp1
258
259         iny                             ; Stopbits
260         lda     (ptr1),y
261         tax
262         lda     StopTable,x
263         ora     tmp1
264         sta     tmp1
265
266         iny                             ; Parity
267         lda     (ptr1),y
268         tax
269         lda     ParityTable,x
270         ora     tmp1
271         sta     tmp1
272
273         iny                             ; Handshake
274         lda     (ptr1),y
275         tax
276         lda     HandshakeTable,x
277         bmi     invalid
278
279
280
281
282
283
284 ;----------------------------------------------------------------------------
285 ;
286 ; unsigned char __fastcall__ rs232_params (unsigned char params, unsigned char parity);
287 ; /* Set the port parameters. Use a combination of the #defined values above. */
288 ;
289 ; Set communication parameters.
290 ;
291 ; baud rates              stops     word    |   parity
292 ; ---------------------   -----     -----   |   ---------
293 ; $00=50     $08=9600     $00=1     $00=8   |   $00=none
294 ; $01=110    $09=19200    $80=2     $20=7   |   $20=odd
295 ; $02=134.5  $0a=38400              $40=6   |   $60=even
296 ; $03=300    $0b=57600              $60=5   |   $A0=mark
297 ; $04=600    $0c=115200                     |   $E0=space
298 ; $05=1200   $0d=230400
299 ; $06=2400   $0e=future
300 ; $07=4800   $0f=future
301 ;
302
303 _rs232_params:
304         and     #%11100000              ; Save new parity
305         ora     #%00000001
306         sta     tmp2
307
308 ; Check cpu speed against baud rate
309
310         jsr     popa
311         sta     tmp1
312         and     #$0f
313         cmp     #$0c
314         bcc     @L3
315
316         ldx     CpuSpeed                ; ###
317         cpx     #1+1
318         bcc     @L2
319         cmp     #$0c
320         beq     @L3
321         cpx     #4
322         bcs     @L3
323 @L2:    lda     #RS_ERR_BAUD_TOO_FAST
324         bne     @L9
325
326 ; Set baud/parameters
327
328 @L3:    lda     tmp1
329         and     #$0f
330         tax
331         lda     BaudTable,x
332         cmp     #$ff
333         bne     @L5
334         lda     #RS_ERR_BAUD_NOT_AVAIL
335         bne     @L9
336
337 @L5:    tax
338         and     #$30
339         beq     @L6
340         bit     Turbo232
341         bmi     @L6
342         lda     #RS_ERR_BAUD_NOT_AVAIL
343         bne     @L9
344
345 @L6:    lda     tmp1
346         and     #$0f
347         sta     BaudCode
348         lda     tmp1
349         and     #%11100000
350         ora     #%00010000
351         sta     tmp1
352         txa
353         and     #$0f
354         ora     tmp1
355         sta     ACIA_CTRL
356         txa
357         and     #%00110000
358         beq     @L7
359         lsr
360         lsr
361         lsr
362         lsr
363         eor     #%00000011
364         sta     ACIA_CLOCK
365
366 ; Set new parity
367
368 @L7:    lda     tmp2
369         sta     RtsOff
370         ora     #%00001000
371         sta     ACIA_CMD
372         lda     #0
373 @L9:    ldx     #0
374         rts
375
376 .rodata
377
378 BaudTable:
379    .byte $ff,$ff,$ff,$05,$06,$07,$08,$0a,$0c,$0e,$0f,$10,$20,$30,$ff,$ff
380      ;in:  0   1   2   3   4   5   6   7   8   9   a   b   c   d   e   f
381      ;baud50 110 134   3   6  12  24  48  96  19  38  57 115 230 exp exp
382      ;out masks: $0F=Baud, val$FF=err
383      ;           $30=t232ExtBaud: $00=none, $10=57.6, $20=115.2, $30=230.4
384 .code
385
386 ;----------------------------------------------------------------------------
387 ; GET: Will fetch a character from the receive buffer and store it into the
388 ; variable pointer to by ptr1. If no data is available, SER_ERR_NO_DATA is
389 ; return.
390 ;
391
392 GET:
393
394 ; Check for bytes to send
395
396         ldx     SendFreeCnt
397         cpx     #$ff
398         beq     @L1
399         lda     #$00
400         jsr     TryToSend
401
402 ; Check for buffer empty
403
404 @L1:    lda     RecvFreeCnt             ; (25)
405         cmp     #$ff
406         bne     @L2
407         lda     #SER_ERR_NO_DATA
408         ldx     #0
409         rts
410
411 ; Check for flow stopped & enough free: release flow control
412
413 @L2:    ldx     Stopped                 ; (34)
414         beq     @L3
415         cmp     #63
416         bcc     @L3
417         lda     #$00
418         sta     Stopped
419         lda     RtsOff
420         ora     #%00001000
421         sta     ACIA_CMD
422
423 ; Get byte from buffer
424
425 @L3:    ldx     RecvHead                ; (41)
426         lda     RecvBuf,x
427         inc     RecvHead
428         inc     RecvFreeCnt
429         ldx     #$00                    ; (59)
430         sta     (ptr1,x)
431         txa                             ; Return code = 0
432         rts
433
434 ;----------------------------------------------------------------------------
435 ; PUT: Output character in A. 
436 ; Must return an error code in a/x.
437 ;
438
439 PUT:
440
441 ; Try to send
442
443         ldx     SendFreeCnt
444         cpx     #$ff
445         beq     @L2
446         pha
447         lda     #$00
448         jsr     TryToSend
449         pla
450
451 ; Put byte into send buffer & send
452
453 @L2:    ldx     SendFreeCnt
454         bne     @L3
455         lda     #<SER_ERR_OVERFLOW      ; X is already zero
456         rts
457
458 @L3:    ldx     SendTail
459         sta     SendBuf,x
460         inc     SendTail
461         dec     SendFreeCnt
462         lda     #$ff
463         jsr     TryToSend
464         lda     #<SER_ERR_OK
465         tax
466         rts
467
468 ;----------------------------------------------------------------------------
469 ; PAUSE: Assert flow control and disable interrupts.
470 ; Must return an error code in a/x.
471 ;
472
473 PAUSE:
474
475 ; Assert flow control
476
477         lda     RtsOff
478         sta     Stopped
479         sta     ACIA_CMD
480
481 ; Delay for flow stop to be received
482
483         ldx     BaudCode
484         ldy     PauseTimes,x
485         jsr     DelayMs
486
487 ; Stop rx interrupts
488
489         lda     RtsOff
490         ora     #$02
491         sta     ACIA_CMD
492         lda     #<SER_ERR_OK
493         tax
494         rts
495
496
497 ;----------------------------------------------------------------------------
498 ; UNPAUSE: Re-enable interrupts and release flow control.
499 ; Must return an error code in a/x.
500 ;
501
502 UNPAUSE:
503
504 ; Re-enable rx interrupts & release flow control
505
506 @L1:    lda     #$00
507         sta     Stopped
508         lda     RtsOff
509         ora     #%00001000
510         sta     ACIA_CMD
511
512 ; Poll for stalled char & exit
513
514         jsr     PollReceive
515         lda     #<SER_ERR_OK
516         tax
517         rts
518
519 ;----------------------------------------------------------------------------
520 ; STATUS: Return the status in the variable pointed to by ptr1.
521 ; Must return an error code in a/x.
522 ;
523
524 STATUS:
525
526 ; Get status
527
528         lda     ACIA_STATUS
529         ldy     #0
530         sta     (ptr1),y
531         jsr     PollReceive             ; bug-recovery hack
532         tya                             ; SER_ERR_OK
533         tax
534         rts
535
536 ;----------------------------------------------------------------------------
537 ;
538 ; NMI handler
539 ; C128 NMI overhead=76 cycles: int=7, maxLatency=6, ROMenter=33, ROMexit=30
540 ; C64  NMI overhead=76 cycles: int=7, maxLatency=6, ROMenter=34, ROMexit=29
541 ;
542 ; timing: normal=76+43+9=128 cycles, assertFlow=76+52+9=137 cycles
543 ;
544 ; C128 @ 115.2k: 177 cycles avail (fast)
545 ; C64  @  57.6k: 177 cycles avail, worstAvail=177-43? = 134
546 ; SCPU @ 230.4k: 868 cycles avail: for a joke!
547 ;
548
549 NmiHandler:
550         pha
551         lda     ACIA_STATUS             ;(4) ;status ;check for byte received
552         and     #$08                    ;(2)
553         beq     @L9                     ;(2*)
554         cld
555         txa
556         pha
557         tya
558         pha
559         lda     ACIA_STATUS             ;(4) opt ;status ;check for receive errors
560         and     #$07                    ;(2) opt
561         beq     @L1                     ;(3*)opt
562         inc     Errors                  ;(5^)opt
563 @L1:    lda     ACIA_DATA               ;(4) ;data  ;get byte and put into receive buffer
564         ldy     RecvTail                ;(4)
565         ldx     RecvFreeCnt             ;(4)
566         beq     @L3                     ;(2*)
567         sta     RecvBuf,y               ;(5)
568         inc     RecvTail                ;(6)
569         dec     RecvFreeCnt             ;(6)
570         cpx     #33                     ;(2)  ;check for buffer space low
571         bcc     @L2                     ;(2*)
572         jmp     NMIEXIT                 ;(3)
573
574 ; Assert flow control
575
576 @L2:    lda     RtsOff                  ;(3)  ;assert flow control if buffer space too low
577         sta     ACIA_CMD        ;(4) ;command
578         sta     Stopped                 ;(3)
579         jmp     NMIEXIT                 ;(3)
580
581 ; Drop this char
582
583 @L3:    inc     DropCnt+0               ; not time-critical
584         bne     @L4
585         inc     DropCnt+1
586         bne     @L4
587         inc     DropCnt+2
588         bne     @L4
589         inc     DropCnt+3
590 @L4:    jmp     NMIEXIT
591
592 @L9:    pla
593         jmp     NmiContinue
594
595 ;----------------------------------------------------------------------------
596 ; Try to send a byte. Internal routine. A = TryHard
597
598 .proc   TryToSend
599
600         sta     tmp1            ; Remember tryHard flag
601 @L0:    lda     SendFreeCnt
602         cmp     #$ff
603         beq     @L3             ; Bail out
604
605 ; Check for flow stopped
606
607 @L1:    lda     Stopped
608         bne     @L3             ; Bail out
609
610 ; Check that swiftlink is ready to send
611
612 @L2:    lda     ACIA_STATUS
613         and     #$10
614         bne     @L4
615         bit     tmp1            ;keep trying if must try hard
616         bmi     @L0
617 @L3:    rts
618
619 ; Send byte and try again
620
621 @L4:    ldx     SendHead
622         lda     SendBuf,x
623         sta     ACIA_DATA
624         inc     SendHead
625         inc     SendFreeCnt
626         jmp     @L0
627
628 .endproc
629
630 ;----------------------------------------------------------------------------
631 ;
632 ; PollReceive - poll for rx char
633 ;   This function is useful in odd cases where the 6551 has a character in
634 ;   it but it fails to raise an NMI.  It might be edge-triggering conditions?
635 ;   Actually, I'm not entirely sure that this condition can still arrise, but
636 ;   calling this function does no harm.
637 ;
638
639 .proc   PollReceive
640
641         lda     #$08
642         and     ACIA_STATUS
643         beq     @L9
644         and     ACIA_STATUS
645         beq     @L9
646         lda     ACIA_DATA
647         ldx     RecvFreeCnt
648         beq     @L9
649         ldx     RecvTail
650         sta     RecvBuf,x
651         inc     RecvTail
652         dec     RecvFreeCnt
653 @L9:    rts
654
655 .endproc
656
657 ;----------------------------------------------------------------------------
658 ;
659 ;  DelayMs : delay for given number of milliseconds
660 ;    This implementation isn't very rigerous; it merely delays for the
661 ;    approximate number of clock cycles for the processor speed.
662 ;    Algorithm:
663 ;       repeat for number of milliseconds:
664 ;         delay for 1017 clock cycles
665 ;
666
667
668 .proc   DelayMs                 ;( .A=milliseconds )
669
670 @L1:    ldx     #203            ;(2)
671 @L2:    dex                     ;(2)
672         bne     @L2             ;(3) // 1017 cycles
673         dey
674         bne     @L1
675         rts
676
677 .endproc
678
679
680 .end
681
682
683