diff options
| author | Tharo <17233964+Thar0@users.noreply.github.com> | 2023-05-26 18:57:44 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-05-26 13:57:44 -0400 |
| commit | 6d20839ab0f6e439d159b279acf780061d5183a5 (patch) | |
| tree | f84a3f8e46234a168e551433f50df67a5a540bb4 /src/code | |
| parent | 22af2bb8fe2d941d653f40c159d9e23aa94e0f20 (diff) | |
Decompile padmgr.c (#1246)
* Decompile padmgr.c
* Format
* Enum names
* Update namefixer
* Suggested changes
Co-authored-by: Anghelo Carvajal <angheloalf95@gmail.com>
* Suggested changes, improve motor.c
Co-authored-by: Derek Hensley <hensley.derek58@gmail.com>
* Further suggested changes
* Format
* Fix `controller.h` comment
Co-authored-by: engineer124 <47598039+engineer124@users.noreply.github.com>
---------
Co-authored-by: Anghelo Carvajal <angheloalf95@gmail.com>
Co-authored-by: Derek Hensley <hensley.derek58@gmail.com>
Co-authored-by: engineer124 <47598039+engineer124@users.noreply.github.com>
Diffstat (limited to 'src/code')
| -rw-r--r-- | src/code/game.c | 2 | ||||
| -rw-r--r-- | src/code/main.c | 1 | ||||
| -rw-r--r-- | src/code/padmgr.c | 795 | ||||
| -rw-r--r-- | src/code/sys_rumble.c | 10 | ||||
| -rw-r--r-- | src/code/z_parameter.c | 12 | ||||
| -rw-r--r-- | src/code/z_rumble.c | 4 |
6 files changed, 781 insertions, 43 deletions
diff --git a/src/code/game.c b/src/code/game.c index 3ff4f9962..bc8b9e39f 100644 --- a/src/code/game.c +++ b/src/code/game.c @@ -128,7 +128,7 @@ void func_801736DC(GraphicsContext* gfxCtx) { } void Game_UpdateInput(GameState* gameState) { - Padmgr_GetInput(gameState->input, 1); + PadMgr_GetInput(gameState->input, true); } void Game_Update(GameState* gameState) { diff --git a/src/code/main.c b/src/code/main.c index 7fbb5ba39..6eadd591a 100644 --- a/src/code/main.c +++ b/src/code/main.c @@ -26,7 +26,6 @@ extern StackEntry sSchedStackInfo; extern StackEntry sAudioStackInfo; extern StackEntry sPadMgrStackInfo; extern AudioMgr sAudioMgr; -extern PadMgr gPadMgr; void Main(void* arg) { intptr_t fb; diff --git a/src/code/padmgr.c b/src/code/padmgr.c index 077ef1b2b..a04a14f57 100644 --- a/src/code/padmgr.c +++ b/src/code/padmgr.c @@ -1,58 +1,797 @@ +/** + * @file padmgr.c + * + * This file implements communicating with joybus devices at a high level and serving the results to other threads. + * + * Any device that can be plugged into one of the four controller ports such as a standard N64 controller is a joybus + * device. Some joybus devices are also located inside the cartridge such as EEPROM for save data or the Real-Time + * Clock, however neither of these are used in Zelda64 and so this type of communication is unimplemented. Unlike the + * padmgr implementation in OoT, there are three controller types supported: + * - Standard N64 Controller + * - N64 Mouse + * - Voice Recognition Unit + * + * Communicating with these devices is broken down into various layers: + * + * Other threads : The rest of the program that will use the polled data + * | + * PadMgr : Manages devices, submits polling commands at vertical retrace + * | + * Libultra osCont* routines : Interface for building commands and safely using the Serial Interface + * | + * Serial Interface : Hardware unit for sending joybus commands and receiving data via DMA + * | + * PIF : Forwards joybus commands and receives response data from the devices + * |---¬---¬---¬-------¬ + * 1 2 3 4 5 : The joybus devices plugged into the four controller ports or on the cartridge + * + * Joybus communication is handled on another thread as polling and receiving controller data is a slow process; the + * N64 programming manual section 26.2.4.1 quotes 2 milliseconds as the expected delay from calling + * `osContStartReadData` to receiving the data. By running this on a separate thread to the game state, work can be + * done while waiting for this operation to complete. + */ #include "global.h" +#include "io/controller.h" #include "ultra64/motor.h" -#pragma GLOBAL_ASM("asm/non_matchings/code/padmgr/Padmgr_GetControllerBitmask.s") +#define PADMGR_RETRACE_MSG (1 << 0) +#define PADMGR_PRE_NMI_MSG (1 << 1) +#define PADMGR_NMI_MSG (1 << 2) -#pragma GLOBAL_ASM("asm/non_matchings/code/padmgr/func_80174F24.s") +typedef enum { + /* 0 */ VOICE_INIT_FAILED, // voice initialization failed + /* 1 */ VOICE_INIT_TRY, // try to initialize voice + /* 2 */ VOICE_INIT_SUCCESS // voice initialized +} VoiceInitStatus; -#pragma GLOBAL_ASM("asm/non_matchings/code/padmgr/func_80174F44.s") +PadMgr* sPadMgrInstance = &gPadMgr; -#pragma GLOBAL_ASM("asm/non_matchings/code/padmgr/func_80174F7C.s") +s32 sPadMgrRetraceCount = 0; -#pragma GLOBAL_ASM("asm/non_matchings/code/padmgr/func_80174F9C.s") +s32 sVoiceInitStatus = VOICE_INIT_TRY; -#pragma GLOBAL_ASM("asm/non_matchings/code/padmgr/Padmgr_GetEventCallback.s") +/** + * Returns the bitmask of valid controllers. + * + * bit n set = controller (n+1) available. + */ +u8 PadMgr_GetValidControllersMask(void) { + return sPadMgrInstance->validCtrlrsMask; +} -#pragma GLOBAL_ASM("asm/non_matchings/code/padmgr/func_80175008.s") +/** + * Sets the callback function intended to be used to modify the rumble pak state on each VI with + * calls to `PadMgr_RumbleSetSingle`. The callback may be passed a single user-provided argument. + * + * @param callback callback to run before rumble state is updated for the current VI + * @param arg the argument to pass to the callback + * + * @see PadMgr_RumbleSetSingle + * @see PadMgr_UnsetRumbleRetraceCallback + */ +void PadMgr_SetRumbleRetraceCallback(void (*callback)(void*), void* arg) { + sPadMgrInstance->rumbleRetraceCallback = callback; + sPadMgrInstance->rumbleRetraceArg = arg; +} -#pragma GLOBAL_ASM("asm/non_matchings/code/padmgr/Padmgr_SetEventCallback.s") +/** + * Unsets the rumble retrace callback if the provided function and argument are the same as those already registered. + * + * @param callback the callback to test against + * @param arg the argument to test against + * + * @see PadMgr_SetRumbleRetraceCallback + */ +void PadMgr_UnsetRumbleRetraceCallback(void (*callback)(void*), void* arg) { + if ((sPadMgrInstance->rumbleRetraceCallback == callback) && (sPadMgrInstance->rumbleRetraceArg == arg)) { + sPadMgrInstance->rumbleRetraceCallback = NULL; + sPadMgrInstance->rumbleRetraceArg = NULL; + } +} -#pragma GLOBAL_ASM("asm/non_matchings/code/padmgr/func_8017506C.s") +/** + * Sets the callback function intended to be used to test for particular inputs on each VI, immediately after new + * inputs have been polled. + * + * This is useful for the rare cases where additional resolution is needed in the timings of player inputs, + * while the game is still updating comparatively slower. + * + * @param callback callback to run as soon as new inputs are obtained + * @param arg the argument to pass to the callback + * + * @see PadMgr_UnsetInputRetraceCallback + */ +void PadMgr_SetInputRetraceCallback(void (*callback)(void*), void* arg) { + sPadMgrInstance->inputRetraceCallback = callback; + sPadMgrInstance->inputRetraceArg = arg; +} -#pragma GLOBAL_ASM("asm/non_matchings/code/padmgr/Padmgr_Lock.s") +/** + * Unsets the input retrace callback if the provided function and argument are the same as those already registered. + * + * @param callback the callback to test against + * @param arg the argument to test against + * + * @see PadMgr_SetInputRetraceCallback + */ +void PadMgr_UnsetInputRetraceCallback(void (*callback)(void*), void* arg) { + if ((sPadMgrInstance->inputRetraceCallback == callback) && (sPadMgrInstance->inputRetraceArg == arg)) { + sPadMgrInstance->inputRetraceCallback = NULL; + sPadMgrInstance->inputRetraceArg = NULL; + } +} -#pragma GLOBAL_ASM("asm/non_matchings/code/padmgr/Padmgr_Unlock.s") +/** + * Acquires exclusive access to the serial event queue. + * + * When a DMA to/from PIF RAM completes, an SI interrupt is generated to notify the process that the DMA has completed + * and a message is posted to the serial event queue. If multiple processes are trying to use the SI at the same time + * it becomes ambiguous as to which DMA has completed, so a locking system is required to arbitrate access to the SI. + * + * Once the task requiring the serial event queue is complete, it should be released with a call to + * `PadMgr_ReleaseSerialEventQueue()`. + * + * If another process tries to acquire the event queue, the current thread will be blocked until the event queue is + * released. Note the possibility for a deadlock, if the thread that already holds the serial event queue attempts to + * acquire it again it will block forever. + * + * @return The message queue to which SI interrupt events are posted. + * + * @see PadMgr_ReleaseSerialEventQueue + */ +OSMesgQueue* PadMgr_AcquireSerialEventQueue(void) { + OSMesgQueue* serialEventQueue; -#pragma GLOBAL_ASM("asm/non_matchings/code/padmgr/func_801750FC.s") + osRecvMesg(&sPadMgrInstance->serialLockQueue, (OSMesg*)&serialEventQueue, OS_MESG_BLOCK); + return serialEventQueue; +} -#pragma GLOBAL_ASM("asm/non_matchings/code/padmgr/func_80175364.s") +/** + * Like `PadMgr_AcquireSerialEventQueue` but for Voice access. + * + * @see PadMgr_AcquireSerialEventQueue + * @see PadMgr_VoiceReleaseSerialEventQueue + */ +OSMesgQueue* PadMgr_VoiceAcquireSerialEventQueue(void) { + OSMesgQueue* serialEventQueue; -#pragma GLOBAL_ASM("asm/non_matchings/code/padmgr/func_80175434.s") + osRecvMesg(&sPadMgrInstance->serialLockQueue, (OSMesg*)&serialEventQueue, OS_MESG_BLOCK); + return serialEventQueue; +} -#pragma GLOBAL_ASM("asm/non_matchings/code/padmgr/func_8017544C.s") +/** + * Relinquishes access to the serial message queue, allowing another process to acquire and use it. + * + * @param serialEventQueue The serial message queue acquired by `PadMgr_AcquireSerialEventQueue` + * + * @see PadMgr_AcquireSerialEventQueue + */ +void PadMgr_ReleaseSerialEventQueue(OSMesgQueue* serialEventQueue) { + osSendMesg(&sPadMgrInstance->serialLockQueue, (OSMesg)serialEventQueue, OS_MESG_BLOCK); +} -#pragma GLOBAL_ASM("asm/non_matchings/code/padmgr/PadMgr_RumbleSet.s") +/** + * Like `PadMgr_ReleaseSerialEventQueue` but for Voice access. + * + * @see PadMgr_VoiceAcquireSerialEventQueue + */ +void PadMgr_VoiceReleaseSerialEventQueue(OSMesgQueue* serialEventQueue) { + osSendMesg(&sPadMgrInstance->serialLockQueue, (OSMesg)serialEventQueue, OS_MESG_BLOCK); +} -#pragma GLOBAL_ASM("asm/non_matchings/code/padmgr/PadMgr_ControllerHasRumblePak.s") +/** + * Locks controller input data while padmgr is reading new inputs or another thread is using the current inputs. + * This prevents new inputs overwriting the current inputs while they are in use. + * + * @see PadMgr_UnlockPadData + */ +void PadMgr_LockPadData(void) { + osRecvMesg(&sPadMgrInstance->lockQueue, NULL, OS_MESG_BLOCK); +} -#pragma GLOBAL_ASM("asm/non_matchings/code/padmgr/Padmgr_CalcStickEdges.s") +/** + * Unlocks controller input data, allowing padmgr to read new inputs or another thread to access the most recently + * polled inputs. + * + * @see PadMgr_LockPadData + */ +void PadMgr_UnlockPadData(void) { + osSendMesg(&sPadMgrInstance->lockQueue, NULL, OS_MESG_BLOCK); +} -#pragma GLOBAL_ASM("asm/non_matchings/code/padmgr/Padmgr_ParseState.s") +/** + * Activates the rumble pak for all controllers it is enabled on, stops it for all controllers it is disabled on and + * attempts to initialize it for a controller if it is not already initialized. + */ +void PadMgr_UpdateRumble(void) { + s32 i; + s32 ret; + OSMesgQueue* serialEventQueue = PadMgr_AcquireSerialEventQueue(); + s32 triedRumbleComm = false; -#pragma GLOBAL_ASM("asm/non_matchings/code/padmgr/func_801759BC.s") + for (i = 0; i < MAXCONTROLLERS; i++) { + if (sPadMgrInstance->ctrlrType[i] == PADMGR_CONT_NORMAL) { + // Check status for whether a controller pak is connected + if (sPadMgrInstance->padStatus[i].status & CONT_CARD_ON) { + if (sPadMgrInstance->pakType[i] == CONT_PAK_RUMBLE) { + if (sPadMgrInstance->rumbleEnable[i]) { + if (sPadMgrInstance->rumbleTimer[i] < 3) { + // Rumble pak start -#pragma GLOBAL_ASM("asm/non_matchings/code/padmgr/func_80175AE0.s") + if (osMotorStart(&sPadMgrInstance->rumblePfs[i]) != 0) { + // error + sPadMgrInstance->pakType[i] = CONT_PAK_NONE; + } else { + sPadMgrInstance->rumbleTimer[i] = 3; + } + triedRumbleComm = true; + } + } else { + if (sPadMgrInstance->rumbleTimer[i] != 0) { + // Rumble Pak stop -#pragma GLOBAL_ASM("asm/non_matchings/code/padmgr/Padmgr_Update.s") + if (osMotorStop(&sPadMgrInstance->rumblePfs[i]) != 0) { + // error + sPadMgrInstance->pakType[i] = CONT_PAK_NONE; + } else { + sPadMgrInstance->rumbleTimer[i]--; + } + triedRumbleComm = true; + } + } + } + } else if (sPadMgrInstance->pakType[i] != CONT_PAK_NONE) { + sPadMgrInstance->pakType[i] = CONT_PAK_NONE; + } + } + } -#pragma GLOBAL_ASM("asm/non_matchings/code/padmgr/Padmgr_Stop.s") + if (!triedRumbleComm) { + // Try to initialize the rumble pak for controller port `i` if a controller pak is connected and not already + // known to be an initialized a rumble pak + i = sPadMgrRetraceCount % MAXCONTROLLERS; -#pragma GLOBAL_ASM("asm/non_matchings/code/padmgr/func_80175E68.s") + if ((sPadMgrInstance->ctrlrType[i] == PADMGR_CONT_NORMAL) && + (sPadMgrInstance->padStatus[i].status & CONT_CARD_ON) && (sPadMgrInstance->pakType[i] != CONT_PAK_RUMBLE)) { + ret = osMotorInit(serialEventQueue, &sPadMgrInstance->rumblePfs[i], i); -#pragma GLOBAL_ASM("asm/non_matchings/code/padmgr/Padmgr_GetInput.s") + if (ret == 0) { + // Got rumble pak + sPadMgrInstance->pakType[i] = CONT_PAK_RUMBLE; + sPadMgrInstance->rumbleTimer[i] = 2; + } else if (ret == PFS_ERR_DEVICE) { + // Not a rumble pak + if (sPadMgrInstance->pakType[i] != CONT_PAK_OTHER) { + sPadMgrInstance->pakType[i] = CONT_PAK_OTHER; + } + } else if (ret == PFS_ERR_CONTRFAIL) { + // Pak communication error + } else if (ret == PFS_ERR_NOPACK) { + // No controller pak + } else { + // Unrecognized return code + Fault_AddHungupAndCrash("../padmgr.c", 594); + } + } + } -#pragma GLOBAL_ASM("asm/non_matchings/code/padmgr/Padmgr_GetInput2.s") + PadMgr_ReleaseSerialEventQueue(serialEventQueue); +} -#pragma GLOBAL_ASM("asm/non_matchings/code/padmgr/Padmgr_ThreadEntry.s") +/** + * Immediately stops rumble on all controllers + */ +void PadMgr_RumbleStop(void) { + OSMesgQueue* serialEventQueue = PadMgr_AcquireSerialEventQueue(); + s32 i; -#pragma GLOBAL_ASM("asm/non_matchings/code/padmgr/PadMgr_Init.s") + for (i = 0; i < MAXCONTROLLERS; i++) { + if ((sPadMgrInstance->ctrlrType[i] == PADMGR_CONT_NORMAL) && + (osMotorInit(serialEventQueue, &sPadMgrInstance->rumblePfs[i], i) == 0)) { + // If there is a rumble pak attached to this controller, stop it + osMotorStop(&sPadMgrInstance->rumblePfs[i]); + } + } + PadMgr_ReleaseSerialEventQueue(serialEventQueue); +} + +/** + * Prevents rumble for 12 VI, ~0.2 seconds at 60 VI/sec + */ +void PadMgr_RumblePause(void) { + sPadMgrInstance->rumbleOffTimer = 12; +} + +/** + * Enables or disables rumble on controller port `port` for 24 VI, + * ~0.4 seconds at 60 VI/sec and ~0.48 seconds at 50 VI/sec + */ +void PadMgr_RumbleSetSingle(s32 port, s32 enable) { + sPadMgrInstance->rumbleEnable[port] = enable; + sPadMgrInstance->rumbleOnTimer = 24; +} + +/** + * Enables or disables rumble on all controller ports for 24 VI, + * ~0.4 seconds at 60 VI/sec and ~0.48 seconds at 50 VI/sec + * + * @param enable Array of u8 of length MAXCONTROLLERS containing either true or false to enable or disable rumble + * for that controller + */ +void PadMgr_RumbleSet(u8 enable[MAXCONTROLLERS]) { + s32 i; + + for (i = 0; i < MAXCONTROLLERS; i++) { + sPadMgrInstance->rumbleEnable[i] = enable[i]; + } + sPadMgrInstance->rumbleOnTimer = 24; +} + +s32 PadMgr_ControllerHasRumblePak(s32 port) { + return sPadMgrInstance->pakType[port] == CONT_PAK_RUMBLE; +} + +void PadMgr_AdjustInput(Input* input) { + s32 plus; + s32 minus; + s32 pressX; + s32 pressX2; + s32 pressY; + s32 pressY2; + s32 pad1; + s32 pad2; + s32 pad3; + f32 pad4; + f32 newX; + f32 newY; + f32 magnitude; + f32 angle; + s8 curX = input->cur.stick_x; + s8 curY = input->cur.stick_y; + + if (CHECK_BTN_ANY(input->press.button, BTN_RESET) || (input->press.stick_x == 0)) { + input->press.stick_x = 61; + input->press.errno = -61; + input->press.stick_y = 63; + input->rel.errno = -63; + } + pressX = input->press.stick_x; + pressX2 = (s8)input->press.errno; + pressY = input->press.stick_y; + pressY2 = (s8)input->rel.errno; + + if (CHECK_BTN_ANY(input->cur.button, BTN_RESET)) { + minus = curX - 7; + plus = curX + 7; + + if (minus > 0) { + if (pressX < minus - 3) { + pressX = minus - 3; + input->press.stick_x = minus - 3; + } + } else if (plus < 0) { + if (pressX2 > plus + 3) { + pressX2 = plus + 3; + input->press.errno = plus + 3; + } + } + + minus = curY - 7; + plus = curY + 7; + + if (minus > 0) { + if (pressY < minus - 3) { + pressY = minus - 3; + input->press.stick_y = minus - 3; + } + } else if (plus < 0) { + if (pressY2 > plus + 3) { + pressY2 = plus + 3; + input->rel.errno = plus + 3; + } + } + } + + minus = curX - 7; + plus = curX + 7; + + if (minus > 0) { + newX = (pressX < minus) ? 1.0f : (f32)minus / pressX; + } else if (plus < 0) { + newX = (pressX2 > plus) ? -1.0f : -(f32)plus / pressX2; + } else { + newX = 0.0f; + } + + minus = curY - 7; + plus = curY + 7; + + if (minus > 0) { + newY = (pressY < minus) ? 1.0f : (f32)minus / pressY; + } else if (plus < 0) { + newY = (pressY2 > plus) ? -1.0f : -(f32)plus / pressY2; + } else { + newY = 0.0f; + } + + if (1) { + s32 pad; + + magnitude = sqrtf(SQ(newX) + SQ(newY)); + if (magnitude > 1.0f) { + magnitude = 1.0f; + } + angle = Math_Atan2F_XY(curY, -curX); + newX = -sinf(angle) * magnitude; + newY = cosf(angle) * magnitude; + } + input->rel.stick_x = newX * 60.5f; + input->rel.stick_y = newY * 60.5f; +} + +/** + * Updates `sPadMgrInstance->inputs` based on the error response of each controller + */ +void PadMgr_UpdateInputs(void) { + s32 diff; + Input* input = &sPadMgrInstance->inputs[0]; + s32 i; + OSContPad* pad = &sPadMgrInstance->pads[0]; + + for (i = 0; i < sPadMgrInstance->nControllers; i++, input++, pad++) { + s32 isStandardController; + + input->prev = input->cur; + isStandardController = sPadMgrInstance->ctrlrType[i] == PADMGR_CONT_NORMAL; + if (isStandardController) { + switch (pad->errno) { + case 0: + // No error, copy inputs + input->cur = *pad; + break; + + case CHNL_ERR_OVERRUN >> 4: + // Overrun error, reuse previous inputs + input->cur = input->prev; + break; + + case CHNL_ERR_NORESP >> 4: + // No response error, take inputs as 0 + input->cur.button = 0; + input->cur.stick_x = 0; + input->cur.stick_y = 0; + input->cur.errno = pad->errno; + if (sPadMgrInstance->ctrlrType[i] != PADMGR_CONT_NONE) { + // If we get no response, consider the controller disconnected + sPadMgrInstance->ctrlrType[i] = PADMGR_CONT_NONE; + sPadMgrInstance->pakType[i] = CONT_PAK_NONE; + sPadMgrInstance->rumbleTimer[i] = 0xFF; + } + break; + + default: + // Unknown error response + Fault_AddHungupAndCrash("../padmgr.c", 1098); + break; + } + } else { + input->cur.button = 0; + input->cur.stick_x = 0; + input->cur.stick_y = 0; + input->cur.errno = pad->errno; + } + + // If opposed directions on the D-Pad are pressed at the same time, mask both out + if ((input->cur.button & (BTN_DDOWN | BTN_DUP)) == (BTN_DDOWN | BTN_DUP)) { + input->cur.button &= ~(BTN_DDOWN | BTN_DUP); + } + if ((input->cur.button & (BTN_DRIGHT | BTN_DLEFT)) == (BTN_DRIGHT | BTN_DLEFT)) { + input->cur.button &= ~(BTN_DRIGHT | BTN_DLEFT); + } + + // Calculate pressed and relative inputs + diff = input->prev.button ^ input->cur.button; + input->press.button |= (u16)(diff & input->cur.button); + input->rel.button |= (u16)(diff & input->prev.button); + + if (1) {} + PadMgr_AdjustInput(input); + } +} + +/** + * Looks for plugged in voice recognition units and initializes them. + * + * This runs only on startup, there will be no attempts to initialize + * a VRU other than on the first VI retrace. + */ +void PadMgr_InitVoice(void) { + s32 i; + OSMesgQueue* serialEventQueue; + s32 ret; + + for (i = 0; i < sPadMgrInstance->nControllers; i++) { + if (sPadMgrInstance->ctrlrType[i] == PADMGR_CONT_VOICE_PLUGGED) { + serialEventQueue = PadMgr_AcquireSerialEventQueue(); + + ret = osVoiceInit(serialEventQueue, &gVoiceHandle, i); + + PadMgr_ReleaseSerialEventQueue(serialEventQueue); + + if (ret != 0) { + // error + } else { + sPadMgrInstance->ctrlrType[i] = PADMGR_CONT_VOICE; + sVoiceInitStatus = VOICE_INIT_SUCCESS; + func_801A4EB0(); + } + } + } + // If sVoiceInitStatus is still VOICE_INIT_TRY after the first attempt to initialize a VRU, don't try again + if (sVoiceInitStatus == VOICE_INIT_TRY) { + sVoiceInitStatus = VOICE_INIT_FAILED; + } +} + +/** + * Update the state of connected controllers + */ +void PadMgr_UpdateConnections(void) { + s32 ctrlrMask = 0; + s32 i; + char msg[50]; + + for (i = 0; i < MAXCONTROLLERS; i++) { + if (sPadMgrInstance->padStatus[i].errno == 0) { + switch (sPadMgrInstance->padStatus[i].type & CONT_TYPE_MASK) { + case CONT_TYPE_NORMAL: + // Standard N64 Controller + ctrlrMask |= (1 << i); + if (sPadMgrInstance->ctrlrType[i] == PADMGR_CONT_NONE) { + sPadMgrInstance->ctrlrType[i] = PADMGR_CONT_NORMAL; + } + break; + + case CONT_TYPE_MOUSE: + // N64 Mouse + if (sPadMgrInstance->ctrlrType[i] == PADMGR_CONT_NONE) { + sPadMgrInstance->ctrlrType[i] = PADMGR_CONT_MOUSE; + } + break; + + case CONT_TYPE_VOICE: + // Voice Recognition Unit + if (sPadMgrInstance->ctrlrType[i] == PADMGR_CONT_NONE) { + sPadMgrInstance->ctrlrType[i] = PADMGR_CONT_VOICE_PLUGGED; + sPadMgrInstance->pakType[i] = CONT_PAK_NONE; + } + break; + + default: + // Other/Unrecognized + if (sPadMgrInstance->ctrlrType[i] == PADMGR_CONT_NONE) { + sPadMgrInstance->ctrlrType[i] = PADMGR_CONT_UNK; + // "Recognized an unknown type of controller (%04x)" + sprintf(msg, "知らない種類のコントローラ(%04x)を認識しました", + sPadMgrInstance->padStatus[i].type); + } + // Missing break required for matching + } + } else if (sPadMgrInstance->ctrlrType[i] != PADMGR_CONT_NONE) { + // Plugged controller errored + sPadMgrInstance->ctrlrType[i] = PADMGR_CONT_NONE; + sPadMgrInstance->pakType[i] = CONT_PAK_NONE; + sPadMgrInstance->rumbleTimer[i] = 0xFF; + } + } + sPadMgrInstance->validCtrlrsMask = ctrlrMask; +} + +void PadMgr_HandleRetrace(void) { + OSMesgQueue* serialEventQueue = PadMgr_AcquireSerialEventQueue(); + + // Begin reading controller data + osContStartReadData(serialEventQueue); + + // Execute rumble callback + if (sPadMgrInstance->rumbleRetraceCallback != NULL) { + sPadMgrInstance->rumbleRetraceCallback(sPadMgrInstance->rumbleRetraceArg); + } + + // Wait for controller data + osRecvMesg(serialEventQueue, NULL, OS_MESG_BLOCK); + osContGetReadData(sPadMgrInstance->pads); + + // Clear all but controller 1 + bzero(&sPadMgrInstance->pads[1], sizeof(*sPadMgrInstance->pads) * (MAXCONTROLLERS - 1)); + + // If in PreNMI, clear all controllers + if (sPadMgrInstance->isResetting) { + bzero(sPadMgrInstance->pads, sizeof(sPadMgrInstance->pads)); + } + + // Query controller statuses + osContStartQuery(serialEventQueue); + osRecvMesg(serialEventQueue, NULL, OS_MESG_BLOCK); + osContGetQuery(sPadMgrInstance->padStatus); + + // Lock serial message queue + PadMgr_ReleaseSerialEventQueue(serialEventQueue); + + // Update connections + PadMgr_UpdateConnections(); + + // Lock input data + PadMgr_LockPadData(); + + // Update input data + PadMgr_UpdateInputs(); + + // Execute input callback + if (sPadMgrInstance->inputRetraceCallback != NULL) { + sPadMgrInstance->inputRetraceCallback(sPadMgrInstance->inputRetraceArg); + } + + // Unlock input data + PadMgr_UnlockPadData(); + + // Try and initialize a Voice Recognition Unit if not already attempted + if (sVoiceInitStatus != VOICE_INIT_FAILED) { + PadMgr_InitVoice(); + } + + // Rumble Pak + if (gFaultStruct.msgId != 0) { + // If fault is active, no rumble + PadMgr_RumbleStop(); + } else if (sPadMgrInstance->rumbleOffTimer > 0) { + // If the rumble off timer is active, no rumble + --sPadMgrInstance->rumbleOffTimer; + PadMgr_RumbleStop(); + } else if (sPadMgrInstance->rumbleOnTimer == 0) { + // If the rumble on timer is inactive, no rumble + PadMgr_RumbleStop(); + } else if (!sPadMgrInstance->isResetting) { + // If not resetting, update rumble + PadMgr_UpdateRumble(); + --sPadMgrInstance->rumbleOnTimer; + } + + sPadMgrRetraceCount++; +} + +void PadMgr_HandlePreNMI(void) { + sPadMgrInstance->isResetting = true; + PadMgr_RumblePause(); +} + +/** + * Fetches the most recently polled inputs from padmgr. + * + * NOTE: This function does not lock the inputs while reading them. + * The padmgr thread may interrupt this function and poll new inputs while the thread running this function is + * currently copying the old inputs potentially resulting in a mix of newly polled and old inputs being used. + * It is preferable to use `PadMgr_GetInput` or `PadMgr_GetInput2`. + * + * @param inputs Array of Input of length MAXCONTROLLERS to copy inputs into + * @param gameRequest True if polling inputs for updating the game state + */ +void PadMgr_GetInputNoLock(Input* inputs, s32 gameRequest) { + s32 i; + Input* input = &sPadMgrInstance->inputs[0]; + Input* inputOut = &inputs[0]; + s32 buttonDiff; + + for (i = 0; i < MAXCONTROLLERS; i++) { + if (gameRequest) { + // Copy inputs as-is, press and rel are calculated prior in `PadMgr_UpdateInputs` + *inputOut = *input; + // Zero parts of the press and rel inputs in the polled inputs so they are not read more than once + input->press.button = 0; + input->rel.button = 0; + } else { + // Take as the previous inputs the inputs that are currently in the destination array + inputOut->prev = inputOut->cur; + // Copy current inputs from the polled inputs + inputOut->cur = input->cur; + // Calculate press and rel from these + buttonDiff = inputOut->prev.button ^ inputOut->cur.button; + inputOut->press.button = inputOut->cur.button & buttonDiff; + inputOut->rel.button = inputOut->prev.button & buttonDiff; + PadUtils_UpdateRelXY(inputOut); + } + input++; + inputOut++; + } +} + +/** + * Fetches the most recently polled inputs from padmgr. + * + * @param inputs Array of Input of length MAXCONTROLLERS to copy inputs into + * @param gameRequest True if polling inputs for updating the game state + */ +void PadMgr_GetInput(Input* inputs, s32 gameRequest) { + PadMgr_LockPadData(); + PadMgr_GetInputNoLock(inputs, gameRequest); + PadMgr_UnlockPadData(); +} + +/** + * Copy of `PadMgr_GetInput` + * + * @see PadMgr_GetInput + */ +void PadMgr_GetInput2(Input* inputs, s32 gameRequest) { + PadMgr_LockPadData(); + PadMgr_GetInputNoLock(inputs, gameRequest); + PadMgr_UnlockPadData(); +} + +void PadMgr_ThreadEntry() { + s16* interruptMsg = NULL; + s32 actionBits; + s32 exit; + + osCreateMesgQueue(&sPadMgrInstance->interruptQueue, sPadMgrInstance->interruptMsgBuf, + ARRAY_COUNT(sPadMgrInstance->interruptMsgBuf)); + IrqMgr_AddClient(sPadMgrInstance->irqMgr, &sPadMgrInstance->irqClient, &sPadMgrInstance->interruptQueue); + + actionBits = 0; + exit = false; + + while (!exit) { + // Process all messages currently in the queue, instead of only a single mssage. + // Deduplicates the same message. + do { + osRecvMesg(&sPadMgrInstance->interruptQueue, (OSMesg*)&interruptMsg, OS_MESG_BLOCK); + switch (*interruptMsg) { + case OS_SC_RETRACE_MSG: + actionBits |= PADMGR_RETRACE_MSG; + break; + + case OS_SC_PRE_NMI_MSG: + actionBits |= PADMGR_PRE_NMI_MSG; + break; + + case OS_SC_NMI_MSG: + actionBits |= PADMGR_NMI_MSG; + break; + } + } while (!MQ_IS_EMPTY(&sPadMgrInstance->interruptQueue)); + + // Act on received messages + while (actionBits != 0) { + if (actionBits & PADMGR_NMI_MSG) { + actionBits &= ~PADMGR_NMI_MSG; + exit = true; + } else if (actionBits & PADMGR_PRE_NMI_MSG) { + actionBits &= ~PADMGR_PRE_NMI_MSG; + PadMgr_HandlePreNMI(); + } else if (actionBits & PADMGR_RETRACE_MSG) { + actionBits &= ~PADMGR_RETRACE_MSG; + PadMgr_HandleRetrace(); + } + } + } + + IrqMgr_RemoveClient(sPadMgrInstance->irqMgr, &sPadMgrInstance->irqClient); +} + +void PadMgr_Init(OSMesgQueue* siEvtQ, IrqMgr* irqMgr, OSId threadId, OSPri pri, void* stack) { + bzero(sPadMgrInstance, sizeof(PadMgr)); + sPadMgrInstance->irqMgr = irqMgr; + + // These are unique access tokens, there should only ever be room for one OSMesg in these queues + osCreateMesgQueue(&sPadMgrInstance->serialLockQueue, &sPadMgrInstance->serialMsg, 1); + osCreateMesgQueue(&sPadMgrInstance->lockQueue, &sPadMgrInstance->lockMsg, 1); + + PadMgr_UnlockPadData(); + PadSetup_Init(siEvtQ, &sPadMgrInstance->validCtrlrsMask, sPadMgrInstance->padStatus); + sPadMgrInstance->nControllers = MAXCONTROLLERS; + osContSetCh(sPadMgrInstance->nControllers); + PadMgr_ReleaseSerialEventQueue(siEvtQ); + + osCreateThread(&sPadMgrInstance->thread, threadId, PadMgr_ThreadEntry, sPadMgrInstance, stack, pri); + osStartThread(&sPadMgrInstance->thread); +} diff --git a/src/code/sys_rumble.c b/src/code/sys_rumble.c index 4101758be..06bef6e34 100644 --- a/src/code/sys_rumble.c +++ b/src/code/sys_rumble.c @@ -24,12 +24,12 @@ void RumbleManager_Update(RumbleManager* rumbleMgr) { if (D_801D1E70) { for (i = 0; i < MAXCONTROLLERS; i++) { - func_8017544C(i, false); + PadMgr_RumbleSetSingle(i, false); } } D_801D1E70 = rumbleMgr->updateEnabled; - func_80175434(); + PadMgr_RumblePause(); return; } @@ -39,7 +39,7 @@ void RumbleManager_Update(RumbleManager* rumbleMgr) { // Start up the manager by wiping old requests if (rumbleMgr->state == RUMBLEMANAGER_STATE_INITIAL) { for (i = 0; i < MAXCONTROLLERS; i++) { - func_8017544C(i, false); + PadMgr_RumbleSetSingle(i, false); } for (i = 0; i < RUMBLE_REQUEST_BUFFER_SIZE; i++) { @@ -59,7 +59,7 @@ void RumbleManager_Update(RumbleManager* rumbleMgr) { rumbleMgr->state = RUMBLEMANAGER_STATE_RUNNING; - func_80175434(); + PadMgr_RumblePause(); } if (rumbleMgr->state != RUMBLEMANAGER_STATE_WIPE) { @@ -146,7 +146,7 @@ void RumbleManager_Update(RumbleManager* rumbleMgr) { rumbleMgr->overrideDecayStep = 0; rumbleMgr->overrideAccumulator = 0; - func_80175434(); + PadMgr_RumblePause(); } } diff --git a/src/code/z_parameter.c b/src/code/z_parameter.c index 41441f3a2..fe4ca5ef8 100644 --- a/src/code/z_parameter.c +++ b/src/code/z_parameter.c @@ -29,7 +29,7 @@ typedef struct { /* 0x03 */ u8 flags3; } RestrictionFlags; -Input sPostmanTimerInput[4]; +Input sPostmanTimerInput[MAXCONTROLLERS]; #define RESTRICTIONS_TABLE_END 0xFF @@ -887,10 +887,10 @@ void Interface_SetVertices(PlayState* play) { s32 sPostmanTimerInputBtnAPressed = false; -void Interface_PostmanTimerCallback(s32 arg0) { +void Interface_PostmanTimerCallback(void* arg) { s32 btnAPressed; - func_80175E68(&sPostmanTimerInput[0], 0); + PadMgr_GetInputNoLock(sPostmanTimerInput, false); btnAPressed = CHECK_BTN_ALL(sPostmanTimerInput[0].cur.button, BTN_A); if ((btnAPressed != sPostmanTimerInputBtnAPressed) && btnAPressed) { gSaveContext.postmanTimerStopOsTime = osGetTime(); @@ -5654,7 +5654,7 @@ void Interface_DrawTimers(PlayState* play) { } gSaveContext.timerStates[TIMER_ID_POSTMAN] = TIMER_STATE_POSTMAN_COUNTING; sPostmanTimerInputBtnAPressed = true; - func_80174F7C(Interface_PostmanTimerCallback, NULL); + PadMgr_SetInputRetraceCallback(Interface_PostmanTimerCallback, NULL); break; case TIMER_STATE_POSTMAN_STOP: @@ -5663,7 +5663,7 @@ void Interface_DrawTimers(PlayState* play) { postmanTimerStopOsTime - ((void)0, gSaveContext.timerStartOsTimes[TIMER_ID_POSTMAN]) - ((void)0, gSaveContext.timerPausedOsTimes[TIMER_ID_POSTMAN])); gSaveContext.timerStates[TIMER_ID_POSTMAN] = TIMER_STATE_POSTMAN_END; - func_80174F9C(Interface_PostmanTimerCallback, NULL); + PadMgr_UnsetInputRetraceCallback(Interface_PostmanTimerCallback, NULL); break; case TIMER_STATE_POSTMAN_COUNTING: @@ -7211,7 +7211,7 @@ void Interface_Update(PlayState* play) { void Interface_Destroy(PlayState* play) { Map_Destroy(play); - func_80174F9C(Interface_PostmanTimerCallback, NULL); + PadMgr_UnsetInputRetraceCallback(Interface_PostmanTimerCallback, NULL); } void Interface_Init(PlayState* play) { diff --git a/src/code/z_rumble.c b/src/code/z_rumble.c index ce5cc16b4..3cca1d432 100644 --- a/src/code/z_rumble.c +++ b/src/code/z_rumble.c @@ -68,11 +68,11 @@ void Rumble_Request(f32 distSq, u8 sourceIntensity, u8 decayTimer, u8 decayStep) void Rumble_Init(void) { RumbleManager_Init(&gRumbleMgr); - func_80174F24(Rumble_Update, NULL); + PadMgr_SetRumbleRetraceCallback(Rumble_Update, NULL); } void Rumble_Destroy(void) { - func_80174F44(Rumble_Update, NULL); + PadMgr_UnsetRumbleRetraceCallback(Rumble_Update, NULL); RumbleManager_Destroy(&gRumbleMgr); } |
