#include "../copyright"

#include <string.h>
#ifdef HAVE_STRINGS_H
#include <strings.h>
#endif
#include <ctype.h>

#include "snes9x.h"
#include "memmap.h"
#include "cpuexec.h"
#include "ppu.h"
#include "display.h"
#include "cheats.h"
#include "apu.h"
#include "sa1.h"
#include "dsp1.h"
#include "srtc.h"
#include "sdd1.h"
#include "spc7110.h"
#include "seta.h"

#ifdef __W32_HEAP
#include <malloc.h>
#endif

#ifdef _MSC_VER
/* Necessary to build on MSVC */
#define strnicmp _strnicmp
#endif

#define MAP_HIROM_SRAM_OR_NONE (Memory.SRAMSize == 0 ? (uint8_t*) MAP_NONE : (uint8_t*) MAP_HIROM_SRAM)
#define MAP_LOROM_SRAM_OR_NONE (Memory.SRAMSize == 0 ? (uint8_t*) MAP_NONE : (uint8_t*) MAP_LOROM_SRAM)
#define MAP_RONLY_SRAM_OR_NONE (Memory.SRAMSize == 0 ? (uint8_t*) MAP_NONE : (uint8_t*) MAP_RONLY_SRAM)

#include "fxemu.h"
extern FxInit_s SuperFX;

static int32_t retry_count = 0;
static uint8_t bytes0x2000 [0x2000];
static bool is_bsx(uint8_t*);
static bool bs_name(uint8_t*);

void S9xDeinterleaveType2(bool reset);

extern char* rom_filename;

void S9xDeinterleaveType1(int32_t TotalFileSize, uint8_t* base)
{
   int32_t i;
   uint8_t blocks [256];
   uint8_t *tmp    = NULL;
   int32_t nblocks = TotalFileSize >> 16;

   for (i = 0; i < nblocks; i++)
   {
      blocks [i * 2] = i + nblocks;
      blocks [i * 2 + 1] = i;
   }

   /* DS2 DMA notes: base may or may not be 32-byte aligned */
   tmp = (uint8_t*) malloc(0x8000);

   if (tmp)
   {
      for (i = 0; i < nblocks * 2; i++)
      {
         int32_t j;
         for (j = i; j < nblocks * 2; j++)
         {
            if (blocks [j] == i)
            {
               uint8_t b;

               /* memmove converted: Different mallocs [Neb] */
               memcpy(tmp, &base [blocks [j] * 0x8000], 0x8000);
               /* memmove converted: Different addresses, or identical for blocks[i] == blocks[j] [Neb] */
               /* DS2 DMA notes: Don't do DMA at all if blocks[i] == blocks[j] */
               memcpy(&base [blocks [j] * 0x8000],
                      &base [blocks [i] * 0x8000], 0x8000);
               /* memmove converted: Different mallocs [Neb] */
               memcpy(&base [blocks [i] * 0x8000], tmp, 0x8000);
               b          = blocks [j];
               blocks [j] = blocks [i];
               blocks [i] = b;
               break;
            }
         }
      }
      free(tmp);
   }
}

void S9xDeinterleaveGD24(int32_t TotalFileSize, uint8_t* base)
{
   uint8_t *tmp = NULL;
   if (TotalFileSize != 0x300000)
      return;

   /* DS2 DMA notes: base may or may not be 32-byte aligned */
   tmp = (uint8_t*) malloc(0x80000);

   if (tmp)
   {
      /* memmove converted: Different mallocs [Neb] */
      memcpy(tmp, &base[0x180000], 0x80000);
      /* memmove converted: Different addresses [Neb] */
      memcpy(&base[0x180000], &base[0x200000], 0x80000);
      /* memmove converted: Different addresses [Neb] */
      memcpy(&base[0x200000], &base[0x280000], 0x80000);
      /* memmove converted: Different mallocs [Neb] */
      memcpy(&base[0x280000], tmp, 0x80000);
      free(tmp);

      S9xDeinterleaveType1(TotalFileSize, base);
   }
}

static bool AllASCII(uint8_t* b, int32_t size)
{
   int32_t i;
   for (i = 0; i < size; i++)
      if (b[i] < 32 || b[i] > 126)
         return false;
   return true;
}

static int32_t ScoreHiROM(bool skip_header, int32_t romoff)
{
   int32_t score = 0;
   int32_t o = skip_header ? 0xff00 + 0x200 : 0xff00;

   o += romoff;

   if (Memory.ROM [o + 0xd5] & 0x1)
      score += 2;

   /* Mode23 is SA-1 */
   if (Memory.ROM [o + 0xd5] == 0x23)
      score -= 2;

   if (Memory.ROM [o + 0xd4] == 0x20)
      score += 2;

   if ((Memory.ROM [o + 0xdc] + (Memory.ROM [o + 0xdd] << 8) + Memory.ROM [o + 0xde] + (Memory.ROM [o + 0xdf] << 8)) == 0xffff)
   {
      score += 2;
      if (0 != (Memory.ROM [o + 0xde] + (Memory.ROM [o + 0xdf] << 8)))
         score++;
   }

   if (Memory.ROM [o + 0xda] == 0x33)
      score += 2;
   if ((Memory.ROM [o + 0xd5] & 0xf) < 4)
      score += 2;
   if (!(Memory.ROM [o + 0xfd] & 0x80))
      score -= 6;
   if ((Memory.ROM [o + 0xfc] | (Memory.ROM [o + 0xfd] << 8)) > 0xFFB0)
      score -= 2; /* reduced after looking at a scan by Cowering */
   if (Memory.CalculatedSize > 1024 * 1024 * 3)
      score += 4;
   if ((1 << (Memory.ROM [o + 0xd7] - 7)) > 48)
      score -= 1;
   if (!AllASCII(&Memory.ROM [o + 0xb0], 6))
      score -= 1;
   if (!AllASCII(&Memory.ROM [o + 0xc0], ROM_NAME_LEN - 1))
      score -= 1;

   return score;
}

static int32_t ScoreLoROM(bool skip_header, int32_t romoff)
{
   int32_t score = 0;
   int32_t o = skip_header ? 0x7f00 + 0x200 : 0x7f00;

   o += romoff;

   if (!(Memory.ROM [o + 0xd5] & 0x1))
      score += 3;

   /* Mode23 is SA-1 */
   if (Memory.ROM [o + 0xd5] == 0x23)
      score += 2;

   if ((Memory.ROM [o + 0xdc] + (Memory.ROM [o + 0xdd] << 8) + Memory.ROM [o + 0xde] + (Memory.ROM [o + 0xdf] << 8)) == 0xffff)
   {
      score += 2;
      if (0 != (Memory.ROM [o + 0xde] + (Memory.ROM [o + 0xdf] << 8)))
         score++;
   }

   if (Memory.ROM [o + 0xda] == 0x33)
      score += 2;
   if ((Memory.ROM [o + 0xd5] & 0xf) < 4)
      score += 2;
   if (Memory.CalculatedSize <= 1024 * 1024 * 16)
      score += 2;
   if (!(Memory.ROM [o + 0xfd] & 0x80))
      score -= 6;
   if ((Memory.ROM [o + 0xfc] | (Memory.ROM [o + 0xfd] << 8)) > 0xFFB0)
      score -= 2; /* reduced per Cowering suggestion */
   if ((1 << (Memory.ROM [o + 0xd7] - 7)) > 48)
      score -= 1;
   if (!AllASCII(&Memory.ROM [o + 0xb0], 6))
      score -= 1;
   if (!AllASCII(&Memory.ROM [o + 0xc0], ROM_NAME_LEN - 1))
      score -= 1;

   return score;
}

static char* Safe(const char* s)
{
   static char* safe;
   static int32_t safe_len = 0;
   int32_t i;
   int32_t len;

   if (s == NULL)
   {
      if (safe)
      {
         free(safe);
         safe = NULL;
      }
      return NULL;
   }

   len = strlen(s);

   if (!safe || len + 1 > safe_len)
   {
      if (safe)
         free(safe);
      safe = (char*) malloc(safe_len = len + 1);
   }

   for (i = 0; i < len; i++)
   {
      if (s [i] >= 32 && s [i] < 127)
         safe [i] = s[i];
      else
         safe [i] = '?';
   }
   safe [len] = 0;
   return safe;
}

/**********************************************************************************************/
/* S9xInitMemory()                                                                                     */
/* This function allocates and zeroes all the memory needed by the emulator                   */
/**********************************************************************************************/
bool S9xInitMemory(void)
{
   /* DS2 DMA notes: These would do well to be allocated with 32 extra bytes
      so they can be 32-byte aligned. [Neb] */
   Memory.RAM   = (uint8_t*) calloc(0x20000, 1);
   Memory.SRAM  = (uint8_t*) calloc(0x20000, 1);
   Memory.VRAM  = (uint8_t*) calloc(0x10000, 1);
   Memory.BSRAM = (uint8_t*) calloc(0x80000, 1);
   /* Don't bother initializing ROM, we will load a game anyway. */
#ifdef DS2_DMA
   Memory.ROM   = (uint8_t*) AlignedMalloc(MAX_ROM_SIZE + 0x200 + 0x8000, 32, &PtrAdj.ROM);
#else
   Memory.ROM   = (uint8_t*) malloc(MAX_ROM_SIZE + 0x200 + 0x8000);
#endif
   Memory.FillRAM = NULL;

   IPPU.TileCache [TILE_2BIT] = (uint8_t*) calloc(MAX_2BIT_TILES, 128);
   IPPU.TileCache [TILE_4BIT] = (uint8_t*) calloc(MAX_4BIT_TILES, 128);
   IPPU.TileCache [TILE_8BIT] = (uint8_t*) calloc(MAX_8BIT_TILES, 128);

   IPPU.TileCached [TILE_2BIT] = (uint8_t*) calloc(MAX_2BIT_TILES, 1);
   IPPU.TileCached [TILE_4BIT] = (uint8_t*) calloc(MAX_4BIT_TILES, 1);
   IPPU.TileCached [TILE_8BIT] = (uint8_t*) calloc(MAX_8BIT_TILES, 1);

   if (!Memory.RAM || !Memory.SRAM || !Memory.VRAM || !Memory.ROM || !Memory.BSRAM || !IPPU.TileCache [TILE_2BIT] || !IPPU.TileCache [TILE_4BIT] || !IPPU.TileCache [TILE_8BIT] || !IPPU.TileCached [TILE_2BIT] || !IPPU.TileCached [TILE_4BIT] ||  !IPPU.TileCached [TILE_8BIT])
   {
      S9xDeinitMemory();
      return false;
   }

   /* FillRAM uses first 32K of ROM image area, otherwise space just
      wasted. Might be read by the SuperFX code. */
   Memory.FillRAM = Memory.ROM;

   /* Add 0x8000 to ROM image pointer to stop SuperFX code accessing
      unallocated memory (can cause crash on some ports). */
   Memory.ROM += 0x8000; /* still 32-byte aligned */

   SuperFX.pvRegisters = &Memory.FillRAM [0x3000];
   SuperFX.nRamBanks = 2; /* Most only use 1.  1 = 64KB, 2 = 128KB = 1024Mb */
   SuperFX.pvRam = Memory.SRAM;
   SuperFX.nRomBanks = (2 * 1024 * 1024) / (32 * 1024);
   SuperFX.pvRom = (uint8_t*) Memory.ROM;

   return true;
}

void S9xDeinitMemory(void)
{
   int t;
   if (Memory.RAM)
   {
      free(Memory.RAM);
      Memory.RAM = NULL;
   }
   if (Memory.SRAM)
   {
      free(Memory.SRAM);
      Memory.SRAM = NULL;
   }
   if (Memory.VRAM)
   {
      free(Memory.VRAM);
      Memory.VRAM = NULL;
   }
   if (Memory.ROM)
   {
      Memory.ROM -= 0x8000;
#ifdef DS2_RAM
      AlignedFree(ROM, PtrAdj.ROM);
#else
      free(Memory.ROM);
#endif
      Memory.ROM = NULL;
   }

   if (Memory.BSRAM)
   {
      free(Memory.BSRAM);
      Memory.BSRAM = NULL;
   }

   for (t = 0; t < 2; t++)
   {
      if (IPPU.TileCache[t])
      {
         free(IPPU.TileCache[t]);
         IPPU.TileCache[t] = NULL;
      }
      if (IPPU.TileCached[t])
      {
         free(IPPU.TileCached[t]);
         IPPU.TileCached[t] = NULL;
      }
   }
}

#ifndef LOAD_FROM_MEMORY_TEST

/* Read variable size MSB int from a file */
static int32_t ReadInt(FILE* f, uint32_t nbytes)
{
   int32_t v = 0;
   while (nbytes--)
   {
      int32_t c = fgetc(f);
      if (c == EOF)
         return -1;
      v = (v << 8) | (c & 0xFF);
   }
   return v;
}

#define IPS_EOF 0x00454F46l

static void CheckForIPSPatch(const char* rom_filename, bool header, int32_t* rom_size)
{
   char  dir [_MAX_DIR + 1];
   char  drive [_MAX_DRIVE + 1];
   char  name [_MAX_FNAME + 1];
   char  ext [_MAX_EXT + 1];
   char  fname [_MAX_PATH + 1];
   FILE*  patch_file  = NULL;
   int32_t offset = header ? 512 : 0;

   _splitpath(rom_filename, drive, dir, name, ext);
   _makepath(fname, drive, dir, name, "ips");

   if (!(patch_file = fopen(fname, "rb")))
      if (!(patch_file = fopen(S9xGetFilename("ips"), "rb")))
         return;

   if (fread(fname, 1, 5, patch_file) != 5 || strncmp(fname, "PATCH", 5) != 0)
   {
      fclose(patch_file);
      return;
   }

   int32_t ofs;

   for (;;)
   {
      int32_t len;
      int32_t rlen;
      int32_t rchar;

      ofs = ReadInt(patch_file, 3);
      if (ofs == -1)
         goto err_eof;

      if (ofs == IPS_EOF)
         break;

      ofs -= offset;

      len = ReadInt(patch_file, 2);
      if (len == -1)
         goto err_eof;

      /* Apply patch block */
      if (len)
      {
         if (ofs + len > MAX_ROM_SIZE)
            goto err_eof;

         while (len--)
         {
            rchar = fgetc(patch_file);
            if (rchar == EOF)
               goto err_eof;
            Memory.ROM [ofs++] = (uint8_t) rchar;
         }
         if (ofs > *rom_size)
            *rom_size = ofs;
      }
      else
      {
         rlen = ReadInt(patch_file, 2);
         if (rlen == -1)
            goto err_eof;

         rchar = fgetc(patch_file);
         if (rchar == EOF)
            goto err_eof;

         if (ofs + rlen > MAX_ROM_SIZE)
            goto err_eof;

         while (rlen--)
            Memory.ROM [ofs++] = (uint8_t) rchar;

         if (ofs > *rom_size)
            *rom_size = ofs;
      }
   }

   /* Check if ROM image needs to be truncated */
   ofs = ReadInt(patch_file, 3);
   if (ofs != -1 && ofs - offset < *rom_size)
      *rom_size = ofs - offset; /* Need to truncate ROM image */
   fclose(patch_file);
   return;

err_eof:
   if (patch_file)
      fclose(patch_file);
}

