From ad6a85b2550a7ebf6021350e845c600b00fb47cd Mon Sep 17 00:00:00 2001 From: cuz Date: Sun, 14 Dec 2003 17:40:57 +0000 Subject: [PATCH] Added a serial driver for the builtin 6551 of the Plus/4 (untested, but taken mostly from the C64 version which is tested and works). Improved interrupt handling (was needed for the driver). git-svn-id: svn://svn.cc65.org/cc65/trunk@2754 b7a2c559-68d2-44c3-8de9-860c34a00d81 --- libsrc/plus4/.cvsignore | 3 + libsrc/plus4/Makefile | 11 +- libsrc/plus4/break.s | 5 +- libsrc/plus4/crt0.s | 152 ++++++------- libsrc/plus4/plus4-stdser.s | 424 ++++++++++++++++++++++++++++++++++++ 5 files changed, 509 insertions(+), 86 deletions(-) create mode 100644 libsrc/plus4/plus4-stdser.s diff --git a/libsrc/plus4/.cvsignore b/libsrc/plus4/.cvsignore index 9f998e091..581e5ff62 100644 --- a/libsrc/plus4/.cvsignore +++ b/libsrc/plus4/.cvsignore @@ -1 +1,4 @@ +*.emd *.joy +*.ser +*.tgi diff --git a/libsrc/plus4/Makefile b/libsrc/plus4/Makefile index 14e9b6bb6..cb2869740 100644 --- a/libsrc/plus4/Makefile +++ b/libsrc/plus4/Makefile @@ -20,6 +20,9 @@ %.joy: %.o ../runtime/zeropage.o @$(LD) -t module -o $@ $^ +%.ser: %.o ../runtime/zeropage.o + @$(LD) -t module -o $@ $^ + %.tgi: %.o ../runtime/zeropage.o @$(LD) -t module -o $@ $^ @@ -73,6 +76,8 @@ EMDS = JOYS = plus4-stdjoy.joy +SERS = plus4-stdser.ser + TGIS = #-------------------------------------------------------------------------- @@ -80,14 +85,14 @@ TGIS = .PHONY: all clean zap -all: $(OBJS) $(EMDS) $(JOYS) $(TGIS) +all: $(OBJS) $(EMDS) $(JOYS) $(SERS) $(TGIS) ../runtime/zeropage.o: $(MAKE) -C $(dir $@) $(notdir $@) clean: - @$(RM) $(OBJS) $(EMDS:.emd=.o) $(JOYS:.joy=.o) $(TGIS:.tgi=.o) + @$(RM) $(OBJS) $(EMDS:.emd=.o) $(JOYS:.joy=.o) $(SERS:.ser=.o) $(TGIS:.tgi=.o) zap: clean - @$(RM) $(EMDS) $(JOYS) $(TGIS) + @$(RM) $(EMDS) $(JOYS) $(SERS) $(TGIS) diff --git a/libsrc/plus4/break.s b/libsrc/plus4/break.s index 52c77127c..37ead40bb 100644 --- a/libsrc/plus4/break.s +++ b/libsrc/plus4/break.s @@ -55,11 +55,12 @@ uservec: jmp $FFFF ; Patched at runtime -; Break handler, called if a break occurs. Note: Y is not on the stack! +; Break handler, called if a break occurs. .proc brk_handler - sty _brk_y + pla + sta _brk_y pla sta _brk_x pla diff --git a/libsrc/plus4/crt0.s b/libsrc/plus4/crt0.s index 3146e3674..d6039a36b 100644 --- a/libsrc/plus4/crt0.s +++ b/libsrc/plus4/crt0.s @@ -41,33 +41,19 @@ Head: .word @Next ldx #zpspace-1 L1: lda sp,x sta zpsave,x ; save the zero page locations we need - dex + dex bpl L1 sta ENABLE_ROM cli ; Close open files - jsr $FFCC ; CLRCH + jsr $FFCC ; CLRCH ; Switch to second charset - lda #14 - jsr $FFD2 ; BSOUT - -; Setup the IRQ vector in the banked RAM and switch off the ROM - - sei ; No ints, handler not yet in place - sta ENABLE_RAM - lda #IRQ - sta $FFFF - cli ; Allow interrupts - -; Clear the BSS data - - jsr zerobss + lda #14 + jsr $FFD2 ; BSOUT ; Save system stuff and setup the stack. The stack starts at the top of the ; usable RAM. @@ -80,88 +66,110 @@ L1: lda sp,x lda #>$FD00 sta sp+1 -; Call module constructors +; Setup the IRQ vector in the banked RAM and switch off the ROM - jsr initlib + ldx #IRQ + sei ; No ints, handler not yet in place + sta ENABLE_RAM + stx $FFFE ; Install interrupt handler + sty $FFFF + cli ; Allow interrupts -; If we have IRQ functions, chain our stub into the IRQ vector +; Clear the BSS data - lda #<__IRQFUNC_COUNT__ - beq NoIRQ1 - lda IRQVec - ldx IRQVec+1 - sta IRQInd+1 - stx IRQInd+2 - lda #IRQStub - sei - sta IRQVec - stx IRQVec+1 - cli + jsr zerobss -; Push arguments and call main() +; Call module constructors + + jsr initlib -NoIRQ1: jsr callmain +; Initialize irqcount, which means that from now own custom linked in IRQ +; handlers (via condes) will be called. -; Back from main (this is also the _exit entry). Reset the IRQ vector if -; we chained it. + lda #.lobyte(__IRQFUNC_COUNT__*2) + sta irqcount -_exit: lda #<__IRQFUNC_COUNT__ - beq NoIRQ2 - lda IRQInd+1 - ldx IRQInd+2 - sei - sta IRQVec - stx IRQVec+1 - cli +; Push arguments and call main() + + jsr callmain -; Run module destructors. +; Back from main (this is also the _exit entry). Run module destructors. -NoIRQ2: jsr donelib ; Run module destructors +_exit: lda #0 + sta irqcount ; Disable custom IRQ handlers + jsr donelib ; Run module destructors ; Restore system stuff - ldx spsave - txs + ldx spsave + txs ; Copy back the zero page stuff - ldx #zpspace-1 + ldx #zpspace-1 L2: lda zpsave,x - sta sp,x - dex + sta sp,x + dex bpl L2 ; Enable the ROM, reset changed vectors and return to BASIC sta ENABLE_ROM - jmp $FF8A ; RESTOR + jmp $FF8A ; RESTOR ; ------------------------------------------------------------------------ -; IRQ handler +; IRQ handler. The handler in the ROM enables the kernal and jumps to +; $CE00, where the ROM code checks for a BRK or IRQ and branches via the +; indirect vectors at $314/$316. +; To make our stub as fast as possible, we skip the whole part of the ROM +; handler and jump to the indirect vectors directly. We do also call our +; own interrupt handlers if we have any, so they need not use $314. .segment "LOWCODE" -IRQ: pha +IRQ: cld ; Just to be sure + pha txa pha + tya + pha tsx ; Get the stack pointer - lda $0103,x ; Get the saved status register - tax ; Save for later + lda $0104,x ; Get the saved status register and #$10 ; Test for BRK bit bne dobreak - lda #>irq_ret ; Push new return address + +; It's an IRQ and RAM is enabled. If we have handlers, call them. We will use +; a flag here instead of loading __IRQFUNC_COUNT__ directly, since the condes +; function is not reentrant. The irqcount flag will be set/reset from the main +; code, to avoid races. + + ldy irqcount + beq @L1 + lda #<__IRQFUNC_TABLE__ + ldx #>__IRQFUNC_TABLE__ + jsr condes ; Call the IRQ functions + +; Since the ROM handler will end with an RTI, we have to fake an IRQ return +; on stack, so we get control of the CPU after the ROM handler and can switch +; back to RAM. + +@L1: lda #>irq_ret ; Push new return address pha lda #__IRQFUNC_TABLE__ - jsr condes ; Call the IRQ functions - sta ENABLE_ROM - jmp IRQInd ; Jump to the saved IRQ vector - ; ------------------------------------------------------------------------ ; Data @@ -205,8 +195,8 @@ zpsave: .res zpspace ; BRK handling brk_jmp: jmp $0000 -.bss spsave: .res 1 - +.bss +irqcount: .byte 0 diff --git a/libsrc/plus4/plus4-stdser.s b/libsrc/plus4/plus4-stdser.s new file mode 100644 index 000000000..a5aace9f4 --- /dev/null +++ b/libsrc/plus4/plus4-stdser.s @@ -0,0 +1,424 @@ +; +; Serial driver for the builtin 6551 ACIA of the Plus/4. +; +; Ullrich von Bassewitz, 2003-12-13 +; +; The driver is based on the cc65 rs232 module, which in turn is based on +; Craig Bruce device driver for the Switftlink/Turbo-232. +; +; 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. +; + + .include "zeropage.inc" + .include "ser-kernel.inc" + .include "ser-error.inc" + .include "plus4.inc" + + +; ------------------------------------------------------------------------ +; Header. Includes jump table + +.segment "JUMPTABLE" + +; Driver signature + + .byte $73, $65, $72 ; "ser" + .byte SER_API_VERSION ; Serial API version number + +; Jump table. + + .word INSTALL + .word UNINSTALL + .word OPEN + .word CLOSE + .word GET + .word PUT + .word STATUS + .word IOCTL + .word IRQ + +;---------------------------------------------------------------------------- +; I/O definitions + +ACIA = $DE00 +ACIA_DATA = ACIA+0 ; Data register +ACIA_STATUS = ACIA+1 ; Status register +ACIA_CMD = ACIA+2 ; Command register +ACIA_CTRL = ACIA+3 ; Control register + +;---------------------------------------------------------------------------- +; +; Global variables +; + +.bss +RecvHead: .res 1 ; Head of receive buffer +RecvTail: .res 1 ; Tail of receive buffer +RecvFreeCnt: .res 1 ; Number of bytes in receive buffer +SendHead: .res 1 ; Head of send buffer +SendTail: .res 1 ; Tail of send buffer +SendFreeCnt: .res 1 ; Number of bytes in send buffer + +Stopped: .res 1 ; Flow-stopped flag +RtsOff: .res 1 ; + +; Send and receive buffers: 256 bytes each +RecvBuf: .res 256 +SendBuf: .res 256 + +.rodata + +; Tables used to translate RS232 params into register values + +BaudTable: ; bit7 = 1 means setting is invalid + .byte $FF ; SER_BAUD_45_5 + .byte $01 ; SER_BAUD_50 + .byte $02 ; SER_BAUD_75 + .byte $03 ; SER_BAUD_110 + .byte $04 ; SER_BAUD_134_5 + .byte $05 ; SER_BAUD_150 + .byte $06 ; SER_BAUD_300 + .byte $07 ; SER_BAUD_600 + .byte $08 ; SER_BAUD_1200 + .byte $09 ; SER_BAUD_1800 + .byte $0A ; SER_BAUD_2400 + .byte $0B ; SER_BAUD_3600 + .byte $0C ; SER_BAUD_4800 + .byte $0D ; SER_BAUD_7200 + .byte $0E ; SER_BAUD_9600 + .byte $0F ; SER_BAUD_19200 + .byte $FF ; SER_BAUD_38400 + .byte $FF ; SER_BAUD_57600 + .byte $FF ; SER_BAUD_115200 + .byte $FF ; SER_BAUD_230400 + +BitTable: + .byte $60 ; SER_BITS_5 + .byte $40 ; SER_BITS_6 + .byte $20 ; SER_BITS_7 + .byte $00 ; SER_BITS_8 + +StopTable: + .byte $00 ; SER_STOP_1 + .byte $80 ; SER_STOP_2 + +ParityTable: + .byte $00 ; SER_PAR_NONE + .byte $20 ; SER_PAR_ODD + .byte $60 ; SER_PAR_EVEN + .byte $A0 ; SER_PAR_MARK + .byte $E0 ; SER_PAR_SPACE + +.code + +;---------------------------------------------------------------------------- +; INSTALL routine. Is called after the driver is loaded into memory. If +; possible, check if the hardware is present. +; Must return an SER_ERR_xx code in a/x. +; +; Since we don't have to manage the IRQ vector on the Plus/4, this is actually +; the same as: +; +; UNINSTALL routine. Is called before the driver is removed from memory. +; Must return an SER_ERR_xx code in a/x. + + +INSTALL: +UNINSTALL: + +; Deactivate DTR and disable 6551 interrupts + + lda #%00001010 + sta ACIA_CMD + +; Done, return an error code + + lda #SER_ERR_INIT_FAILED + rts + +; Baud rate not available + +InvBaud: + lda #SER_ERR_BAUD_UNAVAIL + rts + +;---------------------------------------------------------------------------- +; CLOSE: Close the port, disable interrupts and flush the buffer. Called +; without parameters. Must return an error code in a/x. +; + +CLOSE: + +; Stop interrupts, drop DTR + + lda #%00001010 + sta ACIA_CMD + +; Initalize buffers. + + jsr InitBuffers + +; Return OK + + lda #SER_ERR_NO_DATA + rts + +; Check for flow stopped & enough free: release flow control + +@L2: ldx Stopped ; (34) + beq @L3 + cmp #63 + bcc @L3 + lda #$00 + sta Stopped + lda RtsOff + ora #%00001000 + sta ACIA_CMD + +; Get byte from buffer + +@L3: ldx RecvHead ; (41) + lda RecvBuf,x + inc RecvHead + inc RecvFreeCnt + ldx #$00 ; (59) + sta (ptr1,x) + txa ; Return code = 0 + rts + +;---------------------------------------------------------------------------- +; PUT: Output character in A. +; Must return an error code in a/x. +; + +PUT: + +; Try to send + + ldx SendFreeCnt + inx ; X = $ff? + beq @L2 + pha + lda #$00 + jsr TryToSend + pla + +; Put byte into send buffer & send + +@L2: ldx SendFreeCnt + bne @L3 + lda #SER_ERR_INV_IOCTL +; rts ; Run into IRQ instead + +;---------------------------------------------------------------------------- +; IRQ: Called from the builtin runtime IRQ handler as a subroutine. All +; registers are already save, no parameters are passed and no return code +; is expected. +; + +IRQ: lda ACIA_STATUS ; Check ACIA status for receive interrupt + and #$08 + beq @L9 ; Jump if no ACIA interrupt + lda ACIA_DATA ; Get byte from ACIA + ldx RecvFreeCnt ; Check if we have free space left + beq @L1 ; Jump if no space in receive buffer + ldy RecvTail ; Load buffer pointer + sta RecvBuf,y ; Store received byte in buffer + inc RecvTail ; Increment buffer pointer + dec RecvFreeCnt ; Decrement free space counter + cpx #33 ; Check for buffer space low + bcc @L1 ; Assert flow control if buffer space low +@L9: rts + +; Assert flow control if buffer space too low + +@L1: lda RtsOff + sta ACIA_CMD + sta Stopped + rts + +;---------------------------------------------------------------------------- +; Try to send a byte. Internal routine. A = TryHard + +.proc TryToSend + + sta tmp1 ; Remember tryHard flag +@L0: lda SendFreeCnt + cmp #$ff + beq @L3 ; Bail out + +; Check for flow stopped + +@L1: lda Stopped + bne @L3 ; Bail out + +; Check that swiftlink is ready to send + +@L2: lda ACIA_STATUS + and #$10 + bne @L4 + bit tmp1 ;keep trying if must try hard + bmi @L0 +@L3: rts + +; Send byte and try again + +@L4: ldx SendHead + lda SendBuf,x + sta ACIA_DATA + inc SendHead + inc SendFreeCnt + jmp @L0 + +.endproc + + +;---------------------------------------------------------------------------- +; Initialize buffers + +InitBuffers: + ldx #0 + stx Stopped + stx RecvHead + stx RecvTail + stx SendHead + stx SendTail + dex ; X = 255 + stx RecvFreeCnt + stx SendFreeCnt + rts + -- 2.39.5