1
0
mirror of https://github.com/cc65/cc65.git synced 2024-07-14 04:29:00 +00:00
cc65/libsrc/lynx/ser/lynx-comlynx.s
Oliver Schmidt d6c3bd29ac Renamed JUMPTABLE and cleaned up module.cfg.
This change was suppsed to fix the issue that the former JUMPTABLE is merked as 'ro' while it is actually written to in several scenarios. When drivers are converted using co65 and then compiled into ROMs the JUMPTABLE isn't copied to RAM and therefore the write operations in question fail.

However unfortunately I didn't succeed in changing that :-( Just setting the former JUMPTABLE to 'rw' broke the drivers. So I placed the DATA segment directly after the former JUMPTABLE segment. This made the drivers converted with co65 work again - obviously after changing libsrc/Makefile:235 from '--code-label' to '--data-label'. But the actual dynamic drivers still didn't work as the former JUMPTABLE wasn't placed as the beginning of the loaded file anymore. That effect could be changed by exchanging src/ld65/o65.c:1391 with src/ld65/o65.c:1394 but doing so broke the drivers again :-((
2014-05-01 21:44:39 +02:00

412 lines
10 KiB
ArmAsm

;
; Serial driver for the Atari Lynx ComLynx port.
;
; Karri Kaksonen, 17.09.2009
;
.include "lynx.inc"
.include "zeropage.inc"
.include "ser-kernel.inc"
.include "ser-error.inc"
; ------------------------------------------------------------------------
; Header. Includes jump table
.segment "HEADER"
; Driver signature
.byte $73, $65, $72 ; "ser"
.byte SER_API_VERSION ; Serial API version number
; Library reference
.addr $0000
; Jump table
.addr INSTALL
.addr UNINSTALL
.addr OPEN
.addr CLOSE
.addr GET
.addr PUT
.addr STATUS
.addr IOCTL
.addr IRQ
;----------------------------------------------------------------------------
; Global variables
;
.bss
TxBuffer: .res 256
RxBuffer: .res 256
RxPtrIn: .res 1
RxPtrOut: .res 1
TxPtrIn: .res 1
TxPtrOut: .res 1
contrl: .res 1
SerialStat: .res 1
TxDone: .res 1
.code
;----------------------------------------------------------------------------
; INSTALL: Is called after the driver is loaded into memory.
;
; Must return an SER_ERR_xx code in a/x.
INSTALL:
; Set up IRQ vector ?
;----------------------------------------------------------------------------
; UNINSTALL: Is called before the driver is removed from memory.
; No return code required (the driver is removed from memory on return).
;
UNINSTALL:
;----------------------------------------------------------------------------
; CLOSE: Close the port and disable interrupts. Called without parameters.
; Must return an SER_ERR_xx code in a/x.
CLOSE:
; Disable interrupts
; Done, return an error code
lda #<SER_ERR_OK
ldx #>SER_ERR_OK
rts
;----------------------------------------------------------------------------
; OPEN: A pointer to a ser_params structure is passed in ptr1.
;
; The Lynx has only two correct serial data formats:
; 8 bits, parity mark, 1 stop bit
; 8 bits, parity space, 1 stop bit
;
; It also has two wrong formats;
; 8 bits, even parity, 1 stop bit
; 8 bits, odd parity, 1 stop bit
;
; Unfortunately the parity bit includes itself in the calculation making
; parity not compatible with the rest of the world.
;
; We can only specify a few baud rates.
; Lynx has two non-standard speeds 31250 and 62500 which are
; frequently used in games.
;
; The receiver will always read the parity and report parity errors.
;
; Must return an SER_ERR_xx code in a/x.
OPEN:
stz RxPtrIn
stz RxPtrOut
stz TxPtrIn
stz TxPtrOut
; clock = 8 * 15625
lda #%00011000
sta TIM4CTLA
ldy #SER_PARAMS::BAUDRATE
lda (ptr1),y
ldx #1
cmp #SER_BAUD_62500
beq setbaudrate
ldx #2
cmp #SER_BAUD_31250
beq setbaudrate
ldx #12
cmp #SER_BAUD_9600
beq setbaudrate
ldx #25
cmp #SER_BAUD_4800
beq setbaudrate
ldx #51
cmp #SER_BAUD_2400
beq setbaudrate
ldx #103
cmp #SER_BAUD_1200
beq setbaudrate
ldx #207
cmp #SER_BAUD_600
beq setbaudrate
; clock = 6 * 15625
ldx #%00011010
stx TIM4CTLA
ldx #12
cmp #SER_BAUD_7200
beq setbaudrate
ldx #25
cmp #SER_BAUD_3600
beq setbaudrate
ldx #207
stx TIM4BKUP
; clock = 4 * 15625
ldx #%00011100
cmp #SER_BAUD_300
beq setprescaler
; clock = 6 * 15625
ldx #%00011110
cmp #SER_BAUD_150
beq setprescaler
; clock = 1 * 15625
ldx #%00011111
stx TIM4CTLA
cmp #SER_BAUD_75
beq baudsuccess
ldx #141
cmp #SER_BAUD_110
beq setbaudrate
; clock = 2 * 15625
ldx #%00011010
stx TIM4CTLA
ldx #68
cmp #SER_BAUD_1800
beq setbaudrate
; clock = 6 * 15625
ldx #%00011110
stx TIM4CTLA
ldx #231
cmp #SER_BAUD_134_5
beq setbaudrate
lda #<SER_ERR_BAUD_UNAVAIL
ldx #>SER_ERR_BAUD_UNAVAIL
rts
setprescaler:
stx TIM4CTLA
bra baudsuccess
setbaudrate:
stx TIM4BKUP
baudsuccess:
ldx #TxOpenColl|ParEven
stx contrl
ldy #SER_PARAMS::DATABITS ; Databits
lda (ptr1),y
cmp #SER_BITS_8
bne invparameter
ldy #SER_PARAMS::STOPBITS ; Stopbits
lda (ptr1),y
cmp #SER_STOP_1
bne invparameter
ldy #SER_PARAMS::PARITY ; Parity
lda (ptr1),y
cmp #SER_PAR_NONE
beq invparameter
cmp #SER_PAR_MARK
beq checkhs
cmp #SER_PAR_SPACE
bne @L0
ldx #TxOpenColl
stx contrl
bra checkhs
@L0:
ldx #TxParEnable|TxOpenColl|ParEven
stx contrl
cmp #SER_PAR_EVEN
beq checkhs
ldx #TxParEnable|TxOpenColl
stx contrl
checkhs:
ldx contrl
stx SERCTL
ldy #SER_PARAMS::HANDSHAKE ; Handshake
lda (ptr1),y
cmp #SER_HS_NONE
bne invparameter
lda SERDAT
lda contrl
ora #RxIntEnable|ResetErr
sta SERCTL
lda #<SER_ERR_OK
ldx #>SER_ERR_OK
rts
invparameter:
lda #<SER_ERR_INIT_FAILED
ldx #>SER_ERR_INIT_FAILED
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:
lda RxPtrIn
cmp RxPtrOut
bne GetByte
lda #<SER_ERR_NO_DATA
ldx #>SER_ERR_NO_DATA
rts
GetByte:
ldy RxPtrOut
lda RxBuffer,y
inc RxPtrOut
ldx #$00
sta (ptr1,x)
txa ; Return code = 0
rts
;----------------------------------------------------------------------------
; PUT: Output character in A.
; Must return an SER_ERR_xx code in a/x.
PUT:
tax
lda TxPtrIn
ina
cmp TxPtrOut
bne PutByte
lda #<SER_ERR_OVERFLOW
ldx #>SER_ERR_OVERFLOW
rts
PutByte:
ldy TxPtrIn
txa
sta TxBuffer,y
inc TxPtrIn
bit TxDone
bmi @L1
php
sei
lda contrl
ora #TxIntEnable|ResetErr
sta SERCTL ; Allow TX-IRQ to hang RX-IRQ
sta TxDone
plp
@L1:
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:
ldy SerialStat
ldx #$00
sta (ptr1,x)
txa ; Return code = 0
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.
;
; Both the Tx and Rx interrupts are level sensitive instead of edge sensitive.
; Due to this bug you have to disable the interrupt before clearing it.
IRQ:
lda INTSET ; Poll all pending interrupts
and #SERIAL_INTERRUPT
bne @L0
clc
rts
@L0:
bit TxDone
bmi @tx_irq ; Transmit in progress
ldx SERDAT
lda SERCTL
and #RxParityErr|RxOverrun|RxFrameErr|RxBreak
beq @rx_irq
tsb SerialStat ; Save error condition
bit #RxBreak
beq @noBreak
stz TxPtrIn ; Break received - drop buffers
stz TxPtrOut
stz RxPtrIn
stz RxPtrOut
@noBreak:
lda contrl
ora #RxIntEnable|ResetErr
sta SERCTL
lda #$10
sta INTRST
bra @IRQexit
@rx_irq:
lda contrl
ora #RxIntEnable|ResetErr
sta SERCTL
txa
ldx RxPtrIn
sta RxBuffer,x
txa
inx
@cont0:
cpx RxPtrOut
beq @1
stx RxPtrIn
lda #SERIAL_INTERRUPT
sta INTRST
bra @IRQexit
@1:
sta RxPtrIn
lda #$80
tsb SerialStat
@tx_irq:
ldx TxPtrOut ; Has all bytes been sent?
cpx TxPtrIn
beq @allSent
lda TxBuffer,x ; Send next byte
sta SERDAT
inc TxPtrOut
@exit1:
lda contrl
ora #TxIntEnable|ResetErr
sta SERCTL
lda #SERIAL_INTERRUPT
sta INTRST
bra @IRQexit
@allSent:
lda SERCTL ; All bytes sent
bit #TxEmpty
beq @exit1
bvs @exit1
stz TxDone
lda contrl
ora #RxIntEnable|ResetErr
sta SERCTL
lda #SERIAL_INTERRUPT
sta INTRST
@IRQexit:
clc
rts