Code not working how I'd expect, possibly going mad

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

nescoda86
Posts: 6
Joined: Tue Feb 13, 2024 2:58 pm

Code not working how I'd expect, possibly going mad

Post by nescoda86 »

Hi,

I might need to post all of my code, as something odd is happening, or it's just a complete misunderstanding on my part.

I have a routine to display metasprites which appears to work fine. Now I am trying to animate them by keeping track of an "anim delay" and the current frame of animation. I increment the delay until it matches the current animation's "delay" value, then increment the frame. I am using the "all in NMI" method for now if this is important.

This is an excerpt from my update_sprites routine, that I run at the end of the NMI routine.

Code: Select all

  	lda spr_delay,x
	cmp #$08
	beq delay_done
	inc spr_delay,x
	jmp update_next_sprite
	
delay_done:
	lda #$00
	sta spr_delay,x
	
	; update frame
	lda spr_frame,x
	cmp #$02
	beq reset_frame
	inc spr_frame,x
	jmp update_next_sprite
	
reset_frame:
	lda #$00
	sta spr_frame,x
		
update_next_sprite:
	inx
	cpx #MAX_SPRITES
	bne -
	rts
This works fine with the hard coded values to compare spr_delay,x and spr_frame,x to, but if I try to load these values from variables in the following code, the whole game seems to lock up.

Code: Select all

	lda #$08
	sta anim_delay
	lda #'$02
	sta anim_frames

  	lda spr_delay,x
	cmp anim_delay
	beq delay_done
	inc spr_delay,x
	jmp update_next_sprite
	
delay_done:
	lda #$00
	sta spr_delay,x
	
	; update frame
	lda spr_frame,x
	cmp anim_frames
	beq reset_frame
	inc spr_frame,x
	jmp update_next_sprite
	
reset_frame:
	lda #$00
	sta spr_frame,x
		
update_next_sprite:
	inx
	cpx #MAX_SPRITES
	bne -
	rts
I don't get it! Any help much appreciated.
Fiskbit
Site Admin
Posts: 1192
Joined: Sat Nov 18, 2017 9:15 pm

Re: Code not working how I'd expect, possibly going mad

Post by Fiskbit »

It's not clear to me what the problem is, but I do see that you have a stray ' when loading the value you write into anim_frames. I don't know what that will do; I'm surprised it even assembles. However, this presumably shouldn't cause it to lock up.

You also have a bne -, but it's not clear where this is branching to. Hopefully just the start of the code you posted?

If you post a ROM, it should be pretty easy for us to figure out why the game has locked up.
User avatar
Memblers
Posts: 4109
Joined: Mon Sep 20, 2004 6:04 am
Location: Indianapolis
Contact:

Re: Code not working how I'd expect, possibly going mad

Post by Memblers »

Which assembler? There's probably not enough code to tell what's happening. I'd recommend putting a breakpoint on the code in question and step through it in a debugger. You could step through after it locked up, but if it long since went off into the weeds, that might not reveal anything.

Only thing I see that doesn't make sense, is lda #'$02. Stray apostrophe in there. I'm surprised the assembler didn't error out.
nescoda86
Posts: 6
Joined: Tue Feb 13, 2024 2:58 pm

Re: Code not working how I'd expect, possibly going mad

Post by nescoda86 »

Thanks guys. I'm using asm6. I think I must have introduced some extra characters while I was trying to format the code, sorry about that.

Here's the compiled ROM and code.

ROM:
https://jottacloud.com/s/3997ca3ce46fb9 ... 41c89f5775

Code:
https://jottacloud.com/s/399bbd7f3de0f4 ... 075b4d4e73

I do need to learn how to use a debugger, I'm sure it would be a lot of help in situations like this. What would be best to use, FCEUX?
Pokun
Posts: 3122
Joined: Tue May 28, 2013 5:49 am
Location: Hokkaido, Japan

Re: Code not working how I'd expect, possibly going mad

