This page is a mirror of Tepples' nesdev forum mirror (URL TBD).
Last updated on Oct-18-2019 Download

Tips on best coding practices?

Tips on best coding practices?
by on (#194155)
Hello NESDev community! I'm a newcomer that's interested in playing NES games, but lately I've been wondering how the heck people program for it. So I tried it myself.

I slightly modified one of the Nerdy Nights tutorials for a quick demo of a game I plan to make, called Wal-Rush!. For the curious, it'll involve a flying walrus, and it's actually a port of my Atari 2600 game of the same name, which is itself a port of a project I did on Scratch. The .asm and .chr files are attached if you want to see them, and I'm using NESASM3 for assembly.

My question is, what are some general coding practices I should follow to make programming easier? For instance, I didn't know from those tutorials how to create a full background, so I had to improvise :P , but I probably did so poorly. Also, I'm not sure putting the input handling routines in NMI is the best practice (from browsing the forums, I've seen that that's largely discouraged).

One last thing I'm confused about is that the walrus moves multiple pixels per frame in FCEUX, but on my EverDrive N8, it seems to correctly move 1 pixel per frame. Can I have a fix/explanation?
Re: Tips on best coding practices?
by on (#194159)
If you haven't seen it already, I strongly recommend this excellent article from the wiki on how to handle NMI, what to put where, and best methods for VRAM writes during vblank. It's probably the single best resource for NES-specific "best practice".

That said, a ton of commercial games don't follow these guidelines. :P

https://wiki.nesdev.com/w/index.php/The_frame_and_NMIs
Re: Tips on best coding practices?
by on (#194160)
There are three ways to structure a game loop on NES, all equally valid.

  • All in main (e.g. Thwaite)
    NMI thread increments a variable. Main thread reads input, moves game objects, generates video memory updates and sprite display list, waits for the variable to change, performs the updates, and sets the scroll.
  • All in NMI (e.g. Super Mario Bros.)
    Main thread clears the update buffer once and then spins in an endless JMP loop. NMI thread performs the previous frame's updates if complete, sets the scroll, reads input, moves game objects, and generates video memory updates and sprite display list, and marks the update list as complete.
  • Split (e.g. The Curse of Possum Hollow)
    Main thread reads input, moves game objects, generates video memory updates and sprite display list, marks the update list as complete, and waits for the variable to change. NMI thread performs the updates if complete, changes the variable, and sets the scroll.

The important part is that the input is read only once per game tick. Otherwise, your logic to detect presses and releases might get confused. This can be done either by always reading input right before moving objects or by reading only after having performed updates.

The other important part is that if you get slowdown, raster splits such as that for a status bar might mess up. This is hard to fix with "All in main", but an NMI handler can guarantee setting the scroll even if there are no updates to process.
Re: Tips on best coding practices?
by on (#194165)
I believe the "split" method is the best.

The NMI code should push VRAM updates (if they are ready), push the sprites to OAM, set the scroll, update the music. These things NEED to be done every frame, during v-blank (except music).

The main code can handle the rest.

I think Shiru's neslib has a good example of an NMI code.
Re: Tips on best coding practices?
by on (#194170)
All methods are fine if your game never slows down, but the split method is the best to handle lag frames without glitching out. "All in main" is the worst, because even high priority tasks like music and raster effects get delayed in case of lag frames.
Re: Tips on best coding practices?
by on (#194171)
While I'm re-writing some of my code, how exactly do you define "constants"? For example, if I wanted to refer to $4017 as JOYPAD1, how would I make it so I can do this? I'm trying JOYPAD1 = $4017 at the beginning of my code (after the .ines things, but before the .bank 0), but it's not working.

Also, I'm trying to name certain areas of memory that I access, like the buttons being held at this frame being called ButtonsHeldNow. I'm using ButtonsHeldNow .rs 1 at the same place, but it's not working.
Re: Tips on best coding practices?
by on (#194172)
JWinslow23 wrote:
While I'm re-writing some of my code, how exactly do you define "constants"? For example, if I wanted to refer to $4017 as JOYPAD1, how would I make it so I can do this? I'm trying JOYPAD1 = $4017 at the beginning of my code (after the .ines things, but before the .bank 0), but it's not working.

How is it "not working"? What error message does it produce? Or is the assembled code incorrect?

For the .rs, have you first .rsset to define the start of your RAM segment?
Re: Tips on best coding practices?
by on (#194176)
All of those defined "constants" I mentioned earlier give Unknown Instruction errors.

And no, I did not do .rsset. I did, however, just try .rsset $0000 before my declarations, and the same error still happens.

Keep in mind, I'm a complete beginner, and I didn't even know what an NMI was 5 minutes ago :P
Re: Tips on best coding practices?
by on (#194181)
JWinslow23 wrote:
While I'm re-writing some of my code, how exactly do you define "constants"? For example, if I wanted to refer to $4017 as JOYPAD1, how would I make it so I can do this? I'm trying JOYPAD1 = $4017 at the beginning of my code (after the .ines things, but before the .bank 0), but it's not working.

Also, I'm trying to name certain areas of memory that I access, like the buttons being held at this frame being called ButtonsHeldNow. I'm using ButtonsHeldNow .rs 1 at the same place, but it's not working.

You don't need to do that, just use $4016 or $4017 directly, problem solved ^^

Personally I was against using labelled constants on the wiki but everyone was against me on this one so I had to resign.

Also, the joypad 1 is acessed by $4016, not $4017, which is joypad 2 reading and APU clock timer on writes (joypad 2 is strobed by $4016 just like joypad 1).
Quote:
All methods are fine if your game never slows down, but the split method is the best to handle lag frames without glitching out. "All in main" is the worst, because even high priority tasks like music and raster effects get delayed in case of lag frames.

This pretty much sums everything up.
Re: Tips on best coding practices?
by on (#194186)
Bregalad wrote:
JWinslow23 wrote:
While I'm re-writing some of my code, how exactly do you define "constants"? For example, if I wanted to refer to $4017 as JOYPAD1, how would I make it so I can do this? I'm trying JOYPAD1 = $4017 at the beginning of my code (after the .ines things, but before the .bank 0), but it's not working.

Also, I'm trying to name certain areas of memory that I access, like the buttons being held at this frame being called ButtonsHeldNow. I'm using ButtonsHeldNow .rs 1 at the same place, but it's not working.

You don't need to do that, just use $4016 or $4017 directly, problem solved ^^

I do know I can do that, but I want to have the benefit of a name. Especially for the ButtonsHeldNow thing.
Bregalad wrote:
Also, the joypad 1 is acessed by $4016, not $4017, which is joypad 2 reading and APU clock timer on writes (joypad 2 is strobed by $4016 just like joypad 1).

I know that. I just goofed. :P

Also, here is the code that's erroring upon compilation:

Code:
  .inesprg 1   ; 1x 16KB bank of PRG code
  .ineschr 1   ; 1x 8KB bank of CHR data
  .inesmap 0   ; mapper 0 = NROM, no bank swapping
  .inesmir 1   ; background mirroring (ignore for now)
 
  BUTTON_A        = 1 << 7
  BUTTON_B        = 1 << 6
  BUTTON_SELECT   = 1 << 5
  BUTTON_START    = 1 << 4
  BUTTON_UP       = 1 << 3
  BUTTON_DOWN     = 1 << 2
  BUTTON_LEFT     = 1 << 1
  BUTTON_RIGHT    = 1 << 0
 
  .rsset $0000
  ButtonsHeldLastFrame .rs 1
  ButtonsHeldNow .rs 1


EDIT: Problem solved. I simply had to put colons after all of the names. A bit annoying, but functional.
Re: Tips on best coding practices?
by on (#194191)
If I recall correctly, NESASM has weird quirks about indentation. Like, some of the directives would break if you tabbed them in, or didn't tab it in. I can't remember.

Maybe some small oddity of syntax is throwing you off.

Quote from Kasumi...
"nesasm gives unknown instruction when you don't indent with whitespace characters."

From...
viewtopic.php?f=10&t=7922

Edit: personal opinion. Drop NESASM now and start using asm6 or ca65.
Re: Tips on best coding practices?
by on (#194194)
dougeff wrote:
Quote from Kasumi...
"nesasm gives unknown instruction when you don't indent with whitespace characters."
Any line without a tab will be a label. After the label you can put tab and instruction; you can omit the label and just start with a tab if that line has no label.

NESASM can be confusing to someone who does not know it, although I think it is good. But, use which one you like.
Re: Tips on best coding practices?
by on (#194196)
I guess I'll stick with NESASM, because it'll work. I might consider a different compiler if I run into too many problems, though.

Also, I've updated the code! Now the walrus goes left and right, and falls forever. Also, he has blue eyes.

Image

One thing, though: the button-reading routine is a subroutine, but all the code that reads the buttons and updates everything is in NMI, because I wanna make sure it's being done once per frame. Any ideas on how I can move the bulk of this code inside the main game loop (which is still empty)? I'm thinking I would store flags for getting things done inside the main loop, and use the NMI to actually update the relevant values, but I'm a bit iffy on what exactly needs to be done.
Re: Tips on best coding practices?
by on (#194201)
Absolutely. You can easily store all 8 button states on a single NES controller in a single byte, with each bit indicating wether the button is pressed. I also do comparisons with the previous NMI to see if a button was just pressed to trigger events on button pulses (such as attacking or jumping), and store those in a separate byte.
Re: Tips on best coding practices?
by on (#194202)
JWinslow23 wrote:
all the code that reads the buttons and updates everything is in NMI, because I wanna make sure it's being done once per frame.

IIRC, Nerdy Nights starts out with one of the worst possible​ program structures ever: everything in the NMI handler, with the game logic before PPU updates. This is terrible, because you basically have to do everything (logic + PPU updates) in 20 or so scanlines (while the remaining 242 lines of CPU time go unused) or else the PPU updates will spill into the visible picture and glitch everything. That basically means you only get to use less than 8% of the total CPU time if you use that structure.

It's beyond me why anyone would base a tutorial around such a poor architecture. Everything in main would be a much better choice for beginners, because it's also very simple and allows you to use 100% of the CPU time without complications (the trouble only starts if you need more than 100%).

Quote:
Any ideas on how I can move the bulk of this code inside the main game loop (which is still empty)?

You can easily turn what you have into "everything in main". Move the game logic out of the NMI handler and put it into the main loop. Then put the following "wait for vblank" loop right after the game logic:

Code:
  lda FrameCounter
WaitForVblank:
  cmp FrameCounter
  beq WaitForVblank

This will keep the CPU waiting until the "FrameCounter" variable changes, something that will happen in the NMI handler.

Now you can remove the PPU update code out of the NMI handler and put it right after the wait for vblank. Then comes​ the JMP back to the start of the game logic. Your NMI handler should be empty now, and you can modify it to change that variable I mentioned earlier, signaling that vblank has started:

Code:
NMI:
  inc FrameCounter
  rti

And that's it. Let me know if something about how this works isn't clear.

Quote:
I'm thinking I would store flags for getting things done inside the main loop, and use the NMI to actually update the relevant values, but I'm a bit iffy on what exactly needs to be done.

That would be split method, the most robust one of you do it right. It's slightly more complicated because you really need flags and such in order to have the separate threads communicate with each other, but you may try going that route if you think it's time.
Re: Tips on best coding practices?
by on (#194205)
Nowadays people put controller reads immediately after sprite DMA in order to prevent a glitch related to DPCM sounds. See: https://wiki.nesdev.com/w/index.php/Con ... ng_OAM_DMA

You don't have to understand what those words mean in order to use it. Just copy/paste the code from the wiki into your NMI subroutine.
Re: Tips on best coding practices?
by on (#194206)
pubby wrote:
Nowadays people put controller reads immediately after sprite DMA in order to prevent a glitch related to DPCM sounds. See: https://wiki.nesdev.com/w/index.php/Con ... ng_OAM_DMA

That could present a problem if done in the NMI and the logic in a lag frame samples different controller states. You can easily solve that if you just copy the controller state to a secondary variable to be used for game logic at the beginning of the game loop though.
Re: Tips on best coding practices?
by on (#194207)
pubby wrote:
Nowadays people put controller reads immediately after sprite DMA in order to prevent a glitch related to DPCM sounds. See: https://wiki.nesdev.com/w/index.php/Con ... ng_OAM_DMA

You don't have to understand what those words mean in order to use it. Just copy/paste the code from the wiki into your NMI subroutine.

It's a valid way to prevent the DPCM glitch but it's not always appropriate to read your controller in the NMI. (ilag frames leading to inconsistent state throughout the update, missed button presses, etc.)

Edit: tokumaru already covered it.
Re: Tips on best coding practices?
by on (#194208)
I said you could easily solve the problem using a secondary variable but I didn't really spend much time thinking about all the implications of that. Missed button presses could happen either way I suppose. Is there anything else that could go wrong in that case?
Re: Tips on best coding practices?
by on (#194209)
tokumaru wrote:
pubby wrote:
Nowadays people put controller reads immediately after sprite DMA in order to prevent a glitch related to DPCM sounds.

That could present a problem if done in the NMI and the logic in a lag frame samples different controller states. You can easily solve that if you just copy the controller state to a secondary variable to be used for game logic at the beginning of the game loop though.

Or by reading input only after DMA to OAM. In lag frames, the flag for "display list is ready" won't be set.
Re: Tips on best coding practices?
by on (#194212)
Sumez wrote:
Absolutely. You can easily store all 8 button states on a single NES controller in a single byte, with each bit indicating wether the button is pressed. I also do comparisons with the previous NMI to see if a button was just pressed to trigger events on button pulses (such as attacking or jumping), and store those in a separate byte.

Yes, I do do this.
Code:
ReadJoypad:
  LDA <ButtonsHeldNow
  STA <ButtonsHeldLastFrame ; Store ButtonsHeldNow into ButtonsHeldLastFrame
  LDA #$01
  STA $4016             ; Continuously reload the buttons
  STA <ButtonsHeldNow   ; Initialize ButtonsHeldNow
  LSR A
  STA $4016             ; Stop reloading; ready to read controller
JoypadLoop:
  LDA $4016
  LSR A                 ; Store button state into carry
  ROL <ButtonsHeldNow   ; Rotate carry into ButtonsHeldNow
  BCC JoypadLoop        ; Carry will be 1 once all buttons are read
                        ; So jump back if carry is 0
  RTS                   ; Return

tokumaru wrote:
stuff that does things in places

Did that, and it works. Thanks!
pubby wrote:
Nowadays people put controller reads immediately after sprite DMA in order to prevent a glitch related to DPCM sounds. See: https://wiki.nesdev.com/w/index.php/Con ... ng_OAM_DMA

You don't have to understand what those words mean in order to use it. Just copy/paste the code from the wiki into your NMI subroutine.

As I don't use DPCM sounds (yet; I might possibly have a title scream or something), I'm just not going to bother with that. Besides, my controller routine will only read the first controller (this will be a one player game), and it also stores the last button held, so I'm not sure what that does to the cycles.
Re: Tips on best coding practices?
by on (#194234)
Bump.

Right now, I want to be able to add scrolling to this game. I'm a bit confused by the docs over at the NESDev Wiki, I'm not exactly sure what to do. How do I actually make the background scroll? I'll be scrolling horizontally, and the background repeats, if that helps.
Re: Tips on best coding practices?
by on (#194235)
JWinslow23 wrote:
Bump.

Right now, I want to be able to add scrolling to this game. I'm a bit confused by the docs over at the NESDev Wiki, I'm not exactly sure what to do. How do I actually make the background scroll? I'll be scrolling horizontally, and the background repeats, if that helps.

The scrolling pages on the wiki are currently being reworked, currently they are a bit messed up. They'll be fixed in the near future.

As how to make a repeating BG scroll it's extremely simple, you use the $2000 and $2005 registers to do that.
Re: Tips on best coding practices?
by on (#194237)
Bregalad wrote:
JWinslow23 wrote:
Bump.

Right now, I want to be able to add scrolling to this game. I'm a bit confused by the docs over at the NESDev Wiki, I'm not exactly sure what to do. How do I actually make the background scroll? I'll be scrolling horizontally, and the background repeats, if that helps.

The scrolling pages on the wiki are currently being reworked, currently they are a bit messed up. They'll be fixed in the near future.

As how to make a repeating BG scroll it's extremely simple, you use the $2000 and $2005 registers to do that.

...use them how? That's my question. :P OK, I've made the screen "scroll" horizontally, but it seems that the background has also been shifted up 2 rows (when my ScrollX is always 0). What do I do to remedy this? Here is my code that handles scrolling (with .inesmir 0 as the mode):
Code:
  DEC <ScrollY
  BIT $2002
  LDA <ScrollY
  STA $2005
  LDA #$00
  STA $2005
Re: Tips on best coding practices?
by on (#194238)
You appear to have switched X and Y there... The first $2005 write sets the horizontal scroll (X) while the second write sets the vertical scroll (Y).

Note that depending on the NT mirroring you're using, the total background size will be either 256x480 pixels (horizontal mirroring) or 512x240 pixels (vertical mirroring), meaning that one of the scroll values can't be represented in just 8 bits (8 bits can only go up to 255, but you need to go up to 479 or 511). For this reason, in addition to writing to $2005, you need to update the "9th bit" of the scroll values using register $2000 (the lower 2 bits).

If the background is supposed to be the same 256x480 or 512x240 area over and over, simply changing the scroll registers will do, but if you want to display a larger background, such as a level in SMB, you'll need to progressively update the name tables each vblank in coordination with the scroll changes.
Re: Tips on best coding practices?
by on (#194239)
tokumaru wrote:
You appear to have switched X and Y there... The first $2005 write sets the horizontal scroll (X) while the second write sets the vertical scroll (Y).

Note that depending on the NT mirroring you're using, the total background size will be either 256x480 pixels (horizontal mirroring) or 512x240 pixels (vertical mirroring), meaning that one of the scroll values can't be represented in just 8 bits (8 bits can only go up to 255, but you need to go up to 479 or 511). For this reason, in addition to writing to $2005, you need to update the "9th bit" of the scroll values using register $2000 (the lower 2 bits).

If the background is supposed to be the same 256x480 or 512x240 area over and over, simply changing the scroll registers will do, but if you want to display a larger background, such as a level in SMB, you'll need to progressively update the name tables each vblank in coordination with the scroll changes.

Well, the writes are how they're supposed to be. I gotta be less stupid in variable naming, that should be ScrollX (horizontal scrolling) :P . And the background will simply be repeating, as in my original two games. The problem here is, there seem to be two blank rows of background appearing now that weren't there earlier. I've "fixed" it by setting the scroll y to always equal $F0, but I'm sure there's a better way.

What I have now seems to work for my purposes, but to do this "correctly", how would I change $2000? I'm trying this:
Code:
  LDA <ScrollX
  BEQ NoWrap
  LDA $2000
  EOR #%00000001
  STA $2000
NoWrap:

This is supposed to toggle the X>256 bit of $2000 every time the scroll x is 0, but this is causing the sprite to render incorrectly. How would I fix this?
Re: Tips on best coding practices?
by on (#194240)
JWinslow23 wrote:
The problem here is, there seem to be two blank rows of background appearing now that weren't there earlier. I've "fixed" it by setting the scroll y to always equal $F0, but I'm sure there's a better way.

You should explain your problem better. $f0 is a "negative" vertical scroll value and you should avoid it as it will show attribute table data as name table data. This should make 16 glitchy lines at the top of the screen, although 8 will be hidden by overscan so 8 will show.

If you need to move the screen down by 16 pixels, use the value $e0 instead.
Re: Tips on best coding practices?
by on (#194241)
Bregalad wrote:
JWinslow23 wrote:
The problem here is, there seem to be two blank rows of background appearing now that weren't there earlier. I've "fixed" it by setting the scroll y to always equal $F0, but I'm sure there's a better way.

You should explain your problem better. $f0 is a "negative" vertical scroll value and you should avoid it as it will show attribute table data as name table data. This should make 16 glitchy lines at the top of the screen, although 8 will be hidden by overscan so 8 will show.

If you need to move the screen down by 16 pixels, use the value $e0 instead.

Not working. The screen is blank if I use $E0 for some reason.

The thing is, I'm thinking $00 should work and show the whole screen, but even in NTSC mode, there seem to be blank rows on the bottom that don't show up without the scrolling code. This is confusing to me. :?
Re: Tips on best coding practices?
by on (#194244)
If Y=224 ($E0) blanks the screen, then you probably have the cartridge set to 256x480 (aka vertical arrangement or horizontal mirroring), and it's reading the wrong nametable. Try setting bit 1 of the value you write to $2000, that is, ORing it with the value $02.


EDIT: corrected brain fart
Re: Tips on best coding practices?
by on (#194246)
tepples wrote:
If Y=224 ($E0) blanks the screen, then you probably have the cartridge set to 256x480 (aka vertical arrangement or horizontal mirroring), and it's reading the wrong nametable. Try setting bit 1 of the value you write to $2001, that is, ORing it with the value $02.

$2000 not $2001. That or simply use vertical mirroring / horizontal arrangement.
Re: Tips on best coding practices?
by on (#194249)
I do have vertical mirroring.

Before my game loop, bit 1 of $2001 is set:
Code:
  LDA #%00011110   ; enable sprites, enable background, no clipping on left side
  STA $2001


And even still, the code that toggles the X>256 bit on wraparound isn't working. That, or the background isn't appearing at all and the sprite is glitchy. That depends on what I'm trying.
Re: Tips on best coding practices?
by on (#194250)
I mistakenly wrote $2000 where I should have written $2001. Please try it again with $2000.
Re: Tips on best coding practices?
by on (#194251)
Whoops.

OK, it seems to work now (the $E0 thing), but the X>256 bit toggle still isn't working. I think I'm fine without it, though.
Re: Tips on best coding practices?
by on (#194254)
JWinslow23 wrote:
Code:
  LDA <ScrollX
  BEQ NoWrap
  LDA $2000
  EOR #%00000001
  STA $2000
NoWrap:

You can't load from $2000, as it's a write-only register. If you want to modify the last written value, you must update a copy of it in RAM every time you modify it and load that instead.

Quote:
This is supposed to toggle the X>256 bit of $2000 every time the scroll x is 0

This is fine if you never scroll more than one pixel at a time, but if you do, the zero might be skipped and the NT will not toggle. To be perfectly safe, the correct thing to do is extended the ScrollX variable to 16 bits, and then you can just copy bit 0 of the high byte (i.e. the 9th bit of the 16-bit value) into the value to be written to $2000.

JWinslow23 wrote:
the X>256 bit toggle still isn't working. I think I'm fine without it, though.

If you're indeed using vertical mirroring, this means that the name tables are arranged side by side, forming a 512x240-pixel background, so you definitely need 9 bits for the X scroll. The only way I can see this working without you caring about the 9th bit is if both NTs contain the exact same graphics.
Re: Tips on best coding practices?
by on (#194259)
tokumaru wrote:
JWinslow23 wrote:
Code:
  LDA <ScrollX
  BEQ NoWrap
  LDA $2000
  EOR #%00000001
  STA $2000
NoWrap:

You can't load from $2000, as it's a write-only register. If you want to modify the last written value, you must update a copy of it in RAM every time you modify it and load that instead.

Quote:
This is supposed to toggle the X>256 bit of $2000 every time the scroll x is 0

This is fine if you never scroll more than one pixel at a time, but if you do, the zero might be skipped and the NT will not toggle. To be perfectly safe, the correct thing to do is extended the ScrollX variable to 16 bits, and then you can just copy bit 0 of the high byte (i.e. the 9th bit of the 16-bit value) into the value to be written to $2000.

JWinslow23 wrote:
the X>256 bit toggle still isn't working. I think I'm fine without it, though.

If you're indeed using vertical mirroring, this means that the name tables are arranged side by side, forming a 512x240-pixel background, so you definitely need 9 bits for the X scroll. The only way I can see this working without you caring about the 9th bit is if both NTs contain the exact same graphics.

They would contain the exact same graphics. This is meant to be a background that repeats every 2 tiles horizontally. I will, however, try and use your suggestions to make the ScrollX 16-bit.

EDIT: I did this, and I made some other adjustments to my game, including an actual death check, and updated BG tiles.

My next question is, how do I change the palette of the background? The final game would have the screen flash red while a death animation plays. I'm trying to set the palettes and/or attribute tables exactly like when it was set up at the start of the game, but this isn't working.
Re: Tips on best coding practices?
by on (#194266)
Quote:
They would contain the exact same graphics. This is meant to be a background that repeats every 2 tiles horizontally. I will, however, try and use your suggestions to make the ScrollX 16-bit.

If your BG repeats every 16 pixels, you don't need the scroll counter to be wider than 8 bits, actually it being only 4 bit is enough as you can wrap arround from 15 to 0 and nobody will notice.

Quote:
My next question is, how do I change the palette of the background?

Come on, this is a really basic question, if you reached the point where you have a scrolling BG I'm pretty sure you know how to write the palettes which is one of the first thing to do if you want to display anything other than a grey screen.
Re: Tips on best coding practices?
by on (#194273)
JWinslow23 wrote:
how do I change the palette of the background?

Exactly like you do it the first time (select PPU address $3F00 using register $2006, write 32 bytes through $2007), but you can only do it during vblank, which is the only time you're allowed to access VRAM.

So when the game logic decides that the death animation will begin, instead of immediately accessing the PPU and changing the palette, just set a flag indicating that the palette needs changing, and then, in your vblank handler, you check this flag and act accordingly. Any changes that require the use of $2006/7 must be buffered and signaled by the game logic and executed during vblank.
Re: Tips on best coding practices?
by on (#194274)
We're reaching the point were your questions are best answered something like this:

https://www.google.dk/search?q=site%3Aw ... tte+colors

It seems like you know how to program, and honestly the NES could hardly be any better documented than it already is thanks for the wiki on this very site.
Re: Tips on best coding practices?
by on (#194279)
I'm sorry, guys, but for some reason, the things I'm trying weren't working. I will try them again, and get back to you.
Re: Tips on best coding practices?
by on (#194281)
Alright, for now I have the palette-swap code. It turns out what I was doing wrong, as stupid as it sounds, was not waiting any frames before I changed it back. I did have the right code; it just was being reverted immediately. Leave it to me to not actually check what my code is doing before compiling it :P

I have now attached my code (same .chr), with one more minor problem: for one frame before and after the red flash, the background is shifted with some weird offset before returning to normal the next frame. Is this normal, or is there some way I can avoid this?
Re: Tips on best coding practices?
by on (#194285)
JWinslow23 wrote:
for one frame before and after the red flash, the background is shifted with some weird offset before returning to normal the next frame. Is this normal, or is there some way I can avoid this?

This usually happens when rendering is enabled mid-screen, in which case the solution is to only enable/disable rendering during vblank. There's no reason to turn rendering off to update the palette though, so I'm guessing you're not setting the scroll after updating the palette. Remember, using $2006/7 messes up the scroll, so you should always set the scroll as the last thing in your vblank code.
Re: Tips on best coding practices?
by on (#194286)
You're right, I'm not setting the scroll after the palette swap. I forgot that register messed up the scroll coordinates. I'll fix this, thanks!
Re: Tips on best coding practices?
by on (#194298)
tepples wrote:
I mistakenly wrote $2000 where I should have written $2001. Please try it again with $2000.

This proves that your practice of assigning names to registers does harm - you don't even remember which register is which ^^

@JWinslow23 : Usually your game engine should have a "system" where the game loop can request palettes updates to the VBlank NMI handler by setting a flag, and a shadow palette somewhere in RAM that you update during the game logic. That's not the only way to do this, but it's a very common "standard" way to do it. You can also restrict yourself to have a more generic VRAM update system in your VBlank handler, and have it write to palettes the same way it writes anywhere else in VRAM. It's really up to your tastes.
Re: Tips on best coding practices?
by on (#194299)
The NES doesn't have that many memory mapped registers, so I think using either addresses or names is OK. The Atari 2600, on the other hand, has nearly 70 memory mapped registers (most are in ZP, but even that doesn't really make them any easier to remember!), so there's no way anyone can memorize all that. Ever since I first worked with the 2600, a adopted the habit of naming registers on the NES too, and I think that does result in slightly more readable code.