static uint32_t FileLoader(uint8_t* buffer, const char* filename, int32_t maxsize)
{
   FILE* ROMFile;
   int32_t TotalFileSize = 0;
   int32_t len = 0;

   char dir [_MAX_DIR + 1];
   char drive [_MAX_DRIVE + 1];
   char name [_MAX_FNAME + 1];
   char ext [_MAX_EXT + 1];
   char fname [_MAX_PATH + 1];

   uint32_t FileSize = 0;

   _splitpath(filename, drive, dir, name, ext);
   _makepath(fname, drive, dir, name, ext);

#ifdef _WIN32
   /* memmove required: Overlapping addresses [Neb] */
   memmove(&ext [0], &ext[1], 4);
#endif

   if ((ROMFile = fopen(fname, "rb")) == NULL)
      return 0;

   strcpy(Memory.ROMFilename, fname);

   Memory.HeaderCount = 0;
   uint8_t* ptr = buffer;
   bool more = false;

   do
   {
      int32_t calc_size;
      FileSize = fread(ptr, 1, maxsize + 0x200 - (ptr - Memory.ROM), ROMFile);
      fclose(ROMFile);
      calc_size = FileSize & ~0x1FFF; /* round to the lower 0x2000 */

      if ((FileSize - calc_size == 512 && !Settings.ForceNoHeader) || Settings.ForceHeader)
      {
         /* memmove required: Overlapping addresses [Neb] */
         /* DS2 DMA notes: Can be split into 512-byte DMA blocks [Neb] */
#ifdef DS2_DMA
         __dcache_writeback_all();
         {
            uint32_t i;
            for (i = 0; i < calc_size; i += 512)
            {
               ds2_DMAcopy_32Byte(2 /* channel: emu internal */, ptr + i, ptr + i + 512, 512);
               ds2_DMA_wait(2);
               ds2_DMA_stop(2);
            }
         }
#else
         memmove(ptr, ptr + 512, calc_size);
#endif
         Memory.HeaderCount++;
         FileSize -= 512;
      }

      ptr += FileSize;
      TotalFileSize += FileSize;


      /* check for multi file roms */

      if ((ptr - Memory.ROM) < (maxsize + 0x200) && (isdigit(ext [0]) && ext [1] == 0 && ext [0] < '9'))
      {
         more = true;
         ext [0]++;
#ifdef _WIN32
         /* memmove required: Overlapping addresses [Neb] */
         memmove(&ext [1], &ext [0], 4);
         ext [0] = '.';
#endif
         _makepath(fname, drive, dir, name, ext);
      }
      else if (ptr - Memory.ROM < maxsize + 0x200 && (((len = strlen(name)) == 7 || len == 8) && strncasecmp(name, "sf", 2) == 0 && isdigit(name [2]) && isdigit(name [3]) && isdigit(name [4]) && isdigit(name [5]) && isalpha(name [len - 1])))
      {
         more = true;
         name [len - 1]++;
#ifdef _WIN32
         /* memmove required: Overlapping addresses [Neb] */
         memmove(&ext [1], &ext [0], 4);
         ext [0] = '.';
#endif
         _makepath(fname, drive, dir, name, ext);
      }
      else
         more = false;

   }
   while (more && (ROMFile = fopen(fname, "rb")) != NULL);

   return TotalFileSize;
}
#endif

/**********************************************************************************************/
/* LoadROM()                                                                                  */
/* This function loads a Snes-Backup image                                                    */
/**********************************************************************************************/

bool LoadROM(
#ifdef LOAD_FROM_MEMORY_TEST
      const struct retro_game_info* game
#else
      const char* filename
#endif
      )
{
   int32_t hi_score, lo_score;
   int32_t TotalFileSize = 0;
   bool Interleaved = false;
   bool Tales = false;
   const uint8_t* src;

   uint8_t* RomHeader = Memory.ROM;
   Memory.ExtendedFormat = NOPE;

   Del7110Gfx();

   memset(bytes0x2000, 0, 0x2000);
   CPU.TriedInterleavedMode2 = false;

   Memory.CalculatedSize = 0;
   retry_count = 0;

again:
#ifdef LOAD_FROM_MEMORY_TEST
   strncpy(Memory.ROMFilename, game->path, sizeof(Memory.ROMFilename));

   Memory.HeaderCount = 0;
   TotalFileSize = game->size;
   src = game->data;
   Memory.HeaderCount = 0;

   if ((((game->size & 0x1FFF) == 0x200) && !Settings.ForceNoHeader) || Settings.ForceHeader)
   {
      TotalFileSize -= 0x200;
      src += 0x200;
      Memory.HeaderCount = 1;

   }

   if (TotalFileSize > MAX_ROM_SIZE)
      return false;

   memcpy(Memory.ROM, src, TotalFileSize);

#else
   TotalFileSize = FileLoader(Memory.ROM, filename, MAX_ROM_SIZE);

   if (!TotalFileSize)
      return false; /* it ends here */
   CheckForIPSPatch(filename, Memory.HeaderCount != 0, &TotalFileSize);
#endif
   /* fix hacked games here. */
   if ((strncmp("HONKAKUHA IGO GOSEI", (char*)&Memory.ROM[0x7FC0], 19) == 0) && (Memory.ROM[0x7FD5] != 0x31))
   {
      Memory.ROM[0x7FD5] = 0x31;
      Memory.ROM[0x7FD6] = 0x02;
   }

#ifndef NO_SPEEDHACKS
   /* SNESAdvance speed hacks (from the speed-hacks branch of CatSFC) */
   if (strncmp("YOSHI'S ISLAND", (char *) &Memory.ROM[0x7FC0], 14) == 0)
   {
      Memory.ROM[0x0000F4] = 0x42;
      Memory.ROM[0x0000F5] = 0x3B;
   }
   else if (strncmp("SUPER MARIOWORLD", (char *) &Memory.ROM[0x7FC0], 16) == 0)
   {
      Memory.ROM[0x00006D] = 0x42;
   }
   else if (strncmp("ALL_STARS + WORLD", (char *) &Memory.ROM[0x7FC0], 17) == 0)
   {
      Memory.ROM[0x0003D0] = 0x42;
      Memory.ROM[0x0003D1] = 0x5B;
      Memory.ROM[0x018522] = 0x42;
      Memory.ROM[0x018523] = 0x5B;
      Memory.ROM[0x02C804] = 0x42;
      Memory.ROM[0x02C805] = 0xBA;
      Memory.ROM[0x0683B5] = 0x42;
      Memory.ROM[0x0683B6] = 0x5B;
      Memory.ROM[0x0696AC] = 0x42;
      Memory.ROM[0x0696AD] = 0xBA;
      Memory.ROM[0x089233] = 0xDB;
      Memory.ROM[0x089234] = 0x61;
      Memory.ROM[0x0895DF] = 0x42;
      Memory.ROM[0x0895E0] = 0x5B;
      Memory.ROM[0x0A7A9D] = 0x42;
      Memory.ROM[0x0A7A9E] = 0xBA;
      Memory.ROM[0x1072E7] = 0x42;
      Memory.ROM[0x1072E8] = 0xD9;
      Memory.ROM[0x107355] = 0x42;
      Memory.ROM[0x107356] = 0x5B;
      Memory.ROM[0x1073CF] = 0x42;
      Memory.ROM[0x1073D0] = 0x5B;
      Memory.ROM[0x107443] = 0x42;
      Memory.ROM[0x107444] = 0x5B;
      Memory.ROM[0x107498] = 0x42;
      Memory.ROM[0x107499] = 0x5B;
      Memory.ROM[0x107505] = 0x42;
      Memory.ROM[0x107506] = 0x5B;
      Memory.ROM[0x107539] = 0x42;
      Memory.ROM[0x10753A] = 0x5B;
      Memory.ROM[0x107563] = 0x42;
      Memory.ROM[0x107564] = 0x5B;
      Memory.ROM[0x18041D] = 0x42;
      Memory.ROM[0x18041E] = 0x79;
   }
#endif

   hi_score = ScoreHiROM(true, 0);
   lo_score = ScoreLoROM(true, 0);

   if (Memory.HeaderCount == 0 && !Settings.ForceNoHeader && strncmp((char *) &Memory.ROM [0], "BANDAI SFC-ADX", 14) && ((hi_score > lo_score && ScoreHiROM(true, 0) > hi_score) || (hi_score <= lo_score && ScoreLoROM(true, 0) > lo_score)))
   {
#ifdef DS2_DMA
      __dcache_writeback_all();
      {
         uint32_t i;
         for (i = 0; i < TotalFileSize; i += 512)
         {
            ds2_DMAcopy_32Byte(2 /* channel: emu internal */, Memory.ROM + i, Memory.ROM + i + 512, 512);
            ds2_DMA_wait(2);
            ds2_DMA_stop(2);
         }
      }
#else
      /* memmove required: Overlapping addresses [Neb] */
      memmove(Memory.ROM, Memory.ROM + 512, TotalFileSize - 512);
#endif
      TotalFileSize -= 512;
   }

   Memory.CalculatedSize = TotalFileSize & ~0x1FFF; /* round down to lower 0x2000 */
   memset(Memory.ROM + Memory.CalculatedSize, 0, MAX_ROM_SIZE - Memory.CalculatedSize);

   if (Memory.CalculatedSize > 0x400000 &&
         !(Memory.ROM[0x7FD5] == 0x32 && ((Memory.ROM[0x7FD6] & 0xF0) == 0x40)) && /* exclude S-DD1 */
         !(Memory.ROM[0xFFD5] == 0x3A && ((Memory.ROM[0xFFD6] & 0xF0) == 0xF0))) /* exclude SPC7110 */
      Memory.ExtendedFormat = YEAH; /* you might be a Jumbo! */

   /* If both vectors are invalid, it's type 1 LoROM */

   if(Memory.ExtendedFormat == NOPE && strncmp((char *) &Memory.ROM[0], "BANDAI SFC-ADX", 14) && ((Memory.ROM[0x7ffc] | (Memory.ROM[0x7ffd] << 8)) < 0x8000) && ((Memory.ROM[0xfffc] | (Memory.ROM[0xFffd] << 8)) < 0x8000) && !Settings.ForceInterleaved)
     S9xDeinterleaveType1(TotalFileSize, Memory.ROM);

   /* CalculatedSize is now set, so rescore */
   hi_score = ScoreHiROM(false, 0);
   lo_score = ScoreLoROM(false, 0);

   if (Memory.ExtendedFormat != NOPE)
   {
      int32_t loromscore, hiromscore, swappedlorom, swappedhirom;
      loromscore = ScoreLoROM(false, 0);
      hiromscore = ScoreHiROM(false, 0);
      swappedlorom = ScoreLoROM(false, 0x400000);
      swappedhirom = ScoreHiROM(false, 0x400000);

      /* set swapped here. */
      if (MAX(swappedlorom, swappedhirom) >= MAX(loromscore, hiromscore))
      {
         Memory.ExtendedFormat = BIGFIRST;
         hi_score = swappedhirom;
         lo_score = swappedlorom;
         RomHeader = Memory.ROM + 0x400000;
      }
      else
      {
         Memory.ExtendedFormat = SMALLFIRST;
         lo_score = loromscore;
         hi_score = hiromscore;
         RomHeader = Memory.ROM;
      }
   }

   Interleaved = Settings.ForceInterleaved || Settings.ForceInterleaved2;
   if (Settings.ForceLoROM || (!Settings.ForceHiROM && lo_score >= hi_score))
   {
      Memory.LoROM = true;
      Memory.HiROM = false;

      /* Ignore map type byte if not 0x2x or 0x3x */
      if ((RomHeader [0x7fd5] & 0xf0) == 0x20 || (RomHeader [0x7fd5] & 0xf0) == 0x30)
      {
         switch (RomHeader [0x7fd5] & 0xf)
         {
         case 1:
            Interleaved = true;
            break;
         case 5:
            Interleaved = true;
            Tales = true;
            break;
         }
      }
   }
   else
   {
      if ((RomHeader [0xffd5] & 0xf0) == 0x20 || (RomHeader [0xffd5] & 0xf0) == 0x30)
      {
         switch (RomHeader [0xffd5] & 0xf)
         {
         case 0:
         case 3:
            Interleaved = true;
            break;
         }
      }
      Memory.LoROM = false;
      Memory.HiROM = true;
   }

   /* More */
   if (!Settings.ForceHiROM &&
       !Settings.ForceLoROM &&
       !Settings.ForceInterleaved &&
       !Settings.ForceInterleaved2 &&
       !Settings.ForceNotInterleaved &&
       !Settings.ForceSuperFX &&
       !Settings.ForceNoSuperFX &&
       !Settings.ForceDSP1 &&
       !Settings.ForceNoDSP1 &&
       !Settings.ForceSA1 &&
       !Settings.ForceNoSA1 &&
       !Settings.ForceC4 &&
       !Settings.ForceNoC4 &&
       !Settings.ForceSDD1 &&
       !Settings.ForceNoSDD1)
   {
      /* スーファミターボ BIOS読み込み */
      if ((strncmp((char*) &Memory.ROM [0], "BANDAI SFC-ADX", 14) == 0) && !(strncmp((char*) &Memory.ROM [0x10], "SFC-ADX BACKUP", 14) == 0))
      {
         Memory.LoROM = true;
         Memory.HiROM = false;
         Interleaved = false;
         Tales = false;
      }
      else if (strncmp ((char *) &Memory.ROM [0x7fc0], "YUYU NO QUIZ DE GO!GO!", 22) == 0 || strncmp ((char *) &Memory.ROM [0x7fc0], "SP MOMOTAROU DENTETSU2", 22) == 0 || strncmp ((char *) &Memory.ROM [0x7fc0], "SUPER FORMATION SOCCE", 21) == 0)
      {
         Memory.LoROM = true;
         Memory.HiROM = false;
         Interleaved = false;
      }
      /* BS Zooっと麻雀 */
      else if ((strncmp ((char *) &Memory.ROM [0xffc0], "Ｚｏｏっと麻雀！", 16) == 0)|| (strncmp ((char *) &Memory.ROM [0xffc0], "Zooっと麻雀!IVT", 15) == 0))
      {
         Memory.LoROM = false;
         Memory.HiROM = true;
      }
      /* 再BS探偵倶楽部 */
      else if (strncmp ((char *) &Memory.ROM [0xffc0], "再BS探偵倶楽部", 14) == 0)
      {
         Memory.LoROM = false;
         Memory.HiROM = true;
      }
      /* BATMAN--REVENGE JOKER (USA) */
      else if (strncmp ((char *) &Memory.ROM [0xffc0], "BATMAN--REVENGE JOKER", 21) == 0)
      {
         Memory.LoROM = true;
         Memory.HiROM = false;
         Interleaved = true;
      }
      /* THE DUEL: TEST DRIVE */
      else if (strncmp ((char *) &Memory.ROM [0x7fc0], "THE DUEL: TEST DRIVE", 20) == 0)
      {
         Memory.LoROM = true;
         Memory.HiROM = false;
         Interleaved = false;
      }
      /* ポパイ いじわる魔女シーハッグの巻 */
      else if (strncmp ((char *) &Memory.ROM [0x7fc0], "POPEYE IJIWARU MAJO", 19) == 0)
      {
         Memory.LoROM = true;
         Memory.HiROM = false;
         Interleaved = false;
      }
      /* Pop'nツインビー サンプル版 */
      else if(strncmp ((char *) &Memory.ROM [0x7fc0], "POPN TWINBEE", 12) == 0)
      {
         Memory.LoROM = true;
         Memory.HiROM = false;
         Interleaved = false;
      }
      /* Mario Early Years: Fun with Numbers */
      else if ((strncmp ((char *) &Memory.ROM [0x7fc0], "MEY Fun with Numbers", 20) == 0))
      {
         int32_t i;
         for (i = 0x87fc0; i < 0x87fe0; i++)
            Memory.ROM [i] = 0;
      }
      else if(Memory.CalculatedSize == 0x100000 && strncmp ((char *) &Memory.ROM [0xffc0], "WWF SUPER WRESTLEMANIA", 22) == 0)
      {
         int32_t cvcount;
         memcpy(&Memory.ROM[0x100000], Memory.ROM, 0x100000);
         for(cvcount = 0; cvcount < 16; cvcount++)
         {
            memcpy(&Memory.ROM[0x8000 * cvcount], &Memory.ROM[0x10000 * cvcount + 0x100000 + 0x8000], 0x8000);
            memcpy(&Memory.ROM[0x8000 * cvcount + 0x80000], &Memory.ROM[0x10000 * cvcount + 0x100000], 0x8000);
         }
         Memory.LoROM = true;
         Memory.HiROM = false;
         memset(&Memory.ROM[Memory.CalculatedSize], 0, MAX_ROM_SIZE - Memory.CalculatedSize);
      }
   }

   if (!Settings.ForceNotInterleaved && Interleaved)
   {
      CPU.TriedInterleavedMode2 = true;

      if (Tales)
      {
         if (Memory.ExtendedFormat == BIGFIRST)
         {
            S9xDeinterleaveType1(0x400000, Memory.ROM);
            S9xDeinterleaveType1(Memory.CalculatedSize - 0x400000, Memory.ROM + 0x400000);
         }
         else
         {
            S9xDeinterleaveType1(Memory.CalculatedSize - 0x400000, Memory.ROM);
            S9xDeinterleaveType1(0x400000, Memory.ROM + Memory.CalculatedSize - 0x400000);
         }

         Memory.LoROM = false;
         Memory.HiROM = true;
      }
      else if (Settings.ForceInterleaved2)
         S9xDeinterleaveType2(false);
      else if (Memory.CalculatedSize == 0x300000)
      {
         bool t = Memory.LoROM;

         Memory.LoROM = Memory.HiROM;
         Memory.HiROM = t;
         S9xDeinterleaveGD24(Memory.CalculatedSize, Memory.ROM);
      }
      else
      {
         bool t = Memory.LoROM;

         Memory.LoROM = Memory.HiROM;
         Memory.HiROM = t;

         S9xDeinterleaveType1(Memory.CalculatedSize, Memory.ROM);
      }

      hi_score = ScoreHiROM(false, 0);
      lo_score = ScoreLoROM(false, 0);

      if ((Memory.HiROM && (lo_score >= hi_score || hi_score < 0)) || (Memory.LoROM && (hi_score > lo_score || lo_score < 0)))
      {
         if (retry_count == 0)
         {
            Settings.ForceNotInterleaved = true;
            Settings.ForceInterleaved = false;
            retry_count++;
            goto again;
         }
      }
   }

   if (Memory.ExtendedFormat == SMALLFIRST)
      Tales = true;

   InitROM(Tales);
   S9xLoadCheatFile(S9xGetFilename("cht"));
   S9xInitCheatData();
   S9xApplyCheats();
   S9xReset();
   return true;
}

