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