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