]> git.sur5r.net Git - cc65/blob - libsrc/c64/c64-swlink.s
Fixed a problem and a typo
[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 Stopped:        .res    1       ; Flow-stopped flag
69 RtsOff:         .res    1       ;
70 RecvHead:       .res    1       ; Head of receive buffer
71 RecvTail:       .res    1       ; Tail of receive buffer
72 RecvFreeCnt:    .res    1       ; Number of bytes in receive buffer
73 SendHead:       .res    1       ; Head of send buffer
74 SendTail:       .res    1       ; Tail of send buffer
75 SendFreeCnt:    .res    1       ; Number of bytes free in send buffer
76 BaudCode:       .res    1       ; Current baud in effect
77
78 ; Send and receive buffers: 256 bytes each
79 RecvBuf:        .res    256
80 SendBuf:        .res    256
81
82 .data
83 NmiContinue:    .byte   $4c     ; JMP instruction for NMI save -- continue
84 NmiSave:        .res    2       ; normal NMI handler
85
86 .rodata
87
88 ; Tables used to translate RS232 params into register values
89
90 BaudTable:                      ; bit7 = 1 means setting is invalid
91         .byte   $FF             ; SER_BAUD_45_5
92         .byte   $FF             ; SER_BAUD_50
93         .byte   $FF             ; SER_BAUD_75
94         .byte   $FF             ; SER_BAUD_110
95         .byte   $FF             ; SER_BAUD_134_5
96         .byte   $02             ; SER_BAUD_150
97         .byte   $05             ; SER_BAUD_300
98         .byte   $06             ; SER_BAUD_600
99         .byte   $07             ; SER_BAUD_1200
100         .byte   $FF             ; SER_BAUD_1800
101         .byte   $08             ; SER_BAUD_2400
102         .byte   $09             ; SER_BAUD_3600
103         .byte   $0A             ; SER_BAUD_4800
104         .byte   $0B             ; SER_BAUD_7200
105         .byte   $0C             ; SER_BAUD_9600
106         .byte   $0E             ; SER_BAUD_19200
107         .byte   $0F             ; SER_BAUD_38400
108         .byte   $FF             ; SER_BAUD_57600
109         .byte   $FF             ; SER_BAUD_115200
110         .byte   $FF             ; SER_BAUD_230400
111
112 BitTable:
113         .byte   $60             ; SER_BITS_5
114         .byte   $40             ; SER_BITS_6
115         .byte   $20             ; SER_BITS_7
116         .byte   $00             ; SER_BITS_8
117
118 StopTable:
119         .byte   $00             ; SER_STOP_1
120         .byte   $80             ; SER_STOP_2
121
122 ParityTable:
123         .byte   $00             ; SER_PAR_NONE
124         .byte   $20             ; SER_PAR_ODD
125         .byte   $60             ; SER_PAR_EVEN
126         .byte   $A0             ; SER_PAR_MARK
127         .byte   $E0             ; SER_PAR_SPACE
128
129 ; Delay times: 32 byte-receive times in milliseconds, or 100 max.
130 ; Formula = 320,000 / baud. Invalid values do contain $FF.
131 PauseTimes:
132         .byte   $FF             ; SER_BAUD_45_5
133         .byte   $FF             ; SER_BAUD_50
134         .byte   $FF             ; SER_BAUD_75
135         .byte   $FF             ; SER_BAUD_110
136         .byte   $FF             ; SER_BAUD_134_5
137         .byte   $FF             ; SER_BAUD_150
138         .byte   100             ; SER_BAUD_300
139         .byte   100             ; SER_BAUD_600
140         .byte   100             ; SER_BAUD_1200
141         .byte   $FF             ; SER_BAUD_1800
142         .byte   100             ; SER_BAUD_2400
143         .byte   $FF             ; SER_BAUD_3600
144         .byte   67              ; SER_BAUD_4800
145         .byte   $FF             ; SER_BAUD_7200
146         .byte   34              ; SER_BAUD_9600
147         .byte   17              ; SER_BAUD_19200
148         .byte   9               ; SER_BAUD_38400
149         .byte   $FF             ; SER_BAUD_57600
150         .byte   $FF             ; SER_BAUD_115200
151         .byte   $FF             ; SER_BAUD_230400
152
153
154 .code
155
156 ;----------------------------------------------------------------------------
157 ; INSTALL routine. Is called after the driver is loaded into memory. If
158 ; possible, check if the hardware is present.
159 ; Must return an SER_ERR_xx code in a/x.
160
161 INSTALL:
162
163 ; Deactivate DTR and disable 6551 interrupts
164
165         lda     #%00001010
166         sta     ACIA_CMD
167
168 ; Initialize buffers & control
169
170 @L4:    ldx     #0
171         stx     RecvHead
172         stx     SendHead
173         stx     RecvTail
174         stx     SendTail
175         stx     Stopped
176         dex                     ; X = 255
177         stx     RecvFreeCnt
178         stx     SendFreeCnt
179
180 ; Set up nmi's
181
182         lda     NMIVec
183         ldy     NMIVec+1
184         sta     NmiSave+0
185         sty     NmiSave+1
186         lda     #<NmiHandler
187         ldy     #>NmiHandler
188 SetNMI: sta     NMIVec
189         sty     NMIVec+1
190
191 ; Done, return an error code
192
193         lda     #<SER_ERR_OK
194         tax                     ; A is zero
195         rts
196
197 ;----------------------------------------------------------------------------
198 ; UNINSTALL routine. Is called before the driver is removed from memory.
199 ; Must return an SER_ERR_xx code in a/x.
200
201 UNINSTALL:
202
203 ; Stop interrupts, drop DTR
204
205         lda     #%00001010
206         sta     ACIA_CMD
207
208 ; Restore NMI vector and return OK
209
210         lda     NmiSave+0
211         ldy     NmiSave+1
212         jmp     SetNMI
213
214 ;----------------------------------------------------------------------------
215 ; PARAMS routine. A pointer to a ser_params structure is passed in ptr1.
216 ; Must return an SER_ERR_xx code in a/x.
217
218 PARAMS:
219
220 ; First, check if the handshake setting is valid
221
222         ldy     #SER_PARAMS_HANDSHAKE   ; Handshake
223         lda     (ptr1),y
224         cmp     #SER_HS_HW              ; This is all we support
225         bne     InvParam
226
227 ; Set the value for the control register, which contains stop bits, word
228 ; length and the baud rate.
229
230         ldy     #SER_PARAMS_BAUDRATE
231         lda     (ptr1),y                ; Baudrate index
232         tay
233         lda     BaudTable,y             ; Get 6551 value
234         bmi     InvBaud                 ; Branch if rate not supported
235         sta     tmp1
236         sty     BaudCode                ; Remember baud rate index
237
238         ldy     #SER_PARAMS_DATABITS    ; Databits
239         lda     (ptr1),y
240         tay
241         lda     BitTable,y
242         ora     tmp1
243         sta     tmp1
244
245         ldy     #SER_PARAMS_STOPBITS    ; Stopbits
246         lda     (ptr1),y
247         tay
248         lda     StopTable,y
249         ora     tmp1
250         ora     #%00010000              ; Receiver clock source = baudrate
251         sta     ACIA_CTRL
252
253 ; Set the value for the command register. We remember the base value in
254 ; RtsOff, since we will have to manipulate ACIA_CMD often.
255
256         ldy     #SER_PARAMS_PARITY      ; Parity
257         lda     (ptr1),y
258         tay
259         lda     ParityTable,y
260         ora     #%00000001              ; DTR active
261         sta     RtsOff
262         ora     #%00001000              ; Enable receive interrupts
263         sta     ACIA_CMD
264
265 ; Done
266
267         lda     #<SER_ERR_OK
268         tax                             ; A is zero
269         rts
270
271 ; Invalid parameter
272
273 InvParam:
274         lda     #<SER_ERR_INIT_FAILED
275         ldx     #>SER_ERR_INIT_FAILED
276         rts
277
278 ; Baud rate not available
279
280 InvBaud:
281         lda     #<SER_ERR_BAUD_UNAVAIL
282         ldx     #>SER_ERR_BAUD_UNAVAIL
283         rts
284
285 ;----------------------------------------------------------------------------
286 ; GET: Will fetch a character from the receive buffer and store it into the
287 ; variable pointer to by ptr1. If no data is available, SER_ERR_NO_DATA is
288 ; return.
289 ;
290
291 GET:
292
293 ; Check for bytes to send
294
295         ldx     SendFreeCnt
296         inx                             ; X == $FF?
297         beq     @L1
298         lda     #$00
299         jsr     TryToSend
300
301 ; Check for buffer empty
302
303 @L1:    lda     RecvFreeCnt             ; (25)
304         cmp     #$ff
305         bne     @L2
306         lda     #<SER_ERR_NO_DATA
307         ldx     #>SER_ERR_NO_DATA
308         rts
309
310 ; Check for flow stopped & enough free: release flow control
311
312 @L2:    ldx     Stopped                 ; (34)
313         beq     @L3
314         cmp     #63
315         bcc     @L3
316         lda     #$00
317         sta     Stopped
318         lda     RtsOff
319         ora     #%00001000
320         sta     ACIA_CMD
321
322 ; Get byte from buffer
323
324 @L3:    ldx     RecvHead                ; (41)
325         lda     RecvBuf,x
326         inc     RecvHead
327         inc     RecvFreeCnt
328         ldx     #$00                    ; (59)
329         sta     (ptr1,x)
330         txa                             ; Return code = 0
331         rts
332
333 ;----------------------------------------------------------------------------
334 ; PUT: Output character in A.
335 ; Must return an error code in a/x.
336 ;
337
338 PUT:
339
340 ; Try to send
341
342         ldx     SendFreeCnt
343         inx                             ; X = $ff?
344         beq     @L2
345         pha
346         lda     #$00
347         jsr     TryToSend
348         pla
349
350 ; Put byte into send buffer & send
351
352 @L2:    ldx     SendFreeCnt
353         bne     @L3
354         lda     #<SER_ERR_OVERFLOW      ; X is already zero
355         rts
356
357 @L3:    ldx     SendTail
358         sta     SendBuf,x
359         inc     SendTail
360         dec     SendFreeCnt
361         lda     #$ff
362         jsr     TryToSend
363         lda     #<SER_ERR_OK
364         tax
365         rts
366
367 ;----------------------------------------------------------------------------
368 ; PAUSE: Assert flow control and disable interrupts.
369 ; Must return an error code in a/x.
370 ;
371
372 PAUSE:
373
374 ; Assert flow control
375
376         lda     RtsOff
377         sta     Stopped
378         sta     ACIA_CMD
379
380 ; Delay for flow stop to be received
381
382         ldx     BaudCode
383         ldy     PauseTimes,x
384         jsr     DelayMs
385
386 ; Stop rx interrupts
387
388         lda     RtsOff
389         ora     #%00000010              ; Disable interrupts
390         sta     ACIA_CMD
391         lda     #<SER_ERR_OK
392         tax
393         rts
394
395
396 ;----------------------------------------------------------------------------
397 ; UNPAUSE: Re-enable interrupts and release flow control.
398 ; Must return an error code in a/x.
399 ;
400
401 UNPAUSE:
402
403 ; Re-enable rx interrupts & release flow control
404
405 @L1:    lda     #$00
406         sta     Stopped
407         lda     RtsOff
408         ora     #%00001000
409         sta     ACIA_CMD
410
411 ; Poll for stalled char & exit
412
413         jsr     PollReceive
414         lda     #<SER_ERR_OK
415         tax
416         rts
417
418 ;----------------------------------------------------------------------------
419 ; STATUS: Return the status in the variable pointed to by ptr1.
420 ; Must return an error code in a/x.
421 ;
422
423 STATUS:
424         lda     ACIA_STATUS
425         ldy     #0
426         sta     (ptr1),y
427         jsr     PollReceive             ; bug-recovery hack
428         tya                             ; SER_ERR_OK
429         tax
430         rts
431
432 ;----------------------------------------------------------------------------
433 ; IOCTL: Driver defined entry point. The wrapper will pass a pointer to ioctl
434 ; specific data in ptr1, and the ioctl code in A.
435 ; Must return an error code in a/x.
436 ;
437
438 IOCTL:
439         lda     #<SER_ERR_INV_IOCTL     ; We don't support ioclts for now
440         ldx     #>SER_ERR_INV_IOCTL
441         rts
442
443 ;----------------------------------------------------------------------------
444 ;
445 ; NMI handler
446 ; C128 NMI overhead=76 cycles: int=7, maxLatency=6, ROMenter=33, ROMexit=30
447 ; C64  NMI overhead=76 cycles: int=7, maxLatency=6, ROMenter=34, ROMexit=29
448 ;
449 ; timing: normal=76+43+9=128 cycles, assertFlow=76+52+9=137 cycles
450 ;
451 ; C128 @ 115.2k: 177 cycles avail (fast)
452 ; C64  @  57.6k: 177 cycles avail, worstAvail=177-43? = 134
453 ; SCPU @ 230.4k: 868 cycles avail: for a joke!
454 ;
455
456 NmiHandler:
457         pha
458         lda     ACIA_STATUS     ;(4) ;status ;check for byte received
459         and     #$08            ;(2)
460         beq     @L9             ;(2*)
461         cld
462         txa
463         pha
464         tya
465         pha
466 @L1:    lda     ACIA_DATA       ;(4)  data  ;get byte and put into receive buffer
467         ldy     RecvTail        ;(4)
468         ldx     RecvFreeCnt     ;(4)
469         beq     @L3             ;(2*) Jump if no space in receive buffer
470         sta     RecvBuf,y       ;(5)
471         inc     RecvTail        ;(6)
472         dec     RecvFreeCnt     ;(6)
473         cpx     #33             ;(2)  check for buffer space low
474         bcc     @L2             ;(2*)
475         jmp     NMIEXIT         ;(3)
476
477 ; Assert flow control
478
479 @L2:    lda     RtsOff          ;(3) assert flow control if buffer space too low
480         sta     ACIA_CMD        ;(4) command
481         sta     Stopped         ;(3)
482 @L3:    jmp     NMIEXIT         ;(3)
483
484 @L9:    pla
485         jmp     NmiContinue
486
487 ;----------------------------------------------------------------------------
488 ; Try to send a byte. Internal routine. A = TryHard
489
490 .proc   TryToSend
491
492         sta     tmp1            ; Remember tryHard flag
493 @L0:    lda     SendFreeCnt
494         cmp     #$ff
495         beq     @L3             ; Bail out
496
497 ; Check for flow stopped
498
499 @L1:    lda     Stopped
500         bne     @L3             ; Bail out
501
502 ; Check that swiftlink is ready to send
503
504 @L2:    lda     ACIA_STATUS
505         and     #$10
506         bne     @L4
507         bit     tmp1            ;keep trying if must try hard
508         bmi     @L0
509 @L3:    rts
510
511 ; Send byte and try again
512
513 @L4:    ldx     SendHead
514         lda     SendBuf,x
515         sta     ACIA_DATA
516         inc     SendHead
517         inc     SendFreeCnt
518         jmp     @L0
519
520 .endproc
521
522 ;----------------------------------------------------------------------------
523 ;
524 ; PollReceive - poll for rx char
525 ;   This function is useful in odd cases where the 6551 has a character in
526 ;   it but it fails to raise an NMI.  It might be edge-triggering conditions?
527 ;   Actually, I'm not entirely sure that this condition can still arrise, but
528 ;   calling this function does no harm.
529 ;
530
531 .proc   PollReceive
532
533         lda     #$08
534         and     ACIA_STATUS
535         beq     @L9
536         and     ACIA_STATUS
537         beq     @L9
538         lda     ACIA_DATA
539         ldx     RecvFreeCnt
540         beq     @L9
541         ldx     RecvTail
542         sta     RecvBuf,x
543         inc     RecvTail
544         dec     RecvFreeCnt
545 @L9:    rts
546
547 .endproc
548
549 ;----------------------------------------------------------------------------
550 ;
551 ;  DelayMs : delay for given number of milliseconds
552 ;    This implementation isn't very rigerous; it merely delays for the
553 ;    approximate number of clock cycles for the processor speed.
554 ;    Algorithm:
555 ;       repeat for number of milliseconds:
556 ;         delay for 1017 clock cycles
557 ;
558
559
560 .proc   DelayMs                 ;( .A=milliseconds )
561
562 @L1:    ldx     #203            ;(2)
563 @L2:    dex                     ;(2)
564         bne     @L2             ;(3) // 1017 cycles
565         dey
566         bne     @L1
567         rts
568
569 .endproc
570
571
572 .end
573
574
575