Jump to content
IGNORED

ComLynx parity


karri

Recommended Posts

Has anyone measured how the ComLynx parity goes?

 

As I understand it the Lynx can send SPACE and MARK correctly.

 

Sending EVEN or ODD parity includes the parity bit itself in the calculation.

 

If the data byte is 0x00 then the ODD parity should be a 0. Including the ODD zero to the calculation makes no difference and the result is correct.

 

If the data byte is 0x01 then the ODD parity should be a 1. Because of the Lynx error the parity becomes 0 again.

 

My theory is that the Lynx will always produce a parity of SPACE when you chose ODD and a MARK when you choose EVEN.

 

If this is the case then emulating Lynx parity with ordinary equipment is trivial. Am I correct?

 

--

Karri

Link to comment
Share on other sites

  • 3 months later...

The docs say that upon receipt, parity calculations happen and the parity error bit is set if the parity calculation detects an error. What is important is that is all that happens. So, despite the parity calculation flaw, data can be sent to and from the lynx. The lynx software just need to ignore the error bit.

Link to comment
Share on other sites

Yes, you are right. What I am saying is that there is no difference in SPACE and ODD modes. Or MARK and EVEN modes. I should really take the time to verify this myself.

 

If this is true then the Lynx can read PC serial ports correctly despite the parity bug.

--

Karri

Link to comment
Share on other sites

  • 6 months later...
  • 1 year later...
  • 5 weeks later...

I jut bought a Saleae Logic 16 logic analyzer and one of the things I intend to do is take a look at the comlynx data on the wire. I think we can settle this once and for all and build a simple USB device that you can plug a comlynx cable into and talk to the lynx with.

Edited by Wookie
Link to comment
Share on other sites

I jut bought a Saleae Logic 16 logic analyzer and one of the things I intend to do is take a look at the comlynx data on the wire. I think we can settle this once and for all and build a simple USB device that you can plug a comlynx cable into and talk to the lynx with.

 

It's simpler than one would expect (or I am just not understanding it all). As long as you select the same parity setting the PC can talk easily to the Lynx. I created a USB to ComLynx cable from a standard USB to serial converter plus a sacrificed ComLynx cable. Here's my report on that: http://atarilynxdeveloper.wordpress.com/2013/10/21/creating-a-comlynx-to-usb-cable/

 

Plus here's a bit of CC65 code that I used to transfer the contents of a cartridge over the cable from Lynx to PC:

 

#include <6502.h>

#include <lynx.h>

#include <tgi.h>

#include <joystick.h>

#include <stdlib.h>

 

#include <stdio.h>

#include <unistd.h>

//#include <sys\types.h>

 

#include <serial.h>

 

extern unsigned char lynxtgi[];

extern unsigned char lynxjoy[];

extern unsigned char comlynx[];

 

char joy;

 

void wait_joystick()

{

__asm__("press: lda $FCB0");

__asm__(" beq press");

__asm__("release: lda $FCB0");

__asm__(" bne release");

}

 

void show_startscreen()

{

tgi_clear();

tgi_setcolor(COLOR_WHITE);

tgi_outtextxy(10, 40, "Press A to start");

tgi_updatedisplay();

while (tgi_busy()) ;

 

wait_joystick();

wait_joystick();

wait_joystick();

}

 

void initialize()

{

tgi_install(&lynxtgi);

joy_install(&lynxjoy);

ser_install(&comlynx);

 

tgi_init();

CLI();

 

while (tgi_busy());

 

tgi_setbgcolor(COLOR_BLACK);

//tgi_setpalette(palette);

tgi_clear();

}

 

void open_comlynx()

{

struct ser_params params = {

SER_BAUD_62500,

SER_BITS_8, // only 8 data bits is supported

SER_STOP_1, // only 1 stop bit is supported

SER_PAR_MARK, // mark, space, even, odd is supported

SER_HS_NONE // only "none" is supported

};

ser_open(&params);

}

void send_cartridge()

