Wasn't it confirmed in this thread that the MMC5 watches for multiple reads from the same address in a row? That's way different from this description.lidnariq wrote:This should be titled "How the MMC5's scanline timer works"
Pirate Kid Dracula reverse engineering
Moderator: Moderators
- NovaSquirrel
- Posts: 483
- Joined: Fri Feb 27, 2009 2:35 pm
- Location: Fort Wayne, Indiana
- Contact:
Re: Pirate Kid Dracula reverse engineering
- krzysiobal
- Posts: 1037
- Joined: Sun Jun 12, 2011 12:06 pm
- Location: Poland
- Contact:
Re: Pirate Kid Dracula reverse engineering
I am in possesion of the famicom bootleg version:
PAL must be quite different at least in pinout. I am in process of creating my own utility for dumping pals (it will autodetect input/outputs, if pal is combinatory or not, all inputs will be fed not in natural binary code but using gray code to minimize hazards and there will be option for manually toggling pins in order to discover new features)
PAL must be quite different at least in pinout. I am in process of creating my own utility for dumping pals (it will autodetect input/outputs, if pal is combinatory or not, all inputs will be fed not in natural binary code but using gray code to minimize hazards and there will be option for manually toggling pins in order to discover new features)
Last edited by krzysiobal on Sat Jul 13, 2019 3:07 am, edited 1 time in total.
Re: Pirate Kid Dracula reverse engineering
That's great!!krzysiobal wrote:I am in process of creating my own utility for dumping pals
Would that be able to dump the N64 GameShark GAL? It's quite different from normal PAL ICs...
Maybe the SNES GameGenie could, since it seems more standard.
- krzysiobal
- Posts: 1037
- Joined: Sun Jun 12, 2011 12:06 pm
- Location: Poland
- Contact:
Re: Pirate Kid Dracula reverse engineering
I performed deep analysis of the PAL from my Kid Dracula version (for Famicom)
Generally it agrees with what lidnariq said except one additional discovered register:
$800c - writing any value forces interrupt to be triggered
Here are equations for the chip:
Generally it agrees with what lidnariq said except one additional discovered register:
$800c - writing any value forces interrupt to be triggered
Here are equations for the chip:
Code: Select all
O6 <= D0 when CPU-!ROMSEL=0 and R/!W=0 and A14=0 and A3=0 and A2=1; --latch
O5 <= D1 when CPU-!ROMSEL=0 and R/!W=0 and A14=0 and A3=0 and A2=1; --latch
02 <= 0 when CPU-!ROMSEL=0 and R/!W=0 and A14=1 and A3=1 and A2=1 else --latch
1 when CPU-!ROMSEL=0 and R/!W=0 and A14=1 and A3=1 and A2=0;
!IRQ! <= 1 when CPU-!ROMSEL=0 and R/!W=0 and A14=1 and A3=0 and A2=1 else --latch
1 when CPU-!ROMSEL=0 and R/!W=0 and A14=1 and A3=1 and A2=1 else
0 when (CPU-!ROMSEL!=1 or R/!W=1) and IRQ_PEN = 1 else
0 when CPU-!ROMSEL=0 and R/!W=0 and A14=0 and A3=1 and A2=1
CLR <= not O2 or IRQ_PEN
CIRAM <= 0 when O6=0 and O5=1 else
1 when 06=1 and 05=1 else
VRC-CIRAM
a a a dddd dddd
1 3 2 7654 3210
4
-----------------
$8004 0 0 1 [.... ..mm] mm - mirroring (source of ciram) (0/1: VRC-CIRAM, 2:GND, 3: VCC)
$800c 0 1 1 [.... ....] writing any value forces interrupt to be triggered
$c004 1 0 1 [.... ....] writing any value acknowledges pending interrupt
$c008 1 1 0 [.... ....] writing any value resets counter from clear
$c00c 1 1 1 [.... ....] writing any value holds counter in clear & acknowledges pending interrupt
* counter is automatically cleared when interrupt becomes pending
* interrupt is triggered when IRQ_PEN=1 and (CPU-!ROMSEL! = 1 or R/!W=1)
Re: Pirate Kid Dracula reverse engineering
Great findings Krzysiobal!
Do your ROMs have the same content as mine?
CRC32
PRG: 6a50b553
CHR: c5d1196e (same as original)
Maybe this was the part of the puzzle that ZxbDragon was needing to finish it's implementation?
Do your ROMs have the same content as mine?
CRC32
PRG: 6a50b553
CHR: c5d1196e (same as original)
Maybe this was the part of the puzzle that ZxbDragon was needing to finish it's implementation?
- krzysiobal
- Posts: 1037
- Joined: Sun Jun 12, 2011 12:06 pm
- Location: Poland
- Contact:
Re: Pirate Kid Dracula reverse engineering
YesDo your ROMs have the same content as mine?
Re: Pirate Kid Dracula reverse engineering
no finish,Krzysiobal info,I try.Fisher wrote:Great findings Krzysiobal!
Do your ROMs have the same content as mine?
CRC32
PRG: 6a50b553
CHR: c5d1196e (same as original)
Maybe this was the part of the puzzle that ZxbDragon was needing to finish it's implementation?
没有成功,Krzysiobal信息,我会尝试。
- krzysiobal
- Posts: 1037
- Joined: Sun Jun 12, 2011 12:06 pm
- Location: Poland
- Contact:
Re: Pirate Kid Dracula reverse engineering
Of course FCEU has ability to track PPU bus, otherwise mappers like MMC2 would not be implemented.
I did my own mapper implementation for Pirate Kid Dracula and it works fine (just there is one quirk - when implementing counter at 86, status bar is switched too late, implementing at 84 seems to be most accurate (should be 83.5 because it is fired in middle of scanline, while in real hardware - almost at end)
Maybe you do it wrong because u assume that when counter reaches 86, it's clocking is stopped?
Analyzing the PAL - reaching 86 makes IRQ_PEN = 1 which makes
CLR <= not O2 or IRQ_PEN
equal to 1 and thus resets counter, but if counter is now reset, AND gate returns 0 (so IRQ_PEN=0) and then CLR=0 so counter is no longer being cleared, so it starts counting from beginning.
https://youtu.be/5hQucuRMnRQ
I did my own mapper implementation for Pirate Kid Dracula and it works fine (just there is one quirk - when implementing counter at 86, status bar is switched too late, implementing at 84 seems to be most accurate (should be 83.5 because it is fired in middle of scanline, while in real hardware - almost at end)
Maybe you do it wrong because u assume that when counter reaches 86, it's clocking is stopped?
Analyzing the PAL - reaching 86 makes IRQ_PEN = 1 which makes
CLR <= not O2 or IRQ_PEN
equal to 1 and thus resets counter, but if counter is now reset, AND gate returns 0 (so IRQ_PEN=0) and then CLR=0 so counter is no longer being cleared, so it starts counting from beginning.
https://youtu.be/5hQucuRMnRQ
Code: Select all
#include "mapinc.h"
static int prg[2];
static int mirr;
static int chr[8];
static int last_pa13;
static int counter;
static int pal_mirr;
static int counter_canclock;
static SFORMAT StateRegs[] =
{
{ 0 }
};
//shifts bit from position `bit` into position `pos` of expression `exp`
#define shi(exp, bit, pos) \
((((exp) & (1 << (bit))) >> (bit)) << (pos))
static void M272Hook(uint32 A) {
int pa13 = (A >> 13) & 1;
if ((last_pa13 == 1) && (pa13 == 0)) {
if (counter_canclock) {
counter++;
if (counter == 84) {
counter = 0;
X6502_IRQBegin(FCEU_IQEXT);
}
}
}
last_pa13 = pa13;
}
static int vrc_addr_mix(int A) {
//this game wires A0 to VRC_A0 and A1 to VRC_A1
return (A & 0xf000) | shi(A, 0, 0) | shi(A, 1, 1);
}
static void Sync(void) {
setprg8(0x8000, prg[0]);
setprg8(0xa000, prg[1]);
setprg16(0xc000, -1);
for (int i = 0; i < 8; ++i) {
setchr1(0x400 * i, chr[i]);
}
switch (pal_mirr) {
case 2: setmirror(MI_0); break;
case 3: setmirror(MI_1); break;
default:
switch (mirr) {
case 0: setmirror(MI_V); break;
case 1: setmirror(MI_H); break;
}
}
}
static DECLFW(M272Write) {
//writes to VRC chip
switch (vrc_addr_mix(A)) {
case 0x8000:
case 0x8001:
case 0x8002:
case 0x8003:
prg[0] = V;
break;
case 0xA000:
case 0xA001:
case 0xA002:
case 0xA003:
prg[1] = V;
break;
case 0x9000:
case 0x9001:
case 0x9002:
case 0x9003:
mirr = V & 1;
break;
case 0xb000: chr[0] = (chr[0] & 0xF0) | (V & 0xF); break;
case 0xb001: chr[0] = (chr[0] & 0xF) | ((V & 0xF) << 4); break;
case 0xb002: chr[1] = (chr[1] & 0xF0) | (V & 0xF); break;
case 0xb003: chr[1] = (chr[1] & 0xF) | ((V & 0xF) << 4); break;
case 0xc000: chr[2] = (chr[2] & 0xF0) | (V & 0xF); break;
case 0xc001: chr[2] = (chr[2] & 0xF) | ((V & 0xF) << 4); break;
case 0xc002: chr[3] = (chr[3] & 0xF0) | (V & 0xF); break;
case 0xc003: chr[3] = (chr[3] & 0xF) | ((V & 0xF) << 4); break;
case 0xd000: chr[4] = (chr[4] & 0xF0) | (V & 0xF); break;
case 0xd001: chr[4] = (chr[4] & 0xF) | ((V & 0xF) << 4); break;
case 0xd002: chr[5] = (chr[5] & 0xF0) | (V & 0xF); break;
case 0xd003: chr[5] = (chr[5] & 0xF) | ((V & 0xF) << 4); break;
case 0xe000: chr[6] = (chr[6] & 0xF0) | (V & 0xF); break;
case 0xe001: chr[6] = (chr[6] & 0xF) | ((V & 0xF) << 4); break;
case 0xe002: chr[7] = (chr[7] & 0xF0) | (V & 0xF); break;
case 0xe003: chr[7] = (chr[7] & 0xF) | ((V & 0xF) << 4); break;
default:
break;
}
//writes to PAL chip
switch (A & 0xC00C) {
case 0x8004: pal_mirr = V & 3; break;
case 0x800c: X6502_IRQBegin(FCEU_IQEXT); break;
case 0xc004: X6502_IRQEnd(FCEU_IQEXT); break;
case 0xc008: counter_canclock = 1; break;
case 0xc00c: counter_canclock = 0; counter = 0; X6502_IRQEnd(FCEU_IQEXT); break;
}
Sync();
}
static void M272Power(void) {
SetWriteHandler(0x8000, 0xFFFF, M272Write);
SetReadHandler(0x8000, 0xFFFF, CartBR);
Sync();
last_pa13 = 0;
counter = 0;
}
static void M272Reset(void) {
last_pa13 = 0;
counter = 0;
Sync();
}
void Mapper272_Init(CartInfo *info) {
info->Power = M272Power;
info->Reset = M272Reset;
PPU_hook = M272Hook;
AddExState(&StateRegs, ~0, 0, 0);
}