2024-03-19 22:24:30 +01:00
|
|
|
|
/*
|
|
|
|
|
* Serial.c
|
|
|
|
|
*
|
|
|
|
|
* This file is part of Emu48
|
|
|
|
|
*
|
|
|
|
|
* Copyright (C) 1998 Christoph Gie<EFBFBD>elink
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
#include "pch.h"
|
|
|
|
|
#include "Emu48.h"
|
2024-03-19 22:25:45 +01:00
|
|
|
|
#include "io.h" // 24.10.99 cg, renamed from Serial.h
|
2024-03-19 22:24:30 +01:00
|
|
|
|
|
|
|
|
|
#define INTERRUPT ((void)(Chipset.SoftInt=TRUE,bInterrupt=TRUE))
|
|
|
|
|
|
2024-03-19 22:25:45 +01:00
|
|
|
|
// 25.10.99 cg, new, state of USRQ
|
|
|
|
|
#define NINT2ERBZ ((Chipset.IORam[IOC] & ERBZ) != 0 && (Chipset.IORam[RCS] & RBZ) != 0)
|
|
|
|
|
#define NINT2ERBF ((Chipset.IORam[IOC] & ERBF) != 0 && (Chipset.IORam[RCS] & RBF) != 0)
|
|
|
|
|
#define NINT2ETBE ((Chipset.IORam[IOC] & ETBE) != 0 && (Chipset.IORam[TCS] & TBF) == 0)
|
|
|
|
|
|
|
|
|
|
#define NINT2USRQ (NINT2ERBZ || NINT2ERBF || NINT2ETBE)
|
|
|
|
|
|
2024-03-19 22:24:30 +01:00
|
|
|
|
static OVERLAPPED os = { 0 };
|
|
|
|
|
static HANDLE hComm = NULL;
|
|
|
|
|
static HANDLE hCThread = NULL;
|
|
|
|
|
static DWORD lSerialThreadId = 0;
|
|
|
|
|
static BOOL bReading = TRUE;
|
|
|
|
|
static WORD wPort = PORT_CLOSE;
|
|
|
|
|
|
2024-03-19 22:25:45 +01:00
|
|
|
|
static BYTE cBuffer[128];
|
|
|
|
|
static WORD nRp;
|
|
|
|
|
static DWORD dwBytesRead = 0L;
|
|
|
|
|
|
|
|
|
|
// static CRITICAL_SECTION csRecv; // 24.10.99 cg, moved to main function
|
2024-03-19 22:24:30 +01:00
|
|
|
|
|
|
|
|
|
static DWORD WINAPI SerialThread(LPVOID pParam)
|
|
|
|
|
{
|
|
|
|
|
DWORD dwEvent;
|
|
|
|
|
SetCommMask(hComm,EV_RXCHAR); // event on RX
|
|
|
|
|
while (bReading)
|
|
|
|
|
{
|
|
|
|
|
_ASSERT(hComm != NULL);
|
|
|
|
|
WaitCommEvent(hComm,&dwEvent,NULL); // wait for serial event
|
|
|
|
|
if (dwEvent & EV_RXCHAR) // signal char received
|
|
|
|
|
{
|
|
|
|
|
CommReceive(); // get data
|
|
|
|
|
// interrupt request and emulation thread down
|
|
|
|
|
if (Chipset.SoftInt && Chipset.Shutdn)
|
2024-03-19 22:25:45 +01:00
|
|
|
|
{
|
|
|
|
|
Chipset.bShutdnWake = TRUE; // wake up from SHUTDN mode
|
|
|
|
|
SetEvent(hEventShutdn); // wake up emulation thread
|
|
|
|
|
}
|
2024-03-19 22:24:30 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
lSerialThreadId = 0; // signal serial thread is down
|
|
|
|
|
return 0;
|
|
|
|
|
UNREFERENCED_PARAMETER(pParam);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
WORD CommConnect(VOID)
|
|
|
|
|
{
|
|
|
|
|
return wPort;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID CommOpen(LPSTR strWirePort,LPSTR strIrPort)
|
|
|
|
|
{
|
|
|
|
|
COMMTIMEOUTS CommTimeouts = { MAXDWORD, 0L, 0L, 0L, 0L };
|
|
|
|
|
|
|
|
|
|
LPSTR strPort = (Chipset.IORam[IR_CTRL] & EIRU) ? strIrPort : strWirePort;
|
|
|
|
|
|
2024-03-19 22:25:45 +01:00
|
|
|
|
_ASSERT(Chipset.IORam[IOC] & SON); // UART on
|
2024-03-19 22:24:30 +01:00
|
|
|
|
if (hComm != NULL) // port already open
|
|
|
|
|
CloseHandle(hComm);
|
|
|
|
|
|
2024-03-19 22:25:45 +01:00
|
|
|
|
if (strcmp(strPort, NO_SERIAL)) // port defined
|
2024-03-19 22:24:30 +01:00
|
|
|
|
{
|
|
|
|
|
hComm = CreateFile(strPort,
|
|
|
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
|
|
|
0,
|
|
|
|
|
NULL,
|
|
|
|
|
OPEN_EXISTING,
|
|
|
|
|
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
|
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
|
|
if(hComm != INVALID_HANDLE_VALUE)
|
|
|
|
|
{
|
2024-03-19 22:25:45 +01:00
|
|
|
|
// InitializeCriticalSection(&csRecv);
|
2024-03-19 22:24:30 +01:00
|
|
|
|
|
|
|
|
|
wPort = (Chipset.IORam[IR_CTRL] & EIRU) ? PORT_IR : PORT_WIRE;
|
|
|
|
|
SetCommTimeouts(hComm,&CommTimeouts);
|
|
|
|
|
CommSetBaud();
|
|
|
|
|
|
|
|
|
|
// set event RXD handler
|
|
|
|
|
bReading = TRUE;
|
|
|
|
|
hCThread = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)&SerialThread,NULL,0,&lSerialThreadId);
|
|
|
|
|
_ASSERT(lSerialThreadId);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
hComm = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if defined DEBUG_SERIAL
|
|
|
|
|
{
|
|
|
|
|
char buffer[256];
|
|
|
|
|
wsprintf(buffer,"COM port %s.\n",hComm ? "opened": "open error");
|
|
|
|
|
OutputDebugString(buffer);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
2024-03-19 22:25:45 +01:00
|
|
|
|
return;
|
2024-03-19 22:24:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID CommClose(VOID)
|
|
|
|
|
{
|
|
|
|
|
if (hComm != NULL) // port open
|
|
|
|
|
{
|
|
|
|
|
bReading = FALSE; // kill read thread
|
|
|
|
|
SetCommMask(hComm,0L); // clear all events and force WaitCommEvent to return
|
|
|
|
|
while (lSerialThreadId != 0) Sleep(0); // wait for termination
|
|
|
|
|
CloseHandle(hComm); // close port
|
|
|
|
|
hComm = NULL;
|
|
|
|
|
#if defined DEBUG_SERIAL
|
|
|
|
|
OutputDebugString("COM port closed.\n");
|
|
|
|
|
#endif
|
2024-03-19 22:25:45 +01:00
|
|
|
|
// DeleteCriticalSection(&csRecv);
|
2024-03-19 22:24:30 +01:00
|
|
|
|
wPort = PORT_CLOSE;
|
|
|
|
|
}
|
2024-03-19 22:25:45 +01:00
|
|
|
|
return;
|
2024-03-19 22:24:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID CommSetBaud(VOID)
|
|
|
|
|
{
|
|
|
|
|
if (hComm != NULL)
|
|
|
|
|
{
|
|
|
|
|
const DWORD dwBaudrates[] = { 1200, 1920, 2400, 3840, 4800, 7680, 9600, 15360 };
|
|
|
|
|
|
|
|
|
|
DCB dcb;
|
|
|
|
|
|
|
|
|
|
FillMemory(&dcb,sizeof(dcb),0);
|
|
|
|
|
dcb.DCBlength = sizeof(dcb);
|
|
|
|
|
dcb.BaudRate = dwBaudrates[Chipset.IORam[BAUD] & 0x7];
|
|
|
|
|
dcb.fBinary = TRUE;
|
|
|
|
|
dcb.fParity = TRUE;
|
|
|
|
|
dcb.fOutxCtsFlow = FALSE;
|
|
|
|
|
dcb.fOutxDsrFlow = FALSE;
|
|
|
|
|
dcb.fDtrControl = DTR_CONTROL_DISABLE;
|
|
|
|
|
dcb.fDsrSensitivity = FALSE;
|
|
|
|
|
dcb.fOutX = FALSE;
|
|
|
|
|
dcb.fErrorChar = FALSE;
|
|
|
|
|
dcb.fNull = FALSE;
|
|
|
|
|
dcb.fRtsControl = RTS_CONTROL_DISABLE;
|
|
|
|
|
dcb.fAbortOnError = FALSE; // may changed in further implementations
|
|
|
|
|
dcb.ByteSize = 8;
|
|
|
|
|
dcb.Parity = NOPARITY; // no parity check, emulated by software
|
|
|
|
|
dcb.StopBits = ONESTOPBIT;
|
|
|
|
|
|
|
|
|
|
#if defined DEBUG_SERIAL
|
|
|
|
|
{
|
|
|
|
|
char buffer[256];
|
|
|
|
|
wsprintf(buffer,"CommsetBaud: %ld\n",dcb.BaudRate);
|
|
|
|
|
OutputDebugString(buffer);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
SetCommState(hComm,&dcb);
|
|
|
|
|
}
|
2024-03-19 22:25:45 +01:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID UpdateUSRQ(VOID) // 25.10.99 cg, new, USRQ handling
|
|
|
|
|
{
|
|
|
|
|
IOBit(SRQ1,USRQ,NINT2USRQ); // update USRQ bit
|
|
|
|
|
return;
|
2024-03-19 22:24:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID CommTransmit(VOID)
|
|
|
|
|
{
|
|
|
|
|
DWORD dwWritten;
|
|
|
|
|
|
|
|
|
|
BYTE tbr = (Chipset.IORam[TBR_MSB] << 4) | Chipset.IORam[TBR_LSB];
|
|
|
|
|
|
|
|
|
|
#if defined DEBUG_SERIAL
|
|
|
|
|
{
|
|
|
|
|
char buffer[256];
|
|
|
|
|
if (isprint(tbr))
|
|
|
|
|
wsprintf(buffer,"-> '%c'\n",tbr);
|
|
|
|
|
else
|
|
|
|
|
wsprintf(buffer,"-> %02X\n",tbr);
|
|
|
|
|
OutputDebugString(buffer);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2024-03-19 22:25:45 +01:00
|
|
|
|
// 23.10.99 cg, bugfix, add serial loopback support
|
|
|
|
|
if (Chipset.IORam[TCS] & LPB) // is loopback bit set
|
|
|
|
|
{
|
|
|
|
|
cBuffer[nRp+dwBytesRead] = tbr; // save character in receive buffer
|
|
|
|
|
++dwBytesRead;
|
|
|
|
|
|
|
|
|
|
CommReceive(); // receive byte available
|
|
|
|
|
}
|
|
|
|
|
// 23.10.99 cg, end of implementation
|
|
|
|
|
|
|
|
|
|
if (hComm != NULL) // com port open
|
2024-03-19 22:24:30 +01:00
|
|
|
|
WriteFile(hComm,(LPCVOID) &tbr,1,&dwWritten,&os);
|
|
|
|
|
|
|
|
|
|
Chipset.IORam[TCS] &= (~TBF); // clear transmit buffer
|
2024-03-19 22:25:45 +01:00
|
|
|
|
UpdateUSRQ(); // 25.10.99 cg, bugfix, update USRQ bit
|
|
|
|
|
if (Chipset.IORam[IOC] & ETBE) // interrupt on transmit buffer empty
|
2024-03-19 22:24:30 +01:00
|
|
|
|
INTERRUPT;
|
2024-03-19 22:25:45 +01:00
|
|
|
|
return;
|
2024-03-19 22:24:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID CommReceive(VOID)
|
|
|
|
|
{
|
2024-03-19 22:25:45 +01:00
|
|
|
|
if (!(Chipset.IORam[IOC] & SON)) // UART off
|
2024-03-19 22:24:30 +01:00
|
|
|
|
{
|
|
|
|
|
dwBytesRead = 0L; // no bytes received
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-19 22:25:45 +01:00
|
|
|
|
EnterCriticalSection(&csRecvLock);
|
2024-03-19 22:24:30 +01:00
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
if (Chipset.IORam[RCS] & RBF) // receive buffer full
|
|
|
|
|
break;
|
|
|
|
|
|
2024-03-19 22:25:45 +01:00
|
|
|
|
// 23.10.99 cg, bugfix, reject reading if com port is closed and not whole operation
|
|
|
|
|
if (hComm && dwBytesRead == 0L) // com port open and buffer empty
|
2024-03-19 22:24:30 +01:00
|
|
|
|
{
|
|
|
|
|
if(ReadFile(hComm,&cBuffer,sizeof(cBuffer),&dwBytesRead,&os) == FALSE)
|
|
|
|
|
dwBytesRead = 0L;
|
|
|
|
|
else // bytes received
|
|
|
|
|
nRp = 0; // reset read pointer
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(dwBytesRead == 0L) // receive buffer empty
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
#if defined DEBUG_SERIAL
|
|
|
|
|
{
|
|
|
|
|
char buffer[256];
|
|
|
|
|
if (isprint(cBuffer[nRp]))
|
|
|
|
|
wsprintf(buffer,"<- '%c'\n",cBuffer[nRp]);
|
|
|
|
|
else
|
|
|
|
|
wsprintf(buffer,"<- %02X\n",cBuffer[nRp]);
|
|
|
|
|
OutputDebugString(buffer);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
Chipset.IORam[RBR_MSB] = (cBuffer[nRp] >> 4);
|
|
|
|
|
Chipset.IORam[RBR_LSB] = (cBuffer[nRp] & 0x0f);
|
|
|
|
|
++nRp;
|
|
|
|
|
--dwBytesRead;
|
|
|
|
|
|
|
|
|
|
Chipset.IORam[RCS] |= RBF; // receive buffer full
|
2024-03-19 22:25:45 +01:00
|
|
|
|
UpdateUSRQ(); // 25.10.99 cg, bugfix, update USRQ bit
|
|
|
|
|
if (Chipset.IORam[IOC] & ERBF) // interrupt on recv buffer full
|
2024-03-19 22:24:30 +01:00
|
|
|
|
INTERRUPT;
|
|
|
|
|
}
|
|
|
|
|
while(0);
|
2024-03-19 22:25:45 +01:00
|
|
|
|
LeaveCriticalSection(&csRecvLock);
|
|
|
|
|
return;
|
2024-03-19 22:24:30 +01:00
|
|
|
|
}
|