Mappers 126/422/534: Update based on information from datasheet. Keep compatibility with existing dumps in wrong bank order, but do not support new dumps in wrong bank order.

This commit is contained in:
NewRisingSun
2023-05-28 10:40:43 +02:00
parent ad2c71d8b6
commit caa52ca3b9

View File

@@ -18,9 +18,19 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
/* Mapper 422: "Normal" version of the mapper. Represents UNIF boards BS-400R and BS-4040R. /* Multicarts based around the ING003C and TEC9719 enhanced MMC3 ASICs.
Mapper 126: Power Joy version of the mapper, connecting CHR A18 and A19 in reverse order. ING003C's scanline counter is inverted from MMC3, while TEC9719 is normal.
Mapper 534: Waixing version of the mapper, inverting the reload value of the MMC3 scanline counter. Both ASICs natively only support 256 KiB of CHR ROM/RAM; cartridges with more than that re-purpose higher PRG address as CHR address lines. There are two connection variants of this re-purposing.
Mapper 422: TEC9719, CHR A18=PRG A20, CHR A19=PRG A21
Mapper 126: TEC9719, CHR A18=PRG A21, CHR A19=PRG A20
Mapper 534: ING003C, CHR A18=PRG A20, CHR A19=PRG A21
Cartridges with more than one PRG ROM chip are sometimes not connected to form one continuous address space. Submappers are used to avoid having to repeat data to accomodate them.
Submapper 0: Normal connection
Submapper 1: PRG A21 (2 MiB bank) selects between two 1 MiB chips
Submapper 2: Register bit 6001.2 (undocumented in data sheet) selects between two 1 MiB chips
Both ASICs invert the register bit that selects PRG A21 (6000.5), hence "EXPREGS[0] ^0x20".
*/ */
#include "mapinc.h" #include "mapinc.h"
@@ -28,108 +38,180 @@
static uint8 reverseCHR_A18_A19; static uint8 reverseCHR_A18_A19;
static uint8 invertC000; static uint8 invertC000;
static uint8 dipSwitch; static uint8 SL0;
static uint8 submapper;
static uint8 getMMC3Bank(int bank) {
if (~bank &1 && MMC3_cmd &0x40) bank ^=2;
return bank &2? 0xFE | bank &1: DRegBuf[6 | bank &1];
}
static void wrapPRG(uint32 A, uint8 V) { static void wrapPRG(uint32 A, uint8 V) {
int prgAND = EXPREGS[0] &0x40? 0x0F: 0x1F; /* 128 KiB or 256 KiB inner PRG bank selection */ int prgAND = EXPREGS[0] &0x40? 0x0F: 0x1F; /* 128 KiB or 256 KiB inner PRG bank selection */
int prgOR =(EXPREGS[0] <<4 &0x70 | EXPREGS[0] <<3 &0x180) &~prgAND; /* outer PRG bank */ int prgOR =(EXPREGS[0] <<4 &0x70 | (EXPREGS[0] ^0x20) <<3 &0x180) &~prgAND; /* Outer PRG bank */
switch(EXPREGS[3] &3) { if (submapper ==1) prgOR |=prgOR >>1 &0x80;
case 0: /* MMC3 PRG mode */ if (submapper ==2) prgOR |=EXPREGS[1] <<5 &0x80;
break; for (A =0; A <4; A++) {
case 1: /* In UNROM-like mode (CT3=1, CT2=1, CT0=1), MMC3 sees A13=0 and A14=CPU A14 during reads, making register 6 apply from $8000-$BFFF, and the fixed bank from $C000-$FFFF.
case 2: /* NROM-128 mode: MMC3 register 6 applies throughout $8000-$FFFF, MMC3 A13 replaced with CPU A13. */ In NROM-128, NROM-256, ANROM and UNROM modes (CT0=1), MMC3 sees A13=0 and A14=0, making register 6 apply from $8000-$FFFF. */
V =DRegBuf[6] &~1 | A >>13 &1; V =getMMC3Bank(A &((EXPREGS[3] &0x0D) ==0x0D? 2: EXPREGS[3] &0x01? 0: 3));
setprg8(A ^0x4000, V &prgAND | prgOR); /* wrapPRG is only called with A containing the switchable banks, so we need to manually switch the normally fixed banks in this mode as well. */
break; /* UNROM and ANROM modes mean that MMC3 register 6 selects 16 and 32 KiB rather than 8 KiB banks. */
case 3: /* NROM-256 mode: MMC3 register 6 applies throughout $8000-$FFFF, MMC3 A13-14 replaced with CPU A13-14. */ if (EXPREGS[3] &0x08) switch(EXPREGS[3] &0x03) {
V =DRegBuf[6] &~3 | A >>13 &3; case 0: V =V &3 | V <<1 &~3; break; /* PRG A14 appears twice */
setprg8(A ^0x4000, (V ^2) &prgAND | prgOR); /* wrapPRG is only called with A containing the switchable banks, so we need to manually switch the normally fixed banks in this mode as well. */ case 1: V =A &3 | V <<1 &~1; break; /* 16 KiB mode, bit 0 OR'd with CPU A14 */
break; case 2: V =V &3 | V <<2 &~3; break; /* PRG A13 and PRG A14 appear twice */
case 3: V =A &3 | V <<2 &~3; break; /* 32 KiB mode */
} else
if (EXPREGS[3] &0x01) { /* regular NROM modes */
V =A &1 | V &~1;
if (EXPREGS[3] &0x02) V =A &2 | V &~2; /* NROM-256 */
}
setprg8(0x8000 +A*0x2000, V &prgAND | prgOR &~prgAND);
} }
setprg8(A, V &prgAND | prgOR); mwrap(A000B); /* After 8000 write */
} }
static void wrapCHR(uint32 A, uint8 V) { static void wrapCHR(uint32 A, uint8 V) {
int chrAND = EXPREGS[0] &0x80? 0x7F: 0xFF; /* 128 KiB or 256 KiB innter CHR bank selection */ int chrAND = EXPREGS[0] &0x80? 0x7F: 0xFF; /* 128 KiB or 256 KiB innter CHR bank selection */
int chrOR; /* outer CHR bank */ int chrOR; /* outer CHR bank */
if (reverseCHR_A18_A19) /* Mapper 126 swaps CHR A18 and A19 */ if (reverseCHR_A18_A19) /* Mapper 126 swaps CHR A18 and A19 */
chrOR =(EXPREGS[0] <<4 &0x080 | EXPREGS[0] <<3 &0x100 | EXPREGS[0] <<5 &0x200) &~chrAND; chrOR =(EXPREGS[0] <<4 &0x080 | (EXPREGS[0] ^0x20) <<3 &0x100 | EXPREGS[0] <<5 &0x200) &~chrAND;
else else
chrOR =EXPREGS[0] <<4 &0x380 &~chrAND; chrOR =(EXPREGS[0] ^0x20) <<4 &0x380 &~chrAND;
if (EXPREGS[3] &0x10) /* CNROM mode: 8 KiB inner CHR bank comes from outer bank register #2 */ if (EXPREGS[3] &0x10) /* CNROM mode: 8 KiB inner CHR bank comes from outer bank register #2 */
setchr8(EXPREGS[2] &(chrAND >>3) | chrOR >>3); setchr8(EXPREGS[2] &(chrAND >>3) | (chrOR &~chrAND) >>3);
else /* MMC3 CHR mode */ else /* MMC3 CHR mode */
setchr1(A, (V & chrAND) | chrOR); setchr1(A, (V & chrAND) | chrOR);
} }
static void wrapMirroring(uint8 V) {
A000B =V;
if (EXPREGS[3] &0x20) { /* ANROM mirroring */
if (DRegBuf[6] &0x10)
setmirror(MI_1);
else
setmirror(MI_0);
} else
if (EXPREGS[1] &0x02) { /* Extended MMC3 mirroring */
switch(A000B &3) {
case 0: setmirror(MI_V); break;
case 1: setmirror(MI_H); break;
case 2: setmirror(MI_0); break;
case 3: setmirror(MI_1); break;
}
} else /* Normal MMC3 mirroring */
setmirror(A000B &1? MI_H: MI_V);
}
static DECLFW(writeWRAM) { static DECLFW(writeWRAM) {
CartBW(A, V);
if ((A &3) ==2) { /* CNROM Bank (D0-D3), Bank Enable (D4-D6) and Bank Enable Lock (D7) */
int latchMask =0xFF &~(EXPREGS[2] &0x80? 0x70: 0x00) &~(EXPREGS [2] >>3 &0x0E);
EXPREGS[2] =EXPREGS[2] &~latchMask | V &latchMask;
FixMMC3CHR(MMC3_cmd);
} else
if (~EXPREGS[3] &0x80) { if (~EXPREGS[3] &0x80) {
/* Lock bit clear: Update any outer bank register */ /* Lock bit clear: Update any outer bank register */
EXPREGS[A &3] =V; EXPREGS[A &3] =V;
FixMMC3PRG(MMC3_cmd); FixMMC3PRG(MMC3_cmd);
FixMMC3CHR(MMC3_cmd); FixMMC3CHR(MMC3_cmd);
} else mwrap(A000B); /* After 6001 or 6003 swite */
if ((A &3) ==2) {
/* Lock bit set: Only update the bottom one or two bits of the CNROM bank */
int latchMask =EXPREGS[2] &0x10? 1: 3; /* 16 or 32 KiB inner CHR bank selection */
EXPREGS[2] &=~latchMask;
EXPREGS[2] |= V &latchMask;
FixMMC3CHR(MMC3_cmd);
} }
CartBW(A, V);
} }
static DECLFR(readDIP) { static DECLFW(writeCart) {
uint8 result =CartBR(A); if ((EXPREGS[3] &0x09) ==0x09) /* UNROM and ANROM modes treat all writes to $8000-$FFFF as if they were going to $8000-$9FFF */
if (EXPREGS[1] &1) result =result &~3 | dipSwitch &3; /* Replace bottom two bits with solder pad or DIP switch setting if so selected */ MMC3_CMDWrite(0x8000 | (EXPREGS[3] &0x08? 1: A) &1, V); /* A0 substitution only looks at bit 3 of register 3 */
return result; else
if (A >=0xC000)
MMC3_IRQWrite(A, V ^(invertC000? 0xFF: 0x00)); /* Mapper 534 inverts the MMC3 scanline counter reload value */
else
MMC3_CMDWrite(A, V);
} }
static DECLFW(writeIRQ) { static DECLFR(readPRG) {
MMC3_IRQWrite(A, V ^0xFF); if (EXPREGS[1] &1) A =A &~1 | SL0 &1; /* Replace A0 with SL0 input */
return CartBR(A);
} }
static void reset(void) { static void reset(void) {
dipSwitch++; /* Soft-resetting cycles through solder pad or DIP switch settings */ SL0++; /* Soft-resetting cycles through SL0 settings */
EXPREGS[0] = EXPREGS[1] = EXPREGS[2] = EXPREGS[3] = 0; EXPREGS[0] = EXPREGS[1] = EXPREGS[2] = EXPREGS[3] = 0;
MMC3RegReset(); MMC3RegReset();
} }
static void power(void) { static void power(void) {
dipSwitch =0; SL0 =0;
EXPREGS[0] = EXPREGS[1] = EXPREGS[2] = EXPREGS[3] = 0; EXPREGS[0] = EXPREGS[1] = EXPREGS[2] = EXPREGS[3] = 0;
GenMMC3Power(); GenMMC3Power();
SetWriteHandler(0x6000, 0x7FFF, writeWRAM); SetWriteHandler(0x6000, 0x7FFF, writeWRAM);
SetReadHandler(0x8000, 0xFFFF, readDIP); SetWriteHandler(0x8000, 0xFFFF, writeCart);
if (invertC000) SetWriteHandler(0xC000, 0xDFFF, writeIRQ); /* Mapper 534 inverts the MMC3 scanline counter reload value */ SetReadHandler(0x8000, 0xFFFF, readPRG);
} }
static void init(CartInfo *info) { static void init(CartInfo *info) {
GenMMC3_Init(info, 512, 256, 8, info->battery); GenMMC3_Init(info, 512, 256, 8, info->battery);
cwrap = wrapCHR; cwrap =wrapCHR;
pwrap = wrapPRG; pwrap =wrapPRG;
mwrap =wrapMirroring;
submapper =info->submapper;
info->Power = power; info->Power = power;
info->Reset = reset; info->Reset = reset;
AddExState(EXPREGS, 4, 0, "EXPR"); AddExState(EXPREGS, 4, 0, "EXPR");
AddExState(&dipSwitch, 1, 0, "DPSW"); AddExState(&SL0, 1, 0, "DPSW");
} }
void Mapper126_Init(CartInfo *info) { void Mapper126_Init(CartInfo *info) {
if (info->CRC32 ==0xEAD80031 || info->CRC32 ==0x6FCBC309) { /* Old dumps: Invert CHR A18 */
int i, a, b;
for (i =0; i <0x40000; i++) {
a =VROM[0x00000 +i]; b =VROM[0x40000 +i];
VROM[0x00000 +i] =b; VROM[0x40000 +i] =a;
a =VROM[0x80000 +i]; b =VROM[0xC0000 +i];
VROM[0x80000 +i] =b; VROM[0xC0000 +i] =a;
}
}
reverseCHR_A18_A19 = 1; reverseCHR_A18_A19 = 1;
invertC000 = 0; invertC000 = 0;
init(info); init(info);
} }
void Mapper422_Init(CartInfo *info) { void Mapper422_Init(CartInfo *info) {
if (info->CRC32 ==0x6D61FE21 || info->CRC32 ==0x3FF46175 || info->CRC32 ==0xA3FF9D9B || info->CRC32 ==0x2BDD0FC2 || info->CRC32 ==0x5789017D || info->CRC32 ==0x46A01871 || info->CRC32 ==0x2466B80A) {
/* Old dumps: Invert CHR A19 */
int i, a, b;
for (i =0; i <0x80000; i++) {
a =VROM[0x00000 +i]; b =VROM[0x80000 +i];
VROM[0x00000 +i] =b; VROM[0x80000 +i] =a;
}
}
reverseCHR_A18_A19 = 0; reverseCHR_A18_A19 = 0;
invertC000 = 0; invertC000 = 0;
init(info); init(info);
} }
void Mapper534_Init(CartInfo *info) { void Mapper534_Init(CartInfo *info) {
if (info->CRC32 ==0x871CFD16) {
/* Old dump: Invert PRG A20 */
int i, a, b;
for (i =0; i <0x100000; i++) {
a =ROM[0x000000 +i]; b =ROM[0x100000 +i];
ROM[0x000000 +i] =b; ROM[0x100000 +i] =a;
}
} else
if (info->CRC32 ==0xB2724618 || info->CRC32 ==0x42A9219D) {
/* Old dumps: Invert PRG A21 */
int i, a, b;
for (i =0; i <0x200000; i++) {
a =ROM[0x000000 +i]; b =ROM[0x200000 +i];
ROM[0x000000 +i] =b; ROM[0x200000 +i] =a;
}
}
reverseCHR_A18_A19 = 0; reverseCHR_A18_A19 = 0;
invertC000 = 1; invertC000 = 1;
init(info); init(info);