Question about NMI / displaying new background
Moderator: Moderators
- NeverCameBack
- Posts: 56
- Joined: Mon Feb 24, 2020 12:22 am
Re: Question about NMI / displaying new background
I may be confused about the order of things happening still:
1- Enable NMI and specify to load background from .chr table #0
2- Infinite loop
3- NMI hits
4- Check for screen update
If update, disable NMI and rendering, send 1024 bytes to $2007, then unlatch screen update request
5- Controller check
6- Re-enable NMI and rendering
7- Wait for sprite 0 flag to be zero (in case it was previously holding a value of 1)
8- Wait for sprite 0 flag to be 1
9- Immediately write $%10010000 to $2000 - this should switch the PPU's mapping of the background to .chr file 1 - Midway through rendering the screen, so long that sprite 0 is placed in the middle of the screen.
10 - RTI takes you back to the rung before the NMI interrupt, in my case: "infinite loop"
It seems to me that after the NMI hits, we have a limited amount of time before the rendering takes place.
IF we can run through steps 3 to 7 before the rendering takes place, then I think this should work.. Do you know if that sounds correct?
I'm getting top and bottom of screen rendered from .chr table #0, however when I hold "A" it flashes between that and a screen where the bottom half is rendered from .chr table #1, and the top half is all white (background color here). I'm currently trying to figure it out...
Update:
I had partial success. In the image below the woman's face is from .chr table 0, and her chest is from table 1.
Obviously the problem is that both images are showing on the bottom half of the screen.
Sprite 0 has a Y coordinate of #$B0 (which is where the .chr table switch is taking place).
I put the two white squares in the image to know that both top and bottom are being rendered from the first 430 background bytes.
My problem now is that of course I want the top half of the screen to be rendered too. At which point I'll move sprite 0 to somewhere near vertical #$80, and have a full screen image made of out .chr tables 0 and 1.
I apologize for the lewd image - she is wearing a shirt. This is from an old comic called Warlock 5. I'm just using the picture for practice.
1- Enable NMI and specify to load background from .chr table #0
2- Infinite loop
3- NMI hits
4- Check for screen update
If update, disable NMI and rendering, send 1024 bytes to $2007, then unlatch screen update request
5- Controller check
6- Re-enable NMI and rendering
7- Wait for sprite 0 flag to be zero (in case it was previously holding a value of 1)
8- Wait for sprite 0 flag to be 1
9- Immediately write $%10010000 to $2000 - this should switch the PPU's mapping of the background to .chr file 1 - Midway through rendering the screen, so long that sprite 0 is placed in the middle of the screen.
10 - RTI takes you back to the rung before the NMI interrupt, in my case: "infinite loop"
It seems to me that after the NMI hits, we have a limited amount of time before the rendering takes place.
IF we can run through steps 3 to 7 before the rendering takes place, then I think this should work.. Do you know if that sounds correct?
I'm getting top and bottom of screen rendered from .chr table #0, however when I hold "A" it flashes between that and a screen where the bottom half is rendered from .chr table #1, and the top half is all white (background color here). I'm currently trying to figure it out...
Update:
I had partial success. In the image below the woman's face is from .chr table 0, and her chest is from table 1.
Obviously the problem is that both images are showing on the bottom half of the screen.
Sprite 0 has a Y coordinate of #$B0 (which is where the .chr table switch is taking place).
I put the two white squares in the image to know that both top and bottom are being rendered from the first 430 background bytes.
My problem now is that of course I want the top half of the screen to be rendered too. At which point I'll move sprite 0 to somewhere near vertical #$80, and have a full screen image made of out .chr tables 0 and 1.
I apologize for the lewd image - she is wearing a shirt. This is from an old comic called Warlock 5. I'm just using the picture for practice.
- Attachments
-
- background3.asm
- (17.34 KiB) Downloaded 157 times
Re: Question about NMI / displaying new background
1- Enable NMI (don't enable rendering now!)NeverCameBack wrote: ↑Mon Mar 23, 2020 12:10 pm1- Enable NMI and specify to load background from .chr table #0
2- Infinite loop
2- Infinite loop
Enabling NMIs is the VERY LAST thing before the infinite loop. After you enable NMIs, an NMI can fire at any time, and you absolutely don't want to have any code interrupted by the NMI handler. You probably want to read $2002 before enabling NMIs too, to avoid having the first NMI fire mid-vblank (without having the entire vblank time available, the PPU updates could spill into the visible frame, resulting in problems like screen "jumps" - not very serious - or VRAM corruption and rendering glitches - way more serious).
Also, don't enable rendering at this point. If you do, you'll risk getting screen "jumps" and corrupted graphics. Just keep rendering disabled and let the NMI handler enable it at the correct time. And since you're not enabling rendering here, there's also no need to set any rendering parameters yet (e.g. background CHR table).
There are a few problem here. Enabling rendering and NMIs is something you must do at specific times in order to avoid graphical glitches (temporary and/or permanent). If you enable rendering after switching screens, that'll most likely happen mid-frame, causing a screen "jump". Better just end this instance of the NMI handler and let the next one do everything at the correct times.3- NMI hits
4- Check for screen update
If update, disable NMI and rendering, send 1024 bytes to $2007, then unlatch screen update request
5- Controller check
6- Re-enable NMI and rendering
3- NMI hits
4- If screen update:
4.1- Unlatch update request (copy NewScreen to CurrentScreen)
4.2- Disable NMI and rendering
4.3- Send 1024 bytes to $2007
4.4- read $2002 and enable NMIs
4.5- terminate NMI
5- Incremental PPU updates (sprite DMA, palettes, scroll seam, etc.)
6- Set rendering parameters (CHR and SPR tables, scroll, etc.) and enable rendering
7- Controller check (all game logic, actually)
Note that game logic comes last. You're basing your code off of the Nerdy Nights tutorials, aren't you? Putting the game logic where they did is completely wrong, and that sadly confuses a lot of newbies. Vblank time is really short (less than 8% of an entire frame), so you should only use that time for things that can ONLY be done at that time (i.e. PPU updates). If you do other stuff (game logic, sound playback, etc.) you'll only be able to use 8% of the CPU power of the console, which may be OK for beginner tutorials but is pitiful for an actual game.
Right. Don't forget that an opaque sprite pixel must overlap an opaque background pixel.7- Wait for sprite 0 flag to be zero (in case it was previously holding a value of 1)
8- Wait for sprite 0 flag to be 1
9- Immediately write $%10010000 to $2000 - this should switch the PPU's mapping of the background to .chr file 1 - Midway through rendering the screen, so long that sprite 0 is placed in the middle of the screen.
Yes. And since you're not doing anything there, the RTI effectively acts as a "wait for the next vblank" command.10 - RTI takes you back to the rung before the NMI interrupt, in my case: "infinite loop"
Yup.It seems to me that after the NMI hits, we have a limited amount of time before the rendering takes place.
There's no way you can draw an entire background in the available vblank time. And you shouldn't be handling the game logic during vblank time either, like I said before.IF we can run through steps 3 to 7 before the rendering takes place, then I think this should work.. Do you know if that sounds correct?
The white portion is probably the time when the new screen is being written to VRAM. Once that's done, rendering is enabled mid-frame (remember that I said above how to avoid this?), causing a partial frame to be rendered. I don't know why your sprite 0 hit isn't working properly, though.I'm getting top and bottom of screen rendered from .chr table #0, however when I hold "A" it flashes between that and a screen where the bottom half is rendered from .chr table #1, and the top half is all white (background color here). I'm currently trying to figure it out...
EDIT: I noticed you're using FCEUX for testing. Since you're still getting the hang of the whole timing stuff, I suggest you also try your programs on emulators more accurate then FCEUX, such as Mesen, Nintendulator or Nestopia. FCEUX is not particularly faithful to the timing constraints of the real console and can often cause an incorrect program to look correct.
- NeverCameBack
- Posts: 56
- Joined: Mon Feb 24, 2020 12:22 am
Re: Question about NMI / displaying new background
I think my problem now is understanding:
- When NMI is turned off, the PPU is still going full blast, 60 FPS, however my CPU will not be interrupted for each NMI.
- When rendering is turned off, the PPU won't do anything having to do with the background. The screen will go blank.
- The PPU can only receive data from $2007 during VBLANK, which directly follows the NMI interrupt.
- We wait for the NMI interrupt to turn rendering off, so that we catch the PPU in a VBlank state, and don't interrupt it in the middle of rendering.
- By turning rendering off, the PPU is guaranteed to not be busy, and is ready to receive data via $2007 and store them into it's RAM. Then when you turn rendering back on, the PPU is already all loaded up and ready to render the data that it's holding in it's RAM.
About the order of things:
When I keep the screen update variable latched, meaning the write to $2007 will run happen each NMI; That is when I can at least see the bottom half of my screen showing tiles from both chr tables 0 and 1.
When you say terminate NMI right after - do you mean as in using an RTI? Meaning for my program, go back to the forever loop and wait for the next NMI?
If the NMI is turned back on at this point, is there any guarantee that we will make it down to #s 5,6,7, etc. before being interrupted by another NMI? Or is it that there will be plenty of time, because the period between two NMIs is made up of 8% VBlank time, and 92% rendering time?
I'm sure at this point that I'm detecting the sprite 0 flag. I partially have happening what I want to happen.. however I haven't been able to solve the problem with the top half of the screen not displaying yet. I'll keep working at it.
IN CASE you or anyone some day wants to try and fix it before I get it.. I attached the zip file.
When running the NES file, pressing A is the trigger to "make the full background show up".
I'm stumped but will keep working at it. Maybe I need to try a different emulator like you said. I'll do that. And yes, I started with Nerdy Nights, I should probably go back and re-do the basic lessons to fill in any gaps in my knowledge from it.
- When NMI is turned off, the PPU is still going full blast, 60 FPS, however my CPU will not be interrupted for each NMI.
- When rendering is turned off, the PPU won't do anything having to do with the background. The screen will go blank.
- The PPU can only receive data from $2007 during VBLANK, which directly follows the NMI interrupt.
- We wait for the NMI interrupt to turn rendering off, so that we catch the PPU in a VBlank state, and don't interrupt it in the middle of rendering.
- By turning rendering off, the PPU is guaranteed to not be busy, and is ready to receive data via $2007 and store them into it's RAM. Then when you turn rendering back on, the PPU is already all loaded up and ready to render the data that it's holding in it's RAM.
About the order of things:
I forgot to mention, when I unlatch the update request, that is when I get two vertical copies of .chr table #0.1- Enable NMI (don't enable rendering now!)
2- Infinite loop
3- NMI hits
4- If screen update:
4.1- Unlatch update request (copy NewScreen to CurrentScreen)
When I keep the screen update variable latched, meaning the write to $2007 will run happen each NMI; That is when I can at least see the bottom half of my screen showing tiles from both chr tables 0 and 1.
It looks like 4.4 should be the final time that I re-enable the NMI.4.2- Disable NMI and rendering
4.3- Send 1024 bytes to $2007
4.4- read $2002 and enable NMIs
4.5- terminate NMI
When you say terminate NMI right after - do you mean as in using an RTI? Meaning for my program, go back to the forever loop and wait for the next NMI?
If the NMI is turned back on at this point, is there any guarantee that we will make it down to #s 5,6,7, etc. before being interrupted by another NMI? Or is it that there will be plenty of time, because the period between two NMIs is made up of 8% VBlank time, and 92% rendering time?
[/quote]5- Incremental PPU updates (sprite DMA, palettes, scroll seam, etc.)
6- Set rendering parameters (CHR and SPR tables, scroll, etc.) and enable rendering
7- Controller check (all game logic, actually)
8- Wait for sprite 0 flag to be 1
9- Immediately write $%10010000 to $2000 - this should switch the PPU's mapping of the background to .chr file 1 - Midway through rendering the screen, so long that sprite 0 is placed in the middle of the screen.
10 - RTI takes you back to the rung before the NMI interrupt, in my case: "infinite loop"
I'm sure at this point that I'm detecting the sprite 0 flag. I partially have happening what I want to happen.. however I haven't been able to solve the problem with the top half of the screen not displaying yet. I'll keep working at it.
IN CASE you or anyone some day wants to try and fix it before I get it.. I attached the zip file.
When running the NES file, pressing A is the trigger to "make the full background show up".
I'm stumped but will keep working at it. Maybe I need to try a different emulator like you said. I'll do that. And yes, I started with Nerdy Nights, I should probably go back and re-do the basic lessons to fill in any gaps in my knowledge from it.
Re: Question about NMI / displaying new background
Yup.NeverCameBack wrote: ↑Mon Mar 23, 2020 5:12 pmWhen NMI is turned off, the PPU is still going full blast, 60 FPS, however my CPU will not be interrupted for each NMI.
Yes, but it's still outputting a valid video signal to the TV, the difference is that the entire picture is just a single color. Vblank still exists even when NMIs are off, but they'll fire if they're on.- When rendering is turned off, the PPU won't do anything having to do with the background. The screen will go blank.
If rendering is on, yeah. If rendering is off, you can write to VRAM at any time.- The PPU can only receive data from $2007 during VBLANK, which directly follows the NMI interrupt.
You only need to explicitly turn rendering off if you expect the update that follows to take longer than vblank (as is the case with a full background update), but for small updates (e.g. sprite DMA, palettes, scroll seam, etc.) guaranteed to finish before vblank does you don't need to turn rendering off.- We wait for the NMI interrupt to turn rendering off, so that we catch the PPU in a VBlank state, and don't interrupt it in the middle of rendering.
Yes.- By turning rendering off, the PPU is guaranteed to not be busy, and is ready to receive data via $2007 and store them into it's RAM. Then when you turn rendering back on, the PPU is already all loaded up and ready to render the data that it's holding in it's RAM.
You were enabling rendering mid-screen in several instances, and that's particularly bad for sprite 0 hits. When rendering starts mid-frame, the image is misaligned, and that can interfere with the opaque pixel overlap required for sprite 0 hits to work. Try to fix that (always read $2002 before enabling NMIs!) and see if that helps.I forgot to mention, when I unlatch the update request, that is when I get two vertical copies of .chr table #0.
When I keep the screen update variable latched, meaning the write to $2007 will run happen each NMI; That is when I can at least see the bottom half of my screen showing tiles from both chr tables 0 and 1.
I don't think it's right to say that, since there could be another background switch at a later time and NMIs would go off and on again.It looks like 4.4 should be the final time that I re-enable the NMI.
Yes. Since uploading a new background takes so much time, you don't really know how the CPU is aligned with the PPU when that task finishes. You can't enable rendering or do other PPU updates because you don't know if you're inside vblank (or how much time vblank time is left), and you can't process the game logic because there might not be enough time for that before the next vblank starts, and if an NMI fires in the middle of your game logic that'd probably crash the program. The most sensible thing to do after a big operation like that is to keep rendering off, stop everything and let the CPU and PPU synchronize naturally on the next NMI.When you say terminate NMI right after - do you mean as in using an RTI? Meaning for my program, go back to the forever loop and wait for the next NMI?
By reading $2002 before enabling NMIs you avoid the case when an NMI fires immediately due to the PPU being in the middle of vblank. That way, even if the PPU is in the middle of vblank when you enable NMIs, an NMI will only fire at the beginning of the NEXT vblank, so you can be sure that your NMI handler has all of the vblank time available. As long as you don't put to much stuff in the "frame updates" (sprite DMA, palettes, scrolling seam, etc.) section, all of that is guaranteed to run during vblank.If the NMI is turned back on at this point, is there any guarantee that we will make it down to #s 5,6,7, etc. before being interrupted by another NMI?
Then when the frame updates are done, the scroll is set and rendering is enabled, you have the remaining 92% of the CPU to run all the game logic and prepare the data for the frame updates of the next vblank.Or is it that there will be plenty of time, because the period between two NMIs is made up of 8% VBlank time, and 92% rendering time?
Now, as programs get more complex it is possible that the remaining CPU time is not enough to compete all the game logic (it may happen if many enemies are on screen at once, for example), and if that's a possibility in your game you will have to implement some kind of safeguard preventing the NMI from firing one over the other and locking/crashing the program. The typical solution is to have a "FrameComplete" flag that you set when everything is ready for the next vblank, and then your NMI handler can check this flag right away and decide whether to behave normally or only do the most critical stuff (handle music, status bars, etc.) and skip the updates, creating what's commonly called a "lag frame". This is what causes slowdowns in games.
I don't think Nerdy Nights is a particularly good tutorial, because it begins with some misinformation (e.g. wrong comments) and a terrible program structure (game logic inside vblank, giving you only 8% of the CPU time), but I think it improves with the latter lessons. Unfortunately I don't have anything better to suggest, so the best I can do is help you fix what's wing with it here, like I've been doing.I started with Nerdy Nights, I should probably go back and re-do the basic lessons to fill in any gaps in my knowledge from it.
Re: Question about NMI / displaying new background
I don't have the time to personally fix it and test it (and you probably would benefit less from that than from fixing it yourself), but I can point out a few things I noticed aren't right:NeverCameBack wrote: ↑Mon Mar 23, 2020 5:12 pmIN CASE you or anyone some day wants to try and fix it before I get it.. I attached the zip file.
1- BackgroundNum is not initialized. The first time the NMI handler runs, it tests this variable without you having prepared it to trigger a background update. Since the memory was cleared to 0 on reset, no background will be displayed until you press A and this variable is changed to 1. You must indicate you need background 1 to be loaded somewhere in your reset code, before the first NMI fires.
2- You don't clear BackgroundNum after the new screen is drawn. According to your logic, whenever BackgroundNum is 1, you draw a new background (any other value doesn't trigger an update). But once the new background is drawn this variable is left at 1, so the same background will be drawn over and over and over. You have to set that variable back to 0 after drawing the screen so it's only drawn once (per button press, that is). Another thing you can do to improve this is make it so it skips an update when the BackgroundNum is 0, and use values 1 to 255 to load different screens.
3- You're doing a sprite DMA before everything in your NMI handler. This doesn't have any bad side effects, it's just pointless to do a sprite DMA if the next thing you'll do is change the entire screen, and before that new screen is displayed, another sprite DMA will happen anyway. My point is: there's two things your NMI handler can do - update the current screen or load a new one. Updating the sprites is part of updating the current screen, you don't need to do it if you're drawing a new one. I suggest putting the BackgroundNum first, and moving the sprite DMA to the NoBackground section.
4- The game logic (controller reading) is sandwiched by the PPU update code. This is what will result in you only having 8% of the CPU time for your entire program. Before the game logic you need to finish all the PPU stuff. In your case, that's the sprite DMA, the PPU configuration ($2000 and $2001) and the scroll ($2005). Then you can have a whole lot of game logic before you start worrying about running out of CPU time.
5- Your sprite 0 hit check is really weird... Why all those bit shifts? Maybe you're not familiar with bitwise operations (you really should be if you're planning on working with assembly!) but you can test any bit in the accumulator by ANDing the value with a bit mask containing the bit you want to test:
Code: Select all
WaitForNoHit:
LDA $2002
AND #%01000000
BNE WaitForNoHit
WaitForHit:
LDA $2002
AND #%01000000
BEQ WaitForHit
Code: Select all
WaitForNoHit:
BIT $2002 ;copies the sprite 0 hit flag to CPU flag V
BVS WaitForNoHit ;go test again if flag is set
WaitForHit:
BIT $2002 ;copies the sprite 0 hit flag to CPU flag V
BVC WaitForHit ;go test again if flag is clear
- NeverCameBack
- Posts: 56
- Joined: Mon Feb 24, 2020 12:22 am
Re: Question about NMI / displaying new background
My last question: What do I owe you for all the help?
Seriously though, I never got as much help from someone over the internet, and to everyone else who chimed in too. I appreciate it... and will pass it on if I can.
Your #6 is mainly what the problem was - I didn't realize that the PPU needed to be instructed every frame. It makes total sense now, but I wasn't seeing it. I guess I was giving the PPU too much credit in thinking that it would just keep putting the same exact thing on the screen 60 FPS without being told to switch mapping to the other .chr table.
Those LSR and ASL shifts I did just to isolate the 6th bit of $2002. I was going to do an AND on checking if sprite 0 was = 1, but to check if it was zero first, the shifts were the first thing I thought of. In my day job working with PLCs, I've come across some programs that use bit-wise operations, but I mainly use "ladder logic".
So I have my full image. I showed my wife, and she is less than thrilled with my progress on the bottom half of the image.
Just curious, but what kind of games have you worked on, and do you have anything in the works currently?
Seriously though, I never got as much help from someone over the internet, and to everyone else who chimed in too. I appreciate it... and will pass it on if I can.
Your #6 is mainly what the problem was - I didn't realize that the PPU needed to be instructed every frame. It makes total sense now, but I wasn't seeing it. I guess I was giving the PPU too much credit in thinking that it would just keep putting the same exact thing on the screen 60 FPS without being told to switch mapping to the other .chr table.
Those LSR and ASL shifts I did just to isolate the 6th bit of $2002. I was going to do an AND on checking if sprite 0 was = 1, but to check if it was zero first, the shifts were the first thing I thought of. In my day job working with PLCs, I've come across some programs that use bit-wise operations, but I mainly use "ladder logic".
So I have my full image. I showed my wife, and she is less than thrilled with my progress on the bottom half of the image.
Just curious, but what kind of games have you worked on, and do you have anything in the works currently?
Re: Question about NMI / displaying new background
I help people here hoping that they'll stick around and help others in the future. If you can do that, that'll be great! Some members simply go away once they learn what they wanted. I hope your username isn't foreshadowing something like this!
The way these old systems work isn't very intuitive, but once you get the hang of it everything makes perfect sense!Your #6 is mainly what the problem was - I didn't realize that the PPU needed to be instructed every frame. It makes total sense now, but I wasn't seeing it. I guess I was giving the PPU too much credit in thinking that it would just keep putting the same exact thing on the screen 60 FPS without being told to switch mapping to the other .chr table.
If you're just getting started with 6502 you don't have to worry too much about writing optimal code. It's just that CPUxPPU synchronization is one of the most timing-sensitive parts of NES programming, so I had to say something. Here's why: You're waiting for a PPU event (sprite 0 hit) that happens with pixel precision, and this event could happen at any point during your polling loop, since the CPU and the PPU are completely out of sync. The longer the loop is, the larger the timing error for the detection of that event is. Your loop was about 27 CPU cycles, and since each CPU cycles is equivalent to 3 rendered pixels, you could have a delay between 0 and 81 pixels when reacting to the sprite 0 hit. Since the delay changes each frame due to the random misalignment, that could translate to very noticeable jittering on that specific scanline. With the improved 7-cycle loop, the error will be at most 21 pixels, and that's much easier to hide in hblank.Those LSR and ASL shifts I did just to isolate the 6th bit of $2002. I was going to do an AND on checking if sprite 0 was = 1, but to check if it was zero first, the shifts were the first thing I thought of. In my day job working with PLCs, I've come across some programs that use bit-wise operations, but I mainly use "ladder logic".
I didn't see the full image yet. Is the bottom part really that controversial?So I have my full image. I showed my wife, and she is less than thrilled with my progress on the bottom half of the image.
My main accomplishment is a raycasting engine I haven't updated in over 10 years! Well, I have worked on it a bit during the past couple of years, but haven't created a new NES ROM yet.Just curious, but what kind of games have you worked on, and do you have anything in the works currently?
- NeverCameBack
- Posts: 56
- Joined: Mon Feb 24, 2020 12:22 am
Re: Question about NMI / displaying new background
Not too bad, but this comic was certainly illustrated for a mature audience. The woman is wearing a night gown, BUT you can basically see some boobs.I didn't see the full image yet. Is the bottom part really that controversial?
What led me here is that I took an atari programming course on Udemy - They don't have an NES one. The instructor has a ray casting course too, using C. Do you have a prototype that would work on an emulator or real NES hardware? I'd love to make a Hexen mod someday... Especially a Hexen 64 mod.
My next goal is to "master" the .chr bank switching, to get more graphics into a single game. Maybe I could make a short graphic novel type game with multiple paths. I imagine that I'll run into some limitations on the size constraints. I had RGB modded my NES earlier this year, so my aim is to make something to play on real hardware.
Re: Question about NMI / displaying new background
The old ROM is still available at the link in the video's description (BTW, thanks, Memblers!). I don't have anything newer than that working on the NES.NeverCameBack wrote: ↑Mon Mar 23, 2020 10:26 pmDo you have a prototype that would work on an emulator or real NES hardware?
I don't see how you'd do that under so many restrictions, but OK!I'd love to make a Hexen mod someday... Especially a Hexen 64 mod.
That's cool.Maybe I could make a short graphic novel type game with multiple paths.
With typical NES hardware from back in the day it'll be hard to go past 512KB or so, which would be enough for a few dozen images the way you're making them. You could go with CHR-RAM instead of CHR-ROM and take advantage of compression, but that complicates the programming. Another option with modern hardware is to stream the data off of an SD card, which can provide gigabytes of storage, but that would only work on a flashcart or a custom cartridge.I imagine that I'll run into some limitations on the size constraints.
Re: Question about NMI / displaying new background
I have a question about NMI interrupt interference in CPU code (forever loop) and I would like to clarify.
For example, I have a function that saves 4x empty tiles to nametable0:
If the NMI is done between STA 2007 records, it will cause an incorrect record of the next 2007, e.g. the PPU address will be reset and the next 2007 will be saved to $ 0000?
Similarly, will the NMI be performed between the two writes of 2006, will the PPU address be wrong?
Is the problem only when in the NMI interrupt there are other functions with writes to 2006/2007, or also the "empty" interrupt itself may interfere with the CPU code in which I write to the PPU?
For example, I have a function that saves 4x empty tiles to nametable0:
Code: Select all
lda #$20
sta $2006
lda #$40
sta $2006
lda #$00
sta $2007
sta $2007
sta $2007
<----NMI will break the code right here
sta $2007
Similarly, will the NMI be performed between the two writes of 2006, will the PPU address be wrong?
Code: Select all
lda #$20
<----NMI will break the code right here
sta $2006
lda #$40
sta $2006
lda #$00
sta $2007
sta $2007
sta $2007
sta $2007
Code: Select all
NMI:
pha ;protect the registers
txa
pha
tya
pha
;
pla ;restore the registers
tay
pla
tax
pla
RTI
Re: Question about NMI / displaying new background
The PPU address will only be reset if your NMI logic explicitly changes it - if it just immediately returns without doing anything, then things should still work fine.sdm2 wrote: ↑Sun Nov 29, 2020 3:25 am If the NMI is done between STA 2007 records, it will cause an incorrect record of the next 2007, e.g. the PPU address will be reset and the next 2007 will be saved to $ 0000?
[snip]
Similarly, will the NMI be performed between the two writes of 2006, will the PPU address be wrong?
[snip]
Is the problem only when in the NMI interrupt there are other functions with writes to 2006/2007, or also the "empty" interrupt itself may interfere with the CPU code in which I write to the PPU?
Given that accessing VRAM outside of VBLANK can only be done if you disable rendering, you typically want to also disable NMIs while you're at it so you won't have to worry about this problem in the first place.
Quietust, QMT Productions
P.S. If you don't get this note, let me know and I'll write you another.
P.S. If you don't get this note, let me know and I'll write you another.
Re: Question about NMI / displaying new background
Thanks, he shouldn't actually interfere. I did a little test. Saving 8 tiles in one line works correctly when interrupted with the "nothing" function (the interrupt effect will probably be similar to that of the NMI).
Code: Select all
LDA #$20
JSR nothing
STA $2006
JSR nothing
LDA #$40
JSR nothing
STA $2006
LDA #$30
STA $2007
STA $2007
STA $2007
STA $2007
JSR nothing
STA $2007
STA $2007
STA $2007
JSR nothing
STA $2007
;----------------------
nothing: ;test
pha
txa
pha
tya
pha
lda #$01 ;does not cause errors
STA somevariable
; lda #$00 ;it causes errors
; STA $2006
; STA $2006
pla
tay
pla
tax
pla
RTS
Re: Question about NMI / displaying new background
As long as you don't touch any PPU registers in the interrupt code, VRAM updates in the main thread should work just fine. In most of my NES engines I have the NMI handler check whether rendering is enabled or disabled, and if it's the latter, it won't do anything with the PPU.
Re: Question about NMI / displaying new background
Can the lack of back up CPU status in the NMI (PHP, PLP) cause errors when using the NMI interrupt along with the code in the CPU loop? I have not used it before and I do not see any mention of it here:
https://wiki.nesdev.com/w/index.php/The_frame_and_NMIs
https://wiki.nesdev.com/w/index.php/The_frame_and_NMIs
Code: Select all
NMI:
PHP ;add?
pha ;protect the registers
txa
pha
tya
pha
;
pla ;restore the registers
tay
pla
tax
pla
PLP ;add?
RTI
- Jarhmander
- Formerly ~J-@D!~
- Posts: 568
- Joined: Sun Mar 12, 2006 12:36 am
- Location: Rive nord de Montréal
Re: Question about NMI / displaying new background
No, the NMI and IRQ "machinery" already save P on the stack —RTI is needed instead of RTS for a NMI/IRQ because RTI pull P from the stack. Saving/restoring A/X/Y however is necessary.sdm2 wrote: ↑Mon Nov 30, 2020 2:31 am Can the lack of back up CPU status in the NMI (PHP, PLP) cause errors when using the NMI interrupt along with the code in the CPU loop? I have not used it before and I do not see any mention of it here:
https://wiki.nesdev.com/w/index.php/The_frame_and_NMIs
Code: Select all
NMI: PHP ;add? pha ;protect the registers txa pha tya pha ; pla ;restore the registers tay pla tax pla PLP ;add? RTI
((λ (x) (x x)) (λ (x) (x x)))