Found at 800125B8
Summary
This function manages the CD based on IO and/or interrupt states.
Behavior
This is a complicated function with lots of branching, as such Behavior will be split into different sections based on how the function handles each state.
When the function is entered a counter is incremented to keep track of how many times the function has been called. Multiple following branches check the current IO state and or CD interrupts to decide what to do with the CD.
For a ReadN IO state if time elapsed since the read began is less than 10 seconds do nothing and exit. Otherwise enough time has passed for a retry and the IO state is set to the retry state, the CD callback is deregistered, and the function returns.
If in a ReadS operation the function tries to call cdlib_xa_manager to check the status of the stream. However in order to throttle XA manage calls, it checks if the current call counter value is a multiple of 5. If its not it immediately returns.
For an IO state of Retry, set the cd mode to our program default. Reset the global cd library values used by the callback that track how many sectors remain to be read and the destination address of data. The ReadN command is submitted again and if its unsuccessful the function exits, we will try again in the next pump. If successful get how many vblanks have passed since the game started, register the callback and return.
For the Error state CdlNop is sent unless the following conditions are all true: the last blocking command completed, it was a CdlNop, and the lid is closed. The spindle motor is then checked, if it is on set the CD to Ready and exit, otherwise start the spindle motor and set the cd mode to our program default and exit.
The remaining IO state conditions fall under this last umbrella which are Ready, Seek and Idle. First the CD interrupt status is checked, for any value that is not CdlComplete or CdlDiskError the function exits.
For disk error interrupt result a branch is taken that depending on if the IO state is ReadN sets the state to Retry and returns or for any other state sets the IO state to error, executes a CdlNop and returns.
For CdlComplete interrupt result a branch is taken depending on the values of our enqueue and exec index. If the values are not equal that means we have commands in the queue we haven’t executed so the function proceeds to execute a command and return. If the indexes are equal regardless and the IO state is seek, the current seek position is set to what the target location was, meaning the head has moved to our target location, then the function returns. Otherwise for all other states the state is set to Idle and the function returns.
Control Flow
- Increment the cdlib_manager_callctr.
- Check if our IO state is 40000h (ReadN) and branch if it is.
- Check if it has been more than 10 seconds since the ReadN operation started by calling VSync(-1) and subtracting against CD_VBLANKS_SINCE_START.
- If it has been less than 10 seconds return, otherwise, set the IO state to E0000h(Retry), deregister the callback and return.
- Check if our IO state is greater than ReadN and branch if it is.
- Check if our IO state is Retry and branch if it is.
- Call CdControlB in a loop with the exit condition being the command was accepted to set the cd mode to our program default.
- Reset global variables that were used to track the progress of the read to the base values for that read. These variables are cdlib_remaining_sectors_to_read and cdlib_current_read_dest_ptr.
- Execute the ReadN operation again and if the command was not accepted return.
- Write the vblanks elapsed since game start to CD_VBLANKS_SINCE_START.
- Set the IO state to ReadN
- Register the callback and exit.
- Check if our IO state is Error and branch if it is.
- Call CdSync to get the interrupt status of the Cd.
- Branch to send a CdlNop command and return if interrupt status was not CdlComplete.
- Branch to send a CdlNop command and return if the last command is not CdlNop
- Branch to send a CdlNop command and return if the lid is or was open based on the CD status code.
- Branch to start the spindle motor if the spindle motor is off based on the CD status code. To start the spindle motor we send a CdlStandby command in a loop us CdControlB with the loop exit condition being the command was accepted. The same thing is done with the CdlSetMode command. The mode is the program default.
- If we dont branch on any of these set the IO state to Ready and return.
- Jump to the ready_seek_idle_label. If we fell through that means our IO state is Ready
- Check if our IO state is Retry and branch if it is.
- Check if our IO state is ReadS and branch if it is.
- Check if our call counter is a multiple of 5 if it isnt exit.
- If it is call cd_xa_manage then exit.
- Jump to the ready_seek_idle_label. If we fell through that means the IO state is Idle or Seek.
- Call CdSync check the interrupt status of the Cd.
- Branch if the interrupt status is CdlComplete.
- Branch if enqueue index is equal to the exec index.
- Check if the IO state is Seek and if it is, set what i believe is the variable that tracks current seek head position to the targe position variable value. (LBA)
- Regardless if the above is done or not set the IO state to Idle and exit.
- If they are not equal, this means we have commands in the buffer that need to be executed. Call cdlib_execute_cmd to execute the next command then exit.
- Branch if enqueue index is equal to the exec index.
- Branch if the interrupt status is CdlDiskError.
- Check if the IO state is currently ReadN.
- If it is set the IO state to Retry and exit, otherwise set the IO state to Error, issue a CdlNop command and exit.
- Jump to exit the function for any other interrupt status.
Notes
- The CD_ VBLANK_SINCE_START global variable is used holds how many vblanks have elapsed since the game started. It is written every time a ReadN command is issued in order to track how many seconds have passed since the command.
- This function is typically pumped in a loop until the IO state becomes idle, meaning the function does a lot of “set it to this state to handle it in the next call”.
- A CdlNop also has to be sent if the lid is or was open because the nop resets the CdlStatShellOpen flag.