You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
TMA is a latch. As soon as it is written, the output shows that value.
128
-
That explains that when TMA is written and TIMA is being incremented,
129
-
the value written to TMA is also written to TIMA. It doesn't affect the
130
-
IF flag though.
35
+
Notice how the bits themselves are connected to the multiplexer and then to the falling-edge detector; this causes a few odd behaviors:
36
+
37
+
- Resetting the entire system counter (by writing to `DIV`) can reset the bit currently selected by the multiplexer, thus sending a "Timer tick" and/or "[DIV-APU event](<#DIV-APU>)" pulse early.
38
+
- Changing which bit of the system counter is selected (by changing the "Clock select" bits of [`TAC`]) from a bit currently set to another that is currently unset, will send a "Timer tick" pulse.
39
+
(For example: if the system counter is equal to \$3FF0 and `TAC` to \$FC, writing \$05 or \$06 to `TAC` will instantly send a "Timer tick", but \$04 or \$07 won't.)
40
+
- On monochrome consoles, disabling the timer if the currently selected bit is set, will send a "Timer tick" once.
41
+
This does not happen on Color models.
42
+
- On Color models, a write to `TAC` that fulfills the previous bullet's conditions *and* turns the timer on (it was disabled before) may or may not send a "Timer tick".
43
+
The exact behaviour varies between individual consoles.
44
+
45
+
## Timer overflow behavior
46
+
47
+
When `TIMA` overflows, the value from `TMA` is copied, and the timer flag is set in [`IF`], but **one M-cycle later**.
48
+
This means that `TIMA` is equal to \$00 for the M-cycle after it overflows.
49
+
50
+
This only happens when `TIMA` overflows from incrementing, it cannot be made to happen by manually writing to `TIMA`.
51
+
52
+
Here is an example; `SYS` represents the lower 8 bits of the system counter, and `TAC` is \$FD (timer enabled, bit 1 of `SYS` selected as source):
53
+
54
+
<figure><figcaption>
55
+
56
+
`TIMA` overflows on cycle <var>A</var>, but the interrupt is only requested on cycle <var>B</var>:
57
+
58
+
</figcaption>
59
+
60
+
M-cycle | | ||<var>A</var>|<var>B</var>||​
61
+
--------|----|----|----|--------|----|----|---
62
+
`SYS` | 2B | 2C | 2D | 2E | 2F | 30 | 31
63
+
`TIMA` | FE | FF | FF | **00** | 23 | 24 | 24
64
+
`TMA` | 23 | 23 | 23 | 23 | 23 | 23 | 23
65
+
`IF` | E0 | E0 | E0 | **E0** | E4 | E4 | E4
66
+
67
+
</figure>
68
+
69
+
Here are some unexpected behaviors:
70
+
71
+
1. Writing to `TIMA` during cycle <var>A</var> acts as if the overflow **didn't happen**!
72
+
`TMA` will not be copied to `TIMA` (the value written will therefore stay), and bit 2 of `IF` will not be set.
73
+
Writing to `DIV`, `TAC`, or other registers won't prevent the `IF` flag from being set or `TIMA` from being reloaded.
74
+
2. Writing to `TIMA` during cycle <var>B</var> will be ignored; `TIMA` will be equal to `TMA` at the end of the cycle anyway.
75
+
3. Writing to `TMA` during cycle <var>B</var> will have the same value copied to `TIMA` as well, on the same cycle.
<details><summary>Explanation of the above behaviors:</summary>
82
+
83
+
1. Writing to `TIMA` blocks the falling edge from the increment from being detected (see the `AND` gate)[^write_edge].
84
+
2. The "Load" signal stays enabled for the entirety of cycle <var>B</var>, and since `TIMA` is made of <abbrtitle="T-flip-flop with Asynchronous Load">TAL</abbr> cells, it's constantly copying its input.
85
+
However, the "Write to TIMA" signal gets reset in the middle of the cycle, thus the multiplexer emits `TMA`'s value again; in essence, the CPU's write to `TIMA`*does* go through, but it's overwritten right after.
86
+
3. As mentioned in the previous bullet point, `TIMA` constantly copies its input, so it updates together with `TMA`.
87
+
This and the previous bullet point can be emulated as if `TMA` was copied to `TIMA` at the very end of the cycle, though this is not quite what's happening in hardware.
88
+
89
+
[^write_edge]: This is necessary, because otherwise writing a number with bit 7 reset (either from the CPU or from `TMA`) when `TIMA`'s bit 7 is set, would trigger the bit 7 falling edge detector and thus schedule a spurious interrupt.
0 commit comments