Post by Pokun »

nescoda86 wrote: Wed Mar 05, 2025 2:08 pm I do need to learn how to use a debugger, I'm sure it would be a lot of help in situations like this. What would be best to use, FCEUX?
Mesen2 is probably best nowadays, besides the actual debugger it got all sorts of viewer tools for viewing WRAM, VRAM, OAM, tilemaps, sprites, audio registers, raster effects etc. The only thing I'm missing from Mesen1 is the trace logger which I just can't get to work correctly.
FCEUX is still good if you use an older computer that Mesen runs a bit slow on though and it has most of those viewer tools.

Create a breakpoint near where you think the problem is then use "step into" to single-step the execution to see that everything works as you expect. If you need to pass a long iteration you just create a new breakpoint after it and press "run" then it will run at real-time speed until the new breakpoint and you can single-step again.
Sour
Posts: 961
Joined: Sun Feb 07, 2016 6:16 pm

Re: Code not working how I'd expect, possibly going mad

Post by Sour »

Pokun wrote: Wed Mar 05, 2025 3:47 pm The only thing I'm missing from Mesen1 is the trace logger which I just can't get to work correctly.
What isn't working in v2 for you? I use the trace logger frequently for multiple consoles and have not seen any issues on my end
Fiskbit
Site Admin
Posts: 1192
Joined: Sat Nov 18, 2017 9:15 pm

Re: Code not working how I'd expect, possibly going mad

Post by Fiskbit »

nescoda86 wrote: Wed Mar 05, 2025 2:08 pm I do need to learn how to use a debugger, I'm sure it would be a lot of help in situations like this. What would be best to use, FCEUX?
Mesen v2, and it's no contest. The main thing FCEUX has on Mesen is resource consumption; it runs faster and it uses less RAM, so if you're on a very limited system where Mesen isn't really usable, FCEUX may be your only option. However, it is significantly less accurate, its tools are less powerful, and it has fewer of them. In particular, it lacks an event viewer, which is one of Mesen's best features. This lets you see what the CPU is doing during each frame and where, instantly conveying a lot of information.

With Mesen, you should also turn on all of the accuracy features in the top half of the Settings -> NES -> Emulation menu and randomize RAM, which will help you find problems that can occur on hardware, though some of the options are more aggressive than real hardware is.

Launching your ROM in Mesen, I have an issue where it won't load because the header claims the game is larger than it actually is. Your header should claim to have only one 8 KiB bank of CHR; right now, it claims to have 2, but the ROM doesn't actually have that data. Your mapper, NROM, also can't have more than 1.

Fixing that makes it load. However, I'm seeing the game crash very quickly, where it hits an illegal instruction that halts the CPU. This causes the onscreen display to note that it crashed, and it breaks there if the debugger is open. Looking in the trace logger, I'm seeing what looks like an RTS to an incorrect location. It looks like after you finish writing data to PPUDATA, you RTS from that function and then RTS again. But that second RTS should actually be an RTI; the current stack frame is actually from the NMI, not a function call, and you have to return from interrupts with RTI. Changing that to an RTI prevents the crash and the game seems to be running now.

Are you still having problems with that fixed? If so, how can I reproduce it?

nescoda86 wrote: Wed Mar 05, 2025 2:08 pm Here's the compiled ROM and code.
Feel free to attach files to the forums in the future; it's easier to download and we don't have to worry about the files disappearing like often happens with external hosting.
Pokun
Posts: 3122
Joined: Tue May 28, 2013 5:49 am
Location: Hokkaido, Japan

Re: Code not working how I'd expect, possibly going mad

Post by Pokun »