/* compatibility wrapper */
void S9xDeinterleaveMode2(void)
{
   S9xDeinterleaveType2(true);
}

void S9xDeinterleaveType2(bool reset)
{
   uint32_t TmpAdj;
   uint8_t *tmp;
   uint8_t blocks [256];
   int32_t i;
   int32_t nblocks = Memory.CalculatedSize >> 16;
   int32_t step = 64;

   while (nblocks <= step)
      step >>= 1;

   nblocks = step;

   for (i = 0; i < nblocks * 2; i++)
   {
      blocks [i] = (i & ~0xF) | ((i & 3) << 2) | ((i & 12) >> 2);
   }

#ifdef DS2_DMA
   tmp = (uint8_t*) AlignedMalloc(0x10000, 32, &TmpAdj);
#else
   tmp = (uint8_t*) malloc(0x10000);
#endif

   if (tmp)
   {
#ifdef DS2_DMA
      __dcache_writeback_all();
#endif
      for (i = 0; i < nblocks * 2; i++)
      {
         int32_t j;
         for (j = i; j < nblocks * 2; j++)
         {
            if (blocks [j] == i)
            {
               uint8_t b;
#ifdef DS2_DMA
               ds2_DMAcopy_32Byte(2 /* channel: emu internal */, tmp, &Memory.ROM [blocks [j] * 0x10000], 0x10000);
               ds2_DMA_wait(2);
               ds2_DMA_stop(2);
               ds2_DMAcopy_32Byte(2 /* channel: emu internal */, &Memory.ROM [blocks [j] * 0x10000], &Memory.ROM [blocks [i] * 0x10000], 0x10000);
               ds2_DMA_wait(2);
               ds2_DMA_stop(2);
               ds2_DMAcopy_32Byte(2 /* channel: emu internal */, &Memory.ROM [blocks [i] * 0x10000], tmp, 0x10000);
               ds2_DMA_wait(2);
               ds2_DMA_stop(2);
#else
               /* memmove converted: Different mallocs [Neb] */
               memcpy(tmp, &Memory.ROM [blocks [j] * 0x10000], 0x10000);
               /* memmove converted: Different addresses, or identical if blocks[i] == blocks[j] [Neb] */
               memcpy(&Memory.ROM [blocks [j] * 0x10000], &Memory.ROM [blocks [i] * 0x10000], 0x10000);
               /* memmove converted: Different mallocs [Neb] */
               memcpy(&Memory.ROM [blocks [i] * 0x10000], tmp, 0x10000);
#endif
               b          = blocks [j];
               blocks [j] = blocks [i];
               blocks [i] = b;
               break;
            }
         }
      }
      free(tmp);
      tmp = NULL;
   }
   if (reset)
   {
      InitROM(false);
      S9xReset();
   }
}

void InitROM(bool Interleaved)
{
   uint8_t* RomHeader;
   uint32_t sum1 = 0;
   uint32_t sum2 = 0;

   SuperFX.nRomBanks = Memory.CalculatedSize >> 15;
   Settings.MultiPlayer5Master = Settings.MultiPlayer5;
   Settings.MouseMaster = Settings.Mouse;
   Settings.SuperScopeMaster = Settings.SuperScope;
   Settings.DSP1Master = Settings.ForceDSP1;
   Settings.SuperFX = false;
   Settings.DSP = 0;
   Settings.SA1 = false;
   Settings.C4 = false;
   Settings.SDD1 = false;
   Settings.SRTC = false;
   Settings.SPC7110 = false;
   Settings.SPC7110RTC = false;
   Settings.BS = false;
   Settings.OBC1 = false;
   Settings.SETA = false;
   s7r.DataRomSize = 0;
   Memory.CalculatedChecksum = 0;

   RomHeader = Memory.ROM + 0x7FB0;

   if (Memory.ExtendedFormat == BIGFIRST)
      RomHeader += 0x400000;

   if (Memory.HiROM)
      RomHeader += 0x8000;

   if (!Settings.BS)
   {
      Settings.BS = is_bsx(Memory.ROM + 0x7FC0);

      if (Settings.BS)
      {
         Memory.LoROM = true;
         Memory.HiROM = false;
      }
      else
      {
         Settings.BS = is_bsx(Memory.ROM + 0xFFC0);
         if (Settings.BS)
         {
            Memory.HiROM = true;
            Memory.LoROM = false;
         }
      }
   }

   memset(Memory.BlockIsRAM, 0, MEMMAP_NUM_BLOCKS);
   memset(Memory.BlockIsROM, 0, MEMMAP_NUM_BLOCKS);

   memset(Memory.ROMId, 0, 5);
   memset(Memory.CompanyId, 0, 3);

   ParseSNESHeader(RomHeader);

   /* Detect and initialize chips - detection codes are compatible with NSRT */

   /* DSP1/2/3/4 */
   if (Memory.ROMType == 0x03)
   {
      if (Memory.ROMSpeed == 0x30)
         Settings.DSP = 4; /* DSP4 */
      else
         Settings.DSP = 1; /* DSP1 */
   }
   else if (Memory.ROMType == 0x05)
   {
      if (Memory.ROMSpeed == 0x20)
         Settings.DSP = 2; /* DSP2 */
      else if (Memory.ROMSpeed == 0x30 && RomHeader[0x2a] == 0xb2)
         Settings.DSP = 3; /* DSP3 */
      else
         Settings.DSP = 1; /* DSP1 */
   }

   switch (Settings.DSP)
   {
      case 1: /* DSP1 */
         SetDSP = &DSP1SetByte;
         GetDSP = &DSP1GetByte;
         break;
      case 2: /* DSP2 */
         SetDSP = &DSP2SetByte;
         GetDSP = &DSP2GetByte;
         break;
      case 3: /* DSP3 */
         /* SetDSP = &DSP3SetByte; */
         /* GetDSP = &DSP3GetByte; */
         break;
      case 4: /* DSP4 */
         SetDSP = &DSP4SetByte;
         GetDSP = &DSP4GetByte;
         break;
      default:
         SetDSP = NULL;
         GetDSP = NULL;
         break;
   }

   if(!Settings.ForceNoDSP1 && Settings.DSP)
      Settings.DSP1Master = true;

   if (Memory.HiROM)
   {
      /* Enable S-RTC (Real Time Clock) emulation for Dai Kaijyu Monogatari 2 */
      Settings.SRTC = ((Memory.ROMType & 0xf0) >> 4) == 5;

      if (((Memory.ROMSpeed & 0x0F) == 0x0A) && ((Memory.ROMType & 0xF0) == 0xF0))
      {
         Settings.SPC7110 = true;
         if ((Memory.ROMType & 0x0F) == 0x09)
            Settings.SPC7110RTC = true;
      }

      if (Settings.SPC7110)
         SPC7110HiROMMap();
      else if ((Memory.ROMSpeed & ~0x10) == 0x25)
         TalesROMMap(Interleaved);
      else
         HiROMMap();
   }
   else
   {
      Settings.SuperFX = Settings.ForceSuperFX;

      if (Memory.ROMType == 0x25)
         Settings.OBC1 = true;

      /* BS-X BIOS */
      if (Memory.ROMType == 0xE5)
         Settings.BS = true;

      if ((Memory.ROMType & 0xf0) == 0x10)
         Settings.SuperFX = !Settings.ForceNoSuperFX;

      /* OBC1 hack ROM */
      if (strncmp(Memory.ROMName, "METAL COMBAT", 12) == 0 && Memory.ROMType == 0x13 && Memory.ROMSpeed == 0x42)
      {
         Settings.OBC1 = true;
         Settings.SuperFX = Settings.ForceSuperFX;
         Memory.ROMSpeed = 0x30;
      }

      Settings.SDD1 = Settings.ForceSDD1;
      if ((Memory.ROMType & 0xf0) == 0x40)
         Settings.SDD1 = !Settings.ForceNoSDD1;

      if (((Memory.ROMType & 0xF0) == 0xF0) & ((Memory.ROMSpeed & 0x0F) != 5))
      {
         Memory.SRAMSize = 2;
         if ((Memory.ROMType & 0x0F) == 6)
         {
            if (Memory.ROM[0x7FD7] == 0x09)
            {
               Settings.SETA = ST_011;
               SetSETA = &S9xSetST011;
               GetSETA = &S9xGetST011;
            }
            else
            {
               Settings.SETA = ST_010;
               SetSETA = &S9xSetST010;
               GetSETA = &S9xGetST010;
            }
         }
         else
            Settings.SETA = ST_018;
      }
      Settings.C4 = Settings.ForceC4;
      if ((Memory.ROMType & 0xf0) == 0xf0 && (strncmp(Memory.ROMName, "MEGAMAN X", 9) == 0 || strncmp(Memory.ROMName, "ROCKMAN X", 9) == 0))
         Settings.C4 = !Settings.ForceNoC4;

      if (Settings.SETA && Settings.SETA != ST_018)
         SetaDSPMap();
      else if (Settings.SuperFX)
      {
         SuperFXROMMap();
         Settings.MultiPlayer5Master = false;
         Settings.DSP1Master = false;
         Settings.SA1 = false;
         Settings.C4 = false;
         Settings.SDD1 = false;
      }
      else if (Settings.ForceSA1 || (!Settings.ForceNoSA1 && (Memory.ROMSpeed & ~0x10) == 0x23 && (Memory.ROMType & 0xf) > 3 && (Memory.ROMType & 0xf0) == 0x30))
      {
         Settings.SA1 = true;
         Settings.DSP1Master = false;
         Settings.C4 = false;
         Settings.SDD1 = false;
         SA1ROMMap();
      }
      else if ((Memory.ROMSpeed & ~0x10) == 0x25)
         TalesROMMap(Interleaved);
      else if (Memory.ExtendedFormat != NOPE)
         JumboLoROMMap(Interleaved);
      else if (strncmp((char*) &Memory.ROM [0x7fc0], "SOUND NOVEL-TCOOL", 17) == 0 || strncmp((char*) &Memory.ROM [0x7fc0], "DERBY STALLION 96", 17) == 0)
      {
         LoROM24MBSMap();
         Settings.DSP1Master = false;
      }
      else if (strncmp((char*) &Memory.ROM [0x7fc0], "THOROUGHBRED BREEDER3", 21) == 0 || strncmp((char*) &Memory.ROM [0x7fc0], "RPG-TCOOL 2", 11) == 0)
      {
         SRAM512KLoROMMap();
         Settings.DSP1Master = false;
      }
      else if (strncmp((char*) &Memory.ROM [0x7fc0], "DEZAEMON  ", 10) == 0)
      {
         SRAM1024KLoROMMap();
         Settings.DSP1Master = false;
      }
      else if (strncmp((char*) &Memory.ROM [0x7fc0], "ADD-ON BASE CASSETE", 19) == 0)
      {
         strncpy(Memory.ROMName, (char *) &Memory.ROM[0x100010], ROM_NAME_LEN - 1);
         Settings.MultiPlayer5Master = false;
         Settings.MouseMaster = false;
         Settings.SuperScopeMaster = false;
         Settings.DSP1Master = false;
         Memory.SRAMSize = 5;
         SufamiTurboLoROMMap();
      }
      else if ((strncmp((char *) &Memory.ROM [0x7fc0], "ROCKMAN X  ", 11) == 0)|| (strncmp((char *) &Memory.ROM [0x7fc0], "MEGAMAN X  ", 11) == 0)|| (strncmp((char *) &Memory.ROM [0x7fc0], "demon's blazon", 14) == 0)|| (strncmp((char *) &Memory.ROM [0x7fc0], "demon's crest", 13) == 0))
         CapcomProtectLoROMMap();
      else if ((Memory.ROMSpeed & ~0x10) == 0x22 && strncmp(Memory.ROMName, "Super Street Fighter", 20) != 0)
         AlphaROMMap();
      else if (strncmp ((char *) &Memory.ROM [0x7fc0], "HITOMI3", 7) == 0)
      {
         Memory.SRAMSize = 3;
         LoROMMap();
      }
      else
         LoROMMap();
   }

   if (Settings.BS)
      Memory.ROMRegion = 0;

   if (!Memory.CalculatedChecksum)
   {
      int32_t i;
      uint32_t remainder;
      int32_t power2 = 0;
      int32_t sub = 0;
      int32_t size = Memory.CalculatedSize;

      while (size >>= 1)
         power2++;

      size      = 1 << power2;
      remainder = Memory.CalculatedSize - size;

      for (i = 0; i < size; i++)
         sum1 += Memory.ROM [i];

      for (i = 0; i < (int32_t) remainder; i++)
         sum2 += Memory.ROM [size + i];

      if (Settings.BS && Memory.ROMType != 0xE5)
      {
         if (Memory.HiROM)
            for (i = 0; i < 48; i++)
               sub += Memory.ROM[0xffb0 + i];
         else if (Memory.LoROM)
            for (i = 0; i < 48; i++)
               sub += Memory.ROM[0x7fb0 + i];
         sum1 -= sub;
      }

      if (remainder)
         sum1 += sum2 * (size / remainder);

      sum1 &= 0xffff;
      Memory.CalculatedChecksum = sum1;
   }

   if (Settings.ForceNTSC)
      Settings.PAL = false;
   else if (Settings.ForcePAL)
      Settings.PAL = true;
   else
   {
      /* Korea refers to South Korea, which uses NTSC */
      switch (Memory.ROMRegion)
      {
      case 13:
      case 1:
      case 0:
         Settings.PAL = false;
         break;
      default:
         Settings.PAL = true;
         break;
      }
   }
   if (Settings.PAL)
   {
      Settings.FrameTime = Settings.FrameTimePAL;
      Memory.ROMFramesPerSecond = 50;
   }
   else
   {
      Settings.FrameTime = Settings.FrameTimeNTSC;
      Memory.ROMFramesPerSecond = 60;
   }

   Memory.ROMName[ROM_NAME_LEN - 1] = 0;
   if (strlen(Memory.ROMName))
   {
      char* p = Memory.ROMName + strlen(Memory.ROMName) - 1;

      while (p > Memory.ROMName && *(p - 1) == ' ')
         p--;
      *p = 0;
   }

   Memory.SRAMMask = Memory.SRAMSize ? ((1 << (Memory.SRAMSize + 3)) * 128) - 1 : 0;

#ifndef USE_BLARGG_APU
   IAPU.OneCycle = ONE_APU_CYCLE;
#endif
   Settings.Shutdown = true;
   ResetSpeedMap();
   ApplyROMFixes();
   sprintf(Memory.ROMName, "%s", Safe(Memory.ROMName));
   sprintf(Memory.ROMId, "%s", Safe(Memory.ROMId));
   sprintf(Memory.CompanyId, "%s", Safe(Memory.CompanyId));

   fprintf(stderr,
           "\"%s\" [%s] %s, %s, Type: %s, Mode: %s, TV: %s, S-RAM: %s, ROMId: %s Company: %2.2s\n",
           Memory.ROMName, (Memory.ROMChecksum + Memory.ROMComplementChecksum != 0xffff || Memory.ROMChecksum != Memory.CalculatedChecksum) ? "bad checksum" : "checksum ok", MapType(), Size(), KartContents(), MapMode(), TVStandard(), StaticRAMSize(), Memory.ROMId, Memory.CompanyId);

   Settings.ForceHeader = Settings.ForceHiROM = Settings.ForceLoROM = Settings.ForceInterleaved = Settings.ForceNoHeader = Settings.ForceNotInterleaved = Settings.ForceInterleaved2 = false;
}

