aboutsummaryrefslogtreecommitdiff
path: root/examples
diff options
context:
space:
mode:
authorspicyjpeg <88942473+spicyjpeg@users.noreply.github.com>2021-11-11 00:37:10 +0100
committerspicyjpeg <88942473+spicyjpeg@users.noreply.github.com>2021-11-11 00:37:10 +0100
commit8b6a76055ca426c494e16042e21f5e19b870b2ac (patch)
treee6c875e8c1bd9169e4f49a1b4ad86026547d7020 /examples
parentb55bc88017aac1bbe9eab21b480093459c0fd0e1 (diff)
downloadpsn00bsdk-8b6a76055ca426c494e16042e21f5e19b870b2ac.tar.gz
Cleaned up pads example, added CMake script for cartrom
Diffstat (limited to 'examples')
-rw-r--r--examples/io/pads/main.c100
-rw-r--r--examples/io/pads/spi.c22
-rw-r--r--examples/lowlevel/cartrom/CMakeLists.txt36
-rw-r--r--examples/lowlevel/cartrom/makefile14
-rw-r--r--examples/system/dynlink/display.c8
5 files changed, 114 insertions, 66 deletions
diff --git a/examples/io/pads/main.c b/examples/io/pads/main.c
index bb56a27..ea7219b 100644
--- a/examples/io/pads/main.c
+++ b/examples/io/pads/main.c
@@ -15,9 +15,12 @@
* but the code in spi.c can be used to read/write sectors on a memory card and
* combined with a higher-level filesystem driver for full support.
*
- * IMPORTANT: this example doesn't work on pcsx-redux, and hasn't yet been
- * tested on real hardware and/or with unofficial controllers. It works on
- * DuckStation and no$psx though.
+ * IMPORTANT: this example hasn't yet been tested on real hardware and/or with
+ * unofficial controllers, which might behave differently at higher poll rates.
+ * Also keep in mind that many emulators emulate controllers and memory cards
+ * inaccurately. It is thus recommended to test controller I/O code extensively
+ * and handle as many edge cases as possible (e.g. partial but valid responses,
+ * zerofilled responses, slow replies) for maximum compatibility.
*/
#include <sys/types.h>
@@ -54,6 +57,10 @@ static const char *const PAD_TYPEIDS[] = {
#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;
@@ -73,14 +80,14 @@ void init_context(CONTEXT *ctx) {
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), 63, 0, 127);
+ 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), 63, 0, 127);
+ setRGB0(&(db->draw), BGCOLOR_R, BGCOLOR_G, BGCOLOR_B);
db->draw.isbg = 1;
db->draw.dtd = 1;
@@ -139,13 +146,17 @@ void send_pad_cmd(
);
}
-// This callback determines whether a pad that identifies as digital is
-// actually a DualShock in digital mode by checking how it replied to the
-// CONFIG_MODE command.
+// This callback determines whether a pad that identified as digital is
+// 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) {
- // Flag the pad as digital-only if it didn't reply.
- // FIXME: rx_len should be 8 for digital pads, but sometimes it's only 4
- if (rx_len < 4) {
+ PADTYPE *pad = (PADTYPE *) buff;
+
+ if (
+ (rx_len < 2) ||
+ (pad->raw.prefix != 0x5a) ||
+ (pad->raw.type != PAD_ID_CONFIG_MODE)
+ ) {
printf("no, pad is digital-only (len = %d)\n", rx_len);
pad_digital_only[port] = 1;
@@ -154,9 +165,13 @@ void dualshock_init_cb(uint32_t port, const volatile uint8_t *buff, size_t rx_le
printf("yes, forcing analog mode (len = %d)\n", rx_len);
- // Issue further commands to finish configuring the controller.
+ // Issue further commands to force analog mode on, unlock rumble (not used
+ // in this example) and enable longer responses containing button pressure
+ // readings.
+ // TODO: find out if passing 0x03 instead of 0x02 in PAD_CMD_SET_ANALOG
+ // locks the analog button, as emulated by DuckStation...
// https://gist.github.com/scanlime/5042071
- send_pad_cmd(port, PAD_CMD_SET_ANALOG, 0x01, 0x02, 0); // 0x03 disables analog button...?
+ send_pad_cmd(port, PAD_CMD_SET_ANALOG, 0x01, 0x02, 0);
send_pad_cmd(port, PAD_CMD_INIT_PRESSURE, 0x00, 0x00, 0); // Ignored by DualShock 1
send_pad_cmd(port, PAD_CMD_REQUEST_CONFIG, 0x00, 0x01, 0);
send_pad_cmd(port, PAD_CMD_RESPONSE_CONFIG, 0xff, 0xff, 0); // Ignored by DualShock 1
@@ -179,21 +194,22 @@ void poll_cb(uint32_t port, const volatile uint8_t *buff, size_t rx_len) {
// configuration mode. It this fails, it will be flagged as digital-only.
// The digital-only flag is reset when the controller is unplugged or stops
// returning digital pad responses.
- if ((pad->raw.prefix == 0x5a) && (pad->raw.type == PAD_ID_DIGITAL)) {
+ if (
+ rx_len &&
+ (pad->raw.prefix == 0x5a) &&
+ (pad->raw.type == PAD_ID_DIGITAL)
+ ) {
if (!pad_digital_only[port]) {
printf("Detecting if pad %d supports config mode... ", port + 1);
- send_pad_cmd(
- port,
- PAD_CMD_CONFIG_MODE,
- 0x01,
- 0x00,
- &dualshock_init_cb
- );
+ // The pad only identifies as CONFIG_MODE after at least another
+ // command is sent.
+ send_pad_cmd(port, PAD_CMD_CONFIG_MODE, 0x01, 0x00, 0);
+ send_pad_cmd(port, PAD_CMD_CONFIG_MODE, 0x01, 0x00, &dualshock_init_cb);
}
- } else if (pad_digital_only[port]) {
- printf("Clearing digital-only flag for pad %d\n", port + 1);
+ } else {
+ //printf("Clearing digital-only flag for pad %d\n", port + 1);
pad_digital_only[port] = 0;
}
@@ -213,13 +229,7 @@ int main(int argc, const char* argv[]) {
FntPrint(-1, "COUNTER=%d", counter++);
for (uint32_t port = 0; port < 2; port++) {
- PADTYPE *pad = (PADTYPE *) 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.
- //if (!pad->raw.prefix != 0x5a) {
+ // TODO.
if (!pad_buff_len[port]) {
FntPrint(-1, "\n\nPORT %d: NO DEVICE FOUND\n", port + 1);
if ((counter % 64) < 32)
@@ -228,26 +238,36 @@ int main(int argc, const char* argv[]) {
continue;
}
- uint32_t type = pad->raw.type;
- uint32_t len = pad->raw.len * 2;
- if (type == PAD_ID_MULTITAP)
- len = 32;
+ PADTYPE *pad = (PADTYPE *) pad_buff[port];
+ PAD_TYPEID type = pad->raw.type;
+
+ // 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)) {
+ FntPrint(-1, "\n\nPORT %d: INVALID RESPONSE\n", port + 1);
+ if ((counter % 64) < 32)
+ FntPrint(-1, " CHECK CONNECTION...");
+
+ continue;
+ }*/
FntPrint(
-1,
- "\n\nPORT %d: %s\n TYPE=%d LEN=%d",
+ "\n\nPORT %d: %s (TYPE=%d)\n",
port + 1,
PAD_TYPEIDS[type],
- type,
- len
+ type
);
// Print a hexdump of the payload returned by the pad.
- for (uint32_t i = 0; i < len; i++)
+ for (uint32_t i = 0; i < pad_buff_len[port]; i++)
FntPrint(
-1,
- (i % 8) ? " %02x" : "\n %02x",
- pad_buff[port][i + 2]
+ ((i - 2) % 8) ? " %02x" : "\n %02x",
+ pad_buff[port][i]
);
}
diff --git a/examples/io/pads/spi.c b/examples/io/pads/spi.c
index 4728626..5fc2648 100644
--- a/examples/io/pads/spi.c
+++ b/examples/io/pads/spi.c
@@ -2,9 +2,9 @@
* PSn00bSDK controller polling example (SPI driver)
* (C) 2021 spicyjpeg - MPL licensed
*
- * This is a fairly complete timer driven high-speed SPI driver, with support
- * for sending custom commands (including memory card reads/writes), in about
- * 200 lines of code. Feel free to copypaste and adapt it.
+ * This is a fairly complete timer driven, asynchronous high-speed SPI driver,
+ * with support for sending custom commands (including memory card access), in
+ * about 200 lines of code. Feel free to copypaste and adapt it.
*
* The way this works is by maintaining a queue of requests to send, each with
* its own payload and callback. Timer 2 is configured to trigger an IRQ at
@@ -42,8 +42,12 @@
#define TIM_VALUE(N) *((volatile uint32_t *) 0x1f801100 + 4 * (N))
#define TIM_CTRL(N) *((volatile uint32_t *) 0x1f801104 + 4 * (N))
#define TIM_RELOAD(N) *((volatile uint32_t *) 0x1f801108 + 4 * (N))
-#define JOY_TXRX *((volatile uint32_t *) 0x1f801040)
-#define JOY_STAT *((volatile uint32_t *) 0x1f801044)
+
+// IMPORTANT: even though JOY_TXRX is a 32-bit register, it should only be
+// accessed as 8-bit. Reading it as 16 or 32-bit works fine on real hardware,
+// but leads to problems in some emulators.
+#define JOY_TXRX *((volatile uint8_t *) 0x1f801040)
+#define JOY_STAT *((volatile uint16_t *) 0x1f801044)
#define JOY_MODE *((volatile uint16_t *) 0x1f801048)
#define JOY_CTRL *((volatile uint16_t *) 0x1f80104a)
#define JOY_BAUD *((volatile uint16_t *) 0x1f80104e)
@@ -142,11 +146,9 @@ static void spi_ack_handler(void) {
if (!ctx.rx_len) {
// We just sent the first address byte. Obviously the response we
// received was read from an open bus, so the SPI port's internal FIFO
- // must be flushed to ensure we are only going to read valid data from
- // now on.
- volatile uint32_t dummy;
- while (JOY_STAT & 0x0002)
- dummy = JOY_TXRX;
+ // must be flushed (by performing dummy reads) to ensure we are only
+ // going to read valid data from now on.
+ JOY_TXRX;
} else if (ctx.rx_len <= SPI_BUFF_LEN) {
// If this is not the first byte, put it in the RX buffer.
diff --git a/examples/lowlevel/cartrom/CMakeLists.txt b/examples/lowlevel/cartrom/CMakeLists.txt
new file mode 100644
index 0000000..3e807a3
--- /dev/null
+++ b/examples/lowlevel/cartrom/CMakeLists.txt
@@ -0,0 +1,36 @@
+# 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(
+ cartrom
+ LANGUAGES C ASM
+ VERSION 1.0.0
+ DESCRIPTION "PSn00bSDK expansion port ROM example"
+ HOMEPAGE_URL "http://lameguy64.net/?page=psn00bsdk"
+)
+
+file(GLOB _sources *.c *.s)
+
+# This example only uses the toolchain (without the rest of the SDK), so the
+# executable has to be created manually and converted into raw binary format
+# (for testing on emulators or flashing to a cheat cartridge).
+add_executable (cartrom ${_sources})
+target_link_libraries(cartrom psn00bsdk_static_exe)
+set_target_properties(cartrom PROPERTIES PREFIX "" SUFFIX ".elf")
+target_link_options (cartrom PRIVATE -T${PROJECT_SOURCE_DIR}/rom.ld)
+
+target_include_directories(cartrom PRIVATE ${PROJECT_SOURCE_DIR})
+
+add_custom_command(
+ TARGET cartrom POST_BUILD
+ COMMAND ${CMAKE_OBJCOPY} -O binary cartrom.elf cartrom.bin
+ BYPRODUCTS cartrom.bin
+)
+
+install(FILES ${PROJECT_BINARY_DIR}/cartrom.bin TYPE BIN)
diff --git a/examples/lowlevel/cartrom/makefile b/examples/lowlevel/cartrom/makefile
deleted file mode 100644
index 2434685..0000000
--- a/examples/lowlevel/cartrom/makefile
+++ /dev/null
@@ -1,14 +0,0 @@
-PREFIX = mipsel-unknown-elf-
-
-CC = $(PREFIX)gcc
-AS = $(PREFIX)as
-LD = $(PREFIX)ld
-
-all: rom.o
- $(LD) --oformat binary -T rom.ld -o cartrom.rom rom.o
-
-%.o: %.s
- $(AS) -msoft-float --warn $< -o $@
-
-clean:
- rm -f rom.o cartrom.rom \ No newline at end of file
diff --git a/examples/system/dynlink/display.c b/examples/system/dynlink/display.c
index d8ad3ed..573f17c 100644
--- a/examples/system/dynlink/display.c
+++ b/examples/system/dynlink/display.c
@@ -10,6 +10,10 @@
#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) {
@@ -23,14 +27,14 @@ void init_context(CONTEXT *ctx) {
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), 63, 0, 127);
+ 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), 63, 0, 127);
+ setRGB0(&(db->draw), BGCOLOR_R, BGCOLOR_G, BGCOLOR_B);
db->draw.isbg = 1;
db->draw.dtd = 1;