Mapper 340

Discuss emulation of the Nintendo Entertainment System and Famicom.

Moderator: Moderators

Post Reply
User avatar
zeroone
Posts: 932
Joined: Mon Dec 29, 2014 1:46 pm
Location: New York, NY
Contact:

Mapper 340

Post by zeroone » Sat Apr 06, 2019 11:57 am

Mapper 340 appears to be a very simple multi-cart mapper. The implementation in NintendulatorNRS matches the description in the wiki. And I attempted a similar implementation in my emulator. However, I found several of the games do not function in my implementation. Contra, Life Force, Rushn Attack and Galaxian all work fine. But the remainder has garbled graphics (wrong tiles). Some of those are playable and some of them crash on start up (KIL instructions encountered).

I checked over my implementation several times and I can't locate a bug. Interestingly, I noticed that the initial RAM state affects which games work. What does NintendulatorNRS set RAM to on startup? Maybe it's expecting some initial value set.

NewRisingSun
Posts: 1199
Joined: Thu May 19, 2005 11:30 am

Re: Mapper 340

Post by NewRisingSun » Sat Apr 06, 2019 12:09 pm

The initial RAM value can be chosen from the menu: CPU->RAM Initialization. That being said, that does not seem to make any difference for me. Maybe you can post your implementation, so I can have a look at it.

User avatar
zeroone
Posts: 932
Joined: Mon Dec 29, 2014 1:46 pm
Location: New York, NY
Contact:

Re: Mapper 340

Post by zeroone » Sat Apr 06, 2019 12:24 pm

Here's my implementation:

Code: Select all

public class BMCK3036 extends Mapper {
  
  public BMCK3036(final CartFile cartFile) {
    super(cartFile, 4, 1);
  }
  
  @Override public void writeRegister(final int address, int value) {
    final int outer = address & 0x0018;
    value &= 7;
    setPrgBank(2, outer | value);
    setPrgBank(3, outer | (getBitBool(address, 5) ? value : 7));
    setNametableMirroring(((address & 0x25) == 0x25) ? HORIZONTAL : VERTICAL);
  }
}
That line in the constructor partitions PRG address space into 4 segments. And PRG banks 2 and 3 refer to the address ranges $8000--$BFFF and $C000-$FFFF, respectively. writeRegister() is called when address >= $8000.

I noticed that in the NintendulatorNRS implementation that the address and data values are not masked:

Code: Select all

void	Sync (void) {
	if (Latch::Addr.b0 &0x20) {
		EMU->SetPRG_ROM16(0x8, Latch::Addr.b0 | Latch::Data);
		EMU->SetPRG_ROM16(0xC, Latch::Addr.b0 | Latch::Data);
	} else {
		EMU->SetPRG_ROM16(0x8, Latch::Addr.b0 | Latch::Data);
		EMU->SetPRG_ROM16(0xC, Latch::Addr.b0 | 7);
	}
	EMU->SetCHR_RAM8(0, 0);
	if ((Latch::Addr.b0 &0x25) ==0x25)
		EMU->Mirror_H();
	else
		EMU->Mirror_V();
}
Logging indicates that the data value is not necessarily confined to <= 7. Though that my be a problem on my end if my emulator is executing code that it shouldn't be.

I also noticed that the cart reads from values in the range $4000--$400F. It never writes into that range. This might suggest that the cart has DIP switches. Or it could just be some other strange issue on my end.

NewRisingSun
Posts: 1199
Joined: Thu May 19, 2005 11:30 am

Re: Mapper 340

Post by NewRisingSun » Sat Apr 06, 2019 12:33 pm

The cart sets the bank from which to copy CHR data from PRG-ROM to CHR-RAM using STA $9000,X with A=$80. That means that the inner bank is set via the address as well, which is why you must use unmasked bank numbers, only masking bits that would exceed the total PRG-ROM size.

The code does not read directly from $4000-$400F. The 6502 however performs a dummy read during the menu code's "STA $4000.X" initialization, maybe that is what you are seeing.

Edit: After checking the "Super Bomberman" variants on the second menu page, it turns out that the data latch should not be applied at all in NROM mode.

Code: Select all

void	Sync (void) {
	if (Latch::Addr.b0 &0x20) {
		EMU->SetPRG_ROM16(0x8, Latch::Addr.b0);
		EMU->SetPRG_ROM16(0xC, Latch::Addr.b0);
	} else {
		EMU->SetPRG_ROM16(0x8, Latch::Addr.b0 | Latch::Data);
		EMU->SetPRG_ROM16(0xC, Latch::Addr.b0 | 7);
	}
	EMU->SetCHR_RAM8(0, 0);
	if ((Latch::Addr.b0 &0x25) ==0x25)
		EMU->Mirror_H();
	else
		EMU->Mirror_V();
}

User avatar
zeroone
Posts: 932
Joined: Mon Dec 29, 2014 1:46 pm
Location: New York, NY
Contact:

Re: Mapper 340

Post by zeroone » Sat Apr 06, 2019 12:52 pm

Yes! That's the secret! It worked for me:

Code: Select all

  @Override public void writeRegister(final int address, int value) {
    final int outer = address & 0x00FF;
    if (getBitBool(address, 5)) {
      setPrgBank(2, outer);
      setPrgBank(3, outer);
    } else {
      setPrgBank(2, outer | value);
      setPrgBank(3, outer | 7);
    }
    setNametableMirroring(((address & 0x25) == 0x25) ? HORIZONTAL : VERTICAL);
  }
Edit: You might want to update the wiki.

NewRisingSun
Posts: 1199
Joined: Thu May 19, 2005 11:30 am

Re: Mapper 340

Post by NewRisingSun » Sat Apr 06, 2019 1:09 pm

Done.

Post Reply