Add X76F100 driver, fix X76F041/DS2401, add GCA15

This commit is contained in:
spicyjpeg 2023-12-23 12:24:44 +01:00
parent ffa70d66fb
commit 36b09d5865
No known key found for this signature in database
GPG Key ID: 5CC87404C01DF393
8 changed files with 184 additions and 82 deletions

View File

@ -74,14 +74,19 @@ target_compile_definitions(
cart_tool PRIVATE
VERSION="${PROJECT_VERSION}"
$<IF:$<CONFIG:Debug>,
#ENABLE_ARGV=1
ENABLE_PS1_CONTROLLER=1
ENABLE_CART_MENU=1
#ENABLE_X76F100_DRIVER=1
ENABLE_DUMMY_DRIVER=1
ENABLE_I2C_LOGGING=1
ENABLE_PS1_CONTROLLER=1
,
#ENABLE_ARGV=1
ENABLE_CART_MENU=1
ENABLE_PS1_CONTROLLER=1
ENABLE_CART_MENU=1
#ENABLE_X76F100_DRIVER=1
#ENABLE_DUMMY_DRIVER=1
#ENABLE_I2C_LOGGING=1
>
)

View File

@ -88,7 +88,6 @@ function(ps1_target_incbin
)
string(MAKE_C_IDENTIFIER "${symbolName}" _symbolName)
string(MAKE_C_IDENTIFIER "${sizeSymbolName}" _sizeSymbolName)
string(MAKE_C_IDENTIFIER "${section}" _section)
cmake_path(ABSOLUTE_PATH path OUTPUT_VARIABLE _path)
@ -101,7 +100,7 @@ function(ps1_target_incbin
CONFIGURE
OUTPUT "${_assemblyFile}"
CONTENT [[
.section ${_section}.${_symbolName}, "aw"
.section ${section}.${_symbolName}, "aw"
.balign 8
.global ${_symbolName}
@ -113,7 +112,7 @@ ${_symbolName}:
.incbin "${_path}"
${_symbolName}_end:
.section ${_section}.${_sizeSymbolName}, "aw"
.section ${section}.${_sizeSymbolName}, "aw"
.balign 4
.global ${_sizeSymbolName}

View File

@ -933,7 +933,7 @@
"gameCart": null,
"ioBoard": "PWB0000073070",
"bootloaderVersion": null,
"lockedToIOBoard": true
"lockedToIOBoard": false
},
{
"id": "hypbbc2p",
@ -1295,6 +1295,18 @@
"bootloaderVersion": null,
"lockedToIOBoard": false
},
{
"id": null,
"code": "GCA15",
"region": "JAA",
"name": "GunMania Zone Plus",
"year": 2000,
"installCart": null,
"gameCart": "X76F041+DS2401",
"ioBoard": "PWB0000073070",
"bootloaderVersion": null,
"lockedToIOBoard": false
},
{
"id": "salarymc",
"code": "GCA18",

View File

@ -22,19 +22,20 @@ bool App::_startupWorker(void) {
// Skip the warning screen in debug builds.
_workerStatus.setNextScreen(_buttonMappingScreen);
#endif
_workerStatus.update(0, 3, WSTR("App.startupWorker.initIDE"));
ide::devices[0].enumerate();
ide::devices[1].enumerate();
for (int i = 0; i < 2; i++) {
_workerStatus.update(i, 4, WSTR("App.startupWorker.initIDE"));
ide::devices[i].enumerate();
}
_workerStatus.update(1, 3, WSTR("App.startupWorker.initFAT"));
_workerStatus.update(2, 4, WSTR("App.startupWorker.initFAT"));
// Attempt to mount the secondary drive first, then in case of failure try
// mounting the primary drive instead.
if (!_fileProvider.init("1:"))
_fileProvider.init("0:");
_workerStatus.update(2, 3, WSTR("App.startupWorker.loadResources"));
_workerStatus.update(3, 4, WSTR("App.startupWorker.loadResources"));
_resourceFile = _fileProvider.openFile(
EXTERNAL_DATA_DIR "/resource.zip", file::READ
@ -300,6 +301,10 @@ bool App::_cartReflashWorker(void) {
return false;
}
// TODO: preserve 0x81 traceid if possible
//uint8_t traceID[8];
//_parser->getIdentifiers()->traceID.copyTo(traceID);
if (!_cartEraseWorker())
return false;
if (_parser)

View File

@ -74,12 +74,6 @@ DriverError DummyDriver::writeData(void) {
}
DriverError DummyDriver::erase(void) {
char buf[128];
util::hexToString(buf,_dump.dataKey,8);
LOG("got %s",buf);
util::hexToString(buf,dummyDriverDump.dataKey,8);
LOG("exp %s",buf);
if (!__builtin_memcmp(
_dump.dataKey, dummyDriverDump.dataKey, sizeof(_dump.dataKey)
)) {
@ -171,7 +165,7 @@ DriverError X76Driver::readCartID(void) {
}
DriverError X76Driver::_x76Command(
uint8_t cmd, uint8_t param, uint8_t pollByte
uint8_t pollByte, uint8_t cmd, int param
) const {
delayMicroseconds(_X76_PACKET_DELAY);
io::i2cStartWithCS();
@ -183,11 +177,13 @@ DriverError X76Driver::_x76Command(
return X76_NACK;
}
io::i2cWriteByte(param);
if (!io::i2cGetACK()) {
io::i2cStopWithCS();
LOG("NACK while sending param=0x%02x", param);
return X76_NACK;
if (param >= 0) {
io::i2cWriteByte(param);
if (!io::i2cGetACK()) {
io::i2cStopWithCS();
LOG("NACK while sending param=0x%02x", param);
return X76_NACK;
}
}
if (!io::i2cWriteBytes(_dump.dataKey, sizeof(_dump.dataKey))) {
@ -200,7 +196,10 @@ DriverError X76Driver::_x76Command(
char buffer[48];
util::hexToString(buffer, _dump.dataKey, sizeof(_dump.dataKey), ' ');
LOG("S: %02X %02X %s", cmd, param, buffer);
if (param >= 0)
LOG("S: %02X %02X %s", cmd, param, buffer);
else
LOG("S: %02X %s", cmd, buffer);
#endif
for (int i = _X76_MAX_ACK_POLLS; i; i--) {
@ -237,7 +236,7 @@ DriverError X76F041Driver::readPrivateData(void) {
// cross 128-byte block boundaries.
for (int i = 0; i < 512; i += 128) {
auto error = _x76Command(
_X76F041_READ | (i >> 8), i & 0xff, _X76F041_ACK_POLL
_X76F041_ACK_POLL, _X76F041_READ | (i >> 8), i & 0xff
);
if (error)
return error;
@ -256,8 +255,10 @@ DriverError X76F041Driver::readPrivateData(void) {
io::i2cStopWithCS();
}
_dump.flags |= DUMP_PRIVATE_DATA_OK;
auto error = _x76Command(
_X76F041_CONFIG, _X76F041_CFG_READ_CONFIG, _X76F041_ACK_POLL
_X76F041_ACK_POLL, _X76F041_CONFIG, _X76F041_CFG_READ_CONFIG
);
if (error)
return error;
@ -272,10 +273,10 @@ DriverError X76F041Driver::readPrivateData(void) {
return X76_NACK;
}
io::i2cReadBytes(_dump.config, 5);
io::i2cReadBytes(_dump.config, 8);
io::i2cStopWithCS();
_dump.flags |= DUMP_PRIVATE_DATA_OK;
_dump.flags |= DUMP_CONFIG_OK;
return NO_ERROR;
}
@ -283,7 +284,7 @@ DriverError X76F041Driver::writeData(void) {
// Writes can only be done in 8-byte blocks.
for (int i = 0; i < 512; i += 8) {
auto error = _x76Command(
_X76F041_WRITE | (i >> 8), i & 0xff, _X76F041_ACK_POLL
_X76F041_ACK_POLL, _X76F041_WRITE | (i >> 8), i & 0xff
);
if (error)
return error;
@ -298,7 +299,7 @@ DriverError X76F041Driver::writeData(void) {
}
auto error = _x76Command(
_X76F041_CONFIG, _X76F041_CFG_WRITE_CONFIG, _X76F041_ACK_POLL
_X76F041_ACK_POLL, _X76F041_CONFIG, _X76F041_CFG_WRITE_CONFIG
);
if (error)
return error;
@ -316,7 +317,7 @@ DriverError X76F041Driver::writeData(void) {
DriverError X76F041Driver::erase(void) {
auto error = _x76Command(
_X76F041_CONFIG, _X76F041_CFG_MASS_PROGRAM, _X76F041_ACK_POLL
_X76F041_ACK_POLL, _X76F041_CONFIG, _X76F041_CFG_MASS_PROGRAM
);
if (error)
return error;
@ -329,7 +330,7 @@ DriverError X76F041Driver::erase(void) {
DriverError X76F041Driver::setDataKey(const uint8_t *key) {
auto error = _x76Command(
_X76F041_CONFIG, _X76F041_CFG_SET_DATA_KEY, _X76F041_ACK_POLL
_X76F041_ACK_POLL, _X76F041_CONFIG, _X76F041_CFG_SET_DATA_KEY
);
if (error)
return error;
@ -353,29 +354,91 @@ DriverError X76F041Driver::setDataKey(const uint8_t *key) {
/* X76F100 driver */
enum X76F100Command : uint8_t {
_X76F100_READ = 0x81,
_X76F100_WRITE = 0x80,
_X76F100_SET_READ_KEY = 0xfe,
_X76F100_SET_WRITE_KEY = 0xfc,
_X76F100_ACK_POLL = 0x55
_X76F100_READ = 0x81,
_X76F100_WRITE = 0x80,
_X76F100_SET_KEY = 0xfc,
_X76F100_ACK_POLL = 0x55
};
// TODO: actually implement this (even though no X76F100 carts were ever made)
DriverError X76F100Driver::readPrivateData(void) {
return UNSUPPORTED_OP;
auto error = _x76Command(_X76F100_ACK_POLL, _X76F100_READ);
if (error)
return error;
//io::i2cStart();
io::i2cReadBytes(_dump.data, 112);
io::i2cStopWithCS();
_dump.flags |= DUMP_PRIVATE_DATA_OK;
return NO_ERROR;
}
DriverError X76F100Driver::writeData(void) {
return UNSUPPORTED_OP;
// Writes can only be done in 8-byte blocks.
for (int i = 0; i < 112; i += 8) {
auto error = _x76Command(
_X76F100_ACK_POLL, _X76F100_WRITE | (i >> 2)
);
if (error)
return error;
if (!io::i2cWriteBytes(&_dump.data[i], 8)) {
io::i2cStopWithCS(_X76_WRITE_DELAY);
LOG("NACK while sending data bytes");
return X76_NACK;
}
io::i2cStopWithCS(_X76_WRITE_DELAY);
}
return NO_ERROR;
}
DriverError X76F100Driver::erase(void) {
return UNSUPPORTED_OP;
// The chip does not have an erase command, so erasing must be performed
// manually one block at a time.
uint8_t dummy[8]{ 0, 0, 0, 0, 0, 0, 0, 0 };
for (int i = 0; i < 112; i += 8) {
auto error = _x76Command(
_X76F100_ACK_POLL, _X76F100_WRITE | (i >> 2)
);
if (error)
return error;
if (!io::i2cWriteBytes(dummy, 8)) {
io::i2cStopWithCS(_X76_WRITE_DELAY);
LOG("NACK while sending data bytes");
return X76_NACK;
}
io::i2cStopWithCS(_X76_WRITE_DELAY);
}
return setDataKey(dummy);
}
DriverError X76F100Driver::setDataKey(const uint8_t *key) {
return UNSUPPORTED_OP;
// There are two separate keys, one for read commands and one for write
// commands.
for (int i = 0; i < 2; i++) {
auto error = _x76Command(
_X76F100_ACK_POLL, _X76F100_SET_KEY | (i << 1)
);
if (error)
return error;
if (!io::i2cWriteBytes(key, sizeof(_dump.dataKey))) {
io::i2cStopWithCS(_X76_WRITE_DELAY);
LOG("NACK while setting new data key");
return X76_NACK;
}
io::i2cStopWithCS(_X76_WRITE_DELAY);
}
_dump.copyKeyFrom(key);
return NO_ERROR;
}
/* ZS01 driver */
@ -597,8 +660,10 @@ CartDriver *newCartDriver(Dump &dump) {
case _ID_X76F041:
return new X76F041Driver(dump);
//case _ID_X76F100:
//return new X76F100Driver(dump);
#ifdef ENABLE_X76F100_DRIVER
case _ID_X76F100:
return new X76F100Driver(dump);
#endif
default:
return new CartDriver(dump);

View File

@ -89,7 +89,9 @@ public:
class [[gnu::packed]] X76Driver : public CartDriver {
protected:
DriverError _x76Command(uint8_t cmd, uint8_t param, uint8_t pollByte) const;
DriverError _x76Command(
uint8_t pollByte, uint8_t cmd, int param = -1
) const;
public:
inline X76Driver(Dump &dump, ChipType chipType)

View File

@ -21,8 +21,9 @@
namespace ide {
static constexpr int _STATUS_TIMEOUT = 100000;
static constexpr int _RESET_STATUS_TIMEOUT = 3000000;
static constexpr int _STATUS_TIMEOUT = 1000000;
static constexpr int _RESET_STATUS_TIMEOUT = 2000000;
static constexpr int _DATA_STATUS_TIMEOUT = 2000000;
static constexpr int _DMA_TIMEOUT = 10000;
/* Utilities */
@ -143,7 +144,9 @@ DeviceError Device::_command(uint8_t cmd, bool drdy) {
DeviceError Device::_transferPIO(void *data, size_t length, bool write) {
util::assertAligned<uint16_t>(data);
auto error = _waitForStatus(CS0_STATUS_DRQ, CS0_STATUS_DRQ, _STATUS_TIMEOUT);
auto error = _waitForStatus(
CS0_STATUS_DRQ, CS0_STATUS_DRQ, _DATA_STATUS_TIMEOUT
);
if (error)
return error;
@ -165,7 +168,9 @@ DeviceError Device::_transferDMA(void *data, size_t length, bool write) {
util::assertAligned<uint32_t>(data);
auto error = _waitForStatus(CS0_STATUS_DRQ, CS0_STATUS_DRQ, _STATUS_TIMEOUT);
auto error = _waitForStatus(
CS0_STATUS_DRQ, CS0_STATUS_DRQ, _DATA_STATUS_TIMEOUT
);
if (error)
return error;

View File

@ -34,13 +34,9 @@ void init(void) {
| BIU_CTRL_DMA_DELAY;
SYS573_WATCHDOG = 0;
SYS573_BANK_CTRL = 0;
SYS573_CART_OUT = 0;
SYS573_MISC_OUT = 0
| SYS573_MISC_OUT_ADC_MOSI
| SYS573_MISC_OUT_ADC_CS
| SYS573_MISC_OUT_ADC_SCK
| SYS573_MISC_OUT_JVS_STAT;
SYS573_BANK_CTRL = _bankSwitchReg;
SYS573_CART_OUT = _cartOutputReg;
SYS573_MISC_OUT = _miscOutputReg;
// Some of the digital I/O board's light outputs are controlled by the FPGA
// and cannot be turned off until the FPGA is initialized.
@ -202,11 +198,13 @@ void i2cStart(void) {
void i2cStartWithCS(int csDelay) {
_SDA(true);
_SCL(true);
_SCL(false);
CS(true);
CS(false);
delayMicroseconds(csDelay);
SCL(true);
SDA(false); // START: SDA falling, SCL high
SCL(false);
}
@ -223,6 +221,8 @@ void i2cStopWithCS(int csDelay) {
SCL(true);
SDA(true); // STOP: SDA rising, SCL high
SCL(false);
delayMicroseconds(csDelay);
CS(true);
}
@ -311,8 +311,8 @@ uint32_t i2cResetX76(void) {
SCL(false);
}
SCL(true);
CS(true);
SCL(true);
return value;
}
@ -347,34 +347,43 @@ uint32_t i2cResetZS01(void) {
/* 1-wire driver */
static constexpr int _DS_RESET_DELAY = 480;
static constexpr int _DS_RESET_LOW_TIME = 480;
static constexpr int _DS_RESET_SAMPLE_DELAY = 70;
static constexpr int _DS_RESET_DELAY = 410;
static constexpr int _DS_READ_LOW_TIME = 3;
static constexpr int _DS_READ_SAMPLE_DELAY = 10;
static constexpr int _DS_READ_DELAY = 53;
static constexpr int _DS_ZERO_LOW_TIME = 65;
static constexpr int _DS_ZERO_HIGH_TIME = 5;
static constexpr int _DS_ONE_LOW_TIME = 10;
static constexpr int _DS_ONE_HIGH_TIME = 55;
#define _CART1WIRE(value) setCartOutput(OUT_1WIRE, !(value))
#define _DIO1WIRE(value) setDIO1Wire(value)
bool dsCartReset(void) {
_CART1WIRE(false);
delayMicroseconds(_DS_RESET_DELAY);
delayMicroseconds(_DS_RESET_LOW_TIME);
_CART1WIRE(true);
delayMicroseconds(60);
delayMicroseconds(_DS_RESET_SAMPLE_DELAY);
bool present = !getCartInput(IN_1WIRE);
delayMicroseconds(60);
delayMicroseconds(_DS_RESET_DELAY);
delayMicroseconds(1000);
return present;
}
bool dsDIOReset(void) {
_DIO1WIRE(false);
delayMicroseconds(_DS_RESET_DELAY);
delayMicroseconds(_DS_RESET_LOW_TIME);
_DIO1WIRE(true);
delayMicroseconds(60);
delayMicroseconds(_DS_RESET_SAMPLE_DELAY);
bool present = !getDIO1Wire();
delayMicroseconds(60);
delayMicroseconds(_DS_RESET_DELAY);
delayMicroseconds(1000);
return present;
}
@ -383,12 +392,12 @@ uint8_t dsCartReadByte(void) {
for (int bit = 0; bit < 8; bit++) { // LSB first
_CART1WIRE(false);
delayMicroseconds(2);
delayMicroseconds(_DS_READ_LOW_TIME);
_CART1WIRE(true);
delayMicroseconds(10);
delayMicroseconds(_DS_READ_SAMPLE_DELAY);
if (getCartInput(IN_1WIRE))
value |= (1 << bit);
delayMicroseconds(50);
delayMicroseconds(_DS_READ_DELAY);
}
return value;
@ -399,12 +408,12 @@ uint8_t dsDIOReadByte(void) {
for (int bit = 0; bit < 8; bit++) { // LSB first
_DIO1WIRE(false);
delayMicroseconds(2);
delayMicroseconds(_DS_READ_LOW_TIME);
_DIO1WIRE(true);
delayMicroseconds(10);
delayMicroseconds(_DS_READ_SAMPLE_DELAY);
if (getDIO1Wire())
value |= (1 << bit);
delayMicroseconds(50);
delayMicroseconds(_DS_READ_DELAY);
}
return value;
@ -414,14 +423,14 @@ void dsCartWriteByte(uint8_t value) {
for (int bit = 0; bit < 8; bit++) { // LSB first
if (value & (1 << bit)) {
_CART1WIRE(false);
delayMicroseconds(2);
delayMicroseconds(_DS_ONE_LOW_TIME);
_CART1WIRE(true);
delayMicroseconds(60);
delayMicroseconds(_DS_ONE_HIGH_TIME);
} else {
_CART1WIRE(false);
delayMicroseconds(60);
delayMicroseconds(_DS_ZERO_LOW_TIME);
_CART1WIRE(true);
delayMicroseconds(2);
delayMicroseconds(_DS_ZERO_HIGH_TIME);
}
}
}
@ -430,14 +439,14 @@ void dsDIOWriteByte(uint8_t value) {
for (int bit = 0; bit < 8; bit++) { // LSB first
if (value & (1 << bit)) {
_DIO1WIRE(false);
delayMicroseconds(2);
delayMicroseconds(_DS_ONE_LOW_TIME);
_DIO1WIRE(true);
delayMicroseconds(60);
delayMicroseconds(_DS_ONE_HIGH_TIME);
} else {
_DIO1WIRE(false);
delayMicroseconds(60);
delayMicroseconds(_DS_ZERO_LOW_TIME);
_DIO1WIRE(true);
delayMicroseconds(2);
delayMicroseconds(_DS_ZERO_HIGH_TIME);
}
}
}