Skip to content
Snippets Groups Projects
Commit af6560f5 authored by Geo Ster's avatar Geo Ster
Browse files

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.
parent 1cce9b6f
No related branches found
No related tags found
No related merge requests found
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment