/* AppleWin : An Apple //e emulator for Windows Copyright (C) 1994-1996, Michael O'Brien Copyright (C) 1999-2001, Oliver Schmidt Copyright (C) 2002-2005, Tom Charlesworth Copyright (C) 2006-2007, Tom Charlesworth, Michael Pohoreski AppleWin is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. AppleWin is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with AppleWin; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* Description: Super Serial Card emulation * * 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 // /* Adaptation for SDL and POSIX (l) by beom beotiger, Nov-Dec 2007 */ // for read() and write() #include #include "stdafx.h" //#pragma hdrstop //#include "resource.h" //#include "wwrapper.h" #include // for terminal structure and funcs #include #include char SSC_rom[] = "\x20\x9B\xC9\xA9\x16\x48\xA9\x00\x9D\xB8\x04\x9D\xB8\x03\x9D\x38" "\x04\x9D\xB8\x05\x9D\x38\x06\x9D\xB8\x06\xB9\x82\xC0\x85\x2B\x4A" "\x4A\x90\x04\x68\x29\xFE\x48\xB8\xB9\x81\xC0\x4A\xB0\x07\x4A\xB0" "\x0E\xA9\x01\xD0\x3D\x4A\xA9\x03\xB0\x02\xA9\x80\x9D\xB8\x04\x2C" "\x58\xFF\xA5\x2B\x29\x20\x49\x20\x9D\xB8\x03\x70\x0A\x20\x9B\xC8" "\xAE\xF8\x07\x9D\xB8\x05\x60\xA5\x2B\x4A\x4A\x29\x03\xA8\xF0\x04" "\x68\x29\x7F\x48\xB9\xA6\xC9\x9D\x38\x06\xA4\x26\x68\x29\x95\x48" "\xA9\x09\x9D\x38\x05\x68\x9D\x38\x07\xA5\x2B\x48\x29\xA0\x50\x02" "\x29\x80\x20\xA1\xCD\x20\x81\xCD\x68\x29\x0C\x50\x02\xA9\x00\x0A" "\x0A\x0A\x09\x0B\x99\x8A\xC0\xB9\x88\xC0\x60\x20\x9B\xC9\x20\xAA" "\xC8\x29\x7F\xAC\xF8\x07\xBE\xB8\x05\x60\x20\xFF\xCA\xB0\x05\x20" "\x2C\xCC\x90\xF6\x60\x20\x1E\xCA\x68\xA8\x68\xAA\xA5\x27\x60\xF0" "\x29\xBD\xB8\x06\x10\x05\x5E\xB8\x06\xD0\x24\x20\x3E\xCC\x90\x1A" "\xBD\xB8\x03\x29\xC0\xF0\x0E\xA5\x27\xC9\xE0\x90\x08\xBD\xB8\x04" "\x09\x40\x9D\xB8\x04\x28\xF0\xD0\xD0\xCB\x20\xFF\xCA\x90\xDC\x20" "\x11\xCC\x28\x08\xF0\xDA\x20\xD1\xC9\x4C\xD0\xC8\x20\x1A\xCB\xB0" "\xB7\xA5\x27\x48\xBD\x38\x07\x29\xC0\xD0\x16\xA5\x24\xF0\x42\xC9" "\x08\xF0\x04\xC9\x10\xD0\x0A\x09\xF0\x3D\xB8\x06\x18\x65\x24\x85" "\x24\xBD\xB8\x06\xC5\x24\xF0\x29\xA9\xA0\x90\x08\xBD\x38\x07\x0A" "\x10\x1F\xA9\x88\x85\x27\x2C\x58\xFF\x08\x70\x0C\xEA\x2C\x58\xFF" "\x50\xB8\xAE\xF8\x07\x4C\xEF\xC9\x20\xB5\xC9\x20\x6B\xCB\x4C\x68" "\xC9\x68\xB8\x08\x85\x27\x48\x20\x68\xCB\x20\xB5\xC9\x68\x49\x8D" "\x0A\xD0\x05\x9D\xB8\x06\x85\x24\xBD\xB8\x04\x10\x0D\xBD\x38\x06" "\xF0\x08\x18\xFD\xB8\x06\xA9\x8D\x90\xDA\x28\x70\xA4\xBD\x38\x07" "\x30\x16\xBC\xB8\x06\x0A\x30\x0E\x98\xA0\x00\x38\xFD\x38\x06\xC9" "\xF8\x90\x03\x69\x27\xA8\x84\x24\x4C\xB8\xC8\x8E\xF8\x07\x84\x26" "\xA9\x00\x9D\xB8\x05\x60\x29\x48\x50\x84\x85\x27\x20\x9B\xC9\x20" "\x63\xCB\x4C\xA3\xC8\xA5\x27\x49\x08\x0A\xF0\x04\x49\xEE\xD0\x09" "\xDE\xB8\x06\x10\x03\x9D\xB8\x06\x60\xC9\xC0\xB0\xFB\xFE\xB8\x06" "\x60\xBD\x38\x07\x29\x08\xF0\x16\xBD\xB8\x04\xA4\x27\xC0\x94\xD0" "\x04\x09\x80\xD0\x06\xC0\x92\xD0\x05\x29\x7F\x9D\xB8\x04\x60\x8A" "\x0A\x0A\x0A\x0A\x85\x26\xA9\x00\x9D\xB8\x05\x70\x0F\xA0\x00\xB1" "\x3C\x85\x27\x20\x02\xCC\x20\xBA\xFC\x90\xF2\x60\x20\xD2\xCA\x90" "\xFB\xB9\x88\xC0\xA0\x00\x91\x3C\x20\xBA\xFC\x90\xEF\x60\xBD\xB8" "\x04\x10\x31\xA9\x02\x48\xA9\x7F\x20\xE2\xCD\xA4\x24\xB1\x28\x85" "\x27\xA9\x07\x25\x4F\xD0\x10\xA4\x24\xA9\xDF\xD1\x28\xD0\x02\xA5" "\x27\x91\x28\xE6\x4F\xE6\x4F\xBD\xB8\x04\x30\x09\x20\x11\xCC\x68" "\xA9\x8D\x85\x27\x60\x20\xFF\xCA\x90\x0C\x20\x11\xCC\x20\xD1\xC9" "\x20\xA3\xCC\x4C\x2B\xCA\x20\x3E\xCC\x90\xC6\x70\xBE\xBD\x38\x07" "\x0A\x10\x22\x68\xA8\xA5\x27\xC0\x01\xF0\x20\xB0\x34\xC9\x9B\xD0" "\x06\xC8\x98\x48\x4C\x2B\xCA\xC9\xC1\x90\x08\xC9\xDB\xB0\x04\x09" "\x20\x85\x27\x98\x48\x20\x68\xCB\x4C\x2B\xCA\xC9\x9B\xF0\xE2\xC9" "\xB0\x90\x0A\xC9\xBB\xB0\x06\xA8\xB9\x09\xCA\x85\x27\xA0\x00\xF0" "\xE2\xC9\x9B\xD0\xDE\xA0\x00\xF0\xC9\x9B\x9C\x9F\xDB\xDC\xDF\xFB" "\xFC\xFD\xFE\xFF\xA2\xCA\xCA\xD0\xFD\x38\xE9\x01\xD0\xF6\xAE\xF8" "\x07\x60\xA4\x26\xB9\x89\xC0\x48\x29\x20\x4A\x4A\x85\x35\x68\x29" "\x0F\xC9\x08\x90\x04\x29\x07\xB0\x02\xA5\x35\x05\x35\xF0\x05\x09" "\x20\x9D\xB8\x05\x60\xA4\x26\xB9\x89\xC0\x29\x70\xC9\x10\x60\x20" "\xD2\xCA\x90\x15\xB9\x88\xC0\x09\x80\xC9\x8A\xD0\x09\xA8\xBD\x38" "\x07\x29\x20\xD0\x03\x98\x38\x60\x18\x60\xA4\x26\xB9\x81\xC0\x4A" "\xB0\x36\xBD\xB8\x04\x29\x07\xF0\x05\x20\xFC\xCD\x38\x60\xA5\x27" "\x29\x7F\xDD\x38\x05\xD0\x05\xFE\xB8\x04\x38\x60\xBD\x38\x07\x29" "\x08\xF0\x15\x20\xFF\xCA\x90\x10\xC9\x93\xF0\x0E\x48\xBD\x38\x07" "\x4A\x4A\x68\x90\x04\x9D\xB8\x06\x18\x60\x20\xAA\xC8\xC9\x91\xD0" "\xF9\x18\x60\x20\x1A\xCB\xB0\xF1\x20\x9E\xCC\xA4\x26\xB9\x81\xC0" "\x4A\x90\x4E\x4A\x90\x4B\xA5\x27\x48\xBD\x38\x04\xC9\x67\x90\x10" "\xC9\x6C\xB0\x22\xC9\x6B\x68\x48\x49\x9B\x29\x7F\xD0\x18\xB0\x19" "\xBD\xB8\x04\x29\x1F\x09\x80\x85\x27\x20\x02\xCC\x20\xAA\xC8\x49" "\x86\xD0\xED\x9D\x38\x04\xDE\x38\x04\x68\x85\x27\x49\x8D\x0A\xD0" "\x0A\xBD\xB8\x03\x29\x30\xF0\x03\x9D\x38\x04\x20\x02\xCC\x4C\xEA" "\xCB\x20\x02\xCC\x0A\xA8\xBD\xB8\x03\xC0\x18\xF0\x0C\x4A\x4A\xC0" "\x14\xF0\x06\x4A\x4A\xC0\x1A\xD0\x25\x29\x03\xF0\x0D\xA8\xB9\xFE" "\xCB\xA8\xA9\x20\x20\xC4\xCA\x88\xD0\xF8\xA5\x27\x0A\xC9\x1A\xD0" "\x0D\xBD\x38\x07\x6A\x90\x07\xA9\x8A\x85\x27\x4C\x6B\xCB\x60\x01" "\x08\x40\x20\xF5\xCA\xD0\xFB\x98\x09\x89\xA8\xA5\x27\x99\xFF\xBF" "\x60\x48\xA4\x24\xA5\x27\x91\x28\x68\xC9\x95\xD0\x0C\xA5\x27\xC9" "\x20\xB0\x06\x20\xDF\xCC\x59\xDB\xCC\x85\x27\x60\x18\xBD\x38\x07" "\x29\x04\xF0\x09\xAD\x00\xC0\x10\x04\x8D\x10\xC0\x38\x60\xE6\x4E" "\xD0\x02\xE6\x4F\x20\x2C\xCC\xB8\x90\xF3\x20\x11\xCC\x29\x7F\xDD" "\x38\x05\xD0\x3D\xA4\x26\xB9\x81\xC0\x4A\xB0\x35\xA0\x0A\xB9\x93" "\xCC\x85\x27\x98\x48\x20\xA3\xCC\x68\xA8\x88\x10\xF1\xA9\x01\x20" "\x7B\xCE\x20\x34\xCC\x10\xFB\xC9\x88\xF0\xE1\x85\x27\x20\xA3\xCC" "\x20\x1A\xCB\xBD\xB8\x04\x29\x07\xD0\xE8\xA9\x8D\x85\x27\x2C\x58" "\xFF\x38\x60\xBA\xC3\xD3\xD3\xA0\xC5\xCC\xD0\xD0\xC1\x8D\xBD\x38" "\x07\x10\x13\xBD\x38\x07\x29\x02\xF0\x0D\xBD\xB8\x04\x29\x38\xF0" "\x06\x8A\x48\xA9\xAF\x48\x60\x20\xDF\xCC\x09\x80\xC9\xE0\x90\x06" "\x59\xD3\xCC\x4C\xF6\xFD\xC9\xC1\x90\xF9\xC9\xDB\xB0\xF5\x59\xD7" "\xCC\x90\xF0\x20\x00\xE0\x20\x00\x00\x00\xC0\x00\x00\xE0\xC0\xBD" "\xB8\x03\x2A\x2A\x2A\x29\x03\xA8\xA5\x27\x60\x42\x67\xC0\x54\x47" "\xA6\x43\x87\xA6\x51\x47\xB8\x52\xC7\xAC\x5A\xE7\xF3\x49\x90\xD3" "\x4B\x90\xDF\x45\x43\x80\x46\xE3\x04\x4C\xE3\x01\x58\xE3\x08\x54" "\x83\x40\x53\x43\x40\x4D\xE3\x20\x00\x42\xF6\x7C\x50\xF6\x9A\x44" "\xF6\x9B\x46\xF6\x46\x4C\xF6\x40\x43\xF6\x3A\x54\xD6\x34\x4E\x90" "\xE8\x53\x56\x60\x00\xA9\x3F\xA0\x07\xD0\x10\xA9\xCF\xA0\x05\xD0" "\x0A\xA9\xF3\xA0\x03\xD0\x04\xA9\xFC\xA0\x01\x3D\xB8\x03\x85\x2A" "\xBD\x38\x04\x29\x03\x18\x6A\x2A\x88\xD0\xFC\x05\x2A\x9D\xB8\x03" "\x60\x29\x07\x0A\x0A\x0A\x85\x2A\x0A\xC5\x26\xF0\x0F\xBD\xB8\x04" "\x29\xC7\x05\x2A\x9D\xB8\x04\xA9\x00\x9D\x38\x06\x60\x29\x0F\xD0" "\x07\xB9\x81\xC0\x4A\x4A\x4A\x4A\x09\x10\x85\x2A\xA9\xE0\x85\x2B" "\xB9\x8B\xC0\x25\x2B\x05\x2A\x99\x8B\xC0\x60\x88\x0A\x0A\x0A\x0A" "\x0A\x85\x2A\xA9\x1F\xD0\xE7\x1E\xB8\x04\x38\xB0\x10\x99\x89\xC0" "\x20\x93\xFE\x20\x89\xFE\xAE\xF8\x07\x1E\xB8\x04\x18\x7E\xB8\x04" "\x60\xB9\x8A\xC0\x48\x09\x0C\x99\x8A\xC0\xA9\xE9\x20\xC4\xCA\x68" "\x99\x8A\xC0\x60\xA9\x28\x9D\x38\x06\xA9\x80\x1D\x38\x07\xD0\x05" "\xA9\xFE\x3D\x38\x07\x9D\x38\x07\x60\xC9\x28\x90\x0E\x9D\x38\x06" "\xA9\x3F\xD0\xEE\x1E\x38\x05\x38\x7E\x38\x05\x60\xA8\xA5\x27\x29" "\x7F\xC9\x20\xD0\x09\xC0\x03\xF0\x01\x60\xA9\x04\xD0\x6D\xC9\x0D" "\xD0\x12\x20\x79\xCE\xC0\x07\xF0\x01\x60\xA9\xCD\x48\xBD\x38\x04" "\x48\xA4\x26\x60\x85\x35\xA9\xCE\x48\xB9\x30\xCE\x48\xA5\x35\x60" "\xA7\x37\x61\x89\x8A\xA7\x89\x89\xDD\x38\x05\xD0\x06\xDE\xB8\x04" "\x4C\x02\xCC\xC9\x30\x90\x0D\xC9\x3A\xB0\x09\x29\x0F\x9D\x38\x04" "\xA9\x02\xD0\x27\xC9\x20\xB0\x06\x9D\x38\x05\x4C\x79\xCE\xA0\x00" "\xF0\x4D\x49\x30\xC9\x0A\xB0\x0D\xA0\x0A\x7D\x38\x04\x88\xD0\xFA" "\x9D\x38\x04\xF0\x15\xA0\x2E\xD0\x36\xA9\x00\x85\x2A\xAE\xF8\x07" "\xBD\xB8\x04\x29\xF8\x05\x2A\x9D\xB8\x04\x60\xA8\xBD\x38\x04\xC0" "\x44\xF0\x09\xC0\x45\xD0\x11\x1D\x38\x07\xD0\x05\x49\xFF\x3D\x38" "\x07\x9D\x38\x07\xA9\x06\xD0\xD3\xA9\x20\x9D\xB8\x05\xD0\xF5\xB9" "\xEB\xCC\xF0\xF4\xC5\x35\xF0\x05\xC8\xC8\xC8\xD0\xF2\xC8\xB9\xEB" "\xCC\x85\x2A\x29\x20\xD0\x07\xBD\x38\x07\x29\x10\xD0\xEB\xBD\x38" "\x07\x4A\x4A\x24\x2A\xB0\x04\x10\xE0\x30\x02\x50\xDC\xA5\x2A\x48" "\x29\x07\x20\x7B\xCE\xC8\x68\x29\x10\xD0\x07\xB9\xEB\xCC\x9D\x38" "\x04\x60\xA9\xCD\x48\xB9\xEB\xCC\x48\xA4\x26\xBD\x38\x04\x60\xC2" "\x2C\x58\xFF\x70\x0C\x38\x90\x18\xB8\x50\x06\x01\x31\x8E\x94\x97" "\x9A\x85\x27\x86\x35\x8A\x48\x98\x48\x08\x78\x8D\xFF\xCF\x20\x58" "\xFF\xBA\xBD\x00\x01\x8D\xF8\x07\xAA\x0A\x0A\x0A\x0A\x85\x26\xA8" "\x28\x50\x29\x1E\x38\x05\x5E\x38\x05\xB9\x8A\xC0\x29\x1F\xD0\x05" "\xA9\xEF\x20\x05\xC8\xE4\x37\xD0\x0B\xA9\x07\xC5\x36\xF0\x05\x85" "\x36\x18\x90\x08\xE4\x39\xD0\xF9\xA9\x05\x85\x38\xBD\x38\x07\x29" "\x02\x08\x90\x03\x4C\xBF\xC8\xBD\xB8\x04\x48\x0A\x10\x0E\xA6\x35" "\xA5\x27\x09\x20\x9D\x00\x02\x85\x27\xAE\xF8\x07\x68\x29\xBF\x9D" "\xB8\x04\x28\xF0\x06\x20\x63\xCB\x4C\xB5\xC8\x4C\xFC\xC8\x20\x00" "\xC8\xA2\x00\x60\x4C\x9B\xC8\x4C\xAA\xC9\x4A\x20\x9B\xC9\xB0\x08" "\x20\xF5\xCA\xF0\x06\x18\x90\x03\x20\xD2\xCA\xBD\xB8\x05\xAA\x60" "\xA2\x03\xB5\x36\x48\xCA\x10\xFA\xAE\xF8\x07\xBD\x38\x06\x85\x36" "\xBD\xB8\x04\x29\x38\x4A\x4A\x4A\x09\xC0\x85\x37\x8A\x48\xA5\x27" "\x48\x09\x80\x20\xED\xFD\x68\x85\x27\x68\x8D\xF8\x07\xAA\x0A\x0A" "\x0A\x0A\x85\x26\x8D\xFF\xCF\xA5\x36\x9D\x38\x06\xA2\x00\x68\x95" "\x36\xE8\xE0\x04\x90\xF8\xAE\xF8\x07\x60\xC1\xD0\xD0\xCC\xC5\x08" ; pthread_mutex_t m_CriticalSection = PTHREAD_MUTEX_INITIALIZER; //#define SUPPORT_MODEM // Default: 19200-8-N-1 // Maybe a better default is: 9600-7-N-1 (for HyperTrm) SSC_DIPSW CSuperSerialCard::m_DIPSWDefault = { // DIPSW1: B19200, // CBR_# (win32) -> B# (*nix?) FWMODE_CIC, // DIPSW2: ONESTOPBIT, 8, // ByteSize NOPARITY, true, // LF false, // INT }; //=========================================================================== CSuperSerialCard::CSuperSerialCard() { m_dwSerialPort = 0; GetDIPSW(); m_vRecvBytes = 0; m_hCommHandle = -1; m_dwCommInactivity = 0; m_bTxIrqEnabled = false; m_bRxIrqEnabled = false; m_bWrittenTx = false; m_vbCommIRQ = false; m_hCommThread = NULL; for (UINT i=0; i [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) { // changed: CBR_# to B# (for *nix?) --bb case B110: return 0x05; case B300: return 0x06; case B600: return 0x07; case B1200: return 0x08; case B2400: return 0x0A; case B4800: return 0x0C; case B9600: return 0x0E; case B19200: return 0x0F; } _ASSERT(0); return BaudRateToIndex(B9600); } //=========================================================================== void CSuperSerialCard::UpdateCommState() { if (m_hCommHandle == -1) return; struct termios dcb; int l_databits = CS8; tcgetattr(m_hCommHandle, &dcb); // get current attributes for m_hCommHandle in dcb // set input/output speed to m_uBaudRate cfsetispeed(&dcb, m_uBaudRate); cfsetospeed(&dcb, m_uBaudRate); /* * Enable the receiver and set localmode */ dcb.c_cflag |= (CLOCAL | CREAD); switch(m_uParity) { case NOPARITY: /* NONE */ dcb.c_cflag &= ~PARENB; // dcb.c_cflag &= ~CSTOPB; break; case EVENPARITY: /* EVEN */ dcb.c_cflag |= PARENB; dcb.c_cflag &= ~PARODD; // dcb.c_cflag &= ~CSTOPB; break; case ODDPARITY: /* ODD */ dcb.c_cflag |= PARENB; dcb.c_cflag |= PARODD; // sp->params->c_cflag &= ~CSTOPB; break; case MARKPARITY: /* MARKPARITY */ dcb.c_cflag |= PARENB | CMSPAR | PARODD; break; case SPACEPARITY: /* SPACEPARITY */ dcb.c_cflag |= PARENB | CMSPAR; dcb.c_cflag &= ~PARODD; break; } switch(m_uByteSize) { // data bits case 5: l_databits = CS5; break; case 6: l_databits = CS6; break; case 7: l_databits = CS7; break; case 8: l_databits = CS8; break; } dcb.c_cflag &= ~CSIZE; dcb.c_cflag |= l_databits; // dcb.c_cflag |= m_uBaudRate; // set here instead of by cfsetispeed and cfsetospeed? switch(m_uStopBits) { case ONE5STOPBITS: case ONESTOPBIT: dcb.c_cflag &= ~CSTOPB; // 1 stopbit break; case TWOSTOPBITS: dcb.c_cflag |= CSTOPB; // 1.5 and 2 stopbits. break; } dcb.c_cflag &= ~CRTSCTS; /* Disable hardware flow control */ /* Enable data to be processed as raw input */ dcb.c_lflag &= ~(ICANON | ECHO | ISIG); /* Set the new options for the port */ tcsetattr(m_hCommHandle, TCSANOW, &dcb); // This code in Win32?? /* DCB dcb; ZeroMemory(&dcb,sizeof(DCB)); dcb.DCBlength = sizeof(DCB); GetCommState(m_hCommHandle,&dcb); dcb.BaudRate = m_uBaudRate; dcb.ByteSize = m_uByteSize; dcb.Parity = m_uParity; dcb.StopBits = m_uStopBits; SetCommState(m_hCommHandle,&dcb);*/ } //=========================================================================== BOOL CSuperSerialCard::CheckComm() { m_dwCommInactivity = 0; if ((m_hCommHandle == -1) && m_dwSerialPort) { TCHAR portname[12]; // we have /dev/ttyS0..X instead of COM1..COMX+1? if(m_dwSerialPort < 0 || m_dwSerialPort > 99) m_dwSerialPort = 1;//buffer overflow check sprintf(portname, TEXT("/dev/ttyS%u"), (unsigned int)(m_dwSerialPort - 1)); /* m_hCommHandle = CreateFile(portname, GENERIC_READ | GENERIC_WRITE, 0, // exclusive access (LPSECURITY_ATTRIBUTES)NULL, // default security attributes OPEN_EXISTING, FILE_FLAG_OVERLAPPED, // required for WaitCommEvent() NULL);*/ m_hCommHandle = open(portname, O_RDWR | O_NOCTTY | O_NDELAY); if (m_hCommHandle != -1) { UpdateCommState(); /* COMMTIMEOUTS ct; ZeroMemory(&ct,sizeof(COMMTIMEOUTS)); ct.ReadIntervalTimeout = MAXDWORD; SetCommTimeouts(m_hCommHandle,&ct);*/ CommThInit(); } else { ;//DWORD uError = 1;//GetLastError(); } } return (m_hCommHandle != -1); } //=========================================================================== void CSuperSerialCard::CloseComm() { CommThUninit(); // Kill CommThread before closing COM handle if (m_hCommHandle != -1) close(m_hCommHandle); // close device (port?) m_hCommHandle = -1; m_dwCommInactivity = 0; } //=========================================================================== BYTE /*__stdcall*/ CSuperSerialCard::SSC_IORead(WORD PC, WORD uAddr, BYTE bWrite, BYTE uValue, ULONG nCyclesLeft) { UINT uSlot = ((uAddr & 0xff) >> 4) - 8; CSuperSerialCard* pSSC = (CSuperSerialCard*) MemGetSlotParameters(uSlot); switch (uAddr & 0xf) { case 0x0: return IO_Null(PC, uAddr, bWrite, uValue, nCyclesLeft); case 0x1: return pSSC->CommDipSw(PC, uAddr, bWrite, uValue, nCyclesLeft); case 0x2: return pSSC->CommDipSw(PC, uAddr, bWrite, uValue, nCyclesLeft); case 0x3: return IO_Null(PC, uAddr, bWrite, uValue, nCyclesLeft); case 0x4: return IO_Null(PC, uAddr, bWrite, uValue, nCyclesLeft); case 0x5: return IO_Null(PC, uAddr, bWrite, uValue, nCyclesLeft); case 0x6: return IO_Null(PC, uAddr, bWrite, uValue, nCyclesLeft); case 0x7: return IO_Null(PC, uAddr, bWrite, uValue, nCyclesLeft); case 0x8: return pSSC->CommReceive(PC, uAddr, bWrite, uValue, nCyclesLeft); case 0x9: return pSSC->CommStatus(PC, uAddr, bWrite, uValue, nCyclesLeft); case 0xA: return pSSC->CommCommand(PC, uAddr, bWrite, uValue, nCyclesLeft); case 0xB: return pSSC->CommControl(PC, uAddr, bWrite, uValue, nCyclesLeft); case 0xC: return IO_Null(PC, uAddr, bWrite, uValue, nCyclesLeft); case 0xD: return IO_Null(PC, uAddr, bWrite, uValue, nCyclesLeft); case 0xE: return IO_Null(PC, uAddr, bWrite, uValue, nCyclesLeft); case 0xF: return IO_Null(PC, uAddr, bWrite, uValue, nCyclesLeft); } return 0; } BYTE /*__stdcall*/ CSuperSerialCard::SSC_IOWrite(WORD PC, WORD uAddr, BYTE bWrite, BYTE uValue, ULONG nCyclesLeft) { UINT uSlot = ((uAddr & 0xff) >> 4) - 8; CSuperSerialCard* pSSC = (CSuperSerialCard*) MemGetSlotParameters(uSlot); switch (uAddr & 0xf) { case 0x0: return IO_Null(PC, uAddr, bWrite, uValue, nCyclesLeft); case 0x1: return IO_Null(PC, uAddr, bWrite, uValue, nCyclesLeft); case 0x2: return IO_Null(PC, uAddr, bWrite, uValue, nCyclesLeft); case 0x3: return IO_Null(PC, uAddr, bWrite, uValue, nCyclesLeft); case 0x4: return IO_Null(PC, uAddr, bWrite, uValue, nCyclesLeft); case 0x5: return IO_Null(PC, uAddr, bWrite, uValue, nCyclesLeft); case 0x6: return IO_Null(PC, uAddr, bWrite, uValue, nCyclesLeft); case 0x7: return IO_Null(PC, uAddr, bWrite, uValue, nCyclesLeft); case 0x8: return pSSC->CommTransmit(PC, uAddr, bWrite, uValue, nCyclesLeft); case 0x9: return pSSC->CommStatus(PC, uAddr, bWrite, uValue, nCyclesLeft); case 0xA: return pSSC->CommCommand(PC, uAddr, bWrite, uValue, nCyclesLeft); case 0xB: return pSSC->CommControl(PC, uAddr, bWrite, uValue, nCyclesLeft); case 0xC: return IO_Null(PC, uAddr, bWrite, uValue, nCyclesLeft); case 0xD: return IO_Null(PC, uAddr, bWrite, uValue, nCyclesLeft); case 0xE: return IO_Null(PC, uAddr, bWrite, uValue, nCyclesLeft); case 0xF: return IO_Null(PC, uAddr, bWrite, uValue, nCyclesLeft); } return 0; } //=========================================================================== // EG. 0x09 = Enable IRQ, No parity [Ref.2] BYTE /*__stdcall*/ CSuperSerialCard::CommCommand(WORD, WORD, BYTE write, BYTE value, ULONG) { if (!CheckComm()) return 0; 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] m_bRxIrqEnabled = ((m_uCommandByte & 0x02) == 0); if (m_uCommandByte & 0x01) // Data Terminal Ready (DTR) setting [0=set DTR high (indicates 'not ready')] { // Note that, although the DTR is generally not used in the SSC (it may actually not // be connected!), it must be set to 'low' in order for the 6551 to function correctly. } UpdateCommState(); } return m_uCommandByte; } //=========================================================================== BYTE /*__stdcall*/ CSuperSerialCard::CommControl(WORD, WORD, BYTE write, BYTE value, ULONG) { if (!CheckComm()) return 0; if (write && (value != m_uControlByte)) { m_uControlByte = value; // UPDATE THE BAUD RATE switch (m_uControlByte & 0x0F) { // Note that 1 MHz Apples (everything other than the Apple IIgs and //c // Plus running in "fast" mode) cannot handle 19.2 kbps, and even 9600 // bps on these machines requires either some highly optimised code or // a decent buffer in the device being accessed. The faster Apples // have no difficulty with this speed, however. case 0x00: // fall through [16x external clock] case 0x01: // fall through [50 bps] case 0x02: // fall through [75 bps] case 0x03: // fall through [109.92 bps] case 0x04: // fall through [134.58 bps] case 0x05: m_uBaudRate = B110; break; // [150 bps] case 0x06: m_uBaudRate = B300; break; case 0x07: m_uBaudRate = B600; break; case 0x08: m_uBaudRate = B1200; break; case 0x09: // fall through [1800 bps] case 0x0A: m_uBaudRate = B2400; break; case 0x0B: // fall through [3600 bps] case 0x0C: m_uBaudRate = B4800; break; case 0x0D: // fall through [7200 bps] case 0x0E: m_uBaudRate = B9600; break; case 0x0F: m_uBaudRate = B19200; break; } if (m_uControlByte & 0x10) { // receiver clock source [0= external, 1= internal] } // UPDATE THE BYTE SIZE switch (m_uControlByte & 0x60) { case 0x00: m_uByteSize = 8; break; case 0x20: m_uByteSize = 7; break; case 0x40: m_uByteSize = 6; break; case 0x60: m_uByteSize = 5; break; } // UPDATE THE NUMBER OF STOP BITS if (m_uControlByte & 0x80) { if ((m_uByteSize == 8) && (m_uParity != NOPARITY)) m_uStopBits = ONESTOPBIT; else if ((m_uByteSize == 5) && (m_uParity == NOPARITY)) m_uStopBits = ONE5STOPBITS; else m_uStopBits = TWOSTOPBITS; } else { m_uStopBits = ONESTOPBIT; } UpdateCommState(); } return m_uControlByte; } //=========================================================================== BYTE /*__stdcall*/ CSuperSerialCard::CommReceive(WORD, WORD, BYTE, BYTE, ULONG) { if (!CheckComm()) return 0; BYTE result = 0; if (m_vRecvBytes) { // Don't need critical section in here as CommThread is waiting for ACK result = m_RecvBuffer[0]; --m_vRecvBytes; if (m_vbCommIRQ && !m_vRecvBytes) { // Read last byte, so get CommThread to call WaitCommEvent() again fprintf(stderr, "CommRecv: SetEvent - ACK\n"); // SetEvent(m_hCommEvent[COMMEVT_ACK]); } } return result; } //=========================================================================== BYTE /*__stdcall*/ CSuperSerialCard::CommTransmit(WORD, WORD, BYTE, BYTE value, ULONG) { if (!CheckComm()) return 0; DWORD uBytesWritten; uBytesWritten = write(m_hCommHandle, &value, 1); // WriteFile(m_hCommHandle, &value, 1, &uBytesWritten, &m_o); m_bWrittenTx = true; // Transmit done // TO DO: // 1) Use CommThread determine when transmit is complete // 2) OR do this: //if (m_bTxIrqEnabled) // CpuIrqAssert(IS_SSC); return 0; } //=========================================================================== // 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)] // 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]) 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, ST_DSR = 1<<6, ST_IRQ = 1<<7 }; BYTE /*__stdcall*/ CSuperSerialCard::CommStatus(WORD, WORD, BYTE, BYTE, ULONG) { if (!CheckComm()) return ST_DSR | ST_DCD | ST_TX_EMPTY; #ifdef SUPPORT_MODEM DWORD modemstatus = 0; GetCommModemStatus(m_hCommHandle,&modemstatus); // Returns 0x30 = MS_DSR_ON|MS_CTS_ON #endif // // TO DO - ST_TX_EMPTY: // . IRQs enabled : set after WaitCommEvent has signaled that TX has completed // . IRQs disabled : always set it [Currently done] // // So that /m_vRecvBytes/ doesn't change midway (from 0 to 1): // . bIRQ=false, but uStatus.ST_RX_FULL=1 // EnterCriticalSection(&m_CriticalSection); pthread_mutex_lock( &m_CriticalSection ); bool bIRQ = false; if (m_bTxIrqEnabled && m_bWrittenTx) { bIRQ = true; } if (m_bRxIrqEnabled && m_vRecvBytes) { bIRQ = true; } m_bWrittenTx = false; // Read status reg always clears IRQ // BYTE uStatus = ST_TX_EMPTY | (m_vRecvBytes ? ST_RX_FULL : 0x00) #ifdef SUPPORT_MODEM | ((modemstatus & MS_RLSD_ON) ? 0x00 : ST_DCD) // Need 0x00 to allow ZLink to start up | ((modemstatus & MS_DSR_ON) ? 0x00 : ST_DSR) #endif | (bIRQ ? ST_IRQ : 0x00); // LeaveCriticalSection(&m_CriticalSection); pthread_mutex_unlock( &m_CriticalSection); CpuIrqDeassert(IS_SSC); return uStatus; } //=========================================================================== BYTE /*__stdcall*/ CSuperSerialCard::CommDipSw(WORD, WORD addr, BYTE, BYTE, ULONG) { BYTE sw = 0; switch (addr & 0xf) { case 1: // DIPSW1 sw = (BaudRateToIndex(m_DIPSWCurrent.uBaudRate)<<4) | m_DIPSWCurrent.eFirmwareMode; 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 // SW2-3 (Parity: odd-ON(0); even-OFF(1)) // SW2-4 (Parity: none-ON(0); SW2-3-OFF(1)) BYTE RDR,OVR; switch (m_DIPSWCurrent.uParity) { case ODDPARITY: RDR = 0; OVR = 1; break; case EVENPARITY: RDR = 1; OVR = 1; break; default: _ASSERT(0); case NOPARITY: RDR = 0; OVR = 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)) sw = (INT<<7) | (DSR<<6) | (DCD<<5) | (TDR<<4) | (RDR<<3) | (OVR<<2) | (FE<<1) | (PE<<0); break; } return sw; } //=========================================================================== void CSuperSerialCard::CommInitialize(LPBYTE pCxRomPeripheral, UINT uSlot) { const UINT SSC_FW_SIZE = 2*1024; const UINT SSC_SLOT_FW_SIZE = 256; const UINT SSC_SLOT_FW_OFFSET = 7*256; /* HRSRC hResInfo = FindResource(NULL, MAKEINTRESOURCE(IDR_SSC_FW), "FIRMWARE"); if(hResInfo == NULL) return; DWORD dwResSize = SizeofResource(NULL, hResInfo); if(dwResSize != SSC_FW_SIZE) return; HGLOBAL hResData = LoadResource(NULL, hResInfo); if(hResData == NULL) return;*/ // #define IDR_SSC_FW "SSC.rom" // char BUFFER[SSC_FW_SIZE]; // FILE * hdfile = NULL; // hdfile = fopen(IDR_SSC_FW, "rb"); // if(hdfile == NULL) return; // no file? // UINT nbytes = fread(BUFFER, 1, SSC_FW_SIZE, hdfile); // fclose(hdfile); // if(nbytes != SSC_FW_SIZE) return; // have not read enough? // BYTE* pData = (BYTE*) SSC_rom; // NB. Don't need to unlock resource // if(pData == NULL) // return; memcpy(pCxRomPeripheral + uSlot*256, pData+SSC_SLOT_FW_OFFSET, SSC_SLOT_FW_SIZE); // Expansion ROM if (m_pExpansionRom == NULL) { m_pExpansionRom = new BYTE [SSC_FW_SIZE]; if (m_pExpansionRom) memcpy(m_pExpansionRom, pData, SSC_FW_SIZE); } // RegisterIoHandler(uSlot, &CSuperSerialCard::SSC_IORead, &CSuperSerialCard::SSC_IOWrite, NULL, NULL, this, m_pExpansionRom); } //=========================================================================== void CSuperSerialCard::CommReset() { CloseComm(); GetDIPSW(); m_vRecvBytes = 0; // m_bTxIrqEnabled = false; m_bRxIrqEnabled = false; m_bWrittenTx = false; m_vbCommIRQ = false; } //=========================================================================== void CSuperSerialCard::CommDestroy() { CommReset(); delete [] m_pExpansionRom; m_pExpansionRom = NULL; } //=========================================================================== void CSuperSerialCard::CommSetSerialPort(/*HWND window,*/ DWORD newserialport) { if (m_hCommHandle == -1) { m_dwSerialPort = newserialport; } else { /* MessageBox(window, TEXT("You cannot change the serial port while it is ") TEXT("in use."), TEXT("Configuration"), MB_ICONEXCLAMATION | MB_SETFOREGROUND);*/ fprintf(stderr, "You cannot change the serial port while it is in use!\n"); } } //=========================================================================== void CSuperSerialCard::CommUpdate(DWORD totalcycles) { if (m_hCommHandle == -1) 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(); } } //=========================================================================== void CSuperSerialCard::CheckCommEvent(DWORD dwEvtMask) { /* if (dwEvtMask & EV_RXCHAR)*/ { pthread_mutex_lock(&m_CriticalSection); // ReadFile(m_hCommHandle, m_RecvBuffer, 1, (DWORD*)&m_vRecvBytes, &m_o); m_vRecvBytes = read(m_hCommHandle, m_RecvBuffer, 1); pthread_mutex_unlock(&m_CriticalSection); if (m_bRxIrqEnabled && m_vRecvBytes) { m_vbCommIRQ = true; CpuIrqAssert(IS_SSC); } } //else if (dwEvtMask & EV_TXEMPTY) //{ // if (m_bTxIrqEnabled) // { // m_vbCommIRQ = true; // CpuIrqAssert(IS_SSC); // } //} } DWORD 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 if (!bRes) return -1; // const UINT nNumEvents = 2; #if 1 HANDLE hCommEvent_Wait[nNumEvents] = {pSSC->m_hCommEvent[COMMEVT_WAIT], pSSC->m_hCommEvent[COMMEVT_TERM]}; HANDLE hCommEvent_Ack[nNumEvents] = {pSSC->m_hCommEvent[COMMEVT_ACK], pSSC->m_hCommEvent[COMMEVT_TERM]}; #else HANDLE hCommEvent_Wait[nNumEvents]; HANDLE hCommEvent_Ack[nNumEvents]; hCommEvent_Wait[0] = m_hCommEvent[COMMEVT_WAIT]; hCommEvent_Wait[1] = m_hCommEvent[COMMEVT_TERM]; hCommEvent_Ack[0] = m_hCommEvent[COMMEVT_ACK]; hCommEvent_Ack[1] = m_hCommEvent[COMMEVT_TERM]; #endif while(1) { DWORD dwEvtMask = 0; DWORD dwWaitResult; bRes = WaitCommEvent(pSSC->m_hCommHandle, &dwEvtMask, &pSSC->m_o); // Will return immediately (probably with ERROR_IO_PENDING) _ASSERT(!bRes); if (!bRes) { DWORD dwRet = GetLastError(); // Got this error once: ERROR_OPERATION_ABORTED _ASSERT(dwRet == ERROR_IO_PENDING); if (dwRet != ERROR_IO_PENDING) return -1; // // Wait for comm event // while(1) { OutputDebugString("CommThread: Wait1\n"); dwWaitResult = WaitForMultipleObjects( nNumEvents, // number of handles in array hCommEvent_Wait, // array of event handles FALSE, // wait until any one is signaled INFINITE); // On very 1st wait *only*: get a false signal (when not running via debugger) if ((dwWaitResult == WAIT_OBJECT_0) && (dwEvtMask == 0)) continue; if ((dwWaitResult >= WAIT_OBJECT_0) && (dwWaitResult <= WAIT_OBJECT_0+nNumEvents-1)) break; } dwWaitResult -= WAIT_OBJECT_0; // Determine event # that signaled sprintf(szDbg, "CommThread: GotEvent1: %d\n", dwWaitResult); OutputDebugString(szDbg); if (dwWaitResult == (nNumEvents-1)) break; // Termination event } // Comm event pSSC->CheckCommEvent(dwEvtMask); if (pSSC->m_vbCommIRQ) { // // Wait for ack // while(1) { OutputDebugString("CommThread: Wait2\n"); dwWaitResult = WaitForMultipleObjects( nNumEvents, // number of handles in array hCommEvent_Ack, // array of event handles FALSE, // wait until any one is signaled INFINITE); if ((dwWaitResult >= WAIT_OBJECT_0) && (dwWaitResult <= WAIT_OBJECT_0+nNumEvents-1)) break; } dwWaitResult -= WAIT_OBJECT_0; // Determine event # that signaled sprintf(szDbg, "CommThread: GotEvent2: %d\n", dwWaitResult); OutputDebugString(szDbg); if (dwWaitResult == (nNumEvents-1)) break; // Termination event pSSC->m_vbCommIRQ = false; } }*/ return 0; } bool CSuperSerialCard::CommThInit() { /* _ASSERT(m_hCommThread == NULL); _ASSERT(m_hCommHandle); if ((m_hCommEvent[0] == NULL) && (m_hCommEvent[1] == NULL) && (m_hCommEvent[2] == NULL)) { // Create an event object for use by WaitCommEvent m_o.hEvent = CreateEvent( NULL, // default security attributes FALSE, // auto reset event (bManualReset) FALSE, // not signaled (bInitialState) NULL // no name ); // Initialize the rest of the OVERLAPPED structure to zero m_o.Internal = 0; m_o.InternalHigh = 0; m_o.Offset = 0; m_o.OffsetHigh = 0; // m_hCommEvent[COMMEVT_WAIT] = m_o.hEvent; m_hCommEvent[COMMEVT_ACK] = CreateEvent(NULL, // lpEventAttributes FALSE, // bManualReset (FALSE = auto-reset) FALSE, // bInitialState (FALSE = non-signaled) NULL); // lpName m_hCommEvent[COMMEVT_TERM] = CreateEvent(NULL, // lpEventAttributes FALSE, // bManualReset (FALSE = auto-reset) FALSE, // bInitialState (FALSE = non-signaled) NULL); // lpName if ((m_hCommEvent[0] == NULL) || (m_hCommEvent[1] == NULL) || (m_hCommEvent[2] == NULL)) { if(g_fh) fprintf(g_fh, "Comm: CreateEvent failed\n"); return false; } } // if (m_hCommThread == NULL) { DWORD dwThreadId; m_hCommThread = CreateThread(NULL, // lpThreadAttributes 0, // dwStackSize (LPTHREAD_START_ROUTINE) &CSuperSerialCard::CommThread, this, // lpParameter 0, // dwCreationFlags : 0 = Run immediately &dwThreadId); // lpThreadId InitializeCriticalSection(&m_CriticalSection); }*/ return true; } void CSuperSerialCard::CommThUninit() { /* if (m_hCommThread) { SetEvent(m_hCommEvent[COMMEVT_TERM]); // Signal to thread that it should exit do { DWORD dwExitCode; if(GetExitCodeThread(m_hCommThread, &dwExitCode)) { if(dwExitCode == STILL_ACTIVE) Sleep(10); else break; } } while(1); CloseHandle(m_hCommThread); m_hCommThread = NULL; DeleteCriticalSection(&m_CriticalSection); } // for (UINT i=0; ibaudrate = m_uBaudRate; pSS->bytesize = m_uByteSize; pSS->commandbyte = m_uCommandByte; pSS->comminactivity = m_dwCommInactivity; pSS->controlbyte = m_uControlByte; pSS->parity = m_uParity; memcpy(pSS->recvbuffer, m_RecvBuffer, uRecvBufferSize); pSS->recvbytes = m_vRecvBytes; pSS->stopbits = m_uStopBits; return 0; } DWORD CSuperSerialCard::CommSetSnapshot(SS_IO_Comms* pSS) { m_uBaudRate = pSS->baudrate; m_uByteSize = pSS->bytesize; m_uCommandByte = pSS->commandbyte; m_dwCommInactivity = pSS->comminactivity; m_uControlByte = pSS->controlbyte; m_uParity = pSS->parity; memcpy(m_RecvBuffer, pSS->recvbuffer, uRecvBufferSize); m_vRecvBytes = pSS->recvbytes; m_uStopBits = pSS->stopbits; return 0; }