NMI won't trigger

Are you new to 6502, NES, or even programming in general? Post any of your questions here. Remember - the only dumb question is the question that remains unasked.

Moderator: Moderators

Post Reply
puppydrum64
Posts: 41
Joined: Sat Apr 24, 2021 2:18 pm

NMI won't trigger

Post by puppydrum64 » Mon May 03, 2021 1:22 pm

I'm not sure if I understand how this works, but after every frame NMI should occur and the program counter performs the following command: JMP ($FFFA), then it executes that code until it hits "RTI" and then returns back to where it was prior to the interrupt. That being said, I don't know why it's not happening here. I ran this ROM in Mesen and set a breakpoint for RTI but it never happens, nor does my custom variable "vblanked" ever change.

Code: Select all

	org $BFF0

	db "NES",$1a		;ID
	db $01				;Rom pages (16k each)
	db $0				;CHR-ROM pages
	db %01000010		;mmmmFTBM		mmmm = mapper no bottom 4 bits , Four screen vram layout, Trainer at &7000, Battery ram at &6000, Mirror (0=horiz, 1=vert)
	db %00000000		;mmmm--PV 		mapper (top 4 bits...  Pc10 arcade, Vs unisystem )
	db 0				;Ram pages
	db 0,0,0,0,0,0,0
	
						;We selected Mapper 4 - it has 8k VRAM , 8K Sram and 128k rom

vblanked 	equ $7F		;Zero page address of Vblank count


nmihandler:				;This procuedure runs after each frame (See footer.asm)
	php
	inc vblanked		;Alter Vblank Zero page entry
	plp
	rti

irqhandler:
	rti					;Do nothing

NES_main:
	sei					;Interrupts on
	cld					;Clear Decimal flag
	
	ldx #$ff			;Set up stack
	txs
	
	lda #%10000000		;Turn on extra ram at $6000-%7FFF
	sta $A001

wait_nmi:
	lda vblanked
notYet:
	cmp vblanked
	beq notYet
	rts
	
infloop:
	jsr wait_nmi
	jmp infloop
	
	org $FFFA
	dw nmihandler			;FFFA - Interrupt handler
	dw NES_main			;FFFC - Entry point
	dw irqhandler			;FFFE - IRQ Handler
	

User avatar
Quietust
Posts: 1737
Joined: Sun Sep 19, 2004 10:59 pm
Contact:

Re: NMI won't trigger

Post by Quietust » Mon May 03, 2021 1:33 pm

puppydrum64 wrote:
Mon May 03, 2021 1:22 pm
I'm not sure if I understand how this works, but after every frame NMI should occur and the program counter performs the following command: JMP ($FFFA), then it executes that code until it hits "RTI" and then returns back to where it was prior to the interrupt. That being said, I don't know why it's not happening here. I ran this ROM in Mesen and set a breakpoint for RTI but it never happens, nor does my custom variable "vblanked" ever change.

Code: Select all

	org $BFF0

	db "NES",$1a		;ID
	db $01				;Rom pages (16k each)
	db $0				;CHR-ROM pages
	db %01000010		;mmmmFTBM		mmmm = mapper no bottom 4 bits , Four screen vram layout, Trainer at &7000, Battery ram at &6000, Mirror (0=horiz, 1=vert)
	db %00000000		;mmmm--PV 		mapper (top 4 bits...  Pc10 arcade, Vs unisystem )
	db 0				;Ram pages
	db 0,0,0,0,0,0,0
	
						;We selected Mapper 4 - it has 8k VRAM , 8K Sram and 128k rom

vblanked 	equ $7F		;Zero page address of Vblank count


nmihandler:				;This procuedure runs after each frame (See footer.asm)
	php
	inc vblanked		;Alter Vblank Zero page entry
	plp
	rti

irqhandler:
	rti					;Do nothing

NES_main:
	sei					;Interrupts on
	cld					;Clear Decimal flag
	
	ldx #$ff			;Set up stack
	txs
	
	lda #%10000000		;Turn on extra ram at $6000-%7FFF
	sta $A001

wait_nmi:
	lda vblanked
notYet:
	cmp vblanked
	beq notYet
	rts
	
infloop:
	jsr wait_nmi
	jmp infloop
	
	org $FFFA
	dw nmihandler			;FFFA - Interrupt handler
	dw NES_main			;FFFC - Entry point
	dw irqhandler			;FFFE - IRQ Handler
	
There are multiple problems with your code:
1. The PPU does not generate NMIs unless you tell it to do so by writing to $2001 with the high order bit (#$80) set.
1a. In order to enable NMIs, you'll also need to wait for the PPU to warm up - most emulators won't punish you for missing this, but on a real NES any writes to $2000/$2001/$2005/$2006 will have no effect until at least ~29658 CPU cycles have elapsed, and most games handle this by polling register $2002 to wait for two frames.
2. Your program flow is incorrect - execution begins at NES_main, flows into the "wait_nmi" routine, and then hits an RTS instruction which will underflow the stack and cause your program to crash.
3. I don't believe the MMC3 is guaranteed to map the 2nd-last 8KB bank at $C000-$DFFF on powerup - if the PRG ROM Bank Mode happens to initialize to 1 (such that $C000-$DFFF is swappable via bank register R6), your program will probably crash on startup.
Quietust, QMT Productions
P.S. If you don't get this note, let me know and I'll write you another.

Garth
Posts: 210
Joined: Wed Nov 30, 2016 4:45 pm
Location: Southern California
Contact:

Re: NMI won't trigger

Post by Garth » Mon May 03, 2021 2:37 pm

This is not totally related to the question, but from a quick look, I'll comment anyway:

You have the line,

Code: Select all

	sei					;Interrupts on
The SEI stands for "SEt Interrupt-disable flag," meaning you're turning maskable interrupts off, not on. (Actually you're turning the sensing of the interrupts off. The interrupts may still hit, but the processor won't respond to them.)

Also, do not bracket the ISR with PHP and PLP. The interrupt sequence already includes an implied PHP, and the RTI also includes an implied PLP. In rare cases of heavy interrupt load, bracketing your ISR with PHP and PLP could actually get you into trouble.

My article on 6502 interrupts, although not NES- or game-specific, is at http://wilsonminesco.com/6502interrupts/ .
http://WilsonMinesCo.com/ lots of 6502 resources

puppydrum64
Posts: 41
Joined: Sat Apr 24, 2021 2:18 pm

Re: NMI won't trigger

Post by puppydrum64 » Tue May 04, 2021 5:16 pm

Garth wrote:
Mon May 03, 2021 2:37 pm
This is not totally related to the question, but from a quick look, I'll comment anyway:

You have the line,

Code: Select all

	sei					;Interrupts on
The SEI stands for "SEt Interrupt-disable flag," meaning you're turning maskable interrupts off, not on. (Actually you're turning the sensing of the interrupts off. The interrupts may still hit, but the processor won't respond to them.)

Also, do not bracket the ISR with PHP and PLP. The interrupt sequence already includes an implied PHP, and the RTI also includes an implied PLP. In rare cases of heavy interrupt load, bracketing your ISR with PHP and PLP could actually get you into trouble.

My article on 6502 interrupts, although not NES- or game-specific, is at http://wilsonminesco.com/6502interrupts/ .
I'll admit most of that article went over my head but I think I get the gist of it. Thanks for the info!

Post Reply