Rewrite FFE (mapper 6/8/12.1/17) emulation. Add Bung Super Game Doctor (mapper 561) und Venus Turbo Game Doctor (mapper 562).
This commit is contained in:
committed by
LibretroAdmin
parent
7b16c23f70
commit
75ae69c0bf
634
src/boards/6_8_12_17_561_562.c
Normal file
634
src/boards/6_8_12_17_561_562.c
Normal file
@@ -0,0 +1,634 @@
|
||||
/* FCE Ultra - NES/Famicom Emulator
|
||||
*
|
||||
* Copyright notice for this file:
|
||||
* Copyright (C) 2025 NewRisingSun
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
/* Extracts from early floppy-disk-based RAM Cartridges ("copiers").
|
||||
- Front Far East Super Magic Card, which is backwards-compatible to Magic Card 1M, Magic Card 2M, Magic Card 4M. Mapper 6/8/12.1/17
|
||||
- Bung Super Game Doctor 4M, which is backwards-compatible to Super Game Doctor 2M and Super Game Doctor 1M. Mapper 561
|
||||
- Venus Turbo Game Doctor 6M, which is backwards-comaptible to Turbo Game Doctor 6+, Turbo Game Doctor 4+, and Venus Game Converter. Mapper 562
|
||||
Cross-compatibility between the devices is spotty and complicated. Generally, games have checks to lock out competing devices, while devices try to run as many competitor games as possible.
|
||||
*/
|
||||
#include "mapinc.h"
|
||||
|
||||
#define FFE 0
|
||||
#define BUNG 1
|
||||
#define VENUS 2
|
||||
|
||||
static uint8 maker;
|
||||
static uint8 mc1Mode, mc1ModeInitial; /* register 42Fx */
|
||||
static uint8 mc2Mode, mc2ModeInitial; /* register 43Fx */
|
||||
static uint8 extMode, extModeInitial; /* register 4500 on FFE, 4411 on Venus, does not exist on Bung */
|
||||
static uint8 latch; /* 8000-FFFF register for 1M modes */
|
||||
static uint8 lockCHR; /* CHR-RAM write-protected via undocumented protection mechanism */
|
||||
static uint8 prg8K[4]; /* 8 KiB PRG registers in 2M or 4M mode */
|
||||
static uint8 chr1K[12]; /* Not used by Bung, 8 registers by Venus, 12 registers by FFE */
|
||||
static uint8 chr8K; /* Not used by FFE, only by Bung and Venus */
|
||||
static uint8 lastCHRBank; /* Only used by Venus */
|
||||
|
||||
/* IRQ counter used by FFE and Bung */
|
||||
static signed short int irqCounter;
|
||||
|
||||
/* IRQ counter used by Venus */
|
||||
static unsigned short int tgdCounter, tgdTarget;
|
||||
|
||||
/* FFE-only registers */
|
||||
static uint8 latchMMC4[2]; /* For MMC4 emulation mode */
|
||||
static uint8 smcIRQ; /* IRQ enable register */
|
||||
|
||||
/* FDS data IRQ, used for frame timing purposes */
|
||||
static uint8 fdsIO;
|
||||
static signed short int fdsCounter;
|
||||
|
||||
static uint8 *WRAM =NULL; /* 0000-1FFF: CPU 6000-7FFF; 2000-2FFF: CPU 5000-5FFF (FFE only) */
|
||||
static uint8 *CHRRAM =NULL; /* up to 32 KiB for Bung, up to 256 KiB for FFE and Venus */
|
||||
|
||||
static uint32 trainerSize;
|
||||
static uint8 *trainerSource, *trainerTarget;
|
||||
static uint16 trainerInit, resetAddress;
|
||||
|
||||
/* For trapping writes to CHR-RAM, which can trigger a protection mechanism in FFE and Venus devices */
|
||||
extern uint32 RefreshAddr;
|
||||
static writefunc writePPU;
|
||||
static DECLFW(interceptPPUWrite) { /* Only called in modes 5-7 with single-screen mirroring. Then, Writing anything to CHR-RAM locks (CIRAM page 1) or unlocks (CIRAM page 0) CHR memory in modes 0-3. Needed for (F4040) Karnov). */
|
||||
if (~RefreshAddr &0x2000) lockCHR =!!(mc1Mode &0x10);
|
||||
writePPU(A, V);
|
||||
}
|
||||
|
||||
static SFORMAT stateRegsFFE[] ={
|
||||
{ &mc1Mode, 1, "MC1M" },
|
||||
{ &mc2Mode, 1, "MC2M" },
|
||||
{ &extMode, 1, "EXTM" },
|
||||
{ &latch, 1, "LATC" },
|
||||
{ &lockCHR, 1, "LOCK" },
|
||||
{ prg8K, 4, "PRG8" },
|
||||
{ chr1K, 12, "CHR1" },
|
||||
{ &irqCounter, 2 | FCEUSTATE_RLSB, "IRQC" },
|
||||
{ latchMMC4, 2, "MMC4" },
|
||||
{ &smcIRQ, 1, "IRQA" },
|
||||
{ &fdsIO, 1, "FDSI" },
|
||||
{ &fdsCounter, 2 | FCEUSTATE_RLSB, "FDSC" },
|
||||
{ 0 }
|
||||
};
|
||||
static SFORMAT stateRegsBung[] ={
|
||||
{ &mc1Mode, 1, "MC1M" },
|
||||
{ &mc2Mode, 1, "MC2M" },
|
||||
{ &latch, 1, "LATC" },
|
||||
{ prg8K, 4, "PRG8" },
|
||||
{ &chr8K, 1, "CHR8" },
|
||||
{ &irqCounter, 2 | FCEUSTATE_RLSB, "IRQC" },
|
||||
{ &fdsIO, 1, "FDSI" },
|
||||
{ &fdsCounter, 2 | FCEUSTATE_RLSB, "FDSC" },
|
||||
{ 0 }
|
||||
};
|
||||
static SFORMAT stateRegsVenus[] ={
|
||||
{ &mc1Mode, 1, "MC1M" },
|
||||
{ &mc2Mode, 1, "MC2M" },
|
||||
{ &extMode, 1, "EXTM" },
|
||||
{ &latch, 1, "LATC" },
|
||||
{ &lockCHR, 1, "LOCK" },
|
||||
{ prg8K, 4, "PRG8" },
|
||||
{ chr1K, 8, "CHR1" },
|
||||
{ &chr8K, 1, "CHR8" },
|
||||
{ &lastCHRBank, 1, "LACB" },
|
||||
{ &tgdCounter, 2 | FCEUSTATE_RLSB, "IRQC" },
|
||||
{ &tgdTarget, 2 | FCEUSTATE_RLSB, "IRQT" },
|
||||
{ &fdsIO, 1, "FDSI" },
|
||||
{ &fdsCounter, 2 | FCEUSTATE_RLSB, "FDSC" },
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
static void sync () {
|
||||
if (maker ==FFE) setprg4r(0x10, 0x5000, 2); /* Only FFE has further 4 KiB of PRG-RAM at 5000-5FFF */
|
||||
setprg8r(0x10, 0x6000, 0); /* Both FFE and Venus actually have 32 KiB of bankswitched PRG-RAM, but the other 24 KiB are not available to games, only to BIOS for various purposes, and are not emulated here */
|
||||
|
||||
/* Write-enable "PRG-ROM" (a.k.a. PRG-DRAM) if the PRG write-protect bit is not set */
|
||||
SetupCartPRGMapping(0, PRGptr[0], PRGsize[0], mc1Mode &0x02? 0: 1);
|
||||
if (~mc2Mode &0x01 && maker !=VENUS || extMode &0x80 && maker ==VENUS) { /* unrestricted 4M PRG mode on all devices */
|
||||
setprg8(0x8000, prg8K[0]);
|
||||
setprg8(0xA000, prg8K[1]);
|
||||
setprg8(0xC000, prg8K[2]);
|
||||
setprg8(0xE000, prg8K[3]);
|
||||
} else
|
||||
if (~mc2Mode &0x01 && maker ==VENUS) { /* On Venus, what enables 4M mode on Bung is an address-restricted 2M mode */
|
||||
setprg8(0x8000, prg8K[0] &0x0F | mc2Mode >>2 &0x10);
|
||||
setprg8(0xA000, prg8K[1] &0x0F | mc2Mode >>2 &0x10);
|
||||
setprg8(0xC000, prg8K[2] &0x0F);
|
||||
setprg8(0xE000, prg8K[3] &0x0F);
|
||||
} else /* 1M PRG mode (all devices) */
|
||||
switch(mc1Mode >>5) {
|
||||
case 0: setprg16(0x8000, latch &7);
|
||||
setprg16(0xC000, 7);
|
||||
break;
|
||||
case 1: setprg16(0x8000, latch >>2 &15);
|
||||
setprg16(0xC000, 7);
|
||||
break;
|
||||
case 2: setprg16(0x8000, latch &15);
|
||||
setprg16(0xC000, 15);
|
||||
break;
|
||||
case 3: setprg16(0x8000, 15);
|
||||
setprg16(0xC000, latch &15);
|
||||
break;
|
||||
case 4: setprg32(0x8000, latch >>4 &3);
|
||||
break;
|
||||
case 5: setprg32(0x8000, 3);
|
||||
break;
|
||||
case 6: if (maker ==FFE) /* Modes 6 and 7 differ between FFE and Bung/enus */
|
||||
setprg32(0x8000, 3);
|
||||
else {
|
||||
setprg8 (0x8000, latch &0xF);
|
||||
setprg8 (0xA000, latch >>4);
|
||||
setprg16(0xC000, 7);
|
||||
}
|
||||
break;
|
||||
case 7: if (maker ==FFE)
|
||||
setprg32(0x8000, 3);
|
||||
else {
|
||||
setprg8 (0x8000, latch &0xF &~1);
|
||||
setprg8 (0xA000, latch >>4 | 1);
|
||||
setprg16(0xC000, 7);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* CHR-RAM is write-protected on FFE ...
|
||||
- ... if 1M mode GNROM with two-screen mirroring is active;
|
||||
- ... if 1M mode (C)NROM is active;
|
||||
- ... if CHR-RAM has been locked before.
|
||||
On Bung/Venus, it's simpler: GNROM or (C)NROM modes, or CHR-RAM lock on Venus only; Bung has no CHR-RAM lock. */
|
||||
if (maker ==FFE)
|
||||
SetupCartCHRMapping(0x10, CHRRAM, CHRsize[0x10], (mc1Mode &0xE1) >=0x81 || lockCHR? 0: 1);
|
||||
else
|
||||
SetupCartCHRMapping(0x10, CHRRAM, CHRsize[0x10], mc1Mode >=0x80 || lockCHR? 0: 1);
|
||||
|
||||
/* CHR-RAM banking */
|
||||
if (maker ==BUNG || maker ==VENUS && ~extMode &0x40)
|
||||
setchr8r(0x10, chr8K); /* Bung and Venus maintain an independent 8 KiB bank number register that is updated from the latch at specific events */
|
||||
else
|
||||
if (extMode &0x01 || maker ==VENUS && extMode &0x40) { /* 1 KiB CHR-RAM modes */
|
||||
if (extMode &0x04 || maker ==VENUS) { /* For FFE, need to further distinguish between actual 1 KiB and 4 KiB MMC4-like mode */
|
||||
setchr1r(0x10, 0x0000, chr1K[0]);
|
||||
setchr1r(0x10, 0x0400, chr1K[1]);
|
||||
setchr1r(0x10, 0x0800, chr1K[2]);
|
||||
setchr1r(0x10, 0x0C00, chr1K[3]);
|
||||
setchr1r(0x10, 0x1000, chr1K[4]);
|
||||
setchr1r(0x10, 0x1400, chr1K[5]);
|
||||
setchr1r(0x10, 0x1800, chr1K[6]);
|
||||
setchr1r(0x10, 0x1C00, chr1K[7]);
|
||||
} else {
|
||||
setchr4r(0x10, 0x0000, chr1K[0 | latchMMC4[0]] >>2);
|
||||
setchr4r(0x10, 0x1000, chr1K[4 | latchMMC4[1]] >>2);
|
||||
}
|
||||
} else
|
||||
switch(mc1Mode >>5) { /* 8 KiB CHR-RAM banking mode on FFE only, which applies the latch directly without an extra 8 KiB CHR-RAM bank register */
|
||||
case 0: case 2:
|
||||
setchr8r(0x10, 0);
|
||||
break;
|
||||
case 1: case 4: case 5:
|
||||
setchr8r(0x10, latch &3);
|
||||
break;
|
||||
case 3:
|
||||
setchr8r(0x10, latch >>4 &3);
|
||||
break;
|
||||
case 6:
|
||||
setchr8r(0x10, latch &1);
|
||||
break;
|
||||
case 7:
|
||||
setchr8r(0x10, 3);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Nametable arrangement. FFE can map CHR-RAM to nametable address space; Bung/Venus only can do normal CIRAM arrangement. */
|
||||
if (maker !=FFE || extMode &0x02) /* CIRAM nametables */
|
||||
setmirror(mc1Mode &0x01? (mc1Mode &0x10? MI_H: MI_V): (mc1Mode &0x10? MI_1: MI_0));
|
||||
else { /* CHR-RAM nametables */
|
||||
setntamem(CHRRAM +0x0400 *chr1K[8], 0, 0);
|
||||
setntamem(CHRRAM +0x0400 *chr1K[9], 0, 1);
|
||||
setntamem(CHRRAM +0x0400 *chr1K[10],0, 2);
|
||||
setntamem(CHRRAM +0x0400 *chr1K[11],0, 3);
|
||||
}
|
||||
|
||||
/* Intercept CHR-RAM writes for undocument CHR-RAM locking mechanism, only on FFE and Venus. */
|
||||
if (maker !=BUNG) SetWriteHandler(0x2007, 0x2007, mc1Mode >=0xA0 && ~mc1Mode &1? interceptPPUWrite: writePPU);
|
||||
}
|
||||
|
||||
|
||||
/* Some Venus games check values in BIOS at 4800-4FFF to lock out Bung and FFE devices. Do not need to include the full BIOS, just a selection of values. */
|
||||
static const uint8 tgdBIOSExtract[32] ={
|
||||
0xfe, 0x60, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0x4c, 0xd9, 0x48, 0x4c, 0x18, 0x49, 0x4c,
|
||||
0x23, 0x4f, 0x4c, 0x3f, 0x4f, 0x4c, 0x78, 0x4f
|
||||
};
|
||||
static DECLFR(readTGDBIOS) {
|
||||
return tgdBIOSExtract[A &0x1F];
|
||||
}
|
||||
|
||||
static DECLFR(readReg) {
|
||||
if (maker ==VENUS) switch(A) {
|
||||
case 0x4400: case 0x4401: case 0x4402: case 0x4403: case 0x4404: case 0x4405: case 0x4406: case 0x4407:
|
||||
return chr1K[A &7];
|
||||
case 0x4408: case 0x4409: case 0x440A: case 0x440B:
|
||||
return prg8K[A &3] <<2 | latch &3;
|
||||
case 0x440C:
|
||||
return tgdCounter >>8;
|
||||
case 0x440D:
|
||||
return tgdCounter &0xFF;
|
||||
case 0x4411:
|
||||
return extMode;
|
||||
case 0x4415:
|
||||
return mc1Mode;
|
||||
case 0x4420: /* Venus allows reading the last accessed CHR-RAM bank register, used by some Venus games to lock out Bung and FFE devices. */
|
||||
return chr1K[lastCHRBank &7];
|
||||
} else
|
||||
if (maker ==FFE) switch(A) {
|
||||
case 0x4500:
|
||||
return extMode;
|
||||
default:
|
||||
return A >>8;
|
||||
} else
|
||||
return A >>8; /* Open Bus */
|
||||
}
|
||||
|
||||
static DECLFW(writeReg) {
|
||||
switch (A) { /* All makers */
|
||||
case 0x4024:
|
||||
X6502_IRQEnd(FCEU_IQEXT);
|
||||
break;
|
||||
case 0x4025:
|
||||
X6502_IRQEnd(FCEU_IQEXT);
|
||||
if (!smcIRQ) {
|
||||
fdsIO =V;
|
||||
if (V &0x42) fdsCounter =0;
|
||||
}
|
||||
break;
|
||||
case 0x42FC: case 0x42FD: case 0x42FE: case 0x42FF:
|
||||
mc1Mode =V &0xF0 | A &0x03;
|
||||
if (mc1Mode >=0x80) lockCHR =0; /* Setting modes 4-7 releases the CHR-RAM lock */
|
||||
sync();
|
||||
break;
|
||||
case 0x43FC: case 0x43FD: case 0x43FE: case 0x43FF:
|
||||
mc2Mode =V &0xF0 | A &0x03;
|
||||
latch =V;
|
||||
chr8K =V &3;
|
||||
sync();
|
||||
break;
|
||||
}
|
||||
|
||||
if (maker ==BUNG) switch(A) {
|
||||
case 0x4100:
|
||||
irqCounter =(irqCounter &~0x00FF) | V;
|
||||
X6502_IRQEnd(FCEU_IQEXT);
|
||||
if (V ==0) irqCounter =0;
|
||||
break;
|
||||
case 0x4101:
|
||||
irqCounter =(irqCounter &~0xFF00) | V <<8;
|
||||
X6502_IRQEnd(FCEU_IQEXT);
|
||||
break;
|
||||
} else
|
||||
if (maker ==VENUS) switch(A) {
|
||||
case 0x4400: case 0x4401: case 0x4402: case 0x4403: case 0x4404: case 0x4405: case 0x4406: case 0x4407:
|
||||
chr1K[A &0x07] =V;
|
||||
sync();
|
||||
break;
|
||||
case 0x440C:
|
||||
X6502_IRQEnd(FCEU_IQEXT);
|
||||
if (~V &0x80) tgdCounter =0x8000;
|
||||
tgdTarget =tgdTarget &0x00FF | V <<8;
|
||||
break;
|
||||
case 0x440D:
|
||||
X6502_IRQEnd(FCEU_IQEXT);
|
||||
tgdTarget =tgdTarget &0xFF00 | V;
|
||||
break;
|
||||
case 0x4411:
|
||||
extMode =V;
|
||||
sync();
|
||||
break;
|
||||
} else
|
||||
if (maker ==FFE) {
|
||||
switch(A) {
|
||||
case 0x4500:
|
||||
extMode =V;
|
||||
sync();
|
||||
break;
|
||||
case 0x4501:
|
||||
smcIRQ =0;
|
||||
X6502_IRQEnd(FCEU_IQEXT);
|
||||
break;
|
||||
case 0x4502:
|
||||
irqCounter =irqCounter &0xFF00 | V;
|
||||
X6502_IRQEnd(FCEU_IQEXT);
|
||||
break;
|
||||
case 0x4503:
|
||||
smcIRQ =true;
|
||||
irqCounter =irqCounter &0x00FF | V <<8 ;
|
||||
X6502_IRQEnd(FCEU_IQEXT);
|
||||
break;
|
||||
case 0x4504: case 0x4505: case 0x4506: case 0x4507:
|
||||
if (mc2Mode &0x02) V >>=2; /* The two bottom bits are unused when 4M mode is not active */
|
||||
prg8K[A &3] =V;
|
||||
sync();
|
||||
break;
|
||||
case 0x4510: case 0x4511: case 0x4512: case 0x4513: case 0x4514: case 0x4515: case 0x4516: case 0x4517: case 0x4518: case 0x4519: case 0x451A: case 0x451B:
|
||||
chr1K[A &0x0F] =V;
|
||||
sync();
|
||||
break;
|
||||
}
|
||||
if (A >=0x4500 && A <=0x451F) WRAM[0x2500 |A &0x1F] =V; /* SMC register content is cached at WRAM $5500-$551F */
|
||||
}
|
||||
}
|
||||
|
||||
static DECLFW(writeLatch) {
|
||||
if (mc1Mode &0x02) {
|
||||
latch =V;
|
||||
if (maker !=FFE || mc2Mode &0x03) prg8K[A >>13 &3] =V >>2; /* The two bottom bits are unused when 4M mode is not active */
|
||||
if (maker !=FFE) switch(mc1Mode >>5) {
|
||||
case 1: case 4: case 5:
|
||||
chr8K =latch &3;
|
||||
break;
|
||||
case 3:
|
||||
chr8K =latch >>4 &3;
|
||||
break;
|
||||
}
|
||||
sync();
|
||||
} else
|
||||
CartBW(A, V);
|
||||
}
|
||||
|
||||
/* Read handler for FFFC and FFFD so that in the presence of a trainer, the trainer's Init address is called before the game's reset handler on power-on reset.
|
||||
If the trainer's Init routine requires being called via JSR, resetAddress will be a routine in RAM at 0500 consisting of JSR trainerInit, JMP ($FFFC), so that address is returned from FFFC instead.
|
||||
If the trainer's Init routine requires being JMPed to, resetAddress will be trainerInit itself.
|
||||
After FFFD has been read, restore the original CartBR read handler, because on a soft reset, trainerInit should not be called. */
|
||||
static DECLFR(redirectReset) {
|
||||
if (A ==0xFFFC)
|
||||
return resetAddress &0xFF;
|
||||
else {
|
||||
SetReadHandler(0xFFFC, 0xFFFD, CartBR);
|
||||
return resetAddress >>8;
|
||||
}
|
||||
}
|
||||
|
||||
static void power(void) {
|
||||
int i;
|
||||
writePPU =GetWriteHandler(0x2007);
|
||||
GetWriteHandler(0x4017)(0x4017, 0x40); /* Disable Frame IRQ */
|
||||
mc1Mode =mc1ModeInitial;
|
||||
mc2Mode =mc2ModeInitial;
|
||||
extMode =extModeInitial;
|
||||
lockCHR =latch =chr8K =lastCHRBank =0;
|
||||
for (i =0; i <4; i++) prg8K[3 -i] =(maker ==FFE? ROM_size *2: 0x20) -i -1;
|
||||
for (i =0; i <8; i++) chr1K[i] =i;
|
||||
for (i =8; i<12; i++) chr1K[i] =i &7;
|
||||
smcIRQ =0;
|
||||
irqCounter =0;
|
||||
fdsIO =0;
|
||||
fdsCounter =0;
|
||||
tgdCounter =0xFFFF;
|
||||
tgdTarget =0;
|
||||
sync();
|
||||
|
||||
SetReadHandler(0x4020, 0x47FF, readReg);
|
||||
if (maker ==VENUS) SetReadHandler(0x4800, 0x4FFF, readTGDBIOS);
|
||||
SetWriteHandler(0x4020, 0x47FF, writeReg);
|
||||
|
||||
SetReadHandler(0x5000, 0x7FFF, CartBR);
|
||||
SetWriteHandler(0x5000, 0x7FFF, CartBW);
|
||||
SetReadHandler(0x8000, 0xFFFF, CartBR);
|
||||
SetWriteHandler(0x8000, 0xFFFF, writeLatch);
|
||||
|
||||
resetAddress =0;
|
||||
/* Initialize the trainer. Its position varies, which is why iNES_ExecPower's initialization is disabled for FFE, BUNG and VENUS mappers. */
|
||||
if (trainerSize) {
|
||||
for (i =0; i <trainerSize; i++) trainerTarget[i] =trainerSource[i];
|
||||
if (maker ==FFE && extModeInitial ==0x47)
|
||||
resetAddress =trainerInit;
|
||||
else
|
||||
if (trainerInit) {
|
||||
X6502_DMW(0x0500, 0x20);
|
||||
X6502_DMW(0x0501, trainerInit &0xFF);
|
||||
X6502_DMW(0x0502, trainerInit >>8);
|
||||
X6502_DMW(0x0503, 0x6C);
|
||||
X6502_DMW(0x0504, 0xFC);
|
||||
X6502_DMW(0x0505, 0xFF);
|
||||
resetAddress =0x0500;
|
||||
}
|
||||
}
|
||||
if (resetAddress) SetReadHandler(0xFFFC, 0xFFFD, redirectReset);
|
||||
if (maker ==FFE) { /* For NARC (SM2006) and Best 競馬: Derby Stallion (SM4064), whose NMI vector point to the FFE BIOS' scratch RAM */
|
||||
WRAM[0x2032] =0x4C;
|
||||
WRAM[0x2033] =ROM[ROM_size *16384 -6 +0];
|
||||
WRAM[0x2034] =ROM[ROM_size *16384 -6 +1];
|
||||
}
|
||||
}
|
||||
|
||||
static void clockFDSCounter () {
|
||||
fdsCounter +=3;
|
||||
while (fdsCounter >=448 && fdsIO &0x80) {
|
||||
X6502_IRQBegin(FCEU_IQEXT);
|
||||
fdsCounter -=448;
|
||||
}
|
||||
}
|
||||
|
||||
static void clockSMCCounter () {
|
||||
if (smcIRQ) {
|
||||
if (irqCounter ==-1) {
|
||||
smcIRQ =0;
|
||||
X6502_IRQBegin(FCEU_IQEXT);
|
||||
} else
|
||||
irqCounter++;
|
||||
}
|
||||
}
|
||||
|
||||
static void FP_FASTAPASS(1) cpuCycleFFE(int a) {
|
||||
while (a--) {
|
||||
clockFDSCounter();
|
||||
if (~extMode &0x08) clockSMCCounter();
|
||||
}
|
||||
}
|
||||
|
||||
static void FP_FASTAPASS(1) cpuCycleBung(int a) {
|
||||
while (a--) {
|
||||
clockFDSCounter();
|
||||
if (irqCounter <0 && !++irqCounter) X6502_IRQBegin(FCEU_IQEXT);
|
||||
}
|
||||
}
|
||||
|
||||
static void FP_FASTAPASS(1) cpuCycleVenus(int a) {
|
||||
while (a--) {
|
||||
clockFDSCounter();
|
||||
if (tgdTarget &0x8000) {
|
||||
if (tgdCounter ==tgdTarget && tgdCounter !=0xFFFF)
|
||||
X6502_IRQBegin(FCEU_IQEXT);
|
||||
else
|
||||
tgdCounter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void pseudoScanlineFFE () {
|
||||
if (extMode &0x08) { /* Eight A12 rises per scanline */
|
||||
clockSMCCounter();
|
||||
clockSMCCounter();
|
||||
clockSMCCounter();
|
||||
clockSMCCounter();
|
||||
clockSMCCounter();
|
||||
clockSMCCounter();
|
||||
clockSMCCounter();
|
||||
clockSMCCounter();
|
||||
}
|
||||
}
|
||||
|
||||
static void FP_FASTAPASS(1) trapPPUAddressChangeFFE (uint32 A) {
|
||||
if (extMode &0x01 && ~extMode &0x04 && ((A &0x2FF0) ==0xFD0 || (A &0x2FF0) ==0xFE0)) {
|
||||
/* If MMC4 mode is enabled, and CHR mode[0] is 4 KiB, and tile FD or FE is being fetched ... */
|
||||
latchMMC4[A >>12 &1] =(A >>10 &4) | (A >>4 &2); /* ... switch the left or right pattern table's latch to 0 (FD) or 2 (FE), being used as an offset for the CHR register index. */
|
||||
sync();
|
||||
}
|
||||
}
|
||||
|
||||
static void FP_FASTAPASS(1) trapPPUAddressChangeVenus (uint32 A) {
|
||||
if (A &~0x2000) lastCHRBank =A >>10;
|
||||
}
|
||||
|
||||
void close(void) {
|
||||
if (CHRRAM) {
|
||||
FCEU_gfree(CHRRAM);
|
||||
CHRRAM =NULL;
|
||||
}
|
||||
if (WRAM) {
|
||||
FCEU_gfree(WRAM);
|
||||
WRAM =NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void FFE_Init(CartInfo *info) {
|
||||
maker =FFE;
|
||||
AddExState(stateRegsFFE, ~0, 0, 0);
|
||||
if (!info->iNES2 && info->mapper ==6) info->submapper =1;
|
||||
if (info->mapper ==8) {
|
||||
info->mapper =6;
|
||||
info->submapper =4;
|
||||
}
|
||||
mc1ModeInitial =(info->mapper ==6? info->submapper: 1) <<5 | (info->mirror &1? 0x01: 0x11) | 0x02;
|
||||
mc2ModeInitial =info->mapper ==12 || info->mapper ==17? 0x00: 0x03;
|
||||
extModeInitial =info->mapper ==17? 0x47: 0x42;
|
||||
|
||||
WRAM =(uint8*)FCEU_gmalloc(16384); /* Just 12 KiB actually, but need power-of-two size */
|
||||
SetupCartPRGMapping(0x10, WRAM, 16384, 1);
|
||||
memset(WRAM, 0x00, 16384);
|
||||
AddExState(WRAM, 16384, 0, "WRAM");
|
||||
|
||||
CHRRAM =(uint8*)FCEU_gmalloc(262144);
|
||||
SetupCartCHRMapping(0x10, CHRRAM, 262144, 1);
|
||||
AddExState(CHRRAM, 262144, 0, "CRAM");
|
||||
memset(CHRRAM, 0xFF, 262144); /* Don't initialize to $00 because 8 Eyes (F4089) uses a sprite 0 hit sprite with uninitialized CHR data */
|
||||
if (info->mapper ==12 && info->submapper ==1 && info->CHRRomSize) { /* FFE F6xxx needs CHR data in PRG-ROM. Enlarge to 512 KiB, copy and replace previous PRG-ROM buffer. */
|
||||
uint8* newROM =(uint8*)FCEU_gmalloc(524288);
|
||||
memset(newROM, 0xFF, 524288);
|
||||
memcpy(newROM + 0, ROM, info->PRGRomSize >524288? 524288: info->PRGRomSize);
|
||||
memcpy(newROM +262144, VROM, info->CHRRomSize >262144? 262144: info->CHRRomSize);
|
||||
FCEU_gfree(ROM);
|
||||
ROM =newROM;
|
||||
SetupCartPRGMapping(0, ROM, 524288, 0);
|
||||
} else
|
||||
if (info->CHRRomSize) /* Need data in CHR-RAM, not CHR-ROM */
|
||||
memcpy(info->CHRRomSize ==8192? (CHRRAM +24576): CHRRAM, VROM, info->CHRRomSize >262144? 262144: info->CHRRomSize);
|
||||
|
||||
trainerSize =trainerpoo? 512: 0;
|
||||
trainerSource =trainerpoo;
|
||||
if (info->mapper ==17) {
|
||||
if (info->submapper >0) {
|
||||
trainerInit =info->submapper <<8 &0x0300 | 0x5C00;
|
||||
trainerTarget =WRAM +0x2000 +(trainerInit &0x0FFF);
|
||||
} else {
|
||||
trainerInit =0x7000;
|
||||
trainerTarget =WRAM +0x1000;
|
||||
}
|
||||
} else {
|
||||
trainerInit =0x7003;
|
||||
trainerTarget =WRAM +0x1000;
|
||||
}
|
||||
|
||||
info->Power =power;
|
||||
info->Close =close;
|
||||
MapIRQHook =cpuCycleFFE;
|
||||
GameHBIRQHook =pseudoScanlineFFE;
|
||||
PPU_hook =trapPPUAddressChangeFFE;
|
||||
}
|
||||
|
||||
void Mapper561_562_Init(CartInfo *info) {
|
||||
int CHRRAMSize;
|
||||
maker =info->mapper ==562? VENUS: BUNG;
|
||||
AddExState(maker ==VENUS? stateRegsVenus: stateRegsBung, ~0, 0, 0);
|
||||
mc1ModeInitial =info->submapper <<5 | (info->mirror &1? 0x01: 0x11) | 0x02;
|
||||
mc2ModeInitial =0x03;
|
||||
extModeInitial =0x03;
|
||||
|
||||
WRAM =(uint8*)FCEU_gmalloc(8192);
|
||||
SetupCartPRGMapping(0x10, WRAM, 8192, 1);
|
||||
memset(WRAM, 0x00, 8192);
|
||||
AddExState(WRAM, 8192, 0, "WRAM");
|
||||
|
||||
CHRRAMSize =info->CHRRomSize? info->CHRRomSize: (info->CHRRamSize +info->CHRRamSaveSize);
|
||||
CHRRAM =(uint8*)FCEU_gmalloc(CHRRAMSize);
|
||||
SetupCartCHRMapping(0x10, CHRRAM, CHRRAMSize, 1);
|
||||
AddExState(CHRRAM, CHRRAMSize, 0, "CRAM");
|
||||
memset(CHRRAM, 0xFF, CHRRAMSize);
|
||||
if (info->mapper ==561 && info->submapper ==3 && info->PRGRomSize ==131072) { /* Crazy Climber (G026) is 128 KiB but expects to find PRG-DRAM above it */
|
||||
uint8* newROM =(uint8*)FCEU_gmalloc(262144);
|
||||
memset(newROM, 0xFF, 262144);
|
||||
memcpy(newROM, ROM, 131072);
|
||||
FCEU_gfree(ROM);
|
||||
ROM =newROM;
|
||||
SetupCartPRGMapping(0, ROM, 262144, 0);
|
||||
}
|
||||
if (info->CHRRomSize) memcpy(CHRRAM, VROM, CHRRAMSize);
|
||||
|
||||
/* Bung and Venus trainers can come in two forms:
|
||||
- If the load address is CPU $7000 and the size is 512 bytes, then they are identical to FFE trainers and come as normal iNES Trainers.
|
||||
- Otherwise, they come as NES 2.0 Misc. ROM, and the first two words indicate the load and init address. */
|
||||
if (trainerpoo) {
|
||||
trainerSize =512;
|
||||
trainerSource =trainerpoo;
|
||||
trainerInit =0x7003;
|
||||
trainerTarget =WRAM +0x1000;
|
||||
} else
|
||||
if (MiscROM && info->miscROMSize >=4) {
|
||||
uint16_t addr;
|
||||
trainerSize =info->miscROMSize -4;
|
||||
trainerSource =MiscROM +4;
|
||||
trainerInit =MiscROM[2] | MiscROM[3] <<8;
|
||||
addr =MiscROM[0] | MiscROM[1] <<8;
|
||||
if (addr <0x2000)
|
||||
trainerTarget =RAM +(addr &0x07FF);
|
||||
else
|
||||
if (addr >=0x6000 && addr <=0x7FFF)
|
||||
trainerTarget =WRAM +(addr &0x1FFF);
|
||||
else
|
||||
trainerSize =0;
|
||||
} else
|
||||
trainerSize =0;
|
||||
|
||||
info->Power =power;
|
||||
info->Close =close;
|
||||
if (maker ==VENUS) {
|
||||
MapIRQHook =cpuCycleVenus;
|
||||
PPU_hook =trapPPUAddressChangeVenus;
|
||||
} else
|
||||
MapIRQHook =cpuCycleBung;
|
||||
}
|
||||
@@ -162,18 +162,6 @@ void ANROM_Init(CartInfo *info) {
|
||||
Latch_Init(info, ANROMSync, 0, 0x4020, 0xFFFF, 0, 0);
|
||||
}
|
||||
|
||||
/*------------------ Map 8 ---------------------------*/
|
||||
|
||||
static void M8Sync(void) {
|
||||
setprg16(0x8000, latche >> 3);
|
||||
setprg16(0xc000, 1);
|
||||
setchr8(latche & 3);
|
||||
}
|
||||
|
||||
void Mapper8_Init(CartInfo *info) {
|
||||
Latch_Init(info, M8Sync, 0, 0x8000, 0xFFFF, 0, 0);
|
||||
}
|
||||
|
||||
/*------------------ Map 11 ---------------------------*/
|
||||
|
||||
static void M11Sync(void) {
|
||||
|
||||
153
src/boards/ffe.c
153
src/boards/ffe.c
@@ -1,153 +0,0 @@
|
||||
/* FCE Ultra - NES/Famicom Emulator
|
||||
*
|
||||
* Copyright notice for this file:
|
||||
* Copyright (C) 2012 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
|
||||
*
|
||||
* FFE Copier Mappers
|
||||
*
|
||||
*/
|
||||
|
||||
#include "mapinc.h"
|
||||
|
||||
static uint8 preg[4], creg[8], latch, ffemode;
|
||||
static uint8 IRQa, mirr;
|
||||
static int32 IRQCount, IRQLatch;
|
||||
static uint8 *WRAM = NULL;
|
||||
static uint32 WRAMSIZE;
|
||||
|
||||
static SFORMAT StateRegs[] =
|
||||
{
|
||||
{ preg, 4, "PREG" },
|
||||
{ creg, 8, "CREG" },
|
||||
{ &mirr, 1, "MIRR" },
|
||||
{ &IRQa, 1, "IRQA" },
|
||||
{ &IRQCount, 4, "IRQC" },
|
||||
{ &IRQLatch, 4, "IRQL" },
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
static void Sync(void) {
|
||||
setprg8r(0x10, 0x6000, 0);
|
||||
if (ffemode) {
|
||||
int i;
|
||||
for (i = 0; i < 8; i++) setchr1(i << 10, creg[i]);
|
||||
setprg8(0x8000, preg[0]);
|
||||
setprg8(0xA000, preg[1]);
|
||||
setprg8(0xC000, preg[2]);
|
||||
setprg8(0xE000, preg[3]);
|
||||
} else {
|
||||
setchr8(latch & 3);
|
||||
setprg16(0x8000, (latch >> 2) & 0x3F);
|
||||
setprg16(0xc000, 0x7);
|
||||
}
|
||||
switch (mirr) {
|
||||
case 0: setmirror(MI_0); break;
|
||||
case 1: setmirror(MI_1); break;
|
||||
case 2: setmirror(MI_V); break;
|
||||
case 3: setmirror(MI_H); break;
|
||||
}
|
||||
}
|
||||
|
||||
static DECLFW(FFEWriteMirr) {
|
||||
mirr = ((A << 1) & 2) | ((V >> 4) & 1);
|
||||
Sync();
|
||||
}
|
||||
|
||||
static DECLFW(FFEWriteIRQ) {
|
||||
switch (A) {
|
||||
case 0x4501: IRQa = 0; X6502_IRQEnd(FCEU_IQEXT); break;
|
||||
case 0x4502: IRQCount &= 0xFF00; IRQCount |= V; X6502_IRQEnd(FCEU_IQEXT); break;
|
||||
case 0x4503: IRQCount &= 0x00FF; IRQCount |= V << 8; IRQa = 1; X6502_IRQEnd(FCEU_IQEXT); break;
|
||||
}
|
||||
}
|
||||
|
||||
static DECLFW(FFEWritePrg) {
|
||||
preg[A & 3] = V;
|
||||
Sync();
|
||||
}
|
||||
|
||||
static DECLFW(FFEWriteChr) {
|
||||
creg[A & 7] = V;
|
||||
Sync();
|
||||
}
|
||||
|
||||
static DECLFW(FFEWriteLatch) {
|
||||
latch = V;
|
||||
Sync();
|
||||
}
|
||||
|
||||
static void FFEPower(void) {
|
||||
preg[3] = ~0;
|
||||
Sync();
|
||||
SetReadHandler(0x8000, 0xFFFF, CartBR);
|
||||
SetWriteHandler(0x42FE, 0x42FF, FFEWriteMirr);
|
||||
SetWriteHandler(0x4500, 0x4503, FFEWriteIRQ);
|
||||
SetWriteHandler(0x4504, 0x4507, FFEWritePrg);
|
||||
SetWriteHandler(0x4510, 0x4517, FFEWriteChr);
|
||||
SetWriteHandler(0x4510, 0x4517, FFEWriteChr);
|
||||
SetWriteHandler(0x6000, 0x7FFF, CartBW);
|
||||
SetReadHandler(0x6000, 0x7FFF, CartBR);
|
||||
SetWriteHandler(0x8000, 0xFFFF, FFEWriteLatch);
|
||||
FCEU_CheatAddRAM(WRAMSIZE >> 10, 0x6000, WRAM);
|
||||
}
|
||||
|
||||
static void FP_FASTAPASS(1) FFEIRQHook(int a) {
|
||||
if (IRQa) {
|
||||
IRQCount += a;
|
||||
if (IRQCount >= 0x10000) {
|
||||
X6502_IRQBegin(FCEU_IQEXT);
|
||||
IRQa = 0;
|
||||
IRQCount = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void FFEClose(void) {
|
||||
if (WRAM)
|
||||
FCEU_gfree(WRAM);
|
||||
WRAM = NULL;
|
||||
}
|
||||
|
||||
static void StateRestore(int version) {
|
||||
Sync();
|
||||
}
|
||||
|
||||
void Mapper6_Init(CartInfo *info) {
|
||||
ffemode = 0;
|
||||
mirr = ((info->mirror & 1) ^ 1) | 2;
|
||||
|
||||
info->Power = FFEPower;
|
||||
info->Close = FFEClose;
|
||||
MapIRQHook = FFEIRQHook;
|
||||
GameStateRestore = StateRestore;
|
||||
|
||||
WRAMSIZE = 8192;
|
||||
WRAM = (uint8*)FCEU_gmalloc(WRAMSIZE);
|
||||
SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1);
|
||||
AddExState(WRAM, WRAMSIZE, 0, "WRAM");
|
||||
if (info->battery) {
|
||||
info->SaveGame[0] = WRAM;
|
||||
info->SaveGameLen[0] = WRAMSIZE;
|
||||
}
|
||||
|
||||
AddExState(&StateRegs, ~0, 0, 0);
|
||||
}
|
||||
|
||||
void Mapper17_Init(CartInfo *info) {
|
||||
ffemode = 1;
|
||||
Mapper6_Init(info);
|
||||
}
|
||||
@@ -383,6 +383,10 @@ static void M12Reset(void) {
|
||||
}
|
||||
|
||||
void Mapper12_Init(CartInfo *info) {
|
||||
if (info->submapper ==1) {
|
||||
FFE_Init(info);
|
||||
return;
|
||||
}
|
||||
GenMMC3_Init(info, 512, 256, 8, info->battery);
|
||||
cwrap = M12CW;
|
||||
isRevB = 0;
|
||||
|
||||
13
src/ines.c
13
src/ines.c
@@ -432,9 +432,9 @@ INES_BOARD_BEGIN()
|
||||
INES_BOARD( "CNROM", 3, CNROM_Init )
|
||||
INES_BOARD( "MMC3", 4, Mapper4_Init )
|
||||
INES_BOARD( "MMC5", 5, Mapper5_Init )
|
||||
INES_BOARD( "FFE Rev. A", 6, Mapper6_Init )
|
||||
INES_BOARD( "FFE Rev. A", 6, FFE_Init )
|
||||
INES_BOARD( "ANROM", 7, ANROM_Init )
|
||||
INES_BOARD( "", 8, Mapper8_Init ) /* no games, it's worthless */
|
||||
INES_BOARD( "", 8, FFE_Init )
|
||||
INES_BOARD( "MMC2", 9, Mapper9_Init )
|
||||
INES_BOARD( "MMC4", 10, Mapper10_Init )
|
||||
INES_BOARD( "Color Dreams", 11, Mapper11_Init )
|
||||
@@ -443,7 +443,7 @@ INES_BOARD_BEGIN()
|
||||
INES_BOARD( "REX SL-1632", 14, UNLSL1632_Init )
|
||||
INES_BOARD( "100-in-1", 15, Mapper15_Init )
|
||||
INES_BOARD( "BANDAI 24C02", 16, Mapper16_Init )
|
||||
INES_BOARD( "FFE Rev. B", 17, Mapper17_Init )
|
||||
INES_BOARD( "FFE Rev. B", 17, FFE_Init )
|
||||
INES_BOARD( "JALECO SS880006", 18, Mapper18_Init ) /* JF-NNX (EB89018-30007) boards */
|
||||
INES_BOARD( "Namcot 106", 19, Mapper19_Init )
|
||||
/* INES_BOARD( "", 20, Mapper20_Init ) */
|
||||
@@ -901,6 +901,8 @@ INES_BOARD_BEGIN()
|
||||
INES_BOARD( "", 550, Mapper550_Init )
|
||||
INES_BOARD( "YC-03-09", 558, Mapper558_Init )
|
||||
INES_BOARD( "Subor Sango II", 559, Mapper559_Init )
|
||||
INES_BOARD( "Bung Super Game Doctor", 561, Mapper561_562_Init )
|
||||
INES_BOARD( "Venus Turbo Game Doctor", 562, Mapper561_562_Init )
|
||||
INES_BOARD( "J-2020", 563, Mapper563_Init )
|
||||
INES_BOARD_END()
|
||||
|
||||
@@ -1015,15 +1017,14 @@ int iNESLoad(const char *name, FCEUFILE *fp)
|
||||
FCEU_PrintError(" File contains %llu bytes of unused data\n", filesize - romSize);
|
||||
|
||||
rom_size_pow2 = uppow2(iNESCart.PRGRomSize);
|
||||
|
||||
|
||||
if ((ROM = (uint8*)FCEU_malloc(rom_size_pow2)) == NULL)
|
||||
return 0;
|
||||
|
||||
memset(ROM, 0xFF, rom_size_pow2);
|
||||
FCEU_fread(ROM, 1, iNESCart.PRGRomSize, fp);
|
||||
|
||||
if (iNESCart.CHRRomSize)
|
||||
{
|
||||
if (iNESCart.CHRRomSize) {
|
||||
vrom_size_pow2 = uppow2(iNESCart.CHRRomSize);
|
||||
|
||||
if ((VROM = (uint8*)FCEU_malloc(vrom_size_pow2)) == NULL)
|
||||
|
||||
@@ -38,6 +38,7 @@ typedef struct {
|
||||
uint8 ExpDevice;
|
||||
} iNES_HEADER;
|
||||
|
||||
extern uint8 *trainerpoo;
|
||||
extern uint8 *ROM;
|
||||
extern uint8 *VROM;
|
||||
extern uint8 *MiscROM;
|
||||
@@ -55,15 +56,12 @@ void NSFVRC7_Init(void);
|
||||
void Mapper1_Init(CartInfo *);
|
||||
void Mapper4_Init(CartInfo *);
|
||||
void Mapper5_Init(CartInfo *);
|
||||
void Mapper6_Init(CartInfo *);
|
||||
void Mapper8_Init(CartInfo *);
|
||||
void Mapper9_Init(CartInfo *);
|
||||
void Mapper10_Init(CartInfo *);
|
||||
void Mapper11_Init(CartInfo *);
|
||||
void Mapper12_Init(CartInfo *);
|
||||
void Mapper15_Init(CartInfo *);
|
||||
void Mapper16_Init(CartInfo *);
|
||||
void Mapper17_Init(CartInfo *);
|
||||
void Mapper18_Init(CartInfo *);
|
||||
void Mapper19_Init(CartInfo *);
|
||||
void Mapper21_Init(CartInfo *);
|
||||
@@ -396,6 +394,9 @@ void Mapper555_Init(CartInfo *);
|
||||
void Mapper556_Init(CartInfo *);
|
||||
void Mapper558_Init(CartInfo *);
|
||||
void Mapper559_Init(CartInfo *);
|
||||
void Mapper561_562_Init(CartInfo *);
|
||||
void Mapper563_Init(CartInfo *);
|
||||
|
||||
void FFE_Init(CartInfo *);
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user