It is currently Mon Oct 16, 2017 11:09 pm

All times are UTC - 7 hours





Post new topic Reply to topic  [ 42 posts ]  Go to page 1, 2, 3  Next
Author Message
PostPosted: Wed Oct 14, 2015 1:03 pm 
Offline
User avatar

Joined: Sat Jul 25, 2015 1:22 pm
Posts: 501
I hoped that I could open up a discussion about the whats and hows of scanline IRQs. I've read some great information on here that's helped me get started, but there's always more to learn.

First off, I'm wondering what has to be done in order to safely change palette colors during a scanline IRQ. I've done this successfully, on an emulator, but am unsure if it would be safe for hardware and I don't want to experiment until I know for sure.

In the Bob Rost document, http://bobrost.com/nes/files/mmc3irqs.txt he just disables background rendering and goes for it. Is that enough? On an emulator, I didn't have any trouble whether I disabled rendering or not, nor whether it was in the middle of a scanline. I'm not willing to take a chance that my game could damage anyone's console though.

Thanks in advance!


Top
 Profile  
 
PostPosted: Wed Oct 14, 2015 1:23 pm 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10046
Location: Rio de Janeiro - Brazil
Relevant thread: viewtopic.php?f=2&t=13188

Mid-screen palette changes are much more complicated than mid-screen scroll changes, for example. You absolutely have to disable rendering, since palette updates are essentially VRAM updates, and you can't update VRAM while the PPU is rendering.

The two things that make mid-screen palette updates tricky are that you have to restore the scroll after you're done (since using $2006/$2007 messes up the scroll), and that the PPU has a "feature" that causes the color being pointed by the VRAM address register to be rendered to the screen while rendering is off. This prevents you from accessing the palette outside of hblank without generating a visible "rainbow" on the screen, as the address register jumps from color to color with each write. Since the hblank period is pretty short, there really isn't enough time to disable rendering, changing palette bytes, resetting the scroll and enabling rendering back from one scanline to the next, so you have to spread these tasks across multiple scanlines, which will appear to be blank on screen.

Disabling rendering mid-frame can also corrupt sprites, if not done carefully, so that's another thing to keep in mind.

Quote:
On an emulator, I didn't have any trouble whether I disabled rendering or not

This is why you should test on multiple emulators, and preferably on the real hardware as well. This particular emulator sounds pretty inaccurate (FCEUX?).

Quote:
nor whether it was in the middle of a scanline.

The drawback of doing it in the middle of a scanline, as opposed to doing it during hblank, is that you'll get a jittering rainbow on that scanline, but as long as you do it when rendering is off and you restore the scroll later, the rest of the frame should be fine.

BTW, this isn't necessarily related to mapper IRQs, since you can just as well use a sprite 0 hit, a sprite overflow, or timed code to time raster effects. Here are the types of raster effects that are commonly done on the NES and how complicated they are to implement (in what I consider to be the increasing order of complexity):

Color emphasis and/or grayscale: write normally to $2001;
Pattern switch: switch banks normally, according to the mapper being used;
Horizontal scroll: write normally to $2000 and $2005 (Y portion has no effect);
Vertical (and horizontal) scroll: write specially arranged values to $2006/$2005/$2005/$2006;
Palette: disable rendering, update bytes, set the scroll using $2006/5/5/6, enable rendering;

With the exception of palette updates, all of these can be performed, without glitches, from one scanline to the next, as long as the final steps take place during hblank, meaning they are safe to be used in the middle of the gameplay area. Palette updates make sense mostly between the gameplay window and a status bar.

EDIT: You have to laugh at comments like this:

mmc3irqs.txt wrote:
// BG off to force stupid emulators to realize i'm changing the BG color


Last edited by tokumaru on Wed Oct 14, 2015 1:53 pm, edited 1 time in total.

Top
 Profile  
 
PostPosted: Wed Oct 14, 2015 1:49 pm 
Offline

