nesdev.com
http://forums.nesdev.com/

Where does execution start for NES programs?
http://forums.nesdev.com/viewtopic.php?f=3&t=459
Page 1 of 2

Author:  Albeit [ Mon Aug 01, 2005 11:55 pm ]
Post subject:  Where does execution start for NES programs?

Hello all,

I am wondering where execution starts for NES roms. I find a post that says there is a reset-vector at 0xfffc (dec=65532) but in the ROM I'm using (MarioBros.nes) there is only 24KB, so that definently isn't where it would be. Any ideas on where it starts executing? Where is the first opcode?

Thanks for the help!

Author:  blargg [ Tue Aug 02, 2005 12:52 am ]
Post subject: 

The CPU starts executing at the values it reads from $fffc and $fffd. What bytes these correspond to in the ROM depend on the mapper used. When the ROM won't fill the entire upper half of the address space, copies (mirrors) of it usually appear.

if you need the value for a particular ROM, it's probably simplest to use an emulator with a built-in debugger.

Author:  Albeit [ Tue Aug 02, 2005 1:34 am ]
Post subject: 

Well how does the emulator author find where it starts? The ROM I'm currently using is MarioBros, a 24KB ROM with one 16 and one 8kb bank. Is there any way to find the starting point in the ROM?

Author:  Bregalad [ Tue Aug 02, 2005 3:34 am ]
Post subject: 

When the PRG-ROM is only 16kb, the data is there both at $8000-$bfff and at $c000-$ffff

Author:  Memblers [ Tue Aug 02, 2005 5:05 am ]
Post subject: 

The reset vector contains the start address. So once it's loaded in memory as it was described, for example if the bytes $55,$8A are at $FFFC, then the first opcode to run will be at $8A55.

Author:  Guest [ Tue Aug 02, 2005 11:17 am ]
Post subject: 

(Sorry for the noobish question, still just starting out :))


So what I would do is take the prg-rom and place it in memory locations $8000-$bfff and $c000-$ffff. However, since this is an emulator that never actually happens. So to find the reset-vector, I would take the 16kb prg-rom start point, imaginairly add $c000 to it, and then find the reset-vector at the imaginary location $fffc. I probably described that very confusingly... but is that correct?

Author:  Albeit [ Tue Aug 02, 2005 11:38 am ]
Post subject: 

(Sorry that was my post above)

Author:  Disch [ Tue Aug 02, 2005 12:08 pm ]
Post subject: 

The game will read from/write to various areas in addressing space, as an emulator you will have to treat each area as it exists on the system

For example, your emu should keep a buffer which is has the PRG, and reads from $8000-$FFFF should read bytes from this buffer and give it back to the game. Therefore when a game does something like "LDA $8000" it will get the proper byte from PRG. However other areas do other things... for example "LDA $0357" will read from system RAM, not from PRG -- and "LDA $2002" will read from a PPU register, not from ROM or RAM.

