mirror of
https://github.com/spicyjpeg/573in1.git
synced 2025-02-02 20:47:17 +01:00
528 lines
11 KiB
C++
528 lines
11 KiB
C++
|
|
#include <stdint.h>
|
|
#include "ps1/system.h"
|
|
#include "cart.hpp"
|
|
#include "cartio.hpp"
|
|
#include "io.hpp"
|
|
#include "zs01.hpp"
|
|
|
|
namespace cart {
|
|
|
|
/* Dummy cartridge driver */
|
|
|
|
DriverError DummyDriver::readSystemID(void) {
|
|
if (_privateDump.flags & DUMP_SYSTEM_ID_OK) {
|
|
_dump.flags |= DUMP_SYSTEM_ID_OK;
|
|
return NO_ERROR;
|
|
}
|
|
|
|
return DS2401_NO_RESP;
|
|
}
|
|
|
|
DriverError DummyDriver::readCartID(void) {
|
|
if (_privateDump.flags & DUMP_CART_ID_OK) {
|
|
_dump.flags |= DUMP_CART_ID_OK;
|
|
return NO_ERROR;
|
|
}
|
|
|
|
return DS2401_NO_RESP;
|
|
}
|
|
|
|
DriverError DummyDriver::readPublicData(void) {
|
|
if (_privateDump.flags & DUMP_PUBLIC_DATA_OK) {
|
|
_dump.flags |= DUMP_PUBLIC_DATA_OK;
|
|
return NO_ERROR;
|
|
}
|
|
|
|
return _getErrorCode();
|
|
}
|
|
|
|
DriverError DummyDriver::readPrivateData(void) {
|
|
if ((_privateDump.flags & DUMP_PRIVATE_DATA_OK) && !__builtin_memcmp(
|
|
_dump.dataKey, _privateDump.dataKey, sizeof(_dump.dataKey)
|
|
)) {
|
|
_dump.flags |= DUMP_PRIVATE_DATA_OK;
|
|
return NO_ERROR;
|
|
}
|
|
|
|
return _getErrorCode();
|
|
}
|
|
|
|
DriverError DummyDriver::writeData(void) {
|
|
if (!__builtin_memcmp(
|
|
_dump.dataKey, _privateDump.dataKey, sizeof(_dump.dataKey)
|
|
)) {
|
|
__builtin_memcpy(_privateDump.data, _dump.data, sizeof(_dump.data));
|
|
return NO_ERROR;
|
|
}
|
|
|
|
return _getErrorCode();
|
|
}
|
|
|
|
DriverError DummyDriver::erase(void) {
|
|
if (!__builtin_memcmp(
|
|
_dump.dataKey, _privateDump.dataKey, sizeof(_dump.dataKey)
|
|
)) {
|
|
_privateDump.clearData();
|
|
// TODO: clear config registers as well
|
|
return NO_ERROR;
|
|
}
|
|
|
|
return _getErrorCode();
|
|
}
|
|
|
|
DriverError DummyDriver::setDataKey(const uint8_t *key) {
|
|
// Update the data key stored in the dump.
|
|
__builtin_memcpy(_dump.dataKey, key, sizeof(_dump.dataKey));
|
|
return NO_ERROR;
|
|
}
|
|
|
|
/* Functions common to all cartridge drivers */
|
|
|
|
static constexpr int _X76_MAX_ACK_POLLS = 5;
|
|
static constexpr int _X76_WRITE_DELAY = 10000;
|
|
static constexpr int _ZS01_PACKET_DELAY = 30000;
|
|
|
|
DriverError CartDriver::readSystemID(void) {
|
|
auto mask = setInterruptMask(0);
|
|
|
|
if (!io::dsDIOReset()) {
|
|
if (mask)
|
|
setInterruptMask(mask);
|
|
|
|
LOG("no 1-wire device found");
|
|
return DS2401_NO_RESP;
|
|
}
|
|
|
|
_dump.flags |= DUMP_HAS_SYSTEM_ID;
|
|
|
|
io::dsDIOWriteByte(0x33);
|
|
for (int i = 0; i < 8; i++)
|
|
_dump.systemID.data[i] = io::dsDIOReadByte();
|
|
|
|
if (mask)
|
|
setInterruptMask(mask);
|
|
if (!_dump.systemID.validateDSCRC())
|
|
return DS2401_ID_ERROR;
|
|
|
|
_dump.flags |= DUMP_SYSTEM_ID_OK;
|
|
return NO_ERROR;
|
|
}
|
|
|
|
DriverError X76Driver::readCartID(void) {
|
|
auto mask = setInterruptMask(0);
|
|
|
|
if (!io::dsCartReset()) {
|
|
if (mask)
|
|
setInterruptMask(mask);
|
|
|
|
LOG("no 1-wire device found");
|
|
return DS2401_NO_RESP;
|
|
}
|
|
|
|
_dump.flags |= DUMP_HAS_CART_ID;
|
|
|
|
io::dsCartWriteByte(0x33);
|
|
for (int i = 0; i < 8; i++)
|
|
_dump.cartID.data[i] = io::dsCartReadByte();
|
|
|
|
if (mask)
|
|
setInterruptMask(mask);
|
|
if (!_dump.cartID.validateDSCRC())
|
|
return DS2401_ID_ERROR;
|
|
|
|
_dump.flags |= DUMP_CART_ID_OK;
|
|
return NO_ERROR;
|
|
}
|
|
|
|
DriverError X76Driver::_x76Command(
|
|
uint8_t cmd, uint8_t param, uint8_t pollByte
|
|
) const {
|
|
io::i2cStartWithCS();
|
|
|
|
io::i2cWriteByte(cmd);
|
|
if (!io::i2cGetACK()) {
|
|
io::i2cStopWithCS();
|
|
LOG("NACK while sending command");
|
|
return X76_NACK;
|
|
}
|
|
|
|
io::i2cWriteByte(param);
|
|
if (!io::i2cGetACK()) {
|
|
io::i2cStopWithCS();
|
|
LOG("NACK while sending parameter");
|
|
return X76_NACK;
|
|
}
|
|
|
|
if (!io::i2cWriteBytes(_dump.dataKey, sizeof(_dump.dataKey))) {
|
|
io::i2cStopWithCS();
|
|
LOG("NACK while sending data key");
|
|
return X76_NACK;
|
|
}
|
|
|
|
for (int i = _X76_MAX_ACK_POLLS; i; i--) {
|
|
delayMicroseconds(_X76_WRITE_DELAY);
|
|
io::i2cStart();
|
|
io::i2cWriteByte(pollByte);
|
|
if (io::i2cGetACK())
|
|
return NO_ERROR;
|
|
}
|
|
|
|
io::i2cStopWithCS();
|
|
LOG("ACK polling timeout (wrong key?)");
|
|
return X76_POLL_FAIL;
|
|
}
|
|
|
|
/* X76F041 driver */
|
|
|
|
enum X76F041Command : uint8_t {
|
|
_X76F041_READ = 0x60,
|
|
_X76F041_WRITE = 0x40,
|
|
_X76F041_CONFIG = 0x80,
|
|
_X76F041_ACK_POLL = 0xc0
|
|
};
|
|
|
|
enum X76F041ConfigOp : uint8_t {
|
|
_X76F041_CFG_SET_DATA_KEY = 0x20,
|
|
_X76F041_CFG_READ_CONFIG = 0x60,
|
|
_X76F041_CFG_WRITE_CONFIG = 0x50,
|
|
_X76F041_CFG_ERASE = 0x70
|
|
};
|
|
|
|
DriverError X76F041Driver::readPrivateData(void) {
|
|
// Reads can be done with any block size, but a single read operation can't
|
|
// 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
|
|
);
|
|
if (error)
|
|
return error;
|
|
|
|
io::i2cReadByte(); // Ignore "secure read setup" byte
|
|
io::i2cStart();
|
|
|
|
io::i2cWriteByte(i & 0xff);
|
|
if (io::i2cGetACK()) {
|
|
LOG("NACK after resending address");
|
|
return X76_NACK;
|
|
}
|
|
|
|
io::i2cReadBytes(&_dump.data[i], 128);
|
|
io::i2cStopWithCS();
|
|
}
|
|
|
|
auto error = _x76Command(
|
|
_X76F041_CONFIG, _X76F041_CFG_READ_CONFIG, _X76F041_ACK_POLL
|
|
);
|
|
if (error)
|
|
return error;
|
|
|
|
// TODO: this may need another "secure read setup"...
|
|
io::i2cReadBytes(_dump.config, 5);
|
|
io::i2cStopWithCS();
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
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
|
|
);
|
|
if (error)
|
|
return error;
|
|
|
|
if (!io::i2cWriteBytes(&_dump.data[i], 8)) {
|
|
LOG("NACK while sending data bytes");
|
|
return X76_NACK;
|
|
}
|
|
|
|
io::i2cStopWithCS(_X76_WRITE_DELAY);
|
|
}
|
|
|
|
auto error = _x76Command(
|
|
_X76F041_CONFIG, _X76F041_CFG_WRITE_CONFIG, _X76F041_ACK_POLL
|
|
);
|
|
if (error)
|
|
return error;
|
|
|
|
if (!io::i2cWriteBytes(_dump.config, 8)) {
|
|
LOG("NACK while sending data bytes");
|
|
return X76_NACK;
|
|
}
|
|
|
|
io::i2cStopWithCS(_X76_WRITE_DELAY);
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
DriverError X76F041Driver::erase(void) {
|
|
auto error = _x76Command(
|
|
_X76F041_CONFIG, _X76F041_CFG_ERASE, _X76F041_ACK_POLL
|
|
);
|
|
if (error)
|
|
return error;
|
|
|
|
io::i2cStopWithCS(_X76_WRITE_DELAY);
|
|
return NO_ERROR;
|
|
}
|
|
|
|
DriverError X76F041Driver::setDataKey(const uint8_t *key) {
|
|
auto error = _x76Command(
|
|
_X76F041_CONFIG, _X76F041_CFG_SET_DATA_KEY, _X76F041_ACK_POLL
|
|
);
|
|
if (error)
|
|
return error;
|
|
|
|
// The X76F041 requires the key to be sent twice as a way of ensuring it
|
|
// gets received correctly.
|
|
for (int i = 2; i; i--) {
|
|
if (!io::i2cWriteBytes(key, sizeof(_dump.dataKey))) {
|
|
io::i2cStopWithCS();
|
|
LOG("NACK while setting new data key");
|
|
return X76_NACK;
|
|
}
|
|
}
|
|
|
|
io::i2cStopWithCS(_X76_WRITE_DELAY);
|
|
|
|
// Update the data key stored in the dump.
|
|
__builtin_memcpy(_dump.dataKey, key, sizeof(_dump.dataKey));
|
|
return NO_ERROR;
|
|
}
|
|
|
|
/* 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
|
|
};
|
|
|
|
// TODO: actually implement this (even though no X76F100 carts were ever made)
|
|
|
|
DriverError X76F100Driver::readPrivateData(void) {
|
|
return UNSUPPORTED_OP;
|
|
}
|
|
|
|
DriverError X76F100Driver::writeData(void) {
|
|
return UNSUPPORTED_OP;
|
|
}
|
|
|
|
DriverError X76F100Driver::erase(void) {
|
|
return UNSUPPORTED_OP;
|
|
}
|
|
|
|
DriverError X76F100Driver::setDataKey(const uint8_t *key) {
|
|
return UNSUPPORTED_OP;
|
|
}
|
|
|
|
/* ZS01 driver */
|
|
|
|
DriverError ZS01Driver::_transact(
|
|
zs01::Packet &request, zs01::Packet &response
|
|
) {
|
|
io::i2cStart();
|
|
|
|
if (!io::i2cWriteBytes(
|
|
&request.command, sizeof(zs01::Packet), _ZS01_PACKET_DELAY
|
|
)) {
|
|
io::i2cStop();
|
|
LOG("NACK while sending request packet");
|
|
return ZS01_NACK;
|
|
}
|
|
|
|
io::i2cReadBytes(&response.command, sizeof(zs01::Packet));
|
|
io::i2cStop();
|
|
|
|
if (!response.decodeResponse())
|
|
return ZS01_CRC_MISMATCH;
|
|
|
|
_encoderState = response.address;
|
|
|
|
if (response.command != zs01::RESP_NO_ERROR) {
|
|
LOG("ZS01 error, code=0x%02x", response.command);
|
|
return ZS01_ERROR;
|
|
}
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
DriverError ZS01Driver::readCartID(void) {
|
|
zs01::Packet request, response;
|
|
DriverError error;
|
|
|
|
request.address = zs01::ADDR_ZS01_ID;
|
|
request.encodeReadRequest();
|
|
|
|
error = _transact(request, response);
|
|
if (error)
|
|
return error;
|
|
|
|
response.copyTo(_dump.zsID.data);
|
|
if (!_dump.zsID.validateDSCRC())
|
|
return DS2401_ID_ERROR;
|
|
|
|
_dump.flags |= DUMP_ZS_ID_OK;
|
|
|
|
request.address = zs01::ADDR_DS2401_ID;
|
|
request.encodeReadRequest();
|
|
|
|
error = _transact(request, response);
|
|
if (error)
|
|
return error;
|
|
|
|
response.copyTo(_dump.cartID.data);
|
|
if (!_dump.cartID.validateDSCRC())
|
|
return DS2401_ID_ERROR;
|
|
|
|
_dump.flags |= DUMP_CART_ID_OK;
|
|
return NO_ERROR;
|
|
}
|
|
|
|
DriverError ZS01Driver::readPublicData(void) {
|
|
zs01::Packet request, response;
|
|
|
|
for (int i = zs01::ADDR_PUBLIC; i < zs01::ADDR_PUBLIC_END; i++) {
|
|
request.address = i;
|
|
request.encodeReadRequest();
|
|
|
|
DriverError error = _transact(request, response);
|
|
if (error)
|
|
return error;
|
|
|
|
response.copyTo(&_dump.data[i * sizeof(response.data)]);
|
|
}
|
|
|
|
_dump.flags |= DUMP_PUBLIC_DATA_OK;
|
|
return NO_ERROR;
|
|
}
|
|
|
|
DriverError ZS01Driver::readPrivateData(void) {
|
|
zs01::Packet request, response;
|
|
zs01::Key key;
|
|
|
|
key.unpackFrom(_dump.dataKey);
|
|
|
|
for (int i = zs01::ADDR_PRIVATE; i < zs01::ADDR_PRIVATE_END; i++) {
|
|
request.address = i;
|
|
request.encodeReadRequest(key, _encoderState);
|
|
|
|
DriverError error = _transact(request, response);
|
|
if (error)
|
|
return error;
|
|
|
|
response.copyTo(&_dump.data[i * sizeof(response.data)]);
|
|
}
|
|
|
|
_dump.flags |= DUMP_PRIVATE_DATA_OK;
|
|
|
|
request.address = zs01::ADDR_CONFIG;
|
|
request.encodeReadRequest(key, _encoderState);
|
|
|
|
DriverError error = _transact(request, response);
|
|
if (error)
|
|
return error;
|
|
|
|
response.copyTo(_dump.config);
|
|
|
|
_dump.flags |= DUMP_CONFIG_OK;
|
|
return NO_ERROR;
|
|
}
|
|
|
|
DriverError ZS01Driver::writeData(void) {
|
|
zs01::Packet request, response;
|
|
zs01::Key key;
|
|
|
|
key.unpackFrom(_dump.dataKey);
|
|
|
|
for (int i = zs01::ADDR_PUBLIC; i < zs01::ADDR_PRIVATE_END; i++) {
|
|
request.address = i;
|
|
request.copyFrom(&_dump.data[i * sizeof(request.data)]);
|
|
request.encodeWriteRequest(key, _encoderState);
|
|
|
|
DriverError error = _transact(request, response);
|
|
if (error)
|
|
return error;
|
|
}
|
|
|
|
request.address = zs01::ADDR_CONFIG;
|
|
request.copyFrom(_dump.config);
|
|
request.encodeWriteRequest(key, _encoderState);
|
|
|
|
return _transact(request, response);
|
|
}
|
|
|
|
DriverError ZS01Driver::erase(void) {
|
|
zs01::Packet request, response;
|
|
zs01::Key key;
|
|
|
|
key.unpackFrom(_dump.dataKey);
|
|
|
|
__builtin_memset(request.data, 0, sizeof(request.data));
|
|
request.address = zs01::ADDR_ERASE;
|
|
request.encodeWriteRequest(key, _encoderState);
|
|
|
|
return _transact(request, response);
|
|
}
|
|
|
|
DriverError ZS01Driver::setDataKey(const uint8_t *key) {
|
|
zs01::Packet request, response;
|
|
zs01::Key newKey;
|
|
|
|
newKey.unpackFrom(_dump.dataKey);
|
|
|
|
request.address = zs01::ADDR_DATA_KEY;
|
|
request.copyFrom(key);
|
|
request.encodeWriteRequest(newKey, _encoderState);
|
|
|
|
DriverError error = _transact(request, response);
|
|
if (error)
|
|
return error;
|
|
|
|
// Update the data key stored in the dump.
|
|
__builtin_memcpy(_dump.dataKey, key, sizeof(_dump.dataKey));
|
|
return NO_ERROR;
|
|
}
|
|
|
|
/* Cartridge identification */
|
|
|
|
enum ChipIdentifier : uint32_t {
|
|
_ID_X76F041 = 0x55aa5519,
|
|
_ID_X76F100 = 0x55aa0019,
|
|
_ID_ZS01 = 0x5a530001
|
|
};
|
|
|
|
CartDriver *newCartDriver(Dump &dump) {
|
|
if (!io::getCartInsertionStatus()) {
|
|
LOG("DSR not asserted");
|
|
return new CartDriver(dump);
|
|
}
|
|
|
|
uint32_t id1 = io::i2cResetZS01();
|
|
LOG("detecting ZS01, id1=0x%08x", id1);
|
|
|
|
if (id1 == _ID_ZS01)
|
|
return new ZS01Driver(dump);
|
|
|
|
uint32_t id2 = io::i2cResetX76();
|
|
LOG("detecting X76, id2=0x%08x", id2);
|
|
|
|
switch (id2) {
|
|
case _ID_X76F041:
|
|
return new X76F041Driver(dump);
|
|
|
|
//case _ID_X76F100:
|
|
//return new X76F100Driver(dump);
|
|
|
|
default:
|
|
return new CartDriver(dump);
|
|
}
|
|
}
|
|
|
|
}
|