]> git.sur5r.net Git - cc65/blob - libsrc/c128/rs232.s
removed objects shared with common conio
[cc65] / libsrc / c128 / 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         .import         popa, popax
27         .export         _rs232_init, _rs232_params, _rs232_done, _rs232_get
28         .export         _rs232_put, _rs232_pause, _rs232_unpause, _rs232_status
29
30         .include        "c128.inc"
31         .include        "rs232.inc"
32
33
34 NmiExit = $ff33     ;exit address for nmi
35
36 ACIA    = $DE00
37
38
39
40 ;----------------------------------------------------------------------------
41 ;
42 ; Global variables
43 ;
44
45 .bss
46 DropCnt:        .res    4       ; Number of bytes lost from rx buffer full
47 Initialized:    .res    1       ; Flag indicating driver is initialized
48 Stopped:        .res    1       ; Flow-stopped flag
49 RtsOff:         .res    1       ;
50 Errors:         .res    1       ; Number of bytes received in error, low byte
51 Turbo232:       .res    1       ; Flag indicating turbo-232
52 HackedFlag:     .res    1       ; Flag indicating hacked-crystal swiftlink
53 CpuSpeed:       .res    1       ; In MHz
54 RecvHead:       .res    1       ; Head of receive buffer
55 RecvTail:       .res    1       ; Tail of receive buffer
56 RecvFreeCnt:    .res    1       ; Number of bytes in receive buffer
57 SendHead:       .res    1       ; Head of send buffer
58 SendTail:       .res    1       ; Tail of send buffer
59 SendFreeCnt:    .res    1       ; Number of bytes free in send buffer
60 BaudCode:       .res    1       ; Current baud in effect
61
62 ; Send and receive buffers: 256 bytes each
63 RecvBuf:        .res    256
64 SendBuf:        .res    256
65
66 .data
67 NmiContinue:    .byte   $4c     ; JMP instruction for NMI save -- continue
68 NmiSave:        .res    2       ; normal NMI handler
69
70 ; Switftlink register offsets
71 RegData                 = 0     ; Data register
72 RegStatus               = 1     ; Status register
73 RegCommand              = 2     ; Command register
74 RegControl              = 3     ; Control register
75 RegClock                = 7     ; Turbo232 external baud-rate generator
76
77
78
79 .code
80
81 ;----------------------------------------------------------------------------
82 ;
83 ; unsigned char __fastcall__ rs232_init (char hacked);
84 ; /* Initialize the serial port, install the interrupt handler. The parameter
85 ;  * must be true (non zero) for a hacked swiftlink and false (zero) otherwise.
86 ;  */
87 ;
88
89 _rs232_init:
90         bit     Initialized     ;** shut down if started
91         bpl     @L1
92         pha
93         jsr     _rs232_done
94         pla
95
96 ;** set hacked-crystal
97
98 @L1:    sta     HackedFlag
99
100 ;** check for turbo-232
101
102         lda     #$00
103         sta     ACIA+RegControl
104         tax
105         lda     ACIA+RegClock
106         beq     @L3
107         dex
108 @L3:    stx     Turbo232
109
110 ;** get C128 cpu speed
111
112         lda     $d030
113         and     #$01
114         clc
115         adc     #1
116         sta     CpuSpeed
117
118 ;** check for super-cpu at 20 MHz
119
120         bit     SCPU_Detect
121         bmi     @L4
122         bit     $d0b8
123         bvs     @L4
124         lda     #20
125         sta     CpuSpeed
126
127 ;** initialize buffers & control
128
129 @L4:    lda     #0
130         sta     RecvHead
131         sta     SendHead
132         sta     RecvTail
133         sta     SendTail
134         sta     Errors
135         sta     Stopped
136         lda     #255
137         sta     RecvFreeCnt
138         sta     SendFreeCnt
139
140 ;** set up nmi's
141
142         lda     NMIVec
143         ldy     NMIVec+1
144         sta     NmiSave+0
145         sty     NmiSave+1
146         lda     #<NmiHandler
147         ldy     #>NmiHandler
148         sta     NMIVec
149         sty     NMIVec+1
150
151 ;** set default to 2400-8N1, enable interrupts
152
153         lda     ACIA+RegData
154         lda     ACIA+RegStatus
155         lda     #$18
156         bit     HackedFlag
157         bpl     @L5
158         lda     #$1a
159 @L5:    sta     ACIA+RegControl
160
161         lda     #$01
162         sta     RtsOff
163         ora     #$08
164         sta     ACIA+RegCommand
165         lda     #$06
166         sta     BaudCode
167
168 ; Done
169
170         ldx     #$ff
171         stx     Initialized
172         inx                             ; X = 0
173         txa                             ; A = 0
174         rts
175
176 ;----------------------------------------------------------------------------
177 ;
178 ; unsigned char __fastcall__ rs232_params (unsigned char params, unsigned char parity);
179 ; /* Set the port parameters. Use a combination of the #defined values above. */
180 ;
181 ; Set communication parameters.
182 ;
183 ; baud rates              stops     word    |   parity
184 ; ---------------------   -----     -----   |   ---------
185 ; $00=50     $08=9600     $00=1     $00=8   |   $00=none
186 ; $01=110    $09=19200    $80=2     $20=7   |   $20=odd
187 ; $02=134.5  $0a=38400              $40=6   |   $60=even
188 ; $03=300    $0b=57600              $60=5   |   $A0=mark
189 ; $04=600    $0c=115200                     |   $E0=space
190 ; $05=1200   $0d=230400
191 ; $06=2400   $0e=future
192 ; $07=4800   $0f=future
193 ;
194
195 _rs232_params:
196         bit     Initialized
197         bmi     @L1
198         jmp     NotInitialized          ; Return an error code
199
200 ; Save new parity
201
202 @L1:    and     #%11100000
203         ora     #%00000001
204         sta     tmp2
205
206 ; Check cpu speed against baud rate
207
208         jsr     popa
209         sta     tmp1
210         and     #$0f
211         cmp     #$0c
212         bcc     @L3
213         ldx     CpuSpeed
214         cpx     #1+1
215         bcc     @L2
216         cmp     #$0c
217         beq     @L3
218         cpx     #4
219         bcs     @L3
220 @L2:    lda     #RS_ERR_BAUD_TOO_FAST
221         bne     @L9
222
223 ; Set baud/parameters
224
225 @L3:    lda     tmp1
226         and     #$0f
227         tax
228         lda     NormBauds,x
229         bit     HackedFlag
230         bpl     @L4
231         lda     HackBauds,x
232 @L4:    cmp     #$ff
233         bne     @L5
234         lda     #RS_ERR_BAUD_NOT_AVAIL
235         bne     @L9
236
237 @L5:    tax
238         and     #$30
239         beq     @L6
240         bit     Turbo232
241         bmi     @L6
242         lda     #RS_ERR_BAUD_NOT_AVAIL
243         bne     @L9
244
245 @L6:    lda     tmp1
246         and     #$0f
247         sta     BaudCode
248         lda     tmp1
249         and     #%11100000
250         ora     #%00010000
251         sta     tmp1
252         txa
253         and     #$0f
254         ora     tmp1
255         sta     ACIA+RegControl
256         txa
257         and     #%00110000
258         beq     @L7
259         lsr
260         lsr
261         lsr
262         lsr
263         eor     #%00000011
264         sta     ACIA+RegClock
265
266 ; Set new parity
267
268 @L7:    lda     tmp2
269         sta     RtsOff
270         ora     #%00001000
271         sta     ACIA+RegCommand
272         lda     #0
273 @L9:    ldx     #0
274         rts
275
276 .rodata
277
278 NormBauds:
279    .byte $ff,$ff,$ff,$05,$06,$07,$08,$0a,$0c,$0e,$0f,$10,$20,$30,$ff,$ff
280 HackBauds:
281    .byte $01,$03,$04,$06,$07,$08,$0a,$0c,$0e,$0f,$ff,$ff,$00,$ff,$ff,$ff
282      ;in:  0   1   2   3   4   5   6   7   8   9   a   b   c   d   e   f
283      ;baud50 110 134   3   6  12  24  48  96  19  38  57 115 230 exp exp
284      ;out masks: $0F=Baud, val$FF=err
285      ;           $30=t232ExtBaud: $00=none, $10=57.6, $20=115.2, $30=230.4
286 .code
287
288 ;----------------------------------------------------------------------------
289 ;
290 ; unsigned char __fastcall__ rs232_done (void);
291 ; /* Close the port, deinstall the interrupt hander. You MUST call this function
292 ;  * before terminating the program, otherwise the machine may crash later. If
293 ;  * in doubt, install an exit handler using atexit(). The function will do
294 ;  * nothing, if it was already called.
295 ;  */
296 ;
297
298
299 _rs232_done:
300         bit     Initialized             ;** check initialized
301         bpl     @L9
302
303 ; Stop interrupts, drop DTR
304
305         lda     RtsOff
306         and     #%11100010
307         ora     #%00000010
308         sta     ACIA+RegCommand
309
310 ; Restore NMI vector
311
312         lda     NmiSave+0
313         ldy     NmiSave+1
314         sta     NMIVec
315         sty     NMIVec+1
316
317 ; Flag uninitialized
318
319 @L9:    lda     #$00
320         sta     Initialized
321         tax
322         rts
323
324 ;----------------------------------------------------------------------------
325 ;
326 ; unsigned char __fastcall__ rs232_get (char* B);
327 ; /* Get a character from the serial port. If no characters are available, the
328 ;  * function will return RS_ERR_NO_DATA, so this is not a fatal error.
329 ;  */
330 ;
331
332 _rs232_get:
333         bit     Initialized
334         bpl     NotInitialized          ; Jump if not initialized
335
336 ; Check for bytes to send
337
338 @L1:    sta     ptr1
339         stx     ptr1+1                  ; Store pointer to received char
340         ldx     SendFreeCnt
341         cpx     #$ff
342         beq     @L2
343         lda     #$00
344         jsr     TryToSend
345
346 ; Check for buffer empty
347
348 @L2:    lda     RecvFreeCnt
349         cmp     #$ff
350         bne     @L3
351         lda     #RS_ERR_NO_DATA
352         ldx     #0
353         rts
354
355 ; Check for flow stopped & enough free: release flow control
356
357 @L3:    ldx     Stopped
358         beq     @L4
359         cmp     #63
360         bcc     @L4
361         lda     #$00
362         sta     Stopped
363         lda     RtsOff
364         ora     #%00001000
365         sta     ACIA+RegCommand
366
367 ; Get byte from buffer
368
369 @L4:    ldx     RecvHead
370         lda     RecvBuf,x
371         inc     RecvHead
372         inc     RecvFreeCnt
373         ldx     #$00
374         sta     (ptr1,x)
375         txa                             ; Return code = 0
376         rts
377
378 ;----------------------------------------------------------------------------
379 ;
380 ; RS232 module not initialized
381
382 NotInitialized:
383         lda     #<RS_ERR_NOT_INITIALIZED
384         ldx     #>RS_ERR_NOT_INITIALIZED
385         rts
386
387 ;----------------------------------------------------------------------------
388 ;
389 ; unsigned char __fastcall__ rs232_put (char B);
390 ; /* Send a character via the serial port. There is a transmit buffer, but
391 ;  * transmitting is not done via interrupt. The function returns
392 ;  * RS_ERR_OVERFLOW if there is no space left in the transmit buffer.
393 ;  */
394 ;
395
396 _rs232_put:
397         bit     Initialized
398         bpl     NotInitialized          ; Jump if not initialized
399
400 ; Try to send
401
402 @L1:    ldx     SendFreeCnt
403         cpx     #$ff
404         beq     @L2
405         pha
406         lda     #$00
407         jsr     TryToSend
408         pla
409
410 ; Put byte into send buffer & send
411
412 @L2:    ldx     SendFreeCnt
413         bne     @L3
414         lda     #RS_ERR_OVERFLOW
415         ldx     #$00
416         rts
417
418 @L3:    ldx     SendTail
419         sta     SendBuf,x
420         inc     SendTail
421         dec     SendFreeCnt
422         lda     #$ff
423         jsr     TryToSend
424         lda     #$00
425         tax
426         rts
427
428 ;----------------------------------------------------------------------------
429 ;
430 ; unsigned char __fastcall__ rs232_pause (void);
431 ; /* Assert flow control and disable interrupts. */
432 ;
433
434 _rs232_pause:
435         bit     Initialized
436         bpl     NotInitialized          ; Jump if not initialized
437
438 ; Assert flow control
439
440 @L1:    lda     RtsOff
441         sta     Stopped
442         sta     ACIA+RegCommand
443
444 ; Delay for flow stop to be received
445
446         ldx     BaudCode
447         lda     PauseTimes,x
448         jsr     DelayMs
449
450 ; Stop rx interrupts
451
452         lda     RtsOff
453         ora     #$02
454         sta     ACIA+RegCommand
455         lda     #0
456         tax
457         rts
458
459
460 .rodata
461 ; Delay times: 32 byte-receive times in milliseconds, or 100 max.
462 ; Formula = 320,000 / baud
463 PauseTimes:
464         .byte 100,100,100,100,100,100,100,067,034,017,009,006,003,002,001,001
465           ;in:  0   1   2   3   4   5   6   7   8   9   a   b   c   d   e   f
466           ;baud50 110 134   3   6  12  24  48  96  19  38  57 115 230 exp exp
467 .code
468
469 ;----------------------------------------------------------------------------
470 ;
471 ; unsigned char __fastcall__ rs232_unpause (void);
472 ; /* Re-enable interrupts and release flow control */
473 ;
474
475 _rs232_unpause:
476         bit     Initialized
477         bpl     NotInitialized          ; Jump if not initialized
478
479 ; Re-enable rx interrupts & release flow control
480
481 @L1:    lda     #$00
482         sta     Stopped
483         lda     RtsOff
484         ora     #%00001000
485         sta     ACIA+RegCommand
486
487 ; Poll for stalled char & exit
488
489         jsr     PollReceive
490         lda     #0
491         tax
492         rts
493
494 ;----------------------------------------------------------------------------
495 ;
496 ; unsigned char __fastcall__ rs232_status (unsigned char* status,
497 ;                                          unsigned char* errors);
498 ; /* Return the serial port status. */
499 ;
500
501 _rs232_status:
502         sta     ptr2
503         stx     ptr2+1
504         jsr     popax
505         sta     ptr1
506         stx     ptr1+1
507         bit     Initialized
508         bmi     @L1
509         jmp     NotInitialized
510
511 ; Get status
512
513 @L1:    lda     ACIA+RegStatus
514         ldy     #0
515         sta     (ptr1),y
516         jsr     PollReceive             ; bug-recovery hack
517         lda     Errors
518         sta     (ptr2),y
519         tya
520         tax
521 @L9:    rts
522
523 ;----------------------------------------------------------------------------
524 ;
525 ; NMI handler
526 ; C128 NMI overhead=76 cycles: int=7, maxLatency=6, ROMenter=33, ROMexit=30
527 ; C64  NMI overhead=76 cycles: int=7, maxLatency=6, ROMenter=34, ROMexit=29
528 ;
529 ; timing: normal=76+43+9=128 cycles, assertFlow=76+52+9=137 cycles
530 ;
531 ; C128 @ 115.2k: 177 cycles avail (fast)
532 ; C64  @  57.6k: 177 cycles avail, worstAvail=177-43? = 134
533 ; SCPU @ 230.4k: 868 cycles avail: for a joke!
534 ;
535 ; Because of the C128 banking, the NMI handler must go into the non banked
536 ; memory, since the ROM NMI entry point will switch to a configuration where
537 ; only the lowest 16K of RAM are visible. We will place the NMI handler into
538 ; the LOWCODE segment and map this segment into the lower 16K in the linker
539 ; config.
540
541 .segment        "LOWCODE"
542
543 NmiHandler:
544         lda     #MMU_CFG_CC65           ;(2)
545         sta     MMU_CR                  ;(4)
546         lda     ACIA+RegStatus          ;(4) ;status ;check for byte received
547         and     #$08                    ;(2)
548         beq     @L9                     ;(2*)
549         lda     ACIA+RegStatus          ;(4) opt ;status ;check for receive errors
550         and     #$07                    ;(2) opt
551         beq     @L1                     ;(3*)opt
552         inc     Errors                  ;(5^)opt
553 @L1:    lda     ACIA+RegData            ;(4) ;data  ;get byte and put into receive buffer
554         ldy     RecvTail                ;(4)
555         ldx     RecvFreeCnt             ;(4)
556         beq     @L3                     ;(2*)
557         sta     RecvBuf,y               ;(5)
558         inc     RecvTail                ;(6)
559         dec     RecvFreeCnt             ;(6)
560         cpx     #33                     ;(2)  ;check for buffer space low
561         bcc     @L2                     ;(2*)
562         jmp     NmiExit                 ;(3)
563
564 ; Assert flow control
565
566 @L2:    lda     RtsOff                  ;(3)  ;assert flow control if buffer space too low
567         sta     ACIA+RegCommand         ;(4) ;command
568         sta     Stopped                 ;(3)
569         jmp     NmiExit                 ;(3)
570
571 ; Drop this char
572
573 @L3:    inc     DropCnt+0               ;not time-critical
574         bne     @L4
575         inc     DropCnt+1
576         bne     @L4
577         inc     DropCnt+2
578         bne     @L4
579         inc     DropCnt+3
580 @L4:    jmp     NmiExit
581
582 @L9:    jmp     NmiContinue
583
584
585 .code
586
587 ;----------------------------------------------------------------------------
588 ; Try to send a byte. Internal routine. A = TryHard
589
590 TryToSend:
591         sta     tmp1            ; Remember tryHard flag
592 @L0:    lda     SendFreeCnt
593         cmp     #$ff
594         beq     @L3             ; Bail out
595
596 ; Check for flow stopped
597
598 @L1:    lda     Stopped
599         bne     @L3             ; Bail out
600
601 ;** check that swiftlink is ready to send
602
603 @L2:    lda     ACIA+RegStatus
604         and     #$10
605         bne     @L4
606         bit     tmp1            ;keep trying if must try hard
607         bmi     @L0
608 @L3:    rts
609
610 ;** send byte and try again
611
612 @L4:    ldx     SendHead
613         lda     SendBuf,x
614         sta     ACIA+RegData
615         inc     SendHead
616         inc     SendFreeCnt
617         jmp     @L0
618
619 ;----------------------------------------------------------------------------
620 ;
621 ; PollReceive - poll for rx char
622 ;   This function is useful in odd cases where the 6551 has a character in
623 ;   it but it fails to raise an NMI.  It might be edge-triggering conditions?
624 ;   Actually, I'm not entirely sure that this condition can still arrise, but
625 ;   calling this function does no harm.
626 ;
627
628 PollReceive:
629         lda     #$08
630         and     ACIA+RegStatus
631         beq     @L9
632         and     ACIA+RegStatus
633         beq     @L9
634         lda     ACIA+RegData
635         ldx     RecvFreeCnt
636         beq     @L9
637         ldx     RecvTail
638         sta     RecvBuf,x
639         inc     RecvTail
640         dec     RecvFreeCnt
641 @L9:    rts
642
643 ;----------------------------------------------------------------------------
644 ;
645 ;  DelayMs : delay for given number of milliseconds
646 ;    This implementation isn't very rigerous; it merely delays for the
647 ;    approximate number of clock cycles for the processor speed.
648 ;    Algorithm:
649 ;       repeat for number of milliseconds:
650 ;          repeat for number of MHz of cpu speed:
651 ;             delay for 1017 clock cycles
652 ;
653
654 DelayMs:                        ;( .A=milliseconds )
655 @L1:    ldy     CpuSpeed
656 @L2:    ldx     #203            ;(2)
657 @L3:    dex                     ;(2)
658         bne     @L3             ;(3) // 1017 cycles
659         dey
660         bne     @L2
661         sec
662         sbc     #1
663         bne     @L1
664         rts
665
666 .end
667
668
669