void FixROMSpeed(void)
{
   int32_t c;

   if (CPU.FastROMSpeed == 0)
      CPU.FastROMSpeed = SLOW_ONE_CYCLE;

   for (c = 0x800; c < 0x1000; c++)
      if (c & 0x8 || c & 0x400)
         Memory.MemorySpeed [c] = (uint8_t) CPU.FastROMSpeed;
}

void ResetSpeedMap(void)
{
   int32_t i;
   memset(Memory.MemorySpeed, SLOW_ONE_CYCLE, 0x1000);
   for (i = 0; i < 0x400; i += 0x10)
   {
      Memory.MemorySpeed[i + 2] = Memory.MemorySpeed[0x800 + i + 2] = ONE_CYCLE;
      Memory.MemorySpeed[i + 3] = Memory.MemorySpeed[0x800 + i + 3] = ONE_CYCLE;
      Memory.MemorySpeed[i + 4] = Memory.MemorySpeed[0x800 + i + 4] = ONE_CYCLE;
      Memory.MemorySpeed[i + 5] = Memory.MemorySpeed[0x800 + i + 5] = ONE_CYCLE;
   }
   FixROMSpeed();
}

void map_space(uint32_t bank_s, uint32_t bank_e, uint32_t addr_s, uint32_t addr_e, uint8_t *data)
{
   uint32_t c, i, p;

   for (c = bank_s; c <= bank_e; c++)
   {
      for (i = addr_s; i <= addr_e; i += 0x1000)
      {
         p = (c << 4) | (i >> 12);
         Memory.Map[p] = data;
         Memory.BlockIsROM[p] = false;
         Memory.BlockIsRAM[p] = true;
      }
   }
}

void map_index(uint32_t bank_s, uint32_t bank_e, uint32_t addr_s, uint32_t addr_e, intptr_t index, int32_t type)
{
   uint32_t c, i, p;
   bool isROM, isRAM;

   isROM = !((type == MAP_TYPE_I_O) || (type == MAP_TYPE_RAM));
   isRAM = !((type == MAP_TYPE_I_O) || (type == MAP_TYPE_ROM));

   for (c = bank_s; c <= bank_e; c++)
   {
      for (i = addr_s; i <= addr_e; i += 0x1000)
      {
         p = (c << 4) | (i >> 12);
         Memory.Map[p] = (uint8_t*) index;
         Memory.BlockIsROM[p] = isROM;
         Memory.BlockIsRAM[p] = isRAM;
      }
   }
}

void WriteProtectROM(void)
{
   int32_t c;

   /* memmove converted: Different mallocs [Neb] */
   memcpy(Memory.WriteMap, Memory.Map, sizeof(Memory.Map));
   for (c = 0; c < 0x1000; c++)
      if (Memory.BlockIsROM [c])
         Memory.WriteMap [c] = (uint8_t*) MAP_NONE;
}

void MapRAM(void)
{
   int32_t c, i;

   if (Memory.LoROM && !Settings.SDD1)
   {
      /* Banks 70->7d and f0->fe 0x0000-0x7FFF, S-RAM */
      for (c = 0; c < 0x0f; c++)
      {
         for (i = 0; i < 8; i++)
         {
            Memory.Map [(c << 4) + 0xF00 + i] = Memory.Map [(c << 4) + 0x700 + i] = MAP_LOROM_SRAM_OR_NONE;
            Memory.BlockIsRAM [(c << 4) + 0xF00 + i] = Memory.BlockIsRAM [(c << 4) + 0x700 + i] = true;
            Memory.BlockIsROM [(c << 4) + 0xF00 + i] = Memory.BlockIsROM [(c << 4) + 0x700 + i] = false;
         }
      }
      if(Memory.CalculatedSize <= 0x200000)
      {
         /* Banks 70->7d 0x8000-0xffff S-RAM */
         for (c = 0; c < 0x0e; c++)
         {
            for(i = 8; i < 16; i++)
            {
               Memory.Map [(c << 4) + 0x700 + i] = MAP_LOROM_SRAM_OR_NONE;
               Memory.BlockIsRAM [(c << 4) + 0x700 + i] = true;
               Memory.BlockIsROM [(c << 4) + 0x700 + i] = false;
            }
         }
      }
   }
   else if(Memory.LoROM && Settings.SDD1)
   {
      /* Banks 70->7d 0x0000-0x7FFF, S-RAM */
      for (c = 0; c < 0x0f; c++)
      {
         for (i = 0; i < 8; i++)
         {
            Memory.Map [(c << 4) + 0x700 + i] = MAP_LOROM_SRAM_OR_NONE;
            Memory.BlockIsRAM [(c << 4) + 0x700 + i] = true;
            Memory.BlockIsROM [(c << 4) + 0x700 + i] = false;
         }
      }
   }
   /* Banks 7e->7f, RAM */
   for (c = 0; c < 16; c++)
   {
      Memory.Map [c + 0x7e0] = Memory.RAM;
      Memory.Map [c + 0x7f0] = Memory.RAM + 0x10000;
      Memory.BlockIsRAM [c + 0x7e0] = true;
      Memory.BlockIsRAM [c + 0x7f0] = true;
      Memory.BlockIsROM [c + 0x7e0] = false;
      Memory.BlockIsROM [c + 0x7f0] = false;
   }
   WriteProtectROM();
}

void MapExtraRAM(void)
{
   int32_t c;

   /* Banks 7e->7f, RAM */
   for (c = 0; c < 16; c++)
   {
      Memory.Map [c + 0x7e0] = Memory.RAM;
      Memory.Map [c + 0x7f0] = Memory.RAM + 0x10000;
      Memory.BlockIsRAM [c + 0x7e0] = true;
      Memory.BlockIsRAM [c + 0x7f0] = true;
      Memory.BlockIsROM [c + 0x7e0] = false;
      Memory.BlockIsROM [c + 0x7f0] = false;
   }

   /* Banks 70->73, S-RAM */
   for (c = 0; c < 16; c++)
   {
      Memory.Map [c + 0x700] = Memory.SRAM;
      Memory.Map [c + 0x710] = Memory.SRAM + 0x8000;
      Memory.Map [c + 0x720] = Memory.SRAM + 0x10000;
      Memory.Map [c + 0x730] = Memory.SRAM + 0x18000;

      Memory.BlockIsRAM [c + 0x700] = true;
      Memory.BlockIsROM [c + 0x700] = false;
      Memory.BlockIsRAM [c + 0x710] = true;
      Memory.BlockIsROM [c + 0x710] = false;
      Memory.BlockIsRAM [c + 0x720] = true;
      Memory.BlockIsROM [c + 0x720] = false;
      Memory.BlockIsRAM [c + 0x730] = true;
      Memory.BlockIsROM [c + 0x730] = false;
   }
}

void LoROMMap(void)
{
   int32_t c;
   int32_t i;

   /* Banks 00->3f and 80->bf */
   for (c = 0; c < 0x400; c += 16)
   {
      Memory.Map [c + 0] = Memory.Map [c + 0x800] = Memory.RAM;
      Memory.Map [c + 1] = Memory.Map [c + 0x801] = Memory.RAM;
      Memory.BlockIsRAM [c + 0] = Memory.BlockIsRAM [c + 0x800] = true;
      Memory.BlockIsRAM [c + 1] = Memory.BlockIsRAM [c + 0x801] = true;

      Memory.Map [c + 2] = Memory.Map [c + 0x802] = (uint8_t*) MAP_PPU;
      if (Settings.SETA == ST_018)
         Memory.Map [c + 3] = Memory.Map [c + 0x803] = (uint8_t*) MAP_SETA_RISC;
      else
         Memory.Map [c + 3] = Memory.Map [c + 0x803] = (uint8_t*) MAP_PPU;
      Memory.Map [c + 4] = Memory.Map [c + 0x804] = (uint8_t*) MAP_CPU;
      Memory.Map [c + 5] = Memory.Map [c + 0x805] = (uint8_t*) MAP_CPU;
      if (Settings.C4)
      {
         Memory.Map [c + 6] = Memory.Map [c + 0x806] = (uint8_t*) MAP_C4;
         Memory.Map [c + 7] = Memory.Map [c + 0x807] = (uint8_t*) MAP_C4;
      }
      else if(Settings.OBC1)
      {
         Memory.Map [c + 6] = Memory.Map [c + 0x806] = (uint8_t*) MAP_OBC_RAM;
         Memory.Map [c + 7] = Memory.Map [c + 0x807] = (uint8_t*) MAP_OBC_RAM;
      }
      else
      {
         Memory.Map [c + 6] = Memory.Map [c + 0x806] = (uint8_t*) bytes0x2000 - 0x6000;
         Memory.Map [c + 7] = Memory.Map [c + 0x807] = (uint8_t*) bytes0x2000 - 0x6000;
      }

      for (i = c + 8; i < c + 16; i++)
      {
         Memory.Map [i] = Memory.Map [i + 0x800] = &Memory.ROM [(c << 11) % Memory.CalculatedSize] - 0x8000;
         Memory.BlockIsROM [i] = Memory.BlockIsROM [i + 0x800] = true;
      }
   }

   /* Banks 40->7f and c0->ff */
   for (c = 0; c < 0x400; c += 16)
   {
      for (i = c; i < c + 8; i++)
         Memory.Map [i + 0x400] = Memory.Map [i + 0xc00] = &Memory.ROM [(c << 11) % Memory.CalculatedSize];

      for (i = c + 8; i < c + 16; i++)
         Memory.Map [i + 0x400] = Memory.Map [i + 0xc00] = &Memory.ROM [((c << 11) + 0x200000) % Memory.CalculatedSize] - 0x8000;

      for (i = c; i < c + 16; i++)
         Memory.BlockIsROM [i + 0x400] = Memory.BlockIsROM [i + 0xc00] = true;
   }

   if (Settings.DSP)
      DSPMap();

   MapRAM();
   WriteProtectROM();
}

void DSPMap(void)
{
   switch (Settings.DSP)
   {
      case 1:
         if (Memory.HiROM)
         {
            map_index(0x00, 0x1f, 0x6000, 0x7fff, MAP_DSP, MAP_TYPE_I_O);
            map_index(0x80, 0x9f, 0x6000, 0x7fff, MAP_DSP, MAP_TYPE_I_O);
            break;
         }
         else if (Memory.CalculatedSize > 0x100000)
         {
            map_index(0x60, 0x6f, 0x0000, 0x7fff, MAP_DSP, MAP_TYPE_I_O);
            map_index(0xe0, 0xef, 0x0000, 0x7fff, MAP_DSP, MAP_TYPE_I_O);
            break;
         }
         else
         {
            map_index(0x20, 0x3f, 0x8000, 0xffff, MAP_DSP, MAP_TYPE_I_O);
            map_index(0xa0, 0xbf, 0x8000, 0xffff, MAP_DSP, MAP_TYPE_I_O);
            break;
         }
      case 2:
         map_index(0x20, 0x3f, 0x6000, 0x6fff, MAP_DSP, MAP_TYPE_I_O);
         map_index(0x20, 0x3f, 0x8000, 0xbfff, MAP_DSP, MAP_TYPE_I_O);
         map_index(0xa0, 0xbf, 0x6000, 0x6fff, MAP_DSP, MAP_TYPE_I_O);
         map_index(0xa0, 0xbf, 0x8000, 0xbfff, MAP_DSP, MAP_TYPE_I_O);
         break;
      case 3:
         map_index(0x20, 0x3f, 0x8000, 0xffff, MAP_DSP, MAP_TYPE_I_O);
         map_index(0xa0, 0xbf, 0x8000, 0xffff, MAP_DSP, MAP_TYPE_I_O);
         break;
      case 4:
         map_index(0x30, 0x3f, 0x8000, 0xffff, MAP_DSP, MAP_TYPE_I_O);
         map_index(0xb0, 0xbf, 0x8000, 0xffff, MAP_DSP, MAP_TYPE_I_O);
         break;
   }
}

void SetaDSPMap(void)
{
   int32_t c;
   int32_t i;

   /* Banks 00->3f and 80->bf */
   for (c = 0; c < 0x400; c += 16)
   {
      Memory.Map [c + 0] = Memory.Map [c + 0x800] = Memory.RAM;
      Memory.Map [c + 1] = Memory.Map [c + 0x801] = Memory.RAM;
      Memory.BlockIsRAM [c + 0] = Memory.BlockIsRAM [c + 0x800] = true;
      Memory.BlockIsRAM [c + 1] = Memory.BlockIsRAM [c + 0x801] = true;

      Memory.Map [c + 2] = Memory.Map [c + 0x802] = (uint8_t*) MAP_PPU;
      Memory.Map [c + 3] = Memory.Map [c + 0x803] = (uint8_t*) MAP_PPU;
      Memory.Map [c + 4] = Memory.Map [c + 0x804] = (uint8_t*) MAP_CPU;
      Memory.Map [c + 5] = Memory.Map [c + 0x805] = (uint8_t*) MAP_CPU;
      Memory.Map [c + 6] = Memory.Map [c + 0x806] = (uint8_t*) bytes0x2000 - 0x6000;
      Memory.Map [c + 7] = Memory.Map [c + 0x807] = (uint8_t*) bytes0x2000 - 0x6000;

      for (i = c + 8; i < c + 16; i++)
      {
         Memory.Map [i] = Memory.Map [i + 0x800] = &Memory.ROM [(c << 11) % Memory.CalculatedSize] - 0x8000;
         Memory.BlockIsROM [i] = Memory.BlockIsROM [i + 0x800] = true;
      }
   }

   /* Banks 40->7f and c0->ff */
   for (c = 0; c < 0x400; c += 16)
   {
      for (i = c + 8; i < c + 16; i++)
         Memory.Map [i + 0x400] = Memory.Map [i + 0xc00] = &Memory.ROM [((c << 11) + 0x200000) % Memory.CalculatedSize] - 0x8000;

      /* only upper half is ROM */
      for (i = c + 8; i < c + 16; i++)
         Memory.BlockIsROM [i + 0x400] = Memory.BlockIsROM [i + 0xc00] = true;
   }

   memset(Memory.SRAM, 0, 0x1000);
   for (c = 0x600; c < 0x680; c += 0x10)
   {
      for (i = 0; i < 0x08; i++)
      {
         /* Where does the SETA chip access, anyway? Please confirm this. */
         Memory.Map[c + 0x80 + i] = (uint8_t*)MAP_SETA_DSP;
         Memory.BlockIsROM [c + 0x80 + i] = false;
         Memory.BlockIsRAM [c + 0x80 + i] = true;
      }

      for (i = 0; i < 0x04; i++)
      {
         /* and this! */
         Memory.Map[c + i] = (uint8_t*)MAP_SETA_DSP;
         Memory.BlockIsROM [c + i] = false;
      }
   }

   MapRAM();
   WriteProtectROM();
}

void HiROMMap(void)
{
   int32_t i;
   int32_t c;

   /* Banks 00->3f and 80->bf */
   for (c = 0; c < 0x400; c += 16)
   {
      Memory.Map [c + 0] = Memory.Map [c + 0x800] = Memory.RAM;
      Memory.BlockIsRAM [c + 0] = Memory.BlockIsRAM [c + 0x800] = true;
      Memory.Map [c + 1] = Memory.Map [c + 0x801] = Memory.RAM;
      Memory.BlockIsRAM [c + 1] = Memory.BlockIsRAM [c + 0x801] = true;

      Memory.Map [c + 2] = Memory.Map [c + 0x802] = (uint8_t*) MAP_PPU;
      Memory.Map [c + 3] = Memory.Map [c + 0x803] = (uint8_t*) MAP_PPU;
      Memory.Map [c + 4] = Memory.Map [c + 0x804] = (uint8_t*) MAP_CPU;
      Memory.Map [c + 5] = Memory.Map [c + 0x805] = (uint8_t*) MAP_CPU;
      Memory.Map [c + 6] = Memory.Map [c + 0x806] = (uint8_t*) MAP_NONE;
      Memory.Map [c + 7] = Memory.Map [c + 0x807] = (uint8_t*) MAP_NONE;

      for (i = c + 8; i < c + 16; i++)
      {
         Memory.Map [i] = Memory.Map [i + 0x800] = &Memory.ROM [(c << 12) % Memory.CalculatedSize];
         Memory.BlockIsROM [i] = Memory.BlockIsROM [i + 0x800] = true;
      }
   }

   /* Banks 30->3f and b0->bf, address ranges 6000->7fff is S-RAM. */
   for (c = 0; c < 16; c++)
   {
      Memory.Map [0x306 + (c << 4)] = MAP_HIROM_SRAM_OR_NONE;
      Memory.Map [0x307 + (c << 4)] = MAP_HIROM_SRAM_OR_NONE;
      Memory.Map [0xb06 + (c << 4)] = MAP_HIROM_SRAM_OR_NONE;
      Memory.Map [0xb07 + (c << 4)] = MAP_HIROM_SRAM_OR_NONE;
      Memory.BlockIsRAM [0x306 + (c << 4)] = true;
      Memory.BlockIsRAM [0x307 + (c << 4)] = true;
      Memory.BlockIsRAM [0xb06 + (c << 4)] = true;
      Memory.BlockIsRAM [0xb07 + (c << 4)] = true;
   }

   /* Banks 40->7f and c0->ff */
   for (c = 0; c < 0x400; c += 16)
   {
      for (i = c; i < c + 16; i++)
      {
         Memory.Map [i + 0x400] = Memory.Map [i + 0xc00] = &Memory.ROM [(c << 12) % Memory.CalculatedSize];
         Memory.BlockIsROM [i + 0x400] = Memory.BlockIsROM [i + 0xc00] = true;
      }
   }

   if (Settings.DSP)
      DSPMap();

   MapRAM();
   WriteProtectROM();
}

