Battle cable support
work in progress f355 and aero dancing seem to work vonot and tetris don't maxspeed is broken
This commit is contained in:
parent
a9d5b77053
commit
4efacd7d71
|
@ -155,6 +155,7 @@ Option<bool> GGPOChatTimeoutToggle("GGPOChatTimeoutToggle", true, "network");
|
|||
Option<int> GGPOChatTimeout("GGPOChatTimeout", 10, "network");
|
||||
Option<bool> NetworkOutput("NetworkOutput", false, "network");
|
||||
Option<int> MultiboardSlaves("MultiboardSlaves", 1, "network");
|
||||
Option<bool> BattleCableEnable("BattleCable", false, "network");
|
||||
|
||||
#ifdef SUPPORT_DISPMANX
|
||||
Option<bool> DispmanxMaintainAspect("maintain_aspect", true, "dispmanx");
|
||||
|
|
|
@ -521,6 +521,7 @@ extern Option<bool> GGPOChatTimeoutToggle;
|
|||
extern Option<int> GGPOChatTimeout;
|
||||
extern Option<bool> NetworkOutput;
|
||||
extern Option<int> MultiboardSlaves;
|
||||
extern Option<bool> BattleCableEnable;
|
||||
|
||||
#ifdef SUPPORT_DISPMANX
|
||||
extern Option<bool> DispmanxMaintainAspect;
|
||||
|
|
|
@ -834,6 +834,7 @@ void Emulator::start()
|
|||
Get_Sh4Interpreter(&sh4_cpu);
|
||||
INFO_LOG(DYNAREC, "Using Interpreter");
|
||||
}
|
||||
setupPtyPipe();
|
||||
|
||||
memwatch::protect();
|
||||
|
||||
|
|
|
@ -300,6 +300,7 @@ public:
|
|||
public:
|
||||
// Serial TX
|
||||
virtual void write(u8 data) { }
|
||||
virtual void sendBreak() { }
|
||||
// RX buffer Size
|
||||
virtual int available() { return 0; }
|
||||
// Serial RX
|
||||
|
@ -310,6 +311,9 @@ public:
|
|||
|
||||
virtual void setPipe(Pipe *pipe) = 0;
|
||||
virtual void updateStatus() = 0;
|
||||
virtual void receiveBreak() {
|
||||
die("unsupported");
|
||||
}
|
||||
|
||||
virtual ~SerialPort() = default;
|
||||
};
|
||||
|
|
|
@ -1287,6 +1287,7 @@ void init()
|
|||
|
||||
void term()
|
||||
{
|
||||
SCIFSerialPort::Instance().setPipe(nullptr);
|
||||
delete hopper;
|
||||
hopper = nullptr;
|
||||
}
|
||||
|
|
|
@ -45,6 +45,7 @@ public:
|
|||
|
||||
~TouchscreenPipe()
|
||||
{
|
||||
SCIFSerialPort::Instance().setPipe(nullptr);
|
||||
sh4_sched_unregister(schedId);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#pragma once
|
||||
#include "hw/hwreg.h"
|
||||
#include <deque>
|
||||
|
||||
extern u32 UBC[9];
|
||||
extern u32 BSC[19];
|
||||
|
@ -99,16 +100,47 @@ public:
|
|||
void setPipe(Pipe *pipe) override {
|
||||
this->pipe = pipe;
|
||||
}
|
||||
void updateStatus() override;
|
||||
Pipe *getPipe() const {
|
||||
return pipe;
|
||||
}
|
||||
void updateStatus() override {}
|
||||
void receiveBreak() override;
|
||||
void init();
|
||||
void term();
|
||||
void serialize(Serializer& ser);
|
||||
void deserialize(Deserializer& deser);
|
||||
|
||||
static u8 readData(u32 addr);
|
||||
static void writeData(u32 addr, u8 data);
|
||||
static u16 readStatus(u32 addr);
|
||||
static void writeStatus(u32 addr, u16 data);
|
||||
static u16 readSCFDR2(u32 addr);
|
||||
u8 SCFRDR2_read();
|
||||
void SCFTDR2_write(u8 data);
|
||||
u16 readStatus();
|
||||
void writeStatus(u16 data);
|
||||
u16 SCFDR2_read();
|
||||
static u16 SCFCR2_read(u32 addr);
|
||||
void SCFCR2_write(u16 data);
|
||||
void SCSPTR2_write(u16 data);
|
||||
static void SCBRR2_write(u32 addr, u8 data);
|
||||
static void SCSMR2_write(u32 addr, u16 data);
|
||||
static void SCSCR2_write(u32 addr, u16 data);
|
||||
|
||||
static SCIFSerialPort& Instance();
|
||||
|
||||
private:
|
||||
void updateBaudRate();
|
||||
void setBreak(bool on);
|
||||
void sendBreak();
|
||||
bool txDone();
|
||||
void rxSched();
|
||||
static int schedCallback(int tag, int cycles, int lag, void *arg);
|
||||
|
||||
Pipe *pipe = nullptr;
|
||||
int schedId = -1;
|
||||
int brkSchedId = -1;
|
||||
int frameSize = 10; // default 8 data bits, 1 stop bit, no parity
|
||||
int cyclesPerBit = SH4_MAIN_CLOCK / 6103;
|
||||
u16 statusLastRead = 0;
|
||||
std::deque<u8> txFifo;
|
||||
std::deque<u8> rxFifo;
|
||||
bool transmitting = false;
|
||||
};
|
||||
|
||||
void setupPtyPipe();
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
/*
|
||||
Dreamcast serial port.
|
||||
This is missing most of the functionality, but works for KOS (And thats all that uses it)
|
||||
*/
|
||||
#include <cerrno>
|
||||
#include <cstdlib>
|
||||
|
@ -17,6 +16,16 @@
|
|||
#include "hw/sh4/sh4_interrupts.h"
|
||||
#include "cfg/option.h"
|
||||
#include "modules.h"
|
||||
#include "hw/sh4/sh4_sched.h"
|
||||
#include "serialize.h"
|
||||
|
||||
//#define DEBUG_SCIF
|
||||
|
||||
#ifdef DEBUG_SCIF
|
||||
#define SCIF_LOG(...) INFO_LOG(SH4, __VA_ARGS__)
|
||||
#else
|
||||
#define SCIF_LOG(...)
|
||||
#endif
|
||||
|
||||
SCIRegisters sci;
|
||||
SCIFRegisters scif;
|
||||
|
@ -28,79 +37,191 @@ static void updateInterrupts()
|
|||
|
||||
InterruptPend(sh4_SCIF_RXI, SCIF_SCFSR2.RDF || SCIF_SCFSR2.DR);
|
||||
InterruptMask(sh4_SCIF_RXI, SCIF_SCSCR2.RIE);
|
||||
|
||||
InterruptPend(sh4_SCIF_BRI, SCIF_SCFSR2.BRK);
|
||||
InterruptMask(sh4_SCIF_BRI, SCIF_SCSCR2.RIE || SCIF_SCSCR2.REIE);
|
||||
|
||||
InterruptPend(sh4_SCIF_ERI, SCIF_SCFSR2.ER || SCIF_SCFSR2.FER || SCIF_SCFSR2.PER);
|
||||
InterruptMask(sh4_SCIF_ERI, SCIF_SCSCR2.RIE || SCIF_SCSCR2.REIE);
|
||||
}
|
||||
|
||||
// SCIF SCFTDR2
|
||||
void SCIFSerialPort::writeData(u32 addr, u8 data)
|
||||
int SCIFSerialPort::schedCallback(int tag, int cycles, int lag, void *arg)
|
||||
{
|
||||
//DEBUG_LOG(COMMON, "serial out %02x %c", data, data);
|
||||
if (Instance().pipe != nullptr)
|
||||
Instance().pipe->write(data);
|
||||
|
||||
SCIF_SCFSR2.TDFE = 1;
|
||||
SCIF_SCFSR2.TEND = 1;
|
||||
|
||||
updateInterrupts();
|
||||
}
|
||||
|
||||
// SCIF_SCFSR2 read
|
||||
u16 SCIFSerialPort::readStatus(u32 addr)
|
||||
{
|
||||
Instance().updateStatus();
|
||||
return SCIF_SCFSR2.full;
|
||||
}
|
||||
|
||||
// SCIF_SCFSR2 write
|
||||
void SCIFSerialPort::writeStatus(u32 addr, u16 data)
|
||||
{
|
||||
if (!SCIF_SCFSR2.BRK)
|
||||
data &= ~0x10;
|
||||
|
||||
SCIF_SCFSR2.full = data & 0x00f3;
|
||||
|
||||
SCIF_SCFSR2.TDFE = 1;
|
||||
SCIF_SCFSR2.TEND = 1;
|
||||
|
||||
Instance().updateStatus();
|
||||
}
|
||||
|
||||
//SCIF_SCFDR2 - 16b
|
||||
u16 SCIFSerialPort::readSCFDR2(u32 addr)
|
||||
{
|
||||
if (Instance().pipe != nullptr)
|
||||
return std::min(16, Instance().pipe->available());
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
//SCIF_SCFRDR2
|
||||
u8 SCIFSerialPort::readData(u32 addr)
|
||||
{
|
||||
u8 data = 0;
|
||||
if (Instance().pipe != nullptr) {
|
||||
data = Instance().pipe->read();
|
||||
//DEBUG_LOG(COMMON, "serial in %02x %c", data, data);
|
||||
SCIFSerialPort& scif = *(SCIFSerialPort *)arg;
|
||||
if (tag == 0)
|
||||
{
|
||||
bool reschedule = scif.txDone();
|
||||
scif.rxSched();
|
||||
if (reschedule || scif.pipe != nullptr)
|
||||
return scif.frameSize * scif.cyclesPerBit;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
scif.sendBreak();
|
||||
return 0;
|
||||
}
|
||||
Instance().updateStatus();
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
void SCIFSerialPort::updateStatus()
|
||||
bool SCIFSerialPort::txDone()
|
||||
{
|
||||
if (!transmitting || SCIF_SCFCR2.TFRST == 1)
|
||||
return false;
|
||||
if (txFifo.empty())
|
||||
{
|
||||
SCIF_SCFSR2.TEND = 1;
|
||||
SCIF_SCFSR2.TDFE = 1; // should not be set since the tx fifo hasn't changed but vonot needs it
|
||||
updateInterrupts();
|
||||
transmitting = false;
|
||||
return false; // don't reschedule
|
||||
}
|
||||
u8 v = txFifo.front();
|
||||
txFifo.pop_front();
|
||||
if (pipe != nullptr)
|
||||
pipe->write(v);
|
||||
u32 txTrigger = 1 << (3 - SCIF_SCFCR2.TTRG);
|
||||
if (txFifo.size() <= txTrigger) {
|
||||
SCIF_SCFSR2.TDFE = 1;
|
||||
updateInterrupts();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void SCIFSerialPort::rxSched()
|
||||
{
|
||||
if (pipe == nullptr)
|
||||
return;
|
||||
|
||||
constexpr int trigLevels[] { 1, 4, 8, 14 };
|
||||
int avail = pipe->available();
|
||||
|
||||
if (avail >= trigLevels[SCIF_SCFCR2.RTRG1 * 2 + SCIF_SCFCR2.RTRG0])
|
||||
// FIXME fifo size checked to avoid overruns but incorrect
|
||||
if (rxFifo.size() >= 16)
|
||||
{
|
||||
SCIF_SCFSR2.RDF = 1;
|
||||
if (avail >= 1)
|
||||
updateInterrupts();
|
||||
return;
|
||||
}
|
||||
|
||||
if (pipe->available() > 0)
|
||||
{
|
||||
u8 v = pipe->read();
|
||||
if (SCIF_SCSCR2.RE == 0 || SCIF_SCFCR2.RFRST == 1)
|
||||
return;
|
||||
if (rxFifo.size() == 16)
|
||||
{
|
||||
// rx overrun
|
||||
SCIF_SCLSR2.ORER = 1;
|
||||
updateInterrupts();
|
||||
WARN_LOG(SH4, "scif: Receive overrun");
|
||||
}
|
||||
else
|
||||
{
|
||||
rxFifo.push_back(v);
|
||||
constexpr u32 trigLevels[] { 1, 4, 8, 14 };
|
||||
if (rxFifo.size() >= trigLevels[SCIF_SCFCR2.RTRG]) {
|
||||
SCIF_SCFSR2.RDF = 1;
|
||||
updateInterrupts();
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO fifo might have been emptied since last rx
|
||||
else if (!rxFifo.empty())
|
||||
{
|
||||
SCIF_SCFSR2.DR = 1;
|
||||
updateInterrupts();
|
||||
}
|
||||
}
|
||||
|
||||
void SCIFSerialPort::updateBaudRate()
|
||||
{
|
||||
// 1 start bit, 7 or 8 data bits, optional parity bit, 1 or 2 stop bits
|
||||
frameSize = 1 + 8 - SCIF_SCSMR2.CHR + SCIF_SCSMR2.PE + 1 + SCIF_SCSMR2.STOP;
|
||||
int bauds = SH4_MAIN_CLOCK / 4 / (SCIF_SCBRR2 + 1) / 32 / (1 << (SCIF_SCSMR2.CKS * 2));
|
||||
cyclesPerBit = SH4_MAIN_CLOCK / bauds;
|
||||
INFO_LOG(SH4, "SCIF: Frame size %d cycles/bit %d (%d bauds)", frameSize, cyclesPerBit, bauds);
|
||||
if (sh4_sched_is_scheduled(schedId))
|
||||
sh4_sched_request(schedId, frameSize * cyclesPerBit);
|
||||
}
|
||||
|
||||
// SCIF SCFTDR2 - Transmit FIFO Data Register
|
||||
void SCIFSerialPort::SCFTDR2_write(u8 data)
|
||||
{
|
||||
SCIF_LOG("serial out %02x %c fifo_sz %d", data, data == '\0' ? ' ' : data, (int)txFifo.size());
|
||||
if (SCIF_SCFCR2.TFRST == 1)
|
||||
return;
|
||||
if (SCIF_SCSMR2.CHR == 1)
|
||||
data &= 0x7f;
|
||||
if (txFifo.empty() && !transmitting && SCIF_SCSCR2.TE == 1)
|
||||
{
|
||||
if (pipe != nullptr)
|
||||
pipe->write(data);
|
||||
transmitting = true;
|
||||
// Need to reschedule so it's doesn't happen too early (f355)
|
||||
sh4_sched_request(schedId, frameSize * cyclesPerBit);
|
||||
SCIF_SCFSR2.TDFE = 1; // immediately transfer SCFTDR2 into the shift register
|
||||
updateInterrupts();
|
||||
}
|
||||
else if (txFifo.size() < 16) {
|
||||
txFifo.push_back(data);
|
||||
}
|
||||
}
|
||||
|
||||
// SCIF_SCFSR2 read - Serial Status Register
|
||||
u16 SCIFSerialPort::readStatus()
|
||||
{
|
||||
// SCIF_LOG("SCIF_SCFSR2.read %s%s%s%s%s%s%s%s",
|
||||
// SCIF_SCFSR2.ER ? "ER " : "",
|
||||
// SCIF_SCFSR2.TEND ? "TEND " : "",
|
||||
// SCIF_SCFSR2.TDFE ? "TDFE " : "",
|
||||
// SCIF_SCFSR2.BRK ? "BRK " : "",
|
||||
// SCIF_SCFSR2.FER ? "FER " : "",
|
||||
// SCIF_SCFSR2.PER ? "PER " : "",
|
||||
// SCIF_SCFSR2.RDF ? "RDF " : "",
|
||||
// SCIF_SCFSR2.DR ? "DR" : "");
|
||||
statusLastRead = SCIF_SCFSR2.full;
|
||||
return SCIF_SCFSR2.full;
|
||||
}
|
||||
|
||||
// SCIF_SCFSR2 write - Serial Status Register
|
||||
void SCIFSerialPort::writeStatus(u16 data)
|
||||
{
|
||||
data = data | ~0x00f3 | ~statusLastRead;
|
||||
SCIF_LOG("SCIF_SCFSR2.reset %s%s%s%s%s%s%s%s",
|
||||
(data & 0x80) ? "" : "ER ",
|
||||
(data & 0x40) ? "" : "TEND ",
|
||||
(data & 0x20) ? "" : "TDFE ",
|
||||
(data & 0x10) ? "" : "BRK ",
|
||||
(data & 0x08) ? "" : "FER ",
|
||||
(data & 0x04) ? "" : "PER ",
|
||||
(data & 0x02) ? "" : "RDF ",
|
||||
(data & 0x01) ? "" : "DR");
|
||||
|
||||
SCIF_SCFSR2.full &= data;
|
||||
statusLastRead &= data;
|
||||
|
||||
updateInterrupts();
|
||||
}
|
||||
|
||||
//SCIF_SCFDR2 - FIFO Data Count Register
|
||||
u16 SCIFSerialPort::SCFDR2_read()
|
||||
{
|
||||
u16 rv = rxFifo.size() | (txFifo.size() << 8);
|
||||
SCIF_LOG("SCIF: fifo count rx %d tx %d", rv & 0xff, rv >> 8);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
//SCIF_SCFRDR2 - Receive FIFO Data Register
|
||||
u8 SCIFSerialPort::SCFRDR2_read()
|
||||
{
|
||||
if (rxFifo.empty()) {
|
||||
WARN_LOG(SH4, "Empty rx fifo read");
|
||||
return 0;
|
||||
}
|
||||
u8 data = rxFifo.front();
|
||||
rxFifo.pop_front();
|
||||
SCIF_LOG("serial in %02x %c", data, data);
|
||||
return data;
|
||||
}
|
||||
|
||||
SCIFSerialPort& SCIFSerialPort::Instance()
|
||||
{
|
||||
static SCIFSerialPort instance;
|
||||
|
@ -108,25 +229,210 @@ SCIFSerialPort& SCIFSerialPort::Instance()
|
|||
return instance;
|
||||
}
|
||||
|
||||
//SCSCR2
|
||||
|
||||
//SCSCR2 - Serial Control Register
|
||||
static u16 SCSCR2_read(u32 addr)
|
||||
{
|
||||
return SCIF_SCSCR2.full;
|
||||
}
|
||||
|
||||
static void SCSCR2_write(u32 addr, u16 data)
|
||||
void SCIFSerialPort::SCSCR2_write(u32 addr, u16 data)
|
||||
{
|
||||
SCIF_SCSCR2.full = data & 0x00fa;
|
||||
|
||||
if (SCIF_SCSCR2.TE == 0)
|
||||
{
|
||||
SCIF_SCFSR2.TEND = 1;
|
||||
//SCIF_SCFSR2.TDFE = 1; // TODO not sure about this one
|
||||
// TE must be cleared to send a break
|
||||
Instance().setBreak(SCIF_SCSPTR2.SPB2IO == 1 && SCIF_SCSPTR2.SPB2DT == 0);
|
||||
}
|
||||
else {
|
||||
Instance().setBreak(false);
|
||||
}
|
||||
updateInterrupts();
|
||||
SCIF_LOG("SCIF_SCSCR2= %s%s%s%s%s",
|
||||
SCIF_SCSCR2.TIE ? "TIE " : "",
|
||||
SCIF_SCSCR2.RIE ? "RIE " : "",
|
||||
SCIF_SCSCR2.TE ? "TE " : "",
|
||||
SCIF_SCSCR2.RE ? "RE " : "",
|
||||
SCIF_SCSCR2.REIE ? "REIE" : "");
|
||||
}
|
||||
|
||||
// SCSPTR2 - Serial Port Register
|
||||
static u16 SCSPTR2_read(u32 addr)
|
||||
{
|
||||
SCIF_LOG("SCIF_SCSPTR2.read %x", SCIF_SCSPTR2.full);
|
||||
return SCIF_SCSPTR2.full & ~0x10; // CTS active/low
|
||||
}
|
||||
|
||||
void SCIFSerialPort::setBreak(bool on)
|
||||
{
|
||||
if (on) {
|
||||
// tetris needs to send/receive breaks
|
||||
if (!sh4_sched_is_scheduled(brkSchedId))
|
||||
sh4_sched_request(brkSchedId, cyclesPerBit * frameSize);
|
||||
}
|
||||
else {
|
||||
if (sh4_sched_is_scheduled(brkSchedId))
|
||||
sh4_sched_request(brkSchedId, -1);
|
||||
}
|
||||
}
|
||||
|
||||
void SCIFSerialPort::SCSPTR2_write(u16 data)
|
||||
{
|
||||
SCIF_SCSPTR2.full = data & 0x00f3;
|
||||
if (SCIF_SCSPTR2.SPB2IO == 1)
|
||||
setBreak(SCIF_SCSPTR2.SPB2DT == 0 && SCIF_SCSCR2.TE == 0);
|
||||
else
|
||||
setBreak(false);
|
||||
|
||||
SCIF_LOG("SCIF_SCSPTR2= %s%s%s%s%s%s",
|
||||
SCIF_SCSPTR2.RTSIO ? "RTSIO " : "",
|
||||
SCIF_SCSPTR2.RTSDT ? "RTSDT " : "",
|
||||
SCIF_SCSPTR2.CTSIO ? "CTSIO " : "",
|
||||
SCIF_SCSPTR2.CTSDT ? "CTSDT " : "",
|
||||
SCIF_SCSPTR2.SPB2IO ? "SPB2IO " : "",
|
||||
SCIF_SCSPTR2.SPB2DT ? "SPB2DT" : "");
|
||||
}
|
||||
|
||||
// SCFCR2 - FIFO Control Register
|
||||
u16 SCIFSerialPort::SCFCR2_read(u32 addr)
|
||||
{
|
||||
// SCIF_LOG("SCIF_SCFCR2.read %x", SCIF_SCFCR2.full);
|
||||
return SCIF_SCFCR2.full;
|
||||
}
|
||||
|
||||
void SCIFSerialPort::SCFCR2_write(u16 data)
|
||||
{
|
||||
if (SCIF_SCFCR2.TFRST == 1 && !(data & 4))
|
||||
{
|
||||
// when TFRST 1 -> 0
|
||||
// seems to help tetris send data during sync
|
||||
SCIF_SCFSR2.TEND = 1;
|
||||
SCIF_SCFSR2.TDFE = 1;
|
||||
updateInterrupts();
|
||||
}
|
||||
SCIF_SCFCR2.full = data & 0x00ff;
|
||||
if (SCIF_SCFCR2.TFRST == 1)
|
||||
{
|
||||
txFifo.clear();
|
||||
if (pipe == nullptr)
|
||||
sh4_sched_request(schedId, -1);
|
||||
transmitting = false;
|
||||
}
|
||||
if (SCIF_SCFCR2.RFRST == 1)
|
||||
rxFifo.clear();
|
||||
SCIF_LOG("SCIF_SCFCR2= %s%s%sTTRG %d RTRG %d",
|
||||
SCIF_SCFCR2.RFRST ? "RFRST " : "",
|
||||
SCIF_SCFCR2.TFRST ? "TFRST " : "",
|
||||
SCIF_SCFCR2.MCE ? "MCE " : "",
|
||||
SCIF_SCFCR2.TTRG,
|
||||
SCIF_SCFCR2.RTRG);
|
||||
}
|
||||
|
||||
// SCBRR2 - Bit Rate Register
|
||||
void SCIFSerialPort::SCBRR2_write(u32 addr, u8 data)
|
||||
{
|
||||
SCIF_SCBRR2 = data;
|
||||
Instance().updateBaudRate();
|
||||
}
|
||||
|
||||
// SCSMR2 - Serial Mode Register
|
||||
void SCIFSerialPort::SCSMR2_write(u32 addr, u16 data)
|
||||
{
|
||||
SCIF_SCSMR2.full = data & 0x007b;
|
||||
Instance().updateBaudRate();
|
||||
}
|
||||
|
||||
void SCIFSerialPort::receiveBreak()
|
||||
{
|
||||
SCIF_LOG("Break received");
|
||||
SCIF_SCFSR2.BRK = 1;
|
||||
updateInterrupts();
|
||||
}
|
||||
|
||||
void SCIFSerialPort::sendBreak()
|
||||
{
|
||||
if (pipe != nullptr)
|
||||
pipe->sendBreak();
|
||||
}
|
||||
|
||||
|
||||
void SCIFSerialPort::init()
|
||||
{
|
||||
if (schedId == -1)
|
||||
schedId = sh4_sched_register(0, schedCallback, this);
|
||||
if (brkSchedId == -1)
|
||||
brkSchedId = sh4_sched_register(1, schedCallback, this);
|
||||
}
|
||||
|
||||
void SCIFSerialPort::term()
|
||||
{
|
||||
if (schedId != -1) {
|
||||
sh4_sched_unregister(schedId);
|
||||
schedId = -1;
|
||||
}
|
||||
if (brkSchedId != -1) {
|
||||
sh4_sched_unregister(brkSchedId);
|
||||
brkSchedId = -1;
|
||||
}
|
||||
}
|
||||
|
||||
void SCIFSerialPort::serialize(Serializer& ser)
|
||||
{
|
||||
sh4_sched_serialize(ser, schedId);
|
||||
sh4_sched_serialize(ser, brkSchedId);
|
||||
ser << statusLastRead;
|
||||
ser << (int)txFifo.size();
|
||||
for (u8 b : txFifo)
|
||||
ser << b;
|
||||
ser << (int)rxFifo.size();
|
||||
for (u8 b : txFifo)
|
||||
ser << b;
|
||||
ser << transmitting;
|
||||
}
|
||||
|
||||
void SCIFSerialPort::deserialize(Deserializer& deser)
|
||||
{
|
||||
txFifo.clear();
|
||||
rxFifo.clear();
|
||||
if (deser.version() >= Deserializer::V43)
|
||||
{
|
||||
sh4_sched_deserialize(deser, schedId);
|
||||
sh4_sched_deserialize(deser, brkSchedId);
|
||||
deser >> statusLastRead;
|
||||
int size;
|
||||
deser >> size;
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
u8 b;
|
||||
deser >> b;
|
||||
txFifo.push_back(b);
|
||||
}
|
||||
deser >> size;
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
u8 b;
|
||||
deser >> b;
|
||||
rxFifo.push_back(b);
|
||||
}
|
||||
deser >> transmitting;
|
||||
}
|
||||
else
|
||||
{
|
||||
statusLastRead = 0;
|
||||
transmitting = false;
|
||||
}
|
||||
updateBaudRate();
|
||||
}
|
||||
|
||||
struct PTYPipe : public SerialPort::Pipe
|
||||
{
|
||||
void write(u8 data) override {
|
||||
if (config::SerialConsole)
|
||||
::write(tty, &data, 1);
|
||||
void write(u8 data) override
|
||||
{
|
||||
if (config::SerialConsole) {
|
||||
int rc = ::write(tty, &data, 1);
|
||||
(void)rc;
|
||||
}
|
||||
}
|
||||
|
||||
int available() override {
|
||||
|
@ -138,10 +444,13 @@ struct PTYPipe : public SerialPort::Pipe
|
|||
return count;
|
||||
}
|
||||
|
||||
u8 read() override {
|
||||
u8 read() override
|
||||
{
|
||||
u8 data = 0;
|
||||
if (tty != 1)
|
||||
::read(tty, &data, 1);
|
||||
if (tty != 1) {
|
||||
int rc = ::read(tty, &data, 1);
|
||||
(void)rc;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
|
@ -189,12 +498,46 @@ struct PTYPipe : public SerialPort::Pipe
|
|||
::close(tty);
|
||||
tty = 1;
|
||||
}
|
||||
SCIFSerialPort::Instance().setPipe(nullptr);
|
||||
}
|
||||
|
||||
private:
|
||||
int tty = 1;
|
||||
};
|
||||
static PTYPipe ptyPipe;
|
||||
|
||||
void setupPtyPipe()
|
||||
{
|
||||
static PTYPipe ptyPipe;
|
||||
|
||||
if (config::SerialConsole || config::SerialPTY)
|
||||
{
|
||||
if (SCIFSerialPort::Instance().getPipe() == nullptr)
|
||||
ptyPipe.init();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (SCIFSerialPort::Instance().getPipe() == &ptyPipe)
|
||||
ptyPipe.term();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
class SingletonForward {
|
||||
|
||||
};
|
||||
|
||||
template<typename Ret, typename Class, typename... Args>
|
||||
struct SingletonForward<Ret(Class::*)(Args...)>
|
||||
{
|
||||
static Ret(*forward(Ret(*function)(u32 addr, Args...)))(u32 addr, Args...) {
|
||||
return function;
|
||||
}
|
||||
};
|
||||
|
||||
#define SINGLETON_FORWARD(accessor, function) \
|
||||
SingletonForward<decltype(&std::remove_reference<decltype(accessor)>::type::function)>::forward([](u32 addr, auto... args) { \
|
||||
return accessor.function(args...); \
|
||||
})
|
||||
|
||||
//Init term res
|
||||
void SCIFRegisters::init()
|
||||
|
@ -204,56 +547,43 @@ void SCIFRegisters::init()
|
|||
// Serial Communication Interface with FIFO
|
||||
|
||||
//SCIF SCSMR2 0xFFE80000 0x1FE80000 16 0x0000 0x0000 Held Held Pclk
|
||||
setRW<SCIF_SCSMR2_addr, u16, 0x007b>();
|
||||
setWriteHandler<SCIF_SCSMR2_addr, u16>(SCIFSerialPort::SCSMR2_write);
|
||||
|
||||
//SCIF SCBRR2 0xFFE80004 0x1FE80004 8 0xFF 0xFF Held Held Pclk
|
||||
setRW<SCIF_SCBRR2_addr, u8>();
|
||||
setWriteHandler<SCIF_SCBRR2_addr, u8>(SCIFSerialPort::SCBRR2_write);
|
||||
|
||||
//SCIF SCSCR2 0xFFE80008 0x1FE80008 16 0x0000 0x0000 Held Held Pclk
|
||||
setHandlers<SCIF_SCSCR2_addr>(SCSCR2_read, SCSCR2_write);
|
||||
setHandlers<SCIF_SCSCR2_addr>(SCSCR2_read, SCIFSerialPort::SCSCR2_write);
|
||||
|
||||
//SCIF SCFTDR2 0xFFE8000C 0x1FE8000C 8 Undefined Undefined Held Held Pclk
|
||||
setWriteOnly<SCIF_SCFTDR2_addr>(SCIFSerialPort::writeData);
|
||||
setWriteOnly<SCIF_SCFTDR2_addr>(SINGLETON_FORWARD(SCIFSerialPort::Instance(), SCFTDR2_write));
|
||||
|
||||
//SCIF SCFSR2 0xFFE80010 0x1FE80010 16 0x0060 0x0060 Held Held Pclk
|
||||
setHandlers<SCIF_SCFSR2_addr>(SCIFSerialPort::readStatus, SCIFSerialPort::writeStatus);
|
||||
setHandlers<SCIF_SCFSR2_addr>(SINGLETON_FORWARD(SCIFSerialPort::Instance(), readStatus),
|
||||
SINGLETON_FORWARD(SCIFSerialPort::Instance(), writeStatus));
|
||||
|
||||
//READ only
|
||||
//SCIF SCFRDR2 0xFFE80014 0x1FE80014 8 Undefined Undefined Held Held Pclk
|
||||
setReadOnly<SCIF_SCFRDR2_addr>(SCIFSerialPort::readData);
|
||||
setReadOnly<SCIF_SCFRDR2_addr>(SINGLETON_FORWARD(SCIFSerialPort::Instance(), SCFRDR2_read));
|
||||
|
||||
//SCIF SCFCR2 0xFFE80018 0x1FE80018 16 0x0000 0x0000 Held Held Pclk
|
||||
setRW<SCIF_SCFCR2_addr, u16, 0x00ff>();
|
||||
setHandlers<SCIF_SCFCR2_addr>(SCIFSerialPort::SCFCR2_read, SINGLETON_FORWARD(SCIFSerialPort::Instance(), SCFCR2_write));
|
||||
|
||||
//Read only
|
||||
//SCIF SCFDR2 0xFFE8001C 0x1FE8001C 16 0x0000 0x0000 Held Held Pclk
|
||||
setReadOnly<SCIF_SCFDR2_addr>(SCIFSerialPort::readSCFDR2);
|
||||
setReadOnly<SCIF_SCFDR2_addr>(SINGLETON_FORWARD(SCIFSerialPort::Instance(), SCFDR2_read));
|
||||
|
||||
//SCIF SCSPTR2 0xFFE80020 0x1FE80020 16 0x0000 0x0000 Held Held Pclk
|
||||
setRW<SCIF_SCSPTR2_addr, u16, 0x00f3>();
|
||||
setHandlers<SCIF_SCSPTR2_addr>(SCSPTR2_read, SINGLETON_FORWARD(SCIFSerialPort::Instance(), SCSPTR2_write));
|
||||
|
||||
//SCIF SCLSR2 0xFFE80024 0x1FE80024 16 0x0000 0x0000 Held Held Pclk
|
||||
setRW<SCIF_SCLSR2_addr, u16, 1>();
|
||||
|
||||
SCIFSerialPort::Instance().init();
|
||||
|
||||
reset(true);
|
||||
}
|
||||
|
||||
void SCIRegisters::init()
|
||||
{
|
||||
super::init();
|
||||
|
||||
// Serial Communication Interface
|
||||
setRW<SCI_SCSMR1_addr, u8>();
|
||||
setRW<SCI_SCBRR1_addr, u8>();
|
||||
setRW<SCI_SCSCR1_addr, u8>();
|
||||
setRW<SCI_SCTDR1_addr, u8>();
|
||||
setRW<SCI_SCSSR1_addr, u8, 0xf9>();
|
||||
setReadOnly<SCI_SCRDR1_addr, u8>();
|
||||
setRW<SCI_SCSPTR1_addr, u8, 0x8f>();
|
||||
|
||||
reset();
|
||||
}
|
||||
|
||||
void SCIFRegisters::reset(bool hard)
|
||||
{
|
||||
super::reset();
|
||||
|
@ -274,7 +604,30 @@ void SCIFRegisters::reset(bool hard)
|
|||
SCIF_SCFSR2.full = 0x060;
|
||||
|
||||
if (hard)
|
||||
ptyPipe.init();
|
||||
SCIFSerialPort::Instance().setPipe(nullptr);
|
||||
}
|
||||
|
||||
void SCIFRegisters::term()
|
||||
{
|
||||
SCIFSerialPort::Instance().term();
|
||||
|
||||
super::term();
|
||||
}
|
||||
|
||||
void SCIRegisters::init()
|
||||
{
|
||||
super::init();
|
||||
|
||||
// Serial Communication Interface
|
||||
setRW<SCI_SCSMR1_addr, u8>();
|
||||
setRW<SCI_SCBRR1_addr, u8>();
|
||||
setRW<SCI_SCSCR1_addr, u8>();
|
||||
setRW<SCI_SCTDR1_addr, u8>();
|
||||
setRW<SCI_SCSSR1_addr, u8, 0xf9>();
|
||||
setReadOnly<SCI_SCRDR1_addr, u8>();
|
||||
setRW<SCI_SCSPTR1_addr, u8, 0x8f>();
|
||||
|
||||
reset();
|
||||
}
|
||||
|
||||
void SCIRegisters::reset()
|
||||
|
@ -285,9 +638,3 @@ void SCIRegisters::reset()
|
|||
SCI_SCTDR1 = 0xff;
|
||||
SCI_SCSSR1 = 0x84;
|
||||
}
|
||||
|
||||
void SCIFRegisters::term()
|
||||
{
|
||||
super::term();
|
||||
ptyPipe.term();
|
||||
}
|
||||
|
|
|
@ -684,6 +684,7 @@ void serialize(Serializer& ser)
|
|||
ser << TMU;
|
||||
ser << SCI;
|
||||
ser << SCIF;
|
||||
SCIFSerialPort::Instance().serialize(ser);
|
||||
icache.Serialize(ser);
|
||||
ocache.Serialize(ser);
|
||||
|
||||
|
@ -739,6 +740,7 @@ void deserialize(Deserializer& deser)
|
|||
deser >> SCI;
|
||||
deser >> SCIF;
|
||||
}
|
||||
SCIFSerialPort::Instance().deserialize(deser);
|
||||
if (deser.version() >= Deserializer::V9
|
||||
// Note (lr): was added in V11 fa49de29 24/12/2020 but ver not updated until V12 (13/4/2021)
|
||||
|| (deser.version() >= Deserializer::V11_LIBRETRO && deser.version() <= Deserializer::VLAST_LIBRETRO))
|
||||
|
|
|
@ -1233,23 +1233,13 @@ union SCIF_SCSMR2_type
|
|||
{
|
||||
struct
|
||||
{
|
||||
u32 CKS0 : 1;
|
||||
u32 CKS1 : 1;
|
||||
u32 res_0 : 1;
|
||||
u32 CKS : 2;
|
||||
u32 : 1;
|
||||
u32 STOP : 1;
|
||||
u32 OE_paritymode : 1;
|
||||
u32 PE : 1;
|
||||
u32 CHR : 1;
|
||||
u32 res_1 : 1;
|
||||
//8
|
||||
u32 res_2 : 1;
|
||||
u32 res_3 : 1;
|
||||
u32 res_4 : 1;
|
||||
u32 res_5 : 1;
|
||||
u32 res_6 : 1;
|
||||
u32 res_7 : 1;
|
||||
u32 res_8 : 1;
|
||||
u32 res_9 : 1;
|
||||
u32 : 9;
|
||||
//16
|
||||
};
|
||||
u16 full;
|
||||
|
@ -1265,23 +1255,16 @@ union SCIF_SCSCR2_type
|
|||
{
|
||||
struct
|
||||
{
|
||||
u32 res_0 : 1;
|
||||
u32 : 1;
|
||||
u32 CKE1 : 1;
|
||||
u32 res_1 : 1;
|
||||
u32 : 1;
|
||||
u32 REIE : 1;
|
||||
u32 RE : 1;
|
||||
u32 TE : 1;
|
||||
u32 RIE : 1;
|
||||
u32 TIE : 1;
|
||||
//8
|
||||
u32 res_2 : 1;
|
||||
u32 res_3 : 1;
|
||||
u32 res_4 : 1;
|
||||
u32 res_5 : 1;
|
||||
u32 res_6 : 1;
|
||||
u32 res_7 : 1;
|
||||
u32 res_8 : 1;
|
||||
u32 res_9 : 1;
|
||||
u32 : 8;
|
||||
//16
|
||||
};
|
||||
u16 full;
|
||||
|
@ -1305,14 +1288,8 @@ union SCIF_SCFSR2_type
|
|||
u32 TEND : 1;
|
||||
u32 ER : 1;
|
||||
//8
|
||||
u32 FER0 : 1;
|
||||
u32 FER1 : 1;
|
||||
u32 FER2 : 1;
|
||||
u32 FER3 : 1;
|
||||
u32 PER0 : 1;
|
||||
u32 PER1 : 1;
|
||||
u32 PER2 : 1;
|
||||
u32 PER3 : 1;
|
||||
u32 FERn : 4;
|
||||
u32 PERn : 4;
|
||||
//16
|
||||
};
|
||||
u16 full;
|
||||
|
@ -1331,19 +1308,10 @@ union SCIF_SCFCR2_type
|
|||
u32 RFRST : 1;
|
||||
u32 TFRST : 1;
|
||||
u32 MCE : 1;
|
||||
u32 TTRG0 : 1;
|
||||
u32 TTRG1 : 1;
|
||||
u32 RTRG0 : 1;
|
||||
u32 RTRG1 : 1;
|
||||
u32 TTRG : 2;
|
||||
u32 RTRG : 2;
|
||||
//8
|
||||
u32 res_0 : 1;
|
||||
u32 res_1 : 1;
|
||||
u32 res_2 : 1;
|
||||
u32 res_3 : 1;
|
||||
u32 res_4 : 1;
|
||||
u32 res_5 : 1;
|
||||
u32 res_6 : 1;
|
||||
u32 res_7 : 1;
|
||||
u32 : 8;
|
||||
//16
|
||||
};
|
||||
u16 full;
|
||||
|
@ -1357,10 +1325,10 @@ union SCIF_SCFDR2_type
|
|||
struct
|
||||
{
|
||||
u32 R : 5;
|
||||
u32 res_0 : 3;
|
||||
u32 : 3;
|
||||
//8
|
||||
u32 T : 5;
|
||||
u32 res_1 : 3;
|
||||
u32 : 3;
|
||||
//16
|
||||
};
|
||||
u16 full;
|
||||
|
@ -1373,21 +1341,13 @@ union SCIF_SCSPTR2_type
|
|||
{
|
||||
u32 SPB2DT : 1;
|
||||
u32 SPB2IO : 1;
|
||||
u32 res_0 : 1;
|
||||
u32 res_1 : 1;
|
||||
u32 : 2;
|
||||
u32 CTSDT : 1;
|
||||
u32 CTSIO : 1;
|
||||
u32 RTSDT : 1;
|
||||
u32 RTSIO : 1;
|
||||
//8
|
||||
u32 res_2 : 1;
|
||||
u32 res_3 : 1;
|
||||
u32 res_4 : 1;
|
||||
u32 res_5 : 1;
|
||||
u32 res_6 : 1;
|
||||
u32 res_7 : 1;
|
||||
u32 res_8 : 1;
|
||||
u32 res_9 : 1;
|
||||
u32 : 8;
|
||||
//16
|
||||
};
|
||||
u16 full;
|
||||
|
@ -1400,9 +1360,7 @@ union SCIF_SCLSR2_type
|
|||
struct
|
||||
{
|
||||
u32 ORER : 1;
|
||||
u32 res_0 : 7;
|
||||
//8
|
||||
u32 res_1 : 8;
|
||||
u32 :15;
|
||||
//16
|
||||
};
|
||||
u16 full;
|
||||
|
|
|
@ -138,6 +138,11 @@ void sh4_sched_request(int id, int cycles)
|
|||
sh4_sched_ffts();
|
||||
}
|
||||
|
||||
bool sh4_sched_is_scheduled(int id)
|
||||
{
|
||||
return sch_list[id].end != -1;
|
||||
}
|
||||
|
||||
/* Returns how much time has passed for this callback */
|
||||
static int sh4_sched_elapsed(sched_list& sched)
|
||||
{
|
||||
|
|
|
@ -31,11 +31,16 @@ u64 sh4_sched_now64();
|
|||
Schedule a callback to be called sh4 *cycles* after the
|
||||
invocation of this function. *Cycles* range is (0, 200M].
|
||||
|
||||
Passing a value of 0 disables the callback.
|
||||
Passing a value of -1 disables the callback.
|
||||
If called multiple times, only the last call is in effect
|
||||
*/
|
||||
void sh4_sched_request(int id, int cycles);
|
||||
|
||||
/*
|
||||
Returns true if the callback is scheduled to be called in the future.
|
||||
*/
|
||||
bool sh4_sched_is_scheduled(int id);
|
||||
|
||||
/*
|
||||
Tick for *cycles*
|
||||
*/
|
||||
|
|
|
@ -20,7 +20,9 @@
|
|||
#include "cfg/option.h"
|
||||
#include "ggpo.h"
|
||||
#include "naomi_network.h"
|
||||
#include "net_serial_maxspeed.h"
|
||||
//#include "net_serial_maxspeed.h"
|
||||
#include "null_modem.h"
|
||||
#include "hw/naomi/naomi_flashrom.h"
|
||||
|
||||
NetworkHandshake *NetworkHandshake::instance;
|
||||
|
||||
|
@ -69,7 +71,13 @@ void NetworkHandshake::init()
|
|||
else if (NaomiNetworkSupported())
|
||||
instance = new NaomiNetworkHandshake();
|
||||
else if (config::NetworkEnable && settings.content.gameId == "MAXIMUM SPEED")
|
||||
instance = new MaxSpeedHandshake();
|
||||
// instance = new MaxSpeedHandshake();
|
||||
{
|
||||
configure_maxspeed_flash(true, config::ActAsServer);
|
||||
instance = new BattleCableHandshake();
|
||||
}
|
||||
else if (config::BattleCableEnable && !settings.platform.isNaomi())
|
||||
instance = new BattleCableHandshake();
|
||||
else
|
||||
instance = nullptr;
|
||||
}
|
||||
|
|
|
@ -88,12 +88,14 @@ struct MaxSpeedNetPipe : public SerialPort::Pipe
|
|||
}
|
||||
|
||||
createSocket();
|
||||
SCIFSerialPort::Instance().setPipe(this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void shutdown()
|
||||
{
|
||||
SCIFSerialPort::Instance().setPipe(nullptr);
|
||||
enableNetworkBroadcast(false);
|
||||
if (VALID(sock))
|
||||
closesocket(sock);
|
||||
|
@ -215,7 +217,8 @@ private:
|
|||
peerAddress.sin_family = AF_INET;
|
||||
peerAddress.sin_addr.s_addr = INADDR_BROADCAST;
|
||||
peerAddress.sin_port = htons(NaomiNetwork::SERVER_PORT);
|
||||
if (!config::NetworkServer.get().empty())
|
||||
// ignore server name if acting as server
|
||||
if (!config::NetworkServer.get().empty() && !config::ActAsServer)
|
||||
{
|
||||
auto pos = config::NetworkServer.get().find_last_of(':');
|
||||
std::string server;
|
||||
|
@ -242,8 +245,6 @@ private:
|
|||
}
|
||||
else
|
||||
enableNetworkBroadcast(true);
|
||||
|
||||
SCIFSerialPort::Instance().setPipe(this);
|
||||
}
|
||||
|
||||
sock_t sock = INVALID_SOCKET;
|
||||
|
|
|
@ -0,0 +1,271 @@
|
|||
/*
|
||||
Copyright 2023 flyinghead
|
||||
|
||||
This file is part of Flycast.
|
||||
|
||||
Flycast 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.
|
||||
|
||||
Flycast 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 Flycast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "hw/sh4/modules/modules.h"
|
||||
#include "net_platform.h"
|
||||
#include "cfg/option.h"
|
||||
#include "miniupnp.h"
|
||||
#include "hw/sh4/sh4_sched.h"
|
||||
#include "naomi_network.h"
|
||||
#include "net_handshake.h"
|
||||
#include <deque>
|
||||
|
||||
class NullModemPipe : public SerialPort::Pipe
|
||||
{
|
||||
public:
|
||||
class Exception : public FlycastException
|
||||
{
|
||||
public:
|
||||
Exception(const std::string& reason) : FlycastException(reason) {}
|
||||
};
|
||||
|
||||
// Serial TX
|
||||
void write(u8 data) override
|
||||
{
|
||||
u8 packet[2] = { 'D', data };
|
||||
int rc = sendto(sock, (const char *)&packet[0], sizeof(packet), 0, (const sockaddr *)&peerAddress, sizeof(peerAddress));
|
||||
if (rc != sizeof(packet))
|
||||
ERROR_LOG(NETWORK, "sendto: %d errno %d", rc, get_last_error());
|
||||
DEBUG_LOG(NETWORK, "Write %02x %c (buf rx %d)", data, data, (int)rxBuffer.size());
|
||||
}
|
||||
|
||||
void sendBreak() override
|
||||
{
|
||||
const char b = 'B';
|
||||
int rc = sendto(sock, &b, 1, 0, (const sockaddr *)&peerAddress, sizeof(peerAddress));
|
||||
if (rc != 1)
|
||||
ERROR_LOG(NETWORK, "sendto: %d errno %d", rc, get_last_error());
|
||||
DEBUG_LOG(NETWORK, "Send Break");
|
||||
}
|
||||
|
||||
// RX buffer Size
|
||||
int available() override {
|
||||
poll();
|
||||
checkBreak();
|
||||
int realSize = 0;
|
||||
for (u32 b : rxBuffer)
|
||||
if (b != (u32)~0)
|
||||
realSize++;
|
||||
return realSize;
|
||||
}
|
||||
|
||||
// Serial RX
|
||||
u8 read() override
|
||||
{
|
||||
poll();
|
||||
if (rxBuffer.empty()) {
|
||||
WARN_LOG(NETWORK, "NetPipe: empty read");
|
||||
return 0;
|
||||
}
|
||||
u8 b = rxBuffer.front();
|
||||
rxBuffer.pop_front();
|
||||
DEBUG_LOG(NETWORK, "Read %02x (buf rx %d)", b, (int)rxBuffer.size());
|
||||
checkBreak();
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
~NullModemPipe()
|
||||
{
|
||||
shutdown();
|
||||
}
|
||||
|
||||
bool init()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
WSADATA wsaData;
|
||||
if (WSAStartup(MAKEWORD(2, 0), &wsaData) != 0)
|
||||
{
|
||||
ERROR_LOG(NETWORK, "WSAStartup failed. errno=%d", get_last_error());
|
||||
throw Exception("WSAStartup failed");
|
||||
}
|
||||
#endif
|
||||
if (config::EnableUPnP)
|
||||
{
|
||||
miniupnp.Init();
|
||||
miniupnp.AddPortMapping(config::LocalPort, true);
|
||||
}
|
||||
|
||||
createSocket();
|
||||
SCIFSerialPort::Instance().setPipe(this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void shutdown()
|
||||
{
|
||||
enableNetworkBroadcast(false);
|
||||
if (VALID(sock))
|
||||
closesocket(sock);
|
||||
sock = INVALID_SOCKET;
|
||||
SCIFSerialPort::Instance().setPipe(nullptr);
|
||||
}
|
||||
|
||||
private:
|
||||
void checkBreak()
|
||||
{
|
||||
if (!rxBuffer.empty() && rxBuffer.front() == (u32)~0) {
|
||||
SCIFSerialPort::Instance().receiveBreak();
|
||||
rxBuffer.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
void poll()
|
||||
{
|
||||
if (lastPoll == sh4_sched_now64())
|
||||
return;
|
||||
lastPoll = sh4_sched_now64();
|
||||
u8 data[0x100];
|
||||
sockaddr_in addr;
|
||||
while (true)
|
||||
{
|
||||
socklen_t len = sizeof(addr);
|
||||
int rc = recvfrom(sock, (char *)data, sizeof(data), 0, (sockaddr *)&addr, &len);
|
||||
if (rc == -1)
|
||||
{
|
||||
int error = get_last_error();
|
||||
if (error == L_EWOULDBLOCK || error == L_EAGAIN)
|
||||
break;
|
||||
#ifdef _WIN32
|
||||
if (error == WSAECONNRESET)
|
||||
// Happens if the previous send resulted in an ICMP Port Unreachable message
|
||||
break;
|
||||
#endif
|
||||
throw Exception("Receive error: errno " + std::to_string(error));
|
||||
}
|
||||
if (peerAddress.sin_addr.s_addr == INADDR_BROADCAST)
|
||||
{
|
||||
if (addr.sin_port != htons(config::LocalPort) || !is_local_address(addr.sin_addr.s_addr))
|
||||
{
|
||||
peerAddress.sin_addr.s_addr = addr.sin_addr.s_addr;
|
||||
peerAddress.sin_port = addr.sin_port;
|
||||
enableNetworkBroadcast(false);
|
||||
NOTICE_LOG(NETWORK, "Data received from peer %x:%d", htonl(addr.sin_addr.s_addr), htons(addr.sin_port));
|
||||
}
|
||||
else
|
||||
{
|
||||
// this is coming from us so ignore it
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (rc == 2)
|
||||
{
|
||||
if (data[0] != 'D')
|
||||
ERROR_LOG(NETWORK, "Unexpected packet '%c'", data[0]);
|
||||
else
|
||||
rxBuffer.push_back(data[1]);
|
||||
}
|
||||
else if (rc == 1)
|
||||
{
|
||||
if (data[0] != 'B')
|
||||
ERROR_LOG(NETWORK, "Unexpected packet '%c'", data[0]);
|
||||
else
|
||||
rxBuffer.push_back(~0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void createSocket()
|
||||
{
|
||||
sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
if (sock == INVALID_SOCKET)
|
||||
{
|
||||
ERROR_LOG(NETWORK, "Socket creation failed: errno %d", get_last_error());
|
||||
throw Exception("Socket creation failed");
|
||||
}
|
||||
int option = 1;
|
||||
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&option, sizeof(option));
|
||||
|
||||
sockaddr_in serveraddr{};
|
||||
serveraddr.sin_family = AF_INET;
|
||||
serveraddr.sin_port = htons(config::LocalPort);
|
||||
|
||||
if (::bind(sock, (sockaddr *)&serveraddr, sizeof(serveraddr)) < 0)
|
||||
{
|
||||
ERROR_LOG(NETWORK, "NaomiServer: bind() failed. errno=%d", get_last_error());
|
||||
closesocket(sock);
|
||||
|
||||
throw Exception("Socket bind failed");
|
||||
}
|
||||
set_non_blocking(sock);
|
||||
|
||||
// Allow broadcast packets to be sent
|
||||
int broadcast = 1;
|
||||
if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (const char *)&broadcast, sizeof(broadcast)) == -1)
|
||||
WARN_LOG(NETWORK, "setsockopt(SO_BROADCAST) failed. errno=%d", get_last_error());
|
||||
|
||||
peerAddress.sin_family = AF_INET;
|
||||
peerAddress.sin_addr.s_addr = INADDR_BROADCAST;
|
||||
peerAddress.sin_port = htons(NaomiNetwork::SERVER_PORT);
|
||||
if (!config::NetworkServer.get().empty()
|
||||
// ignore server name if acting as server (maxspeed)
|
||||
&& (!config::ActAsServer || settings.platform.isConsole()))
|
||||
{
|
||||
auto pos = config::NetworkServer.get().find_last_of(':');
|
||||
std::string server;
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
peerAddress.sin_port = htons(atoi(config::NetworkServer.get().substr(pos + 1).c_str()));
|
||||
server = config::NetworkServer.get().substr(0, pos);
|
||||
}
|
||||
else
|
||||
server = config::NetworkServer;
|
||||
addrinfo *resultAddr;
|
||||
if (getaddrinfo(server.c_str(), 0, nullptr, &resultAddr))
|
||||
WARN_LOG(NETWORK, "Server %s is unknown", server.c_str());
|
||||
else
|
||||
{
|
||||
for (addrinfo *ptr = resultAddr; ptr != nullptr; ptr = ptr->ai_next)
|
||||
if (ptr->ai_family == AF_INET)
|
||||
{
|
||||
peerAddress.sin_addr.s_addr = ((sockaddr_in *)ptr->ai_addr)->sin_addr.s_addr;
|
||||
break;
|
||||
}
|
||||
freeaddrinfo(resultAddr);
|
||||
}
|
||||
}
|
||||
else
|
||||
enableNetworkBroadcast(true);
|
||||
}
|
||||
|
||||
sock_t sock = INVALID_SOCKET;
|
||||
MiniUPnP miniupnp;
|
||||
std::deque<u32> rxBuffer;
|
||||
sockaddr_in peerAddress{};
|
||||
u64 lastPoll = 0;
|
||||
};
|
||||
|
||||
class BattleCableHandshake : public NetworkHandshake
|
||||
{
|
||||
public:
|
||||
std::future<bool> start() override {
|
||||
std::promise<bool> promise;
|
||||
promise.set_value(pipe.init());
|
||||
return promise.get_future();
|
||||
}
|
||||
void stop() override {
|
||||
pipe.shutdown();
|
||||
}
|
||||
bool canStartNow() override {
|
||||
return true;
|
||||
}
|
||||
void startNow() override {}
|
||||
|
||||
private:
|
||||
NullModemPipe pipe;
|
||||
};
|
|
@ -671,7 +671,7 @@ const char *maple_device_types[] =
|
|||
"Keyboard",
|
||||
"Mouse",
|
||||
"Twin Stick",
|
||||
"Ascii Stick",
|
||||
"Arcade/Ascii Stick",
|
||||
"Maracas Controller",
|
||||
"Fishing Controller",
|
||||
"Pop'n Music controller",
|
||||
|
@ -2349,39 +2349,61 @@ static void gui_display_settings()
|
|||
ImGui::PopStyleVar();
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
if (ImGui::BeginTabItem("Advanced"))
|
||||
if (ImGui::BeginTabItem("Network"))
|
||||
{
|
||||
ImGuiStyle& style = ImGui::GetStyle();
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, normal_padding);
|
||||
header("CPU Mode");
|
||||
{
|
||||
ImGui::Columns(2, "cpu_modes", false);
|
||||
OptionRadioButton("Dynarec", config::DynarecEnabled, true,
|
||||
"Use the dynamic recompiler. Recommended in most cases");
|
||||
|
||||
header("Network Type");
|
||||
{
|
||||
DisabledScope scope(game_started);
|
||||
|
||||
int netType = 0;
|
||||
if (config::GGPOEnable)
|
||||
netType = 1;
|
||||
else if (config::NetworkEnable)
|
||||
netType = 2;
|
||||
else if (config::BattleCableEnable)
|
||||
netType = 3;
|
||||
ImGui::Columns(4, "networkType", false);
|
||||
ImGui::RadioButton("Disabled", &netType, 0);
|
||||
ImGui::NextColumn();
|
||||
OptionRadioButton("Interpreter", config::DynarecEnabled, false,
|
||||
"Use the interpreter. Very slow but may help in case of a dynarec problem");
|
||||
ImGui::Columns(1, NULL, false);
|
||||
ImGui::RadioButton("GGPO", &netType, 1);
|
||||
ImGui::SameLine(0, style.ItemInnerSpacing.x);
|
||||
ShowHelpMarker("Enable networking using GGPO");
|
||||
ImGui::NextColumn();
|
||||
ImGui::RadioButton("Naomi", &netType, 2);
|
||||
ImGui::SameLine(0, style.ItemInnerSpacing.x);
|
||||
ShowHelpMarker("Enable networking for supported Naomi and Atomiswave games");
|
||||
ImGui::NextColumn();
|
||||
ImGui::RadioButton("Battle Cable", &netType, 3);
|
||||
ImGui::SameLine(0, style.ItemInnerSpacing.x);
|
||||
ShowHelpMarker("Emulate the Taisen (Battle) null modem cable for games that support it");
|
||||
ImGui::Columns(1, nullptr, false);
|
||||
|
||||
OptionSlider("SH4 Clock", config::Sh4Clock, 100, 300,
|
||||
"Over/Underclock the main SH4 CPU. Default is 200 MHz. Other values may crash, freeze or trigger unexpected nuclear reactions.",
|
||||
"%d MHz");
|
||||
}
|
||||
ImGui::Spacing();
|
||||
header("Network");
|
||||
{
|
||||
config::GGPOEnable = false;
|
||||
config::NetworkEnable = false;
|
||||
config::BattleCableEnable = false;
|
||||
switch (netType) {
|
||||
case 1:
|
||||
config::GGPOEnable = true;
|
||||
break;
|
||||
case 2:
|
||||
config::NetworkEnable = true;
|
||||
break;
|
||||
case 3:
|
||||
config::BattleCableEnable = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (config::GGPOEnable || config::NetworkEnable || config::BattleCableEnable) {
|
||||
ImGui::Spacing();
|
||||
header("Configuration");
|
||||
}
|
||||
{
|
||||
if (config::GGPOEnable)
|
||||
{
|
||||
DisabledScope scope(game_started);
|
||||
|
||||
OptionCheckbox("Broadband Adapter Emulation", config::EmulateBBA,
|
||||
"Emulate the Ethernet Broadband Adapter (BBA) instead of the Modem");
|
||||
}
|
||||
OptionCheckbox("Enable GGPO Networking", config::GGPOEnable,
|
||||
"Enable networking using GGPO");
|
||||
OptionCheckbox("Enable Naomi Networking", config::NetworkEnable,
|
||||
"Enable networking for supported Naomi games");
|
||||
if (config::GGPOEnable)
|
||||
{
|
||||
config::NetworkEnable = false;
|
||||
config::NetworkEnable = false;
|
||||
OptionCheckbox("Play as Player 1", config::ActAsServer,
|
||||
"Deselect to play as player 2");
|
||||
char server_name[256];
|
||||
|
@ -2415,10 +2437,10 @@ static void gui_display_settings()
|
|||
}
|
||||
}
|
||||
OptionCheckbox("Network Statistics", config::NetworkStats,
|
||||
"Display network statistics on screen");
|
||||
}
|
||||
else if (config::NetworkEnable)
|
||||
{
|
||||
"Display network statistics on screen");
|
||||
}
|
||||
else if (config::NetworkEnable)
|
||||
{
|
||||
OptionCheckbox("Act as Server", config::ActAsServer,
|
||||
"Create a local server for Naomi network games");
|
||||
if (!config::ActAsServer)
|
||||
|
@ -2436,17 +2458,65 @@ static void gui_display_settings()
|
|||
ImGui::SameLine();
|
||||
ShowHelpMarker("The local UDP port to use");
|
||||
config::LocalPort.set(atoi(localPort));
|
||||
}
|
||||
}
|
||||
else if (config::BattleCableEnable)
|
||||
{
|
||||
char server_name[256];
|
||||
strcpy(server_name, config::NetworkServer.get().c_str());
|
||||
ImGui::InputText("Peer", server_name, sizeof(server_name), ImGuiInputTextFlags_CharsNoBlank, nullptr, nullptr);
|
||||
ImGui::SameLine();
|
||||
ShowHelpMarker("The peer to connect to. Leave blank to find a player automatically on the default port");
|
||||
config::NetworkServer.set(server_name);
|
||||
char localPort[256];
|
||||
sprintf(localPort, "%d", (int)config::LocalPort);
|
||||
ImGui::InputText("Local Port", localPort, sizeof(localPort), ImGuiInputTextFlags_CharsDecimal, nullptr, nullptr);
|
||||
ImGui::SameLine();
|
||||
ShowHelpMarker("The local UDP port to use");
|
||||
config::LocalPort.set(atoi(localPort));
|
||||
}
|
||||
}
|
||||
ImGui::Spacing();
|
||||
header("Network Options");
|
||||
{
|
||||
OptionCheckbox("Enable UPnP", config::EnableUPnP, "Automatically configure your network router for netplay");
|
||||
OptionCheckbox("Broadcast Digital Outputs", config::NetworkOutput, "Broadcast digital outputs and force-feedback state on TCP port 8000. "
|
||||
"Compatible with the \"-output network\" MAME option. Arcade games only.");
|
||||
{
|
||||
DisabledScope scope(game_started);
|
||||
|
||||
OptionCheckbox("Broadband Adapter Emulation", config::EmulateBBA,
|
||||
"Emulate the Ethernet Broadband Adapter (BBA) instead of the Modem");
|
||||
}
|
||||
}
|
||||
#ifdef NAOMI_MULTIBOARD
|
||||
ImGui::Text("Multiboard Screens:");
|
||||
ImGui::Spacing();
|
||||
header("Multiboard Screens");
|
||||
{
|
||||
//OptionRadioButton<int>("Disabled", config::MultiboardSlaves, 0, "Multiboard disabled (when optional)");
|
||||
OptionRadioButton<int>("1 (Twin)", config::MultiboardSlaves, 1, "One screen configuration (F355 Twin)");
|
||||
ImGui::SameLine();
|
||||
OptionRadioButton<int>("3 (Deluxe)", config::MultiboardSlaves, 2, "Three screens configuration");
|
||||
}
|
||||
#endif
|
||||
ImGui::PopStyleVar();
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
if (ImGui::BeginTabItem("Advanced"))
|
||||
{
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, normal_padding);
|
||||
header("CPU Mode");
|
||||
{
|
||||
ImGui::Columns(2, "cpu_modes", false);
|
||||
OptionRadioButton("Dynarec", config::DynarecEnabled, true,
|
||||
"Use the dynamic recompiler. Recommended in most cases");
|
||||
ImGui::NextColumn();
|
||||
OptionRadioButton("Interpreter", config::DynarecEnabled, false,
|
||||
"Use the interpreter. Very slow but may help in case of a dynarec problem");
|
||||
ImGui::Columns(1, NULL, false);
|
||||
|
||||
OptionSlider("SH4 Clock", config::Sh4Clock, 100, 300,
|
||||
"Over/Underclock the main SH4 CPU. Default is 200 MHz. Other values may crash, freeze or trigger unexpected nuclear reactions.",
|
||||
"%d MHz");
|
||||
}
|
||||
ImGui::Spacing();
|
||||
header("Other");
|
||||
|
|
|
@ -68,7 +68,8 @@ public:
|
|||
V40,
|
||||
V41,
|
||||
V42,
|
||||
Current = V42,
|
||||
V43,
|
||||
Current = V43,
|
||||
|
||||
Next = Current + 1,
|
||||
};
|
||||
|
|
|
@ -121,6 +121,7 @@ Option<bool> NetworkStats("", false);
|
|||
Option<int> GGPOAnalogAxes("", 0);
|
||||
Option<bool> NetworkOutput(CORE_OPTION_NAME "_network_output", false);
|
||||
Option<int> MultiboardSlaves("", 0);
|
||||
Option<bool> BattleCableEnable("", false);
|
||||
|
||||
// Maple
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ TEST_F(SerializeTest, SizeTest)
|
|||
std::vector<char> data(30000000);
|
||||
Serializer ser(data.data(), data.size());
|
||||
dc_serialize(ser);
|
||||
ASSERT_EQ(28191382u, ser.size());
|
||||
ASSERT_EQ(28191417u, ser.size());
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue