From ec946cae512c53e1d873ba980db5af751b68a3aa Mon Sep 17 00:00:00 2001 From: uz Date: Sun, 20 Sep 2009 14:22:04 +0000 Subject: [PATCH] Lynx patches by Karri Kaksonen. Improvements for the graphics driver, new serial driver. git-svn-id: svn://svn.cc65.org/cc65/trunk@4198 b7a2c559-68d2-44c3-8de9-860c34a00d81 --- asminc/lynx.inc | 29 +++ asminc/ser-kernel.inc | 2 + doc/lynx.sgml | 48 +++- include/serial.h | 2 + libsrc/Makefile | 3 +- libsrc/lynx/Makefile | 2 +- libsrc/lynx/comlynx.s | 412 ++++++++++++++++++++++++++++++++++ libsrc/lynx/lynx-160-102-16.s | 16 +- 8 files changed, 498 insertions(+), 16 deletions(-) create mode 100644 libsrc/lynx/comlynx.s diff --git a/asminc/lynx.inc b/asminc/lynx.inc index 19ddef8ac..7c720542e 100644 --- a/asminc/lynx.inc +++ b/asminc/lynx.inc @@ -203,6 +203,20 @@ MSTEREO = $FD50 ; Mikey Misc +; Interrupt bits in INTRST and INTSET +TIMER0_INTERRUPT = $01 +TIMER1_INTERRUPT = $02 +TIMER2_INTERRUPT = $04 +TIMER3_INTERRUPT = $08 +TIMER4_INTERRUPT = $10 +TIMER5_INTERRUPT = $20 +TIMER6_INTERRUPT = $40 +TIMER7_INTERRUPT = $80 + +HBL_INTERRUPT = TIMER0_INTERRUPT +VBL_INTERRUPT = TIMER2_INTERRUPT +SERIAL_INTERRUPT = TIMER4_INTERRUPT + INTRST = $FD80 INTSET = $FD81 MAGRDY0 = $FD84 @@ -213,6 +227,21 @@ MIKEYHREV = $FD88 MIKEYSREV = $FD89 IODIR = $FD8A IODAT = $FD8B +TxIntEnable = %10000000 +RxIntEnable = %01000000 +TxParEnable = %00010000 +ResetErr = %00001000 +TxOpenColl = %00000100 +TxBreak = %00000010 +ParEven = %00000001 +TxReady = %10000000 +RxReady = %01000000 +TxEmpty = %00100000 +RxParityErr = %00010000 +RxOverrun = %00001000 +RxFrameErr = %00000100 +RxBreak = %00000010 +ParityBit = %00000001 SERCTL = $FD8C SERDAT = $FD8D SDONEACK = $FD90 diff --git a/asminc/ser-kernel.inc b/asminc/ser-kernel.inc index 5d448775f..50f8ec06d 100644 --- a/asminc/ser-kernel.inc +++ b/asminc/ser-kernel.inc @@ -93,6 +93,8 @@ SER_BAUD_38400 = $10 SER_BAUD_57600 = $11 SER_BAUD_115200 = $12 SER_BAUD_230400 = $13 +SER_BAUD_31250 = $14 +SER_BAUD_62500 = $15 ; Data bit settings SER_BITS_5 = $00 diff --git a/doc/lynx.sgml b/doc/lynx.sgml index c897a4dad..e76b6060a 100644 --- a/doc/lynx.sgml +++ b/doc/lynx.sgml @@ -168,8 +168,54 @@ No mouse drivers are currently available for the Lynx. RS232 device drivers

-No serial drivers are currently available for the Lynx. + + The ComLynx port has Tx and Rx wired together. Every byte is sent + to all connected Lynxes. Only one Lynx can send at a time. There is no + protocol created for communication. You are on your own. + + If the Lynx returns framing error then it is likely that another Lynx is + sending data at the same time. + + The Lynx can also send a break and receive a break. The Lynx break is + recognized if the bit is down for 24 bit cycles or more. + + To send a break you just set the break bit. The length of the break depends + on how long this bit is down. + + The driver supports the baudrates: + + 62500 + 31250 + 9600 + 7200 + 4800 + 3600 + 2400 + 1800 + 1200 + 600 + 300 + 150 + 134.5 + 110 + 75 + + The parity bit supports MARK and SPACE. It also supports EVEN and ODD parity + but the parity bit is included in the calculation. Most of us don't want it + this way. But there is nothing we can do about it. Just don't use EVEN or ODD + when communicating to other equipment than the Lynx. + + There is always only one stop bit. And the data length is always 8 bits. + + We have no handshaking available. Even software handshake is impossible + as ComLynx has only one wire for the data. + + Both transmit and receive are interrupt driven. The driver reserves a fixed + area $200-$2ff for the transmit ring buffer and $300-$3ff for the receive + ring buffer. This area can not be used at startup for anything as the Lynx + ROM needs this area for decryption purposes. +