{

off_t offset = 0;

unsigned char buffer[256];

unsigned char byte;

char text[20];

unsigned char index = 0;

char status;

int block = 0;

 

 

while (1)

{

show_startscreen();

 

open_comlynx();

 

MIKEY.iodir = 0x1A;

asm("lda #$0A");

asm("sta __iodat");

MIKEY.iodat = 0x0A;

lseek(1, offset, SEEK_SET);

 

do

{

// ; int __fastcall__ read(int fd,void *buf,int count)

// Read 256 bytes into buffer

read(1, &buffer, 256);

 

// Send a single 256 byte chunk

index = 0;

do

{

byte = buffer[index];

status = ser_put(byte);

if (status == SER_ERR_OVERFLOW)

{

// Oops, we were too fast

tgi_clear();

tgi_outtextxy(0, 0, "Too fast");

tgi_updatedisplay();

while(1);

}

}

while (index++ != 255);

 

// Draw a primitive progress bar

itoa(block, text, 10);

tgi_clear();

tgi_outtextxy(10, 10, text);

tgi_bar(10, 20, 10 + (block >> 4), 28);

 

//itoa(MIKEY.iodir, text, 16);

//tgi_outtextxy(10, 40, text);

itoa(MIKEY.iodat, text, 16);

tgi_outtextxy(10, 50, text);

 

tgi_updatedisplay();

while (tgi_busy()) ;

 

// Go again for the next block

}

while (block++ < 2048);

}

}

 

void main()

{

initialize();

 

send_cartridge();

}

 

 

Here's the C# code for the PC side to receive the data:

 

using System;

using System.Collections.Generic;

using System.IO;

using System.IO.Ports;

using System.Linq;

using System.Text;

using System.Threading.Tasks;

 

namespace ComLynxReceiver

{

class Program

{

static void Main(string[] args)

{

SerialPort port = new SerialPort("COM3", 62500, Parity.Mark, 8, StopBits.One);

port.ReceivedBytesThreshold = 256;

port.ReadBufferSize = 256;

port.DataReceived += OnDataReceived;

port.Open();

 

Console.WriteLine("Listening. Press ENTER to quit");

Console.ReadLine();

 

File.WriteAllBytes(@".\dump.bin", file);

}

 

static int totalBytes = 0;

static int bytesRead = 0;

static byte[] file = new byte[65536*8+1024];

 

static void OnDataReceived(object sender, SerialDataReceivedEventArgs e)

{

SerialPort port = (SerialPort)sender;

byte[] buffer = new byte[256];

bytesRead = port.Read(buffer, 0, 256);

Array.Copy(buffer, 0, file, totalBytes, bytesRead);

totalBytes += bytesRead;

Console.Clear();

Console.WriteLine("Read bytes: {0}", bytesRead);

Console.WriteLine("Total bytes: {0}", totalBytes);

}

}

}

 

Link to comment
Share on other sites

  • 5 months later...

Revitalizing this discussion, as obschan has asked me to look into ComLynx emulation.

 

I've done some measurements with a logic analyzer and this is a part of the output on a 2 Lynx II models ComLynxed together, where I tapped into the RX/TX line:

post-27403-0-36237500-1405611724_thumb.jpg

 

It seems that by setting the parity to odd (odd calculation, not SPACE), all transmitted data has a correct parity bit. These are both zero and one, so not all zero (for odd) like Karri suspected.

 

The output in the analysis is from Checkered Flag, so I ran the emulator to see which values are being written to SERCTL ($FD8C) and these turn out to be three:

$5C (RXINTEN|PAREN|RESETERR|TXOPEN)

$94 (TXINTEN|PAREN|TXOPEN)

$14 (PAREN|TXOPEN)

 

This shows how the transmitter is always set to open collector (TXOPEN) and that the parity is always enabled (PAREN) and parity is odd (PAREVEN is never set). That corresponds to what is observed with the analyzer.

 

My theory: UART in ComLynx was not broken (anymore) in the Lynx II model.

 

Comments anyone?

Link to comment
Share on other sites

But Lynx I and Lynx II work together over ComLynx without problems. How is that possible?

 

Could you also explain why Lynx I and Lynx II work together.

--

Karri

That could mean two things, if you ask me:

  1. The Lynx 1 also wasn't broken at all (or anymore)
  2. The games have no checking of the parity error bit in SERCTL.

