]> git.sur5r.net Git - cc65/commitdiff
Added the rs232 module
authorcuz <cuz@b7a2c559-68d2-44c3-8de9-860c34a00d81>
Fri, 14 Sep 2001 20:52:54 +0000 (20:52 +0000)
committercuz <cuz@b7a2c559-68d2-44c3-8de9-860c34a00d81>
Fri, 14 Sep 2001 20:52:54 +0000 (20:52 +0000)
git-svn-id: svn://svn.cc65.org/cc65/trunk@926 b7a2c559-68d2-44c3-8de9-860c34a00d81

libsrc/cbm510/Makefile
libsrc/cbm510/rs232.s [new file with mode: 0644]

index b518bd010a6c7627320af86d218c706c92b65e8d..8b152550d010ce4579ea460adb0b5803a40a1764 100644 (file)
@@ -26,7 +26,8 @@ OBJS =        _scrsize.o      \
        kscnkey.o       \
        kudtim.o        \
        peeksys.o       \
-       pokesys.o
+       pokesys.o       \
+       rs232.o
 
 all:   $(OBJS)
 
diff --git a/libsrc/cbm510/rs232.s b/libsrc/cbm510/rs232.s
new file mode 100644 (file)
index 0000000..6e6451c
--- /dev/null
@@ -0,0 +1,631 @@
+;
+; SwiftLink/Turbo-232 v0.90 device driver, by Craig Bruce, 14-Apr-1998.
+;
+; This software is Public Domain.  It is in Buddy assembler format.
+;
+; This device driver uses the SwiftLink RS-232 Serial Cartridge, available from
+; Creative Micro Designs, Inc, and also supports the extensions of the Turbo232
+; Serial Cartridge.  Both devices are based on the 6551 ACIA chip.  It also
+; supports the "hacked" SwiftLink with a 1.8432 MHz crystal.
+;
+; The code assumes that the kernal + I/O are in context.  On the C128, call
+; it from Bank 15.  On the C64, don't flip out the Kernal unless a suitable
+; NMI catcher is put into the RAM under then Kernal.  For the SuperCPU, the
+; interrupt handling assumes that the 65816 is in 6502-emulation mode.
+;
+;--------------------------------------------------------------------------
+;
+; Adapted for the use with the cc65 runtime library by
+; Ullrich von Bassewitz (uz@musoftware.de) 02-May-1999.
+;
+; All external functions are C callable, the return value is an error code.
+;
+
+
+       .importzp       ptr1, ptr2, tmp1, tmp2
+       .importzp       acia, RecvBuf, SendBuf
+       .import         popa, popax
+       .import         sys_bank, restore_bank
+       .export         _rs232_init, _rs232_params, _rs232_done, _rs232_get
+       .export         _rs232_put, _rs232_pause, _rs232_unpause, _rs232_status
+       .export         k_rs232
+
+       .include        "zeropage.inc"
+
+
+;----------------------------------------------------------------------------
+;
+; Global variables
+;
+
+.bss
+DropCnt:       .res    4       ; Number of bytes lost from rx buffer full
+Initialized:   .res    1       ; Flag indicating driver is initialized
+Stopped:       .res    1       ; Flow-stopped flag
+RtsOff:                .res    1       ;
+Errors:                .res    1       ; Number of bytes received in error, low byte
+BaudCode:      .res    1       ; Current baud in effect
+
+; Segment, the RS232 buffers are in
+BufferSeg              = 2
+
+; UART register offsets
+RegData                = 0     ; Data register
+RegStatus              = 1     ; Status register
+RegCommand             = 2     ; Command register
+RegControl             = 3     ; Control register
+
+; Error codes. Beware: The codes must match the codes in the C header file
+ErrNotInitialized      = $01
+ErrBaudTooFast         = $02
+ErrBaudNotAvail        = $03
+ErrNoData              = $04
+ErrOverflow            = $05
+
+
+.code
+
+;----------------------------------------------------------------------------
+;
+; unsigned char __fastcall__ rs232_init (char hacked);
+; /* Initialize the serial port, install the interrupt handler. The parameter
+;  * must be true (non zero) for a hacked swiftlink and false (zero) otherwise.
+;  */
+;
+
+_rs232_init:
+               bit     Initialized     ;** shut down if started
+               bpl     @L1
+               pha
+               jsr     _rs232_done
+               pla
+
+; Initialize buffers & control
+
+@L1:   lda     #0
+       sta     RecvHead
+       sta     SendHead
+       sta     RecvTail
+       sta     SendTail
+       sta     Errors
+       sta     Stopped
+       lda     #255
+               sta     RecvFreeCnt
+       sta     SendFreeCnt
+
+; Set default to 2400-8N1, enable interrupts
+
+       jsr     sys_bank                ; Switch indirect to system bank
+
+       ldy     #RegData
+       lda     (acia),y
+       ldy     #RegStatus
+       lda     (acia),y
+       lda     #$18
+       ldy     #RegControl
+       sta     (acia),y
+
+       lda     #$01
+       sta     RtsOff
+       ora     #$08
+       ldy     #RegCommand
+               sta     (acia),y
+       lda     #$06
+       sta     BaudCode
+
+       jsr     restore_bank
+
+       lda     #$ff
+       sta     Initialized
+       lda     #$00
+       tax
+       rts
+
+;----------------------------------------------------------------------------
+;
+; unsigned char __fastcall__ rs232_params (unsigned char params, unsigned char parity);
+; /* Set the port parameters. Use a combination of the #defined values above. */
+;
+; Set communication parameters.
+;
+; baud rates              stops     word    |   parity
+; ---------------------   -----     -----   |   ---------
+; $00=50     $08=9600     $00=1     $00=8   |   $00=none
+; $01=110    $09=19200    $80=2     $20=7   |   $20=odd
+; $02=134.5  $0a=38400              $40=6   |   $60=even
+; $03=300    $0b=57600              $60=5   |   $A0=mark
+; $04=600    $0c=115200                     |   $E0=space
+; $05=1200   $0d=230400
+; $06=2400   $0e=future
+; $07=4800   $0f=future
+;
+
+_rs232_params:
+               jsr     CheckInitialized        ;** check initialized
+               bcc     @L1
+       rts
+
+; Save new parity
+
+@L1:           and     #%11100000
+               ora     #%00000001
+               sta     tmp2
+
+; Set baud/parameters
+
+               jsr     popa
+               sta     tmp1
+               and     #$0f
+               tax
+               lda     Bauds,x
+               cmp     #$ff
+               bne     @L5
+               lda     #ErrBaudNotAvail
+               bne     @L9
+
+@L5:           jsr     sys_bank                ; Indirect segment to system bank
+               tax
+               lda     tmp1
+               and     #$0f
+               sta     BaudCode
+               lda     tmp1
+               and     #%11100000
+               ora     #%00010000
+               sta     tmp1
+               txa
+               and     #$0f
+               ora     tmp1
+               ldy     #RegControl
+               sta     (acia),y
+
+; Set new parity
+
+@L7:           lda     tmp2
+               sta     RtsOff
+               ora     #%00001000
+               ldy     #RegCommand
+               sta     (acia),y
+               jsr     restore_bank            ; Restore indirect bank
+               lda     #0
+@L9:           ldx     #0
+               rts
+
+.rodata
+Bauds:
+   .byte $01,$03,$04,$06,$07,$08,$0a,$0c,$0e,$0f,$ff,$ff,$ff,$ff,$ff,$ff
+     ;in:  0   1   2   3   4   5   6   7   8   9   a   b   c   d   e   f
+     ;baud50 110 134   3   6  12  24  48  96  19  38  57 115 230 exp exp
+     ;out masks: $0F=Baud, val$FF=err
+.code
+
+;----------------------------------------------------------------------------
+;
+; unsigned char __fastcall__ rs232_done (void);
+; /* Close the port, deinstall the interrupt hander. You MUST call this function
+;  * before terminating the program, otherwise the machine may crash later. If
+;  * in doubt, install an exit handler using atexit(). The function will do
+;  * nothing, if it was already called.
+;  */
+;
+
+
+_rs232_done:
+       bit     Initialized             ;** check initialized
+               bpl     @L9
+
+; Stop interrupts, drop DTR
+
+       lda     RtsOff
+       and     #%11100010
+       ora     #%00000010
+       ldx     IndReg
+       ldy     #$0F
+       sty     IndReg                  ; Set indirect to system bank
+       ldy     #RegCommand
+       sta     (acia),y
+       stx     IndReg                  ; Restore old indirect bank
+
+; Flag uninitialized
+
+@L9:           lda     #$00
+       sta     Initialized
+       tax
+       rts
+
+;----------------------------------------------------------------------------
+;
+; unsigned char __fastcall__ rs232_get (char* B);
+; /* Get a character from the serial port. If no characters are available, the
+;  * function will return RS_ERR_NO_DATA, so this is not a fatal error.
+;  */
+;
+
+_rs232_get:
+       jsr     CheckInitialized        ; Check if initialized
+       bcc     @L1
+       rts
+
+; Check for bytes to send
+
+@L1:   sta     ptr1
+       stx     ptr1+1                  ; Store pointer to received char
+       ldx     SendFreeCnt
+       cpx     #$ff
+       beq     @L2
+       lda     #$00
+       jsr     TryToSend
+
+; Check for buffer empty
+
+@L2:   lda     RecvFreeCnt
+       cmp     #$ff
+       bne     @L3
+       lda     #ErrNoData
+       ldx     #0
+       rts
+
+; Check for flow stopped & enough free: release flow control
+
+@L3:   ldx     Stopped
+       beq     @L4
+       cmp     #63
+       bcc     @L4
+       lda     #$00
+       sta     Stopped
+       lda     RtsOff
+       ora     #%00001000
+       ldx     IndReg
+       ldy     #$0F                    ; Set indirect to system bank
+       sty     IndReg
+       ldy     #RegCommand
+       sta     (acia),y
+       stx     IndReg
+
+; Get byte from buffer
+
+@L4:   ldx     IndReg
+       lda     #BufferSeg              ; Set indirect to buffer bank
+       sta     IndReg
+       ldy     RecvHead
+       lda     (RecvBuf),y
+       stx     IndReg                  ; Restore indirect bank
+       inc     RecvHead
+       inc     RecvFreeCnt
+               ldx     #$00
+       sta     (ptr1,x)
+               txa                             ; Return code = 0
+       rts
+
+;----------------------------------------------------------------------------
+;
+; unsigned char __fastcall__ rs232_put (char B);
+; /* Send a character via the serial port. There is a transmit buffer, but
+;  * transmitting is not done via interrupt. The function returns
+;  * RS_ERR_OVERFLOW if there is no space left in the transmit buffer.
+;  */
+;
+
+_rs232_put:
+       jsr     CheckInitialized        ; Check initialized
+       bcc     @L1
+       rts
+
+; Try to send
+
+@L1:   ldx     SendFreeCnt
+       cpx     #$ff
+       beq     @L2
+       pha
+       lda     #$00
+       jsr     TryToSend
+       pla
+
+; Put byte into send buffer & send
+
+@L2:   ldx     SendFreeCnt
+       bne     @L3
+       lda     #ErrOverflow
+       ldx     #$00
+       rts
+
+; There is enough room (character still in A)
+
+@L3:   ldx     IndReg
+               ldy     #BufferSeg              ; Set indirect to buffer segment
+       sty     IndReg
+       ldy     SendTail
+       sta     (SendBuf),y
+       stx     IndReg                  ; Restore indirect bank
+       inc     SendTail
+       dec     SendFreeCnt
+       lda     #$ff
+       jsr     TryToSend
+       lda     #$00
+       tax
+               rts
+
+;----------------------------------------------------------------------------
+;
+; unsigned char __fastcall__ rs232_pause (void);
+; /* Assert flow control and disable interrupts. */
+;
+
+_rs232_pause:
+; Check initialized
+       jsr     CheckInitialized
+       bcc     @L1
+       rts
+
+; Assert flow control
+
+@L1:   lda     RtsOff
+       sta     Stopped
+       jsr     sys_bank                ; Set indirect to system bank
+       ldy     #RegCommand
+       sta     (acia),y
+
+; Delay for flow stop to be received
+
+       ldx     BaudCode
+       lda     PauseTimes,x
+       jsr     DelayMs
+
+; Stop rx interrupts
+
+       lda     RtsOff
+       ora     #$02
+       ldy     #RegCommand
+       sta     (acia),y
+               jsr     restore_bank            ; Restore indirect segment
+       lda     #0
+       tax
+       rts
+
+
+.rodata
+; Delay times: 32 byte-receive times in milliseconds, or 100 max.
+; Formula = 320,000 / baud
+PauseTimes:
+               .byte 100,100,100,100,100,100,100,067,034,017,009,006,003,002,001,001
+          ;in:  0   1   2   3   4   5   6   7   8   9   a   b   c   d   e   f
+          ;baud50 110 134   3   6  12  24  48  96  19  38  57 115 230 exp exp
+.code
+
+;----------------------------------------------------------------------------
+;
+; unsigned char __fastcall__ rs232_unpause (void);
+; /* Re-enable interrupts and release flow control */
+;
+
+_rs232_unpause:
+; Check initialized
+       jsr     CheckInitialized
+       bcc     @L1
+       rts
+
+; Re-enable rx interrupts & release flow control
+
+@L1:   lda     #$00
+       sta     Stopped
+       lda     RtsOff
+       ora     #%00001000
+       ldx     IndReg
+       ldy     #$0F
+       sty     IndReg                  ; Set indirect to system bank
+       ldy     #RegCommand
+       sta     (acia),y
+       stx     IndReg                  ; Restore indirect bank
+
+; Poll for stalled char & exit
+
+       jsr     PollReceive
+       lda     #0
+       tax
+       rts
+
+;----------------------------------------------------------------------------
+;
+; unsigned char __fastcall__ rs232_status (unsigned char* status,
+;                                         unsigned char* errors);
+; /* Return the serial port status. */
+;
+
+_rs232_status:
+       sta     ptr2
+       stx     ptr2+1
+       jsr     popax
+       sta     ptr1
+       stx     ptr1+1
+       jsr     CheckInitialized
+               bcs     @L9
+
+; Get status
+
+       ldx     IndReg                  ; Save indirect segment
+       lda     #$0F
+       sta     IndReg                  ; Set system bank as indirect segment
+       ldy     #RegStatus
+       lda     (acia),y                ; Read status register
+       stx     IndReg
+       ldy     #0
+       sta     (ptr1),y
+       jsr     PollReceive             ; bug-recovery hack
+       lda     Errors
+       ldy     #0
+               sta     (ptr2),y
+               tya
+       tax
+@L9:   rts
+
+;----------------------------------------------------------------------------
+;
+; RS232 interrupt handler.
+; The RS232 handler will be called with        the system bank as indirect bank
+; and all registers saved.
+;
+
+k_rs232:
+       ldy     #RegStatus
+               lda     (acia),y                ; check for byte received
+       and     #$08
+       beq     @L9                     ; Nothing to receive
+               lda     (acia),y                ; check for receive errors
+       and     #$07
+               beq     @L1
+       inc     Errors
+@L1:           ldy     #RegData
+       lda     (acia),y                ; get byte and put into receive buffer
+       ldx     RecvFreeCnt
+       beq     @L3
+       ldy     #BufferSeg
+       sty     IndReg
+               ldy     RecvTail
+       sta     (RecvBuf),y             ; Store received character
+       lda     #$0F
+       sta     IndReg                  ; Restore indirect segment
+       inc     RecvTail
+       dec     RecvFreeCnt
+       cpx     #33                     ; check for buffer space low
+               bcs     @L9
+
+; Assert flow control
+
+@L2:   lda     RtsOff                  ; assert flow control if buffer space too low
+       ldy     #RegCommand
+       sta     (acia),y
+       sta     Stopped
+       rts
+
+; Drop this char
+
+@L3:   inc     DropCnt+0               ;not time-critical
+       bne     @L9
+       inc     DropCnt+1
+       bne     @L9
+       inc     DropCnt+2
+       bne     @L9
+       inc     DropCnt+3
+@L9:           rts
+
+
+;----------------------------------------------------------------------------
+;
+; CheckInitialized  -  internal check if initialized
+; Set carry and an error code if not initialized, clear carry and do not
+; change any registers if initialized.
+;
+
+CheckInitialized:
+       bit     Initialized
+       bmi     @L1
+       lda     #ErrNotInitialized
+       ldx     #0
+       sec
+       rts
+
+@L1:   clc
+       rts
+
+;----------------------------------------------------------------------------
+; Try to send a byte. Internal routine. A = TryHard
+
+TryToSend:
+       sta     tmp1            ; Remember tryHard flag
+       ldx     IndReg          ; Save indirect segment
+       lda     #$0F
+       sta     IndReg          ; Set system segment as indirect segment
+@L0:           lda     SendFreeCnt
+       cmp     #$ff
+       beq     @L3             ; Bail out
+
+; Check for flow stopped
+
+@L1:   lda     Stopped
+               bne     @L3             ; Bail out
+
+; Check that the UART is ready to send
+
+@L2:           ldy     #RegStatus
+       lda     (acia),y
+       and     #$10
+       bne     @L4
+       bit     tmp1            ; Keep trying if must try hard
+               bmi     @L0
+@L3:           stx     IndReg          ; Restore indirect segment
+       rts
+
+; Send byte and try again
+
+@L4:           lda     #BufferSeg
+       sta     IndReg
+       ldy     SendHead
+       lda     (SendBuf),y
+       ldy     #$0F
+       sty     IndReg
+       ldy     #RegData
+       sta     (acia),y
+       inc     SendHead
+       inc     SendFreeCnt
+       jmp     @L0
+
+
+;----------------------------------------------------------------------------
+;
+; PollReceive - poll for rx char
+;   This function is useful in odd cases where the 6551 has a character in
+;   it but it fails to raise an NMI.  It might be edge-triggering conditions?
+;   Actually, I'm not entirely sure that this condition can still arrise, but
+;   calling this function does no harm.
+;
+
+PollReceive:
+       ldx     IndReg                  ; Save indirect segment
+       lda     #$0F
+       sta     IndReg                  ; Set system bank as indirect segment
+       ldy     #RegStatus
+       lda     (acia),y
+       and     #$08
+       beq     @L9
+       lda     (acia),y                ; Read a second time? ###
+       and     #$08
+       beq     @L9
+       ldy     #RegData
+       lda     (acia),y
+               ldy     RecvFreeCnt
+       beq     @L9
+       ldy     #BufferSeg
+       sty     IndReg
+               ldy     RecvTail
+       sta     (RecvBuf),y
+       inc     RecvTail
+       dec     RecvFreeCnt
+@L9:   stx     IndReg                  ; Restore indirect segment
+       rts
+
+;----------------------------------------------------------------------------
+;
+;  DelayMs : delay for given number of milliseconds
+;    This implementation isn't very rigerous; it merely delays for the
+;    approximate number of clock cycles for the processor speed.
+;    Algorithm:
+;       repeat for number of milliseconds:
+;          repeat for number of MHz of cpu speed:
+;             delay for 1017 clock cycles
+;
+
+DelayMs:                       ;( .A=milliseconds )
+@L1:           ldy     #1              ; 1MHz
+@L2:           ldx     #203            ;(2)
+@L3:           dex                     ;(2)
+       bne     @L3             ;(3) // 1017 cycles
+       dey
+       bne     @L2
+       sec
+       sbc     #1
+       bne     @L1
+       rts
+
+.end
+
+
+