Hello,
I have been playing around with NES programming for the last few weekends and put together a small program to see if I could move a meta sprite with animation. What is troubling me is that sprite 0 is always a few pixels ahead during the run animation. If I jump directly to the MoveSprites routine after left or right is pressed, it looks fine, but when executed in the NMI, sprite 0 is always ahead a little bit. Any suggestions on what might be causing this? I have attached my source below.
Also, feel free to critique my code. I am very new at NES programming and am looking to improve. Any feedback would be greatly appreciated.
Thank you in advance,
Hallie
Not sure where your problem is, specifically, but this is how I recommend structuring your game loop and NMI:
Code:
int frame
int last_frame
bool update_PPU
nmi:
push registers
if (update_PPU)
{
disable rendering // $2001
sprite DMA // $2003/4014
update palette // $2006/2007
update nametables // $2006/2007
set scroll // $2005
enable rendering // $2001
update_PPU = false
}
play sound/music
frame = frame + 1
pop registers
RTI
game_loop:
while (frame == last_frame) {} // wait for NMI to finish
last_frame = frame
read input
update game state
build sprite buffer for sprite DMA
build nametable change buffer
update_PPU = true
JMP game_loop
Your NMI begins in vblank and needs to update the PPU as quickly as possible, finishing before vblank is over. Rendering should be turned off explicitly because if your update runs long, trying to update PPU memory will cause corruption when vblank ends.
This allows your game loop to go overtime (resulting in slowdown) without causing a graphics corruption. Music is run in the NMI so that it won't slow down even if the game does.
Edit: renamed and commented to make things a little clearer.
Did you check all your ADC/SBC's to make sure the C flag is always cleared before adds and set on subtracts?
Also the reason your sprite moves ahead of the rest is that you're calling your two routines "MoveSprites" and "UpdateGraphics" in the wrong order. They do this:
Code:
MoveSprites:
reads player X stored at PLAYER1+3 (sprite 0)
propagates player X to all other sprites
UpdateGraphics:
updates player X stored at PLAYER1+3
So, in your NMI:
Code:
JSR MoveSprites ; updates sprites based on previous frame's PLAYER1+3
JSR UpdateGraphics ; updates PLAYER1+3 to next frame's position
Obviously you want to call
UpdateGraphics first. I'll also recommend moving these two calls outside of your NMI, for the reason I suggested in my previous post.
Thank you for the prompt responses. I *think* I implemented the suggestions that rainwarrior made. I separated the UpdateGraphics to two routines and placed only the portion that actually updates the graphics into the NMI. This fixed my leading sprite issue. I also made a few tweaks to the input handling and placed some BEQs to skip calling routines that aren't needed if no updates need to be made. Code is attached in case anyone wants to review.
Well, the suggestion I was making was to restrict the NMI to only update the PPU. (I've edited my above post to make it a little clearer.) Get that out of the way as efficiently as possible so you don't miss the end of vblank. After this, update your sound, then RTI. This is to protect against graphical problems or music slowdown caused by your game loop running too long.
Of course, if you want to do something more complicated, like wait for a sprite 0 hit for a status bar scroll split at the top, then that should go in the NMI as well, before music playback. If you want to get any more advanced than that, I'll leave that as its own topic.
I'm a little confused as to why you have divided the remaining code into "SetUpdateGraphics", "UpdateGraphics" and "MoveSprites". It seems complicated to me to put just one of these in your NMI. (UpdateGraphics might be running a frame behind the rest?) All of this I think should just be in your game update loop. Your game update loop should do things like:
- check controller input
- run game logic, update the player position, update enemies, etc.
- set up your graphics buffers to be sent to the PPU next NMI (e.g. sprites at $0200-02FF)
On an unrelated note, seems like there's a small bug in ASM6's parser. This...
Code:
STA #PLAYER1, x
...is accepted as valid code (works when extra #'s are added too). You don't need the # and probably shouldn't be using it neither if you want others to read/use your code.
I think that dividing the game logic between the NMI and the main loop might be a bit confusing... You should either do everything in the main loop (and use the NMI just for PPU & APU updates, and maybe raster effects) or everything in the NMI (and have an empty main loop). Everything in the NMI is easier, but it's not possible to have controlled slowdowns (music will slow down and raster effects will glitch if the frame calculations take too long).
Not everyone is going to agree exactly on what should be in your NMI, but mine is setup something like this. I think it is pretty much ideal, though I like to tag on more and more functions to the end of NMI...
Treat the IF-ENDIF and other macros as pseudo code.
Code:
nmi:
if flagset MAIN_Done ; we are ready!
; Main is ready, it doesn't expect CPU flags to be saved
if flagset NMIdoPPU
update_PPU_CTRL
update_PPU_MASK
clrflag NMIdoPPU
endif
if flagset NMIdoVRAM
jsr PPU_Transfer ; update VRAM data
clrflag NMIdoVRAM
endif
if flagset NMIdoOAM
lda #0 ; Do OAM refresh
sta OAM_ADDRESS
lda #$02
sta OAM_DMA
clrflag NMIdoOAM
endif
jsr Do_Always
; NO PPU WRITES after here, scroll will be set
if flagset NMIdoPADS ; Do not clear this flag, keep reading pads until main logic turns off
call Read_Pads
endif
clrflag MAIN_Done ; This also signals NMI is complete
rti
else
; Not ready for NMI!
.ifdef DEBUG_MODE
debugOut "NMI hit, Game Logic not ready!"
.endif
pha ; back up registers (important)
txa
pha
tya
pha
jsr Do_Always
pla ; restore regs and exit
tay
pla
tax
pla
rti
endif
Do_Always:
; call do_sound_engine
lda _ppu_mask
and #MA_BACKVISIBLE ; rendering is on so we need to set scroll, otherwise leave ppu alone
if true
; SET SCROLL
endif
; no PPU writes after here! NO PPU WRITES AFTER HERE
; do every-time 'system' functions here:
inc frame_counter
call do_clock
call do_countdown_timer
; other custom frame stuff
rts
Thanks for the feedback. I've learned a lot and am feeling that I am beginning to wrap my mind around NES code structure logic. It was also great to hear everyone's different views on the topic. My small "game" runs on my Powerpak as intended, so I'm happy. I re-arranged the code a bit and attached below if anyone wanted to view it.
I dunno if ASM6 does this, I'd be surprised if it didn't..but take all those .db statements that are not small and put them in to files, and then just do .incbin's to include the files so the source isn't filled with 1 byte values that you can accidentally change one and screw up everything.
But otherwise, looks okay to me, although I only scrolled through it.
Movax12 mentioned something important that I forgot to put in my suggestion originally.
Your NMI should push the registers A/X/Y to the stack if you're going to modify them in the NMI. The NMI interrupt process and RTI automatically preserve the flags, but they do not preserve the A/X/Y registers. (See Movax12's code for how to do this.)
Since your main loop runs so quickly, it only ever gets interrupted in your wait loop for vblank (which doesn't depend on the state of A/X/Y) but as your game gets more complex it may be harder to guarantee this, and you'll get some really hard to track errors if you aren't preserving those registers.
There is another alternative, which is just to discard the whole game loop (turn it into a single JMP instruction jumping to itself) and put all your "game loop" code at the end of the NMI after turning on rendering (and playing your sound/music). This simplifies things a little (e.g. no need to preserve registers, since A/X/Y aren't being used outside of the NMI interrupt), though it is subject to music slowdown problems and makes IRQ use difficult, since most of the frame is spent inside NMI. (Many games do not use IRQ though, so this can be fine.)
rainwarrior wrote:
There is another alternative, which is just to discard the whole game loop (turn it into a single JMP instruction jumping to itself) and put all your "game loop" code at the end of the NMI after turning on rendering (and playing your sound/music). This simplifies things a little (e.g. no need to preserve registers, since A/X/Y aren't being used outside of the NMI interrupt), though it is subject to music slowdown problems and makes IRQ use difficult, since most of the frame is spent inside NMI. (Many games do not use IRQ though, so this can be fine.)
If one goes down this route, I'd actually do it the other way around: have just "inc counter" in your NMI handler, and have a waitVBlank routine that polls that counter, then put all the logic in the main loop. This way you don't have to worry about another NMI firing in the middle of another NMI.
I do that in all my projects. It's also good because you're not limited to one NMI trying to do a bunch of different game engine state checks to see what to do, you just make as many mini NMI's to do all the tasks needed as needed.
I thought it was impossible for a second NMI to fire before your RTI from your NMI routine...? (As well as impossible for IRQs to fire during NMI.)
Edit: Apparently not. Thanks for the correction, thefox.
Usually people clear them right off the bat. I also do that. I don't think you can really call 2 NMI's unless your program sloppily clears NMI enable regs on the PPU then enables it inside NMI.
rainwarrior wrote:
I thought it was impossible for a second NMI to fire before your RTI from your NMI routine...? (As well as impossible for IRQs to fire during NMI.)
It is possible for a second NMI to fire, because the interrupt inhibit flag set on entry (the same one affected by SEI/CLI) only affects normal (non-NMI) interrupts. Of course the NMI can be explicitly disabled or a flag can be checked to see if the NMI interrupted another one, so what method to use comes down to preference.
Ah, okay, I understand now how the NMI can be retriggered after reading its wiki page a few times (I didn't realize it was edge triggered, or that it asserts for the whole vblank unless disabled or cleared). I've never tried the everything-in-the-NMI approach, myself.
So, if I understand this correctly, if you put your whole game in NMI you should:
1. Clear the NMI flag in $2000 on entry.
2. Read $2002 at least once, to avoid retriggering NMI if you enable NMI via $2000 before vblank is over.
3. Re-enable NMI flag in $2000 directly before RTI.
I see that Super Mario Bros. does this, though it's also careful to maintain a RAM copy of the flags stored at $2000 so that it doesn't accidentally change any of the rendering state flags.
Hi,
I've been playing around the with the PPU registers and updating the nametables a bit inside the NMI to get a better understanding of them all. I came across something that I can't quite understand and I didn't think it required a whole new thread on it so I thought I'd ask here. Simply, I was just trying to update 1 tile on the second name table. I was wondering why if I omit writing to the PPUCTRL that I want the first nametable to display, the program will display the second nametable. If anyone could help me understand the logic behind this behavior, it would be greatly appreciated. I included the code I have in my NMI below.
Code:
NMI:
BIT vBlankFlag ;check if vblank is finished
BPL @NotReady
PHA;
TXA;
PHA; backup registers
TYA;
PHA;
LDA GraphicsUpdate
BEQ @NoGraphicUpdate
LDA #$00 ; disable rendering
STA $2001
;Enable DMA
LDA #$00
STA $2003 ; set the low byte (00) of the RAM address
LDA #$02
STA $4014 ; set the high byte (02) of the RAM address, start the transfer
LDA #$24; *
STA $2006; |
LDA #$56; | EXPERIMENTING WITH UPDATING NAMETABLES
STA $2006; |
LDA #$56; |
STA $2007; *
LDA #%00011110 ; enable sprites, enable background, no clipping on left side
STA $2001
LDA #$00 ;;tell the ppu there is no background scrolling
STA $2005
STA $2005
LDA #%10010000 ; enable NMI, sprites from Pattern Table 0, background from Pattern Table 0
STA $2000
LDA #$00 ;;tell the ppu there is no background scrolling
STA $2005
STA $2005
@NoGraphicUpdate:
INC vBlankFlag ;set the flag to "false", vblank is done
@NotReady:
PLA;
TAY;
PLA; restore registers
TAX;
PLA;
RTI; return from interrupt
That is because one of the bits you write to $2006 affects PPUCTRL. It's hard to explain (search on the wikipedia for loopys doc) but basically, there's a temprary register, where some data of the PPUCtrl,X,Y, etc registers go. When you write $2006, it uses that as it's temporary RAM before it assignes the PPU data pointer, so basically PPUCtrl, Mask, X+Y Scroll all get trashed. That's why you need to write all those at the end of VBlank. Hope you understand this.
ETA: And in the NMI, DO NOT loop check the vblank flag. While that should always be set if you get there from an NMI, if you just jump to your NMI again to wait, it will NOT run correctly and will "drop" frames because of a glitch in the PPU. Turn on NMI via PPUCtrl and use the NMI off that, not the $2002 read loop. Remember to read $2002 at the beginning of VBlank (BIT $2002, LDA $2002 are common) and then remember to keep track of $2006 high/low write counters in your code. That's it though.
^I'm pretty sure the loopy scrolling stuff has to do with writing to $2006 while rendering, not in vblank. It shouldn't trash stuff during vblank as far as I know.
You should have to write to $2000 at least somewhere in your code to set the nametable you are using. (Try writing to $2000 once outside of NMI.)
Code:
BIT vBlankFlag ;check if vblank is finished
BPL @NotReady
PHA;
TXA;
PHA; backup registers
TYA;
PHA;
; ...code...
@NotReady:
PLA;
TAY;
PLA; restore registers
TAX;
PLA;
RTI;
It does look like you are using vblank to test ... if the system is executing NMI code, it means you are in fact in vblank and you should be testing a flag (
semephore) that was set from your main logic that indicates everything is complete.
Your code will probably always be ready for NMI, but if it's not your program is going to crash due to the fact that you always restore regsiters from the stack, but don't save them when the initial test branches. If you push something onto the stack you have to pull it in a logical manner.
BTW you don't need to disable rendering if you are going to finish writing to the PPU before it starts to render the next frame, not a big deal though.
Internally, the PPU has only one address register which it uses to access PPU memory, and this includes all the reading it has to do in order to draw the screen. When you use $2006 to write to the name tables, you are modifying this address, so unless you reset it to point to where you want rendering to start from (commonly referred to as "setting the scroll", which is done through $2000 and $2005, but in special cases also through $2006), it will start rendering from the last name table you accessed during VBlank.
3gengames, it appears he just named the variable "vBlankFlag", he's not actually reading $2002. That flag most likely indicates whether the game logic has finished by the time the NMI hits, so he can skip the video updates in case the game logic is not done yet.
Movax12 wrote:
^I'm pretty sure the loopy scrolling stuff has to do with writing to $2006 while rendering, not in vblank. It shouldn't trash stuff during vblank as far as I know.
I know for a fact what I said is 100% true, it trashes everything in VBlank too as that is exactly how it is made to work. Doesn't matter where the writes occur, LoopyT is used to temporarily store the value before the bits are transferred to LoopyV. LoopyV is used for rendering, though. It also only updates certain bits inside the video register at the end of each scanline, unless you use the 06,05,05,06 write combo to assign the bits you need to and then force it to update all the bits to consequently change the scanline/screen position to anywhere you need it when you need to.
http://wiki.nesdev.com/w/index.php/SkinnyETA: Okay, I dunno what else he is doing, but I just saw those right off the bat when I skimmed it, I honestly rarely read and fully understand others code because it's too hard if I did not write it.
I understand you should reset the scroll at the end of NMI, but is it really necessary to re-write the PPU control/mask? I've been coding with nintendulator and it does not emulate any issue with not re-writing the PPU registers.
Movax12 wrote:
I understand you should reset the scroll at the end of NMI, but is it really necessary to re-write the PPU control/mask? I've been coding with nintendulator and it does not emulate any issue with not re-writing the PPU registers.
Yeah, it is necessary if you write to $2006 during vblank ever for sceen updates. You're just getting lucky because the value you write to the nametable has the correct bits for the nametable you're on I am guessing.
Update nametable address always just so happens to set the right nametable display bits.
Code:
%0000.VH00 0000.0000 ;LoopyT scrolling register. V=Add 240 to Y scroll, H=Add 256 to X scroll.
#%0010.0000 0000.0000 ;$2000 nametable write to update nametable minimum. V=0,H=0
#%0010.0011 1111.1111 ;$2000 nametable write to update nametable maximum. V=0,H=0
#%0010.0100 0000.0000 ;$2400 nametable write to update nametable minimum. V=0,H=1
#%0010.0111 1111.1111 ;$2400 nametable write to update nametable maximum. V=0,H=1
#%0010.1000 0000.0000 ;$2800 nametable write to update nametable minimum. V=1,H=0
#%0010.1011 1111.1111 ;$2800 nametable write to update nametable maximum. V=1,H=0
#%0010.1100 0000.0000 ;$2B00 nametable write to update nametable minimum. V=1,H=1
#%0010.1111 1111.1111 ;$2B00 nametable write to update nametable maximum. V=1,H=1
Seems I can learn something from OP's NMI routine then.. going to do some testing/coding..
Movax12 wrote:
I understand you should reset the scroll at the end of NMI, but is it really necessary to re-write the PPU control/mask? I've been coding with nintendulator and it does not emulate any issue with not re-writing the PPU registers.
To fully set the scroll you do have to write to $2000 in addition to $2005. You can think of the name table selection bits as the 9th bit of the horizontal and vertical scroll values. If you don't select a name table through $2000 every VBlank, the name table that will be rendered will be the last one you wrote to. Even if the last PPU access you made were to pattern tables or palettes, they would affect the scroll too.
tokumaru wrote:
Movax12 wrote:
I understand you should reset the scroll at the end of NMI, but is it really necessary to re-write the PPU control/mask? I've been coding with nintendulator and it does not emulate any issue with not re-writing the PPU registers.
To fully set the scroll you do have to write to $2000 in addition to $2005. You can think of the name table selection bits as the 9th bit of the horizontal and vertical scroll values.
Just remember that since the vertical resolution is only 240 pixels, you need to adjust for that if you want to convert a vertical scroll value (0..479) to an Y scroll value and vertical nametable selection bit (can't use nt_sel_bit = y_scroll>>8 like in the horizontal case). So, in a nutshell, saying it's the 9th bit of scroll isn't technically correct for the vertical scroll, although I remember saying the same in the past, too.
BTW, just a curiosity, this has to be one of the most common questions/problems that newcomers have about NES.
thefox wrote:
Just remember that since the vertical resolution is only 240 pixels, you need to adjust for that if you want to convert a vertical scroll value (0..479) to an Y scroll value and vertical nametable selection bit (can't use nt_sel_bit = y_scroll>>8 like in the horizontal case).
True, the vertical scroll is somewhat more complex. My preferred way to deal with it is to keep 2 separate vertical offsets in RAM and update them in parallel: one is just a regular 16-bit unsigned number, and the other is treated specially to skip that range that isn't used on the NES, either on increments and decrements.
Quote:
BTW, just a curiosity, this has to be one of the most common questions/problems that newcomers have about NES.
To be fair, it's a hard concept to grasp. The fact that loopy's doc is called "The Skinny on NES Scrolling" doesn't help, because people think they have to understand it in order to make their programs scroll, while in fact it talks about a more complex subject, which you don't need to know about in order to implement simple scrolling. In fact, I believe that most of the developers back in the day didn't know the details of the $2005/$2006 trickery.
Thank you for the replies. Everyone's different ideas and suggestions have given me some homework for the weekend to test out. However, I think I understand the PPU a *tad* bit more. Next, I wanted to update a complete nametable next to observe the behavior. I added a simple JSR in the NMI to a UpdateRoom routine that updates the second nametable. The nametable updates fine according the the nametable viewer in FCEUX, but the scroll lines shoot to the top right hand corner which leaves the most of the first nametable not visible. Would someone be so kind to explain why this occurs and what I am doing wrong so I can avoid this in the future? My goal in the next few months is to gain a solid understanding of NES development so I can actually start making a serious attempt at a game without running into too many roadblocks. I've included my NMI and UpdateRoom routines below and attached the complete code below.
Code:
NMI:
PHA;
TXA;
PHA; backup registers
TYA;
PHA;
BIT vBlankFlag ;check if vblank is finished
BPL @NotReady ;N flag is set, still negative
LDA GraphicsUpdate
BEQ @NoGraphicUpdate
LDA #$00 ; disable rendering
STA $2001
;Enable DMA
LDA #$00
STA $2003 ; set the low byte (00) of the RAM address
LDA #$02
STA $4014 ; set the high byte (02) of the RAM address, start the transfer
;LDA #$24; *
;STA $2006; |
;LDA #$56; | EXPERIMENTING WITH UPDATING NAMETABLES
;STA $2006; |
;LDA #$56; |
;STA $2007; *
JSR UpdateRoom ;updates second nametable with background3
LDA #%10010000 ; enable NMI, sprites from Pattern Table 0, background from Pattern Table 0
STA $2000
LDA #%00011110 ; enable sprites, enable background, no clipping on left side
STA $2001
LDA #$00 ;;tell the ppu there is no background scrolling
STA $2005
STA $2005
@NoGraphicUpdate:
INC vBlankFlag ;set the flag to "false", vblank is done
@NotReady:
PLA;
TAY;
PLA; restore registers
TAX;
PLA;
RTI; return from interrupt
Code:
UpdateRoom:
@LoadBackground:
;LDA $2002 ; read PPU status to reset the high/low latch
LDA #$24
STA $2006 ; write the high byte of $2000 address
LDA #$00
STA $2006 ; write the low byte of $2000 address
LDA #<background3
STA BackgroundPointer+4 ; put the low byte of the address of background into pointer
LDA #>background3
STA BackgroundPointer+5 ; put the high byte of the address into pointer
LDX #$00 ; start at pointer + 0
LDY #$00
@OutsideLoop:
@InsideLoop:
LDA (BackgroundPointer+4), y ; copy one background byte from address in pointer plus Y
STA $2007 ; this runs 256 * 4 times
INY ; inside loop counter
CPY #$00
BNE @InsideLoop ; run the inside loop 256 times before continuing down
INC BackgroundPointer+5 ; low byte went 0 to 256, so high byte needs to be changed now
INX
CPX #$04
BNE @OutsideLoop ; run the outside loop 256 times before continuing down
RTS
Updating a full name table takes much longer than the time of VBlank, so it's just not possible to update a full name table from one frame to the next. I debugged your ROM, and it seems that when the name table updates finally finish the PPU is already rendering scanline 128! And by the time rendering is turned on, the scroll is pointing to the top of the name table, which is all cyan, so that's what gets rendered for the remaining of the frame.
Since you are updating the name table every frame (a game would typically update it just once whenever a new screen needed to be loaded), you get the top 128 scanlines blanked out (rendering is disabled) and the rest filled with sky every frame, which is why you can't see anything but cyan.
Most games will update the name tables incrementally, instead of all at once. Just open any game that scrolls in FCEUX, look at the name table viewer and move around a little. All name table updates are incremental, because the time available for sending data to VRAM is very limited. A game that doesn't scroll could switch rendering off, update the new name table all at once, and then enable rendering on, but that would cause the screen to blink briefly, so even games without scrolling could benefit from incremental name table updates.
tokumaru wrote:
Updating a full name table takes much longer than the time of VBlank, so it's just not possible to update a full name table from one frame to the next. I debugged your ROM, and it seems that when the name table updates finally finish the PPU is already rendering scanline 128! And by the time rendering is turned on, the scroll is pointing to the top of the name table, which is all cyan, so that's what gets rendered for the remaining of the frame.
Since you are updating the name table every frame (a game would typically update it just once whenever a new screen needed to be loaded), you get the top 128 scanlines blanked out (rendering is disabled) and the rest filled with sky every frame, which is why you can't see anything but cyan.
Most games will update the name tables incrementally, instead of all at once. Just open any game that scrolls in FCEUX, look at the name table viewer and move around a little. All name table updates are incremental, because the time available for sending data to VRAM is very limited. A game that doesn't scroll could switch rendering off, update the new name table all at once, and then enable rendering on, but that would cause the screen to blink briefly, so even games without scrolling could benefit from incremental name table updates.
Thank you for the clear and concise reply!