nes emulator

Discuss emulation of the Nintendo Entertainment System and Famicom.

Moderator: Moderators

nesemuguy
Posts: 7
Joined: Fri Dec 15, 2006 3:01 am

nes emulator

Post by nesemuguy » Fri Dec 15, 2006 3:10 am

Hi guys, i just joined this forum, and am very happy to find a forum dedicated to nes emulator related topics. Im new to emulation, and i really want to design a nes emulator. I have read marat's, yoshi's, and several other nes tech docs, and have understood the basics. So my first thing on the agenda is to create the 6502 cpu emulator. But here is my problem, since my emulator will read in the .nes file produced by the nesasm assembler, how xactly am i suppose to translate the instructions from .nes file. Since .nes is actually a binary file, i tried to open a stream buffer in c++ to the file, but the stream buffer only displayed comments. I understand 6502 cpu design really well, but xactly how am suppose to get the instructions from the .nes file?

Please, any suggestion would be really helpful. Thnkx.

User avatar
Memblers
Site Admin
Posts: 3897
Joined: Mon Sep 20, 2004 6:04 am
Location: Indianapolis
Contact:

Post by Memblers » Fri Dec 15, 2006 3:53 am

I don't know C++ (and barely know C), and haven't written an emulator before, so I can only offer basic suggestions..

There must be a lot of ways to do it. One way that comes to mind is to use a switch statement, with a case for every instruction. Use the program counter as a pointer into the 6502's memory, and do each instruction from there. Could be helpful to look at other CPU emulators and see how they do it.

User avatar
dXtr
Posts: 375
Joined: Tue Sep 21, 2004 12:11 am
Location: Karlshamn (Sweden)

Post by dXtr » Fri Dec 15, 2006 4:11 am

Sorry for misspellings, I'm from Sweden ^^

albailey
Posts: 177
Joined: Thu Jul 13, 2006 3:15 pm

Post by albailey » Fri Dec 15, 2006 7:56 am

Having never written an emulator, I cant help with that. But I might be able to help a little.

