Skip to content

Input fixes and VBlank ISR rewrite#282

Merged
alekmaul merged 42 commits intoalekmaul:developfrom
undisbeliever:fix-inputs-new-nmi-isr
Jun 10, 2024
Merged

Input fixes and VBlank ISR rewrite#282
alekmaul merged 42 commits intoalekmaul:developfrom
undisbeliever:fix-inputs-new-nmi-isr

Conversation

@undisbeliever
Copy link
Copy Markdown
Contributor

@undisbeliever undisbeliever commented May 28, 2024

This PR started as series of fixes to the input reading code and evolved to me rewriting and improving the VBlank ISR.

Continuing on from develop commit 555a85b, scanPads() and scanMPlay5() has been inlined to the VBlank ISR and the input reading code has been moved to a new vblank.asm file.

Superscope and mouse reading code is mostly unchanged. I moved it to vblank.asm, changed the labels and changed how the code was called (swapping a jsl with jsr).

As discussed on Discord, the new VBlank ISR can detect lag frames. While I was testing I also discovered a few more bugs in the existing VBlank ISR, causing me to practically rewrite it.

I've also added a lag-frame counter. This u16 variable is user editable and is incremented on every lag-frame by the VBlank ISR.

The code was tested by running all of the examples on my 1-CHIP NTSC Super Famicom console.
Two examples remain untested on real hardware, could you arrange for these to be tested before merging this PR.

  • multiplay5
  • superscope

As the PVSnesLib changelog is handled by GitHub releases, I have written out the list of normal and breaking changes here:

Breaking Changes

Interrupts:

  • The VBlank ISR now detects lag frames.
    • Code that uses WaitForVBlank() should be unaffected
    • Code that uses wai to pause execution will be treated as a lag-frame.
      See interrupt.h documentation for more details.
    • CAUTION: nmi_handler is called on every VBlank interrupt (ie, no lag-frame detection).
  • The VBlank ISR will automatically transfer oamMemory to the PPU OAM on non lag-frames.
    • nmi_handler's that contain a DMA transfer to OAM should remove it.
  • nmiSet() is now a function.
    • nmiSet() will disable IRQ interrupts, enable VBlank interrupts and enable Joypad Auto-Read.
  • tcc__registers_irq has been renamed to tcc__registers_nmi_isr (it is not used by IRQ interrupts).

Inputs:

  • The VBlank ISR will automatically read inputs on non-lag frames.
    • Code that loops until a button is pressed must call WaitForVBlank() within the loop otherwise it will loop forever (See commit d450096).
    • The lag-frame detection will prevent the input variables from suddenly changing in the middle of the mainloop.
    • The lag-frame detection will reduce dropped keysUp() and keyDown() events during lag-frames.
  • Removed scanPads(), scanMPlay5(), mouseRead(), scanScope()
    • scanPads() and scanMPlay5() has been inlined into the VBlank ISR.
    • mouseRead() and scanScope() is now private and automatically called by the VBlank ISR.
  • Renamed pad_keysrepeat to pad_keysdown to better describe what it holds.
  • padsDown(), padsUp() and padsClear() argument has changed to match padsCurrent().
    • padsDown(0) is unaffected. padsDown(1) now returns the down keys of the 2nd controller.
  • padsDown() is now a macro.

Changes

Interrupts:

  • Rewrote the VBlank Interrupt Service Routine
    • Please see the VBlank-ISR section of interrupt.h documentation for more details.
  • Moved the VBlank ISR, VBlank variables and input reading code to a new vblank.asm file.
  • Added lag-frame detection to the VBlank ISR
    • WaitForVBlank() is used to detect lag frames. A lag-frame is when a VBlank Interrupt occurs and the S-CPU is not in WaitForVBlank().
    • nmi_handler's can test for lag-frames using the vblank_flag variable.
      See myconsoleVblank() in the Mode1ContinuosScroll example.
  • Added a lag-frame counter: lag_frame_counter
  • Fixed a potential crash a VBlank Interrupt occurs in the middle of nmiSet()
  • Forced tcc imaginary registers to address 0
    • .RAMSECTIONs that would have moved tcc__registers out of address 0 now cause a wla-dx linker error.
  • VBlank ISR imaginary registers are now page-aligned to prevent a D.l != 0 direct-page cycle penalty.

