How do I use Vblank?

Are you new to 6502, NES, or even programming in general? Post any of your questions here. Remember - the only dumb question is the question that remains unasked.

Moderator: Moderators

tepples
Posts: 22708
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: How do I use Vblank?

Post by tepples »

The NMI vector is at $FFFA. From "NMI" on NESdev Wiki:
When the CPU checks for interrupts and find that the flip-flop is set, it pushes the processor status register and return address on the stack, reads the NMI handler's address from $FFFA-$FFFB, clears the flip-flop, and jumps to this address."
Thus the behavior of the CPU when servicing NMI is similar to the following pseudocode:

Code: Select all

push pc>>8
push pc & #$FF
php
jmp ($FFFA)
Thank you for prompting me to check "CPU interrupts" on NESdev Wiki and discover it was missing information.
User avatar
dougeff
Posts: 3079
Joined: Fri May 08, 2015 7:17 pm

Re: How do I use Vblank?

Post by dougeff »

Ok, let's talk about what these are doing

.base $8000
RESET:

(code here)

NMI:
IRQ:
rti

;Vectors
.org $FFFA
.dw NMI
.dw RESET
.dw IRQ

######################

As the assembler goes line by line, converting ASM into machine code, it is also counting. BASE $8000, tells it to start counting at $8000. Why is it counting? So we can use Labels in our code.

The first thing the assembler sees after BASE $8000 is the label RESET. It assigns the value $8000 to the label RESET. Now, any time the assembler sees the word RESET, it replaces it (in the machine code) with the value $8000 (actually $00, $80, little endian style)

The advantage here, is, you should be able to move code around in your file, but the assembler should still be able to reassign a new value to RESET, if needed. Let's say that you wrote some code between the BASE $8000 and the label RESET. Now when it counts, it might be at $823c by the time it sees the RESET label... and would then replace all uses of that label with $823c.

The lines past .ORG FFFA, are addresses. DW = 2 bytes. The assembler will see the label for NMI, and replace that with the address of the NMI label. But, the important part, is the NMI label can be anywhere. You don't have to calculate its position and insert it at FFFA. The assembler will do that for you, if you put .DW NMI here.
nesdoug.com -- blog/tutorial on programming for the NES
DementedPurple
Posts: 318
Joined: Mon Jan 30, 2017 5:20 pm
Location: Colorado USA

Re: How do I use Vblank?

Post by DementedPurple »

Would it be like in C# where you use the below code

Code: Select all

int main()
{
NMI();
return 0;
}
static void NMI()
{
// put NMI code here
}
But instead of the NMI function you use BASE($8000)? And also, would I just use the command

Code: Select all

bit $FFFA
to test the state of the NMI?
tepples
Posts: 22708
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: How do I use Vblank?

Post by tepples »

$FFFA is a ROM location. Doing bit $FFFA will not read the state of the PPU.

The state of the NMI generator is at $2002, but there's a race condition associated with reading it. Normally you want to make $FFFA point at a routine that modifies a variable in RAM, then make your main program wait for that variable to change.

If you want an analogy to high-level languages, then $FFFA, $FFFC, and $FFFE contain hardcoded function pointers to listeners for NMI, reset, and IRQ events respectively, and the NMI and IRQ listeners run in a separate thread with priority over the main thread.

Code: Select all

volatile unsigned char nmis = 0;
volatile unsigned char display_list_ready = 0;

int main() {
  RegisterNMIListener(NMI);
  
  // Omitted: init code
  while (!gameover) {
    PollControllers();
    GameLogic();
    PrepareDisplayList();
    display_list_ready = 1;
    vsync();
  }
}

static void vsync() {
  unsigned char oldnmis;
  while (oldnmis == nmis) { }
}

static void NMI() {
  nmis += 1;
  if (display_list_ready) {
    BlitOAM();
    BlitVRAM();
    display_list_ready = 0;
  }
}
In 6502 assembly language, you RegisterNMIListener() by putting a function pointer at $FFFA.
DementedPurple
Posts: 318
Joined: Mon Jan 30, 2017 5:20 pm
Location: Colorado USA

Re: How do I use Vblank?

Post by DementedPurple »

What is a function pointer, and second, what does BASE $8000 and labels have to do with testing a bit? can't you just use a bit command? I don't understand why having the reset label be in $8000 would help test the state of the NMI.
User avatar
dougeff
Posts: 3079
Joined: Fri May 08, 2015 7:17 pm