My method (and I'd assume most emus methods) for handling this is creating Read/Write function pointers. Whenever the CPU reads a byte, you will call the appropriate function pointer which returns the desired info. My emu has this organized so that each 4k of addressing space has its own function pointer.. to paraphrase my code:

Code:
ReadProcs[0] = ReadMemory_RAM;  // $0000-$0FFF
ReadProcs[1] = ReadMemory_RAM;  // $1000-$1FFF
ReadProcs[2] = ReadMemory_PPU;  //$2000-$2FFF
//etc
ReadProcs[8] = ReadMemory_PRG;  //$8000-$8FFF


When the CPU performs a read, it calls the appropriate function pointer which returns the desired data:

Code:
#define CPURead(a)   ReadProcs[(a) >> 12](a)


My read procs would look something like:

Code:
u8 ReadMemory_RAM(u16 a)
{
  return SystemRAM[a & 0x07FF];
}



To handle PRG in this fashion, I keep a big buffer containing all the PRG for the ROM, but also keep 10 pointers which represent each of the 4k pages of PRG.

Code:
u8* pPRGBuffer;  /* this will be allocated with malloc or new[] and the PRG will be store here*/

u8* pPRG[10]; /* these will be what the emu actually uses -- each pointer will point to different areas in pPRGBuffer */


pPRG[0] would represent the PRG at $6000-$6FFF, pPRG[1] would represent the PRG at $7000-$7FFF, pPRG[2] would be $8000-$8FFF, etc. Note that most games don't have PRG at $6000-$7FFF, however some mappers (FME-07) do, so it helps to prepare for it.

Whenver your emu does ANY reading from PRG space, it should go through these pointers and not through 'pPRGBuffer' directly -- as these pointers will represent the current PRG banks which are swapped in.

My previously mentioned 'ReadMemory_PRG' function might look something like:

Code:
u8 ReadMemory_PRG(u16 a)
{
  return pPRG[(a >> 12) - 6][a & 0x0FFF];
}


This way... PRG swapping can be easily implimented just by changing a few pointers... rather than copying large portions of memory:

Code:
void Swap8kPRG(u8 where,u8 page)
{
  u32 offset = page << 13;

  pPRG[where] = pPRGBuffer + offset;
  pPRG[where + 1] = pPRGBuffer + offset + 0x2000;
}



When pulling out your Reset (or NMI/IRQ vectors), you should treat it just like any other read.


That's my advice anyway.

Author:  Albeit [ Tue Aug 02, 2005 1:18 pm ]
Post subject: 

Okay, so I have buffer that contains the PRG-rom and pointers that access certain portions of it. If I encounter a "LDA $8000", that essentially means load the byte at the very beginning of the buffer... because the prg-rom is loaded at $8000. Correct?

Now if there is a 16kb and a 8kb rom bank, would the 16kb be loaded at $8000 and the 8kb bank loaded directly after it? At $C000?

Author:  Disch [ Tue Aug 02, 2005 1:29 pm ]
Post subject: 

Albeit wrote:
Okay, so I have buffer that contains the PRG-rom and pointers that access certain portions of it. If I encounter a "LDA $8000", that essentially means load the byte at the very beginning of the buffer... because the prg-rom is loaded at $8000. Correct?


Well.. in this case yes... but not always. $8000 may be WELL into the ROM depending on what banks are swapped in.

Quote:
Now if there is a 16kb and a 8kb rom bank, would the 16kb be loaded at $8000 and the 8kb bank loaded directly after it? At $C000?


No ROM I'm aware of will have 24k of PRG. The 8k you're referring to sounds like it's CHR-ROM -- which is something completely different and does not have anything to do with anything mentioned in this thread. CHR-ROM is graphics data which is used the the PPU for rendering -- it does not exist anywhere in the addressing space used by the CPU.

When there's only 16k of PRG in a mapperless ROM (mapper 0) -- both $8000 and $C000 will be swapped to it. That is... LDA $C000 would have the exact same effect as LDA $8000 -- it's kind of a mirroring effect. This can be emulated very easily if you used the PRG pointer method I mentioned earlier.

Author:  tepples [ Tue Aug 02, 2005 1:56 pm ]
Post subject: 

Disch wrote:
CHR-ROM is graphics data which is used the the PPU for rendering -- it does not exist anywhere in the addressing space used by the CPU.

Anywhere? LDA #0 STA $3131 STA $3636 STA $3636 BIT $3737 and then you can read the CHR ROM out from $2007. But then, CHR is only there because the PPU is there.

Author:  Disch [ Tue Aug 02, 2005 3:05 pm ]
Post subject: 

$2007 is the PPU I/O register, not CHR addressing space.

I never said CHR was inaccessable to the CPU, I said it doesn't exist anywhere in CPU addressing space. And it doesn't =P

Author:  blargg [ Tue Aug 02, 2005 6:51 pm ]
Post subject: 

Well sure, we can get technical and claim that the PPU is a memory mapper for CHR ROM. Each bank is one byte and you write to $2006 twice to change the bank. When you read from $2007, it increments the bank by 1 or 32. :)

Author:  Albeit [ Tue Aug 02, 2005 9:42 pm ]
Post subject: 

So I've found the reset-vector (or what I think is the reset-vector). I loaded the 16kb prg-rom bank into a buffer, and then found the [0xC000-0xFFFC] and [previous+1] bytes. I did this because C000 is where the rom would be loaded, and subtractice this form FFFC gives me the location of the reset-vector if it was actually loaded at 0x0000 like my buffer is. Is this correct? Then due to little endian, i bitshifted FFFD right 8 times and then added FFFC to get my full reset-vector address. However, the reset vector is $241E. How can this be? Does that not point to a part of the part that mirrors $2000-$2007, an io register?

This is my code of finding the reset-vector:
Code:
int romSize=16384;
char rom[romSize];

FILE* zeldaFile = fopen("MarioBro.nes", "rb");
fseek(zeldaFile, 16, SEEK_SET); // The 16th byte is the start of prg-rom
for (i=0;i<romSize;i++)
                fscanf(zeldaFile, "%c", &rom[i]);
fclose(zeldaFile);

printf("%X",rom[rom[16302]<<8)+rom[16300]);


The output of this is "241E". What is wrong? Or is this correct?

Thanks for the great help so far!


(Btw, as I said earlier MarioBros.nes has a 16KB and a 8KB bank. Could the 8KB bank begin at byte 16 and I'm actually loading the 8KB bank and half of hte 16KB bank? Or does the 16KB bank start at the start of prg-rom in nes files?)

Author:  Albeit [ Tue Aug 02, 2005 11:53 pm ]
Post subject: 

I switched what .nes file I was using and am now using the one for Super Mario Brothers. It has two 16KB banks, and I am assuming that they are stored in the rom on after another. If I assume the first is loaded at $C000 the reset-vector is at $85BF, and is the opcode "AC" (LDY). But if I assume that the second bank is loaded into $C000, the opcode that the reset-vector points to is "C5" (CMP memory and accumulator). Now, the second one doesnt seem like the first instruction that would be execute by a program, but hte first one does.

Is the first bank loaded into $C000 and the second loaded into $8000? Do the banks actually come after one another in the cartridge rom? Am I doing something wrong?

Thanks for the help!

(EDIT: I just downloaded a different version of the Super Mario Brothers rom (Super Mario Brothers (E).nes) and assuming the second 16KB bank is loaded at $C000, the reset-vector is the same as the old file (9494) but the opcode at that location is now "B0" (BCS, branch on c=1). Does that seem right?

Page 1 of 2 All times are UTC - 7 hours
Powered by phpBB® Forum Software © phpBB Group
http://www.phpbb.com/