Root Counters: Difference between revisions
| Revision as of 02:43, 23 October 2026 RemmyLee (GPU at register depth: GP0, GP1, GPUSTAT, VRAM, rendering, T-cache, video timing) | Revision as of 02:44, 23 October 2026 RemmyLee (CPU and system bus at register depth: instruction set, COP0, I/O map, memory control, interrupts, DMA, timers) | ||
| Line 1: | Line 1: | ||
| + | --- | ||
| + | title: Root Counters | ||
| + | type: register | ||
| + | summary: Three 16-bit hardware timers and the vertical sync counter of the PlayStation. | ||
| + | category: Hardware | ||
| + | --- | ||
| + | |||
| + | ## Summary | ||
| + | |||
| + | The root counters are timers that count automatically. Sony provides them for time limits and timing adjustments, which game programs need. A counter counts up from zero, and when it reaches its target value it generates an interrupt, clears to zero and starts again. Each interrupt can be masked.[^ovr27] | ||
| + | |||
| + | Sony's libraries name four root counters. RCntCNT0 to RCntCNT2 are 16-bit hardware timers with registers at 1F801100h, 1F801110h and 1F801120h. The table below shows all four. RCntCNT3 counts vertical synchronization, and its target value is fixed at 1, so it generates an interrupt at each vertical blank.[^ovr27][^tm] | ||
| + | |||
| + | Table: Root counters | ||
| + | |||
| + | | Macro | Registers | Clock sources | Notes | | ||
| + | | --- | --- | --- | --- | | ||
| + | | RCntCNT0 | 1F801100h | System clock or dot clock | | | ||
| + | | RCntCNT1 | 1F801110h | System clock or H-blank | Target value more than 2 | | ||
| + | | RCntCNT2 | 1F801120h | System clock or system clock / 8 | Target value more than 2 | | ||
| + | | RCntCNT3 | None | Vertical synchronization | Target value is fixed at 1; halting the count is invalid | | ||
| + | |||
| + | The Notes column is Sony's. psx-spx documents no timer registers for the vertical sync counter. The vertical blank reaches the CPU as IRQ0 in [I_STAT](/hardware/interrupts/), while the three hardware timers raise IRQ4, IRQ5 and IRQ6.[^ovr27][^io][^irq] | ||
| + | |||
| + | ## Registers | ||
| + | |||
| + | Each hardware timer has three registers at its base address. Official names for the registers and bits are in timerman.h of Sony's PlayStation 2 IOP SDK, which psx-spx follows. The PlayStation libraries call the third register TARGET.[^tm] | ||
| + | |||
| + | Table: Timer registers | ||
| + | |||
| + | | Address | Name | Content | | ||
| + | | --- | --- | --- | | ||
| + | | 1F801100h + 10h x N | COUNT | Current counter value (R/W) | | ||
| + | | 1F801104h + 10h x N | MODE | Counter mode (R/W) | | ||
| + | | 1F801108h + 10h x N | COMP (TARGET) | Counter target value (R/W) | | ||
| + | |||
| + | ### COUNT - Current Value | ||
| + | |||
| + | Bits 0 to 15 hold the current counter value and bits 16 to 31 are garbage. The counter increments by itself and may be written with any value. It is reset to 0000h on every write to MODE and when it reaches its reset condition, which is either FFFFh or the target value.[^tmc] | ||
| + | |||
| + | A current value written above the target value does not match the target on the way up. The counter therefore runs to FFFFh, wraps to 0000h once and only then uses the target value.[^tmc] | ||
| + | |||
| + | ### MODE - Counter Mode | ||
| + | |||
| + | MODE selects the synchronization, reset point, interrupt conditions and clock source of a counter:[^tmm] | ||
| + | |||
| + | Table: MODE bits | ||
| + | |||
| + | | Bits | Name | Content | | ||
| + | | --- | --- | --- | | ||
| + | | 0 | GATF | Synchronization enable (0 = free run, 1 = synchronize by GATM) | | ||
| + | | 1 to 2 | GATM | Synchronization mode (0 to 3, see below) | | ||
| + | | 3 | ZRET | Reset counter to 0000h (0 = after FFFFh, 1 = after COMP) | | ||
| + | | 4 | CMP | IRQ when counter = COMP (0 = off, 1 = on) | | ||
| + | | 5 | OVFL | IRQ when counter = FFFFh (0 = off, 1 = on) | | ||
| + | | 6 | REPT | IRQ once or repeatedly (0 = one-shot, 1 = repeat) | | ||
| + | | 7 | LEVL | IRQ pulse or toggle (0 = short pulse of INTF = 0, 1 = toggle INTF) | | ||
| + | | 8 | EXTC | Clock source of timers 0 and 1 (0 = system clock, 1 = dot clock or H-blank) | | ||
| + | | 9 | PSCL | Prescaler of timer 2 (0 = divide by 1, 1 = divide by 8) | | ||
| + | | 10 | INTF | Interrupt request (0 = yes, 1 = no), set by a write (R) | | ||
| + | | 11 | EQUF | Reached COMP (0 = no, 1 = yes), reset after reading (R) | | ||
| + | | 12 | OVFF | Reached FFFFh (0 = no, 1 = yes), reset after reading (R) | | ||
| + | | 13 to 15 | | Unused (zero) | | ||
| + | | 16 to 31 | | Garbage | | ||
| + | |||
| + | The meaning of GATM depends on the counter. Timer 0 synchronizes to H-blank and timer 1 to V-blank with the same four modes, while timer 2 has only two behaviors:[^tmm] | ||
| + | |||
| + | Table: Synchronization modes | ||
| + | |||
| + | | GATM | Timer 0 (H-blank) and timer 1 (V-blank) | Timer 2 | | ||
| + | | --- | --- | --- | | ||
| + | | 0 | Pause the counter during the blank | Stop the counter at its current value | | ||
| + | | 1 | Reset the counter to 0000h at the blank | Free run | | ||
| + | | 2 | Reset at the blank and pause outside it | Free run | | ||
| + | | 3 | Pause until the first blank, then free run | Stop the counter at its current value | | ||
| + | |||
| + | In one-shot mode the interrupt is pulsed or toggled only once. The counter keeps counting, but further interrupts are suppressed until MODE is written again. If both CMP and OVFL are set, one-shot mode triggers only the condition that occurs first.[^tmm] | ||
| + | |||
| + | Pulse mode is the normal setting. INTF then stays 1 except for a few clock cycles at each interrupt. In toggle mode INTF is set by the write to MODE and inverts at each interrupt, so in repeat mode IRQ4 to IRQ6 occur only on every second match, when INTF changes from 1 to 0.[^tmm] | ||
| + | |||
| + | ### COMP - Target Value | ||
| + | |||
| + | Bits 0 to 15 hold the target value and bits 16 to 31 are garbage. With ZRET set the counter counts up to and including the target value and then restarts at 0000h.[^tmt] | ||
| + | |||
| + | ## Reset and Wrap Timing | ||
| + | |||
| + | A counter reset by a write to MODE stays at 0000h for 2 clock cycles before it counts. A written current value likewise stays for 2 cycles before the counter counts or checks the target. A wrap at FFFFh holds 0000h for only 1 cycle, but a reset at the target value holds 0000h for 2 cycles.[^tmr] | ||
| + | |||
| + | With a target of 0001h and ZRET set the counter therefore repeats the sequence 0000h 0000h 0001h.[^tmr] | ||
| + | |||
| + | ## Clock Sources and Counter Timing | ||
| + | |||
| + | One system clock tick is about 0.03 microseconds. The display counters run at the timings below:[^ovr28] | ||
| + | |||
| + | Table: Counter timing | ||
| + | |||
| + | | Counter event | NTSC | PAL | Unit | | ||
| + | | --- | --- | --- | --- | | ||
| + | | Vertical sync | 1/60 | 1/50 | Second | | ||
| + | | Horizontal sync | 63.56 | 64.00 | Microsecond | | ||
| + | | Pixel display | N x 0.0186243 | N x 0.01879578 | Microsecond | | ||
| + | |||
| + | The pixel display period depends on the display width through N:[^ovr28] | ||
| + | |||
| + | Table: Pixel display timing and display width | ||
| + | |||
| + | | Display width | N | | ||
| + | | --- | --- | | ||
| + | | 256 pixels | 10 | | ||
| + | | 320 | 8 | | ||
| + | | 384 | 7 | | ||
| + | | 512 | 5 | | ||
| + | | 640 | 4 | | ||
| + | |||
| + | The GPU dot clock and H-blank are not synchronized with the CPU clock, so the counter may change during the CPU read cycle. A program should therefore read the counter repeatedly until two reads give the same value or the second is slightly larger.[^tmd] | ||
| + | |||
| + | ## Library Modes | ||
| + | |||
| + | The kernel library sets the counters with mode macros combined by logical OR. RCntMdINTR permits the interrupt and RCntMdNOINTR prohibits it, which leaves polling as the only method.[^ovr28] | ||
| + | |||
| + | Table: Root counter mode (2) | ||
| + | |||
| + | | Macro | Root counter | Type of counter | | ||
| + | | --- | --- | --- | | ||
| + | | RCnDtMdSP (default) | RCntCNT0, 1 | (Use prohibited) | | ||
| + | | RCnDtMdSP (default) | RCntCNT2 | System clock 8 second cycle | | ||
| + | | RCnDtMdSP (default) | RCntCNT3 | Vertical blanking | | ||
| + | | RCntMdSC | RCntCNT0, 1 | System clock | | ||
| + | | RCntMdSC | RCntCNT2, 3 | Not valid | | ||
| + | |||
| + | RCntMdFR, the default, counts normally. RCntMdGATE makes the counter count only while its gate condition holds:[^ovr28][^ovr29] | ||
| + | |||
| + | Table: Root counter gate condition | ||
| + | |||
| + | | Root counter | Gate condition | | ||
| + | | --- | --- | | ||
| + | | RCntCNT0 | Not during horizontal blanking | | ||
| + | | RCntCNT1 | Not during vertical blanking | | ||
| + | | RCntCNT2, 3 | None (usual time count) | | ||
| + | |||
| + | The counters count in hardware, so StopRCnt() does not stop a count. StopRCnt() only stops the counter from generating interrupts, and StartRCnt() only permits them.[^ovr28] | ||
| + | |||
| + | ## Use by the Kernel | ||
| + | |||
| + | All counters are stopped or free running immediately after the kernel starts, and a program must therefore initialize a counter before it uses one. The kernel itself uses root counter 3 (vertical blanking) to read the controller buttons. The button state cannot be read while root counter 3 is stopped or uninitialized.[^ovr29] | ||
| + | |||
| + | The beginning of the vertical blanking period can be detected with root counter 3 and the event management service. The PS-X OS manual gives this sequence:[^os44] | ||
| + | |||
| + | ``` | ||
| + | EnterCriticalSection(); | ||
| + | EV = OpenEvent(RCntCNT3, EvSpINT, EvMdNOINTR, NULL); | ||
| + | EnableEvent(EV); | ||
| + | SetRCnt(RCntCNT3, 1, RCntMdINTR); | ||
| + | StartRCnt(RCntCNT3); | ||
| + | while (1) { | ||
| + | WaitEvent(EV); | ||
| + | } | ||
| + | ``` | ||
| + | |||
| + | ## References | ||
| + | |||
| + | [^ovr27]: Sony Computer Entertainment Inc. Run-Time Library Overview. January 1999. Page 2-7, Table 2-5. | ||
| + | [^ovr28]: Sony Computer Entertainment Inc. Run-Time Library Overview. January 1999. Page 2-8, Tables 2-6 to 2-10. | ||
| + | [^ovr29]: Sony Computer Entertainment Inc. Run-Time Library Overview. January 1999. Page 2-9, Table 2-11. | ||
| + | [^os44]: Sony Computer Entertainment Inc. PlayStation Operating System. August 1998. Page 4-4. | ||
| + | [^io]: psx-spx. I/O Map, Timers (aka Root counters). https://psx-spx.consoledev.net/iomap/ | ||
| + | [^irq]: psx-spx. Interrupts, 1F801070h I_STAT and 1F801074h I_MASK. https://psx-spx.consoledev.net/interrupts/ | ||
| + | [^tm]: psx-spx. Timers. https://psx-spx.consoledev.net/timers/ | ||
| + | [^tmc]: psx-spx. Timers, 0x1f801100 + 0x10*N: COUNT. https://psx-spx.consoledev.net/timers/ | ||
| + | [^tmm]: psx-spx. Timers, 0x1f801104 + 0x10*N: MODE. https://psx-spx.consoledev.net/timers/ | ||
| + | [^tmt]: psx-spx. Timers, 0x1f801108 + 0x10*N: COMP / TARGET. https://psx-spx.consoledev.net/timers/ | ||
| + | [^tmd]: psx-spx. Timers, Dotclock/Hblank. https://psx-spx.consoledev.net/timers/ | ||
| + | [^tmr]: psx-spx. Timers, Reset and Wrap. https://psx-spx.consoledev.net/timers/ | ||