naomi: derbyocw card support. serial pipes refactoring. systemsp fixes

common interface for most serial pipes
jvs: MIE RS422 port support
card reader: allow reading/writing of individual tracks
systemsp:  fix interrupt handling. refactor serial ports. isshoni wanwan
puppy touchscreen support
This commit is contained in:
Flyinghead 2023-07-26 21:34:03 +02:00
parent 92a19e9250
commit 7061c043d9
17 changed files with 759 additions and 293 deletions

View File

@ -291,3 +291,25 @@ void WriteMemArr(u8 *array, u32 addr, T data)
{
*(T *)&array[addr] = data;
}
class SerialPort
{
public:
class Pipe
{
public:
// Serial TX
virtual void write(u8 data) { }
// RX buffer Size
virtual int available() { return 0; }
// Serial RX
virtual u8 read() { return 0; }
virtual ~Pipe() = default;
};
virtual void setPipe(Pipe *pipe) = 0;
virtual void updateStatus() = 0;
virtual ~SerialPort() = default;
};

View File

@ -2,6 +2,7 @@
#include "maple_helper.h"
#include "maple_if.h"
#include "hw/naomi/naomi_cart.h"
#include "hw/naomi/card_reader.h"
#include "cfg/option.h"
#include "stdclass.h"
#include "serialize.h"
@ -238,6 +239,10 @@ static void createNaomiDevices()
mcfg_Create(MDT_SegaController, 2, 5);
mcfg_Create(MDT_SegaVMU, 2, 0);
}
if (settings.content.gameId == " DERBY OWNERS CLUB WE ---------"
|| settings.content.gameId == " DERBY OWNERS CLUB ------------"
|| settings.content.gameId == " DERBY OWNERS CLUB II-----------")
card_reader::derbyInit();
}
static void createAtomiswaveDevices()
@ -370,15 +375,17 @@ void mcfg_CreateDevices()
vmuDigest();
}
void mcfg_DestroyDevices()
// Don't destroy the JVS MIE if full is false
void mcfg_DestroyDevices(bool full)
{
for (int i = 0; i < MAPLE_PORTS; i++)
for (int j=0;j<=5;j++)
for (int j = 0; j <= 5; j++)
{
if (MapleDevices[i][j] != NULL)
if (MapleDevices[i][j] != nullptr
&& (full || MapleDevices[i][j]->get_device_type() != MDT_NaomiJamma))
{
delete MapleDevices[i][j];
MapleDevices[i][j] = NULL;
MapleDevices[i][j] = nullptr;
}
}
}
@ -409,7 +416,7 @@ void mcfg_SerializeDevices(Serializer& ser)
void mcfg_DeserializeDevices(Deserializer& deser)
{
if (!deser.rollback())
mcfg_DestroyDevices();
mcfg_DestroyDevices(false);
u8 eeprom[sizeof(maple_naomi_jamma::eeprom)];
if (deser.version() < Deserializer::V23)
{
@ -441,7 +448,7 @@ void mcfg_DeserializeDevices(Deserializer& deser)
deser >> deviceType;
if (deviceType != MDT_None)
{
if (!deser.rollback())
if (!deser.rollback() && deviceType != MDT_NaomiJamma)
mcfg_Create((MapleDeviceType)deviceType, i, j);
MapleDevices[i][j]->deserialize(deser);
}
@ -449,3 +456,10 @@ void mcfg_DeserializeDevices(Deserializer& deser)
if (deser.version() < Deserializer::V23 && EEPROM != nullptr)
memcpy(EEPROM, eeprom, sizeof(eeprom));
}
maple_naomi_jamma *getMieDevice()
{
if (MapleDevices[0][5] == nullptr || MapleDevices[0][5]->get_device_type() != MDT_NaomiJamma)
return nullptr;
return (maple_naomi_jamma *)MapleDevices[0][5];
}

View File

@ -111,7 +111,7 @@ struct MapleInputState
extern MapleInputState mapleInputState[4];
void mcfg_CreateDevices();
void mcfg_DestroyDevices();
void mcfg_DestroyDevices(bool full = true);
void mcfg_SerializeDevices(Serializer& ser);
void mcfg_DeserializeDevices(Deserializer& deser);
@ -120,3 +120,6 @@ void push_vmu_screen(int bus_id, int bus_port, u8* buffer);
void insertRfidCard(int playerNum);
const u8 *getRfidCardData(int playerNum);
void setRfidCardData(int playerNum, u8 *data);
struct maple_naomi_jamma;
maple_naomi_jamma *getMieDevice();

View File

@ -5,6 +5,7 @@
#include <cmath>
#include "input/gamepad.h"
#include "serialize.h"
#include "hw/hwreg.h"
#include <memory>
#include <vector>
@ -257,7 +258,7 @@ struct maple_base: maple_device
class jvs_io_board;
struct maple_naomi_jamma : maple_base
struct maple_naomi_jamma : maple_base, SerialPort
{
static constexpr u8 ALL_NODES = 0xff;
@ -295,4 +296,11 @@ struct maple_naomi_jamma : maple_base
void serialize(Serializer& ser) const override;
void deserialize(Deserializer& deser) override;
void setPipe(Pipe *pipe) override {
serialPipe = pipe;
}
void updateStatus() override {}
Pipe *serialPipe = nullptr;
};

View File

@ -1464,6 +1464,47 @@ void maple_naomi_jamma::handle_86_subcommand()
w8(0x0);
break;
// RS422 port
case 0x41: // reset?
DEBUG_LOG(MAPLE, "JVS: RS422 reset");
if (serialPipe != nullptr)
while (serialPipe->available())
serialPipe->read();
break;
case 0x47: // send data
DEBUG_LOG(MAPLE, "JVS: RS422 send %02x", dma_buffer_in[4]);
if (serialPipe != nullptr)
serialPipe->write(dma_buffer_in[4]);
break;
case 0x4d: // receive data
{
int avail = 0;
if (serialPipe != nullptr)
avail = std::min(serialPipe->available(), 0xfe);
DEBUG_LOG(MAPLE, "JVS: RS422 receive %d bytes", avail);
w8(MDRS_JVSReply);
w8(0);
w8(0x20);
w8(1 + (avail + 3) / 4);
w8(0);
w8(0);
w8(0);
w8(avail == 0 ? 0xff : avail); // 0xff => no data, else byte count
for (int i = 0; i < ((avail + 3) / 4) * 4; i++)
w8(i >= avail ? 0 : serialPipe->read());
break;
}
case 0x49: // I?
case 0x4b: // K?
case 0x4f: // O?
//DEBUG_LOG(MAPLE, "JVS: 0x86,%02x RS422 len %d", subcode, dma_count_in - 3);
break;
default:
INFO_LOG(MAPLE, "JVS: Unknown 0x86 sub-command %x", subcode);
w8(MDRE_UnknownCmd);

View File

@ -20,57 +20,16 @@
#include "oslib/oslib.h"
#include "hw/sh4/modules/modules.h"
#include "hw/maple/maple_cfg.h"
#include "hw/maple/maple_devs.h"
#include <deque>
#include <memory>
#include <errno.h>
namespace card_reader {
class CardReader
{
protected:
u8 calcCrc(u8 *data, u32 len)
{
u32 crc = 0;
for (u32 i = 0; i < len; i++)
crc ^= data[i];
return crc;
}
bool loadCard(u8 *cardData, u32 len)
{
std::string path = hostfs::getArcadeFlashPath() + ".card";
FILE *fp = nowide::fopen(path.c_str(), "rb");
if (fp == nullptr)
return false;
DEBUG_LOG(NAOMI, "Loading card file from %s", path.c_str());
if (fread(cardData, 1, len, fp) != len)
WARN_LOG(NAOMI, "Truncated or empty card file: %s" ,path.c_str());
fclose(fp);
return true;
}
void saveCard(const u8 *cardData, u32 len)
{
std::string path = hostfs::getArcadeFlashPath() + ".card";
FILE *fp = nowide::fopen(path.c_str(), "wb");
if (fp == nullptr)
{
WARN_LOG(NAOMI, "Can't create card file %s: errno %d", path.c_str(), errno);
return;
}
DEBUG_LOG(NAOMI, "Saving card file to %s", path.c_str());
if (fwrite(cardData, 1, len, fp) != len)
WARN_LOG(NAOMI, "Truncated write to file: %s", path.c_str());
fclose(fp);
}
};
/*
Initial D card reader protocol (from my good friend Metallic)
Sanwa CRP-1231BR-10 card reader/writer protocol (from my good friend Metallic)
used in InitialD and Derby Owners Club
>>> SEND PKT: [START 02][LEN][CMD][0][0][0]{optional data}[STOP 03][CRC]
<<< RECV ACK: [OK 06]
@ -99,18 +58,15 @@ protected:
Protocol dumps and more:
https://www.arcade-projects.com/threads/naomi-2-chihiro-triforce-card-reader-emulator-initial-d3-wmmt-mario-kart-f-zero-ax.814/
*/
class InitialDCardReader final : public CardReader, SerialPipe
Bits from YACardEmu: https://github.com/GXTX/YACardEmu/
Copyright (C) 2020-2023 wutno (https://github.com/GXTX)
Copyright (C) 2022-2023 tugpoat (https://github.com/tugpoat)
*/
class CardReader : public SerialPort::Pipe
{
public:
InitialDCardReader() {
serial_setPipe(this);
}
~InitialDCardReader() {
serial_setPipe(nullptr);
}
void write(u8 b) override
{
if (inBufferIdx == 0 && b == 5)
@ -204,7 +160,6 @@ private:
return;
outBuffer[outBufferLen++] = 2;
u32 crcIdx = outBufferLen;
u8 len = 6;
u8 status1 = getStatus1();
u8 status2 = '0';
u8 status3 = '0';
@ -222,14 +177,47 @@ private:
break;
case CARD_WRITE:
memcpy(cardData, &rxCommand[7], sizeof(cardData));
// 4: mode ('0': read 0x45 bytes, '1': variable length write 1-47 bytes)
// 5: parity ('0': 7-bit parity, '1': 8-bit no parity)
// 6: track (see below)
INFO_LOG(NAOMI, "Card write mode %c parity %c track %c", rxCommand[4], rxCommand[5], rxCommand[6]);
switch (rxCommand[6])
{
case '0': // track 1
memcpy(cardData, &rxCommand[7], TRACK_SIZE);
break;
case '1': // track 2
memcpy(cardData + TRACK_SIZE, &rxCommand[7], TRACK_SIZE);
break;
case '2': // track 3
memcpy(cardData + TRACK_SIZE * 2, &rxCommand[7], TRACK_SIZE);
break;
case '3': // track 1 & 2
memcpy(cardData, &rxCommand[7], TRACK_SIZE * 2);
break;
case '4': // track 1 & 3
memcpy(cardData, &rxCommand[7], TRACK_SIZE);
memcpy(cardData + TRACK_SIZE * 2, &rxCommand[7 + TRACK_SIZE], TRACK_SIZE);
break;
case '5': // track 2 & 3
memcpy(cardData + TRACK_SIZE, &rxCommand[7], TRACK_SIZE * 2);
break;
case '6': // track 1 2 & 3
memcpy(cardData, &rxCommand[7], TRACK_SIZE * 3);
break;
default:
WARN_LOG(NAOMI, "Unknown track# %02x", rxCommand[6]);
break;
}
saveCard(cardData, sizeof(cardData));
break;
case CARD_READ:
if (cardInserted && !doorOpen)
len = 6 + sizeof(cardData);
else
// 4: mode ('0': read 0x45 bytes, '1': variable length read 1-47 bytes, '2': card capture, pull in card?)
// 5: parity ('0': 7-bit parity, '1': 8-bit no parity)
// 6: track (see below)
INFO_LOG(NAOMI, "Card read mode %c parity %c track %c", rxCommand[4], rxCommand[5], rxCommand[6]);
if (!cardInserted || doorOpen)
status3 = cardInserted ? '0' : '4';
break;
@ -251,21 +239,93 @@ private:
WARN_LOG(NAOMI, "Unknown command %x", rxCommand[0]);
break;
}
outBuffer[outBufferLen++] = len;
outBuffer[outBufferLen++] = 6;
outBuffer[outBufferLen++] = rxCommand[0];
outBuffer[outBufferLen++] = status1;
outBuffer[outBufferLen++] = status2;
outBuffer[outBufferLen++] = status3;
if (rxCommand[0] == CARD_READ && cardInserted && !doorOpen)
if (rxCommand[0] == CARD_READ && cardInserted && !doorOpen && rxCommand[4] != '2')
{
memcpy(&outBuffer[outBufferLen], cardData, sizeof(cardData));
outBufferLen += sizeof(cardData);
u32 idx = 0;
u32 size = TRACK_SIZE;
switch (rxCommand[6])
{
case '0': // track 1
break;
case '1': // track 2
idx = TRACK_SIZE;
break;
case '2': // track 3
idx = TRACK_SIZE * 2;
break;
case '3': // track 1 & 2
size = TRACK_SIZE * 2;
break;
case '4': // track 1 & 3
memcpy(&outBuffer[outBufferLen], cardData, TRACK_SIZE);
outBufferLen += TRACK_SIZE;
outBuffer[crcIdx] += size;
idx = TRACK_SIZE * 2;
break;
case '5': // track 2 & 3
idx = TRACK_SIZE;
size = TRACK_SIZE * 2;
break;
case '6': // track 1 2 & 3
size = TRACK_SIZE * 3;
break;
default:
WARN_LOG(NAOMI, "Unknown track# %02x", rxCommand[6]);
size = 0;
break;
}
memcpy(&outBuffer[outBufferLen], cardData + idx, size);
outBufferLen += size;
outBuffer[crcIdx] += size;
}
outBuffer[outBufferLen++] = 3;
outBuffer[outBufferLen] = calcCrc(&outBuffer[crcIdx], outBufferLen - crcIdx);
outBufferLen++;
}
u8 calcCrc(u8 *data, u32 len)
{
u32 crc = 0;
for (u32 i = 0; i < len; i++)
crc ^= data[i];
return crc;
}
bool loadCard(u8 *cardData, u32 len)
{
std::string path = hostfs::getArcadeFlashPath() + ".card";
FILE *fp = nowide::fopen(path.c_str(), "rb");
if (fp == nullptr)
return false;
DEBUG_LOG(NAOMI, "Loading card file from %s", path.c_str());
if (fread(cardData, 1, len, fp) != len)
WARN_LOG(NAOMI, "Truncated or empty card file: %s" ,path.c_str());
fclose(fp);
return true;
}
void saveCard(const u8 *cardData, u32 len)
{
std::string path = hostfs::getArcadeFlashPath() + ".card";
FILE *fp = nowide::fopen(path.c_str(), "wb");
if (fp == nullptr)
{
WARN_LOG(NAOMI, "Can't create card file %s: errno %d", path.c_str(), errno);
return;
}
DEBUG_LOG(NAOMI, "Saving card file to %s", path.c_str());
if (fwrite(cardData, 1, len, fp) != len)
WARN_LOG(NAOMI, "Truncated write to file: %s", path.c_str());
fclose(fp);
}
u8 inBuffer[256];
u32 inBufferIdx = 0;
@ -276,30 +336,63 @@ private:
u32 outBufferIdx = 0;
u32 outBufferLen = 0;
u8 cardData[207];
static constexpr u32 TRACK_SIZE = 0x45;
u8 cardData[TRACK_SIZE * 3];
bool doorOpen = false;
bool cardInserted = false;
};
static std::unique_ptr<InitialDCardReader> initdReader;
// Hooked to the SH4 SCIF serial port
class InitialDCardReader final : public CardReader
{
public:
InitialDCardReader() {
SCIFSerialPort::Instance().setPipe(this);
}
~InitialDCardReader() {
SCIFSerialPort::Instance().setPipe(nullptr);
}
};
// Hooked to the MIE via a 838-13661 RS232/RS422 converter board
class DerbyCardReader final : public CardReader
{
public:
DerbyCardReader() {
getMieDevice()->setPipe(this);
}
~DerbyCardReader() {
getMieDevice()->setPipe(nullptr);
}
};
static std::unique_ptr<CardReader> cardReader;
void initdInit() {
initdReader = std::make_unique<InitialDCardReader>();
term();
cardReader = std::make_unique<InitialDCardReader>();
}
void initdTerm() {
initdReader.reset();
void derbyInit() {
term();
cardReader = std::make_unique<DerbyCardReader>();
}
class BarcodeReader final : public SerialPipe
void term() {
cardReader.reset();
}
class BarcodeReader final : public SerialPort::Pipe
{
public:
BarcodeReader() {
serial_setPipe(this);
SCIFSerialPort::Instance().setPipe(this);
}
~BarcodeReader() {
serial_setPipe(nullptr);
SCIFSerialPort::Instance().setPipe(nullptr);
}
int available() override {
@ -320,7 +413,7 @@ public:
INFO_LOG(NAOMI, "Card read: %s", card.c_str());
std::string data = card + "*";
toSend.insert(toSend.end(), (const u8 *)&data[0], (const u8 *)(&data.back() + 1));
serial_updateStatusRegister();
SCIFSerialPort::Instance().updateStatus();
}
std::string getCard() const {
@ -366,8 +459,8 @@ void barcodeSetCard(const std::string& card)
void insertCard(int playerNum)
{
if (initdReader != nullptr)
initdReader->insertCard();
if (cardReader != nullptr)
cardReader->insertCard();
else if (barcodeReader != nullptr)
barcodeReader->insertCard();
else

View File

@ -22,7 +22,8 @@
namespace card_reader {
void initdInit();
void initdTerm();
void derbyInit();
void term();
void barcodeInit();
void barcodeTerm();

View File

@ -35,7 +35,7 @@
namespace hopper
{
class BaseHopper : public SerialPipe
class BaseHopper : public SerialPort::Pipe
{
public:
BaseHopper()
@ -183,7 +183,7 @@ protected:
chksum += *payload;
}
toSend.push_back(chksum);
serial_updateStatusRegister();
SCIFSerialPort::Instance().updateStatus();
}
void bet(const u32 *values)
@ -1281,7 +1281,7 @@ void init()
hopper = new Sega837_14438Hopper();
else
hopper = new NaomiHopper();
serial_setPipe(hopper);
SCIFSerialPort::Instance().setPipe(hopper);
config::ForceFreePlay.override(false);
}

View File

@ -716,7 +716,7 @@ void initMidiForceFeedback()
aica::setMidiReceiver(forceFeedbackMidiReceiver);
}
struct DriveSimPipe : public SerialPipe
struct DriveSimPipe : public SerialPort::Pipe
{
void write(u8 data) override
{
@ -773,7 +773,7 @@ void initDriveSimSerialPipe()
static DriveSimPipe pipe;
pipe.reset();
serial_setPipe(&pipe);
SCIFSerialPort::Instance().setPipe(&pipe);
}
G2PrinterConnection g2PrinterConnection;

View File

@ -652,7 +652,8 @@ void naomi_cart_LoadRom(const std::string& path, const std::string& fileName, Lo
}
else if (gameId.substr(0, 8) == "MKG TKOB"
|| gameId.substr(0, 9) == "MUSHIKING"
|| gameId == "DINOSAUR KING")
|| gameId == "DINOSAUR KING"
|| gameId == "INW PUPPY 2008 VER1.001") // SystemSP isshoni
{
card_reader::barcodeInit();
}
@ -730,7 +731,7 @@ void naomi_cart_Close()
{
touchscreen::term();
printer::term();
card_reader::initdTerm();
card_reader::term();
card_reader::barcodeTerm();
serialModemTerm();
hopper::term();

View File

@ -33,10 +33,10 @@
#include "oslib/oslib.h"
#include "cfg/option.h"
#include "card_reader.h"
#include "naomi_roms.h"
#include "stdclass.h"
#include <errno.h>
#include "hw/sh4/sh4_if.h"
#ifdef DEBUG_EEPROM
#define EEPROM_LOG(...) DEBUG_LOG(FLASHROM, __VA_ARGS__)
#else
@ -162,67 +162,20 @@ void SerialEeprom93Cxx::writeCLK(int state)
clk = state;
}
u8 SerialPort::readReg(u32 addr)
//
// RS232C I/F board (838-14244) connected to RFID Chip R/W board (838-14243)
//
class RfidReaderWriter : public SerialPort::Pipe
{
switch ((addr & 0x3f) / 4)
public:
RfidReaderWriter(SerialPort *port, int index) : port(port), index(index)
{
case 0: // data in
{
u8 data = 0;
if (!toSend.empty())
{
data = toSend.front();
toSend.pop_front();
}
if (toSend.empty())
asic_CancelInterrupt(holly_EXP_PCI);
SERIAL_LOG("UART%d read data in %x", index, data);
return data;
}
case 1: // out buffer len
SERIAL_LOG("UART%d read out buf len", index);
return 0;
case 2: // in buffer len
SERIAL_LOG("UART%d read in buf len %d", index, (int)toSend.size());
return toSend.size();
case 3: // errors?
SERIAL_LOG("UART%d read errors", index);
return 0;
case 4: // unknown
SERIAL_LOG("UART%d read reg4", index);
return 0;
case 5: // flow control
SERIAL_LOG("UART%d read flow control", index);
return 0;
case 6: // status. bit 3: receive buffer not empty
SERIAL_LOG("UART%d read status %x pr %x", index, (u8)(!toSend.empty()) << 3, p_sh4rcb->cntx.pr);
return (u8)(!toSend.empty()) << 3;
case 7: // interrupt status/mask?
SERIAL_LOG("UART%d read interrupt mask/status?", index);
return 0;
case 8: // unknown
SERIAL_LOG("UART%d read reg8", index);
return 0;
case 9: // control?
SERIAL_LOG("UART%d read control?", index);
return 0;
case 10: // baudrate (lsb)
SERIAL_LOG("UART%d read baudrate(lsb)", index);
return 0;
case 11: // baudrate (msb)
SERIAL_LOG("UART%d read baudrate(msb)", index);
return 0;
default:
INFO_LOG(NAOMI, "Unknown UART%d port %x\n", index, addr);
return 0;
port->setPipe(this);
// TODO load card
}
}
void SerialPort::writeReg(u32 addr, u8 v)
{
switch ((addr & 0x3f) / 4)
void write(u8 v) override
{
case 0: // data out
if (expectedBytes > 0)
{
SERIAL_LOG("UART%d write data out: %02x", index, v);
@ -248,7 +201,7 @@ void SerialPort::writeReg(u32 addr, u8 v)
toSend.push_back(1);
SERIAL_LOG("UART%d COUNT %x", index, recvBuffer[1]);
}
asic_RaiseInterrupt(holly_EXP_PCI);
port->updateStatus();
expectedBytes = 0;
}
return;
@ -329,10 +282,312 @@ void SerialPort::writeReg(u32 addr, u8 v)
toSend.push_back(0x3a); // ng
break;
}
if (toSend.empty())
asic_CancelInterrupt(holly_EXP_PCI);
}
int available() override {
return toSend.size();
}
u8 read() override
{
u8 b = 0;
if (!toSend.empty())
{
b = toSend.front();
toSend.pop_front();
}
return b;
}
void serialize(Serializer& ser) const override
{
ser << (u32)toSend.size();
for (u8 b : toSend)
ser << b;
ser << expectedBytes;
ser << (u32)recvBuffer.size();
ser.serialize(recvBuffer.data(), recvBuffer.size());
}
void deserialize(Deserializer& deser) override
{
u32 size;
deser >> size;
toSend.resize(size);
for (u32 i = 0; i < size; i++)
deser >> toSend[i];
deser >> expectedBytes;
deser >> size;
recvBuffer.resize(size);
deser.deserialize(recvBuffer.data(), recvBuffer.size());
}
private:
SerialPort *port;
const int index;
std::deque<u8> toSend;
std::array<u8, 128> cardData;
u8 expectedBytes = 0;
std::vector<u8> recvBuffer;
};
//
// Isshoni Wanwan Waiwai Puppy touchscreen
//
class Touchscreen : public SerialPort::Pipe
{
public:
Touchscreen(SerialPort *port) : port(port)
{
port->setPipe(this);
schedId = sh4_sched_register(0, schedCallback, this);
}
~Touchscreen()
{
sh4_sched_unregister(schedId);
}
void write(u8 v) override
{
if (v == '\r')
{
if (recvBuffer.size() >= 2 && recvBuffer[0] == 1)
{
toSend.push_back(1);
if (recvBuffer.size() == 3 && recvBuffer[1] == 'O' && recvBuffer[2] == 'I')
{
SERIAL_LOG("Received cmd OI: get name");
toSend.push_back('A');
toSend.push_back('3');
toSend.push_back('0');
toSend.push_back('9');
toSend.push_back('9');
toSend.push_back('9');
}
else if (recvBuffer.size() == 3 && recvBuffer[1] == 'N' && recvBuffer[2] == 'M')
{
SERIAL_LOG("Received cmd NM: unit verify");
const std::array<u8, 19> id { 'E','X','I','I','-','7','7','2','0','S','C',' ','R','e','v',' ','3','.','0' };
toSend.insert(toSend.end(), id.begin(), id.end());
}
else if (recvBuffer.size() == 3 && recvBuffer[1] == 'U' && recvBuffer[2] == 'V')
{
SERIAL_LOG("Received cmd UV: reset");
const std::array<u8, 8> resp { 'Q','M','V','*','*','*','0','0' };
toSend.insert(toSend.end(), resp.begin(), resp.end());
}
else if (recvBuffer.size() == 2 && recvBuffer[1] == 'R')
{
SERIAL_LOG("Received cmd R");
toSend.push_back('0');
sh4_sched_request(schedId, SCHED_CYCLES);
}
else
{
SERIAL_LOG("Received cmd %c", recvBuffer[1]);
toSend.push_back('0');
}
toSend.push_back('\r');
port->updateStatus();
// FIXME
if (recvBuffer.size() == 2 && recvBuffer[1] == 'Z')
sendPosition(0);
}
else
{
WARN_LOG(NAOMI, "\\r ignored. buf size %d", (int)recvBuffer.size());
}
recvBuffer.clear();
}
else
asic_RaiseInterrupt(holly_EXP_PCI);
{
if (recvBuffer.size() == 9)
{
if (!memcmp(&recvBuffer[0], "Ua0000000", 9))
{
SERIAL_LOG("UART receive Ua...%c", v);
sendPosition(1);
}
else
WARN_LOG(NAOMI, "Unknown command %.9s", &recvBuffer[0]);
recvBuffer.clear();
}
else
{
recvBuffer.push_back(v);
}
}
}
int available() override {
return toSend.size();
}
u8 read() override
{
u8 data = 0;
if (!toSend.empty())
{
data = toSend.front();
toSend.pop_front();
}
if (toSend.empty())
port->updateStatus();
SERIAL_LOG("UART read data %x", data);
return data;
}
void serialize(Serializer& ser) const override
{
ser << (u32)toSend.size();
for (u8 b : toSend)
ser << b;
ser << (u32)recvBuffer.size();
ser.serialize(recvBuffer.data(), recvBuffer.size());
}
void deserialize(Deserializer& deser) override
{
u32 size;
deser >> size;
toSend.resize(size);
for (u32 i = 0; i < size; i++)
deser >> toSend[i];
deser >> size;
recvBuffer.resize(size);
deser.deserialize(recvBuffer.data(), recvBuffer.size());
}
private:
void sendPosition(int type)
{
MapleInputState input[4];
ggpo::getInput(input);
// 0-1023 ?
const u32 x = (640 - input[0].absPos.x) * 1023 / 639;
const u32 y = input[0].absPos.y * 1023 / 479;
size_t start = toSend.size();
if (type == 1)
{
toSend.push_back('U');
toSend.push_back('T');
toSend.push_back(0x20); // bit 0 and 1 are checked
toSend.push_back(x & 0xff);
toSend.push_back((x >> 8) & 0x1f);
toSend.push_back(y & 0xff);
toSend.push_back((y >> 8) & 0x1f);
toSend.push_back(0); // z pos
u8 crc = 0xaa;
for (; start < toSend.size(); start++)
crc += toSend[start];
toSend.push_back(crc);
port->updateStatus();
}
else
{
bool button = (input[0].kcode & DC_BTN_B) == 0; // FIXME use button A instead
if (button != lastButton || x != lastPosX || y != lastPosY)
{
// bit 6 is touch down
if (button)
toSend.push_back(0xc0);
else
toSend.push_back(0x80);
toSend.push_back((x & 7) << 4);
toSend.push_back((x >> 3) & 0x7f);
toSend.push_back((y & 7) << 4);
toSend.push_back((y >> 3) & 0x7f);
lastButton = button;
lastPosX = x;
lastPosY = y;
port->updateStatus();
}
}
}
static int schedCallback(int tag, int cycles, int jitter, void *p)
{
((Touchscreen *)p)->sendPosition(0);
return SCHED_CYCLES;
}
SerialPort *port;
std::deque<u8> toSend;
std::vector<u8> recvBuffer;
int schedId = 0;
u32 lastPosX = ~0;
u32 lastPosY = ~0;
bool lastButton = false;
static constexpr u32 SCHED_CYCLES = SH4_MAIN_CLOCK / 60;
};
u8 SerialPort::readReg(u32 addr)
{
switch ((addr & 0x3f) / 4)
{
case 0: // data in
if (pipe != nullptr)
return pipe->read();
else
return 0;
case 1: // out buffer len
//SERIAL_LOG("UART%d read out buf len", index);
return 0;
case 2: // in buffer len
//SERIAL_LOG("UART%d read in buf len %d", index, (int)toSend.size());
if (pipe != nullptr)
return pipe->available();
else
return 0;
case 3: // errors?
SERIAL_LOG("UART%d read errors", index);
return 0;
case 4: // unknown
SERIAL_LOG("UART%d read reg4", index);
return 0;
case 5: // flow control
SERIAL_LOG("UART%d read flow control", index);
return 0;
case 6: // status. bit 3: receive buffer not empty
SERIAL_LOG("UART%d read status", index);
if (pipe != nullptr && pipe->available() > 0)
return 8;
else
return 0;
case 7: // interrupt status/mask?
SERIAL_LOG("UART%d read interrupt mask/status?", index);
return 0;
case 8: // unknown
SERIAL_LOG("UART%d read reg8", index);
return 0;
case 9: // control?
SERIAL_LOG("UART%d read control?", index);
return 0;
case 10: // baudrate (lsb)
SERIAL_LOG("UART%d read baudrate(lsb)", index);
return 0;
case 11: // baudrate (msb)
SERIAL_LOG("UART%d read baudrate(msb)", index);
return 0;
default:
INFO_LOG(NAOMI, "Unknown UART%d port %x\n", index, addr);
return 0;
}
}
void SerialPort::writeReg(u32 addr, u8 v)
{
switch ((addr & 0x3f) / 4)
{
case 0: // data out
if (pipe != nullptr)
pipe->write(v);
else
INFO_LOG(NAOMI, "UART%d out: %02x %c", index, v, v);
break;
case 1: // out buffer len
@ -373,10 +628,12 @@ void SerialPort::writeReg(u32 addr, u8 v)
case 10: // baudrate (lsb)
SERIAL_LOG("UART%d write baudrate(lsb): %x", index, v);
flush();
break;
case 11: // baudrate (msb)
SERIAL_LOG("UART%d write baudrate(msb): %x", index, v);
flush();
break;
default:
@ -385,6 +642,11 @@ void SerialPort::writeReg(u32 addr, u8 v)
}
}
void SerialPort::updateStatus()
{
cart->updateInterrupt(index == 1 ? SystemSpCart::INT_UART1 : SystemSpCart::INT_UART2);
}
template<typename T>
T readMemArea0(u32 addr)
{
@ -455,9 +717,7 @@ T SystemSpCart::readMemArea0(u32 addr)
if (ata.cylinder == 0)
ata.driveHead.head++;
readSectors();
ata.interruptPending |= 0x10;
if (ata.devCtrl.nien == 0)
asic_RaiseInterrupt(holly_EXP_PCI);
updateInterrupt(INT_ATA);
}
else
{
@ -544,11 +804,17 @@ T SystemSpCart::readMemArea0(u32 addr)
case 0x30:
return 0;
case 0x80:
// interrupt status
asic_CancelInterrupt(holly_EXP_PCI);
// bit3 is for DIMM
// bit4 is for ATA controller
return uart1.hasData() | (uart2.hasData() << 1) | ata.interruptPending;
{
// interrupt status
const u8 intPending = ata.interruptPending;
ata.interruptPending = 0;
updateInterrupt();
// b0: UART1
// b1: UART2
// b3: DIMM
// b4: ATA controller
return intPending;
}
case 0x84:
// interrupt mask?
// 10084: (dinoking) bit0,1 reset, sometimes set
@ -961,7 +1227,17 @@ void SystemSpCart::writeMemArea0(u32 addr, T v)
INFO_LOG(NAOMI, "systemsp::writeMemArea0<%d>: Unknown addr %x = %x", (int)sizeof(T), addr, (int)v);
}
SystemSpCart::SystemSpCart(u32 size) : M4Cartridge(size), eeprom(128), uart1(1), uart2(2)
void SystemSpCart::updateInterrupt(u32 mask)
{
ata.interruptPending |= mask;
if ((ata.interruptPending & (INT_UART1 | INT_UART2 | INT_DIMM))
|| ((ata.interruptPending & INT_ATA) && ata.devCtrl.nien == 0))
asic_RaiseInterrupt(holly_EXP_PCI);
else
asic_CancelInterrupt(holly_EXP_PCI);
}
SystemSpCart::SystemSpCart(u32 size) : M4Cartridge(size), eeprom(128), uart1(this, 1), uart2(this, 2)
{
schedId = sh4_sched_register(0, schedCallback, this);
Instance = this;
@ -1068,6 +1344,16 @@ void SystemSpCart::Init(LoadProgress *progress, std::vector<u8> *digest)
ata.status.rdy = 0;
ata.status.df = 1;
}
if ((!strncmp(game->name, "dinoki", 6) && strcmp(game->name, "dinoki4") != 0))
{
new RfidReaderWriter(&uart1, 1);
new RfidReaderWriter(&uart2, 2);
}
else if (!strcmp(game->name, "isshoni"))
{
new Touchscreen(&uart1);
}
EventManager::listen(Event::Pause, handleEvent, this);
}
@ -1083,6 +1369,8 @@ void SystemSpCart::WriteMem(u32 address, u32 data, u32 size)
bool SystemSpCart::Read(u32 offset, u32 size, void *dst)
{
// TODO sram? if ((offset & 0x3f000000) == 0x39000000)
if ((offset & 0x3f000000) == 0x3f000000)
{
// network card present
@ -1208,9 +1496,7 @@ int SystemSpCart::schedCallback()
{
ata.status.rdy = 1;
ata.status.bsy = 0;
ata.interruptPending |= 0x10;
if (ata.devCtrl.nien == 0)
asic_RaiseInterrupt(holly_EXP_PCI);
updateInterrupt(INT_ATA);
return 0;
}

View File

@ -19,6 +19,7 @@
#pragma once
#include "types.h"
#include "emulator.h"
#include "hw/hwreg.h"
#include "hw/naomi/m4cartridge.h"
#include "hw/flashrom/flashrom.h"
#include "serialize.h"
@ -35,6 +36,9 @@ T readMemArea0(u32 addr);
template<typename T>
void writeMemArea0(u32 addr, T v);
//
// rom board eeprom
//
class SerialEeprom93Cxx : public WritableChip
{
public:
@ -102,8 +106,6 @@ public:
deser >> dataOutBits;
}
void save(const std::string& path);
private:
u8 getCommandAddress() const
{
@ -137,51 +139,63 @@ private:
u8 dataOutBits = 0;
};
class SerialPort
class SystemSpCart;
// rom board custom UART ports
class SerialPort : public ::SerialPort
{
public:
SerialPort(int index) : index(index)
class Pipe : public ::SerialPort::Pipe
{
public:
virtual void serialize(Serializer& ser) const {}
virtual void deserialize(Deserializer& deser) {}
};
SerialPort(SystemSpCart *cart, int index) : cart(cart), index(index) {
}
~SerialPort() {
if (pipe != nullptr)
delete pipe;
}
u8 readReg(u32 addr);
void writeReg(u32 addr, u8 v);
bool hasData() const {
return !toSend.empty();
void serialize(Serializer& ser) const {
if (pipe != nullptr)
pipe->serialize(ser);
}
void serialize(Serializer& ser) const
{
ser << (u32)toSend.size();
for (u8 b : toSend)
ser << b;
ser << expectedBytes;
ser << (u32)recvBuffer.size();
ser.serialize(recvBuffer.data(), recvBuffer.size());
void deserialize(Deserializer& deser) {
if (pipe != nullptr)
pipe->deserialize(deser);
}
void deserialize(Deserializer& deser)
{
u32 size;
deser >> size;
toSend.resize(size);
for (u32 i = 0; i < size; i++)
deser >> toSend[i];
deser >> expectedBytes;
deser >> size;
recvBuffer.resize(size);
deser.deserialize(recvBuffer.data(), recvBuffer.size());
void setPipe(::SerialPort::Pipe *pipe) override {
this->pipe = (Pipe *)pipe;
}
void updateStatus() override;
private:
void flush()
{
if (pipe != nullptr)
while (pipe->available())
pipe->read();
}
SystemSpCart *cart;
const int index;
std::deque<u8> toSend;
std::array<u8, 128> cardData;
u8 expectedBytes = 0;
std::vector<u8> recvBuffer;
Pipe *pipe = nullptr;
};
//
// ATA registers for CompactFlash interface
//
union AtaStatusRegister
{
u8 full;
@ -256,6 +270,8 @@ public:
return false;
}
void updateInterrupt(u32 mask = 0);
private:
static int schedCallback(int tag, int sch_cycl, int jitter, void *arg);
int schedCallback();
@ -319,6 +335,11 @@ private:
} flash;
public:
static constexpr u32 INT_UART1 = 0x01;
static constexpr u32 INT_UART2 = 0x02;
static constexpr u32 INT_DIMM = 0x08;
static constexpr u32 INT_ATA = 0x10;
static SystemSpCart *Instance;
};

View File

@ -34,13 +34,13 @@ namespace touchscreen
// 837-14672 touchscreen sensor board
// used by Manic Panic Ghosts and Touch De Zunou
//
class TouchscreenPipe final : public SerialPipe
class TouchscreenPipe final : public SerialPort::Pipe
{
public:
TouchscreenPipe()
{
schedId = sh4_sched_register(0, schedCallback, this);
serial_setPipe(this);
SCIFSerialPort::Instance().setPipe(this);
}
~TouchscreenPipe()
@ -101,7 +101,7 @@ private:
return;
toSend.insert(toSend.end(), &msg[0], &msg[size]);
toSend.push_back(calcChecksum(msg, size));
serial_updateStatusRegister();
SCIFSerialPort::Instance().updateStatus();
}
u8 calcChecksum(const u8 *data, int size)

View File

@ -93,16 +93,22 @@ public:
};
extern SCIFRegisters scif;
struct SerialPipe
class SCIFSerialPort : public SerialPort
{
// Serial TX
virtual void write(u8 data) { }
// RX buffer Size
virtual int available() { return 0; }
// Serial RX
virtual u8 read() { return 0; }
public:
void setPipe(Pipe *pipe) override {
this->pipe = pipe;
}
void updateStatus() override;
virtual ~SerialPipe() = default;
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);
static SCIFSerialPort& Instance();
private:
Pipe *pipe = nullptr;
};
void serial_setPipe(SerialPipe *pipe);
void serial_updateStatusRegister();

View File

@ -20,43 +20,8 @@
SCIRegisters sci;
SCIFRegisters scif;
static SerialPipe *serialPipe;
/*
//SCIF SCSMR2 0xFFE80000 0x1FE80000 16 0x0000 0x0000 Held Held Pclk
SCSMR2_type SCIF_SCSMR2;
//SCIF SCBRR2 0xFFE80004 0x1FE80004 8 0xFF 0xFF Held Held Pclk
u8 SCIF_SCBRR2;
//SCIF SCSCR2 0xFFE80008 0x1FE80008 16 0x0000 0x0000 Held Held Pclk
SCSCR2_type SCIF_SCSCR2;
//SCIF SCFTDR2 0xFFE8000C 0x1FE8000C 8 Undefined Undefined Held Held Pclk
u8 SCIF_SCFTDR2;
//SCIF SCFSR2 0xFFE80010 0x1FE80010 16 0x0060 0x0060 Held Held Pclk
SCSCR2_type SCIF_SCFSR2;
//SCIF SCFRDR2 0xFFE80014 0x1FE80014 8 Undefined Undefined Held Held Pclk
//Read OLNY
u8 SCIF_SCFRDR2;
//SCIF SCFCR2 0xFFE80018 0x1FE80018 16 0x0000 0x0000 Held Held Pclk
SCFCR2_type SCIF_SCFCR2;
//Read OLNY
//SCIF SCFDR2 0xFFE8001C 0x1FE8001C 16 0x0000 0x0000 Held Held Pclk
SCFDR2_type SCIF_SCFDR2;
//SCIF SCSPTR2 0xFFE80020 0x1FE80020 16 0x0000 0x0000 Held Held Pclk
SCSPTR2_type SCIF_SCSPTR2;
//SCIF SCLSR2 0xFFE80024 0x1FE80024 16 0x0000 0x0000 Held Held Pclk
SCLSR2_type SCIF_SCLSR2;
*/
static void Serial_UpdateInterrupts()
static void updateInterrupts()
{
InterruptPend(sh4_SCIF_TXI, SCIF_SCFSR2.TDFE);
InterruptMask(sh4_SCIF_TXI, SCIF_SCSCR2.TIE);
@ -65,42 +30,28 @@ static void Serial_UpdateInterrupts()
InterruptMask(sh4_SCIF_RXI, SCIF_SCSCR2.RIE);
}
void serial_updateStatusRegister()
{
if (serialPipe != nullptr)
{
constexpr int trigLevels[] { 1, 4, 8, 14 };
int avail = serialPipe->available();
if (avail >= trigLevels[SCIF_SCFCR2.RTRG1 * 2 + SCIF_SCFCR2.RTRG0])
SCIF_SCFSR2.RDF = 1;
if (avail >= 1)
SCIF_SCFSR2.DR = 1;
Serial_UpdateInterrupts();
}
}
// SCIF SCFTDR2
static void SerialWrite(u32 addr, u8 data)
void SCIFSerialPort::writeData(u32 addr, u8 data)
{
//DEBUG_LOG(COMMON, "serial %02x", data);
if (serialPipe != nullptr)
serialPipe->write(data);
INFO_LOG(COMMON, "serial out %02x %c", data, data);
if (Instance().pipe != nullptr)
Instance().pipe->write(data);
SCIF_SCFSR2.TDFE = 1;
SCIF_SCFSR2.TEND = 1;
Serial_UpdateInterrupts();
updateInterrupts();
}
//SCIF_SCFSR2 read
static u16 ReadSerialStatus(u32 addr)
// SCIF_SCFSR2 read
u16 SCIFSerialPort::readStatus(u32 addr)
{
serial_updateStatusRegister();
Instance().updateStatus();
return SCIF_SCFSR2.full;
}
static void WriteSerialStatus(u32 addr, u16 data)
// SCIF_SCFSR2 write
void SCIFSerialPort::writeStatus(u32 addr, u16 data)
{
if (!SCIF_SCFSR2.BRK)
data &= ~0x10;
@ -110,29 +61,53 @@ static void WriteSerialStatus(u32 addr, u16 data)
SCIF_SCFSR2.TDFE = 1;
SCIF_SCFSR2.TEND = 1;
serial_updateStatusRegister();
Instance().updateStatus();
}
//SCIF_SCFDR2 - 16b
static u16 Read_SCFDR2(u32 addr)
u16 SCIFSerialPort::readSCFDR2(u32 addr)
{
if (serialPipe != nullptr)
return std::min(16, serialPipe->available());
if (Instance().pipe != nullptr)
return std::min(16, Instance().pipe->available());
else
return 0;
}
//SCIF_SCFRDR2
static u8 ReadSerialData(u32 addr)
u8 SCIFSerialPort::readData(u32 addr)
{
u8 data = 0;
if (serialPipe != nullptr)
data = serialPipe->read();
serial_updateStatusRegister();
if (Instance().pipe != nullptr) {
data = Instance().pipe->read();
//DEBUG_LOG(COMMON, "serial in %02x %c", data, data);
}
Instance().updateStatus();
return data;
}
void SCIFSerialPort::updateStatus()
{
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])
SCIF_SCFSR2.RDF = 1;
if (avail >= 1)
SCIF_SCFSR2.DR = 1;
updateInterrupts();
}
SCIFSerialPort& SCIFSerialPort::Instance()
{
static SCIFSerialPort instance;
return instance;
}
//SCSCR2
static u16 SCSCR2_read(u32 addr)
@ -144,10 +119,10 @@ static void SCSCR2_write(u32 addr, u16 data)
{
SCIF_SCSCR2.full = data & 0x00fa;
Serial_UpdateInterrupts();
updateInterrupts();
}
struct PTYPipe : public SerialPipe
struct PTYPipe : public SerialPort::Pipe
{
void write(u8 data) override {
if (config::SerialConsole)
@ -204,7 +179,7 @@ struct PTYPipe : public SerialPipe
}
#endif
}
serial_setPipe(this);
SCIFSerialPort::Instance().setPipe(this);
}
void term()
@ -238,21 +213,21 @@ void SCIFRegisters::init()
setHandlers<SCIF_SCSCR2_addr>(SCSCR2_read, SCSCR2_write);
//SCIF SCFTDR2 0xFFE8000C 0x1FE8000C 8 Undefined Undefined Held Held Pclk
setWriteOnly<SCIF_SCFTDR2_addr>(SerialWrite);
setWriteOnly<SCIF_SCFTDR2_addr>(SCIFSerialPort::writeData);
//SCIF SCFSR2 0xFFE80010 0x1FE80010 16 0x0060 0x0060 Held Held Pclk
setHandlers<SCIF_SCFSR2_addr>(ReadSerialStatus, WriteSerialStatus);
setHandlers<SCIF_SCFSR2_addr>(SCIFSerialPort::readStatus, SCIFSerialPort::writeStatus);
//READ only
//SCIF SCFRDR2 0xFFE80014 0x1FE80014 8 Undefined Undefined Held Held Pclk
setReadOnly<SCIF_SCFRDR2_addr>(ReadSerialData);
setReadOnly<SCIF_SCFRDR2_addr>(SCIFSerialPort::readData);
//SCIF SCFCR2 0xFFE80018 0x1FE80018 16 0x0000 0x0000 Held Held Pclk
setRW<SCIF_SCFCR2_addr, u16, 0x00ff>();
//Read only
//SCIF SCFDR2 0xFFE8001C 0x1FE8001C 16 0x0000 0x0000 Held Held Pclk
setReadOnly<SCIF_SCFDR2_addr>(Read_SCFDR2);
setReadOnly<SCIF_SCFDR2_addr>(SCIFSerialPort::readSCFDR2);
//SCIF SCSPTR2 0xFFE80020 0x1FE80020 16 0x0000 0x0000 Held Held Pclk
setRW<SCIF_SCSPTR2_addr, u16, 0x00f3>();
@ -316,8 +291,3 @@ void SCIFRegisters::term()
super::term();
ptyPipe.term();
}
void serial_setPipe(SerialPipe *pipe)
{
serialPipe = pipe;
}

