rendered paste body/*
* This file is part of the TuxNES project codebase.
*
* Please see the README and COPYING files for more information regarding
* this project.
*
* $Id: emu.c,v 1.79 2001/04/11 21:45:47 tmmm Exp $
*
* Description: This file contains the main() function of the emulator.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */
#ifdef HAVE_FEATURES_H
#include <features.h>
#endif /* HAVE_FEATRES_H */
#include <ctype.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#ifdef HAVE_LINUX_JOYSTICK_H
#include <linux/joystick.h>
#endif /* HAVE_LINUX_JOYSTICK_H */
#include <pwd.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#ifdef HAVE_LIBZ
#include <zlib.h>
#include "unzip.h"
#include "ziploader.h"
#endif
#include "consts.h"
#include "gamegenie.h"
#include "globals.h"
#include "mapper.h"
#include "renderer.h"
#include "sound.h"
u_char *ROM_BASE;
u_char *VROM_BASE;
char *filename;
char *homedir;
char savefile[1024];
char *tuxnesdir; /* buffer for $HOME/.tuxnes dir */
char *basefilename; /* base filename without the extensions */
unsigned int ROM_PAGES;
unsigned int VROM_PAGES;
unsigned int ROM_MASK;
unsigned int VROM_MASK;
unsigned int VROM_MASK_1k;
char *sample_format_name = "8";
int dirtyheader = 0;
int disassemble = 0;
int dolink = 0;
int MAPPERNUMBER = -1;
int SRAM_ENABLED;
int gamegenie = 0;
int irqflag = 0;
int mapmirror = 0;
int mapperoverride = 0;
int ignorebadinstr = 0;
int showheader = 0;
int unisystem = 0;
int verbose = 0;
/* joystick variables */
unsigned char jsaxes[2] = {2, 2};
unsigned char jsbuttons[2] = {2, 2};
int jsfd[2] = {-1, -1};
int jsversion[2] = {0x000800, 0x000800};
char *jsdevice[2] = {0, 0};
char *rendname = "auto";
#define JS_DEFAULT_MAP \
{ { BUTTONB, SELECTBUTTON, BUTTONA, STARTBUTTON, \
BUTTONA, BUTTONB, SELECTBUTTON, STARTBUTTON, \
PAUSEDISPLAY, 0, 0, 0, 0, 0, 0, 0, \
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, \
{ { LEFT, RIGHT }, { UP, DOWN }, { 0, 0 }, { 0, 0 }, \
{ LEFT, RIGHT }, { UP, DOWN }, { 0, 0 }, { 0, 0 } } }
struct js_nesmap js_nesmaps[2] = { JS_DEFAULT_MAP, JS_DEFAULT_MAP };
#undef JS_DEFAULT_MAP
unsigned char js_mapsequence[JS_MAX_NES_BUTTONS] =
{ BUTTONA, BUTTONB, STARTBUTTON, SELECTBUTTON,
LEFT, RIGHT, UP, DOWN, PAUSEDISPLAY };
unsigned char js_axismeso[JS_MAX_AXES] = { 0, 0, 0, 0, 0, 0, 0, 0 };
unsigned char js_usermapped2button[2][2] = {{0, 0}, {0, 0}};
#define USAGE "Usage: %s [--help] [options] filename\n"
static void help_help(int);
static void help_version(int);
static void help_options(int);
static void help_controls(int);
static void help_synonyms(int);
static void help_palettes(int);
static void help_sound(int);
/* help variables */
const struct {
int is_terse;
char *name, *desc;
void (*dfn)(int);
} topics[] = {
/* The interactive default help topic is the first in the array */
{ 1,
"help",
"Help topics",
help_help },
{ 1,
"options",
"Major command-line options",
help_options },
{ 1,
"sound",
"Sound synthesis options",
help_sound },
{ 1,
"palettes",
"Custom palette options",
help_palettes },
{ 0,
"synonyms",
"Alternate option names",
help_synonyms },
{ 0,
"controls",
"Keyboard and joystick options and bindings",
help_controls },
{ 0,
"version",
"Version and copyright information",
help_version },
/* The non-interactive default help topic is the last in the array */
{ 1, "all",
"All help topics",
(void (*)(int))0 }
};
static void help(char *progname, char *topic);
#ifdef HAVE_LIBM
extern unsigned int *ntsc_palette(double hue, double tint);
double hue = 332.0;
double tint = 0.5;
#endif /* HAVE_LIBM */
static void js_set_nesmaps(char *);
static void loadpal(char *);
static void restoresavedgame(void);
static void traphandler(int);
void InitMapperSubsystem(void);
char MapperName[MAXMAPPER + 1][30];
void quit(void);
void START(void);
void translate(int);
/* color palettes */
struct
{
char *name, *desc;
unsigned int data[64];
}
palettes[] =
{
/* The default NES palette must be the first entry in the array */
{ "loopy", "Loopy's NES palette",
{ 0x757575, 0x271b8f, 0x0000ab, 0x47009f,
0x8f0077, 0xab0013, 0xa70000, 0x7f0b00,
0x432f00, 0x004700, 0x005100, 0x003f17,
0x1b3f5f, 0x000000, 0x000000, 0x000000,
0xbcbcbc, 0x0073ef, 0x233bef, 0x8300f3,
0xbf00bf, 0xe7005b, 0xdb2b00, 0xcb4f0f,
0x8b7300, 0x009700, 0x00ab00, 0x00933b,
0x00838b, 0x000000, 0x000000, 0x000000,
0xffffff, 0x3fbfff, 0x5f97ff, 0xa78bfd,
0xf77bff, 0xff77b7, 0xff7763, 0xff9b3b,
0xf3bf3f, 0x83d313, 0x4fdf4b, 0x58f898,
0x00ebdb, 0x000000, 0x000000, 0x000000,
0xffffff, 0xabe7ff, 0xc7d7ff, 0xd7cbff,
0xffc7ff, 0xffc7db, 0xffbfb3, 0xffdbab,
0xffe7a3, 0xe3ffa3, 0xabf3bf, 0xb3ffcf,
0x9ffff3, 0x000000, 0x000000, 0x000000 }
},
{ "quor", "Quor's palette from Nestra 0.63",
{ 0x3f3f3f, 0x001f3f, 0x00003f, 0x1f003f,
0x3f003f, 0x3f0020, 0x3f0000, 0x3f2000,
0x3f3f00, 0x203f00, 0x003f00, 0x003f20,
0x003f3f, 0x000000, 0x000000, 0x000000,
0x7f7f7f, 0x405f7f, 0x40407f, 0x5f407f,
0x7f407f, 0x7f4060, 0x7f4040, 0x7f6040,
0x7f7f40, 0x607f40, 0x407f40, 0x407f60,
0x407f7f, 0x000000, 0x000000, 0x000000,
0xbfbfbf, 0x809fbf, 0x8080bf, 0x9f80bf,
0xbf80bf, 0xbf80a0, 0xbf8080, 0xbfa080,
0xbfbf80, 0xa0bf80, 0x80bf80, 0x80bfa0,
0x80bfbf, 0x000000, 0x000000, 0x000000,
0xffffff, 0xc0dfff, 0xc0c0ff, 0xdfc0ff,
0xffc0ff, 0xffc0e0, 0xffc0c0, 0xffe0c0,
0xffffc0, 0xe0ffc0, 0xc0ffc0, 0xc0ffe0,
0xc0ffff, 0x000000, 0x000000, 0x000000 }
},
{ "chris", "Chris Covell's NES palette",
{ 0x808080, 0x003DA6, 0x0012B0, 0x440096,
0xA1005E, 0xC70028, 0xBA0600, 0x8C1700,
0x5C2F00, 0x104500, 0x054A00, 0x00472E,
0x004166, 0x000000, 0x050505, 0x050505,
0xC7C7C7, 0x0077FF, 0x2155FF, 0x8237FA,
0xEB2FB5, 0xFF2950, 0xFF2200, 0xD63200,
0xC46200, 0x358000, 0x058F00, 0x008A55,
0x0099CC, 0x212121, 0x090909, 0x090909,
0xFFFFFF, 0x0FD7FF, 0x69A2FF, 0xD480FF,
0xFF45F3, 0xFF618B, 0xFF8833, 0xFF9C12,
0xFABC20, 0x9FE30E, 0x2BF035, 0x0CF0A4,
0x05FBFF, 0x5E5E5E, 0x0D0D0D, 0x0D0D0D,
0xFFFFFF, 0xA6FCFF, 0xB3ECFF, 0xDAABEB,
0xFFA8F9, 0xFFABB3, 0xFFD2B0, 0xFFEFA6,
0xFFF79C, 0xD7E895, 0xA6EDAF, 0xA2F2DA,
0x99FFFC, 0xDDDDDD, 0x111111, 0x111111 }
},
{ "matt", "Matthew Conte's NES palette",
{ 0x808080, 0x0000bb, 0x3700bf, 0x8400a6,
0xbb006a, 0xb7001e, 0xb30000, 0x912600,
0x7b2b00, 0x003e00, 0x00480d, 0x003c22,
0x002f66, 0x000000, 0x050505, 0x050505,
0xc8c8c8, 0x0059ff, 0x443cff, 0xb733cc,
0xff33aa, 0xff375e, 0xff371a, 0xd54b00,
0xc46200, 0x3c7b00, 0x1e8415, 0x009566,
0x0084c4, 0x111111, 0x090909, 0x090909,
0xffffff, 0x0095ff, 0x6f84ff, 0xd56fff,
0xff77cc, 0xff6f99, 0xff7b59, 0xff915f,
0xffa233, 0xa6bf00, 0x51d96a, 0x4dd5ae,
0x00d9ff, 0x666666, 0x0d0d0d, 0x0d0d0d,
0xffffff, 0x84bfff, 0xbbbbff, 0xd0bbff,
0xffbfea, 0xffbfcc, 0xffc4b7, 0xffccae,
0xffd9a2, 0xcce199, 0xaeeeb7, 0xaaf7ee,
0xb3eeff, 0xdddddd, 0x111111, 0x111111 }
},
{ "pasofami", "Palette from PasoFami/99",
{ 0x7f7f7f, 0x0000ff, 0x0000bf, 0x472bbf,
0x970087, 0xab0023, 0xab1300, 0x8b1700,
0x533000, 0x007800, 0x006b00, 0x005b00,
0x004358, 0x000000, 0x000000, 0x000000,
0xbfbfbf, 0x0078f8, 0x0058f8, 0x6b47ff,
0xdb00cd, 0xe7005b, 0xf83800, 0xe75f13,
0xaf7f00, 0x00b800, 0x00ab00, 0x00ab47,
0x008b8b, 0x000000, 0x000000, 0x000000,
0xf8f8f8, 0x3fbfff, 0x6b88ff, 0x9878f8,
0xf878f8, 0xf85898, 0xf87858, 0xffa347,
0xf8b800, 0xb8f818, 0x5bdb57, 0x58f898,
0x00ebdb, 0x787878, 0x000000, 0x000000,
0xffffff, 0xa7e7ff, 0xb8b8f8, 0xd8b8f8,
0xf8b8f8, 0xfba7c3, 0xf0d0b0, 0xffe3ab,
0xfbdb7b, 0xd8f878, 0xb8f8b8, 0xb8f8d8,
0x00ffff, 0xf8d8f8, 0x000000, 0x000000 }
},
{ "crashman", "CrashMan's NES palette",
{ 0x585858, 0x001173, 0x000062, 0x472bbf,
0x970087, 0x910009, 0x6f1100, 0x4c1008,
0x371e00, 0x002f00, 0x005500, 0x004d15,
0x002840, 0x000000, 0x000000, 0x000000,
0xa0a0a0, 0x004499, 0x2c2cc8, 0x590daa,
0xae006a, 0xb00040, 0xb83418, 0x983010,
0x704000, 0x308000, 0x207808, 0x007b33,
0x1c6888, 0x000000, 0x000000, 0x000000,
0xf8f8f8, 0x267be1, 0x5870f0, 0x9878f8,
0xff73c8, 0xf060a8, 0xd07b37, 0xe09040,
0xf8b300, 0x8cbc00, 0x40a858, 0x58f898,
0x00b7bf, 0x787878, 0x000000, 0x000000,
0xffffff, 0xa7e7ff, 0xb8b8f8, 0xd8b8f8,
0xe6a6ff, 0xf29dc4, 0xf0c0b0, 0xfce4b0,
0xe0e01e, 0xd8f878, 0xc0e890, 0x95f7c8,
0x98e0e8, 0xf8d8f8, 0x000000, 0x000000 }
},
{ "mess", "palette from the MESS NES driver",
{ 0x747474, 0x24188c, 0x0000a8, 0x44009c,
0x8c0074, 0xa80010, 0xa40000, 0x7c0800,
0x402c00, 0x004400, 0x005000, 0x003c14,
0x183c5c, 0x000000, 0x000000, 0x000000,
0xbcbcbc, 0x0070ec, 0x2038ec, 0x8000f0,
0xbc00bc, 0xe40058, 0xd82800, 0xc84c0c,
0x887000, 0x009400, 0x00a800, 0x009038,
0x008088, 0x000000, 0x000000, 0x000000,
0xfcfcfc, 0x3cbcfc, 0x5c94fc, 0x4088fc,
0xf478fc, 0xfc74b4, 0xfc7460, 0xfc9838,
0xf0bc3c, 0x80d010, 0x4cdc48, 0x58f898,
0x00e8d8, 0x000000, 0x000000, 0x000000,
0xfcfcfc, 0xa8e4fc, 0xc4d4fc, 0xd4c8fc,
0xfcc4fc, 0xfcc4d8, 0xfcbcb0, 0xfcd8a8,
0xfce4a0, 0xe0fca0, 0xa8f0bc, 0xb0fccc,
0x9cfcf0, 0x000000, 0x000000, 0x000000 }
},
{ "zaphod-cv", "Zaphod's VS Castlevania palette",
{ 0x7f7f7f, 0xffa347, 0x0000bf, 0x472bbf,
0x970087, 0xf85898, 0xab1300, 0xf8b8f8,
0xbf0000, 0x007800, 0x006b00, 0x005b00,
0xffffff, 0x9878f8, 0x000000, 0x000000,
0xbfbfbf, 0x0078f8, 0xab1300, 0x6b47ff,
0x00ae00, 0xe7005b, 0xf83800, 0x7777ff,
0xaf7f00, 0x00b800, 0x00ab00, 0x00ab47,
0x008b8b, 0x000000, 0x000000, 0x472bbf,
0xf8f8f8, 0xffe3ab, 0xf87858, 0x9878f8,
0x0078f8, 0xf85898, 0xbfbfbf, 0xffa347,
0xc800c8, 0xb8f818, 0x7f7f7f, 0x007800,
0x00ebdb, 0x000000, 0x000000, 0xffffff,
0xffffff, 0xa7e7ff, 0x5bdb57, 0xe75f13,
0x004358, 0x0000ff, 0xe7005b, 0x00b800,
0xfbdb7b, 0xd8f878, 0x8b1700, 0xffe3ab,
0x00ffff, 0xab0023, 0x000000, 0x000000 }
},
{ "zaphod-smb", "Zaphod's VS SMB palette",
{ 0x626a00, 0x0000ff, 0x006a77, 0x472bbf,
0x970087, 0xab0023, 0xab1300, 0xb74800,
0xa2a2a2, 0x007800, 0x006b00, 0x005b00,
0xffd599, 0xffff00, 0x009900, 0x000000,
0xff66ff, 0x0078f8, 0x0058f8, 0x6b47ff,
0x000000, 0xe7005b, 0xf83800, 0xe75f13,
0xaf7f00, 0x00b800, 0x5173ff, 0x00ab47,
0x008b8b, 0x000000, 0x91ff88, 0x000088,
0xf8f8f8, 0x3fbfff, 0x6b0000, 0x4855f8,
0xf878f8, 0xf85898, 0x595958, 0xff009d,
0x002f2f, 0xb8f818, 0x5bdb57, 0x58f898,
0x00ebdb, 0x787878, 0x000000, 0x000000,
0xffffff, 0xa7e7ff, 0x590400, 0xbb0000,
0xf8b8f8, 0xfba7c3, 0xffffff, 0x00e3e1,
0xfbdb7b, 0xffae00, 0xb8f8b8, 0xb8f8d8,
0x00ff00, 0xf8d8f8, 0xffaaaa, 0x004000 }
},
{ "vs-drmar", "VS Dr. Mario palette",
{ 0x5f97ff, 0x000000, 0x000000, 0x47009f,
0x00ab00, 0xffffff, 0xabe7ff, 0x000000,
0x000000, 0x000000, 0x000000, 0x000000,
0xe7005b, 0x000000, 0x000000, 0x000000,
0x5f97ff, 0x000000, 0x000000, 0x000000,
0x000000, 0x8b7300, 0xcb4f0f, 0x000000,
0xbcbcbc, 0x000000, 0x000000, 0x000000,
0x000000, 0x000000, 0x000000, 0x000000,
0x00ebdb, 0x000000, 0x000000, 0x000000,
0x000000, 0xff9b3b, 0x000000, 0x000000,
0x83d313, 0x000000, 0x3fbfff, 0x000000,
0x0073ef, 0x000000, 0x000000, 0x000000,
0x00ebdb, 0x000000, 0x000000, 0x000000,
0x000000, 0x000000, 0xf3bf3f, 0x000000,
0x005100, 0x000000, 0xc7d7ff, 0xffdbab,
0x000000, 0x000000, 0x000000, 0x000000 }
},
{ "vs-cv", "VS Castlevania palette",
{ 0xaf7f00, 0xffa347, 0x008b8b, 0x472bbf,
0x970087, 0xf85898, 0xab1300, 0xf8b8f8,
0xf83800, 0x007800, 0x006b00, 0x005b00,
0xffffff, 0x9878f8, 0x00ab00, 0x000000,
0xbfbfbf, 0x0078f8, 0xab1300, 0x6b47ff,
0x000000, 0xe7005b, 0xf83800, 0x6b88ff,
0xaf7f00, 0x00b800, 0x6b88ff, 0x00ab47,
0x008b8b, 0x000000, 0x000000, 0x472bbf,
0xf8f8f8, 0xffe3ab, 0xf87858, 0x9878f8,
0x0078f8, 0xf85898, 0xbfbfbf, 0xffa347,
0x004358, 0xb8f818, 0x7f7f7f, 0x007800,
0x00ebdb, 0x000000, 0x000000, 0xffffff,
0xffffff, 0xa7e7ff, 0x5bdb57, 0x6b88ff,
0x004358, 0x0000ff, 0xe7005b, 0x00b800,
0xfbdb7b, 0xffa347, 0x8b1700, 0xffe3ab,
0xb8f818, 0xab0023, 0x000000, 0x007800 }
},
/* The default VS palette must be the last entry in the array */
{ "vs-smb", "VS SMB/VS Ice Climber palette",
{ 0xaf7f00, 0x0000ff, 0x008b8b, 0x472bbf,
0x970087, 0xab0023, 0x0000ff, 0xe75f13,
0xbfbfbf, 0x007800, 0x5bdb57, 0x005b00,
0xf0d0b0, 0xffe3ab, 0x00ab00, 0x000000,
0xbfbfbf, 0x0078f8, 0x0058f8, 0x6b47ff,
0x000000, 0xe7005b, 0xf83800, 0xf87858,
0xaf7f00, 0x00b800, 0x6b88ff, 0x00ab47,
0x008b8b, 0x000000, 0x000000, 0x3fbfff,
0xf8f8f8, 0x006b00, 0x8b1700, 0x9878f8,
0x6b47ff, 0xf85898, 0x7f7f7f, 0xe7005b,
0x004358, 0xb8f818, 0x0078f8, 0x58f898,
0x00ebdb, 0xfbdb7b, 0x000000, 0x000000,
0xffffff, 0xa7e7ff, 0xb8b8f8, 0xf83800,
0xf8b8f8, 0xfba7c3, 0xffffff, 0x00ffff,
0xfbdb7b, 0xffa347, 0xb8f8b8, 0xb8f8d8,
0xb8f818, 0xf8d8f8, 0x000000, 0x007800 }
}
};
unsigned char *palremap = 0, *paldata = 0;
unsigned int *NES_palette = 0;
int monochrome = 0;
void (*oldtraphandler)(int);
/****************************************************************************/
void
traphandler (int signum)
{
if (signal (SIGTRAP, &traphandler) == SIG_ERR)
{
perror ("signal");
}
if (ignorebadinstr)
return;
/* this will only affect subsequently-compiled code */
ignorebadinstr = 1;
fprintf (stderr, "======================================================\n");
fprintf (stderr, "Trap/Breakpoint: Unimplemented instruction detected\n");
fprintf (stderr, "\n");
fprintf (stderr, "To ignore such instructions, run " PRETTY_NAME " with the\n");
fprintf (stderr, "--ignore-unhandled option.\n");
fprintf (stderr, "\n");
fprintf (stderr, "To find out which instruction caused the problem,\n");
fprintf (stderr, "run " PRETTY_NAME " with the --disassemble option, and then\n");
fprintf (stderr, "run GDB on the resulting core-file to find out which\n");
fprintf (stderr, "recompiled instruction caused the problem. Please\n");
fprintf (stderr, "send a description of the problem (and, if possible,\n");
fprintf (stderr, "a fix) to <tuxnes-dev@dod.hpi.net>. Include this\n");
fprintf (stderr, "information in your description:\n");
fprintf (stderr, "\n");
fprintf (stderr, PRETTY_NAME " release: %s-%s\n", PACKAGE, VERSION);
fprintf (stderr, "Built on %s at %s\n", __DATE__, __TIME__);
fprintf (stderr, "\n");
fprintf (stderr, "OS version and platform:\n");
fflush (stderr);
system ("uname -a >&2");
fprintf (stderr, "======================================================\n");
fflush (stderr);
}
/****************************************************************************/
void
quit(void)
{
int fd;
int x;
/* Clean up char pointers */
free (tuxnesdir);
free (basefilename);
/* Save ram */
for (x = 0; ((long long *) (RAM + 0x6000))[x] == 0; x++)
if (x >= 1023)
exit (0); /* Nothing to save */
fd = open (savefile, O_CREAT | O_RDWR, 0666 & ~mask);
if (fd > 0)
{
write (fd, RAM + 0x6000, 8192);
close (fd);
exit (0);
}
perror (savefile);
exit (1);
}
/****************************************************************************/
static void
help(char *progname, char *topic)
{
int i;
void (*dfn)(int) = (void (*)(int))0;
char *desc = 0;
int dfns = 0;
int len;
int terse = 0;
if (! topic)
topic = "";
len = strlen(topic);
if ((! *topic) ||
((*topic == '-') &&
! topic[1])) {
terse = 1;
if (isatty (fileno (stdout)))
topic = topics[0].name;
else
topic = topics[sizeof(topics)/sizeof(*topics) - 1].name;
} else if (*topic == '-') {
terse = 1;
topic ++;
len --;
}
for (i = 0; i < sizeof(topics)/sizeof(*topics); i++)
if (terse ? topics[i].is_terse : 1)
{
if (!strcmp (topics[i].name, topic))
{
desc = topics[i].desc;
dfn = topics[i].dfn;
dfns = 1;
break;
}
else if (!strncmp (topics[i].name, topic, len))
{
desc = topics[i].desc;
dfn = topics[i].dfn;
dfns++;
}
}
if (dfns != 1) {
if (dfns)
fprintf (stderr, "%s: help topic name `%s' is ambiguous\n",
progname, topic);
else
fprintf (stderr, "%s: unrecognized help topic name `%s'\n",
progname, topic);
fprintf (stderr, USAGE, progname);
exit (2);
}
printf (USAGE, progname);
if (dfn)
{
printf ("%s:\n", desc);
dfn (terse);
}
else
{
for (i = 0; i < sizeof(topics)/sizeof(*topics); i++)
if (topics[i].dfn && (terse ? topics[i].is_terse : 1))
{
printf ("%s:\n", topics[i].desc);
topics[i].dfn (terse);
}
}
}
/****************************************************************************/
static void
help_help(int terse)
{
int i;
for (i = 0; i < sizeof(topics)/sizeof(*topics); i++)
printf (" --help=%-9s %s%s\n",
topics[i].name,
topics[i].desc,
(topics[i].dfn == help_help) ? " (this list)" : "");
for (i = 0; i < sizeof(topics)/sizeof(*topics); i++)
if (topics[i].is_terse)
printf (" --help=-%-8s Concise version of --help=%s\n",
topics[i].name,
topics[i].name);
}
/****************************************************************************/
static void
help_options(int terse)
{
printf (" -v, --verbose Verbose output\n");
printf (" -f, --fix-mapper Use only the low four bits of the mapper number\n");
printf (" -M, --mapper=... Specify mapper to use\n");
printf (" -g, --gamegenie=... Game Genie code\n");
printf (" -H, --show-header Show iNES header bytes\n");
printf (" -d, --disassemble Disassemble\n");
printf (" -l, --link Link branches optimization (may improve speed)\n");
printf (" -i, --ignore-unhandled\n");
printf (" Ignore unhandled instructions (don't breakpoint)\n");
printf (" -m, --mirror=... Manually specify type of mirroring\n");
printf (" h = Use horizontal mirroring\n");
printf (" v = Use vertical mirroring\n");
printf (" s = Use single-screen mirroring\n");
printf (" n = Use no mirroring\n");
printf (" -G, --geometry=WxH Specify window/screen geometry\n");
printf (" --display=ID Specify display/driver ID\n");
printf (" -E, --enlarge[=NUM] Enlarge by a factor of NUM (default: 2)\n");
printf (" -L, --scanlines[=%%] Set alternate scanline intensity (default: 0%%)\n");
printf (" -S, --static-color Force static color allocation (prevents flicker)\n");
printf (" -r, --renderer=... Select a rendering engine (default: auto)\n");
for (renderer = renderers; renderer->name; renderer++)
printf (" %-10s %s\n",
renderer->name,
renderer->fullname);
printf (" -I, --in-root Display in root window\n");
printf (" -K, --sticky-keys Hit keys once to press buttons, again to release\n");
printf (" -X, --swap-inputs Swap P1 and P2 controls\n");
if (terse)
{
printf (" -1, --js1[=FILE] Use P1 joystick FILE (default: %s)\n", JS1);
printf (" -2, --js2[=FILE] Use P2 joystick FILE (default: %s)\n", JS2);
}
}
/****************************************************************************/
static void
help_sound(int terse)
{
printf (" -s, --sound[=FILE] Append sound data to FILE (default: %s)\n", DSP);
printf (" (specify sound file as mute or none for no sound, e.g., -smute)\n");
printf (" -F, --format=... Use the specified sound sample format (default: %s)\n",
sample_format_name);
for (sample_format = sample_formats; sample_format->name; sample_format++)
printf (" %-10s %s\n",
sample_format->name,
sample_format->fullname);
printf (" -R, --soundrate=NUM Set sound sample rate to NUM Hz (default: %d)\n",
sound_config.audiorate);
printf (" -D, --delay=NUM Resynchronize if sound delay exceeds NUM seconds\n");
/* printf (" -e, --echo Simulate echoing\n");
*/
}
/****************************************************************************/
static void
help_palettes(int terse)
{
int i;
printf (" -p, --palfile=FILE Load palette data from FILE\n");
printf (" -P, --palette=... Use a specified built-in palette (default: %s)\n",
palettes[0].name);
for (i = 0; i < (sizeof (palettes) / sizeof (*palettes)); i++)
printf (" %-10s %s\n", palettes[i].name, palettes[i].desc);
printf (" -b, --bw Convert palette to grayscale\n");
#ifdef HAVE_LIBM
printf (" -N, --ntsc-palette[=[HUE][,TINT]]\n"
" Use Kevin Horton's dynamic NTSC palette generator\n"
" with given hue angle (default: %g degrees) and tint\n"
" level (default: %g)\n", hue, tint);
#endif /* HAVE_LIBM */
}
/****************************************************************************/
static void
help_synonyms(int terse)
{
printf (" -c, --controls Equivalent to --help=controls\n");
printf (" -h, --help Equivalent to --help=-%s if stdout is a tty,\n",
topics[0].name);
printf (" otherwise equivalent to --help=-%s\n",
topics[sizeof(topics)/sizeof(*topics) - 1].name);
printf (" -j, --joystick=FILE Equivalent to --js1=FILE\n");
printf (" -o, --old Equivalent to --renderer=old\n");
printf (" -V, --version Same information as --help=version\n");
}
/****************************************************************************/
static void
help_version(int terse)
{
printf ("%s %s\n\n%s",
PACKAGE, VERSION,
"Copyright \xa9 1999-2001, The TuxNES Team\n"
"\n"
"This program is free software; you can redistribute it and/or modify\n"
"it under the terms of the GNU General Public License as published by\n"
"the Free Software Foundation; either version 2 of the License, or\n"
"(at your option) any later version.\n"
"\n"
"This program is distributed in the hope that it will be useful,\n"
"but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
"GNU General Public License for more details.\n"
"\n"
"You should have received a copy of the GNU General Public License\n"
"along with this program; if not, write to the Free Software\n"
"Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\n"
"\n"
"Written by The TuxNES Team; see the AUTHORS file.\n"
"Based on Nestra v0.66 by Quor\n"
);
}
/****************************************************************************/
static void
help_controls(int terse)
{
printf (" -1, --js1[=FILE] Use P1 joystick FILE (default: %s)\n", JS1);
printf (" -2, --js2[=FILE] Use P2 joystick FILE (default: %s)\n", JS2);
printf (" -J, --joystick-map=MAPSPEC Reassign joystick button bindings\n"
" MAPSPEC:\n"
" Specify joystick, then indicate buttons with 'B' and axes with 'A' in\n"
" sequence: A, B, Start, Select, Left, Right, Up, Down, Pause\n"
" Multiple buttons may be bound to a single emulator control.\n"
" Default bindings will be used if no buttons are specified.\n"
" Examples:\n"
" 1:B0,B1,B5,B2,A0,A1,B4\n"
" 2:B0,B1,B5,B2,B10,B12,B11,B13,B4\n"
" 1:B0B8,B1B9,B5,B2\n"
" 2:,,,,A2,A3\n"
);
printf ("\n");
printf (" Keyboard:\n");
printf (" Arrows - Move (P1)\n");
printf (" A, C or Space\n");
printf (" - A button (P1)\n");
printf (" Z, X or D - B button (P1)\n");
printf (" Tab - Select button (P1)\n");
printf (" Enter - Start button (P1)\n");
printf (" H, J, K, L - Move (P2)\n");
printf (" B - A button (P2)\n");
printf (" V - B button (P2)\n");
printf (" F - Select button (P2)\n");
printf (" G - Start button (P2)\n");
printf (" BackSpace - Reset game\n");
printf (" P, Pause - Pause or resume emulation\n");
printf (" ESC - Exit game\n");
printf (" 0-8, ` - Adjust emulation speed\n");
printf (" 0 stops the game, ` runs the game at half speed\n");
printf (" 1 runs at normal speed, 2..8 runs at 2..8x normal speed\n");
printf (" S, F7, PrintScreen\n"
" - Capture screenshot\n"
#ifdef HAVE_X
" Under X11, save to ~/.tuxnes/snap\?\?\?\?.xpm\n"
#endif /* HAVE_X */
#ifdef HAVE_GGI
" Under GGI, save to ~/.tuxnes/snap\?\?\?\?.ppm\n"
#endif /* HAVE_GGI */
#ifdef HAVE_W
" Under W, save to ~/.tuxnes/snap\?\?\?\?.p8m\n"
#endif /* HAVE_W */
);
printf (" (Note: XPM/PPM support must be installed and compiled\n");
printf (" for this to work; see the README file for more info.)\n");
printf ("\n");
printf (" Keypad:\n");
printf (" Arrows/12346789 - Move (P1)\n");
printf (" Space/Begin/5, Delete/Decimal\n");
printf (" - A button (P1)\n");
printf (" Insert/0 - B button (P1)\n");
printf (" Add - Select button (P1)\n");
printf (" Enter - Start button (P1)\n");
printf ("\n");
printf (" Joystick Axes:\n");
printf (" Axis 0 - Horizontal movement\n");
printf (" Axis 1 - Vertical movement\n");
printf (" Axis 2 - (unused)\n");
printf (" Axis 3 - (unused)\n");
printf (" Axis 4 - Horizontal movement\n");
printf (" Axis 5 - Vertical movement\n");
printf ("\n");
printf (" Joystick Buttons (2-button joysticks):\n");
printf (" Button 0 - B button\n");
printf (" Button 1 - A button\n");
printf ("\n");
printf (" Joystick Buttons (other joysticks):\n");
printf (" Button 0 - B button\n");
printf (" Button 1 - Select button\n");
printf (" Button 2 - A button\n");
printf (" Button 3 - Start button\n");
printf (" Button 4 - A button\n");
printf (" Button 5 - B button\n");
printf (" Button 6 - Select button\n");
printf (" Button 7 - Start button\n");
printf (" Button 8 - Pause or resume emulation\n");
printf ("\n");
#ifdef HAVE_X
printf (" Extra keys for old X11 renderer (--renderer=old)\n");
printf (" F1 - Disable background\n");
printf (" F2 - Re-enable background\n");
printf (" F3 - Disable sprites\n");
printf (" F4 - Re-enable sprites\n");
printf (" F5 - Alternate tiles\n");
printf (" F6 - Default tiles\n");
printf (" F8 - Flush tile cache\n");
printf ("\n");
#endif /* HAVE_X */
printf (" Extra Keys for VS UniSystem games:\n");
printf (" [/{ - Insert Coin (Left)\n");
printf (" ]/} - Insert Coin (Right)\n");
printf (" |/\\ - Service Credit\n");
printf (" Q/q - Toggle Dipswitch #1\n");
printf (" W/w - Toggle Dipswitch #2\n");
printf (" E/e - Toggle Dipswitch #3\n");
printf (" R/r - Toggle Dipswitch #4\n");
printf (" T/t - Toggle Dipswitch #5\n");
printf (" Y/y - Toggle Dipswitch #6\n");
printf (" U/u - Toggle Dipswitch #7\n");
printf (" I/i - Toggle Dipswitch #8\n");
}
/****************************************************************************/
static void
restoresavedgame(void)
{
int fd, result, x;
char buffer[1024];
struct stat statbuf;
*savefile = 0;
strncpy (buffer, homedir, 1010);
if (*buffer != 0 && buffer[strlen (buffer) - 1] == '/')
buffer[strlen (buffer) - 1] = 0;
strcat (buffer, "/.tuxnes");
result = stat (buffer, &statbuf);
if (result == 0)
{
for (x = strlen (filename) - 1; x > 0 && filename[x - 1] != '/';
x--);
strcat (buffer, "/");
strncat (buffer, filename + x, 1019 - strlen (buffer));
strcat (buffer, ".sav");
strcpy (savefile, buffer);
}
if (!*savefile)
{
strncpy (buffer, filename, 1013);
savefile[1013] = 0;
for (x = strlen (buffer); x >= 0 && buffer[x] != '/'; x--)
buffer[x] = 0;
strcat (buffer, "tuxnes.tmp");
result = open (buffer, O_CREAT | O_RDWR, 0666 & ~mask);
unlink (buffer);
if (result < 0)
{
buffer[strlen (buffer) - 10] = 0;
fprintf (stderr, "Warning: can not write to directory %s\n", buffer);
fprintf (stderr,
"Create directory ~/.tuxnes if you want to save games there instead.\n");
}
else
close (result);
strncpy (savefile, filename, 1019);
savefile[1019] = 0;
strcat (savefile, ".sav");
}
if ((fd = open (savefile, O_RDWR, 0666 & ~mask)) >= 0)
{
read (fd, RAM + 0x6000, 8192);
close (fd);
}
else
{
/* In this special case, there is a ~/.tuxnes directory but no save
file in it. If there is a save file in the romfile directory,
copy it into the ~/.tuxnes directory. */
strncpy (buffer, filename, 1019);
buffer[1019] = 0;
strcat (buffer, ".sav");
if ((fd = open (buffer, O_RDWR, 0666 & ~mask)) >= 0)
{
read (fd, RAM + 0x6000, 8192);
close (fd);
}
else
{
/* Finally, if no save file is found either place, look for a
iNES-type save file. */
strncpy (buffer, filename, 1023);
buffer[1023] = 0;
buffer[strlen (buffer) - 4] = 0;
strcat (buffer, ".sav");
fd = open (buffer, O_RDWR, 0666 & ~mask);
read (fd, RAM + 0x6000, 8192);
close (fd);
}
}
}
/****************************************************************************/
void
loadpal(char *palfile)
{
/* for the raw palette data */
unsigned char palette[192];
int pen, pens;
int len;
int fd = -1;
if (unisystem && !palremap)
NES_palette = palettes[sizeof (palettes) / sizeof (*palettes) - 1].data;
else
NES_palette = palettes[0].data;
if (palfile)
{
char *buffer;
len = strlen(palfile) + 1;
if (! (buffer = malloc(len)))
{
perror (__FUNCTION__ ": malloc");
return;
}
memcpy (buffer, palfile, len);
palfile = buffer;
}
if (palfile)
{
if ((fd = open (palfile, O_RDONLY)) < 0)
{
perror (palfile);
free (palfile);
palfile = 0;
}
}
if (paldata && !palfile)
{
char *buffer;
len = strlen(filename) + 1;
if (! (buffer = malloc(len)))
{
perror (__FUNCTION__ ": malloc");
return;
}
memcpy (buffer, filename, len);
palfile = buffer;
}
if (!palfile)
{
if (palremap)
return;
if (!(palfile = malloc ((len = strlen (filename)) + 11)))
{
perror (__FUNCTION__ ": malloc");
return;
}
strcpy (palfile, filename);
/* .NES -> .pal */
if ((len > 4)
&& (palfile[len - 4] == '.')
&& (tolower(palfile[len - 3]) == tolower('N'))
&& (tolower(palfile[len - 2]) == tolower('E'))
&& (tolower(palfile[len - 1]) == tolower('S')))
palfile[len -= 4] = '\0';
strcat (palfile + len, ".pal");
if ((fd = open (palfile, O_RDONLY)) < 0) {
if ((fd = open ("tuxnes.pal", O_RDONLY)) < 0)
{
while (len && (palfile[len] != '/'))
len--;
palfile[len] = '/';
palfile[len + 1] = '\0';
strcat (palfile + len, "tuxnes.pal");
if ((fd = open (palfile, O_RDONLY)) < 0)
{
free (palfile);
return;
}
}
else
{
strcpy (palfile, "tuxnes.pal");
}
}
}
if (verbose)
{
fprintf (stderr, "Reading palette from %s\n", palfile);
}
/* read the raw palette data -- it's ok if it's short */
if ((fd < 0) && paldata)
{
memcpy (palette, paldata, 192);
pens = 64;
}
else {
char buf[9];
ssize_t count;
if ((count = read (fd, buf, 9)) < 0)
{
perror (palfile);
free (palfile);
return;
}
/* handler for iNES-style hexadecimal palette files */
if ((count == 9)
&& isxdigit(buf[0])
&& isxdigit(buf[1])
&& (buf[2] == ',')
&& isxdigit(buf[3])
&& isxdigit(buf[4])
&& (buf[5] == ',')
&& isxdigit(buf[6])
&& isxdigit(buf[7])
&& ((buf[8] == '\r')
|| (buf[8] == '\n')
|| isspace(buf[8])))
{
for (pens = 0; pens < 64; pens ++)
{
int r, g, b;
if (pens)
{
if ((count = read (fd, buf, 9)) < 0)
{
perror (palfile);
free (palfile);
return;
}
if (count == 8)
{
buf[count ++] = '\n';
}
if (count < 9) break;
while ((*buf == '\r')
|| (*buf == '\n')
|| isspace (*buf))
{
memmove (buf, buf + 1, 8);
if ((count = read (fd, buf + 8, 1)) < 0)
{
perror (palfile);
free (palfile);
return;
}
}
if (count < 1) break;
if (! (isxdigit(buf[0])
&& isxdigit(buf[1])
&& (buf[2] == ',')
&& isxdigit(buf[3])
&& isxdigit(buf[4])
&& (buf[5] == ',')
&& isxdigit(buf[6])
&& isxdigit(buf[7])
&& ((buf[8] == '\r')
|| (buf[8] == '\n')
|| isspace(buf[8])))) break;
}
sscanf (buf, "%x,%x,%x", &r, &g, &b);
palette [pens * 3] = r;
palette [pens * 3 + 1] = g;
palette [pens * 3 + 2] = b;
}
}
/* handler for Nesticle-style raw palette files */
else
{
if (count) memcpy ((char *) palette, buf, count);
if ((pens = read (fd, (char *) palette + count, 192 - count) / 3) < 0)
{
perror (palfile);
free (palfile);
return;
}
pens += count / 3;
}
close (fd);
}
/* convert the palette */
if (!(NES_palette = (unsigned int *) malloc (64 * sizeof (*NES_palette))))
{
perror ("malloc");
free (palfile);
return;
}
for (pen = 0; pen < 64; pen++)
{
if (pen < pens)
{
NES_palette[pen] =
(((unsigned long) palette[pen * 3]) << 16) |
(((unsigned long) palette[pen * 3 + 1]) << 8) |
(((unsigned long) palette[pen * 3 + 2]));
}
else
{
if (unisystem)
NES_palette[pen] = palettes[sizeof (palettes) / sizeof
(*palettes) - 1].data[pen];
else
NES_palette[pen] = palettes[0].data[pen];
}
/* dump the loaded palette to stdout in C format, for adding to palettes[] */
/* printf ("0x%6.6x%s", NES_palette[pen], */
/* (pen < 63) ? ((pen + 1) % 4) ? ", " : ",\n" : "\n"); */
}
/* clean up */
free (palfile);
}
/****************************************************************************/
static void
js_set_nesmaps(char *mapspec)
{
enum buttonaxis_state { none = 0, button, axis };
int js_dev = -1;
int nes_button_i;
int nes_button_inc = 1;
int expect_buttonaxis = none;
int buttonaxis = -1;
unsigned char *s = (unsigned char *) mapspec;
while( *s != '\0' )
if( isdigit( *s ) && (*(s + 1) == ':' ) )
{
js_dev = (*s - '1');
++s;
if( (js_dev < 0) || (js_dev > 2) )
{
fprintf( stderr,
"ignoring joystick map (limited to joystick 1 and 2)\n" );
continue;
}
nes_button_i = 0;
while( (*(++s) != '\0') || (expect_buttonaxis) )
{
if( expect_buttonaxis != none )
{
if( isdigit( *s ) )
{
buttonaxis *= 10;
buttonaxis += (*s - '0');
continue;
}
else
{
if( expect_buttonaxis == button )
{
if( buttonaxis < 2 )
js_usermapped2button[js_dev][buttonaxis] = 1;
expect_buttonaxis = none;
if( buttonaxis < JS_MAX_BUTTONS )
js_nesmaps[js_dev].button[buttonaxis] = js_mapsequence[nes_button_i];
}
else /* (expect_buttonaxis == axis) */
{
expect_buttonaxis = none;
nes_button_inc = 2;
if( buttonaxis < JS_MAX_AXES )
if( (nes_button_i + 1) < JS_MAX_NES_BUTTONS )
{
js_nesmaps[js_dev].axis[buttonaxis].neg
= js_mapsequence[nes_button_i];
js_nesmaps[js_dev].axis[buttonaxis].pos
= js_mapsequence[nes_button_i + 1];
}
}
if( *s == '\0' )
break;
}
}
if( *s == ',' )
if( (nes_button_i += nes_button_inc) < JS_MAX_NES_BUTTONS )
nes_button_inc = 1;
else
break;
else if( *s == '+' )
break;
else if ( expect_buttonaxis == none )
{
if( (*s == 'b') || (*s == 'B') )
expect_buttonaxis = button;
else if( (*s == 'a') || (*s == 'A') )
expect_buttonaxis = axis;
else
continue;
if( isdigit( *(s+1) ) )
buttonaxis = 0;
else
expect_buttonaxis = none;
}
}
}
else
++s;
}
/****************************************************************************/
int
main (int argc, char **argv)
{
int x;
int r;
int i, basestart, baseend; /* these are for working out the base filename */
int audiofd;
int size;
int cmirror;
DIR *dir; /* for checking if .tuxnes directory is present */
char *mkdircall; /* buffer for /bin/mkdir call */
char *palfile = 0; /* palette file */
char *ggcode;
int ggret, parseret;
/* for the Game Genie */
int address, data, compare;
#ifdef HAVE_LIBZ
gzFile rom_file_gz = NULL;
unzFile rom_file_zip = NULL;
char *extension = NULL;
#else
int romfd;
#endif
ggcode = NULL;
/* set up the mapper arrays */
InitMapperSubsystem();
/* find the file creation mask */
mask = umask (022);
umask (mask);
/* Find the user's home directory */
if (!(homedir = getenv ("HOME")))
{
struct passwd *pwent = getpwuid (getuid ());
if (pwent)
homedir = pwent->pw_dir;
if (!homedir)
homedir = "";
}
/* Make sure there's a ~/.tuxnes directory */
if ((tuxnesdir = malloc (strlen (homedir) + strlen ("/.tuxnes/") + 1))
== NULL)
{
fprintf (stderr, "Out of memory\n");
return (1);
}
sprintf (tuxnesdir, "%s%s", homedir, "/.tuxnes/");
dir = opendir (tuxnesdir);
if ((dir == NULL)
&& (errno == ENOENT))
{
if ((mkdircall = malloc (strlen ("/bin/mkdir ") + strlen (tuxnesdir) + 1))
== NULL)
{
fprintf (stderr, "Out of memory\n");
return (1);
}
sprintf (mkdircall, "/bin/mkdir %s", tuxnesdir);
system (mkdircall);
free (mkdircall);
if ((dir = opendir (tuxnesdir)) == NULL)
{
fprintf (stderr, "Cannot open directory %s\n", tuxnesdir);
return (1);
}
}
else if ((dir == NULL)
&& (errno == ENOENT))
{
fprintf (stderr, "Cannot open directory %s\n", tuxnesdir);
return (1);
}
else
{
closedir (dir);
}
/* initialize variables */
verbose = 0;
cmirror = 0;
dolink = 0;
disassemble = 0;
gamegenie = 0;
/* check for the default output device */
if ((audiofd = open (DSP, O_CREAT | O_WRONLY | O_APPEND)) < 0)
sound_config.audiofile = NULL;
else {
sound_config.audiofile = DSP;
close(audiofd);
}
/*
* Parse args
* the following parsing code is adapted from the getopt(3) man page
* the long option handling has been removed, since having duplicate code
* for equivalent long and short options can only lead to inconsistency
*/
while (1)
{
int option_index = 0;
static struct option long_options[] =
{
{"help", 2, 0, 'h'},
{"controls", 0, 0, 'c'},
{"joystick", 1, 0, 'j'},
{"js1", 2, 0, '1'},
{"js2", 2, 0, '2'},
{"joystick-map", 1, 0, 'J'},
{"verbose", 0, 0, 'v'},
{"old", 0, 0, 'o'},
{"ignore-unhandled", 0, 0, 'i'},
{"link", 0, 0, 'l'},
{"disassemble", 0, 0, 'd'},
{"gamegenie", 1, 0, 'g'},
{"mirror", 1, 0, 'm'},
{"show-header", 0, 0, 'H'},
{"mapper", 1, 0, 'M'},
{"fix-mapper", 0, 0, 'f'},
{"sound", 2, 0, 's'},
{"static-color", 0, 0, 'S'},
{"static-colour", 0, 0, 'S'},
{"format", 1, 0, 'F'},
{"soundrate", 1, 0, 'R'},
{"delay", 1, 0, 'D'},
{"palfile", 1, 0, 'p'},
{"palette", 1, 0, 'P'},
{"version", 0, 0, 'V'},
{"enlarge", 2, 0, 'E'},
{"geometry", 1, 0, 'G'},
{"display", 1, 0, 0},
{"scanlines", 2, 0, 'L'},
{"renderer", 1, 0, 'r'},
{"echo", 0, 0, 'e'},
{"swap-inputs", 0, 0, 'X'},
{"sticky-keys", 0, 0, 'K'},
{"in-root", 0, 0, 'I'},
{"bw", 0, 0, 'b'},
#ifdef HAVE_LIBM
{"ntsc-palette", 2, 0, 'N'},
#endif
{0, 0, 0, 0}
};
parseret = getopt_long (argc, argv,
#ifdef HAVE_LIBM
"N::"
#endif
"bcdfhHg:j:J:lom:M:F:R:p:P:vV1::2::s::SL::r:E::D:ieUG:XKI",
long_options,
&option_index);
if (parseret == -1)
break;
switch (parseret)
{
case 'b':
monochrome = 1;
break;
case 'c':
help (*argv, "controls");
exit (0);
break;
case 'X':
swap_inputs = 1;
break;
case 'K':
renderer_config.sticky_keys = 1;
break;
case 'I':
renderer_config.inroot = 1;
break;
case 'd':
disassemble = 1;
break;
case 'f':
dirtyheader = 1;
break;
case 'h':
help (*argv, optarg);
exit (0);
break;
case 'H':
showheader = 1;
break;
case 'g':
gamegenie = 1;
ggcode = optarg;
break;
case 'j':
case '1':
jsdevice[0] = optarg ? optarg : JS1;
break;
case '2':
jsdevice[1] = optarg ? optarg : JS2;
break;
case 'J':
js_set_nesmaps( optarg );
break;
case 'l':
dolink = 1;
break;
case 'o':
rendname = "old";
break;
case 'i':
ignorebadinstr = 1;
break;
case 'm':
switch (*optarg ? optarg[1] ? 0 : *optarg : 0)
{
case 'h':
cmirror = 1;
break;
case 'v':
cmirror = 2;
break;
case 's':
cmirror = 3;
break;
case 'n':
cmirror = 4;
break;
default:
fprintf (stderr, "bad mirror argument: %s\n", optarg);
exit (2);
break;
}
break;
case 'M':
mapperoverride = 1;
MAPPERNUMBER = atoi (optarg);
break;
case 'v':
verbose = 1;
break;
case 's':
if (optarg &&
((strcmp (optarg, "mute") == 0)
|| (strcmp (optarg, "none") == 0)))
sound_config.audiofile = NULL;
else
sound_config.audiofile = optarg ? optarg : DSP;
break;
case 'S':
indexedcolor = 0;
break;
case 'F':
sample_format_name = optarg;
break;
case 'R':
sound_config.audiorate = atoi (optarg);
if (sound_config.audiorate < 1)
{
fprintf (stderr, "%s: not a valid audio rate (need a positive integer)\n", optarg);
exit (2);
}
break;
case 'D':
sound_config.max_sound_delay = atof (optarg);
if (sound_config.max_sound_delay < 0.0)
{
fprintf (stderr, "%s: not a valid delay (must be non-negative))\n", optarg);
exit (2);
}
break;
case 'e':
sound_config.reverb = 1;
break;
case 'p':
palfile = optarg ? optarg : 0;
NES_palette = 0;
break;
case 'P':
if (optarg && *optarg)
{
int i;
unsigned int *partial = 0;
int partials = 0;
int len;
palfile = 0;
NES_palette = 0;
len = strlen (optarg);
for (i = 0; i < (sizeof (palettes) / sizeof (*palettes)); i++)
if (!strcmp (palettes[i].name, optarg))
{
NES_palette = palettes[i].data;
break;
}
else if (!strncmp (palettes[i].name, optarg, len))
{
partial = palettes[i].data;
partials++;
}
if ((partials == 1) && !NES_palette)
NES_palette = partial;
if (!NES_palette)
{
if (partials)
fprintf (stderr, "%s: palette name `%s' is ambiguous\n",
*argv, optarg);
else
fprintf (stderr, "%s: unrecognized palette name `%s'\n",
*argv, optarg);
exit (2);
}
}
else
NES_palette = palettes[0].data;
break;
case 'V':
help_version (0);
exit (0);
break;
case 'G':
renderer_config.geometry = optarg;
break;
case 'E':
if (optarg && *optarg)
magstep = atoi (optarg);
else
magstep = 2;
break;
case 'L':
if (optarg && *optarg)
scanlines = atoi (optarg);
else
scanlines = 0;
break;
case 'r':
rendname = optarg;
break;
#ifdef HAVE_LIBM
case 'N':
if (optarg)
{
char *p;
double foo;
foo = strtod(optarg, &p);
if (optarg != p)
{
hue = foo;
while (hue < 0.0) hue += 360.0;
while (hue >= 360.0) hue -= 360.0;
}
if (*p == ',')
{
optarg = p+1;
foo = strtod(optarg, &p);
if (optarg != p)
{
tint = foo;
}
if (*p || (tint < 0.0) || (tint > 1.0))
{
fprintf (stderr, "%s: not a valid tint level (must be a number from 0 to 1)\n", optarg);
exit (2);
}
}
else if (*p)
{
fprintf (stderr, "%s: not a valid hue angle (must be an angle in degrees)\n", optarg);
exit (2);
}
}
NES_palette = ntsc_palette(hue, tint);
break;
#endif
case 0: /* long options with no short equivalents */
if (! strcmp(long_options[option_index] . name, "display"))
{
renderer_config.display_id = optarg;
break;
}
default:
fprintf (stderr, USAGE, *argv);
exit (2);
break;
}
}
if ((scanlines != 100) && ! magstep)
{
magstep = 2;
}
if ((argc - optind) != 1)
{
fprintf (stderr, "%s: too %s arguments\n", *argv, (argc > optind) ? "many" : "few");
fprintf (stderr, USAGE, *argv);
exit (2);
}
filename = argv[optind];
/*
This initializes the base filename by taking the filename of the ROM and
parsing of leading directory information as well as the trailing extension
(.nes, .gz, .Z).
*/
basestart = baseend = 0;
for (i = 0; i < strlen(filename); i++)
{
if (filename[i] == '/')
{
basestart = i + 1;
}
else if (filename[i] == '.')
{
baseend = i - 1;
}
}
if (! (basefilename = malloc(baseend - basestart + 1)))
{
perror (__FUNCTION__ ": malloc");
exit (1);
}
for (i = 0; i <= (baseend - basestart); i++)
{
basefilename[i] = filename[basestart + i];
}
basefilename[baseend - basestart + 1] = '\0';
/* initialize joysticks */
{
int stick;
for (stick = 0; stick < 2; stick ++)
{
if (jsdevice[stick])
{
#ifdef HAVE_LINUX_JOYSTICK_H
if ((jsfd[stick] = open (jsdevice[stick], O_RDONLY)) < 0)
{
perror (jsdevice[stick]);
}
else
{
ioctl (jsfd[stick], JSIOCGVERSION, jsversion + stick);
ioctl (jsfd[stick], JSIOCGAXES, jsaxes + stick);
ioctl (jsfd[stick], JSIOCGBUTTONS, jsbuttons + stick);
fcntl (jsfd[stick], F_SETFL, O_NONBLOCK); /* set joystick to non-blocking */
if( jsbuttons[stick] == 2 )
{ /* modify button map for a 2-button joystick */
if( !js_usermapped2button[stick][0] )
js_nesmaps[stick].button[0] = BUTTONB;
if( !js_usermapped2button[stick][1] )
js_nesmaps[stick].button[1] = BUTTONA;
}
if (verbose)
{
fprintf (stderr,
"Joystick %d (%s) has %d axes and %d buttons. Driver version is %d.%d.%d.\n",
stick + 1,
jsdevice[stick],
jsaxes[stick],
jsbuttons[stick],
jsversion[stick] >> 16,
(jsversion[stick] >> 8) & 0xff,
jsversion[stick] & 0xff);
}
}
#else
fprintf (stderr,
"Joystick support was disabled at compile-time. To enable it, make sure\n"
"you are running a recent version of the Linux kernel with the joystick\n"
"driver enabled, and install the header file <linux/joystick.h>.\n");
#endif /* HAVE_LINUX_JOYSTICK_H */
}
}
}
/* Select a sound sample format */
{
struct SampleFormat *match = 0;
int partials = 0;
int len;
len = strlen (sample_format_name);
for (sample_format = sample_formats; sample_format->name; sample_format++)
if (!strcmp (sample_format->name, sample_format_name))
{
break;
}
else if (!strncmp (sample_format->name, sample_format_name, len))
{
match = sample_format;
partials++;
}
if (*sample_format_name && !sample_format->name)
{
char *tail;
int sample_format_number;
sample_format_number = strtoul (sample_format_name, &tail, 0);
if (! *tail)
{
for (sample_format = sample_formats; sample_format->name; sample_format ++)
{
if (sample_format_number == sample_format->number) break;
}
}
}
if ((partials == 1) && !sample_format->name)
sample_format = match;
if (!sample_format->name)
{
if (partials)
fprintf (stderr, "%s: sound sample format name `%s' is ambiguous\n",
*argv, sample_format_name);
else
fprintf (stderr, "%s: unrecognized sound sample format name `%s'\n",
*argv, sample_format_name);
exit (2);
}
}
/* Open ROM file */
#ifdef HAVE_LIBZ
extension = strrchr(filename,'.');
/* If the extension is .zip open the file */
if (extension && !strcasecmp(extension,".zip")) {
rom_file_zip = unzOpen(filename);
if (rom_file_zip == NULL) {
perror(filename);
exit(EXIT_FAILURE);
}
}
/* If its not a .zip file treat it as a .gz */
else {
rom_file_gz = gzopen (filename, "rb");
if (rom_file_gz == NULL)
{
perror (filename);
exit (EXIT_FAILURE);
}
}
/* FIXME: Error */
#else
romfd = open (filename, O_RDONLY);
if (romfd < 0)
{
perror (filename);
exit (1);
}
#endif
#if HAVE_LIBZ
/*
* FIXME: Do not calculate the size, instead read the complete file with
* getc(), this looks wrong but working.
*/
#else
size = lseek (romfd, 0, SEEK_END);
lseek (romfd, 0, SEEK_SET); /* Get file size */
if (size < 0)
{
perror (filename);
exit (1);
}
if (size == 0)
{
fprintf (stderr, "Unable to read %s (empty file)\n", filename);
exit (1);
}
#endif
/* allocate space for the ROM */
if ((r = (int) mmap (ROM, 0x300000,
PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_FIXED | MAP_PRIVATE | MAP_ANON,
-1, 0)) < 0)
{
perror ("mmap");
exit (1);
}
/* load the ROM */
#if HAVE_LIBZ
/* Treat as a .zip file */
if (extension && !strcasecmp(extension,".zip")) {
ziploader(rom_file_zip,filename);
unzClose(rom_file_zip);
}
/* Treat as a .gz file */
else {
int c;
int i = 0;
while ((c = gzgetc (rom_file_gz)) != -1)
{
ROM[i++] = c;
}
size = i;
gzclose (rom_file_gz);
}
#else
if (read (romfd, ROM, size) != size)
{
perror (filename);
exit (1);
}
close (romfd);
#endif
if (verbose)
fprintf (stderr, "Rom size: %d\n", size);
/* Allocate memory */
r = (int) mmap (RAM, 0x8000,
PROT_READ | PROT_WRITE,
MAP_FIXED | MAP_PRIVATE | MAP_ANON,
-1, 0);
if (r <= 0)
{
perror ("mmap");
exit (1);
}
r = (int) mmap (CODE_BASE, 0x800000,
PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_FIXED | MAP_PRIVATE | MAP_ANON,
-1, 0);
if (r <= 0)
{
perror ("mmap");
exit (1);
}
r = (int) mmap ((void *) INT_MAP, 0x400000,
PROT_READ | PROT_WRITE,
MAP_FIXED | MAP_PRIVATE | MAP_ANON,
-1, 0);
if (r <= 0)
{
perror ("mmap");
exit (1);
}
/* Initialize sound playback */
if (InitAudio (argc, argv))
{
fprintf (stderr,
"%s: warning: failed to initialize sound playback\n",
*argv);
}
/* if the user requested header bytes, show them */
if (showheader)
{
fprintf (stderr,
"iNES header bytes:\n 0 1 2 3 4 5 6 7 8 9 A B C D E F\n ");
for (x = 0; x < 16; x++)
{
fprintf (stderr, "%02X ", ROM[x]);
}
fprintf (stderr, "\n ");
for (x = 0; x < 16; x++)
{
if ((ROM[x] >= 0x20) && (ROM[x] <= 0x7F))
fprintf (stderr, " %c ", ROM[x]);
else
fprintf (stderr, " ");
}
fprintf (stderr, "\n");
}
/* Initialize ROM mapper */
MAPTABLE[0] = RAM;
MAPTABLE[1] = RAM;
MAPTABLE[2] = RAM;
MAPTABLE[4] = RAM;
MAPTABLE[16] = RAM - 65536; /* Wrap around accesses which index above FFFF */
MAPTABLE[6] = MAPTABLE[7] = RAM; /* Save RAM */
MAPTABLE[3] = RAM + 0x2000;
MAPTABLE[5] = RAM; /* 3xxx and 5xxx really shouldn't be mapped to anything, but they're mapped to some unused ram just to prevent pagefaults by weird code that tries to access these areas. */
SRAM_ENABLED = 1;
if (!strncmp ("NES\x1a", (char *) ROM, 4)) /* .NES */
{
int used;
ROM_BASE = ROM + 16; /* 16 bytes for .nes header */
/* check for battery-backed SRAM */
if (!(ROM[6] & 2))
SRAM_ENABLED = 0;
/* check for a 512-byte trainer */
if (ROM[6] & 4)
{
if (verbose)
{
fprintf (stderr, "512-byte trainer present\n");
}
memcpy (RAM + 0x7000, ROM_BASE, 512);
ROM_BASE += 512;
}
/* figure out the mapper */
if (!mapperoverride && dirtyheader)
MAPPERNUMBER = ROM[6] >> 4;
else if (!mapperoverride)
{
MAPPERNUMBER = (ROM[6] >> 4) | (ROM[7] & 0xf0);
if ((ROM[7] & 0x0c) || ROM[8] | ROM[9])
fprintf (stderr, "Warning: may need -f to force 4-bit mapper\n");
/* there's actually a defined header bit past ROM[7]! */
if ((ROM[10] & 0x10) && verbose)
fprintf (stderr,
"Cartridge has bus conflicts (not emulated)\n");
switch (ROM[7] & 0x03)
{
case 0x00:
if (verbose)
fprintf (stderr, "NES/Famicom ROM\n");
break;
case 0x01:
if (verbose)
fprintf (stderr, "VS UniSystem ROM\n");
unisystem = 1;
break;
case 0x02:
if (verbose)
fprintf (stderr, "PlayChoice-10 ROM\n");
break;
default:
fprintf (stderr, "Warning: unrecognized system ID %d\n",
ROM[7] & 0x03);
}
}
if (MAPPERNUMBER == 99)
{
unisystem = 1;
}
VROM_BASE = ROM_BASE + ROM[4] * 16384;
if (ROM[5])
memcpy (VRAM, VROM_BASE, 8192);
ROM_PAGES = ROM[4];
VROM_PAGES = ROM[5];
/* calculate used space in image */
used = 16 /* header size */
+ ((ROM[6] & 4) ? 512 : 0) /* trainer size */
+ 16384 * ROM_PAGES /* PRG-ROM pages */
+ 8192 * VROM_PAGES; /* CHR-ROM pages */
/* handle palette remapping/redefinition tables */
if (size == (used + 64)) {
palremap = ROM + used;
} else if (size == (used + 192)) {
paldata = ROM + used;
} else if ((size != used) && verbose) {
fprintf (stderr, "File contains %d extra bytes\n",
size - used);
}
hvmirror = (ROM[6] & 1) ^ 1;
nomirror = ROM[6] & 8;
}
else if (!strncmp ("FDS\x1a", (char *) ROM, 4)) /* .FDS */
{
fprintf (stderr,
"Famicom Disk System (FDS) disk images aren't yet supported\n");
exit (1);
}
else if (size == 40960)
{
/* No header, assume raw image */
ROM_BASE = ROM;
MAPPERNUMBER = 0;
memcpy (VRAM, ROM + 32768, 8192);
ROM_PAGES = 2;
VROM_PAGES = 2;
VROM_BASE = ROM + 32768;
nomirror = 1;
mapmirror = 0;
}
else
{
fprintf (stderr, "Unrecognized ROM file format\n");
exit (1);
}
if (MAPPERNUMBER > MAXMAPPER || ((int *) MapperInit)[MAPPERNUMBER] == 0)
{
fprintf (stderr, "Unknown Mapper: %d (0x%02X)\n", MAPPERNUMBER,
MAPPERNUMBER);
exit (1);
}
if (verbose)
{
fprintf (stderr, "Mapper %d (%s) Program ROM %dk Video ROM %dk\n",
MAPPERNUMBER, MapperName[MAPPERNUMBER], ROM_PAGES * 16,
VROM_PAGES * 8);
}
/* How many ROM address bits are used */
if (ROM_PAGES <= 2)
ROM_MASK = 1;
else if (ROM_PAGES <= 4)
ROM_MASK = 3;
else if (ROM_PAGES <= 8)
ROM_MASK = 7;
else if (ROM_PAGES <= 16)
ROM_MASK = 15;
else if (ROM_PAGES <= 32)
ROM_MASK = 31;
else
ROM_MASK = 63;
/* How many VROM address bits are used */
if (VROM_PAGES <= 2)
VROM_MASK = 1;
else if (VROM_PAGES <= 4)
VROM_MASK = 3;
else if (VROM_PAGES <= 8)
VROM_MASK = 7;
else if (VROM_PAGES <= 16)
VROM_MASK = 15;
else if (VROM_PAGES <= 32)
VROM_MASK = 31;
else if (VROM_PAGES <= 64)
VROM_MASK = 63;
else
VROM_MASK = 127;
/* VROM mask for 1k-sized pages */
VROM_MASK_1k = (VROM_MASK << 3) | 7;
((void (*)(void))(MapperInit[MAPPERNUMBER]))();
if (cmirror == 1 && mapmirror == 0)
{
if (verbose && !hvmirror)
fprintf (stderr, "Overriding default vertical mirroring.\n");
hvmirror = 1;
}
if (cmirror == 2 && mapmirror == 0)
{
if (verbose && hvmirror)
fprintf (stderr, "Overriding default horizontal mirroring.\n");
hvmirror = 0;
}
if (cmirror == 3 && mapmirror == 0)
{
if (verbose && !nomirror && !osmirror)
fprintf (stderr, "Overriding default mirroring.\n");
osmirror = 1;
}
if (cmirror == 4 && mapmirror == 0)
{
if (verbose && !nomirror)
fprintf (stderr, "Overriding default mirroring.\n");
nomirror = 1;
}
if (verbose && mapmirror == 0)
{
if (nomirror)
fprintf (stderr, "Using no mirroring.\n");
else if (osmirror)
fprintf (stderr, "Using one-screen mirroring.\n");
else if (hvmirror)
fprintf (stderr, "Using horizontal mirroring\n");
else
fprintf (stderr, "Using vertical mirroring\n");
}
if (SRAM_ENABLED)
restoresavedgame ();
if (!NES_palette)
loadpal (palfile);
if (verbose)
{
int i;
for (i = 0; i < (sizeof (palettes) / sizeof (*palettes)); i++)
if (NES_palette == palettes[i].data)
{
fprintf (stderr, "Using built-in palette \"%s\"\n", palettes[i].name);
break;
}
}
if (palremap) {
unsigned int *new_palette;
int pen;
if (!(new_palette = (unsigned int *) malloc (64 * sizeof (*new_palette))))
{
fprintf(stderr, "Can't remap palette: ");
fflush (stderr);
perror ("malloc");
}
else
{
if (verbose)
fprintf (stderr, "Remapping palette\n");
for (pen = 0; pen < 64; pen ++)
new_palette[pen] = (palremap[pen] <= 64)
? NES_palette[palremap[pen]]
: NES_palette[pen];
memcpy ((void *)NES_palette,
(void *)new_palette,
64 * sizeof(*NES_palette));
free (new_palette);
}
}
/* (Possibly) convert palette to monochrome */
if (monochrome)
{
int pen;
for (pen = 0; pen < 64; pen ++)
{
unsigned long red, blue, green, gray;
red = (NES_palette[pen] & 0xFF0000) >> 16;
green = (NES_palette[pen] & 0xFF00) >> 8;
blue = NES_palette[pen] & 0xFF;
gray = 0.299 * red + 0.587 * green + 0.114 * blue;
if (gray > 0xFFU)
gray = 0xFFU;
NES_palette[pen] = (gray << 16) | (gray << 8) | gray;
}
}
/* enter the Game Genie codes */
if (gamegenie)
{
ggret = DecodeGameGenieCode (ggcode, &address, &data, &compare);
if (ggret == GAME_GENIE_BAD_CODE)
{
fprintf (stderr, "invalid Game Genie code: %s\n", ggcode);
}
else if (ggret == GAME_GENIE_8_CHAR)
{
if (verbose)
{
fprintf (stderr, "Game Genie: address = %04X, data = %02X\n",
address, data);
fprintf (stderr, "Game Genie: compare value = %02X\n", compare);
fprintf (stderr, "Game Genie: value at %04X = %02X\n",
address, MAPTABLE[address >> 12][address]);
}
if (MAPTABLE[address >> 12][address] == compare)
{
MAPTABLE[address >> 12][address] = data;
if (verbose)
{
fprintf (stderr, "Game Genie: replacing...\n");
fprintf (stderr, "Game Genie: value at %04X = %02X\n",
address, MAPTABLE[address >> 12][address]);
}
}
}
else
{
if (verbose)
{
fprintf (stderr, "Game Genie: address = %04X, data = %02X\n",
address, data);
fprintf (stderr, "Game Genie: replacing...\n");
}
MAPTABLE[address >> 12][address] = data;
if (verbose)
fprintf (stderr, "Game Genie: value at %04X = %02X\n", address,
MAPTABLE[address >> 12][address]);
}
}
/* Choose renderer */
{
int matches = 0; /* number of matches */
struct Renderer *match = 0; /* first match */
for (renderer = renderers; renderer->name; renderer++)
if (!strcmp (renderer->name, rendname)) {
match = renderer;
matches = 1;
break;
} else if (!strncmp (renderer->name, rendname, strlen(rendname))) {
match = renderer;
matches++;
}
if (matches != 1) {
if (matches)
fprintf (stderr, "%s: renderer name `%s' is ambiguous\n",
*argv, rendname);
else
fprintf (stderr, "%s: unrecognized renderer name `%s'\n",
*argv, rendname);
fprintf (stderr, USAGE, *argv);
exit (2);
}
renderer = match;
}
/* Initialize graphic display */
if (renderer->InitDisplay (argc, argv))
{
fprintf (stderr,
"%s: failed to initialize renderer `%s'\n",
*argv, renderer->name);
exit (1);
}
/* trap traps */
if (! disassemble)
if ((oldtraphandler = signal (SIGTRAP, &traphandler)) == SIG_ERR)
{
perror ("signal");
}
/* start the show */
STACKPTR = (int) STACK + 0xFF;
translate (*((unsigned short *) (MAPTABLE[15] + 0xFFFC)));
START(); /* execute translated code */
/* Not Reached, but return something anyway to get rid of warnings */
return(0);
}