The NES file (I'll assume its .ines format) has a 16 byte header, some 8K CHR banks and some 16K PRG banks (and maybe some junk at the end)

Remember its binary so you need to open the file in binary mode.
FILE *fp = fopen(romFile , "rb");

So to start you probably want to read in the 16 byte header
Based on the docs you read, you'll know which bytes are for the CHR banks and which for the PRG banks. You can then extract those CHR and PRG banks. Thats all a ROM splitter does.

Once you have isolated your PRG banks you can try to process them.
My approach. (theres probably a better way).
- Allocate a byte array of 64K (0xFFFF + 1) in size. I will call it "junk" in my examples.
- Copy the PRG bank to the last 16K (offset = 0xC000) of that array.
- Go to the address for the reset vector. the reset vector is at 0xFFFC (I think). The 6502 is little endian so the reset starting point is:
[Edited to correct an operations precedence error pointed out by other posts]
(junk[0xFFFD] << 8 ) | junk[0xFFFC]
-Read the byte at that address. Look it up in the opcode table. The opcode table will tell you the length (meaning if the next byte is an operand for this opcode, or is a new opcode)
Process the opcode and read the next one.

This should get you going.

I found that some of the documentation with the 6502 simualtor was good.

This page was really good http://homepage.ntlworld.com/cyborgsyst ... 2/6502.htm

Al
Last edited by albailey on Fri Dec 15, 2006 12:42 pm, edited 3 times in total.

User avatar
blargg
Posts: 3715
Joined: Mon Sep 27, 2004 8:33 am
Location: Central Texas, USA
Contact:

Post by blargg » Fri Dec 15, 2006 10:44 am

Always parenthesize when mixing bitwise and arithmetic operators, otherwise you'll get surprises like the above, which is equivalent to

junk[0xFFFD] << (8 + junk[0xFFFC])

If you use all bitwise, the precedence works with you:

junk[0xFFFD] << 8 | junk[0xFFFC]

I personally like to avoid bitwise unless I really need it, so I do

junk[0xFFFD] * 0x100 + junk[0xFFFC]

User avatar
Quietust
Posts: 1684
Joined: Sun Sep 19, 2004 10:59 pm
Contact:

Post by Quietust » Fri Dec 15, 2006 11:32 am

blargg wrote:If you use all bitwise, the precedence works with you:

junk[0xFFFD] << 8 | junk[0xFFFC]

I personally like to avoid bitwise unless I really need it, so I do

junk[0xFFFD] * 0x100 + junk[0xFFFC]
I would argue that doing it all bitwise is the "correct" way of doing it, since merging two bytes into a word doesn't logically involve multiplication and addition, but instead consists of joining together two sets of bits (by shifting one of them to the left, and then ORing them together).
Quietust, QMT Productions
P.S. If you don't get this note, let me know and I'll write you another.

WedNESday
Posts: 1236
Joined: Thu Sep 15, 2005 9:23 am
Location: Berlin, Germany
Contact:

Post by WedNESday » Fri Dec 15, 2006 11:52 am

It seems to me that you only have a very basic knowledge of C++ which concerns me a little. May I suggest that you read up on C++ a little more before starting. Although, a 6502 core is a very simple one to emulate, and I recommend downloading some 6502 cores first of all to give you a taste of what to expect.

User avatar
Disch
Posts: 1849
Joined: Wed Nov 10, 2004 6:47 pm

Post by Disch » Fri Dec 15, 2006 12:42 pm

blargg wrote:Always parenthesize when mixing bitwise and arithmetic operators, otherwise you'll get surprises like the above,
Heh... I always parenthesize EVERYTHING (even when I know I don't need to), simply because it makes things much more clear to me and it's less error prone.

The only time I don't parenthesize is when I'm doing several of the same operator

a + b + c
or
a * b * c

However when I mix opreators... I do: a + (b * c) even though I know I don't need to -- it just makes it much more clear.



I also like to stick with bitwise stuff where applicable

junk[0xFFFC] | (junk[0xFFFD] << 8) <-- is my preferred method

nesemuguy
Posts: 7
Joined: Fri Dec 15, 2006 3:01 am

How to read binary files into byte array

Post by nesemuguy » Sat Dec 16, 2006 12:32 am

Wow thankx guys for you responses and help. I think i am the one that needs a bit of training or good references on how to read in binary files in c++. Since all the rom image files used by any emulator will technically be in binary format, i need to how to read in the binary files, and extract the appropriate instructions from the rom files. For example, lets say i just wanted to extract the assembly code from the .nes file of a rom, how do i go about doing that, i mean how do i find the code in the binary file where the actualy assembly instructions begin, you get me? I know this is a newbie question, but seriously, i am new to emulation, and that is why i plan on emulating the 6502 cpu first. I have yoshi's, marat, nes tech docs, and have studied them fairly well, but first thing is to understand how to read in the rom file. Anyone have any suggestions or sites that you can refer to me to, thnkx!

mattmatteh
Posts: 345
Joined: Fri Jul 29, 2005 3:40 pm
Location: near chicago
Contact:

Post by mattmatteh » Sat Dec 16, 2006 12:54 am

http://nesdevwiki.ath.cx/index.php/Main_Page

look at the iNES file format. you read in the program code section. for now i would only do nrom (ines mapper 0), which is plain and simple.

matt

nesemuguy
Posts: 7
Joined: Fri Dec 15, 2006 3:01 am

reading in .nes rom file

Post by nesemuguy » Sat Dec 16, 2006 3:12 am

ok, if my understanding is correct, my c++ file will look something like this to read a .nes file:

#include <iostream.h>
#include <fstream.h>

int main( )
{
char buffer[255];

ifstream fin("testing.nes", ios::binary );
fin.read( buffer, 255);
// Code for later...
fin.close( );
return 0;
}

Since "testing.nes" is in binary format, i opened a reader to it in binary mode.
Then i read in all the binary data into the char buffer array.
Now that the buffer array has all the byte data, i go about looking for the starting code by traversing thru the array right?

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

Post by tepples » Sat Dec 16, 2006 10:41 am

First off, you'll need to divide the .nes file into header (first 16 bytes), then PRG banks, then CHR banks. The header describes the circuit board inside the cartridge, telling how to map the PRG banks into CPU memory space.

Bytes $FFFA-$FFFF of CPU memory space hold three important vectors. In the mappers that you'll be working with first, these are also the last six bytes of the PRG section.

$FFFA: NMI entry point (little endian)
$FFFC: Reset entry point (little endian)
$FFFE: IRQ entry point (little endian)

The 6502 looks up the reset entry point in $FFFC and then sets the program counter to that value. For instance, if peek($FFFC) = $23 and peek($FFFD) = $F1, jump to $F123.

mattmatteh
Posts: 345
Joined: Fri Jul 29, 2005 3:40 pm
Location: near chicago
Contact:

Post by mattmatteh » Sat Dec 16, 2006 1:44 pm

all files i think are binary. the text mode was only dos and i think useless now.

i would not use a static arrar for the program code. instead have a pointer to the data. and use new after you get the size from the ines header. then read in that size and your pointer will point to it. then use it in the cpu as my_pointer[address]. (very basic example)

matt

nesemuguy
Posts: 7
Joined: Fri Dec 15, 2006 3:01 am

Reading the header

Post by nesemuguy » Sat Dec 16, 2006 5:51 pm

In c++ how do i read in bytes to my buffer array, I mean is there data type called byte in c++, or is char it?

User avatar
Disch
Posts: 1849
Joined: Wed Nov 10, 2004 6:47 pm

Re: Reading the header

Post by Disch » Sat Dec 16, 2006 6:40 pm

nesemuguy wrote:In c++ how do i read in bytes to my buffer array, I mean is there data type called byte in c++, or is char it?
I don't mean to sound harsh -- but if you're struggling with these kinds of issues, I wonder if you're really ready for emu dev?

But anyway -- even if your project doesn't get anywhere I still think it'll be a good learning experience. So don't let me discourage you -- just don't set your expectations too high. Making an emu is a big job.

'char' is signed 8-bits. For a byte, I generally use an 'unsigned char' -- although I make typedefs of everything for ease and simplicity:

Code: Select all

typedef   signed char    s8;
typedef   signed short   s16;
typedef   signed long    s32;
typedef unsigned char    u8;
typedef unsigned short   u16;
typedef unsigned long    u32;
Those typedefs have become a staple in my programming -- I find that I include them into every project I work on. That way you can make a byte with "u8 mybyte;"

as for the file issue -- I'm not familiar with C++'s file streams so I can't really help you. I still use C's fopen/fread/fclose functions (if it ain't broke, don't fix it)

Post Reply