View File

@ -25,17 +25,17 @@
#include <deque>
#include <memory>
struct ModemEmu : public SerialPipe
struct ModemEmu : public SerialPort::Pipe
{
ModemEmu() {
serial_setPipe(this);
SCIFSerialPort::Instance().setPipe(this);
schedId = sh4_sched_register(0, schedCallback);
}
~ModemEmu() {
sh4_sched_unregister(schedId);
stop_pico();
serial_setPipe(nullptr);
SCIFSerialPort::Instance().setPipe(nullptr);
}
u8 read() override
@ -132,12 +132,12 @@ private:
{
toSend.insert(toSend.end(), l.begin(), l.end());
toSend.push_back('\n');
serial_updateStatusRegister();
SCIFSerialPort::Instance().updateStatus();
}
static int schedCallback(int tag, int cycles, int lag, void *arg)
{
serial_updateStatusRegister();
SCIFSerialPort::Instance().updateStatus();
return SH4_MAIN_CLOCK / 60;
}

View File

@ -26,7 +26,7 @@
#include "hw/naomi/naomi_flashrom.h"
#include <deque>
struct MaxSpeedNetPipe : public SerialPipe
struct MaxSpeedNetPipe : public SerialPort::Pipe
{
class Exception : public FlycastException
{
@ -243,7 +243,7 @@ private:
else
enableNetworkBroadcast(true);
serial_setPipe(this);
SCIFSerialPort::Instance().setPipe(this);
}
sock_t sock = INVALID_SOCKET;