More refactoring, remove layer queue from GPU driver

This commit is contained in:
spicyjpeg 2023-06-03 08:25:41 +02:00
parent bd50057976
commit 9788a3fbcc
No known key found for this signature in database
GPG Key ID: 5CC87404C01DF393
12 changed files with 684 additions and 678 deletions

View File

@ -6,7 +6,7 @@ set(CMAKE_TOOLCHAIN_FILE "${CMAKE_CURRENT_LIST_DIR}/cmake/toolchain.cmake")
project(
cart_tool_private
LANGUAGES C CXX ASM
VERSION 0.3.0
VERSION 0.3.1
DESCRIPTION "Konami System 573 security cartridge tool"
)
@ -17,6 +17,7 @@ find_package(Python3 REQUIRED COMPONENTS Interpreter)
add_executable(
cart_tool
src/asset.cpp
src/cart.cpp
src/cartdata.cpp
src/cartio.cpp
src/gpu.cpp
@ -67,21 +68,26 @@ target_compile_definitions(
add_custom_command(
TARGET cart_tool POST_BUILD
COMMAND
"${Python3_EXECUTABLE}" "${PROJECT_SOURCE_DIR}/tools/convertExecutable.py"
"${Python3_EXECUTABLE}"
"${PROJECT_SOURCE_DIR}/tools/convertExecutable.py"
-r "cart_tool build ${PROJECT_VERSION} - (C) 2022-2023 spicyjpeg"
$<TARGET_FILE:cart_tool> cart_tool.psexe
BYPRODUCTS cart_tool.psexe
COMMENT "Converting executable"
VERBATIM
)
## Default resource archive
add_custom_command(
COMMAND
"${Python3_EXECUTABLE}" "${PROJECT_SOURCE_DIR}/tools/buildResourceArchive.py"
"${Python3_EXECUTABLE}"
"${PROJECT_SOURCE_DIR}/tools/buildResourceArchive.py"
"${PROJECT_SOURCE_DIR}/resources.json" resources.zip
OUTPUT resources.zip
DEPENDS resources.json
COMMENT "Building resource archive"
VERBATIM
)
ps1_target_incbin(
cart_tool PRIVATE
@ -99,6 +105,7 @@ ps1_target_incbin(
# OUTPUT cart_tool.iso
# DEPENDS cd.json cart_tool
# COMMENT "Building CD-ROM image"
# VERBATIM
#)
# Add a dummy target that depends on the CD image to make sure it gets built.

View File

@ -4,6 +4,7 @@
#include "app/app.hpp"
#include "ps1/system.h"
#include "asset.hpp"
#include "cart.hpp"
#include "cartdata.hpp"
#include "cartio.hpp"
#include "io.hpp"
@ -12,17 +13,18 @@
/* App class */
App::App(void)
: _cart(nullptr), _cartData(nullptr), _identified(nullptr) {
_workerStack = new uint8_t[WORKER_STACK_SIZE];
}
void App::_unloadCartData(void) {
if (_driver) {
delete _driver;
_driver = nullptr;
}
if (_parser) {
delete _parser;
_parser = nullptr;
}
App::~App(void) {
//_dbFile.unload();
delete[] _workerStack;
if (_cart)
delete _cart;
_db.unload();
_identified = nullptr;
}
void App::_setupWorker(void (App::* func)(void)) {
@ -41,7 +43,7 @@ void App::_setupInterrupts(void) {
util::forcedCast<ArgFunction>(&App::_interruptHandler), this
);
setInterruptMask((1 << IRQ_VBLANK) | (1 << IRQ_GPU));
setInterruptMask(1 << IRQ_VBLANK);
}
void App::_dummyWorker(void) {
@ -59,27 +61,20 @@ static const char *const _CARTDB_PATHS[cart::NUM_CHIP_TYPES]{
void App::_cartDetectWorker(void) {
_workerStatus.update(0, 4, WSTR("App.cartDetectWorker.identifyCart"));
_db.unload();
if (_cart)
delete _cart;
if (_cartData)
delete _cartData;
_cart = cart::createCart(_dump);
_cartData = nullptr;
_identified = nullptr;
_unloadCartData();
_driver = cart::newCartDriver(_dump);
if (_dump.chipType) {
LOG("dump @ 0x%08x, cart object @ 0x%08x", &_dump, _cart);
LOG("dump @ 0x%08x, cart driver @ 0x%08x", &_dump, _driver);
_workerStatus.update(1, 4, WSTR("App.cartDetectWorker.readCart"));
_cart->readCartID();
if (!_cart->readPublicData())
_cartData = cart::createCartData(_dump);
if (!_cartData)
_driver->readCartID();
if (!_driver->readPublicData())
_parser = cart::newCartParser(_dump);
if (!_parser)
goto _cartInitDone;
LOG("cart data object @ 0x%08x", _cartData);
LOG("cart parser @ 0x%08x", _parser);
_workerStatus.update(2, 4, WSTR("App.cartDetectWorker.identifyGame"));
if (!_loader->loadAsset(_db, _CARTDB_PATHS[_dump.chipType])) {
@ -115,7 +110,7 @@ _cartInitDone:
delayMicroseconds(5000); // Probably not necessary
io::initKonamiBitstream();
_cart->readSystemID();
_driver->readSystemID();
}
_initDone:
@ -126,12 +121,12 @@ _initDone:
void App::_cartUnlockWorker(void) {
_workerStatus.update(0, 2, WSTR("App.cartUnlockWorker.read"));
//_cart->readPrivateData(); // TODO: implement this
//_driver->readPrivateData(); // TODO: implement this
_workerStatus.update(1, 2, WSTR("App.cartUnlockWorker.identify"));
//if (_dump.flags & cart::DUMP_PRIVATE_DATA_OK)
//_identifyResult = _db.identifyCart(*_cart);
//_identifyResult = _db.identifyCart(*_driver);
_workerStatus.finish(_cartInfoScreen, true);
_dummyWorker();
@ -154,11 +149,10 @@ void App::_interruptHandler(void) {
if (acknowledgeInterrupt(IRQ_VBLANK)) {
_ctx->tick();
io::clearWatchdog();
switchThread(nullptr);
}
if (acknowledgeInterrupt(IRQ_GPU))
_ctx->gpuCtx.drawNextLayer();
if (gpu::isIdle())
switchThread(nullptr);
}
}
void App::run(

View File

@ -7,6 +7,7 @@
#include "app/unlock.hpp"
#include "ps1/system.h"
#include "asset.hpp"
#include "cart.hpp"
#include "cartdata.hpp"
#include "cartio.hpp"
#include "uibase.hpp"
@ -90,11 +91,12 @@ private:
Thread _workerThread;
WorkerStatus _workerStatus;
uint8_t *_workerStack;
cart::Cart *_cart;
cart::CartData *_cartData;
cart::DBEntry *_identified;
uint8_t *_workerStack;
cart::Driver *_driver;
cart::Parser *_parser;
cart::DBEntry *_identified;
void _unloadCartData(void);
void _setupWorker(void (App::* func)(void));
void _setupInterrupts(void);
@ -106,8 +108,16 @@ private:
void _interruptHandler(void);
public:
App(void);
~App(void);
inline App(void)
: _driver(nullptr), _parser(nullptr), _identified(nullptr) {
_workerStack = new uint8_t[WORKER_STACK_SIZE];
}
inline ~App(void) {
_unloadCartData();
delete[] _workerStack;
}
void run(
ui::Context &ctx, asset::AssetLoader &loader,
asset::StringTable &strings

164
src/cart.cpp Normal file
View File

@ -0,0 +1,164 @@
#include <stddef.h>
#include <stdint.h>
#include "vendor/miniz.h"
#include "cart.hpp"
#include "util.hpp"
namespace cart {
/* Common data structures */
void Identifier::updateChecksum(void) {
data[7] = (util::sum(data, 7) & 0xff) ^ 0xff;
}
bool Identifier::validateChecksum(void) const {
uint8_t value = (util::sum(data, 7) & 0xff) ^ 0xff;
if (value != data[7]) {
LOG("checksum mismatch, exp=0x%02x, got=0x%02x", value, data[7]);
return false;
}
return true;
}
void Identifier::updateDSCRC(void) {
data[7] = util::dsCRC8(data, 7);
}
bool Identifier::validateDSCRC(void) const {
uint8_t value = util::dsCRC8(data, 7);
if (value != data[7]) {
LOG("CRC mismatch, exp=0x%02x, got=0x%02x", value, data[7]);
return false;
}
return true;
}
uint8_t IdentifierSet::getFlags(void) const {
uint8_t flags = 0;
if (!traceID.isEmpty())
flags |= DATA_HAS_TRACE_ID;
if (!cartID.isEmpty())
flags |= DATA_HAS_CART_ID;
if (!installID.isEmpty())
flags |= DATA_HAS_INSTALL_ID;
if (!systemID.isEmpty())
flags |= DATA_HAS_SYSTEM_ID;
return flags;
}
void IdentifierSet::setInstallID(uint8_t prefix) {
installID.clear();
installID.data[0] = prefix;
installID.updateChecksum();
}
void IdentifierSet::updateTraceID(uint8_t prefix, int param) {
traceID.clear();
uint8_t *input = &cartID.data[1];
uint16_t checksum = 0;
switch (prefix) {
case 0x81:
// TODO: reverse engineer this TID format
traceID.data[2] = 0;
traceID.data[5] = 0;
traceID.data[6] = 0;
LOG("prefix=0x81");
break;
case 0x82:
for (size_t i = 0; i < (sizeof(cartID.data) - 2); i++) {
uint8_t value = *(input++);
for (int j = 0; j < 8; j++, value >>= 1) {
if (value % 2)
checksum ^= 1 << (i % param);
}
}
traceID.data[1] = checksum >> 8;
traceID.data[2] = checksum & 0xff;
LOG("prefix=0x82, checksum=%04x", checksum);
break;
default:
LOG("unknown prefix 0x%02x", prefix);
}
traceID.data[0] = prefix;
traceID.updateChecksum();
}
void BasicHeader::updateChecksum(void) {
auto value = util::sum(reinterpret_cast<const uint8_t *>(this), 4);
checksum = uint8_t((value & 0xff) ^ 0xff);
}
bool BasicHeader::validateChecksum(void) const {
auto value = util::sum(reinterpret_cast<const uint8_t *>(this), 4);
return (checksum == ((value & 0xff) ^ 0xff));
}
void ExtendedHeader::updateChecksum(void) {
auto value = util::sum(reinterpret_cast<const uint16_t *>(this), 14);
checksum = uint16_t(value & 0xffff);
}
bool ExtendedHeader::validateChecksum(void) const {
auto value = util::sum(reinterpret_cast<const uint16_t *>(this), 14);
return (checksum == (value & 0xffff));
}
/* Dump structure and utilities */
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 }
};
size_t Dump::toQRString(char *output) const {
uint8_t compressed[MAX_QR_STRING_LENGTH];
size_t uncompLength = getDumpLength();
size_t compLength = MAX_QR_STRING_LENGTH;
int error = mz_compress2(
compressed, reinterpret_cast<mz_ulong *>(&compLength),
reinterpret_cast<const uint8_t *>(this), uncompLength,
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);
__builtin_memcpy(&output[0], "573::", 5);
__builtin_memcpy(&output[compLength + 5], "::", 3);
return compLength + 7;
}
}

175
src/cart.hpp Normal file
View File

@ -0,0 +1,175 @@
#pragma once
#include <stddef.h>
#include <stdint.h>
#include "util.hpp"
namespace cart {
/* Definitions */
enum ChipType : uint8_t {
NONE = 0,
X76F041 = 1,
X76F100 = 2,
ZS01 = 3
};
enum FormatType : uint8_t {
BLANK = 0,
SIMPLE = 1,
BASIC = 2,
EXTENDED = 3
};
enum DumpFlag : uint8_t {
DUMP_HAS_SYSTEM_ID = 1 << 0,
DUMP_HAS_CART_ID = 1 << 1,
DUMP_CONFIG_OK = 1 << 2,
DUMP_SYSTEM_ID_OK = 1 << 3,
DUMP_CART_ID_OK = 1 << 4,
DUMP_ZS_ID_OK = 1 << 5,
DUMP_PUBLIC_DATA_OK = 1 << 6,
DUMP_PRIVATE_DATA_OK = 1 << 7
};
// | | Simple | Basic | Extended |
// | :---------------------- | :-------- | :------- | :-------- |
// | DATA_HAS_CODE_PREFIX | | Optional | Mandatory |
// | DATA_HAS_*_ID | | Optional | Optional |
// | DATA_HAS_PUBLIC_SECTION | Mandatory | | Optional |
enum DataFlag : uint8_t {
DATA_HAS_CODE_PREFIX = 1 << 0,
DATA_HAS_TRACE_ID = 1 << 1,
DATA_HAS_CART_ID = 1 << 2,
DATA_HAS_INSTALL_ID = 1 << 3,
DATA_HAS_SYSTEM_ID = 1 << 4,
DATA_HAS_PUBLIC_SECTION = 1 << 5
};
static constexpr int NUM_CHIP_TYPES = 4;
static constexpr size_t MAX_QR_STRING_LENGTH = 0x600;
/* Common data structures */
class [[gnu::packed]] Identifier {
public:
uint8_t data[8];
inline void copyFrom(const uint8_t *source) {
__builtin_memcpy(data, source, sizeof(data));
}
inline void copyTo(uint8_t *dest) const {
__builtin_memcpy(dest, data, sizeof(data));
}
inline void clear(void) {
__builtin_memset(data, 0, sizeof(data));
}
inline bool isEmpty(void) const {
return (util::sum(data, sizeof(data)) == 0);
}
inline size_t toString(char *output) const {
return util::hexToString(output, data, sizeof(data), '-');
}
inline size_t toSerialNumber(char *output) const {
return util::serialNumberToString(output, &data[1]);
}
void updateChecksum(void);
bool validateChecksum(void) const;
void updateDSCRC(void);
bool validateDSCRC(void) const;
};
class [[gnu::packed]] IdentifierSet {
public:
Identifier traceID, cartID, installID, systemID; // aka TID, SID, MID, XID
inline void clear(void) {
__builtin_memset(this, 0, sizeof(IdentifierSet));
}
uint8_t getFlags(void) const;
void setInstallID(uint8_t prefix);
void updateTraceID(uint8_t prefix, int param);
};
class [[gnu::packed]] PublicIdentifierSet {
public:
Identifier traceID, cartID; // aka TID, SID
inline void clear(void) {
__builtin_memset(this, 0, sizeof(PublicIdentifierSet));
}
};
class [[gnu::packed]] SimpleHeader {
public:
char region[4];
};
class [[gnu::packed]] BasicHeader {
public:
char region[2], codePrefix[2];
uint8_t checksum, _pad[3];
void updateChecksum(void);
bool validateChecksum(void) const;
};
class [[gnu::packed]] ExtendedHeader {
public:
char code[8];
uint16_t year; // BCD, can be little endian, big endian or zero
char region[4];
uint16_t checksum;
void updateChecksum(void);
bool validateChecksum(void) const;
};
/* Cartridge dump structure */
struct ChipSize {
public:
size_t dataLength, publicDataOffset, publicDataLength;
};
extern const ChipSize CHIP_SIZES[NUM_CHIP_TYPES];
class [[gnu::packed]] Dump {
public:
ChipType chipType;
uint8_t flags;
Identifier systemID, cartID, zsID;
uint8_t dataKey[8], config[8];
uint8_t data[512];
inline const ChipSize &getChipSize(void) const {
return CHIP_SIZES[chipType];
}
inline size_t getDumpLength(void) const {
return (sizeof(Dump) - sizeof(data)) + getChipSize().dataLength;
}
inline void clear(void) {
__builtin_memset(this, 0, sizeof(Dump));
}
inline void clearData(void) {
__builtin_memset(data, 0, getChipSize().dataLength);
}
inline bool isDataEmpty(void) const {
size_t length = getChipSize().dataLength;
auto sum = util::sum(data, length);
return (!sum || (sum == (0xff * length)));
}
size_t toQRString(char *output) const;
};
}

View File

@ -3,12 +3,151 @@
#include <stddef.h>
#include <stdint.h>
#include "asset.hpp"
#include "cart.hpp"
#include "cartdata.hpp"
#include "cartio.hpp"
namespace cart {
/* Utilities */
/* Cartridge data parsers */
bool Parser::validate(void) {
char region[8];
if (getRegion(region) < REGION_MIN_LENGTH)
return false;
if (!isValidRegion(region))
return false;
auto id = getIdentifiers();
if (id) {
// The system ID is excluded from validation as it is going to be
// missing if the game hasn't yet been installed.
uint8_t idFlags = flags & (
DATA_HAS_TRACE_ID | DATA_HAS_CART_ID | DATA_HAS_INSTALL_ID
);
if (id->getFlags() != idFlags)
return false;
}
return true;
}
size_t SimpleParser::getRegion(char *output) const {
auto header = _getHeader();
__builtin_memcpy(output, header->region, 4);
output[4] = 0;
return __builtin_strlen(output);
}
size_t BasicParser::getRegion(char *output) const {
auto header = _getHeader();
output[0] = header->region[0];
output[1] = header->region[1];
output[2] = 0;
return 2;
}
IdentifierSet *BasicParser::getIdentifiers(void) {
return reinterpret_cast<IdentifierSet *>(&_dump.data[sizeof(BasicHeader)]);
}
bool BasicParser::validate(void) {
return (Parser::validate() && _getHeader()->validateChecksum());
}
size_t ExtendedParser::getRegion(char *output) const {
auto header = _getHeader();
__builtin_memcpy(output, header->region, 4);
output[4] = 0;
return __builtin_strlen(output);
}
IdentifierSet *ExtendedParser::getIdentifiers(void) {
if (!(flags & DATA_HAS_PUBLIC_SECTION))
return nullptr;
return reinterpret_cast<IdentifierSet *>(
&_dump.data[sizeof(ExtendedHeader) + sizeof(PublicIdentifierSet)]
);
}
void ExtendedParser::flush(void) {
// Copy over the private identifiers to the public data area. On X76F041
// carts this area is in the last sector, while on ZS01 carts it is placed
// in the first 32 bytes.
auto pri = getIdentifiers();
auto pub = reinterpret_cast<PublicIdentifierSet *>(
&_getPublicData()[sizeof(ExtendedHeader)]
);
pub->traceID.copyFrom(pri->traceID.data);
pub->cartID.copyFrom(pri->cartID.data);
}
bool ExtendedParser::validate(void) {
return (Parser::validate() && _getHeader()->validateChecksum());
}
/* Data format identification */
struct KnownFormat {
public:
const char *name;
FormatType format;
uint8_t flags;
};
static constexpr int _NUM_KNOWN_FORMATS = 8;
static const KnownFormat _KNOWN_FORMATS[_NUM_KNOWN_FORMATS]{
{
// Used by GCB48 (and possibly other games?)
.name = "region string only",
.format = SIMPLE,
.flags = DATA_HAS_PUBLIC_SECTION
}, {
.name = "basic with no IDs",
.format = BASIC,
.flags = 0
}, {
.name = "basic with TID",
.format = BASIC,
.flags = DATA_HAS_TRACE_ID
}, {
.name = "basic with TID, SID",
.format = BASIC,
.flags = DATA_HAS_TRACE_ID | DATA_HAS_CART_ID
}, {
.name = "basic with code prefix, TID, SID",
.format = BASIC,
.flags = DATA_HAS_CODE_PREFIX | DATA_HAS_TRACE_ID | DATA_HAS_CART_ID
}, {
// Used by most pre-ZS01 Bemani games
.name = "basic with code prefix, all IDs",
.format = BASIC,
.flags = DATA_HAS_CODE_PREFIX | DATA_HAS_TRACE_ID | DATA_HAS_CART_ID
| DATA_HAS_INSTALL_ID | DATA_HAS_SYSTEM_ID
}, {
// Used by early (pre-digital-I/O) Bemani games
.name = "extended with no IDs",
.format = EXTENDED,
.flags = DATA_HAS_CODE_PREFIX
}, {
// Used by GE936/GK936 and all ZS01 Bemani games
.name = "extended with all IDs",
.format = EXTENDED,
.flags = DATA_HAS_CODE_PREFIX | DATA_HAS_TRACE_ID | DATA_HAS_CART_ID
| DATA_HAS_INSTALL_ID | DATA_HAS_SYSTEM_ID | DATA_HAS_PUBLIC_SECTION
}
};
bool isValidRegion(const char *region) {
// Character 0: region (A=Asia?, E=Europe, J=Japan, K=Korea, S=?, U=US)
@ -36,235 +175,38 @@ bool isValidRegion(const char *region) {
return true;
}
/* Common data structures */
void BasicHeader::updateChecksum(void) {
auto value = util::sum(reinterpret_cast<const uint8_t *>(this), 4);
checksum = uint8_t((value & 0xff) ^ 0xff);
}
bool BasicHeader::validateChecksum(void) const {
auto value = util::sum(reinterpret_cast<const uint8_t *>(this), 4);
return (checksum == ((value & 0xff) ^ 0xff));
}
void ExtendedHeader::updateChecksum(void) {
auto value = util::sum(reinterpret_cast<const uint16_t *>(this), 14);
checksum = uint16_t(value & 0xffff);
}
bool ExtendedHeader::validateChecksum(void) const {
auto value = util::sum(reinterpret_cast<const uint16_t *>(this), 14);
return (checksum == (value & 0xffff));
}
uint8_t IdentifierSet::getFlags(void) const {
uint8_t flags = 0;
if (!traceID.isEmpty())
flags |= DATA_HAS_TRACE_ID;
if (!cartID.isEmpty())
flags |= DATA_HAS_CART_ID;
if (!installID.isEmpty())
flags |= DATA_HAS_INSTALL_ID;
if (!systemID.isEmpty())
flags |= DATA_HAS_SYSTEM_ID;
return flags;
}
void IdentifierSet::setInstallID(uint8_t prefix) {
installID.clear();
installID.data[0] = prefix;
installID.updateChecksum();
}
void IdentifierSet::updateTraceID(uint8_t prefix, int param) {
traceID.clear();
uint8_t *input = &cartID.data[1];
uint16_t checksum = 0;
switch (prefix) {
case 0x81:
// TODO: reverse engineer this TID format
traceID.data[2] = 0;
traceID.data[5] = 0;
traceID.data[6] = 0;
LOG("prefix=0x81");
break;
case 0x82:
for (size_t i = 0; i < (sizeof(cartID.data) - 2); i++) {
uint8_t value = *(input++);
for (int j = 0; j < 8; j++, value >>= 1) {
if (value % 2)
checksum ^= 1 << (i % param);
}
}
traceID.data[1] = checksum >> 8;
traceID.data[2] = checksum & 0xff;
LOG("prefix=0x82, checksum=%04x", checksum);
break;
default:
LOG("unknown prefix 0x%02x", prefix);
}
traceID.data[0] = prefix;
traceID.updateChecksum();
}
/* Data formats */
bool CartData::validate(void) {
char region[8];
if (getRegion(region) < REGION_MIN_LENGTH)
return false;
if (!isValidRegion(region))
return false;
auto id = getIdentifiers();
if (id) {
uint8_t idFlags = flags & (
DATA_HAS_TRACE_ID | DATA_HAS_CART_ID | DATA_HAS_INSTALL_ID |
DATA_HAS_SYSTEM_ID
);
if (id->getFlags() != idFlags)
return false;
}
return true;
}
size_t SimpleCartData::getRegion(char *output) const {
auto header = _getHeader();
__builtin_memcpy(output, header->region, 4);
output[4] = 0;
return __builtin_strlen(output);
}
size_t BasicCartData::getRegion(char *output) const {
auto header = _getHeader();
output[0] = header->region[0];
output[1] = header->region[1];
output[2] = 0;
return 2;
}
IdentifierSet *BasicCartData::getIdentifiers(void) {
return reinterpret_cast<IdentifierSet *>(&_dump.data[sizeof(BasicHeader)]);
}
bool BasicCartData::validate(void) {
return (CartData::validate() && _getHeader()->validateChecksum());
}
size_t ExtendedCartData::getRegion(char *output) const {
auto header = _getHeader();
__builtin_memcpy(output, header->region, 4);
output[4] = 0;
return __builtin_strlen(output);
}
IdentifierSet *ExtendedCartData::getIdentifiers(void) {
if (!(flags & DATA_HAS_PUBLIC_SECTION))
return nullptr;
return reinterpret_cast<IdentifierSet *>(
&_dump.data[sizeof(ExtendedHeader) + sizeof(PublicIdentifierSet)]
);
}
void ExtendedCartData::flush(void) {
// Copy over the private identifiers to the public data area. On X76F041
// carts this area is in the last sector, while on ZS01 carts it is placed
// in the first 32 bytes.
auto pri = getIdentifiers();
auto pub = reinterpret_cast<PublicIdentifierSet *>(
&_getPublicData()[sizeof(ExtendedHeader)]
);
pub->traceID.copyFrom(pri->traceID.data);
pub->cartID.copyFrom(pri->cartID.data);
}
bool ExtendedCartData::validate(void) {
return (CartData::validate() && _getHeader()->validateChecksum());
}
/* Data format identification */
struct KnownFormat {
public:
FormatType formatType;
uint8_t flags;
};
static constexpr int _NUM_KNOWN_FORMATS = 8;
// More variants may exist.
static const KnownFormat _KNOWN_FORMATS[_NUM_KNOWN_FORMATS]{
{ SIMPLE, DATA_HAS_PUBLIC_SECTION },
{ BASIC, 0 },
{ BASIC, DATA_HAS_TRACE_ID },
{ BASIC, DATA_HAS_TRACE_ID | DATA_HAS_CART_ID },
{ BASIC, DATA_HAS_CODE_PREFIX | DATA_HAS_TRACE_ID | DATA_HAS_CART_ID },
{ BASIC, DATA_HAS_CODE_PREFIX | DATA_HAS_TRACE_ID | DATA_HAS_CART_ID
| DATA_HAS_INSTALL_ID | DATA_HAS_SYSTEM_ID },
{ EXTENDED, 0 },
{ EXTENDED, DATA_HAS_CODE_PREFIX | DATA_HAS_TRACE_ID | DATA_HAS_CART_ID
| DATA_HAS_INSTALL_ID | DATA_HAS_SYSTEM_ID | DATA_HAS_PUBLIC_SECTION },
};
CartData *createCartData(Dump &dump, FormatType formatType, uint8_t flags) {
Parser *newCartParser(Dump &dump, FormatType formatType, uint8_t flags) {
switch (formatType) {
case SIMPLE:
return new SimpleCartData(dump, flags);
return new SimpleParser(dump, flags);
case BASIC:
return new BasicCartData(dump, flags);
return new BasicParser(dump, flags);
case EXTENDED:
return new ExtendedCartData(dump, flags);
return new ExtendedParser(dump, flags);
default:
return new CartData(dump, flags);
return new Parser(dump, flags);
}
}
CartData *createCartData(Dump &dump) {
for (int i = 0; i < _NUM_KNOWN_FORMATS; i++) {
auto &fmt = _KNOWN_FORMATS[i];
CartData *data = createCartData(dump, fmt.formatType, fmt.flags);
Parser *newCartParser(Dump &dump) {
// Try all formats from the most complex one to the simplest.
for (int i = _NUM_KNOWN_FORMATS - 1; i >= 0; i++) {
auto &format = _KNOWN_FORMATS[i];
Parser *parser = newCartParser(dump, format.format, format.flags);
if (data->validate()) {
LOG("found known format, index=%d", i);
return data;
if (parser->validate()) {
LOG("found known data format");
LOG("%s, index=%d", format.name, i);
return parser;
}
delete data;
delete parser;
}
LOG("unrecognized dump format");
LOG("unrecognized data format");
return nullptr;
}

View File

@ -5,92 +5,17 @@
#include <stdint.h>
#include <stdio.h>
#include "asset.hpp"
#include "cartio.hpp"
#include "cart.hpp"
namespace cart {
/* Definitions */
enum FormatType : uint8_t {
BLANK = 0,
SIMPLE = 1,
BASIC = 2,
EXTENDED = 3
};
// | | Simple | Basic | Extended |
// | :---------------------- | :-------- | :------- | :-------- |
// | DATA_HAS_CODE_PREFIX | | Optional | Mandatory |
// | DATA_HAS_*_ID | | Optional | Optional |
// | DATA_HAS_PUBLIC_SECTION | Mandatory | | Optional |
enum FormatFlags : uint8_t {
DATA_HAS_CODE_PREFIX = 1 << 0,
DATA_HAS_TRACE_ID = 1 << 1,
DATA_HAS_CART_ID = 1 << 2,
DATA_HAS_INSTALL_ID = 1 << 3,
DATA_HAS_SYSTEM_ID = 1 << 4,
DATA_HAS_PUBLIC_SECTION = 1 << 5
};
/* Cartridge data parsers */
static constexpr size_t CODE_LENGTH = 5;
static constexpr size_t REGION_MIN_LENGTH = 2;
static constexpr size_t REGION_MAX_LENGTH = 5;
bool isValidRegion(const char *region);
/* Common data structures */
class [[gnu::packed]] SimpleHeader {
public:
char region[4];
};
class [[gnu::packed]] BasicHeader {
public:
char region[2], codePrefix[2];
uint8_t checksum, _pad[3];
void updateChecksum(void);
bool validateChecksum(void) const;
};
class [[gnu::packed]] ExtendedHeader {
public:
char code[8];
uint16_t year; // BCD, can be little endian, big endian or zero
char region[4];
uint16_t checksum;
void updateChecksum(void);
bool validateChecksum(void) const;
};
class [[gnu::packed]] IdentifierSet {
public:
Identifier traceID, cartID, installID, systemID; // aka TID, SID, MID, XID
inline void clear(void) {
__builtin_memset(this, 0, sizeof(IdentifierSet));
}
uint8_t getFlags(void) const;
void setInstallID(uint8_t prefix);
void updateTraceID(uint8_t prefix, int param);
};
class [[gnu::packed]] PublicIdentifierSet {
public:
Identifier traceID, cartID; // aka TID, SID
inline void clear(void) {
__builtin_memset(this, 0, sizeof(PublicIdentifierSet));
}
};
/* Data formats */
class CartData {
class Parser {
protected:
Dump &_dump;
@ -104,52 +29,53 @@ protected:
public:
uint8_t flags;
inline CartData(Dump &dump, uint8_t flags = 0)
inline Parser(Dump &dump, uint8_t flags = 0)
: _dump(dump), flags(flags) {}
virtual ~Parser(void) {}
virtual size_t getRegion(char *output) const { return 0; }
virtual IdentifierSet *getIdentifiers(void) { return nullptr; }
virtual void flush(void) {}
virtual bool validate(void);
};
class SimpleCartData : public CartData {
class SimpleParser : public Parser {
private:
inline SimpleHeader *_getHeader(void) const {
return reinterpret_cast<SimpleHeader *>(_getPublicData());
}
public:
inline SimpleCartData(Dump &dump, uint8_t flags = 0)
: CartData(dump, flags | DATA_HAS_PUBLIC_SECTION) {}
inline SimpleParser(Dump &dump, uint8_t flags = 0)
: Parser(dump, flags | DATA_HAS_PUBLIC_SECTION) {}
size_t getRegion(char *output) const;
};
class BasicCartData : public CartData {
class BasicParser : public Parser {
private:
inline BasicHeader *_getHeader(void) const {
return reinterpret_cast<BasicHeader *>(_getPublicData());
}
public:
inline BasicCartData(Dump &dump, uint8_t flags = 0)
: CartData(dump, flags) {}
inline BasicParser(Dump &dump, uint8_t flags = 0)
: Parser(dump, flags) {}
size_t getRegion(char *output) const;
IdentifierSet *getIdentifiers(void);
bool validate(void);
};
class ExtendedCartData : public CartData {
class ExtendedParser : public Parser {
private:
inline ExtendedHeader *_getHeader(void) const {
return reinterpret_cast<ExtendedHeader *>(_getPublicData());
}
public:
inline ExtendedCartData(Dump &dump, uint8_t flags = 0)
: CartData(dump, flags | DATA_HAS_CODE_PREFIX) {}
inline ExtendedParser(Dump &dump, uint8_t flags = 0)
: Parser(dump, flags | DATA_HAS_CODE_PREFIX) {}
size_t getRegion(char *output) const;
IdentifierSet *getIdentifiers(void);
@ -157,8 +83,9 @@ public:
bool validate(void);
};
CartData *createCartData(Dump &dump, FormatType formatType, uint8_t flags = 0);
CartData *createCartData(Dump &dump);
bool isValidRegion(const char *region);
Parser *newCartParser(Dump &dump, FormatType formatType, uint8_t flags = 0);
Parser *newCartParser(Dump &dump);
/* Cartridge database */
@ -168,9 +95,10 @@ public:
FormatType formatType;
uint8_t flags;
uint8_t traceIDPrefix, traceIDParam, installIDPrefix, _reserved[2];
uint8_t dataKey[8];
char code[8], region[8], name[64];
uint8_t traceIDPrefix, traceIDParam, installIDPrefix;
uint16_t year;
uint8_t dataKey[8];
char code[8], region[8], name[64];
inline int compare(const char *_code, const char *_region) const {
int diff = __builtin_strncmp(code, _code, CODE_LENGTH + 1);

View File

@ -1,90 +1,20 @@
#include <stddef.h>
#include <stdint.h>
#include "ps1/system.h"
#include "vendor/miniz.h"
#include "cart.hpp"
#include "cartio.hpp"
#include "io.hpp"
#include "util.hpp"
#include "zs01.hpp"
namespace cart {
/* Common data structures */
void Identifier::updateChecksum(void) {
data[7] = (util::sum(data, 7) & 0xff) ^ 0xff;
}
bool Identifier::validateChecksum(void) const {
uint8_t value = (util::sum(data, 7) & 0xff) ^ 0xff;
if (value != data[7]) {
LOG("checksum mismatch, exp=0x%02x, got=0x%02x", value, data[7]);
return false;
}
return true;
}
void Identifier::updateDSCRC(void) {
data[7] = util::dsCRC8(data, 7);
}
bool Identifier::validateDSCRC(void) const {
uint8_t value = util::dsCRC8(data, 7);
if (value != data[7]) {
LOG("CRC mismatch, exp=0x%02x, got=0x%02x", value, data[7]);
return false;
}
return true;
}
/* Dump structure and utilities */
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 }
};
size_t Dump::toQRString(char *output) const {
uint8_t compressed[MAX_QR_STRING_LENGTH];
size_t uncompLength = getDumpLength();
size_t compLength = MAX_QR_STRING_LENGTH;
int error = mz_compress2(
compressed, reinterpret_cast<mz_ulong *>(&compLength),
reinterpret_cast<const uint8_t *>(this), uncompLength,
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);
__builtin_memcpy(&output[0], "573::", 5);
__builtin_memcpy(&output[compLength + 5], "::", 3);
return compLength + 7;
}
/* 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) {
DriverError Driver::readSystemID(void) {
auto mask = setInterruptMask(0);
if (!io::dsDIOReset()) {
@ -110,7 +40,7 @@ CartError Cart::readSystemID(void) {
return NO_ERROR;
}
CartError X76Cart::readCartID(void) {
DriverError X76Driver::readCartID(void) {
auto mask = setInterruptMask(0);
if (!io::dsCartReset()) {
@ -136,7 +66,7 @@ CartError X76Cart::readCartID(void) {
return NO_ERROR;
}
CartError X76Cart::_x76Command(
DriverError X76Driver::_x76Command(
uint8_t cmd, uint8_t param, uint8_t pollByte
) const {
io::i2cStartWithCS();
@ -190,7 +120,7 @@ enum X76F041ConfigOp : uint8_t {
_X76F041_CFG_ERASE = 0x70
};
CartError X76F041Cart::readPrivateData(void) {
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) {
@ -216,7 +146,7 @@ CartError X76F041Cart::readPrivateData(void) {
return NO_ERROR;
}
CartError X76F041Cart::writeData(void) {
DriverError X76F041Driver::writeData(void) {
// Writes can only be done in 8-byte blocks.
for (int i = 0; i < 512; i += 8) {
auto error = _x76Command(
@ -236,7 +166,7 @@ CartError X76F041Cart::writeData(void) {
return NO_ERROR;
}
CartError X76F041Cart::erase(void) {
DriverError X76F041Driver::erase(void) {
auto error = _x76Command(
_X76F041_CONFIG, _X76F041_CFG_ERASE, _X76F041_ACK_POLL
);
@ -247,7 +177,7 @@ CartError X76F041Cart::erase(void) {
return NO_ERROR;
}
CartError X76F041Cart::setDataKey(const uint8_t *key) {
DriverError X76F041Driver::setDataKey(const uint8_t *key) {
auto error = _x76Command(
_X76F041_CONFIG, _X76F041_CFG_SET_DATA_KEY, _X76F041_ACK_POLL
);
@ -283,25 +213,27 @@ enum X76F100Command : uint8_t {
// TODO: actually implement this (even though no X76F100 carts were ever made)
CartError X76F100Cart::readPrivateData(void) {
DriverError X76F100Driver::readPrivateData(void) {
return UNSUPPORTED_OP;
}
CartError X76F100Cart::writeData(void) {
DriverError X76F100Driver::writeData(void) {
return UNSUPPORTED_OP;
}
CartError X76F100Cart::erase(void) {
DriverError X76F100Driver::erase(void) {
return UNSUPPORTED_OP;
}
CartError X76F100Cart::setDataKey(const uint8_t *key) {
DriverError X76F100Driver::setDataKey(const uint8_t *key) {
return UNSUPPORTED_OP;
}
/* ZS01 driver */
CartError ZS01Cart::_transact(zs01::Packet &request, zs01::Packet &response) {
DriverError ZS01Driver::_transact(
zs01::Packet &request, zs01::Packet &response
) {
io::i2cStart();
if (!io::i2cWriteBytes(
@ -328,9 +260,9 @@ CartError ZS01Cart::_transact(zs01::Packet &request, zs01::Packet &response) {
return NO_ERROR;
}
CartError ZS01Cart::readCartID(void) {
DriverError ZS01Driver::readCartID(void) {
zs01::Packet request, response;
CartError error;
DriverError error;
request.address = zs01::ADDR_ZS01_ID;
request.encodeReadRequest();
@ -360,14 +292,14 @@ CartError ZS01Cart::readCartID(void) {
return NO_ERROR;
}
CartError ZS01Cart::readPublicData(void) {
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();
CartError error = _transact(request, response);
DriverError error = _transact(request, response);
if (error)
return error;
@ -378,7 +310,7 @@ CartError ZS01Cart::readPublicData(void) {
return NO_ERROR;
}
CartError ZS01Cart::readPrivateData(void) {
DriverError ZS01Driver::readPrivateData(void) {
zs01::Packet request, response;
zs01::Key key;
@ -388,7 +320,7 @@ CartError ZS01Cart::readPrivateData(void) {
request.address = i;
request.encodeReadRequest(key, _encoderState);
CartError error = _transact(request, response);
DriverError error = _transact(request, response);
if (error)
return error;
@ -400,7 +332,7 @@ CartError ZS01Cart::readPrivateData(void) {
request.address = zs01::ADDR_CONFIG;
request.encodeReadRequest(key, _encoderState);
CartError error = _transact(request, response);
DriverError error = _transact(request, response);
if (error)
return error;
@ -410,7 +342,7 @@ CartError ZS01Cart::readPrivateData(void) {
return NO_ERROR;
}
CartError ZS01Cart::writeData(void) {
DriverError ZS01Driver::writeData(void) {
zs01::Packet request, response;
zs01::Key key;
@ -421,7 +353,7 @@ CartError ZS01Cart::writeData(void) {
request.copyFrom(&_dump.data[i * sizeof(request.data)]);
request.encodeWriteRequest(key, _encoderState);
CartError error = _transact(request, response);
DriverError error = _transact(request, response);
if (error)
return error;
}
@ -433,7 +365,7 @@ CartError ZS01Cart::writeData(void) {
return _transact(request, response);
}
CartError ZS01Cart::erase(void) {
DriverError ZS01Driver::erase(void) {
zs01::Packet request, response;
zs01::Key key;
@ -446,7 +378,7 @@ CartError ZS01Cart::erase(void) {
return _transact(request, response);
}
CartError ZS01Cart::setDataKey(const uint8_t *key) {
DriverError ZS01Driver::setDataKey(const uint8_t *key) {
zs01::Packet request, response;
zs01::Key newKey;
@ -456,7 +388,7 @@ CartError ZS01Cart::setDataKey(const uint8_t *key) {
request.copyFrom(key);
request.encodeWriteRequest(newKey, _encoderState);
CartError error = _transact(request, response);
DriverError error = _transact(request, response);
if (error)
return error;
@ -473,30 +405,30 @@ enum ChipIdentifier : uint32_t {
_ID_ZS01 = 0x5a530001
};
Cart *createCart(Dump &dump) {
Driver *newCartDriver(Dump &dump) {
if (!io::getCartInsertionStatus()) {
LOG("DSR not asserted");
return new Cart(dump);
return new Driver(dump);
}
uint32_t id1 = io::i2cResetZS01();
LOG("id1=0x%08x", id1);
LOG("detecting ZS01, id1=0x%08x", id1);
if (id1 == _ID_ZS01)
return new ZS01Cart(dump);
return new ZS01Driver(dump);
uint32_t id2 = io::i2cResetX76();
LOG("id2=0x%08x", id2);
LOG("detecting X76, id2=0x%08x", id2);
switch (id2) {
case _ID_X76F041:
return new X76F041Cart(dump);
return new X76F041Driver(dump);
//case _ID_X76F100:
//return new X76F100Cart(dump);
//return new X76F100Driver(dump);
default:
return new Cart(dump);
return new Driver(dump);
}
}

View File

@ -1,16 +1,13 @@
#pragma once
#include <stddef.h>
#include <stdint.h>
#include "util.hpp"
#include "zs01.hpp"
namespace cart {
/* Definitions */
enum CartError {
enum DriverError {
NO_ERROR = 0,
UNSUPPORTED_OP = 1,
DS2401_NO_RESP = 2,
@ -23,108 +20,14 @@ enum CartError {
ZS01_CRC_MISMATCH = 9
};
enum ChipType : uint8_t {
NONE = 0,
X76F041 = 1,
X76F100 = 2,
ZS01 = 3
};
enum DumpFlag : uint8_t {
DUMP_HAS_SYSTEM_ID = 1 << 0,
DUMP_HAS_CART_ID = 1 << 1,
DUMP_CONFIG_OK = 1 << 2,
DUMP_SYSTEM_ID_OK = 1 << 3,
DUMP_CART_ID_OK = 1 << 4,
DUMP_ZS_ID_OK = 1 << 5,
DUMP_PUBLIC_DATA_OK = 1 << 6,
DUMP_PRIVATE_DATA_OK = 1 << 7
};
static constexpr int NUM_CHIP_TYPES = 4;
static constexpr size_t MAX_QR_STRING_LENGTH = 0x600;
/* Common data structures */
class [[gnu::packed]] Identifier {
public:
uint8_t data[8];
inline void copyFrom(const uint8_t *source) {
__builtin_memcpy(data, source, sizeof(data));
}
inline void copyTo(uint8_t *dest) const {
__builtin_memcpy(dest, data, sizeof(data));
}
inline void clear(void) {
__builtin_memset(data, 0, sizeof(data));
}
inline bool isEmpty(void) const {
return (util::sum(data, sizeof(data)) == 0);
}
inline size_t toString(char *output) const {
return util::hexToString(output, data, sizeof(data), '-');
}
inline size_t toSerialNumber(char *output) const {
return util::serialNumberToString(output, &data[1]);
}
void updateChecksum(void);
bool validateChecksum(void) const;
void updateDSCRC(void);
bool validateDSCRC(void) const;
};
/* Dump structure and utilities */
struct ChipSize {
public:
size_t dataLength, publicDataOffset, publicDataLength;
};
extern const ChipSize CHIP_SIZES[NUM_CHIP_TYPES];
class [[gnu::packed]] Dump {
public:
ChipType chipType;
uint8_t flags;
Identifier systemID, cartID, zsID;
uint8_t dataKey[8], config[8];
uint8_t data[512];
inline const ChipSize &getChipSize(void) const {
return CHIP_SIZES[chipType];
}
inline size_t getDumpLength(void) const {
return (sizeof(Dump) - sizeof(data)) + getChipSize().dataLength;
}
inline void clear(void) {
__builtin_memset(this, 0, sizeof(Dump));
}
inline void clearData(void) {
__builtin_memset(data, 0, getChipSize().dataLength);
}
inline bool isDataEmpty(void) const {
size_t length = getChipSize().dataLength;
auto sum = util::sum(data, length);
return (!sum || (sum == (0xff * length)));
}
size_t toQRString(char *output) const;
};
/* Cartridge driver classes */
class Cart {
class Driver {
protected:
Dump &_dump;
public:
inline Cart(Dump &dump, ChipType chipType = NONE, uint8_t flags = 0)
inline Driver(Dump &dump, ChipType chipType = NONE, uint8_t flags = 0)
: _dump(dump) {
dump.clear();
@ -132,67 +35,68 @@ public:
dump.flags = flags;
}
virtual CartError readSystemID(void);
virtual CartError readCartID(void) { return UNSUPPORTED_OP; }
virtual CartError readPublicData(void) { return UNSUPPORTED_OP; }
virtual CartError readPrivateData(void) { return UNSUPPORTED_OP; }
virtual CartError writeData(void) { return UNSUPPORTED_OP; }
virtual CartError erase(void) { return UNSUPPORTED_OP; }
virtual CartError setDataKey(const uint8_t *key) { return UNSUPPORTED_OP; }
virtual ~Driver(void) {}
virtual DriverError readSystemID(void);
virtual DriverError readCartID(void) { return UNSUPPORTED_OP; }
virtual DriverError readPublicData(void) { return UNSUPPORTED_OP; }
virtual DriverError readPrivateData(void) { return UNSUPPORTED_OP; }
virtual DriverError writeData(void) { return UNSUPPORTED_OP; }
virtual DriverError erase(void) { return UNSUPPORTED_OP; }
virtual DriverError setDataKey(const uint8_t *key) { return UNSUPPORTED_OP; }
};
class [[gnu::packed]] X76Cart : public Cart {
class [[gnu::packed]] X76Driver : public Driver {
protected:
CartError _readDS2401(void);
CartError _x76Command(uint8_t cmd, uint8_t param, uint8_t pollByte) const;
DriverError _readDS2401(void);
DriverError _x76Command(uint8_t cmd, uint8_t param, uint8_t pollByte) const;
public:
inline X76Cart(Dump &dump, ChipType chipType)
: Cart(dump, chipType) {}
inline X76Driver(Dump &dump, ChipType chipType)
: Driver(dump, chipType) {}
CartError readCartID(void);
DriverError readCartID(void);
};
class [[gnu::packed]] X76F041Cart : public X76Cart {
class [[gnu::packed]] X76F041Driver : public X76Driver {
public:
inline X76F041Cart(Dump &dump)
: X76Cart(dump, X76F041) {}
inline X76F041Driver(Dump &dump)
: X76Driver(dump, X76F041) {}
CartError readPrivateData(void);
CartError writeData(void);
CartError erase(void);
CartError setDataKey(const uint8_t *key);
DriverError readPrivateData(void);
DriverError writeData(void);
DriverError erase(void);
DriverError setDataKey(const uint8_t *key);
};
class [[gnu::packed]] X76F100Cart : public X76Cart {
class [[gnu::packed]] X76F100Driver : public X76Driver {
public:
inline X76F100Cart(Dump &dump)
: X76Cart(dump, X76F100) {}
inline X76F100Driver(Dump &dump)
: X76Driver(dump, X76F100) {}
CartError readPrivateData(void);
CartError writeData(void);
CartError erase(void);
CartError setDataKey(const uint8_t *key);
DriverError readPrivateData(void);
DriverError writeData(void);
DriverError erase(void);
DriverError setDataKey(const uint8_t *key);
};
class [[gnu::packed]] ZS01Cart : public Cart {
class [[gnu::packed]] ZS01Driver : public Driver {
private:
uint8_t _encoderState;
CartError _transact(zs01::Packet &request, zs01::Packet &response);
DriverError _transact(zs01::Packet &request, zs01::Packet &response);
public:
inline ZS01Cart(Dump &dump)
: Cart(dump, ZS01, DUMP_HAS_CART_ID), _encoderState(0) {}
inline ZS01Driver(Dump &dump)
: Driver(dump, ZS01, DUMP_HAS_CART_ID), _encoderState(0) {}
CartError readCartID(void);
CartError readPublicData(void);
CartError readPrivateData(void);
CartError writeData(void);
CartError erase(void);
CartError setDataKey(const uint8_t *key);
DriverError readCartID(void);
DriverError readPublicData(void);
DriverError readPrivateData(void);
DriverError writeData(void);
DriverError erase(void);
DriverError setDataKey(const uint8_t *key);
};
Cart *createCart(Dump &dump);
Driver *newCartDriver(Dump &dump);
}

View File

@ -1,10 +1,12 @@
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
#include "ps1/gpucmd.h"
#include "ps1/registers.h"
#include "ps1/system.h"
#include "gpu.hpp"
#include "util.hpp"
namespace gpu {
@ -49,20 +51,6 @@ size_t upload(const RectWH &rect, const void *data, bool wait) {
/* Rendering context */
void Context::_flushLayer(void) {
if (_currentListPtr == _lastListPtr)
return;
auto layer = _drawBuffer().layers.pushItem();
assert(layer);
*(_currentListPtr++) = gp0_endTag(1);
*(_currentListPtr++) = gp0_irq();
*layer = _lastListPtr;
_lastListPtr = _currentListPtr;
}
void Context::_applyResolution(VideoMode mode, int shiftX, int shiftY) const {
GP1HorizontalRes hres;
GP1VerticalRes vres = (height > 256) ? GP1_VRES_512 : GP1_VRES_256;
@ -98,42 +86,17 @@ void Context::flip(void) {
auto &oldBuffer = _drawBuffer(), &newBuffer = _dispBuffer();
// Ensure the GPU has finished drawing the previous frame.
while (newBuffer.layers.length)
while (!isIdle())
__asm__ volatile("");
auto mask = setInterruptMask(0);
*_currentListPtr = gp0_endTag(0);
_currentListPtr = newBuffer.displayList;
_currentBuffer ^= 1;
_flushLayer();
_currentListPtr = newBuffer.displayList;
_lastListPtr = newBuffer.displayList;
_currentBuffer ^= 1;
GPU_GP1 = gp1_fbOffset(oldBuffer.clip.x1, oldBuffer.clip.y1);
// Kick off drawing.
drawNextLayer();
if (mask)
setInterruptMask(mask);
}
void Context::drawNextLayer(void) {
//auto mask = setInterruptMask(0);
auto layer = _dispBuffer().layers.popItem();
//if (mask)
//setInterruptMask(mask);
if (!layer)
return;
while (DMA_CHCR(DMA_GPU) & DMA_CHCR_ENABLE)
__asm__ volatile("");
while (!(GPU_GP1 & GP1_STAT_CMD_READY))
__asm__ volatile("");
GPU_GP1 = gp1_acknowledge();
GPU_GP1 = gp1_fbOffset(newBuffer.clip.x1, newBuffer.clip.y1);
GPU_GP1 = gp1_dmaRequestMode(GP1_DREQ_GP0_WRITE);
DMA_MADR(DMA_GPU) = reinterpret_cast<uint32_t>(*layer);
DMA_MADR(DMA_GPU) = reinterpret_cast<uint32_t>(oldBuffer.displayList);
DMA_CHCR(DMA_GPU) = DMA_CHCR_WRITE | DMA_CHCR_MODE_LIST | DMA_CHCR_ENABLE;
}
@ -156,7 +119,6 @@ void Context::setResolution(
}
_currentListPtr = _buffers[0].displayList;
_lastListPtr = _buffers[0].displayList;
_currentBuffer = 0;
_applyResolution(mode);
@ -175,13 +137,7 @@ uint32_t *Context::newPacket(size_t length) {
}
void Context::newLayer(int x, int y, int drawWidth, int drawHeight) {
auto mask = setInterruptMask(0);
_flushLayer();
if (mask)
setInterruptMask(mask);
auto &clip = _dispBuffer().clip;
auto &clip = _drawBuffer().clip;
x += clip.x1;
y += clip.y1;

View File

@ -5,7 +5,6 @@
#include <stdint.h>
#include "ps1/gpucmd.h"
#include "ps1/registers.h"
#include "util.hpp"
namespace gpu {
@ -41,6 +40,12 @@ static inline void init(void) {
TIMER_CTRL(1) = TIMER_CTRL_EXT_CLOCK;
}
static inline bool isIdle(void) {
return (
!(DMA_CHCR(DMA_GPU) & DMA_CHCR_ENABLE) && (GPU_GP1 & GP1_STAT_CMD_READY)
);
}
static inline void enableDisplay(bool enable) {
GPU_GP1 = gp1_dispBlank(!enable);
}
@ -56,14 +61,12 @@ struct Buffer {
public:
Rect clip;
uint32_t displayList[DISPLAY_LIST_SIZE];
volatile util::RingBuffer<uint32_t *volatile, LAYER_STACK_SIZE> layers;
};
class Context {
private:
Buffer _buffers[2];
uint32_t *_currentListPtr, *_lastListPtr;
uint32_t *_currentListPtr;
int _currentBuffer;
uint32_t _lastTexpage;
@ -75,7 +78,6 @@ private:
return _buffers[_currentBuffer ^ 1];
}
void _flushLayer(void);
void _applyResolution(VideoMode mode, int shiftX = 0, int shiftY = 0) const;
public:
@ -114,7 +116,6 @@ public:
void setResolution(
VideoMode mode, int width, int height, bool sideBySide = false
);
void drawNextLayer(void);
void flip(void);
uint32_t *newPacket(size_t length);

View File

@ -12,10 +12,11 @@ should start in PAL or NTSC mode by default). Requires no external dependencies.
__version__ = "0.1.0"
__author__ = "spicyjpeg"
from argparse import ArgumentParser, FileType, Namespace
from enum import IntEnum, IntFlag
from struct import Struct
from typing import BinaryIO, ByteString, Generator
from argparse import ArgumentParser, FileType, Namespace
from dataclasses import dataclass
from enum import IntEnum, IntFlag
from struct import Struct
from typing import BinaryIO, ByteString, Generator
## Utilities
@ -67,11 +68,11 @@ class ProgHeaderFlag(IntFlag):
WRITE = 1 << 1
READ = 1 << 2
@dataclass
class Segment:
def __init__(self, address: int, data: ByteString, flags: int):
self.address: int = address
self.data: ByteString = data
self.flags: ProgHeaderFlag = ProgHeaderFlag(flags)
address: int
data: bytes
flags: ProgHeaderFlag
def isReadOnly(self) -> bool:
return not \
@ -79,41 +80,33 @@ class Segment:
class ELF:
def __init__(self, _file: BinaryIO):
self.type: ELFType | None = None
self.architecture: ELFArchitecture | None = None
self.abi: int | None = None
self.entryPoint: int | None = None
self.flags: int | None = None
self.segments: list[Segment] = []
self._parse(_file)
def _parse(self, _file: BinaryIO):
# Parse the file header and perform some minimal validation.
_file.seek(0)
magic, wordSize, endianness, _, self.abi, _type, architecture, _, \
self.entryPoint, progHeaderOffset, secHeaderOffset, self.flags, \
elfHeaderLength, progHeaderLength, progHeaderCount, secHeaderLength, \
secHeaderCount, _ = \
magic, wordSize, endianness, _, abi, _type, architecture, _, \
entryPoint, progHeaderOffset, secHeaderOffset, flags, elfHeaderSize, \
progHeaderSize, progHeaderCount, secHeaderSize, secHeaderCount, _ = \
parseStructFromFile(_file, ELF_HEADER_STRUCT)
self.type = ELFType(_type)
self.architecture = ELFArchitecture(architecture)
self.type: ELFType = ELFType(_type)
self.architecture: int = architecture
self.abi: int = abi
self.entryPoint: int = entryPoint
self.flags: int = flags
if magic != ELF_HEADER_MAGIC:
raise RuntimeError("file is not a valid ELF")
if wordSize != 1 or endianness != ELFEndianness.LITTLE:
raise RuntimeError("ELF file must be 32-bit little-endian")
if (
elfHeaderLength != ELF_HEADER_STRUCT.size or
progHeaderLength != PROG_HEADER_STRUCT.size
elfHeaderSize != ELF_HEADER_STRUCT.size or
progHeaderSize != PROG_HEADER_STRUCT.size
):
raise RuntimeError("unsupported ELF format")
# Parse the program headers and extract all loadable segments.
self.segments: list[Segment] = []
_file.seek(progHeaderOffset)
for (