Sour wrote: Wed Mar 05, 2025 4:22 pm
Pokun wrote: Wed Mar 05, 2025 3:47 pm The only thing I'm missing from Mesen1 is the trace logger which I just can't get to work correctly.
What isn't working in v2 for you? I use the trace logger frequently for multiple consoles and have not seen any issues on my end
When it's working it shows a list of the last executed instructions. When it's not working it's completely empty. The latter happens when I actually need it and pauses emulation to check it. Last time this happened was when I was debugging my NES homebrew some month ago or so. I solved the problems using just the debugger instead, but it only shows what instructions are about to be executed and not much past history.
nescoda86
Posts: 6
Joined: Tue Feb 13, 2024 2:58 pm

Re: Code not working how I'd expect, possibly going mad

Post by nescoda86 »

Thank you for all the Mesen2 suggestions, I'll check it out!
Fiskbit wrote: Wed Mar 05, 2025 5:15 pm Fixing that makes it load. However, I'm seeing the game crash very quickly, where it hits an illegal instruction that halts the CPU. This causes the onscreen display to note that it crashed, and it breaks there if the debugger is open. Looking in the trace logger, I'm seeing what looks like an RTS to an incorrect location. It looks like after you finish writing data to PPUDATA, you RTS from that function and then RTS again. But that second RTS should actually be an RTI; the current stack frame is actually from the NMI, not a function call, and you have to return from interrupts with RTI. Changing that to an RTI prevents the crash and the game seems to be running now.

Are you still having problems with that fixed? If so, how can I reproduce it?
I don't follow you I'm afraid, as far as I can see all of the routines I JSR to from the NMI routine return with RTS, then the NMI terminates with an RTI. Am I misunderstanding something, or maybe not looking in the right place?

Code: Select all

nmi:
	lda new_screen
	cmp curr_screen
	bne load_new_screen 

	jsr draw_sprites <--- returns with rts

	lda #$00
	sta $2003
	lda #$02
	sta $4014
	
	; ppu cleanup
	jsr init_ppu <--- returns with rts
	; reset scroll
	lda #$00
	sta $2005
	sta $2005

	jsr read_pads <--- returns with rts
	jsr update_sprites <--- returns with rts

	rti  <--- return from nmi with rti
Thanks for the CHR count fix, that was a silly mistake. FCEUX seems to ignore that, as it loaded the ROM OK. A downside to using less strict/accurate emulators I guess!
Fiskbit
Site Admin
Posts: 1192
Joined: Sat Nov 18, 2017 9:15 pm

Re: Code not working how I'd expect, possibly going mad

Post by Fiskbit »

Code: Select all

nmi:
	lda new_screen
	cmp curr_screen
	bne load_new_screen 
	
	....
	
load_new_screen:
	lda new_screen
	sta curr_screen
	jsr load_screen
	rts
This RTS causes a crash. You reached this code with a branch, so the current stack frame is still the interrupt context.


Edit: Also, another minor issue I noticed is that your reset code needs to bit $2002 before the first bit $2002 loop. This is because the state of the vblank flag is undefined at power-on or reset, so it may be 1 and cause the first loop to exit immediately. People leave this out all the time and it can cause subsequent PPU register accesses to randomly fail.
Sour
Posts: 961
Joined: Sun Feb 07, 2016 6:16 pm

Re: Code not working how I'd expect, possibly going mad

Post by Sour »