Joined: Sun Apr 13, 2008 11:12 am
Posts: 6273
Location: Seattle
darryl.revok wrote:
I'm not willing to take a chance that my game could damage anyone's console though.
To add to what tokumaru said, failing to do the full dance won't damage any console. It just won't work at all.

Also, on the 6502, the IRQ has a random latency up to the number of cycles of the instructions that the IRQ interrupts (so a dead wait of JMP self could produce up to 4 3 CPU cycles of latency, while a busy wait of BIT zeropagesomething / BNZ theabovebit could produce up to 3 CPU cycles, and interrupting a huge NOP slide could be later than the ideal by up to 2 cycles)


Last edited by lidnariq on Wed Oct 14, 2015 5:08 pm, edited 1 time in total.

Top
 Profile  
 
PostPosted: Wed Oct 14, 2015 4:57 pm 
Offline

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 19084
Location: NE Indiana, USA (NTSC)
How does JMP aaaa have 4 cycles? As I understand it, JMP aaaa is a 3-cycle instruction, meaning it responds to an interrupt 1, 2, or 3 cycles after the interrupt is asserted. That's two plus or minus one.


Top
 Profile  
 
PostPosted: Wed Oct 14, 2015 10:04 pm 
Offline
User avatar

Joined: Sat Jul 25, 2015 1:22 pm
Posts: 501
tokumaru wrote:
you have to restore the scroll after you're done (since using $2006/$2007 messes up the scroll),

From what I read, hBlank gives you 340 PPU cycles. If so, then does that equate to 1020 CPU cycles? (NTSC) That seems excessive. If that's actually the case, then that's enough to do a ton of stuff. I feel like that can't be correct though.

(From what I'm reading in the thread you posted, I can see this isn't the case. Not sure about how many cycles I actually have though.)

So from what I'm reading, I have to have a blank scanline to change palettes. That's unfortunate. I'm guessing there's no workaround.

The scene in particular that I'm working on right now is of the main character walking down a cliff with a scene of the game world in the background. It has eight strips of parallax scrolling, and I had hoped to integrate mid-screen CHR switching as well as palette swapping during the process. Seems I may need to do some careful planning to get the effects I wanted.

So, a black scan line is what's needed for a mid-screen palette swap? That means if I have a strip of black sky, I can use the bottom row of that strip to switch my palette, right? That's one I can recover. And I can switch one palette out in NMI when it gets scrolled off the screen. This leaves me, I believe, one palette shy of what I was shooting for.

So, the palette color getting written is drawn on the screen. I wonder if I can use this to my advantage. Is there any way to make it draw a single color across the entire screen? Like, utilizing a palette swap to change the BG color, and using the color written as a line on the image? For the tops of the sky strips that could work.

And what is the trouble with leaving the PPU targeting a color that isn't color 0?

Quote:
as the address register jumps from color to color with each write.


So if I'm understanding how this works properly, is this possible:

    Stuff as many palette swaps as possible into hBlank
    Make the last write the color you want the next scanline to appear (could even be the same color it already was)
    Next tile drawn uses palette written last
    First row are solid pixels anyway, so scroll won't matter
    Set scroll on next scanline

Quote:
Disabling rendering mid-frame can also corrupt sprites, if not done carefully, so that's another thing to keep in mind.


Is there an easy explanation for how to do so carefully? :)

Quote:
This is why you should test on multiple emulators, and preferably on the real hardware as well.


I have an Everdrive that I use for testing but I haven't done so with my raster effects yet. Is there anything other than setting PPU to slave mode that could damage hardware?

Quote:
This particular emulator sounds pretty inaccurate (FCEUX?).

That's the one.

mmc3irqs.txt wrote:
// BG off to force stupid emulators to realize i'm changing the BG color

Hehe. Stupid emulators!


Top
 Profile  
 
PostPosted: Wed Oct 14, 2015 11:42 pm 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10046
Location: Rio de Janeiro - Brazil
darryl.revok wrote:
From what I read, hBlank gives you 340 PPU cycles. If so, then does that equate to 1020 CPU cycles? (NTSC) That seems excessive.

