From 0739ca624c795fb11bfbb7ab85ee5a9f22c0e76e Mon Sep 17 00:00:00 2001 From: Mateusz Faderewski Date: Sun, 9 Mar 2025 20:48:15 +0100 Subject: [PATCH] [SC64][SW] Added controller reading functions in the bootloader --- sw/bootloader/Makefile | 1 + sw/bootloader/src/boot.c | 1 + sw/bootloader/src/io.c | 26 +++++- sw/bootloader/src/io.h | 32 ++++++- sw/bootloader/src/joybus.c | 169 +++++++++++++++++++++++++++++++++++++ sw/bootloader/src/joybus.h | 39 +++++++++ 6 files changed, 264 insertions(+), 4 deletions(-) create mode 100644 sw/bootloader/src/joybus.c create mode 100644 sw/bootloader/src/joybus.h diff --git a/sw/bootloader/Makefile b/sw/bootloader/Makefile index 96539de..d0b73f2 100644 --- a/sw/bootloader/Makefile +++ b/sw/bootloader/Makefile @@ -34,6 +34,7 @@ SRC_FILES = \ interrupts.c \ interrupts.S \ io.c \ + joybus.c \ main.c \ menu.c \ reboot.S \ diff --git a/sw/bootloader/src/boot.c b/sw/bootloader/src/boot.c index b62608a..e69b419 100644 --- a/sw/bootloader/src/boot.c +++ b/sw/bootloader/src/boot.c @@ -79,6 +79,7 @@ void boot (boot_params_t *params) { while (cpu_io_read(&SP->DMA_BUSY)); cpu_io_write(&PI->SR, PI_SR_CLR_INTR | PI_SR_RESET); + cpu_io_write(&SI->SR, 0); while ((cpu_io_read(&VI->CURR_LINE) & ~(VI_CURR_LINE_FIELD)) != 0); cpu_io_write(&VI->V_INTR, 0x3FF); cpu_io_write(&VI->H_LIMITS, 0); diff --git a/sw/bootloader/src/io.c b/sw/bootloader/src/io.c index bad37f2..05cba0f 100644 --- a/sw/bootloader/src/io.c +++ b/sw/bootloader/src/io.c @@ -3,7 +3,7 @@ #include "vr4300.h" -static void cache_operation (uint8_t operation, uint8_t line_size, void *address, size_t length) { +static inline void cache_operation (const uint8_t operation, uint8_t line_size, void *address, size_t length) { uint32_t cache_address = (((uint32_t) (address)) & (~(line_size - 1))); while (cache_address < ((uint32_t) (address) + length)) { asm volatile ( @@ -109,3 +109,27 @@ void pi_dma_write (io32_t *address, void *buffer, size_t length) { }); while (pi_busy()); } + +uint32_t si_busy (void) { + return (cpu_io_read(&SI->SR) & (SI_SR_IO_BUSY | SI_SR_DMA_BUSY)); +} + +void si_dma_read (void *buffer) { + cache_data_hit_writeback_invalidate(buffer, PIF_RAM_LENGTH); + WITH_INTERRUPTS_DISABLED({ + while (si_busy()); + cpu_io_write(&SI->MADDR, (uint32_t) (PHYSICAL(buffer))); + cpu_io_write(&SI->RDMA, (uint32_t) (PIF_RAM)); + }); + while (si_busy()); +} + +void si_dma_write (void *buffer) { + cache_data_hit_writeback(buffer, PIF_RAM_LENGTH); + WITH_INTERRUPTS_DISABLED({ + while (si_busy()); + cpu_io_write(&SI->MADDR, (uint32_t) (PHYSICAL(buffer))); + cpu_io_write(&SI->WDMA, (uint32_t) (PIF_RAM)); + }); + while (si_busy()); +} diff --git a/sw/bootloader/src/io.h b/sw/bootloader/src/io.h index d90ef04..9a36099 100644 --- a/sw/bootloader/src/io.h +++ b/sw/bootloader/src/io.h @@ -202,6 +202,24 @@ typedef struct { #define PI_SR_CLR_INTR (1 << 1) +typedef struct { + io32_t MADDR; + io32_t RDMA; + io32_t __reserved_0[2]; + io32_t WDMA; + io32_t __reserved_1[1]; + io32_t SR; +} si_regs_t; + +#define SI_BASE (0x04800000UL) +#define SI ((si_regs_t *) SI_BASE) + +#define SI_SR_DMA_BUSY (1 << 0) +#define SI_SR_IO_BUSY (1 << 1) +#define SI_SR_DMA_ERROR (1 << 3) +#define SI_SR_INTERRUPT (1 << 12) + + #define ROM_DDIPL_BASE (0x06000000UL) #define ROM_DDIPL ((io32_t *) ROM_DDIPL_BASE) @@ -210,6 +228,14 @@ typedef struct { #define ROM_CART ((io32_t *) ROM_CART_BASE) +#define PIF_RAM_BASE (0x1FC007C0) +#define PIF_RAM_LENGTH (64) +#define PIF_RAM ((io32_t *) PIF_RAM_BASE) + + +void cache_data_hit_writeback_invalidate (void *address, size_t length); +void cache_data_hit_writeback (void *address, size_t length); +void cache_inst_hit_invalidate (void *address, size_t length); uint32_t c0_count (void); void delay_ms (int ms); uint32_t cpu_io_read (io32_t *address); @@ -220,9 +246,9 @@ uint32_t pi_io_read (io32_t *address); void pi_io_write (io32_t *address, uint32_t value); void pi_dma_read (io32_t *address, void *buffer, size_t length); void pi_dma_write (io32_t *address, void *buffer, size_t length); -void cache_data_hit_writeback_invalidate (void *address, size_t length); -void cache_data_hit_writeback (void *address, size_t length); -void cache_inst_hit_invalidate (void *address, size_t length); +uint32_t si_busy (void); +void si_dma_read (void *buffer); +void si_dma_write (void *buffer); #endif diff --git a/sw/bootloader/src/joybus.c b/sw/bootloader/src/joybus.c new file mode 100644 index 0000000..8256928 --- /dev/null +++ b/sw/bootloader/src/joybus.c @@ -0,0 +1,169 @@ +#include +#include "io.h" +#include "joybus.h" + + +#define PIF_ESCAPE_SKIP_CHANNEL (0x00) +#define PIF_ESCAPE_END_OF_FRAME (0xFE) + +#define PIF_COMMAND_JOYBUS_START (1 << 0) + +#define JOYBUS_CMD_INFO (0x00) +#define JOYBUS_CMD_STATE (0x01) +#define JOYBUS_CMD_RESET (0xFF) + + +typedef struct __attribute__((packed)) { + struct __attribute__((packed)) { + uint8_t skip : 1; + uint8_t reset : 1; + uint8_t length : 6; + } tx; + struct __attribute__((packed)) { + uint8_t no_device : 1; + uint8_t timeout : 1; + uint8_t length : 6; + } rx; + uint8_t cmd; +} joybus_trx_info_t; + +typedef struct __attribute__((packed)) { + joybus_trx_info_t info; + struct __attribute__((packed)) { + uint16_t id; + uint8_t flags; + } rx; +} joybus_cmd_info_t; + +typedef struct __attribute__((packed)) { + joybus_trx_info_t info; + struct __attribute__((packed)) { + uint8_t a : 1; + uint8_t b : 1; + uint8_t z : 1; + uint8_t start : 1; + uint8_t up : 1; + uint8_t down : 1; + uint8_t left : 1; + uint8_t right : 1; + uint8_t reset : 1; + uint8_t __unused : 1; + uint8_t l : 1; + uint8_t r : 1; + uint8_t c_up : 1; + uint8_t c_down : 1; + uint8_t c_left : 1; + uint8_t c_right : 1; + int8_t x; + int8_t y; + } rx; +} joybus_cmd_state_t; + + +static void joybus_clear_buffer (uint8_t *buffer) { + for (int i = 0; i < PIF_RAM_LENGTH; i++) { + buffer[i] = 0; + } +} + +static void joybus_set_starting_channel (uint8_t **ptr, int channel) { + for (int i = 0; i < channel; i++) { + **ptr = PIF_ESCAPE_SKIP_CHANNEL; + *ptr += 1; + } +} + +static uint8_t *joybus_append_command (uint8_t **ptr, void *cmd, size_t length) { + ((joybus_trx_info_t *) (cmd))->tx.length += 1; + uint8_t *cmd_ptr = *ptr; + memcpy(cmd_ptr, cmd, length); + *ptr += length; + return cmd_ptr; +} + +static void joybus_load_result (uint8_t *cmd_ptr, void *cmd, size_t length) { + memcpy(cmd, cmd_ptr, length); +} + +static void joybus_finish_frame (uint8_t **ptr) { + **ptr = PIF_ESCAPE_END_OF_FRAME; + *ptr += 1; +} + +static void joybus_perform_transaction (uint8_t *buffer) { + buffer[PIF_RAM_LENGTH - 1] = PIF_COMMAND_JOYBUS_START; + + si_dma_write(buffer); + si_dma_read(buffer); +} + +static bool joybus_device_present (joybus_trx_info_t *trx_info) { + return !(trx_info->rx.no_device || trx_info->rx.timeout); +} + +static bool joybus_execute_command (int port, void *cmd, size_t length) { + uint8_t buffer[PIF_RAM_LENGTH] __attribute__((aligned(8))); + uint8_t *ptr = buffer; + + joybus_clear_buffer(buffer); + joybus_set_starting_channel(&ptr, port); + uint8_t *cmd_ptr = joybus_append_command(&ptr, cmd, length); + joybus_finish_frame(&ptr); + joybus_perform_transaction(buffer); + joybus_load_result(cmd_ptr, cmd, length); + return joybus_device_present((joybus_trx_info_t *) (cmd_ptr)); +} + +static void joybus_copy_controller_info (joybus_cmd_info_t *cmd, joybus_controller_info_t *info) { + info->id = cmd->rx.id; + info->flags = cmd->rx.flags; +} + +static void joybus_copy_controller_state (joybus_cmd_state_t *cmd, joybus_controller_state_t *state) { + state->a = cmd->rx.a; + state->b = cmd->rx.b; + state->z = cmd->rx.z; + state->start = cmd->rx.start; + state->up = cmd->rx.up; + state->down = cmd->rx.down; + state->left = cmd->rx.left; + state->right = cmd->rx.right; + state->reset = cmd->rx.reset; + state->l = cmd->rx.l; + state->r = cmd->rx.r; + state->c_up = cmd->rx.c_up; + state->c_down = cmd->rx.c_down; + state->c_left = cmd->rx.c_left; + state->c_right = cmd->rx.c_right; + state->x = cmd->rx.x; + state->y = cmd->rx.y; +} + + +bool joybus_get_controller_info (int port, joybus_controller_info_t *info, bool reset) { + joybus_cmd_info_t cmd = { .info = { + .cmd = reset ? JOYBUS_CMD_RESET : JOYBUS_CMD_INFO, + .rx = { .length = sizeof(cmd.rx) } + } }; + + if (joybus_execute_command(port, &cmd, sizeof(cmd))) { + joybus_copy_controller_info(&cmd, info); + return true; + } + + return false; +} + +bool joybus_get_controller_state (int port, joybus_controller_state_t *state) { + joybus_cmd_state_t cmd = { .info = { + .cmd = JOYBUS_CMD_STATE, + .rx = { .length = sizeof(cmd.rx) } + } }; + + if (joybus_execute_command(port, &cmd, sizeof(cmd))) { + joybus_copy_controller_state(&cmd, state); + return true; + } + + return false; +} diff --git a/sw/bootloader/src/joybus.h b/sw/bootloader/src/joybus.h new file mode 100644 index 0000000..564c563 --- /dev/null +++ b/sw/bootloader/src/joybus.h @@ -0,0 +1,39 @@ +#ifndef JOYBUS_H__ +#define JOYBUS_H__ + + +#include +#include + + +typedef struct { + uint16_t id; + uint8_t flags; +} joybus_controller_info_t; + +typedef struct { + bool a; + bool b; + bool z; + bool start; + bool up; + bool down; + bool left; + bool right; + bool reset; + bool l; + bool r; + bool c_up; + bool c_down; + bool c_left; + bool c_right; + int8_t x; + int8_t y; +} joybus_controller_state_t; + + +bool joybus_get_controller_info (int port, joybus_controller_info_t *info, bool reset); +bool joybus_get_controller_state (int port, joybus_controller_state_t *state); + + +#endif