Konami VRC4 and the iNES Mess

Discuss emulation of the Nintendo Entertainment System and Famicom.
User avatar
MottZilla
Posts: 2837
Joined: Wed Dec 06, 2006 8:18 pm

Konami VRC4 and the iNES Mess

Post by MottZilla »

So I thought it would be nice to add Konami VRC4 support to my emulator. I understand the different address lines being used differently in different variants.

But what I don't understand is when variants share the same number, and how you are supposed to know which address lines to use. For example mapper 21:

VRC4A uses A1 & A2.
VRC4C uses A6 & A7.

How am I supposed to know where to have the register when they use the same mapper? Will the games work properly if I have both sets for mapper 21 as valid? No information on this was on the Wiki. The information that is there is incomplete.

Edit: So far I've just started a small Checksum database and entering games into it and it uses a default if it fails to find a match. But while doing this I noticed some strange things. Like Akumajou Special (Kid Dracula) writes to $8FFF and $AFFF. But the documents tell you only to do PRG regs at $8000 to $8006 and same for $A000. If I do the whole range then it breaks the other games, not to mention Dracula doesn't seem to run then anyway. So I had to make a special case. I can't believe have disorganized all of this is.
User avatar
Zepper
Formerly Fx3
Posts: 3262
Joined: Fri Nov 12, 2004 4:59 pm
Location: Brazil

Post by Zepper »

