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