diff --git a/include/PR/os_cont.h b/include/PR/os_cont.h index 860a7926..f1b20980 100644 --- a/include/PR/os_cont.h +++ b/include/PR/os_cont.h @@ -212,6 +212,13 @@ typedef struct { #define CONT_ERR_VOICE_WORD 14 #define CONT_ERR_VOICE_NO_RESPONSE 15 +// Controller mask values +#define CONT_P1 (1 << 0) +#define CONT_P2 (1 << 1) +#define CONT_P3 (1 << 2) +#define CONT_P4 (1 << 3) + +// Controller types #define CONT_TYPE_N64 0 #define CONT_TYPE_GCN 1 @@ -228,6 +235,7 @@ typedef struct { * Extern variables * */ +extern u8 __osControllerMask; /************************************************************************** * @@ -244,6 +252,7 @@ extern s32 osContStartReadData(OSMesgQueue*); #ifndef _HW_VERSION_1 extern s32 osContSetCh(u8); #endif +extern s32 osContSetMask(u8 ch); extern void osContGetQuery(OSContStatus*); extern void osContGetReadData(OSContPad*); diff --git a/src/io/contreaddata.c b/src/io/contreaddata.c index 1c1ac8e7..7e859d4f 100644 --- a/src/io/contreaddata.c +++ b/src/io/contreaddata.c @@ -62,6 +62,12 @@ void osContGetReadData(OSContPad* data) { int i; for (i = 0; i < __osMaxControllers; i++, data++) { + // Skip current channel if controller not connected + if ((__osControllerMask & (1 << i)) == 0) { + ptr += sizeof(__OSContReadFormat); + continue; + } + if (__osControllerTypes[i] == CONT_TYPE_GCN) { s32 stick_x, stick_y, c_stick_x, c_stick_y; readformatgcn = *(__OSContGCNShortPollFormat*)ptr; @@ -113,9 +119,6 @@ static void __osPackReadData(void) { __osContPifRam.pifstatus = CONT_CMD_EXE; readformat.dummy = CONT_CMD_NOP; - readformat.txsize = CONT_CMD_READ_BUTTON_TX; - readformat.rxsize = CONT_CMD_READ_BUTTON_RX; - readformat.cmd = CONT_CMD_READ_BUTTON; readformat.button = 0xFFFF; readformat.stick_x = -1; readformat.stick_y = -1; @@ -135,7 +138,18 @@ static void __osPackReadData(void) { readformatgcn.stick_y = -1; for (i = 0; i < __osMaxControllers; i++) { - if (__osControllerTypes[i] == CONT_TYPE_GCN) { + if ((__osControllerMask & (1 << i)) == 0) { + // In the Joybus frame, a packet is sent to the PIF chip describing the number of bytes to transfer and + // receive over the protocol. There are two special commands, 0x00 (skip) and 0xFF (nop). This function + // sends a packet with the layout: FF FF FF 00 FF FF FF FF, basically saying to skip the channel, with some + // extra bytes that do nothing, allowing the next packet to be processed. This wastes memory, but it works. + // Source: https://n64brew.dev/wiki/PIF-NUS#Joybus_frame_(controller_and_EEPROM_communication) + readformat.txsize = CONT_CMD_NOP; + readformat.rxsize = CONT_CMD_NOP; + readformat.cmd = 0; + *(__OSContReadFormat*)ptr = readformat; + ptr += sizeof(__OSContReadFormat); + } else if (__osControllerTypes[i] == CONT_TYPE_GCN) { readformatgcn.rumble = __osGamecubeRumbleEnabled[i]; *(__OSContGCNShortPollFormat*)ptr = readformatgcn; ptr += sizeof(__OSContGCNShortPollFormat); diff --git a/src/io/controller.c b/src/io/controller.c index 2210f6df..001bdd9e 100644 --- a/src/io/controller.c +++ b/src/io/controller.c @@ -12,6 +12,7 @@ u8 __osGamecubeRumbleEnabled[MAXCONTROLLERS]; OSTimer __osEepromTimer; OSMesgQueue __osEepromTimerQ ALIGNED(0x8); OSMesg __osEepromTimerMsg; +u8 __osControllerMask; s32 __osContinitialized = FALSE; @@ -27,6 +28,7 @@ s32 osContInit(OSMesgQueue* mq, u8* bitpattern, OSContStatus* data) { } __osContinitialized = TRUE; + __osControllerMask = CONT_P1 | CONT_P2 | CONT_P3 | CONT_P4; t = osGetTime(); if (t < OS_USEC_TO_CYCLES(500000)) { @@ -35,7 +37,7 @@ s32 osContInit(OSMesgQueue* mq, u8* bitpattern, OSContStatus* data) { osRecvMesg(&timerMesgQueue, &dummy, OS_MESG_BLOCK); } - __osMaxControllers = 4; + __osMaxControllers = MAXCONTROLLERS; __osPackRequestData(CONT_CMD_REQUEST_STATUS); diff --git a/src/io/contsetch.c b/src/io/contsetch.c index 2f7a33fe..52ce50bd 100644 --- a/src/io/contsetch.c +++ b/src/io/contsetch.c @@ -7,17 +7,39 @@ * to multiple direct SI devices. */ s32 osContSetCh(u8 ch) { + return osContSetMask((1 << ch) - 1); +} + +/* + * Use a mask to detect controller channels instead, allowing the PIF to skip channels instead of + * spending time reading data. + * This does not work on iQue Player. + */ +s32 osContSetMask(u8 ch) { s32 ret = 0; +#ifdef BBPLAYER + __osControllerMask = (CONT_P1 | CONT_P2 | CONT_P3 | CONT_P4); + __osMaxControllers = MAXCONTROLLERS; + return ret; +#else __osSiGetAccess(); - if (ch > MAXCONTROLLERS) { + if ((ch == 0) || (ch > (CONT_P1 | CONT_P2 | CONT_P3 | CONT_P4))) { + __osControllerMask = (CONT_P1 | CONT_P2 | CONT_P3 | CONT_P4); __osMaxControllers = MAXCONTROLLERS; } else { - __osMaxControllers = ch; + __osControllerMask = ch; + for (s32 i = 0; i < MAXCONTROLLERS; i++) { + if (ch & (1 << i)) { + __osMaxControllers = i; + } + } + __osMaxControllers++; } __osContLastCmd = CONT_CMD_END; __osSiRelAccess(); return ret; +#endif } diff --git a/src/io/pfsisplug.c b/src/io/pfsisplug.c index 01040305..a2278efe 100644 --- a/src/io/pfsisplug.c +++ b/src/io/pfsisplug.c @@ -28,6 +28,9 @@ s32 osPfsIsPlug(OSMesgQueue* mq, u8* pattern) { __osPfsGetInitData(&bitpattern, &contData[0]); for (channel = 0; channel < __osMaxControllers; channel++) { + if ((__osControllerMask & (1 << channel)) == 0) { + continue; + } if ((contData[channel].status & CONT_ADDR_CRC_ER) == 0) { crcErrorCount--; break; @@ -40,6 +43,9 @@ s32 osPfsIsPlug(OSMesgQueue* mq, u8* pattern) { } while (crcErrorCount > 0); for (channel = 0; channel < __osMaxControllers; channel++) { + if ((__osControllerMask & (1 << channel)) == 0) { + continue; + } if ((contData[channel].errno == 0) && ((contData[channel].status & CONT_CARD_ON) != 0)) { bits |= (1 << channel); } @@ -57,15 +63,22 @@ void __osPfsRequestData(u8 cmd) { __osContLastCmd = cmd; __osPfsPifRam.pifstatus = CONT_CMD_EXE; requestformat.dummy = CONT_CMD_NOP; - requestformat.txsize = CONT_CMD_REQUEST_STATUS_TX; - requestformat.rxsize = CONT_CMD_REQUEST_STATUS_RX; - requestformat.cmd = cmd; requestformat.typeh = CONT_CMD_NOP; requestformat.typel = CONT_CMD_NOP; requestformat.status = CONT_CMD_NOP; requestformat.dummy1 = CONT_CMD_NOP; for (i = 0; i < __osMaxControllers; i++) { + if ((__osControllerMask & (1 << i)) == 0) { + // See __osPackReadData in contreaddata.c for an explanation for this behavior. + requestformat.txsize = CONT_CMD_NOP; + requestformat.rxsize = CONT_CMD_NOP; + requestformat.cmd = 0; + } else { + requestformat.txsize = CONT_CMD_REQUEST_STATUS_TX; + requestformat.rxsize = CONT_CMD_REQUEST_STATUS_RX; + requestformat.cmd = cmd; + } *((__OSContRequesFormat*)ptr) = requestformat; ptr += sizeof(__OSContRequesFormat); } @@ -82,6 +95,9 @@ void __osPfsGetInitData(u8* pattern, OSContStatus* data) { ptr = (u8*)&__osPfsPifRam; for (i = 0; i < __osMaxControllers; i++, ptr += sizeof(requestformat), data++) { + if ((__osControllerMask & (1 << i)) == 0) { + continue; + } requestformat = *((__OSContRequesFormat*)ptr); data->errno = CHNL_ERR(requestformat);