]> git.sur5r.net Git - cc65/blob - libsrc/apple2/ser/a2.ssc.s
Merge remote-tracking branch 'upstream/master'
[cc65] / libsrc / apple2 / ser / a2.ssc.s
1 ;
2 ; Serial driver for the Apple II Super Serial Card.
3 ;
4 ; Oliver Schmidt, 21.04.2005
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
28 ; ------------------------------------------------------------------------
29 ; Header. Includes jump table
30
31         .segment        "JUMPTABLE"
32
33         ; Driver signature
34         .byte   $73, $65, $72           ; "ser"
35         .byte   SER_API_VERSION         ; Serial API version number
36
37         ; Library reference
38         .addr   $0000
39
40         ; Jump table
41         .addr   INSTALL
42         .addr   UNINSTALL
43         .addr   OPEN
44         .addr   CLOSE
45         .addr   GET
46         .addr   PUT
47         .addr   STATUS
48         .addr   IOCTL
49         .addr   IRQ
50
51 ;----------------------------------------------------------------------------
52 ; I/O definitions
53
54 ACIA            = $C088
55 ACIA_DATA       = ACIA+0        ; Data register
56 ACIA_STATUS     = ACIA+1        ; Status register
57 ACIA_CMD        = ACIA+2        ; Command register
58 ACIA_CTRL       = ACIA+3        ; Control register
59
60 ;----------------------------------------------------------------------------
61 ; Global variables
62
63         .bss
64
65 RecvHead:       .res    1       ; Head of receive buffer
66 RecvTail:       .res    1       ; Tail of receive buffer
67 RecvFreeCnt:    .res    1       ; Number of bytes in receive buffer
68 SendHead:       .res    1       ; Head of send buffer
69 SendTail:       .res    1       ; Tail of send buffer
70 SendFreeCnt:    .res    1       ; Number of bytes in send buffer
71
72 Stopped:        .res    1       ; Flow-stopped flag
73 RtsOff:         .res    1       ;
74
75 RecvBuf:        .res    256     ; Receive buffers: 256 bytes
76 SendBuf:        .res    256     ; Send buffers: 256 bytes
77
78 Index:          .res    1       ; I/O register index
79
80         .data
81
82 Slot:   .byte   $02             ; Default to SSC in slot 2
83
84         .rodata
85
86         ; Tables used to translate RS232 params into register values
87 BaudTable:                      ; bit7 = 1 means setting is invalid
88         .byte   $FF             ; SER_BAUD_45_5
89         .byte   $01             ; SER_BAUD_50
90         .byte   $02             ; SER_BAUD_75
91         .byte   $03             ; SER_BAUD_110
92         .byte   $04             ; SER_BAUD_134_5
93         .byte   $05             ; SER_BAUD_150
94         .byte   $06             ; SER_BAUD_300
95         .byte   $07             ; SER_BAUD_600
96         .byte   $08             ; SER_BAUD_1200
97         .byte   $09             ; SER_BAUD_1800
98         .byte   $0A             ; SER_BAUD_2400
99         .byte   $0B             ; SER_BAUD_3600
100         .byte   $0C             ; SER_BAUD_4800
101         .byte   $0D             ; SER_BAUD_7200
102         .byte   $0E             ; SER_BAUD_9600
103         .byte   $0F             ; SER_BAUD_19200
104         .byte   $FF             ; SER_BAUD_38400
105         .byte   $FF             ; SER_BAUD_57600
106         .byte   $FF             ; SER_BAUD_115200
107         .byte   $FF             ; SER_BAUD_230400
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 StopTable:
114         .byte   $00             ; SER_STOP_1
115         .byte   $80             ; SER_STOP_2
116 ParityTable:
117         .byte   $00             ; SER_PAR_NONE
118         .byte   $20             ; SER_PAR_ODD
119         .byte   $60             ; SER_PAR_EVEN
120         .byte   $A0             ; SER_PAR_MARK
121         .byte   $E0             ; SER_PAR_SPACE
122 IdOfsTable:
123         .byte   $05             ; Pascal 1.0 ID byte
124         .byte   $07             ; Pascal 1.0 ID byte
125         .byte   $0B             ; Pascal 1.1 generic signature byte
126         .byte   $0C             ; Device signature byte
127 IdValTable:
128         .byte   $38             ; Fixed
129         .byte   $18             ; Fixed
130         .byte   $01             ; Fixed
131         .byte   $31             ; Serial or parallel I/O card type 1
132
133 IdTableLen      = * - IdValTable
134
135         .code
136
137 ;----------------------------------------------------------------------------
138 ; INSTALL: Is called after the driver is loaded into memory. If possible,
139 ; check if the hardware is present. Must return an SER_ERR_xx code in a/x.
140 ;
141 ; Since we don't have to manage the IRQ vector on the Apple II, this is
142 ; actually the same as:
143 ;
144 ; UNINSTALL: Is called before the driver is removed from memory.
145 ; No return code required (the driver is removed from memory on return).
146 ;
147 ; and:
148 ;
149 ; CLOSE: Close the port and disable interrupts. Called without parameters.
150 ; Must return an SER_ERR_xx code in a/x.
151
152 INSTALL:
153 UNINSTALL:
154 CLOSE:
155         ldx     Index           ; Check for open port
156         beq     :+
157
158         ; Deactivate DTR and disable 6551 interrupts
159         lda     #%00001010
160         sta     ACIA_CMD,x
161
162         ; Done, return an error code
163 :       lda     #<SER_ERR_OK
164         tax                     ; A is zero
165         stx     Index           ; Mark port as closed
166         rts
167
168 ;----------------------------------------------------------------------------
169 ; OPEN: A pointer to a ser_params structure is passed in ptr1.
170 ; Must return an SER_ERR_xx code in a/x.
171
172 OPEN:   
173         ldx     #<$C000
174         stx     ptr2
175         lda     #>$C000
176         ora     Slot
177         sta     ptr2+1
178         
179         ; Check Pascal 1.1 Firmware Protocol ID bytes
180 :       ldy     IdOfsTable,x
181         lda     IdValTable,x
182         cmp     (ptr2),y
183         bne     NoDevice
184         inx
185         cpx     #IdTableLen
186         bcc     :-
187         
188         ; Convert slot to I/O register index
189         lda     Slot
190         asl
191         asl
192         asl
193         asl
194         tax
195
196         ; Check if the handshake setting is valid
197         ldy     #SER_PARAMS::HANDSHAKE  ; Handshake
198         lda     (ptr1),y
199         cmp     #SER_HS_HW              ; This is all we support
200         bne     InvParam
201
202         ; Initialize buffers
203         ldy     #$00
204         sty     Stopped
205         sty     RecvHead
206         sty     RecvTail
207         sty     SendHead
208         sty     SendTail
209         dey                             ; Y = 255
210         sty     RecvFreeCnt
211         sty     SendFreeCnt
212
213         ; Set the value for the control register, which contains stop bits,
214         ; word length and the baud rate.
215         ldy     #SER_PARAMS::BAUDRATE
216         lda     (ptr1),y                ; Baudrate index
217         tay
218         lda     BaudTable,y             ; Get 6551 value
219         bmi     InvBaud                 ; Branch if rate not supported
220         sta     tmp1
221
222         ldy     #SER_PARAMS::DATABITS   ; Databits
223         lda     (ptr1),y
224         tay
225         lda     BitTable,y
226         ora     tmp1
227         sta     tmp1
228
229         ldy     #SER_PARAMS::STOPBITS   ; Stopbits
230         lda     (ptr1),y
231         tay
232         lda     StopTable,y
233         ora     tmp1
234         ora     #%00010000              ; Receiver clock source = baudrate
235         sta     ACIA_CTRL,x
236
237         ; Set the value for the command register. We remember the base value
238         ; in RtsOff, since we will have to manipulate ACIA_CMD often.
239         ldy     #SER_PARAMS::PARITY     ; Parity
240         lda     (ptr1),y
241         tay
242         lda     ParityTable,y
243         ora     #%00000001              ; DTR active
244         sta     RtsOff
245         ora     #%00001000              ; Enable receive interrupts
246         sta     ACIA_CMD,x
247
248         ; Done
249         stx     Index                   ; Mark port as open
250         lda     #<SER_ERR_OK
251         tax                             ; A is zero
252         rts
253
254         ; Device (hardware) not found
255 NoDevice:lda    #<SER_ERR_NO_DEVICE
256         ldx     #>SER_ERR_NO_DEVICE
257         rts
258
259         ; Invalid parameter
260 InvParam:lda    #<SER_ERR_INIT_FAILED
261         ldx     #>SER_ERR_INIT_FAILED
262         rts
263
264         ; Baud rate not available
265 InvBaud:lda     #<SER_ERR_BAUD_UNAVAIL
266         ldx     #>SER_ERR_BAUD_UNAVAIL
267         rts
268
269 ;----------------------------------------------------------------------------
270 ; GET: Will fetch a character from the receive buffer and store it into the
271 ; variable pointed to by ptr1. If no data is available, SER_ERR_NO_DATA is
272 ; returned.
273
274 GET:
275         ldx     Index
276         ldy     SendFreeCnt     ; Send data if necessary
277         iny                     ; Y == $FF?
278         beq     :+
279         lda     #$00            ; TryHard = false
280         jsr     TryToSend
281
282         ; Check for buffer empty
283 :       lda     RecvFreeCnt     ; (25)
284         cmp     #$FF
285         bne     :+
286         lda     #<SER_ERR_NO_DATA
287         ldx     #>SER_ERR_NO_DATA
288         rts
289
290         ; Check for flow stopped & enough free: release flow control
291 :       ldy     Stopped         ; (34)
292         beq     :+
293         cmp     #63
294         bcc     :+
295         lda     #$00
296         sta     Stopped
297         lda     RtsOff
298         ora     #%00001000
299         sta     ACIA_CMD,x
300
301         ; Get byte from buffer
302 :       ldy     RecvHead        ; (41)
303         lda     RecvBuf,y
304         inc     RecvHead
305         inc     RecvFreeCnt
306         ldx     #$00            ; (59)
307         sta     (ptr1,x)
308         txa                     ; Return code = 0
309         rts
310
311 ;----------------------------------------------------------------------------
312 ; PUT: Output character in A.
313 ; Must return an SER_ERR_xx code in a/x.
314
315 PUT:
316         ldx     Index
317
318         ; Try to send
319         ldy     SendFreeCnt
320         iny                     ; Y = $FF?
321         beq     :+
322         pha
323         lda     #$00            ; TryHard = false
324         jsr     TryToSend
325         pla
326
327         ; Put byte into send buffer & send
328 :       ldy     SendFreeCnt
329         bne     :+
330         lda     #<SER_ERR_OVERFLOW
331         ldx     #>SER_ERR_OVERFLOW
332         rts
333
334 :       ldy     SendTail
335         sta     SendBuf,y
336         inc     SendTail
337         dec     SendFreeCnt
338         lda     #$FF            ; TryHard = true
339         jsr     TryToSend
340         lda     #<SER_ERR_OK
341         tax
342         rts
343
344 ;----------------------------------------------------------------------------
345 ; STATUS: Return the status in the variable pointed to by ptr1.
346 ; Must return an SER_ERR_xx code in a/x.
347
348 STATUS:
349         ldx     Index
350         lda     ACIA_STATUS,x
351         ldx     #$00
352         sta     (ptr1,x)
353         txa                     ; SER_ERR_OK
354         rts
355
356 ;----------------------------------------------------------------------------
357 ; IOCTL: Driver defined entry point. The wrapper will pass a pointer to ioctl
358 ; specific data in ptr1, and the ioctl code in A.
359 ; Must return an SER_ERR_xx code in a/x.
360
361 IOCTL:
362         ; Check data msb and code to be 0
363         ora     ptr1+1
364         bne     :+
365
366         ; Check data lsb to be [1..7]
367         ldx     ptr1
368         beq     :+
369         cpx     #7+1
370         bcs     :+
371
372         stx     Slot
373         tax                     ; SER_ERR_OK
374         rts
375
376 :       lda     #<SER_ERR_INV_IOCTL
377         ldx     #>SER_ERR_INV_IOCTL
378         rts
379
380 ;----------------------------------------------------------------------------
381 ; IRQ: Called from the builtin runtime IRQ handler as a subroutine. All
382 ; registers are already saved, no parameters are passed, but the carry flag
383 ; is clear on entry. The routine must return with carry set if the interrupt
384 ; was handled, otherwise with carry clear.
385
386 IRQ:
387         ldx     Index           ; Check for open port
388         beq     Done
389         lda     ACIA_STATUS,x   ; Check ACIA status for receive interrupt
390         and     #$08
391         beq     Done            ; Jump if no ACIA interrupt
392         lda     ACIA_DATA,x     ; Get byte from ACIA
393         ldy     RecvFreeCnt     ; Check if we have free space left
394         beq     Flow            ; Jump if no space in receive buffer
395         ldy     RecvTail        ; Load buffer pointer
396         sta     RecvBuf,y       ; Store received byte in buffer
397         inc     RecvTail        ; Increment buffer pointer
398         dec     RecvFreeCnt     ; Decrement free space counter
399         ldy     RecvFreeCnt     ; Check for buffer space low
400         cpy     #33
401         bcc     Flow            ; Assert flow control if buffer space low
402         rts                     ; Interrupt handled (carry already set)
403
404         ; Assert flow control if buffer space too low
405 Flow:   lda     RtsOff
406         sta     ACIA_CMD,x
407         sta     Stopped
408         sec                     ; Interrupt handled
409 Done:   rts
410
411 ;----------------------------------------------------------------------------
412 ; Try to send a byte. Internal routine. A = TryHard
413
414 TryToSend:
415         sta     tmp1            ; Remember tryHard flag
416 Again:  lda     SendFreeCnt
417         cmp     #$FF
418         beq     Quit            ; Bail out
419
420         ; Check for flow stopped
421         lda     Stopped
422         bne     Quit            ; Bail out
423
424         ; Check that ACIA is ready to send
425         lda     ACIA_STATUS,x
426         and     #$10
427         bne     Send
428         bit     tmp1            ; Keep trying if must try hard  
429         bmi     Again
430 Quit:   rts
431
432         ; Send byte and try again
433 Send:   ldy     SendHead
434         lda     SendBuf,y
435         sta     ACIA_DATA,x
436         inc     SendHead
437         inc     SendFreeCnt
438         jmp     Again