Pirate Kid Dracula reverse engineering

Discuss hardware-related topics, such as development cartridges, CopyNES, PowerPak, EPROMs, or whatever.

Moderator: Moderators

User avatar
NovaSquirrel
Posts: 483
Joined: Fri Feb 27, 2009 2:35 pm
Location: Fort Wayne, Indiana
Contact:

Re: Pirate Kid Dracula reverse engineering

Post by NovaSquirrel »

lidnariq wrote:This should be titled "How the MMC5's scanline timer works"
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.
User avatar
krzysiobal
Posts: 1037
Joined: Sun Jun 12, 2011 12:06 pm
Location: Poland
Contact:

Re: Pirate Kid Dracula reverse engineering

Post by krzysiobal »

I am in possesion of the famicom bootleg version:
Image Image Image Image

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)
Image Image Image Image Image Image
Last edited by krzysiobal on Sat Jul 13, 2019 3:07 am, edited 1 time in total.
User avatar
Fisher
Posts: 1249
Joined: Sat Jul 04, 2015 9:58 am
Location: -29.794229 -55.795374

Re: Pirate Kid Dracula reverse engineering

Post by Fisher »

krzysiobal wrote:I am in process of creating my own utility for dumping pals
That's great!!
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.
User avatar
krzysiobal
Posts: 1037
Joined: Sun Jun 12, 2011 12:06 pm
Location: Poland
Contact:

Re: Pirate Kid Dracula reverse engineering

Post by krzysiobal »

I performed deep analysis of the PAL from my Kid Dracula version (for Famicom)
Image

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)
User avatar
Fisher
Posts: 1249
Joined: Sat Jul 04, 2015 9:58 am
Location: -29.794229 -55.795374

Re: Pirate Kid Dracula reverse engineering

Post by Fisher »

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?
User avatar
krzysiobal
Posts: 1037
Joined: Sun Jun 12, 2011 12:06 pm
Location: Poland
Contact:

Re: Pirate Kid Dracula reverse engineering

Post by krzysiobal »

Do your ROMs have the same content as mine?
Yes
zxbdragon
Posts: 498
Joined: Mon Dec 12, 2011 8:15 pm

Re: Pirate Kid Dracula reverse engineering

Post by zxbdragon »

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?
no finish,Krzysiobal info,I try.
没有成功,Krzysiobal信息,我会尝试。
User avatar
krzysiobal
Posts: 1037
Joined: Sun Jun 12, 2011 12:06 pm
Location: Poland
Contact:

Re: Pirate Kid Dracula reverse engineering

Post by krzysiobal »

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

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);
}

Post Reply