Controller acting odd with NMI vblank

Discuss technical or other issues relating to programming the Nintendo Entertainment System, Famicom, or compatible systems. See the NESdev wiki for more information.

Moderator: Moderators

Post Reply
jackwilsdon
Posts: 2
Joined: Sat Apr 06, 2019 8:17 pm

Controller acting odd with NMI vblank

Post by jackwilsdon »

I feel like I must be missing something here, but I'm seeing some strange behavior with my controls when using the NMI with vblank (bit 7 of PPUSTATUS set).

My code works fine most of the time, however it seems to occasionally read all buttons as being pressed when NMI vblank is enabled, and I can reproduce this both in Mesen and FCEUX.

Here is the minimum amount of code I could still reproduce this with;

Code: Select all

JOYPAD1 = $4016
JOYPAD2 = $4017
PPU_CTRL = $2000
PPU_SCROLL = $2005

.segment "HEADER"
	.byte "NES"
	.byte $1A
	.byte $01
	.byte $01

.segment "ZEROPAGE"
	JOYPAD1_STATE: .res $01
	JOYPAD1_SEEN: .res $01

.segment "STARTUP"
.proc nmi
	LDA #$00
	STA PPU_SCROLL
	STA PPU_SCROLL
	RTI
.endproc

.proc readinput
	LDA #$01
	STA JOYPAD1
	LDA #$00
	STA JOYPAD1
	LDA #$01
	STA JOYPAD1_STATE

loop:
	LDA JOYPAD1
	LSR A
	ROL JOYPAD1_STATE
	BCC loop
	RTS
.endproc

.proc handleinput
	LDA JOYPAD1_STATE
	CMP #$00
	BEQ nothingpressed
	STA JOYPAD1_SEEN

nothingpressed:
	RTS
.endproc

.proc reset
	LDA #%10000000     
	STA PPU_CTRL

loop:
	JSR readinput
	JSR handleinput
	JMP loop
.endproc

.proc irq
	RTI
.endproc

.segment "VECTORS"
	.addr nmi
	.addr reset
	.addr irq
My controller management code (readinput) is very heavily based on the Controller Reading article from the wiki, and as such I'm sure that I must be doing something wrong elsewhere.

I know that the NMI in my example isn't actually doing anything by setting PPUSCROLL to zero, but an empty NMI does not cause the issue. The program that this was taken from set PPU_SCROLL to the value of a memory address, and as such the altered code above is not much different and still reproduces the issue.

After leaving the program running for a couple of seconds, FF is written to JOYPAD_SEEN, indicating that all bits were somehow set when reading the controller.

Has anyone experienced anything like this before?
jackwilsdon
Posts: 2
Joined: Sat Apr 06, 2019 8:17 pm

Re: Controller acting odd with NMI vblank

Post by jackwilsdon »

Someone pointed out that I'm not restoring the A register in my NMI, which I'm using within readinput. Oops! :roll:
lidnariq
Posts: 11429
Joined: Sun Apr 13, 2008 11:12 am

Re: Controller acting odd with NMI vblank

Post by lidnariq »

jackwilsdon wrote:.proc readinput
Uh. This stops after the first button that wasn't pressed...
Last edited by lidnariq on Sat Apr 06, 2019 10:57 pm, edited 1 time in total.
tepples
Posts: 22705
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: Controller acting odd with NMI vblank

Post by tepples »

lidnariq wrote:Uh. This stops after the first button that wasn't pressed...
It looks to me like the same one-hot ring counter that my own controller reading routine uses. The bit read from the controller goes into the least significant bit of JOYPAD1_STATE. The bit that controls the loop comes out the other (most significant) end, and it always takes eight shifts before the lone 1 bit in the $01 value written to JOYPAD1_STATE at the start of the loop reaches the carry.

Other examples of ring counters on the NES: The MMC1 is believed to use the same structure for its 5-bit serial load register. The master clock bus divider on the NES CPU uses a different form of ring counter.
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Controller acting odd with NMI vblank

Post by tokumaru »

jackwilsdon wrote:Someone pointed out that I'm not restoring the A register in my NMI, which I'm using within readinput. Oops! :roll:
Yup, when the NMI interrupts the main thread, which is running over and over (without waiting for the next frame), it trashes the accumulator and returns, screwing up the logic in the main thread.

It's good practice to backup and restore any registers you use inside interrupt handlers.
Post Reply