INIDISP Register
INIDISP
| d7 | d6 | d5 | d4 | d3 | d2 | d1 | d0 | ||
|---|---|---|---|---|---|---|---|---|---|
| $2100 | INIDISP | force blank | - | - | - | brightness | |||
Brightness
The brightness bits control the intensity of the video output.
Brightness 15 (b1111) will output full intensity and brightness 0
(b0000) will output a very weak (but not black) signal.
1
Internally the bightness bits are connected to a 4 bit Digital-to-Analog converter inside S-PPU2, whose output is used as reference voltage for the Blue-Green-Red DACs. 2
Force Blank
When the force-blank bit is set, the PPU will disable the screen output and allow the CPU to safely access the PPU registers outside the blanking periods.
Force-blank is normally used by CPU code to initialize the PPU registers and transfer large amounts of data to the VRAM/CGRAM/OAM at the beginning of a scene or level.
Enabling force-blank does not stop PPU execution, the PPU will continue to process the display frame. It is theorized that force-blank prevents the PPU from latching PPU-registers/VRAM/CGRAM/OAM and thus process junk data. This junk data will persist until the PPU's tile and object buffers are overridden with good data.
Failing to enable force-blank before accessing the PPU registers outside their timing window can cause bus contention inside the PPU which can result in ignored reads/writes, data corruption and/or address bus corruption.
-
VRAM access outside of Vertical-Blank or Force-Blank is silently ignored. The VRAM address is incremented as per normal but no data is read or written.
-
CGRAM access outside of Horizontal-Blank, Vertical-Blank or Force-Blank will read or write to the wrong CGRAM address.
-
OAM access outside of Vertical-Blank or Force-Blank will read or write to the wrong OAM address.
The VRAM writes were ignored by the PPU and console is outputting stale data.
The corruption is inconsistent, all four screen captures are executing the same binary.
Recommend Usage
When to write to INIDISP
The best time to write to INIDISP is during the Vertical Blanking
period. This prevents graphical glitches when force-blank is cleared
and screen-tearing when changing the brightness bits.
When enabling force-blank, the brightness bits should also be set to mitigate the brightness delay glitch on 1-CHIP consoles.
It is a good idea to create a shadow variable for the INIDISP
register. A shadow variable is a RAM variable that can be read or
written at any time and will be transferred to INIDISP by the VBlank
routine.
Shadow variables have three advantages over directly writing to the
INIDISP register:
- The
INIDISPregister will be written inside the Vertical Blanking period. - The game code can read the value of the shadow variable.
- The game code can write to the shadow variable at any time.
One important note about enabling force-blank via an INIDISP shadow
variable: You will need to set the shadow variable and then wait
until the end of the VBlank routine before transferring data to or
from the PPU.
Writing to INIDISP outside of Vertical Blank
Writes to INIDISP outside of the Vertical Blanking period should be
handled with care to avoid glitches and hardware bugs.
All INIDISP writes using HDMA should be preformed using the 2
registers write once transfer mode (DMAPx = 0x01) to B-Bus address
$21ff (BBADx = 0xff)10. Both bytes in the HDMA
table must contain the same value. This mitigates both the early read
glitch and the DMA failure hardware bug.
All INIDISP writes by a CPU instruction outside of the Vertical
Blanking period should be preformed with long addressing, using address
$802100 if the force-blank bit is set or address $0f2100 if the
force-blank bit is clear or unknown (to mitigate the early read
glitch).
Any INIDISP write that clears the force-blank bit outside of
the Vertical Blanking period will cause graphical glitches.
Fade-in/Fade-out screen transitions
The INIDISP register can be used to create a simple and inexpensive
fade-to-black and fade-in screen transitions.
A fade-in transition is preformed by slowly incrementing the brightness bits over multiple frames.
A fade-to-black transition is preformed by slowly decrementing the brightness bits over multiple frames.
INIDISP register.
Minimizing glitched V-Blank overruns
A V-Blank overrun occurs when the V-Blank routine takes too long and does not finish before the end of the Vertical Blank period. This usually results in the V-Blank routine writing to the PPU during active display, leading to glitches that can persist for multiple frames.
V-Blank overruns can be detected by reading the VBlank flag of the
HVBJOY register at the end of the V-Blank routine.
A simple way to minimize a glitched V-Blank overrun is to enable force-blank at the start of the V-Blank routine and clear force-blank at the end of the V-Blank routine.
If a V-Blank overrun occurs and force-blank is enabled, the PPU writes will still be valid and no long-term glitches will occur. Unfortunately, this introduces three new glitches when V-Blank overruns; a small black bar at the top of the display and a mid frame screen activation glitch, both lasting for a single frame. Finally, a HDMA glitch can occur if the HDMA register setup was not completed before the Vertical-Blanking period ends.
V-Blank overruns should rarely occur. Any V-Blank routine that regularly overruns should be redesigned and/or rewritten.
Glitches and Hardware Bugs
Mid frame screen activation
Turning on the screen (clearing the force-blank bit) mid frame will create graphical glitches.
Activating the display during Horizontal Blank can cause sprite glitches on the next scanline.
Recorded on a 1-CHIP SFC console (raw screen capture). 3-chip consoles show similar output.
Activating the display in the middle of a scanline can cause tile glitches for the current scanline and sprite glitches for the next two scanlines.
Recorded on a 1-CHIP SFC console (raw screen capture). 3-chip consoles show similar output.
Brightness Delay
The brightness DAC in 1-CHIP consoles has a surprisingly large rise time, exhibiting as a glitched brightness gradient that can last multiple pixels. There are reports of this glitch lasting multiple scanlines on modded 1-CHIP consoles with the C11 capacitor anti-ghosting mod.
Most instances of this glitch occur when the value 0x80 (force-blank
set, 0 brightness) is written to INIDISP, followed by a 0x0f
(force-blank clear, full brightness) some time later.
// 8 bit A
// DB access PPU registers
// Disable screen (0 brightness)
lda #0x80
sta INIDISP
// DMA transfer to VRAM/CGRAM/OAM
// Update PPU registers
// Enable screen (full brightness)
lda #0x0f
sta INIDISP
The fix for this glitch is to set the brightness bits whenever force-blank is enabled. There will still be a transition delay if the brightness changes, but it now occurs during force-blank and should be invisible to the player.
// 8 bit A
// DB access PPU registers
// Disable screen (and set brightness bits)
lda inidisp_shadow
ora #0x80
sta INIDISP
// DMA transfer to VRAM/CGRAM/OAM
// Update PPU registers
// Enable screen
lda inidisp_shadow
sta INIDISP
This glitch affects multiple retail games, usually games that extended VBlank or change the screen brightness mid-screen.
INIDISP Early Read Glitch
In 2021, it was discovered the PPU erroneously latches the data-bus too
early during a write to INIDISP. This can causes the PPU to use the
previous value on the data-bus for the brightness and force-blank
bits for about a single dot. 4
5
This hardware bug can result in the following glitches:
-
Corrupted object tile data on 3-chip consoles when a write to
INIDISPoccurs outside of Vertical-Blank and the previous value on the data-bus has bit 7 (MSB) set.A HDMA write to
INIDISPcan easily exhibit this glitch.It is unknown why a 1-chip console does not exhibit corrupted tile data. More investigation is required.
Corrupted object tiles caused by a HDMA write to INIDISPon every scanline.
Recorded on a 3 Chip SFC console (raw screen capture).
1-CHIP consoles do not show object tile corruption (raw screen capture). -
The display erroneously turning on for a single dot when force-blank is enabled and an
INIDISPwrite occurs outside the Horizontal or Vertical blanking-periods while the previous value on the data-bus has bit 7 (MSB) clear.On 1-CHIP consoles the glitch is extremely faint and hard to see. 6
Test ROM that repeatedly writes 0x8ftoINIDISPwhen the previous value on the data-bus was0x0f.
Recorded on a 3 Chip SFC console (raw screen capture).
1-CHIP consoles show an extremely faint glitch. 6 -
A sudden change in screen brightness, lasting for a single dot, when an
INIDISPwrite occurs outside the Horizontal or Vertical blanking-periods and the previous value of the data-bus does not match the value of theINIDISPwrite. This can also cause tile corruption on 3-chip consoles if previous value on the data-bus has bit 7 (MSB) set.
Tiny dots on the screen, no tile corruption.
Test ROM that repeatedly writes0x0ftoINIDISPwhen the previous value on the data-bus was0x00.
Recorded on a 3 Chip SFC console (raw screen capture).
1-CHIP consoles show similar output (raw screen capture).
Black dots and corrupted tiles.
Test ROM that repeatedly writes0x0ftoINIDISPwhen the previous value on the data-bus was0x8f.
Recorded on a 3 Chip SFC console (raw screen capture).
1-CHIP consoles do not show tile corruption. (raw screen capture).
Mitigations
Writes to INIDISP during Vertical-Blank will not exhibit this glitch.
If the INIDISP write is emitted by a CPU instruction, an sta long
instruction can be used to pre-load the data-bus in the CPU-cycle before
the INIDISP write. 8
-
When enabling force-blank, use a
sta $802100(long addressing) instruction (8 bit accumulator).
The data-bus will hold the value$80before the write-cycle toINIDISP. -
When disabling force-blank, use a
sta $0f2100(long addressing) instruction (8 bit accumulator).
The data-bus will hold the value$0fbefore the write-cycle toINIDISP. -
If the state of the force-blank bit is unknown, prefer
sta $0f2100.
It may erroneously enable the display for a single dot (if the write occurs outside of the blanking-period) but it should not result in glitched Object tiles.
Alternatively the bug can be mitigated by preforming a 16 bit write to
$0020ff with identical high and low bytes. 8
9
// DB access registers
// 8 bit Accumulator
// 16 bit Index
// tmp is a 16 bit variable
sta tmp
sta tmp + 1
ldx tmp
stx $20ff
Mitigations (HDMA)
When writing to INIDISP via HDMA, a 2 registers write once HDMA
transfer (DMAPx = 0x01) to B-Bus address $21ff (BBADx = 0xff)
can be used to pre-load the data-bus with the correct value before the
write to INIDISP. 10
DMA failure when HDMAing to INIDISP
On a S-CPU-A (CPU v2) SNES console, a DMA transfer can fail if there was
a recent HDMA transfer with a BBADx (DMA Bus B address) value of
0x00 (transfer to/from INIDISP).
If the transfer fails, no data is transferred during DMA and the DASx
(DMA Size) register remains unchanged.
Only S-CPU-A is affected. S-CPU (v1), S-CPU-B and S-CPUN (1-CHIP) do not experience this hardware bug. 11
The exact cause of this hardware bug is still unknown.
The most reliable mitigation for this bug (aside from not HDMAing to
INIDISP) is to use a 2 registers write once HDMA transfer (DMAPx =
0x01) to B-Bus address $21ff (BBADx = 0xff). The first byte
will be transferred to $21ff, the second byte to $2100. Both bytes
should contain the same value to prevent the early read glitch
mentioned above. 10
Retrying the DMA transfer if BBADx is non-zero is not recommended.
Scattered reports of DMA failing in homebrew code have been around for years. The cause was unknown until late December 2020 when Near encountered a corrupted text box while working on their Bahamut Lagoon localization and investigated why DMA was failing with Ikari_01.
Advanced Usage
Extending VBlank time with force-blank
Extended VBlank (commonly called letterboxing) is a common technique to increase the amount of data that can be transferred to the PPU during Vertical-Blank. An IRQ Interrupt is used to disable the screen (with force-blank) and start the VBlank routine earlier then normal, decreasing the number of visible scanlines and allocating them to the VBlank routine.
For aesthetic purposes, the screen remains disabled at the start of the next frame and will be enabled (by clearing force-blank, usually by an IRQ Interrupt) a number of scanlines later to create a letterbox effect.
Every scanline removed from active display adds an extra 165 bytes of DMA time to the extended VBlank.
To prevent background tile glitches the screen must be disabled and reenabled during Horizontal-Blank.
One downside of extended VBlank is that the visible first scanline will be missing sprites and may contain sprite glitches. It is possible to hide them by using one the following techniques:
- Disabling the Sprite layer on the first visible scanline (useful for status bars at the top of the screen).
- Disabling the Background and Sprite layers for the first visible scanline. (Works best if the background colour is black.)
- Covering the sprites with a black background tile (using Mode 1 with BG3-priority).
- Covering the background and sprites with a Window.
The glitched scanline with missing/corrupted Object tiles is hidden by disabling Background and Sprite layers with the
TM register.
Extended VBlanks are more sensitive to timing issues then regular VBlanks. Extra care must be taken to ensure correct operation at all times. Always ensure an extended-VBlank will never overrun.
Depending on how the extended VBlank is implemented, an extended-VBlank overrun can result in one or more of the following:
- Fullscreen flashing (major photosensitity issue).
- A blank display frame.
- A model-1 DMA/HDMA crash.
- Corrupted VRAM/CGRAM/OAM data.
- Corrupted HDMA effects.
- Missing lines at the top of the screen (photosensitity issue).
It is better to shrink the active display and add more scanlines to VBlank then to overrun an extended-VBlank.
Using INIDISP as a quick and dirty CPU usage meter
As the brightness bits can be written to at any time, the INIDISP
register can be used to create a quick and dirty CPU usage meter.
By changing the brightness bits just before the main-loop pauses execution, a visual representation of CPU usage will be shown on screen.
The screen will output a sudden change in brightness at the point where the main-loop is waiting for the VBlank interrupt, allowing developers to quickly estimate the amount of free CPU time available.
// DB access registers
// 8 bit Accumulator
lda #10
sta INIDISP
// Wait until the end of the VBlank routine
jsr WaitUntilVBlank
// VBlank routine will restore INIDISP back to full brightness
INIDIDP CPU usage meter.
The first frame has ~15 scanlines at full brightness. (224 - 15) / 262 = ~79.8% CPU free, ~20.2% CPU used.
The second frame has ~25½ scanlines at full brightness. (224 - 25.5) / 262 = ~75.8% CPU free, ~24.2% CPU used
Photosensitivity Warning
This technique will create an unpredictable flashing pattern.
The flashing is worsened if the game ever experiences a lag frame. During a lag frame the flash can cover the entire screen. For example, one frame would display 100% of the screen at full brightness, the next frame would display over 95% of the screen at reduced brightness.
In this example, the reduced brightness
INIDISP write occurs during
the Vertical Blanking period of every second display frame.
This technique is only included for legacy reasons.
You should not include an INIDISP CPU Usage meter in a home-brew game.
For more information about photosensitity in video games, please read the Xbox Accessibility Guideline 118: Photosensitivity.
-
SNES-Chips decapped (2PPU, 1CHIP, APU, DSP) - circuit-board.de forum ↩
-
sd2snes git commit - FPGA: experimental brightness patching for 1CHIP systems,
Firmware v1.8.0 released ↩ -
Status update – important stability fixes
(Contains a detailed description of theINIDISPearly-read glitch) ↩↩ -
While I am able to see the glitch on screen it is too faint to capture with my screen capture setup or with a camera. ↩↩
-
FXPAK Firmware v1.11.0 beta 1 ↩
-
This mitigation will not work if HDMA activates in the middle of the write instruction. ↩↩
-
This mitigation can only be used if address
$0020ffis unmapped by the cartridge. ↩ -
This mitigation can only be used if B-Bus address
$21ffis unmapped by the cartridge. ↩↩↩ -
@undisbeliever - Over the past few days I have been working on a test ROM to investigate a new hardware bug that Near found. ↩