406 lines
9.7 KiB
C
406 lines
9.7 KiB
C
/* FCEUmm - NES/Famicom Emulator
|
|
*
|
|
* Copyright notice for this file:
|
|
* Copyright (C) 2012 CaH4e3
|
|
* Copyright (C) 2019 Libretro Team
|
|
* Copyright (C) 2020
|
|
*
|
|
* 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
|
|
*
|
|
* TXC/Micro Genius simplified mapper
|
|
* updated 06-2019 http://wiki.nesdev.com/w/index.php/INES_Mapper_036
|
|
*
|
|
* Known games:
|
|
* - Strike Wolf (Asia) (Unl)
|
|
* - Policeman (Gluk Video) (unl)
|
|
* - F-15 City War (Spain) (Gluk Video) (Unl)
|
|
*
|
|
* TXC mappers, originally much complex banksitching
|
|
*
|
|
* 01-22111-000 (05-00002-010) (132, 22211) - MGC-001 Qi Wang
|
|
* 01-22110-000 (52S ) - MGC-002 2-in-1 Gun
|
|
* 01-22111-100 (02-00002-010) (173 ) - MGC-008 Mahjong Block
|
|
* (079 ) - MGC-012 Poke Block
|
|
* 01-22110-200 (05-00002-010) (036 ) - MGC-014 Strike Wolf
|
|
* 01-22000-400 (05-00002-010) (036 ) - MGC-015 Policeman
|
|
* 01-22017-000 (05-PT017-080) (189 ) - MGC-017 Thunder Warrior
|
|
* 01-11160-000 (04-02310-000) ( , 11160) - MGC-023 6-in-1
|
|
* 01-22270-000 (05-00002-010) (132, 22211) - MGC-xxx Creatom
|
|
* 01-22200-400 (------------) (079 ) - ET.03 F-15 City War
|
|
* (172 ) - 1991 Du Ma Racing
|
|
*
|
|
*/
|
|
|
|
/* added 2020-2-16
|
|
* Updated based on latest source
|
|
* Mappers 36, 132, 173
|
|
* Mappers 136, 147, 172
|
|
*/
|
|
|
|
#include "mapinc.h"
|
|
|
|
typedef struct {
|
|
uint8 mask;
|
|
uint8 isJV001;
|
|
uint8 accumulator;
|
|
uint8 inverter;
|
|
uint8 staging;
|
|
uint8 output;
|
|
uint8 increase;
|
|
uint8 Y;
|
|
uint8 invert;
|
|
} TXC;
|
|
|
|
static TXC txc;
|
|
|
|
static void Dummyfunc(void) { }
|
|
static void (*WSync)(void) = Dummyfunc;
|
|
|
|
static SFORMAT StateRegs[] =
|
|
{
|
|
{ &txc.accumulator, 1, "ACC0" },
|
|
{ &txc.inverter, 1, "INVR" },
|
|
{ &txc.staging, 1, "STG0" },
|
|
{ &txc.output, 1, "OUT0" },
|
|
{ &txc.increase, 1, "INC0" },
|
|
{ &txc.Y, 1, "YFLG" },
|
|
{ &txc.invert, 1, "INVT" },
|
|
{ 0 }
|
|
};
|
|
|
|
static uint8 TXC_CMDRead(void) {
|
|
uint8 ret = ((txc.accumulator & txc.mask) | ((txc.inverter ^ txc.invert) & ~txc.mask));
|
|
txc.Y = !txc.invert || ((ret & 0x10) != 0);
|
|
WSync();
|
|
return ret;
|
|
}
|
|
|
|
static DECLFW(TXC_CMDWrite) {
|
|
if (A & 0x8000) {
|
|
if (txc.isJV001)
|
|
txc.output = (txc.accumulator & 0x0F) | (txc.inverter & 0xF0);
|
|
else
|
|
txc.output = (txc.accumulator & 0x0F) | ((txc.inverter << 1) & 0x10);
|
|
} else {
|
|
switch (A & 0x103) {
|
|
case 0x100:
|
|
if (txc.increase)
|
|
txc.accumulator++;
|
|
else
|
|
txc.accumulator = ((txc.accumulator & ~txc.mask) | ((txc.staging ^ txc.invert) & txc.mask));
|
|
break;
|
|
case 0x101:
|
|
txc.invert = (V & 0x01) ? 0xFF : 0x00;
|
|
break;
|
|
case 0x102:
|
|
txc.staging = V & txc.mask;
|
|
txc.inverter = V & ~txc.mask;
|
|
break;
|
|
case 0x103:
|
|
txc.increase = ((V & 0x01) != 0);
|
|
break;
|
|
}
|
|
}
|
|
txc.Y = !txc.invert || ((V & 0x10) != 0);
|
|
WSync();
|
|
}
|
|
|
|
static void TXCRegReset(void) {
|
|
txc.output = 0;
|
|
txc.accumulator = 0;
|
|
txc.inverter = 0;
|
|
txc.staging = 0;
|
|
txc.increase = 0;
|
|
txc.Y = 0;
|
|
txc.mask = txc.isJV001 ? 0x0F : 0x07;
|
|
txc.invert = txc.isJV001 ? 0xFF : 0x00;
|
|
|
|
WSync();
|
|
}
|
|
|
|
static void GenTXCPower(void) {
|
|
TXCRegReset();
|
|
}
|
|
|
|
static void StateRestore(int version) {
|
|
WSync();
|
|
}
|
|
|
|
static void GenTXC_Init(CartInfo *info, void (*proc)(void), uint32 jv001) {
|
|
txc.isJV001 = jv001;
|
|
WSync = proc;
|
|
GameStateRestore = StateRestore;
|
|
AddExState(StateRegs, ~0, 0, 0);
|
|
}
|
|
|
|
static int CheckHash(CartInfo *info) {
|
|
int x = 0;
|
|
uint64 partialmd5 = 0;
|
|
|
|
/* These carts do not work with new mapper implementation.
|
|
* This is a hack to use previous mapper implementation for such carts. */
|
|
for (x = 0; x < 8; x++)
|
|
partialmd5 |= (uint64)info->MD5[15 - x] << (x * 8);
|
|
switch (partialmd5) {
|
|
case 0x2dd8f958850f21f4LL: /* Jin Gwok Sei Chuen Saang (Ch) [U][!] */
|
|
FCEU_printf(" WARNING: Using alternate mapper implementation.\n");
|
|
UNL22211_Init(info);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* --------------- Mapper 36 --------------- */
|
|
|
|
static uint8 creg = 0;
|
|
|
|
static void M36Sync(void) {
|
|
setprg32(0x8000, txc.output & 0x03);
|
|
setchr8(creg & 0x0F);
|
|
}
|
|
|
|
static DECLFW(M36Write) {
|
|
if ((A & 0xF200) == 0x4200) creg = V;
|
|
TXC_CMDWrite(A, (V >> 4) & 0x03);
|
|
}
|
|
|
|
static DECLFR(M36Read) {
|
|
uint8 ret = X.DB;
|
|
if ((A & 0x103) == 0x100)
|
|
ret = (X.DB & 0xCF) | ((TXC_CMDRead() << 4) & 0x30);
|
|
return ret;
|
|
}
|
|
|
|
static void M36Power(void) {
|
|
creg = 0;
|
|
GenTXCPower();
|
|
SetReadHandler(0x8000, 0xFFFF, CartBR);
|
|
SetReadHandler(0x4100, 0x5FFF, M36Read);
|
|
SetWriteHandler(0x4100, 0xFFFF, M36Write);
|
|
}
|
|
|
|
void Mapper36_Init(CartInfo *info) {
|
|
GenTXC_Init(info, M36Sync, 0);
|
|
info->Power = M36Power;
|
|
AddExState(&creg, 1, 0, "CREG");
|
|
}
|
|
|
|
/* --------------- Mapper 132 --------------- */
|
|
|
|
static void M132Sync(void) {
|
|
setprg32(0x8000, (txc.output >> 2) & 0x01);
|
|
setchr8(txc.output & 0x03);
|
|
}
|
|
|
|
static DECLFW(M132Write) {
|
|
TXC_CMDWrite(A, V & 0x0F);
|
|
}
|
|
|
|
static DECLFR(M132Read) {
|
|
uint8 ret = X.DB;
|
|
if ((A & 0x103) == 0x100)
|
|
ret = ((X.DB & 0xF0) | (TXC_CMDRead() & 0x0F));
|
|
return ret;
|
|
}
|
|
|
|
static void M132Power(void) {
|
|
GenTXCPower();
|
|
SetReadHandler(0x8000, 0xFFFF, CartBR);
|
|
SetReadHandler(0x4100, 0x5FFF, M132Read);
|
|
SetWriteHandler(0x4100, 0xFFFF, M132Write);
|
|
}
|
|
|
|
void Mapper132_Init(CartInfo *info) {
|
|
if (CheckHash(info) != 0) return;
|
|
GenTXC_Init(info, M132Sync, 0);
|
|
info->Power = M132Power;
|
|
}
|
|
|
|
/* --------------- Mapper 173 --------------- */
|
|
|
|
static void M173Sync(void) {
|
|
setprg32(0x8000, 0);
|
|
if (CHRsize[0] > 0x2000)
|
|
setchr8(((txc.output & 0x01) | (txc.Y ? 0x02 : 0x00) | ((txc.output & 2) << 0x01)));
|
|
else
|
|
setchr8(0);
|
|
}
|
|
|
|
void Mapper173_Init(CartInfo *info) {
|
|
GenTXC_Init(info, M173Sync, 0);
|
|
info->Power = M132Power;
|
|
}
|
|
|
|
/* ---------------- Joy/Van ----------------- */
|
|
|
|
/* --------------- Mapper 136 --------------- */
|
|
|
|
static void M136Sync(void) {
|
|
setprg32(0x8000, (txc.output >> 4) & 0x01);
|
|
setchr8(txc.output & 0x07);
|
|
}
|
|
|
|
static DECLFW(M136Write) {
|
|
TXC_CMDWrite(A, V & 0x3F);
|
|
}
|
|
|
|
static DECLFR(M136Read) {
|
|
uint8 ret = X.DB;
|
|
if ((A & 0x103) == 0x100)
|
|
ret = ((X.DB & 0xC0) | (TXC_CMDRead() & 0x3F));
|
|
return ret;
|
|
}
|
|
|
|
static void M136Power(void) {
|
|
GenTXCPower();
|
|
SetReadHandler(0x8000, 0xFFFF, CartBR);
|
|
SetReadHandler(0x4100, 0x5FFF, M136Read);
|
|
SetWriteHandler(0x4100, 0xFFFF, M136Write);
|
|
}
|
|
|
|
void Mapper136_Init(CartInfo *info) {
|
|
GenTXC_Init(info, M136Sync, 1);
|
|
info->Power = M136Power;
|
|
}
|
|
|
|
/* --------------- Mapper 147 --------------- */
|
|
|
|
static void M147Sync(void) {
|
|
setprg32(0x8000, ((txc.output >> 4) & 0x02) | (txc.output & 0x01));
|
|
setchr8((txc.output >> 1) & 0x0F);
|
|
}
|
|
|
|
static DECLFW(M147Write) {
|
|
TXC_CMDWrite(A, ((V >> 2) & 0x3F) | ((V << 6) & 0xC0));
|
|
}
|
|
|
|
static DECLFR(M147Read) {
|
|
uint8 ret = X.DB;
|
|
if ((A & 0x103) == 0x100) {
|
|
uint8 value = TXC_CMDRead();
|
|
ret = ((value << 2) & 0xFC) | ((value >> 6) & 0x03);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static void M147Power(void) {
|
|
GenTXCPower();
|
|
SetReadHandler(0x8000, 0xFFFF, CartBR);
|
|
SetReadHandler(0x4100, 0x5FFF, M147Read);
|
|
SetWriteHandler(0x4100, 0xFFFF, M147Write);
|
|
}
|
|
|
|
void Mapper147_Init(CartInfo *info) {
|
|
GenTXC_Init(info, M147Sync, 1);
|
|
info->Power = M147Power;
|
|
}
|
|
|
|
/* --------------- Mapper 172 --------------- */
|
|
|
|
static void M172Sync(void) {
|
|
setprg32(0x8000, 0);
|
|
setchr8(txc.output);
|
|
setmirror(txc.invert ? 1 : 0);
|
|
}
|
|
|
|
static uint8 GetValue(uint8 value) {
|
|
return (((value << 5) & 0x20) | ((value << 3) & 0x10) | ((value << 1) & 0x08) |
|
|
((value >> 1) & 0x04) | ((value >> 3) & 0x02) | ((value >> 5) & 0x01));
|
|
}
|
|
|
|
static DECLFW(M172Write) {
|
|
TXC_CMDWrite(A, GetValue(V));
|
|
}
|
|
|
|
static DECLFR(M172Read) {
|
|
uint8 ret = X.DB;
|
|
if ((A & 0x103) == 0x100)
|
|
ret = (X.DB & 0xC0) | GetValue(TXC_CMDRead());
|
|
return ret;
|
|
}
|
|
|
|
static void M172Power(void) {
|
|
GenTXCPower();
|
|
SetReadHandler(0x8000, 0xFFFF, CartBR);
|
|
SetReadHandler(0x4100, 0x5FFF, M172Read);
|
|
SetWriteHandler(0x4100, 0xFFFF, M172Write);
|
|
}
|
|
|
|
void Mapper172_Init(CartInfo *info) {
|
|
GenTXC_Init(info, M172Sync, 1);
|
|
info->Power = M172Power;
|
|
}
|
|
|
|
/* === LEGACY MAPPER IMPLEMENTATION === */
|
|
|
|
static uint8 reg[4], cmd, is172, is173;
|
|
|
|
static SFORMAT UNL22211StateRegs[] =
|
|
{
|
|
{ reg, 4, "REGS" },
|
|
{ &cmd, 1, "CMD" },
|
|
{ 0 }
|
|
};
|
|
|
|
static void UNL22211Sync(void) {
|
|
setprg32(0x8000, (reg[2] >> 2) & 1);
|
|
if (is172)
|
|
setchr8((((cmd ^ reg[2]) >> 3) & 2) | (((cmd ^ reg[2]) >> 5) & 1)); /* 1991 DU MA Racing probably CHR bank sequence is WRONG, so it is possible to
|
|
* rearrange CHR banks for normal UNIF board and mapper 172 is unneccessary */
|
|
else
|
|
setchr8(reg[2] & 3);
|
|
}
|
|
|
|
static DECLFW(UNL22211WriteLo) {
|
|
/* FCEU_printf("bs %04x %02x\n",A,V); */
|
|
reg[A & 3] = V;
|
|
}
|
|
|
|
static DECLFW(UNL22211WriteHi) {
|
|
/* FCEU_printf("bs %04x %02x\n",A,V); */
|
|
cmd = V;
|
|
UNL22211Sync();
|
|
}
|
|
|
|
static DECLFR(UNL22211ReadLo) {
|
|
return (reg[1] ^ reg[2]) | (is173 ? 0x01 : 0x40);
|
|
#if 0
|
|
if(reg[3])
|
|
return reg[2];
|
|
else
|
|
return X.DB;
|
|
#endif
|
|
}
|
|
|
|
static void UNL22211Power(void) {
|
|
UNL22211Sync();
|
|
SetReadHandler(0x8000, 0xFFFF, CartBR);
|
|
SetReadHandler(0x4100, 0x4100, UNL22211ReadLo);
|
|
SetWriteHandler(0x4100, 0x4103, UNL22211WriteLo);
|
|
SetWriteHandler(0x8000, 0xFFFF, UNL22211WriteHi);
|
|
}
|
|
|
|
static void UNL22211StateRestore(int version) {
|
|
UNL22211Sync();
|
|
}
|
|
|
|
void UNL22211_Init(CartInfo *info) {
|
|
is172 = 0;
|
|
is173 = 0;
|
|
info->Power = UNL22211Power;
|
|
GameStateRestore = UNL22211StateRestore;
|
|
AddExState(&UNL22211StateRegs, ~0, 0, 0);
|
|
}
|