Limitations

diff --git a/include/serial.h b/include/serial.h index 23934e926..671ec193f 100644 --- a/include/serial.h +++ b/include/serial.h @@ -65,6 +65,8 @@ #define SER_BAUD_57600 0x11 #define SER_BAUD_115200 0x12 #define SER_BAUD_230400 0x13 +#define SER_BAUD_31250 0x14 +#define SER_BAUD_62500 0x15 /* Data bit settings */ #define SER_BITS_5 0x00 diff --git a/libsrc/Makefile b/libsrc/Makefile index 85a5a64f6..d03c4aec9 100644 --- a/libsrc/Makefile +++ b/libsrc/Makefile @@ -191,12 +191,13 @@ geoslib: # Lynx lynxlib: - for i in lynx common conio runtime em joystick tgi zlib; do \ + for i in lynx common conio runtime em joystick serial tgi zlib; do \ $(MAKE) SYS=lynx -C $$i || exit 1; \ $(AR) a lynx.lib $$i/*.o;\ done cp lynx/*.joy . cp lynx/*.tgi . + cp lynx/*.ser . #----------------------------------------------------------------------------- # NES diff --git a/libsrc/lynx/Makefile b/libsrc/lynx/Makefile index eaa52872d..76643b0b7 100644 --- a/libsrc/lynx/Makefile +++ b/libsrc/lynx/Makefile @@ -46,11 +46,11 @@ CFLAGS = -Osir -g -T -t $(SYS) --forget-inc-paths -I . -I ../../include # Object files OBJS = cgetc.o \ + comlynx.o \ crt0.o \ ctype.o \ eeprom.o \ extzp.o \ - framerate.o \ kbhit.o \ mainargs.o \ sysuname.o \ diff --git a/libsrc/lynx/comlynx.s b/libsrc/lynx/comlynx.s new file mode 100644 index 000000000..8b55e9322 --- /dev/null +++ b/libsrc/lynx/comlynx.s @@ -0,0 +1,412 @@ +; +; 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 "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 + +;---------------------------------------------------------------------------- +; Global variables +; +; The ring buffers will be at the fixed place +; Tx buffer $200 - $2ff. Rx buffer $300 - $3ff. +; This memory area can usually not be used for anything as the encryption +; stuff needs it. But for this purpose it fits perfectly. + + .bss + +TxBuffer = $0200 +RxBuffer = $0300 +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 + 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 + 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 + rts +invparameter: + lda #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 + 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 + 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_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 + diff --git a/libsrc/lynx/lynx-160-102-16.s b/libsrc/lynx/lynx-160-102-16.s index a27496cbc..625c01fb7 100644 --- a/libsrc/lynx/lynx-160-102-16.s +++ b/libsrc/lynx/lynx-160-102-16.s @@ -196,6 +196,9 @@ UNINSTALL: ; INIT: +; Enable interrupts for VBL + lda #$80 + tsb VTIMCTLA ; Done, reset the error code lda #TGI_ERR_OK sta ERROR @@ -434,19 +437,6 @@ SETDRAWPAGE: ; IRQ: VBL interrupt handler ; -TIMER0_INTERRUPT = $01 -TIMER1_INTERRUPT = $02 -TIMER2_INTERRUPT = $04 -TIMER3_INTERRUPT = $08 -TIMER4_INTERRUPT = $10 -TIMER5_INTERRUPT = $20 -TIMER6_INTERRUPT = $40 -TIMER7_INTERRUPT = $80 - -HBL_INTERRUPT = TIMER0_INTERRUPT -VBL_INTERRUPT = TIMER2_INTERRUPT -SERIAL_INTERRUPT = TIMER4_INTERRUPT - IRQ: lda INTSET ; Poll all pending interrupts and #VBL_INTERRUPT -- 2.39.5