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.
I wrote some code to load pattern tiles onto the PPU, but I am having a strange bug I cannot seem to figure out. The code is below. I am using FCEUX 2.2.3, New PPU enabled. The pattern table I have included has NINE pages (or 9 x 16 tiles = 2304 bytes). When I set NUMPAGES to 9 all I get is a gray screen. When I set it to 10 I have an extra page of garbage tiles in the PPU, but the ROM works fine. If I set the NUMPAGES to 10 and add a DEX command immediately after the LDX command it also works and I don't have the extra garbage tiles in the PPU. If I set NUMPAGES to 9 and add a NOP command right after the LDX it also works. Does anyone have a clue what might be causing this? I've never seen any timing bug like this before.
What's in your NMI handler? What address is LBL.OriginStory.LoadTiles.Background.Loop getting assembled at? You can use your assembler's map file or place a breakpoint on $2006 writes to answer the latter.
I ask because I'm guessing some combination of page crossing and NMI clobbering registers.
dougeff wrote:Assuming you've correctly set FLAG.NMI.Wait to avoid the reads from 2002 and writes to 2006/2007...
I believe the 2 writes to $2005 at the end of NMI might be the problem, but I can't remember exactly how they affect 2006/2007 writes.
Writing $2000/$2005/$2005 at the end of NMI is the correct way of resetting the VRAM address for rendering, and it overrides whatever you wrote to $2006 previously.
However, the most important part is to ensure that it's before the end of VBLANK, which means you need to make sure your VRAM writes aren't taking too long (and that you do it before other non-essential stuff like reading controller input or updating your sound engine).
Quietust, QMT Productions
P.S. If you don't get this note, let me know and I'll write you another.
If the code that loads patterns is interrupted by the NMI, and the NMI uses any of $2000, $2005, $2006 or $2007, that will certainly interfere with the VRAM address and mess up the update. If you're keeping NMIs on while the main thread performs PPU updates (which is a good thing, because you can continue playing sounds during screen transitions, for example), be sure to implement a way to skip all PPU operations if rendering is disabled. I normally check the buffered copy of PPUMASK to know whether it's safe to use the PPU in the NMI handler or not.
Ah, I misunderstood what was going on - that's what I get for not fully reading the entire thread.
One solution to this problem is to just ensure that an NMI will never occur during VRAM writes - either disable NMI during any VRAM writes (which is probably a good idea anyways), or schedule them to occur during your NMI routine.
Quietust, QMT Productions
P.S. If you don't get this note, let me know and I'll write you another.
; Set NMI FLAGS
LDA FALSE
STA FLAG.NMI.Wait
LDA TRUE
STA FLAG.NMI.Update.Sprites.Palette
STA FLAG.NMI.Update.Background.Palette
STA FLAG.NMI.Update.Sprites
; Wait for Next NMI
- BIT $2002
BPL -
; Set NMI FLAGS
LDA FALSE
STA FLAG.NMI.Wait
STA FLAG.NMI.Update.Sprites.Palette
STA FLAG.NMI.Update.Background.Palette
; Re-Enable Rendering
LDA #%00011110
STA $2001
It seems I need to add another NMI FLAG that can bypass the $2005 writes. Something like
Quietust wrote:either disable NMI during any VRAM writes (which is probably a good idea anyways)
Unless you want music to keep playing during screen transitions... It can be weird in screen-by-screen or room-by-room games to have the music interrupted or distorted every time new screens/rooms are loaded.
I usually leave NMIs on all the time, and use flags and other variables to make sure the NMI handler doesn't screw up anything the main thread is doing.
Shouldn't you have LDA #TRUE instead of LDA TRUE? I guess you might have TRUE defined with the # built-in, but it makes it like you're using a variable.. seems confusing. Definitely would recommend avoiding that when using constants, better to type one more character and have the code look more clear.
...and to be totally clear, it's not a convention or choice for the individual programmer - the assembler will assemble the instruction into different opcodes based on whether or not there's a '#' to indicate the immediate addressing mode.
Here is my constant declaration header and how I handle the True/False. Probably not the best way, but it helps me to differentiate between CPU variables (those defined using .dsb/dsw that have a memory location), compiler variables (such as PRGCOUNT), and compiler enumerations (TRUE/FALSE).