How to Port Snes9x to a New Platform ==================================== Version: 1.01 Date: 23-December-1998 (c) Copyright 1998 Gary Henderson (gary@daniver.demon.co.uk) Introduction ============ This is brief description of the steps involved in porting Snes9x, the Super Nintendo Entertainment System emulator, to new hardware which is at least similar to Workstation or PC. It describes what code you have to write and what functions exist that you can make use of. It also gives some insights as to how Snes9x actually works, although that will be subject of another document yet to be written. Host System Requirements ======================== A C++ compiler, so you can compile the emulator! Snes9x really isn't written in C++, it just uses the C++ compiler as a 'better C' compiler to get inline functions and so on. With some modification, it could be converted to be compiled with an ordinary C compiler. Snes9x isn't very C type safe and will probably not work on a system who's integers are less than 32-bits wide without lots of editing. If the host system uses a CPU that implements the i386 instruction set then you will also want to use the three assembler CPU cores, although I recently scrapped the SPC700 assembler code (too many bugs) and replaced it with compiler generated assembler code that I haven't got around to optimising yet. The 65c816 and SPC700 code needs to be assembled using the GNU assembler that comes with gcc and the Super FX code assembled with NASM v0.97 or higher. gcc is available from lots of sites. NASM is available from http://www.cryogen.com/Nasm A fast CPU. SNES emulation is very compute intensive: two, or sometimes three CPUs to emulate, an 8-channel 16-bit stereo sound digital signal processor with real-time sample decompression, filter and echo effects, two custom graphics processor chips that can produce transparency, scaling, rotation and window effects in 32768 colors, and finally hardware DMA all take their toll on the host CPU. Lots of RAM. The SNES itself has 128k work RAM, 64k V-RAM and 64k sound CPU RAM. If a Super FX game is being emulated, that usually comes with another 64k inside the game pack. Snes9x itself needs 4Mb to load SNES ROM images into (or 6Mb if I ever figure out the SNES memory map of the 48Mbit ROM images out there), 256k to cache decompressed sound samples in, 512k to cache converted SNES tiles in, and another 64k for S-RAM emulation. And that's not counting a few large lookup tables that the graphics code needs for speeding up transparency effects plus few other tables used by the ZSNES Super FX code. It all adds up to 7Mb (ish). Add to that RAM needed to store the actual emulator code and RAM required by the host operating system and any other process that is running; that's lots of RAM. Well, it is if your host system only has a few mega-bytes of RAM available. An 8-bit, 256 color (one byte per pixel) or deeper display, at least 256x239 pixels in resolution, or 512x478 if you're going to support the SNES' hi-res. background screen modes. Ideally, a 16-bit, 65536 color screen mode is required if you want to support transparency at speed, as that is what the code renders internally. Any other format screen, with transparency enabled, will require picture format conversion before you can place the rendered SNES image on to the screen. Sound output requires spooling 8-bit or 16-bit, mono or stereo digital sound data to the host computer's sound hardware. The DOS port uses interrupts from the sound card to know when more sound data is required, most other ports have to periodically poll the host sound hardware to see if more data is required; if it is then the SNES sound mixing code provided by Snes9x is called to fill an area of system memory with ready mixed SNES sound data, which then can be passed on to the host sound hardware. Sound data is generated as an array of bytes (uint8) for 8-bit sound or shorts (int16) for 16-bit data. Stereo sound data generates twice as many samples, with each channel's samples interleaved, first left's then right's. For the user to be able to control and play SNES games, some form of input device is required, a joystick or keyboard, for example. The real SNES can have 2 eight-button digital joy-pads connected to it or 5 joy-pads when an optional multi-player adaptor was purchased, although most games only require a single joy-pad. Access to all eight buttons and the direction pad, of course, are usually required by most games. Snes9x does emulate the multi-player adaptor hardware, if you were wondering, but its still up to you to provide the emulation of the individual joy-pads. The SNES also had a mouse and light gun available as optional extras, Snes9x can emulate both of these using some form of pointing device, usually the host system's mouse. If an accurate, constant SNES play rate is required, then a real-time timer will be needed that can time intervals of 16.7ms (NTSC frame time) or 20ms (PAL frame time). Some SNES game packs contained a small amount of extra RAM and a battery so ROMs could save a player's progress through a game for games that takes many hours to play from start to finish. Snes9x simulates this S-RAM by saving the contents of the area of memory normally occupied by the S-RAM into file then automatically restoring it again the next time the user plays the same game. If the hardware you're porting to doesn't have a hard disk available then you could be in trouble. Snes9x also implements freeze-game files which can record the state of the SNES hardware and RAM at a particular point in time and can restore it to that exact state at a later date - the result is that users can save a game at any point, not just at save-game or password points provided by the original game coders. Each freeze file is over 400k in size. To help save disk space, Snes9x can be compiled with zlib, which is used to compress the freeze files, reducing the size to typically below 100k. Download zlib from its homepage at http://www.cdrom.com/pub/infozip/zlib/, compile Snes9x with ZLIB defined and link with zlib. zlib is also used to load any compressed ROM images Snes9x my encounter, compressed with gzip or compress. Porting ======= In theory you will only need to edit port.h, then in a separate file write all the initialisation code and interface routines that Snes9x expects the you to implement. You, no doubt, will discover otherwise.... There are several compile-time only options available: DEBUGGER -------- Enables extra code to assist me in debugging SNES ROMs. The debugger has only ever been a quick-hack by me and user-interface to debugger facilities is virtually non-existent. Most of the debugger information is output via stdout and enabling the compile-time options slows the whole emulator down slightly. However, the debugger options available are very powerful; you could use it to help get your port working. You probably still want to ship the finished version with the debugger disabled, it will only confuse non-technical users. VAR_CYCLES ---------- I recommend you define this. The main CPU in the SNES actually varies in speed depending on what area of memory its accessing and the ROM access speed of the game pack; defining VAR_CYCLES causes Snes9x to emulate this, using a good approximation, rather than fixed cycle length as ZSNES does. The resultant code is slightly slower. Leaving it undefined results in many more emulation timing errors appearing while playing games. CPU_SHUTDOWN and SPC700_SHUTDOWN -------------------------------- Again I recommend defining both of these. They are both speed up hacks. When defined, Snes9x starts watching for when either the main or sound CPUs are in simply loops waiting for a known event to happen - like the end of the current scan-line, and interrupt or a sound timer to reach a particular value. If Snes9x spots either CPU in such a loop it uses its insider knowledge to simply skip the emulation of that CPU's instructions until the event happens. It can be a big win with lots of SNES games. I'm constantly amazed at the ingenuity of some programmers who are able to produce complex code to do simple things: some ROM's wait loops are so complex Snes9x fails to spot the CPU is in such a loop and the shutdown speed up hacks don't work. You might be wondering why VAR_CYCLES, and the two SHUTDOWN options have to be enabled with defines, well, in the past they sometimes introduced problems with some ROMs, so I kept them as options. I think I've fixed all the problems now, but you never know... SPC700_C -------- Define this if you are using the C/C++ version of the SPC700 CPU core. It enables a ROM compatibility feature that executes SPC700 instructions during SNES DMA, it allows several games to start that would otherwise lock up and fixes music pauses when ROMs do lots of DMA, usually when switching between game screens. ZLIB ---- Define this if you have the zlib library available and you want it to compress freeze-game files to save disk space. The library is also used to support compressed ROM images. NO_INLINE_SET_GET ----------------- Define this to stop several of the memory access routines from being defined in-line. Whether the C++ compiler actually in-lines when this symbol is not defined is up to the compiler itself. In-lines functions can speed up the C++ CPU emulations on some architectures at the cost of increased code size. Try fiddling with this option once you've got port working to see if it helps the speed of your port. EXECUTE_SUPERFX_PER_LINE and ZSNES_FX ------------------------------------- Define these if you're going to be using the ZSNES Super FX i386 assembler code, otherwise leave them both undefined. In theory, EXECUTE_SUPERFX_PER_LINE can also be defined when using the C++ Super FX emulation code, but the code is still buggy and enabling the option introduces more problems than it fixes. Any takers for fixing the C++ code? JOYSTICK_SUPPORT, SIDEWINDER_SUPPORT and GRIP_SUPPORT ----------------------------------------------------- These options enable support for various input devices in the UNIX and MS-DOS port code. They're only of interest if you're able to use the existing UNIX or MS-DOS port specific code. port.h ====== If the byte ordering of the target system is least significant byte first, make sure LSB_FIRST is defined in this header, otherwise, make sure its not defined. If you're going to support 16-bit screen rendering (required if you want transparency effects) and your system doesn't use RGB 565 - 5 bits for red, 6 bits for green and 5 bits for blue - then you'll need make sure RGB555, BGR565 or BGR555 is defined instead. You might want to take a look at the *_LOW_BIT_MASKs, *_HI_BIT_MASKs and BUILD_PIXEL macros to make sure they're correct, because I've only every tested the RGB565 version, though the Mac port uses the RGB555 option. If your system is 24 or 32-bit only, then don't define anything; instead write a conversion routine that will take a complete rendered 16-bit SNES screen in RGB565 format and convert to the format required to be displayed on your hardware. port.h also typedefs some types, uint8 for an unsigned, 8-bit quantity, uint16 for an unsigned, 16-bit quantity, uint32 for a 32-bit, unsigned quantity and bool8 for a true/false type. Signed versions are also typedef'ed. The CHECK_SOUND macro can be defined to invoke some code that polls the host system's sound hardware to see if it can accept any more sound data. Snes9x makes calls to this macro several times when it is rendering the SNES screen, during large SNES DMAs and after every emulated CPU instruction. Since this CHECK_SOUND macro is invoked often, the code should only take a very small amount of time to execute or it will slow down the emulator's performance. The Linux and UNIX ports use a system timer and set a variable when it has expired; the CHECK_SOUND only has to check to see if the variable is set. On the MS-DOS and Mac ports, the sound hardware is not polled at all, instead it is driven by interrupts or callbacks and the CHECK_SOUND macro is defined to be empty. Initialisation Code ------------------- This is what the Linux, UNIX and MS-DOS ports do, I suspect your code might be similar: - The Settings structure is initialised to some sensible default values - check the main function in unix.cpp for the values it uses. - The command line is parsed, options specified override default values in the Settings structure and specify a ROM image filename that the user wants loaded. Your port could load user preferences from a file or some other source at this point. Most values, with a little care, can be changed via a GUI once the emulator is running. - Some Settings structure value validation takes place, for example if transparency effects are requested the code also makes sure 16-bit screen rendering is turned on as well. - Memory.Init() and S9xInitAPU() are called, checking neither failed. The only reason they would fail is if memory allocation failed. - Memory.LoadROM (filename) is called to load the specified ROM image into memory. If that worked Memory.LoadSRAM (sram_filename) is called to load the ROM's S-RAM file, if one exists. The all current ports base the sram_filename on the filename of the ROM image, changing the file's extension (the .smc or whatever bit) and changing the directory where its located - you won't be able to save S-RAM files onto a CD if that's where the ROM image is located! If your port has a GUI, you can delay this step until the user picks an image to load. SNES roms images come in all shapes and sizes, some with headers, some without, some have been mangled by the copier device in one of two ways, and some split into several pieces; plus the SNES itself has several different memory map models. The code tries to auto-detect all these various types, but sometimes the SNES ROM header information has been manually edited by someone at some stage and the code guesses wrong. To help it out it these situations, the Settings structure contains several options to force a particular ROM image format; these values must be initialised prior to each call to Memory.LoadROM(filename). - The Linux and UNIX ports now do some more operating system initialisation ready for a system timer to be started. - The host display hardware is now initialised. The actual screen depth and resolution should be picked based on the user preferences if possible. The X Window System port can't control the screen depth or resolution, if the user requests transparency effects but the display hardware is only set to 8-bit, it has to invoke an extra step of converting the 16-bit SNES rendered screen to a fixed palette 8-bit display just before the SNES screen is copied to the display hardware. The GFX.Screen pointer needs to be initialised to point to an array of uint8 for 8-bit screen rendering or uint16 for 16-bit rendering, cast to an array of uint8. The array needs to be at least 256x239 bytes or shorts in size for lo-res only support (Settings.SupportHiRes = FALSE) or 512x478 for lo-res and hi-res support. If transparency effects are required, the GFX.SubScreen array also needs to be initialised to another identically sized array of the same type, otherwise it can be just initialised to NULL. The GFX.Pitch variable needs to be set to the number of bytes on each line of the arrays, e.g. 256 for lo-res only support, up to 1024 for 16-bit hi-res support. If GFX.Screen is pointing into an existing array, one created by the library function rather than just calling malloc or new, then set GFX.Pitch to the number of bytes per line of that array, including any padding the library function may have added. If the target hardware supports fast access to video RAM, the screen is in 16-bit format supported by the SNES rendering code and you can double buffer the display, you might want to point GFX.Screen directly at the video buffer RAM. You will need to recompute the GFX.Delta value every time you change the GFX.Screen value to double-buffer the rendering and display. - A call to S9xGraphicsInit() is made; make sure all your graphics rendering options are setup correctly by now. If later, you want to change some settings, for example 16-bit to 8-bit rendering, call S9xGraphicsDeinit() first, change your settings, GFX.Screen and GFX.SubScreen arrays, etc., then call S9xGraphicsInit() again. - S9xInitSound(int playbackrate, bool8 stereo, int sound_buffer_size) is now called, which in turn will call your S9xOpenSoundDevice function - see below. - The display is switched to graphics mode using a call to S9xGraphicsMode(). - The system timer is started; its used for keeping the emulator speed relatively constant on the MS-DOS port and noting when the sound hardware sound should be able to accept more sound data on the Linux and UNIX ports. - A main loop is entered which is just a loop constantly calling S9xMainLoop() then polling the operating system for any pending events such as key presses and releases, joystick updates, mouse position updates, GUI user interaction, etc. Pause functionality can be implemented by skipping the call to S9xMainLoop and muting the sound output by calling S9xSetSoundMute (TRUE). Don't enter the main loop until a SNES ROM image has been loaded, or at least skip calling S9xMainLoop inside the loop until one is and make sure S9xReset is called instead before entering the main loop. The Mac port implements this technique by starting in pause mode and refusing to unpause until a ROM image is loaded. S9xMainLoop processes SNES CPU emulation, SNES screen rendering, DMA and H-DMA emulation, until emulated scan-line 0 is reached, then it returns. Now is your chance to process any system events pending, scan the keyboard, read joystick values, etc. If DEBUGGER compile-time support is enabled and the CPU emulation has hit a break point or single-stepping is switched on, or the DEBUG_MODE_FLAG is set in the CPU.Flags variable, then the S9xMainLoop routine returns early, allowing you to act on the event in some way. The Linux, DOS and UNIX ports respond to the DEBUG_MODE_FLAG being set by calling S9xDoDebug(), which in turn outputs the current instruction and loops reading commands from stdin and outputting debug information, currently via stdout. The debugger desperately needs rewriting to support a GUI interface, more descriptive commands and better error handling; maybe one day... Existing Interface Routines --------------------------- These are routines already written that you will either need to call or might find useful. -> bool8 Memory.Init () Allocates and initialises several major lumps of memory, for example the SNES ROM and RAM arrays, tile cache arrays, etc. Returns FALSE if memory allocation fails. -> void Memory.Deinit () Undoes the memory allocations made by Memory.Init. -> bool8 S9xGraphicsInit () Allocated and initialises several lookup tables used to speed up SNES graphics rendering. Call after you have initialised the GFX.Screen, GFX.SubScreen and GFX.Pitch values. If Settings.Transparency is false it does not allocate tables used to speed up transparency effects. If you want to provide the user with option to turn the effects on and off during game play, make sure Settings.Transparency is true when this function is called, it can later be set to FALSE. Returns FALSE if memory allocation fails. -> void S9xGraphicsDeinit () Undoes the memory allocations made by S9xGraphicsInit. -> bool8 S9xInitAPU () Allocates and initialises several arrays used by the sound CPU and sound generation code. -> void S9xDeinitAPU () Undoes the allocations made by S9xInitAPU. -> bool8 S9xInitSound (int mode, bool8 stereo, int buffer_size) Does more sound code initialisation and opens the host system's sound hardware by calling the S9xOpenSoundDevice function provided by you. -> void S9xReset () Resets the SNES emulated hardware back to the state it was in at 'switch-on' except the S-RAM area is presevered. The effect is it resets the current game back to the start. This function is automatically called by Memory.LoROM. -> bool8 Memory.LoadROM (const char *filename) Attempts to load the specified ROM image filename into the emulated ROM area. There are many different SNES ROM image formats and the code attempts to auto-detect as many different types as it can and in a vast majority of the cases gets it right. However, some ROM images have been edited by someone at some stage or have been mangled by the ROM copier that produced them and LoadROM needs help. Inparticular, it can't auto-detect the odd way in which some Super FX games have been mangled and needs to be told, via Settings.Interleaved2, that the ROM image is in that format, or that odd-sized ROM images have a 512 byte copier header. There are several other ROM image options in the Settings structure; allow the user to set them before calling LoadROM, or make sure they all reset to default values before each call to LoadROM. -> bool8 Memory.LoadSRAM (const char *filename) Call this routine to load the associated S-RAM save file (if any). The filename should be based on the ROM image name to allow easy linkage. The current ports change the directory and the filename extension of the ROM filename to derive the S-RAM filename. -> bool8 Memory.SaveSRAM (const char *filename) Call this routine to save the emulated S-RAM area into a file so it can be restored again the next time the user wants to play the game. Remember to call this when just before the emulator exits or when the user has been playing a game and is about to load another one. -> void S9xMainLoop() The emulator main loop. Call this from your own main loop that calls this function (if a ROM image is loaded and the game is not paused), processes any pending host system events, then goes back around the loop again until the emulator exits. S9xMainLoop normally returns control to your main loop once every emulated frame, when it reaches the start of scan-line zero. However, the routine can return more often if the DEBUGGER compile-time flag is defined and the CPU has hit a break point, or the DEBUG_MODE_FLAG bit is set in CPU.Flags or instruction single-stepping is enabled. -> void S9xMixSamples (uint8 *buffer, int sample_count) Call this routine from your host sound hardware handling code to fill the specified buffer with ready mixed SNES sound data. If 16-bit sound mode is choosen, then the buffer will be filled with an array of sample_count int16, otherwise an array of sample_count uint8. If stereo sound generation is selected the buffer is filled with the same number of samples, but in pairs, first a left channel sample followed by the right channel sample. There is a limit on how much data S9xMixSamples can deal with in one go and hence a limit on the sample_count value; the limit is the value of the MAX_BUFFER_SIZE symbol, normally 4096 bytes. -> bool8 S9xSetSoundMute (bool8 mute) Call with a TRUE parmeter to prevent S9xMixSamples from processing SNES sample data and instead just filling the return buffer with silent sound data. Useful if your sound system is interrupt or callback driven and the game has been paused either directly or indirectly because the user interacting with the emulator's user interface in some way. -> bool8 S9xFreezeGame (const char *filename) Call this routine to record the current SNES hardware state into a file, the file can be loaded back using S9xUnfreezeGame at a later date effectively restoring the current game to exact same spot. Call this routine while you're processing any pending system events when S9xMainLoop has returned control to you in your main loop. -> bool8 S9xUnfreezeGame (const char *filename) Restore the SNES hardware back to the exactly the state it was in when S9xFreezeGame was used to generate the file specified. You have to arrange the correct ROM is already loaded using Memory.LoadROM, an easy way to arrange this is to base freeze-game filenames on the ROM image name. The Linux, UNIX and DOS ports load freeze-game files when the user presses a function key, with the names romfilename.000 for F1, romfilename.001 for F2, etc. Games are frozen in the first place when the user presses Shift-function key. You could choose some other scheme. -> void S9xNextController () The real SNES allows several different types of devices to be plugged into the game controller ports. The devices Snes9x emulates are a joy-pad, multi-player adaptor (allowing a further 4 joy-pads to be plugged in), a 2-button mouse and a light gun known as the SuperScope. Each call to S9xNextController will step the current emulated device on to the next device in the sequence multi-player, joy-pad, mouse on port 1, mouse on port 2, light gun then back to multi-player again. Defines allocating a number of each device type are in snes9x.h. The currently selected device is stored in IPPU.Controller if you want to give some feedback to the user. The initial value of IPPU.Controller (set when S9xReset is called) is obtained from Settings.ControllerOption based on currently enabled options. Some ROMs object to certain non-joy-pad devices being plugged into the real SNES while they are running, all Super FX games should only allow joy-pads to be plugged in because the Super FX chip and any other device would overload the SNES power supply. Tetris and Dr. Mario also objects for reasons best known to itself. For this reason there are switches in the Settings structure to enable and display the emulation of the various devices. const char *S9xGameGenieToRaw (const char *code, uint32 &address, uint8 &byte) const char *S9xProActionReplayToRaw (const char *code, uint32 &address, uint8 &byte) const char *S9xGoldFingerToRaw (const char *code, uint32 &address, bool8 &sram, uint8 &num_bytes, uint8 bytes[3]) void S9xApplyCheats (bool8 apply) void S9xRemoveCheats () void S9xAddCheat (uint32 address, bool8 cpu_address, bool8 sram, uint8 num_bytes, uint8 byte1, uint8 byte2, uint8 byte3) void S9xDeleteCheats () void S9xDoDebug () Interface Routines You Need to Implement ---------------------------------------- bool8 S9xOpenSnapshotFile (const char *base, bool8 read_only, STREAM *file) *************************************************************************** void S9xCloseSnapshotFile (STREAM file) *************************************** Routines to open and close freeze-game files. STREAM is defined as a gzFile if ZLIB is defined else its defined as FILE *. The read_only parameter is set to TRUE when reading a freeze-game file and FALSE when writing a freeze-game file. void S9xExit () *************** Called when some fatal error situation arises or when the 'q' debugger command is used. The Mac port just beeps and drops back to the GUI when S9xExit is called, the MS-DOS, Linux and Solaris ports all call exit () to terminate the emulator process. void S9xParseArg (char **argv, int &index, int argc) **************************************************** void S9xExtraUsage () ********************* If you're going to be using the simple command line parser, when it encounters an unknown option it calls S9xUsage which is supposed to report all options the generic parse knows about (I haven't been keeping it up to date of late). S9xUsage then, in turn calls S9xExtraUsage which you implement to report any port-specific options available. void S9xGraphicsMode () *********************** void S9xTextMode () ******************* The SNES debugger calls these routines to switch from a graphics screen mode used to display the SNES game to a debugger screen used to display debugger output. If the SNES screen can be displayed at the same time as a text display, as would be the case when the host system implements a graphical window system, or you're not going to support the SNES debugger, then these routines should do nothing. On the X Window System UNIX/Linux port, these routines do nothing where as on the MS-DOS port they switch between a graphics screen mode and a text-only screen mode. bool8 S9xInitUpdate () ********************** Called just before Snes9x starts to render a SNES screen. The Windows port uses this call to lock Direct X screen area to allow exclusive access; on other existing ports its implemented as an empty function. bool8 S9xDeinitDisplay (int width, int height, bool8 sixteen_bit) ***************************************************************** Called once a complete SNES screen has been rendered into the GFX.Screen memory buffer, now is your chance to copy the SNES rendered screen to the host computer's screen memory. The problem is that you have to cope with different sized SNES rendered screens. Width is always 256, unless you're supporting SNES hi-res. screen modes (Settings.SupportHiRes is TRUE), in which case it can be 256 or 512. The height parameter can be either 224 or 239 if you're only supporting SNES lo-res. screen modes, or 224, 239, 448 or 478 if hi-res. SNES screen modes are being supported. All current ports support scaling the SNES screen to fill the host system's screen, the many ports even supports interpolation - blending the colours of adjacent pixels to help hide the fact they've been scaled - and scan-line simulation - slightly darkening every other horizontal line. Don't forget that if you're just placing the SNES image centerally in the screen then you might need to clear areas of the screen if the SNES image changes size between calls to S9xDeinitDisplay. The MS-DOS and UNIX ports currently don't do this which results in junk being left on the screen if the ROM changes SNES screen modes. The sixteen_bit is just a copy of the Settings.SixteenBit setting and if TRUE indicates a 16-bit SNES screen image has been rendered, 8-bit otherwise. void S9xMessage (int type, int number, const char *message) *********************************************************** I've started work on converting all the old printfs into calls to this routine. When Snes9x wants to display an error, information or warning message, it calls this routine. Check in messages.h for the types and individual message numbers that Snes9x currently passes as parameters. The idea is display the message string so the user can see it, but you choose not to display anything at all, or change the message based on the message number or message type. Eventually all debug output will also go via this function, trace information already does. bool8 S9xOpenSoundDevice(int mode, bool8 stereo, int buffer_size) ***************************************************************** S9xInitSound calls this function to actually open the host operating system's sound device, or initialise the sound card in MS-DOS port. The mode parameter is the value passed in on the command line with the -r command line flag, assuming you're using the Snes9x parser. Its meant to indicate what playback the sound hardware should be set to, value 1 to 7. I think the real SNES sound chip playback rate is 30kHz, but such high playback rates take a lot of native CPU power to emulate. The default playback rate is 22kHz for the MS-DOS and UNIX ports. The stereo flag indicates if the user wants stereo sound. Again, stereo sound takes more CPU to power to emulate compared to mono sound. The buffer_size value indicates what sample buffer size the user wants, usually zero, meaning you should pick the value best suited to the current playback rate. Sound data is normally passed to the sound hardware in blocks, the smaller the block the less latency between the SNES game playing a sound and it being heard by the user. But if you pick a too smaller value, and you're having to periodically poll the operating system to see if it can accept more sound data, then the sound output will break up because other actions such as rendering the SNES screen can prevent you from polling the hardware often enough and the operating system runs out of sound data to play. The MS-DOS port uses a buffer size of 128 samples since the sound card sends an interrupt when more data is required which is acted upon promptly, where as the Linux and Solaris ports use a buffer size of 512 samples or more depending on the playback rate. Stereo and 16-bit sound both double the actual size of the buffer in bytes. uint32 S9xReadJoypad (int which1_0_to_4) **************************************** This function is called to return a bit-wise mask of the state of one of the five emulated SNES controllers. Return 0 if you're not supporting controllers past a certain number or return the mask representing the current state of the controller number passed as a parameter or'ed with 0x80000000. Symbolic constants are defined in snes9x.h indicating the bit positions of the various SNES buttons and direction indicators; they're all in the form SNES_X_MASK where X is the SNES controller button name. The MS-DOS and X Window System ports record what keys are currently pressed and use that to build up a mask, the Windows port polls the operating system when S9xReadJoypad is called to find out what keys are pressed. All ports also implement host joysticks and joy-pads via this interface. bool8 S9xReadMousePosition (int which1_0_to_1, int &x, int &y, uint32 &buttons) ******************************************************************************* Used by Snes9x to get the current position of the host pointing device, usually a mouse, used to emulated the SNES mouse. Snes9x converts the x and y values to delta values required by the SNES mouse, so the actual x and y values are unimportant, only the change in value since the last call to this function is used. Graphical windowing systems normally restrict the movement of the pointer on the screen, if you're porting to such an environment you might want to make a note of the change in position in the mouse since the last time you asked the operating system the mouse position, add this change in value to some saved x and y value, the reposition the pointer back to the centre of the SNES display window. The saved x and y values will be the values returned by this function. The buttons return value is a bit-wise mask of the two SNES mouse buttons, bit 0 for button 1 (left) and bit 1 for button 2 (right). bool8 S9xReadSuperScopePosition (int &x, int &y, uint32 &buttons) ***************************************************************** void S9xSetPalette () ********************* void S9xSyncSpeed () S9xUnixProcessSound void _makepath(char *, char const *, char const *, char const *, char const *) void _splitpath(char const *, char *, char *, char *, char *) Sound Generation ---------------- Settings --------