1 /* FCE Ultra - NES/Famicom Emulator
2  *
3  * Copyright notice for this file:
4  *  Copyright (C) 2011 CaH4e3
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
19  */
20 
21 #ifdef COPYFAMI
22 
23 /* *** COPY FAMICOM HARDWARE INTERFACE *** */
24 
25 #define MESSAGE_LOG
26 #define NO_CACHE
27 #define NO_RAM
28 
29 #include "__serial.h"
30 #include "mapinc.h"
31 
32 #define FNV_32_PRIME    ((uint32)0x01000193)
33 
34 #define CHR_CACHE_SIZE  (1024 * 4)
35 #define WRAM_CACHE_SIZE (1024 / 2)
36 #define PRG_CACHE_SIZE  (1024 / 4)
37 #define CMD_CACHE_SIZE  (1024 * 128)
38 
39 #define CMD_MAX_SIZE    (5)
40 #define CMD_MAX_RETEST  (16)
41 #define CMD_MAX_VERIFY  (16)
42 
43 static uint8 *WRAM = NULL;
44 
45 uint8 InitVector[] = { 0xDE, 0xAD, 0xBE, 0xEF };/* args none,               return DE AD BE EF		*/
46 uint8 ResetCmd[] = { 0x00 };					/* args none,               return none				*/
47 uint8 StateCmd[] = { 0x01 };					/* args none,               return 7 bytes status	*/
48 uint8 StatusCmd[] = { 0x02 };					/* args none,               return 32 bytes status	*/
49 uint8 LoadPlugCmd[] = { 0x03, 0x00, 0x00 };		/* args 2b size, Nb data    return none				*/
50 uint8 RunPlugCmd[] = { 0x04 };					/* args none,               return none				*/
51 uint8 RunGameCmd[] = { 0x05 };					/* args none,               return none				*/
52 uint8 NROMSave[] = { 0x06 };					/* args none,               return 16b + 32kb + 8kb	*/
53 
54 uint8 PRGWBCmd[] = { 0x08, 0x00, 0x00, 0x00 };	/* args 2b addr, 1b data    return none				*/
55 uint8 PRGRBCmd[] = { 0x09, 0x00, 0x00 };		/* args 2b addr             return 1b data			*/
56 uint8 CHRWBCmd[] = { 0x0A, 0x00, 0x00, 0x00 };	/* args 2b addr, 1b data    return none				*/
57 uint8 CHRRBCmd[] = { 0x0B, 0x00, 0x00 };		/* args 2b addr,            return 1b data			*/
58 
59 uint8 PRGSUMCmd[] = { 0x10, 0x00, 0x00 };		/* args 1b addr, 1b size    return (256 * N)b		*/
60 uint8 PRG32KSUMCmd[] = { 0x10, 0x80, 0x80 };	/* args 1b addr, 1b size    return 32kb				*/
61 uint8 PRG16KSUMCmd[] = { 0x10, 0x00, 0x40 };	/* args 1b addr, 1b size    return 16kb				*/
62 uint8 PRG8KSUMCmd[] = { 0x10, 0x00, 0x20 };		/* args 1b addr, 1b size    return 8kb				*/
63 uint8 PRG4KSUMCmd[] = { 0x10, 0x00, 0x10 };		/* args 1b addr, 1b size    return 4kb				*/
64 
65 uint8 CHRSUMCmd[] = { 0x11, 0x00, 0x00 };		/* args 1b addr, 1b size    return (256 * N)b		*/
66 uint8 CHR8KSUMCmd[] = { 0x11, 0x00, 0x20 };		/* args 1b addr, 1b size    return 8kb				*/
67 uint8 CHR4KSUMCmd[] = { 0x11, 0x00, 0x10 };		/* args 1b addr, 1b size    return 4kb				*/
68 uint8 CHR2KSUMCmd[] = { 0x11, 0x00, 0x08 };		/* args 1b addr, 1b size    return 2kb				*/
69 uint8 CHR1KSUMCmd[] = { 0x11, 0x00, 0x04 };		/* args 1b addr, 1b size    return 1kb				*/
70 
71 uint8 PRGGetCmd[] = { 0x12, 0x00, 0x00 };		/* args 1b addr, 1b size    return (256 * N)b		*/
72 uint8 PRG32KGetCmd[] = { 0x12, 0x80, 0x80 };	/* args 1b addr, 1b size    return 32kb				*/
73 uint8 PRG16KGetCmd[] = { 0x12, 0x00, 0x40 };	/* args 1b addr, 1b size    return 16kb				*/
74 uint8 PRG8KGetCmd[] = { 0x12, 0x00, 0x20 };		/* args 1b addr, 1b size    return 8kb				*/
75 uint8 PRG4KGetCmd[] = { 0x12, 0x00, 0x10 };		/* args 1b addr, 1b size    return 4kb				*/
76 
77 uint8 CHRGetCmd[] = { 0x13, 0x00, 0x00 };		/* args 1b addr, 1b size    return (256 * N)b		*/
78 uint8 CHR8KGetCmd[] = { 0x13, 0x00, 0x20 };		/* args 1b addr, 1b size    return 8kb				*/
79 uint8 CHR4KGetCmd[] = { 0x13, 0x00, 0x10 };		/* args 1b addr, 1b size    return 4kb				*/
80 uint8 CHR2KGetCmd[] = { 0x13, 0x00, 0x08 };		/* args 1b addr, 1b size    return 2kb				*/
81 uint8 CHR1KGetCmd[] = { 0x13, 0x00, 0x04 };		/* args 1b addr, 1b size    return 1kb				*/
82 
83 uint8 CPUTestCmd[] = { 0x14, 0x00, 0x00 };		/* args 1b addr, 1b size    return (2b + 1b) * N + 3b	*/
84 
85 typedef struct {
86 	int32 mirror;
87 	int32 chrsum[8];
88 	int32 prgsum[4];
89 } SYNC_STATE;
90 
91 static SYNC_STATE state_cur, state_new, state_def;
92 
93 typedef struct {
94 	uint8 *buf;
95 	int32 count;
96 } DATA_BANKS;
97 
98 static DATA_BANKS chr_data;
99 static int32 chr_bank[0x10000];
100 static DATA_BANKS prg_data;
101 static int32 prg_bank[0x10000];
102 
103 typedef struct {
104 	SYNC_STATE states[CMD_CACHE_SIZE];
105 	int32 seqs[CMD_CACHE_SIZE][CMD_MAX_SIZE];
106 	int32 count;
107 } SYNC_CMDS;
108 
109 typedef struct {
110 	int32 seq[CMD_MAX_SIZE];
111 	int32 size;
112 	int32 found;
113 	uint32 hash;
114 	uint16 hashf;
115 } SYNC_CMD;
116 
117 static SYNC_CMD cmd;
118 static SYNC_CMDS cmds;
119 
120 typedef struct {
121 	int32 index;
122 	int32 size;
123 	int32 retest;
124 	int32 verify;
125 } CMD_CACHE;
126 
127 static CMD_CACHE cmd_cache[0x10000];
128 
129 static SFORMAT StateRegs[] =
130 {
131 	{ state_cur.chrsum, sizeof(state_cur.chrsum), "CHRREG" },
132 	{ state_cur.prgsum, sizeof(state_cur.prgsum), "ROMREG" },
133 	{ &state_cur.mirror, sizeof(state_cur.mirror), "MIRREG" },
134 	{ 0 }
135 };
136 
137 #define MI_U 4
138 
139 static char *mirror_names[5] = { "Horizontal", "Vertical", "Mirror 0", "Mirror 1", "Unknown mirror" };
140 static int32 mirror_modes[16] = {
141 	MI_0, MI_U, MI_U, MI_H, MI_U, MI_V, MI_U, MI_U,
142 	MI_U, MI_U, MI_U, MI_U, MI_U, MI_U, MI_U, MI_1
143 };
144 
145 #define CHRDEF(slot)   (chr_bank[state_def.chrsum[slot]])
146 #define PRGDEF(slot)   (prg_bank[state_def.prgsum[slot]])
147 #define CHRCUR(slot)   (chr_bank[state_cur.chrsum[slot]])
148 #define PRGCUR(slot)   (prg_bank[state_cur.prgsum[slot]])
149 #define CHRNEW(slot)   (chr_bank[state_new.chrsum[slot]])
150 #define PRGNEW(slot)   (prg_bank[state_new.prgsum[slot]])
151 
GetStatus(SYNC_STATE * state)152 static void GetStatus(SYNC_STATE *state) {
153 	uint8 resp0;
154 	uint16 resp1, i;
155 	SEND(StatusCmd);
156 	GET(resp0, 1);
157 	state->mirror = resp0;
158 	GET(resp0, 1);
159 	for (i = 0; i < 8; i++) {
160 		GET(resp1, 2);
161 		state->chrsum[i] = resp1;
162 	}
163 	for (i = 0; i < 4; i++) {
164 		GET(resp1, 2);
165 		state->prgsum[i] = resp1;
166 	}
167 }
168 
FetchNewCHRBank(int32 slot)169 static int32 FetchNewCHRBank(int32 slot) {
170 	FILE *ofile;
171 	char name[256];
172 	int32 bank = chr_data.count++;
173 	CHR1KGetCmd[1] = slot << 2;
174 	SENDGET(CHR1KGetCmd, chr_data.buf[bank * 1024], 1024);
175 	sprintf(name, "%04x.chr", bank);
176 	ofile = fopen(name, "wb");
177 	fwrite((void*)&chr_data.buf[bank * 1024], 1, 1024, ofile);
178 	fclose(ofile);
179 	return bank;
180 }
181 
FetchNewPRGBank(int32 slot)182 static int32 FetchNewPRGBank(int32 slot) {
183 	FILE *ofile;
184 	char name[256];
185 	int32 bank = prg_data.count++;
186 	PRG8KGetCmd[1] = 0x80 + (slot << 5);
187 	SENDGET(PRG8KGetCmd, prg_data.buf[bank * 8192], 8192);
188 	sprintf(name, "%04x.prg", bank);
189 	ofile = fopen(name, "wb");
190 	fwrite((void*)&prg_data.buf[bank * 8192], 1, 8192, ofile);
191 	fclose(ofile);
192 	return bank;
193 }
194 
CheckStatus(void)195 static int CheckStatus(void) {
196 	int32 i, ischanged = 0;
197 	GetStatus(&state_new);
198 	if (state_cur.mirror != state_new.mirror) {
199 		state_cur.mirror = state_new.mirror;
200 #ifdef MESSAGE_LOG
201 		FCEU_printf(">> mirror changed to %s (%02X)\n", mirror_names[mirror_modes[state_cur.mirror]], state_cur.mirror);
202 #endif
203 		ischanged = 1;
204 	} else {
205 		state_new.mirror = -1;
206 	}
207 	for (i = 0; i < 8; i++) {
208 		if (state_cur.chrsum[i] != state_new.chrsum[i]) {
209 			state_cur.chrsum[i] = state_new.chrsum[i];
210 			if (CHRCUR(i) == -1) {
211 				CHRCUR(i) = FetchNewCHRBank(i);
212 #ifdef MESSAGE_LOG
213 				FCEU_printf(">> chr[%d] bank %d loaded\n", i, CHRCUR(i));
214 #endif
215 			}
216 #ifdef MESSAGE_LOG
217 			else
218 				FCEU_printf(">> chr[%d] bank %d switched\n", i, CHRCUR(i));
219 #endif
220 			ischanged = 1;
221 		} else {
222 			state_new.chrsum[i] = -1;
223 		}
224 	}
225 	for (i = 0; i < 4; i++) {
226 		if (state_cur.prgsum[i] != state_new.prgsum[i]) {
227 			state_cur.prgsum[i] = state_new.prgsum[i];
228 			if (PRGCUR(i) == -1) {
229 				PRGCUR(i) = FetchNewPRGBank(i);
230 #ifdef MESSAGE_LOG
231 				FCEU_printf(">> prg[%d] bank %d loaded\n", i, PRGCUR(i));
232 #endif
233 			}
234 #ifdef MESSAGE_LOG
235 			else
236 				FCEU_printf(">> prg[%d] bank %d switched\n", i, PRGCUR(i));
237 #endif
238 			ischanged = 1;
239 		} else {
240 			state_new.prgsum[i] = -1;
241 		}
242 	}
243 	return ischanged;
244 }
245 
246 #ifndef NO_CACHE
ApplyStatus()247 static void ApplyStatus() {
248 	int32 i;
249 	if ((cmds.states[cmd.found].mirror != -1) && (cmds.states[cmd.found].mirror != state_cur.mirror)) {
250 		state_cur.mirror = cmds.states[cmd.found].mirror;
251 		setmirror(mirror_modes[state_cur.mirror]);
252 #ifdef MESSAGE_LOG
253 		FCEU_printf(">> mirror changed to %s (%02X)\n", mirror_names[mirror_modes[state_cur.mirror]], state_cur.mirror);
254 #endif
255 	}
256 	for (i = 0; i < 8; i++) {
257 		int32 sum = cmds.states[cmd.found].chrsum[i];
258 		if (sum != -1) {
259 			if (sum != state_cur.chrsum[i]) {
260 				state_cur.chrsum[i] = sum;
261 				setchr1r(1, i * 1024, CHRCUR(i));
262 #ifdef MESSAGE_LOG
263 				FCEU_printf(">> chr[%d] bank %d switched\n", i, chr_bank[sum]);
264 #endif
265 			} else
266 #ifdef MESSAGE_LOG
267 				FCEU_printf(">> chr[%d] bank %d switched the same\n", i, chr_bank[sum]);
268 		}
269 #endif
270 				}
271 				for (i = 0; i < 4; i++) {
272 					int32 sum = cmds.states[cmd.found].prgsum[i];
273 					if (sum != -1) {
274 						if (sum != state_cur.prgsum[i]) {
275 							state_cur.prgsum[i] = sum;
276 							setprg8r(2, 0x8000 + (i * 8192), PRGCUR(i));
277 #ifdef MESSAGE_LOG
278 							FCEU_printf(">> prg[%d] bank %d switched\n", i, prg_bank[sum]);
279 #endif
280 						} else
281 #ifdef MESSAGE_LOG
282 							FCEU_printf(">> prg[%d] bank %d switched the same\n", i, prg_bank[sum]);
283 					}
284 #endif
285 							}
286 							}
287 
LogCmd()288 							static void LogCmd() {
289 								int32 i;
290 								FCEU_printf(">> new cmd size %d [", cmd_cache[cmd.hashf].size);
291 								for (i = 0; i < cmd_cache[cmd.hashf].size; i++)
292 									FCEU_printf(" %06X", cmds.seqs[cmd.found][i]);
293 								FCEU_printf(" ], switched to (");
294 								if (cmds.states[cmd.found].mirror != -1)
295 									FCEU_printf(" mirror=%s", mirror_names[mirror_modes[cmds.states[cmd.found].mirror]]);
296 								for (i = 0; i < 8; i++)
297 									if (cmds.states[cmd.found].chrsum[i] != -1)
298 										FCEU_printf(" chr%d=%02X", i, chr_bank[cmds.states[cmd.found].chrsum[i]]);
299 								for (i = 0; i < 4; i++)
300 									if (cmds.states[cmd.found].prgsum[i] != -1)
301 										FCEU_printf(" prg%d=%02X", i, prg_bank[cmds.states[cmd.found].prgsum[i]]);
302 								FCEU_printf(" )\n");
303 							}
304 #endif
Sync()305 static void Sync() {
306 	setchr1r(1, 0x0000, CHRCUR(0));
307 	setchr1r(1, 0x0400, CHRCUR(1));
308 	setchr1r(1, 0x0800, CHRCUR(2));
309 	setchr1r(1, 0x0C00, CHRCUR(3));
310 	setchr1r(1, 0x1000, CHRCUR(4));
311 	setchr1r(1, 0x1400, CHRCUR(5));
312 	setchr1r(1, 0x1800, CHRCUR(6));
313 	setchr1r(1, 0x1C00, CHRCUR(7));
314 #ifndef NO_RAM
315 	setprg8r(1, 0x6000, 0);
316 #endif
317 	setprg8r(2, 0x8000, PRGCUR(0));
318 	setprg8r(2, 0xA000, PRGCUR(1));
319 	setprg8r(2, 0xC000, PRGCUR(2));
320 	setprg8r(2, 0xE000, PRGCUR(3));
321 	setmirror(mirror_modes[state_cur.mirror]);
322 }
323 #ifndef NO_CACHE
UpdateCmd(uint32 val)324 static void UpdateCmd(uint32 val) {
325 	int32 index;
326 	if (cmd.size < CMD_MAX_SIZE) {
327 		index = cmd.size++;
328 	} else {
329 		/* ���� ��������� �������� ��� �������, ��������� ����������, �������� �����,
330 		 * ���������� �� ������������
331 		 */
332 		cmd.hash = 0;
333 		for (index = 0; index < (CMD_MAX_SIZE - 1); index++) {
334 			cmd.seq[index] = cmd.seq[index + 1];
335 			cmd.hash *= FNV_32_PRIME;
336 			cmd.hash ^= cmd.seq[index];
337 		}
338 	}
339 	cmd.seq[index] = val;
340 	cmd.hash *= FNV_32_PRIME;
341 	cmd.hash ^= val;
342 	cmd.hashf = (cmd.hash >> 16) ^ (cmd.hash & 0xffff);
343 	cmd.found = cmd_cache[cmd.hashf].index;
344 }
345 #endif
346 
DECLFW(MCopyFamiWrite)347 static DECLFW(MCopyFamiWrite) {
348 #ifndef NO_CACHE
349 	int32 i;
350 #endif
351 
352 #ifdef MESSAGE_LOG
353 	FCEU_printf("> WRITE %04X:%02X\n", A, V);
354 #endif
355 
356 	PRGWBCmd[1] = A & 0xFF;
357 	PRGWBCmd[2] = A >> 8;
358 	PRGWBCmd[3] = V & 0xFF;
359 	SEND(PRGWBCmd);
360 #ifdef NO_CACHE
361 	CheckStatus();
362 	Sync();
363 #else
364 	UpdateCmd((A << 8) | V);
365 	/* ���� ������� � ���� */
366 	if (cmd.found == -1) {
367 		/* �� �������, ���������, ���������� �� ��������� ������
368 		 * ���� �� ���������� �� ��� ����� ��� �������
369 		 */
370 		cmd_cache[cmd.hashf].index = cmd.found = cmds.count++;
371 		cmd_cache[cmd.hashf].retest = 0;
372 		cmd_cache[cmd.hashf].verify = 0;
373 		for (i = 0; i < cmd.size; i++)
374 			cmds.seqs[cmd.found][i] = cmd.seq[i];
375 		cmd_cache[cmd.hashf].size = cmd.size;
376 		if (CheckStatus()) {
377 			cmds.states[cmd.found] = state_new;
378 			LogCmd();
379 			cmd.size = 0;
380 			cmd.hash = 0;
381 			Sync();
382 		} else {
383 			/* ���� ��������� ������ ������� ��� ������������� */
384 			cmd_cache[cmd.hashf].index = -2;
385 		}
386 	} else if (cmd.found == -2) {
387 		/* ��������� ����������, ���� ����� �������� �� ��������� ����� */
388 		if (cmd_cache[cmd.hashf].retest < CMD_MAX_RETEST) {
389 			/* �� �������� ��������� ������ */
390 			if (CheckStatus()) {
391 				/* ����������, ������� ����� ������� */
392 				cmd_cache[cmd.hashf].index = cmd.found = cmds.count++;
393 				cmd_cache[cmd.hashf].retest = 0;
394 				cmd_cache[cmd.hashf].verify = 0;
395 				for (i = 0; i < cmd.size; i++)
396 					cmds.seqs[cmd.found][i] = cmd.seq[i];
397 				cmd_cache[cmd.hashf].size = cmd.size;
398 				cmds.states[cmd.found] = state_new;
399 				LogCmd();
400 				cmd.size = 0;
401 				cmd.hash = 0;
402 				Sync();
403 			} else {
404 				/* �� ����������, ������� �������� ������� �������� */
405 				cmd_cache[cmd.hashf].retest++;
406 			}
407 		}
408 	} else {
409 		/* �������, ��������� ����� ������� �� ����� */
410 #if 0
411 		if(cmd_cache[cmd.hashf].verify < CMD_MAX_VERIFY) {
412 			if(CheckStatus()) {
413 				int32 changed = 0;
414 				if(cmds.states[cmd.found].mirror != state_new.mirror)
415 					changed = 1;
416 				for(i=0; i<8; i++)
417 					if(cmds.states[cmd.found].chrsum[i] != state_new.chrsum[i])
418 						changed = 1;
419 				for(i=0; i<4; i++)
420 					if(cmds.states[cmd.found].prgsum[i] != state_new.prgsum[i])
421 						changed = 1;
422 				if(changed) {
423 					cmd_cache[cmd.hashf].index = -1;
424 					cmd_cache[cmd.hashf].retest = 0;
425 					cmd_cache[cmd.hashf].verify = 0;
426 					Sync();
427 				}
428 			} else
429 				cmd_cache[cmd.hashf].verify++;
430 		} else
431 #endif
432 		{
433 			/* ��������� ��� ��������� �������� ������� */
434 			ApplyStatus();
435 			cmd.size = 0;
436 			cmd.hash = 0;
437 		}
438 	}
439 #endif
440 }
441 
DECLFR(MCopyFamiRead)442 static DECLFR(MCopyFamiRead) {
443 	uint8 result;
444 	PRGRBCmd[1] = A & 0xFF;
445 	PRGRBCmd[2] = A >> 8;
446 	SENDGET(PRGRBCmd, result, 1);
447 #ifdef MESSAGE_LOG
448 	FCEU_printf("> READ %04X:%02X\n", A, result);
449 #endif
450 	return result;
451 }
452 
MCopyFamiReset(void)453 static void MCopyFamiReset(void) {
454 	state_cur = state_def;
455 	Sync();
456 }
457 
MCopyFamiPower(void)458 static void MCopyFamiPower(void) {
459 /*  uint32 resp, presp; */
460 
461 	FCEU_printf("NOW POWERING... ");
462 
463 	Sync();
464 
465 	SetWriteHandler(0x4018, 0x7fff, MCopyFamiWrite);
466 	SetReadHandler(0x4018, 0x7fff, MCopyFamiRead);
467 #ifndef NO_RAM
468 	SetWriteHandler(0x6000, 0x7fff, CartBW);
469 	SetReadHandler(0x6000, 0x7fff, CartBR);
470 #endif
471 
472 #if 0
473 	FCEU_printf("READING MEMORY MAP...\n");
474 	CPUTestCmd[1] = 0x50;
475 	CPUTestCmd[2] = 0x30;
476 	SEND(CPUTestCmd);
477 	resp = 0;
478 	presp = 0xffffffff;
479 	while (presp != 0x00ff0000) {
480 		GET(resp, 3);
481 		if(presp != 0xffffffff) {
482 			switch(presp & 0x00FF0000) {
483 				case 0x00000000: /* BUS */
484 					FCEU_printf("  %04X-%04X OPEN BUS\n",presp & 0x7fff, (resp - 1) & 0x7fff);
485 					break;
486 				case 0x00010000: /* RAM */
487 					FCEU_printf("  %04X-%04X RAM\n",presp & 0x7fff, (resp - 1) & 0x7fff);
488 					SetWriteHandler(presp & 0x7fff, (resp - 1) & 0x7fff, CartBW);
489 					SetReadHandler(presp & 0x7fff, (resp - 1) & 0x7fff, CartBR);
490 					break;
491 			}
492 		}
493 		presp = resp;
494 	}
495 #endif
496 	SetWriteHandler(0x8000, 0xffff, MCopyFamiWrite);
497 	SetReadHandler(0x8000, 0xffff, CartBR);
498 
499 	FCEU_printf("DONE!\nNOW COLLECTING DATA...\n");
500 }
501 
MCopyFamiClose(void)502 static void MCopyFamiClose(void) {
503 	if (chr_data.buf)
504 		FCEU_gfree(chr_data.buf);
505 	chr_data.buf = NULL;
506 	if (prg_data.buf)
507 		FCEU_gfree(prg_data.buf);
508 	prg_data.buf = NULL;
509 	if (WRAM)
510 		FCEU_gfree(WRAM);
511 	WRAM = NULL;
512 
513 	SerialClose();
514 }
515 
StateRestore(int version)516 static void StateRestore(int version) {
517 	Sync();
518 }
519 
MapperCopyFami_Init(CartInfo * info)520 void MapperCopyFami_Init(CartInfo *info) {
521 	uint32 resp = 0, i, size;
522 
523 	memset(chr_bank, -1, sizeof(chr_bank));
524 	memset(prg_bank, -1, sizeof(chr_bank));
525 	memset(cmd_cache, -1, sizeof(cmd_cache));
526 	memset(&cmds, 0, sizeof(cmds));
527 	memset(&cmd, 0, sizeof(cmd));
528 
529 	info->Reset = MCopyFamiReset;
530 	info->Power = MCopyFamiPower;
531 	info->Close = MCopyFamiClose;
532 	GameStateRestore = StateRestore;
533 
534 	size = 1024 * CHR_CACHE_SIZE;	/* ������ �������� 1�� */
535 	chr_data.buf = (uint8*)FCEU_gmalloc(size);
536 	SetupCartCHRMapping(1, chr_data.buf, size, 1);	/* ��������� ��� ���, ����� ���� ����� ������� ������ */
537 	AddExState(chr_data.buf, size, 0, "CCHR");
538 
539 	size = 8192;	/* ������ �������� 8�� */
540 	WRAM = (uint8*)FCEU_gmalloc(size);
541 	SetupCartPRGMapping(1, WRAM, size, 1);
542 	AddExState(WRAM, size, 0, "CPRM");
543 
544 	size = 8192 * PRG_CACHE_SIZE;	/* ������ �������� 8�� */
545 	prg_data.buf = (uint8*)FCEU_gmalloc(size);
546 	SetupCartPRGMapping(2, prg_data.buf, size, 0);
547 	AddExState(prg_data.buf, size, 0, "CPPR");
548 
549 
550 	FCEU_printf("WAITING FOR SERIAL PORT... ");
551 	while (!SerialOpen(19, 921600)) {
552 		Sleep(500);
553 	}
554 	FCEU_printf("READY!\n");
555 
556 	FCEU_printf("WAITING FOR DEVICE... ");
557 
558 	while (resp != *(uint32*)&InitVector[0]) {
559 		SEND(ResetCmd);
560 		SENDGET(InitVector, resp, 4);
561 		Sleep(500);
562 	}
563 
564 	FCEU_printf("READY!\n");
565 	FCEU_printf("READING STATUS...\n");
566 	GetStatus(&state_cur);
567 	FCEU_printf("MIRRORING IS %s (%02X)\n", mirror_names[mirror_modes[state_cur.mirror]], state_cur.mirror);
568 	FCEU_printf("READING CHR...\n INITIAL STATE:");
569 
570 	for (i = 0; i < 8; i++) {
571 		if (CHRCUR(i) == -1)
572 			CHRCUR(i) = FetchNewCHRBank(i);
573 		FCEU_printf(" CHR%d=%02X", i, CHRCUR(i));
574 	}
575 	FCEU_printf("\n");
576 
577 	FCEU_printf("READING PRG...\n INITIAL STATE:");
578 	for (i = 0; i < 4; i++) {
579 		if (PRGCUR(i) == -1)
580 			PRGCUR(i) = FetchNewPRGBank(i);
581 		FCEU_printf(" PRG%d=%02X", i, PRGCUR(i));
582 	}
583 	FCEU_printf("\nDONE!\n");
584 
585 	state_def = state_cur;
586 
587 	AddExState(&StateRegs, ~0, 0, 0);
588 }
589 
590 #endif
591