merge mappers from FCEU-mm
[fceu.git] / boards / copyfami_hwi.c
diff --git a/boards/copyfami_hwi.c b/boards/copyfami_hwi.c
new file mode 100644 (file)
index 0000000..fb9dad4
--- /dev/null
@@ -0,0 +1,601 @@
+/* FCE Ultra - NES/Famicom Emulator\r
+ *\r
+ * Copyright notice for this file:\r
+ *  Copyright (C) 2011 CaH4e3\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License\r
+ * along with this program; if not, write to the Free Software\r
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
+ */\r
+\r
+// *** COPY FAMICOM HARDWARE INTERFACE ***\r
+\r
+#ifdef COPYFAMI\r
+\r
+#define MESSAGE_LOG\r
+#define NO_CACHE\r
+#define NO_RAM\r
+\r
+#include "__serial.h"\r
+#include "mapinc.h"\r
+\r
+#define FNV_32_PRIME    ((uint32)0x01000193)\r
+\r
+#define CHR_CACHE_SIZE (1024 * 4)\r
+#define WRAM_CACHE_SIZE        (1024 / 2)\r
+#define PRG_CACHE_SIZE (1024 / 4)\r
+#define CMD_CACHE_SIZE (1024 * 128)\r
+\r
+#define CMD_MAX_SIZE    (5)\r
+#define CMD_MAX_RETEST  (16)\r
+#define CMD_MAX_VERIFY  (16)\r
+\r
+static uint8 *WRAM = NULL;\r
+\r
+uint8 InitVector[] =   {0xDE, 0xAD, 0xBE, 0xEF}; // args none,               return DE AD BE EF\r
+uint8 ResetCmd[] =     {0x00};                                  // args none,                           return none\r
+uint8 StateCmd[] =     {0x01};                   // args none,               return 7 bytes status\r
+uint8 StatusCmd[] =    {0x02};                   // args none,               return 32 bytes status\r
+uint8 LoadPlugCmd[] =  {0x03, 0x00, 0x00};       // args 2b size, Nb data    return none\r
+uint8 RunPlugCmd[] =   {0x04};                   // args none,               return none\r
+uint8 RunGameCmd[] =   {0x05};                   // args none,               return none\r
+uint8 NROMSave[] =     {0x06};                   // args none,               return 16b + 32kb + 8kb\r
+\r
+uint8 PRGWBCmd[] =     {0x08, 0x00, 0x00, 0x00}; // args 2b addr, 1b data    return none\r
+uint8 PRGRBCmd[] =     {0x09, 0x00, 0x00};       // args 2b addr             return 1b data\r
+uint8 CHRWBCmd[] =     {0x0A, 0x00, 0x00, 0x00}; // args 2b addr, 1b data    return none\r
+uint8 CHRRBCmd[] =     {0x0B, 0x00, 0x00};       // args 2b addr,            return 1b data\r
+\r
+uint8 PRGSUMCmd[] =    {0x10, 0x00, 0x00};       // args 1b addr, 1b size    return (256 * N)b\r
+uint8 PRG32KSUMCmd[] = {0x10, 0x80, 0x80};       // args 1b addr, 1b size    return 32kb\r
+uint8 PRG16KSUMCmd[] = {0x10, 0x00, 0x40};       // args 1b addr, 1b size    return 16kb\r
+uint8 PRG8KSUMCmd[] =  {0x10, 0x00, 0x20};       // args 1b addr, 1b size    return 8kb\r
+uint8 PRG4KSUMCmd[] =  {0x10, 0x00, 0x10};       // args 1b addr, 1b size    return 4kb\r
+\r
+uint8 CHRSUMCmd[] =    {0x11, 0x00, 0x00};       // args 1b addr, 1b size    return (256 * N)b\r
+uint8 CHR8KSUMCmd[] =  {0x11, 0x00, 0x20};       // args 1b addr, 1b size    return 8kb\r
+uint8 CHR4KSUMCmd[] =  {0x11, 0x00, 0x10};       // args 1b addr, 1b size    return 4kb\r
+uint8 CHR2KSUMCmd[] =  {0x11, 0x00, 0x08};       // args 1b addr, 1b size    return 2kb\r
+uint8 CHR1KSUMCmd[] =  {0x11, 0x00, 0x04};       // args 1b addr, 1b size    return 1kb\r
+\r
+uint8 PRGGetCmd[] =    {0x12, 0x00, 0x00};       // args 1b addr, 1b size    return (256 * N)b\r
+uint8 PRG32KGetCmd[] = {0x12, 0x80, 0x80};       // args 1b addr, 1b size    return 32kb\r
+uint8 PRG16KGetCmd[] = {0x12, 0x00, 0x40};       // args 1b addr, 1b size    return 16kb\r
+uint8 PRG8KGetCmd[] =  {0x12, 0x00, 0x20};       // args 1b addr, 1b size    return 8kb\r
+uint8 PRG4KGetCmd[] =  {0x12, 0x00, 0x10};       // args 1b addr, 1b size    return 4kb\r
+\r
+uint8 CHRGetCmd[] =    {0x13, 0x00, 0x00};       // args 1b addr, 1b size    return (256 * N)b\r
+uint8 CHR8KGetCmd[] =  {0x13, 0x00, 0x20};       // args 1b addr, 1b size    return 8kb\r
+uint8 CHR4KGetCmd[] =  {0x13, 0x00, 0x10};       // args 1b addr, 1b size    return 4kb\r
+uint8 CHR2KGetCmd[] =  {0x13, 0x00, 0x08};       // args 1b addr, 1b size    return 2kb\r
+uint8 CHR1KGetCmd[] =  {0x13, 0x00, 0x04};       // args 1b addr, 1b size    return 1kb\r
+\r
+uint8 CPUTestCmd[] =   {0x14, 0x00, 0x00};       // args 1b addr, 1b size    return (2b + 1b) * N + 3b\r
+\r
+typedef struct {\r
+  int32 mirror;\r
+  int32 chrsum[8];\r
+  int32 prgsum[4];\r
+} SYNC_STATE;\r
+\r
+static SYNC_STATE    state_cur, state_new, state_def;\r
+\r
+typedef struct {\r
+  uint8 *buf;\r
+  int32 count;\r
+} DATA_BANKS;\r
+\r
+static DATA_BANKS  chr_data;\r
+static int32       chr_bank[0x10000];\r
+static DATA_BANKS  prg_data;\r
+static int32       prg_bank[0x10000];\r
+\r
+typedef struct {\r
+  SYNC_STATE   states[CMD_CACHE_SIZE];\r
+  int32        seqs[CMD_CACHE_SIZE][CMD_MAX_SIZE];\r
+  int32        count;\r
+} SYNC_CMDS;\r
+\r
+typedef struct {\r
+  int32        seq[CMD_MAX_SIZE];\r
+  int32        size;\r
+  int32        found;\r
+  uint32       hash;\r
+  uint16       hashf;\r
+} SYNC_CMD;\r
+\r
+static SYNC_CMD     cmd;\r
+static SYNC_CMDS    cmds;\r
+\r
+typedef struct {\r
+  int32 index;\r
+  int32 size;\r
+  int32 retest;\r
+  int32 verify; \r
+} CMD_CACHE;\r
+\r
+static CMD_CACHE   cmd_cache[0x10000];\r
+\r
+static SFORMAT StateRegs[]=\r
+{\r
+  {state_cur.chrsum, sizeof(state_cur.chrsum), "CHRREG"},\r
+  {state_cur.prgsum, sizeof(state_cur.prgsum), "ROMREG"},\r
+  {&state_cur.mirror, sizeof(state_cur.mirror), "MIRREG"},\r
+  {0}\r
+};\r
+\r
+#define MI_U 4\r
+\r
+static char *mirror_names[5] = {"Horizontal", "Vertical", "Mirror 0", "Mirror 1", "Unknown mirror"};\r
+static int32 mirror_modes[16] = {\r
+  MI_0, MI_U, MI_U, MI_H, MI_U, MI_V, MI_U, MI_U,\r
+  MI_U, MI_U, MI_U, MI_U, MI_U, MI_U, MI_U, MI_1 };\r
+\r
+#define CHRDEF(slot)   (chr_bank[state_def.chrsum[slot]])\r
+#define PRGDEF(slot)   (prg_bank[state_def.prgsum[slot]])\r
+#define CHRCUR(slot)   (chr_bank[state_cur.chrsum[slot]])\r
+#define PRGCUR(slot)   (prg_bank[state_cur.prgsum[slot]])\r
+#define CHRNEW(slot)   (chr_bank[state_new.chrsum[slot]])\r
+#define PRGNEW(slot)   (prg_bank[state_new.prgsum[slot]])\r
+\r
+static void GetStatus(SYNC_STATE *state)\r
+{\r
+  uint8  resp0;\r
+  uint16 resp1, i;\r
+  SEND(StatusCmd);\r
+  GET(resp0, 1);\r
+  state->mirror = resp0;\r
+  GET(resp0, 1);\r
+  for(i=0; i<8; i++) {\r
+    GET(resp1, 2);\r
+    state->chrsum[i] = resp1;\r
+  }\r
+  for(i=0; i<4; i++) {\r
+    GET(resp1, 2);\r
+    state->prgsum[i] = resp1;\r
+  }  \r
+}\r
+\r
+static int32 FetchNewCHRBank(int32 slot)\r
+{\r
+  FILE *ofile;\r
+  char name[256];\r
+  int32 bank = chr_data.count++;\r
+  CHR1KGetCmd[1] = slot << 2;\r
+  SENDGET(CHR1KGetCmd, chr_data.buf[bank * 1024], 1024);\r
+  sprintf(name,"%04x.chr",bank);\r
+  ofile=fopen(name,"wb");\r
+  fwrite((void *)&chr_data.buf[bank * 1024], 1, 1024, ofile);\r
+  fclose(ofile);\r
+  return bank;\r
+}\r
+\r
+static int32 FetchNewPRGBank(int32 slot)\r
+{\r
+  FILE *ofile;\r
+  char name[256];\r
+  int32 bank = prg_data.count++;\r
+  PRG8KGetCmd[1] = 0x80 + (slot << 5);\r
+  SENDGET(PRG8KGetCmd, prg_data.buf[bank * 8192], 8192);\r
+  sprintf(name,"%04x.prg",bank);\r
+  ofile=fopen(name,"wb");\r
+  fwrite((void *)&prg_data.buf[bank * 8192], 1, 8192, ofile);\r
+  fclose(ofile);\r
+  return bank;\r
+}\r
+\r
+static int CheckStatus(void)\r
+{\r
+  int32 i, ischanged = 0;\r
+  GetStatus(&state_new);\r
+  if(state_cur.mirror != state_new.mirror) {\r
+    state_cur.mirror = state_new.mirror;\r
+#ifdef MESSAGE_LOG\r
+    FCEU_printf(">> mirror changed to %s (%02X)\n",mirror_names[mirror_modes[state_cur.mirror]], state_cur.mirror);\r
+#endif\r
+    ischanged = 1;\r
+  } else {\r
+    state_new.mirror = -1;\r
+  }\r
+  for(i=0; i<8; i++) {\r
+    if(state_cur.chrsum[i] != state_new.chrsum[i]) {\r
+      state_cur.chrsum[i] = state_new.chrsum[i];\r
+      if(CHRCUR(i) == -1) {\r
+        CHRCUR(i) = FetchNewCHRBank(i);\r
+#ifdef MESSAGE_LOG\r
+        FCEU_printf(">> chr[%d] bank %d loaded\n", i, CHRCUR(i));\r
+#endif\r
+      }\r
+#ifdef MESSAGE_LOG\r
+      else\r
+        FCEU_printf(">> chr[%d] bank %d switched\n", i, CHRCUR(i));\r
+#endif\r
+      ischanged = 1;\r
+    } else {\r
+      state_new.chrsum[i] = -1;\r
+    }\r
+  }\r
+  for(i=0; i<4; i++) {\r
+    if(state_cur.prgsum[i] != state_new.prgsum[i]) {\r
+      state_cur.prgsum[i] = state_new.prgsum[i];                                \r
+      if(PRGCUR(i) == -1) {\r
+        PRGCUR(i) = FetchNewPRGBank(i);\r
+#ifdef MESSAGE_LOG\r
+        FCEU_printf(">> prg[%d] bank %d loaded\n", i, PRGCUR(i));\r
+#endif\r
+      }\r
+#ifdef MESSAGE_LOG\r
+      else\r
+        FCEU_printf(">> prg[%d] bank %d switched\n", i, PRGCUR(i));\r
+#endif\r
+      ischanged = 1;\r
+    } else {\r
+      state_new.prgsum[i] = -1;\r
+    }\r
+  }\r
+  return ischanged;\r
+}\r
+\r
+#ifndef NO_CACHE\r
+static void ApplyStatus()\r
+{\r
+  int32 i;\r
+  if ((cmds.states[cmd.found].mirror != -1) && (cmds.states[cmd.found].mirror != state_cur.mirror)) {\r
+    state_cur.mirror = cmds.states[cmd.found].mirror;\r
+    setmirror(mirror_modes[state_cur.mirror]);\r
+#ifdef MESSAGE_LOG\r
+    FCEU_printf(">> mirror changed to %s (%02X)\n",mirror_names[mirror_modes[state_cur.mirror]], state_cur.mirror);\r
+#endif\r
+  }\r
+  for(i=0; i<8; i++) {\r
+    int32 sum = cmds.states[cmd.found].chrsum[i];\r
+    if (sum != -1) {\r
+      if (sum != state_cur.chrsum[i]) {\r
+        state_cur.chrsum[i] = sum;\r
+        setchr1r(1, i * 1024, CHRCUR(i));\r
+#ifdef MESSAGE_LOG\r
+        FCEU_printf(">> chr[%d] bank %d switched\n", i, chr_bank[sum]);\r
+#endif\r
+      }\r
+      else\r
+#ifdef MESSAGE_LOG\r
+        FCEU_printf(">> chr[%d] bank %d switched the same\n", i, chr_bank[sum]);\r
+    }\r
+#endif\r
+  }\r
+  for(i=0; i<4; i++) {\r
+    int32 sum = cmds.states[cmd.found].prgsum[i];\r
+    if (sum != -1) {\r
+      if (sum != state_cur.prgsum[i]) {\r
+        state_cur.prgsum[i] = sum;\r
+        setprg8r(2, 0x8000 + (i * 8192), PRGCUR(i));\r
+#ifdef MESSAGE_LOG\r
+        FCEU_printf(">> prg[%d] bank %d switched\n", i, prg_bank[sum]);\r
+#endif\r
+      }\r
+      else\r
+#ifdef MESSAGE_LOG\r
+        FCEU_printf(">> prg[%d] bank %d switched the same\n", i, prg_bank[sum]);\r
+    }\r
+#endif\r
+  }\r
+}\r
+\r
+static void LogCmd()\r
+{\r
+  int32 i;\r
+  FCEU_printf(">> new cmd size %d [", cmd_cache[cmd.hashf].size);\r
+  for(i=0; i<cmd_cache[cmd.hashf].size; i++)\r
+    FCEU_printf(" %06X",cmds.seqs[cmd.found][i]);\r
+  FCEU_printf(" ], switched to (");    \r
+  if (cmds.states[cmd.found].mirror != -1)\r
+    FCEU_printf(" mirror=%s",mirror_names[mirror_modes[cmds.states[cmd.found].mirror]]);\r
+  for(i=0; i<8; i++)\r
+    if (cmds.states[cmd.found].chrsum[i] != -1)\r
+      FCEU_printf(" chr%d=%02X", i, chr_bank[cmds.states[cmd.found].chrsum[i]]);\r
+  for(i=0; i<4; i++)\r
+    if (cmds.states[cmd.found].prgsum[i] != -1)\r
+      FCEU_printf(" prg%d=%02X", i, prg_bank[cmds.states[cmd.found].prgsum[i]]);\r
+  FCEU_printf(" )\n");    \r
+}\r
+#endif\r
+static void Sync()\r
+{\r
+  setchr1r(1, 0x0000, CHRCUR(0));\r
+  setchr1r(1, 0x0400, CHRCUR(1));\r
+  setchr1r(1, 0x0800, CHRCUR(2));\r
+  setchr1r(1, 0x0C00, CHRCUR(3));\r
+  setchr1r(1, 0x1000, CHRCUR(4));\r
+  setchr1r(1, 0x1400, CHRCUR(5));\r
+  setchr1r(1, 0x1800, CHRCUR(6));\r
+  setchr1r(1, 0x1C00, CHRCUR(7));\r
+#ifndef NO_RAM\r
+  setprg8r(1, 0x6000, 0);\r
+#endif\r
+  setprg8r(2, 0x8000, PRGCUR(0));\r
+  setprg8r(2, 0xA000, PRGCUR(1));\r
+  setprg8r(2, 0xC000, PRGCUR(2));\r
+  setprg8r(2, 0xE000, PRGCUR(3));\r
+  setmirror(mirror_modes[state_cur.mirror]);\r
+}\r
+#ifndef NO_CACHE\r
+static void UpdateCmd(uint32 val)\r
+{\r
+  int32 index;\r
+  if(cmd.size < CMD_MAX_SIZE) {\r
+    index = cmd.size++;\r
+  } else {\r
+    // åñëè äîñòèãíóò ìàêñèìóì äëÿ êîìàíäû, âûáðîñèòü ïîñëåäíóþþ, äîáàâèòü íîâóþ,\r
+       // ïðîäîëæàòü äî áàíêñâè÷èíãà\r
+       cmd.hash = 0;\r
+       for(index = 0; index < (CMD_MAX_SIZE - 1); index++) {\r
+         cmd.seq[index] = cmd.seq[index + 1];\r
+      cmd.hash *= FNV_32_PRIME;\r
+      cmd.hash ^= cmd.seq[index];\r
+       }\r
+  }\r
+    cmd.seq[index] = val;\r
+    cmd.hash *= FNV_32_PRIME;\r
+    cmd.hash ^= val;\r
+    cmd.hashf = (cmd.hash >> 16) ^ (cmd.hash & 0xffff);\r
+    cmd.found = cmd_cache[cmd.hashf].index;\r
+  }\r
+#endif\r
+\r
+static DECLFW(MCopyFamiWrite)\r
+{\r
+#ifndef NO_CACHE\r
+  int32 i;\r
+#endif\r
+\r
+#ifdef MESSAGE_LOG\r
+  FCEU_printf("> WRITE %04X:%02X\n",A,V);\r
+#endif\r
+\r
+  PRGWBCmd[1] = A & 0xFF;\r
+  PRGWBCmd[2] = A >> 8;\r
+  PRGWBCmd[3] = V & 0xFF;\r
+  SEND(PRGWBCmd);\r
+#ifdef NO_CACHE\r
+  CheckStatus();\r
+  Sync();\r
+#else\r
+  UpdateCmd((A << 8) | V);\r
+  // èùåì êîìàíäó â êåøå\r
+  if(cmd.found == -1) {\r
+    // íå íàéäåíà, ïðîâåðÿåì, èçìåíèëîñü ëè ñîñòîÿíèå áàíêîâ\r
+    // ëèáî íå ïðåäåëüíîé ëè îíà äëèíû äëÿ êîìàíäû\r
+    cmd_cache[cmd.hashf].index = cmd.found = cmds.count++;\r
+    cmd_cache[cmd.hashf].retest = 0;\r
+    cmd_cache[cmd.hashf].verify = 0;\r
+    for(i=0; i<cmd.size; i++)\r
+      cmds.seqs[cmd.found][i] = cmd.seq[i];\r
+    cmd_cache[cmd.hashf].size = cmd.size;\r
+    if(CheckStatus()) {\r
+      cmds.states[cmd.found] = state_new;\r
+      LogCmd();\r
+      cmd.size = 0;\r
+      cmd.hash = 0;\r
+      Sync();\r
+    } else {\r
+      // åñëè äîáàâëåíà ïîëíàÿ êîìàíäà áåç áàíêñâèò÷èíãà\r
+      cmd_cache[cmd.hashf].index = -2;\r
+    }\r
+  } else if(cmd.found == -2) {\r
+    // ÷àñòè÷íîå ñîâïàäåíèå, åñëè ÷èñëî ïðîâåðîê íå ïðåâûñèëî ëèìèò\r
+    if(cmd_cache[cmd.hashf].retest < CMD_MAX_RETEST) {\r
+      // òî ïðîâåðèì ñîñòîÿíèå áàíêîâ\r
+      if(CheckStatus()) {\r
+        // èçìåíèëîñü, çàïèøåì íîâóþ êîìàíäó\r
+        cmd_cache[cmd.hashf].index = cmd.found = cmds.count++;\r
+        cmd_cache[cmd.hashf].retest = 0;\r
+        cmd_cache[cmd.hashf].verify = 0;\r
+        for(i=0; i<cmd.size; i++)\r
+          cmds.seqs[cmd.found][i] = cmd.seq[i];\r
+        cmd_cache[cmd.hashf].size = cmd.size;\r
+        cmds.states[cmd.found] = state_new;\r
+        LogCmd();\r
+        cmd.size = 0;\r
+        cmd.hash = 0;\r
+        Sync();\r
+      } else {\r
+        // íå èçìåíèëîñü, îòìåòèì óâåëè÷èì ñ÷åò÷èê ïðîâåðîê\r
+        cmd_cache[cmd.hashf].retest++;\r
+      }\r
+    }\r
+  } else {\r
+    // íàéäåíà, ïîñëåäíèé ðóáåæ îáîðîíû îò ãîâíà\r
+/*\r
+    if(cmd_cache[cmd.hashf].verify < CMD_MAX_VERIFY) {\r
+      if(CheckStatus()) {\r
+        int32 changed = 0;\r
+      // åñëè åñòü èçìåíåíèÿ, ñðàâíèì íîâîå ñîñòîÿíèå ñ çàïèñàííûì\r
+        if(cmds.states[cmd.found].mirror != state_new.mirror)\r
+          changed = 1;\r
+        for(i=0; i<8; i++)\r
+          if(cmds.states[cmd.found].chrsum[i] != state_new.chrsum[i])\r
+            changed = 1;\r
+        for(i=0; i<4; i++)\r
+          if(cmds.states[cmd.found].prgsum[i] != state_new.prgsum[i])\r
+            changed = 1;\r
+        if(changed) {\r
+          cmd_cache[cmd.hashf].index = -1;\r
+          cmd_cache[cmd.hashf].retest = 0;\r
+          cmd_cache[cmd.hashf].verify = 0;\r
+          Sync();\r
+        }\r
+      } else\r
+        cmd_cache[cmd.hashf].verify++;\r
+    } else */ {\r
+      // ïðèìåíÿåì áåç ìàëåéøåãî çàçðåíèÿ ñîâåñòè\r
+      ApplyStatus();\r
+      cmd.size = 0;\r
+      cmd.hash = 0;\r
+    }\r
+  }\r
+#endif\r
+}\r
+\r
+static DECLFR(MCopyFamiRead)\r
+{\r
+  uint8 result;\r
+  PRGRBCmd[1] = A & 0xFF;\r
+  PRGRBCmd[2] = A >> 8;\r
+  SENDGET(PRGRBCmd, result, 1);\r
+#ifdef MESSAGE_LOG\r
+  FCEU_printf("> READ %04X:%02X\n",A,result);\r
+#endif\r
+  return result;\r
+}\r
+\r
+static void MCopyFamiReset(void)\r
+{\r
+  state_cur = state_def;\r
+  Sync();\r
+}\r
+\r
+static void MCopyFamiPower(void)\r
+{\r
+//  uint32 resp, presp;\r
+\r
+  FCEU_printf("NOW POWERING... ");\r
+  \r
+  Sync();\r
+  \r
+  SetWriteHandler(0x4018, 0x7fff, MCopyFamiWrite);\r
+  SetReadHandler(0x4018, 0x7fff, MCopyFamiRead);\r
+#ifndef NO_RAM\r
+  SetWriteHandler(0x6000, 0x7fff, CartBW);\r
+  SetReadHandler(0x6000, 0x7fff, CartBR);\r
+#endif\r
+\r
+/*\r
+  FCEU_printf("READING MEMORY MAP...\n");\r
+  CPUTestCmd[1] = 0x50;\r
+  CPUTestCmd[2] = 0x30;\r
+  SEND(CPUTestCmd);\r
+  resp = 0;\r
+  presp = 0xffffffff;\r
+  while (presp != 0x00ff0000) {\r
+    GET(resp, 3);\r
+    if(presp != 0xffffffff) {\r
+      switch(presp & 0x00FF0000) {\r
+        case 0x00000000: // BUS\r
+          FCEU_printf("  %04X-%04X OPEN BUS\n",presp & 0x7fff, (resp - 1) & 0x7fff);\r
+          break;\r
+        case 0x00010000: // RAM\r
+          FCEU_printf("  %04X-%04X RAM\n",presp & 0x7fff, (resp - 1) & 0x7fff);\r
+          SetWriteHandler(presp & 0x7fff, (resp - 1) & 0x7fff, CartBW);\r
+          SetReadHandler(presp & 0x7fff, (resp - 1) & 0x7fff, CartBR);\r
+          break;\r
+      }      \r
+    }\r
+    presp = resp;\r
+  }\r
+*/\r
+  SetWriteHandler(0x8000, 0xffff, MCopyFamiWrite);\r
+  SetReadHandler(0x8000, 0xffff, CartBR);\r
+\r
+  FCEU_printf("DONE!\nNOW COLLECTING DATA...\n");\r
+}\r
+\r
+static void MCopyFamiClose(void)\r
+{\r
+  if(chr_data.buf)\r
+    FCEU_gfree(chr_data.buf);\r
+  chr_data.buf=NULL;\r
+  if(prg_data.buf)\r
+    FCEU_gfree(prg_data.buf);\r
+  prg_data.buf=NULL;\r
+  if(WRAM)\r
+    FCEU_gfree(WRAM);\r
+  WRAM = NULL;\r
+  \r
+  SerialClose();\r
+}\r
+\r
+static void StateRestore(int version)\r
+{\r
+  Sync();\r
+}\r
+\r
+void MapperCopyFami_Init(CartInfo *info)\r
+{\r
+  uint32 resp = 0, i, size;\r
+\r
+  memset(chr_bank, -1, sizeof(chr_bank));\r
+  memset(prg_bank, -1, sizeof(chr_bank));\r
+  memset(cmd_cache, -1, sizeof(cmd_cache));\r
+  memset(&cmds, 0, sizeof(cmds));\r
+  memset(&cmd, 0, sizeof(cmd));\r
+  \r
+  info->Reset=MCopyFamiReset;\r
+  info->Power=MCopyFamiPower;\r
+  info->Close=MCopyFamiClose;\r
+  GameStateRestore=StateRestore;\r
+\r
+  size = 1024 * CHR_CACHE_SIZE;        // ðàçìåð ñòðàíèöû 1êá\r
+  chr_data.buf = (uint8*)FCEU_gmalloc(size);\r
+  SetupCartCHRMapping(1, chr_data.buf, size, 1); // ïðîâåðÿòü ïïó ðàì, èíà÷å èãðà ìîæåò ïîðòèòü äàííûå\r
+  AddExState(chr_data.buf, size, 0, "CCHR");\r
+\r
+  size = 8192; // ðàçìåð ñòðàíèöû 8êá\r
+  WRAM = (uint8*)FCEU_gmalloc(size);\r
+  SetupCartPRGMapping(1, WRAM, size, 1);\r
+  AddExState(WRAM, size, 0, "CPRM");\r
+\r
+  size = 8192 * PRG_CACHE_SIZE; // ðàçìåð ñòðàíèöû 8êá\r
+  prg_data.buf = (uint8*)FCEU_gmalloc(size);\r
+  SetupCartPRGMapping(2, prg_data.buf, size, 0);\r
+  AddExState(prg_data.buf, size, 0, "CPPR");\r
+\r
+\r
+  FCEU_printf("WAITING FOR SERIAL PORT... ");\r
+  while(!SerialOpen(19, 921600)) { Sleep(500); }\r
+  FCEU_printf("READY!\n");\r
+\r
+  FCEU_printf("WAITING FOR DEVICE... ");\r
+\r
+  while(resp != *(uint32 *)&InitVector[0]) {\r
+    SEND(ResetCmd);\r
+    SENDGET(InitVector, resp, 4);\r
+    Sleep(500);\r
+  }\r
+\r
+  FCEU_printf("READY!\n");\r
+  FCEU_printf("READING STATUS...\n");\r
+  GetStatus(&state_cur);\r
+  FCEU_printf("MIRRORING IS %s (%02X)\n",mirror_names[mirror_modes[state_cur.mirror]], state_cur.mirror);\r
+  FCEU_printf("READING CHR...\n INITIAL STATE:");\r
+    \r
+  for(i=0; i<8; i++) {\r
+    if(CHRCUR(i) == -1)\r
+      CHRCUR(i) = FetchNewCHRBank(i); \r
+    FCEU_printf(" CHR%d=%02X", i, CHRCUR(i));\r
+  }\r
+  FCEU_printf("\n");\r
+      \r
+  FCEU_printf("READING PRG...\n INITIAL STATE:");\r
+  for(i=0; i<4; i++) {\r
+    if(PRGCUR(i) == -1)\r
+      PRGCUR(i) = FetchNewPRGBank(i);\r
+    FCEU_printf(" PRG%d=%02X", i, PRGCUR(i));\r
+  }\r
+  FCEU_printf("\nDONE!\n");\r
+  \r
+  state_def = state_cur;\r
+\r
+  AddExState(&StateRegs, ~0, 0, 0);\r
+}\r
+\r
+#endif\r