For the game I'm designing I need to use the Vblank bit, but I don't exactly understand how I'm supposed to access it. Is it a memory address? I don't know.
Vertical blanking (vblank) is the period between frames, during which your program can write to video memory through the PPU. It lasts about 2,270 cycles, after which point the rendering logic takes control of video memory again for the next frame. To be notified that vblank has occurred, you need to do three things:
- Make an NMI handler, which is a special kind of subroutine that ends with RTI instead of RTS.
- Set the vector at $FFFA to point to the NMI handler. Exactly how this is done depends on the assembler you're using.
- 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.
The NMI handler usually sets a variable in memory notifying the main program that vblank has occurred. There are two schools of thought on how to organize a game loop:
- The main program waits for the variable to change and then proceeds to update video memory.
- The main program sets a variable that the updates have been prepared in video memory, the NMI handler does the video memory updates and clears the ready variable, and then the main program waits for the variable to change before beginning the next frame of game logic. This method resembles multithreading, and it's more stable if you're using a raster split such as a status bar.
Which assembler are you using? Is it NESASM, asm6, ca65, or something else?
I don't know, none of the assemblers I've ran into ever seem to work.
What was the problem when you tried to use these assemblers?
In a previous thread, it seemed like you were trying to double-click on the assembler executables. If that's what's happening, you need to learn all about the command-line and how to use command-line applications. Read this -
http://www.makeuseof.com/tag/a-beginner ... mand-line/ Once you're done with that, you need to "cd" to the directory that has the assembler and run a command such as:
Code:
nesasm3 code.asm
Where "code.asm" is a source file that contains your assembly code.
These steps cannot be skipped. Otherwise, you'll never get anywhere, let alone close to using vblank.
DementedPurple wrote:
I don't know, none of the assemblers I've ran into ever seem to work.
Well, they do work, because many people are making games with them... But they're not magical, you have to know what you're doing.
Anyway, don't ever use the vblank bit ($2002.7) to control the frame rate of your games. The PPU has a bug that causes a false negative to be returned in that bit if the register happens to be read at the exact time vblank starts, causing the program to constantly miss vblanks. The reliable way to detect the start of the vertical blank is with NMIs. The NMI is an interrupt that reliably fires every time vblank starts, letting you know it's safe to perform video updates for the next frame.
Okay then, so how would I use the NMI?
tepples explained one way to do it.
Make sure you understand the basics.
Nerdy Nights covers most of it pretty good, although it's not perfect:
http://nintendoage.com/forum/messagevie ... eadid=7155
Doesn't Nerdy Nights use the less-than-ideal "everything in the NMI" model?
Yep, so it's not perfect (and it also has a few other flaws). But I mean it teaches other things, like working the assembler, I think.
Nerdy nights isn't perfect but there isn't a lot of other choices.
I can't find a tutorial anywhere that mentions the address of NMI
It's like the reset address: you set it in the last six bytes of your game.
What assembler are you using?
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:
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.
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.
Would it be like in C# where you use the below code
Code:
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:
bit $FFFA
to test the state of the NMI?
$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:
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.
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.
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.
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".
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.
Okay, so would do the BASE $5000 but instead of $5000 use $FFFA?
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.
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.
So what would I do if I wanted to put my game on an actual NES cartridge?
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.
Would I be able to use the BASE command on an EPROM cartridge? What equivalent would I have?
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?
Sorry, I thought the BASE statement was something like a .db statement that wouldn't work on the actual hardware. If I were to write the binary file to an actual EPROM, would the NMI code appear in that section?
.db reserves space in memory (RAM or ROM, depending on usage and the assembler), and it works just fine on hardware.
Generally speaking, anything in a compiled .nes file that runs in emulator will work the same on hardware when burned on an EPROM. There are tons of exceptions (buggy emulators, startup state, improper mapper usage, lua scripting), but generally speaking the features are the same, especially when talking about how assemblers work.
NMI code will appear in the NES hardware's address space the same way it will in an emulator, regardless of whether you use .BANK for linker relocation, or .SEGMENT directives, or whatever.
The .nes file you run on emulators contains the exact same binary data you burn on EPROMs to run on real hardware, you don't need 2 separate builds when coding the program. To burn EPROMs you just need to take the .nes file and use a tool or an hex editor to split it into header, PRG-ROM and CHR-ROM, and then burn the PRG and CHR to different chips.
Then how would the actual NES know where to go when the NMI is true?
Tepples mentioned this earlier. The address at $FFFA contains the NMI "vector", which is just an address that points to the NMI function. So for example, if your NMI function gets compiled such that it is in the ROM at $8123, then the value at $FFFA is $23, and the value at $FFFB is $81 (little-endian order swaps the bytes of the address). You maybe were confused and thought that the vectors were "metadata", like the 16 byte header at the start of the ROM, but they're not. They exist in the binary even when on hardware.
If the NMI vector is at $FFFA, would that mean I'm limited to only 4 bytes of NMI code?
The vector $FFFA can point anywhere in ROM or RAM.
The vast majority of
Super Mario Bros., for instance, is its NMI handler. That's because it's structured roughly like the following pseudocode:
Code:
main() {
setup_hardware();
init_globals();
enable_nmi();
for (;;) { }
}
__interrupt__ void nmi() {
PUSH_ALL;
if (vram_data_ready) {
copy_data_to_vram();
vram_data_ready = 0;
}
read_controllers();
game_logic();
prepare_data_for_vram();
vram_data_ready = 1;
POP_ALL;
}
Nothing at fffa but an address.
When the processor gets an Interrupt signal, it will jump somewhere. Fffa tells it where to jump.
DementedPurple wrote:
If the NMI vector is at $FFFA, would that mean I'm limited to only 4 bytes of NMI code?
Think of it as a function pointer.
DementedPurple wrote:
What is a function pointer
DementedPurple wrote:
If the NMI vector is at $FFFA, would that mean I'm limited to only 4 bytes of NMI code?
No the interrupt vectors just points to your interrupt handlers. They don't contain any code, just addresses (addresses are 2 byte each). The interrupt handlers contains the code.
The templates you are using looks something like this:
Code:
;iNES header here
.org $8000
RESET:
;init code here
main:
;main loop code here
jmp main
NMI:
;nmi code here
rti
IRQ:
;irq code here
rti
;Interrupt vectors:
.org $FFFA
.dw NMI
.dw RESET
.dw IRQ
You use labels like "RESET" so you don't have to hard-code the addresses for your interrupt handlers. Of course when the ROM is assembled the vectors will contain constant addresses, but that is the assemblers job to solve. Instructions like .org and .base is for the assembler to know where in the output file everything should be placed. The reason we start at $8000 (or $C000 if ROM is small) is because $8000 is where the ROM area starts in the address space. If we would use .org $0000 as start address, the interrupt vectors (if we don't change their start addresses) would be in the wrong place and the assembled ROM would become too big. It makes more sense to start counting from a number that matches the start address in the ROM area in the address space.
The order of the three interrupt handlers (RESET, NMI and IRQ) doesn't matter, they can be anywhere in the ROM (or RAM as Tepples said). Just make sure that NMI and IRQ handlers ends with an RTI instruction, and the RESET handler ends with an endless loop.
The order of the interrupt vectors DO matter however.
Think of it as the NES doing something similar to
Code:
JSR $FFFA
every time there is an NMI. That's not literally what's going on, but the point is that $FFFA stores the
address of your NMI routine, and the 6502 reads that memory to find where to jump. We say that $FFFA
points to a location in memory.
So it goes to the NMI code automatically, no matter what the CPU is doing. So if I where to have a loop like the one down below:
Code:
example:
jmp example
would it still go to the NMI? And also, does the NROM template have that built in? Also, could I just do BASE $FFFA before the header?
DementedPurple wrote:
would it still go to the NMI?
Yes, as long as NMI generation is enabled in the PPU (bit 7 of register $2000), which you normally do at the end of the initialization code.
Quote:
And also, does the NROM template have that built in?
All mappers behave the same way in regard to NMIs.
Quote:
Also, could I just do BASE $FFFA before the header?
Again, what's with this BASE stuff, man? No more BASE, forget about BASE. Specially before the header, that makes no sense whatsoever. Just out of curiosity, what would you be trying to accomplish with a BASE before the header anyway?
I don't know, I think that a BASE command is like a header, I honestly have no idea. How would the NES know where $FFFA is?
DementedPurple wrote:
I don't know, I think that a BASE command is like a header, I honestly have no idea. How would the NES know where $FFFA is?
The 6502 processor, as well as the derivative 2A03 processor in the NES, is hard-coded to look to $FFFA, $FFFC, and $FFFE for the three vectors. When it boots up (reset state), it immediately does a JMP ($FFFC) effectively, and that's how your program begins. Whatever you put in $FFFC tells it where to start execution.
I don't know what BASE is - it may be related to the assembler you're using, or whatever - but there's nothing linking it to 6502 programming intrinsically.
OK, let's look at what happens in my simple NROM example:
First we have the variables. To set their address we use ENUM, which is an ASM6 command that temporarily sets the program counter without generating any output for anything inside the ENUM block. This is great for variables because they live in RAM, and an .NES file only contains ROM, so the goal is to define the addresses of all variables, without storing anything anywhere.
Then comes the header. The header must be written to the output file, but since it's never mapped to any address space (it's only used to give the emulator or Flash cart information about what to do with the rest of the file), we don't even need to set an address, just output the header bytes.
Then the PRG-ROM starts. To let the assembler know where in the CPU space the code that follows will be mapped to (so it can calculate addresses for labels and stuff), we use BASE, another ASM6 command. BASE simply sets the program counter to whatever value we want. Since we know that PRG-ROM on the NES starts at $8000 (or $C000 if you only have 16KB of it), we use BASE $8000.
Then comes all of the code, and normally we don't need to do any address manipulation in this part. We just write the code and let the assembler update the program counter accordingly.
Now we need to write the interrupt vectors to the output file. Those must be at $FFFA, always, because that's where the CPU will look for them. So now we use ORG $FFFA (ORG is more universal, and works mostly the same in any assembler). Why ORG and not BASE? Because ORG pads the ROM with zeroes (or another value of our choice) up until the specified address. We need that because we don't know how big our code is, so we need this padding to make the ROM size correct. BASE doesn't pad anything, so if you had only 6KB of code and used BASE, your PRG-ROM would end up being 6KB large, instead of the expected 16 or 32KB.
For NROM we don't really need to use BASE... that command is only necessary when we have to roll back the program counter, which normally only happens when we use bankswitching. I only used BASE in the NROM example for consistency with the other templates, that have multiple banks. For NROM ORG would have worked fine, because ORG behaves exactly like BASE if the program counter hasn't been set yet.
DementedPurple wrote:
So it goes to the NMI code automatically, no matter what the CPU is doing. So if I where to have a loop like the one down below:
Code:
example:
jmp example
would it still go to the NMI?
Sounds like maybe you are not fully familiar with interrupts yet? When an interrupt happens, the program counter is interrupted in the code, no matter where it currently is, and then it jumps to the interrupt handler (the NMI handler in this case). It will then run the code from the start of the interrupt handler until it runs into an RTI instruction. When it does, it will jump back to wherever it was before the interrupt happened.
Quote:
does the NROM template have that built in?
As long as the interrupt vectors are in the correct place the CPU will be able to find the address for the three interrupt handlers, so yes it does have this built in. The three vectors are a minimal requirement for a 6502 program to work at all I think, it's not NES-specific.
DementedPurple wrote:
So it goes to the NMI code automatically, no matter what the CPU is doing. So if I where to have a loop like the one down below:
Code:
example:
jmp example
would it still go to the NMI?
Yes.
Super Mario Bros. does exactly this. It sets up the hardware and game state and falls into an infinite JMP loop, and then the entire game runs inside of the NMI handler.
DementedPurple wrote:
And also, does the NROM template have that built in? Also, could I just do BASE $FFFA before the header?
As far as I can tell, BASE isn't very useful unless you're trying to copy code to RAM, such as for self-modification or to keep code available no matter which 32K bank is switched in.
mikejmoffitt wrote:
When it boots up (reset state), it immediately does a JMP $FFFC effectively, and that's how your program begins. Whatever you put in $FFFC tells it where to start execution.
I thought it was
JMP ($FFFC), which reads the program counter from $FFFC instead of actually looking for an opcode at $FFFC.
tepples wrote:
mikejmoffitt wrote:
When it boots up (reset state), it immediately does a JMP $FFFC effectively, and that's how your program begins. Whatever you put in $FFFC tells it where to start execution.
I thought it was JMP ($FFFC), which reads the program counter from $FFFC instead of actually looking for an opcode at $FFFC.
As far as I can tell, BASE isn't very useful unless you're trying to copy code to RAM, such as for self-modification or to keep code available no matter which 32K bank is switched in.
You are right. That is what I meant, I'll fix it.
tepples wrote:
As far as I can tell, BASE isn't very useful unless you're trying to copy code to RAM
Or if you have more than one PRG-ROM bank, in which case you have to move the PC from the end of the bank back to the beginning ($C000 back to $8000 in the case of UNROM, for example). Another option for multiple banks in ASM6 I'm aware of is to assemble each bank separately, but that can get really hard if the banks reference each other a lot.
Good point, if you're just using it like .bank in NESASM or .segment in ca65.
tokumaru wrote:
Another option for multiple banks in ASM6 I'm aware of is to assemble each bank separately, but that can get really hard if the banks reference each other a lot.
That is what I do for the FDS mapper, I couldn't figure out another way that would work.
I'm sure BASE would've worked.
I tried with base, but I think I probably didn't understand how it worked back then. I might try it again.
Also, is there a way to know when the NMI is not true?
It depends on what exactly you mean by "know[ing] when the NMI is not true". What would "know[ing] when the NMI is not true" tell you? Are you trying to wait for the end of vertical blanking?
tepples wrote:
It depends on what exactly you mean by "know[ing] when the NMI is not true". What would "know[ing] when the NMI is not true" tell you? Are you trying to wait for the end of vertical blanking?
Yes.
The only reliable way to know when vblank ends is to wait for the sprite overflow or sprite 0 hit flags to be cleared (which happens near the end of vblank), but you have to make sure they were set in the previous frame to begin with. May I ask why you'd need this information? Most people just optimize their vblank handling code so it never extrapolates the allowed time.
I'm afraid that my Vblank my be too short, and it'll finish early and go to the part where it will read the controllers and then you will be still pressing the right arrow and the x-position will be increasing too fast.
DementedPurple wrote:
I'm afraid that my Vblank my be too short
That's not a problem, don't worry.
Quote:
and it'll finish early and go to the part where it will read the controllers and then you will be still pressing the right arrow and the x-position will be increasing too fast.
People will normally hold buttons for several frames (specially if it's a direction button, you have to hold them if you want to walk around!), so this is expected. What you have to do is structure your program so that only one cycle of game logic + PPU updates happens per frame. This will guarantee that your game runs at 60Hz (or 50 for PAL) and all the physics constants can be adjusted for that speed. What you absolutely can't do is read the controllers and react to input several times per frame, that'll sure make things go faster than they should, but if you're doing it only once per frame, it doesn't really matter *when* in the frame you're doing it, because 1/60th of a second is too little of a window for humans to be able to analyze what happens inside it. The vertical blank lasts about 20 scanlines, which is about 1.3 milliseconds, so it really doesn't matter.
I think, what tokumaru and tepples mean, is...for a new programmer, you aren't going to do anything that requires timing something mid-frame.
I think a sprite zero hit at the top left would be the easiest way to time when the frame is on scanline 1.
You could also do an MMC3 scanline IRQ count of 1 scanlines.
Neither one do you need, until much later, when you know much more about screen rendering.
I've looked at your code from the other thread and yes, you're running the game logic over and over in the same frame, causing things to move too fast. This has nothing to do with a short vblank handler. You jump to a label called "wait" but... there's no waiting at all, the program simply reads the controller again and performs the movement again, over and over, without ever waiting for the next frame to start.
What you need to do is set up a way to make your program wait for the vblank handler to run, so it knows that one frame is complete and it can proceed to processing the next one. The simplest way to do this is to increment a counter anywhere in your NMI handler:
Code:
INC FrameCounter
And wait for this counter to change in your main loop:
Code:
LDA FrameCounter
Wait:
CMP FrameCounter
BEQ Wait
Another thing you're not doing is backing up registers before you use them in the NMI handler. Remember, this code is going to interrupt the main loop, and if you change the registers without restoring them to their original values, when you return from the NMI the program will not be able to resume whatever it was doing, resulting in bugs and possibly even crashes. Most people have their NMI handlers save and restore all 3 registers, like this:
Code:
NMI:
PHA ;save A
TXA
PHA ;save X
TYA
PHA ;save Y
;(NMI logic here)
PLA ;restore Y
TAY
PLA ;restore X
TAX
PLA ;restore A
RTI
EDIT: Corrected mistake pointed out by tepples.
tokumaru wrote:
And wait for this counter to change in your main loop:
Code:
Wait:
LDA FrameCounter
CMP FrameCounter
BEQ Wait
This will catch the change only if NMI happens between LDA and CMP, a roughly 1 in 3 chance if FrameCounter is in zero page.
A more reliable loop loads the previous frame counter value only once and then waits for it to change:
Code:
LDA FrameCounter
Wait:
CMP FrameCounter
BEQ Wait
Oh yeah, I misplaced the label. Sorry about that.