Add executable launchers and related build-time options

This commit is contained in:
spicyjpeg 2023-12-31 20:42:59 +01:00
parent 51de642c71
commit 324fa36f22
No known key found for this signature in database
GPG Key ID: 5CC87404C01DF393
16 changed files with 545 additions and 125 deletions

View File

@ -26,16 +26,6 @@ find_program(
add_library(
common OBJECT
src/common/file.cpp
src/common/gpu.cpp
src/common/gpufont.cpp
src/common/ide.cpp
src/common/ideglue.cpp
src/common/io.cpp
src/common/pad.cpp
src/common/rom.cpp
src/common/spu.cpp
src/common/util.cpp
src/libc/crt0.c
src/libc/cxxsupport.cpp
src/libc/malloc.c
@ -47,31 +37,44 @@ add_library(
src/ps1/system.c
src/ps1/system.s
src/ps1/unhandledexc.c
src/vendor/ff.c
src/vendor/ffunicode.c
src/vendor/miniz.c
src/vendor/printf.c
src/vendor/qrcodegen.c
)
target_include_directories(
common PUBLIC
src
src/libc
)
target_compile_definitions(
common PUBLIC
VERSION="${PROJECT_VERSION}"
$<IF:$<CONFIG:Debug>,
ENABLE_LOGGING=1
,
ENABLE_LOGGING=1
>
)
target_compile_definitions(common PUBLIC VERSION="${PROJECT_VERSION}")
function(addExecutable name stackTop)
add_executable(${name} ${ARGN})
target_link_libraries(${name} PRIVATE common)
add_custom_command(
TARGET ${name} POST_BUILD
BYPRODUCTS ${name}.psexe
COMMAND
"${Python3_EXECUTABLE}"
"${PROJECT_SOURCE_DIR}/tools/convertExecutable.py"
-r "cart_tool build ${PROJECT_VERSION} - (C) 2022-2024 spicyjpeg"
-s 0x${stackTop} "$<TARGET_FILE:${name}>" ${name}.psexe
VERBATIM
)
endfunction()
## Main executable
add_executable(
main
addExecutable(
main 801dfff0
src/common/file.cpp
src/common/gpu.cpp
src/common/gpufont.cpp
src/common/ide.cpp
src/common/ideglue.cpp
src/common/io.cpp
src/common/pad.cpp
src/common/rom.cpp
src/common/spu.cpp
src/common/util.cpp
src/main/cart.cpp
src/main/cartdata.cpp
src/main/cartio.cpp
@ -85,19 +88,29 @@ add_executable(
src/main/app/cartunlock.cpp
src/main/app/main.cpp
src/main/app/misc.cpp
src/vendor/ff.c
src/vendor/ffunicode.c
src/vendor/miniz.c
src/vendor/printf.c
src/vendor/qrcodegen.c
)
target_compile_definitions(
main PRIVATE
VERSION="${PROJECT_VERSION}"
$<IF:$<CONFIG:Debug>,
ENABLE_LOGGING=1
ENABLE_FILE_WRITING=1
#ENABLE_ARGV=1
ENABLE_LOG_BUFFER=1
ENABLE_PS1_CONTROLLER=1
ENABLE_CART_MENU=1
#ENABLE_X76F100_DRIVER=1
ENABLE_DUMMY_DRIVER=1
ENABLE_I2C_LOGGING=1
,
ENABLE_LOGGING=1
ENABLE_FILE_WRITING=1
#ENABLE_ARGV=1
ENABLE_LOG_BUFFER=1
ENABLE_PS1_CONTROLLER=1
ENABLE_CART_MENU=1
#ENABLE_X76F100_DRIVER=1
@ -105,30 +118,49 @@ target_compile_definitions(
#ENABLE_I2C_LOGGING=1
>
)
target_link_libraries(main PRIVATE common)
add_custom_command(
TARGET main POST_BUILD
COMMAND
"${Python3_EXECUTABLE}"
"${PROJECT_SOURCE_DIR}/tools/convertExecutable.py"
-r "cart_tool build ${PROJECT_VERSION} - (C) 2022-2023 spicyjpeg"
-s 0x801dfff0
$<TARGET_FILE:main> main.psexe
BYPRODUCTS main.psexe
COMMENT "Converting executable"
VERBATIM
)
## Executable launchers
function(addLauncher address stackTop)
addExecutable(
launcher${address} ${stackTop}
src/common/ide.cpp
src/common/ideglue.cpp
src/common/io.cpp
src/common/util.cpp
src/launcher/main.cpp
src/vendor/ff.c
src/vendor/ffunicode.c
src/vendor/printf.c
)
target_compile_definitions(
launcher${address} PRIVATE
$<IF:$<CONFIG:Debug>,
ENABLE_LOGGING=1
#ENABLE_FILE_WRITING=1
,
#ENABLE_LOGGING=1
#ENABLE_FILE_WRITING=1
>
)
target_link_options(launcher${address} PRIVATE -Ttext=0x${address})
endfunction()
# Note that the launchers must be <48 KB (0xc000 bytes) in order for this to
# work properly.
addLauncher(801f4000 801ffff0)
addLauncher(803f4000 803ffff0)
## Default resource archive
configure_file(resources.json resources.json ESCAPE_QUOTES)
add_custom_command(
COMMAND
"${Python3_EXECUTABLE}"
"${PROJECT_SOURCE_DIR}/tools/buildResourceArchive.py"
"${PROJECT_SOURCE_DIR}/resources.json" resources.zip
resources.json resources.zip
OUTPUT resources.zip
DEPENDS resources.json
DEPENDS resources.json launcher801f4000 launcher803f4000
COMMENT "Building resource archive"
VERBATIM
)

View File

@ -26,40 +26,48 @@ SECTIONS {
/* Code sections */
.text : {
_textStart = .;
*(.text .text.* .gnu.linkonce.t.*)
*(.plt .MIPS.stubs)
} > APP_RAM
.rodata : {
*(.rodata .rodata.* .gnu.linkonce.r.*)
_textEnd = .;
} > APP_RAM
/* Global constructor/destructor arrays */
. = ALIGN((. != 0) ? 4 : 1);
_preinitArrayStart = .;
.preinit_array : {
KEEP(*(.preinit_array))
}
. = ALIGN((. != 0) ? 4 : 1);
_preinitArrayStart = .;
_preinitArrayEnd = .;
_initArrayStart = .;
KEEP(*(.preinit_array))
_preinitArrayEnd = .;
} > APP_RAM
.init_array : {
. = ALIGN((. != 0) ? 4 : 1);
_initArrayStart = .;
KEEP(*(SORT(.init_array.*) SORT(.ctors.*)))
KEEP(*(.init_array .ctors))
} > APP_RAM
_initArrayEnd = .;
_finiArrayStart = .;
_initArrayEnd = .;
} > APP_RAM
.fini_array : {
. = ALIGN((. != 0) ? 4 : 1);
_finiArrayStart = .;
KEEP(*(.fini_array .dtors))
KEEP(*(SORT(.fini_array.*) SORT(.dtors.*)))
} > APP_RAM
_finiArrayEnd = .;
_finiArrayEnd = .;
} > APP_RAM
/* Data sections */
@ -72,16 +80,16 @@ SECTIONS {
* so anything within .sdata and .sbss can be accessed using the $gp
* register as base plus a signed 16-bit immediate.
*/
_gp = ALIGN(16) + 0x7ff0;
.sdata : {
_gp = ALIGN(16) + 0x7ff0;
*(.sdata .sdata.* .gnu.linkonce.s.*)
} > APP_RAM
. = ALIGN((. != 0) ? 4 : 1);
_bssStart = .;
.sbss (NOLOAD) : {
. = ALIGN((. != 0) ? 4 : 1);
_bssStart = .;
*(.sbss .sbss.* .gnu.linkonce.sb.*)
*(.scommon)
} > APP_RAM
@ -89,10 +97,10 @@ SECTIONS {
.bss (NOLOAD) : {
*(.bss .bss.* .gnu.linkonce.b.*)
*(COMMON)
} > APP_RAM
. = ALIGN((. != 0) ? 4 : 1);
_bssEnd = .;
. = ALIGN((. != 0) ? 4 : 1);
_bssEnd = .;
} > APP_RAM
/* Dummy sections */

View File

@ -2,7 +2,7 @@
{
"type": "tim",
"name": "assets/textures/background.tim",
"source": "assets/textures/background.png",
"source": "${PROJECT_SOURCE_DIR}/assets/textures/background.png",
"quantize": 16,
"imagePos": { "x": 960, "y": 0 },
"clutPos": { "x": 1008, "y": 0 }
@ -10,7 +10,7 @@
{
"type": "tim",
"name": "assets/textures/font.tim",
"source": "assets/textures/font.png",
"source": "${PROJECT_SOURCE_DIR}/assets/textures/font.png",
"quantize": 16,
"imagePos": { "x": 984, "y": 0 },
"clutPos": { "x": 1008, "y": 1 }
@ -18,63 +18,63 @@
{
"type": "metrics",
"name": "assets/textures/font.metrics",
"source": "assets/textures/font.metrics.json"
"source": "${PROJECT_SOURCE_DIR}/assets/textures/font.metrics.json"
},
{
"type": "binary",
"name": "assets/sounds/alert.vag",
"source": "assets/sounds/alert.vag",
"source": "${PROJECT_SOURCE_DIR}/assets/sounds/alert.vag",
"compress": null
},
{
"type": "binary",
"name": "assets/sounds/move.vag",
"source": "assets/sounds/move.vag",
"source": "${PROJECT_SOURCE_DIR}/assets/sounds/move.vag",
"compress": null
},
{
"type": "binary",
"name": "assets/sounds/enter.vag",
"source": "assets/sounds/enter.vag",
"source": "${PROJECT_SOURCE_DIR}/assets/sounds/enter.vag",
"compress": null
},
{
"type": "binary",
"name": "assets/sounds/exit.vag",
"source": "assets/sounds/exit.vag",
"source": "${PROJECT_SOURCE_DIR}/assets/sounds/exit.vag",
"compress": null
},
{
"type": "binary",
"name": "assets/sounds/click.vag",
"source": "assets/sounds/click.vag",
"source": "${PROJECT_SOURCE_DIR}/assets/sounds/click.vag",
"compress": null
},
{
"type": "palette",
"name": "assets/app.palette",
"source": "assets/app.palette.json"
"source": "${PROJECT_SOURCE_DIR}/assets/app.palette.json"
},
{
"type": "strings",
"name": "assets/app.strings",
"source": "assets/app.strings.json"
"source": "${PROJECT_SOURCE_DIR}/assets/app.strings.json"
},
{
"type": "text",
"name": "assets/about.txt",
"source": "assets/about.txt"
"source": "${PROJECT_SOURCE_DIR}/assets/about.txt"
},
{
"type": "binary",
"name": "data/fpga.bit",
"source": "data/fpga.bit"
"source": "${PROJECT_SOURCE_DIR}/data/fpga.bit"
},
{
"type": "binary",
"name": "data/x76f041.cartdb",
"source": "data/x76f041.cartdb"
"source": "${PROJECT_SOURCE_DIR}/data/x76f041.cartdb"
},
{
"type": "empty",
@ -83,6 +83,17 @@
{
"type": "binary",
"name": "data/zs01.cartdb",
"source": "data/zs01.cartdb"
"source": "${PROJECT_SOURCE_DIR}/data/zs01.cartdb"
},
{
"type": "binary",
"name": "launchers/801f4000.psexe",
"source": "launcher801f4000.psexe"
},
{
"type": "binary",
"name": "launchers/803f4000.psexe",
"source": "launcher803f4000.psexe"
}
]

View File

@ -85,6 +85,7 @@ size_t HostFile::read(void *output, size_t length) {
return size_t(actualLength);
}
#ifdef ENABLE_FILE_WRITING
size_t HostFile::write(const void *input, size_t length) {
int actualLength = pcdrvWrite(_fd, input, length);
@ -95,6 +96,7 @@ size_t HostFile::write(const void *input, size_t length) {
return size_t(actualLength);
}
#endif
uint64_t HostFile::seek(uint64_t offset) {
int actualOffset = pcdrvSeek(_fd, int(offset), PCDRV_SEEK_SET);
@ -134,6 +136,7 @@ size_t FATFile::read(void *output, size_t length) {
return uint64_t(actualLength);
}
#ifdef ENABLE_FILE_WRITING
size_t FATFile::write(const void *input, size_t length) {
size_t actualLength;
auto error = f_write(&_fd, input, length, &actualLength);
@ -145,6 +148,7 @@ size_t FATFile::write(const void *input, size_t length) {
return uint64_t(actualLength);
}
#endif
uint64_t FATFile::seek(uint64_t offset) {
auto error = f_lseek(&_fd, offset);
@ -240,6 +244,7 @@ size_t Provider::loadData(void *output, size_t length, const char *path) {
}
size_t Provider::saveData(const void *input, size_t length, const char *path) {
#ifdef ENABLE_FILE_WRITING
auto file = openFile(path, WRITE | ALLOW_CREATE);
if (!file)
@ -250,6 +255,9 @@ size_t Provider::saveData(const void *input, size_t length, const char *path) {
delete file;
return actualLength;
#else
return 0;
#endif
}
size_t Provider::loadTIM(gpu::Image &output, const char *path) {
@ -319,6 +327,7 @@ FileSystemType HostProvider::getFileSystemType(void) {
return HOST;
}
#ifdef ENABLE_FILE_WRITING
bool HostProvider::createDirectory(const char *path) {
int fd = pcdrvCreate(path, DIRECTORY);
@ -330,6 +339,7 @@ bool HostProvider::createDirectory(const char *path) {
pcdrvClose(fd);
return true;
}
#endif
File *HostProvider::openFile(const char *path, uint32_t flags) {
PCDRVOpenMode mode = PCDRV_MODE_READ;
@ -393,6 +403,7 @@ uint64_t FATProvider::getCapacity(void) {
return uint64_t(_fs.n_fatent - 2) * uint64_t(clusterSize);
}
#ifdef ENABLE_FILE_WRITING
uint64_t FATProvider::getFreeSpace(void) {
if (!_fs.fs_type)
return 0;
@ -410,6 +421,7 @@ uint64_t FATProvider::getFreeSpace(void) {
return uint64_t(count) * uint64_t(clusterSize);
}
#endif
size_t FATProvider::getVolumeLabel(char *output, size_t length) {
//assert(length >= 23);
@ -487,6 +499,7 @@ Directory *FATProvider::openDirectory(const char *path) {
return dir;
}
#ifdef ENABLE_FILE_WRITING
bool FATProvider::createDirectory(const char *path) {
if (!_selectDrive())
return false;
@ -500,6 +513,7 @@ bool FATProvider::createDirectory(const char *path) {
return true;
}
#endif
File *FATProvider::openFile(const char *path, uint32_t flags) {
if (!_selectDrive())

View File

@ -72,7 +72,9 @@ private:
public:
size_t read(void *output, size_t length);
#ifdef ENABLE_FILE_WRITING
size_t write(const void *input, size_t length);
#endif
uint64_t seek(uint64_t offset);
uint64_t tell(void) const;
void close(void);
@ -86,7 +88,9 @@ private:
public:
size_t read(void *output, size_t length);
#ifdef ENABLE_FILE_WRITING
size_t write(const void *input, size_t length);
#endif
uint64_t seek(uint64_t offset);
uint64_t tell(void) const;
void close(void);
@ -155,7 +159,9 @@ public:
FileSystemType getFileSystemType(void);
#ifdef ENABLE_FILE_WRITING
bool createDirectory(const char *path);
#endif
File *openFile(const char *path, uint32_t flags);
};
@ -178,13 +184,17 @@ public:
FileSystemType getFileSystemType(void);
uint64_t getCapacity(void);
#ifdef ENABLE_FILE_WRITING
uint64_t getFreeSpace(void);
#endif
size_t getVolumeLabel(char *output, size_t length);
uint32_t getSerialNumber(void);
bool getFileInfo(FileInfo &output, const char *path);
Directory *openDirectory(const char *path);
#ifdef ENABLE_FILE_WRITING
bool createDirectory(const char *path);
#endif
File *openFile(const char *path, uint32_t flags);
};

View File

@ -153,8 +153,9 @@ bool FlashRegion::hasBootExecutable(void) const {
// The integrity of the executable is verified by calculating the CRC32 of
// its bytes whose offsets are powers of 2 (i.e. the bytes at indices 0, 1,
// 2, 4, 8 and so on). Note that the actual size of the executable is
// header.textLength + 2048, as the CRC is also calculated on the header,
// but Konami's shell ignores the last 2048 bytes due to a bug.
// header.textLength + util::EXECUTABLE_BODY_OFFSET, as the CRC is also
// calculated on the header, but Konami's shell ignores the last 2048 bytes
// due to a bug.
size_t length = header.textLength;
uint32_t crc = ~0;

View File

@ -31,35 +31,64 @@ Hash hash(const uint8_t *data, size_t length) {
return value;
}
/* Logger */
/* Logging framework */
// Global state, I know, but it's a necessary evil.
Logger logger;
Logger::Logger(void)
: _tail(0), enableSyslog(false) {
clear();
}
void Logger::clear(void) {
void LogBuffer::clear(void) {
for (auto line : _lines)
line[0] = 0;
}
void Logger::log(const char *format, ...) {
char *LogBuffer::allocateLine(void) {
size_t tail = _tail;
_tail = (tail + 1) % MAX_LOG_LINES;
return _lines[tail];
}
void Logger::setLogBuffer(LogBuffer *buffer) {
auto enable = disableInterrupts();
_buffer = buffer;
if (enable)
enableInterrupts();
}
void Logger::setupSyslog(int baudRate) {
auto enable = disableInterrupts();
size_t tail = _tail;
if (baudRate) {
initSerialIO(baudRate);
_enableSyslog = true;
} else {
_enableSyslog = false;
}
if (enable)
enableInterrupts();
}
void Logger::log(const char *format, ...) {
auto enable = disableInterrupts();
va_list ap;
_tail = size_t(tail + 1) % MAX_LOG_LINES;
if (_buffer) {
auto line = _buffer->allocateLine();
va_start(ap, format);
vsnprintf(_lines[tail], MAX_LOG_LINE_LENGTH, format, ap);
va_end(ap);
va_start(ap, format);
vsnprintf(line, MAX_LOG_LINE_LENGTH, format, ap);
va_end(ap);
if (_enableSyslog)
puts(line);
} else if (_enableSyslog) {
va_start(ap, format);
vprintf(format, ap);
va_end(ap);
}
if (enableSyslog)
puts(_lines[tail]);
if (enable)
enableInterrupts();
}
@ -219,10 +248,59 @@ size_t encodeBase41(char *output, const uint8_t *input, size_t length) {
return outLength;
}
/* PS1 executable header */
/* PS1 executable loader */
bool ExecutableHeader::validateMagic(void) const {
return (hash(magic, sizeof(magic)) == "PS-X EXE"_h);
}
ExecutableLoader::ExecutableLoader(
const ExecutableHeader &header, void *defaultStackTop
) : _header(header), _argCount(0) {
uintptr_t stackTop = header.stackOffset + header.stackLength;
if (!stackTop)
stackTop = reinterpret_cast<uintptr_t>(defaultStackTop);
_argListPtr = reinterpret_cast<char **>(stackTop & ~7)
- MAX_EXECUTABLE_ARGS;
_currentStackPtr = reinterpret_cast<char *>(_argListPtr);
}
void ExecutableLoader::addArgument(const char *arg) {
// Command-line arguments must be copied to the top of the new stack in
// order to ensure the executable is going to be able to access them at any
// time.
size_t length = __builtin_strlen(arg) + 1;
size_t aligned = (length + 7) & ~7;
_currentStackPtr -= aligned;
_argListPtr[_argCount++] = _currentStackPtr;
__builtin_memcpy(_currentStackPtr, arg, length);
//assert(_argCount <= MAX_EXECUTABLE_ARGS);
}
[[noreturn]] void ExecutableLoader::run(void) {
disableInterrupts();
register int a0 __asm__("a0") = _argCount;
register char **a1 __asm__("a1") = _argListPtr;
register uintptr_t gp __asm__("gp") = _header.initialGP;
// Changing the stack pointer and return address is not something that
// should be done in a C++ function, but hopefully it's fine here since
// we're jumping out right after setting it.
__asm__ volatile(
".set noreorder\n"
"li $ra, %0\n"
"jr %1\n"
"addiu $sp, %2, -8\n"
".set reorder\n"
:: "i"(DEV2_BASE), "r"(_header.entryPoint), "r"(_currentStackPtr),
"r"(a0), "r"(a1), "r"(gp)
);
__builtin_unreachable();
}
}

View File

@ -221,34 +221,53 @@ public:
}
};
/* Logger (basically a ring buffer of lines) */
/* Logging framework */
static constexpr int MAX_LOG_LINE_LENGTH = 128;
static constexpr int MAX_LOG_LINES = 64;
class Logger {
class LogBuffer {
private:
char _lines[MAX_LOG_LINES][MAX_LOG_LINE_LENGTH];
int _tail;
public:
bool enableSyslog;
inline LogBuffer(void)
: _tail(0) {
clear();
}
// 0 = last line, 1 = second to last, etc.
inline const char *getLine(int line) const {
return _lines[size_t(_tail - (line + 1)) % MAX_LOG_LINES];
}
Logger(void);
void clear(void);
char *allocateLine(void);
};
class Logger {
private:
LogBuffer *_buffer;
bool _enableSyslog;
public:
inline Logger(void)
: _buffer(nullptr), _enableSyslog(false) {}
void setLogBuffer(LogBuffer *buffer);
void setupSyslog(int baudRate);
void log(const char *format, ...);
};
extern Logger logger;
/* PS1 executable header */
/* PS1 executable loader */
struct [[gnu::packed]] ExecutableHeader {
static constexpr size_t EXECUTABLE_BODY_OFFSET = 2048;
static constexpr size_t MAX_EXECUTABLE_ARGS = 32;
class [[gnu::packed]] ExecutableHeader {
public:
uint8_t magic[8], _pad[8];
@ -262,6 +281,20 @@ public:
bool validateMagic(void) const;
};
class ExecutableLoader {
private:
const ExecutableHeader &_header;
int _argCount;
char **_argListPtr;
char *_currentStackPtr;
public:
ExecutableLoader(const ExecutableHeader &header, void *defaultStackTop);
void addArgument(const char *arg);
[[noreturn]] void run(void);
};
/* Other APIs */
uint8_t dsCRC8(const uint8_t *data, size_t length);

217
src/launcher/main.cpp Normal file
View File

@ -0,0 +1,217 @@
#include <stdio.h>
#include <stdlib.h>
#include "common/ide.hpp"
#include "common/io.hpp"
#include "common/util.hpp"
#include "ps1/system.h"
#include "vendor/ff.h"
extern "C" uint8_t _textStart[];
class Settings {
public:
int baudRate, argCount;
const char *drive, *path;
const char *args[util::MAX_EXECUTABLE_ARGS];
inline Settings(void)
: baudRate(0), argCount(0), drive(nullptr), path(nullptr) {}
bool parse(const char *arg);
};
bool Settings::parse(const char *arg) {
if (!arg)
return false;
switch (util::hash(arg, '=')) {
case "boot.rom"_h:
//LOG("boot.rom=%s", &arg[9]);
return true;
case "boot.from"_h:
//LOG("boot.from=%s", &arg[10]);
return true;
case "console"_h:
baudRate = int(strtol(&arg[8], nullptr, 0));
return true;
case "launcher.drive"_h:
drive = &arg[15];
return true;
case "launcher.path"_h:
path = &arg[14];
return true;
case "launcher.arg"_h:
if (argCount >= int(util::countOf(args)))
return false;
args[argCount++] = &arg[13];
return true;
default:
return false;
}
}
class Launcher {
private:
Settings &_settings;
// Using the FatFs API directly (rather than through file::FATProvider)
// yields a smaller executable as it avoids pulling in malloc.
FATFS _fs;
FIL _file;
util::ExecutableHeader _header;
public:
inline Launcher(Settings &settings)
: _settings(settings) {
_fs.fs_type = 0;
_file.obj.fs = nullptr;
}
inline ~Launcher(void) {
exit();
}
bool openFile(void);
bool readHeader(void);
bool readBody(void);
void exit(void);
[[noreturn]] void run(void);
};
bool Launcher::openFile(void) {
if (!_settings.drive || !_settings.path) {
LOG("required arguments missing");
return false;
}
// As long as it works...
int drive = _settings.drive[0] - '0';
if (drive < 0 || drive > 1) {
LOG("invalid drive ID");
return false;
}
if (ide::devices[drive].enumerate()) {
LOG("IDE init failed, drive=%s", _settings.drive);
return false;
}
if (f_mount(&_fs, _settings.drive, 1)) {
LOG("FAT mount failed, drive=%s", _settings.drive);
return false;
}
f_chdrive(_settings.drive);
if (f_open(&_file, _settings.path, FA_READ)) {
LOG("open failed, path=%s", _settings.path);
return false;
}
return true;
}
bool Launcher::readHeader(void) {
size_t length;
if (f_read(&_file, &_header, sizeof(_header), &length)) {
LOG("header read failed, path=%s", _settings.path);
return false;
}
if (length != sizeof(_header)) {
LOG("invalid header length %d", length);
return false;
}
auto ptr = reinterpret_cast<void *>(_header.textOffset);
if (ptr >= _textStart) {
LOG("executable overlaps launcher");
return false;
}
LOG("ptr=0x%08x, length=0x%08x", ptr, _header.textLength);
return true;
}
bool Launcher::readBody(void) {
auto ptr = reinterpret_cast<void *>(_header.textOffset);
size_t length;
if (f_lseek(&_file, util::EXECUTABLE_BODY_OFFSET)) {
LOG("seek to body failed, path=%s", _settings.path);
return false;
}
if (f_read(&_file, ptr, _header.textLength, &length)) {
LOG("body read failed, path=%s", _settings.path);
return false;
}
if (length != _header.textLength) {
LOG("invalid body length %d", length);
return false;
}
return true;
}
void Launcher::exit(void) {
if (_file.obj.fs)
f_close(&_file);
if (_fs.fs_type)
f_unmount(_settings.drive);
uninstallExceptionHandler();
}
[[noreturn]] void Launcher::run(void) {
util::ExecutableLoader loader(_header, _textStart - 16);
for (int i = 0; i < _settings.argCount; i++)
loader.addArgument(_settings.args[i]);
exit();
loader.run();
}
int main(int argc, const char **argv) {
installExceptionHandler();
io::init();
setInterruptHandler([](void *dummy) {
if (acknowledgeInterrupt(IRQ_VSYNC))
io::clearWatchdog();
}, nullptr);
IRQ_MASK = 1 << IRQ_VSYNC;
enableInterrupts();
Settings settings;
Launcher launcher(settings);
#ifndef NDEBUG
// Enable serial port logging by default in debug builds.
settings.baudRate = 115200;
#endif
for (; argc > 0; argc--)
settings.parse(*(argv++));
util::logger.setupSyslog(settings.baudRate);
if (!launcher.openFile())
return 1;
if (!launcher.readHeader())
return 2;
if (!launcher.readBody())
return 3;
launcher.run();
return 0;
}

View File

@ -58,6 +58,17 @@ void WorkerStatus::finish(void) {
/* App class */
App::App(ui::Context &ctx, file::ZIPProvider &resourceProvider)
#ifdef ENABLE_LOG_BUFFER
: _overlayLayer(_logBuffer),
#else
:
#endif
_ctx(ctx), _resourceProvider(resourceProvider), _resourceFile(nullptr),
_driver(nullptr), _parser(nullptr), _identified(nullptr) {
_workerStack = new uint8_t[WORKER_STACK_SIZE];
}
App::~App(void) {
_unloadCartData();
@ -157,7 +168,12 @@ void App::_interruptHandler(void) {
}
[[noreturn]] void App::run(void) {
LOG("starting app @ 0x%08x", this);
#ifdef ENABLE_LOG_BUFFER
util::logger.setLogBuffer(&_logBuffer);
#endif
LOG("build " VERSION_STRING " (" __DATE__ " " __TIME__ ")");
LOG("(C) 2022-2024 spicyjpeg");
_ctx.screenData = this;
_setupWorker(&App::_startupWorker);
@ -166,7 +182,7 @@ void App::_interruptHandler(void) {
_backgroundLayer.text = "v" VERSION_STRING;
_ctx.background = &_backgroundLayer;
#ifdef ENABLE_LOGGING
#ifdef ENABLE_LOG_BUFFER
_ctx.overlay = &_overlayLayer;
#endif
_ctx.show(_workerStatusScreen);

View File

@ -131,8 +131,11 @@ private:
ReflashGameScreen _reflashGameScreen;
SystemIDEntryScreen _systemIDEntryScreen;
ui::TiledBackground _backgroundLayer;
#ifdef ENABLE_LOG_BUFFER
util::LogBuffer _logBuffer;
ui::LogOverlay _overlayLayer;
#endif
ui::TiledBackground _backgroundLayer;
ui::Context &_ctx;
file::ZIPProvider &_resourceProvider;
@ -174,13 +177,8 @@ private:
void _interruptHandler(void);
public:
inline App(ui::Context &ctx, file::ZIPProvider &resourceProvider)
: _overlayLayer(util::logger), _ctx(ctx),
_resourceProvider(resourceProvider), _resourceFile(nullptr),
_driver(nullptr), _parser(nullptr), _identified(nullptr) {
_workerStack = new uint8_t[WORKER_STACK_SIZE];
}
App(ui::Context &ctx, file::ZIPProvider &resourceProvider);
~App(void);
[[noreturn]] void run(void);
};

View File

@ -12,7 +12,6 @@
#include "ps1/gpucmd.h"
#include "ps1/system.h"
extern "C" const uint8_t _resources[];
extern "C" const size_t _resourcesSize;
@ -90,18 +89,13 @@ int main(int argc, const char **argv) {
// Enable serial port logging by default in debug builds.
settings.baudRate = 115200;
#endif
#ifdef ENABLE_ARGV
for (; argc > 0; argc--)
settings.parse(*(argv++));
#endif
if (settings.baudRate) {
initSerialIO(settings.baudRate);
util::logger.enableSyslog = true;
}
LOG("build " VERSION_STRING " (" __DATE__ " " __TIME__ ")");
LOG("(C) 2022-2023 spicyjpeg");
util::logger.setupSyslog(settings.baudRate);
// Load the resource archive, first from memory if a pointer was given and
// then from the HDD. If both attempts fail, fall back to the archive

View File

@ -227,8 +227,8 @@ void TiledBackground::draw(Context &ctx) const {
ctx.font.draw(ctx.gpuCtx, text, rect, ctx.colors[COLOR_TEXT2]);
}
LogOverlay::LogOverlay(util::Logger &logger)
: _logger(logger) {
LogOverlay::LogOverlay(util::LogBuffer &buffer)
: _buffer(buffer) {
_slideAnim.setValue(0);
}
@ -255,7 +255,7 @@ void LogOverlay::draw(Context &ctx) const {
for (int i = linesShown - 1; i >= 0; i--) {
ctx.font.draw(
ctx.gpuCtx, _logger.getLine(i), rect, ctx.colors[COLOR_TEXT1]
ctx.gpuCtx, _buffer.getLine(i), rect, ctx.colors[COLOR_TEXT1]
);
rect.y1 = rect.y2;

View File

@ -193,11 +193,11 @@ public:
class LogOverlay : public Layer {
private:
util::Logger &_logger;
util::LogBuffer &_buffer;
util::Tween<int, util::QuadOutEasing> _slideAnim;
public:
LogOverlay(util::Logger &logger);
LogOverlay(util::LogBuffer &buffer);
void draw(Context &ctx) const;
void update(Context &ctx);
};

View File

@ -51,8 +51,8 @@ void installExceptionHandler(void) {
DMA_DPCR = 0;
DMA_DICR = DMA_DICR_CH_STAT_BITMASK;
// Ensure interrupts and the GTE are enabled at the COP0 side.
cop0_setSR(COP0_SR_IEc | COP0_SR_Im2 | COP0_SR_CU0 | COP0_SR_CU2);
// Disable interrupts and the GTE at the COP0 side.
cop0_setSR(COP0_SR_CU0);
// Grab a direct pointer to the BIOS function to flush the instruction
// cache. This is the only function that must always run from the BIOS ROM
@ -69,6 +69,9 @@ void installExceptionHandler(void) {
DMA_DPCR = 0x0bbbbbbb;
DMA_DICR = DMA_DICR_IRQ_ENABLE;
// Ensure interrupts and the GTE are enabled at the COP0 side.
cop0_setSR(COP0_SR_IEc | COP0_SR_Im2 | COP0_SR_CU0 | COP0_SR_CU2);
}
void uninstallExceptionHandler(void) {

View File

@ -3,7 +3,12 @@
/* FatFs configuration */
#define FF_FS_READONLY 0
#ifdef ENABLE_FILE_WRITING
#define FF_FS_READONLY 0
#else
#define FF_FS_READONLY 1
#endif
#define FF_FS_MINIMIZE 0
#define FF_USE_FIND 0
#define FF_USE_MKFS 0