No, no, you got it all wrong. There are 341 PPU cycles per scanline, 256 of which output pixels, 85 of which are hblank. The PPU is 3 times *faster* than the CPU, so you have to *divide* by 3 to find the corresponding number of CPU cycles, which is 28.3. It's not a lot of time at all, specially if you have rendering enabled, because the last few cycles (20 PPU cycles, I believe) are used to fetch NT and AT data for the next scanline.

Quote:
So from what I'm reading, I have to have a blank scanline to change palettes. That's unfortunate. I'm guessing there's no workaround.

If not an entire scanline, at least some blanking near the sides of the screen, but you'll hardly be able to change many colors at a time that way.

Quote:
So, a black scan line is what's needed for a mid-screen palette swap? That means if I have a strip of black sky, I can use the bottom row of that strip to switch my palette, right?

It's a blank scanline, not black. If black is your background color (color 0), then the scanline will be black, if color 0 is something else, that's the same color the blank scanline will be. Depending on the number of colors you have to change and where the values come from, 1 blank scanline might not be enough. In order to update 3 colors in a palette, you need something like this:

Code:
lda Color1 ;pre-load the first color
ldx Address+1
stx $2006
ldx Address+0 ;preload the low byte of the address
ldy #$00
sty $2001 ;must finish as soon as hblank starts
stx $2006 ;4
sta $2007 ;4
lda Color2 ;3
sta $2007 ;4
lda Color3 ;3
sta $2007 ;4 = 22

That's 22 cycles out of the 28 of hblank, but there's the IRQ latency that depends on the instruction that was running when the IRQ fired, and you'll probably lose a couple of cycles to make sure the $2001 write doesn't happen before hblank starts. So yeah, if the color values come from ZP variables, it does seem possible to update 3 colors right in the first hblank, and then you can use the next scanline to reset the scroll and enable rendering on the next hblank.

Quote:
So, the palette color getting written is drawn on the screen. I wonder if I can use this to my advantage. Is there any way to make it draw a single color across the entire screen? Like, utilizing a palette swap to change the BG color, and using the color written as a line on the image? For the tops of the sky strips that could work.

I don't understand what you want to do. If you want the entire screen to be the same color, why not do it the conventional way? If you just want to draw strips using any of the colors in your palette, then yeah, you can just change the VRAM pointer during hblank whenever you want to show another color. And you can even show the otherwise unrenderable colors at $3f04, $3f08, $3f0c.

Quote:
And what is the trouble with leaving the PPU targeting a color that isn't color 0?

No trouble, that color will simply be rendered on the screen.

Quote:
Make the last write the color you want the next scanline to appear (could even be the same color it already was)

The PPU address auto increments, so it actually points to the next color that will be changed, not to the one you just changed, so what you write last won't matter, but what was already stored in the slot after that.

Quote:
Is there an easy explanation for how to do so carefully? :)

I never quite understood the details, but it has something to do with interrupting the sprite evaluation process. To be sure you'll not do that, I believe you have to turn rendering off as soon as hblank starts, since the latest the sprite evaluation process can extend to is the end of the visible scanline. Don't take my word for it though, it's better to look it up in past discussions.

Quote:
Is there anything other than setting PPU to slave mode that could damage hardware?

I'm not sure, but I don't think it's easy to damage the hardware, considering that a bad connection between the cartridge and the cartridge slot could result in "random" code running and writing unexpected values to registers and such, and that happens somewhat often with the NES.


Top
 Profile  
 
PostPosted: Wed Oct 14, 2015 11:55 pm 
Offline
User avatar

Joined: Sat Jul 25, 2015 1:22 pm
Posts: 501
Quote:
No, no, you got it all wrong.

Okay, that seems a lot more reasonable. I knew that had to be way off...

Quote:
I don't understand what you want to do.

