Help with noob problems in my noob code?

Discuss technical or other issues relating to programming the Nintendo Entertainment System, Famicom, or compatible systems.

Moderator: Moderators

User avatar
MetalSlime
Posts: 186
Joined: Tue Aug 19, 2008 11:01 pm
Location: Japan

Post by MetalSlime »

tepples wrote:
Also, your game logic can prepare data for different types of updates gradually, so that the NMI can use whatever is ready when it fires
Which means you have to learn how to use semaphores so that your VRAM update logic never sees any half-finished buffers.
I just realized that my drawing buffer code doesn't protect against this. Right now if the NMI comes while my main program is adding to the buffer, bad things would definitely happen!

I don't think that will ever happen in my current project, but I can't be sure. I need to fix this right away.
MetalSlime runs away.
User avatar
Bregalad
Posts: 8036
Joined: Fri Nov 12, 2004 2:49 pm
Location: Caen, France

Post by Bregalad »

Well, that's simply not true, it could have as well returned from NMI to the main loop which checks the "nmi flag". There's no upside to doing that though if you need raster timed code, so might as well have everything in NMI. (And there's a small downside: the NMI flag polling loop will result in a more variable delay depending what code is executing when NMI occurs.)
You are right - but forget an important detail : in order for it to work you'll have to design ALL your NMI routine so that everything takes exactly or almost exacly the same number of cycles no matter what. So basically if you don't need to do ALL updates to VRAM, you'll have to kill time, and design your sound code so that it always execute in a constant time, etc... which sounds quite tedious to me (altough it's very possible).

If you do the raster effect in NMI and do the variable lenght stuff (such as music) after it it would make things simpler - but you'll need to code an alternate NMI routine that does the effect (the only time I've ever done this is in my "midscanline" demo where 7 cycle jitter wasn't acceptable).
Useless, lumbering half-wits don't scare us.
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Post by tokumaru »

Bregalad wrote:I'd still recommand doing what Celius says for standard applications, but in some cases if you do crazy raster effects and want to synchronize with NMI (instead of sprite zero hit) you're pretty much forced to do otherwise and have your raster effect in NMI.
SMB, for example, could have avoided the shaky status bar if the NMI, firing at the start of every VBlank, handled everything related to it (scroll, sprite 0 hit), only returning control to the game logic after having displayed it and adjusted the scroll for the gameplay window.

Things that should happen even during lag frames should go in the NMI, it's the only way to make sure they will always happen. But you can't have only the critical stuff in the NMI, because you can't, for example, run the music code and then return control to the main thread to take care of VRAM updates, because a lot of VBlank time would have been wasted with music. So the logical answer is to put all video updates in the NMI as well, before the critical stuff.

Celius' set up allows for this, VRAM updates and critical stuff (music, status bar) go into the "blah code" part. It's important that the code that updates VRAM makes sure a frame is complete and that the values in the buffers are valid though.
tepples
Posts: 22345
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Post by tepples »

Bregalad wrote:(the only time I've ever done this is in my "midscanline" demo where 7 cycle jitter wasn't acceptable).
The 7-cycle jitter will always happen, whether from a loop like this:

Code: Select all

: cmp nmis
  beq :-
or from a single 7-cycle instruction like "rol $0100,x" (which I in fact use in my CHR decompressor).
User avatar
Bregalad
Posts: 8036
Joined: Fri Nov 12, 2004 2:49 pm
Location: Caen, France

Post by Bregalad »

No, inside an interrupt which happen in a cmp/beq loop, there is 3 cycle jitter because both the cmp and the beq takes 3 cycles (and all similar loops, such as lda/bne, lda/beq, cmp/bne, bit/bpl, etc... are the same which is useful). This is what I tried to explain in my latest update of my raster code FAQ.
Useless, lumbering half-wits don't scare us.
tepples
Posts: 22345
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Post by tepples »

But if the NMI occurs anywhere but a tight loop like the cmp/beq loop, it might run into one of these 7-cycle RMW instructions.
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Post by tokumaru »

tepples wrote:The 7-cycle jitter will always happen, whether from a loop like this:

Code: Select all

: cmp nmis
  beq :-
With this code, the jitter will not be as big if the timing-sensitive code is inside the NMI, because the instruction that is executing when the interrupt fires is the only one that matters in this case. If however your NMI just sets a flag (which is how you said your programs work), the whole time this loop takes matters.
or from a single 7-cycle instruction like "rol $0100,x" (which I in fact use in my CHR decompressor).
Only slow instructions like these will cause 7-cycle jitters if the timing-sensitive code is inside the NMI, but that's the worst that can happen because no instruction takes longer than this.
User avatar
bigjt_2
Posts: 82
Joined: Wed Feb 10, 2010 4:00 pm
Location: Indianapolis, IN

