]> git.sur5r.net Git - cc65/blob - libsrc/cbm610/rs232.s
Added o65 symbol export capability
[cc65] / libsrc / cbm610 / rs232.s
1 ;
2 ; SwiftLink/Turbo-232 v0.90 device driver, by Craig Bruce, 14-Apr-1998.
3 ;
4 ; This software is Public Domain.  It is in Buddy assembler format.
5 ;
6 ; This device driver uses the SwiftLink RS-232 Serial Cartridge, available from
7 ; Creative Micro Designs, Inc, and also supports the extensions of the Turbo232
8 ; Serial Cartridge.  Both devices are based on the 6551 ACIA chip.  It also
9 ; supports the "hacked" SwiftLink with a 1.8432 MHz crystal.
10 ;
11 ; The code assumes that the kernal + I/O are in context.  On the C128, call
12 ; it from Bank 15.  On the C64, don't flip out the Kernal unless a suitable
13 ; NMI catcher is put into the RAM under then Kernal.  For the SuperCPU, the
14 ; interrupt handling assumes that the 65816 is in 6502-emulation mode.
15 ;
16 ;--------------------------------------------------------------------------
17 ;
18 ; Adapted for the use with the cc65 runtime library by
19 ; Ullrich von Bassewitz (uz@musoftware.de) 02-May-1999.
20 ;
21 ; All external functions are C callable, the return value is an error code.
22 ;
23
24
25         .importzp       ptr1, ptr2, tmp1, tmp2
26         .importzp       acia, RecvBuf, SendBuf
27         .import         popa, popax
28         .import         sys_bank, restore_bank
29         .export         _rs232_init, _rs232_params, _rs232_done, _rs232_get
30         .export         _rs232_put, _rs232_pause, _rs232_unpause, _rs232_status
31         .export         k_rs232
32
33         .include        "zeropage.inc"
34
35
36 ;----------------------------------------------------------------------------
37 ;
38 ; Global variables
39 ;
40
41 .bss
42 DropCnt:        .res    4       ; Number of bytes lost from rx buffer full
43 Initialized:    .res    1       ; Flag indicating driver is initialized
44 Stopped:        .res    1       ; Flow-stopped flag
45 RtsOff:         .res    1       ;
46 Errors:         .res    1       ; Number of bytes received in error, low byte
47 BaudCode:       .res    1       ; Current baud in effect
48
49 ; Segment, the RS232 buffers are in
50 BufferSeg               = 2
51
52 ; UART register offsets
53 RegData                 = 0     ; Data register
54 RegStatus               = 1     ; Status register
55 RegCommand              = 2     ; Command register
56 RegControl              = 3     ; Control register
57
58 ; Error codes. Beware: The codes must match the codes in the C header file
59 ErrNotInitialized       = $01
60 ErrBaudTooFast          = $02
61 ErrBaudNotAvail         = $03
62 ErrNoData               = $04
63 ErrOverflow             = $05
64
65
66 .code
67
68 ;----------------------------------------------------------------------------
69 ;
70 ; unsigned char __fastcall__ rs232_init (char hacked);
71 ; /* Initialize the serial port, install the interrupt handler. The parameter
72 ;  * must be true (non zero) for a hacked swiftlink and false (zero) otherwise.
73 ;  */
74 ;
75
76 _rs232_init:
77         bit     Initialized     ;** shut down if started
78         bpl     @L1
79         pha
80         jsr     _rs232_done
81         pla
82
83 ; Initialize buffers & control
84
85 @L1:    lda     #0
86         sta     RecvHead
87         sta     SendHead
88         sta     RecvTail
89         sta     SendTail
90         sta     Errors
91         sta     Stopped
92         lda     #255
93         sta     RecvFreeCnt
94         sta     SendFreeCnt
95
96 ; Set default to 2400-8N1, enable interrupts
97
98         jsr     sys_bank                ; Switch indirect to system bank
99
100         ldy     #RegData
101         lda     (acia),y
102         ldy     #RegStatus
103         lda     (acia),y
104         lda     #$18
105         ldy     #RegControl
106         sta     (acia),y
107
108         lda     #$01
109         sta     RtsOff
110         ora     #$08
111         ldy     #RegCommand
112         sta     (acia),y
113         lda     #$06
114         sta     BaudCode
115
116         jsr     restore_bank
117
118         lda     #$ff
119         sta     Initialized
120         lda     #$00
121         tax
122         rts
123
124 ;----------------------------------------------------------------------------
125 ;
126 ; unsigned char __fastcall__ rs232_params (unsigned char params, unsigned char parity);
127 ; /* Set the port parameters. Use a combination of the #defined values above. */
128 ;
129 ; Set communication parameters.
130 ;
131 ; baud rates              stops     word    |   parity
132 ; ---------------------   -----     -----   |   ---------
133 ; $00=50     $08=9600     $00=1     $00=8   |   $00=none
134 ; $01=110    $09=19200    $80=2     $20=7   |   $20=odd
135 ; $02=134.5  $0a=38400              $40=6   |   $60=even
136 ; $03=300    $0b=57600              $60=5   |   $A0=mark
137 ; $04=600    $0c=115200                     |   $E0=space
138 ; $05=1200   $0d=230400
139 ; $06=2400   $0e=future
140 ; $07=4800   $0f=future
141 ;
142
143 _rs232_params:
144         jsr     CheckInitialized        ;** check initialized
145         bcc     @L1
146         rts
147
148 ; Save new parity
149
150 @L1:    and     #%11100000
151         ora     #%00000001
152         sta     tmp2
153
154 ; Set baud/parameters
155
156         jsr     popa
157         sta     tmp1
158         and     #$0f
159         tax
160         lda     Bauds,x
161         cmp     #$ff
162         bne     @L5
163         lda     #ErrBaudNotAvail
164         bne     @L9
165
166 @L5:    jsr     sys_bank                ; Indirect segment to system bank
167         tax
168         lda     tmp1
169         and     #$0f
170         sta     BaudCode
171         lda     tmp1
172         and     #%11100000
173         ora     #%00010000
174         sta     tmp1
175         txa
176         and     #$0f
177         ora     tmp1
178         ldy     #RegControl
179         sta     (acia),y
180
181 ; Set new parity
182
183 @L7:    lda     tmp2
184         sta     RtsOff
185         ora     #%00001000
186         ldy     #RegCommand
187         sta     (acia),y
188         jsr     restore_bank            ; Restore indirect bank
189         lda     #0
190 @L9:    ldx     #0
191         rts
192
193 .rodata
194 Bauds:
195    .byte $01,$03,$04,$06,$07,$08,$0a,$0c,$0e,$0f,$ff,$ff,$ff,$ff,$ff,$ff
196      ;in:  0   1   2   3   4   5   6   7   8   9   a   b   c   d   e   f
197      ;baud50 110 134   3   6  12  24  48  96  19  38  57 115 230 exp exp
198      ;out masks: $0F=Baud, val$FF=err
199 .code
200
201 ;----------------------------------------------------------------------------
202 ;
203 ; unsigned char __fastcall__ rs232_done (void);
204 ; /* Close the port, deinstall the interrupt hander. You MUST call this function
205 ;  * before terminating the program, otherwise the machine may crash later. If
206 ;  * in doubt, install an exit handler using atexit(). The function will do
207 ;  * nothing, if it was already called.
208 ;  */
209 ;
210
211
212 _rs232_done:
213         bit     Initialized             ;** check initialized
214         bpl     @L9
215
216 ; Stop interrupts, drop DTR
217
218         lda     RtsOff
219         and     #%11100010
220         ora     #%00000010
221         ldx     IndReg
222         ldy     #$0F
223         sty     IndReg                  ; Set indirect to system bank
224         ldy     #RegCommand
225         sta     (acia),y
226         stx     IndReg                  ; Restore old indirect bank
227
228 ; Flag uninitialized
229
230 @L9:    lda     #$00
231         sta     Initialized
232         tax
233         rts
234
235 ;----------------------------------------------------------------------------
236 ;
237 ; unsigned char __fastcall__ rs232_get (char* B);
238 ; /* Get a character from the serial port. If no characters are available, the
239 ;  * function will return RS_ERR_NO_DATA, so this is not a fatal error.
240 ;  */
241 ;
242
243 _rs232_get:
244         jsr     CheckInitialized        ; Check if initialized
245         bcc     @L1
246         rts
247
248 ; Check for bytes to send
249
250 @L1:    sta     ptr1
251         stx     ptr1+1                  ; Store pointer to received char
252         ldx     SendFreeCnt
253         cpx     #$ff
254         beq     @L2
255         lda     #$00
256         jsr     TryToSend
257
258 ; Check for buffer empty
259
260 @L2:    lda     RecvFreeCnt
261         cmp     #$ff
262         bne     @L3
263         lda     #ErrNoData
264         ldx     #0
265         rts
266
267 ; Check for flow stopped & enough free: release flow control
268
269 @L3:    ldx     Stopped
270         beq     @L4
271         cmp     #63
272         bcc     @L4
273         lda     #$00
274         sta     Stopped
275         lda     RtsOff
276         ora     #%00001000
277         ldx     IndReg
278         ldy     #$0F                    ; Set indirect to system bank
279         sty     IndReg
280         ldy     #RegCommand
281         sta     (acia),y
282         stx     IndReg
283
284 ; Get byte from buffer
285
286 @L4:    ldx     IndReg
287         lda     #BufferSeg              ; Set indirect to buffer bank
288         sta     IndReg
289         ldy     RecvHead
290         lda     (RecvBuf),y
291         stx     IndReg                  ; Restore indirect bank
292         inc     RecvHead
293         inc     RecvFreeCnt
294         ldx     #$00
295         sta     (ptr1,x)
296         txa                             ; Return code = 0
297         rts
298
299 ;----------------------------------------------------------------------------
300 ;
301 ; unsigned char __fastcall__ rs232_put (char B);
302 ; /* Send a character via the serial port. There is a transmit buffer, but
303 ;  * transmitting is not done via interrupt. The function returns
304 ;  * RS_ERR_OVERFLOW if there is no space left in the transmit buffer.
305 ;  */
306 ;
307
308 _rs232_put:
309         jsr     CheckInitialized        ; Check initialized
310         bcc     @L1
311         rts
312
313 ; Try to send
314
315 @L1:    ldx     SendFreeCnt
316         cpx     #$ff
317         beq     @L2
318         pha
319         lda     #$00
320         jsr     TryToSend
321         pla
322
323 ; Put byte into send buffer & send
324
325 @L2:    ldx     SendFreeCnt
326         bne     @L3
327         lda     #ErrOverflow
328         ldx     #$00
329         rts
330           
331 ; There is enough room (character still in A)
332
333 @L3:    ldx     IndReg
334         ldy     #BufferSeg              ; Set indirect to buffer segment
335         sty     IndReg
336         ldy     SendTail
337         sta     (SendBuf),y
338         stx     IndReg                  ; Restore indirect bank
339         inc     SendTail
340         dec     SendFreeCnt
341         lda     #$ff
342         jsr     TryToSend
343         lda     #$00
344         tax
345         rts
346
347 ;----------------------------------------------------------------------------
348 ;
349 ; unsigned char __fastcall__ rs232_pause (void);
350 ; /* Assert flow control and disable interrupts. */
351 ;
352
353 _rs232_pause:
354 ; Check initialized
355         jsr     CheckInitialized
356         bcc     @L1
357         rts
358
359 ; Assert flow control
360
361 @L1:    lda     RtsOff
362         sta     Stopped
363         jsr     sys_bank                ; Set indirect to system bank
364         ldy     #RegCommand
365         sta     (acia),y
366
367 ; Delay for flow stop to be received
368
369         ldx     BaudCode
370         lda     PauseTimes,x
371         jsr     DelayMs
372
373 ; Stop rx interrupts
374
375         lda     RtsOff
376         ora     #$02
377         ldy     #RegCommand
378         sta     (acia),y
379         jsr     restore_bank            ; Restore indirect segment
380         lda     #0
381         tax
382         rts
383
384
385 .rodata
386 ; Delay times: 32 byte-receive times in milliseconds, or 100 max.
387 ; Formula = 320,000 / baud
388 PauseTimes:
389         .byte 100,100,100,100,100,100,100,067,034,017,009,006,003,002,001,001
390           ;in:  0   1   2   3   4   5   6   7   8   9   a   b   c   d   e   f
391           ;baud50 110 134   3   6  12  24  48  96  19  38  57 115 230 exp exp
392 .code
393
394 ;----------------------------------------------------------------------------
395 ;
396 ; unsigned char __fastcall__ rs232_unpause (void);
397 ; /* Re-enable interrupts and release flow control */
398 ;
399
400 _rs232_unpause:
401 ; Check initialized
402         jsr     CheckInitialized
403         bcc     @L1
404         rts
405
406 ; Re-enable rx interrupts & release flow control
407
408 @L1:    lda     #$00
409         sta     Stopped
410         lda     RtsOff
411         ora     #%00001000
412         ldx     IndReg
413         ldy     #$0F
414         sty     IndReg                  ; Set indirect to system bank
415         ldy     #RegCommand
416         sta     (acia),y
417         stx     IndReg                  ; Restore indirect bank
418
419 ; Poll for stalled char & exit
420
421         jsr     PollReceive
422         lda     #0
423         tax
424         rts
425
426 ;----------------------------------------------------------------------------
427 ;
428 ; unsigned char __fastcall__ rs232_status (unsigned char* status,
429 ;                                          unsigned char* errors);
430 ; /* Return the serial port status. */
431 ;
432
433 _rs232_status:
434         sta     ptr2
435         stx     ptr2+1
436         jsr     popax
437         sta     ptr1
438         stx     ptr1+1
439         jsr     CheckInitialized
440         bcs     @L9
441
442 ; Get status
443
444         ldx     IndReg                  ; Save indirect segment
445         lda     #$0F
446         sta     IndReg                  ; Set system bank as indirect segment
447         ldy     #RegStatus
448         lda     (acia),y                ; Read status register
449         stx     IndReg
450         ldy     #0
451         sta     (ptr1),y
452         jsr     PollReceive             ; bug-recovery hack
453         lda     Errors
454         ldy     #0
455         sta     (ptr2),y
456         tya
457         tax
458 @L9:    rts
459
460 ;----------------------------------------------------------------------------
461 ;
462 ; RS232 interrupt handler.
463 ; The RS232 handler will be called with the system bank as indirect bank
464 ; and all registers saved.
465 ;
466
467 k_rs232:
468         ldy     #RegStatus
469         lda     (acia),y                ; check for byte received
470         and     #$08
471         beq     @L9                     ; Nothing to receive
472         lda     (acia),y                ; check for receive errors
473         and     #$07
474         beq     @L1
475         inc     Errors
476 @L1:    ldy     #RegData
477         lda     (acia),y                ; get byte and put into receive buffer
478         ldx     RecvFreeCnt
479         beq     @L3
480         ldy     #BufferSeg
481         sty     IndReg
482         ldy     RecvTail
483         sta     (RecvBuf),y             ; Store received character
484         lda     #$0F
485         sta     IndReg                  ; Restore indirect segment
486         inc     RecvTail
487         dec     RecvFreeCnt
488         cpx     #33                     ; check for buffer space low
489         bcs     @L9
490
491 ; Assert flow control
492
493 @L2:    lda     RtsOff                  ; assert flow control if buffer space too low
494         ldy     #RegCommand
495         sta     (acia),y
496         sta     Stopped
497         rts
498
499 ; Drop this char
500
501 @L3:    inc     DropCnt+0               ;not time-critical
502         bne     @L9
503         inc     DropCnt+1
504         bne     @L9
505         inc     DropCnt+2
506         bne     @L9
507         inc     DropCnt+3
508 @L9:    rts
509
510
511 ;----------------------------------------------------------------------------
512 ;
513 ; CheckInitialized  -  internal check if initialized
514 ; Set carry and an error code if not initialized, clear carry and do not
515 ; change any registers if initialized.
516 ;
517
518 CheckInitialized:
519         bit     Initialized
520         bmi     @L1
521         lda     #ErrNotInitialized
522         ldx     #0
523         sec
524         rts
525
526 @L1:    clc
527         rts
528
529 ;----------------------------------------------------------------------------
530 ; Try to send a byte. Internal routine. A = TryHard
531
532 TryToSend:
533         sta     tmp1            ; Remember tryHard flag
534         ldx     IndReg          ; Save indirect segment
535         lda     #$0F
536         sta     IndReg          ; Set system segment as indirect segment
537 @L0:    lda     SendFreeCnt
538         cmp     #$ff
539         beq     @L3             ; Bail out
540
541 ; Check for flow stopped
542
543 @L1:    lda     Stopped
544         bne     @L3             ; Bail out
545
546 ; Check that the UART is ready to send
547
548 @L2:    ldy     #RegStatus
549         lda     (acia),y
550         and     #$10
551         bne     @L4
552         bit     tmp1            ; Keep trying if must try hard
553         bmi     @L0
554 @L3:    stx     IndReg          ; Restore indirect segment
555         rts
556
557 ; Send byte and try again
558
559 @L4:    lda     #BufferSeg
560         sta     IndReg
561         ldy     SendHead
562         lda     (SendBuf),y
563         ldy     #$0F
564         sty     IndReg
565         ldy     #RegData
566         sta     (acia),y
567         inc     SendHead
568         inc     SendFreeCnt
569         jmp     @L0
570
571
572 ;----------------------------------------------------------------------------
573 ;
574 ; PollReceive - poll for rx char
575 ;   This function is useful in odd cases where the 6551 has a character in
576 ;   it but it fails to raise an NMI.  It might be edge-triggering conditions?
577 ;   Actually, I'm not entirely sure that this condition can still arrise, but
578 ;   calling this function does no harm.
579 ;
580
581 PollReceive:
582         ldx     IndReg                  ; Save indirect segment
583         lda     #$0F
584         sta     IndReg                  ; Set system bank as indirect segment
585         ldy     #RegStatus
586         lda     (acia),y
587         and     #$08
588         beq     @L9
589         lda     (acia),y                ; Read a second time? ###
590         and     #$08
591         beq     @L9
592         ldy     #RegData
593         lda     (acia),y
594         ldy     RecvFreeCnt
595         beq     @L9
596         ldy     #BufferSeg
597         sty     IndReg
598         ldy     RecvTail
599         sta     (RecvBuf),y
600         inc     RecvTail
601         dec     RecvFreeCnt
602 @L9:    stx     IndReg                  ; Restore indirect segment
603         rts
604
605 ;----------------------------------------------------------------------------
606 ;
607 ;  DelayMs : delay for given number of milliseconds
608 ;    This implementation isn't very rigerous; it merely delays for the
609 ;    approximate number of clock cycles for the processor speed.
610 ;    Algorithm:
611 ;       repeat for number of milliseconds:
612 ;          repeat for number of MHz of cpu speed:
613 ;             delay for 1017 clock cycles
614 ;
615
616 DelayMs:                        ;( .A=milliseconds )
617 @L1:    ldy     #2              ; 2MHz
618 @L2:    ldx     #203            ;(2)
619 @L3:    dex                     ;(2)
620         bne     @L3             ;(3) // 1017 cycles
621         dey
622         bne     @L2
623         sec
624         sbc     #1
625         bne     @L1
626         rts
627
628 .end
629
630
631