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