1
0
mirror of https://github.com/cc65/cc65.git synced 2025-02-25 01:29:04 +00:00

Added a new serial driver for the atmos. By Stefan Haubenthal.

git-svn-id: svn://svn.cc65.org/cc65/trunk@5580 b7a2c559-68d2-44c3-8de9-860c34a00d81
This commit is contained in:
uz 2012-03-09 10:48:59 +00:00
parent a564450415
commit 65a8da9fde
4 changed files with 386 additions and 2 deletions

View File

@ -130,7 +130,16 @@ No mouse drivers are currently available for the Atmos.
<sect1>RS232 device drivers<p>
No serial drivers are currently available for the Atmos.
<descrip>
<tag><tt/atmos-acia.ser/</tag>
Driver for the Telestrat integrated serial controller and the Atmos with a
serial add-on.
Note that because of the peculiarities of the 6551 chip together with the
use of the NMI, transmits are not interrupt driven, and the transceiver
blocks if the receiver asserts flow control because of a full buffer.
</descrip><p>

View File

@ -138,6 +138,7 @@ atmoslib:
$(AR) a atmos.lib $$i/*.o || exit 1; \
done
cp atmos/*.joy .
cp atmos/*.ser .
cp atmos/*.tgi .
if [ -d atmos/extra ]; then \
for i in atmos/extra/*.o; do \

View File

@ -79,7 +79,7 @@ JOYS = atmos-pase.joy
MOUS =
SERS =
SERS = atmos-acia.ser
TGIS = atmos-240-200-2.tgi

374
libsrc/atmos/atmos-acia.s Normal file
View File

@ -0,0 +1,374 @@
;
; Serial driver for the Telestrat integrated serial controller and the
; Atmos with a serial add-on.
;
; Stefan Haubenthal, 2012-03-05
;
; 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"
; ------------------------------------------------------------------------
; Header. Includes jump table
.segment "JUMPTABLE"
; Driver signature
.byte $73, $65, $72 ; "ser"
.byte SER_API_VERSION ; Serial API version number
; Jump table.
.addr INSTALL
.addr UNINSTALL
.addr OPEN
.addr CLOSE
.addr GET
.addr PUT
.addr STATUS
.addr IOCTL
.addr IRQ
;----------------------------------------------------------------------------
; I/O definitions
ACIA = $031C
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 ;
RecvBuf: .res 256 ; Receive buffers: 256 bytes
SendBuf: .res 256 ; Send buffers: 256 bytes
Index: .res 1 ; I/O register index
.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: 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 Telestrat/Atmos, this is
; actually the same as:
;
; UNINSTALL: Is called before the driver is removed from memory.
; No return code required (the driver is removed from memory on return).
;
; and:
;
; CLOSE: Close the port and disable interrupts. Called without parameters.
; Must return an SER_ERR_xx code in a/x.
INSTALL:
UNINSTALL:
CLOSE:
ldx Index ; Check for open port
beq :+
; Deactivate DTR and disable 6551 interrupts
lda #%00001010
sta ACIA_CMD,x
; Done, return an error code
: lda #<SER_ERR_OK
tax ; A is zero
stx Index ; Mark port as closed
rts
;----------------------------------------------------------------------------
; OPEN: 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
ldy #$00
sty Stopped
sty RecvHead
sty RecvTail
sty SendHead
sty SendTail
dey ; Y = 255
sty RecvFreeCnt
sty SendFreeCnt
; 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
stx Index ; Mark port as open
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
;----------------------------------------------------------------------------
; GET: Will fetch a character from the receive buffer and store it into the
; variable pointed to by ptr1. If no data is available, SER_ERR_NO_DATA is
; returned.
GET:
ldy SendFreeCnt ; Send data if necessary
iny ; Y == $FF?
beq :+
lda #$00 ; TryHard = false
jsr TryToSend
; Check for buffer empty
: lda RecvFreeCnt ; (25)
cmp #$FF
bne :+
lda #<SER_ERR_NO_DATA
ldx #>SER_ERR_NO_DATA
rts
; Check for flow stopped & enough free: release flow control
: ldy Stopped ; (34)
beq :+
cmp #63
bcc :+
lda #$00
sta Stopped
lda RtsOff
ora #%00001000
sta ACIA_CMD
; Get byte from buffer
: ldy RecvHead ; (41)
lda RecvBuf,y
inc RecvHead
inc RecvFreeCnt
ldx #$00 ; (59)
sta (ptr1,x)
txa ; Return code = 0
rts
;----------------------------------------------------------------------------
; PUT: Output character in A.
; Must return an SER_ERR_xx code in a/x.
PUT:
; Try to send
ldy SendFreeCnt
iny ; Y = $FF?
beq :+
pha
lda #$00 ; TryHard = false
jsr TryToSend
pla
; Put byte into send buffer & send
: ldy SendFreeCnt
bne :+
lda #<SER_ERR_OVERFLOW
ldx #>SER_ERR_OVERFLOW
rts
: ldy SendTail
sta SendBuf,y
inc SendTail
dec SendFreeCnt
lda #$FF ; TryHard = true
jsr TryToSend
lda #<SER_ERR_OK
tax
rts
;----------------------------------------------------------------------------
; STATUS: Return the status in the variable pointed to by ptr1.
; Must return an SER_ERR_xx code in a/x.
STATUS:
lda ACIA_STATUS
ldx #$00
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 SER_ERR_xx code in a/x.
IOCTL:
lda #<SER_ERR_INV_IOCTL
ldx #>SER_ERR_INV_IOCTL
rts
;----------------------------------------------------------------------------
; IRQ: Called from the builtin runtime IRQ handler as a subroutine. All
; registers are already saved, no parameters are passed, but the carry flag
; is clear on entry. The routine must return with carry set if the interrupt
; was handled, otherwise with carry clear.
IRQ:
ldx Index ; Check for open port
beq Done
lda ACIA_STATUS,x ; Check ACIA status for receive interrupt
and #$08
beq Done ; Jump if no ACIA interrupt
lda ACIA_DATA,x ; Get byte from ACIA
ldy RecvFreeCnt ; Check if we have free space left
beq Flow ; 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
ldy RecvFreeCnt ; Check for buffer space low
cpy #33
bcc Flow ; Assert flow control if buffer space low
rts ; Interrupt handled (carry already set)
; Assert flow control if buffer space too low
Flow: lda RtsOff
sta ACIA_CMD,x
sta Stopped
sec ; Interrupt handled
Done: rts
;----------------------------------------------------------------------------
; Try to send a byte. Internal routine. A = TryHard
TryToSend:
sta tmp1 ; Remember tryHard flag
Again: lda SendFreeCnt
cmp #$FF
beq Quit ; Bail out
; Check for flow stopped
lda Stopped
bne Quit ; Bail out
; Check that ACIA is ready to send
lda ACIA_STATUS
and #$10
bne Send
bit tmp1 ; Keep trying if must try hard
bmi Again
Quit: rts
; Send byte and try again
Send: ldy SendHead
lda SendBuf,y
sta ACIA_DATA
inc SendHead
inc SendFreeCnt
jmp Again