Hello, I could use some help with my mmc1 emulation. I have gone through the wiki and docs best i could and have come up with an implementation that works almost all the time but a couple games still give me trouble. One is Adams Family Puglsy scavenger hunt and the other Ninja Gaiden. Adams family breaks within the first 30 instructions so its a little easier to follow but Im still getting the banks wrong. Ive included the log below for Adams Family and my mmc1 code. On startup I load 0xC into Reg0 and all other regs are 0. The lower PRG space has bank 0 and upper PRG space has the last bank which is 7.
As i follow the log below i can see that it starts by a series of resets then writes to Reg1 with an initial value of 1. Which to my understanding means 32k switching mode. Then on line 23 writes begin to Reg3 with an initial value of 1. After the 5th write i switch in bank 0 at the lower PRG Space and bank 1 into the upper PRG space. This is where it falls apart. Checking with nintendulator i can see that after the 5th write that the lower PRG space has bank 7 and the upper PRG space has 7 as well. I cant seem to figure out how bank 7 ends up in either bank when 32k should of been switched out using a Reg3 value of 1.
1 FF80 A9 FF LDA #$FF A:00 X:00 Y:00 P:24 SP:FD CYC: 0 SL:241
2 FF82 8D 00 80 STA $8000 = 00 A:FF X:00 Y:00 P:A4 SP:FD CYC: 6 SL:241
3 FF85 8D 00 A0 STA $A000 = 01 A:FF X:00 Y:00 P:A4 SP:FD CYC: 18 SL:241
4 FF88 8D 00 C0 STA $C000 = 00 A:FF X:00 Y:00 P:A4 SP:FD CYC: 30 SL:241
5 FF8B 8D 00 E0 STA $E000 = D0 A:FF X:00 Y:00 P:A4 SP:FD CYC: 42 SL:241
6 FF8E A2 01 LDX #$01 A:FF X:00 Y:00 P:A4 SP:FD CYC: 54 SL:241
7 FF90 A0 00 LDY #$00 A:FF X:01 Y:00 P:24 SP:FD CYC: 60 SL:241
8 FF92 8E FF 9F STX $9FFF = 01 A:FF X:01 Y:00 P:26 SP:FD CYC: 66 SL:241
9 FF95 8E FF 9F STX $9FFF = 01 A:FF X:01 Y:00 P:26 SP:FD CYC: 78 SL:241
10 FF98 8E FF 9F STX $9FFF = 01 A:FF X:01 Y:00 P:26 SP:FD CYC: 90 SL:241
11 FF9B 8E FF 9F STX $9FFF = 01 A:FF X:01 Y:00 P:26 SP:FD CYC:102 SL:241
12 FF9E 8C FF 9F STY $9FFF = 01 A:FF X:01 Y:00 P:26 SP:FD CYC:114 SL:241
13 FFA1 8C FF BF STY $BFFF = C3 A:FF X:01 Y:00 P:26 SP:FD CYC:126 SL:241
14 FFA4 8C FF BF STY $BFFF = C3 A:FF X:01 Y:00 P:26 SP:FD CYC:138 SL:241
15 FFA7 8C FF BF STY $BFFF = C3 A:FF X:01 Y:00 P:26 SP:FD CYC:150 SL:241
16 FFAA 8C FF BF STY $BFFF = C3 A:FF X:01 Y:00 P:26 SP:FD CYC:162 SL:241
17 FFAD 8C FF BF STY $BFFF = C3 A:FF X:01 Y:00 P:26 SP:FD CYC:174 SL:241
18 FFB0 8E FF DF STX $DFFF = FA A:FF X:01 Y:00 P:26 SP:FD CYC:186 SL:241
19 FFB3 8C FF DF STY $DFFF = FA A:FF X:01 Y:00 P:26 SP:FD CYC:198 SL:241
20 FFB6 8C FF DF STY $DFFF = FA A:FF X:01 Y:00 P:26 SP:FD CYC:210 SL:241
21 FFB9 8C FF DF STY $DFFF = FA A:FF X:01 Y:00 P:26 SP:FD CYC:222 SL:241
22 FFBC 8C FF DF STY $DFFF = FA A:FF X:01 Y:00 P:26 SP:FD CYC:234 SL:241
23 FFBF 8E FF FF STX $FFFF = C3 A:FF X:01 Y:00 P:26 SP:FD CYC:246 SL:241
24 FFC2 8E FF FF STX $FFFF = C3 A:FF X:01 Y:00 P:26 SP:FD CYC:258 SL:241
25 FFC5 8E FF FF STX $FFFF = C3 A:FF X:01 Y:00 P:26 SP:FD CYC:270 SL:241
26 FFC8 8C FF FF STY $FFFF = C3 A:FF X:01 Y:00 P:26 SP:FD CYC:282 SL:241
27 FFCB 8C FF FF STY $FFFF = C3 A:FF X:01 Y:00 P:26 SP:FD CYC:294 SL:241
28 FFCE 4C A4 C5 JMP $C5A4 A:FF X:01 Y:00 P:26 SP:FD CYC:306 SL:241
29 C5A4 A:FF X:01 Y:00 P:26 SP:FD CYC:315 SL:241
*Code Updated 7/22/18 CHR RAM/PRG RAM Bank Swicthing
*Code Updated 7/22/18
As i follow the log below i can see that it starts by a series of resets then writes to Reg1 with an initial value of 1. Which to my understanding means 32k switching mode. Then on line 23 writes begin to Reg3 with an initial value of 1. After the 5th write i switch in bank 0 at the lower PRG Space and bank 1 into the upper PRG space. This is where it falls apart. Checking with nintendulator i can see that after the 5th write that the lower PRG space has bank 7 and the upper PRG space has 7 as well. I cant seem to figure out how bank 7 ends up in either bank when 32k should of been switched out using a Reg3 value of 1.
1 FF80 A9 FF LDA #$FF A:00 X:00 Y:00 P:24 SP:FD CYC: 0 SL:241
2 FF82 8D 00 80 STA $8000 = 00 A:FF X:00 Y:00 P:A4 SP:FD CYC: 6 SL:241
3 FF85 8D 00 A0 STA $A000 = 01 A:FF X:00 Y:00 P:A4 SP:FD CYC: 18 SL:241
4 FF88 8D 00 C0 STA $C000 = 00 A:FF X:00 Y:00 P:A4 SP:FD CYC: 30 SL:241
5 FF8B 8D 00 E0 STA $E000 = D0 A:FF X:00 Y:00 P:A4 SP:FD CYC: 42 SL:241
6 FF8E A2 01 LDX #$01 A:FF X:00 Y:00 P:A4 SP:FD CYC: 54 SL:241
7 FF90 A0 00 LDY #$00 A:FF X:01 Y:00 P:24 SP:FD CYC: 60 SL:241
8 FF92 8E FF 9F STX $9FFF = 01 A:FF X:01 Y:00 P:26 SP:FD CYC: 66 SL:241
9 FF95 8E FF 9F STX $9FFF = 01 A:FF X:01 Y:00 P:26 SP:FD CYC: 78 SL:241
10 FF98 8E FF 9F STX $9FFF = 01 A:FF X:01 Y:00 P:26 SP:FD CYC: 90 SL:241
11 FF9B 8E FF 9F STX $9FFF = 01 A:FF X:01 Y:00 P:26 SP:FD CYC:102 SL:241
12 FF9E 8C FF 9F STY $9FFF = 01 A:FF X:01 Y:00 P:26 SP:FD CYC:114 SL:241
13 FFA1 8C FF BF STY $BFFF = C3 A:FF X:01 Y:00 P:26 SP:FD CYC:126 SL:241
14 FFA4 8C FF BF STY $BFFF = C3 A:FF X:01 Y:00 P:26 SP:FD CYC:138 SL:241
15 FFA7 8C FF BF STY $BFFF = C3 A:FF X:01 Y:00 P:26 SP:FD CYC:150 SL:241
16 FFAA 8C FF BF STY $BFFF = C3 A:FF X:01 Y:00 P:26 SP:FD CYC:162 SL:241
17 FFAD 8C FF BF STY $BFFF = C3 A:FF X:01 Y:00 P:26 SP:FD CYC:174 SL:241
18 FFB0 8E FF DF STX $DFFF = FA A:FF X:01 Y:00 P:26 SP:FD CYC:186 SL:241
19 FFB3 8C FF DF STY $DFFF = FA A:FF X:01 Y:00 P:26 SP:FD CYC:198 SL:241
20 FFB6 8C FF DF STY $DFFF = FA A:FF X:01 Y:00 P:26 SP:FD CYC:210 SL:241
21 FFB9 8C FF DF STY $DFFF = FA A:FF X:01 Y:00 P:26 SP:FD CYC:222 SL:241
22 FFBC 8C FF DF STY $DFFF = FA A:FF X:01 Y:00 P:26 SP:FD CYC:234 SL:241
23 FFBF 8E FF FF STX $FFFF = C3 A:FF X:01 Y:00 P:26 SP:FD CYC:246 SL:241
24 FFC2 8E FF FF STX $FFFF = C3 A:FF X:01 Y:00 P:26 SP:FD CYC:258 SL:241
25 FFC5 8E FF FF STX $FFFF = C3 A:FF X:01 Y:00 P:26 SP:FD CYC:270 SL:241
26 FFC8 8C FF FF STY $FFFF = C3 A:FF X:01 Y:00 P:26 SP:FD CYC:282 SL:241
27 FFCB 8C FF FF STY $FFFF = C3 A:FF X:01 Y:00 P:26 SP:FD CYC:294 SL:241
28 FFCE 4C A4 C5 JMP $C5A4 A:FF X:01 Y:00 P:26 SP:FD CYC:306 SL:241
29 C5A4 A:FF X:01 Y:00 P:26 SP:FD CYC:315 SL:241
*Code Updated 7/22/18 CHR RAM/PRG RAM Bank Swicthing
*Code Updated 7/22/18
Code:
public override void WriteToAddress(int MemoryAddress, int val)
{
if (MemoryAddress >= 0x8000 && MemoryAddress <= 0xffff && (((CPU.TotalCPUCycles - 1)!=LastWriteCycle) ))
{
LastWriteCycle = CPU.TotalCPUCycles;
if (val > 127)
{
MMC1COUNT = 0;
MMC1REG0 = MMC1REG0 | 12;
}
else
{
MMC1COUNT++;
if (MMC1COUNT == 1)
{
MMC1FIVEBITVALUE = val & 31;
}
if (MMC1COUNT == 5)
{
if (MemoryAddress >= 0x8000 && MemoryAddress < 0xA000)
{
MMC1REG0 = MMC1FIVEBITVALUE & 31;
PPU.MIRROR= MMC1REG0 &3;
MMC1PRG();
}
if (MemoryAddress >= 0xA000 && MemoryAddress < 0xC000)
{
MMC1REG1 = MMC1FIVEBITVALUE;
MMC1PRG();
if (GAME.NUMCHRPAGES > 0)
{
if ((((MMC1REG0 >> 4) & 1) == 0))
{
GAME.SwitchCHRBank((MMC1REG1 >> 1), 0x2000, 0x2000, 0x0000);
}
else if ((((MMC1REG0 >> 4) & 1) == 1))
{
GAME.SwitchCHRBank(MMC1REG1, 0x1000, 0x1000, 0x0000);
}
}
else if (GAME.NUMCHRPAGES == 0 && (((MMC1REG0 >> 4) & 1) == 1))
{
GAME.SwitchCHRRAMBank((MMC1REG1 & 1), 0x1000, 0x1000, 0x0000);
}
if (GAME.NUMCHRPAGES <= 2)
{
GAME.SwitchPRGRAMBank(((MMC1REG1 >> 2) & 3), 0x2000, 0x2000, 0x6000);
}
}
if (MemoryAddress >= 0xc000 && MemoryAddress <= 0xdfff)
{
MMC1REG2 = MMC1FIVEBITVALUE & 31;
if (GAME.NUMCHRPAGES > 0 && (((MMC1REG0 >> 4) & 1) == 1))
{
GAME.SwitchCHRBank(MMC1REG2, 0x1000, 0x1000, 0x1000);
}
else if (GAME.NUMCHRPAGES == 0 && (((MMC1REG0 >> 4) & 1) == 1))
{
GAME.SwitchCHRRAMBank((MMC1REG2 & 1), 0x1000, 0x1000, 0x1000);
}
if(GAME.NUMCHRPAGES <= 2 && (((MMC1REG0 >> 4) & 1) == 1))
{
GAME.SwitchPRGRAMBank(((MMC1REG2 >> 2) & 3), 0x2000, 0x2000, 0x6000);
}
}
if (MemoryAddress >= 0xe000 && MemoryAddress < 0x10000)
{
MMC1REG3 = MMC1FIVEBITVALUE & 15;
MMC1PRG();
}
}
if (MMC1COUNT == 5) { MMC1COUNT = 0; }
}
}
if(MemoryAddress>=0x6000 && MemoryAddress < 0x8000)
{
GAME.TheMapper.WritePRGRAM(MemoryAddress, val);
}
}
public static void MMC1PRG()
{
int offset = (((MMC1REG1 >> 4) & 1) * 16 * (0x4000));
if ((((MMC1REG0 >> 2) & 3) < 2))
{
GAME.SwitchPRGBank((MMC1REG3 & ~1), 0x4000, 0x4000, 0x8000, offset);
GAME.SwitchPRGBank(((MMC1REG3 & ~1) | 0x01), 0x4000, 0x4000, 0xC000, offset);
}
else if ((((MMC1REG0 >> 2) & 3) == 2))
{
GAME.SwitchPRGBank(((MMC1REG3 & 15)), 0x4000, 0x4000, 0xC000, offset);
GAME.SwitchPRGBank(0, 0x4000, 0x4000, 0x8000, offset);
}
else if ((((MMC1REG0 >> 2) & 3) == 3))
{
GAME.SwitchPRGBank(((MMC1REG3 & 15)), 0x4000, 0x4000, 0x8000, offset);
GAME.SwitchPRGBank(((GAME.NUMPRGPAGES - 1) & 15), 0x4000, 0x4000, 0xC000, offset);
}
}
{
if (MemoryAddress >= 0x8000 && MemoryAddress <= 0xffff && (((CPU.TotalCPUCycles - 1)!=LastWriteCycle) ))
{
LastWriteCycle = CPU.TotalCPUCycles;
if (val > 127)
{
MMC1COUNT = 0;
MMC1REG0 = MMC1REG0 | 12;
}
else
{
MMC1COUNT++;
if (MMC1COUNT == 1)
{
MMC1FIVEBITVALUE = val & 31;
}
if (MMC1COUNT == 5)
{
if (MemoryAddress >= 0x8000 && MemoryAddress < 0xA000)
{
MMC1REG0 = MMC1FIVEBITVALUE & 31;
PPU.MIRROR= MMC1REG0 &3;
MMC1PRG();
}
if (MemoryAddress >= 0xA000 && MemoryAddress < 0xC000)
{
MMC1REG1 = MMC1FIVEBITVALUE;
MMC1PRG();
if (GAME.NUMCHRPAGES > 0)
{
if ((((MMC1REG0 >> 4) & 1) == 0))
{
GAME.SwitchCHRBank((MMC1REG1 >> 1), 0x2000, 0x2000, 0x0000);
}
else if ((((MMC1REG0 >> 4) & 1) == 1))
{
GAME.SwitchCHRBank(MMC1REG1, 0x1000, 0x1000, 0x0000);
}
}
else if (GAME.NUMCHRPAGES == 0 && (((MMC1REG0 >> 4) & 1) == 1))
{
GAME.SwitchCHRRAMBank((MMC1REG1 & 1), 0x1000, 0x1000, 0x0000);
}
if (GAME.NUMCHRPAGES <= 2)
{
GAME.SwitchPRGRAMBank(((MMC1REG1 >> 2) & 3), 0x2000, 0x2000, 0x6000);
}
}
if (MemoryAddress >= 0xc000 && MemoryAddress <= 0xdfff)
{
MMC1REG2 = MMC1FIVEBITVALUE & 31;
if (GAME.NUMCHRPAGES > 0 && (((MMC1REG0 >> 4) & 1) == 1))
{
GAME.SwitchCHRBank(MMC1REG2, 0x1000, 0x1000, 0x1000);
}
else if (GAME.NUMCHRPAGES == 0 && (((MMC1REG0 >> 4) & 1) == 1))
{
GAME.SwitchCHRRAMBank((MMC1REG2 & 1), 0x1000, 0x1000, 0x1000);
}
if(GAME.NUMCHRPAGES <= 2 && (((MMC1REG0 >> 4) & 1) == 1))
{
GAME.SwitchPRGRAMBank(((MMC1REG2 >> 2) & 3), 0x2000, 0x2000, 0x6000);
}
}
if (MemoryAddress >= 0xe000 && MemoryAddress < 0x10000)
{
MMC1REG3 = MMC1FIVEBITVALUE & 15;
MMC1PRG();
}
}
if (MMC1COUNT == 5) { MMC1COUNT = 0; }
}
}
if(MemoryAddress>=0x6000 && MemoryAddress < 0x8000)
{
GAME.TheMapper.WritePRGRAM(MemoryAddress, val);
}
}
public static void MMC1PRG()
{
int offset = (((MMC1REG1 >> 4) & 1) * 16 * (0x4000));
if ((((MMC1REG0 >> 2) & 3) < 2))
{
GAME.SwitchPRGBank((MMC1REG3 & ~1), 0x4000, 0x4000, 0x8000, offset);
GAME.SwitchPRGBank(((MMC1REG3 & ~1) | 0x01), 0x4000, 0x4000, 0xC000, offset);
}
else if ((((MMC1REG0 >> 2) & 3) == 2))
{
GAME.SwitchPRGBank(((MMC1REG3 & 15)), 0x4000, 0x4000, 0xC000, offset);
GAME.SwitchPRGBank(0, 0x4000, 0x4000, 0x8000, offset);
}
else if ((((MMC1REG0 >> 2) & 3) == 3))
{
GAME.SwitchPRGBank(((MMC1REG3 & 15)), 0x4000, 0x4000, 0x8000, offset);
GAME.SwitchPRGBank(((GAME.NUMPRGPAGES - 1) & 15), 0x4000, 0x4000, 0xC000, offset);
}
}