1 /* FCE Ultra - NES/Famicom Emulator
\r
3 * Copyright notice for this file:
\r
4 * Copyright (C) 2011 CaH4e3
\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
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
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
21 // *** COPY FAMICOM HARDWARE INTERFACE ***
\r
29 #include "__serial.h"
\r
32 #define FNV_32_PRIME ((uint32)0x01000193)
\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
39 #define CMD_MAX_SIZE (5)
\r
40 #define CMD_MAX_RETEST (16)
\r
41 #define CMD_MAX_VERIFY (16)
\r
43 static uint8 *WRAM = NULL;
\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
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
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
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
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
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
83 uint8 CPUTestCmd[] = {0x14, 0x00, 0x00}; // args 1b addr, 1b size return (2b + 1b) * N + 3b
\r
91 static SYNC_STATE state_cur, state_new, state_def;
\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
104 SYNC_STATE states[CMD_CACHE_SIZE];
\r
105 int32 seqs[CMD_CACHE_SIZE][CMD_MAX_SIZE];
\r
110 int32 seq[CMD_MAX_SIZE];
\r
117 static SYNC_CMD cmd;
\r
118 static SYNC_CMDS cmds;
\r
127 static CMD_CACHE cmd_cache[0x10000];
\r
129 static SFORMAT StateRegs[]=
\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
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
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
151 static void GetStatus(SYNC_STATE *state)
\r
157 state->mirror = resp0;
\r
159 for(i=0; i<8; i++) {
\r
161 state->chrsum[i] = resp1;
\r
163 for(i=0; i<4; i++) {
\r
165 state->prgsum[i] = resp1;
\r
169 static int32 FetchNewCHRBank(int32 slot)
\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
183 static int32 FetchNewPRGBank(int32 slot)
\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
197 static int CheckStatus(void)
\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
204 FCEU_printf(">> mirror changed to %s (%02X)\n",mirror_names[mirror_modes[state_cur.mirror]], state_cur.mirror);
\r
208 state_new.mirror = -1;
\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
216 FCEU_printf(">> chr[%d] bank %d loaded\n", i, CHRCUR(i));
\r
221 FCEU_printf(">> chr[%d] bank %d switched\n", i, CHRCUR(i));
\r
225 state_new.chrsum[i] = -1;
\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
234 FCEU_printf(">> prg[%d] bank %d loaded\n", i, PRGCUR(i));
\r
239 FCEU_printf(">> prg[%d] bank %d switched\n", i, PRGCUR(i));
\r
243 state_new.prgsum[i] = -1;
\r
250 static void ApplyStatus()
\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
257 FCEU_printf(">> mirror changed to %s (%02X)\n",mirror_names[mirror_modes[state_cur.mirror]], state_cur.mirror);
\r
260 for(i=0; i<8; i++) {
\r
261 int32 sum = cmds.states[cmd.found].chrsum[i];
\r
263 if (sum != state_cur.chrsum[i]) {
\r
264 state_cur.chrsum[i] = sum;
\r
265 setchr1r(1, i * 1024, CHRCUR(i));
\r
267 FCEU_printf(">> chr[%d] bank %d switched\n", i, chr_bank[sum]);
\r
272 FCEU_printf(">> chr[%d] bank %d switched the same\n", i, chr_bank[sum]);
\r
276 for(i=0; i<4; i++) {
\r
277 int32 sum = cmds.states[cmd.found].prgsum[i];
\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
283 FCEU_printf(">> prg[%d] bank %d switched\n", i, prg_bank[sum]);
\r
288 FCEU_printf(">> prg[%d] bank %d switched the same\n", i, prg_bank[sum]);
\r
294 static void LogCmd()
\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
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
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
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
323 setprg8r(1, 0x6000, 0);
\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
332 static void UpdateCmd(uint32 val)
\r
335 if(cmd.size < CMD_MAX_SIZE) {
\r
336 index = cmd.size++;
\r
338 // åñëè äîñòèãíóò ìàêñèìóì äëÿ êîìàíäû, âûáðîñèòü ïîñëåäíóþþ, äîáàâèòü íîâóþ,
\r
339 // ïðîäîëæàòü äî áàíêñâè÷èíãà
\r
341 for(index = 0; index < (CMD_MAX_SIZE - 1); index++) {
\r
342 cmd.seq[index] = cmd.seq[index + 1];
\r
343 cmd.hash *= FNV_32_PRIME;
\r
344 cmd.hash ^= cmd.seq[index];
\r
347 cmd.seq[index] = val;
\r
348 cmd.hash *= FNV_32_PRIME;
\r
350 cmd.hashf = (cmd.hash >> 16) ^ (cmd.hash & 0xffff);
\r
351 cmd.found = cmd_cache[cmd.hashf].index;
\r
355 static DECLFW(MCopyFamiWrite)
\r
362 FCEU_printf("> WRITE %04X:%02X\n",A,V);
\r
365 PRGWBCmd[1] = A & 0xFF;
\r
366 PRGWBCmd[2] = A >> 8;
\r
367 PRGWBCmd[3] = V & 0xFF;
\r
373 UpdateCmd((A << 8) | V);
\r
374 // èùåì êîìàíäó â êåøå
\r
375 if(cmd.found == -1) {
\r
376 // íå íàéäåíà, ïðîâåðÿåì, èçìåíèëîñü ëè ñîñòîÿíèå áàíêîâ
\r
377 // ëèáî íå ïðåäåëüíîé ëè îíà äëèíû äëÿ êîìàíäû
\r
378 cmd_cache[cmd.hashf].index = cmd.found = cmds.count++;
\r
379 cmd_cache[cmd.hashf].retest = 0;
\r
380 cmd_cache[cmd.hashf].verify = 0;
\r
381 for(i=0; i<cmd.size; i++)
\r
382 cmds.seqs[cmd.found][i] = cmd.seq[i];
\r
383 cmd_cache[cmd.hashf].size = cmd.size;
\r
384 if(CheckStatus()) {
\r
385 cmds.states[cmd.found] = state_new;
\r
391 // åñëè äîáàâëåíà ïîëíàÿ êîìàíäà áåç áàíêñâèò÷èíãà
\r
392 cmd_cache[cmd.hashf].index = -2;
\r
394 } else if(cmd.found == -2) {
\r
395 // ÷àñòè÷íîå ñîâïàäåíèå, åñëè ÷èñëî ïðîâåðîê íå ïðåâûñèëî ëèìèò
\r
396 if(cmd_cache[cmd.hashf].retest < CMD_MAX_RETEST) {
\r
397 // òî ïðîâåðèì ñîñòîÿíèå áàíêîâ
\r
398 if(CheckStatus()) {
\r
399 // èçìåíèëîñü, çàïèøåì íîâóþ êîìàíäó
\r
400 cmd_cache[cmd.hashf].index = cmd.found = cmds.count++;
\r
401 cmd_cache[cmd.hashf].retest = 0;
\r
402 cmd_cache[cmd.hashf].verify = 0;
\r
403 for(i=0; i<cmd.size; i++)
\r
404 cmds.seqs[cmd.found][i] = cmd.seq[i];
\r
405 cmd_cache[cmd.hashf].size = cmd.size;
\r
406 cmds.states[cmd.found] = state_new;
\r
412 // íå èçìåíèëîñü, îòìåòèì óâåëè÷èì ñ÷åò÷èê ïðîâåðîê
\r
413 cmd_cache[cmd.hashf].retest++;
\r
417 // íàéäåíà, ïîñëåäíèé ðóáåæ îáîðîíû îò ãîâíà
\r
419 if(cmd_cache[cmd.hashf].verify < CMD_MAX_VERIFY) {
\r
420 if(CheckStatus()) {
\r
422 // åñëè åñòü èçìåíåíèÿ, ñðàâíèì íîâîå ñîñòîÿíèå ñ çàïèñàííûì
\r
423 if(cmds.states[cmd.found].mirror != state_new.mirror)
\r
426 if(cmds.states[cmd.found].chrsum[i] != state_new.chrsum[i])
\r
429 if(cmds.states[cmd.found].prgsum[i] != state_new.prgsum[i])
\r
432 cmd_cache[cmd.hashf].index = -1;
\r
433 cmd_cache[cmd.hashf].retest = 0;
\r
434 cmd_cache[cmd.hashf].verify = 0;
\r
438 cmd_cache[cmd.hashf].verify++;
\r
440 // ïðèìåíÿåì áåç ìàëåéøåãî çàçðåíèÿ ñîâåñòè
\r
449 static DECLFR(MCopyFamiRead)
\r
452 PRGRBCmd[1] = A & 0xFF;
\r
453 PRGRBCmd[2] = A >> 8;
\r
454 SENDGET(PRGRBCmd, result, 1);
\r
456 FCEU_printf("> READ %04X:%02X\n",A,result);
\r
461 static void MCopyFamiReset(void)
\r
463 state_cur = state_def;
\r
467 static void MCopyFamiPower(void)
\r
469 // uint32 resp, presp;
\r
471 FCEU_printf("NOW POWERING... ");
\r
475 SetWriteHandler(0x4018, 0x7fff, MCopyFamiWrite);
\r
476 SetReadHandler(0x4018, 0x7fff, MCopyFamiRead);
\r
478 SetWriteHandler(0x6000, 0x7fff, CartBW);
\r
479 SetReadHandler(0x6000, 0x7fff, CartBR);
\r
483 FCEU_printf("READING MEMORY MAP...\n");
\r
484 CPUTestCmd[1] = 0x50;
\r
485 CPUTestCmd[2] = 0x30;
\r
488 presp = 0xffffffff;
\r
489 while (presp != 0x00ff0000) {
\r
491 if(presp != 0xffffffff) {
\r
492 switch(presp & 0x00FF0000) {
\r
493 case 0x00000000: // BUS
\r
494 FCEU_printf(" %04X-%04X OPEN BUS\n",presp & 0x7fff, (resp - 1) & 0x7fff);
\r
496 case 0x00010000: // RAM
\r
497 FCEU_printf(" %04X-%04X RAM\n",presp & 0x7fff, (resp - 1) & 0x7fff);
\r
498 SetWriteHandler(presp & 0x7fff, (resp - 1) & 0x7fff, CartBW);
\r
499 SetReadHandler(presp & 0x7fff, (resp - 1) & 0x7fff, CartBR);
\r
506 SetWriteHandler(0x8000, 0xffff, MCopyFamiWrite);
\r
507 SetReadHandler(0x8000, 0xffff, CartBR);
\r
509 FCEU_printf("DONE!\nNOW COLLECTING DATA...\n");
\r
512 static void MCopyFamiClose(void)
\r
515 FCEU_gfree(chr_data.buf);
\r
518 FCEU_gfree(prg_data.buf);
\r
527 static void StateRestore(int version)
\r
532 void MapperCopyFami_Init(CartInfo *info)
\r
534 uint32 resp = 0, i, size;
\r
536 memset(chr_bank, -1, sizeof(chr_bank));
\r
537 memset(prg_bank, -1, sizeof(chr_bank));
\r
538 memset(cmd_cache, -1, sizeof(cmd_cache));
\r
539 memset(&cmds, 0, sizeof(cmds));
\r
540 memset(&cmd, 0, sizeof(cmd));
\r
542 info->Reset=MCopyFamiReset;
\r
543 info->Power=MCopyFamiPower;
\r
544 info->Close=MCopyFamiClose;
\r
545 GameStateRestore=StateRestore;
\r
547 size = 1024 * CHR_CACHE_SIZE; // ðàçìåð ñòðàíèöû 1êá
\r
548 chr_data.buf = (uint8*)FCEU_gmalloc(size);
\r
549 SetupCartCHRMapping(1, chr_data.buf, size, 1); // ïðîâåðÿòü ïïó ðàì, èíà÷å èãðà ìîæåò ïîðòèòü äàííûå
\r
550 AddExState(chr_data.buf, size, 0, "CCHR");
\r
552 size = 8192; // ðàçìåð ñòðàíèöû 8êá
\r
553 WRAM = (uint8*)FCEU_gmalloc(size);
\r
554 SetupCartPRGMapping(1, WRAM, size, 1);
\r
555 AddExState(WRAM, size, 0, "CPRM");
\r
557 size = 8192 * PRG_CACHE_SIZE; // ðàçìåð ñòðàíèöû 8êá
\r
558 prg_data.buf = (uint8*)FCEU_gmalloc(size);
\r
559 SetupCartPRGMapping(2, prg_data.buf, size, 0);
\r
560 AddExState(prg_data.buf, size, 0, "CPPR");
\r
563 FCEU_printf("WAITING FOR SERIAL PORT... ");
\r
564 while(!SerialOpen(19, 921600)) { Sleep(500); }
\r
565 FCEU_printf("READY!\n");
\r
567 FCEU_printf("WAITING FOR DEVICE... ");
\r
569 while(resp != *(uint32 *)&InitVector[0]) {
\r
571 SENDGET(InitVector, resp, 4);
\r
575 FCEU_printf("READY!\n");
\r
576 FCEU_printf("READING STATUS...\n");
\r
577 GetStatus(&state_cur);
\r
578 FCEU_printf("MIRRORING IS %s (%02X)\n",mirror_names[mirror_modes[state_cur.mirror]], state_cur.mirror);
\r
579 FCEU_printf("READING CHR...\n INITIAL STATE:");
\r
581 for(i=0; i<8; i++) {
\r
582 if(CHRCUR(i) == -1)
\r
583 CHRCUR(i) = FetchNewCHRBank(i);
\r
584 FCEU_printf(" CHR%d=%02X", i, CHRCUR(i));
\r
588 FCEU_printf("READING PRG...\n INITIAL STATE:");
\r
589 for(i=0; i<4; i++) {
\r
590 if(PRGCUR(i) == -1)
\r
591 PRGCUR(i) = FetchNewPRGBank(i);
\r
592 FCEU_printf(" PRG%d=%02X", i, PRGCUR(i));
\r
594 FCEU_printf("\nDONE!\n");
\r
596 state_def = state_cur;
\r
598 AddExState(&StateRegs, ~0, 0, 0);
\r