]> git.sur5r.net Git - cc65/blob - libsrc/c128/ser/c128-swlink.s
Fix joystick driver. Add interruptor support.
[cc65] / libsrc / c128 / ser / 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         .macpack        module
30
31
32 ; ------------------------------------------------------------------------
33 ; Header. Includes jump table
34
35         module_header   _c128_swlink_ser
36
37 ; Driver signature
38
39         .byte   $73, $65, $72           ; "ser"
40         .byte   SER_API_VERSION         ; Serial API version number
41
42 ; Library reference
43
44         .addr   $0000
45
46 ; Jump table
47
48         .word   INSTALL
49         .word   UNINSTALL
50         .word   OPEN
51         .word   CLOSE
52         .word   GET
53         .word   PUT
54         .word   STATUS
55         .word   IOCTL
56         .word   IRQ
57
58 ;----------------------------------------------------------------------------
59 ; I/O definitions
60
61 ACIA            = $DE00
62 ACIA_DATA       = ACIA+0        ; Data register
63 ACIA_STATUS     = ACIA+1        ; Status register
64 ACIA_CMD        = ACIA+2        ; Command register
65 ACIA_CTRL       = ACIA+3        ; Control register
66
67 ;----------------------------------------------------------------------------
68 ;
69 ; Global variables
70 ;
71
72 ; We reuse the RS232 zero page variables for the driver, since the ROM
73 ; routines cannot be used together with this driver (may also use $A0F
74 ; and following in case of problems).
75 RecvHead        := $A7          ; Head of receive buffer
76 RecvTail        := $A8          ; Tail of receive buffer
77 RecvFreeCnt     := $A9          ; Number of bytes in receive buffer
78 SendHead        := $AA          ; Head of send buffer
79 SendTail        := $AB          ; Tail of send buffer
80 SendFreeCnt     := $B4          ; Number of bytes free in send buffer
81 Stopped         := $B5          ; Flow-stopped flag
82 RtsOff          := $B6          ;
83
84 ; Send and receive buffers: 256 bytes each
85 RecvBuf         := $0C00        ; Use the ROM buffers
86 SendBuf         := $0D00
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   $02             ; 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   $09             ; SER_BAUD_3600
105         .byte   $0A             ; SER_BAUD_4800
106         .byte   $0B             ; 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 .code
132
133 ;----------------------------------------------------------------------------
134 ; Interrupt stub that is copied into low RAM. The startup code uses a special
135 ; memory configuration with just kernal and I/O enabled (anything else is RAM).
136 ; The NMI handler in ROM will switch back to a configuration where just the
137 ; low 16K RAM are accessible. So we have to copy a smal piece of code into
138 ; low RAM that enables the cc65 configuration and then jumps to the real NMI
139 ; handler.
140
141 NmiStubOrig     := *
142
143 .org    $1150                           ; BASIC graphics area
144 .proc   NmiStub
145
146         lda     #MMU_CFG_CC65           ; Bank 0 with kernal ROM...
147         sta     MMU_CR                  ; ...enable
148         jsr     NmiHandler              ; Call the actual NMI handler
149         lda     #$00                    ; Get ROM config...
150         sta     MMU_CR                  ; ...and enable it
151 Vector  := *+1
152         .byte   $4C                     ; Jump to the saved IRQ vector
153
154 .endproc
155 .reloc
156
157 ;----------------------------------------------------------------------------
158 ; INSTALL routine. Is called after the driver is loaded into memory. If
159 ; possible, check if the hardware is present.
160 ; Must return an SER_ERR_xx code in a/x.
161
162 INSTALL:
163
164 ; Deactivate DTR and disable 6551 interrupts
165
166         lda     #%00001010
167         sta     ACIA_CMD
168
169 ; Copy the NMI stub into low memory
170
171         ldy     #.sizeof (NmiStub)-1
172 @L1:    lda     NmiStubOrig,y
173         sta     NmiStub,y
174         dey
175         bpl     @L1
176
177 ; Set up the nmi vector
178
179         lda     NMIVec
180         ldy     NMIVec+1
181         sta     NmiStub::Vector+0
182         sty     NmiStub::Vector+1
183         lda     #<NmiStub
184         ldy     #>NmiStub
185 SetNMI: sta     NMIVec
186         sty     NMIVec+1
187
188 ; Done, return an error code
189
190         lda     #<SER_ERR_OK
191         tax                     ; A is zero
192         rts
193
194 ;----------------------------------------------------------------------------
195 ; UNINSTALL routine. Is called before the driver is removed from memory.
196 ; Must return an SER_ERR_xx code in a/x.
197
198 UNINSTALL:
199
200 ; Stop interrupts, drop DTR
201
202         lda     #%00001010
203         sta     ACIA_CMD
204
205 ; Restore NMI vector and return OK
206
207         lda     NmiStub::Vector+0
208         ldy     NmiStub::Vector+1
209         jmp     SetNMI
210
211 ;----------------------------------------------------------------------------
212 ; PARAMS routine. A pointer to a ser_params structure is passed in ptr1.
213 ; Must return an SER_ERR_xx code in a/x.
214
215 OPEN:
216
217 ; Check if the handshake setting is valid
218
219         ldy     #SER_PARAMS::HANDSHAKE  ; Handshake
220         lda     (ptr1),y
221         cmp     #SER_HS_HW              ; This is all we support
222         bne     InvParam
223
224 ; Initialize buffers
225
226         jsr     InitBuffers
227
228 ; Set the value for the control register, which contains stop bits, word
229 ; length and the baud rate.
230
231         ldy     #SER_PARAMS::BAUDRATE
232         lda     (ptr1),y                ; Baudrate index
233         tay
234         lda     BaudTable,y             ; Get 6551 value
235         bmi     InvBaud                 ; Branch if rate not supported
236         sta     tmp1
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 ; CLOSE: Close the port, disable interrupts and flush the buffer. Called
287 ; without parameters. Must return an error code in a/x.
288 ;
289
290 CLOSE:
291
292 ; Stop interrupts, drop DTR
293
294         lda     #%00001010
295         sta     ACIA_CMD
296
297 ; Initalize buffers. Returns zero in a
298
299         jsr     InitBuffers
300
301 ; Return OK
302
303         lda     #<SER_ERR_OK
304         tax                             ; A is zero
305         rts
306
307 ;----------------------------------------------------------------------------
308 ; GET: Will fetch a character from the receive buffer and store it into the
309 ; variable pointer to by ptr1. If no data is available, SER_ERR_NO_DATA is
310 ; return.
311 ;
312
313 GET:    ldx     SendFreeCnt             ; Send data if necessary
314         inx                             ; X == $FF?
315         beq     @L1
316         lda     #$00
317         jsr     TryToSend
318
319 ; Check for buffer empty
320
321 @L1:    lda     RecvFreeCnt             ; (25)
322         cmp     #$ff
323         bne     @L2
324         lda     #<SER_ERR_NO_DATA
325         ldx     #>SER_ERR_NO_DATA
326         rts
327
328 ; Check for flow stopped & enough free: release flow control
329
330 @L2:    ldx     Stopped                 ; (34)
331         beq     @L3
332         cmp     #63
333         bcc     @L3
334         lda     #$00
335         sta     Stopped
336         lda     RtsOff
337         ora     #%00001000
338         sta     ACIA_CMD
339
340 ; Get byte from buffer
341
342 @L3:    ldx     RecvHead                ; (41)
343         lda     RecvBuf,x
344         inc     RecvHead
345         inc     RecvFreeCnt
346         ldx     #$00                    ; (59)
347         sta     (ptr1,x)
348         txa                             ; Return code = 0
349         rts
350
351 ;----------------------------------------------------------------------------
352 ; PUT: Output character in A.
353 ; Must return an error code in a/x.
354 ;
355
356 PUT:
357
358 ; Try to send
359
360         ldx     SendFreeCnt
361         inx                             ; X = $ff?
362         beq     @L2
363         pha
364         lda     #$00
365         jsr     TryToSend
366         pla
367
368 ; Put byte into send buffer & send
369
370 @L2:    ldx     SendFreeCnt
371         bne     @L3
372         lda     #<SER_ERR_OVERFLOW      ; X is already zero
373         rts
374
375 @L3:    ldx     SendTail
376         sta     SendBuf,x
377         inc     SendTail
378         dec     SendFreeCnt
379         lda     #$ff
380         jsr     TryToSend
381         lda     #<SER_ERR_OK
382         tax
383         rts
384
385 ;----------------------------------------------------------------------------
386 ; STATUS: Return the status in the variable pointed to by ptr1.
387 ; Must return an error code in a/x.
388 ;
389
390 STATUS: lda     ACIA_STATUS
391         ldx     #0
392         sta     (ptr1,x)
393         txa                             ; SER_ERR_OK
394         rts
395
396 ;----------------------------------------------------------------------------
397 ; IOCTL: Driver defined entry point. The wrapper will pass a pointer to ioctl
398 ; specific data in ptr1, and the ioctl code in A.
399 ; Must return an error code in a/x.
400 ;
401
402 IOCTL:  lda     #<SER_ERR_INV_IOCTL     ; We don't support ioclts for now
403         ldx     #>SER_ERR_INV_IOCTL
404         rts
405
406 ;----------------------------------------------------------------------------
407 ; IRQ: Not used on the C128
408 ;
409
410 IRQ     = $0000
411
412 ;----------------------------------------------------------------------------
413 ;
414 ; NMI handler
415 ; C128 NMI overhead=76 cycles: int=7, maxLatency=6, ROMenter=33, ROMexit=30
416 ; C64  NMI overhead=76 cycles: int=7, maxLatency=6, ROMenter=34, ROMexit=29
417 ;
418 ; timing: normal=76+43+9=128 cycles, assertFlow=76+52+9=137 cycles
419 ;
420 ; C128 @ 115.2k: 177 cycles avail (fast)
421 ; C64  @  57.6k: 177 cycles avail, worstAvail=177-43? = 134
422 ; SCPU @ 230.4k: 868 cycles avail: for a joke!
423 ;
424 ; Note: Because of the C128 banking, a small stub has to go into low memory,
425 ; since the ROM NMI entry point switches to a configuration, where only the
426 ; low 16K of RAM are visible. The entry code switches into the standard cc65
427 ; configuration (I/O + 16K kernal) and then jumps here. Registers are already
428 ; saved by the ROM code.
429
430 NmiHandler:
431         lda     ACIA_STATUS     ;(4) ;status ;check for byte received
432         and     #$08            ;(2)
433         beq     @L9             ;(2*)
434         cld
435         lda     ACIA_DATA       ;(4)  data  ;get byte and put into receive buffer
436         ldy     RecvTail        ;(4)
437         ldx     RecvFreeCnt     ;(4)
438         beq     @L9             ;(2*) Jump if no space in receive buffer
439         sta     RecvBuf,y       ;(5)
440         inc     RecvTail        ;(6)
441         dec     RecvFreeCnt     ;(6)
442         cpx     #33             ;(2)  check for buffer space low
443         bcc     @L2             ;(2*)
444         rts
445
446 ; Assert flow control
447
448 @L2:    lda     RtsOff          ;(3) assert flow control if buffer space too low
449         sta     ACIA_CMD        ;(4) command
450         sta     Stopped         ;(3)
451 @L9:    rts
452
453 ;----------------------------------------------------------------------------
454 ; Try to send a byte. Internal routine. A = TryHard
455
456 .proc   TryToSend
457
458         sta     tmp1            ; Remember tryHard flag
459 @L0:    lda     SendFreeCnt
460         cmp     #$ff
461         beq     @L3             ; Bail out
462
463 ; Check for flow stopped
464
465 @L1:    lda     Stopped
466         bne     @L3             ; Bail out
467
468 ; Check that swiftlink is ready to send
469
470 @L2:    lda     ACIA_STATUS
471         and     #$10
472         bne     @L4
473         bit     tmp1            ;keep trying if must try hard
474         bmi     @L0
475 @L3:    rts
476
477 ; Send byte and try again
478
479 @L4:    ldx     SendHead
480         lda     SendBuf,x
481         sta     ACIA_DATA
482         inc     SendHead
483         inc     SendFreeCnt
484         jmp     @L0
485
486 .endproc
487
488
489 ;----------------------------------------------------------------------------
490 ; Initialize buffers
491
492 InitBuffers:
493         ldx     #0
494         stx     Stopped
495         stx     RecvHead
496         stx     RecvTail
497         stx     SendHead
498         stx     SendTail
499         dex                             ; X = 255
500         stx     RecvFreeCnt
501         stx     SendFreeCnt
502         rts
503
504