Files
ci-libretro-fceumm/src/boards/copyfami_hwi.c
retro-wertz 2d76eb4a0f Change *.c and *.h end-of-line character to LF
change eol char to LF using dos2unix
2017-12-17 13:40:35 +08:00

591 lines
17 KiB
C

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