Skip to content

Commit

Permalink
More strict/deliberate synchronization between instances
Browse files Browse the repository at this point in the history
  • Loading branch information
caldog20 committed Dec 3, 2022
1 parent 69b52b6 commit bbf7839
Show file tree
Hide file tree
Showing 2 changed files with 111 additions and 125 deletions.
151 changes: 74 additions & 77 deletions src/core/sio1.cc
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
PCSX::SIOPayload PCSX::SIO1::makeFlowControlMessage() {
return SIOPayload{
DataTransfer{},
FlowControl{m_flowControl.dxr, m_flowControl.xts},
FlowControl{m_regs.control},
};
}

Expand All @@ -31,7 +31,7 @@ PCSX::SIOPayload PCSX::SIO1::makeDataMessage(std::string &&data) {
DataTransfer{
DataTransferData{std::move(data)},
},
FlowControl{},
FlowControl{m_regs.control},
};
}

Expand Down Expand Up @@ -60,20 +60,9 @@ void PCSX::SIO1::sendDataMessage() {
void PCSX::SIO1::sendFlowControlMessage() {
if (fifoError()) return;

pollFlowControl();
if (!initialMessage) {
if (m_flowControl == m_prevFlowControl) return;
}
m_prevFlowControl = m_flowControl;

SIOPayload payload = makeFlowControlMessage();
std::string message = encodeMessage(payload);
transmitMessage(std::move(message));

if (initialMessage) {
if (!connecting() && !fifoError()) g_system->printf("%s", _("SIO1 client connected\n"));
initialMessage = false;
}
}

void PCSX::SIO1::decodeMessage() {
Expand All @@ -96,34 +85,66 @@ void PCSX::SIO1::decodeMessage() {
}

void PCSX::SIO1::processMessage(SIOPayload payload) {
if (!payload.get<DataTransferField>().get<DataTransferData>().hasData()) {
setDsr(payload.get<FlowControlField>().get<FlowControlDXR>().value);
setCts(payload.get<FlowControlField>().get<FlowControlXTS>().value);
} else {
// Flow control is always sent/received to ensure synchronization
setDsr(payload.get<FlowControlField>().get<FlowControlReg>().value & CR_DTR);
setCts(payload.get<FlowControlField>().get<FlowControlReg>().value & CR_RTS);
if (payload.get<DataTransferField>().get<DataTransferData>().hasData()) {
std::string &byte = payload.get<DataTransferField>().get<DataTransferData>().value;
PCSX::Slice pushByte;
pushByte.acquire(std::move(byte));
if (m_regs.control & CR_RTS) {
m_sio1fifo.asA<Fifo>()->pushSlice(std::move(pushByte));
receiveCallback();
m_sio1fifo.asA<Fifo>()->pushSlice(std::move(pushByte));
receiveCallback();
}

if (m_sio1fifo->size() > 8) {
m_regs.status |= SR_RXOVERRUN;
}

updateStat();

// DSR Interrupt
if (m_regs.control & CR_DSRIRQEN) {
if (m_regs.status & SR_DSR) {
if (!(m_regs.status & SR_IRQ)) {
scheduleInterrupt(m_cycleCount);
m_regs.status |= SR_IRQ;
}
}
}
}

void PCSX::SIO1::sio1StateMachine() {
void PCSX::SIO1::sio1StateMachine(bool data) {
if (fifoError()) return;

switch (m_decodeState) {
case READ_SIZE:
while (m_fifo->size() >= 1) {
messageSize = m_fifo->byte();
m_decodeState = READ_MESSAGE;
case READ_MESSAGE:
if (m_fifo->size() < messageSize) return;
decodeMessage();
m_decodeState = READ_SIZE;
}
// Server is master - send first, receive after
if (g_emulator->m_sio1Server->getServerStatus() == SIO1Server::SIO1ServerStatus::SERVER_STARTED) {
if (data) {
sendDataMessage();
} else {
sendFlowControlMessage();
}

waitOnMessage(); // Wait for the next message to be fully received
}

// Client is slave - receive first, send after
if (g_emulator->m_sio1Client->getClientStatus() == SIO1Client::SIO1ClientStatus::CLIENT_STARTED) {
// If connection is new, run slave delay
if (m_slaveDelay) {
slaveDelay();
return;
}

waitOnMessage(); // Wait for the next message to be fully received

if (data) {
sendDataMessage();
} else {
sendFlowControlMessage();
}
}

decodeMessage();
}

void PCSX::SIO1::interrupt() {
Expand All @@ -143,59 +164,39 @@ void PCSX::SIO1::interrupt() {
}

uint8_t PCSX::SIO1::readData8() {
updateStat();
if (m_sio1fifo || !m_sio1fifo->eof()) {
if (m_regs.status & SR_RXRDY) {
m_regs.data = m_sio1fifo->byte();
psxHu8(0x1050) = m_regs.data;
}
m_regs.data = m_sio1fifo->byte();
}
updateStat();
if (m_sio1Mode == SIO1Mode::Protobuf) sio1StateMachine();
return m_regs.data;
}

uint16_t PCSX::SIO1::readData16() {
updateStat();
if (m_sio1fifo || !m_sio1fifo->eof()) {
if (m_regs.status & SR_RXRDY) {
m_sio1fifo->read(&m_regs.data, 2);
psxHu16(0x1050) = m_regs.data;
}
m_sio1fifo->read(&m_regs.data, 2);
}
updateStat();
if (m_sio1Mode == SIO1Mode::Protobuf) sio1StateMachine();
return m_regs.data;
}

uint32_t PCSX::SIO1::readData32() {
updateStat();
if (m_sio1fifo || !m_sio1fifo->eof()) {
if (m_regs.status & SR_RXRDY) {
m_sio1fifo->read(&m_regs.data, 4);
psxHu32(0x1050) = m_regs.data;
}
m_sio1fifo->read(&m_regs.data, 4);
}
updateStat();
if (m_sio1Mode == SIO1Mode::Protobuf) sio1StateMachine();
return m_regs.data;
}

uint8_t PCSX::SIO1::readStat8() {
updateStat();
return m_regs.status;
}

uint16_t PCSX::SIO1::readStat16() {
updateStat();
if (m_sio1Mode == SIO1Mode::Protobuf) sio1StateMachine();
return m_regs.status;
}

uint32_t PCSX::SIO1::readStat32() {
updateStat();
return m_regs.status;
}
uint32_t PCSX::SIO1::readStat32() { return m_regs.status; }

void PCSX::SIO1::receiveCallback() {
updateStat();
if (!m_sio1fifo || m_sio1fifo->eof()) return;
// RX Interrupt
if (m_regs.control & CR_RXIRQEN) {
if (!(m_regs.status & SR_IRQ)) {
switch ((m_regs.control & 0x300) >> 8) {
Expand Down Expand Up @@ -224,14 +225,14 @@ void PCSX::SIO1::transmitData() {
switch (m_sio1Mode) {
case SIO1Mode::Protobuf:
if (fifoError()) return;
sendDataMessage();
sio1StateMachine(true);
break;
case SIO1Mode::Raw:
if (!m_sio1fifo || m_sio1fifo->eof()) return;
m_sio1fifo->write<uint8_t>(m_regs.data);
break;
}

// TX Interrupt
if (m_regs.control & CR_TXIRQEN) {
if (m_regs.status & SR_TXRDY || m_regs.status & SR_TXRDY2) {
if (!(m_regs.status & SR_IRQ)) {
Expand All @@ -253,16 +254,15 @@ void PCSX::SIO1::updateStat() {
} else {
m_regs.status &= ~SR_RXRDY;
}
psxHu32ref(0x1054) = SWAP_LEu32(m_regs.status);
}

void PCSX::SIO1::writeBaud16(uint16_t v) {
m_regs.baud = v;
psxHu8ref(0x105E) = m_regs.baud;
calcCycleCount();
}

void PCSX::SIO1::writeCtrl16(uint16_t v) {
uint16_t control_backup = m_regs.control;
m_regs.control = v;

if (m_regs.control & CR_ACK) {
Expand All @@ -284,33 +284,30 @@ void PCSX::SIO1::writeCtrl16(uint16_t v) {
PCSX::g_emulator->m_cpu->m_regs.interrupt &= ~(1 << PCSX::PSXINT_SIO1);
}

// Behavior to clear FIFO if RXEN is disabled
if (!(m_regs.control & CR_RXEN)) {
if (m_sio1fifo.isA<Fifo>()) {
m_sio1fifo.asA<Fifo>()->reset();
}
}

if (m_sio1Mode == SIO1Mode::Protobuf) sendFlowControlMessage();
psxHu16ref(0x105A) = SWAP_LE16(m_regs.control);
if (((m_regs.control >> 8) & 0x03) != ((control_backup >> 8) & 0x03)) {
if (m_sio1fifo.isA<Fifo>()) {
m_sio1fifo.asA<Fifo>()->reset();
}
}

if (m_sio1Mode == SIO1Mode::Protobuf) sio1StateMachine();
}

void PCSX::SIO1::writeData8(uint8_t v) {
m_regs.data = v;
if (isTransmitReady()) {
transmitData();
}
psxHu8ref(0x1050) = m_regs.data;
transmitData();
}

void PCSX::SIO1::writeMode16(uint16_t v) { m_regs.mode = v; }

void PCSX::SIO1::writeStat32(uint32_t v) {
m_regs.status = v;
if (isTransmitReady()) {
transmitData();
}
psxHu32ref(0x1054) = SWAP_LE32(m_regs.status);
}
void PCSX::SIO1::writeStat16(uint16_t v) { m_regs.status = v; }

void PCSX::SIO1::calcCycleCount() {
int reload = m_reloadFactor[m_regs.mode & 0x3];
Expand Down
Loading

0 comments on commit bbf7839

Please sign in to comment.