Pokun wrote: Thu Mar 06, 2025 11:00 am When it's working it shows a list of the last executed instructions. When it's not working it's completely empty. The latter happens when I actually need it and pauses emulation to check it.
Is it because you're expecting the instructions to be logged even if the trace logger isn't opened? This was a functionality in 0.9.9, but isn't a thing in v2 due to performance concerns. Some supported consoles/cpus have clock speeds of 10+mhz, and some games have 3 separate CPUs running at once, so keeping the trace logger on at all times impacts performance a fair amount. I think that might be what's making you think the trace logger is broken because it's blank? If that's it, the simplest way to get a log for the past instructions is to click the "Step back (1 frame)" icon at the top of the trace logger and then do "run 1 frame", which will let you see the last 30k instructions (or the last frame's worth of instructions)
nescoda86
Posts: 6
Joined: Tue Feb 13, 2024 2:58 pm

Re: Code not working how I'd expect, possibly going mad

Post by nescoda86 »

Fiskbit wrote: Thu Mar 06, 2025 3:31 pm

Code: Select all

nmi:
	lda new_screen
	cmp curr_screen
	bne load_new_screen 
	
	....
	
load_new_screen:
	lda new_screen
	sta curr_screen
	jsr load_screen
	rts
This RTS causes a crash. You reached this code with a branch, so the current stack frame is still the interrupt context.


Edit: Also, another minor issue I noticed is that your reset code needs to bit $2002 before the first bit $2002 loop. This is because the state of the vblank flag is undefined at power-on or reset, so it may be 1 and cause the first loop to exit immediately. People leave this out all the time and it can cause subsequent PPU register accesses to randomly fail.
Ahhh, I see! It looks like I intended load_new_screen to be a subroutine, but then jump to the label instead. Thank you so much, I never would have found that. On that note, I'll start debugging with Mesen2 from now on.

Thanks for spotting the missing bit $2002 as well, I'll add that in :)
Pokun
Posts: 3122
Joined: Tue May 28, 2013 5:49 am
Location: Hokkaido, Japan

Re: Code not working how I'd expect, possibly going mad

Post by Pokun »

Sour wrote: Thu Mar 06, 2025 4:32 pm
Pokun wrote: Thu Mar 06, 2025 11:00 am When it's working it shows a list of the last executed instructions. When it's not working it's completely empty. The latter happens when I actually need it and pauses emulation to check it.
Is it because you're expecting the instructions to be logged even if the trace logger isn't opened? This was a functionality in 0.9.9, but isn't a thing in v2 due to performance concerns. Some supported consoles/cpus have clock speeds of 10+mhz, and some games have 3 separate CPUs running at once, so keeping the trace logger on at all times impacts performance a fair amount. I think that might be what's making you think the trace logger is broken because it's blank? If that's it, the simplest way to get a log for the past instructions is to click the "Step back (1 frame)" icon at the top of the trace logger and then do "run 1 frame", which will let you see the last 30k instructions (or the last frame's worth of instructions)
I see so the trace logger has changed, that's probably why it didn't seem to work as in old Mesen.
In that case how is it supposed to be used after these changes? Something like this maybe:
*Open the trace logger and debugger.
*Set breakpoint.
*Get to the point in the game that is to be debugged.
*Run until breakpoint.
v.depatie
Posts: 235
Joined: Sun Nov 03, 2024 4:01 pm
Contact:

Re: Code not working how I'd expect, possibly going mad

Post by v.depatie »

Im not the best coder around but I dont see anywhere you backing up your registers and restore them . It's not mandatory but I do that in my nmi, It can save you alot of headaches


;; Backup A, X, Y registers
PHA ; Push A to the stack
TXA ; Transfer X to A
PHA ; Push X (now in A) to the stack
TYA ; Transfer Y to A
PHA ; Push Y (now in A) to the stack

Rest of nmi

; Restore A, X, Y registers
PLA ; Pull Y from the stack
TAY ; Restore Y
PLA ; Pull X from the stack
TAX ; Restore X
PLA ; Pull A from the stack
TYA ; Restore A


Maybe its there and I didint read it
Pokun
Posts: 3122
Joined: Tue May 28, 2013 5:49 am
Location: Hokkaido, Japan

Re: Code not working how I'd expect, possibly going mad

Post by Pokun »

OP said he is using the "all in NMI" method which means he is basically only using the NMI service routine (like SMB do).
Backing up the registers is mainly needed if using multiple interrupt service routines which can interrupt each others in the middle of it.

If the NMI routine takes too long and is interrupted by another NMI request before it has finished, it may benefit from backing up registers, but I'm not sure if it can return correctly after that. I think "all in NMI" basically requires that you can guarantee that no interrupt will ever happen before a routine has finished.
Post Reply