Post by bigjt_2 »

Thanks so much guys,

I've learned a lot from this thread and the document metalslime recommended about NMI and Vblank. Just to check that I understand and am doing things a little better, I've placed some new code at the link below where I've placed the logic into a main loop called GameLogic:, separated it from the NMI, and left only the update logic for my moving box sprite within the NMI. I also implemented the technique Celius recommended at the end of the main loop (again, called GameLogic), as well as the better controller reading bunnyboy covers in one of his tutorials.

http://www.mediafire.com/?w2hgoyzd4mm

I know it's unlikely that the small amount of logic code I have in this basic collision test would spill over the frame and get nailed by the next NMI firing, but I can easily see where this would cause problems on more advanced projects and wanted to start teaching myself better habits now. If any of you wish to take a look and let me know what I can improve, please do.
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Post by tokumaru »

bigjt_2 wrote:I've placed the logic into a main loop called GameLogic:, separated it from the NMI, and left only the update logic for my moving box sprite within the NMI.
Technically, updating the positions of the sprites should be part of the game logic too. Ideally, the NMI would just do the sprite DMA and set up the display for the next frame. In the NMI you should only have the bare essentials for updating VRAM.

Maybe you though you had to update the sprite coordinates during Vblank because everyone says that video-related stuff should be done at that time, but modifying the RAM where the sprite data is does not count as a video-update operation. That's just regular RAM, you can mess with it whenever you want. What must be done during Vblank is the sprite DMA, which will copy the data from RAM to the OAM. As long as the DMA is done during Vblank, you can modify the values in the page you set aside for sprites all you want, no restrictions.
I know it's unlikely that the small amount of logic code I have in this basic collision test would spill over the frame and get nailed by the next NMI firing, but I can easily see where this would cause problems on more advanced projects and wanted to start teaching myself better habits now.
Yeah, that's the idea. If you design the structure of your program well, it will give you less headaches in the future.
User avatar
bigjt_2
Posts: 82
Joined: Wed Feb 10, 2010 4:00 pm
Location: Indianapolis, IN

Post by bigjt_2 »

Okay, I see the difference now. So in this instance the graphic update (and the only thing that really needs done in NMI) is the DMA, which is simply transferring my sprites (with their current attributes and coordinates) from my RAM (in this case the $0200 page) to the PPU (through the $4014 write register). And that, of course, is done right after the NMI: label.

I just re-incorporated my UpdateSprites back into the main logic and see it's still working fine. Cool, learned something again. Getting there slowly but surely. Thanks!

So, just as a clue for later, when I get advanced enough to do scrolling and start wanting to shift nametables, does it work kind of like this, where there's a quick graphical update in NMI and the logic to shift into one nametable or another is handled in logic outside of NMI? I'm assuming it is. (I'm not asking for specifics, just a general idea, mostly because I barely understand how scrolling works right now).
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Post by tokumaru »

bigjt_2 wrote:So, just as a clue for later, when I get advanced enough to do scrolling and start wanting to shift nametables, does it work kind of like this, where there's a quick graphical update in NMI and the logic to shift into one nametable or another is handled in logic outside of NMI?
It's similar, but unfortunately Nintendo didn't include something to speed things up (like the DMA does for sprites), so we must do most of it ourselves.

Inside the game logic loop is where all the camera movement will be, and whenever the camera moves enough to show a new part of the level you have to read data from your level map (what format will be used to encode it is up to you) and feed some buffers with the tile and attribute data corresponding to the new part of the level. Then, inside the NMI, you copy that data to VRAM as fast as you can.

It's the same principle: compute all the data inside the game logic loop and in the NMI just upload the computed data to VRAM.

Note that to make a scrolling game you don't have to shift the whole background, as that would take too much time and would be impossible to update in a single VBlank. This is why the hardware scroll exists, to display different parts of the name tables based on the coordinates you give it. This means that each frame you only have to worry about new background elements that enter the game view from the edges of the camera.

Like I always recommend, play a scrolling game (preferably one that doesn't use 1-screen mirroring, because that's hard to see) in an emulator that has name table viewing (FCEUX(D), Nintendulator... even Nesticle!) and watch how the new things that enter the view replace old ones that are no longer visible.
Post Reply