I will need to check with a Lynx 1 device (should be easy, but will have to wait two weeks until I can get my hands on one), and I will look into the msmgr sources of the Epyx dev kit, to see whether they look at the parity bit at all.

Link to comment
Share on other sites

Well, point 2 seems to be proven wrong pretty quickly.

From msgmgr,src:

;-- get current byte's reception errors
and #PARERR+OVERRUN+FRAMERR+RXBRK
bne .80		; this is bad data

and

;-- check last received byte's errors
tya
bit #PARERR+FRAMERR
bne txErr

Link to comment
Share on other sites

The only reason I suspected that the parity does not work was the EPYX documentation:

 

 

Data Format
The serial data format is the standard 11 bits. We do not offer a choice.
The standard bits are:

	
		1 start bit (binary 0);
	
		8 data bits, LSB first;
	
		1 parity bit (or 9th bit as defined by the control byte);
	
		1 stop bit (binary 1).

The parity (or 9th) bit operates as follows:

	
		Receive:
	
		The state of the 9th bit is always available for read in the control byte. In addition, the parity of the received character is calculated and if it does not match the setting of the parity select bit in the control byte, the parity error bit will be set. Receive parity error can not be disabled. If you don't want it, don't read it.
	
		Transmit:
	
		The 9th bit is always sent. It is either the result of a parity calculation on the transmit data byte or it is the value set in the parity select bit in the control register.
		The choice is made by the parity enable bit in the control byte. For example :
		If PAREN is '1' and PAREVEN is '0', then the 9th bit will be the result of an 'odd' parity calculation on the transmit data byte.
		If PAREN is '0', then the 9th bit will be whatever the state of PAREVEN is.
	

We have just discovered that the calculation for parity includes the parity bit itself. Most of us don't like that, but it is too late to change it.
Link to comment
Share on other sites

It would be nice to get some stream like "ABABABABABABABAB" transmitted and measured with a scope for all settings SPACE, MARK, EVEN and ODD. After that we would know how the Lynx works.

 

But I agree that your data capture shows that the ODD parity works as it should. That is really not what I was expecting.

 

--

Karri

Link to comment
Share on other sites

Well, it took me a few tries, but I managed to analyze this.

Here are my results of the logic analyzer measurements: odd, even, mark and space parity work as they should (on a Lynx 2). I still have to test on a Lynx 1 though.

These are some code fragments I used to test this with:

void parity_check(unsigned char parity, char* parity_text)
{
	char count;
	char SERCTLtext[4];

	tgi_clear();
	tgi_outtextxy(0, 10, "Parity set as:");
	tgi_outtextxy(0, 20, parity_text);
	ser_close();

	open_comlynx(parity);
	itoa(contrl, SERCTLtext, 16);
	tgi_outtextxy(0, 30, SERCTLtext);

	tgi_updatedisplay();
	while (tgi_busy()) ;

	for (count = 0; count < 5; count++)
	{
		ser_put('A');
		ser_put('C');
	}
}

And also:

void open_comlynx(unsigned char parity)
{
	struct ser_params params = {
		SER_BAUD_9600,
		SER_BITS_8,     // only 8 data bits is supported
		SER_STOP_1,     // only 1 stop bit is supported
		SER_PAR_MARK,		// mark, space, even, odd is supported
		SER_HS_NONE     // only "none" is supported
	};
	params.parity = parity;
	ser_open(&params);
}

Finally, in main:

parity_check(SER_PAR_ODD, "Odd");
wait_joystick();
parity_check(SER_PAR_EVEN, "Even");
wait_joystick();
parity_check(SER_PAR_SPACE, "Space");
wait_joystick();
parity_check(SER_PAR_MARK, "Mark");
wait_joystick();

At first I was seeing that either odd or even was successful, but also mark and space were (one of them no errors, other all errors). It took me a while to notice that A is 0x41 and B is 0x42.This means that both values have an even number of 2 bits. I changed that to A and C, with A even bits (2), and C (0x43) odd bits (3). Then I saw the expected error/ok/error/ok pattern for both mark and space.

post-27403-0-43519200-1406066454_thumb.jpg

