mirror of
https://github.com/spicyjpeg/573in1.git
synced 2025-01-22 19:52:05 +01:00
Add executable launchers and related build-time options
This commit is contained in:
parent
51de642c71
commit
324fa36f22
116
CMakeLists.txt
116
CMakeLists.txt
@ -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
|
||||
)
|
||||
|
@ -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 */
|
||||
|
||||
|
@ -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"
|
||||
}
|
||||
]
|
||||
|
@ -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())
|
||||
|
@ -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);
|
||||
};
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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
217
src/launcher/main.cpp
Normal 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;
|
||||
}
|
@ -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);
|
||||
|
@ -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);
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
};
|
||||
|
@ -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) {
|
||||
|
7
src/vendor/vendorconfig.h
vendored
7
src/vendor/vendorconfig.h
vendored
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user