2023-05-30 18:08:52 +02:00
|
|
|
|
|
|
|
#include <stddef.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
#include "ps1/system.h"
|
|
|
|
#include "vendor/miniz.h"
|
2023-06-02 20:51:28 +02:00
|
|
|
#include "cartio.hpp"
|
2023-05-30 18:08:52 +02:00
|
|
|
#include "io.hpp"
|
|
|
|
#include "util.hpp"
|
|
|
|
#include "zs01.hpp"
|
|
|
|
|
|
|
|
namespace cart {
|
|
|
|
|
2023-06-02 20:51:28 +02:00
|
|
|
/* Common data structures */
|
2023-05-30 18:08:52 +02:00
|
|
|
|
2023-06-02 20:51:28 +02:00
|
|
|
void Identifier::updateChecksum(void) {
|
|
|
|
data[7] = (util::sum(data, 7) & 0xff) ^ 0xff;
|
|
|
|
}
|
2023-05-30 18:08:52 +02:00
|
|
|
|
2023-06-02 20:51:28 +02:00
|
|
|
bool Identifier::validateChecksum(void) const {
|
|
|
|
uint8_t value = (util::sum(data, 7) & 0xff) ^ 0xff;
|
2023-05-30 18:08:52 +02:00
|
|
|
|
2023-06-02 20:51:28 +02:00
|
|
|
if (value != data[7]) {
|
|
|
|
LOG("checksum mismatch, exp=0x%02x, got=0x%02x", value, data[7]);
|
2023-05-30 18:08:52 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2023-06-02 20:51:28 +02:00
|
|
|
void Identifier::updateDSCRC(void) {
|
|
|
|
data[7] = util::dsCRC8(data, 7);
|
2023-05-30 18:08:52 +02:00
|
|
|
}
|
|
|
|
|
2023-06-02 20:51:28 +02:00
|
|
|
bool Identifier::validateDSCRC(void) const {
|
|
|
|
uint8_t value = util::dsCRC8(data, 7);
|
2023-05-30 18:08:52 +02:00
|
|
|
|
2023-06-02 20:51:28 +02:00
|
|
|
if (value != data[7]) {
|
|
|
|
LOG("CRC mismatch, exp=0x%02x, got=0x%02x", value, data[7]);
|
|
|
|
return false;
|
2023-05-30 18:08:52 +02:00
|
|
|
}
|
|
|
|
|
2023-06-02 20:51:28 +02:00
|
|
|
return true;
|
|
|
|
}
|
2023-05-30 18:08:52 +02:00
|
|
|
|
2023-06-02 20:51:28 +02:00
|
|
|
/* Dump structure and utilities */
|
2023-05-30 18:08:52 +02:00
|
|
|
|
2023-06-02 20:51:28 +02:00
|
|
|
const ChipSize CHIP_SIZES[NUM_CHIP_TYPES]{
|
|
|
|
{ .dataLength = 0, .publicDataOffset = 0, .publicDataLength = 0 },
|
|
|
|
{ .dataLength = 512, .publicDataOffset = 384, .publicDataLength = 128 },
|
|
|
|
{ .dataLength = 112, .publicDataOffset = 0, .publicDataLength = 0 },
|
|
|
|
{ .dataLength = 112, .publicDataOffset = 0, .publicDataLength = 32 }
|
|
|
|
};
|
2023-05-30 18:08:52 +02:00
|
|
|
|
2023-06-02 20:51:28 +02:00
|
|
|
size_t Dump::toQRString(char *output) const {
|
2023-05-30 18:08:52 +02:00
|
|
|
uint8_t compressed[MAX_QR_STRING_LENGTH];
|
|
|
|
size_t uncompLength = getDumpLength();
|
|
|
|
size_t compLength = MAX_QR_STRING_LENGTH;
|
|
|
|
|
|
|
|
int error = mz_compress2(
|
2023-06-02 20:51:28 +02:00
|
|
|
compressed, reinterpret_cast<mz_ulong *>(&compLength),
|
|
|
|
reinterpret_cast<const uint8_t *>(this), uncompLength,
|
2023-05-30 18:08:52 +02:00
|
|
|
MZ_BEST_COMPRESSION
|
|
|
|
);
|
|
|
|
|
|
|
|
if (error != MZ_OK) {
|
|
|
|
LOG("compression error, code=%d", error);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
LOG(
|
|
|
|
"dump compressed, size=%d, ratio=%d%%", compLength,
|
|
|
|
compLength * 100 / uncompLength
|
|
|
|
);
|
|
|
|
|
|
|
|
compLength = util::encodeBase45(&output[5], compressed, compLength);
|
2023-06-02 20:51:28 +02:00
|
|
|
__builtin_memcpy(&output[0], "573::", 5);
|
|
|
|
__builtin_memcpy(&output[compLength + 5], "::", 3);
|
2023-05-30 18:08:52 +02:00
|
|
|
|
|
|
|
return compLength + 7;
|
|
|
|
}
|
|
|
|
|
2023-06-02 20:51:28 +02:00
|
|
|
/* 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;
|
|
|
|
|
|
|
|
CartError Cart::readSystemID(void) {
|
|
|
|
auto mask = setInterruptMask(0);
|
2023-05-30 18:08:52 +02:00
|
|
|
|
|
|
|
if (!io::dsDIOReset()) {
|
|
|
|
if (mask)
|
|
|
|
setInterruptMask(mask);
|
|
|
|
|
|
|
|
LOG("no 1-wire device found");
|
|
|
|
return DS2401_NO_RESP;
|
|
|
|
}
|
|
|
|
|
2023-06-02 20:51:28 +02:00
|
|
|
_dump.flags |= DUMP_HAS_SYSTEM_ID;
|
2023-05-30 18:08:52 +02:00
|
|
|
|
|
|
|
io::dsDIOWriteByte(0x33);
|
|
|
|
for (int i = 0; i < 8; i++)
|
2023-06-02 20:51:28 +02:00
|
|
|
_dump.systemID.data[i] = io::dsDIOReadByte();
|
2023-05-30 18:08:52 +02:00
|
|
|
|
|
|
|
if (mask)
|
|
|
|
setInterruptMask(mask);
|
2023-06-02 20:51:28 +02:00
|
|
|
if (!_dump.systemID.validateDSCRC())
|
2023-05-30 18:08:52 +02:00
|
|
|
return DS2401_ID_ERROR;
|
|
|
|
|
2023-06-02 20:51:28 +02:00
|
|
|
_dump.flags |= DUMP_SYSTEM_ID_OK;
|
2023-05-30 18:08:52 +02:00
|
|
|
return NO_ERROR;
|
|
|
|
}
|
|
|
|
|
2023-06-02 20:51:28 +02:00
|
|
|
CartError X76Cart::readCartID(void) {
|
|
|
|
auto mask = setInterruptMask(0);
|
2023-05-30 18:08:52 +02:00
|
|
|
|
|
|
|
if (!io::dsCartReset()) {
|
|
|
|
if (mask)
|
|
|
|
setInterruptMask(mask);
|
|
|
|
|
|
|
|
LOG("no 1-wire device found");
|
|
|
|
return DS2401_NO_RESP;
|
|
|
|
}
|
|
|
|
|
2023-06-02 20:51:28 +02:00
|
|
|
_dump.flags |= DUMP_HAS_CART_ID;
|
2023-05-30 18:08:52 +02:00
|
|
|
|
|
|
|
io::dsCartWriteByte(0x33);
|
|
|
|
for (int i = 0; i < 8; i++)
|
2023-06-02 20:51:28 +02:00
|
|
|
_dump.cartID.data[i] = io::dsCartReadByte();
|
2023-05-30 18:08:52 +02:00
|
|
|
|
|
|
|
if (mask)
|
|
|
|
setInterruptMask(mask);
|
2023-06-02 20:51:28 +02:00
|
|
|
if (!_dump.cartID.validateDSCRC())
|
2023-05-30 18:08:52 +02:00
|
|
|
return DS2401_ID_ERROR;
|
|
|
|
|
2023-06-02 20:51:28 +02:00
|
|
|
_dump.flags |= DUMP_CART_ID_OK;
|
2023-05-30 18:08:52 +02:00
|
|
|
return NO_ERROR;
|
|
|
|
}
|
|
|
|
|
2023-06-02 20:51:28 +02:00
|
|
|
CartError X76Cart::_x76Command(
|
|
|
|
uint8_t cmd, uint8_t param, uint8_t pollByte
|
2023-05-30 18:08:52 +02:00
|
|
|
) const {
|
|
|
|
io::i2cStartWithCS();
|
|
|
|
|
2023-06-02 20:51:28 +02:00
|
|
|
io::i2cWriteByte(cmd);
|
2023-05-30 18:08:52 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2023-06-02 20:51:28 +02:00
|
|
|
if (!io::i2cWriteBytes(_dump.dataKey, sizeof(_dump.dataKey))) {
|
2023-05-30 18:08:52 +02:00
|
|
|
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
|
|
|
|
};
|
|
|
|
|
2023-06-02 20:51:28 +02:00
|
|
|
CartError X76F041Cart::readPrivateData(void) {
|
2023-05-30 18:08:52 +02:00
|
|
|
// 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) {
|
2023-06-02 20:51:28 +02:00
|
|
|
auto error = _x76Command(
|
2023-05-30 18:08:52 +02:00
|
|
|
_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;
|
|
|
|
}
|
|
|
|
|
2023-06-02 20:51:28 +02:00
|
|
|
io::i2cReadBytes(&_dump.data[i], 128);
|
2023-05-30 18:08:52 +02:00
|
|
|
io::i2cStopWithCS();
|
|
|
|
}
|
|
|
|
|
|
|
|
return NO_ERROR;
|
|
|
|
}
|
|
|
|
|
2023-06-02 20:51:28 +02:00
|
|
|
CartError X76F041Cart::writeData(void) {
|
2023-05-30 18:08:52 +02:00
|
|
|
// Writes can only be done in 8-byte blocks.
|
|
|
|
for (int i = 0; i < 512; i += 8) {
|
2023-06-02 20:51:28 +02:00
|
|
|
auto error = _x76Command(
|
2023-05-30 18:08:52 +02:00
|
|
|
_X76F041_WRITE | (i >> 8), i & 0xff, _X76F041_ACK_POLL
|
|
|
|
);
|
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
|
2023-06-02 20:51:28 +02:00
|
|
|
if (!io::i2cWriteBytes(&_dump.data[i], 8)) {
|
2023-05-30 18:08:52 +02:00
|
|
|
LOG("NACK while sending data bytes");
|
|
|
|
return X76_NACK;
|
|
|
|
}
|
|
|
|
|
|
|
|
io::i2cStopWithCS(_X76_WRITE_DELAY);
|
|
|
|
}
|
|
|
|
|
|
|
|
return NO_ERROR;
|
|
|
|
}
|
|
|
|
|
2023-06-02 20:51:28 +02:00
|
|
|
CartError X76F041Cart::erase(void) {
|
|
|
|
auto error = _x76Command(
|
2023-05-30 18:08:52 +02:00
|
|
|
_X76F041_CONFIG, _X76F041_CFG_ERASE, _X76F041_ACK_POLL
|
|
|
|
);
|
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
|
|
|
|
io::i2cStopWithCS(_X76_WRITE_DELAY);
|
|
|
|
return NO_ERROR;
|
|
|
|
}
|
|
|
|
|
2023-06-02 20:51:28 +02:00
|
|
|
CartError X76F041Cart::setDataKey(const uint8_t *key) {
|
|
|
|
auto error = _x76Command(
|
2023-05-30 18:08:52 +02:00
|
|
|
_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--) {
|
2023-06-02 20:51:28 +02:00
|
|
|
if (!io::i2cWriteBytes(key, sizeof(_dump.dataKey))) {
|
2023-05-30 18:08:52 +02:00
|
|
|
io::i2cStopWithCS();
|
|
|
|
LOG("NACK while setting new data key");
|
|
|
|
return X76_NACK;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
io::i2cStopWithCS(_X76_WRITE_DELAY);
|
|
|
|
|
2023-06-02 20:51:28 +02:00
|
|
|
// Update the data key stored in the dump.
|
|
|
|
__builtin_memcpy(_dump.dataKey, key, sizeof(_dump.dataKey));
|
2023-05-30 18:08:52 +02:00
|
|
|
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
|
|
|
|
};
|
|
|
|
|
2023-06-02 20:51:28 +02:00
|
|
|
// TODO: actually implement this (even though no X76F100 carts were ever made)
|
2023-05-30 18:08:52 +02:00
|
|
|
|
2023-06-02 20:51:28 +02:00
|
|
|
CartError X76F100Cart::readPrivateData(void) {
|
|
|
|
return UNSUPPORTED_OP;
|
2023-05-30 18:08:52 +02:00
|
|
|
}
|
|
|
|
|
2023-06-02 20:51:28 +02:00
|
|
|
CartError X76F100Cart::writeData(void) {
|
|
|
|
return UNSUPPORTED_OP;
|
|
|
|
}
|
2023-05-30 18:08:52 +02:00
|
|
|
|
2023-06-02 20:51:28 +02:00
|
|
|
CartError X76F100Cart::erase(void) {
|
|
|
|
return UNSUPPORTED_OP;
|
|
|
|
}
|
2023-05-30 18:08:52 +02:00
|
|
|
|
2023-06-02 20:51:28 +02:00
|
|
|
CartError X76F100Cart::setDataKey(const uint8_t *key) {
|
|
|
|
return UNSUPPORTED_OP;
|
2023-05-30 18:08:52 +02:00
|
|
|
}
|
|
|
|
|
2023-06-02 20:51:28 +02:00
|
|
|
/* ZS01 driver */
|
|
|
|
|
|
|
|
CartError ZS01Cart::_transact(zs01::Packet &request, zs01::Packet &response) {
|
2023-05-30 18:08:52 +02:00
|
|
|
io::i2cStart();
|
2023-06-02 20:51:28 +02:00
|
|
|
|
2023-05-30 18:08:52 +02:00
|
|
|
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;
|
|
|
|
|
2023-06-02 20:51:28 +02:00
|
|
|
_encoderState = response.address;
|
|
|
|
|
2023-05-30 18:08:52 +02:00
|
|
|
if (response.command != zs01::RESP_NO_ERROR) {
|
|
|
|
LOG("ZS01 error, code=0x%02x", response.command);
|
|
|
|
return ZS01_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NO_ERROR;
|
|
|
|
}
|
|
|
|
|
2023-06-02 20:51:28 +02:00
|
|
|
CartError ZS01Cart::readCartID(void) {
|
2023-05-30 18:08:52 +02:00
|
|
|
zs01::Packet request, response;
|
2023-06-02 20:51:28 +02:00
|
|
|
CartError error;
|
2023-05-30 18:08:52 +02:00
|
|
|
|
|
|
|
request.address = zs01::ADDR_ZS01_ID;
|
|
|
|
request.encodeReadRequest();
|
|
|
|
|
|
|
|
error = _transact(request, response);
|
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
|
2023-06-02 20:51:28 +02:00
|
|
|
response.copyTo(_dump.zsID.data);
|
|
|
|
if (!_dump.zsID.validateDSCRC())
|
|
|
|
return DS2401_ID_ERROR;
|
2023-05-30 18:08:52 +02:00
|
|
|
|
2023-06-02 20:51:28 +02:00
|
|
|
_dump.flags |= DUMP_ZS_ID_OK;
|
2023-05-30 18:08:52 +02:00
|
|
|
|
|
|
|
request.address = zs01::ADDR_DS2401_ID;
|
|
|
|
request.encodeReadRequest();
|
|
|
|
|
|
|
|
error = _transact(request, response);
|
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
|
2023-06-02 20:51:28 +02:00
|
|
|
response.copyTo(_dump.cartID.data);
|
|
|
|
if (!_dump.cartID.validateDSCRC())
|
|
|
|
return DS2401_ID_ERROR;
|
2023-05-30 18:08:52 +02:00
|
|
|
|
2023-06-02 20:51:28 +02:00
|
|
|
_dump.flags |= DUMP_CART_ID_OK;
|
2023-05-30 18:08:52 +02:00
|
|
|
return NO_ERROR;
|
|
|
|
}
|
|
|
|
|
2023-06-02 20:51:28 +02:00
|
|
|
CartError ZS01Cart::readPublicData(void) {
|
2023-05-30 18:08:52 +02:00
|
|
|
zs01::Packet request, response;
|
|
|
|
|
|
|
|
for (int i = zs01::ADDR_PUBLIC; i < zs01::ADDR_PUBLIC_END; i++) {
|
|
|
|
request.address = i;
|
|
|
|
request.encodeReadRequest();
|
|
|
|
|
2023-06-02 20:51:28 +02:00
|
|
|
CartError error = _transact(request, response);
|
2023-05-30 18:08:52 +02:00
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
|
2023-06-02 20:51:28 +02:00
|
|
|
response.copyTo(&_dump.data[i * sizeof(response.data)]);
|
2023-05-30 18:08:52 +02:00
|
|
|
}
|
|
|
|
|
2023-06-02 20:51:28 +02:00
|
|
|
_dump.flags |= DUMP_PUBLIC_DATA_OK;
|
2023-05-30 18:08:52 +02:00
|
|
|
return NO_ERROR;
|
|
|
|
}
|
|
|
|
|
2023-06-02 20:51:28 +02:00
|
|
|
CartError ZS01Cart::readPrivateData(void) {
|
2023-05-30 18:08:52 +02:00
|
|
|
zs01::Packet request, response;
|
|
|
|
zs01::Key key;
|
2023-06-02 20:51:28 +02:00
|
|
|
|
|
|
|
key.unpackFrom(_dump.dataKey);
|
2023-05-30 18:08:52 +02:00
|
|
|
|
|
|
|
for (int i = zs01::ADDR_PRIVATE; i < zs01::ADDR_PRIVATE_END; i++) {
|
|
|
|
request.address = i;
|
2023-06-02 20:51:28 +02:00
|
|
|
request.encodeReadRequest(key, _encoderState);
|
2023-05-30 18:08:52 +02:00
|
|
|
|
2023-06-02 20:51:28 +02:00
|
|
|
CartError error = _transact(request, response);
|
2023-05-30 18:08:52 +02:00
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
|
2023-06-02 20:51:28 +02:00
|
|
|
response.copyTo(&_dump.data[i * sizeof(response.data)]);
|
2023-05-30 18:08:52 +02:00
|
|
|
}
|
|
|
|
|
2023-06-02 20:51:28 +02:00
|
|
|
_dump.flags |= DUMP_PRIVATE_DATA_OK;
|
2023-05-30 18:08:52 +02:00
|
|
|
|
|
|
|
request.address = zs01::ADDR_CONFIG;
|
2023-06-02 20:51:28 +02:00
|
|
|
request.encodeReadRequest(key, _encoderState);
|
2023-05-30 18:08:52 +02:00
|
|
|
|
2023-06-02 20:51:28 +02:00
|
|
|
CartError error = _transact(request, response);
|
2023-05-30 18:08:52 +02:00
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
|
2023-06-02 20:51:28 +02:00
|
|
|
response.copyTo(_dump.config);
|
2023-05-30 18:08:52 +02:00
|
|
|
|
2023-06-02 20:51:28 +02:00
|
|
|
_dump.flags |= DUMP_CONFIG_OK;
|
2023-05-30 18:08:52 +02:00
|
|
|
return NO_ERROR;
|
|
|
|
}
|
|
|
|
|
2023-06-02 20:51:28 +02:00
|
|
|
CartError ZS01Cart::writeData(void) {
|
2023-05-30 18:08:52 +02:00
|
|
|
zs01::Packet request, response;
|
|
|
|
zs01::Key key;
|
2023-06-02 20:51:28 +02:00
|
|
|
|
|
|
|
key.unpackFrom(_dump.dataKey);
|
2023-05-30 18:08:52 +02:00
|
|
|
|
|
|
|
for (int i = zs01::ADDR_PUBLIC; i < zs01::ADDR_PRIVATE_END; i++) {
|
|
|
|
request.address = i;
|
2023-06-02 20:51:28 +02:00
|
|
|
request.copyFrom(&_dump.data[i * sizeof(request.data)]);
|
|
|
|
request.encodeWriteRequest(key, _encoderState);
|
2023-05-30 18:08:52 +02:00
|
|
|
|
2023-06-02 20:51:28 +02:00
|
|
|
CartError error = _transact(request, response);
|
2023-05-30 18:08:52 +02:00
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
request.address = zs01::ADDR_CONFIG;
|
2023-06-02 20:51:28 +02:00
|
|
|
request.copyFrom(_dump.config);
|
|
|
|
request.encodeWriteRequest(key, _encoderState);
|
2023-05-30 18:08:52 +02:00
|
|
|
|
|
|
|
return _transact(request, response);
|
|
|
|
}
|
|
|
|
|
2023-06-02 20:51:28 +02:00
|
|
|
CartError ZS01Cart::erase(void) {
|
2023-05-30 18:08:52 +02:00
|
|
|
zs01::Packet request, response;
|
|
|
|
zs01::Key key;
|
|
|
|
|
2023-06-02 20:51:28 +02:00
|
|
|
key.unpackFrom(_dump.dataKey);
|
|
|
|
|
|
|
|
__builtin_memset(request.data, 0, sizeof(request.data));
|
2023-05-30 18:08:52 +02:00
|
|
|
request.address = zs01::ADDR_ERASE;
|
2023-06-02 20:51:28 +02:00
|
|
|
request.encodeWriteRequest(key, _encoderState);
|
2023-05-30 18:08:52 +02:00
|
|
|
|
|
|
|
return _transact(request, response);
|
|
|
|
}
|
|
|
|
|
2023-06-02 20:51:28 +02:00
|
|
|
CartError ZS01Cart::setDataKey(const uint8_t *key) {
|
2023-05-30 18:08:52 +02:00
|
|
|
zs01::Packet request, response;
|
2023-06-02 20:51:28 +02:00
|
|
|
zs01::Key newKey;
|
|
|
|
|
|
|
|
newKey.unpackFrom(_dump.dataKey);
|
2023-05-30 18:08:52 +02:00
|
|
|
|
|
|
|
request.address = zs01::ADDR_DATA_KEY;
|
2023-06-02 20:51:28 +02:00
|
|
|
request.copyFrom(key);
|
|
|
|
request.encodeWriteRequest(newKey, _encoderState);
|
2023-05-30 18:08:52 +02:00
|
|
|
|
2023-06-02 20:51:28 +02:00
|
|
|
CartError error = _transact(request, response);
|
2023-05-30 18:08:52 +02:00
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
|
2023-06-02 20:51:28 +02:00
|
|
|
// Update the data key stored in the dump.
|
|
|
|
__builtin_memcpy(_dump.dataKey, key, sizeof(_dump.dataKey));
|
2023-05-30 18:08:52 +02:00
|
|
|
return NO_ERROR;
|
|
|
|
}
|
|
|
|
|
2023-06-02 20:51:28 +02:00
|
|
|
/* Cartridge identification */
|
|
|
|
|
|
|
|
enum ChipIdentifier : uint32_t {
|
|
|
|
_ID_X76F041 = 0x55aa5519,
|
|
|
|
_ID_X76F100 = 0x55aa0019,
|
|
|
|
_ID_ZS01 = 0x5a530001
|
|
|
|
};
|
|
|
|
|
|
|
|
Cart *createCart(Dump &dump) {
|
|
|
|
if (!io::getCartInsertionStatus()) {
|
|
|
|
LOG("DSR not asserted");
|
|
|
|
return new Cart(dump);
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t id1 = io::i2cResetZS01();
|
|
|
|
LOG("id1=0x%08x", id1);
|
|
|
|
|
|
|
|
if (id1 == _ID_ZS01)
|
|
|
|
return new ZS01Cart(dump);
|
|
|
|
|
|
|
|
uint32_t id2 = io::i2cResetX76();
|
|
|
|
LOG("id2=0x%08x", id2);
|
|
|
|
|
|
|
|
switch (id2) {
|
|
|
|
case _ID_X76F041:
|
|
|
|
return new X76F041Cart(dump);
|
|
|
|
|
|
|
|
//case _ID_X76F100:
|
|
|
|
//return new X76F100Cart(dump);
|
|
|
|
|
|
|
|
default:
|
|
|
|
return new Cart(dump);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-30 18:08:52 +02:00
|
|
|
}
|