I do not recommend this approach. It is fraught is big problems. I don't even think it's possible.
1) HBlank is extremely
short, and moreover, is largely undetectable
. You basically just have to time it out in cycles based on when you expect the IRQ to fire, but even then there's a lot of wiggle room because depending on the instruction that was being executed at the time of the IRQ, there's like a 2-7 cycle window, so you can't get it exactly perfect every time.
There are some tricks to sync to HBlank but they're questionable. Micro Machines famously polls $2004 to exploit a PPU quirk to help it find HBlank, but I'm a little fuzzy on the details of how it gets it to work. From what I understand it looks for a large patch of $FFs to be read back from $2004 which is an indication that HBlank has just ended
. But again, very sketchy and I don't really recommend it. If you want to explore this, reading $2004 mid-frame is an absolute monster and I barely understand it myself. Here's a reference page as to what you get back when you read it: https://wiki.nesdev.com/w/index.php/PPU_sprite_evaluation
IIRC MMC5 IRQs fire at the very end of the scanline, which means you've already missed HBlank and will have to wait a full scanline to reach it again. Probably the most realiable thing to do would be to wait ~76 cycles after the IRQ trips before shutting off the PPU, which would put you right around HBlank on the following line. Earlier than that might not hurt, as the right side of the screen is black anyway so if you shut the PPU off early you'll just chop off a few pixels that you wouldn't be seeing anyway.
2) $2006 changes the PPU address, but the PPU address is ALSO used as the screen scroll. You cannot just write to $2005 to reset the scroll back to what it was. It's a LOT more complicated than that. The PPU is basically modifying $2006 in real time as the screen is drawn. You'll need to calculate what $2006 should be based on what scanline you're on, and write that.
But even just writing to $2006 isn't enough, because there are some scroll bits that can only be set by $2005 that $2006 zeros out when you write to it. It's a total mess. To accomplish this feat you have to write to $2005 and $2006 in a weird pattern to properly set the scroll. The Nesdev wiki has a little outline on how to do it:https://wiki.nesdev.com/w/index.php/PPU_scrolling#Split_X.2FY_scroll
But note that this is 4 STAs, and probably also 4 LDAs. That's 24 cycles, which is at least like 70% of your HBlank time. There's no way you have enough time to turn the screen off, change the palette, correct the scroll, then turn the screen back on all before HBlank ends.
3) Turning off the screen mid frame is necessary if you want to touch the palette. But it will also completely confuse the MMC5 IRQ counter making it unreliable for the rest of the frame. It's probably
still usable and will resync once the PPU gets turned back on, but the scanline count will be reset and there is NO WAY
I would trust an emulator to simulate it properly.
4) Turning off the screen is also going to trash sprites for the next line, as the PPU uses HBlank to fetch CHR for sprites on the following line (as well as some other stuff). So to do this safely, you'd need to leave sprites off for 1 scanline after adjusting the palette, and then turn them on afterwards.
TL;DR: Changing the palette mid-frame is not feasible. I only know of like 2 games (and some tech demos) that actually do it, and all of them leave the screen off for several
consecutive scanlines to pull it off.