I have attached the measurements in a zip file in the logicdata format of the Logic 1.1.20 tool, that you can download for free from Saleae. https://www.saleae.com/downloads

Also attached the LNX file (remove .txt file extension) and the Visual Studio 2012/2013 source files. I did some hacking of the lynx-comlynx file to export the _contrl variable that is internal for setting the parity stuff in SERCTL. I wanted to make sure that it did so correctly, which it does.

Anyone is free to inspect and verify my findings. But, it seems that the UART serial in the Lynx (2 at least, 1 potentially) is fully functional.

ComLynxParityExperiments.zip

tutorial-comlynx.lnx.txt

Comlynx.zip

Edited by LX.NET
Link to comment
Share on other sites

There has been a few ComLynx engines floating around. I wonder if it would make sense to create a "standard" protocol for ComLynxing stuff between Lynxes and PC's.

 

Perhaps something like

 

 

message Player {
  required unsigned char lynx_id = 1;
  required string name = 2;
}
 
player.set_lynx_id = 0;
player.set_name = "Karri";
 
player.serializeToComLynx();
 
 
...
 
 
if (player.parseFromComLynx()) {
  opponent.id = player.lynx_id();
  opponent.name = player.name();
}

 

Perhaps written in C-syntax and not objects as above.

 

--

Karri

Link to comment
Share on other sites

  • 9 years later...

For the past weeks I have been working on some updates in cc65 and focussed on serial communication first. I found this thread and it seems relevant again. I have some new findings on the behavior of the Lynx and ComLynx/UART.

  • When sending data, the Lynx seems to respect all settings of even, odd, mark and space.
  • On receiving the parity calculation is always done, even if parity is not enabled. It seems to be calculated correctly. 

 

From the EPYX documentation:
"The state of the 9th bit is always available for read in the control byte. In addition, the parity of the received character is calculated and if it does not match the setting of the parity select bit in the control byte, the parity error bit will be set. Receive parity error can not be disabled. If you don't want it, don't read it" 

Control byte is SERCTL. The parity is available as byte 0 by reading from it. SERCTL surfaces three errors PARERR, 

 

Conclusions so far:

  • Sending: PAREN is relevant and enables parity calculation if set. PAREVEN will be even parity, not setting PAREVEN means odd parity. If PAREN is not set, PAREVEN set will be MARK, and SPACE otherwise.
  • Receiving: PAREN is irrelevant. Parity calculation is always done based on PAREVEN (or omission of it) for even/odd parity. It sets the PARERR bit in SERCTL when the calculation is failing. Calculations seems to be correct, despite the documentation stating that they made a mistake there.

 

Especially with the second conclusion, the current implementation of lynx-comlynx.s is not correct. It always checks for PARERR as one of the errors, which should not be done for SER_PAR_SPACE and SER_PAR_MARK. Instead it should check the parity bit by reading SERCTL against a 1 (for MARK) or 0 (for SPACE).

 

; Excerpt from SER_IRQ in lynx-comlynx.s

ldx     SERDAT
lda     SERCTL
and     #PARERR|OVERRUN|FRAMERR|RXBRK
beq     @rx_irq
tsb     SerialStat  ; Save error condition
bit     #RXBRK

 

I might need some help in writing a proper control flow for the part above to not check for PARERR in SERCTL when receiving for the SER_PAR_MARK and SER_PAR_SPACE settings, and instead check the PARBIT (0x01, byte 0) by reading SERCTL and comparing it to 1 for MARK and 0 for SPACE and discard if different.

Edited by LX.NET
  • Like 1
Link to comment
Share on other sites

Also, I think that quite a few of the baud rates are incorrect. Working on improving that as well, including checking it on real hardware.

 

ldx     #2
cmp     #SER_BAUD_31250
beq     setbaudrate

 

Especially here, it states that for 31250 baud, the calculation is off. The LDX should be #3 to get a countdown of 3 + 1 (for underflowing from 0 to virtual -1) before reload. 

The baud rate then becomes 1MHz/8 bits = 125000 bits per loop, which is 125000/4 = 31250 baud. 