void TalesROMMap(bool Interleaved)
{
   int32_t c;
   int32_t i;
   int32_t sum = 0;

   uint32_t OFFSET0 = 0x400000;
   uint32_t OFFSET1 = 0x400000;
   uint32_t OFFSET2 = 0x000000;

   if (Interleaved)
   {
      OFFSET0 = 0x000000;
      OFFSET1 = 0x000000;
      OFFSET2 = Memory.CalculatedSize - 0x400000; /* changed to work with interleaved DKJM2. */
   }

   /* Banks 00->3f and 80->bf */
   for (c = 0; c < 0x400; c += 16)
   {
      Memory.Map [c + 0] = Memory.Map [c + 0x800] = Memory.RAM;
      Memory.Map [c + 1] = Memory.Map [c + 0x801] = Memory.RAM;
      Memory.BlockIsRAM [c + 0] = Memory.BlockIsRAM [c + 0x800] = true;
      Memory.BlockIsRAM [c + 1] = Memory.BlockIsRAM [c + 0x801] = true;

      Memory.Map [c + 2] = Memory.Map [c + 0x802] = (uint8_t*) MAP_PPU;
      Memory.Map [c + 3] = Memory.Map [c + 0x803] = (uint8_t*) MAP_PPU;
      Memory.Map [c + 4] = Memory.Map [c + 0x804] = (uint8_t*) MAP_CPU;
      Memory.Map [c + 5] = Memory.Map [c + 0x805] = (uint8_t*) MAP_CPU;

      /* makes more sense to map the range here. */
      /* ToP seems to use sram to skip intro??? */
      if (c >= 0x300)
      {
         Memory.Map [c + 6] = Memory.Map [c + 0x806] = MAP_HIROM_SRAM_OR_NONE;
         Memory.Map [c + 7] = Memory.Map [c + 0x807] = MAP_HIROM_SRAM_OR_NONE;
         Memory.BlockIsRAM [6 + c] = Memory.BlockIsRAM [7 + c] = Memory.BlockIsRAM [0x806 + c] = Memory.BlockIsRAM [0x807 + c] = true;
      }
      else
      {
         Memory.Map [c + 6] = Memory.Map [c + 0x806] = (uint8_t*) MAP_NONE;
         Memory.Map [c + 7] = Memory.Map [c + 0x807] = (uint8_t*) MAP_NONE;
      }
      for (i = c + 8; i < c + 16; i++)
      {
         Memory.Map [i] = &Memory.ROM [((c << 12) % (Memory.CalculatedSize - 0x400000)) + OFFSET0];
         Memory.Map [i + 0x800] = &Memory.ROM [((c << 12) % 0x400000) + OFFSET2];
         Memory.BlockIsROM [i] = true;
         Memory.BlockIsROM [i + 0x800] = true;
      }
   }

   /* Banks 40->7f and c0->ff */
   for (c = 0; c < 0x400; c += 16)
   {
      for (i = c; i < c + 8; i++)
      {
         Memory.Map [i + 0x400] = &Memory.ROM [((c << 12) % (Memory.CalculatedSize - 0x400000)) + OFFSET1];
         Memory.Map [i + 0x408] = &Memory.ROM [((c << 12) % (Memory.CalculatedSize - 0x400000)) + OFFSET1];
         Memory.Map [i + 0xc00] = &Memory.ROM [((c << 12) % 0x400000) + OFFSET2];
         Memory.Map [i + 0xc08] = &Memory.ROM [((c << 12) % 0x400000) + OFFSET2];
         Memory.BlockIsROM [i + 0x400] = true;
         Memory.BlockIsROM [i + 0x408] = true;
         Memory.BlockIsROM [i + 0xc00] = true;
         Memory.BlockIsROM [i + 0xc08] = true;
      }
   }

   Memory.ROMChecksum = *(Memory.Map[8] + 0xFFDE) + (*(Memory.Map[8] + 0xFFDF) << 8);
   Memory.ROMComplementChecksum = *(Memory.Map[8] + 0xFFDC) + (*(Memory.Map[8] + 0xFFDD) << 8);

   for (i = 0x40; i < 0x80; i++)
   {
      uint8_t* bank_low = (uint8_t*)Memory.Map[i << 4];
      uint8_t* bank_high = (uint8_t*)Memory.Map[(i << 4) + 0x800];
      for (c = 0; c < 0x10000; c++)
      {
         sum += bank_low[c];
         sum += bank_high[c];
      }
   }

   Memory.CalculatedChecksum = sum & 0xFFFF;
   MapRAM();
   WriteProtectROM();
}

void AlphaROMMap(void)
{
   int32_t c;
   int32_t i;

   /* Banks 00->3f and 80->bf */
   for (c = 0; c < 0x400; c += 16)
   {
      Memory.Map [c + 0] = Memory.Map [c + 0x800] = Memory.RAM;
      Memory.Map [c + 1] = Memory.Map [c + 0x801] = Memory.RAM;
      Memory.BlockIsRAM [c + 0] = Memory.BlockIsRAM [c + 0x800] = true;
      Memory.BlockIsRAM [c + 1] = Memory.BlockIsRAM [c + 0x801] = true;

      Memory.Map [c + 2] = Memory.Map [c + 0x802] = (uint8_t*) MAP_PPU;
      Memory.Map [c + 3] = Memory.Map [c + 0x803] = (uint8_t*) MAP_PPU;
      Memory.Map [c + 4] = Memory.Map [c + 0x804] = (uint8_t*) MAP_CPU;
      Memory.Map [c + 5] = Memory.Map [c + 0x805] = (uint8_t*) MAP_CPU;
      Memory.Map [c + 6] = Memory.Map [c + 0x806] = (uint8_t*) MAP_NONE;
      Memory.Map [c + 7] = Memory.Map [c + 0x807] = (uint8_t*) MAP_NONE;

      for (i = c + 8; i < c + 16; i++)
      {
         Memory.Map [i] = Memory.Map [i + 0x800] = &Memory.ROM [(c << 11) % Memory.CalculatedSize] - 0x8000;
         Memory.BlockIsROM [i] = true;
      }
   }

   /* Banks 40->7f and c0->ff */

   for (c = 0; c < 0x400; c += 16)
   {
      for (i = c; i < c + 16; i++)
      {
         Memory.Map [i + 0x400] = &Memory.ROM [(c << 12) % Memory.CalculatedSize];
         Memory.Map [i + 0xc00] = &Memory.ROM [(c << 12) % Memory.CalculatedSize];
         Memory.BlockIsROM [i + 0x400] = Memory.BlockIsROM [i + 0xc00] = true;
      }
   }

   MapRAM();
   WriteProtectROM();
}

void DetectSuperFxRamSize(void)
{
   if (Memory.ROM[0x7FDA] == 0x33)
      Memory.SRAMSize = Memory.ROM[0x7FBD];
   else if (strncmp(Memory.ROMName, "STAR FOX 2", 10) == 0)
      Memory.SRAMSize = 6;
   else
      Memory.SRAMSize = 5;
}

void SuperFXROMMap(void)
{
   int32_t c;
   int32_t i;

   DetectSuperFxRamSize();

   /* Banks 00->3f and 80->bf */
   for (c = 0; c < 0x400; c += 16)
   {
      Memory.Map [c + 0] = Memory.Map [c + 0x800] = Memory.RAM;
      Memory.Map [c + 1] = Memory.Map [c + 0x801] = Memory.RAM;
      Memory.BlockIsRAM [c + 0] = Memory.BlockIsRAM [c + 0x800] = true;
      Memory.BlockIsRAM [c + 1] = Memory.BlockIsRAM [c + 0x801] = true;

      Memory.Map [c + 2] = Memory.Map [c + 0x802] = (uint8_t*) MAP_PPU;
      Memory.Map [c + 3] = Memory.Map [c + 0x803] = (uint8_t*) MAP_PPU;
      Memory.Map [c + 4] = Memory.Map [c + 0x804] = (uint8_t*) MAP_CPU;
      Memory.Map [c + 5] = Memory.Map [c + 0x805] = (uint8_t*) MAP_CPU;
      Memory.Map [0x006 + c] = Memory.Map [0x806 + c] = (uint8_t*) Memory.SRAM - 0x6000;
      Memory.Map [0x007 + c] = Memory.Map [0x807 + c] = (uint8_t*) Memory.SRAM - 0x6000;
      Memory.BlockIsRAM [0x006 + c] = Memory.BlockIsRAM [0x007 + c] = Memory.BlockIsRAM [0x806 + c] = Memory.BlockIsRAM [0x807 + c] = true;

      for (i = c + 8; i < c + 16; i++)
      {
         Memory.Map [i] = Memory.Map [i + 0x800] = &Memory.ROM [(c << 11) % Memory.CalculatedSize] - 0x8000;
         Memory.BlockIsROM [i] = Memory.BlockIsROM [i + 0x800] = true;
      }
   }

   /* Banks 40->7f and c0->ff */
   for (c = 0; c < 0x400; c += 16)
   {
      for (i = c; i < c + 16; i++)
      {
         Memory.Map [i + 0x400] = Memory.Map [i + 0xc00] = &Memory.ROM [(c << 12) % Memory.CalculatedSize];
         Memory.BlockIsROM [i + 0x400] = Memory.BlockIsROM [i + 0xc00] = true;
      }
   }

   /* Banks 7e->7f, RAM */
   for (c = 0; c < 16; c++)
   {
      Memory.Map [c + 0x7e0] = Memory.RAM;
      Memory.Map [c + 0x7f0] = Memory.RAM + 0x10000;
      Memory.BlockIsRAM [c + 0x7e0] = true;
      Memory.BlockIsRAM [c + 0x7f0] = true;
      Memory.BlockIsROM [c + 0x7e0] = false;
      Memory.BlockIsROM [c + 0x7f0] = false;
   }

   /* Banks 70->71, S-RAM */
   for (c = 0; c < 32; c++)
   {
      Memory.Map [c + 0x700] = Memory.SRAM + (((c >> 4) & 1) << 16);
      Memory.BlockIsRAM [c + 0x700] = true;
      Memory.BlockIsROM [c + 0x700] = false;
   }

   /* Replicate the first 2Mb of the ROM at ROM + 2MB such that each 32K
      block is repeated twice in each 64K block. */
#ifdef DS2_DMA
   __dcache_writeback_all();
#endif
   for (c = 0; c < 64; c++)
   {
#ifdef DS2_DMA
      ds2_DMAcopy_32Byte(2 /* channel: emu internal */, &ROM [0x200000 + c * 0x10000], &ROM [c * 0x8000], 0x8000);
      ds2_DMAcopy_32Byte(3 /* channel: emu internal 2 */, &ROM [0x208000 + c * 0x10000], &ROM [c * 0x8000], 0x8000);
      ds2_DMA_wait(2);
      ds2_DMA_wait(3);
      ds2_DMA_stop(2);
      ds2_DMA_stop(3);
#else
      /* memmove converted: Different addresses [Neb] */
      memcpy(&Memory.ROM [0x200000 + c * 0x10000], &Memory.ROM [c * 0x8000], 0x8000);
      /* memmove converted: Different addresses [Neb] */
      memcpy(&Memory.ROM [0x208000 + c * 0x10000], &Memory.ROM [c * 0x8000], 0x8000);
#endif
   }

   WriteProtectROM();
}

void SA1ROMMap(void)
{
   int32_t c;
   int32_t i;

   /* Banks 00->3f and 80->bf */
   for (c = 0; c < 0x400; c += 16)
   {
      Memory.Map [c + 0] = Memory.Map [c + 0x800] = Memory.RAM;
      Memory.Map [c + 1] = Memory.Map [c + 0x801] = Memory.RAM;
      Memory.BlockIsRAM [c + 0] = Memory.BlockIsRAM [c + 0x800] = true;
      Memory.BlockIsRAM [c + 1] = Memory.BlockIsRAM [c + 0x801] = true;

      Memory.Map [c + 2] = Memory.Map [c + 0x802] = (uint8_t*) MAP_PPU;
      Memory.Map [c + 3] = Memory.Map [c + 0x803] = (uint8_t*) &Memory.FillRAM [0x3000] - 0x3000;
      Memory.Map [c + 4] = Memory.Map [c + 0x804] = (uint8_t*) MAP_CPU;
      Memory.Map [c + 5] = Memory.Map [c + 0x805] = (uint8_t*) MAP_CPU;
      Memory.Map [c + 6] = Memory.Map [c + 0x806] = (uint8_t*) MAP_BWRAM;
      Memory.Map [c + 7] = Memory.Map [c + 0x807] = (uint8_t*) MAP_BWRAM;
      for (i = c + 8; i < c + 16; i++)
      {
         Memory.Map [i] = Memory.Map [i + 0x800] = &Memory.ROM [(c << 11) % Memory.CalculatedSize] - 0x8000;
         Memory.BlockIsROM [i] = Memory.BlockIsROM [i + 0x800] = true;
      }
   }

   /* Banks 40->7f */
   for (c = 0; c < 0x400; c += 16)
   {
      for (i = c; i < c + 16; i++)
         Memory.Map [i + 0x400] = (uint8_t*) &Memory.SRAM [(c << 12) & 0x1ffff];

      for (i = c; i < c + 16; i++)
         Memory.BlockIsROM [i + 0x400] = false;
   }

   /* c0->ff */
   for (c = 0; c < 0x400; c += 16)
   {
      for (i = c;  i < c + 16; i++)
      {
         Memory.Map [i + 0xc00] = &Memory.ROM [(c << 12) % Memory.CalculatedSize];
         Memory.BlockIsROM [i + 0xc00] = true;
      }
   }

   for (c = 0; c < 16; c++)
   {
      Memory.Map [c + 0x7e0] = Memory.RAM;
      Memory.Map [c + 0x7f0] = Memory.RAM + 0x10000;
      Memory.BlockIsRAM [c + 0x7e0] = true;
      Memory.BlockIsRAM [c + 0x7f0] = true;
      Memory.BlockIsROM [c + 0x7e0] = false;
      Memory.BlockIsROM [c + 0x7f0] = false;
   }
   WriteProtectROM();

   /* Now copy the map and correct it for the SA1 CPU. */
   /* memmove converted: Different mallocs [Neb] */
   memcpy((void*) SA1.WriteMap, (void*) Memory.WriteMap, sizeof(Memory.WriteMap));
   /* memmove converted: Different mallocs [Neb] */
   memcpy((void*) SA1.Map, (void*) Memory.Map, sizeof(Memory.Map));

   /* Banks 00->3f and 80->bf */
   for (c = 0; c < 0x400; c += 16)
   {
      SA1.Map [c + 0] = SA1.Map [c + 0x800] = &Memory.FillRAM [0x3000];
      SA1.Map [c + 1] = SA1.Map [c + 0x801] = (uint8_t*) MAP_NONE;
      SA1.WriteMap [c + 0] = SA1.WriteMap [c + 0x800] = &Memory.FillRAM [0x3000];
      SA1.WriteMap [c + 1] = SA1.WriteMap [c + 0x801] = (uint8_t*) MAP_NONE;
   }

   /* Banks 60->6f */
   for (c = 0; c < 0x100; c++)
      SA1.Map [c + 0x600] = SA1.WriteMap [c + 0x600] = (uint8_t*) MAP_BWRAM_BITMAP;

   Memory.BWRAM = Memory.SRAM;
}

