Implementing Mapper 15 (100 in 1)

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
Implementing Mapper 15 (100 in 1)
by on (#173129)
Hi,

I'm trying to implement Mapper 15 but I can't get it to work.

The following is the relevant code/logic of my implementation. Can anyone point out where I'm going wrong?

Thanks

Code:
        public byte this[ushort address]
        {
            get
            {
                if (address < 0x2000)
                    return Cartridge.CharacterRom[address];

                int index = 0;
                switch (bankMode)
                {
                    case 0:
                        index = address & 0x3FFF;
                        if (address >= 0x8000 && address < 0xC000)
                        {
                            return Cartridge.ProgramRom[programRomBank * 0x4000 + index];
                        }
                        else if (address >= 0xC000)
                        {
                            return Cartridge.ProgramRom[(programRomBank | 1) * 0x4000 + index];
                        }
                        break;
                    case 1:
                        index = address & 0x3FFF;
                        if (address >= 0x8000 && address < 0xC000)
                        {
                            return Cartridge.ProgramRom[programRomBank * 0x4000 + index];
                        }
                        else if (address >= 0xC000)
                        {
                            // last bank
                            return Cartridge.ProgramRom[Cartridge.ProgramRom.Count - 0x4000 + index];
                        }
                        break;
                    case 2:
                        // 8k banks
                        index = address & 0x1FFF;
                        if (address >= 0x8000)
                        {
                            return Cartridge.ProgramRom[programRomBank * 0x4000 + subBank * 0x2000 + index];
                        }
                        break;
                    case 3:
                        // 16k banks (mirrored)
                        index = address & 0x3FFF;
                        if (address >= 0x8000)
                        {
                            return Cartridge.ProgramRom[programRomBank * 0x4000 + index];
                        }
                        break;
                }

                throw new Exception("Unhandled " + Name + " mapper read at address: " + Hex.Format(address));
            }

            set
            {
                if (address < 0x2000)
                {
                    Cartridge.CharacterRom[address] = value;
                    return;
                }

                if (address >= 0x8000)
                {
                    bankMode = address & 0x03;
                    programRomBank = value & 0x3f;
                    subBank = value >> 7;

                    byte mirrorMode = (value & 0x40) != 0 ? Cartridge.MirrorHorizontal : Cartridge.MirrorVertical;
                    if (Cartridge.MirrorMode != mirrorMode)
                    {
                        Cartridge.MirrorMode = mirrorMode;
                        Cartridge.MirrorModeChanged?.Invoke();
                    }
                    return;
                }

                throw new Exception("Unhandled " + Name + " mapper write at address: " + Hex.Format(address));
            }
        }

        private int bankMode;
        private int programRomBank;
        private int subBank;
Re: Implementing Mapper 15 (100 in 1)
by on (#173131)
Your memory map design is really weird, you seem to be combining the CPU and PPU memory maps.
Re: Implementing Mapper 15 (100 in 1)
by on (#173132)
Dwedit wrote:
Your memory map design is really weird, you seem to be combining the CPU and PPU memory maps.


I have a dispatch-based memory class where I plug in the Mapper to the address range $0000-$1FFF and $6000-$FFFF when a cartridge is loaded. Hence the logic for CPU and PPU memory is in the same mapper instance and its isolated within it. I have so far managed to implement NROM, CNROM, AXROM, MMC1, MMC2, MMC3, Colour Dreams, UxROM and CPROM.
Re: Implementing Mapper 15 (100 in 1)
by on (#173136)
I think I solved the problem, or at least found a solution that works with the Contra 16 100-in-1 rom.

The NesDev wiki for Mapper 15 states that the starting bank mode should be 2. There is a dispute at the end of the page that states there's a disagreement on the starting mode and some say it should be 0. I did try using 0 instead of 2 and it works.