| 1 | /* FCE Ultra - NES/Famicom Emulator\r |
| 2 | *\r |
| 3 | * Copyright notice for this file:\r |
| 4 | * Copyright (C) 2011 CaH4e3\r |
| 5 | *\r |
| 6 | * This program is free software; you can redistribute it and/or modify\r |
| 7 | * it under the terms of the GNU General Public License as published by\r |
| 8 | * the Free Software Foundation; either version 2 of the License, or\r |
| 9 | * (at your option) any later version.\r |
| 10 | *\r |
| 11 | * This program is distributed in the hope that it will be useful,\r |
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of\r |
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r |
| 14 | * GNU General Public License for more details.\r |
| 15 | *\r |
| 16 | * You should have received a copy of the GNU General Public License\r |
| 17 | * along with this program; if not, write to the Free Software\r |
| 18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r |
| 19 | */\r |
| 20 | \r |
| 21 | // *** COPY FAMICOM HARDWARE INTERFACE ***\r |
| 22 | \r |
| 23 | #ifdef COPYFAMI\r |
| 24 | \r |
| 25 | #define MESSAGE_LOG\r |
| 26 | #define NO_CACHE\r |
| 27 | #define NO_RAM\r |
| 28 | \r |
| 29 | #include "__serial.h"\r |
| 30 | #include "mapinc.h"\r |
| 31 | \r |
| 32 | #define FNV_32_PRIME ((uint32)0x01000193)\r |
| 33 | \r |
| 34 | #define CHR_CACHE_SIZE (1024 * 4)\r |
| 35 | #define WRAM_CACHE_SIZE (1024 / 2)\r |
| 36 | #define PRG_CACHE_SIZE (1024 / 4)\r |
| 37 | #define CMD_CACHE_SIZE (1024 * 128)\r |
| 38 | \r |
| 39 | #define CMD_MAX_SIZE (5)\r |
| 40 | #define CMD_MAX_RETEST (16)\r |
| 41 | #define CMD_MAX_VERIFY (16)\r |
| 42 | \r |
| 43 | static uint8 *WRAM = NULL;\r |
| 44 | \r |
| 45 | uint8 InitVector[] = {0xDE, 0xAD, 0xBE, 0xEF}; // args none, return DE AD BE EF\r |
| 46 | uint8 ResetCmd[] = {0x00}; // args none, return none\r |
| 47 | uint8 StateCmd[] = {0x01}; // args none, return 7 bytes status\r |
| 48 | uint8 StatusCmd[] = {0x02}; // args none, return 32 bytes status\r |
| 49 | uint8 LoadPlugCmd[] = {0x03, 0x00, 0x00}; // args 2b size, Nb data return none\r |
| 50 | uint8 RunPlugCmd[] = {0x04}; // args none, return none\r |
| 51 | uint8 RunGameCmd[] = {0x05}; // args none, return none\r |
| 52 | uint8 NROMSave[] = {0x06}; // args none, return 16b + 32kb + 8kb\r |
| 53 | \r |
| 54 | uint8 PRGWBCmd[] = {0x08, 0x00, 0x00, 0x00}; // args 2b addr, 1b data return none\r |
| 55 | uint8 PRGRBCmd[] = {0x09, 0x00, 0x00}; // args 2b addr return 1b data\r |
| 56 | uint8 CHRWBCmd[] = {0x0A, 0x00, 0x00, 0x00}; // args 2b addr, 1b data return none\r |
| 57 | uint8 CHRRBCmd[] = {0x0B, 0x00, 0x00}; // args 2b addr, return 1b data\r |
| 58 | \r |
| 59 | uint8 PRGSUMCmd[] = {0x10, 0x00, 0x00}; // args 1b addr, 1b size return (256 * N)b\r |
| 60 | uint8 PRG32KSUMCmd[] = {0x10, 0x80, 0x80}; // args 1b addr, 1b size return 32kb\r |
| 61 | uint8 PRG16KSUMCmd[] = {0x10, 0x00, 0x40}; // args 1b addr, 1b size return 16kb\r |
| 62 | uint8 PRG8KSUMCmd[] = {0x10, 0x00, 0x20}; // args 1b addr, 1b size return 8kb\r |
| 63 | uint8 PRG4KSUMCmd[] = {0x10, 0x00, 0x10}; // args 1b addr, 1b size return 4kb\r |
| 64 | \r |
| 65 | uint8 CHRSUMCmd[] = {0x11, 0x00, 0x00}; // args 1b addr, 1b size return (256 * N)b\r |
| 66 | uint8 CHR8KSUMCmd[] = {0x11, 0x00, 0x20}; // args 1b addr, 1b size return 8kb\r |
| 67 | uint8 CHR4KSUMCmd[] = {0x11, 0x00, 0x10}; // args 1b addr, 1b size return 4kb\r |
| 68 | uint8 CHR2KSUMCmd[] = {0x11, 0x00, 0x08}; // args 1b addr, 1b size return 2kb\r |
| 69 | uint8 CHR1KSUMCmd[] = {0x11, 0x00, 0x04}; // args 1b addr, 1b size return 1kb\r |
| 70 | \r |
| 71 | uint8 PRGGetCmd[] = {0x12, 0x00, 0x00}; // args 1b addr, 1b size return (256 * N)b\r |
| 72 | uint8 PRG32KGetCmd[] = {0x12, 0x80, 0x80}; // args 1b addr, 1b size return 32kb\r |
| 73 | uint8 PRG16KGetCmd[] = {0x12, 0x00, 0x40}; // args 1b addr, 1b size return 16kb\r |
| 74 | uint8 PRG8KGetCmd[] = {0x12, 0x00, 0x20}; // args 1b addr, 1b size return 8kb\r |
| 75 | uint8 PRG4KGetCmd[] = {0x12, 0x00, 0x10}; // args 1b addr, 1b size return 4kb\r |
| 76 | \r |
| 77 | uint8 CHRGetCmd[] = {0x13, 0x00, 0x00}; // args 1b addr, 1b size return (256 * N)b\r |
| 78 | uint8 CHR8KGetCmd[] = {0x13, 0x00, 0x20}; // args 1b addr, 1b size return 8kb\r |
| 79 | uint8 CHR4KGetCmd[] = {0x13, 0x00, 0x10}; // args 1b addr, 1b size return 4kb\r |
| 80 | uint8 CHR2KGetCmd[] = {0x13, 0x00, 0x08}; // args 1b addr, 1b size return 2kb\r |
| 81 | uint8 CHR1KGetCmd[] = {0x13, 0x00, 0x04}; // args 1b addr, 1b size return 1kb\r |
| 82 | \r |
| 83 | uint8 CPUTestCmd[] = {0x14, 0x00, 0x00}; // args 1b addr, 1b size return (2b + 1b) * N + 3b\r |
| 84 | \r |
| 85 | typedef struct {\r |
| 86 | int32 mirror;\r |
| 87 | int32 chrsum[8];\r |
| 88 | int32 prgsum[4];\r |
| 89 | } SYNC_STATE;\r |
| 90 | \r |
| 91 | static SYNC_STATE state_cur, state_new, state_def;\r |
| 92 | \r |
| 93 | typedef struct {\r |
| 94 | uint8 *buf;\r |
| 95 | int32 count;\r |
| 96 | } DATA_BANKS;\r |
| 97 | \r |
| 98 | static DATA_BANKS chr_data;\r |
| 99 | static int32 chr_bank[0x10000];\r |
| 100 | static DATA_BANKS prg_data;\r |
| 101 | static int32 prg_bank[0x10000];\r |
| 102 | \r |
| 103 | typedef struct {\r |
| 104 | SYNC_STATE states[CMD_CACHE_SIZE];\r |
| 105 | int32 seqs[CMD_CACHE_SIZE][CMD_MAX_SIZE];\r |
| 106 | int32 count;\r |
| 107 | } SYNC_CMDS;\r |
| 108 | \r |
| 109 | typedef struct {\r |
| 110 | int32 seq[CMD_MAX_SIZE];\r |
| 111 | int32 size;\r |
| 112 | int32 found;\r |
| 113 | uint32 hash;\r |
| 114 | uint16 hashf;\r |
| 115 | } SYNC_CMD;\r |
| 116 | \r |
| 117 | static SYNC_CMD cmd;\r |
| 118 | static SYNC_CMDS cmds;\r |
| 119 | \r |
| 120 | typedef struct {\r |
| 121 | int32 index;\r |
| 122 | int32 size;\r |
| 123 | int32 retest;\r |
| 124 | int32 verify; \r |
| 125 | } CMD_CACHE;\r |
| 126 | \r |
| 127 | static CMD_CACHE cmd_cache[0x10000];\r |
| 128 | \r |
| 129 | static SFORMAT StateRegs[]=\r |
| 130 | {\r |
| 131 | {state_cur.chrsum, sizeof(state_cur.chrsum), "CHRREG"},\r |
| 132 | {state_cur.prgsum, sizeof(state_cur.prgsum), "ROMREG"},\r |
| 133 | {&state_cur.mirror, sizeof(state_cur.mirror), "MIRREG"},\r |
| 134 | {0}\r |
| 135 | };\r |
| 136 | \r |
| 137 | #define MI_U 4\r |
| 138 | \r |
| 139 | static char *mirror_names[5] = {"Horizontal", "Vertical", "Mirror 0", "Mirror 1", "Unknown mirror"};\r |
| 140 | static int32 mirror_modes[16] = {\r |
| 141 | MI_0, MI_U, MI_U, MI_H, MI_U, MI_V, MI_U, MI_U,\r |
| 142 | MI_U, MI_U, MI_U, MI_U, MI_U, MI_U, MI_U, MI_1 };\r |
| 143 | \r |
| 144 | #define CHRDEF(slot) (chr_bank[state_def.chrsum[slot]])\r |
| 145 | #define PRGDEF(slot) (prg_bank[state_def.prgsum[slot]])\r |
| 146 | #define CHRCUR(slot) (chr_bank[state_cur.chrsum[slot]])\r |
| 147 | #define PRGCUR(slot) (prg_bank[state_cur.prgsum[slot]])\r |
| 148 | #define CHRNEW(slot) (chr_bank[state_new.chrsum[slot]])\r |
| 149 | #define PRGNEW(slot) (prg_bank[state_new.prgsum[slot]])\r |
| 150 | \r |
| 151 | static void GetStatus(SYNC_STATE *state)\r |
| 152 | {\r |
| 153 | uint8 resp0;\r |
| 154 | uint16 resp1, i;\r |
| 155 | SEND(StatusCmd);\r |
| 156 | GET(resp0, 1);\r |
| 157 | state->mirror = resp0;\r |
| 158 | GET(resp0, 1);\r |
| 159 | for(i=0; i<8; i++) {\r |
| 160 | GET(resp1, 2);\r |
| 161 | state->chrsum[i] = resp1;\r |
| 162 | }\r |
| 163 | for(i=0; i<4; i++) {\r |
| 164 | GET(resp1, 2);\r |
| 165 | state->prgsum[i] = resp1;\r |
| 166 | } \r |
| 167 | }\r |
| 168 | \r |
| 169 | static int32 FetchNewCHRBank(int32 slot)\r |
| 170 | {\r |
| 171 | FILE *ofile;\r |
| 172 | char name[256];\r |
| 173 | int32 bank = chr_data.count++;\r |
| 174 | CHR1KGetCmd[1] = slot << 2;\r |
| 175 | SENDGET(CHR1KGetCmd, chr_data.buf[bank * 1024], 1024);\r |
| 176 | sprintf(name,"%04x.chr",bank);\r |
| 177 | ofile=fopen(name,"wb");\r |
| 178 | fwrite((void *)&chr_data.buf[bank * 1024], 1, 1024, ofile);\r |
| 179 | fclose(ofile);\r |
| 180 | return bank;\r |
| 181 | }\r |
| 182 | \r |
| 183 | static int32 FetchNewPRGBank(int32 slot)\r |
| 184 | {\r |
| 185 | FILE *ofile;\r |
| 186 | char name[256];\r |
| 187 | int32 bank = prg_data.count++;\r |
| 188 | PRG8KGetCmd[1] = 0x80 + (slot << 5);\r |
| 189 | SENDGET(PRG8KGetCmd, prg_data.buf[bank * 8192], 8192);\r |
| 190 | sprintf(name,"%04x.prg",bank);\r |
| 191 | ofile=fopen(name,"wb");\r |
| 192 | fwrite((void *)&prg_data.buf[bank * 8192], 1, 8192, ofile);\r |
| 193 | fclose(ofile);\r |
| 194 | return bank;\r |
| 195 | }\r |
| 196 | \r |
| 197 | static int CheckStatus(void)\r |
| 198 | {\r |
| 199 | int32 i, ischanged = 0;\r |
| 200 | GetStatus(&state_new);\r |
| 201 | if(state_cur.mirror != state_new.mirror) {\r |
| 202 | state_cur.mirror = state_new.mirror;\r |
| 203 | #ifdef MESSAGE_LOG\r |
| 204 | FCEU_printf(">> mirror changed to %s (%02X)\n",mirror_names[mirror_modes[state_cur.mirror]], state_cur.mirror);\r |
| 205 | #endif\r |
| 206 | ischanged = 1;\r |
| 207 | } else {\r |
| 208 | state_new.mirror = -1;\r |
| 209 | }\r |
| 210 | for(i=0; i<8; i++) {\r |
| 211 | if(state_cur.chrsum[i] != state_new.chrsum[i]) {\r |
| 212 | state_cur.chrsum[i] = state_new.chrsum[i];\r |
| 213 | if(CHRCUR(i) == -1) {\r |
| 214 | CHRCUR(i) = FetchNewCHRBank(i);\r |
| 215 | #ifdef MESSAGE_LOG\r |
| 216 | FCEU_printf(">> chr[%d] bank %d loaded\n", i, CHRCUR(i));\r |
| 217 | #endif\r |
| 218 | }\r |
| 219 | #ifdef MESSAGE_LOG\r |
| 220 | else\r |
| 221 | FCEU_printf(">> chr[%d] bank %d switched\n", i, CHRCUR(i));\r |
| 222 | #endif\r |
| 223 | ischanged = 1;\r |
| 224 | } else {\r |
| 225 | state_new.chrsum[i] = -1;\r |
| 226 | }\r |
| 227 | }\r |
| 228 | for(i=0; i<4; i++) {\r |
| 229 | if(state_cur.prgsum[i] != state_new.prgsum[i]) {\r |
| 230 | state_cur.prgsum[i] = state_new.prgsum[i]; \r |
| 231 | if(PRGCUR(i) == -1) {\r |
| 232 | PRGCUR(i) = FetchNewPRGBank(i);\r |
| 233 | #ifdef MESSAGE_LOG\r |
| 234 | FCEU_printf(">> prg[%d] bank %d loaded\n", i, PRGCUR(i));\r |
| 235 | #endif\r |
| 236 | }\r |
| 237 | #ifdef MESSAGE_LOG\r |
| 238 | else\r |
| 239 | FCEU_printf(">> prg[%d] bank %d switched\n", i, PRGCUR(i));\r |
| 240 | #endif\r |
| 241 | ischanged = 1;\r |
| 242 | } else {\r |
| 243 | state_new.prgsum[i] = -1;\r |
| 244 | }\r |
| 245 | }\r |
| 246 | return ischanged;\r |
| 247 | }\r |
| 248 | \r |
| 249 | #ifndef NO_CACHE\r |
| 250 | static void ApplyStatus()\r |
| 251 | {\r |
| 252 | int32 i;\r |
| 253 | if ((cmds.states[cmd.found].mirror != -1) && (cmds.states[cmd.found].mirror != state_cur.mirror)) {\r |
| 254 | state_cur.mirror = cmds.states[cmd.found].mirror;\r |
| 255 | setmirror(mirror_modes[state_cur.mirror]);\r |
| 256 | #ifdef MESSAGE_LOG\r |
| 257 | FCEU_printf(">> mirror changed to %s (%02X)\n",mirror_names[mirror_modes[state_cur.mirror]], state_cur.mirror);\r |
| 258 | #endif\r |
| 259 | }\r |
| 260 | for(i=0; i<8; i++) {\r |
| 261 | int32 sum = cmds.states[cmd.found].chrsum[i];\r |
| 262 | if (sum != -1) {\r |
| 263 | if (sum != state_cur.chrsum[i]) {\r |
| 264 | state_cur.chrsum[i] = sum;\r |
| 265 | setchr1r(1, i * 1024, CHRCUR(i));\r |
| 266 | #ifdef MESSAGE_LOG\r |
| 267 | FCEU_printf(">> chr[%d] bank %d switched\n", i, chr_bank[sum]);\r |
| 268 | #endif\r |
| 269 | }\r |
| 270 | else\r |
| 271 | #ifdef MESSAGE_LOG\r |
| 272 | FCEU_printf(">> chr[%d] bank %d switched the same\n", i, chr_bank[sum]);\r |
| 273 | }\r |
| 274 | #endif\r |
| 275 | }\r |
| 276 | for(i=0; i<4; i++) {\r |
| 277 | int32 sum = cmds.states[cmd.found].prgsum[i];\r |
| 278 | if (sum != -1) {\r |
| 279 | if (sum != state_cur.prgsum[i]) {\r |
| 280 | state_cur.prgsum[i] = sum;\r |
| 281 | setprg8r(2, 0x8000 + (i * 8192), PRGCUR(i));\r |
| 282 | #ifdef MESSAGE_LOG\r |
| 283 | FCEU_printf(">> prg[%d] bank %d switched\n", i, prg_bank[sum]);\r |
| 284 | #endif\r |
| 285 | }\r |
| 286 | else\r |
| 287 | #ifdef MESSAGE_LOG\r |
| 288 | FCEU_printf(">> prg[%d] bank %d switched the same\n", i, prg_bank[sum]);\r |
| 289 | }\r |
| 290 | #endif\r |
| 291 | }\r |
| 292 | }\r |
| 293 | \r |
| 294 | static void LogCmd()\r |
| 295 | {\r |
| 296 | int32 i;\r |
| 297 | FCEU_printf(">> new cmd size %d [", cmd_cache[cmd.hashf].size);\r |
| 298 | for(i=0; i<cmd_cache[cmd.hashf].size; i++)\r |
| 299 | FCEU_printf(" %06X",cmds.seqs[cmd.found][i]);\r |
| 300 | FCEU_printf(" ], switched to ("); \r |
| 301 | if (cmds.states[cmd.found].mirror != -1)\r |
| 302 | FCEU_printf(" mirror=%s",mirror_names[mirror_modes[cmds.states[cmd.found].mirror]]);\r |
| 303 | for(i=0; i<8; i++)\r |
| 304 | if (cmds.states[cmd.found].chrsum[i] != -1)\r |
| 305 | FCEU_printf(" chr%d=%02X", i, chr_bank[cmds.states[cmd.found].chrsum[i]]);\r |
| 306 | for(i=0; i<4; i++)\r |
| 307 | if (cmds.states[cmd.found].prgsum[i] != -1)\r |
| 308 | FCEU_printf(" prg%d=%02X", i, prg_bank[cmds.states[cmd.found].prgsum[i]]);\r |
| 309 | FCEU_printf(" )\n"); \r |
| 310 | }\r |
| 311 | #endif\r |
| 312 | static void Sync()\r |
| 313 | {\r |
| 314 | setchr1r(1, 0x0000, CHRCUR(0));\r |
| 315 | setchr1r(1, 0x0400, CHRCUR(1));\r |
| 316 | setchr1r(1, 0x0800, CHRCUR(2));\r |
| 317 | setchr1r(1, 0x0C00, CHRCUR(3));\r |
| 318 | setchr1r(1, 0x1000, CHRCUR(4));\r |
| 319 | setchr1r(1, 0x1400, CHRCUR(5));\r |
| 320 | setchr1r(1, 0x1800, CHRCUR(6));\r |
| 321 | setchr1r(1, 0x1C00, CHRCUR(7));\r |
| 322 | #ifndef NO_RAM\r |
| 323 | setprg8r(1, 0x6000, 0);\r |
| 324 | #endif\r |
| 325 | setprg8r(2, 0x8000, PRGCUR(0));\r |
| 326 | setprg8r(2, 0xA000, PRGCUR(1));\r |
| 327 | setprg8r(2, 0xC000, PRGCUR(2));\r |
| 328 | setprg8r(2, 0xE000, PRGCUR(3));\r |
| 329 | setmirror(mirror_modes[state_cur.mirror]);\r |
| 330 | }\r |
| 331 | #ifndef NO_CACHE\r |
| 332 | static void UpdateCmd(uint32 val)\r |
| 333 | {\r |
| 334 | int32 index;\r |
| 335 | if(cmd.size < CMD_MAX_SIZE) {\r |
| 336 | index = cmd.size++;\r |
| 337 | } else {\r |
| 338 |