void LoROM24MBSMap(void)
{
   int32_t c;
   int32_t i;

   /* Banks 00->3f and 80->bf */
   for (c = 0; c < 0x400; c += 16)
   {
      Memory.Map [c + 0] = Memory.Map [c + 0x800] = Memory.RAM;
      Memory.Map [c + 1] = Memory.Map [c + 0x801] = Memory.RAM;
      Memory.BlockIsRAM [c + 0] = Memory.BlockIsRAM [c + 0x800] = true;
      Memory.BlockIsRAM [c + 1] = Memory.BlockIsRAM [c + 0x801] = true;

      Memory.Map [c + 2] = Memory.Map [c + 0x802] = (uint8_t*) MAP_PPU;
      Memory.Map [c + 3] = Memory.Map [c + 0x803] = (uint8_t*) MAP_PPU;
      Memory.Map [c + 4] = Memory.Map [c + 0x804] = (uint8_t*) MAP_CPU;
      Memory.Map [c + 5] = Memory.Map [c + 0x805] = (uint8_t*) MAP_CPU;
      Memory.Map [c + 6] = Memory.Map [c + 0x806] = (uint8_t*) MAP_NONE;
      Memory.Map [c + 7] = Memory.Map [c + 0x807] = (uint8_t*) MAP_NONE;

      for (i = c + 8; i < c + 16; i++)
      {
         Memory.Map [i] = Memory.Map [i + 0x800] = &Memory.ROM [(c << 11) % Memory.CalculatedSize] - 0x8000;
         Memory.BlockIsROM [i] = Memory.BlockIsROM [i + 0x800] = true;
      }
   }

   /* Banks 00->3f and 80->bf */
   for (c = 0; c < 0x200; c += 16)
   {
      Memory.Map [c + 0x800] = Memory.RAM;
      Memory.Map [c + 0x801] = Memory.RAM;
      Memory.BlockIsRAM [c + 0x800] = true;
      Memory.BlockIsRAM [c + 0x801] = true;

      Memory.Map [c + 0x802] = (uint8_t*) MAP_PPU;
      Memory.Map [c + 0x803] = (uint8_t*) MAP_PPU;
      Memory.Map [c + 0x804] = (uint8_t*) MAP_CPU;
      Memory.Map [c + 0x805] = (uint8_t*) MAP_CPU;
      Memory.Map [c + 0x806] = (uint8_t*) MAP_NONE;
      Memory.Map [c + 0x807] = (uint8_t*) MAP_NONE;

      for (i = c + 8; i < c + 16; i++)
      {
         Memory.Map [i + 0x800] = &Memory.ROM [((c << 11) + 0x200000) % Memory.CalculatedSize] - 0x8000;
         Memory.BlockIsROM [i + 0x800] = true;
      }
   }

   /* Banks 40->7f and c0->ff */
   for (c = 0; c < 0x400; c += 16)
   {
      for (i = c; i < c + 8; i++)
         Memory.Map [i + 0x400] = Memory.Map [i + 0xc00] = &Memory.ROM [((c << 11) + 0x200000) % Memory.CalculatedSize];

      for (i = c + 8; i < c + 16; i++)
         Memory.Map [i + 0x400] = Memory.Map [i + 0xc00] = &Memory.ROM [((c << 11) + 0x200000) % Memory.CalculatedSize] - 0x8000;

      for (i = c; i < c + 16; i++)
         Memory.BlockIsROM [i + 0x400] = Memory.BlockIsROM [i + 0xc00] = true;
   }

   MapExtraRAM();
   WriteProtectROM();
}

void SufamiTurboLoROMMap(void)
{
   int32_t c;
   int32_t i;

   /* Banks 00->3f and 80->bf */
   for (c = 0; c < 0x400; c += 16)
   {
      Memory.Map [c + 0] = Memory.Map [c + 0x800] = Memory.RAM;
      Memory.Map [c + 1] = Memory.Map [c + 0x801] = Memory.RAM;
      Memory.BlockIsRAM [c + 0] = Memory.BlockIsRAM [c + 0x800] = true;
      Memory.BlockIsRAM [c + 1] = Memory.BlockIsRAM [c + 0x801] = true;

      Memory.Map [c + 2] = Memory.Map [c + 0x802] = (uint8_t*) MAP_PPU;
      Memory.Map [c + 3] = Memory.Map [c + 0x803] = (uint8_t*) MAP_PPU;
      Memory.Map [c + 4] = Memory.Map [c + 0x804] = (uint8_t*) MAP_CPU;
      Memory.Map [c + 5] = Memory.Map [c + 0x805] = (uint8_t*) MAP_CPU;
      Memory.Map [c + 6] = Memory.Map [c + 0x806] = (uint8_t*) MAP_NONE;
      Memory.Map [c + 7] = Memory.Map [c + 0x807] = (uint8_t*) MAP_NONE;
      for (i = c + 8; i < c + 16; i++)
      {
         Memory.Map [i] = Memory.Map [i + 0x800] = &Memory.ROM [c << 11] - 0x8000;
         Memory.BlockIsROM [i] = Memory.BlockIsROM [i + 0x800] = true;
      }
   }

   /* Banks 40->7f and c0->ff */
   for (c = 0; c < 0x400; c += 16)
   {
      for (i = c; i < c + 8; i++)
         Memory.Map [i + 0x400] = Memory.Map [i + 0xc00] = &Memory.ROM [((c << 11) + 0x200000) % Memory.CalculatedSize];

      for (i = c + 8; i < c + 16; i++)
         Memory.Map [i + 0x400] = Memory.Map [i + 0xc00] = &Memory.ROM [((c << 11) + 0x200000) % Memory.CalculatedSize] - 0x8000;

      for (i = c; i < c + 16; i++)
         Memory.BlockIsROM [i + 0x400] = Memory.BlockIsROM [i + 0xc00] = true;
   }

   if (Settings.DSP1Master)
   {
      for (c = 0; c < 0x100; c++)
      {
         Memory.Map [c + 0xe00] = (uint8_t*) MAP_DSP;
         Memory.BlockIsROM [c + 0xe00] = false;
      }
   }

   /* Banks 7e->7f, RAM */
   for (c = 0; c < 16; c++)
   {
      Memory.Map [c + 0x7e0] = Memory.RAM;
      Memory.Map [c + 0x7f0] = Memory.RAM + 0x10000;
      Memory.BlockIsRAM [c + 0x7e0] = true;
      Memory.BlockIsRAM [c + 0x7f0] = true;
      Memory.BlockIsROM [c + 0x7e0] = false;
      Memory.BlockIsROM [c + 0x7f0] = false;
   }

   /* Banks 60->67, S-RAM */
   for (c = 0; c < 0x80; c++)
   {
      Memory.Map [c + 0x600] = MAP_LOROM_SRAM_OR_NONE;
      Memory.BlockIsRAM [c + 0x600] = true;
      Memory.BlockIsROM [c + 0x600] = false;
   }

   WriteProtectROM();
}

void SRAM512KLoROMMap(void)
{
   int32_t c;
   int32_t i;

   /* Banks 00->3f and 80->bf */
   for (c = 0; c < 0x400; c += 16)
   {
      Memory.Map [c + 0] = Memory.Map [c + 0x800] = Memory.RAM;
      Memory.Map [c + 1] = Memory.Map [c + 0x801] = Memory.RAM;
      Memory.BlockIsRAM [c + 0] = Memory.BlockIsRAM [c + 0x800] = true;
      Memory.BlockIsRAM [c + 1] = Memory.BlockIsRAM [c + 0x801] = true;

      Memory.Map [c + 2] = Memory.Map [c + 0x802] = (uint8_t*) MAP_PPU;
      Memory.Map [c + 3] = Memory.Map [c + 0x803] = (uint8_t*) MAP_PPU;
      Memory.Map [c + 4] = Memory.Map [c + 0x804] = (uint8_t*) MAP_CPU;
      Memory.Map [c + 5] = Memory.Map [c + 0x805] = (uint8_t*) MAP_CPU;
      Memory.Map [c + 6] = Memory.Map [c + 0x806] = (uint8_t*) MAP_NONE;
      Memory.Map [c + 7] = Memory.Map [c + 0x807] = (uint8_t*) MAP_NONE;
      for (i = c + 8; i < c + 16; i++)
      {
         Memory.Map [i] = Memory.Map [i + 0x800] = &Memory.ROM [(c << 11) % Memory.CalculatedSize] - 0x8000;
         Memory.BlockIsROM [i] = Memory.BlockIsROM [i + 0x800] = true;
      }
   }

   /* Banks 40->7f and c0->ff */
   for (c = 0; c < 0x400; c += 16)
   {
      for (i = c; i < c + 8; i++)
         Memory.Map [i + 0x400] = Memory.Map [i + 0xc00] = &Memory.ROM [((c << 11) + 0x200000) % Memory.CalculatedSize];

      for (i = c + 8; i < c + 16; i++)
         Memory.Map [i + 0x400] = Memory.Map [i + 0xc00] = &Memory.ROM [((c << 11) + 0x200000) % Memory.CalculatedSize] - 0x8000;

      for (i = c; i < c + 16; i++)
         Memory.BlockIsROM [i + 0x400] = Memory.BlockIsROM [i + 0xc00] = true;
   }

   MapExtraRAM();
   WriteProtectROM();
}

void SRAM1024KLoROMMap(void)
{
   int32_t c;
   int32_t i;

   /* Banks 00->3f and 80->bf */
   for (c = 0; c < 0x400; c += 16)
   {
      Memory.Map [c + 0] = Memory.Map [c + 0x800] = Memory.RAM;
      Memory.Map [c + 1] = Memory.Map [c + 0x801] = Memory.RAM;
      Memory.BlockIsRAM [c + 0] = Memory.BlockIsRAM [c + 0x800] = Memory.BlockIsRAM [c + 0x400] = Memory.BlockIsRAM [c + 0xc00] = true;
      Memory.BlockIsRAM [c + 1] = Memory.BlockIsRAM [c + 0x801] = Memory.BlockIsRAM [c + 0x401] = Memory.BlockIsRAM [c + 0xc01] = true;

      Memory.Map [c + 2] = Memory.Map [c + 0x802] = Memory.Map [c + 0x402] = Memory.Map [c + 0xc02] = (uint8_t*) MAP_PPU;
      Memory.Map [c + 3] = Memory.Map [c + 0x803] = Memory.Map [c + 0x403] = Memory.Map [c + 0xc03] = (uint8_t*) MAP_PPU;
      Memory.Map [c + 4] = Memory.Map [c + 0x804] = Memory.Map [c + 0x404] = Memory.Map [c + 0xc04] = (uint8_t*) MAP_CPU;
      Memory.Map [c + 5] = Memory.Map [c + 0x805] = Memory.Map [c + 0x405] = Memory.Map [c + 0xc05] = (uint8_t*) MAP_CPU;
      Memory.Map [c + 6] = Memory.Map [c + 0x806] = Memory.Map [c + 0x406] = Memory.Map [c + 0xc06] = (uint8_t*) MAP_NONE;
      Memory.Map [c + 7] = Memory.Map [c + 0x807] = Memory.Map [c + 0x407] = Memory.Map [c + 0xc07] = (uint8_t*) MAP_NONE;
      for (i = c + 8; i < c + 16; i++)
      {
         Memory.Map [i] = Memory.Map [i + 0x800] = Memory.Map [i + 0x400] = Memory.Map [i + 0xc00] = &Memory.ROM [(c << 11) % Memory.CalculatedSize] - 0x8000;
         Memory.BlockIsROM [i] = Memory.BlockIsROM [i + 0x800] = Memory.BlockIsROM [i + 0x400] = Memory.BlockIsROM [i + 0xc00] = true;
      }
   }

   MapExtraRAM();
   WriteProtectROM();
}

void CapcomProtectLoROMMap(void)
{
   int32_t c;
   int32_t i;

   /* Banks 00->3f and 80->bf */
   for (c = 0; c < 0x400; c += 16)
   {
      Memory.Map [c + 0] = Memory.Map [c + 0x800] = Memory.Map [c + 0x400] = Memory.Map [c + 0xc00] = Memory.RAM;
      Memory.Map [c + 1] = Memory.Map [c + 0x801] = Memory.Map [c + 0x401] = Memory.Map [c + 0xc01] = Memory.RAM;
      Memory.BlockIsRAM [c + 0] = Memory.BlockIsRAM [c + 0x800] = Memory.BlockIsRAM [c + 0x400] = Memory.BlockIsRAM [c + 0xc00] = true;
      Memory.BlockIsRAM [c + 1] = Memory.BlockIsRAM [c + 0x801] = Memory.BlockIsRAM [c + 0x401] = Memory.BlockIsRAM [c + 0xc01] = true;

      Memory.Map [c + 2] = Memory.Map [c + 0x802] = Memory.Map [c + 0x402] = Memory.Map [c + 0xc02] = (uint8_t*) MAP_PPU;
      Memory.Map [c + 3] = Memory.Map [c + 0x803] = Memory.Map [c + 0x403] = Memory.Map [c + 0xc03] = (uint8_t*) MAP_PPU;
      Memory.Map [c + 4] = Memory.Map [c + 0x804] = Memory.Map [c + 0x404] = Memory.Map [c + 0xc04] = (uint8_t*) MAP_CPU;
      Memory.Map [c + 5] = Memory.Map [c + 0x805] = Memory.Map [c + 0x405] = Memory.Map [c + 0xc05] = (uint8_t*) MAP_CPU;
      Memory.Map [c + 6] = Memory.Map [c + 0x806] = Memory.Map [c + 0x406] = Memory.Map [c + 0xc06] = (uint8_t*) MAP_NONE;
      Memory.Map [c + 7] = Memory.Map [c + 0x807] = Memory.Map [c + 0x407] = Memory.Map [c + 0xc07] = (uint8_t*) MAP_NONE;
      for (i = c + 8; i < c + 16; i++)
      {
         Memory.Map [i] = Memory.Map [i + 0x800] = Memory.Map [i + 0x400] = Memory.Map [i + 0xc00] = &Memory.ROM [(c << 11) % Memory.CalculatedSize] - 0x8000;
         Memory.BlockIsROM [i] = Memory.BlockIsROM [i + 0x800] = Memory.BlockIsROM [i + 0x400] = Memory.BlockIsROM [i + 0xc00] = true;
      }
   }

   MapRAM();
   WriteProtectROM();
}

void JumboLoROMMap(bool Interleaved)
{
   int32_t c;
   int32_t i;
   int32_t sum = 0, k, l;

   uint32_t OFFSET0 = 0x400000;
   uint32_t OFFSET2 = 0x000000;

   if (Interleaved)
   {
      OFFSET0 = 0x000000;
      OFFSET2 = Memory.CalculatedSize - 0x400000; /* changed to work with interleaved DKJM2. */
   }
   /* Banks 00->3f and 80->bf */
   for (c = 0; c < 0x400; c += 16)
   {
      Memory.Map [c + 0] = Memory.Map [c + 0x800] = Memory.RAM;
      Memory.Map [c + 1] = Memory.Map [c + 0x801] = Memory.RAM;
      Memory.BlockIsRAM [c + 0] = Memory.BlockIsRAM [c + 0x800] = true;
      Memory.BlockIsRAM [c + 1] = Memory.BlockIsRAM [c + 0x801] = true;

      Memory.Map [c + 2] = Memory.Map [c + 0x802] = (uint8_t*) MAP_PPU;
      Memory.Map [c + 3] = Memory.Map [c + 0x803] = (uint8_t*) MAP_PPU;
      Memory.Map [c + 4] = Memory.Map [c + 0x804] = (uint8_t*) MAP_CPU;
      Memory.Map [c + 5] = Memory.Map [c + 0x805] = (uint8_t*) MAP_CPU;
      if (Settings.DSP1Master)
      {
         Memory.Map [c + 6] = Memory.Map [c + 0x806] = (uint8_t*) MAP_DSP;
         Memory.Map [c + 7] = Memory.Map [c + 0x807] = (uint8_t*) MAP_DSP;
      }
      else if (Settings.C4)
      {
         Memory.Map [c + 6] = Memory.Map [c + 0x806] = (uint8_t*) MAP_C4;
         Memory.Map [c + 7] = Memory.Map [c + 0x807] = (uint8_t*) MAP_C4;
      }
      else
      {
         Memory.Map [c + 6] = Memory.Map [c + 0x806] = (uint8_t*) bytes0x2000 - 0x6000;
         Memory.Map [c + 7] = Memory.Map [c + 0x807] = (uint8_t*) bytes0x2000 - 0x6000;
      }

      for (i = c + 8; i < c + 16; i++)
      {
         Memory.Map [i] = &Memory.ROM [((c << 11) % (Memory.CalculatedSize - 0x400000)) + OFFSET0] - 0x8000;
         Memory.Map [i + 0x800] = &Memory.ROM [((c << 11) % (0x400000)) + OFFSET2] - 0x8000;
         Memory.BlockIsROM [i + 0x800] = Memory.BlockIsROM [i] = true;
      }
   }

   if (Settings.DSP1Master)
   {
      /* Banks 30->3f and b0->bf */
      for (c = 0x300; c < 0x400; c += 16)
      {
         for (i = c + 8; i < c + 16; i++)
         {
            Memory.Map [i + 0x800] = (uint8_t*) MAP_DSP;
            Memory.BlockIsROM [i] = Memory.BlockIsROM [i + 0x800] = false;
         }
      }
   }

   /* Banks 40->7f and c0->ff */
   for (c = 0x400; c < 0x800; c += 16)
   {
      /* updated mappings to correct A15 mirroring */
      for (i = c; i < c + 8; i++)
      {
         Memory.Map [i] = &Memory.ROM [((c << 11) % (Memory.CalculatedSize - 0x400000)) + OFFSET0];
         Memory.Map [i + 0x800] = &Memory.ROM [((c << 11) % 0x400000) + OFFSET2];
      }

      for (i = c + 8; i < c + 16; i++)
      {
         Memory.Map [i] = &Memory.ROM [((c << 11) % (Memory.CalculatedSize - 0x400000)) + OFFSET0] - 0x8000;
         Memory.Map [i + 0x800] = &Memory.ROM [((c << 11) % 0x400000) + OFFSET2 ] - 0x8000;
      }

      for (i = c; i < c + 16; i++)
         Memory.BlockIsROM [i] = Memory.BlockIsROM [i + 0x800] = true;
   }

   /* ROM type has to be 64 Mbit header! */
   for (k = 0; k < 256; k++)
   {
      uint8_t* bank = 0x8000 + Memory.Map[8 + (k << 4)]; /* use upper half of the banks, and adjust for LoROM. */
      for (l = 0; l < 0x8000; l++)
         sum += bank[l];
   }
   Memory.CalculatedChecksum = sum & 0xFFFF;
   MapRAM();
   WriteProtectROM();
}

