/* * Serial.c * * This file is part of Emu48 * * Copyright (C) 1998 Christoph Gießelink * */ #include "pch.h" #include "Emu48.h" #include "io.h" // 24.10.99 cg, renamed from Serial.h #define INTERRUPT ((void)(Chipset.SoftInt=TRUE,bInterrupt=TRUE)) // 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) 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; static BYTE cBuffer[128]; static WORD nRp; static DWORD dwBytesRead = 0L; // static CRITICAL_SECTION csRecv; // 24.10.99 cg, moved to main function 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) { Chipset.bShutdnWake = TRUE; // wake up from SHUTDN mode SetEvent(hEventShutdn); // wake up emulation thread } } } 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; _ASSERT(Chipset.IORam[IOC] & SON); // UART on if (hComm != NULL) // port already open CloseHandle(hComm); if (strcmp(strPort, NO_SERIAL)) // port defined { hComm = CreateFile(strPort, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL); if(hComm != INVALID_HANDLE_VALUE) { // InitializeCriticalSection(&csRecv); 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 return; } 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 // DeleteCriticalSection(&csRecv); wPort = PORT_CLOSE; } return; } 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); } return; } VOID UpdateUSRQ(VOID) // 25.10.99 cg, new, USRQ handling { IOBit(SRQ1,USRQ,NINT2USRQ); // update USRQ bit return; } 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 // 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 WriteFile(hComm,(LPCVOID) &tbr,1,&dwWritten,&os); Chipset.IORam[TCS] &= (~TBF); // clear transmit buffer UpdateUSRQ(); // 25.10.99 cg, bugfix, update USRQ bit if (Chipset.IORam[IOC] & ETBE) // interrupt on transmit buffer empty INTERRUPT; return; } VOID CommReceive(VOID) { if (!(Chipset.IORam[IOC] & SON)) // UART off { dwBytesRead = 0L; // no bytes received return; } EnterCriticalSection(&csRecvLock); do { if (Chipset.IORam[RCS] & RBF) // receive buffer full break; // 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 { 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 UpdateUSRQ(); // 25.10.99 cg, bugfix, update USRQ bit if (Chipset.IORam[IOC] & ERBF) // interrupt on recv buffer full INTERRUPT; } while(0); LeaveCriticalSection(&csRecvLock); return; }