From a78f1e04da0f5b87116af6e3c4d8dd73f282129a Mon Sep 17 00:00:00 2001 From: TomCh Date: Sun, 8 Apr 2018 17:37:26 +0100 Subject: [PATCH] SSC: Support for TX IRQ and other SSC updates (#552) . Support TX IRQ for both TCP and COM modes (fixes #522) . Support CTS/RTS (fixes #311) . Fixed reading DIPSW registers . Fixed TCP mode which was only generating an IRQ for first byte received . Added UpdateCommandReg() to consolidate all updates to SSC command reg . Updated reference URLs --- source/Applewin.cpp | 3 - source/Frame.cpp | 3 - source/Log.cpp | 3 +- source/Log.h | 2 + source/SaveState.cpp | 2 + source/SerialComms.cpp | 516 +++++++++++++++++++++-------------------- source/SerialComms.h | 29 ++- 7 files changed, 286 insertions(+), 272 deletions(-) diff --git a/source/Applewin.cpp b/source/Applewin.cpp index 3978d97d..37909826 100644 --- a/source/Applewin.cpp +++ b/source/Applewin.cpp @@ -89,7 +89,6 @@ static double g_fMHz = 1.0; // Affected by Config dialog's speed slider bar int g_nCpuCyclesFeedback = 0; DWORD g_dwCyclesThisFrame = 0; -FILE* g_fh = NULL; bool g_bDisableDirectInput = false; bool g_bDisableDirectSound = false; bool g_bDisableDirectSoundMockingboard = false; @@ -301,8 +300,6 @@ static void ContinueExecution(void) DiskUpdateDriveState(uActualCyclesExecuted); JoyUpdateButtonLatch(nExecutionPeriodUsec); // Button latch time is independent of CPU clock frequency - - sg_SSC.CommUpdate(uActualCyclesExecuted); PrintUpdate(uActualCyclesExecuted); // diff --git a/source/Frame.cpp b/source/Frame.cpp index 64e498e8..f2cb3e3f 100644 --- a/source/Frame.cpp +++ b/source/Frame.cpp @@ -1814,9 +1814,6 @@ LRESULT CALLBACK FrameWndProc ( case FD_READ: sg_SSC.CommTcpSerialReceive(); break; - - case FD_WRITE: - break; } } break; diff --git a/source/Log.cpp b/source/Log.cpp index f5988399..3c8753be 100644 --- a/source/Log.cpp +++ b/source/Log.cpp @@ -28,6 +28,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "StdAfx.h" +FILE* g_fh = NULL; //--------------------------------------------------------------------------- @@ -45,8 +46,6 @@ void LogOutput(LPCTSTR format, ...) //--------------------------------------------------------------------------- -extern FILE* g_fh; // Filehandle for log file - void LogFileOutput(LPCTSTR format, ...) { if (!g_fh) diff --git a/source/Log.h b/source/Log.h index 2f07c9f8..d97224de 100644 --- a/source/Log.h +++ b/source/Log.h @@ -8,5 +8,7 @@ #endif #endif +extern FILE* g_fh; // Filehandle for log file + extern void LogOutput(LPCTSTR format, ...); extern void LogFileOutput(LPCTSTR format, ...); diff --git a/source/SaveState.cpp b/source/SaveState.cpp index ad762717..d067a381 100644 --- a/source/SaveState.cpp +++ b/source/SaveState.cpp @@ -175,6 +175,7 @@ static void Snapshot_LoadState_v1() // .aws v1.0.0.1, up to (and including) Appl KeybReset(); VideoResetState(); MB_Reset(); + sg_SSC.CommReset(); // // Apple2 unit @@ -497,6 +498,7 @@ static void Snapshot_LoadState_v2(void) KeybReset(); VideoResetState(); MB_Reset(); + sg_SSC.CommReset(); #ifdef USE_SPEECH_API g_Speech.Reset(); #endif diff --git a/source/SerialComms.cpp b/source/SerialComms.cpp index 41be3fc4..12a3d3d5 100644 --- a/source/SerialComms.cpp +++ b/source/SerialComms.cpp @@ -26,35 +26,28 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * Author: Various */ -// TO DO: -// . Enable & test Tx IRQ -// . DIP switch read values -// - // Refs: -// (1) "Super Serial Card (SSC) Memory Locations for Programmers" - Aaron Heiss -// (2) SSC recv IRQ example: http://www.wright.edu/~john.matthews/ssc.html#lst - John B. Matthews, 5/13/87 -// (3) WaitCommEvent, etc: http://mail.python.org/pipermail/python-list/2002-November/131437.html -// (4) SY6551 info: http://www.axess.com/twilight/sock/rs232pak.html +// [Ref.1] AppleWin\docs\SSC Memory Locations for Programmers.txt +// [Ref.2] SSC recv IRQ example: https://sites.google.com/site/drjohnbmatthews/apple2/ssc - John B. Matthews, 5/13/87 +// [Ref.3] SY6551 info: http://users.axess.com/twilight/sock/rs232pak.html // +// SSC-pg is an abbreviation for pages references to "Super Serial Card, Installation and Operating Manual" by Apple #include "StdAfx.h" #include "Applewin.h" #include "CPU.h" -#include "Disk.h" // DiskIsSpinning() #include "Frame.h" +#include "Log.h" #include "Memory.h" #include "SerialComms.h" #include "YamlHelper.h" #include "../resource/resource.h" -//#define SUPPORT_MODEM #define TCP_SERIAL_PORT 1977 // Default: 19200-8-N-1 -// Maybe a better default is: 9600-7-N-1 (for HyperTrm) SSC_DIPSW CSuperSerialCard::m_DIPSWDefault = { // DIPSW1: @@ -65,8 +58,8 @@ SSC_DIPSW CSuperSerialCard::m_DIPSWDefault = ONESTOPBIT, 8, // ByteSize NOPARITY, - true, // LF - false, // INT + false, // SW2-5: LF(0x0A). SSC-24: In Comms mode, SSC automatically discards LF immediately following CR + true, // SW2-6: Interrupts. SSC-47: Passes interrupt requests from ACIA to the Apple II. NB. Can't be read from software }; //=========================================================================== @@ -85,7 +78,6 @@ CSuperSerialCard::CSuperSerialCard() : m_hCommHandle = INVALID_HANDLE_VALUE; m_hCommListenSocket = INVALID_SOCKET; m_hCommAcceptSocket = INVALID_SOCKET; - m_dwCommInactivity = 0; m_hCommThread = NULL; @@ -101,21 +93,32 @@ void CSuperSerialCard::InternalReset() { GetDIPSW(); - m_bTxIrqEnabled = false; - m_bRxIrqEnabled = false; + // SY6551 datasheet: Hardware reset sets Control register to 0 - the DIPSW settings are not used by h/w to setup this register + m_uControlByte = 0; - m_bWrittenTx = false; + // SY6551 datasheet: Hardware reset sets Command register to 0 + // . NB. MOS6551 datasheet: Hardware reset: b#00000010 (so ACIA not init'd on IN#2!) + UpdateCommandReg(0); + + m_uBaudRate = CBR_19200; // Undefined, as CONTROL.CLK_SOURCE=0=External clock is not supported for SSC - so nominally use 19200 + m_uStopBits = ONESTOPBIT; + m_uByteSize = 8; + m_uParity = NOPARITY; + + // m_vuRxCurrBuffer = 0; m_vbTxIrqPending = false; m_vbRxIrqPending = false; + m_vbTxEmpty = true; m_qComSerialBuffer[0].clear(); m_qComSerialBuffer[1].clear(); m_qTcpSerialBuffer.clear(); m_uDTR = DTR_CONTROL_DISABLE; + m_uRTS = RTS_CONTROL_DISABLE; } CSuperSerialCard::~CSuperSerialCard() @@ -128,26 +131,12 @@ CSuperSerialCard::~CSuperSerialCard() // TODO: Serial Comms - UI Property Sheet Page: // . Ability to config the 2x DIPSWs - only takes affect after next Apple2 reset // . 'Default' button that resets DIPSWs to DIPSWDefaults -// . Need to respect IRQ disable dipswitch (cannot be overridden by software) +// . Must respect IRQ disable dipswitch (cannot be overridden or read by software) void CSuperSerialCard::GetDIPSW() { - // TODO: Read settings from Registry - // In the meantime, use the defaults: + // TODO: Read settings from Registry(?). In the meantime, use the defaults: SetDIPSWDefaults(); - - // - - m_uBaudRate = m_DIPSWCurrent.uBaudRate; - - m_uStopBits = m_DIPSWCurrent.uStopBits; - m_uByteSize = m_DIPSWCurrent.uByteSize; - m_uParity = m_DIPSWCurrent.uParity; - - // - - m_uControlByte = GenerateControl(); - m_uCommandByte = 0x00; } void CSuperSerialCard::SetDIPSWDefaults() @@ -166,23 +155,6 @@ void CSuperSerialCard::SetDIPSWDefaults() m_DIPSWCurrent.bInterrupts = m_DIPSWDefault.bInterrupts; } -BYTE CSuperSerialCard::GenerateControl() -{ - const UINT CLK=1; // Internal - - UINT bmByteSize = (8 - m_uByteSize); // [8,7,6,5] -> [0,1,2,3] - _ASSERT(bmByteSize <= 3); - - UINT StopBit; - if ( ((m_uByteSize == 8) && (m_uParity != NOPARITY)) || - ( m_uStopBits != ONESTOPBIT ) ) - StopBit = 1; - else - StopBit = 0; - - return (StopBit<<7) | (bmByteSize<<5) | (CLK<<4) | BaudRateToIndex(m_uBaudRate); -} - UINT CSuperSerialCard::BaudRateToIndex(UINT uBaudRate) { switch (uBaudRate) @@ -199,7 +171,8 @@ UINT CSuperSerialCard::BaudRateToIndex(UINT uBaudRate) } _ASSERT(0); - return BaudRateToIndex(CBR_9600); + LogFileOutput("SSC: BaudRateToIndex(): unsupported rate: %d\n", uBaudRate); + return BaudRateToIndex(CBR_19200); // nominally use 19200 } //=========================================================================== @@ -217,31 +190,29 @@ void CSuperSerialCard::UpdateCommState() dcb.ByteSize = m_uByteSize; dcb.Parity = m_uParity; dcb.StopBits = m_uStopBits; - dcb.fDtrControl = m_uDTR; + dcb.fDtrControl = m_uDTR; // GH#386 + dcb.fRtsControl = m_uRTS; // GH#311 SetCommState(m_hCommHandle,&dcb); } //=========================================================================== -BOOL CSuperSerialCard::CheckComm() +bool CSuperSerialCard::CheckComm() { - m_dwCommInactivity = 0; - // check for COM or TCP socket handle, and setup if invalid if (IsActive()) return true; if (m_dwSerialPortItem == m_uTCPChoiceItemIdx) { - // init Winsock 1.1 (for Win95, otherwise could use 2.2) WSADATA wsaData; - if (WSAStartup(MAKEWORD(1, 1), &wsaData) == 0) // or (2, 2) for Winsock 2.2 + if (WSAStartup(MAKEWORD(2, 2), &wsaData) == 0) // Winsock 2.2 { - if (wsaData.wVersion != 0x0101) // or 0x0202 for Winsock 2.2 + if (wsaData.wVersion != 0x0202) { WSACleanup(); - return FALSE; + return false; } // initialized, so try to create a socket @@ -249,7 +220,7 @@ BOOL CSuperSerialCard::CheckComm() if (m_hCommListenSocket == INVALID_SOCKET) { WSACleanup(); - return FALSE; + return false; } // have socket so attempt to bind it @@ -261,7 +232,7 @@ BOOL CSuperSerialCard::CheckComm() { m_hCommListenSocket = INVALID_SOCKET; WSACleanup(); - return FALSE; + return false; } // bound, so listen @@ -269,7 +240,7 @@ BOOL CSuperSerialCard::CheckComm() { m_hCommListenSocket = INVALID_SOCKET; WSACleanup(); - return FALSE; + return false; } // now send async events to our app's message handler @@ -281,7 +252,7 @@ BOOL CSuperSerialCard::CheckComm() { m_hCommListenSocket = INVALID_SOCKET; WSACleanup(); - return FALSE; + return false; } } } @@ -337,7 +308,6 @@ void CSuperSerialCard::CloseComm() CloseHandle(m_hCommHandle); m_hCommHandle = INVALID_HANDLE_VALUE; - m_dwCommInactivity = 0; } //=========================================================================== @@ -378,12 +348,14 @@ void CSuperSerialCard::CommTcpSerialAccept() if ((m_hCommListenSocket != INVALID_SOCKET) && (m_hCommAcceptSocket == INVALID_SOCKET)) { // Y: accept the connection - m_hCommAcceptSocket = accept(m_hCommListenSocket, NULL, NULL ); + m_hCommAcceptSocket = accept(m_hCommListenSocket, NULL, NULL); } } //=========================================================================== +// Called when there's a TCP event via the message pump +// . Because it's via the message pump, then this call is synchronous to CommReceive(), so there's no need for a critical section void CSuperSerialCard::CommTcpSerialReceive() { if (m_hCommAcceptSocket != INVALID_SOCKET) @@ -452,7 +424,7 @@ BYTE __stdcall CSuperSerialCard::SSC_IOWrite(WORD PC, WORD uAddr, BYTE bWrite, B case 0x6: return IO_Null(PC, uAddr, bWrite, uValue, nExecutedCycles); case 0x7: return IO_Null(PC, uAddr, bWrite, uValue, nExecutedCycles); case 0x8: return pSSC->CommTransmit(PC, uAddr, bWrite, uValue, nExecutedCycles); - case 0x9: return pSSC->CommStatus(PC, uAddr, bWrite, uValue, nExecutedCycles); + case 0x9: return pSSC->CommProgramReset(PC, uAddr, bWrite, uValue, nExecutedCycles); case 0xA: return pSSC->CommCommand(PC, uAddr, bWrite, uValue, nExecutedCycles); case 0xB: return pSSC->CommControl(PC, uAddr, bWrite, uValue, nExecutedCycles); case 0xC: return IO_Null(PC, uAddr, bWrite, uValue, nExecutedCycles); @@ -466,7 +438,98 @@ BYTE __stdcall CSuperSerialCard::SSC_IOWrite(WORD PC, WORD uAddr, BYTE bWrite, B //=========================================================================== -// EG. 0x09 = Enable IRQ, No parity [Ref.2] +// 6551 ACIA Command Register ($C08A+s0) +// . EG. 0x09 = "no parity, enable IRQ" - b7:5(No parity), b4 (No echo), b3:1(Enable TX,RX IRQs), b0(DTR: Enable Rx/Tx) [Ref.2] +enum { CMD_PARITY_MASK = 3<<6, + CMD_PARITY_ODD = 0<<6, // Odd parity + CMD_PARITY_EVEN = 1<<6, // Even parity + CMD_PARITY_MARK = 2<<6, // Mark parity + CMD_PARITY_SPACE = 3<<6, // Space parity + CMD_PARITY_ENA = 1<<5, + CMD_ECHO_MODE = 1<<4, + CMD_TX_MASK = 3<<2, + CMD_TX_IRQ_DIS_RTS_HIGH = 0<<2, + CMD_TX_IRQ_ENA_RTS_LOW = 1<<2, + CMD_TX_IRQ_DIS_RTS_LOW = 2<<2, + CMD_TX_IRQ_DIS_RTS_LOW_BRK = 3<<2, // Transmit BRK + CMD_RX_IRQ_DIS = 1<<1, // 1=IRQ interrupt disabled + CMD_DTR = 1<<0, // Data Terminal Ready: 1=Enable Rx/Tx (!DTR low) +}; + +BYTE __stdcall CSuperSerialCard::CommProgramReset(WORD, WORD, BYTE, BYTE, ULONG) +{ + // Command: top-3 parity bits unaffected + UpdateCommandReg( m_uCommandByte & (CMD_PARITY_MASK|CMD_PARITY_ENA) ); + + // Control: all bits unaffected + // Status: all bits unaffects, except Overrun(bit2) is cleared + + return 0; +} + +//=========================================================================== + +void CSuperSerialCard::UpdateCommandReg(BYTE command) +{ + m_uCommandByte = command; + + if (m_uCommandByte & CMD_PARITY_ENA) + { + switch (m_uCommandByte & CMD_PARITY_MASK) + { + case CMD_PARITY_ODD: m_uParity = ODDPARITY; break; + case CMD_PARITY_EVEN: m_uParity = EVENPARITY; break; + case CMD_PARITY_MARK: m_uParity = MARKPARITY; break; + case CMD_PARITY_SPACE: m_uParity = SPACEPARITY; break; + } + } + else + { + m_uParity = NOPARITY; + } + + if (m_uCommandByte & CMD_ECHO_MODE) // Receiver mode echo (0=no echo, 1=echo) + { + _ASSERT(0); + LogFileOutput("SSC: CommCommand(): unsupported Echo mode. Command=0x%02X\n", m_uCommandByte); + } + + switch (m_uCommandByte & CMD_TX_MASK) // transmitter interrupt control + { + // Note: the RTS signal must be set 'low' in order to receive any incoming data from the serial device [Ref.1] + case CMD_TX_IRQ_DIS_RTS_HIGH: // set RTS high and transmit no interrupts (transmitter is off [Ref.3]) + m_uRTS = RTS_CONTROL_DISABLE; + break; + case CMD_TX_IRQ_ENA_RTS_LOW: // set RTS low and transmit interrupts + m_uRTS = RTS_CONTROL_ENABLE; + break; + case CMD_TX_IRQ_DIS_RTS_LOW: // set RTS low and transmit no interrupts + m_uRTS = RTS_CONTROL_ENABLE; + break; + case CMD_TX_IRQ_DIS_RTS_LOW_BRK: // set RTS low and transmit break signals instead of interrupts + m_uRTS = RTS_CONTROL_ENABLE; + _ASSERT(0); + LogFileOutput("SSC: CommCommand(): unsupported TX mode. Command=0x%02X\n", m_uCommandByte); + break; + } + + if (m_DIPSWCurrent.bInterrupts) + { + m_bTxIrqEnabled = (m_uCommandByte & CMD_TX_MASK) == CMD_TX_IRQ_ENA_RTS_LOW; + m_bRxIrqEnabled = (m_uCommandByte & CMD_RX_IRQ_DIS) == 0; + } + else + { + m_bTxIrqEnabled = false; + m_bRxIrqEnabled = false; + } + + if (m_bCfgSupportDTR) // GH#386 + { + // Data Terminal Ready (DTR) setting (0=set DTR high (indicates 'not ready')) + m_uDTR = (m_uCommandByte & CMD_DTR) ? DTR_CONTROL_ENABLE : DTR_CONTROL_DISABLE; + } +} BYTE __stdcall CSuperSerialCard::CommCommand(WORD, WORD, BYTE write, BYTE value, ULONG) { @@ -475,55 +538,7 @@ BYTE __stdcall CSuperSerialCard::CommCommand(WORD, WORD, BYTE write, BYTE value, if (write && (value != m_uCommandByte)) { - m_uCommandByte = value; - - // UPDATE THE PARITY - if (m_uCommandByte & 0x20) - { - switch (m_uCommandByte & 0xC0) - { - case 0x00 : m_uParity = ODDPARITY; break; - case 0x40 : m_uParity = EVENPARITY; break; - case 0x80 : m_uParity = MARKPARITY; break; - case 0xC0 : m_uParity = SPACEPARITY; break; - } - } - else - { - m_uParity = NOPARITY; - } - - if (m_uCommandByte & 0x10) // Receiver mode echo [0=no echo, 1=echo] - { - } - - switch (m_uCommandByte & 0x0C) // transmitter interrupt control - { - // Note: the RTS signal must be set 'low' in order to receive any - // incoming data from the serial device - case 0<<2: // set RTS high and transmit no interrupts - m_bTxIrqEnabled = false; - break; - case 1<<2: // set RTS low and transmit interrupts - m_bTxIrqEnabled = true; - break; - case 2<<2: // set RTS low and transmit no interrupts - m_bTxIrqEnabled = false; - break; - case 3<<2: // set RTS low and transmit break signals instead of interrupts - m_bTxIrqEnabled = false; - break; - } - - // interrupt request disable [0=enable receiver interrupts] - NOTE: SSC docs get this wrong! - m_bRxIrqEnabled = ((m_uCommandByte & 0x02) == 0); - - if (m_bCfgSupportDTR) // GH#386 - { - // Data Terminal Ready (DTR) setting [0=set DTR high (indicates 'not ready')] - m_uDTR = (m_uCommandByte & 0x01) ? DTR_CONTROL_ENABLE : DTR_CONTROL_DISABLE; - } - + UpdateCommandReg(value); UpdateCommState(); } @@ -605,8 +620,6 @@ BYTE __stdcall CSuperSerialCard::CommControl(WORD, WORD, BYTE write, BYTE value, //=========================================================================== -static UINT g_uDbgTotalSSCRx = 0; - BYTE __stdcall CSuperSerialCard::CommReceive(WORD, WORD, BYTE, BYTE, ULONG) { if (!CheckComm()) @@ -616,8 +629,15 @@ BYTE __stdcall CSuperSerialCard::CommReceive(WORD, WORD, BYTE, BYTE, ULONG) if (!m_qTcpSerialBuffer.empty()) { + // NB. See CommTcpSerialReceive() above, for a note explaining why there's no need for a critical section here result = m_qTcpSerialBuffer.front(); m_qTcpSerialBuffer.pop_front(); + + if (m_bRxIrqEnabled && !m_qTcpSerialBuffer.empty()) + { + CpuIrqAssert(IS_SSC); + m_vbRxIrqPending = true; + } } else if (m_hCommHandle != INVALID_HANDLE_VALUE) // COM { @@ -646,8 +666,6 @@ BYTE __stdcall CSuperSerialCard::CommReceive(WORD, WORD, BYTE, BYTE, ULONG) } } LeaveCriticalSection(&m_CriticalSection); - - g_uDbgTotalSSCRx++; } return result; @@ -655,6 +673,17 @@ BYTE __stdcall CSuperSerialCard::CommReceive(WORD, WORD, BYTE, BYTE, ULONG) //=========================================================================== +void CSuperSerialCard::TransmitDone(void) +{ + m_vbTxEmpty = true; // Transmit done + + if (m_bTxIrqEnabled) // GH#522 + { + CpuIrqAssert(IS_SSC); + m_vbTxIrqPending = true; + } +} + BYTE __stdcall CSuperSerialCard::CommTransmit(WORD, WORD, BYTE, BYTE value, ULONG) { if (!CheckComm()) @@ -667,22 +696,22 @@ BYTE __stdcall CSuperSerialCard::CommTransmit(WORD, WORD, BYTE, BYTE value, ULON { data &= ~(1 << m_uByteSize); } - send(m_hCommAcceptSocket, (const char*)&data, 1, 0); - m_bWrittenTx = true; // Transmit done + int sent = send(m_hCommAcceptSocket, (const char*)&data, 1, 0); + if (sent == 1) + { + m_vbTxEmpty = false; + // Assume that send() completes immediately + TransmitDone(); + } } else if (m_hCommHandle != INVALID_HANDLE_VALUE) { DWORD uBytesWritten; WriteFile(m_hCommHandle, &value, 1, &uBytesWritten, &m_o); - m_bWrittenTx = true; // Transmit done + m_vbTxEmpty = false; + // NB. Now CommThread() determines when transmit buffer is empty and calls TransmitDone() } - // TO DO: - // 1) Use CommThread determine when transmit is complete - // 2) OR do this: - //if (m_bTxIrqEnabled) - // CpuIrqAssert(IS_SSC); - return 0; } @@ -691,24 +720,24 @@ BYTE __stdcall CSuperSerialCard::CommTransmit(WORD, WORD, BYTE, BYTE value, ULON // 6551 ACIA Status Register ($C089+s0) // ------------------------------------ // Bit Value Meaning -// 0 1 Parity error -// 1 1 Framing error -// 2 1 Overrun error -// 3 1 Receive register full -// 4 1 Transmit register empty -// 5 0 Data Carrier Detect (DCD) true [0=DCD low (detected), 1=DCD high (not detected)] +// 7 1 Interrupt (IRQ) true (cleared by reading status reg [Ref.3]) // 6 0 Data Set Ready (DSR) true [0=DSR low (ready), 1=DSR high (not ready)] -// 7 1 Interrupt (IRQ) true (cleared by reading status reg [Ref.4]) +// 5 0 Data Carrier Detect (DCD) true [0=DCD low (detected), 1=DCD high (not detected)] +// 4 1 Transmit register empty +// 3 1 Receive register full +// 2 1 Overrun error +// 1 1 Framing error +// 0 1 Parity error -enum { ST_PARITY_ERR = 1<<0, - ST_FRAMING_ERR = 1<<1, - ST_OVERRUN_ERR = 1<<2, - ST_RX_FULL = 1<<3, - ST_TX_EMPTY = 1<<4, - ST_DCD = 1<<5, +enum { ST_IRQ = 1<<7, ST_DSR = 1<<6, - ST_IRQ = 1<<7 - }; + ST_DCD = 1<<5, + ST_TX_EMPTY = 1<<4, + ST_RX_FULL = 1<<3, + ST_OVERRUN_ERR = 1<<2, + ST_FRAMING_ERR = 1<<1, + ST_PARITY_ERR = 1<<0, +}; BYTE __stdcall CSuperSerialCard::CommStatus(WORD, WORD, BYTE, BYTE, ULONG) { @@ -724,11 +753,6 @@ BYTE __stdcall CSuperSerialCard::CommStatus(WORD, WORD, BYTE, BYTE, ULONG) // - // TO DO - ST_TX_EMPTY: - // . IRQs enabled : set after WaitCommEvent has signaled that TX has completed - // . IRQs disabled : always set it [Currently done] - // - bool bComSerialBufferEmpty = true; // Assume true, so if using TCP then logic below works if (m_hCommHandle != INVALID_HANDLE_VALUE) @@ -738,54 +762,67 @@ BYTE __stdcall CSuperSerialCard::CommStatus(WORD, WORD, BYTE, BYTE, ULONG) bComSerialBufferEmpty = m_qComSerialBuffer[uSSCIdx].empty(); } - bool bIRQ = false; - if (m_bTxIrqEnabled && m_bWrittenTx) + BYTE IRQ = 0; + if (m_bTxIrqEnabled) { - bIRQ = true; + IRQ |= m_vbTxIrqPending ? ST_IRQ : 0; + m_vbTxIrqPending = false; // Ensure 2 reads of STATUS reg only return ST_IRQ for first read } if (m_bRxIrqEnabled) { - bIRQ = m_vbRxIrqPending; + IRQ |= m_vbRxIrqPending ? ST_IRQ : 0; m_vbRxIrqPending = false; // Ensure 2 reads of STATUS reg only return ST_IRQ for first read } - m_bWrittenTx = false; // Read status reg always clears IRQ - // - BYTE DCD = 0; BYTE DSR = 0; + BYTE DCD = 0; if ((m_hCommHandle != INVALID_HANDLE_VALUE) && (m_bCfgSupportDCD || m_bCfgSupportDSR)) // GH#386 { - if (m_bCfgSupportDCD) - DCD = (modemStatus & MS_RLSD_ON) ? 0x00 : ST_DCD; - if (m_bCfgSupportDSR) DSR = (modemStatus & MS_DSR_ON) ? 0x00 : ST_DSR; + + if (m_bCfgSupportDCD) + DCD = (modemStatus & MS_RLSD_ON) ? 0x00 : ST_DCD; } - BYTE uStatus = ST_TX_EMPTY - | ((!bComSerialBufferEmpty || !m_qTcpSerialBuffer.empty()) ? ST_RX_FULL : 0x00) - | DCD // Need 0x00 to allow ZLink to start up - | DSR - | (bIRQ ? ST_IRQ : 0x00); + // + + BYTE TX_EMPTY = m_vbTxEmpty ? ST_TX_EMPTY : 0; + BYTE RX_FULL = (!bComSerialBufferEmpty || !m_qTcpSerialBuffer.empty()) ? ST_RX_FULL : 0; + + // + + BYTE uStatus = + IRQ + | DSR + | DCD // Need 0x00 to allow ZLink to start up + | TX_EMPTY + | RX_FULL; if (m_hCommHandle != INVALID_HANDLE_VALUE) { LeaveCriticalSection(&m_CriticalSection); } - CpuIrqDeassert(IS_SSC); + CpuIrqDeassert(IS_SSC); // Read status reg always clears IRQ return uStatus; } //=========================================================================== +// NB. Some DIPSW settings can't be read: +// SSC-47: Three switches are not connected to the LS365s: +// . SW2-6: passes interrupt requests from ACIA to the Apple II +// . SW1-7 ON and SW2-7 OFF: connects DCD to the DCD input of the ACIA +// . SW1-7 OFF and SW2-7 ON: splices SCTS to the DCD input of the ACIA (when jumper is in TERMINAL position) BYTE __stdcall CSuperSerialCard::CommDipSw(WORD, WORD addr, BYTE, BYTE, ULONG) { BYTE sw = 0; + switch (addr & 0xf) { case 1: // DIPSW1 @@ -793,36 +830,51 @@ BYTE __stdcall CSuperSerialCard::CommDipSw(WORD, WORD addr, BYTE, BYTE, ULONG) break; case 2: // DIPSW2 - // Comms mode - SSC manual, pg23/24 - BYTE INT = m_DIPSWCurrent.uStopBits == TWOSTOPBITS ? 1 : 0; // SW2-1 (Stop bits: 1-ON(0); 2-OFF(1)) - BYTE DSR = 0; // Always zero - BYTE DCD = m_DIPSWCurrent.uByteSize == 7 ? 1 : 0; // SW2-2 (Data bits: 8-ON(0); 7-OFF(1)) - BYTE TDR = 0; // Always zero + // Comms mode: SSC-23 + BYTE SW2_1 = m_DIPSWCurrent.uStopBits == TWOSTOPBITS ? 1 : 0; // SW2-1 (Stop bits: 1-ON(0); 2-OFF(1)) + BYTE SW2_2 = m_DIPSWCurrent.uByteSize == 7 ? 1 : 0; // SW2-2 (Data bits: 8-ON(0); 7-OFF(1)) // SW2-3 (Parity: odd-ON(0); even-OFF(1)) - // SW2-4 (Parity: none-ON(0); SW2-3-OFF(1)) - BYTE RDR,OVR; + // SW2-4 (Parity: none-ON(0); SW2-3 don't care) + BYTE SW2_3,SW2_4; switch (m_DIPSWCurrent.uParity) { case ODDPARITY: - RDR = 0; OVR = 1; + SW2_3 = 0; SW2_4 = 1; break; case EVENPARITY: - RDR = 1; OVR = 1; + SW2_3 = 1; SW2_4 = 1; break; default: _ASSERT(0); + // fall through... case NOPARITY: - RDR = 0; OVR = 0; + SW2_3 = 0; SW2_4 = 0; break; } - BYTE FE = m_DIPSWCurrent.bLinefeed ? 1 : 0; // SW2-5 (LF: yes-ON(0); no-OFF(1)) - BYTE PE = m_DIPSWCurrent.bInterrupts ? 1 : 0; // SW2-6 (Interrupts: yes-ON(0); no-OFF(1)) + BYTE SW2_5 = m_DIPSWCurrent.bLinefeed ? 0 : 1; // SW2-5 (LF: yes-ON(0); no-OFF(1)) - sw = (INT<<7) | (DSR<<6) | (DCD<<5) | (TDR<<4) | (RDR<<3) | (OVR<<2) | (FE<<1) | (PE<<0); + BYTE CTS = 0; // GH#311 + if (CheckComm() && m_hCommHandle != INVALID_HANDLE_VALUE) + { + DWORD modemStatus = 0; + if (GetCommModemStatus(m_hCommHandle, &modemStatus)) + CTS = (modemStatus & MS_CTS_ON) ? 0 : 1; // CTS is true when 0 + } + + // SSC-54: + sw = SW2_1<<7 | // b7 : SW2-1 + 0<<6 | // b6 : - + SW2_2<<5 | // b5 : SW2-2 + 0<<4 | // b4 : - + SW2_3<<3 | // b3 : SW2-3 + SW2_4<<2 | // b2 : SW2-4 + SW2_5<<1 | // b1 : SW2-5 + CTS<<0; // b0 : CTS break; } + return sw; } @@ -911,35 +963,6 @@ void CSuperSerialCard::CommSetSerialPort(HWND hWindow, DWORD dwNewSerialPortItem //=========================================================================== -void CSuperSerialCard::CommUpdate(DWORD totalcycles) -{ - if (!IsActive()) - return; - - if ((m_dwCommInactivity += totalcycles) > 1000000) - { - static DWORD lastcheck = 0; - - if ((m_dwCommInactivity > 2000000) || (m_dwCommInactivity-lastcheck > 99950)) - { -#ifdef SUPPORT_MODEM - DWORD modemstatus = 0; - GetCommModemStatus(m_hCommHandle,&modemstatus); - if ((modemstatus & MS_RLSD_ON) || DiskIsSpinning()) - m_dwCommInactivity = 0; -#else - if (DiskIsSpinning()) - m_dwCommInactivity = 0; -#endif - } - - //if (m_dwCommInactivity > 2000000) - // CloseComm(); - } -} - -//=========================================================================== - // Had this error when sizeof(m_RecvBuffer)==1 was used // UPDATE: Fixed by using double-buffered queue // @@ -1016,25 +1039,26 @@ void CSuperSerialCard::CheckCommEvent(DWORD dwEvtMask) LeaveCriticalSection(&m_CriticalSection); } } - //else if (dwEvtMask & EV_TXEMPTY) - //{ - // if (m_bTxIrqEnabled) - // { - // m_vbTxIrqPending = true; - // CpuIrqAssert(IS_SSC); - // } - //} + else if (dwEvtMask & EV_TXEMPTY) + { + TransmitDone(); + } } DWORD WINAPI CSuperSerialCard::CommThread(LPVOID lpParameter) { CSuperSerialCard* pSSC = (CSuperSerialCard*) lpParameter; - char szDbg[100]; -// BOOL bRes = SetCommMask(pSSC->m_hCommHandle, EV_TXEMPTY | EV_RXCHAR); - BOOL bRes = SetCommMask(pSSC->m_hCommHandle, EV_RXCHAR); // Just RX + + BOOL bRes = SetCommMask(pSSC->m_hCommHandle, EV_TXEMPTY | EV_RXCHAR); +// BOOL bRes = SetCommMask(pSSC->m_hCommHandle, EV_RXCHAR); // Just RX if (!bRes) + { + sprintf(szDbg, "SSC: CommThread(): SetCommMask() failed\n"); + LogOutput("%s", szDbg); + LogFileOutput("%s", szDbg); return -1; + } // @@ -1060,16 +1084,12 @@ DWORD WINAPI CSuperSerialCard::CommThread(LPVOID lpParameter) DWORD dwErrors; COMSTAT Stat; ClearCommError(pSSC->m_hCommHandle, &dwErrors, &Stat); - if (dwErrors) - { - if (dwErrors & CE_RXOVER) - sprintf(szDbg, "CommThread: Err=CE_RXOVER (0x%08X): InQueue=0x%08X\n", dwErrors, Stat.cbInQue); - else - sprintf(szDbg, "CommThread: Err=Other (0x%08X): InQueue=0x%08X, OutQueue=0x%08X\n", dwErrors, Stat.cbInQue, Stat.cbOutQue); - OutputDebugString(szDbg); - if (g_fh) - fprintf(g_fh, "%s", szDbg); - } + if (dwErrors & CE_RXOVER) + sprintf(szDbg, "SSC: CommThread(): LastError=0x%08X, CommError=CE_RXOVER (0x%08X): InQueue=0x%08X\n", dwRet, dwErrors, Stat.cbInQue); + else + sprintf(szDbg, "SSC: CommThread(): LastError=0x%08X, CommError=Other (0x%08X): InQueue=0x%08X, OutQueue=0x%08X\n", dwRet, dwErrors, Stat.cbInQue, Stat.cbOutQue); + LogOutput("%s", szDbg); + LogFileOutput("%s", szDbg); return -1; } @@ -1153,8 +1173,9 @@ bool CSuperSerialCard::CommThInit() if (m_hCommThread == NULL) { - DWORD dwThreadId; + InitializeCriticalSection(&m_CriticalSection); + DWORD dwThreadId; m_hCommThread = CreateThread(NULL, // lpThreadAttributes 0, // dwStackSize (LPTHREAD_START_ROUTINE) &CSuperSerialCard::CommThread, @@ -1163,8 +1184,6 @@ bool CSuperSerialCard::CommThInit() &dwThreadId); // lpThreadId SetThreadPriority(m_hCommThread, THREAD_PRIORITY_TIME_CRITICAL); - - InitializeCriticalSection(&m_CriticalSection); } return true; @@ -1325,16 +1344,15 @@ void CSuperSerialCard::SetSnapshot_v1( const DWORD baudrate, const BYTE stopbits) { m_uBaudRate = baudrate; + m_uStopBits = stopbits; m_uByteSize = bytesize; - m_uCommandByte = commandbyte; - m_dwCommInactivity = comminactivity; +// m_dwCommInactivity = comminactivity; // Obsolete m_uControlByte = controlbyte; - m_uParity = parity; +// m_uParity = parity; // Redundant: derived from commandbyte in UpdateCommandReg() // memcpy(m_RecvBuffer, pSS->recvbuffer, uRecvBufferSize); // m_vRecvBytes = recvbytes; - m_uStopBits = stopbits; - m_uDTR = (m_uCommandByte & 0x01) ? DTR_CONTROL_ENABLE : DTR_CONTROL_DISABLE; + UpdateCommandReg(commandbyte); } //=========================================================================== @@ -1389,15 +1407,15 @@ void CSuperSerialCard::SaveSnapshot(YamlSaveHelper& yamlSaveHelper) yamlSaveHelper.SaveUint(SS_YAML_KEY_BAUDRATE, m_uBaudRate); yamlSaveHelper.SaveUint(SS_YAML_KEY_STOPBITS, m_uStopBits); yamlSaveHelper.SaveUint(SS_YAML_KEY_BYTESIZE, m_uByteSize); - yamlSaveHelper.SaveUint(SS_YAML_KEY_PARITY, m_uParity); + yamlSaveHelper.SaveUint(SS_YAML_KEY_PARITY, m_uParity); // Redundant yamlSaveHelper.SaveHexUint8(SS_YAML_KEY_CONTROL, m_uControlByte); yamlSaveHelper.SaveHexUint8(SS_YAML_KEY_COMMAND, m_uCommandByte); - yamlSaveHelper.SaveUint(SS_YAML_KEY_INACTIVITY, m_dwCommInactivity); - yamlSaveHelper.SaveBool(SS_YAML_KEY_TXIRQENABLED, m_bTxIrqEnabled); - yamlSaveHelper.SaveBool(SS_YAML_KEY_RXIRQENABLED, m_bRxIrqEnabled); + yamlSaveHelper.SaveUint(SS_YAML_KEY_INACTIVITY, 0); // Obsolete + yamlSaveHelper.SaveBool(SS_YAML_KEY_TXIRQENABLED, m_bTxIrqEnabled); // Redundant + yamlSaveHelper.SaveBool(SS_YAML_KEY_RXIRQENABLED, m_bRxIrqEnabled); // Redundant yamlSaveHelper.SaveBool(SS_YAML_KEY_TXIRQPENDING, m_vbTxIrqPending); yamlSaveHelper.SaveBool(SS_YAML_KEY_RXIRQPENDING, m_vbRxIrqPending); - yamlSaveHelper.SaveBool(SS_YAML_KEY_WRITTENTX, m_bWrittenTx); + yamlSaveHelper.SaveBool(SS_YAML_KEY_WRITTENTX, m_vbTxEmpty); yamlSaveHelper.SaveString(SS_YAML_KEY_SERIALPORTNAME, GetSerialPortName()); } @@ -1431,17 +1449,17 @@ bool CSuperSerialCard::LoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT slot, U m_uBaudRate = yamlLoadHelper.LoadUint(SS_YAML_KEY_BAUDRATE); m_uStopBits = yamlLoadHelper.LoadUint(SS_YAML_KEY_STOPBITS); m_uByteSize = yamlLoadHelper.LoadUint(SS_YAML_KEY_BYTESIZE); - m_uParity = yamlLoadHelper.LoadUint(SS_YAML_KEY_PARITY); + yamlLoadHelper.LoadUint(SS_YAML_KEY_PARITY); // Redundant: derived from uCommandByte in UpdateCommandReg() m_uControlByte = yamlLoadHelper.LoadUint(SS_YAML_KEY_CONTROL); - m_uCommandByte = yamlLoadHelper.LoadUint(SS_YAML_KEY_COMMAND); - m_dwCommInactivity = yamlLoadHelper.LoadUint(SS_YAML_KEY_INACTIVITY); - m_bTxIrqEnabled = yamlLoadHelper.LoadBool(SS_YAML_KEY_TXIRQENABLED); - m_bRxIrqEnabled = yamlLoadHelper.LoadBool(SS_YAML_KEY_RXIRQENABLED); + UINT uCommandByte = yamlLoadHelper.LoadUint(SS_YAML_KEY_COMMAND); + yamlLoadHelper.LoadUint(SS_YAML_KEY_INACTIVITY); // Obsolete (so just consume) + yamlLoadHelper.LoadBool(SS_YAML_KEY_TXIRQENABLED); // Redundant: derived from uCommandByte in UpdateCommandReg() + yamlLoadHelper.LoadBool(SS_YAML_KEY_RXIRQENABLED); // Redundant: derived from uCommandByte in UpdateCommandReg() m_vbTxIrqPending = yamlLoadHelper.LoadBool(SS_YAML_KEY_TXIRQPENDING); m_vbRxIrqPending = yamlLoadHelper.LoadBool(SS_YAML_KEY_RXIRQPENDING); - m_bWrittenTx = yamlLoadHelper.LoadBool(SS_YAML_KEY_WRITTENTX); + m_vbTxEmpty = yamlLoadHelper.LoadBool(SS_YAML_KEY_WRITTENTX); - m_uDTR = (m_uCommandByte & 0x01) ? DTR_CONTROL_ENABLE : DTR_CONTROL_DISABLE; + UpdateCommandReg(uCommandByte); std::string serialPortName = yamlLoadHelper.LoadString(SS_YAML_KEY_SERIALPORTNAME); SetSerialPortName(serialPortName.c_str()); diff --git a/source/SerialComms.h b/source/SerialComms.h index 0033f777..cfc393ca 100644 --- a/source/SerialComms.h +++ b/source/SerialComms.h @@ -16,7 +16,7 @@ typedef struct UINT uByteSize; UINT uParity; bool bLinefeed; - bool bInterrupts; + bool bInterrupts; // NB. Can't be read from s/w } SSC_DIPSW; #define TEXT_SERIAL_COM TEXT("COM") @@ -32,7 +32,6 @@ public: void CommReset(); void CommDestroy(); void CommSetSerialPort(HWND hWindow, DWORD dwNewSerialPortItem); - void CommUpdate(DWORD); void SetSnapshot_v1(const DWORD baudrate, const BYTE bytesize, const BYTE commandbyte, const DWORD comminactivity, const BYTE controlbyte, const BYTE parity, const BYTE stopbits); std::string GetSnapshotCardName(void); void SaveSnapshot(class YamlSaveHelper& yamlSaveHelper); @@ -62,14 +61,16 @@ private: BYTE __stdcall CommReceive(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG nExecutedCycles); BYTE __stdcall CommStatus(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG nExecutedCycles); BYTE __stdcall CommTransmit(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG nExecutedCycles); + BYTE __stdcall CommProgramReset(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG nExecutedCycles); void InternalReset(); + void UpdateCommandReg(BYTE command); void GetDIPSW(); void SetDIPSWDefaults(); - BYTE GenerateControl(); UINT BaudRateToIndex(UINT uBaudRate); void UpdateCommState(); - BOOL CheckComm(); + void TransmitDone(void); + bool CheckComm(); void CloseComm(); void CheckCommEvent(DWORD dwEvtMask); static DWORD WINAPI CommThread(LPVOID lpParameter); @@ -97,10 +98,7 @@ private: static SSC_DIPSW m_DIPSWDefault; SSC_DIPSW m_DIPSWCurrent; - // Derived from DIPSW1 UINT m_uBaudRate; - - // Derived from DIPSW2 UINT m_uStopBits; UINT m_uByteSize; UINT m_uParity; @@ -114,24 +112,22 @@ private: HANDLE m_hCommHandle; SOCKET m_hCommListenSocket; SOCKET m_hCommAcceptSocket; - DWORD m_dwCommInactivity; // CRITICAL_SECTION m_CriticalSection; // To guard /g_vRecvBytes/ - std::deque m_qComSerialBuffer[2]; + std::deque m_qComSerialBuffer[2]; volatile UINT m_vuRxCurrBuffer; // Written to on COM recv. SSC reads from other one - std::deque m_qTcpSerialBuffer; + std::deque m_qTcpSerialBuffer; // bool m_bTxIrqEnabled; bool m_bRxIrqEnabled; - volatile bool m_vbTxIrqPending; - volatile bool m_vbRxIrqPending; - - bool m_bWrittenTx; + volatile bool m_vbTxIrqPending; + volatile bool m_vbRxIrqPending; + volatile bool m_vbTxEmpty; // @@ -146,6 +142,9 @@ private: // Modem bool m_bCfgSupportDCD; bool m_bCfgSupportDSR; - UINT m_uDTR; bool m_bCfgSupportDTR; + UINT m_uDTR; + // Modem (end) + + UINT m_uRTS; };