void SPC7110HiROMMap(void)
{
   int32_t c;
   int32_t i;
   int32_t sum = 0;

   /* Banks 00->3f and 80->bf */
   for (c = 0; c < 0x400; c += 16)
   {
      Memory.Map [c + 0] = Memory.Map [c + 0x800] = Memory.RAM;
      Memory.BlockIsRAM [c + 0] = Memory.BlockIsRAM [c + 0x800] = true;
      Memory.Map [c + 1] = Memory.Map [c + 0x801] = Memory.RAM;
      Memory.BlockIsRAM [c + 1] = Memory.BlockIsRAM [c + 0x801] = true;

      Memory.Map [c + 2] = Memory.Map [c + 0x802] = (uint8_t*) MAP_PPU;
      Memory.Map [c + 3] = Memory.Map [c + 0x803] = (uint8_t*) MAP_PPU;
      Memory.Map [c + 4] = Memory.Map [c + 0x804] = (uint8_t*) MAP_CPU;
      Memory.Map [c + 5] = Memory.Map [c + 0x805] = (uint8_t*) MAP_CPU;

      Memory.Map [c + 6] = MAP_HIROM_SRAM_OR_NONE;
      Memory.Map [c + 7] = MAP_HIROM_SRAM_OR_NONE;
      Memory.Map [c + 0x806] = Memory.Map [c + 0x807] = (uint8_t*) MAP_NONE;

      for (i = c + 8; i < c + 16; i++)
      {
         Memory.Map [i] = Memory.Map [i + 0x800] = &Memory.ROM [(c << 12) % Memory.CalculatedSize];
         Memory.BlockIsROM [i] = Memory.BlockIsROM [i + 0x800] = true;
      }
   }

   /* Banks 30->3f and b0->bf, address ranges 6000->7fff is S-RAM. */
   for (c = 0; c < 16; c++)
   {
      Memory.Map [0x306 + (c << 4)] = MAP_HIROM_SRAM_OR_NONE;
      Memory.Map [0x307 + (c << 4)] = MAP_HIROM_SRAM_OR_NONE;
      Memory.Map [0xb06 + (c << 4)] = (uint8_t*) MAP_NONE;
      Memory.Map [0xb07 + (c << 4)] = (uint8_t*) MAP_NONE;
      Memory.BlockIsRAM [0x306 + (c << 4)] = true;
      Memory.BlockIsRAM [0x307 + (c << 4)] = true;
   }

   /* Banks 40->7f and c0->ff */
   for (c = 0; c < 0x400; c += 16)
   {
      for (i = c; i < c + 16; i++)
      {
         Memory.Map [i + 0x400] = Memory.Map [i + 0xc00] = &Memory.ROM [(c << 12) % Memory.CalculatedSize];
         Memory.BlockIsROM [i + 0x400] = Memory.BlockIsROM [i + 0xc00] = true;
      }
   }

   for (c = 0; c < 0x10; c++)
   {
      Memory.Map [0x500 + c] = (uint8_t*)MAP_SPC7110_DRAM;
      Memory.BlockIsROM [0x500 + c] = true;
   }

   for (c = 0; c < 0x100; c++)
   {
      Memory.Map [0xD00 + c] = (uint8_t*) MAP_SPC7110_ROM;
      Memory.Map [0xE00 + c] = (uint8_t*) MAP_SPC7110_ROM;
      Memory.Map [0xF00 + c] = (uint8_t*) MAP_SPC7110_ROM;
      Memory.BlockIsROM [0xD00 + c] = Memory.BlockIsROM [0xE00 + c] = Memory.BlockIsROM [0xF00 + c] = true;

   }
   S9xSpc7110Init();

   for (i = 0; i < (int32_t)Memory.CalculatedSize; i++)
      sum += Memory.ROM[i];

   if (Memory.CalculatedSize == 0x300000)
      sum <<= 1;
   Memory.CalculatedChecksum = sum & 0xFFFF;

   MapRAM();
   WriteProtectROM();
}

void SPC7110Sram(uint8_t newstate)
{
   if (newstate & 0x80)
   {
      Memory.Map[6] = MAP_HIROM_SRAM_OR_NONE;
      Memory.Map[7] = MAP_HIROM_SRAM_OR_NONE;
      Memory.Map[0x306] = MAP_HIROM_SRAM_OR_NONE;
      Memory.Map[0x307] = MAP_HIROM_SRAM_OR_NONE;
   }
   else
   {
      Memory.Map[6] = MAP_RONLY_SRAM_OR_NONE;
      Memory.Map[7] = MAP_RONLY_SRAM_OR_NONE;
      Memory.Map[0x306] = MAP_RONLY_SRAM_OR_NONE;
      Memory.Map[0x307] = MAP_RONLY_SRAM_OR_NONE;
   }
}

const char* TVStandard(void)
{
   return Settings.PAL ? "PAL" : "NTSC";
}

const char* Speed(void)
{
   return Memory.ROMSpeed & 0x10 ? "120ns" : "200ns";
}

const char* MapType(void)
{
   return Memory.HiROM ? "HiROM" : "LoROM";
}

const char* StaticRAMSize(void)
{
   static char tmp [20];

   if (Memory.SRAMSize > 16)
      return "Corrupt";
   sprintf(tmp, "%dKB", (Memory.SRAMMask + 1) / 1024);
   return tmp;
}

const char* Size(void)
{
   static char tmp [20];

   if (Memory.ROMSize < 7 || Memory.ROMSize - 7 > 23)
      return "Corrupt";
   sprintf(tmp, "%dMbits", 1 << (Memory.ROMSize - 7));
   return tmp;
}

const char* KartContents(void)
{
   static char tmp [30];
   static const char* CoPro [16] =
   {
      "DSP", "SuperFX", "OBC1", "SA-1", "S-DD1", "S-RTC", "CoPro#6",
      "CoPro#7", "CoPro#8", "CoPro#9", "CoPro#10", "CoPro#11", "CoPro#12",
      "CoPro#13", "CoPro#14", "CoPro-Custom"
   };
   static const char* Contents [3] =
   {
      "ROM", "ROM+RAM", "ROM+RAM+BAT"
   };
   if (Memory.ROMType == 0 && !Settings.BS)
      return "ROM only";

   sprintf(tmp, "%s", Contents [(Memory.ROMType & 0xf) % 3]);

   if (Settings.BS)
      sprintf(tmp, "%s+%s", tmp, "BSX");
   else if (Settings.SPC7110 && Settings.SPC7110RTC)
      sprintf(tmp, "%s+%s", tmp, "SPC7110+RTC");
   else if (Settings.SPC7110)
      sprintf(tmp, "%s+%s", tmp, "SPC7110");
   else if(Settings.C4)
      sprintf(tmp, "%s+%s", tmp, "C4");
   else if (Settings.SETA != 0)
   {
      switch (Settings.SETA)
      {
      case ST_010:
         sprintf(tmp, "%s+%s", tmp, "ST-010");
         break;
      case ST_011:
         sprintf(tmp, "%s+%s", tmp, "ST-011");
         break;
      case ST_018:
         sprintf(tmp, "%s+%s", tmp, "ST-018");
         break;
      }
   }
   else if ((Memory.ROMType & 0xf) >= 3)
   {
      if (Memory.ROMType & 0xf0)
         sprintf(tmp, "%s+%s", tmp, CoPro [(Memory.ROMType & 0xf0) >> 4]);
      else
         sprintf(tmp, "%s+DSP%d", tmp, Settings.DSP == 0 ? 1 : Settings.DSP);
   }

   return tmp;
}

const char* MapMode(void)
{
   static char tmp [4];
   sprintf(tmp, "%02x", Memory.ROMSpeed & ~0x10);
   return tmp;
}

const char* ROMID(void)
{
   return Memory.ROMId;
}

bool match_na(const char* str)
{
   return strcmp(Memory.ROMName, str) == 0;
}

bool match_id(const char* str)
{
   return strncmp(Memory.ROMId, str, strlen(str)) == 0;
}

