Files
RP412/MUNGA_L4/L4PCSPAK.cpp
T
CydandClaude Opus 4.8 4abbf8879f Initial import of Red Planet v4.10 Win32 source
Imports the current Win32 source for the pod-racing game 'Red Planet',
built on the MUNGA engine and its L4 (Win32/DirectX) platform layer:

- MUNGA / MUNGA_L4: cross-platform engine core and Win32 backend
- RP / RP_L4: Red Planet game logic and Win32 application
- DivLoader, Setup1: asset loader and installer project
- lib, MUNGA_L4/openal, MUNGA_L4/sos: third-party audio dependencies

Removed stale Subversion metadata and added .gitignore/.gitattributes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 07:59:51 -05:00

951 lines
30 KiB
C++

#include "mungal4.h"
#include <windows.h>
#include "L4SERIAL.h"
#include "L4PCSPAK.h"
#include <assert.h>
//Win32 Serial support: ADB 02/10/07
//------------------------------------------------
// COM_INIT macro
// Initializes COMBUF structure
//------------------------------------------------
void PCSerialPacket::COM_INIT(PCSerPackBuf* buffer)
{
WaitForSingleObject(hMutex, INFINITE);
buffer->head = 0;
buffer->tempHead = 0;
buffer->tempTail = 0;
buffer->tail = 0;
buffer->count = 0;
buffer->tempIn = 0;
buffer->tempOut = 0;
ReleaseMutex(hMutex);
}
//Win32 Serial support: ADB 02/13/07
//------------------------------------------------
// COM_PUT_AL macro
// 'Temporarily' puts character in AL into COMBUF
//------------------------------------------------
void PCSerialPacket::COM_PUT_AL(PCSerPackBuf* buffer, BYTE al)
{
WaitForSingleObject(hMutex, INFINITE);
if(buffer->tempIn > COMBUFSIZE)
{
#if DIE_ON_OVERFLOW
DISABLE_AND_DIE("Serial Packet Buffer Overflow!");
#else
ReleaseMutex(hMutex);
return;
#endif
}
buffer->tempIn++; //increment 'tempIn' count
buffer->data[buffer->tempHead] = al; //write the data
buffer->tempHead++; //bump 'tempHead'
buffer->tempHead &= (COMBUFSIZE-1); //limit it
ReleaseMutex(hMutex);
}
//Win32 Serial support: ADB 02/13/07
//------------------------------------------------
// COM_RESTORE_IN macro
// Discards characters 'temporarily' placed in COMBUF
//------------------------------------------------
void PCSerialPacket::COM_RESTORE_IN(PCSerPackBuf* buffer)
{
WaitForSingleObject(hMutex, INFINITE);
buffer->tempIn = 0; //clear the 'tempIn' count
buffer->tempHead = buffer->head; //restore 'tempHead' index
ReleaseMutex(hMutex);
}
//Win32 Serial support: ADB 02/13/07
//------------------------------------------------
// COM_ACCEPT_IN macro
// Accepts characters 'temporarily' placed in COMBUF
//------------------------------------------------
void PCSerialPacket::COM_ACCEPT_IN(PCSerPackBuf* buffer)
{
WaitForSingleObject(hMutex, INFINITE);
buffer->count += buffer->tempIn; //add to current count
buffer->tempIn = 0;
buffer->head = buffer->tempHead; //set the new head offset
ReleaseMutex(hMutex);
}
//Win32 Serial support: ADB 02/13/07
//------------------------------------------------
// COM_GET_AL macro
// 'Temporarily' gets character into AL from COMBUF
//------------------------------------------------
int PCSerialPacket::COM_GET_AL(PCSerPackBuf* buffer)
{
WaitForSingleObject(hMutex, INFINITE);
buffer->tempOut++; //inc 'tempOut' count
int al = buffer->data[buffer->tempTail]; //read the data
buffer->tempTail ++; //bump 'tempTail'
buffer->tempTail &= (COMBUFSIZE-1); //limit it
ReleaseMutex(hMutex);
return al;
}
//Win32 Serial support: ADB 02/13/07
//------------------------------------------------
// COM_RESTORE_OUT macro
// Restores characters 'temporarily' removed from COMBUF
//------------------------------------------------
void PCSerialPacket::COM_RESTORE_OUT(PCSerPackBuf* buffer)
{
WaitForSingleObject(hMutex, INFINITE);
buffer->tempOut = 0; //clear the 'tempOut' count
buffer->tempTail = buffer->tail; //restore 'tempTail' index
ReleaseMutex(hMutex);
}
//Win32 Serial support: ADB 02/13/07
//------------------------------------------------
// COM_ACCEPT_OUT macro
// Discards characters 'temporarily' removed from COMBUF
//------------------------------------------------
void PCSerialPacket::COM_ACCEPT_OUT(PCSerPackBuf* buffer)
{
WaitForSingleObject(hMutex, INFINITE);
buffer->count -= buffer->tempOut; //subtract from current count
buffer->tempOut = 0; //clear 'tempOut' count
buffer->tail = buffer->tempTail; //set the new tail offset
ReleaseMutex(hMutex);
}
//Win32 Serial support: ADB 02/13/07
//------------------------------------------------
// COM_EAT_PACKET macro
// Discards entire buffer item from COMBUF
//------------------------------------------------
void PCSerialPacket::COM_EAT_PACKET(PCSerPackBuf* buffer)
{
WaitForSingleObject(hMutex, INFINITE);
BYTE command = buffer->data[buffer->tail]; //read the packet command byte
command &= 0x07; //remove the MSB
int length = this->rxLengthPtr[command]; //convert packet command to length
length++; //include the command byte
buffer->count -= length; //subtract from count
buffer->tail += length; //move the tail
buffer->tail &= (COMBUFSIZE-1); //limit it
buffer->tempTail = buffer->tail; //set the new tempTail index
buffer->tempOut = 0;
ReleaseMutex(hMutex);
}
//Win32 Serial support: ADB 02/13/07
//------------------------------------------------
// COM_SPACE_IN macro
// Returns input bytes available in COMBUF
//------------------------------------------------
int PCSerialPacket::COM_SPACE_IN(PCSerPackBuf* buffer)
{
WaitForSingleObject(hMutex, INFINITE);
int available = COMBUFSIZE;
available -= buffer->count;
available -= buffer->tempIn;
ReleaseMutex(hMutex);
return available;
}
//Win32 Serial support: ADB 02/13/07
//------------------------------------------------
// COM_SPACE_OUT macro
// Returns output bytes available in COMBUF
//------------------------------------------------
int PCSerialPacket::COM_SPACE_OUT(PCSerPackBuf* buffer)
{
WaitForSingleObject(hMutex, INFINITE);
int output = buffer->count;
output -= buffer->tempOut;
ReleaseMutex(hMutex);
return output;
}
//Win32 Serial support: ADB 06/23/07
PCSerialPacket::PCSerialPacket()
{
}
void PCSerialPacket::ShutdownRxThread()
{
//Win32 Serial support: ADB 06/23/07
enabled = false;
bThreadAbort = TRUE;
if (overlapSend.hEvent != INVALID_HANDLE_VALUE)
{
CloseHandle(overlapSend.hEvent);
overlapSend.hEvent = INVALID_HANDLE_VALUE;
}
if(hRxAbortEvent != INVALID_HANDLE_VALUE)
{
SetEvent(hRxAbortEvent);
CloseHandle(hRxAbortEvent);
hRxAbortEvent = INVALID_HANDLE_VALUE;
}
if(hRxThread != INVALID_HANDLE_VALUE)
{
WaitForSingleObject(hRxThread, INFINITE);
CloseHandle(hRxThread);
hRxThread = INVALID_HANDLE_VALUE;
}
if(hMutex != INVALID_HANDLE_VALUE)
{
CloseHandle(hMutex);
hMutex = INVALID_HANDLE_VALUE;
}
if(hComm != INVALID_HANDLE_VALUE)
{
CloseHandle(hComm);
hComm = INVALID_HANDLE_VALUE;
}
}
PCSerialPacket::~PCSerialPacket()
{
if (this == NULL)
{
std::cout << "~PCSerialPacket() has been passed a NULL pointer\n" << std::flush;
}
else
{
ShutdownRxThread();
}
}
//Win32 Serial support: ADB 01/15/07
// Only call Initialize(...) ONCE during the lifetime of the object!
int PCSerialPacket::Initialize(WORD rate, WORD format, const char* port, BYTE* rxLengths, BYTE rxCount, BYTE* txLengths, BYTE txCount)
{
//;-------------------------------------------------------------------------
//; PCSPAKInit()
//; Constructor for serial packet driver interface
//;-------------------------------------------------------------------------
//; Enter:
//; init_this = pointer to object
//; init_port = address of serial port
//; init_irq_num = interrupt number
//; init_rx_len = pointer to (byte) receive length table
//; init_tx_len = pointer to (byte) transmit length table
//;-------------------------------------------------------------------------
//; Returns:
//; 1 if ok,
//; -1 if IRQ's all used,
//; -2 if DPMI error,
//; -3 if really weird bogus error,
//; else IIR value
//;-------------------------------------------------------------------------
//DWORD return_status = 0;
//;--------------------------------------------------------------
//; Clear the data buffers
//;--------------------------------------------------------------
COM_INIT(&rxBuf); //assumes esi points to object
COM_INIT(&txBuf); //assumes esi points to object
//;--------------------------------------------------------------
//; Initialize values
//;--------------------------------------------------------------
enabled = 0;
txHoldOff = 0;
txReply = 0;
rxFlags = 0;
Error = 0;
intDataPtr = 0;
historyIndex = 0;
txState = TX_IDLE_STATE; //initialize to idle state
rxState = RX_IDLE_STATE; //initialize to idle state
txLengthPtr = txLengths; //set dialect transmit table pointer
txMaximum = txCount; //set maximum dialect transmit index
rxLengthPtr = rxLengths; //set dialect receive table pointer
rxMaximum = rxCount; //set maximum dialect receive index
hComm = INVALID_HANDLE_VALUE;
hRxAbortEvent = INVALID_HANDLE_VALUE;
hRxThread = INVALID_HANDLE_VALUE;
hMutex = INVALID_HANDLE_VALUE;
bThreadAbort = FALSE;
overlapSend.hEvent = INVALID_HANDLE_VALUE;
hComm = InitSerialPort(rate, format, port, true);
if(hComm == INVALID_HANDLE_VALUE)
{
enabled = 0;
DWORD err = GetLastError();
DEBUG_STREAM << "ERROR: InitSerialPort() failed [GetLastError() = " << GetLastError() << "]!\n" << std::flush;
return PCSPAKWin32Err;
}
enabled = 1;
//Setup multi-threading for rx thread
hMutex = CreateMutex(NULL, false, NULL);
if(hMutex == NULL)
{
DEBUG_STREAM << "ERROR: CreateMutex() failed [GetLastError() = " << GetLastError() << "]!\n" << std::flush;
enabled = 0;
return PCSPAKWin32Err;
}
if(!SetCommMask(hComm, EV_RXCHAR))
{
DEBUG_STREAM << "ERROR: SetCommMask() failed [GetLastError() = " << GetLastError() << "]!\n" << std::flush;
enabled = 0;
return PCSPAKWin32Err;
}
hRxAbortEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
hRxThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)(&RxThreadProc), this, NULL, NULL);
if(hRxThread == NULL)
{
DEBUG_STREAM << "ERROR: CreateThread() failed [GetLastError() = " << GetLastError() << "]!\n" << std::flush;
enabled = 0;
return PCSPAKWin32Err;
}
SetThreadPriority(hRxThread, THREAD_PRIORITY_TIME_CRITICAL);
overlapSend.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
overlapSend.Internal = 0;
overlapSend.InternalHigh = 0;
overlapSend.Offset = 0;
overlapSend.OffsetHigh = 0;
return PCSPAKInitOk;
}
//Win32 Serial support: ADB 01/15/07
Logical PCSerialPacket::TestInstance()
{
//return PCSPAKTestInstance(this);
return 1;
}
//Win32 Serial support: ADB 02/10/07
//-------------------------------------------------------------------------
// PCSPAKReceive()
// Returns packet if available
//-------------------------------------------------------------------------
// Enter:
// BYTE *destPtr = pointer to 128-byte destination buffer
//-------------------------------------------------------------------------
// Returns:
// int packetSize (or zero if no packet available)
//-------------------------------------------------------------------------
int PCSerialPacket::ReceivePacket(BYTE *destPtr) // returns len (zero if none)
{
if(!enabled)
return 0;
//--------------------------------------------------------------
// Check character count: non-zero indicates entire packet
//--------------------------------------------------------------
if(rxBuf.count == 0)
return 0;
//--------------------------------------------------------------
// Get buffer length
//--------------------------------------------------------------
BYTE c = COM_GET_AL(&rxBuf); //get character into al
//--------------------------------------------------------------
// Verify first char is a valid command character
//--------------------------------------------------------------
if((c & 0x80) == 0)
{
DISABLE_AND_DIE("Character was not a command character!");
return 0;
}
BYTE *overflow = destPtr + COMBUFSIZE - 1;
*destPtr = c; //save it
destPtr++;
BYTE length = rxLengthPtr[c & 0x7F]; //translate into length
int count = length; //save in edx
count++; //return at least one character
assert(length < 256);
//--------------------------------------------------------------
// Transfer data from buffer
//--------------------------------------------------------------
do
{
c = COM_GET_AL(&rxBuf); //get character into al
*destPtr = c; //save it
destPtr++;
assert(destPtr < overflow);
}while(--length > 0);
COM_ACCEPT_OUT(&rxBuf); //accept all 'taken' characters
return count;
}
//Win32 Serial Support
//-------------------------------------------------------------------------
// PCSPAKSend()
// Transmits packet
//-------------------------------------------------------------------------
// Enter:
// BYTE *sp_src = pointer to (up to) 128-byte source buffer
//-------------------------------------------------------------------------
// Returns:
// void
//-------------------------------------------------------------------------
void PCSerialPacket::SendPacket(BYTE *srcPtr) //sends packet
{
//--------------------------------------------------------------
// Wait for sufficient room
//--------------------------------------------------------------
if(!enabled) //if disabled, don't even try
return;
BYTE cmd = *srcPtr; //get the command byte
cmd &= 0x7F; //remove MSB
int length = txLengthPtr[cmd]; //get the length in al
length++; //add one for command byte
//This code has been replaced for your sanity:
//while(COM_SPACE_IN(&txBuf) < length) //enough room?
// DISABLE_AND_DIE("Not enough space in serial packet buffer");
////--------------------------------------------------------------
//// Copy data into tx buffer
////--------------------------------------------------------------
//do
//{
// COM_PUT_AL(&txBuf, *srcPtr); //place it in the buffer
// srcPtr++; //increment the source pointer
//}
//while(--length > 0);
//COM_ACCEPT_IN(&txBuf); //accept all 'placed' characters
//
////call StartSending ;attempt to start tx interrupt
//Sane code:
BYTE *buffer = new BYTE[length + 1];//add one for the checksum
memcpy(&buffer[0], srcPtr, length);//copy packet
buffer[length] = 0;//initialize checksum
for(int b=0; b<length; b++)//for each byte
buffer[length] += buffer[b];//add to checksum;
buffer[length] &= 0x007F;//limit checksum;
if(!WriteFile(hComm, &buffer[0], length + 1, NULL, &overlapSend))
{
DWORD errNum = GetLastError();
if (errNum != ERROR_IO_PENDING)
{
DEBUG_STREAM << "Could not begin writing data to serial port (GetLastError() = " << GetLastError() << ")!\n" << std::flush;
delete buffer;
return;
}
}
DWORD bytesWritten;
GetOverlappedResult(hComm, &overlapSend, &bytesWritten, TRUE);
if(bytesWritten != length + 1)
{
DEBUG_STREAM << "Not all data was written to serial port!\n" << std::flush;
}
delete buffer;
}
//Win32 Serial support: ADB 01/15/07
int PCSerialPacket::ReceiveQueueCount()
{
//return PCSPAKReceiveCount(this);
return rxBuf.count;
}
//Win32 Serial support: ADB 01/15/07
int PCSerialPacket::TransmitQueueCount()
{
//return PCSPAKTransmitCount(this);
return txBuf.count;
}
//Win32 Serial Support: ADB 02/25/2007
void PCSerialPacket::SetDTR(Logical on)
{
EscapeCommFunction(hComm, on ? SETDTR : CLRDTR);
}
//Win32 Serial support: ADB 01/15/2007
//-------------------------------------------------------------------------
// PCSPAKActive()
// Returns transmitter 'active' state (=0 when all transmission is done).
//-------------------------------------------------------------------------
// This function is now deprecated.
int PCSerialPacket::IsActive()
{
//return PCSPAKActive(this);
return enabled;
}
//Win32 Serial support: ADB 01/15/07
Word PCSerialPacket::Errors()// returns error value, clears it
{
//return PCSPAKErrors(this);
return Error;
}
//Win32 Serial support: ADB 06/25/07
//The folowing code has been ommited for your sanity
//-------------------------------------------------------------------------
// SendChar - send character through UART
// ...called from the interrupt routine and PCSPAK::sendCommand.
//-------------------------------------------------------------------------
// Enter:
// ESI = pointer to PCSPAK structure
// DX = UART base address
//-------------------------------------------------------------------------
// Returns:
// (void)
//-------------------------------------------------------------------------
// Side effects:
// registers EAX, EBX, ECX modified
//-------------------------------------------------------------------------
// void SendChar()
// {
// //--------------------------------------------------------------
// // Send reply character if one is waiting
// //--------------------------------------------------------------
// if(txReply == 0)//"reply" character waiting?
// goto noReply;//no, continue
//
// TransmitCommChar(hComm, txReply);//yes, send the reply character
// txReply = 0;//get and clear the "reply" character
// goto sendDone;//done for now.
//noReply:
// //--------------------------------------------------------------
// // Enter the appropriate state handler
// //--------------------------------------------------------------
// if(txState == TX_IDLE_STATE)//idle state?
// goto txIdleState;//yes, go to it
//
// if(txState == TX_BODY_STATE)//"body" state?
// goto txBodyState;//yes, go to it
//
// if(txState == TX_WAIT_STATE)//"wait" state?
// goto txWaitState;//yes, go to it
//
// if(txState == TX_RESTART_STATE)//restart state?
// goto txRestartState;//yes, go to it
//
// DISABLE_AND_DIE("Unknown Send State!");
// goto txWaitState;//must be "wait" state
// //--------------------------------------------------------------
// // Transmitter idle state
// //--------------------------------------------------------------
//txIdleState:
// if(txHoldOff != 0)//If transmitter 'held off', do nothing
// goto txIsIdle;
//
// if(txBuf.count != 0)//input buffer available?
// goto txIdleAvail;//yes, send something
//txIsIdle:
// //nothing to send, just return
// goto sendDone;
//
//txIdleAvail:
// //---------------------------
// // Buffer ready, start
// //---------------------------
// txResetCount = TXMAXRESET;//set the 'reset' counter
// txErrorCount = TXMAXERROR;//set the 'error' counter
//
// //...deliberately fall through into 'txRestartState'
//
// //--------------------------------------------------------------
// // Transmitter restart state
// //--------------------------------------------------------------
//txRestartState:
// BYTE al = COM_GET_AL(&txBuf);//assumes esi points to object
//
// if(al <= 0x007F)//is it a command?
// goto txRestartBad;//no, discard it
//
// BYTE ah = al & 0x007F;//remove MSB
// if(ah < txMaximum)//legitimate command?
// goto txRestartOk;//yes
//
//txRestartBad:
// DISABLE_AND_DIE("Unknown Command In Send Queue!");
// COM_ACCEPT_OUT(&txBuf);//eat the character
// txState = TX_IDLE_STATE;//move to the "idle" state
// goto sendDone;
//
//txRestartOk:
// TransmitCommChar(hComm, al);//transmit the command character
// txChecksum = al;//begin a new checksum count
//
// //look up length
// //include the command byte
// txCount = txLengthPtr[ah] + 1;//save the length
//
// txState = TX_BODY_STATE;//move to the "body" state
// goto sendDone;
//
// //--------------------------------------------------------------
// // Transmitter "body" state
// // Send message body, then checksum (unless interrupted by ACK/NAK)
// //--------------------------------------------------------------
//txBodyState:
// //---------------------------
// // Interrupted by ACK/NAK?
// //---------------------------
// if(rxFlags == 0)//flag set?
// goto txBodySend;//no, send normal body
//
// COM_RESTORE_OUT(&txBuf);//interrupted, reset 'out' pointer
//
// BYTE flags = rxFlags;//get flags
// rxFlags = 0;//clear them
//
// if((flags & RX_ACK_BIT) != 0)//was it an ACK?
// goto txBodyNak;//no
// Error |= TX_EARLY_ERR;//yes, set error flag
//txBodyNak:
// //---------------------------
// // Restart or die
// //---------------------------
// txResetCount--;//check the restart count
// if(txResetCount <= 0)
// goto txBodyGiveUp;//if too many errors, give up
// //---------------------------
// // Attempt restart
// //---------------------------
// TransmitCommChar(hComm, (BYTE)RESTART_CHAR);//no, send restart character
//
// txState = TX_RESTART_STATE;//move to the "restart" state
// goto sendDone;
// //---------------------------
// // Too many errors, die!
// //---------------------------
//txBodyGiveUp:
// Error |= TX_ABANDON;//set error flag
// COM_EAT_PACKET(&txBuf);//discard current packet
// goto txIdleState;//start another packet
// //---------------------------
// // Send body or checksum
// //---------------------------
//txBodySend:
// txCount--;//is buffer done?
// if(txCount <= 0)
// goto txBodyCheck;//yes, send checksum
// //---------------------------
// // Send a body character
// //---------------------------
// al = COM_GET_AL(&txBuf);//get a character from the buffer
//
// if((al & 0x007F) == 0)//legitimate body char?
// goto txBodyOk;//yes, continue
// DISABLE_AND_DIE("Invalid Body Char In Send Queue!");
// al &= 0x007F;//prevent bad data from becoming command
//txBodyOk:
// TransmitCommChar(hComm, al);
// txChecksum += al;//add to the checksum
// goto sendDone;
// //---------------------------
// // End of body, send checksum
// //---------------------------
//txBodyCheck:
// al = txChecksum;//get the checksum
// al &= 0x007F;//limit it
// TransmitCommChar(hComm, al);//send it
// //---------------------------
// // prepare for "wait" state
// //---------------------------
// txIdleCount = TXMAXIDLE;//set the idle counter
// txState = TX_WAIT_STATE;//move to the "wait" state
// goto sendDone;
//
// //--------------------------------------------------------------
// // Transmitter "wait" state
// // Waits for reply from other end
// //--------------------------------------------------------------
//txWaitState:
// flags = rxFlags;//get flags, also clear them
// rxFlags = 0;
// if(flags == 0)//flags clear?
// goto txWaitMore;//yes, keep waiting
//
// if((flags & RX_ACK_BIT) == 0)//ACK?
// goto txWaitNak;//no, process NAK
// //---------------------------
// // ACK, buffer received OK
// //---------------------------
// COM_ACCEPT_OUT(&txBuf);//yes, good transmission. Discard packet.
// txState = TX_IDLE_STATE;//move to the "idle" state
// goto txIdleState;//start another buffer
// //---------------------------
// // NAK!
// //---------------------------
//txWaitNak:
// Error |= TX_NAK;//set error flag
// COM_RESTORE_OUT(&txBuf);//restore output pointers
//
// txErrorCount--;//check the error count
// if(txErrorCount == 0)
// goto txWaitGiveUp;//if too many errors, give up
// goto txRestartState;//no, resend it
// //---------------------------
// // Keep waiting
// //---------------------------
//txWaitMore:
// txIdleCount--;//have we waited too long?
// if(txIdleCount == 0)
// goto txWaitDone;//yes, attempt to restart
//
// TransmitCommChar(hComm, (BYTE)IDLE_CHAR);//no, send idle character
// goto sendDone;
// //---------------------------
// // Attempt restart
// //---------------------------
//txWaitDone:
// Error |= TX_RESTART;//set error flag
// COM_RESTORE_OUT(&txBuf);//restore output pointers
//
// txResetCount--;//too many restarts?
// if(txResetCount == 0)
// goto txWaitGiveUp;//yes, abandon buffer
//
// TransmitCommChar(hComm, (BYTE)RESTART_CHAR);//no, send restart character
//
// txIdleCount = TXMAXIDLE;//reset the idle counter
// txState = TX_RESTART_STATE;//move to the "idle" state
// goto sendDone;
// //---------------------------
// // Give up
// //---------------------------
//txWaitGiveUp:
// Error |= TX_ABANDON;//set error flag
// COM_EAT_PACKET(&txBuf);//discard the packet
// txState = TX_IDLE_STATE;//move to the "idle" state
// goto txIdleState;//start another packet
//sendDone:
// return;
// }
//Win32 Serial support: ADB 06/23/07
//RX Thread Proc
DWORD WINAPI RxThreadProc(LPVOID lpParameter)
{
//DEBUG_STREAM<<"RX Thread Running!\n"<<std::flush;
PCSerialPacket* pcPack = (PCSerialPacket*)lpParameter;
//DEBUG_STREAM<<"RX Max: "<<(unsigned int)pcPack->rxMaximum<<"\n";
//initialize com port event
OVERLAPPED o;
o.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
o.Internal = 0;
o.InternalHigh = 0;
o.Offset = 0;
o.OffsetHigh = 0;
HANDLE events[] = {pcPack->hRxAbortEvent, o.hEvent};
while(true)
{
DWORD dwEvent;
//DEBUG_STREAM<<"Waiting for Data...\n"<<std::flush;
WaitCommEvent(pcPack->hComm, &dwEvent, &o);
if(WaitForMultipleObjects(2, &events[0], FALSE, INFINITE) == WAIT_OBJECT_0)//indicates that the abort event was triggered
if(pcPack->bThreadAbort)//if we really are supposed to abort
break;//then abort
//perform read
BYTE buffer[COMBUFSIZE];
memset(&buffer, 0, COMBUFSIZE);
DWORD bytesRead = 0;
//DEBUG_STREAM << "Reading Data\n" << std::flush;
ReadFile(pcPack->hComm, &buffer[0], COMBUFSIZE, NULL, &o);
GetOverlappedResult(pcPack->hComm, &o, &bytesRead, TRUE);
//process data
//DEBUG_STREAM<<"Received "<<bytesRead<<" bytes! {"<<std::flush;
for(DWORD b = 0; b < bytesRead; b++)
{
//if (buffer[b] == 135)
//DEBUG_STREAM << "Received AnalogReply: " << GetTickCount() << std::endl << std::flush;
//DEBUG_STREAM<<"0x"<<std::hex<<(unsigned int)(BYTE)buffer[b]<<std::dec<<std::flush;
//if(b < bytesRead - 1)
// DEBUG_STREAM<<", ";
//--------------------------------------------------------------
// Process universal special characters: ACK, NAK, IDLE
//--------------------------------------------------------------
if(((unsigned int)buffer[b] & 0x0080) == 0)//MSB set?
goto rcNotSpecial;//no, can't be special character
//------------------------------
// IDLE
//------------------------------
if((unsigned int)buffer[b] != IDLE_CHAR)//IDLE character?
goto rcNotIdle;//no
goto recvDone;//yes, ignore it
rcNotIdle:
//------------------------------
// ACK
//------------------------------
if((unsigned int)buffer[b] != ACK_CHAR)//ACK character?
goto rcNotAck;//no
pcPack->rxFlags |= RX_ACK_BIT;//yes, flag for transmitter
goto recvDone;
rcNotAck:
//------------------------------
// NAK
//------------------------------
if((unsigned int)buffer[b] != NAK_CHAR)//NAK character?
goto rcNotNak;//no
pcPack->rxFlags |= RX_NAK_BIT;//yes, flag for transmitter
goto recvDone;
rcNotNak:
rcNotSpecial:
//--------------------------------------------------------------
// Process state-dependent characters
//--------------------------------------------------------------
if(pcPack->rxState != 0)//test the state
goto rxActive;//non-zero, so active
//--------------------------------------------------------------
// Receiver "idle" state
// Wait for command character
//--------------------------------------------------------------
if(((unsigned int)buffer[b] & 0x0080) != 0)//is it a control character?
goto rxIdleSpecial;//yes, process it
pcPack->Error |= RX_SEQ_ERR;//Not expecting data, so error.
goto recvDone;
rxIdleSpecial:
if((unsigned int)buffer[b] == RESTART_CHAR)//restart?
goto recvDone;//yes, discard it
unsigned int bx = pcPack->COM_SPACE_IN(&pcPack->rxBuf);//get remaining space
if(bx != 0)//zero?
goto rxIdleSpace;//no, continue
DEBUG_STREAM<<"RX Buffer Full!"<<std::flush;
DISABLE_AND_DIE("RX Buffer Full!");
pcPack->Error |= RX_COM_ERR;//error. Set flag.
goto recvDone;
rxIdleSpace:
unsigned int ah = (unsigned int)buffer[b];//move to ah for testing
ah &= 0x007F;//remove MSB
if(ah < pcPack->rxMaximum)//legitimate command?
goto rxIdleOk;//yes, continue
DEBUG_STREAM<<"RX Unknown Command!"<<std::flush;
DISABLE_AND_DIE("RX Unknown Command!");
pcPack->Error |= RX_COM_ERR;//no.
goto recvDone;
rxIdleOk:
pcPack->COM_PUT_AL(&pcPack->rxBuf, buffer[b]);//save command character in buffer
pcPack->rxChecksum = buffer[b];//start a new receive checksum
pcPack->rxCount = pcPack->rxLengthPtr[ah] + 1;//save the length
pcPack->rxState = RX_ACTIVE_STATE;//change to "active" state
goto recvDone;
//--------------------------------------------------------------
// Receiver "active" state
// Place N characters into buffer, test checksum, and reply
//--------------------------------------------------------------
rxActive:
if(((unsigned int)buffer[b] & 0x0080) == 0)//control character?
goto rxActiveData;//no, therefore data
pcPack->Error |= RX_SEQ_ERR;//yes, error. Set error flag
pcPack->COM_RESTORE_IN(&pcPack->rxBuf);//Discard received data
//------------------------------
// Process control char
//------------------------------
if((unsigned int)buffer[b] == RESTART_CHAR)//restart character?
goto rxActiveAbandon;//yes, abandon received data
pcPack->txReply = NAK_CHAR;//error, tell transmitter to send NAK
goto rxActiveReply;
//------------------------------
// Process data
//------------------------------
rxActiveData:
pcPack->rxCount--;//check the receive count
if(pcPack->rxCount <= 0)
goto rxActiveCheck;//if end of message, process checksum
//------------------------------
// Enough space in buffer?
//------------------------------
bx = pcPack->COM_SPACE_IN(&pcPack->rxBuf);//get remaining space
if(bx != 0)//zero?
goto rxActiveSpace;//no, continue
DEBUG_STREAM<<"RX Buffer Full @ 2!"<<std::flush;
DISABLE_AND_DIE("RX Buffer Full @ 2!");
pcPack->Error |= RX_COM_ERR;//error. Set flag.
pcPack->COM_RESTORE_IN(&pcPack->rxBuf);//Discard received data
goto rxActiveAbandon;//give up on packet
rxActiveSpace:
//------------------------------
// Save data in buffer
//------------------------------
pcPack->rxChecksum += buffer[b];//update the checksum
pcPack->COM_PUT_AL(&pcPack->rxBuf, buffer[b]);//save the character
goto recvDone;
//------------------------------
// Test checksum
//------------------------------
rxActiveCheck:
unsigned int bl = pcPack->rxChecksum;//get the checksum
bl &= 0x007F;//remove MSB
if(bl == buffer[b])//checksum OK?
goto rxActiveOk;//yes!
//------------------------------
// Buffer error
//------------------------------
pcPack->Error |= RX_BAD_CHECKSUM;//error. Set flag.
pcPack->COM_RESTORE_IN(&pcPack->rxBuf);//Discard received data
pcPack->txReply = NAK_CHAR;//tell transmitter to send NAK
TransmitCommChar(pcPack->hComm, (char)NAK_CHAR);
goto rxActiveReply;
//------------------------------
// Buffer OK
//------------------------------
rxActiveOk:
pcPack->COM_ACCEPT_IN(&pcPack->rxBuf);//accept the received data
pcPack->txReply = ACK_CHAR;//tell transmitter to send ACK
TransmitCommChar(pcPack->hComm, (char)ACK_CHAR);
rxActiveReply:
//call StartSending ;restart transmitter if needed
//------------------------------
// Return to inactive state
//------------------------------
rxActiveAbandon:
pcPack->rxState = RX_IDLE_STATE;
recvDone:
;
}
//DEBUG_STREAM<<"}\n"<<std::flush;
}
CloseHandle(o.hEvent);
DEBUG_STREAM<<"Exiting RX Thread!\n"<<std::flush;
return 0;
}