Even better news! I got it to work on my real Amiga... and in the process figured out how to send CTRL data reliably over serial.
Here is the code I finally came up with. This is C code, designed to be compiled with GCC under Mingw32 (yes, I actually wrote this for Windows - let me know if you need help getting it up and running) will send the CTRL command over the COM1 serial port.
Code: Select all
#include <windows.h>
#include <stdint.h>
HANDLE hSerial;
void uSleep(int waitTime);
void CTRL(int val) {
if (val) EscapeCommFunction(hSerial, SETRTS);
else EscapeCommFunction(hSerial, CLRRTS);
uSleep(3177);
}
void CTRLGroup(int r, int s) {
int i;
for (i=0;i<5;i++) {
CTRL(r);
}
for (i=0;i<5;i++) {
CTRL(s);
}
}
void sendByte(int8_t byt) {
CTRL(1); CTRL(1); CTRL(1); CTRL(1); CTRL(1); CTRL(1); // start
CTRLGroup(1, !(byt&1)>0); // start
CTRLGroup(!(byt&1)>0, !(byt&2)>0); // data
CTRLGroup(!(byt&2)>0, !(byt&4)>0); // data
CTRLGroup(!(byt&4)>0, !(byt&8)>0); // data
CTRLGroup(!(byt&8)>0, !(byt&16)>0); // data
CTRLGroup(!(byt&16)>0, !(byt&32)>0); // data
CTRLGroup(!(byt&32)>0, !(byt&64)>0); // data
CTRLGroup(!(byt&64)>0, !(byt&128)>0); // data
CTRLGroup(!(byt&128)>0, 0); // data
CTRLGroup(0, 0); // stop
}
int main(int argc, char *argv[]) {
// Set up serial
LPCSTR portname = "COM1";
DWORD accessdirection = GENERIC_WRITE;
hSerial = CreateFile(portname, accessdirection, 0, 0, OPEN_EXISTING, 0, 0);
DCB dcbSerialParams = {0};
dcbSerialParams.DCBlength = sizeof(dcbSerialParams);
GetCommState(hSerial, &dcbSerialParams);
dcbSerialParams.BaudRate = 2400;
dcbSerialParams.ByteSize = 8;
dcbSerialParams.StopBits = ONESTOPBIT;
dcbSerialParams.Parity = NOPARITY;
dcbSerialParams.fRtsControl = RTS_CONTROL_ENABLE;
SetCommState(hSerial, &dcbSerialParams);
COMMTIMEOUTS timeouts = {0};
timeouts.WriteTotalTimeoutConstant = 50;
timeouts.WriteTotalTimeoutMultiplier = 10;
SetCommTimeouts(hSerial, &timeouts);
sendByte(0x05);
sendByte(0x01);
sendByte(0x01);
sendByte(0x0D);
sendByte(0x08);
CloseHandle(hSerial);
return 1;
}
void uSleep(int waitTime) {
__int64 time1 = 0, time2 = 0, sysFreq = 0;
QueryPerformanceCounter((LARGE_INTEGER *) &time1);
QueryPerformanceFrequency((LARGE_INTEGER *)&sysFreq);
do {
QueryPerformanceCounter((LARGE_INTEGER *) &time2);
} while((time2-time1) < waitTime);
}
The important parts of this code are the sendByte function - which I figured out a while ago by reversing the CTRL I/O functions in ESQ - and the delay interval. The interval that I found to work was 3177 microseconds, which is probably pretty flexible, by +/- 10 microseconds maybe. I found this by writing code to send the 0501010D08 command repeatedly, incrementing the interval each time, and repeatedly pressing the L and R keys on the keyboard. Once it started drawing the overlay, I stopped the code and looked at the log, where it had been outputting each tested interval. This seems to be considerably less than what I would have expected for 110 baud - it's something like 25 BITS per second! The CTRL I/O routines therefore run about 325 times per second.

(The overlay on the left is drawn by my old Prevue simulator, while the overlay on the right is drawn by ESQ.)