void ApplyROMFixes(void)
{
   /*
   HACKS NSRT can fix that we hadn't detected before.
   [14:25:13] <@Nach>     case 0x0c572ef0: So called Hook (US)(2648)
   [14:25:13] <@Nach>     case 0x6810aa95: Bazooka Blitzkreig swapped sizes hack -handled
   [14:25:17] <@Nach>     case 0x61E29C06: The Tick region hack
   [14:25:19] <@Nach>     case 0x1EF90F74: Jikkyou Keiba Simulation Stable Star PAL hack
   [14:25:23] <@Nach>     case 0x4ab225b5: So called Krusty's Super Fun House (E)
   [14:25:25] <@Nach>     case 0x77fd806a: Donkey Kong Country 2 (E) v1.1 bad dump -handled
   [14:25:27] <@Nach>     case 0x340f23e5: Donkey Kong Country 3 (U) copier hack - handled
   */

   /* not MAD-1 compliant */
   if (match_na("WANDERERS FROM YS"))
   {
      int32_t c;
      for (c = 0; c < 0xE0; c++)
      {
         Memory.Map[c + 0x700] = MAP_LOROM_SRAM_OR_NONE;
         Memory.BlockIsROM[c + 0x700] = false;
         Memory.BlockIsRAM[c + 0x700] = true;
      }
      WriteProtectROM();
   }

   if (strncmp(Memory.ROMName, "WAR 2410", 8) == 0)
   {
      Memory.Map [0x005] = (uint8_t*) Memory.RAM;
      Memory.BlockIsRAM [0x005] = true;
      Memory.BlockIsROM [0x005] = false;
   }

   /* NMI hacks */
   CPU.NMITriggerPoint = 4;
   if (match_na("CACOMA KNIGHT"))
      CPU.NMITriggerPoint = 25;

   /* Disabling a speed-up:
      Games which spool sound samples between the SNES and sound CPU using
      H-DMA as the sample is playing. */
   if (match_na("EARTHWORM JIM 2") ||
         match_na("PRIMAL RAGE") ||
         match_na("CLAY FIGHTER") ||
         match_na("ClayFighter 2") ||
         strncasecmp(Memory.ROMName, "MADDEN", 6) == 0 ||
         strncmp(Memory.ROMName, "NHL", 3) == 0 ||
         match_na("WeaponLord") ||
         strncmp(Memory.ROMName, "WAR 2410", 8) == 0)
      Settings.Shutdown = false;

   /* APU timing hacks */

#ifndef USE_BLARGG_APU
   /* Stunt Racer FX */
   if (match_id("CQ  ") ||
         /* Illusion of Gaia */
         strncmp(Memory.ROMId, "JG", 2) == 0 ||
         match_na("GAIA GENSOUKI 1 JPN"))
      IAPU.OneCycle = 13;
   else if (strcmp (Memory.ROMName, "UMIHARAKAWASE") == 0)
      IAPU.OneCycle = 20;
   /* RENDERING RANGER R2 */
   else if (match_id("AVCJ") ||
         /* Mark Davis */
         strncmp(Memory.ROMName, "THE FISHING MASTER", 18) == 0 || /* needs >= actual APU timing. (21 is .002 Mhz slower) */
         /* Star Ocean */
         strncmp(Memory.ROMId, "ARF", 3) == 0 ||
         /* Tales of Phantasia */
         strncmp(Memory.ROMId, "ATV", 3) == 0 ||
         /* Act Raiser 1 & 2 */
         strncasecmp(Memory.ROMName, "ActRaiser", 9) == 0 ||
         /* Soulblazer */
         match_na("SOULBLAZER - 1 USA") ||
         match_na("SOULBLADER - 1") ||
         /* Terranigma */
         strncmp(Memory.ROMId, "AQT", 3) == 0 ||
         /* Robotrek */
         strncmp(Memory.ROMId, "E9 ", 3) == 0 ||
         match_na("SLAP STICK 1 JPN") ||
         /* ZENNIHON PURORESU2 */
         strncmp(Memory.ROMId, "APR", 3) == 0 ||
         /* Bomberman 4 */
         strncmp(Memory.ROMId, "A4B", 3) == 0 ||
         /* UFO KAMEN YAKISOBAN */
         strncmp(Memory.ROMId, "Y7 ", 3) == 0 ||
         strncmp(Memory.ROMId, "Y9 ", 3) == 0 ||
         /* Panic Bomber World */
         strncmp(Memory.ROMId, "APB", 3) == 0 ||
         ((strncmp(Memory.ROMName, "Parlor", 6) == 0 ||
         match_na("HEIWA Parlor!Mini8") ||
         strncmp(Memory.ROMName, "SANKYO Fever! \xCC\xA8\xB0\xCA\xDE\xB0!", 21) == 0) &&
         strcmp(Memory.CompanyId, "A0") == 0) ||
         match_na("DARK KINGDOM") ||
         match_na("ZAN3 SFC") ||
         match_na("HIOUDEN") ||
         match_na("\xC3\xDD\xBC\xC9\xB3\xC0") || /* Tenshi no Uta */
         match_na("FORTUNE QUEST") ||
         match_na("FISHING TO BASSING") ||
         strncmp(Memory.ROMName, "TokyoDome '95Battle 7", 21) == 0 ||
         match_na("OHMONO BLACKBASS") ||
         strncmp(Memory.ROMName, "SWORD WORLD SFC", 15) == 0 ||
         match_na("MASTERS") || /* Augusta 2 J */
         match_na("SFC \xB6\xD2\xDD\xD7\xB2\xC0\xDE\xB0") || /* Kamen Rider */
         strncmp(Memory.ROMName, "LETs PACHINKO(", 14) == 0)  /* A set of BS games */
      IAPU.OneCycle = 15;
#endif

   /* Specific game fixes */
   Settings.StarfoxHack = match_na("STAR FOX") || match_na("STAR WING");
   Settings.WinterGold = match_na("FX SKIING NINTENDO 96") || match_na("DIRT RACER") || Settings.StarfoxHack;
   Settings.HBlankStart = (256 * Settings.H_Max) / SNES_HCOUNTER_MAX;

   /* CPU timing hacks */
   Settings.H_Max = (SNES_CYCLES_PER_SCANLINE * Settings.CyclesPercentage) / 100;

   /* A Couple of HDMA related hacks - Lantus */
   if ((match_na("SFX SUPERBUTOUDEN2")) || (match_na("ALIEN vs. PREDATOR")) || (match_na("STONE PROTECTORS")) || (match_na("SUPER BATTLETANK 2")))
      Settings.H_Max = (SNES_CYCLES_PER_SCANLINE * 130) / 100;
   else if (match_na("HOME IMPROVEMENT"))
      Settings.H_Max = (SNES_CYCLES_PER_SCANLINE * 200) / 100;
   else if (match_id("ASRJ") && Settings.CyclesPercentage == 100)
      /* Street Racer */
      Settings.H_Max = (SNES_CYCLES_PER_SCANLINE * 95) / 100;
   /* Power Rangers Fight */
   else if (strncmp(Memory.ROMId, "A3R", 3) == 0 ||
         /* Clock Tower */
         strncmp(Memory.ROMId, "AJE", 3) == 0)
      Settings.H_Max = (SNES_CYCLES_PER_SCANLINE * 103) / 100;
   else if (strncmp(Memory.ROMId, "A3M", 3) == 0 && Settings.CyclesPercentage == 100)
      /* Mortal Kombat 3. Fixes cut off speech sample */
      Settings.H_Max = (SNES_CYCLES_PER_SCANLINE * 110) / 100;
   else if (match_na("\x0bd\x0da\x0b2\x0d4\x0b0\x0bd\x0de") &&
         Settings.CyclesPercentage == 100)
      Settings.H_Max = (SNES_CYCLES_PER_SCANLINE * 101) / 100;
   else if (match_na("WILD TRAX") || match_na("STAR FOX 2") || match_na("YOSSY'S ISLAND") || match_na("YOSHI'S ISLAND"))
      CPU.TriedInterleavedMode2 = true;
   /* Start Trek: Deep Sleep 9 */
   else if (strncmp(Memory.ROMId, "A9D", 3) == 0 && Settings.CyclesPercentage == 100)
      Settings.H_Max = (SNES_CYCLES_PER_SCANLINE * 110) / 100;

   /* SA-1 Speedup settings */
   SA1.WaitAddress = NULL;
   SA1.WaitByteAddress1 = NULL;
   SA1.WaitByteAddress2 = NULL;

   if (Settings.SA1)
   {
      /* Itoi Shigesato no Bass Tsuri No.1 (J) */
      if (match_id("ZBPJ"))
      {
         SA1.WaitAddress = SA1.Map [0x0093f1 >> MEMMAP_SHIFT] + 0x93f1;
         SA1.WaitByteAddress1 = Memory.FillRAM + 0x304a;
      }
      /* Daisenryaku Expert WWII (J) */
      else if (match_id("AEVJ"))
      {
         SA1.WaitAddress = SA1.Map [0x0ed18d >> MEMMAP_SHIFT] + 0xd18d;
         SA1.WaitByteAddress1 = Memory.FillRAM + 0x3000;
      }
      /* Derby Jockey 2 (J) */
      else if (match_id("A2DJ"))
         SA1.WaitAddress = SA1.Map [0x008b62 >> MEMMAP_SHIFT] + 0x8b62;
      /* Dragon Ball Z - Hyper Dimension (J) */
      else if (match_id("AZIJ"))
      {
         SA1.WaitAddress = SA1.Map [0x008083 >> MEMMAP_SHIFT] + 0x8083;
         SA1.WaitByteAddress1 = Memory.FillRAM + 0x3020;
      }
      /* SD Gundam G NEXT (J) */
      else if (match_id("ZX3J"))
      {
         SA1.WaitAddress = SA1.Map [0x0087f2 >> MEMMAP_SHIFT] + 0x87f2;
         SA1.WaitByteAddress1 = Memory.FillRAM + 0x30c4;
      }
      /* Shougi no Hanamichi (J) */
      else if (match_id("AARJ"))
      {
         SA1.WaitAddress = SA1.Map [0xc1f85a >> MEMMAP_SHIFT] + 0xf85a;
         SA1.WaitByteAddress1 = Memory.SRAM + 0x0c64;
         SA1.WaitByteAddress2 = Memory.SRAM + 0x0c66;
      }
      /* Asahi Shinbun Rensai Katou Hifumi Kudan Shougi Shingiryu (J) */
      if (match_id("A23J"))
      {
         SA1.WaitAddress = SA1.Map [0xc25037 >> MEMMAP_SHIFT] + 0x5037;
         SA1.WaitByteAddress1 = Memory.SRAM + 0x0c06;
         SA1.WaitByteAddress2 = Memory.SRAM + 0x0c08;
      }
      /* Taikyoku Igo - Idaten (J) */
      else if (match_id("AIIJ"))
      {
         SA1.WaitAddress = SA1.Map [0xc100be >> MEMMAP_SHIFT] + 0x00be;
         SA1.WaitByteAddress1 = Memory.SRAM + 0x1002;
         SA1.WaitByteAddress2 = Memory.SRAM + 0x1004;
      }
      /* Takemiya Masaki Kudan no Igo Taishou (J) */
      else if (match_id("AITJ"))
         SA1.WaitAddress = SA1.Map [0x0080b7 >> MEMMAP_SHIFT] + 0x80b7;
      /* J. League '96 Dream Stadium (J) */
      else if (match_id("AJ6J"))
         SA1.WaitAddress = SA1.Map [0xc0f74a >> MEMMAP_SHIFT] + 0xf74a;
      /* Jumpin' Derby (J) */
      else if (match_id("AJUJ"))
         SA1.WaitAddress = SA1.Map [0x00d926 >> MEMMAP_SHIFT] + 0xd926;
      /* Kakinoki Shougi (J) */
      else if (match_id("AKAJ"))
         SA1.WaitAddress = SA1.Map [0x00f070 >> MEMMAP_SHIFT] + 0xf070;
      /* Hoshi no Kirby 3 (J), Kirby's Dream Land 3 (U) */
      else if (match_id("AFJJ") || match_id("AFJE"))
      {
         SA1.WaitAddress = SA1.Map [0x0082d4 >> MEMMAP_SHIFT] + 0x82d4;
         SA1.WaitByteAddress1 = Memory.SRAM + 0x72a4;
      }
      /* Hoshi no Kirby - Super Deluxe (J) */
      else if (match_id("AKFJ"))
      {
         SA1.WaitAddress = SA1.Map [0x008c93 >> MEMMAP_SHIFT] + 0x8c93;
         SA1.WaitByteAddress1 = Memory.FillRAM + 0x300a;
         SA1.WaitByteAddress2 = Memory.FillRAM + 0x300e;
      }
      /* Kirby Super Star (U) */
      else if (match_id("AKFE"))
      {
         SA1.WaitAddress = SA1.Map [0x008cb8 >> MEMMAP_SHIFT] + 0x8cb8;
         SA1.WaitByteAddress1 = Memory.FillRAM + 0x300a;
         SA1.WaitByteAddress2 = Memory.FillRAM + 0x300e;
      }
      /* Super Mario RPG (J), (U) */
      else if (match_id("ARWJ") || match_id("ARWE"))
      {
         SA1.WaitAddress = SA1.Map [0xc0816f >> MEMMAP_SHIFT] + 0x816f;
         SA1.WaitByteAddress1 = Memory.FillRAM + 0x3000;
      }
      /* Marvelous (J) */
      else if (match_id("AVRJ"))
      {
         SA1.WaitAddress = SA1.Map [0x0085f2 >> MEMMAP_SHIFT] + 0x85f2;
         SA1.WaitByteAddress1 = Memory.FillRAM + 0x3024;
      }
      /* Harukanaru Augusta 3 - Masters New (J) */
      else if (match_id("AO3J"))
      {
         SA1.WaitAddress = SA1.Map [0x00dddb >> MEMMAP_SHIFT] + 0xdddb;
         SA1.WaitByteAddress1 = Memory.FillRAM + 0x37b4;
      }
      /* Jikkyou Oshaberi Parodius (J) */
      else if (match_id("AJOJ"))
         SA1.WaitAddress = SA1.Map [0x8084e5 >> MEMMAP_SHIFT] + 0x84e5;
      /* Super Bomberman - Panic Bomber W (J) */
      else if (match_id("APBJ"))
         SA1.WaitAddress = SA1.Map [0x00857a >> MEMMAP_SHIFT] + 0x857a;
      /* Pebble Beach no Hatou New - Tournament Edition (J) */
      else if (match_id("AONJ"))
      {
         SA1.WaitAddress = SA1.Map [0x00df33 >> MEMMAP_SHIFT] + 0xdf33;
         SA1.WaitByteAddress1 = Memory.FillRAM + 0x37b4;
      }
      /* PGA European Tour (U) */
      else if (match_id("AEPE"))
      {
         SA1.WaitAddress = SA1.Map [0x003700 >> MEMMAP_SHIFT] + 0x3700;
         SA1.WaitByteAddress1 = Memory.FillRAM + 0x3102;
      }
      /* PGA Tour 96 (U) */
      else if (match_id("A3GE"))
      {
         SA1.WaitAddress = SA1.Map [0x003700 >> MEMMAP_SHIFT] + 0x3700;
         SA1.WaitByteAddress1 = Memory.FillRAM + 0x3102;
      }
      /* Power Rangers Zeo - Battle Racers (U) */
      else if (match_id("A4RE"))
      {
         SA1.WaitAddress = SA1.Map [0x009899 >> MEMMAP_SHIFT] + 0x9899;
         SA1.WaitByteAddress1 = Memory.FillRAM + 0x3000;
      }
      /* SD F-1 Grand Prix (J) */
      else if (match_id("AGFJ"))
         SA1.WaitAddress = SA1.Map [0x0181bc >> MEMMAP_SHIFT] + 0x81bc;
      /* Saikousoku Shikou Shougi Mahjong (J) */
      else if (match_id("ASYJ"))
      {
         SA1.WaitAddress = SA1.Map [0x00f2cc >> MEMMAP_SHIFT] + 0xf2cc;
         SA1.WaitByteAddress1 = Memory.SRAM + 0x7ffe;
         SA1.WaitByteAddress2 = Memory.SRAM + 0x7ffc;
      }
      /* Shougi Saikyou II (J) */
      else if (match_id("AX2J"))
         SA1.WaitAddress = SA1.Map [0x00d675 >> MEMMAP_SHIFT] + 0xd675;
      /* Mini Yonku Shining Scorpion - Let's & Go!! (J) */
      else if (match_id("A4WJ"))
         SA1.WaitAddress = SA1.Map [0xc048be >> MEMMAP_SHIFT] + 0x48be;
      /* Shin Shougi Club (J) */
      else if (match_id("AHJJ"))
      {
         SA1.WaitAddress = SA1.Map [0xc1002a >> MEMMAP_SHIFT] + 0x002a;
         SA1.WaitByteAddress1 = Memory.SRAM + 0x0806;
         SA1.WaitByteAddress2 = Memory.SRAM + 0x0808;
      }
      /* ｼｮｳｷﾞｻｲｷｮｳ */
      else if (match_id("AMSJ"))
         SA1.WaitAddress = SA1.Map [0x00CD6A >> MEMMAP_SHIFT] + 0xCD6A;
      /* ﾊﾌﾞﾒｲｼﾞﾝﾉｵﾓｼﾛｼｮｳｷﾞ */
      else if (match_id("IL"))
         SA1.WaitAddress = SA1.Map [0x008549 >> MEMMAP_SHIFT] + 0x8549;
      /* MASOUKISHIN */
      else if (match_id("ALXJ"))
      {
         SA1.WaitAddress = SA1.Map [0x00EC9C >> MEMMAP_SHIFT] + 0xEC9C;
         SA1.WaitByteAddress1 = Memory.FillRAM + 0x3072;
      }
      /* SUPER SHOGI3 */
      else if (match_id("A3IJ"))
         SA1.WaitAddress = SA1.Map [0x00F669 >> MEMMAP_SHIFT] + 0xF669;
   }

   /* Other */

   /* Additional game fixes by sanmaiwashi ... */
   /* Gundam Knight Story */
   if (match_na("SFX \xC5\xB2\xC4\xB6\xDE\xDD\xC0\xDE\xD1\xD3\xC9\xB6\xDE\xC0\xD8 1"))
   {
      bytes0x2000 [0xb18] = 0x4c;
      bytes0x2000 [0xb19] = 0x4b;
      bytes0x2000 [0xb1a] = 0xea;
   }

   if(Settings.BS && Memory.LoROM &&
      match_na("F-ZERO") &&
      Memory.ROMChecksum == 0xb10d &&
      Memory.ROMComplementChecksum == 0x4ef2)
     Memory.ROM[0x7fd0] = 0xFF; /* fix memory pack position bits */
}

/* 7FC0h or FFC0h */
/* FFC0h - FFCFh: CartName */
/* FFD0h        : Memory pack location */
/* FFD1h - FFD5 : 00:00:00:00:00 (??) */
/* FFD6h        : Month 10h, 20h, 30h... */
/* FFD7h        : Day   This byte / 8  low 3bits is unknown. */
/* FFD8h        : ROMSpeed */
/* FFD9h        : Satellaview ROM Type */
/* FFDAh        : Maker ID */
/* FFDBh        : ROM Version */

static bool is_bsx(uint8_t *p) /* p == "0xFFC0" or "0x7FC0" ROM offset pointer */
{
   uint32_t c;
   int32_t i;
   bool b = false;
   bool bb = false;

   /* Satellaview ROM Type */
   if (p[0x19] & 0x4f)
      return false;

   /* Maker ID */
   c = p[0x1a];
   if ((c != 0x33) && (c != 0xff)) /* 0x33 = Manufacturer: Nintendo */
      return false;

   /* Month, Day */
   c = (p[0x17] << 8) | p[0x16];
   if ((c != 0x0000) && (c != 0xffff))
   {
      if ((c & 0x040f) != 0)
         return false;
      if ((c & 0xff) > 0xc0)
         return false;
   }

   /* ROMSpeed */
   c = p[0x18];
   if ((c & 0xce) || ((c & 0x30) == 0))
      return false;

   /* Memory pack location */
   if(p[0x10] == 0)
      return false;

   for(i = 0; i < 8; i++)
   {
      if(p[0x10] & (1 << i))
      {
         if(bb)
            return false;
         else
            b = true;
      }
      else if(b)
         bb = true;
   }

   if ((p[0x15] & 0x03) != 0)
      return false;
   c = p[0x13];
   if ((c != 0x00) && (c != 0xff))
      return false;
   if (p[0x14] != 0x00)
      return false;
   return bs_name(p);
}

static bool bs_name(uint8_t* p)
{
   int32_t lcount;
   for(lcount = 16; lcount > 0; lcount--)
   {
      /* null strings */
      if(*p == 0)
      {
         if(lcount != 16)
            p++;
         else
            return false;
      }
      /* SJIS single byte char */
      else if((*p >= 0x20 && *p <= 0x7f) || (*p >= 0xa0 && *p <= 0xdf))
         p++;
      /* SJIS multi byte char */
      else if(lcount >= 2)
      {
         if(((*p >= 0x81 && *p <= 0x9f) || (*p >= 0xe0 && *p <= 0xfc)) && ((*(p + 1) >= 0x40 && *(p + 1) <= 0x7e) || (*(p + 1) >= 0x80 && *(p + 1) <= 0xfc)))
         {
            p += 2;
            lcount--;
         }
         else
            return false;
      }
      else
         return false;
   }
   return true;
}

void ParseSNESHeader(uint8_t* RomHeader)
{
   if(Settings.BS)
   {
      uint32_t size_count;

      Memory.SRAMSize = 0x05;
      strncpy(Memory.ROMName, (char *) &RomHeader[0x10], 17);
      memset(&Memory.ROMName[0x11], 0, ROM_NAME_LEN - 1 - 17);
      Memory.ROMSpeed = RomHeader [0x28];
      Memory.ROMType = 0xe5;
      Memory.ROMSize = 1;

      for(size_count = 0x800; size_count < Memory.CalculatedSize; size_count <<= 1, ++Memory.ROMSize);
   }
   else
   {
      Memory.SRAMSize = RomHeader [0x28];
      strncpy(Memory.ROMName, (char*) &RomHeader[0x10], ROM_NAME_LEN - 1);
      Memory.ROMSpeed = RomHeader [0x25];
      Memory.ROMType = RomHeader [0x26];
      Memory.ROMSize = RomHeader [0x27];
   }

   Memory.ROMChecksum = RomHeader [0x2e] + (RomHeader [0x2f] << 8);
   Memory.ROMComplementChecksum = RomHeader [0x2c] + (RomHeader [0x2d] << 8);
   Memory.ROMRegion = RomHeader[0x29];
   /* memmove converted: Different mallocs [Neb] */
   memcpy(Memory.ROMId, &RomHeader [0x2], 4);
   if (RomHeader[0x2A] == 0x33)
      /* memmove converted: Different mallocs [Neb] */
      memcpy(Memory.CompanyId, &RomHeader [0], 2);
   else
      sprintf(Memory.CompanyId, "%02X", RomHeader[0x2A]);
}
