![]() |
PVSnesLib
4.4.0
Documentation to code in C or ASM for the Nintendo SNES
|
snes interrupt support. More...
#include <snes/snestypes.h>
Go to the source code of this file.
Macros | |
#define | HBL_READY (1 << 6) |
H-Blank Period Flag (0=No, 1=HBlank) | |
#define | INT_HVIRQ_H (1 << 4) |
H/V IRQ (0=Disable, 1=At H=H + V=Any, 2=At V=V + H=0, 3=At H=H + V=V) | |
#define | INT_HVIRQ_HV (2 << 4) |
H/V IRQ (0=Disable, 1=At H=H + V=Any, 2=At V=V + H=0, 3=At H=H + V=V) | |
#define | INT_HVIRQ_V (1 << 5) |
H/V IRQ (0=Disable, 1=At H=H + V=Any, 2=At V=V + H=0, 3=At H=H + V=V) | |
#define | INT_JOYPAD_ENABLE (1) |
Joypad Enable (0=Disable, 1=Enable Automatic Reading of Joypad) | |
#define | INT_VBLENABLE (1 << 7) |
VBlank NMI Enable (0=Disable, 1=Enable) (Initially disabled on reset) | |
#define | PAD_BUSY (1) |
Auto-Joypad-Read Busy Flag (1=Busy) (see 4200h, and 4218h..421Fh) | |
#define | REG_HVBJOY (*(vuint8 *)0x4212) |
H/V-Blank flag and Joypad Busy flag (R). | |
#define | REG_NMITIMEN (*(vuint8 *)0x4200) |
Interrupt Enable and Joypad Request (W). | |
#define | REG_RDNMI (*(vuint8 *)0x4210) |
V-Blank NMI Flag and CPU Version Number (R) (Read/Ack) | |
#define | REG_TIMEUP (*(vuint8 *)0x4211) |
TIMEUP - H/V-Timer IRQ Flag (R) (Read/Ack) | |
#define | VBL_READY (1 << 7) |
V-Blank Period Flag (0=No, 1=VBlank) | |
#define | WaitVBLFlag |
Wait for VBL flag to be OK | |
Functions | |
void | nmiSet (void(*vblankRoutine)(void)) |
Sets the nmi_handler (VBlank routine). | |
void | WaitForVBlank (void) |
Waits for a VBlank interrupt. | |
void | WaitNVBlank (u16 ntime) |
Wait for vblank interrupt ntime times | |
Variables | |
u16 | lag_frame_counter |
Lag-frame counter. | |
void * | nmi_handler |
VBlank routine. | |
u8 | vblank_flag |
VBlank ISR flag. | |
snes interrupt support.
PVSnesLib includes a Vertical Blank Interrupt Service Routine (VBlank ISR or NMI ISR) that will do the following actions (in this order) at the start of the Vertical Blanking Period when VBlank interrupts are enabled:
A lag-frame is when the execution time between two WaitForVBlank() calls exceeds a 50/60Hz display frame. By testing for lag-frames, the VBlank ISR will not transfer a partially populated oamMemory to the PPU OAM.
Lag-frames are determined by the vblank_flag variable, which is set on WaitForVBlank() and cleared in the VBlank ISR.
Inputs are only read on non lag-frames to prevent the input state from unexpectedly changing in the middle of the main loop (potentially causing a heisenbug). Code that loops until a button is pressed must call WaitForVBlank() within the loop otherwise it will loop forever.
The nmi_handler function pointer (set using nmiSet()) is called on every VBlank interrupt (even during force-blank / setScreenOff()). To prevent glitches, nmi_handler's should either:
#define REG_HVBJOY (*(vuint8 *)0x4212) |
H/V-Blank flag and Joypad Busy flag (R).
7 V-Blank Period Flag (0=No, 1=VBlank)
6 H-Blank Period Flag (0=No, 1=HBlank)
5-1 Not used
0 Auto-Joypad-Read Busy Flag (1=Busy) (see 4200h, and 4218h..421Fh)
The Hblank flag gets toggled in ALL scanlines (including during Vblank/Vsync).
Both Vblank and Hblank are always toggling (even during Forced Blank,
and no matter if IRQs or NMIs are enabled
#define REG_NMITIMEN (*(vuint8 *)0x4200) |
Interrupt Enable and Joypad Request (W).
7 VBlank NMI Enable (0=Disable, 1=Enable) (Initially disabled on reset)
6 Not used
5-4 H/V IRQ (0=Disable, 1=At H=H + V=Any, 2=At V=V + H=0, 3=At H=H + V=V)
3-1 Not used
0 Joypad Enable (0=Disable, 1=Enable Automatic Reading of Joypad)
Disabling IRQs (via bit4-5) does additionally acknowledge IRQs.
There's no such effect when disabling NMIs (via bit7).
#define REG_RDNMI (*(vuint8 *)0x4210) |
V-Blank NMI Flag and CPU Version Number (R) (Read/Ack)
7 Vblank NMI Flag (0=None, 1=Interrupt Request) (set on Begin of Vblank)
6-4 Not used
3-0 CPU 5A22 Version Number (version 2 exists)
The NMI flag gets set at begin of Vblank (this happens even if NMIs are disabled). The flag gets reset automatically
at end of Vblank, and gets also reset after reading from this register.
The SNES has only one NMI source (vblank), and the NMI flag is automatically reset (on vblank end), so there's
normally no need to read/acknowledge the flag, except one special case: If one does disable and re-enable NMIs,
then an old NMI may be executed again; acknowledging avoids that effect.
The CPU includes another internal NMI flag, which gets set when "[4200h].7 AND [4210h].7" changes from 0-to-1, and
gets cleared when the NMI gets executed (which should happen around after the next opcode) (if a DMA transfer is
in progress, then it is somewhere after the DMA, in that case the NMI can get executed outside of the Vblank
period, ie. at a time when [4210h].7 is no longer set).
#define REG_TIMEUP (*(vuint8 *)0x4211) |
TIMEUP - H/V-Timer IRQ Flag (R) (Read/Ack)
7 H/V-Count Timer IRQ Flag (0=None, 1=Interrupt Request)
6-0 Not used
The IRQ flag is automatically reset after reading from this register
(except when reading at the very time when the IRQ condition is true
(which lasts for 4-8 master cycles), then the CPU receives bit7=1,
but register bit7 isn't cleared). The flag is also automatically cleared
when disabling IRQs (by setting 4200h.Bit5-4 to zero).
Unlike NMI handlers, IRQ handlers MUST acknowledge IRQs, otherwise the IRQ
gets executed again (ie. immediately after the RTI opcode).
void nmiSet | ( | void(*)(void) | vblankRoutine | ) |
Sets the nmi_handler (VBlank routine).
This function will also disable any active IRQ interrupts, enable VBlank interrupts and enable Joypad Auto-Read.
vblankRoutine | the function to call on every VBlank (NMI) interrupt. |
CAUTION: vblankRoutine
is called on every VBlank interrupt. vblank_flag can be used to determine if vblankRoutine
was called during a lag-frame.
CAUTION: This function will override the default nmi_handler. If you are using consoleDrawText(), you will need to call consoleVblank() inside vblankRoutine
.
void WaitForVBlank | ( | void | ) |
Waits for a VBlank interrupt.
Sets the vblank_flag and pauses execution until the vblank_flag is cleared (by the VBlank-ISR).
CAUTION: This function will loop forever if VBlank interrupts are disabled.
Assembly note: This function will not modify the A/X/Y registers and can be called with an 8 or 16 bit .ACCU
/.INDEX
.
void WaitNVBlank | ( | u16 | ntime | ) |
Wait for vblank interrupt ntime times
ntime | number of time to wait VBlank Interrupt |
|
extern |
Lag-frame counter.
This variable is incremented on every VBlank Interrupt that occurs during a lag-frame.
CAUTION: The lag frame counter cannot tell the difference between a lag-frame and force-blank setup loading graphics to the PPU.
lag_frame_counter
can be modified. This is useful in development builds to measure the amount of lag in a level by resetting lag_frame_counter
on level load and printing lag_frame_counter
on a pause screen or at the end of the level.
|
extern |
VBlank routine.
This function is called on every VBlank interrupt by the VBlank Interrupt Service Routine.
CAUTION: Writes to nmi_handler
are not atomic and can cause a crash if a VBlank Interrupt occurs in the middle of the nmi_handler
write. Use nmiSet() or disable NMI interrupts when modifying nmi_handler
.
Assembly note: This function pointer will be called with a non-zero Direct Page register to prevent nmi_handler
from clobbering the tcc imaginary registers.
|
extern |
VBlank ISR flag.
Used to detect lag-frames in the VBlank ISR.
This variable is set to a truthy (non-zero) value in WaitForVBlank() and cleared in the NMI ISR after the call to nmi_handler.
vblank_flag can be used in a custom nmi_handler to detect lag frames. Within nmi_handler:
CAUTION: This variable SHOULD NOT be changed outside of WaitForVBlank()