- With respect, the mapper docs (probably the same I'm using) are slightly inaccurate. Well, I don't mind - they are different mappers, even with similarities. Here's a scop of my mapper 23:

8000 or 8FFF - PRG bankswitch
9000 - mirroring
9008 - WRAM enable/disable (japanese docs cover this register)
A000 or AFFF - PRG bankswitch

- For CHR bankswitch or IRQ, take xxx0,xxx1,xxx2,xxx3 as mirrors of xxx0,xxx4,xxx8,xxxC.

- Be clear that I didn't make any recent deep analysis in the Konami mappers. Most of my "corrections" dates ~5 years ago.

- For mapper 21, yes, I couldn't rewrite it in order to match the docs, so I left my old stuff. For IRQ register writes:

Code: Select all

if(addr&0x0f) paddr=(addr&0xff00)|((addr>>1)&0x000f);
else if(addr&0xf0) paddr=(addr&0xff00)|((addr>>6)&0x000f);

switch(paddr&0xf003) 
Last edited by Zepper on Sun Aug 03, 2008 8:40 am, edited 1 time in total.
User avatar
Disch
Posts: 1848
Joined: Wed Nov 10, 2004 6:47 pm

Post by Disch »

One way I've seen it done (and the way I do it myself) is to just mask out the desired bits and OR them together.

For example for 021:

Code: Select all

void Write_Mapper021(u16 a, u8 v)
{
  a = (a & 0xF000) |
      (a & 0x0006) |
      ((a & 0x00C0) >> 5);

  Write_VRC4(a,v);
}
While this doesn't really provide correct mirroring, it works for all commercial games I've tried. (Getting correct mirroring in all cases in impossible without using a database of sorts -- the iNES number alone is not enough to know which address lines to use)
Like Akumajou Special (Kid Dracula) writes to $8FFF and $AFFF. But the documents tell you only to do PRG regs at $8000 to $8006
$8000 to $8006 and mirrors. Remember that unused address lines are ignored, so Akumajou Special writing to $8FFF is effectively writing to $8006.
User avatar
Zepper
Formerly Fx3
Posts: 3262
Joined: Fri Nov 12, 2004 4:59 pm
Location: Brazil

Post by Zepper »

I tried out a few months ago, but it didn't work here, unfortunately. :P
User avatar
Disch
Posts: 1848
Joined: Wed Nov 10, 2004 6:47 pm

Post by Disch »

Fx3 wrote:I tried out a few months ago, but it didn't work here, unfortunately. :P
I haven't had any problems doing it that way. What games were giving you trouble?
User avatar
Zepper
Formerly Fx3
Posts: 3262
Joined: Fri Nov 12, 2004 4:59 pm
Location: Brazil

Post by Zepper »

Some of Wai Wai World titles (map21). I tried to rewrite the mapper in order to match your doc, but it never worked. Well, perhaps my mistake regarding the address masking..?
User avatar
MottZilla
Posts: 2837
Joined: Wed Dec 06, 2006 8:18 pm

Post by MottZilla »

Oh so I should actually have masked for $8007, and then I suppose I could check if its $8007 instead of a number between $8000 and $8006. For some reason reading the docs made this all very unclear.

I'll try that out. But I see a checksum/hashing function is basically required to identify a database entry to ensure you use the proper lines due to iNES having not allocated each configuration a separate mapper. Does anyone have a complete list of Konami VRC games somewhere? I'm sure I don't know them all. I'd really like to know of a VRC4 game that uses the second PRG mode where $8000 and $E000 are locked but $A000 and $C000 are swappable.

As far as I've tested, every VRC4 game I've tried works fine. Including Wai Wai World as far as I could tell.

Edit: I changed this to this and everything seems to work.

Code: Select all

	if((Address&0xF007)>=0x8000 && (Address&0xF007)<=0x8007)
	{
		VRC4_PRGUpdate();
		return;
	}

	if((Address&0xF007)>=0xA000 && (Address&0xF007)<=0xA007)
	{
		VRC4_PRGUpdate();
		return;
	}
Last edited by MottZilla on Sun Aug 03, 2008 12:28 pm, edited 1 time in total.
User avatar
Disch
Posts: 1848
Joined: Wed Nov 10, 2004 6:47 pm

Post by Disch »

MottZilla wrote:Oh so I should actually have masked for $8007, and then I suppose I could check if its $8007 instead of a number between $8000 and $8006. For some reason reading the docs made this all very unclear.
Well.. you wouldn't mask with $F007... rather, you'd mask with the address lines the mapper uses. So for example if the game uses A4 and A5, you'd mask with $F030.

I probably could've explained it better in my doc. But it sounds like you've got it working now anyway.
User avatar
MottZilla
Posts: 2837
Joined: Wed Dec 06, 2006 8:18 pm

Post by MottZilla »

I believe this is a complete list of VRC games.

VRC1:
Ganbare Goemon! - Karakuri Douchuu
King Kong 2 - Ikari no Megaton Punch
Tetsuwan Atom

VRC3:
Salamander

VRC4 and VRC2:
Akumajou Special - Boku Dracula Kun
Bio Miracle Bokutte Upa
Crisis Force
Ganbare Goemon 2
Ganbare Goemon Gaiden 2 - Tenka no Zaihou
Ganbare Goemon Gaiden - Kieta Ougon Kiseru
Ganbare Pennant Race!
Getsufuu Maden
Gradius 2
Gryzor
Jarinko Chie - Bakudan Musume no Shiawase Sagashi
Parodius da!
Racer Mini Yonku - Japan Cup
Teenage Mutant Ninja Turtles 2 (J)
Teenage Mutant Ninja Turtles (J)
Tiny Toon Adventures (J)
TwinBee 3 - Poko Poko Dai Maou
Wai Wai World 2 - SOS!! Paseri Jou
Wai Wai World

VRC6:
Akumajou Densetsu
Esper Dream 2 - Aratanaru Tatakai
Mouryou Senki Madara

VRC7:
Lagrange Point
Tiny Toon Adventures 2 - Montana Land he Youkoso (J)

If any games are missing let me know. Otherwise I consider this list complete.
Last edited by MottZilla on Sun Aug 03, 2008 3:15 pm, edited 1 time in total.
User avatar
Disch
Posts: 1848
Joined: Wed Nov 10, 2004 6:47 pm

Post by Disch »

Salamander is VRC3 (073)

I also have a few more 075 games as well -- but I don't know if they're VRC1 or a similar mapper.
User avatar
MottZilla
Posts: 2837
Joined: Wed Dec 06, 2006 8:18 pm

Post by MottZilla »

Ah yes I forgot to list Salamander, but I do have that in my folder. I don't believe any of the other mapper 075 games are made by Konami, which makes me doubt they would be VRC1.
User avatar
MottZilla
Posts: 2837
Joined: Wed Dec 06, 2006 8:18 pm

Post by MottZilla »

Disch, would this be more accurate?

Code: Select all

	int Mask = 0xF000;
	Mask = Mask | VRC4.LA;
	Mask = Mask | VRC4.LB;
...
	if( (Address&Mask) >= 0x8000 && (Address&Mask) <= 0x8FFF )
	{
		VRC4.PRG0 = (Byte&0x1F) & ((PRG_Pages*2)-1);
		VRC4_PRGUpdate();
		return;
	}
I tried this, and it works for all the games I've tried. Well except the Kid Dracula translation.
User avatar
Disch
Posts: 1848
Joined: Wed Nov 10, 2004 6:47 pm

Post by Disch »

That should equate to the same thing. I just prefer switch statements, myself, rather than range checks. But yeah that should work fine.

How I go about it:

021

Code: Select all

	void Write(u16 a,u8 v,int cu)
	{
		a |= (a>>5) & 6;
		NESMapperVRC4::Write(a,v,cu);
	}
025

Code: Select all

	void Write(u16 a,u8 v,int cu)
	{
		a = (a & 0xF000) | ((a>>2) & 3) | (a & 3);
		if(a & 1)	a += 4 - 1;		// move bit 0 to bit 2

		NESMapperVRC4::Write(a,v,cu);
	}

Code: Select all

void NESMapperVRC4::Write(u16 a,u8 v,int cu)
{
	u8 v5 = v & 0x1F;
	v &= 0x0F;

	switch(a & 0xF006)
	{
	case 0x8000: case 0x8002: case 0x8004: case 0x8006:
		nPRG[0] = v5;		SyncPRG(cu);		break;
...
As you might be able to see... with all the masking, the PRG reg is spread across all of $8xxx.
Near
Founder of higan project
Posts: 1553
Joined: Mon Mar 27, 2006 5:23 pm

Post by Near »

I just prefer switch statements, myself, rather than range checks.
Range checks? Pssh, all the cool kids use bitmasks :P

if(addr >= 0x8000 && addr <= 0x8fff) == if((addr & 0xf000) == 0x8000)
if(addr >= 0x4200 && addr <= 0x421f) == if((addr & 0xffe0) == 0x4200)

In your specific case, you have a mask of either 0xf006 or 0xf0c0. Yet the lower-byte masking makes no difference since those particular bits can be anything and your test will still pass.

It would seem as though you could simply reduce it to: if((Address&0xf000) == 0x8000), and get the same result. Perhaps I'm missing something in the elipses. No big deal, just a quick observation.
User avatar
Zepper
Formerly Fx3
Posts: 3262
Joined: Fri Nov 12, 2004 4:59 pm
Location: Brazil

Post by Zepper »

byuu wrote:It would seem as though you could simply reduce it to: if((Address&0xf000) == 0x8000), and get the same result. Perhaps I'm missing something in the elipses. No big deal, just a quick observation.
It would be fine except that IRQs use 3 registers with different behaviours. Yaya, if addr AND F000h == F000h, "another" statement could start, but it's dumb. :P