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.
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.
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
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]
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).
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.
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
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!
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
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?
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.
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
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?
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:
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)
This is a common issue no matter how experienced you are: going enough beyond the known that you can't trace the cause of a bug because there are so many possibilities. The only sane way to write programs is to keep the field of unknowns small enough. If you are just learning C++, attempting something complex like an emulator will probably result in many unsolved problems that you work around by developing superstitions that will harm your learning.
"Debugging is twice as hard as writing code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." - Brian Kernighan
I just realized something. In order to read a binary file and parse it with regards to a specific format, in this case the structure of the rom file, i need to write a simple loader for .nes roms, am I correct? If I am, then ill have to read up on loaders first, lol.
you dont need to use defines for your data types. there is stdint.h that does that already. its part of c99 and should work with any compiler. more infor can be found on google or man stdint.h
matt
Finally, i getting the hang of it. So basically every byte i read in from .nes file, has a representation to it just like ines format specifies, like the first 3 bytes = "NES". And for now i only gonna test with nrom (ines mapper 0), so the actual game instructions to execute will start from mem[0x8000] - mem[0xbfff] if .inesprg = 1, and mem[0x8000] - mem[0xffff] if .inesprg = 2. Wow, sorry for wasting the time of all the people that helped realize this, its just i never dealt with binary files before and did not realize that all binary files are created by the compiler or assembler with a specific format to follow. Dang, thnkx again!!!
nesemuguy wrote:
so the actual game instructions to execute will start from mem[0x8000] - mem[0xbfff] if .inesprg = 1, and mem[0x8000] - mem[0xffff] if .inesprg = 2.
If there's only 16k PRG (prg byte=1) then that 16k gets put at $8000-BFFF
and $C000-FFFF (it is mirrored both places). There must always be something at $FFFA-FFFF because that's where the vectors lie as someone previously mentioned.
Here is a short program I wrote for someone with the exact same problem, hope it helps someone understand reading binary files.
Code:
#include <iostream>
#include <stdio.h>
int main()
{
unsigned char *buffer; // Unsigned char = 1 Byte (0 - 255)
const char *filePath = "test.bin"; // File name (Path)
long fileSize;
FILE *file = NULL;
file = fopen(filePath,"rb"); // "rb" = binary mode
// Get size of file
long lCurPos, lEndPos;
lCurPos = ftell(file);
fseek(file, 0, 2);
lEndPos = ftell(file);
fseek(file, lCurPos, 0);
fileSize = lEndPos;
// Allocate space for the file (In bytes)
buffer = (unsigned char*)malloc(fileSize);
// fread(buffer (from offset 0), Amount to read, Amount of times to read it, Pointer to file);
fread(&buffer[0], fileSize, 1, file);
// Output the bytes in the file as hex
for (int i = 0; i < fileSize; i ++)
printf("%X ", buffer[i]);
// Close handle
fclose(file);
free(buffer); // no memory leaks
std::cin.get();
}