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
This commit is contained in:
TomCh 2018-04-08 17:37:26 +01:00 committed by GitHub
parent 598b20fb52
commit a78f1e04da
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 286 additions and 272 deletions

View File

@ -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);
//

View File

@ -1814,9 +1814,6 @@ LRESULT CALLBACK FrameWndProc (
case FD_READ:
sg_SSC.CommTcpSerialReceive();
break;
case FD_WRITE:
break;
}
}
break;

View File

@ -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)

View File

@ -8,5 +8,7 @@
#endif
#endif
extern FILE* g_fh; // Filehandle for log file
extern void LogOutput(LPCTSTR format, ...);
extern void LogFileOutput(LPCTSTR format, ...);

View File

@ -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

View File

@ -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());

View File

@ -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<BYTE> m_qComSerialBuffer[2];
std::deque<BYTE> m_qComSerialBuffer[2];
volatile UINT m_vuRxCurrBuffer; // Written to on COM recv. SSC reads from other one
std::deque<BYTE> m_qTcpSerialBuffer;
std::deque<BYTE> 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;
};