Skip to content
9 changes: 9 additions & 0 deletions include/PR/os_cont.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -228,6 +235,7 @@ typedef struct {
* Extern variables
*
*/
extern u8 __osControllerMask;

/**************************************************************************
*
Expand All @@ -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*);

Expand Down
22 changes: 18 additions & 4 deletions src/io/contreaddata.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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);
Expand Down
4 changes: 3 additions & 1 deletion src/io/controller.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ u8 __osGamecubeRumbleEnabled[MAXCONTROLLERS];
OSTimer __osEepromTimer;
OSMesgQueue __osEepromTimerQ ALIGNED(0x8);
OSMesg __osEepromTimerMsg;
u8 __osControllerMask;

s32 __osContinitialized = FALSE;

Expand All @@ -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)) {
Expand All @@ -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);

Expand Down
26 changes: 24 additions & 2 deletions src/io/contsetch.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
22 changes: 19 additions & 3 deletions src/io/pfsisplug.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
}
Expand All @@ -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);
}
Expand All @@ -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);

Expand Down