From 6d97fa1383c1377c775ef5cc4a0786d229fb517a Mon Sep 17 00:00:00 2001 From: cuz Date: Fri, 14 Sep 2001 20:52:54 +0000 Subject: [PATCH] Added the rs232 module git-svn-id: svn://svn.cc65.org/cc65/trunk@926 b7a2c559-68d2-44c3-8de9-860c34a00d81 --- libsrc/cbm510/Makefile | 3 +- libsrc/cbm510/rs232.s | 631 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 633 insertions(+), 1 deletion(-) create mode 100644 libsrc/cbm510/rs232.s diff --git a/libsrc/cbm510/Makefile b/libsrc/cbm510/Makefile index b518bd010..8b152550d 100644 --- a/libsrc/cbm510/Makefile +++ b/libsrc/cbm510/Makefile @@ -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 index 000000000..6e6451cd4 --- /dev/null +++ b/libsrc/cbm510/rs232.s @@ -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 + + + -- 2.39.5