]> git.sur5r.net Git - cc65/blob - libsrc/apple2/ser/a2.ssc.s
Fixed _textcolor definition.
[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   SER_INSTALL
48         .addr   SER_UNINSTALL
49         .addr   SER_OPEN
50         .addr   SER_CLOSE
51         .addr   SER_GET
52         .addr   SER_PUT
53         .addr   SER_STATUS
54         .addr   SER_IOCTL
55         .addr   SER_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 ; SER_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 ; SER_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 ; SER_CLOSE: Close the port and disable interrupts. Called without parameters.
156 ; Must return an SER_ERR_xx code in a/x.
157
158 SER_INSTALL:
159 SER_UNINSTALL:
160 SER_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 ; SER_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 SER_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 ; SER_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 SER_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 ; SER_PUT: Output character in A.
319 ; Must return an SER_ERR_xx code in a/x.
320
321 SER_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 ; SER_STATUS: Return the status in the variable pointed to by ptr1.
352 ; Must return an SER_ERR_xx code in a/x.
353
354 SER_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 ; SER_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 SER_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 ; SER_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 SER_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