This image is terrible, but this is a quick mockup of the idea. The character moves diagonally across the background.

Attachment:
Mountains.png
Mountains.png [ 10.57 KiB | Viewed 1631 times ]


Being able to use more palettes would increase design options a lot but it sounds like it could be a few headaches. I may be able to find a way to just use it in the sky, changing just one color but otherwise using the same palette for the sky portions. Then I'd have three palettes left for the ground.

So is two the maximum number of colors that can be swapped with a sacrifice of just one scanline?


Top
 Profile  
 
PostPosted: Thu Oct 15, 2015 12:04 am 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10046
Location: Rio de Janeiro - Brazil
darryl.revok wrote:
So is two the maximum number of colors that can be swapped with a sacrifice of just one scanline?

Judging from the sample code I wrote above, it seems like 3 is possible.


Top
 Profile  
 
PostPosted: Thu Oct 15, 2015 12:19 am 
Offline
User avatar

Joined: Sat Jul 25, 2015 1:22 pm
Posts: 501
Cool. Three is a pretty flexible number to work with.

So would it be typical to, after setting colors on one hBlank, set up the next IRQ during the scanline so as to get the proper timing for the following scanline where scroll will be set? I haven't experimented with fancy timing options yet as I just wanted to get the basics working first.

Edit: Okay, I went back and reread this and thought about it a little more. If you leave IRQ and set another one, it's going to fire too late to get the next line, so that would be two lines skipped. After changing palettes, the hBlank timing will be known +/- a couple cycles, so that should be 91.66 cycles to eat up before rendering is enable, if I'm figuring correctly.

Just to make sure I'm doing this right, I currently set the IRQ one line less than where I want to change scroll, then I use that scanline to eat up time before hBlank, then I set scroll and then test to see if another IRQ needs to be made.

Just to get things to work, I did a really simple method at first of just looking at the screen and seeing if my scroll was lining up properly, and adding code to extend it until it did. I realize there are better methods though. I seem to recall reading that Blargg wrote a good one. I downloaded it the other day but have looked into it yet.

Also, another question that I didn't think about yet, is whether or not it's okay to just turn off BG rendering for that scanline, or if sprites must be disabled as well.


Last edited by darryl.revok on Thu Oct 15, 2015 12:39 am, edited 1 time in total.

Top
 Profile  
 
PostPosted: Thu Oct 15, 2015 12:34 am 
Offline

Joined: Sun Apr 13, 2008 11:12 am
Posts: 6273
Location: Seattle
See also the previous threads about this: viewtopic.php?t=12586
And this one: viewtopic.php?p=122763#p122763
Neither cover IRQ latency.

IRQ latency is between 7 and 13 CPU cycles from when it's requested; MMC3 IRQ fires at either dot 260 (when background=$0000 and sprite=$1000, shortly after hblank begins) or 324 (vice versa, shortly before it ends), so your IRQ code will start approximately in the middle-to-end of hblank, giving you no time to set up registers or anything and requiring you to spend an entire scanline waiting to make the writes. At least you should have plenty of time to prepare everything...


Top
 Profile  
 
PostPosted: Thu Oct 15, 2015 3:53 am 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10046
Location: Rio de Janeiro - Brazil
darryl.revok wrote:
Just to get things to work, I did a really simple method at first of just looking at the screen and seeing if my scroll was lining up properly, and adding code to extend it until it did.

This is fine, but make it sure to get it down to the cycle. I say that because the most common method for padding is using NOPs, which take 2 cycles each, so unless you try a 3-cycle instruction (such as CMP $XX) by the end of the NOP chain, you'll can't be sure you found the best alignment. Once you find the exact number of cycles to wait, you can optimize the timed code to take less space.

Quote:
I seem to recall reading that Blargg wrote a good one. I downloaded it the other day but have looked into it yet.

