It is currently Sat Oct 21, 2017 9:59 pm

All times are UTC - 7 hours





Post new topic Reply to topic  [ 17 posts ]  Go to page 1, 2  Next
Author Message
PostPosted: Mon Aug 01, 2005 11:55 pm 
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!


Top
  
 
 Post subject:
PostPosted: Tue Aug 02, 2005 12:52 am 
Offline
User avatar

Joined: Mon Sep 27, 2004 8:33 am
Posts: 3715
Location: Central Texas, USA
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.


Top
 Profile  
 
 Post subject:
PostPosted: Tue Aug 02, 2005 1:34 am 
Offline

Joined: Tue Aug 02, 2005 12:10 am
Posts: 5
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?


Top
 Profile  
 
 Post subject:
PostPosted: Tue Aug 02, 2005 3:34 am 
Offline
User avatar

Joined: Fri Nov 12, 2004 2:49 pm
Posts: 7233
Location: Chexbres, VD, Switzerland
When the PRG-ROM is only 16kb, the data is there both at $8000-$bfff and at $c000-$ffff

_________________
Life is complex: it has both real and imaginary components.


Top
 Profile  
 
 Post subject:
PostPosted: Tue Aug 02, 2005 5:05 am 
Offline
Site Admin
User avatar

Joined: Mon Sep 20, 2004 6:04 am
Posts: 3470
Location: Indianapolis
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.


Top
 Profile  
 
 Post subject:
PostPosted: Tue Aug 02, 2005 11:17 am 
(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?


Top
  
 
 Post subject:
PostPosted: Tue Aug 02, 2005 11:38 am 
Offline

Joined: Tue Aug 02, 2005 12:10 am
Posts: 5
(Sorry that was my post above)


Top
 Profile  
 
 Post subject:
PostPosted: Tue Aug 02, 2005 12:08 pm 
Offline
User avatar

Joined: Wed Nov 10, 2004 6:47 pm
Posts: 1845
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.


Top
 Profile  
 
 Post subject:
PostPosted: Tue Aug 02, 2005 1:18 pm 
Offline

Joined: Tue Aug 02, 2005 12:10 am
Posts: 5
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?


Top
 Profile  
 
 Post subject:
PostPosted: Tue Aug 02, 2005 1:29 pm 
Offline
User avatar

Joined: Wed Nov 10, 2004 6:47 pm
Posts: 1845
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.


Top
 Profile  
 
 Post subject:
PostPosted: Tue Aug 02, 2005 1:56 pm 
Offline

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 19115
Location: NE Indiana, USA (NTSC)
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.


Top
 Profile  
 
 Post subject:
PostPosted: Tue Aug 02, 2005 3:05 pm 
Offline
User avatar

Joined: Wed Nov 10, 2004 6:47 pm
Posts: 1845
$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


Top
 Profile  
 
 Post subject:
PostPosted: Tue Aug 02, 2005 6:51 pm 
Offline
User avatar

Joined: Mon Sep 27, 2004 8:33 am
Posts: 3715
Location: Central Texas, USA
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. :)


Top
 Profile  
 
 Post subject:
PostPosted: Tue Aug 02, 2005 9:42 pm 
Offline

Joined: Tue Aug 02, 2005 12:10 am
Posts: 5
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?)


Top
 Profile  
 
 Post subject:
PostPosted: Tue Aug 02, 2005 11:53 pm 
Offline

Joined: Tue Aug 02, 2005 12:10 am
Posts: 5
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?


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 17 posts ]  Go to page 1, 2  Next

All times are UTC - 7 hours


Who is online

Users browsing this forum: No registered users and 8 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group