Questions about code details in the Nerdy Nights tutorial

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
Questions about code details in the Nerdy Nights tutorial
by on (#118487)
I'd like to clear up some things in the Nerdy Nights tutorial that I didn't understand.

As an example, I use the code in this file:
www.nespowerpak.com/nesasm/background2.zip


1. There is vblankwait1 to make sure that the PPU is ready. But in the next step, all he does is resetting the RAM values. Before he actually accesses the PPU, he does a second vblankwait. So, is the first one even necessary? And if yes, why?

1.1. The guy who converted the source code to CC65 even included a third vblankwait between the end of LoadPalettes and the start of LoadSprites. Is that necessary?

2. In clrmem, when the sprites are put outside the screen, why are the sprite data ($0200-$02FF) set to value #$FE instead of #$FF?

3. In the initialization, right before the Forever loop, the following line is written:
Code:
  LDA #%00011110   ; enable sprites, enable background, no clipping on left side
  STA $2001

But later, in the PPU clean up section, this is done anyway. So, why is it done right before the infinite loop? Manipulating the values there (like disabling sprites) doesn't seem to have any influence on the program anyway. The only time the manipulation of these values shows any effect is in the PPU cleanup section.

4. In one of the first chapters he said that the initialization is not enough for a real NES, but good enough for an emulator. Does that still apply to this chapter or is the initialization complete now?

5. As far as I see it, the first color in the sprite palette (which is rendered transparent for the sprite anyway) will be the global background color of the screen, is this correct? Why did they choose to do it like that? Why not the first color of the background palette?
Re: Questions about code details in the Nerdy Nights tutoria
by on (#118488)
DRW wrote:
2. In clrmem, when the sprites are put outside the screen, why are the sprite data ($0200-$02FF) set to value #$FE instead of #$FF?

Probably someone afraid that $FF would cause sprites to appear on line 0. (It doesn't.) Any value from $F0 to $FF works.

Quote:
3. In the initialization, right before the Forever loop, [rendering is turned on.] But later, in the PPU clean up section, this is done anyway. So, why is it done right before the infinite loop?

I don't know if this particular example uses a scroll split. But if you are using a scroll split, as in a status bar, you need to have rendering turned on before waiting for the split point. Sprite 0 (common in discrete and MMC1 games) and MMC3 scanline IRQs will not trigger if rendering is off. Turning on rendering first avoids an infinite loop while waiting for a split point that never triggers.

Quote:
5. As far as I see it, the first color in the sprite palette (which is rendered transparent for the sprite anyway) will be the global background color of the screen, is this correct? Why did they choose to do it like that? Why not the first color of the background palette?

PPU $3F00 and $3F10 point to the same actual location inside the PPU. Whatever is written to last will control the backdrop color. And if you just copy in 32 bytes, as a lot of programs do, $3F10 will end up controlling.
Re: Questions about code details in the Nerdy Nights tutoria
by on (#118491)
tepples wrote:
Probably someone afraid that $FF would cause sprites to appear on line 0. (It doesn't.) Any value from $F0 to $FF works.

As does $EF, because of the one scanline delay in sprite rendering.
Re: Questions about code details in the Nerdy Nights tutoria
by on (#118492)
DRW wrote:
1. There is vblankwait1 to make sure that the PPU is ready. But in the next step, all he does is resetting the RAM values. Before he actually accesses the PPU, he does a second vblankwait. So, is the first one even necessary? And if yes, why?

Yes, because you have to wait 2 frames (actually it's only 1 frame, but 1 wait loop does not guarantee a 1 frame delay) before the PPU is ready to be used, and each loop only waits 1 frame. Don't be deceived by the comments, the PPU is NOT ready after the first wait. Notice how the PPU is not used between the loops, only the CPU is used.

Quote:
1.1. The guy who converted the source code to CC65 even included a third vblankwait between the end of LoadPalettes and the start of LoadSprites. Is that necessary?

Maybe. In this thread we discussed how much time is actually necessary. Tests have proven that the PPU needs nearly a frame (~29658 CPU cycles) to be ready, but since you have to account for resets and such, you might need more wait loops to be absolute sure that this much has passed before the PPU is used. In that thread, we concluded that 2 wait loops are fine (you may clear the RAM between them if you wish) as long as you read $2002 first, to make sure the first loop doesn't end prematurely. Without the $2002 read, 3 wait loops would be necessary, which might be the case of the sources you're talking about.

Quote:
2. In clrmem, when the sprites are put outside the screen, why are the sprite data ($0200-$02FF) set to value #$FE instead of #$FF?

Anything between $EF and $FF will be outside the screen, although $EF will still be found by the sprite rendering logic - if you don't want that for some reason, stick to $F0-$FF for hiding sprites.

Quote:
3. In the initialization, right before the Forever loop, the following line is written:
Code:
  LDA #%00011110   ; enable sprites, enable background, no clipping on left side
  STA $2001

But later, in the PPU clean up section, this is done anyway. So, why is it done right before the infinite loop? Manipulating the values there (like disabling sprites) doesn't seem to have any influence on the program anyway. The only time the manipulation of these values shows any effect is in the PPU cleanup section.

That might indeed be redundant. If it's done every frame during VBlank, there's no need to do it before the game loop. Might be a left over from a program that doesn't setup the PPU every frame.

Quote:
4. In one of the first chapters he said that the initialization is not enough for a real NES, but good enough for an emulator. Does that still apply to this chapter or is the initialization complete now?

Except for reading $2002 before the first wait for VBlank, everything appears to be initialized properly.

Quote:
5. As far as I see it, the first color in the sprite palette (which is rendered transparent for the sprite anyway) will be the global background color of the screen, is this correct? Why did they choose to do it like that? Why not the first color of the background palette?

There's some memory mirroring involved. The PPU has enough memory to hold 28 colors: 4 x 4 background colors (yes, it can store different colors in the first slot of each background palette, but only the one in the first palette is actually displayed) and 4 x 3 sprite colors, but the first slot in each sprite palettes is a mirror of the first slot in the corresponding background palette, so if you write the sprites colors last they'll overwrite the mirrored background colors.
Re: Questions about code details in the Nerdy Nights tutoria
by on (#118521)
About 2 and 5:
O.k., I understand that now.


About 1:
tkumaru wrote:
In this thread we discussed how much time is actually necessary. Tests have proven that the PPU needs nearly a frame (~29658 CPU cycles) to be ready, but since you have to account for resets and such, you might need more wait loops to be absolute sure that this much has passed before the PPU is used.

Why is this implemented in the following way?
Do something
waitvblank
Do something
waitvblank
Do something
waitvblank


Why not just:
waitvblank 10 times (should be more than enough)
Do everything

?

tokumaru wrote:
In that thread, we concluded that 2 wait loops are fine (you may clear the RAM between them if you wish) as long as you read $2002 first, to make sure the first loop doesn't end prematurely. Without the $2002 read, 3 wait loops would be necessary, which might be the case of the sources you're talking about.

O.k., I can't really follow you anymore. Sorry, that's all too technical.
If I post my own program code of my sample program based on that tutorial, could you please check if everything is correct, what can be removed and what is wrong?


About 3:
tokumaru wrote:
That might indeed be redundant. If it's done every frame during VBlank, there's no need to do it before the game loop. Might be a left over from a program that doesn't setup the PPU every frame.

Is it even good to do this every frame? How about this: If I write this code right before the forever loop:
Code:
  LDA #%10010000
  STA $2000
  LDA #%00011110
  STA $2001
  LDA #$00
  STA $2005
  STA $2005
and don't do anything as PPU cleanup before returning from the NMI interrupt, wouldn't that be fine as well? Why should I do the above code after every frame if doing it once in the beginning works without problems?


Besides, when is the NMI called? Before the current frame or after the current frame? For example if I don't use the above code before everything else, but only in every NMI call, does that mean the first frame isn't influenced by that code (because the NMI is called after each frame, so the first time the code is called is between the first and the second frame)? Or is the NMI code the preparation for the frame output, meaning that whatever is written in the NMI function is applied to every frame because it comes before the output?


About 4:
tokumaru wrote:
Except for reading $2002 before the first wait for VBlank, everything appears to be initialized properly.

What do you mean? The first time $2002 is read is during the waitvblank function. O.k., technically it is before the first vblank, but how else should he check if the vblank has occured?
So, what should be changed in the code?


Isn't there a program to disassemble NES games? This way we could see the source code of some very simple, yet official Nintendo game, like "Donkey Kong", and see how this one is initialized. Wouldn't that be more effective than discussing how many frames the PPU might need etc.? This way you would have a definite way, demonstrated by the inventors of the NES.
Re: Questions about code details in the Nerdy Nights tutoria
by on (#118522)
1: Because, why make your game inefficient and crap? Wait for the 1st one, do everything that needs done setting up your engines an such, wait 2nd, set up your engine the rest of the way and start rendering video/get ready to, 3rd+ run whatever...loading code, menus, etc. It taks maybe 8-9 frames total, after the 2 waited, to load a screen and get the menu running if you're lucky.

3: It's example code, NN is actually very....not shit, but not great written code, it's average...lots of places for improvement in it.

4: You set the NMI and time everything off that. The NMI shown above will not work 100% of the time, it's only good for booting. NMI program to run the vblank code and run the main engine is how it works, you get to set NMI enable by the PPU register $2000, and start running it like:

(Main process)
-ends, and waits for an flag to change that means NMI has run-
(NMI)
-tells main process NMI has happened.

My main code is:
Code:
Frame is a incrementing counter. Each NMI, it goes up 1.
Main engine:
-vblank code-
-main engine-
  LDA Frame
.WaitForNMI:
  CMP Frame
  BEQ .WaitForNMI
  JMP -to vblank code-

NMI:
  INC Frame
  RTI
Re: Questions about code details in the Nerdy Nights tutoria
by on (#118525)
DRW wrote:
Why is this implemented in the following way?

That's up to the programmer. I like to do the basic initialization stuff (SEI, CLD, stack, etc.), then this:
Code:
   bit $2002
-   bit $2002
   bpl -
-   bit $2002
   bpl -

Then I consider the system "initialized". I choose not to clear the RAM like many tutorials suggest, because IMO that tends to hide bugs that would normally arise from you forgetting to initialize specific variables (which you should always do, you shouldn't expect any variables to start out as 0).

tokumaru wrote:
If I post my own program code of my sample program based on that tutorial, could you please check if everything is correct, what can be removed and what is wrong?

Most of us don't mind reviewing small sections of code, but when someone posts their entire 1 mile long ASM file, that tends to drive people away.

tokumaru wrote:
Is it even good to do this every frame?

For games that change these parameters during gameplay, it makes sense. For example, games that use a status bar might enable/disable sprites at the split point so that they don't appear in front of the status bar, and that's a setting they'll have to reset every frame.

Another reason to have this in the VBlank handler is to make sure that rendering will not be enabled in the middle of the screen, causing a quick but possibly annoying glitched frame before the gameplay starts. Some people let it run every frame even though it's only necessary for the first frame because it's easier that way.

Quote:
How about this: If I write this code right before the forever loop:
Code:
  LDA #%10010000
  STA $2000
  LDA #%00011110
  STA $2001
  LDA #$00
  STA $2005
  STA $2005
and don't do anything as PPU cleanup before returning from the NMI interrupt, wouldn't that be fine as well?

Yes, as long as you wait for VBlank before doing this, otherwise you'll get that glitched frame I talked about above.

Quote:
Besides, when is the NMI called?

As soon as VBlank starts, so you can make use of that time for VRAM/PPU updates.

Quote:
Before the current frame or after the current frame?

I guess it makes sense to say that the NMI happens before the frame, because it's in the NMI that you'll set up everything (background updates, sprites, scroll, etc.) for the following frame.

Quote:
The first time $2002 is read is during the waitvblank function. O.k., technically it is before the first vblank, but how else should he check if the vblank has occured?
So, what should be changed in the code?

The problem with this loop is that if it starts in the middle of VBlank, there will be no waiting at all, the loop will immediately end. Reading $2002 beforehand (outside of the loop) will clear the VBlank flag, making sure that the loop that follows will wait for the NEXT Vblank, regardless of whether it's VBlank now or not.

Quote:
Isn't there a program to disassemble NES games? This way we could see the source code of some very simple, yet official Nintendo game, like "Donkey Kong", and see how this one is initialized.

We have seen how many official games work, and they all differ greatly.

Quote:
Wouldn't that be more effective than discussing how many frames the PPU might need etc.?

We know for a fact how much time is needed for the PPU to be ready, what we discuss is the most effective way of waiting that time. Official games didn't always do things the best possible way. If all you want is to be safe and not bother with the hardware details, just do 3 typical VBlank wait loops in a row and you're good to go.

Quote:
This way you would have a definite way, demonstrated by the inventors of the NES.

Don't assume that programmers back then knew more about the system than we do today. The early games were often programmed while the system was still being developed, so the hardware could still be going through changes, meaning that there could be left over code in these games from when the console was not yet final. Also, from what we know, the programmers weren't given particularly good documentation, and didn't have much time to study the system, because of deadlines and such. I know it may sound surprising, but looking at what official games do isn't always be the best way to learn, because programmers back then were often in a hurry and would go with go through a good deal of trial and error to achieve what they wanted.
Re: Questions about code details in the Nerdy Nights tutoria
by on (#118573)
tokumaru wrote:
Most of us don't mind reviewing small sections of code, but when someone posts their entire 1 mile long ASM file, that tends to drive people away.

Yeah, I understand that of course. I will post the parts that are there for hardware-dependent initialization stuff. Please tell me if anything is missing, anything is unnecessary or whatever remarks you have. For example, is the whole "disable this, disable that" necessary? Isn' t it disabled by default?
All in all, I understood the general things that you all told me. So, if you could tell me what's wrong with the following code (like the waitvblank stuff: Where and how should I read the value again to be sure everything is fine? And with LDA or again with BIT?), that would be great. Passages with question marks are stuff where I don't really get what is done there from a logical perspective or what it is good for.

Code:
.segment "CODE"

Reset:
  SEI                   ; Disable IRQ???
  CLD                   ; Disable decimal mode.
  LDA #$40
  STA $4017             ; Disable APU frame IRQ???
  LDX #$FF
  TXS                   ; Put stack pointer at $FF. Does this reset the stack status?
  LDA #$00
  STA $2000             ; Disable NMI???
  STA $2001             ; Disable rendering???
  STA $4010             ; Disable DMC IRQ???

WaitVBlank:
  BIT $2002
  BPL WaitVBlank

  LDX #$00

ClearRam:               ; Set the RAM at 0, except for the sprites that go outside the screen.
  LDA #$00
  STA $0000, X
  STA $0100, X
  STA $0300, X
  STA $0400, X
  STA $0500, X
  STA $0600, X
  STA $0700, X
  LDA #$F0
  STA $0200, X
  INX
  BNE ClearRam

WaitVBlank2:            ; PPU is ready after this.
  BIT $2002
  BPL WaitVBlank2

; SKIPPED CODE: Load palettes into PPU
; SKIPPED CODE: Load sprites to $0200
; SKIPPED CODE: Load background into PPU
; SKIPPED CODE: Load attributes into PPU

FinalInitialization:
  LDA #%10010000        ; Enable NMI??? Enable sprites from Pattern Table 0, background from Pattern Table 1.
  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

@Loop:
  JMP @Loop             ; Wait until the interrupt puts the program at Nmi.

Nmi:                    ; Jumped to once per frame.

Dma:                    ; DMA for sprite copying.
  LDA #$00
  STA $2003             ; Set low byte ($00) of the RAM address.
  LDA #$02
  STA $4014             ; Set high byte ($02) of the RAM address and start sprite transfer.

; SKIPPED CODE: Read controller
; SKIPPED CODE: Check for each button

; Should the controller be read before or after the sprite copy function?

  RTI                   ; NMI is finished.


.segment "VECTORS"      ; The interrupts.
  .word Nmi
  .word Reset
  .word $0000           ; IRQ isn't used.
Re: Questions about code details in the Nerdy Nights tutoria
by on (#118575)
DRW wrote:
is the whole "disable this, disable that" necessary? Isn' t it disabled by default?

If the program enabled these things, and the player pressed the Reset button on the Control Deck, then these things will remain enabled. The Reset button doesn't do much except modify the stack pointer, disable interrupts, and cause the CPU to JMP ($FFFC). (On the front-loading NES, it also resets the PPU, but not on any top-loading console.)

Quote:
Code:
.segment "CODE"

Reset:
  SEI                   ; Disable IRQ???

IRQ is disabled whenever the CPU is reset. However, disabling them explicitly allows JMP ($FFFC) to work like the reset button.

Quote:
Code:
  LDA #$40
  STA $4017             ; Disable APU frame IRQ???

APU frame IRQ is a feature of the 2A03 that generates a 60 Hz time base even when no PPU is connected. It's rarely used on the NES; it may be used more often in arcade games that use the 2A03 as a sound processor.

Quote:
Code:
  LDX #$FF
  TXS                   ; Put stack pointer at $FF. Does this reset the stack status?

What do you think you mean by "stack status"?

Quote:
Code:
  LDA #$00
  STA $2000             ; Disable NMI???

NMI (non-maskable interrupt) is how the PPU tells the CPU that it has completed rendering one frame. Copying the new sprite display list to OAM takes 520 cycles. After NMI, the CPU has about 2200 cycles to send changes to the background and sprites to the PPU. Disabling NMI allows the CPU to complete the init code without being bothered by these notifications.

Quote:
Code:
  STA $2001             ; Disable rendering???

This blanks the screen so that the CPU can load in a new background.

Quote:
Code:
  STA $4010             ; Disable DMC IRQ???

The 2A03 contains hardware to play back compressed sampled sound. DMC IRQ (delta modulation channel interrupt request) is how it tells the CPU that a sound has finished playing.

Quote:
Code:
FinalInitialization:
  LDA #%10010000        ; Enable NMI??? Enable sprites from Pattern Table 0, background from Pattern Table 1.

Now that most of the init code is finished, the CPU is ready to receive notifications that each frame has finished. When the CPU receives this notification, it calls the NMI handler. Some programs are designed to run all the code inside the NMI handler; others just set a flag stating that the notification has been received.

Quote:
Code:
  LDA #$00              ; Tell the ppu there is no background scrolling???
  STA $2005
  STA $2005

Writes to $2005 set the scroll position of the background. The first sets the horizontal position, and the second the vertical.

Quote:
Code:
; Should the controller be read before or after the sprite copy function?

The first thing the NMI handler should do is write all needed background and sprite changes to the PPU, as there are only 2200 cycles to do so before the next frame begins. After that, the program should start preparing the next frame, and the first thing to do when preparing the next frame is read the controller. So because it belongs to the following frame the controller should be read after the sprite copy.
Re: Questions about code details in the Nerdy Nights tutoria
by on (#118583)
DRW wrote:
Isn' t it disabled by default?

Even if it is, sometimes the hardware goes through small revisions and things change... some mappers for example have shown different power on status across different revisions (MMC1A vs. MMC1B, etc.). The PPU and the CPU have gone through a few revisions too, and although we're not sure whether something relevant changed, it doesn't hut to initialize everything to the specific state we want. Also, like tepples said, there's the reset button to consider.

Quote:
like the waitvblank stuff: Where and how should I read the value again to be sure everything is fine? And with LDA or again with BIT?

BIT or LDA (or also LDX, LDY, CMP, and many other instructions) will work just fine for reading $2002, the only advantage of using BIT is that the contents of A remain untouched. Might not seem like a big deal in the initialization code, but if you ever have to read flags in the middle of the game logic, preserving A might be an advantage, so many people make a habit of using BIT for this.

I don't think you need another $2002 read. The code you have will work fine after power on. On the Famicom and on the top loading NES there could be a situation when those two loops don't wait enough time for the PPU to warm up, but since the PPU isn't reset in that case, it's already warmed up, so it's all good I think.
Quote:
Code:
  LDA #$00              ; Tell the ppu there is no background scrolling???
  STA $2005
  STA $2005

That comment is a bit misleading... Writing to $2005 tells the PPU which part of the name tables you want to be visible. Writing $00 twice means that the top left corner of the name table will be at the top left corner of the screen. It doesn not mean that "there's no scrolling", because on the next NMI you might decide to write another value to $2005 and then there will be background scrolling.

What I mean is that $00 is just one of the possible values you can write to $2005, and it doesn't necessarily mean the absense of scrolling, becouse you could very well write $80, $00 to $2005 and as long as you never touched that register again there would be no scrolling either, you'd just permanently see another part of the name tables.

Scrolling is not something the PPU does automatically, it's the programmer who creates the illusion of scrolling by changing what parts of the name tables are visible a little every frame. If you don't modify anything, there's no scrolling.

Anyway, resetting the scroll is something that should be done every frame, after you're done with all PPU updates. That's because writing to the PPU messes up its internal address register, which is also used for rendering the picture. If you don't reset the scroll, you'll get a messed up picture in the following frame. The reason you're getting away with resetting the scroll just once is because you're not doing any PPU updates (the OAM DMA doesn't count, because sprites go to a different memory that doesn't need the PPU address register) in your NMI, but that's not typical. Most games will often rewrite tiles, change the palette, etc., and all of that requires the scroll to be reset afterwards.

Quote:
Code:
  LDA #$00
  STA $2003             ; Set low byte ($00) of the RAM address.
  LDA #$02
  STA $4014             ; Set high byte ($02) of the RAM address and start sprite transfer.

The comment here is wrong. $2003 doesn't "set the low byte of the RAM address", it sets the destination address for the sprites in the OAM, which is 256 bytes long. Sprites can only be copied from addresses that are multiple of 256 ($0000, $0100, $0200, $0300, $0400, and so on, depending on the value written to $4014), it's not possible to change the low byte.
Re: Questions about code details in the Nerdy Nights tutoria
by on (#118595)
tepples wrote:
IRQ is disabled whenever the CPU is reset. However, disabling them explicitly allows JMP ($FFFC) to work like the reset button.

What do you mean? In how far does JMP working like the reset button and JMP not working like the reset button differ? Isn't JMP just the Assembly equivalent of goto?

tepples wrote:
Quote:
Code:
  LDX #$FF
  TXS                   ; Put stack pointer at $FF. Does this reset the stack status?

What do you think you mean by "stack status"?

What I mean is this:
Sets the stack pointer to #$FF, its original starting address. Every values that were pushed to the stack until now are ignored. The stack behaves as if it's totally empty, so that new variables can be added and the old ones aren't in the way anymore.
That's how I understood it. Instead of popping every remaining stuff from the stack, just set the stack pointer to the starting address.

tepples wrote:
Disabling NMI allows the CPU to complete the init code without being bothered by these notifications.

So, would it be wise to disable NMI before every game level, so that we can load all the level data into memory? And then we enable it again to start the level?

tepples wrote:
Quote:
Code:
  STA $2001             ; Disable rendering???

This blanks the screen so that the CPU can load in a new background.

Same as above: Should we do this before any level?

tepples wrote:
The first thing the NMI handler should do is write all needed background and sprite changes to the PPU, as there are only 2200 cycles to do so before the next frame begins.

What happens when, for some reason, my program takes more time for each frame than usual? Does that mean that the program is simply slowed down (so that for example it only runs at 30 FPS instead of 60 FPS) or does the program immediately jump to the NMI label again, disrupting my program flow because I was just in the middle of the previous NMI call?

tokumaru wrote:
I don't think you need another $2002 read. The code you have will work fine after power on. On the Famicom and on the top loading NES there could be a situation when those two loops don't wait enough time for the PPU to warm up, but since the PPU isn't reset in that case, it's already warmed up, so it's all good I think.

I'd still rather play it safe. So, where do I put another BIT $2002? Right before the first waitvblank function?
Code:
  BIT $2002
WaitVBlank:
  BIT $2002
  BPL WaitVBlank


tokumaru wrote:
Anyway, resetting the scroll is something that should be done every frame, after you're done with all PPU updates.

O.k., I'll do. What about the other stuff?
Code:
  LDA #%10010000
  STA $2000
  LDA #%00011110
  STA $2001

Does this need to be updated at every frame as well or is it enough to do it once in the beginning (and whenever I might want to change these values)?

Apart from the additional reading of $2002 and the relocation of the scrolling data after every frame, are any other changes necessary? (The game is supposed to be a mapper 0, non scrolling game, so anything that refers to specific mappers can be ignored.)
Re: Questions about code details in the Nerdy Nights tutoria
by on (#118611)
For registers, use the Wiki. Heck, read over the wiki now so you understand the registers real meaning sooner. That's one gripe with the nerdy nights is the comments and some of the descriptions of how the registers work is off, or just flat out suck.

http://wiki.nesdev.com/w/index.php/PPU_registers

And when you see assembly code LDX #$FF and TXS when TXS assigned the stack a new value, yes it's pretty certain to change the stacks status, that's what it's supposed to do.
Re: Questions about code details in the Nerdy Nights tutoria
by on (#118614)
DRW wrote:
What do you mean? In how far does JMP working like the reset button and JMP not working like the reset button differ? Isn't JMP just the Assembly equivalent of goto?

Jumping to the "Reset" label will only work like a hardware reset if you fully initialize everything. For example, if you don't disable interrupts in the reset code counting on the fact that IRQs are disabled on power on, and then simulate a reset by JMP'ing to the "Reset" label after a point in the program where you have used IRQs, IRQs will be enabled after the "reset".

Quote:
Every values that were pushed to the stack until now are ignored.

That's not so. They are written to whatever location the stack pointer is pointing to, you just don't know what that location is for sure unless you initialize the SP.

Quote:
That's how I understood it. Instead of popping every remaining stuff from the stack, just set the stack pointer to the starting address.

Yes, you can "empty" (the values will remain in RAM though, nothing will be actually erased until the old values are overwritten) the stack just by resetting the SP.

Quote:
So, would it be wise to disable NMI before every game level, so that we can load all the level data into memory? And then we enable it again to start the level?

Yes.

Quote:
Same as above: Should we do this before any level?

Yes.

Quote:
What happens when, for some reason, my program takes more time for each frame than usual?

That depends on whether you are prepared for that situation, because the NES doesn't handle this automatically. If PPU updates take too long, the screen glitches (you can't write to the PPU and display a picture at the same time).

Quote:
Does that mean that the program is simply slowed down (so that for example it only runs at 30 FPS instead of 60 FPS) or does the program immediately jump to the NMI label again, disrupting my program flow because I was just in the middle of the previous NMI call?

It's not automatically slowed down. It's common practice to have a "FrameReady" flag, which you set whenever the frame calculations are ready. You should check this flag at the beginning of your NMI handler (taking care to not trash any registers that might be in use by a possible unfinished frame) to make sure that the previous frame is done before processing a new frame. If the frame isn't done, you just return from the NMI (some like to at least call the music playback routine here, so that music doesn't slow down), which effectively slows down the gameplay.

tokumaru wrote:
I'd still rather play it safe. So, where do I put another BIT $2002? Right before the first waitvblank function?

Yup.

Quote:
O.k., I'll do. What about the other stuff?

$2000 should be updated every frame as well, because it contains part of the scroll information (the name table selection bits), but $2001 only needs to be changed if you actually need to modify the rendering parameters it affects.

Quote:
Apart from the additional reading of $2002 and the relocation of the scrolling data after every frame, are any other changes necessary? (The game is supposed to be a mapper 0, non scrolling game, so anything that refers to specific mappers can be ignored.)

As far as I can see, everything is fine.
Re: Questions about code details in the Nerdy Nights tutoria
by on (#118618)
DRW wrote:
What happens when, for some reason, my program takes more time for each frame than usual? Does that mean that the program is simply slowed down (so that for example it only runs at 30 FPS instead of 60 FPS) or does the program immediately jump to the NMI label again, disrupting my program flow because I was just in the middle of the previous NMI call?
Your code has approximately 2200 cycles to tell the PPU all the changes to make from the previous time to drew anything. If you haven't finished my then, the display will be incorrect.
For example, if you haven't written the correct value to 2005/2006 to specify where the viewport is before this, it will start rendering at the wrong place. If you subsequently read or write from 2007, the display will warp. If you haven't finished uploading sprites, they may all disappear. Even writes to 2000 and 2001 require care, either in timing or the value written.
Your code then has another 27000 cycles to do whatever it wants—physics, controller input, play music, preparing for the next screen, &c.
Re: Questions about code details in the Nerdy Nights tutoria
by on (#118690)
3gengames wrote:
http://wiki.nesdev.com/w/index.php/PPU_registers

Thanks. Pretty interesting stuff.

tokumaru wrote:
It's common practice to have a "FrameReady" flag, which you set whenever the frame calculations are ready. You should check this flag at the beginning of your NMI handler (taking care to not trash any registers that might be in use by a possible unfinished frame) to make sure that the previous frame is done before processing a new frame. If the frame isn't done, you just return from the NMI (some like to at least call the music playback routine here, so that music doesn't slow down), which effectively slows down the gameplay.

What I don't understand here: Let's say my game logic is implemented inside NMI and it takes too long. So, the next NMI interrupt is called. Does that mean that there are now basically two instances of NMI running in parallel? One where I'm right inside my game logic and one that just started? How is this possible?

Let's show it with pseudo code (I use a C-like syntax for better understanding):
Code:
void Nmi()
{
    WriteToPpu();
    ReadController();
    GameLogicStep1();
    GameLogicStep2();
    GameLogicStep3();
    GameLogicStep4();
    GameLogicStep5();
    GameLogicStep6();
    GameLogicStep7();
    GameLogicStep8();
    GameLogicStep9();
    GameLogicStep10();
}


Now, let's say we're in GameLogicStep5 and it's time for the next NMI. Originally, I thought that the program flow simply jumps from GameLogicStep5 to the function entry, leaving GameLogic6-10 unexecuted for this time.
But if you say that the NMI should set a flag, does this mean that two "instances" of NMI are able to execute simulatenously?
Code:
void Nmi()
{
    if (NextNmiCanBeProcessed)
    {
        NextNmiCanBeProcessed = false;

        WriteToPpu();
        ReadController();
        GameLogicStep1();
        GameLogicStep2();
        GameLogicStep3();
        GameLogicStep4();
        GameLogicStep5();
        GameLogicStep6();
        GameLogicStep7();
        GameLogicStep8();
        GameLogicStep9();
        GameLogicStep10();

        NextNmiCanBeProcessed = true;
    }
}

In this case, the NMI is at GameLogic5. Suddenly, the next NMI happens. While the first NMI is now at GameLogic6, the second NMI is at the if check. While the first NMI is at GameLogic7, the second NMI is at the "don't jump into the conditional code" area. While the first NMI is then at GameLogic8, the second NMI is at the "leave function" location.

Is this how the NMI interrupts are implemented? Because this looks almost like some kind of multithreading or multitasking.

If it is not as I described and the NMI simply rips the "program pointer" from its current location and places it at the start of NMI, then how does a flag guarantee that the current NMI just quits itself and the program continues from the right location where it has been before the new NMI jumped in?


Another question about reading the $2002 variable: The tutorial uses LDA $2002 before copying background, palette and attribute data to the PPU. Is this necessary even though waitvblank is not included here? And would BIT $2002 serve the same purpose in this case again?
Re: Questions about code details in the Nerdy Nights tutoria
by on (#118697)
DRW wrote:
What I don't understand here: Let's say my game logic is implemented inside NMI and it takes too long. So, the next NMI interrupt is called. Does that mean that there are now basically two instances of NMI running in parallel? One where I'm right inside my game logic and one that just started? How is this possible?
There is no parallel processing. When NMI occurs, it "pauses" whatever is currently executing. At the end of your NMI handler, you use the RTI instruction to "unpause". This means if another NMI happens while the first one is executing, the new NMI pauses the old NMI.

In C-like syntax:
Code:
void Nmi()
{
    if (NextNmiCanBeProcessed)
    {
        NextNmiCanBeProcessed = false;

        WriteToPpu();
        ReadController();
        GameLogic();

        NextNmiCanBeProcessed = true;
    }
}

When NMI is triggered and there is not another NMI handler currently running, it will write to the PPU, read the controllers, and begin executing the game logic. If another NMI occurs before the first one finishes, the first NMI handler will be paused. But, NextNmiCanBeProcessed will be false, causing the second NMI handler to exit without doing anything. Once the second NMI handler ends, the first NMI handler will continue from exactly where it was paused. (And once the first NMI handler ends, whatever it interrupted will resume.)

It's important to remember that this "pause" analogy is not perfect. The three registers (A, X, and Y) are not saved, so if either NMI routine modifies them without restoring their original values, the "paused" code will not resume properly. The flags are saved, so if you use the BIT instruction to decide if the NMI should run or not, you don't need to worry about accidentally messing up the "paused" code.
Re: Questions about code details in the Nerdy Nights tutoria
by on (#118698)
DRW wrote:
What I don't understand here: Let's say my game logic is implemented inside NMI and it takes too long. So, the next NMI interrupt is called. Does that mean that there are now basically two instances of NMI running in parallel?

I must confess I don't remember whether an NMI will interrupt another NMI... It's a possibility, but I'm not sure it will happen. Hopefully someone will know for sure.

Quote:
One where I'm right inside my game logic and one that just started? How is this possible?

If it does happen, it's like all other interrupts: the status flags and the program counter are pushed into the stack, and the NMI code is called. The problem is that all sorts of things can go wrong in this case, since the status of your game is inconsistent because it the previous frame calculations didn't finish, and as more and more NMIs interrupt the previous ones, the stack will eventually overflow.

Quote:
Now, let's say we're in GameLogicStep5 and it's time for the next NMI. Originally, I thought that the program flow simply jumps from GameLogicStep5 to the function entry, leaving GameLogic6-10 unexecuted for this time.
But if you say that the NMI should set a flag, does this mean that two "instances" of NMI are able to execute simulatenously?

The CPU can only run one program at a time, so they don't run simultaneously. The first NMI is interrupted (it can be resumed once the one that interrupted it returns with RTI) and a new one begins.

Quote:
In this case, the NMI is at GameLogic5. Suddenly, the next NMI happens. While the first NMI is now at GameLogic6, the second NMI is at the if check. While the first NMI is at GameLogic7, the second NMI is at the "don't jump into the conditional code" area. While the first NMI is then at GameLogic8, the second NMI is at the "leave function" location.

No. The first NMI will stop at GameLogic5, and only after the second NMI "leaves the function" GameLogic5 and the rest of the first NMI will continue.

Quote:
If it is not as I described and the NMI simply rips the "program pointer" from its current location and places it at the start of NMI, then how does a flag guarantee that the current NMI just quits itself and the program continues from the right location where it has been before the new NMI jumped in?

A return address is ALWAYS pushed into the stack. So when an interrupt interrupts another interrupt, you can safely return to the code that was interrupted as long as you don't trash registers or the interrupts pile up to the point of overflowing the stack. But the best thing is to avoid the situation altogether with a flag, which also gives you control of what to do and what not to do (like always running the music code so the music doesn't slow down).

Quote:
The tutorial uses LDA $2002 before copying background, palette and attribute data to the PPU. Is this necessary even though waitvblank is not included here? And would BIT $2002 serve the same purpose in this case again?

It's not the same purpose. When used before the wait for VBlank loop the purpose is to clear the VBlank flag, so that the wait loop doesn't pick up the middle of a VBlank. When $2002 is read before access to $2006, the purpose is to clear the latch that selects between high and low bytes of $2005/$2006. This is generally unnecessary with well programmed code, because $2005/$2006 are always written in pairs, but some people like to put that $2002 read just to be safe.
Re: Questions about code details in the Nerdy Nights tutoria
by on (#118705)
tokumaru wrote:
I must confess I don't remember whether an NMI will interrupt another NMI... It's a possibility, but I'm not sure it will happen. Hopefully someone will know for sure.
NMIs, being not maskable, can't be masked by their own execution. Thus the only way to prevent reentry is to either disable the source (by writing to $2000) or to use a flag as this thread is recommending.

Blocking NMIs by writing to $2000 has its own problems, however.
Re: Questions about code details in the Nerdy Nights tutoria
by on (#118706)
lidnariq wrote:
NMIs, being not maskable, can't be masked by their own execution.

Cool, good to get confirmation on that.

Quote:

Yeah, and it's less versatile too, since you don't have the option to run high priority tasks (such as playing music or drawing a status bar) even during lag frames.
Re: Questions about code details in the Nerdy Nights tutoria
by on (#118843)
Alright, the NMI re-entry I understand now. I've written a notice in my todo list to include a check.