Add support for IOP timers and interrupts
* After a while the IOP starts setting up timer 5 so we need to start implementing timer support. Timers are pretty simple actually. Each one has 3 32bit registers (we use 64bit registers to check for overflow), a count register that counts the number of cycles, the mode register which configures the timer and the target register which generates an interrupt when count == target. For now that's all we need. * In addition, interrupts are also implmented. These are a bit more complicated since they involve COP0, but not to difficult either. The IOP has 3 registers, I_MASK, I_STAT and I_CTRL. I_CTRL acts as a global enable/disable so it's pretty simple. I_STAT is a bit mask that states which interrupts are pending. I_MASK on the other hand has the ability to enable/disable specific interrupts. So to check if the interrupt will be executed we must do !I_CTRL && (I_MASK & I_STAT). All info can be found on ps2tek/nocash psx * On the EE side, a few new instructions are added to progress further. Now the EE starts setting up the GIF, which is quite exciting! * I think it's a good time to also elaborate on how we read structs. Instead of using switch statements I prefer pointers and struct because these generate a lot more compat code, even with compiler optimizations and eliminate the need for branches. This should not be of concern on normal applications but we are special ;). Most registers on the console are 32bit, so structs are cast to uint32_t* to access them. And since the offsets are always in bytes we must divide them by sizeof(uint32_t) = 4 (I prefer >> 2 since it's more efficient) * Some registers however are peculiar in a sense that a parts of them are located in completely different address ranges. This is bad because for example timer 0 and timer 3 have the same offset of 0 from their relative address ranges. To fix this, we introduce a variable called group that records which "group" the write/read is refering to, with some simple bit masks. The result is casted to bool which converts the result to 0 or 1. Then the expression "offset + group * <number>" is used to access registers. In the timer examples accessing timer 0 will give group 0 and offset 0 so timer 0 will be accessed. With timer 3 though group will be 1 so 0 + 1 * 3 = 3 will be accessed. This a convenient way to bypass branches.
Showing
- CMakeLists.txt 1 addition, 1 deletionCMakeLists.txt
- src/common/memory.h 2 additions, 1 deletionsrc/common/memory.h
- src/cpu/ee/ee.cc 63 additions, 3 deletionssrc/cpu/ee/ee.cc
- src/cpu/ee/ee.h 5 additions, 1 deletionsrc/cpu/ee/ee.h
- src/cpu/iop/cop0.h 20 additions, 18 deletionssrc/cpu/iop/cop0.h
- src/cpu/iop/iop.cc 28 additions, 6 deletionssrc/cpu/iop/iop.cc
- src/cpu/iop/iop.h 76 additions, 13 deletionssrc/cpu/iop/iop.h
- src/cpu/iop/timers.cc 69 additions, 0 deletionssrc/cpu/iop/timers.cc
- src/cpu/iop/timers.h 53 additions, 0 deletionssrc/cpu/iop/timers.h
- src/main.cc 0 additions, 1 deletionsrc/main.cc
Loading
Please register or sign in to comment