Re: How do I use Vblank?

Post by dougeff »

Base $8000 and Labels have nothing to do with testing a bit. They are instructions for the assembler to generate code with correct addresses.

The memory map of the NES has the program ROM located between $8000 and $ffff. That's why we (have the assembler) start counting bytes from $8000.

The last 6 bytes of the program ROM are special addresses, the starting address of NMI, the starting address of RESET, and the starting address of an IRQ (and BRK).

Writing the code like I did, will ensure that when the ROM is assembled, the last 6 bytes will contain the correct location of these functions.

These addresses POINT to a function. They are function POINTERS.
nesdoug.com -- blog/tutorial on programming for the NES
tepples
Posts: 22708
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: How do I use Vblank?

Post by tepples »

DementedPurple wrote:What is a function pointer
"Function pointer" is the term used in C and C++ for something akin to what C# calls a "delegate".
User avatar
dougeff
Posts: 3079
Joined: Fri May 08, 2015 7:17 pm

Re: How do I use Vblank?

Post by dougeff »

Just to be clear, I was talking about RESET vectors as an example of how to set up a functional ROM, with a functioning NMI.

From tepples example earlier...
  • 1. Make an NMI handler, which is a special kind of subroutine that ends with RTI instead of RTS.

    2. Set the vector at $FFFA to point to the NMI handler. Exactly how this is done depends on the assembler you're using.

    3. Set bit 7 of PPUCTRL ($2000) to 1. If bit 7 of PPUCTRL is 1, the CPU will call the NMI handler at the start of every vblank.
I was explaining part 2. Set the NMI vector to point to the NMI handler. I guess I was also explaining part 1...the NMI handler itself is everything between the label NMI: and the RTI.

To USE the NMI, you do part 3, and the PPU will generate an NMI Interrupt at the start of every v-blank, and your code will AUTOMATICALLY jump to the NMI code at this time (which happens 60 times a second).

EDIT, the NMI code should terminate in RTI. At which point it will return to your regular code.
nesdoug.com -- blog/tutorial on programming for the NES
DementedPurple
Posts: 318
Joined: Mon Jan 30, 2017 5:20 pm
Location: Colorado USA

Re: How do I use Vblank?

Post by DementedPurple »

Okay, so would do the BASE $5000 but instead of $5000 use $FFFA?
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: How do I use Vblank?

Post by tokumaru »

BASE changes the PC instantaneously, while ORG fills the ROM with the fill value up to the specified address. For this reason, BASE has to be used carefully, or you might end up with an invalid ROM. Normally you'll use BASE to switch to a different addressing space (from bank to bank, from PRG to CHR, etc.), but when advancing within the same space you probably want to use ORG.
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: How do I use Vblank?

Post by tokumaru »

Don't mix up assembler commands and the actual program. Things like BASE and ORG are used only by the assembler, to know where to put the code in the ROM and what addresses to use for labels, variables and such. The program running in the CPU doesn't care or know about any of that, it just needs everything to be in the correct places.
DementedPurple
Posts: 318
Joined: Mon Jan 30, 2017 5:20 pm
Location: Colorado USA

Re: How do I use Vblank?

Post by DementedPurple »

So what would I do if I wanted to put my game on an actual NES cartridge?
tepples
Posts: 22708
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: How do I use Vblank?

Post by tepples »

If you mean put it on an NES cartridge for testing, the PowerPak and EverDrive N8 are the most common choices for this. These adapters take digital camera memory (CompactFlash or Secure Digital respectively) and plug into an NES Control Deck.

If you mean put it on an NES cartridge for distribution to the public, you have a few options: DIY kits from RetroStage, preassembled user-flashable carts from Infinite NES Lives, or having RetroUSB or Infinite NES Lives manufacture and sell the things for you.
DementedPurple
Posts: 318
Joined: Mon Jan 30, 2017 5:20 pm
Location: Colorado USA

Re: How do I use Vblank?

Post by DementedPurple »

Would I be able to use the BASE command on an EPROM cartridge? What equivalent would I have?
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: How do I use Vblank?

Post by tokumaru »

What is this thing about BASE? BASE is something you use to structure the ROM correctly. If you already have a working ROM, you don't need anymore BASE statements. If you put anymore of those you'll probably break the ROM's structure. Why are you insisting on that? What exactly are you trying to accomplish with it?
Post Reply