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