Now that I understand the basic of IRQ, I'm trying to test them in my current code. At first it seemed to work but now I saw an interesting side effect: for some reason, my counter after waiting for an nmi in the C code part seems to increase faster.
I'm sure it must be because of a bug somewhere but for now I cannot pin point what it is. Before activating IRQ, I was using a counter on the C side to time sequences and it was working well. After the IRQ, when I split on line 75, there is a little bit or artifact (like some black line flashing) so something is not working properly yet and the counter reaches it counts faster. Disabling the IRQ makes it work back normally again.
Maybe another question I will need to ask if when I'm mid-screen in the middle of a irq, what is the proper way to scroll then. I first updated the $2000, reset the latch $2002 before setting x/y on $2005 but it seemed to flickers.
Banshaku wrote:
when I'm mid-screen in the middle of a irq, what is the proper way to scroll then. I first updated the $2000, reset the latch $2002 before setting x/y on $2005 but it seemed to flickers.
Mid-screen, only the X coordinate (from $2000 and the first write to $2005) sticks. For any Y scrolling, you have to use $2006. Depending on the specific X/Y you're setting, you may also have to write to both.
Even slight deviations in when scroll changes happen can cause a whole scanline to look glitchy. The safest time to commit scroll changes is the beginning of hblank, when the PPU is busy fetching sprite patterns and is not doing anything scroll-related.
If you want full control of the scroll, down to the pixel level in both X and Y axes, your needed to use the $2006/5/5/6 trick I posted before. You can do most of the setup during the scanlines, and have only the final 2 writes ($2005/6) fall within hblank. It's 5 CPU cycles to fit in a ~20 window, so even considering any possible latency it should be easy to time right.
MMC3 IRQs fire well within hblank already, so they're isn't any usable hblank time to do anything immediately, specially after backing up the registers you'll modify, so you have to wait until the next scanline to apply any raster effects anyway, so you might as well use this time for useful things like setting up the next IRQ and the scroll change.
I tested the code in the middle of the night (...) and now that it's the morning and drinking coffee, I realized a few things:
- My sample did not save the register (what was I thinking ^^;;)
- I didn't know about scroll mid-screen had issues so the 2006/5/5/6 trick, since I never did up to now mid-screen scroll, the comment flied well above my head
First, the register to backup. That should hopefully fix the strange counter bugs. Second, Right now I do not need to move Y since it is only scrolling top from right, middle from left and bottom doesn't move, all X only. This is the part that made me confused yesteday. I know in the NMI I restore $2000 values before I restore the scroll value but do I need to do that everytime I act on the PPU? That part is fuzzy and don't remember about it.
My guess would be that only in NMI we restore the state of PPU then midway I just need to update the address to allow to scroll. This means I would first reset the latch to make sure I can write the X properly then write X only. Does it make sense? If it fails after that then something else is missing and I need to understand more about mid-screen scroll then.
My coffee has not kicked in yet, my writing style is a mess and typo are everywhere
You only need to write to $2000 if you want to change the 256s bit of X scrolling (the one that changes nametables).
Otherwise, the problem is that the PPU reloads all of the fine X behavior on the last visible pixel of a scanline. To get a clean split scroll, you need to write to $2005 as close as possible to the end of the scanline, without going over; if it's early the pixels between that write and the end of the scanline will be glitchy, and if it's late all the pixels on the following scanline will be glitchy.
You could look at Galaxian in Mesen's event viewer for an example.
I do not understand yet about the "256s bit of X scrolling" but it make me think about my current issue and what I saw on the screen regarding my bugs.
- when I scroll until the end, I'm 1 pixel off. My guess this is the issue of "not changing name table yet"
- about timing, thing where flashing or creating corruption which could mean it was not done in blanking
Now I can see why changing palette mid-screen is an issue if just scrolling can cause so much problem. If the code is executed in the IRQ how can I be sure that I'm really in hblank. Hmmm... I still don't visualise all the details in my head yet but it's getting clearer. I may be "slow" to get it but the more I bang my head on the wall, the more I'm getting closer to the solution
edit:
Avoiding the double post. Saving registers fixed the counter issue. As for scroll, for now it doesn't work yet but the colors are becoming darker so something it going wrong and will need to check why.
I was thinking about making a call to a C function from the IRQ, like a callback so it will use the same state as the method managing the scroll but for now it is not working well (and maybe not a good idea?). Managing state between the C/asm code is tricky so I want to see what is possible at the same time but... I guess I should fix the scroll bug first before trying the callback thing.
"256s bit of X scrolling" - the single bit of X scrolling that's written to $2000. The one that specifies which nametable the view starts in. (Xscroll & $100).
Another way to put it is: the full background area is 512 pixels wide, which requires a 9-bit value to reference - the lower 8 bits are updated via $2005, while the topmost bit is updated via $2000.
The same is true for the vertical scroll, the only difference being that since name tables are only 240 pixels tall, scroll values 240-255 and 496-511 must not be used, otherwise the PPU will try to interpret attribute table data as if it were name table data.
Since the code that handle the map has been done a long, long time ago, I guess nametable handling has been quite forgotten ^^;;
The previous comments gave me some idea about what could be going wrong. I guess without context, it is hard to give advice so I will try to explain what I'm doing at the moment. My approach is maybe wrong but I think doable (overdoing it) but instead of focusing on the approach, we should focus on what I did wrong in the current scenario so it will help me understand more about scrolling. Then I can do it the right way. Understanding the cause of the current bug will help to learn more on the subject.
For making it easier (and maybe I was wrong but that's we can talk about later), I decided to separate the screen in 2 section: the first section contains the top of the screen and bottom text. This is set in nt $2000. The second middle section is set in nt $2400. From that layout, my goal is to set it in vertical mirroring and scroll left/right to "merge" the data.
- I load first section one in NT $2000
- I load second section in NT $2400
One thing here: after loading the second section, the address for NT is still (I think) $2400 so it may be the cause of flickering. I think I need to put it back to $2000 and I always forget about it. (will test later, the kids are using my main computer)
What I'm doing is:
- the top scroll from 250->0 to give the impression that is coming from the left
- the middle scroll from 0->250 to give the impression that is coming from the right
- (not done) the last IRQ will move what is in nt $2000 to 0 to show the text at bottom
The goal was to stay in NT 2000 and scroll both part together. I think that is doable but I must have made one mistake somewhere (maybe it's the adress only, need to test).
- I wait for NMI and scroll the top part (inside C code)
- Once IRQ is triggered, I move the middle part
- another IRQ will need to be set for bottom but not done yet
- once this IRQ is triggered it would move nt 2000 back to 0
So this is what I was trying to do but I may have a few basic bug that just make it go bad.
After reading the comments about 256, something clicked. Maybe one way simpler is:
- put top and middle in $2000
- put bottom text in $2400
- On nmi, scroll top from left (250 -> 0)
- on IRQ, change NT to $2400 and do the same scroll (250->0) giving the impression that it's coming to the right
- On next IRQ, set scroll to 0 in NT $2400 to show bottom text
- on next NMI go back to NT $2000 and do it again
It seems simpler for the data BUT I do not know if changing NT mid-screen is a good thing.
Are those 2 doable, altought the first one now seems like I'm overdoing it? If not, what could be the possible issue with it?
Thank you in advance for more information on the subject.
edit:
the nt adress was not the cause. The color issue was caused by fade was not finished when enabling the IRQ, causing the color to not be completely updated. For now I cannot make a proper split yet and changing NT midscreen (testing second scenario) causes more garbage to appear on the screen and in the second name table. This one will be long to debug ^^;;;