From 4858ea84dd3c500f5a860c08a0851625c3bca231 Mon Sep 17 00:00:00 2001 From: someone2639 Date: Sat, 13 Sep 2025 22:26:44 -0400 Subject: [PATCH] Gamecube Controller Centering (#21) * implement gc controller centering on boot * add clamp macros too * format --------- Co-authored-by: someone2639 --- include/PR/os_cont.h | 8 +++++++ include/PRinternal/macros.h | 38 ++++++++++++++++++++++++++++++ src/host/writehost.c | 4 +--- src/io/contreaddata.c | 46 ++++++++++++++++++++++++++----------- src/io/controller.c | 2 +- 5 files changed, 80 insertions(+), 18 deletions(-) diff --git a/include/PR/os_cont.h b/include/PR/os_cont.h index 860a792..f512cc6 100644 --- a/include/PR/os_cont.h +++ b/include/PR/os_cont.h @@ -93,6 +93,14 @@ typedef struct { } cStickMap; } OSContButtonMap; +typedef struct { + s8 initialized; + u8 stick_x; + u8 stick_y; + u8 c_stick_x; + u8 c_stick_y; +} OSContCenterMapping; + typedef struct { void* address; /* Ram pad Address: 11 bits */ u8 databuffer[32]; /* address of the data buffer */ diff --git a/include/PRinternal/macros.h b/include/PRinternal/macros.h index 1bdfc58..0baeeea 100644 --- a/include/PRinternal/macros.h +++ b/include/PRinternal/macros.h @@ -21,4 +21,42 @@ #define STACK_START(stack) ((u8*)(stack) + sizeof(stack)) +#ifndef MIN +#define MIN(a, b) \ + ({ \ + __auto_type _a = (a); \ + __auto_type _b = (b); \ + _a < _b ? _a : _b; \ + }) +#endif // MIN + +#ifndef MAX +#define MAX(a, b) \ + ({ \ + __auto_type _a = (a); \ + __auto_type _b = (b); \ + _a > _b ? _a : _b; \ + }) +#endif // MAX + +// Integer limits and clamping +#define S8_MAX 127 +#define S8_MIN -128 +#define U8_MAX 255 +#define S16_MAX 32767 +#define S16_MIN -32768 +#define U16_MAX 65535 +#define S32_MAX 2147483647 +#define S32_MIN -2147483648 +#define U32_MAX 4294967295 + +// Clamp a value inbetween a range +#define CLAMP(x, low, high) MIN(MAX((x), (low)), (high)) + +// Clamp a value to the range of a specific data type +#define CLAMP_U8(x) CLAMP((x), 0, U8_MAX) +#define CLAMP_S8(x) CLAMP((x), S8_MIN, S8_MAX) +#define CLAMP_U16(x) CLAMP((x), 0, U16_MAX) +#define CLAMP_S16(x) CLAMP((x), S16_MIN, S16_MAX) + #endif diff --git a/src/host/writehost.c b/src/host/writehost.c index 1d1baf4..9643b17 100644 --- a/src/host/writehost.c +++ b/src/host/writehost.c @@ -10,8 +10,6 @@ static int writeHostInitialized = FALSE; static OSMesgQueue writeHostMesgQueue ALIGNED(0x8); static OSMesg writeHostMesgBuf[1]; -#define MIN(a, b) (((a) < (b)) ? (a) : (b)) - void osWriteHost(void* dramAddr, u32 nbytes) { u8* tPtr = dramAddr; u32 sent; @@ -32,7 +30,7 @@ void osWriteHost(void* dramAddr, u32 nbytes) { } while (nbytes != 0) { - count = MIN(nbytes, 0x8000); + count = MIN(nbytes, 0x8000U); dCount[0] = (count & 0xFF0000) >> 0x10; dCount[1] = (count & 0xFF00) >> 8; diff --git a/src/io/contreaddata.c b/src/io/contreaddata.c index 1c1ac8e..06a9a0c 100644 --- a/src/io/contreaddata.c +++ b/src/io/contreaddata.c @@ -9,6 +9,8 @@ static void __osPackReadData(void); static u16 __osTranslateGCNButtons(u16, s32, s32); static u16 __osTranslateN64Buttons(u16); +static OSContCenterMapping __osControllerCenters[MAXCONTROLLERS] = { 0 }; + static OSContButtonMap __osDefaultControllerMap = { .buttonMap = { .l_jpad = L_JPAD, @@ -64,21 +66,37 @@ void osContGetReadData(OSContPad* data) { for (i = 0; i < __osMaxControllers; i++, data++) { if (__osControllerTypes[i] == CONT_TYPE_GCN) { s32 stick_x, stick_y, c_stick_x, c_stick_y; + readformatgcn = *(__OSContGCNShortPollFormat*)ptr; - // The analog stick data is encoded unsigned, with (0, 0) being the bottom left of the stick plane, - // compared to the N64 where (0, 0) is the center. We correct it here so that the end user does not - // have to account for this discrepancy. - stick_x = ((s32)readformatgcn.stick_x) - 128; - stick_y = ((s32)readformatgcn.stick_y) - 128; - data->stick_x = stick_x; - data->stick_y = stick_y; - c_stick_x = ((s32)readformatgcn.c_stick_x) - 128; - c_stick_y = ((s32)readformatgcn.c_stick_y) - 128; - data->c_stick_x = c_stick_x; - data->c_stick_y = c_stick_y; - data->button = __osTranslateGCNButtons(readformatgcn.button, c_stick_x, c_stick_y); - data->l_trig = readformatgcn.l_trig; - data->r_trig = readformatgcn.r_trig; + data->errno = CHNL_ERR(readformatgcn); + + if (data->errno == 0) { + // The analog stick data is encoded unsigned, with (0, 0) being the bottom left of the stick plane, + // compared to the N64 where (0, 0) is the center. We correct it here so that the end user does not + // have to account for this discrepancy. + if (!__osControllerCenters[i].initialized) { + __osControllerCenters[i].initialized = TRUE; + __osControllerCenters[i].stick_x = readformatgcn.stick_x; + __osControllerCenters[i].stick_y = readformatgcn.stick_y; + __osControllerCenters[i].c_stick_x = readformatgcn.c_stick_x; + __osControllerCenters[i].c_stick_y = readformatgcn.c_stick_y; + } + + stick_x = CLAMP_S8(((s32)readformatgcn.stick_x) - __osControllerCenters[i].stick_x); + stick_y = CLAMP_S8(((s32)readformatgcn.stick_y) - __osControllerCenters[i].stick_y); + data->stick_x = stick_x; + data->stick_y = stick_y; + c_stick_x = CLAMP_S8(((s32)readformatgcn.c_stick_x) - __osControllerCenters[i].c_stick_x); + c_stick_y = CLAMP_S8(((s32)readformatgcn.c_stick_y) - __osControllerCenters[i].c_stick_y); + data->c_stick_x = c_stick_x; + data->c_stick_y = c_stick_y; + data->button = __osTranslateGCNButtons(readformatgcn.button, c_stick_x, c_stick_y); + data->l_trig = readformatgcn.l_trig; + data->r_trig = readformatgcn.r_trig; + } else { + __osControllerCenters[i].initialized = FALSE; + } + ptr += sizeof(__OSContGCNShortPollFormat); } else { readformat = *(__OSContReadFormat*)ptr; diff --git a/src/io/controller.c b/src/io/controller.c index 2210f6d..1027a42 100644 --- a/src/io/controller.c +++ b/src/io/controller.c @@ -35,7 +35,7 @@ s32 osContInit(OSMesgQueue* mq, u8* bitpattern, OSContStatus* data) { osRecvMesg(&timerMesgQueue, &dummy, OS_MESG_BLOCK); } - __osMaxControllers = 4; + __osMaxControllers = MAXCONTROLLERS; __osPackRequestData(CONT_CMD_REQUEST_STATUS);