Blargg's level of synchronization is way too extreme for your needs, IMO... I'm not even sure you can easily combine his method with things like a music player or scanline IRQs, which could introduce timing variations. Anyway, you still have enough cycles to prevent the IRQ latency from being an issue, so you should be fine.

Quote:
Also, another question that I didn't think about yet, is whether or not it's okay to just turn off BG rendering for that scanline, or if sprites must be disabled as well.

Both have to be disabled. Disabling one but not the other only prevents the respective pixels from being displayed, but the PPU keeps doing everything it would do if both layers were enabled. It would be really handy if we could use sprites to hide background glitches, or to make the scanlines not completely blank, but unfortunately that's not the case.


Top
 Profile  
 
PostPosted: Thu Oct 15, 2015 12:52 pm 
Offline
User avatar

Joined: Sat Jul 25, 2015 1:22 pm
Posts: 501
Well, the good times with raster effects roll on.

It seems like a mid-screen palette change may not be very practical. If the sprites disappear for a scanline, that's going to cause some serious issues with the way I have this set up. I guess the only practical way to get more colors in the overall background is going to be to reclaim the palettes that are scrolled off-screen vertically.

Also, I might be able to squeeze out the appearance of more colors by playing with color emphasize bits and grayscale.

What I've been working on now is getting my tile fetch to sync with the different scroll rates as well as nametable updates in NMI. It was a pain but I think I got it. I left attributes out of the equation for the moment since I'll have to entirely rewrite that routine to handle 1/2 of an attribute being in one scroll strip.

tokumaru wrote:
Blargg's level of synchronization is way too extreme for your needs, IMO

If I understand correctly, his is to time off of NMI anyway, which would essentially take the place of what I'm doing with the MMC3 scanline counter.

lidnariq wrote:
IRQ latency is between 7 and 13 CPU cycles from when it's requested

I was worried about IRQ latency but I don't THINK it's going to be an issue for what I'm not doing. Since I won't be doing mid-frame palette swaps, I'm not doing anything too intensive on timing. X/Y scroll setting seems to fall into the hBlank fine with my simple NOP array. I will look into optimizing it when everything's done, but no need in doing anything complex if it's not necessary. I might add a mid-screen CHR bankswitch but that shouldn't be too intensive.


Top
 Profile  
 
PostPosted: Thu Oct 15, 2015 1:00 pm 
Offline

Joined: Sun Apr 13, 2008 11:12 am
Posts: 6273
Location: Seattle
If you're still exclusively testing on FCEUX, you should know that FCEUX's renderer is scanline-at-a-time, so any minor timing errors won't show up.


Top
 Profile  
 
PostPosted: Thu Oct 15, 2015 1:47 pm 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10046
Location: Rio de Janeiro - Brazil
darryl.revok wrote:
Also, I might be able to squeeze out the appearance of more colors by playing with color emphasize bits and grayscale.

But those affect sprites too, so they won't help with creating more colorful backgrounds with overlaying sprites.


Top
 Profile  
 
PostPosted: Thu Oct 15, 2015 8:29 pm 
Offline
User avatar

Joined: Mon Jan 03, 2005 10:36 am
Posts: 2961
Location: Tampere, Finland
Note about IRQ latency: If you can't control what code is executing when the IRQ fires, you may be able to chain two IRQs to minimize latency. First generate an IRQ slightly earlier than you need, set up the next IRQ, and start executing a NOP slide. This way you know a NOP was executing when the second IRQ fires, and you won't need a ton of NOPs since the second IRQ happens soon enough. I think I first heard of this trick from the C64 scene, but don't remember it being mentioned on this forum.

(Disclaimer: I never tried using this trick on NES myself, but don't see why it wouldn't work with a PPU-synchronized IRQ source such as MMC3's.)

_________________
Download STREEMERZ for NES from fauxgame.com! — Some other stuff I've done: kkfos.aspekt.fi


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 42 posts ]  Go to page 1, 2, 3  Next

All times are UTC - 7 hours


Who is online

Users browsing this forum: No registered users and 7 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group