For #2 this will be correct 125000/3 = 41667 baud.

 

Also, for any other source period, some of the calculations are off as well. 

I will construct a pull request to improve the setting of baud rates.  

  • Like 1
Link to comment
Share on other sites

I believed that the parity was wrong for a long time until I tried to communicate with a PC.

A really long time ago I wrote a Windows 3.1 program for communicating with the Lynx. It even had automatic BLL loading. For some strange reason it worked with all Windows versions during the years. This works with any USB serial dongle using FT232 kind of chips. So the 62500 and 31250 speeds were also tested with it.

 

The source for this is in the lynx/contrib/mttty directory in my repo.

Mttty.zip

Link to comment
Share on other sites

@42bs 
In your serial.inc code for new_bll it states: 

                ldx RxPtrIn
                sta RxBuffer,x
                txa
                inx

                cpx RxPtrOut
                beq .1
                stx RxPtrIn
                lda #$10
                sta $fd80
                END_IRQ

.1              sta RxPtrIn
                lda #$80
                tsb SerialStat

See also https://github.com/42Bastian/new_bll/blob/fd69e5e04581bf661b552010661374333c3b36e2/includes/serial.inc#L109

 

I believe this code stores the value of A (which is loaded prior with the value of SERDAT) in the circular buffer at RxPtrIn and then checks if we overrun the out pointer RxOutPtr. If so, it adds an error flag to SerialStat. 

 

Was it intentional to set this to #$80? Why not #$08, which corresponds to OVERRUN as in SERCTL?

 

Link to comment
Share on other sites

BTW, I do believe that the code in lynx-comlynx.s is flawed in a number of places.

 

1. Error on receive buffer overflow falls through into tx_irq routine and does not exit

        cpx     RxPtrOut
        beq     @1
        stx     RxPtrIn
        lda     #SERIAL_INTERRUPT
        sta     INTRST
        bra     @IRQexit

@1:
        sta     RxPtrIn
        lda     #$80
        tsb     SerialStat  ; <------ Should exit irq, right?
@tx_irq:
        ldx     TxPtrOut    ; Has all bytes been sent?
        cpx     TxPtrIn

See https://github.com/cc65/cc65/blob/3dfe0330003f19680f2afc9a607bea397b5c9fec/libsrc/lynx/ser/lynx-comlynx.s#L384

 

2. Baud rate for 31250 is incorrect as mentioned before

 

3. Duplicate code and inconsistent paths for resetting errors and ending IRQ 

 

4. Asking for serial status does not return actual SerialStat value?

SER_STATUS:
        ldy     SerialStat      ; <----- Should this be lda SerialStat???
        ldx     #$00
        sta     (ptr1,x)
        txa                     ; Return code = 0
        rts

 

 

I want to improve and fix the serial support, and verify my findings. All help is appreciated. 

Still working on a better version and running into more and more as I go along and understand everything better (at least, I think I do)

Edited by LX.NET
Link to comment
Share on other sites

I have implemented parity and mark/space checking for receiving bytes in the source code and started testing. 

Since I am novice at 6502 assembly, I would appreciate any and all of you checking the source code as it is now. I saw that the original implementation of lynx-comlynx.s was taken from @42bs serial.inc implementation.

 

You can find the new code here. 

Changes made:

  • Removed duplicate code (clearing errors for receive and (re)enabling RX-IRQ) 
  • Added mark/space parity checking
  • Fixed achievable baud rates. (Confirmed: 300, 600, 1200, 2400, 4800, 9600, 31250, 62500)
  • Removed baud rates 3600 and 7200, as these were not achievable according to my calculations
  • Discarded prescaler logic and changed selection of clock select and backup value for timer 4 in baud rate setting
  • Added logic to do a proper close on serial port

Still looking into low baud rates. 150 does not seem to work, so will check how the others are doing.

 

You can check the code here:

https://github.com/alexthissen/cc65/blob/serial/libsrc/lynx/ser/lynx-comlynx.s

https://github.com/alexthissen/cc65/blob/serial/asminc/lynx.inc

 

Once this checks out, I will submit a pull request to cc65.

Thanks. 

Edited by LX.NET
  • Like 1
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...