Larswad / uno2iec

A commodore (CBM) 1541 emulator on the Arduino Uno, using any desktop PC (or raspberry PI with raspbian) as a media host.
http://larswad.github.io/uno2iec/
134 stars 36 forks source link

WIP: Modifications for Teensy 2.0 on Linux #24

Open niclashoyer opened 4 years ago

niclashoyer commented 4 years ago

These are my modifications to get the emulator running on Linux with a Teensy 2.0 board (which was used as a cheap XUM1541 / ZoomFloppy before).

The first thing I noticed is that all line endings in the communication with a Linux device are \n, so I had to replace every \r with \n to get the initial connection / version string working.

The second big problem with a Teensy board is that the internal serial port via USB always uses 12 MB/s baud rate and thus is really really fast and also buffered by the USB chip. It breaks several timing write/read cycles, because after sending e.g. an L command there are simply no bytes to read. I had to add several while loops to wait for data using Serial.available.

Please take this as a hint. I just hacked this together to get it working, hence the WIP prefix. Maybe it is helpful to others.

Larswad commented 4 years ago

Many thanks Niclas for your contribution and time put down for this board, it's appreciated and I will do my best to get in the master. I will try to digest these changes, I have very quickly had a look now. One question I have is because I don't understand the reason for the line ending changes. While I agree the in a Linux context LF is preferred over CR (even though the pc side is meant to be platform independent), I would argue that it doesn't matter what character we chose, as long as the PC and Arduino/Teensy does the exact same thing. It is simply a termination character and could have been anything. Is this a change in correctness or was there a reason that made it work? Please explain that because it doesn't make any sense to me.

About the timing, yes, if there is a caching story in the teensy board I would expect there are places where "flushing" is needed. But otherwise, this simple messaging protocol is just more or less a ping pong game between the pc and arduino/teensy side. There are states on both sides, but I am not sure anything would timeout? Ok, I need to check for thorough your changes, that way I can extract the things that made it work. I need to be careful not to break other boards.

I see there are some pin changes (led I can understand, I would have to add that to configurable pins). But I hope you are aware that all other pins are configurable from the pc side. So default ones is not really needed to change. Best regards, Lars

niclashoyer commented 4 years ago

Regarding the line endings, I'm with you, as long as the Qt program and the avr firmware use the same character it shouldn't matter. I did not understand it either, because the first error messages I got were on connection, like in #22. Then I threw in a bunch of debug logs (as you can see) that logged all communication and some extra hints (like the connection string). I think the buffering on the com port has a special role here, because without changing everything to \n I got debug messages for received bytes like this:

connect_arduino:2\nconnect_arduino:2\nconnect_arduino:2\r

I really don't know if the buffer / usb serial firmware or QtSerial changed the line endings in the received message. As you can see one read in the host program returns three actual messages from the mcu, even with flushing on the mcu side. So maybe QtSerial has a buffer, too?

Regarding the timing issue, what I think happens is the following. The mcu sends a command, e.g. L. This command is buffered. Now I added Serial.flush and the Teensyduino has an extra special command Serial.send_now to bypass the usb buffer. But even then the readBytes in the following code would return wrong results (I checked that using debug responses):

COMPORT.write('L'); // initiate request.
COMPORT.send_now();
COMPORT.flush();
COMPORT.readBytes(serCmdIOBuf, 2);

As far as I understood the usb/serial implementation using 12MB/s is so fast that even when flushing everything COMPORT.readBytes still returns old results from the buffer, because the pc has not sent anything yet. So I added a while loop that just waits for data in the buffer with Serial.available.

COMPORT.write('L'); // initiate request.
COMPORT.send_now();
COMPORT.flush();
while (COMPORT.available() == 0);
COMPORT.readBytes(serCmdIOBuf, 2);

After that change I finally got the message LOADING on the C64, but the mcu responded weird error messages regarding received != actual bytes.

The last thing that prevented sending the floppy image successfully where some buffer issues. I saw that the constant MAX_BYTES_PER_REQUEST is used. Is that set somewhere? For me this was always 0 and thus did not work. I just added a bigger buffer (256 bytes) and later found in the documentation of the Arduino-Serial implementation that the buffer is 64 bytes long, so I hard coded that for now.

Also sorry for the wrong indentation. It was way past midnight yesterday and I just wanted to get it working :roller_coaster: I just bought my C64 two days ago and needed something until I get hold of some real floppy disks :floppy_disk:

I'm happy to test anything if you have changes ready.