]> git.sur5r.net Git - cc65/commitdiff
Added C128 swiftlink driver
authorcuz <cuz@b7a2c559-68d2-44c3-8de9-860c34a00d81>
Sat, 13 Dec 2003 11:02:48 +0000 (11:02 +0000)
committercuz <cuz@b7a2c559-68d2-44c3-8de9-860c34a00d81>
Sat, 13 Dec 2003 11:02:48 +0000 (11:02 +0000)
git-svn-id: svn://svn.cc65.org/cc65/trunk@2738 b7a2c559-68d2-44c3-8de9-860c34a00d81

libsrc/Makefile
libsrc/c128/Makefile
libsrc/c128/c128-swlink.s [new file with mode: 0644]
libsrc/c128/c128.inc

index 8e1995a75dfd8fdfaea437b51b887bc1c98c5b04..567d3911d9f30630c2c4d243ff261d7f9f4b1997 100644 (file)
@@ -123,7 +123,7 @@ c64lib:
 # C128
 
 c128lib:
-       for i in c128 cbm common runtime conio dbg em joystick tgi zlib; do \
+       for i in c128 cbm common runtime conio dbg em joystick serial tgi zlib; do \
            AS=$(AS) \
            CC=$(CC) \
            LD=$(LD) \
@@ -135,6 +135,7 @@ c128lib:
        mv c128/crt0.o c128.o
        cp c128/*.emd .
        cp c128/*.joy .
+       cp c128/*.ser .
        cp c128/c128-640-200-2.tgi c128-vdc.tgi
        cp c128/c128-640-480-2.tgi c128-vdc2.tgi
 
index 2c80aaf57e8731dc17b05eaae82db5b57261b6bd..e8b53bf956bfc36635f6a42d432da4300abc76b8 100644 (file)
@@ -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 $@ $^
 
@@ -57,6 +60,8 @@ EMDS = c128-georam.emd c128-ram.emd c128-ramcart.emd c128-reu.emd c128-vdc.emd
 
 JOYS = c128-ptvjoy.joy c128-stdjoy.joy
 
+SERS = c128-swlink.ser
+
 TGIS = c128-640-200-2.tgi c128-640-480-2.tgi
 
 #--------------------------------------------------------------------------
@@ -64,14 +69,16 @@ TGIS = c128-640-200-2.tgi c128-640-480-2.tgi
 
 .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/c128/c128-swlink.s b/libsrc/c128/c128-swlink.s
new file mode 100644 (file)
index 0000000..3194cf0
--- /dev/null
@@ -0,0 +1,492 @@
+;
+; Serial driver for the C128 using a Swiftlink or Turbo-232 cartridge.
+;
+; Ullrich von Bassewitz, 2003-04-18
+;
+; 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        "c128.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
+
+;----------------------------------------------------------------------------
+; 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
+ACIA_CLOCK      = ACIA+7        ; Turbo232 external baud-rate generator
+
+;----------------------------------------------------------------------------
+;
+; Global variables
+;
+
+; We reuse the RS232 zero page variables for the driver, since the ROM
+; routines cannot be used together with this driver (may also use $A0F
+; and following in case of problems).
+RecvHead        := $A7          ; Head of receive buffer
+RecvTail        := $A8          ; Tail of receive buffer
+RecvFreeCnt     := $A9          ; Number of bytes in receive buffer
+SendHead        := $AA          ; Head of send buffer
+SendTail        := $AB          ; Tail of send buffer
+SendFreeCnt     := $B4          ; Number of bytes free in send buffer
+Stopped         := $B5          ; Flow-stopped flag
+RtsOff          := $B6          ;
+
+; Send and receive buffers: 256 bytes each
+RecvBuf         := $0C00        ; Use the ROM buffers
+SendBuf         := $0D00
+
+.rodata
+
+; Tables used to translate RS232 params into register values
+
+BaudTable:                      ; bit7 = 1 means setting is invalid
+        .byte   $FF             ; SER_BAUD_45_5
+        .byte   $FF             ; SER_BAUD_50
+        .byte   $FF             ; SER_BAUD_75
+        .byte   $FF             ; SER_BAUD_110
+        .byte   $FF             ; SER_BAUD_134_5
+        .byte   $02             ; SER_BAUD_150
+        .byte   $05             ; SER_BAUD_300
+        .byte   $06             ; SER_BAUD_600
+        .byte   $07             ; SER_BAUD_1200
+        .byte   $FF             ; SER_BAUD_1800
+        .byte   $08             ; SER_BAUD_2400
+        .byte   $09             ; SER_BAUD_3600
+        .byte   $0A             ; SER_BAUD_4800
+        .byte   $0B             ; SER_BAUD_7200
+        .byte   $0C             ; SER_BAUD_9600
+        .byte   $0E             ; SER_BAUD_19200
+        .byte   $0F             ; 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
+
+;----------------------------------------------------------------------------
+; Interrupt stub that is copied into low RAM. The startup code uses a special
+; memory configuration with just kernal and I/O enabled (anything else is RAM).
+; The NMI handler in ROM will switch back to a configuration where just the
+; low 16K RAM are accessible. So we have to copy a smal piece of code into
+; low RAM that enables the cc65 configuration and then jumps to the real NMI
+; handler.
+
+NmiStubOrig     := *
+
+.org    $1150                           ; BASIC graphics area
+.proc   NmiStub
+
+       lda     #MMU_CFG_CC65           ; Bank 0 with kernal ROM...
+       sta     MMU_CR                  ; ...enable
+        jsr     NmiHandler              ; Call the actual NMI handler
+               lda     #$00                    ; Get ROM config...
+       sta     MMU_CR                  ; ...and enable it
+Vector  := *+1
+        .byte   $4C                     ; Jump to the saved IRQ vector
+
+.endproc
+.reloc
+
+;----------------------------------------------------------------------------
+; 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.
+
+INSTALL:
+
+; Deactivate DTR and disable 6551 interrupts
+
+       lda     #%00001010
+               sta     ACIA_CMD
+
+; Copy the NMI stub into low memory
+
+        ldy     #.sizeof (NmiStub)-1
+@L1:    lda     NmiStubOrig,y
+        sta     NmiStub,y
+        dey
+        bpl     @L1
+
+; Set up the nmi vector
+
+       lda     NMIVec
+       ldy     NMIVec+1
+       sta     NmiStub::Vector+0
+       sty     NmiStub::Vector+1
+       lda     #<NmiStub
+       ldy     #>NmiStub
+SetNMI: sta    NMIVec
+       sty     NMIVec+1
+
+; Done, return an error code
+
+        lda     #<SER_ERR_OK
+        tax                     ; A is zero
+       rts
+
+;----------------------------------------------------------------------------
+; UNINSTALL routine. Is called before the driver is removed from memory.
+; Must return an SER_ERR_xx code in a/x.
+
+UNINSTALL:
+
+; Stop interrupts, drop DTR
+
+       lda     #%00001010
+               sta     ACIA_CMD
+
+; Restore NMI vector and return OK
+
+       lda     NmiStub::Vector+0
+       ldy     NmiStub::Vector+1
+       jmp     SetNMI
+
+;----------------------------------------------------------------------------
+; PARAMS routine. A pointer to a ser_params structure is passed in ptr1.
+; Must return an SER_ERR_xx code in a/x.
+
+OPEN:
+
+; Check if the handshake setting is valid
+
+        ldy    #SER_PARAMS::HANDSHAKE  ; Handshake
+        lda     (ptr1),y
+        cmp    #SER_HS_HW              ; This is all we support
+        bne    InvParam
+
+; Initialize buffers
+
+        jsr     InitBuffers
+
+; Set the value for the control register, which contains stop bits, word
+; length and the baud rate.
+
+        ldy     #SER_PARAMS::BAUDRATE
+        lda     (ptr1),y                ; Baudrate index
+        tay
+        lda     BaudTable,y             ; Get 6551 value
+        bmi     InvBaud                        ; Branch if rate not supported
+        sta     tmp1
+
+        ldy    #SER_PARAMS::DATABITS   ; Databits
+        lda     (ptr1),y
+        tay
+        lda     BitTable,y
+        ora     tmp1
+        sta     tmp1
+
+        ldy    #SER_PARAMS::STOPBITS   ; Stopbits
+        lda     (ptr1),y
+        tay
+        lda     StopTable,y
+        ora     tmp1
+        ora    #%00010000              ; Receiver clock source = baudrate
+       sta     ACIA_CTRL
+
+; Set the value for the command register. We remember the base value in
+; RtsOff, since we will have to manipulate ACIA_CMD often.
+
+        ldy            #SER_PARAMS::PARITY     ; Parity
+        lda     (ptr1),y
+        tay
+        lda     ParityTable,y
+       ora     #%00000001              ; DTR active
+       sta     RtsOff
+               ora     #%00001000              ; Enable receive interrupts
+               sta     ACIA_CMD
+
+; Done
+
+        lda     #<SER_ERR_OK
+        tax                             ; A is zero
+               rts
+
+; Invalid parameter
+
+InvParam:
+       lda     #<SER_ERR_INIT_FAILED
+       ldx     #>SER_ERR_INIT_FAILED
+       rts
+
+; Baud rate not available
+
+InvBaud:
+        lda     #<SER_ERR_BAUD_UNAVAIL
+        ldx     #>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. Returns zero in a
+
+        jsr     InitBuffers
+
+; Return OK
+
+        lda     #<SER_ERR_OK
+        tax                             ; A is zero
+               rts
+
+;----------------------------------------------------------------------------
+; GET: Will fetch a character from the receive buffer and store it into the
+; variable pointer to by ptr1. If no data is available, SER_ERR_NO_DATA is
+; return.
+;
+
+GET:    ldx    SendFreeCnt             ; Send data if necessary
+               inx                             ; X == $FF?
+       beq     @L1
+       lda     #$00
+       jsr     TryToSend
+
+; Check for buffer empty
+
+@L1:   lda     RecvFreeCnt             ; (25)
+       cmp     #$ff
+       bne     @L2
+       lda     #<SER_ERR_NO_DATA
+       ldx     #>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_OVERFLOW      ; X is already zero
+       rts
+
+@L3:   ldx     SendTail
+       sta     SendBuf,x
+       inc     SendTail
+       dec     SendFreeCnt
+       lda     #$ff
+       jsr     TryToSend
+       lda     #<SER_ERR_OK
+       tax
+               rts
+
+;----------------------------------------------------------------------------
+; STATUS: Return the status in the variable pointed to by ptr1.
+; Must return an error code in a/x.
+;
+
+STATUS: lda            ACIA_STATUS
+               ldx     #0
+       sta     (ptr1,x)
+       txa                             ; SER_ERR_OK
+        rts
+
+;----------------------------------------------------------------------------
+; IOCTL: Driver defined entry point. The wrapper will pass a pointer to ioctl
+; specific data in ptr1, and the ioctl code in A.
+; Must return an error code in a/x.
+;
+
+IOCTL:  lda     #<SER_ERR_INV_IOCTL     ; We don't support ioclts for now
+        ldx     #>SER_ERR_INV_IOCTL
+        rts
+
+;----------------------------------------------------------------------------
+;
+; NMI handler
+; C128 NMI overhead=76 cycles: int=7, maxLatency=6, ROMenter=33, ROMexit=30
+; C64  NMI overhead=76 cycles: int=7, maxLatency=6, ROMenter=34, ROMexit=29
+;
+; timing: normal=76+43+9=128 cycles, assertFlow=76+52+9=137 cycles
+;
+; C128 @ 115.2k: 177 cycles avail (fast)
+; C64  @  57.6k: 177 cycles avail, worstAvail=177-43? = 134
+; SCPU @ 230.4k: 868 cycles avail: for a joke!
+;
+; Note: Because of the C128 banking, a small stub has to go into low memory,
+; since the ROM NMI entry point switches to a configuration, where only the
+; low 16K of RAM are visible. The entry code switches into the standard cc65
+; configuration (I/O + 16K kernal) and then jumps here. Registers are already
+; saved by the ROM code.
+
+NmiHandler:
+               lda     ACIA_STATUS     ;(4) ;status ;check for byte received
+       and     #$08            ;(2)
+               beq     @L9             ;(2*)
+       cld
+        lda    ACIA_DATA       ;(4)  data  ;get byte and put into receive buffer
+       ldy     RecvTail        ;(4)
+       ldx     RecvFreeCnt     ;(4)
+               beq     @L9             ;(2*) Jump if no space in receive buffer
+       sta     RecvBuf,y       ;(5)
+       inc     RecvTail        ;(6)
+       dec     RecvFreeCnt     ;(6)
+       cpx     #33             ;(2)  check for buffer space low
+               bcc     @L2             ;(2*)
+               rts
+
+; Assert flow control
+
+@L2:   lda     RtsOff          ;(3) assert flow control if buffer space too low
+       sta     ACIA_CMD        ;(4) command
+       sta     Stopped         ;(3)
+@L9:    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
+
+
index 69c18e09cfddba42cc116ddcba28b8545565737f..688b210a4be6de9446eb408ecae1f5e928986281 100644 (file)
@@ -46,6 +46,7 @@ CLRSCR                = $C142
 KBDREAD                = $C006
 NEWLINE                = $C363
 PRINT          = $C322
+NMIEXIT         = $FF33
 
 ; ---------------------------------------------------------------------------
 ; Vectors