NESCore v1.2.0 ACKNOWLEDGEMENS Core emulator work NerveGas PPU and Rendering Enhancements Jordan WHAT IS NESCORE? NESCore is a multi-platform Nintendo Entertainment System emulation core designed for high portability, especially on mobile and embedded devices. Implementing NESCore requires minimal coding, only of operating-system specific functions to manage controller presses, video, and sound. NESCore was originally forked from InfoNES, which was derived from pNESx. The original author of InfoNES refused to repair many (many) problems with his emulator core, and thus NESCore was born. NESCore provides a significant rewrite of the old InfoNES core, fixes many problems, and provides new features such as integrated Game Genie support. If you are using InfoNES in your project, it should be very simple to switch to NESCore. See the IMPLEMENTATION section below. KNOWN BUGS I welcome patches to fix the following issues. 1. Mapper compatibility issues Although most games work very well, certain mappers do not yet function properly (5, 13, 21, 119), and so neither will games for these mappers. 2. White noise generator Something's a bit wonky with the white noise generator, as some sounds do not sound right. Steps in Kung Fu and Zelda (into a cave) for one. IMPLEMENTATION To implement NESCore in your own emulator project, a set of NESCore-Callback functions will need to be written per NESCore-Callback.h. These include: void NESCore_Callback_OutputFrame(word *WorkFrame); Primary video output. This callback is called once per frame to transfer the contents of the work buffer to the display. The work frame contains a 256x240 16-bit color image which your code will transfer to video. For example: void NESCore_Callback_OutputFrame(word *WorkFrame) { word *MyDisplay = (Pointer to your 256x240x2 video buffer); memcpy(MyDisplay, WorkFrame, NES_DISP_WIDTH * NES_DISP_HEIGHT * 2); } By default (and for speed), the video will be rendered in 16-bit color, but if you must perform a 24-bit RGB conversion, the following can be used instead: void NESCore_Callback_OutputFrame(word *WorkFrame) { register int x; register int y; byte *MyDisplay = (Pointer to your RGB buffer); for (y = 0; y < NES_DISP_HEIGHT; y++) { for (x = 0; x < NES_DISP_WIDTH; x++) { word pixel = WorkFrame[( y << 8 ) + x]; *(MyDisplay++) = ( ( pixel & 0x7c00 ) >> 7 ); *(MyDisplay++) = ( ( pixel & 0x03e0 ) >> 2 ); *(MyDisplay++) = ( ( pixel & 0x001f ) << 3 ); } } } void NESCore_Callback_InputPadState(dword *pdwPad1, dword *pdwPad2); Controller input. It's up to your application how controller presses will be handled, and what keys / areas on the screen they map to. The following bit values can be used to OR the various controller presses, and whenever this function is called, it will be the responsibility of your program to write the correct values into the pointers. #define NCTL_A 0x01 #define NCTL_B 0x02 #define NCTL_SELECT 0x04 #define NCTL_START 0x08 #define NCTL_UP 0x10 #define NCTL_DOWN 0x20 #define NCTL_LEFT 0x40 #define NCTL_RIGHT 0x80 void NESCore_Callback_Wait(); If your processor is too fast to emulate NES, you'll need to write a small wait function to calculate the desired frame rate. If you are designing on a mobile processor or embedded device, it is usually acceptable to leave this empty. This function only gets called if you compile with -DNEEDS_WAIT. void NESCore_Callback_InitSound(void); This function is called when the emulator is initialized, and should initialize any local sound channel. int NESCore_Callback_OpenSound(int nSamplesPerSync, int nSampleRate); This function is called to provide your application with information about the sample rate and the number of samples that will be sent per callback to NESCore_Callback_OutputSample. void NESCore_Callback_OutputSample(int nSamples, byte *channel1, byte *channel2, byte *channel3, byte *channel4, byte *channel5); This function provides a set of samples as audio output and should be fed into your operating system's audio device. Typically, the five channels are averaged to mix them into a single mono output channel. The default sample rate is 44100. void NESCore_Callback_CloseSound(void); This function is called when the emulator has been stopped, and instructs your program to close the sound channel. void NESCore_Callback_Wait(); This function is called every time a frame is rendered, and is responsible for placing the necessary amount of delay between frames to prevent the emulator from overclocking. For systems with a UNIX gettimeofday() implementation, compiling with -DUNIX_WAIT will invoke the built-in UNIX implementation of this function. All other implementations will need custom code. word NesPalette[64] NesPalette is an array of 64 16-bit colors used as the palette for your screen. Many different palettes can be found in the NES.app project, in NESCore_iPhone.c. The standard palette is below: word NesPalette[64] = { 0x528a, 0x0010, 0x0811, 0x280f, 0x4809, 0x5000, 0x4000, 0x2040, 0x0900, 0x0160, 0x0180, 0x0121, 0x00e9, 0x0000, 0x0000, 0x0000, 0xa534, 0x01d9, 0x30bd, 0x583b, 0x8816, 0x9809, 0x90c0, 0x71a0, 0x4aa0, 0x0b60, 0x03a0, 0x0365, 0x02f0, 0x0000, 0x0000, 0x0000, 0xffff, 0x4cff, 0x7bdf, 0xa33f, 0xdadf, 0xf2b8, 0xf34a, 0xd422, 0xbd20, 0x7600, 0x4663, 0x2e4c, 0x3617, 0x39c7, 0x0000, 0x0000, 0xffff, 0xb6df, 0xce5f, 0xde1f, 0xf5ff, 0xfdfd, 0xfe18, 0xf674, 0xe6d2, 0xcf31, 0xbf74, 0xaf57, 0xaf5c, 0xb596, 0x0000, 0x0000 }; NESCore can be adapted to function with 24-bit colors, however 16-bit color allows for slightly faster rendering. NOTE: Older versions of NESCore used different values, and performed different calculations on them prior to rendering. The colors used now are exact colors, so please be sure to update your palette if you are upgrading from v1.0.x. That's it! If you can implement these few functions, you can build your own NESCore project. An example of all these can be found in NESapp for iPhone at http://svn.natetrue.com/nesapp/ GAME GENIE NESCore supports up to four Game Genie codes by default. Game Genie codes can be initialized after a call to NESCore_Reset, prior to calling the NESCore_Run() function. To initialize Game Genie codes, set the text code directly into the array: strcpy(GG[0].code, "GOSSIP"); strcpu(GG{1].code, "INVALIDCODE"); Then make the appropriate call: valid_codes = NESCore_Init_GameGenie(); NESCore_Init_GameGenie() will return the number of codes accepted, and enable the Game Genie. To find invalid codes, check GG[i].enabled to see whether it is enabled. BUILD FLAGS #define DEBUG Calls implementor-provided NESCore_Debug(const char *fmt, ...) function to log debug messages #define DOUBLE_BUFFERING Uses double buffering. Much slower, and not really necessary. SAVE STATE Saving and restoring game state can be done easily with calls to NESCore_SaveState() and NESCore_LoadState(). The SaveState() function should be called after a call to NESCore_Halt(), but prior to NECore_Finish() so that the game is still in memory. The LoadState() function should be called after a call to NESCore_Reset() so that the game's wiring and memory will be initialized and ready. It should be called prior to a call to NESCore_Run(). These functions also store additional mapper state information for mappers that have registered the appropriate hooks with NESCore. Presently, limited mappers appear to need this. Contact us if you come across a mapper that requires a state export and we will add a hook in the mapper code. Mappers currently exported: 1, 4, 5, 9, 50, 118 PAL VS NTSC NESCore supports both PAL and NTSC images, however not all PAL images are appropriately identified as such in their headers. To force NESCore to use PAL mode, the filename passed to NESCore_LoadROM() should have the label (E) in it (including parenthesis). LICENSE This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; version 3 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. This program includes the M6502 CPU core by Marat Fayzullin