From 31fba94b8e8c15c1d127925f01a38efc68933fd1 Mon Sep 17 00:00:00 2001
From: spicyjpeg <88942473+spicyjpeg@users.noreply.github.com>
Date: Mon, 29 Nov 2021 00:17:28 +0100
Subject: Add io/system573 example, rewrite dev notes, fix CI
---
examples/io/system573/CMakeLists.txt | 27 +++
examples/io/system573/iso.xml | 34 ++++
examples/io/system573/main.c | 371 +++++++++++++++++++++++++++++++++++
3 files changed, 432 insertions(+)
create mode 100644 examples/io/system573/CMakeLists.txt
create mode 100644 examples/io/system573/iso.xml
create mode 100644 examples/io/system573/main.c
(limited to 'examples')
diff --git a/examples/io/system573/CMakeLists.txt b/examples/io/system573/CMakeLists.txt
new file mode 100644
index 0000000..34c9153
--- /dev/null
+++ b/examples/io/system573/CMakeLists.txt
@@ -0,0 +1,27 @@
+# PSn00bSDK example CMake script
+# (C) 2021 spicyjpeg - MPL licensed
+
+cmake_minimum_required(VERSION 3.21)
+
+if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{PSN00BSDK_LIBS})
+ set(CMAKE_TOOLCHAIN_FILE $ENV{PSN00BSDK_LIBS}/cmake/sdk.cmake)
+endif()
+
+project(
+ system573
+ LANGUAGES C ASM
+ VERSION 1.0.0
+ DESCRIPTION "PSn00bSDK Konami System 573 example"
+ HOMEPAGE_URL "http://lameguy64.net/?page=psn00bsdk"
+)
+
+file(GLOB _sources *.c *.s)
+psn00bsdk_add_executable(system573 STATIC ${_sources})
+psn00bsdk_add_cd_image(system573_iso system573 iso.xml DEPENDS system573)
+
+install(
+ FILES
+ ${PROJECT_BINARY_DIR}/system573.bin
+ ${PROJECT_BINARY_DIR}/system573.cue
+ TYPE BIN
+)
diff --git a/examples/io/system573/iso.xml b/examples/io/system573/iso.xml
new file mode 100644
index 0000000..09b4d85
--- /dev/null
+++ b/examples/io/system573/iso.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
diff --git a/examples/io/system573/main.c b/examples/io/system573/main.c
new file mode 100644
index 0000000..67a98da
--- /dev/null
+++ b/examples/io/system573/main.c
@@ -0,0 +1,371 @@
+/*
+ * PSn00bSDK Konami System 573 example
+ * (C) 2021 spicyjpeg - MPL licensed
+ *
+ * This is a minimal example demonstrating how to target the Konami System 573
+ * using PSn00bSDK. The System 573 is a PS1-based arcade motherboard that
+ * powered various Konami arcade games throughout the late 1990s, most notably
+ * Dance Dance Revolution and other Bemani rhythm games. It came in several
+ * configurations, with slightly different I/O connectors depending on the game
+ * and two optional add-on modules (known as the "analog I/O" and "digital I/O"
+ * boards respectively) providing light control outputs and, in the case of the
+ * digital I/O board, MP3 audio playback.
+ *
+ * Unlike other arcade systems based on PS1 hardware, the 573 is mostly
+ * identical to a regular PS1, with almost all custom extensions mapped into
+ * the expansion port region at 0x1f000000. The major differences are:
+ *
+ * - RAM is 4 MB instead of 2, and VRAM is 2 MB instead of 1. It is recommended
+ * *not* to use the additional memory to preserve PS1 compatibility.
+ *
+ * - The CD drive is replaced by a standard IDE/ATAPI drive (which most of the
+ * time is going to be an aftermarket DVD drive, as the original drives the
+ * system shipped with were prone to failure and couldn't read CD-Rs). This
+ * also means the 573 has no support at all for XA audio playback, as XA is
+ * not part of the CD-ROM specification implemented by IDE drives. CD audio
+ * is supported by most IDE drives, but 573 units with the digital I/O board
+ * installed have the 4-pin audio cable plugged into that instead of the
+ * drive. The IDE bus is connected to IRQ10 and DMA5 (expansion port) instead
+ * of IRQ2 and DMA3, which go unused.
+ *
+ * - The BIOS seems to have most file I/O APIs removed and exposes no functions
+ * whatsoever for accessing the IDE drive or the filesystem on the disc. The
+ * launcher/shell is completely different from Sony's shell and is capable of
+ * loading an executable from the CD drive, a PCMCIA memory-mapped flash card
+ * or the internal 16 MB flash memory.
+ *
+ * - The SPI controller bus seems to be left unconnected. Inputs are routed to
+ * a JAMMA PCB edge connector and handled through two custom/relabeled chips,
+ * which expose the inputs as memory-mapped registers. There is also a JVS
+ * port (i.e. RS-485 serial bus wired to a USB-A connector, commonly used for
+ * daisy-chaining peripherals in arcade cabinets) managed by a
+ * microcontroller.
+ *
+ * - There is a "security cartridge" slot, which breaks out the serial port as
+ * well as several GPIO pins. All security cartridge communication and DRM is
+ * handled by games rather than by the BIOS, so a security cartridge is *not*
+ * required to boot homebrew. Each game came with a different cartridge type;
+ * many of them expose the serial port or provide additional game-specific
+ * I/O connectors.
+ *
+ * Currently the only publicly available documentation for the custom registers
+ * is the System 573 MAME driver. Also keep in mind that the psxcd library does
+ * not yet support IDE drives, so the 573's drive can only be accessed by
+ * writing a custom ATAPI driver and ISO9660 parser (which is out of the scope
+ * of this example).
+ *
+ * https://github.com/mamedev/mame/blob/master/src/mame/drivers/ksys573.cpp
+ * https://github.com/mamedev/mame/blob/master/src/mame/machine/k573dio.cpp
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+/* Register definitions */
+
+#define EXP1_ADDR *((volatile uint32_t *) 0x1f801000)
+#define EXP1_CTRL *((volatile uint32_t *) 0x1f801008)
+
+#define K573_IN0 *((volatile uint16_t *) 0x1f400000)
+#define K573_IN1_L *((volatile uint16_t *) 0x1f400004)
+#define K573_IN1_H *((volatile uint16_t *) 0x1f400006)
+#define K573_IN2 *((volatile uint16_t *) 0x1f400008)
+#define K573_IN3_L *((volatile uint16_t *) 0x1f40000c)
+#define K573_IN3_H *((volatile uint16_t *) 0x1f40000e)
+#define K573_BANK *((volatile uint16_t *) 0x1f500000)
+#define K573_WATCHDOG *((volatile uint16_t *) 0x1f5c0000)
+
+#define K573_IDE_CS0 ((volatile uint16_t *) 0x1f480000)
+#define K573_IDE_CS1 ((volatile uint16_t *) 0x1f4c0000)
+#define K573_RTC ((volatile uint16_t *) 0x1f620000)
+#define K573_IO_BOARD ((volatile uint16_t *) 0x1f640000)
+
+typedef enum {
+ ANALOG_IO_LIGHTS0 = 0x20,
+ ANALOG_IO_LIGHTS1 = 0x22,
+ ANALOG_IO_LIGHTS2 = 0x24,
+ ANALOG_IO_LIGHTS3 = 0x26,
+
+ // The digital I/O board has a lot more registers than these, but there
+ // seems to be no DIGITAL_IO_LIGHTS6 register. WTF
+ DIGITAL_IO_LIGHTS1 = 0x70,
+ DIGITAL_IO_LIGHTS0 = 0x71,
+ DIGITAL_IO_LIGHTS3 = 0x72,
+ DIGITAL_IO_LIGHTS7 = 0x73,
+ DIGITAL_IO_LIGHTS4 = 0x7d,
+ DIGITAL_IO_LIGHTS5 = 0x7e,
+ DIGITAL_IO_LIGHTS2 = 0x7f
+} IO_BOARD_REG;
+
+// The 573's real-time clock chip is an M48T58, which behaves like a standard
+// 8 KB battery-backed SRAM with a bunch of special registers. Official games
+// store highscores and settings in RTC RAM.
+typedef enum {
+ RTC_CTRL = 0x1ff8,
+ RTC_SECONDS = 0x1ff9,
+ RTC_MINUTES = 0x1ffa,
+ RTC_HOURS = 0x1ffb,
+ RTC_DAY_OF_WEEK = 0x1ffc,
+ RTC_DAY_OF_MONTH = 0x1ffd,
+ RTC_MONTH = 0x1ffe,
+ RTC_YEAR = 0x1fff
+} RTC_REG;
+
+#define btoi(x) ((((x) >> 4) & 0xf) * 10 + ((x) & 0xf))
+
+/* Display/GPU context utilities */
+
+#define SCREEN_XRES 320
+#define SCREEN_YRES 240
+
+#define BGCOLOR_R 48
+#define BGCOLOR_G 24
+#define BGCOLOR_B 0
+
+typedef struct {
+ DISPENV disp;
+ DRAWENV draw;
+} DB;
+
+typedef struct {
+ DB db[2];
+ uint32_t db_active;
+} CONTEXT;
+
+void init_context(CONTEXT *ctx) {
+ DB *db;
+
+ ResetGraph(0);
+ ctx->db_active = 0;
+
+ db = &(ctx->db[0]);
+ SetDefDispEnv(&(db->disp), 0, 0, SCREEN_XRES, SCREEN_YRES);
+ SetDefDrawEnv(&(db->draw), SCREEN_XRES, 0, SCREEN_XRES, SCREEN_YRES);
+ setRGB0(&(db->draw), BGCOLOR_R, BGCOLOR_G, BGCOLOR_B);
+ db->draw.isbg = 1;
+ db->draw.dtd = 1;
+
+ db = &(ctx->db[1]);
+ SetDefDispEnv(&(db->disp), SCREEN_XRES, 0, SCREEN_XRES, SCREEN_YRES);
+ SetDefDrawEnv(&(db->draw), 0, 0, SCREEN_XRES, SCREEN_YRES);
+ setRGB0(&(db->draw), BGCOLOR_R, BGCOLOR_G, BGCOLOR_B);
+ db->draw.isbg = 1;
+ db->draw.dtd = 1;
+
+ PutDrawEnv(&(db->draw));
+ //PutDispEnv(&(db->disp));
+
+ // Create a text stream at the top of the screen.
+ FntLoad(960, 0);
+ FntOpen(8, 16, 304, 208, 2, 512);
+}
+
+void display(CONTEXT *ctx) {
+ DB *db;
+
+ DrawSync(0);
+ VSync(0);
+ ctx->db_active ^= 1;
+
+ db = &(ctx->db[ctx->db_active]);
+ PutDrawEnv(&(db->draw));
+ PutDispEnv(&(db->disp));
+ SetDispMask(1);
+}
+
+/* Input polling utilities */
+
+typedef struct {
+ uint8_t p1_joy, p1_btn;
+ uint8_t p2_joy, p2_btn;
+ uint8_t coin, dip_sw;
+} JAMMA_INPUTS;
+
+void get_jamma_inputs(JAMMA_INPUTS *output) {
+ uint16_t in1l = K573_IN1_L;
+ uint16_t in1h = K573_IN1_H;
+ uint16_t in2 = K573_IN2;
+ uint16_t in3l = K573_IN3_L;
+ uint16_t in3h = K573_IN3_H;
+ uint8_t p1_btn, p2_btn, coin;
+
+ // Rearrange the bits read from the input register into something that's
+ // easier to parse and display. Refer to MAME for information on what each
+ // bit in the IN* registers does.
+ p1_btn = ((in2 >> 15) & 0x0001); // Bit 0 = start button
+ p1_btn |= ((in2 >> 8) & 0x0007) << 1; // Bit 1-3 = buttons 1-3
+ p1_btn |= ((in3l >> 8) & 0x0003) << 4; // Bit 4-5 = buttons 4-5
+ p1_btn |= ((in3l >> 11) & 0x0001) << 6; // Bit 6 = button 6
+ p2_btn = ((in2 >> 7) & 0x0001); // Bit 0 = start button
+ p2_btn |= ((in2 >> 4) & 0x0007) << 1; // Bit 1-3 = buttons 1-3
+ p2_btn |= ((in3h >> 8) & 0x0003) << 4; // Bit 4-5 = buttons 4-5
+ p2_btn |= ((in3h >> 11) & 0x0001) << 6; // Bit 6 = button 6
+ coin = ((in1h >> 8) & 0x0003); // Bit 0-1 = coin switches
+ coin |= ((in1h >> 12) & 0x0001) << 2; // Bit 2 = service button
+ coin |= ((in3l >> 10) & 0x0001) << 3; // Bit 3 = test button
+ coin |= ((in1h >> 10) & 0x0003) << 4; // Bit 4-5 = PCMCIA cards
+
+ output->p1_joy = (in2 >> 8) & 0x000f;
+ output->p1_btn = p1_btn;
+ output->p2_joy = in2 & 0x000f;
+ output->p2_btn = p2_btn;
+ output->coin = coin;
+ output->dip_sw = in1l & 0x000f;
+}
+
+/* I/O board (light control) utilities */
+
+// This function controls light outputs on analog I/O boards.
+void set_lights_analog(uint32_t lights) {
+ uint32_t bits;
+
+ bits = (lights & 0x01010101) << 7; // Lamp 0 -> bit 7
+ bits |= (lights & 0x02020202) << 5; // Lamp 1 -> bit 6
+ bits |= (lights & 0x04040404) >> 1; // Lamp 2 -> bit 1
+ bits |= (lights & 0x08080808) >> 3; // Lamp 3 -> bit 0
+ bits |= (lights & 0x10101010) << 1; // Lamp 4 -> bit 5
+ bits |= (lights & 0x20202020) >> 1; // Lamp 5 -> bit 4
+ bits |= (lights & 0x40404040) >> 3; // Lamp 6 -> bit 3
+ bits |= (lights & 0x80808080) >> 5; // Lamp 7 -> bit 2
+
+ K573_IO_BOARD[ANALOG_IO_LIGHTS0] = (bits) & 0xff;
+ K573_IO_BOARD[ANALOG_IO_LIGHTS1] = (bits >> 8) & 0xff;
+ K573_IO_BOARD[ANALOG_IO_LIGHTS2] = (bits >> 16) & 0xff;
+ K573_IO_BOARD[ANALOG_IO_LIGHTS3] = (bits >> 24) & 0xff;
+}
+
+// This function controls light outputs on digital I/O boards (i.e. the ones
+// that include MP3 playback hardware in addition to the light control).
+// TODO: test this on real hardware -- it might not work if lights are handled
+// by the board's FPGA, which requires a binary blob...
+void set_lights_digital(uint32_t lights) {
+ uint32_t bits;
+
+ bits = (lights & 0x11111111); // Lamp 0 -> bit 0
+ bits |= (lights & 0x22222222) << 1; // Lamp 1 -> bit 2
+ bits |= (lights & 0x44444444) << 1; // Lamp 2 -> bit 3
+ bits |= (lights & 0x88888888) >> 2; // Lamp 3 -> bit 1
+
+ K573_IO_BOARD[DIGITAL_IO_LIGHTS0] = ((bits) & 0xf) << 12;
+ K573_IO_BOARD[DIGITAL_IO_LIGHTS1] = ((bits >> 4) & 0xf) << 12;
+ K573_IO_BOARD[DIGITAL_IO_LIGHTS2] = ((bits >> 8) & 0xf) << 12;
+ K573_IO_BOARD[DIGITAL_IO_LIGHTS3] = ((bits >> 12) & 0xf) << 12;
+ K573_IO_BOARD[DIGITAL_IO_LIGHTS4] = ((bits >> 16) & 0xf) << 12;
+ K573_IO_BOARD[DIGITAL_IO_LIGHTS5] = ((bits >> 20) & 0xf) << 12;
+ //K573_IO_BOARD[DIGITAL_IO_LIGHTS6] = ((bits >> 24) & 0xf) << 12;
+ K573_IO_BOARD[DIGITAL_IO_LIGHTS7] = ((bits >> 28) & 0xf) << 12;
+}
+
+/* Main */
+
+static CONTEXT ctx;
+
+#define SHOW_STATUS(...) { FntPrint(-1, __VA_ARGS__); FntFlush(-1); display(&ctx); }
+#define SHOW_ERROR(...) { SHOW_STATUS(__VA_ARGS__); while (1) __asm__("nop"); }
+
+int main(int argc, const char* argv[]) {
+ // Reinitialize the heap and relocate the stack to allow the 573's full 4
+ // MB of RAM to be used. This isn't strictly required; executables designed
+ // for 2 MB of RAM will also run fine on the 573 (obviously).
+ // FIXME: this seems to be broken currently
+ //__asm__ volatile("li $sp, 0x803fffe0");
+ //_mem_init(0x400000, 0x20000);
+
+ EXP1_ADDR = 0x1f000000;
+ EXP1_CTRL = 0x24173f47; // 573 BIOS uses this value
+ K573_WATCHDOG = 0;
+
+ init_context(&ctx);
+
+ // Determine whether we are running on a 573 by fetching the version string
+ // from the BIOS.
+ const char *const version = (const char *const) GetSystemInfo(0x02);
+ //if (strncmp(version, "Konami OS", 9))
+ //SHOW_ERROR("ERROR: NOT RUNNING ON A SYSTEM 573!\n\n[%s]\n", version);
+
+ uint32_t counter = 0;
+ uint8_t last_joystick = 0xff;
+ uint8_t last_buttons = 0xff;
+ uint32_t current_light = 0;
+ uint32_t is_digital = 0;
+
+ while (1) {
+ FntPrint(-1, "COUNTER=%d\n", counter++);
+
+ JAMMA_INPUTS inputs;
+ get_jamma_inputs(&inputs);
+
+ FntPrint(-1, "\nJAMMA INPUTS:\n");
+ FntPrint(-1, " P1 JOYSTICK =%04@\n", inputs.p1_joy);
+ FntPrint(-1, " P1 BUTTONS =%07@\n", inputs.p1_btn);
+ FntPrint(-1, " P2 JOYSTICK =%04@\n", inputs.p2_joy);
+ FntPrint(-1, " P2 BUTTONS =%07@\n", inputs.p2_btn);
+ FntPrint(-1, " COIN/SERVICE=%04@\n", inputs.coin);
+ FntPrint(-1, " DIP SWITCHES=%04@\n", inputs.dip_sw);
+
+ FntPrint(-1, "\nCABINET LIGHTS:\n");
+ FntPrint(-1, " BOARD=%s I/O\n", is_digital ? "DIGITAL" : "ANALOG");
+ FntPrint(-1, " LIGHT=%d\n\n", current_light);
+ FntPrint(-1, " [START] CHANGE BOARD TYPE\n");
+ FntPrint(-1, " [LEFT/RIGHT] SELECT LIGHT TO TEST\n");
+
+ // Request the current date/time from the RTC and display it.
+ K573_RTC[RTC_CTRL] |= 0x40;
+ FntPrint(-1, "\nRTC:\n");
+ FntPrint(
+ -1,
+ " %02d-%02d-%02d %02d:%02d:%02d\n",
+ btoi(K573_RTC[RTC_YEAR]),
+ btoi(K573_RTC[RTC_MONTH]),
+ btoi(K573_RTC[RTC_DAY_OF_MONTH] & 0x3f),
+ btoi(K573_RTC[RTC_HOURS]),
+ btoi(K573_RTC[RTC_MINUTES]),
+ btoi(K573_RTC[RTC_SECONDS] & 0x7f)
+ );
+
+ FntPrint(-1, "\nSYSTEM:\n");
+ FntPrint(-1, " KERNEL=%s\n", version);
+ FntPrint(-1, " PCMCIA=%02@\n", inputs.coin >> 4);
+
+ FntFlush(-1);
+ display(&ctx);
+
+ // Reset the watchdog. This must be done at least once per frame to
+ // prevent the 573 from rebooting.
+ K573_WATCHDOG = 0;
+
+ if (is_digital)
+ set_lights_digital(1 << current_light);
+ else
+ set_lights_analog(1 << current_light);
+
+ // Handle inputs.
+ if ((last_joystick & 0x01) && !(inputs.p1_joy & 0x01)) // Left
+ current_light--;
+ if ((last_joystick & 0x02) && !(inputs.p1_joy & 0x02)) // Right
+ current_light++;
+ if ((last_buttons & 0x02) && !(inputs.p1_btn & 0x02)) // Button 1
+ current_light--;
+ if ((last_buttons & 0x04) && !(inputs.p1_btn & 0x04)) // Button 2
+ current_light++;
+ if ((last_buttons & 0x01) && !(inputs.p1_btn & 0x01)) { // Start
+ is_digital = !is_digital;
+ if (is_digital)
+ set_lights_analog(0);
+ else
+ set_lights_digital(0);
+ }
+
+ current_light %= 32;
+ last_joystick = inputs.p1_joy;
+ last_buttons = inputs.p1_btn;
+ }
+
+ return 0;
+}
--
cgit v1.2.3
From 6c7512ff42805e9399dfee8b67e2f70fa55909d3 Mon Sep 17 00:00:00 2001
From: spicyjpeg <88942473+spicyjpeg@users.noreply.github.com>
Date: Thu, 23 Dec 2021 21:58:56 +0100
Subject: Add sound/spustream example
---
examples/sound/spustream/CMakeLists.txt | 29 ++
examples/sound/spustream/convert_stream.py | 112 +++++++
examples/sound/spustream/iso.xml | 29 ++
examples/sound/spustream/main.c | 456 +++++++++++++++++++++++++++++
examples/sound/spustream/system.cnf | 4 +
5 files changed, 630 insertions(+)
create mode 100644 examples/sound/spustream/CMakeLists.txt
create mode 100644 examples/sound/spustream/convert_stream.py
create mode 100644 examples/sound/spustream/iso.xml
create mode 100644 examples/sound/spustream/main.c
create mode 100644 examples/sound/spustream/system.cnf
(limited to 'examples')
diff --git a/examples/sound/spustream/CMakeLists.txt b/examples/sound/spustream/CMakeLists.txt
new file mode 100644
index 0000000..91243cf
--- /dev/null
+++ b/examples/sound/spustream/CMakeLists.txt
@@ -0,0 +1,29 @@
+# PSn00bSDK example CMake script
+# (C) 2021 spicyjpeg - MPL licensed
+
+cmake_minimum_required(VERSION 3.20)
+
+if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{PSN00BSDK_LIBS})
+ set(CMAKE_TOOLCHAIN_FILE $ENV{PSN00BSDK_LIBS}/cmake/sdk.cmake)
+endif()
+
+project(
+ spustream
+ LANGUAGES C
+ VERSION 1.0.0
+ DESCRIPTION "PSn00bSDK SPU custom streaming example"
+ HOMEPAGE_URL "http://lameguy64.net/?page=psn00bsdk"
+)
+
+# TODO: add rules to actually generate a valid STREAM.BIN file
+file(GLOB _sources *.c)
+psn00bsdk_add_executable(spustream STATIC ${_sources})
+#psn00bsdk_add_cd_image(spustream_iso spustream iso.xml DEPENDS spustream)
+
+install(
+ FILES
+ #${PROJECT_BINARY_DIR}/spustream.bin
+ #${PROJECT_BINARY_DIR}/spustream.cue
+ ${PROJECT_BINARY_DIR}/spustream.exe
+ TYPE BIN
+)
diff --git a/examples/sound/spustream/convert_stream.py b/examples/sound/spustream/convert_stream.py
new file mode 100644
index 0000000..1b1696f
--- /dev/null
+++ b/examples/sound/spustream/convert_stream.py
@@ -0,0 +1,112 @@
+#!/usr/bin/env python3
+# Simple .VAG to STREAM.BIN interleaving tool
+# (C) 2021 spicyjpeg - MPL licensed
+
+import sys
+from warnings import warn
+from struct import Struct
+from itertools import zip_longest
+from argparse import ArgumentParser, FileType
+
+VAG_HEADER = Struct("> 4s I 4x 2I 12x 16s")
+VAG_MAGIC = b"VAGp"
+SAMPLE_RATE = 44100
+BUFFER_SIZE = 26624 # (26624 / 16 * 28) / 44100 = 1.05 seconds
+ALIGN_SIZE = 2048
+
+## Helpers
+
+def align(data, size):
+ chunks = (len(data) + size - 1) // size
+
+ return data.ljust(chunks * size, b"\x00")
+
+def set_loop_flag(data):
+ last_block = bytearray(data[-16:])
+ last_block[1] = 0x03 # Jump to loop point + sustain
+
+ return data[:-16] + last_block
+
+## .VAG file reader
+
+def read_vag(_file, chunk_size):
+ with _file:
+ header = _file.read(VAG_HEADER.size)
+ (
+ magic,
+ version,
+ size,
+ sample_rate,
+ name
+ ) = VAG_HEADER.unpack(header)
+
+ #if magic != VAG_MAGIC:
+ #raise RuntimeError(f"{_file.name} is not a valid .VAG file")
+ if sample_rate != SAMPLE_RATE:
+ warn(RuntimeWarning(f"{_file.name} sample rate is not {SAMPLE_RATE} Hz"))
+
+ for i in range(0, size, chunk_size):
+ chunk = _file.read(chunk_size)
+
+ if len(chunk) % 16:
+ warn(RuntimeWarning(f"{_file.name} is not 16-byte aligned, trimming"))
+ chunk = chunk[0:len(chunk) // 16 * 16]
+
+ chunk = set_loop_flag(chunk)
+
+ yield chunk.ljust(chunk_size, b"\x00")
+
+## Main
+
+def get_args():
+ parser = ArgumentParser(
+ description = "Generates interleaved stream data from one or more .VAG files."
+ )
+ parser.add_argument(
+ "input_file",
+ nargs = "+",
+ type = FileType("rb"),
+ help = f"mono input files for each channel (must be {SAMPLE_RATE} Hz .VAG)"
+ )
+ parser.add_argument(
+ "-o", "--output",
+ type = FileType("wb"),
+ default = "stream.bin",
+ help = "where to output converted stream data (stream.bin by default)",
+ metavar = "file"
+ )
+ parser.add_argument(
+ "-b", "--buffer-size",
+ type = int,
+ default = BUFFER_SIZE,
+ help = f"size of each interleaved chunk (one per channel, default {BUFFER_SIZE})",
+ metavar = "bytes"
+ )
+ parser.add_argument(
+ "-a", "--align",
+ type = int,
+ default = ALIGN_SIZE,
+ help = f"align each group of chunks to N bytes (default {ALIGN_SIZE})",
+ metavar = "bytes"
+ )
+
+ return parser.parse_args()
+
+def main():
+ args = get_args()
+ if args.buffer_size % 16:
+ raise ValueError("buffer size must be a multiple of 16 bytes")
+
+ interleave = zip_longest(
+ *( read_vag(_file, args.buffer_size) for _file in args.input_file ),
+ fillvalue = b"\x00" * args.buffer_size
+ )
+
+ with args.output as _file:
+ for chunks in interleave:
+ data = b"".join(chunks)
+
+ _file.write(align(data, args.align))
+
+if __name__ == "__main__":
+ main()
diff --git a/examples/sound/spustream/iso.xml b/examples/sound/spustream/iso.xml
new file mode 100644
index 0000000..3807046
--- /dev/null
+++ b/examples/sound/spustream/iso.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
diff --git a/examples/sound/spustream/main.c b/examples/sound/spustream/main.c
new file mode 100644
index 0000000..2032df5
--- /dev/null
+++ b/examples/sound/spustream/main.c
@@ -0,0 +1,456 @@
+/*
+ * PSn00bSDK SPU audio streaming example
+ * (C) 2021 spicyjpeg - MPL licensed
+ *
+ * This example demonstrates how to play a large multi-channel audio file
+ * "manually" by streaming it through the SPU, without having to rely on the CD
+ * drive's ability to play audio tracks or XA files.
+ *
+ * The way this works is by splitting the audio file into a series of ~1 second
+ * "chunks", each of which in turn is an array of concatenated buffers holding
+ * SPU ADPCM data (one for each channel, so a stereo stream would have 2
+ * buffers per chunk). All buffers in a chunk are played simultaneously using
+ * multiple SPU channels; each buffer has the loop flag set at the end, so each
+ * channel will jump to its loop address (SPU_CHANNELS[n].loop_addr) once the
+ * chunk is played.
+ *
+ * Since the loop point doesn't necessarily have to be within the chunk itself,
+ * we can abuse it to "queue" another set of buffers to be played immediately
+ * after the currently playing chunk. This allows us to fetch a chunk from the
+ * CD, upload it to SPU RAM (2048 bytes at a time to avoid having to keep
+ * another large buffer in main RAM) and queue it for playback while a
+ * previously buffered chunk is playing in the background. SPU RAM always holds
+ * two chunks, one of which is played while the other one is buffered. This is
+ * the layout used in this example:
+ *
+ * /================================================\
+ * | /==================\ |
+ * v Loop point | v Loop point |
+ * +-------+----------------+----------------+----------------+----------------+
+ * | Dummy | Left buffer 0 | Right buffer 0 | Left buffer 1 | Right buffer 1 |
+ * +-------+----------------+----------------+----------------+----------------+
+ * \____________Chunk 0____________/ \____________Chunk 1____________/
+ *
+ * It's pretty much the same thing as GPU double buffering (aka page flipping),
+ * just with chunks instead of framebuffers.
+ *
+ * We need to know when the chunk we've buffered actually starts playing in
+ * order to start buffering the next one. The SPU can be configured to trigger
+ * an interrupt whenever a specific address in SPU RAM is read by a channel, so
+ * we can just point it to the beginning of the buffered chunk's first buffer.
+ * The interrupt callback will then kick off CD reading and adjust the loop/IRQ
+ * addresses to the ones of the chunk that is going to be buffered next.
+ *
+ * Chunks are read from a STREAM.BIN file which is just a series of sector
+ * aligned chunks, arranged as follows:
+ *
+ * +--Sector--+--Sector--+--Sector--+--Sector--+--Sector--+--Sector--+----
+ * | +--------------------------+--------------------------+ |
+ * | | Left channel data | Right channel data | Padding | ...
+ * | +--------------------------+--------------------------+ |
+ * +----------+----------+----------+----------+----------+----------+----
+ * \________________________Chunk________________________/
+ *
+ * Such file isn't provided as PSn00bSDK doesn't yet have a tool for audio
+ * transcoding. A Python script is included to generate STREAM.BIN from one or
+ * more SPU ADPCM (.VAG) files, one for each channel (the .VAG format only
+ * supports mono).
+ *
+ * Of course SPU streaming isn't the only way to play music, as the CD drive
+ * can play CD-DA tracks and XA files natively with zero CPU overhead. However
+ * streaming has a number of advantages over CD audio or XA:
+ *
+ * - Any sample rate up to 44.1 kHz can be used. The sample rate can also be
+ * changed on-the-fly to play the stream at different speeds and pitches (as
+ * long as the CD drive can keep up of course), or even interpolated for
+ * effects like tape stops or DJ scratches.
+ * - Manual streaming is not limited to mono or stereo but can be expanded to
+ * as many channels as needed, only limited by the amount of SPU RAM required
+ * for chunks and CD bandwidth. Having more than 2 channels can be useful for
+ * e.g. crossfading between tracks (not possible with XA) or controlling
+ * volume and panning of each individual instrument.
+ * - Depending on how streaming/interleaving is implemented it is possible to
+ * have 500-1000ms idle periods during which the CD drive isn't buffering the
+ * stream, that can be used to read small amounts of other data without ever
+ * interrupting playback. This is different from XA-style interleaving as the
+ * drive is free to seek to *any* region of the disc during these periods
+ * (it must seek back to the stream's next chunk afterwards though).
+ * - Thanks to the idle periods it is possible to seek back to the beginning of
+ * the stream and preload the first chunk before the end is reached, allowing
+ * the track to be looped seamlessly without having to resort to tricks like
+ * filler samples.
+ * - Unlike XA, SPU streaming can be used on some PS1-based arcade boards such
+ * as the Konami System 573. These systems usually use IDE/SCSI CD drives or
+ * flash memory, neither of which supports XA playback.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+// To maximize STREAM.BIN packing efficiency and get rid of padding between
+// chunks, buffer size should be a multiple of sector size (2048 bytes). Buffer
+// size can be increased to get more idle time between CD reads, however it is
+// usually best to keep it to 1-2 seconds as SPU RAM is only 512 KB.
+#define SAMPLE_RATE 0x1000 // 44100 Hz
+#define BUFFER_SIZE 26624 // (26624 / 16 * 28) / 44100 = 1.05 seconds
+
+#define NUM_CHANNELS 2
+#define CHANNEL_MASK 0x03
+
+/* Register definitions */
+
+// For some reason SpuVoiceRaw doesn't actually match the layout of SPU
+// registers, so here we go.
+typedef struct {
+ uint16_t vol_left;
+ uint16_t vol_right;
+ uint16_t freq;
+ uint16_t addr;
+ uint32_t adsr_param;
+ uint16_t _reserved;
+ uint16_t loop_addr;
+} SPUCHANNEL;
+
+#define SPU_CTRL *((volatile uint16_t *) 0x1f801daa)
+#define SPU_IRQ_ADDR *((volatile uint16_t *) 0x1f801da4)
+#define SPU_KEY_ON *((volatile uint32_t *) 0x1f801d88)
+#define SPU_KEY_OFF *((volatile uint32_t *) 0x1f801d8c)
+
+// SPU RAM is addressed in 8-byte units, using 16-bit pointers.
+#define SPU_CHANNELS ((volatile SPUCHANNEL *) 0x1f801c00)
+#define SPU_RAM_ADDR(x) ((uint16_t) (((uint32_t) (x)) >> 3))
+
+/* Display/GPU context utilities */
+
+#define SCREEN_XRES 320
+#define SCREEN_YRES 240
+
+#define BGCOLOR_R 48
+#define BGCOLOR_G 24
+#define BGCOLOR_B 0
+
+typedef struct {
+ DISPENV disp;
+ DRAWENV draw;
+} DB;
+
+typedef struct {
+ DB db[2];
+ uint32_t db_active;
+} CONTEXT;
+
+void init_context(CONTEXT *ctx) {
+ DB *db;
+
+ ResetGraph(0);
+ ctx->db_active = 0;
+
+ db = &(ctx->db[0]);
+ SetDefDispEnv(&(db->disp), 0, 0, SCREEN_XRES, SCREEN_YRES);
+ SetDefDrawEnv(&(db->draw), SCREEN_XRES, 0, SCREEN_XRES, SCREEN_YRES);
+ setRGB0(&(db->draw), BGCOLOR_R, BGCOLOR_G, BGCOLOR_B);
+ db->draw.isbg = 1;
+ db->draw.dtd = 1;
+
+ db = &(ctx->db[1]);
+ SetDefDispEnv(&(db->disp), SCREEN_XRES, 0, SCREEN_XRES, SCREEN_YRES);
+ SetDefDrawEnv(&(db->draw), 0, 0, SCREEN_XRES, SCREEN_YRES);
+ setRGB0(&(db->draw), BGCOLOR_R, BGCOLOR_G, BGCOLOR_B);
+ db->draw.isbg = 1;
+ db->draw.dtd = 1;
+
+ PutDrawEnv(&(db->draw));
+ //PutDispEnv(&(db->disp));
+
+ // Create a text stream at the top of the screen.
+ FntLoad(960, 0);
+ FntOpen(8, 16, 304, 208, 2, 512);
+}
+
+void display(CONTEXT *ctx) {
+ DB *db;
+
+ DrawSync(0);
+ VSync(0);
+ ctx->db_active ^= 1;
+
+ db = &(ctx->db[ctx->db_active]);
+ PutDrawEnv(&(db->draw));
+ PutDispEnv(&(db->disp));
+ SetDispMask(1);
+}
+
+/* Stream interrupt handlers */
+
+// This is a silent looping sample used to keep unused SPU channels busy,
+// preventing them from accidentally triggering the SPU RAM interrupt and
+// throwing off the timing (all channels are always reading sample data, even
+// when "stopped"). It is 64 bytes as that is the minimum size for SPU DMA
+// transfers, however only the first 16 bytes are kept. The rest is going to be
+// overwritten by chunks.
+// https://problemkaputt.de/psx-spx.htm#spuinterrupt
+const uint8_t SPU_DUMMY_BLOCK[] = {
+ 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+// The first 4 KB of SPU RAM are reserved for capture buffers, so we have to
+// place stream buffers after those. Sony's SPU library additionally places a
+// dummy sample at 0x1000; we are going to do the same with the block above.
+#define DUMMY_BLOCK_ADDR 0x1000
+#define BUFFER_START_ADDR 0x1010
+#define CHUNK_SIZE (BUFFER_SIZE * NUM_CHANNELS)
+
+typedef struct {
+ uint32_t lba;
+ uint32_t length;
+ uint32_t pos;
+
+ uint32_t spu_addr;
+ uint32_t spu_pos;
+ uint32_t db_active;
+} STREAMCONTEXT;
+
+static volatile STREAMCONTEXT str_ctx;
+
+// This buffer is used by cd_event_handler() as a temporary area for sectors
+// read from the CD and uploaded to SPU RAM. Due to DMA limitations it can't be
+// allocated on the stack (especially not in the interrupt callbacks' stack,
+// whose size is very limited).
+static uint8_t sector_buffer[2048];
+
+void spu_irq_handler(void) {
+ // Acknowledge the interrupt to ensure it can be triggered again. The only
+ // way to do this is actually to disable the interrupt entirely; we'll
+ // enable it again once the buffer is ready.
+ SPU_CTRL &= 0xffbf;
+
+ str_ctx.db_active ^= 1;
+ str_ctx.spu_pos = 0;
+
+ // Align the sector counter to the size of a chunk (to prevent glitches
+ // after seeking) and reset it if it exceeds the stream's length.
+ str_ctx.pos %= str_ctx.length;
+ str_ctx.pos -= str_ctx.pos % ((CHUNK_SIZE + 2047) / 2048);
+
+ // Configure to SPU to trigger an IRQ once the buffer that is going to be
+ // filled now starts playing (so the next buffer can be loaded) and
+ // override both channels' loop addresses to make them "jump" to the new
+ // buffer rather than actually looping when they encounter the loop flag at
+ // the end of the currently playing buffer.
+ str_ctx.spu_addr = BUFFER_START_ADDR + CHUNK_SIZE * str_ctx.db_active;
+ SPU_IRQ_ADDR = SPU_RAM_ADDR(str_ctx.spu_addr);
+
+ for (uint32_t i = 0; i < NUM_CHANNELS; i++)
+ SPU_CHANNELS[i].loop_addr = SPU_RAM_ADDR(str_ctx.spu_addr + BUFFER_SIZE * i);
+
+ // Start loading the next chunk. cd_event_handler() will be called
+ // repeatedly for each sector until the entire chunk is read.
+ CdlLOC pos;
+ CdIntToPos(str_ctx.lba + str_ctx.pos, &pos);
+ CdControlF(CdlReadN, &pos);
+}
+
+void cd_event_handler(int32_t event, uint8_t *payload) {
+ // Ignore all events other than a sector being ready.
+ // TODO: read errors should be handled properly
+ if (event != CdlDataReady)
+ return;
+
+ // Fetch the sector that has been read from the drive.
+ CdGetSector(sector_buffer, 512);
+ str_ctx.pos++;
+
+ // Set loop flags to make sure the buffer will loop (actually jump to the
+ // other buffer, as we're overriding loop addresses) at the end.
+ // NOTE: this isn't actually necessary here as the stream converter script
+ // already sets these flags in the file.
+ /*for (uint32_t i = 0; i < NUM_CHANNELS; i++) {
+ if (
+ str_ctx.spu_pos >= (BUFFER_SIZE * i - 2048) &&
+ str_ctx.spu_pos < (BUFFER_SIZE * i)
+ )
+ sector[(BUFFER_SIZE * i - str_ctx.spu_pos) - 15] = 0x03;
+ }*/
+
+ // Copy the sector to SPU RAM, appending it to the buffer that is not
+ // playing currently. As the left and right buffers are adjacent, we can
+ // just treat the chunk as a single blob of data and copy it as-is; we only
+ // have to trim the padding at the end (if any) to avoid overwriting other
+ // data in SPU RAM.
+ uint32_t length = CHUNK_SIZE - str_ctx.spu_pos;
+ if (length > 2048)
+ length = 2048;
+
+ SpuSetTransferStartAddr(str_ctx.spu_addr + str_ctx.spu_pos);
+ SpuWrite(sector_buffer, length);
+ str_ctx.spu_pos += length;
+
+ // If the buffer has been filled completely, stop reading and re-enable the
+ // SPU IRQ.
+ // TODO TODO: preload first sector
+ if (str_ctx.spu_pos >= CHUNK_SIZE) {
+ CdControlF(CdlPause, 0);
+ SPU_CTRL |= 0x0040;
+ }
+}
+
+/* Stream helpers */
+
+void init_spu_channels(void) {
+ // Upload the dummy block to the SPU and play it on all channels, locking
+ // them up and stopping them from messing with the SPU interrupt.
+ // TODO: is this really necessary? (needs testing on real hardware)
+ SpuSetTransferStartAddr(DUMMY_BLOCK_ADDR);
+ SpuWrite(SPU_DUMMY_BLOCK, 64);
+
+ SPU_KEY_OFF = 0x00ffffff;
+
+ for (uint32_t i = 0; i < 24; i++)
+ SPU_CHANNELS[i].addr = SPU_RAM_ADDR(DUMMY_BLOCK_ADDR);
+
+ SPU_KEY_ON = 0x00ffffff;
+}
+
+void init_stream(CdlFILE *file) {
+ EnterCriticalSection();
+ InterruptCallback(9, &spu_irq_handler);
+ CdReadyCallback(&cd_event_handler);
+ ExitCriticalSection();
+
+ // Set the initial LBA of the stream file, which is going to be incremented
+ // as the stream is played.
+ str_ctx.lba = CdPosToInt(&(file->pos));
+ str_ctx.length = file->size / 2048;
+ str_ctx.pos = 0;
+
+ // Ensure at least one chunk is in SPU RAM by invoking the SPU IRQ handler
+ // manually and blocking until the chunk has loaded.
+ str_ctx.db_active = 1;
+ spu_irq_handler();
+
+ while (str_ctx.spu_pos < CHUNK_SIZE)
+ __asm__("nop");
+}
+
+void start_stream(void) {
+ SPU_KEY_OFF = CHANNEL_MASK;
+
+ for (uint32_t i = 0; i < NUM_CHANNELS; i++) {
+ SPU_CHANNELS[i].addr = SPU_RAM_ADDR(BUFFER_START_ADDR + BUFFER_SIZE * i);
+ SPU_CHANNELS[i].freq = SAMPLE_RATE;
+ SPU_CHANNELS[i].adsr_param = 0xdfee80ff; // or 0x9fc080ff, 0xdff18087
+ }
+
+ // Unmute the channels and route them for stereo output. You'll want to
+ // edit this if you are using more than 2 channels, and/or if you want to
+ // provide an option to output mono audio instead of stereo.
+ SPU_CHANNELS[0].vol_left = 0x3fff;
+ SPU_CHANNELS[0].vol_right = 0x0000;
+ SPU_CHANNELS[1].vol_left = 0x0000;
+ SPU_CHANNELS[1].vol_right = 0x3fff;
+
+ SPU_KEY_ON = CHANNEL_MASK;
+ spu_irq_handler();
+}
+
+/* Main */
+
+static CONTEXT ctx;
+
+#define SHOW_STATUS(...) { FntPrint(-1, __VA_ARGS__); FntFlush(-1); display(&ctx); }
+#define SHOW_ERROR(...) { SHOW_STATUS(__VA_ARGS__); while (1) __asm__("nop"); }
+
+int main(int argc, const char* argv[]) {
+ init_context(&ctx);
+
+ SHOW_STATUS("INITIALIZING\n");
+ SpuInit();
+ CdInit();
+ init_spu_channels();
+
+ SHOW_STATUS("LOCATING STREAM FILE\n");
+
+ CdlFILE file;
+ if (!CdSearchFile(&file, "\\STREAM.BIN"))
+ SHOW_ERROR("FAILED TO FIND STREAM.BIN\n");
+
+ SHOW_STATUS("BUFFERING STREAM\n");
+ init_stream(&file);
+ start_stream();
+
+ // Set up controller polling.
+ uint8_t pad_buff[2][34];
+ InitPAD(pad_buff[0], 34, pad_buff[1], 34);
+ StartPAD();
+ ChangeClearPAD(0);
+
+ uint16_t sample_rate = SAMPLE_RATE;
+ uint16_t last_buttons = 0xffff;
+
+ while (1) {
+ FntPrint(-1, "PLAYING SPU STREAM\n");
+ if (str_ctx.spu_pos >= CHUNK_SIZE)
+ FntPrint(-1, "STATUS: IDLE\n\n");
+ else if (!str_ctx.spu_pos)
+ FntPrint(-1, "STATUS: SEEKING\n\n");
+ else
+ FntPrint(-1, "STATUS: BUFFERING\n\n");
+
+ FntPrint(-1, "POSITION=%5d/%5d\n", str_ctx.pos, str_ctx.length);
+ FntPrint(-1, "BUFFERED=%5d/%5d\n", str_ctx.spu_pos, CHUNK_SIZE);
+ FntPrint(-1, "SMP RATE=%5d HZ\n\n", (sample_rate * 44100) >> 12);
+
+ FntPrint(-1, "[LEFT/RIGHT] SEEK\n");
+ FntPrint(-1, "[O] RESET POSITION\n");
+ FntPrint(-1, "[UP/DOWN] CHANGE SAMPLE RATE\n");
+ FntPrint(-1, "[X] RESET SAMPLE RATE\n");
+
+ FntFlush(-1);
+ display(&ctx);
+
+ // Check if a compatible controller is connected and handle button
+ // presses.
+ PADTYPE *pad = (PADTYPE *) pad_buff[0];
+ if (pad->stat)
+ continue;
+ if ((pad->type != 4) && (pad->type != 5) && (pad->type != 7))
+ continue;
+
+ // Seeking by an arbitrary number of sectors isn't a problem as
+ // spu_irq_handler() always realigns the counter.
+ if (!(pad->btn & PAD_LEFT))
+ str_ctx.pos -= 16;
+ if (!(pad->btn & PAD_RIGHT))
+ str_ctx.pos += 16;
+ if ((last_buttons & PAD_CIRCLE) && !(pad->btn & PAD_CIRCLE))
+ str_ctx.pos = 0;
+
+ if (!(pad->btn & PAD_DOWN) && (sample_rate > 0x400))
+ sample_rate -= 0x40;
+ if (!(pad->btn & PAD_UP) && (sample_rate < 0x2000))
+ sample_rate += 0x40;
+ if ((last_buttons & PAD_CROSS) && !(pad->btn & PAD_CROSS))
+ sample_rate = SAMPLE_RATE;
+
+ // Only set the sample rate registers if necessary.
+ if (pad->btn != 0xffff) {
+ for (uint32_t i = 0; i < NUM_CHANNELS; i++)
+ SPU_CHANNELS[i].freq = sample_rate;
+ }
+
+ last_buttons = pad->btn;
+ }
+
+ return 0;
+}
diff --git a/examples/sound/spustream/system.cnf b/examples/sound/spustream/system.cnf
new file mode 100644
index 0000000..0c4561a
--- /dev/null
+++ b/examples/sound/spustream/system.cnf
@@ -0,0 +1,4 @@
+BOOT=cdrom:\spustrm.exe;1
+TCB=4
+EVENT=10
+STACK=801FFFF0
--
cgit v1.2.3
From 2e6625481cd006d0a9d68285ce557f195030718e Mon Sep 17 00:00:00 2001
From: spicyjpeg <88942473+spicyjpeg@users.noreply.github.com>
Date: Thu, 23 Dec 2021 22:09:06 +0100
Subject: Fix CdGetSector size inconsistency, update changelog
---
.github/scripts/generate_release_notes.py | 46 ++++++++++---------------------
CHANGELOG.md | 29 +++++++++++++------
examples/cdrom/cdxa/main.c | 2 +-
examples/io/system573/main.c | 2 +-
libpsn00b/psxcd/cdgetsector.s | 3 +-
libpsn00b/psxcd/isofs.c | 2 +-
libpsn00b/psxcd/psxcd.c | 8 +++---
7 files changed, 44 insertions(+), 48 deletions(-)
(limited to 'examples')
diff --git a/.github/scripts/generate_release_notes.py b/.github/scripts/generate_release_notes.py
index f3e4870..e3fbc7f 100644
--- a/.github/scripts/generate_release_notes.py
+++ b/.github/scripts/generate_release_notes.py
@@ -3,7 +3,7 @@
# (C) 2021 spicyjpeg - MPL licensed
import sys, re
-from time import gmtime, strptime
+from time import gmtime, strptime, struct_time
from argparse import ArgumentParser, FileType
## Helpers
@@ -11,14 +11,13 @@ from argparse import ArgumentParser, FileType
VERSION_REGEX = re.compile(r"^(?:refs\/tags\/)?(?:v|ver|version|release)? *(.*)")
def parse_date(date):
+ if isinstance(date, struct_time):
+ return date
+
return strptime(date.strip(), "%Y-%m-%d")
def normalize_version(version):
- match = VERSION_REGEX.match(version.lower())
- if not match:
- raise ValueError(f"invalid version string: {version}")
-
- return match.group(1)
+ return VERSION_REGEX.match(version.lower()).group(1)
## Changelog parser
@@ -50,9 +49,6 @@ def parse_blocks(changelog):
# [ _crap, date, version, body, date, version, body, ... ]
items = BLOCK_REGEX.split(changelog.strip())
- #if items[0].strip():
- #raise RuntimeError("the changelog doesn't start with a valid block")
-
# Iterate over all blocks from bottom to top (i.e. oldest first).
last_version = FIRST_VERSION
@@ -110,51 +106,39 @@ def generate_notes(versions):
def get_args():
parser = ArgumentParser(
- description = "Generates and outputs release notes from a Markdown changelog file.",
- add_help = False
+ description = "Generates and outputs release notes from a Markdown changelog file."
)
-
- tools_group = parser.add_argument_group("Tools")
- tools_group.add_argument(
- "-h", "--help",
- action = "help",
- help = "Show this help message and exits"
- )
-
- files_group = parser.add_argument_group("Files")
- files_group.add_argument(
+ parser.add_argument(
"changelog",
type = FileType("rt"),
help = "Markdown changelog file to parse"
)
- files_group.add_argument(
+ parser.add_argument(
"-o", "--output",
type = FileType("wt"),
default = sys.stdout,
- help = "Where to output release notes (stdout by default)",
+ help = "where to output release notes (stdout by default)",
metavar = "file"
)
-
- filter_group = parser.add_argument_group("Filters")
- filter_group.add_argument(
+ parser.add_argument(
"-v", "--version",
action = "append",
type = str,
- help = "Ignore all changes not belonging to a version (can be specified multiple times)",
+ help = "ignore all changes not belonging to a version (can be specified multiple times)",
metavar = "name"
)
- filter_group.add_argument(
+ parser.add_argument(
"-f", "--from-date",
type = parse_date,
default = parse_date("2000-01-01"),
- help = "Ignore all changes before date",
+ help = "ignore all changes before date",
metavar = "yyyy-mm-dd"
)
- filter_group.add_argument(
+ parser.add_argument(
"-t", "--to-date",
type = parse_date,
default = gmtime(),
- help = "Ignore all changes after date",
+ help = "ignore all changes after date",
metavar = "yyyy-mm-dd"
)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9463b83..ea4f4d2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,7 +5,7 @@
contributing to PSn00bSDK, add a new block at the top following this template:
```
-## --:
+## --: [optional new version]
:
@@ -19,6 +19,17 @@ to ensure the changelog can be parsed correctly.
-------------------------------------------------------------------------------
+## 2021-12-23
+
+spicyjpeg:
+
+- psxcd: `CdGetSector()` now expects the sector size to be in 32-bit word units
+ (rather than bytes) for consistency with the official CD-ROM library. The
+ library's ISO9660 parser and helper functions have been updated accordingly.
+ **This is a breaking change**.
+
+- examples: Added `spustream` audio streaming example.
+
## 2021-11-28: 0.18
spicyjpeg:
@@ -453,7 +464,7 @@ Lameguy64:
- psxgpu: Fixed typos in `setUVWH()` macro.
-- Added `_boot()` BIOS function (A(A0h) aka Warmboot, useful for CD based
+- Added `_boot()` BIOS function (`A(A0h)` aka Warmboot, useful for CD based
serial loaders).
## 2019-08-17: 0.13b
@@ -505,9 +516,9 @@ Lameguy64:
executable to return to a parent executable, return logic automatically
calls `EnterCriticalSection()`.
-- libc: Updated build method which takes libgcc from the compiler and adds
+- libc: Updated build method which takes `libgcc` from the compiler and adds
its own object files into it, eliminating linker problems caused by having
- to order libc and libgcc libraries in a specific manner.
+ to order `libc` and `libgcc` libraries in a specific manner.
- psxgpu: Added `RestartCallback()`.
@@ -526,7 +537,7 @@ Lameguy64:
performance, as the R3000 does not support 64-bit arithmetic natively
so its emulated like floats. `int64` still used for processing floats and
doubles and old `vsprintf.c` file is still included for those who really
- want int64 support for whatever reason.
+ want `int64` support for whatever reason.
- libc: Removed `stdarg.h` which is part of GCC and not license compatible
with MPL. The toolchain compiled with libgcc provides `stdarg.h` and other
@@ -540,7 +551,7 @@ Lameguy64:
- psxgpu: Fixed bug in DMACallback where the internal DMA handler would fail
to install due to `GetInterruptCallback()` retrieving the callback value
- immediately in the branch delay slot of a jr instruction, which resuls to
+ immediately in the branch delay slot of a `jr` instruction, which resuls to
an inconsistent return value. This also broke `DrawSyncCallback()`.
- psxsio: Done fixes on `_sio_control()` from the aformentioned issues with
@@ -619,7 +630,7 @@ Lameguy64:
ready instead of simply waiting for GPU transfer ready which is the likely
cause of subtle GPU related timing issues, it also sets GPU DMA transfer
mode to off afterwards. It can also read number of words remaining in DMA
- transfer if a0 is non-zero but it likely only returns the correct value on
+ transfer if `a0` is non-zero but it likely only returns the correct value on
VRAM transfers. Exact way how `DrawSync()` returns the count in the official
SDK is currently unknown.
@@ -645,10 +656,10 @@ Lameguy64:
- Added `rgb24` example.
- Got custom exit handler set using `SetCustomExitFromException()` (BIOS
- function B(19h)) working. Currently used to acknowledge VSync IRQ but
+ function `B(19h)`) working. Currently used to acknowledge VSync IRQ but
actual VSync handling is still done with events and needs to be
transferred to the custom exit handler. At least it lets BIOS
- controller functions to work now. See doc/dev `notes.txt` for details
+ controller functions to work now. See `doc/dev notes.txt` for details
on how this handler behaves.
- Made stack usage in `ResetGraph()` less wasteful. You only need to
diff --git a/examples/cdrom/cdxa/main.c b/examples/cdrom/cdxa/main.c
index 5f11d8d..284b92f 100644
--- a/examples/cdrom/cdxa/main.c
+++ b/examples/cdrom/cdxa/main.c
@@ -199,7 +199,7 @@ void xa_callback(int intr, unsigned char *result)
if (intr == CdlDataReady)
{
/* Fetch data sector */
- CdGetSector((u_long*)&xa_sector_buff, 2048);
+ CdGetSector((u_long*)&xa_sector_buff, 512);
/* Quirk: This CdGetSector() implementation must fetch 2048 bytes */
/* or more otherwise the following sectors will be read in an */
diff --git a/examples/io/system573/main.c b/examples/io/system573/main.c
index 67a98da..95c3155 100644
--- a/examples/io/system573/main.c
+++ b/examples/io/system573/main.c
@@ -306,7 +306,7 @@ int main(int argc, const char* argv[]) {
FntPrint(-1, " P1 BUTTONS =%07@\n", inputs.p1_btn);
FntPrint(-1, " P2 JOYSTICK =%04@\n", inputs.p2_joy);
FntPrint(-1, " P2 BUTTONS =%07@\n", inputs.p2_btn);
- FntPrint(-1, " COIN/SERVICE=%04@\n", inputs.coin);
+ FntPrint(-1, " COIN/SERVICE=%04@\n", inputs.coin & 0xf);
FntPrint(-1, " DIP SWITCHES=%04@\n", inputs.dip_sw);
FntPrint(-1, "\nCABINET LIGHTS:\n");
diff --git a/libpsn00b/psxcd/cdgetsector.s b/libpsn00b/psxcd/cdgetsector.s
index 9af3543..dbe95cb 100644
--- a/libpsn00b/psxcd/cdgetsector.s
+++ b/libpsn00b/psxcd/cdgetsector.s
@@ -18,7 +18,8 @@ CdGetSector:
# nop
lui $v0, 0x1
- srl $a1, 2
+# srl $a1, 2 # (the official implementation expects $a1/size
+ # to be in 32-bit words rather than bytes)
or $v0, $a1
sw $a0, D3_MADR($a2) # Set DMA base address and transfer length
sw $v0, D3_BCR($a2)
diff --git a/libpsn00b/psxcd/isofs.c b/libpsn00b/psxcd/isofs.c
index 29bbb76..d1c1b18 100644
--- a/libpsn00b/psxcd/isofs.c
+++ b/libpsn00b/psxcd/isofs.c
@@ -795,7 +795,7 @@ static void _scan_callback(int status, unsigned char *result)
{
if( status == CdlDataReady )
{
- CdGetSector((void*)_ses_scanbuff, 2048);
+ CdGetSector((void*)_ses_scanbuff, 512);
if( _ses_scanbuff[0] == 0x1 )
{
diff --git a/libpsn00b/psxcd/psxcd.c b/libpsn00b/psxcd/psxcd.c
index 74c6c1c..76415f9 100644
--- a/libpsn00b/psxcd/psxcd.c
+++ b/libpsn00b/psxcd/psxcd.c
@@ -255,7 +255,7 @@ static void _CdReadReadyCallback(int status, unsigned char *result)
CdGetSector((void*)_cd_read_addr, _cd_read_sector_sz);
// Increment destination address
- _cd_read_addr += _cd_read_sector_sz>>2;
+ _cd_read_addr += _cd_read_sector_sz;
// Subtract sector count
_cd_sector_count--;
@@ -290,15 +290,15 @@ int CdRead(int sectors, u_long *buf, int mode)
// Determine sector based on mode flags
if( mode & CdlModeSize0 )
{
- _cd_read_sector_sz = 2328;
+ _cd_read_sector_sz = 2328 / 4;
}
else if( mode & CdlModeSize1 )
{
- _cd_read_sector_sz = 2340;
+ _cd_read_sector_sz = 2340 / 4;
}
else
{
- _cd_read_sector_sz = 2048;
+ _cd_read_sector_sz = 2048 / 4;
}
_cd_read_counter = VSync(-1);
--
cgit v1.2.3
From d60b046bf362fcc9332f463823e8d02147d516de Mon Sep 17 00:00:00 2001
From: spicyjpeg <88942473+spicyjpeg@users.noreply.github.com>
Date: Thu, 23 Dec 2021 23:20:12 +0100
Subject: Remove PSN00BSDK_LIBS leftovers, improve CI artifact upload
---
.github/workflows/build.yml | 32 +++++++++++++++++++++++------
doc/installation.md | 25 +++++++++++-----------
examples/beginner/cppdemo/CMakeLists.txt | 4 ----
examples/beginner/hello/CMakeLists.txt | 4 ----
examples/cdrom/cdbrowse/CMakeLists.txt | 4 ----
examples/cdrom/cdxa/CMakeLists.txt | 4 ----
examples/demos/n00bdemo/CMakeLists.txt | 4 ----
examples/graphics/balls/CMakeLists.txt | 4 ----
examples/graphics/billboard/CMakeLists.txt | 4 ----
examples/graphics/fpscam/CMakeLists.txt | 4 ----
examples/graphics/gte/CMakeLists.txt | 4 ----
examples/graphics/hdtv/CMakeLists.txt | 4 ----
examples/graphics/render2tex/CMakeLists.txt | 4 ----
examples/graphics/rgb24/CMakeLists.txt | 4 ----
examples/io/pads/CMakeLists.txt | 4 ----
examples/io/system573/CMakeLists.txt | 4 ----
examples/lowlevel/cartrom/CMakeLists.txt | 4 ----
examples/sound/spustream/CMakeLists.txt | 4 ----
examples/sound/vagsample/CMakeLists.txt | 4 ----
examples/system/childexec/CMakeLists.txt | 4 ----
examples/system/console/CMakeLists.txt | 4 ----
examples/system/dynlink/CMakeLists.txt | 4 ----
examples/system/timer/CMakeLists.txt | 4 ----
examples/system/tty/CMakeLists.txt | 4 ----
24 files changed, 38 insertions(+), 107 deletions(-)
(limited to 'examples')
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 8c16ac3..3cbefb1 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -124,13 +124,20 @@ jobs:
cmake --build build
cmake --build build -t package
+ # The GitHub Actions UI doesn't allow downloading individual files from
+ # an artifact, so it's best to upload each package type as a separate
+ # artifact.
- name: Upload build artifacts
uses: actions/upload-artifact@v2
with:
name: psn00bsdk-windows
- path: |
- build/packages/*
- !build/packages/_CPack_Packages
+ path: build/packages/*.zip
+
+ - name: Upload build artifacts (NSIS)
+ uses: actions/upload-artifact@v2
+ with:
+ name: psn00bsdk-windows-nsis
+ path: build/packages/*.exe
build-sdk-linux:
name: Build PSn00bSDK on Linux
@@ -169,9 +176,19 @@ jobs:
uses: actions/upload-artifact@v2
with:
name: psn00bsdk-linux
- path: |
- build/packages/*
- !build/packages/_CPack_Packages
+ path: build/packages/*.zip
+
+ - name: Upload build artifacts (DEB)
+ uses: actions/upload-artifact@v2
+ with:
+ name: psn00bsdk-linux-deb
+ path: build/packages/*.deb
+
+ - name: Upload build artifacts (RPM)
+ uses: actions/upload-artifact@v2
+ with:
+ name: psn00bsdk-linux-rpm
+ path: build/packages/*.rpm
# This job takes care of creating a new release and upload the build
# artifacts if the last commit is associated to a tag.
@@ -222,4 +239,7 @@ jobs:
files: |
*.zip
psn00bsdk-windows/*
+ psn00bsdk-windows-nsis/*
psn00bsdk-linux/*
+ psn00bsdk-linux-deb/*
+ psn00bsdk-linux-rpm/*
diff --git a/doc/installation.md b/doc/installation.md
index 576eb7a..ca3aab4 100644
--- a/doc/installation.md
+++ b/doc/installation.md
@@ -96,9 +96,9 @@ and installed properly.
- `/lib/libpsn00b`
- `/share/psn00bsdk`
-7. Set the `PSN00BSDK_LIBS` environment variable to point to the `lib/libpsn00b`
- subfolder inside the install directory. You might also want to add the `bin`
- folder to `PATH` if it's not listed already.
+7. You may optionally set the `PSN00BSDK_LIBS` environment variable to point to
+ the `lib/libpsn00b` subfolder inside the install directory. You might also
+ want to add the `bin` folder to `PATH` if it's not listed already.
Although not strictly required, you'll probably want to install a PS1 emulator
with debugging capabilities such as [no$psx](https://problemkaputt.de/psx.htm)
@@ -132,24 +132,23 @@ far from being feature-complete.
1. Copy the contents of `/share/psn00bsdk/template` (or the
`template` folder within the repo) to your new project's root directory.
-2. Configure and build the template by running:
+2. If you haven't set the `PSN00BSDK_LIBS` environment variable previously or
+ if you want to use a different PSn00bSDK installation for the project, edit
+ `CMakePresets.json` to set the path you installed the SDK to. See the
+ [setup guide](cmake_reference.md#setup) for details.
+
+3. Configure and build the template by running:
```bash
- cmake -S . -B ./build
+ cmake --preset default .
cmake --build ./build
```
If you did everything correctly there should be a `template.bin` CD image in
the `build` folder. Test it in an emulator to ensure it works.
-Note that, even though the template relies on the `PSN00BSDK_LIBS` environment
-variable to locate the SDK by default, you can also specify the path directly
-on the CMake command line by adding
-`-DCMAKE_TOOLCHAIN_FILE=/lib/libpsn00b/cmake/sdk.cmake` to the
-CMake command line.
-
The toolchain script defines a few CMake macros to create PS1 executables, DLLs
-and CD images. See the [reference](doc/cmake_reference.md) for details.
+and CD images. See the [reference](cmake_reference.md) for details.
-----------------------------------------
-_Last updated on 2021-11-19 by spicyjpeg_
+_Last updated on 2021-12-23 by spicyjpeg_
diff --git a/examples/beginner/cppdemo/CMakeLists.txt b/examples/beginner/cppdemo/CMakeLists.txt
index becf464..c43d4a1 100644
--- a/examples/beginner/cppdemo/CMakeLists.txt
+++ b/examples/beginner/cppdemo/CMakeLists.txt
@@ -3,10 +3,6 @@
cmake_minimum_required(VERSION 3.20)
-if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{PSN00BSDK_LIBS})
- set(CMAKE_TOOLCHAIN_FILE $ENV{PSN00BSDK_LIBS}/cmake/sdk.cmake)
-endif()
-
project(
cppdemo
LANGUAGES CXX
diff --git a/examples/beginner/hello/CMakeLists.txt b/examples/beginner/hello/CMakeLists.txt
index 7fb7c22..d8297c5 100644
--- a/examples/beginner/hello/CMakeLists.txt
+++ b/examples/beginner/hello/CMakeLists.txt
@@ -3,10 +3,6 @@
cmake_minimum_required(VERSION 3.20)
-if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{PSN00BSDK_LIBS})
- set(CMAKE_TOOLCHAIN_FILE $ENV{PSN00BSDK_LIBS}/cmake/sdk.cmake)
-endif()
-
project(
hello
LANGUAGES C
diff --git a/examples/cdrom/cdbrowse/CMakeLists.txt b/examples/cdrom/cdbrowse/CMakeLists.txt
index e5ec759..e36407d 100644
--- a/examples/cdrom/cdbrowse/CMakeLists.txt
+++ b/examples/cdrom/cdbrowse/CMakeLists.txt
@@ -3,10 +3,6 @@
cmake_minimum_required(VERSION 3.20)
-if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{PSN00BSDK_LIBS})
- set(CMAKE_TOOLCHAIN_FILE $ENV{PSN00BSDK_LIBS}/cmake/sdk.cmake)
-endif()
-
project(
cdbrowse
LANGUAGES C
diff --git a/examples/cdrom/cdxa/CMakeLists.txt b/examples/cdrom/cdxa/CMakeLists.txt
index 18dcc69..7b90f59 100644
--- a/examples/cdrom/cdxa/CMakeLists.txt
+++ b/examples/cdrom/cdxa/CMakeLists.txt
@@ -3,10 +3,6 @@
cmake_minimum_required(VERSION 3.20)
-if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{PSN00BSDK_LIBS})
- set(CMAKE_TOOLCHAIN_FILE $ENV{PSN00BSDK_LIBS}/cmake/sdk.cmake)
-endif()
-
project(
cdxa
LANGUAGES C
diff --git a/examples/demos/n00bdemo/CMakeLists.txt b/examples/demos/n00bdemo/CMakeLists.txt
index c62c4ef..1c211b3 100644
--- a/examples/demos/n00bdemo/CMakeLists.txt
+++ b/examples/demos/n00bdemo/CMakeLists.txt
@@ -3,10 +3,6 @@
cmake_minimum_required(VERSION 3.20)
-if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{PSN00BSDK_LIBS})
- set(CMAKE_TOOLCHAIN_FILE $ENV{PSN00BSDK_LIBS}/cmake/sdk.cmake)
-endif()
-
project(
n00bdemo
LANGUAGES C ASM
diff --git a/examples/graphics/balls/CMakeLists.txt b/examples/graphics/balls/CMakeLists.txt
index 5886484..f5297c3 100644
--- a/examples/graphics/balls/CMakeLists.txt
+++ b/examples/graphics/balls/CMakeLists.txt
@@ -3,10 +3,6 @@
cmake_minimum_required(VERSION 3.20)
-if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{PSN00BSDK_LIBS})
- set(CMAKE_TOOLCHAIN_FILE $ENV{PSN00BSDK_LIBS}/cmake/sdk.cmake)
-endif()
-
project(
balls
LANGUAGES C
diff --git a/examples/graphics/billboard/CMakeLists.txt b/examples/graphics/billboard/CMakeLists.txt
index 8cd31a9..1b417d2 100644
--- a/examples/graphics/billboard/CMakeLists.txt
+++ b/examples/graphics/billboard/CMakeLists.txt
@@ -3,10 +3,6 @@
cmake_minimum_required(VERSION 3.20)
-if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{PSN00BSDK_LIBS})
- set(CMAKE_TOOLCHAIN_FILE $ENV{PSN00BSDK_LIBS}/cmake/sdk.cmake)
-endif()
-
project(
billboard
LANGUAGES C ASM
diff --git a/examples/graphics/fpscam/CMakeLists.txt b/examples/graphics/fpscam/CMakeLists.txt
index 791f6c2..cb0c086 100644
--- a/examples/graphics/fpscam/CMakeLists.txt
+++ b/examples/graphics/fpscam/CMakeLists.txt
@@ -3,10 +3,6 @@
cmake_minimum_required(VERSION 3.20)
-if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{PSN00BSDK_LIBS})
- set(CMAKE_TOOLCHAIN_FILE $ENV{PSN00BSDK_LIBS}/cmake/sdk.cmake)
-endif()
-
project(
fpscam
LANGUAGES C
diff --git a/examples/graphics/gte/CMakeLists.txt b/examples/graphics/gte/CMakeLists.txt
index 85b2942..f95c5ff 100644
--- a/examples/graphics/gte/CMakeLists.txt
+++ b/examples/graphics/gte/CMakeLists.txt
@@ -3,10 +3,6 @@
cmake_minimum_required(VERSION 3.20)
-if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{PSN00BSDK_LIBS})
- set(CMAKE_TOOLCHAIN_FILE $ENV{PSN00BSDK_LIBS}/cmake/sdk.cmake)
-endif()
-
project(
gte
LANGUAGES C
diff --git a/examples/graphics/hdtv/CMakeLists.txt b/examples/graphics/hdtv/CMakeLists.txt
index f92faeb..804b096 100644
--- a/examples/graphics/hdtv/CMakeLists.txt
+++ b/examples/graphics/hdtv/CMakeLists.txt
@@ -3,10 +3,6 @@
cmake_minimum_required(VERSION 3.20)
-if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{PSN00BSDK_LIBS})
- set(CMAKE_TOOLCHAIN_FILE $ENV{PSN00BSDK_LIBS}/cmake/sdk.cmake)
-endif()
-
project(
hdtv
LANGUAGES C
diff --git a/examples/graphics/render2tex/CMakeLists.txt b/examples/graphics/render2tex/CMakeLists.txt
index 360840d..70e489e 100644
--- a/examples/graphics/render2tex/CMakeLists.txt
+++ b/examples/graphics/render2tex/CMakeLists.txt
@@ -3,10 +3,6 @@
cmake_minimum_required(VERSION 3.20)
-if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{PSN00BSDK_LIBS})
- set(CMAKE_TOOLCHAIN_FILE $ENV{PSN00BSDK_LIBS}/cmake/sdk.cmake)
-endif()
-
project(
render2tex
LANGUAGES C ASM
diff --git a/examples/graphics/rgb24/CMakeLists.txt b/examples/graphics/rgb24/CMakeLists.txt
index bf8a8fa..449981a 100644
--- a/examples/graphics/rgb24/CMakeLists.txt
+++ b/examples/graphics/rgb24/CMakeLists.txt
@@ -3,10 +3,6 @@
cmake_minimum_required(VERSION 3.20)
-if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{PSN00BSDK_LIBS})
- set(CMAKE_TOOLCHAIN_FILE $ENV{PSN00BSDK_LIBS}/cmake/sdk.cmake)
-endif()
-
project(
rgb24
LANGUAGES C ASM
diff --git a/examples/io/pads/CMakeLists.txt b/examples/io/pads/CMakeLists.txt
index 5bd7f5d..cf5f817 100644
--- a/examples/io/pads/CMakeLists.txt
+++ b/examples/io/pads/CMakeLists.txt
@@ -3,10 +3,6 @@
cmake_minimum_required(VERSION 3.20)
-if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{PSN00BSDK_LIBS})
- set(CMAKE_TOOLCHAIN_FILE $ENV{PSN00BSDK_LIBS}/cmake/sdk.cmake)
-endif()
-
project(
pads
LANGUAGES C ASM
diff --git a/examples/io/system573/CMakeLists.txt b/examples/io/system573/CMakeLists.txt
index 34c9153..1c74347 100644
--- a/examples/io/system573/CMakeLists.txt
+++ b/examples/io/system573/CMakeLists.txt
@@ -3,10 +3,6 @@
cmake_minimum_required(VERSION 3.21)
-if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{PSN00BSDK_LIBS})
- set(CMAKE_TOOLCHAIN_FILE $ENV{PSN00BSDK_LIBS}/cmake/sdk.cmake)
-endif()
-
project(
system573
LANGUAGES C ASM
diff --git a/examples/lowlevel/cartrom/CMakeLists.txt b/examples/lowlevel/cartrom/CMakeLists.txt
index 3e807a3..107cc3d 100644
--- a/examples/lowlevel/cartrom/CMakeLists.txt
+++ b/examples/lowlevel/cartrom/CMakeLists.txt
@@ -3,10 +3,6 @@
cmake_minimum_required(VERSION 3.21)
-if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{PSN00BSDK_LIBS})
- set(CMAKE_TOOLCHAIN_FILE $ENV{PSN00BSDK_LIBS}/cmake/sdk.cmake)
-endif()
-
project(
cartrom
LANGUAGES C ASM
diff --git a/examples/sound/spustream/CMakeLists.txt b/examples/sound/spustream/CMakeLists.txt
index 91243cf..9e84fa3 100644
--- a/examples/sound/spustream/CMakeLists.txt
+++ b/examples/sound/spustream/CMakeLists.txt
@@ -3,10 +3,6 @@
cmake_minimum_required(VERSION 3.20)
-if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{PSN00BSDK_LIBS})
- set(CMAKE_TOOLCHAIN_FILE $ENV{PSN00BSDK_LIBS}/cmake/sdk.cmake)
-endif()
-
project(
spustream
LANGUAGES C
diff --git a/examples/sound/vagsample/CMakeLists.txt b/examples/sound/vagsample/CMakeLists.txt
index 1a15d9c..f37be97 100644
--- a/examples/sound/vagsample/CMakeLists.txt
+++ b/examples/sound/vagsample/CMakeLists.txt
@@ -3,10 +3,6 @@
cmake_minimum_required(VERSION 3.20)
-if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{PSN00BSDK_LIBS})
- set(CMAKE_TOOLCHAIN_FILE $ENV{PSN00BSDK_LIBS}/cmake/sdk.cmake)
-endif()
-
project(
vagsample
LANGUAGES C
diff --git a/examples/system/childexec/CMakeLists.txt b/examples/system/childexec/CMakeLists.txt
index 88168e0..ca0c110 100644
--- a/examples/system/childexec/CMakeLists.txt
+++ b/examples/system/childexec/CMakeLists.txt
@@ -3,10 +3,6 @@
cmake_minimum_required(VERSION 3.20)
-if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{PSN00BSDK_LIBS})
- set(CMAKE_TOOLCHAIN_FILE $ENV{PSN00BSDK_LIBS}/cmake/sdk.cmake)
-endif()
-
project(
childexec
LANGUAGES C ASM
diff --git a/examples/system/console/CMakeLists.txt b/examples/system/console/CMakeLists.txt
index 6dc6154..d58f212 100644
--- a/examples/system/console/CMakeLists.txt
+++ b/examples/system/console/CMakeLists.txt
@@ -3,10 +3,6 @@
cmake_minimum_required(VERSION 3.20)
-if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{PSN00BSDK_LIBS})
- set(CMAKE_TOOLCHAIN_FILE $ENV{PSN00BSDK_LIBS}/cmake/sdk.cmake)
-endif()
-
project(
console
LANGUAGES C
diff --git a/examples/system/dynlink/CMakeLists.txt b/examples/system/dynlink/CMakeLists.txt
index 5834647..aae3bb3 100644
--- a/examples/system/dynlink/CMakeLists.txt
+++ b/examples/system/dynlink/CMakeLists.txt
@@ -3,10 +3,6 @@
cmake_minimum_required(VERSION 3.20)
-if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{PSN00BSDK_LIBS})
- set(CMAKE_TOOLCHAIN_FILE $ENV{PSN00BSDK_LIBS}/cmake/sdk.cmake)
-endif()
-
project(
dynlink
LANGUAGES C
diff --git a/examples/system/timer/CMakeLists.txt b/examples/system/timer/CMakeLists.txt
index 58daf9b..328e07e 100644
--- a/examples/system/timer/CMakeLists.txt
+++ b/examples/system/timer/CMakeLists.txt
@@ -3,10 +3,6 @@
cmake_minimum_required(VERSION 3.20)
-if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{PSN00BSDK_LIBS})
- set(CMAKE_TOOLCHAIN_FILE $ENV{PSN00BSDK_LIBS}/cmake/sdk.cmake)
-endif()
-
project(
timer
LANGUAGES C
diff --git a/examples/system/tty/CMakeLists.txt b/examples/system/tty/CMakeLists.txt
index 4e0ca36..0664502 100644
--- a/examples/system/tty/CMakeLists.txt
+++ b/examples/system/tty/CMakeLists.txt
@@ -3,10 +3,6 @@
cmake_minimum_required(VERSION 3.20)
-if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{PSN00BSDK_LIBS})
- set(CMAKE_TOOLCHAIN_FILE $ENV{PSN00BSDK_LIBS}/cmake/sdk.cmake)
-endif()
-
project(
tty
LANGUAGES C
--
cgit v1.2.3
From fe42ce7f1c98947baa49427835deb5ce70470afb Mon Sep 17 00:00:00 2001
From: spicyjpeg <88942473+spicyjpeg@users.noreply.github.com>
Date: Wed, 29 Dec 2021 17:30:56 +0100
Subject: Fix CMake and CI bugs, set version number, update docs
---
.github/workflows/build.yml | 4 ++--
CMakeLists.txt | 33 ++++++++++++++++++++++-----------
CMakePresets.json | 9 +++++----
cpack/setup.cmake | 3 +--
cpack/welcome.txt | 3 ---
doc/cmake_reference.md | 10 +++++++---
doc/installation.md | 9 ++++-----
examples/sound/spustream/main.c | 2 +-
libpsn00b/CMakeLists.txt | 1 -
libpsn00b/build.json.template | 9 ++++++---
libpsn00b/cmake/internal_setup.cmake | 5 +++--
11 files changed, 51 insertions(+), 37 deletions(-)
delete mode 100644 cpack/welcome.txt
(limited to 'examples')
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 3cbefb1..a72f5d3 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -116,7 +116,7 @@ jobs:
- name: Update repo submodules
run: |
cd sdk
- git submodule update --init --recursive --remote
+ git submodule update --init --recursive
- name: Build and package PSn00bSDK
run: |
@@ -164,7 +164,7 @@ jobs:
- name: Update repo submodules
run: |
cd sdk
- git submodule update --init --recursive --remote
+ git submodule update --init --recursive
- name: Build and package PSn00bSDK
run: |
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 720ca8c..6a20a0e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -12,7 +12,7 @@ include(ExternalProject)
project(
PSn00bSDK
LANGUAGES NONE
- VERSION 0.1.0
+ VERSION 0.18
DESCRIPTION "Open source PlayStation 1 SDK"
HOMEPAGE_URL "http://lameguy64.net/?page=psn00bsdk"
)
@@ -39,9 +39,15 @@ set(
CACHE BOOL "Skip building SDK examples (not required for installation)"
)
set(
- BUILD_INFO ""
- CACHE STRING "Information about this build to be included in build.json"
+ PSN00BSDK_GIT_TAG ""
+ CACHE STRING "Git tag or branch name (used by CI)"
)
+set(
+ PSN00BSDK_GIT_COMMIT ""
+ CACHE STRING "Git commit hash (used by CI)"
+)
+
+string(TIMESTAMP PSN00BSDK_BUILD_DATE UTC)
# Forward some important variables to mkpsxiso and to the subprojects (they are
# not inherited automatically as they are not environment variables). This also
@@ -50,20 +56,24 @@ set(
# invoked (ExternalProject_Add() runs the subprojects' install step at build
# time).
set(
- COMMON_ARGS
+ _common_args
-DPSN00BSDK_TC:PATH=${PSN00BSDK_TC}
-DPSN00BSDK_TARGET:STRING=${PSN00BSDK_TARGET}
-DPSN00BSDK_VERSION:STRING=${PROJECT_VERSION}
- -DPSN00BSDK_BUILD_INFO:STRING=${BUILD_INFO}
+ -DPSN00BSDK_BUILD_DATE:STRING=${PSN00BSDK_BUILD_DATE}
+ -DPSN00BSDK_GIT_TAG:STRING=${PSN00BSDK_GIT_TAG}
+ -DPSN00BSDK_GIT_COMMIT:STRING=${PSN00BSDK_GIT_COMMIT}
-DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE}
)
set(
- SUBPROJECT_ARGS
+ _sdk_args
+ ${_common_args}
-DCMAKE_TOOLCHAIN_FILE:FILEPATH=${CMAKE_TOOLCHAIN_FILE}
-DCMAKE_INSTALL_PREFIX:PATH=${PROJECT_BINARY_DIR}/install_tree
)
set(
- EXAMPLES_ARGS
+ _examples_args
+ ${_common_args}
-DCMAKE_TOOLCHAIN_FILE:FILEPATH=${PROJECT_BINARY_DIR}/install_tree/${CMAKE_INSTALL_LIBDIR}/libpsn00b/cmake/sdk.cmake
-DCMAKE_INSTALL_PREFIX:PATH=${PROJECT_BINARY_DIR}/examples
)
@@ -77,26 +87,26 @@ endif()
ExternalProject_Add(
tools
SOURCE_DIR ${PROJECT_SOURCE_DIR}/tools
- CMAKE_CACHE_ARGS ${COMMON_ARGS} ${SUBPROJECT_ARGS}
+ CMAKE_CACHE_ARGS ${_sdk_args}
INSTALL_DIR install_tree
)
ExternalProject_Add(
mkpsxiso
SOURCE_DIR ${PROJECT_SOURCE_DIR}/tools/mkpsxiso
- CMAKE_CACHE_ARGS ${COMMON_ARGS} ${SUBPROJECT_ARGS}
+ CMAKE_CACHE_ARGS ${_sdk_args}
INSTALL_DIR install_tree
)
ExternalProject_Add(
libpsn00b
SOURCE_DIR ${PROJECT_SOURCE_DIR}/libpsn00b
- CMAKE_CACHE_ARGS ${COMMON_ARGS} ${SUBPROJECT_ARGS}
+ CMAKE_CACHE_ARGS ${_sdk_args}
INSTALL_DIR install_tree
#DEPENDS tools
)
ExternalProject_Add(
examples
SOURCE_DIR ${PROJECT_SOURCE_DIR}/examples
- CMAKE_CACHE_ARGS ${COMMON_ARGS} ${EXAMPLES_ARGS}
+ CMAKE_CACHE_ARGS ${_examples_args}
INSTALL_DIR examples
DEPENDS libpsn00b tools mkpsxiso
EXCLUDE_FROM_ALL ${SKIP_EXAMPLES}
@@ -108,6 +118,7 @@ foreach(
_subdir IN ITEMS
${CMAKE_INSTALL_BINDIR}
${CMAKE_INSTALL_LIBDIR}
+ ${CMAKE_INSTALL_INCLUDEDIR}
${CMAKE_INSTALL_DATADIR}
)
install(
diff --git a/CMakePresets.json b/CMakePresets.json
index 7c9fdd3..d71c1ae 100644
--- a/CMakePresets.json
+++ b/CMakePresets.json
@@ -32,10 +32,11 @@
"generator": "Ninja",
"binaryDir": "${sourceDir}/../build",
"cacheVariables": {
- "CMAKE_BUILD_TYPE": "Release",
- "BUNDLE_TOOLCHAIN": "ON",
- "BUILD_INFO": "Built by GitHub Actions ($env{GITHUB_REF_NAME} $env{GITHUB_SHA})",
- "PSN00BSDK_TARGET": "$env{GCC_TARGET}"
+ "CMAKE_BUILD_TYPE": "Release",
+ "BUNDLE_TOOLCHAIN": "ON",
+ "PSN00BSDK_TARGET": "$env{GCC_TARGET}",
+ "PSN00BSDK_GIT_TAG": "$env{GITHUB_REF_NAME}",
+ "PSN00BSDK_GIT_COMMIT": "$env{GITHUB_SHA}"
}
}
]
diff --git a/cpack/setup.cmake b/cpack/setup.cmake
index 57568bb..a5d71ff 100644
--- a/cpack/setup.cmake
+++ b/cpack/setup.cmake
@@ -21,7 +21,7 @@ set(
## Bundled components
-# "Install" the toolchain and CMake (by pulling files from its their install
+# "Install" the toolchain and CMake (by pulling files from their install
# locations). This is only useful when building installers, as CPack will pick
# up these installation rules and bundle the toolchain in the installers.
# NOTE: unfortunately there is no easy way to reuse the toolchain finding logic
@@ -105,7 +105,6 @@ set(CPACK_RESOURCE_FILE_README ${PROJECT_SOURCE_DIR}/README.md)
set(CPACK_RESOURCE_FILE_LICENSE ${PROJECT_SOURCE_DIR}/LICENSE.md)
set(CPACK_PACKAGE_ICON ${CMAKE_CURRENT_LIST_DIR}/icon.ico)
set(CPACK_PACKAGE_DESCRIPTION_FILE ${CMAKE_CURRENT_LIST_DIR}/description.txt)
-set(CPACK_RESOURCE_FILE_WELCOME ${CMAKE_CURRENT_LIST_DIR}/welcome.txt)
set(CPACK_PRE_BUILD_SCRIPTS ${CMAKE_CURRENT_LIST_DIR}/fakeroot_fix.cmake)
set(CPACK_PACKAGE_INSTALL_DIRECTORY PSn00bSDK)
diff --git a/cpack/welcome.txt b/cpack/welcome.txt
deleted file mode 100644
index e024336..0000000
--- a/cpack/welcome.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-This installer will set up PSn00bSDK, including prebuilt libraries, CMake scripts as well as several command-line utilities for PlayStation 1 homebrew development.
-
-NOTE: CMake and the GCC toolchain must be installed separately.
diff --git a/doc/cmake_reference.md b/doc/cmake_reference.md
index 9e134d9..3c89da3 100644
--- a/doc/cmake_reference.md
+++ b/doc/cmake_reference.md
@@ -207,9 +207,13 @@ the build script.
## Read-only variables
-- `PSN00BSDK_VERSION`
+- `PSN00BSDK_VERSION`, `PSN00BSDK_BUILD_DATE`, `PSN00BSDK_GIT_TAG`,
+ `PSN00BSDK_GIT_COMMIT`
- The SDK's version number (`major.minor.patch`).
+ These variables are loaded from `lib/libpsn00b/build.json` and contain
+ information about the SDK's version. Note that `PSN00BSDK_GIT_TAG` and
+ `PSN00BSDK_GIT_COMMIT` are not populated by default when building PSn00bSDK
+ manually from source, so they might be empty strings.
- `PSN00BSDK_TOOLS`, `PSN00BSDK_INCLUDE`, `PSN00BSDK_LDSCRIPTS`
@@ -229,4 +233,4 @@ the build script.
LZP archives as part of the build pipeline.
-----------------------------------------
-_Last updated on 2021-11-24 by spicyjpeg_
+_Last updated on 2021-12-29 by spicyjpeg_
diff --git a/doc/installation.md b/doc/installation.md
index ca3aab4..1646653 100644
--- a/doc/installation.md
+++ b/doc/installation.md
@@ -62,7 +62,7 @@ and installed properly.
repository to download additional dependencies:
```bash
- git submodule update --init --recursive --remote
+ git submodule update --init --recursive
```
5. Compile the libraries, tools and examples using CMake:
@@ -74,9 +74,8 @@ and installed properly.
If you want to install the SDK to a custom location rather than the default
one (`C:\Program Files\PSn00bSDK` or `/usr/local` depending on your OS), add
- `--install-prefix ` to the first command. Add
- `-DPSN00BSDK_TARGET=mipsel-none-elf` if your toolchain targets
- `mipsel-none-elf` rather than `mipsel-unknown-elf`.
+ `--install-prefix ` to the first command. Remember to add
+ `-DPSN00BSDK_TARGET=mipsel-unknown-elf` if necessary.
**NOTE**: Ninja is used by default to build the SDK. If you can't get it to
work or don't have it installed, pass `-G "Unix Makefiles"` (or
@@ -151,4 +150,4 @@ The toolchain script defines a few CMake macros to create PS1 executables, DLLs
and CD images. See the [reference](cmake_reference.md) for details.
-----------------------------------------
-_Last updated on 2021-12-23 by spicyjpeg_
+_Last updated on 2021-12-29 by spicyjpeg_
diff --git a/examples/sound/spustream/main.c b/examples/sound/spustream/main.c
index 2032df5..e268c7d 100644
--- a/examples/sound/spustream/main.c
+++ b/examples/sound/spustream/main.c
@@ -349,7 +349,7 @@ void start_stream(void) {
for (uint32_t i = 0; i < NUM_CHANNELS; i++) {
SPU_CHANNELS[i].addr = SPU_RAM_ADDR(BUFFER_START_ADDR + BUFFER_SIZE * i);
SPU_CHANNELS[i].freq = SAMPLE_RATE;
- SPU_CHANNELS[i].adsr_param = 0xdfee80ff; // or 0x9fc080ff, 0xdff18087
+ SPU_CHANNELS[i].adsr_param = 0x1fee80ff; // or 0x9fc080ff, 0xdff18087
}
// Unmute the channels and route them for stereo output. You'll want to
diff --git a/libpsn00b/CMakeLists.txt b/libpsn00b/CMakeLists.txt
index 2b2e76d..829a2f7 100644
--- a/libpsn00b/CMakeLists.txt
+++ b/libpsn00b/CMakeLists.txt
@@ -73,7 +73,6 @@ install(
# Generate build.json. This file is used to determine the SDK version after
# installation and may contain additional metadata about the build.
-string(TIMESTAMP PSN00BSDK_BUILD_DATE UTC)
configure_file(
build.json.template build.json
ESCAPE_QUOTES
diff --git a/libpsn00b/build.json.template b/libpsn00b/build.json.template
index 666bb43..374b22a 100644
--- a/libpsn00b/build.json.template
+++ b/libpsn00b/build.json.template
@@ -1,5 +1,8 @@
{
- "version": "${PSN00BSDK_VERSION}",
- "build_date": "${PSN00BSDK_BUILD_DATE}",
- "build_info": "${PSN00BSDK_BUILD_INFO}"
+ "version": "${PSN00BSDK_VERSION}",
+ "build_date": "${PSN00BSDK_BUILD_DATE}",
+ "git_tag": "${PSN00BSDK_GIT_TAG}",
+ "git_commit": "${PSN00BSDK_GIT_COMMIT}",
+ "cmake_version": "${CMAKE_VERSION}",
+ "host_system": "${CMAKE_HOST_SYSTEM_NAME}"
}
diff --git a/libpsn00b/cmake/internal_setup.cmake b/libpsn00b/cmake/internal_setup.cmake
index 1d63e92..7d6bfdd 100644
--- a/libpsn00b/cmake/internal_setup.cmake
+++ b/libpsn00b/cmake/internal_setup.cmake
@@ -7,13 +7,14 @@
cmake_minimum_required(VERSION 3.20)
include(GNUInstallDirs)
-# Fetch the SDK version number from build.json.
+# Fetch SDK version information from build.json.
if(NOT DEFINED PSN00BSDK_VERSION)
file(READ ${CMAKE_CURRENT_LIST_DIR}/../build.json _json)
string(JSON PSN00BSDK_VERSION GET ${_json} version)
string(JSON PSN00BSDK_BUILD_DATE GET ${_json} build_date)
- string(JSON PSN00BSDK_BUILD_INFO GET ${_json} build_info)
+ string(JSON PSN00BSDK_GIT_TAG GET ${_json} git_tag)
+ string(JSON PSN00BSDK_GIT_COMMIT GET ${_json} git_commit)
endif()
## Settings (can be overridden by projects)
--
cgit v1.2.3
From ba03884e3d52d47a4fa1b474ca7dc6b419ee6898 Mon Sep 17 00:00:00 2001
From: spicyjpeg <88942473+spicyjpeg@users.noreply.github.com>
Date: Thu, 30 Dec 2021 14:56:55 +0100
Subject: Refactor dynamic linker API, fix system/dynlink example
---
examples/system/dynlink/display.c | 74 -----------------
examples/system/dynlink/main.c | 148 +++++++++++++++++++++++++++------
libpsn00b/include/dlfcn.h | 101 ++++++++++++----------
libpsn00b/psxetc/_dl_resolve_wrapper.s | 8 +-
libpsn00b/psxetc/dl.c | 51 +++++-------
5 files changed, 207 insertions(+), 175 deletions(-)
delete mode 100644 examples/system/dynlink/display.c
(limited to 'examples')
diff --git a/examples/system/dynlink/display.c b/examples/system/dynlink/display.c
deleted file mode 100644
index 573f17c..0000000
--- a/examples/system/dynlink/display.c
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * PSn00bSDK dynamic linker example (utilities)
- * (C) 2021 spicyjpeg - MPL licensed
- */
-
-#include
-
-#include "library/dll_common.h"
-
-#define SCREEN_XRES 320
-#define SCREEN_YRES 240
-
-#define BGCOLOR_R 48
-#define BGCOLOR_G 24
-#define BGCOLOR_B 0
-
-/* Display/GPU context utilities */
-
-void init_context(CONTEXT *ctx) {
- DB *db;
-
- ResetGraph(0);
- ctx->xres = SCREEN_XRES;
- ctx->yres = SCREEN_YRES;
- ctx->db_active = 0;
-
- db = &(ctx->db[0]);
- SetDefDispEnv(&(db->disp), 0, 0, SCREEN_XRES, SCREEN_YRES);
- SetDefDrawEnv(&(db->draw), SCREEN_XRES, 0, SCREEN_XRES, SCREEN_YRES);
- setRGB0(&(db->draw), BGCOLOR_R, BGCOLOR_G, BGCOLOR_B);
- db->draw.isbg = 1;
- db->draw.dtd = 1;
-
- db = &(ctx->db[1]);
- SetDefDispEnv(&(db->disp), SCREEN_XRES, 0, SCREEN_XRES, SCREEN_YRES);
- SetDefDrawEnv(&(db->draw), 0, 0, SCREEN_XRES, SCREEN_YRES);
- setRGB0(&(db->draw), BGCOLOR_R, BGCOLOR_G, BGCOLOR_B);
- db->draw.isbg = 1;
- db->draw.dtd = 1;
-
- // Set up the ordering tables and primitive buffers.
- db = &(ctx->db[0]);
- ctx->db_nextpri = db->p;
- ClearOTagR((u_long *) db->ot, OT_LEN);
-
- PutDrawEnv(&(db->draw));
- //PutDispEnv(&(db->disp));
-
- db = &(ctx->db[1]);
- ClearOTagR((u_long *) db->ot, OT_LEN);
-
- // Create a text stream at the top of the screen.
- FntLoad(960, 0);
- FntOpen(4, 12, 312, 32, 2, 256);
-}
-
-void display(CONTEXT *ctx) {
- DB *db;
-
- DrawSync(0);
- VSync(0);
- ctx->db_active ^= 1;
-
- db = &(ctx->db[ctx->db_active]);
- ctx->db_nextpri = db->p;
- ClearOTagR((u_long *) db->ot, OT_LEN);
-
- PutDrawEnv(&(db->draw));
- PutDispEnv(&(db->disp));
- SetDispMask(1);
-
- db = &(ctx->db[!ctx->db_active]);
- DrawOTag((u_long *) &(db->ot[OT_LEN - 1]));
-}
diff --git a/examples/system/dynlink/main.c b/examples/system/dynlink/main.c
index 6d93e71..690371e 100644
--- a/examples/system/dynlink/main.c
+++ b/examples/system/dynlink/main.c
@@ -11,14 +11,14 @@
* however this may be expanded in the future.
*
* Being able to introspect local symbols at runtime, in turn, allows us to use
- * the dl*() set of APIs to load, link and execute code from an external file
+ * another set of APIs to load, link and execute code from an external file
* (compiled with the dll.ld linker script). A dynamically-loaded library can
* reference and access any non-static function or variable within the main
* executable (and the libraries the main executable has been compiled with);
* the dynamic linker will automatically patch the DLL's code and resolve these
* references so that they point to the addresses listed in the map file. DLLs
* also have their own symbol tables, and any symbol in a DLL is accessible to
- * the main executable through dlsym().
+ * the main executable through DL_GetDLLSymbol().
*
* This example shows how DLLs can be loaded and unloaded at any time. Pressing
* START will unload the current DLL and load an alternate one on-the-fly. A
@@ -47,6 +47,7 @@
#include
#include
#include
+#include
#include "library/dll_common.h"
@@ -67,14 +68,77 @@ const void *const DO_NOT_STRIP[] __attribute__((section(".dummy"))) = {
};
static const char *const DLL_FILENAMES[] = {
- "cdrom:CUBE.DLL;1",
- "cdrom:BALLS.DLL;1"
+ "\\CUBE.DLL;1",
+ "\\BALLS.DLL;1"
};
#define DLL_COUNT 2
-void init_context(CONTEXT *ctx);
-void display(CONTEXT *ctx);
+/* Display/GPU context utilities */
+
+#define SCREEN_XRES 320
+#define SCREEN_YRES 240
+
+#define BGCOLOR_R 48
+#define BGCOLOR_G 24
+#define BGCOLOR_B 0
+
+void init_context(CONTEXT *ctx) {
+ DB *db;
+
+ ResetGraph(0);
+ ctx->xres = SCREEN_XRES;
+ ctx->yres = SCREEN_YRES;
+ ctx->db_active = 0;
+
+ db = &(ctx->db[0]);
+ SetDefDispEnv(&(db->disp), 0, 0, SCREEN_XRES, SCREEN_YRES);
+ SetDefDrawEnv(&(db->draw), SCREEN_XRES, 0, SCREEN_XRES, SCREEN_YRES);
+ setRGB0(&(db->draw), BGCOLOR_R, BGCOLOR_G, BGCOLOR_B);
+ db->draw.isbg = 1;
+ db->draw.dtd = 1;
+
+ db = &(ctx->db[1]);
+ SetDefDispEnv(&(db->disp), SCREEN_XRES, 0, SCREEN_XRES, SCREEN_YRES);
+ SetDefDrawEnv(&(db->draw), 0, 0, SCREEN_XRES, SCREEN_YRES);
+ setRGB0(&(db->draw), BGCOLOR_R, BGCOLOR_G, BGCOLOR_B);
+ db->draw.isbg = 1;
+ db->draw.dtd = 1;
+
+ // Set up the ordering tables and primitive buffers.
+ db = &(ctx->db[0]);
+ ctx->db_nextpri = db->p;
+ ClearOTagR((u_long *) db->ot, OT_LEN);
+
+ PutDrawEnv(&(db->draw));
+ //PutDispEnv(&(db->disp));
+
+ db = &(ctx->db[1]);
+ ClearOTagR((u_long *) db->ot, OT_LEN);
+
+ // Create a text stream at the top of the screen.
+ FntLoad(960, 0);
+ FntOpen(4, 12, 312, 32, 2, 256);
+}
+
+void display(CONTEXT *ctx) {
+ DB *db;
+
+ DrawSync(0);
+ VSync(0);
+ ctx->db_active ^= 1;
+
+ db = &(ctx->db[ctx->db_active]);
+ ctx->db_nextpri = db->p;
+ ClearOTagR((u_long *) db->ot, OT_LEN);
+
+ PutDrawEnv(&(db->draw));
+ PutDispEnv(&(db->disp));
+ SetDispMask(1);
+
+ db = &(ctx->db[!ctx->db_active]);
+ DrawOTag((u_long *) &(db->ot[OT_LEN - 1]));
+}
/* Symbol overriding example */
@@ -118,8 +182,8 @@ void *custom_resolver(DLL *dll, const char *name) {
// Define a struct to store pointers to a DLL's functions into. This is not
// strictly required, however looking up symbols is a relatively slow operation
-// and the pointers returned by dlsym() should be saved and reused as much as
-// possible.
+// and the pointers returned by DL_GetDLLSymbol() should be saved and reused as
+// much as possible.
typedef struct {
void (*init)(CONTEXT *);
void (*render)(CONTEXT *, uint16_t buttons);
@@ -134,47 +198,79 @@ static CONTEXT ctx;
#define SHOW_STATUS(...) { FntPrint(-1, __VA_ARGS__); FntFlush(-1); display(&ctx); }
#define SHOW_ERROR(...) { SHOW_STATUS(__VA_ARGS__); while (1) __asm__("nop"); }
+// This is a simple function to read a CD-ROM file into memory (the dynamic
+// linker no longer provides APIs that take file paths directly). You might
+// want to replace this with code that e.g. loads the DLL from a compressed
+// archive or even from the serial port.
+size_t load_file(const char *filename, void **ptr) {
+ SHOW_STATUS("LOADING %s\n", filename);
+
+ CdlFILE file;
+ if (!CdSearchFile(&file, filename))
+ SHOW_ERROR("FAILED TO FIND %s\n", filename);
+
+ // Round up the file size so it matches the number of sectors occupied by
+ // the file (as CdRead() can only return entire sectors).
+ size_t len = (file.size + 2047) & 0xfffff800;
+ void *_ptr = malloc(len);
+ if (!_ptr)
+ SHOW_ERROR("FAILED TO ALLOCATE %d BYTES\n", len);
+
+ CdControl(CdlSetloc, &(file.pos), 0);
+ CdRead(len / 2048, _ptr, CdlModeSpeed);
+ if (CdReadSync(0, 0) < 0)
+ SHOW_ERROR("FAILED TO READ %s\n", filename);
+
+ *ptr = _ptr;
+ return file.size;
+}
+
void load_dll(const char *filename) {
+ // As we're passing RTLD_FREE_ON_DESTROY to DL_CreateDLL(), calling
+ // DL_DestroyDLL() will also deallocate the buffer the DLL was loaded into.
if (dll)
- dlclose(dll);
+ DL_DestroyDLL(dll);
- SHOW_STATUS("LOADING %s\n", filename);
+ void *ptr;
+ size_t len = load_file(filename, &ptr);
- dll = dlopen(filename, RTLD_LAZY);
+ dll = DL_CreateDLL(ptr, len, RTLD_LAZY | RTLD_FREE_ON_DESTROY);
if (!dll)
- SHOW_ERROR("FAILED TO LOAD %s\nERROR=%d\n", filename, (int32_t) dlerror());
+ SHOW_ERROR("FAILED TO PARSE %s\nERROR=%d\n", filename, (int32_t) DL_GetLastError());
- dll_api.init = dlsym(dll, "init");
- dll_api.render = dlsym(dll, "render");
+ dll_api.init = DL_GetDLLSymbol(dll, "init");
+ dll_api.render = DL_GetDLLSymbol(dll, "render");
printf("DLL init() @ %08x, render() @ %08x\n", dll_api.init, dll_api.render);
// Unfortunately, due to how position-independent code works, function
- // pointers returned by dlsym() can't be called directly. We have to use
- // the DL_CALL() macro instead, which sets up register $t9 to ensure the
- // function can locate and reference the DLL's relocation table.
+ // pointers returned by DL_GetDLLSymbol() can't be called directly. We have
+ // to use the DL_CALL() macro instead, which sets up register $t9 to ensure
+ // the function can locate and reference the DLL's relocation table.
DL_CALL(dll_api.init, &ctx);
}
int main(int argc, const char* argv[]) {
- // As DL_LoadSymbolMap() and dlopen() rely on BIOS file APIs, the BIOS CD
- // driver must be initialized by calling _InitCd() prior to loading the
- // symbol map (but after setting up the GPU, for some reason).
init_context(&ctx);
SHOW_STATUS("INITIALIZING CD\n");
- _InitCd();
+ CdInit();
+
+ // Load the symbol map file, let the dynamic linker parse it and then
+ // unload it.
+ void *ptr;
+ size_t len = load_file("\\MAIN.MAP;1", &ptr);
- SHOW_STATUS("LOADING SYMBOL MAP\n");
+ if (!DL_ParseSymbolMap(ptr, len))
+ SHOW_ERROR("FAILED TO PARSE SYMBOL MAP\nERROR=%d\n", (int32_t) DL_GetLastError());
- if (!DL_LoadSymbolMap("cdrom:MAIN.MAP;1"))
- SHOW_ERROR("FAILED TO LOAD SYMBOL MAP\nERROR=%d\n", (int32_t) dlerror());
+ free(ptr);
// Try to obtain a reference to a local function.
void (*_display)() = DL_GetSymbolByName("display");
if (!_display)
- SHOW_ERROR("FAILED TO LOOK UP LOCAL FUNCTION\nERROR=%d\n", (int32_t) dlerror());
+ SHOW_ERROR("FAILED TO LOOK UP LOCAL FUNCTION\nERROR=%d\n", (int32_t) DL_GetLastError());
printf("Symbol map test, display() @ %08x\n", _display);
@@ -220,7 +316,7 @@ int main(int argc, const char* argv[]) {
last_buttons = pad->btn;
}
- //dlclose(dll);
+ //DL_DestroyDLL(dll);
//DL_UnloadSymbolMap();
return 0;
}
diff --git a/libpsn00b/include/dlfcn.h b/libpsn00b/include/dlfcn.h
index b3a5cec..6874d06 100644
--- a/libpsn00b/include/dlfcn.h
+++ b/libpsn00b/include/dlfcn.h
@@ -21,39 +21,39 @@
#define RTLD_DEFAULT ((DLL *) 0)
typedef enum _DL_Error {
- RTLD_E_NONE = 0, // No error
- RTLD_E_FILE_OPEN = 1, // Unable to find or open file
- RTLD_E_FILE_ALLOC = 2, // Unable to allocate buffer to load file into
- RTLD_E_FILE_READ = 3, // Failed to read file
- RTLD_E_NO_MAP = 4, // No symbol map has been loaded yet
- RTLD_E_MAP_ALLOC = 5, // Unable to allocate symbol map structures
- RTLD_E_NO_SYMBOLS = 6, // No symbols found in symbol map
- RTLD_E_DLL_NULL = 7, // Unable to initialize DLL from null pointer
- RTLD_E_DLL_ALLOC = 8, // Unable to allocate DLL metadata structures
- RTLD_E_DLL_FORMAT = 9, // Unsupported DLL type or format
- RTLD_E_NO_FILE_API = 10, // psxetc has been built without file support
- RTLD_E_MAP_SYMBOL = 11, // Symbol not found in symbol map
- RTLD_E_DLL_SYMBOL = 12, // Symbol not found in DLL
- RTLD_E_HASH_LOOKUP = 13 // Hash table lookup failed due to internal error
+ RTLD_E_NONE = 0, // No error
+ RTLD_E_FILE_OPEN = 1, // Unable to find or open file
+ RTLD_E_FILE_ALLOC = 2, // Unable to allocate buffer to load file into
+ RTLD_E_FILE_READ = 3, // Failed to read file
+ RTLD_E_NO_MAP = 4, // No symbol map has been loaded yet
+ RTLD_E_MAP_ALLOC = 5, // Unable to allocate symbol map structures
+ RTLD_E_NO_SYMBOLS = 6, // No symbols found in symbol map
+ RTLD_E_DLL_NULL = 7, // Unable to initialize DLL from null pointer
+ RTLD_E_DLL_ALLOC = 8, // Unable to allocate DLL metadata structures
+ RTLD_E_DLL_FORMAT = 9, // Unsupported DLL type or format
+ RTLD_E_MAP_SYMBOL = 10, // Symbol not found in symbol map
+ RTLD_E_DLL_SYMBOL = 11, // Symbol not found in DLL
+ RTLD_E_HASH_LOOKUP = 12 // Hash table lookup failed due to internal error
} DL_Error;
typedef enum _DL_ResolveMode {
- RTLD_LAZY = 1, // Resolve functions when they are first called (default)
- RTLD_NOW = 2 // Resolve all symbols immediately on load
+ RTLD_LAZY = 1, // Resolve functions when they are first called (default)
+ RTLD_NOW = 2, // Resolve all symbols immediately on load
+ RTLD_FREE_ON_DESTROY = 4 // Automatically free DLL buffer when closing DLL
} DL_ResolveMode;
// Members of this struct should not be accessed directly in most cases, but
// they are intentionally exposed for easier expandability.
typedef struct _DLL {
- void *ptr;
- void *malloc_ptr;
- size_t size;
- const uint32_t *hash;
- uint32_t *got;
- Elf32_Sym *symtab;
- const char *strtab;
- uint16_t symbol_count;
- uint16_t got_length;
+ void *ptr;
+ void *malloc_ptr;
+ size_t size;
+ const uint32_t *hash;
+ uint32_t *got;
+ Elf32_Sym *symtab;
+ const char *strtab;
+ uint16_t symbol_count;
+ uint16_t got_length;
} DLL;
/* API */
@@ -62,8 +62,6 @@ typedef struct _DLL {
extern "C" {
#endif
-// TODO: rewrite these javadoc comments into proper documentation
-
/**
* @brief Reads the symbol table from the provided string buffer (which may or
* may not be null-terminated), parses it and stores the parsed entries into a
@@ -80,13 +78,14 @@ extern "C" {
* file in the appropriate format after building the executable, by using this
* command:
*
- * mipsel-unknown-elf-nm -f posix -l -n executable.elf >executable.map
+ * mipsel-none-elf-nm -f posix -l -n executable.elf >executable.map
*
* @param ptr
* @param size
* @return -1 or number of entries parsed
*/
int32_t DL_ParseSymbolMap(const char *ptr, size_t size);
+
/**
* @brief File wrapper around DL_ParseSymbolMap(). Allocates a temporary buffer
* then loads the specified map file into it (using BIOS APIs) and calls
@@ -96,14 +95,16 @@ int32_t DL_ParseSymbolMap(const char *ptr, size_t size);
* @param filename Must always contain device name, e.g. "cdrom:MODULE.DLL;1"
* @return -1 or number of entries parsed
*/
-int32_t DL_LoadSymbolMap(const char *filename);
+//int32_t DL_LoadSymbolMapFromFile(const char *filename);
+
/**
* @brief Frees internal buffers containing the currently loaded symbol map.
* This is automatically done before loading a new symbol map so there is no
* need to call this function in most cases, however it can still be useful to
* free up space on the heap once the symbol map is no longer needed.
*/
-void DL_UnloadSymbolMap(void);
+void DL_UnloadSymbolMap(void);
+
/**
* @brief Queries the currently loaded symbol map for the symbol with the given
* name and returns a pointer to it, which can then be used to directly access
@@ -112,7 +113,8 @@ void DL_UnloadSymbolMap(void);
* @param name
* @return NULL or pointer to symbol (any type)
*/
-void *DL_GetSymbolByName(const char *name);
+void *DL_GetSymbolByName(const char *name);
+
/**
* @brief Sets a custom function to be called for resolving symbols in DLLs.
* The function will be given a pointer to the current DLL and the unresolved
@@ -123,7 +125,7 @@ void *DL_GetSymbolByName(const char *name);
*
* @param callback NULL or pointer to callback function
*/
-void DL_SetResolveCallback(void *(*callback)(DLL *, const char *));
+void DL_SetResolveCallback(void *(*callback)(DLL *, const char *));
/**
* @brief Initializes a buffer holding the contents of a dynamically-loaded
@@ -131,8 +133,8 @@ void DL_SetResolveCallback(void *(*callback)(DLL *, const char *));
* binary) *in-place*. A new DLL struct is allocated to store metadata but,
* unlike DL_ParseSymbolMap(), the DLL's actual code, data and tables are
* referenced directly from the provided buffer. The buffer must not be moved
- * or deallocated, at least not before calling dlclose() on the DLL struct
- * returned by this function.
+ * or deallocated, at least not before calling DL_DestroyDLL() on the DLL
+ * struct returned by this function.
*
* The third argument specifies when symbols in the DLL should be resolved.
* Setting it to RTLD_LAZY defers resolution of undefined functions to when
@@ -145,7 +147,8 @@ void DL_SetResolveCallback(void *(*callback)(DLL *, const char *));
* @param mode RTLD_LAZY or RTLD_NOW
* @return NULL or pointer to a new DLL struct
*/
-DLL *dlinit(void *ptr, size_t size, DL_ResolveMode mode);
+DLL *DL_CreateDLL(void *ptr, size_t size, DL_ResolveMode mode);
+
/**
* @brief File wrapper around dlinit(). Allocates a new buffer, loads the
* specified file into it (using BIOS APIs) and calls dlinit() on that. When
@@ -153,19 +156,22 @@ DLL *dlinit(void *ptr, size_t size, DL_ResolveMode mode);
* automatically destroyed.
*
* @param filename Must always contain device name, e.g. "cdrom:MODULE.DLL;1"
- * @param mode RTLD_LAZY or RTLD_NOW
+ * @param mode RTLD_LAZY or RTLD_NOW + optionally RTLD_FREE_ON_DESTROY
* @return NULL or pointer to a new DLL struct
*/
-DLL *dlopen(const char *filename, DL_ResolveMode mode);
+//DLL *DL_LoadDLLFromFile(const char *filename, DL_ResolveMode mode);
+
/**
* @brief Destroys a loaded DLL by calling its global destructors and freeing
- * the buffer it's loaded in. Any pointer passed to dlclose() should no longer
- * be used after the call. If the DLL was initialized in-place using dlinit(),
- * dlclose() will *NOT* free the buffer initially passed to dlinit().
+ * the buffer it's loaded in. Any pointer passed to DL_DestroyDLL() should no
+ * longer be used after the call. If the DLL was initialized in-place using
+ * DL_CreateDLL(), DL_DestroyDLL() will only free the buffer initially passed
+ * to DL_CreateDLL() if RTLD_FREE_ON_DESTROY was used.
*
* @param dll
*/
-void dlclose(DLL *dll);
+void DL_DestroyDLL(DLL *dll);
+
/**
* @brief Returns a pointer to the DLL symbol with the given name, or null if
* it can't be found. If null or RTLD_DEFAULT is passed as first argument, the
@@ -176,7 +182,8 @@ void dlclose(DLL *dll);
* @param name
* @return NULL or pointer to symbol (any type)
*/
-void *dlsym(DLL *dll, const char *name);
+void *DL_GetDLLSymbol(const DLL *dll, const char *name);
+
/**
* @brief Returns a code describing the last error that occurred, or DL_E_NONE
* if no error has occurred since the last call to dlerror() (i.e. calling this
@@ -184,7 +191,15 @@ void *dlsym(DLL *dll, const char *name);
*
* @return NULL or member of DL_Error enum
*/
-DL_Error dlerror(void);
+DL_Error DL_GetLastError(void);
+
+/* POSIX "compatibility" macros */
+
+#define dlinit(ptr, size, mode) DL_CreateDLL(ptr, size, mode)
+//#define dlopen(filename, mode) DL_LoadDLLFromFile(filename, mode)
+#define dlsym(dll, name) DL_GetDLLSymbol(dll, name)
+#define dlclose(dll) DL_DestroyDLL(dll)
+#define dlerror() DL_GetLastError()
#ifdef __cplusplus
}
diff --git a/libpsn00b/psxetc/_dl_resolve_wrapper.s b/libpsn00b/psxetc/_dl_resolve_wrapper.s
index 069ee84..7f1132b 100644
--- a/libpsn00b/psxetc/_dl_resolve_wrapper.s
+++ b/libpsn00b/psxetc/_dl_resolve_wrapper.s
@@ -4,7 +4,7 @@
# This function is called by the lazy loader stubs generated by GCC in the
# .plt/.MIPS.stubs section when attempting to call a GOT entry whose address
# hasn't yet been resolved. The generated stubs conform to the MIPS ABI and
-# uses the following registers to pass the following parameters:
+# uses the following registers:
# - $t7 = address the resolved function should return to (i.e. $ra of the
# caller that triggered the stub)
# - $t8 = index of the function in the .dynsym symbol table
@@ -25,13 +25,13 @@ _dl_resolve_wrapper:
# Figure out where the DLL's struct is. dlinit() places a pointer to the
# struct in the second GOT entry, so it's just a matter of indexing the GOT
- # using GP. Then call _dl_resolve_helper with the struct and $t8 as
+ # using $gp. Then call _dl_resolve_helper with the struct and $t8 as
# arguments, and store the return value into $t0.
- lw $a0, -0x7fec($gp) # sizeof(uint32_t) - 0x7ff0 (see dll.ld)
+ lw $a0, -0x7fec($gp) # (DLL *) sizeof(uint32_t) - 0x7ff0 [see dll.ld]
move $a1, $t8
jal _dl_resolve_helper
- addiu $sp, -8 # (branch delay)
+ addiu $sp, -8
addiu $sp, 8
move $t0, $v0
diff --git a/libpsn00b/psxetc/dl.c b/libpsn00b/psxetc/dl.c
index e43374f..cbdcb66 100644
--- a/libpsn00b/psxetc/dl.c
+++ b/libpsn00b/psxetc/dl.c
@@ -38,8 +38,9 @@
//#define DEBUG
// Comment before building to disable functions that rely on BIOS file APIs,
-// i.e. DL_LoadSymbolMap() and dlopen().
-#define USE_FILE_API
+// i.e. DL_LoadSymbolMapFromFile() and DL_LoadDLLFromFile().
+// FIXME: those seem to be broken currently, and shouldn't be used anyway
+//#define USE_FILE_API
/* Private types */
@@ -132,7 +133,7 @@ static uint32_t _elf_hash(const char *str) {
}
#ifdef USE_FILE_API
-static uint8_t *_load_file(const char *filename, size_t *size_output) {
+static uint8_t *_dl_load_file(const char *filename, size_t *size_output) {
int32_t fd = open(filename, 1);
if (fd < 0) {
_LOG("psxetc: Can't open %s, error = %d\n", filename, fd);
@@ -286,10 +287,10 @@ int32_t DL_ParseSymbolMap(const char *ptr, size_t size) {
return entries;
}
-int32_t DL_LoadSymbolMap(const char *filename) {
#ifdef USE_FILE_API
+int32_t DL_LoadSymbolMapFromFile(const char *filename) {
size_t size;
- char *ptr = _load_file(filename, &size);
+ char *ptr = _dl_load_file(filename, &size);
if (!ptr)
return -1;
@@ -297,10 +298,8 @@ int32_t DL_LoadSymbolMap(const char *filename) {
free(ptr);
return entries;
-#else
- _ERROR(RTLD_E_NO_FILE_API, -1);
-#endif
}
+#endif
void DL_UnloadSymbolMap(void) {
if (!_symbol_map.entries)
@@ -354,7 +353,7 @@ void DL_SetResolveCallback(void *(*callback)(DLL *, const char *)) {
/* Library loading and linking API */
-DLL *dlinit(void *ptr, size_t size, DL_ResolveMode mode) {
+DLL *DL_CreateDLL(void *ptr, size_t size, DL_ResolveMode mode) {
if (!ptr)
_ERROR(RTLD_E_DLL_NULL, 0);
@@ -365,7 +364,7 @@ DLL *dlinit(void *ptr, size_t size, DL_ResolveMode mode) {
}
dll->ptr = ptr;
- dll->malloc_ptr = 0;
+ dll->malloc_ptr = (mode & RTLD_FREE_ON_DESTROY) ? ptr : 0;
dll->size = size;
_LOG("psxetc: Initializing DLL at %08x\n", ptr);
@@ -548,7 +547,7 @@ DLL *dlinit(void *ptr, size_t size, DL_ResolveMode mode) {
// If RTLD_NOW was passed, resolve GOT entries ahead of time by
// cross-referencing them with the symbol table.
- if (mode != RTLD_NOW)
+ if (!(mode & RTLD_NOW))
continue;
for (uint32_t j = got_offset; j < dll->got_length; j++) {
@@ -584,7 +583,7 @@ DLL *dlinit(void *ptr, size_t size, DL_ResolveMode mode) {
// _start() for regular executables, but we have to do it outside of the
// DLL as there's no _start() or even a defined entry point within the
// DLL itself.
- const uint32_t *ctor_list = dlsym(dll, "__CTOR_LIST__");
+ const uint32_t *ctor_list = DL_GetDLLSymbol(dll, "__CTOR_LIST__");
if (ctor_list) {
for (uint32_t i = ((uint32_t) ctor_list[0]); i >= 1; i--) {
void (*ctor)(void) = (void (*)(void)) ctor_list[i];
@@ -595,32 +594,28 @@ DLL *dlinit(void *ptr, size_t size, DL_ResolveMode mode) {
return dll;
}
-DLL *dlopen(const char *filename, DL_ResolveMode mode) {
#ifdef USE_FILE_API
+DLL *DL_LoadDLLFromFile(const char *filename, DL_ResolveMode mode) {
size_t size;
- char *ptr = _load_file(filename, &size);
+ char *ptr = _dl_load_file(filename, &size);
if (!ptr)
return 0;
- DLL *dll = dlinit(ptr, size, mode);
- if (dll)
- dll->malloc_ptr = dll->ptr;
- else
+ DLL *dll = DL_CreateDLL(ptr, size, mode | RTLD_FREE_ON_DESTROY);
+ if (!dll)
free(ptr);
return dll;
-#else
- _ERROR(RTLD_E_NO_FILE_API, 0);
-#endif
}
+#endif
-void dlclose(DLL *dll) {
+void DL_DestroyDLL(DLL *dll) {
if (dll == RTLD_DEFAULT)
return;
if (dll->ptr) {
// Call the DLL's global destructors.
- const uint32_t *dtor_list = dlsym(dll, "__DTOR_LIST__");
+ const uint32_t *dtor_list = DL_GetDLLSymbol(dll, "__DTOR_LIST__");
if (dtor_list) {
for (uint32_t i = 0; i < ((uint32_t) dtor_list[0]); i++) {
void (*dtor)(void) = (void (*)(void)) dtor_list[i + 1];
@@ -629,15 +624,15 @@ void dlclose(DLL *dll) {
}
}
- // If the DLL is associated to a buffer allocated by dlopen(), free that
- // buffer.
+ // If the DLL is associated to a buffer allocated by DL_LoadDLLFromFile(),
+ // free that buffer.
if (dll->malloc_ptr)
free(dll->malloc_ptr);
free(dll);
}
-void *dlsym(DLL *dll, const char *name) {
+void *DL_GetDLLSymbol(const DLL *dll, const char *name) {
if (dll == RTLD_DEFAULT)
return DL_GetSymbolByName(name);
//return _dl_resolve_callback(RTLD_DEFAULT, name);
@@ -654,7 +649,7 @@ void *dlsym(DLL *dll, const char *name) {
// provided.
for (uint32_t i = bucket[hash_mod]; i != 0xffffffff;) {
if (i >= nchain) {
- _LOG("psxetc: dlsym() index out of bounds (i = %d, n = %d)\n", i, nchain);
+ _LOG("psxetc: DL_GetDLLSymbol() index out of bounds (i = %d, n = %d)\n", i, nchain);
_ERROR(RTLD_E_HASH_LOOKUP, 0);
}
@@ -673,7 +668,7 @@ void *dlsym(DLL *dll, const char *name) {
_ERROR(RTLD_E_DLL_SYMBOL, 0);
}
-DL_Error dlerror(void) {
+DL_Error DL_GetLastError(void) {
DL_Error last = _error_code;
_error_code = RTLD_E_NONE;
--
cgit v1.2.3
From de9047f568f2f3509b56a2b566d7353cae616eb7 Mon Sep 17 00:00:00 2001
From: spicyjpeg <88942473+spicyjpeg@users.noreply.github.com>
Date: Thu, 30 Dec 2021 14:58:14 +0100
Subject: Add known_bugs.md, fix sound/vagsample and declarations
---
CMakeLists.txt | 6 ++++
doc/known_bugs.md | 52 ++++++++++++++++++++++++++++++
examples/sound/vagsample/0proyt.h | 2 +-
examples/sound/vagsample/threedeeffeggzz.h | 2 +-
libpsn00b/include/psxcd.h | 6 ++--
libpsn00b/include/psxspu.h | 4 +--
libpsn00b/psxcd/psxcd.c | 8 ++---
7 files changed, 69 insertions(+), 11 deletions(-)
create mode 100644 doc/known_bugs.md
(limited to 'examples')
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6a20a0e..2079fd1 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -78,6 +78,12 @@ set(
-DCMAKE_INSTALL_PREFIX:PATH=${PROJECT_BINARY_DIR}/examples
)
+# Ensure PSn00bSDK isn't being built using the toolchain file from PSn00bSDK
+# itself (or from another version of it).
+if(CMAKE_TOOLCHAIN_FILE MATCHES ".*libpsn00b[/\\]cmake[/\\]sdk\.cmake$")
+ message(FATAL_ERROR "CMAKE_TOOLCHAIN_FILE is set to the toolchain file of an existing PSn00bSDK installation. It must be unset or overridden by passing '-DCMAKE_TOOLCHAIN_FILE=\"\"' to CMake.")
+endif()
+
## Subprojects
if(NOT EXISTS ${PROJECT_SOURCE_DIR}/tools/mkpsxiso/CMakeLists.txt)
diff --git a/doc/known_bugs.md b/doc/known_bugs.md
new file mode 100644
index 0000000..2af9e3f
--- /dev/null
+++ b/doc/known_bugs.md
@@ -0,0 +1,52 @@
+
+# Known PSn00bSDK bugs
+
+This is an incomplete list of things that are currently broken (or not behaving
+as they should, or untested on real hardware) and haven't yet been fixed.
+
+## Libraries
+
+`psxspu`:
+
+- Calls to `SpuSetTransferMode()` are ignored. SPU transfers are always
+ performed using DMA, which imposes limitations such as the data length having
+ to be a multiple of 64 bytes.
+
+`psxetc`:
+
+- `DL_LoadSymbolMapFromFile()`, `DL_LoadDLLFromFile()` and `dlopen()` have been
+ disabled due to bugs in the BIOS file APIs. The dynamic linker can still be
+ used by loading DLL binaries into RAM manually and calling `DL_CreateDLL()`
+ on them (see the `system/dynlink` example).
+
+## Tools
+
+- The `mkpsxiso` submodule is temporarily set to point to a fork of `mkpsxiso`
+ with bugfixed CMake scripts (the main repo is broken to the point it fails to
+ build). There is [another fork](https://github.com/CookiePLMonster/mkpsxiso)
+ which is currently work-in-progress and includes more fixes as well as a tool
+ to dump existing CD images: PSn00bSDK will switch back to the main `mkpsxiso`
+ repo once the changes get upstreamed.
+
+## Examples
+
+- `cdrom/cdxa` and `sound/spustream` demonstrate how to stream an audio file
+ from CD-ROM. Such a file isn't provided however, as PSn00bSDK does not yet
+ come with the tooling required for transcoding audio from a source file. In
+ order to run these examples you'll have to provide your own audio files,
+ convert them and build the CD image manually.
+
+- `demos/n00bdemo` suffers from flickering on real hardware, especially when
+ masking/stencil buffering is used.
+
+- `graphics/render2tex` gets stuck after initialization on real hardware.
+
+- `io/pads` seems to work on real hardware, but fails to automatically enable
+ analog mode on DualShock controllers. This example needs more testing with
+ official and unofficial controllers.
+
+- `io/system573` hasn't been tested on a real Konami System 573. It runs on
+ MAME, however MAME's System 573 emulation is *very* inaccurate.
+
+-----------------------------------------
+_Last updated on 2021-12-30 by spicyjpeg_
diff --git a/examples/sound/vagsample/0proyt.h b/examples/sound/vagsample/0proyt.h
index 4402b9a..73629f9 100644
--- a/examples/sound/vagsample/0proyt.h
+++ b/examples/sound/vagsample/0proyt.h
@@ -12616,6 +12616,6 @@ unsigned char proyt[] = {
0xf2,0xf0,0xf2,0x15,0xc2,0x1b,0x00,0x00,0x01,0xe3,0x31,0x13,0xf3,0x1f,0xe2,
0x2f,0x13,0xd6,0x20,0x6e,0x2d,0x1b,0x00,0x03,0x42,0xc3,0x14,0x30,0x20,0x21,
0x21,0x32,0x0e,0x32,0x12,0x42,0xf0,0x39,0x01,0x13,0x12,0x04,0xb3,0x06,0x1f,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x77,0x77,0x77,0x77,0x77,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x05,0x77,0x77,0x77,0x77,0x77,
0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77
};
diff --git a/examples/sound/vagsample/threedeeffeggzz.h b/examples/sound/vagsample/threedeeffeggzz.h
index ed0e098..f0815aa 100644
--- a/examples/sound/vagsample/threedeeffeggzz.h
+++ b/examples/sound/vagsample/threedeeffeggzz.h
@@ -15194,6 +15194,6 @@ unsigned char tdfx[] = {
0x0b,0x1e,0x21,0xa0,0x4f,0xe2,0x2d,0x17,0x00,0xd2,0x02,0x1d,0xd1,0xf0,0x21,
0xfa,0x2e,0xc4,0x1e,0xfe,0xf2,0x2f,0x24,0x17,0x00,0xef,0xee,0x1f,0xf2,0xce,
0x0d,0x25,0x01,0x3e,0xcb,0xe1,0x30,0xeb,0xc4,0x17,0x01,0x1f,0x1f,0xaf,0x22,
-0xfd,0xe0,0xf3,0x0b,0x2e,0xdd,0xc3,0xde,0x12,0x2d,0x07,0x00,0x77,0x77,0x77,
+0xfd,0xe0,0xf3,0x0b,0x2e,0xdd,0xc3,0xde,0x12,0x2d,0x07,0x05,0x77,0x77,0x77,
0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77
};
diff --git a/libpsn00b/include/psxcd.h b/libpsn00b/include/psxcd.h
index ffbe86b..3336963 100644
--- a/libpsn00b/include/psxcd.h
+++ b/libpsn00b/include/psxcd.h
@@ -132,9 +132,9 @@ CdlLOC* CdIntToPos(int i, CdlLOC *p);
int CdPosToInt(CdlLOC *p);
int CdGetToc(CdlLOC *toc);
-int CdControl(u_char com, u_char *param, u_char *result);
-int CdControlB(u_char com, u_char *param, u_char *result);
-int CdControlF(u_char com, u_char *param);
+int CdControl(u_char com, const void *param, u_char *result);
+int CdControlB(u_char com, const void *param, u_char *result);
+int CdControlF(u_char com, const void *param);
int CdSync(int mode, u_char *result);
u_long CdSyncCallback(CdlCB func);
diff --git a/libpsn00b/include/psxspu.h b/libpsn00b/include/psxspu.h
index a87e347..da000e3 100644
--- a/libpsn00b/include/psxspu.h
+++ b/libpsn00b/include/psxspu.h
@@ -115,7 +115,7 @@ extern "C" {
void SpuInit();
-void SpuSetVoiceRaw( int voice, SpuVoiceRaw* param );
+void SpuSetVoiceRaw( int voice, const SpuVoiceRaw* param );
void SpuReverbOn( int voice );
void SpuSetReverb();
@@ -128,7 +128,7 @@ void SpuSetKey(int on_off, u_int voice_bit);
// SPU transfer functions
int SpuSetTransferMode(int mode);
int SpuSetTransferStartAddr(int addr);
-int SpuWrite(unsigned char* addr, int size);
+int SpuWrite(const unsigned char* addr, int size);
void SpuWait();
#ifdef __cplusplus
diff --git a/libpsn00b/psxcd/psxcd.c b/libpsn00b/psxcd/psxcd.c
index 76415f9..8f19c8d 100644
--- a/libpsn00b/psxcd/psxcd.c
+++ b/libpsn00b/psxcd/psxcd.c
@@ -21,7 +21,7 @@ volatile int _cd_last_sector_count;
int _cd_media_changed;
void _cd_init(void);
-void _cd_control(unsigned char com, unsigned char *param, int plen);
+void _cd_control(unsigned char com, const void *param, int plen);
void _cd_wait_ack(void);
void _cd_wait(void);
@@ -50,7 +50,7 @@ int CdInit(void)
return 1;
}
-int CdControl(unsigned char com, unsigned char *param, unsigned char *result)
+int CdControl(unsigned char com, const void *param, unsigned char *result)
{
// Don't issue command if ack is not received yet
if( _cd_ack_wait )
@@ -72,7 +72,7 @@ int CdControl(unsigned char com, unsigned char *param, unsigned char *result)
return 1;
}
-int CdControlB(unsigned char com, unsigned char *param, unsigned char *result)
+int CdControlB(unsigned char com, const void *param, unsigned char *result)
{
if( !CdControl(com, param, result) )
{
@@ -83,7 +83,7 @@ int CdControlB(unsigned char com, unsigned char *param, unsigned char *result)
return 1;
}
-int CdControlF(unsigned char com, unsigned char *param)
+int CdControlF(unsigned char com, const void *param)
{
int param_len=0;
--
cgit v1.2.3
From de38196a978548b61c4b45115d24ef743b9eef90 Mon Sep 17 00:00:00 2001
From: spicyjpeg <88942473+spicyjpeg@users.noreply.github.com>
Date: Mon, 17 Jan 2022 15:57:04 +0100
Subject: Minor psxgpu/psxpad header changes
---
examples/io/pads/main.c | 37 ++--
libpsn00b/include/psxgpu.h | 2 +-
libpsn00b/include/psxpad.h | 328 ++++++++++++++++++---------------
libpsn00b/psxetc/_dl_resolve_wrapper.s | 6 +-
libpsn00b/psxgpu/gettimimage.c | 2 +-
5 files changed, 203 insertions(+), 172 deletions(-)
(limited to 'examples')
diff --git a/examples/io/pads/main.c b/examples/io/pads/main.c
index 92beb1c..cc4ef56 100644
--- a/examples/io/pads/main.c
+++ b/examples/io/pads/main.c
@@ -118,16 +118,16 @@ static volatile uint8_t pad_buff[2][34];
static volatile size_t pad_buff_len[2];
static volatile uint32_t pad_digital_only[2] = { 0, 0 };
-// Just a wrapper around spi_new_request(). This does not send the command
+// Just a wrapper around SPI_CreateRequest(). This does not send the command
// immediately but adds it to the driver's request queue.
void send_pad_cmd(
- uint32_t port,
- PAD_COMMAND cmd,
- uint8_t arg1,
- uint8_t arg2,
- SPICALLBACK callback
+ uint32_t port,
+ PadCommand cmd,
+ uint8_t arg1,
+ uint8_t arg2,
+ SPI_Callback callback
) {
- SPIREQUEST *req = spi_new_request();
+ SPI_request *req = SPI_CreateRequest();
req->len = 9;
req->port = port;
@@ -150,12 +150,12 @@ void send_pad_cmd(
// actually a DualShock in digital mode by checking if it started identifying
// as CONFIG_MODE after receiving a configuration command.
void dualshock_init_cb(uint32_t port, const volatile uint8_t *buff, size_t rx_len) {
- PADTYPE *pad = (PADTYPE *) buff;
+ PadResponse *pad = (PadResponse *) buff;
if (
(rx_len < 2) ||
- (pad->raw.prefix != 0x5a) ||
- (pad->raw.type != PAD_ID_CONFIG_MODE)
+ (pad->prefix != 0x5a) ||
+ (pad->type != PAD_ID_CONFIG_MODE)
) {
printf("no, pad is digital-only (len = %d)\n", rx_len);
@@ -187,7 +187,7 @@ void poll_cb(uint32_t port, const volatile uint8_t *buff, size_t rx_len) {
if (rx_len)
memcpy((void *) pad_buff[port], (void *) buff, rx_len);
- PADTYPE *pad = (PADTYPE *) buff;
+ PadResponse *pad = (PadResponse *) buff;
// If this pad identifies as a digital pad and hasn't been flagged as a
// digital-only pad already, attempt to put it into analog mode by entering
@@ -196,8 +196,8 @@ void poll_cb(uint32_t port, const volatile uint8_t *buff, size_t rx_len) {
// returning digital pad responses.
if (
rx_len &&
- (pad->raw.prefix == 0x5a) &&
- (pad->raw.type == PAD_ID_DIGITAL)
+ (pad->prefix == 0x5a) &&
+ (pad->type == PAD_ID_DIGITAL)
) {
if (!pad_digital_only[port]) {
printf("Detecting if pad %d supports config mode... ", port + 1);
@@ -221,7 +221,7 @@ static CONTEXT ctx;
int main(int argc, const char* argv[]) {
init_context(&ctx);
- spi_init(&poll_cb);
+ SPI_Init(&poll_cb);
uint32_t counter = 0;
@@ -238,15 +238,14 @@ int main(int argc, const char* argv[]) {
continue;
}
- PADTYPE *pad = (PADTYPE *) pad_buff[port];
- PAD_TYPEID type = pad->raw.type;
+ PadResponse *pad = (PadResponse *) pad_buff[port];
// According to nocash docs, there is a hardware bug in DualShock
// controllers that causes the prefix byte (normally 0x5a) to turn
// into 0x00 if the analog button is pressed after configuration
// commands have been used. Thus making sure the prefix is 0x5a
// isn't enough to reliably detect pads.
- /*if ((pad->raw.prefix != 0x5a) && (type != PAD_ID_ANALOG)) {
+ /*if ((pad->prefix != 0x5a) && (pad->type != PAD_ID_ANALOG)) {
FntPrint(-1, "\n\nPORT %d: INVALID RESPONSE\n", port + 1);
if ((counter % 64) < 32)
FntPrint(-1, " CHECK CONNECTION...");
@@ -258,8 +257,8 @@ int main(int argc, const char* argv[]) {
-1,
"\n\nPORT %d: %s (TYPE=%d)\n",
port + 1,
- PAD_TYPEIDS[type],
- type
+ PAD_TYPEIDS[pad->type],
+ pad->type
);
// Print a hexdump of the payload returned by the pad.
diff --git a/libpsn00b/include/psxgpu.h b/libpsn00b/include/psxgpu.h
index f50b841..f061219 100644
--- a/libpsn00b/include/psxgpu.h
+++ b/libpsn00b/include/psxgpu.h
@@ -609,7 +609,7 @@ void AddPrim(u_long* ot, void* pri);
// Function definitions (C)
-int GetTimInfo(u_long *tim, TIM_IMAGE *timimg); /* ORIGINAL */
+int GetTimInfo(const u_long *tim, TIM_IMAGE *timimg); /* ORIGINAL */
DISPENV *SetDefDispEnv(DISPENV *disp, int x, int y, int w, int h);
DRAWENV *SetDefDrawEnv(DRAWENV *draw, int x, int y, int w, int h);
diff --git a/libpsn00b/include/psxpad.h b/libpsn00b/include/psxpad.h
index d152896..9638ec1 100644
--- a/libpsn00b/include/psxpad.h
+++ b/libpsn00b/include/psxpad.h
@@ -14,43 +14,48 @@
#ifndef _PSXPAD_H
#define _PSXPAD_H
-// Pad button definitions for digital pad, joystick, dual analog,
-// Dualshock and Jogcon
-#define PAD_SELECT 1
-#define PAD_L3 2
-#define PAD_R3 4
-#define PAD_START 8
-#define PAD_UP 16
-#define PAD_RIGHT 32
-#define PAD_DOWN 64
-#define PAD_LEFT 128
-#define PAD_L2 256
-#define PAD_R2 512
-#define PAD_L1 1024
-#define PAD_R1 2048
-#define PAD_TRIANGLE 4096
-#define PAD_CIRCLE 8192
-#define PAD_CROSS 16384
-#define PAD_SQUARE 32768
-
-// Mouse button definitions
-#define MOUSE_RIGHT 1024
-#define MOUSE_LEFT 2048
-
-// neGcon button definitions
-#define NCON_START 8
-#define NCON_UP 16
-#define NCON_RIGHT 32
-#define NCON_DOWN 64
-#define NCON_LEFT 128
-#define NCON_R 256
-#define NCON_B 512
-#define NCON_A 1024
-
-// Guncon button definitions
-#define GCON_A 8
-#define GCON_TRIGGER 8192
-#define GCON_B 16384
+#include
+
+/* Controller type and button definitions */
+
+typedef enum {
+ // Standard pads, analog joystick, Jogcon
+ PAD_SELECT = 1 << 0,
+ PAD_L3 = 1 << 1,
+ PAD_R3 = 1 << 2,
+ PAD_START = 1 << 3,
+ PAD_UP = 1 << 4,
+ PAD_RIGHT = 1 << 5,
+ PAD_DOWN = 1 << 6,
+ PAD_LEFT = 1 << 7,
+ PAD_L2 = 1 << 8,
+ PAD_R2 = 1 << 9,
+ PAD_L1 = 1 << 10,
+ PAD_R1 = 1 << 11,
+ PAD_TRIANGLE = 1 << 12,
+ PAD_CIRCLE = 1 << 13,
+ PAD_CROSS = 1 << 14,
+ PAD_SQUARE = 1 << 15,
+
+ // Mouse
+ MOUSE_LEFT = 1 << 10,
+ MOUSE_RIGHT = 1 << 11,
+
+ // neGcon
+ NCON_START = 1 << 3,
+ NCON_UP = 1 << 4,
+ NCON_RIGHT = 1 << 5,
+ NCON_DOWN = 1 << 6,
+ NCON_LEFT = 1 << 7,
+ NCON_R = 1 << 8,
+ NCON_B = 1 << 9,
+ NCON_A = 1 << 10,
+
+ // Guncon
+ GCON_A = 1 << 3,
+ GCON_TRIGGER = 1 << 13,
+ GCON_B = 1 << 14
+} PadButton;
typedef enum {
PAD_ID_MOUSE = 0x1, // Sony PS1 mouse
@@ -64,9 +69,10 @@ typedef enum {
PAD_ID_JOGCON = 0xe, // Namco Jogcon
PAD_ID_CONFIG_MODE = 0xf, // Dual Analog/DualShock in config mode (if len == 0x3)
PAD_ID_NONE = 0xf // No pad connected (if len == 0xf)
-} PAD_TYPEID;
+} PadTypeID;
+
+/* Pad and memory card commands */
-// Controller command definitions
typedef enum {
PAD_CMD_INIT_PRESSURE = '@', // Initialize DS2 button pressure sensors (in config mode)
PAD_CMD_READ = 'B', // Read pad state and set rumble
@@ -74,131 +80,155 @@ typedef enum {
PAD_CMD_SET_ANALOG = 'D', // Set analog mode/LED state (in config mode)
PAD_CMD_GET_ANALOG = 'E', // Get analog mode/LED state (in config mode)
PAD_CMD_REQUEST_CONFIG = 'M', // Configure request/unlock vibration (in config mode)
- PAD_CMD_RESPONSE_CONFIG = 'O' // Configure response/unlock DS2 pressure (in config mode)
-} PAD_COMMAND;
+ PAD_CMD_RESPONSE_CONFIG = 'O', // Configure response/unlock DS2 pressure (in config mode)
-// Memory card command/response definitions
-typedef enum {
- MCD_CMD_READ = 'R', // Read sector
- MCD_CMD_IDENTIFY = 'S', // Retrieve ID and card size information
- MCD_CMD_WRITE = 'W' // Write sector
-} MCD_COMMAND;
+ MCD_CMD_READ_SECTOR = 'R', // Read 128-byte sector
+ MCD_CMD_IDENTIFY = 'S', // Retrieve ID and card size information (Sony cards only)
+ MCD_CMD_WRITE_SECTOR = 'W' // Erase and write 128-byte sector
+} PadCommand;
typedef enum {
MCD_STAT_OK = 'G',
MCD_STAT_BAD_CHECKSUM = 'N',
MCD_STAT_BAD_SECTOR = 0xff
-} MCD_STATUS;
-
-#define MCD_CMD_READ_LEN 139
-#define MCD_CMD_IDENTIFY_LEN 9
-#define MCD_CMD_WRITE_LEN 137
-
-// Memory card status flags
-#define MCD_FLAG_WRITE_ERROR 4 // Last write command failed
-#define MCD_FLAG_NOT_WRITTEN 8 // No writes have been issued yet
-#define MCD_FLAG_UNKNOWN 16 // Might be set on third-party cards
-
-// Struct for data returned by controllers
-typedef struct _PADTYPE {
- union { // Header:
- struct __attribute__((packed)) { // When parsing data returned by BIOS:
- unsigned char stat; // Status
- unsigned char len:4; // Payload length / 2, 0 for multitap
- unsigned char type:4; // Device type (PAD_TYPEID)
+} MemCardStatus;
+
+typedef enum {
+ MCD_FLAG_WRITE_ERROR = 1 << 2, // Last write command failed
+ MCD_FLAG_NOT_WRITTEN = 1 << 3, // No writes have been issued yet
+ MCD_FLAG_UNKNOWN = 1 << 4 // Might be set on third-party cards
+} MemCardStatusFlag;
+
+#define MEMCARD_CMD_READ_LEN 139
+#define MEMCARD_CMD_IDENTIFY_LEN 9
+#define MEMCARD_CMD_WRITE_LEN 137
+
+/* Controller response as returned by BIOS driver */
+
+typedef struct __attribute__((packed)) _PADTYPE {
+ uint8_t stat; // Status
+ uint8_t len:4; // Payload length / 2, 0 for multitap
+ uint8_t type:4; // Device type (PadTypeID)
+
+ uint16_t btn; // Button states
+ union {
+ struct { // Analog controller:
+ uint8_t rs_x,rs_y; // - Right stick coordinates
+ uint8_t ls_x,ls_y; // - Left stick coordinates
+ uint8_t press[12]; // - Button pressure (DualShock 2 only)
};
- struct __attribute__((packed)) { // When parsing raw controller response:
- unsigned char len:4; // Payload length / 2, 0 for multitap
- unsigned char type:4; // Device type (PAD_TYPEID)
- unsigned char prefix; // Must be 0x5a
- } raw;
- };
- struct { // Payload:
- unsigned short btn; // Button states
- union {
- struct { // Analog controller:
- unsigned char rs_x,rs_y; // Right stick coordinates
- unsigned char ls_x,ls_y; // Left stick coordinates
- unsigned char press[12]; // Button pressure (DualShock 2 only)
- };
- struct { // Mouse:
- char x_mov; // X movement of mouse
- char y_mov; // Y movement of mouse
- };
- struct { // neGcon:
- unsigned char twist; // Controller twist
- unsigned char btn_i; // I button value
- unsigned char btn_ii; // II button value
- unsigned char trg_l; // L trigger value
- };
- struct { // Jogcon:
- unsigned short jog_rot; // Jog rotation
- };
- struct { // Guncon:
- unsigned short gun_x; // Gun X position in dotclocks
- unsigned short gun_y; // Gun Y position in scanlines
- };
+ struct { // Mouse:
+ int8_t x_mov; // - X movement of mouse
+ int8_t y_mov; // - Y movement of mouse
+ };
+ struct { // neGcon:
+ uint8_t twist; // - Controller twist
+ uint8_t btn_i; // - I button value
+ uint8_t btn_ii; // - II button value
+ uint8_t trg_l; // - L trigger value
+ };
+ struct { // Jogcon:
+ uint16_t jog_rot; // - Jog rotation
+ };
+ struct { // Guncon:
+ uint16_t gun_x; // - Gun X position in dotclocks
+ uint16_t gun_y; // - Gun Y position in scanlines
};
};
} PADTYPE;
-typedef struct _MCDRESPONSE {
- unsigned char flags; // Status flags
- unsigned char type1; // Must be 0x5a
- unsigned char type2; // Must be 0x5d
+//typedef struct _PADTYPE MOUSETYPE;
+//typedef struct _PADTYPE NCONTYPE;
+//typedef struct _PADTYPE JCONTYPE;
+//typedef struct _PADTYPE GCONTYPE;
+
+/* Raw responses */
+
+typedef struct __attribute__((packed)) _PadResponse {
+ uint8_t len:4; // Payload length / 2, 0 for multitap
+ uint8_t type:4; // Device type (PadTypeID)
+ uint8_t prefix; // Must be 0x5a
+
+ uint16_t btn; // Button states
+ union {
+ struct { // Analog controller:
+ uint8_t rs_x,rs_y; // - Right stick coordinates
+ uint8_t ls_x,ls_y; // - Left stick coordinates
+ uint8_t press[12]; // - Button pressure (DualShock 2 only)
+ };
+ struct { // Mouse:
+ int8_t x_mov; // - X movement of mouse
+ int8_t y_mov; // - Y movement of mouse
+ };
+ struct { // neGcon:
+ uint8_t twist; // - Controller twist
+ uint8_t btn_i; // - I button value
+ uint8_t btn_ii; // - II button value
+ uint8_t trg_l; // - L trigger value
+ };
+ struct { // Jogcon:
+ uint16_t jog_rot; // - Jog rotation
+ };
+ struct { // Guncon:
+ uint16_t gun_x; // - Gun X position in dotclocks
+ uint16_t gun_y; // - Gun Y position in scanlines
+ };
+ };
+} PadResponse;
+
+typedef struct __attribute__((packed)) _MemCardResponse {
+ uint8_t flags; // Status flags (MemCardStatusFlag)
+ uint8_t type1; // Must be 0x5a
+ uint8_t type2; // Must be 0x5d
+
union {
- struct { // MCD_CMD_READ response:
- unsigned char dummy[2];
- unsigned char ack1; // Must be 0x5c
- unsigned char ack2; // Must be 0x5d
- unsigned char lba_h;
- unsigned char lba_l;
- unsigned char data[128];
- unsigned char checksum; // = lba_h ^ lba_l ^ data
- unsigned char stat; // Status (MCD_STATUS)
+ struct { // CMD_READ response:
+ uint8_t dummy[2];
+ uint8_t ack1; // Must be 0x5c
+ uint8_t ack2; // Must be 0x5d
+ uint8_t lba_h;
+ uint8_t lba_l;
+ uint8_t data[128];
+ uint8_t checksum; // = lba_h ^ lba_l ^ data
+ uint8_t stat; // Status (MemCardStatus)
} read;
- struct { // MCD_CMD_IDENTIFY response:
- unsigned char ack1; // Must be 0x5c
- unsigned char ack2; // Must be 0x5d
- unsigned char size_h; // Card capacity bits 8-15 (0x04 = 128KB)
- unsigned char size_l; // Card capacity bits 0-7 (0x00 = 128KB)
- unsigned char blksize_h; // Sector size bits 8-15 (must be 0x00)
- unsigned char blksize_l; // Sector size bits 0-7 (must be 0x80)
+ struct { // CMD_IDENTIFY response:
+ uint8_t ack1; // Must be 0x5c
+ uint8_t ack2; // Must be 0x5d
+ uint8_t size_h; // Card capacity bits 8-15 (0x04 = 128KB)
+ uint8_t size_l; // Card capacity bits 0-7 (0x00 = 128KB)
+ uint8_t blksize_h; // Sector size bits 8-15 (must be 0x00)
+ uint8_t blksize_l; // Sector size bits 0-7 (must be 0x80)
} identify;
- struct { // MCD_CMD_WRITE response:
- unsigned char dummy[131];
- unsigned char ack1; // Must be 0x5c
- unsigned char ack2; // Must be 0x5d
- unsigned char stat; // Status (MCD_STATUS)
+ struct { // CMD_WRITE response:
+ uint8_t dummy[131];
+ uint8_t ack1; // Must be 0x5c
+ uint8_t ack2; // Must be 0x5d
+ uint8_t stat; // Status (MemCardStatus)
} write;
};
-} MCDRESPONSE;
-
-//typedef PADTYPE MOUSETYPE;
-//typedef PADTYPE NCONTYPE;
-//typedef PADTYPE JCONTYPE;
-//typedef PADTYPE GCONTYPE;
-
-// Structs for raw controller request
-typedef struct _PADREQUEST {
- unsigned char addr; // Must be 0x01 (or 02/03/04 for multitap pads)
- unsigned char cmd; // Command (PAD_COMMAND)
- unsigned char tap_mode; // 0x01 to enable multitap response
- unsigned char motor_r; // Right motor control (on/off)
- unsigned char motor_l; // Left motor control (PWM)
- unsigned char dummy[4];
-} PADREQUEST;
-
-// Structs for raw memory card request
-typedef struct _MCDREQUEST {
- unsigned char addr; // Must be 0x81 (or 02/03/04 for multitap cards)
- unsigned char cmd; // Command (MCD_COMMAND)
- unsigned char dummy[2];
- unsigned char lba_h; // Sector address bits 8-15 (dummy for CMD_IDENTIFY)
- unsigned char lba_l; // Sector address bits 0-7 (dummy for CMD_IDENTIFY)
- unsigned char data[128]; // Sector payload (dummy for CMD_READ/CMD_IDENTIFY)
- unsigned char checksum; // = lba_h ^ lba_l ^ data (CMD_WRITE only)
- unsigned char dummy2[3];
-} MCDREQUEST;
+} MemCardResponse;
+
+/* Raw requests */
+
+typedef struct __attribute__((packed)) _PadRequest {
+ uint8_t addr; // Must be 0x01 (or 02/03/04 for multitap pads)
+ uint8_t cmd; // Command (PadCommand)
+ uint8_t tap_mode; // 0x01 to enable multitap response
+ uint8_t motor_r; // Right motor control (on/off)
+ uint8_t motor_l; // Left motor control (PWM)
+ uint8_t dummy[4];
+} PadRequest;
+
+typedef struct __attribute__((packed)) _MemCardRequest {
+ uint8_t addr; // Must be 0x81 (or 02/03/04 for multitap cards)
+ uint8_t cmd; // Command (MemCardCommand)
+ uint8_t dummy[2];
+ uint8_t lba_h; // Sector address bits 8-15 (dummy for CMD_IDENTIFY)
+ uint8_t lba_l; // Sector address bits 0-7 (dummy for CMD_IDENTIFY)
+ uint8_t data[128]; // Sector payload (dummy for CMD_READ/CMD_IDENTIFY)
+ uint8_t checksum; // = lba_h ^ lba_l ^ data (CMD_WRITE only)
+ uint8_t dummy2[3];
+} MemCardRequest;
#endif
\ No newline at end of file
diff --git a/libpsn00b/psxetc/_dl_resolve_wrapper.s b/libpsn00b/psxetc/_dl_resolve_wrapper.s
index 7f1132b..01ebf3a 100644
--- a/libpsn00b/psxetc/_dl_resolve_wrapper.s
+++ b/libpsn00b/psxetc/_dl_resolve_wrapper.s
@@ -48,7 +48,9 @@ _dl_resolve_wrapper:
jr $t0
nop
-.global _dl_credits
-.type _dl_credits, @object
+.section .data
+
+.global _dl_credits
+.type _dl_credits, @object
_dl_credits:
.asciiz "psxetc runtime dynamic linker by spicyjpeg\n"
diff --git a/libpsn00b/psxgpu/gettimimage.c b/libpsn00b/psxgpu/gettimimage.c
index d9cf1bf..5598e07 100644
--- a/libpsn00b/psxgpu/gettimimage.c
+++ b/libpsn00b/psxgpu/gettimimage.c
@@ -1,7 +1,7 @@
#include
#include
-int GetTimInfo(u_long *tim, TIM_IMAGE *timimg) {
+int GetTimInfo(const u_long *tim, TIM_IMAGE *timimg) {
u_long *rtim;
--
cgit v1.2.3