Inputs:

  • Inputs are now read at the end of the VBlank ISR (to free VBlank time that can be used by the nmi_handler)
  • Fixed scanPads() and scanMPlay5() erroneously reading REG_RDIO.
  • Fixed high byte of pad_keys not cleared if input is not a standard controller.
  • Fixed scanMPlay5() not reading controllers 2 & 3 (3a3db70).
    • CAUTION: REG_WRIO ($4201) must not be written to while MultiPlayer5 is active.
  • Multiplayer5 reading code clears pad_keys if the input is not a standard controller.
  • Fixed padsClear() not clearing the high-byte of pad_keys* variables.
  • Rewrote padsClear() and padsUp()
  • Fixed pad_key* array size in input.h

to free VBlank time that can be used by the `nmi_handler`
The functions that are called by the VBlank ISR have been moved to
vblank.asm
These functions are called by the VBlank ISR and should not be called
multiple times per frame.
The getpad1data loop did a 16-bit read of REG_JOYA, which also reads
from REG_JOYB, preventing the getpad23data loop from reading joypad
values.

scanMplay5 now tests if a standard controller is connected to the
multitap.

This commit also optimises the multitap reading code by:
 * Using Joypad Auto-Read to read the first three controllers.
 * Using ScanPads to read the first two controllers.
 * Using an 8 bit A when reading the last two controllers.
 * Using the carry output of `rol` to determine when all of the
   controller bits have been read.

As a consequence of the Joypad Auto-Read optimisation, the WRIO register
must not be written to when `snes_mplay5` is set.  I do not think this
will be an issue, since `REG_WRIO` (nor 4201) is not defined in the
header files nor the snes-examples directory.

Tested by running the multiplay5 example in Mesen
This is safe as the input reading subroutines/macros are private to the
vblank_isr section
During a lag frame:
 * oamMemory will not be transfered to the PPU OAM
 * the pads/mouse/scope will not be read

nmi_handler will be called on every VBlank interrupt.

The vblank_flag variable can be used to detect lag-frames in the
nmi_handler.
Crash was caused by a missing `.BASE 0` in the `.vblank_bss` ramsection.

This commit also fixes 2 more sections with the wrong .BASE.
Modifies WaitForVBlank() so it can be called with any DB register value.

Also documents that WaitForVBlank() will preserve A/X/Y registers.
The speed_change jsr in `_MouseRead` can crash if `mouseSpeedChange` is
in a different bank to `_MouseRead`.

speed_change has been changed to a child label to illuminate that
`_MouseRead` is calling a subroutine outside of vblank.asm.
mouse_read has been moved to vblank.asm and the same comments already
exist in vblank.asm.
Saves 2 cycles and matches the _GetScope call in the VBlank ISR.
Writes to nmi_handler are not atomic and the VBlank ISR can crash if a
VBlank Interrupt occurs in the middle of the nmi_handler write.

To fix this crash, nmiSet() is now a function that will temporarily
disable interrupts so nmi_handler can be safely written to.

This commit also modifies consoleInit() to use nmiSet().
 * Test for lag-frames
 * Remove oamMemory DMA transfer (now handled by the VBlank ISR)
 * Remove snes_vblank_count++ (now handled by the VBlank ISR)
These functions did not multiply the argument by 2 when converting the
`value` argument (0-4) to an X index register value (0, 2, 4, 6, 8).
I have tested and confirmed `padsDown(i) == pad_keysrepeat[i]` before
making this change.
padsClear() is not clearing the high byte of the pad variables.

Using long addressing is faster then changing DB register.

Also:
 * Added bounds testing to padsClear()
 * padsClear() can be called with an 8 bit Index
 * Document pad arrays.
 * pads*() value parameter is an index, not an address.
 * Clarify pads*() parameter range
It is only used in the NMI ISR and should not be used for IRQ interrupts.
Fixes potential memory corruption in NMI ISR.
The VBlank ISR changes the Direct Page Register to
`tcc__registers_nmi_isr`.  If `tcc__registers_nmi_isr` is not page
aligned, a 1 cycle penalty will be applied to all direct-page
instructions in the VBlank ISR (including the `nmi_handler`).
`vblank_flag` can be used to determine if it is OK to read from `bgInfo`
in `myconsoleVblank()`.
@undisbeliever
Copy link
Copy Markdown
Contributor Author

Fixed input.h -> interrupt.h typo in the changelog above.

@alekmaul alekmaul merged commit e5f7e98 into alekmaul:develop Jun 10, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants