The CPU seems to be fine. It passes the nestest tests (with cycle precision).
The mirroring logic also seems fine to me. Here is a snippet
Code:
public class NameTableMirroring {
public enum NameTableIndex {
A,
B,
C,
D
}
public enum NameTableMirroringType {
HORIZONTAL,
VERTICAL,
ONE_SCREEN_A,
ONE_SCREEN_B,
FOUR_SCREEN
}
public static int getNameTableOffset(int address) {
if (0x2000 <= address && address < 0x2400) {
return address - 0x2000;
} else if (0x2400 <= address && address < 0x2800) {
return address - 0x2400;
} else if (0x2800 <= address && address < 0x2C00) {
return address - 0x2800;
} else {
assert (0x2C00 <= address && address < 0x3000);
return address - 0x2C00;
}
}
public static NameTableIndex getNameTableIndex(int address, NameTableMirroringType mirroringType) {
if (mirroringType == NameTableMirroringType.HORIZONTAL) {
// Horizontal Mirroring:
// [0x2000, 0x23FF] -> NTA, [0x2400, 0x27FF] -> NTA
// [0x2800, 0x2BFF] -> NTB, [0x2C00, 0x2FFF] -> NTB
if (0x2000 <= address && address < 0x2400) {
return NameTableIndex.A;
} else if (0x2400 <= address && address < 0x2800) {
return NameTableIndex.A;
} else if (0x2800 <= address && address < 0x2C00) {
return NameTableIndex.B;
} else {
assert(0x2C00 <= address && address < 0x3000);
return NameTableIndex.B;
}
} else if (mirroringType == NameTableMirroringType.VERTICAL) {
// Vertical Mirroring:
// [0x2000, 0x23FF] -> NTA, [0x2400, 0x27FF] -> NTB
// [0x2800, 0x2BFF] -> NTA, [0x2C00, 0x2FFF] -> NTB
if (0x2000 <= address && address < 0x2400) {
return NameTableIndex.A;
} else if (0x2400 <= address && address < 0x2800) {
return NameTableIndex.B;
} else if (0x2800 <= address && address < 0x2C00) {
return NameTableIndex.A;
} else {
assert(0x2C00 <= address && address < 0x3000);
return NameTableIndex.B;
}
} else if (mirroringType == NameTableMirroringType.ONE_SCREEN_A) {
// One Screen Mirroring: All address points to the same data.
// [0x2000, 0x23FF] -> NTA, [0x2400, 0x27FF] -> NTA
// [0x2800, 0x2BFF] -> NTA, [0x2C00, 0x2FFF] -> NTA
// Enabled by a mapper usually.
return NameTableIndex.A;
} else if (mirroringType == NameTableMirroringType.ONE_SCREEN_B) {
// One Screen Mirroring: All address points to the same data.
// [0x2000, 0x23FF] -> NTB, [0x2400, 0x27FF] -> NTB
// [0x2800, 0x2BFF] -> NTB, [0x2C00, 0x2FFF] -> NTB
// Enabled by a mapper usually.
return NameTableIndex.B;
} else if (mirroringType == NameTableMirroringType.FOUR_SCREEN) {
// 4 screen Mirroring: Each addresses have their own memory space.
// Enable by a mapper usually.
// [0x2000, 0x23FF] -> NTA, [0x2400, 0x27FF] -> NTB
// [0x2800, 0x2BFF] -> NTC, [0x2C00, 0x2FFF] -> NTD
if (0x2000 <= address && address < 0x2400) {
return NameTableIndex.A;
} else if (0x2400 <= address && address < 0x2800) {
return NameTableIndex.B;
} else if (0x2800 <= address && address < 0x2C00) {
return NameTableIndex.C;
} else {
assert(0x2C00 <= address && address < 0x3000);
return NameTableIndex.D;
}
}
// Keep compiler silent
return NameTableIndex.A;
}
}
ant the actual read and write to the name tables:
Code:
@Override
public int readNameTable(int address, int[][] extNTRAM) {
NameTableIndex nameTableIndex = NameTableMirroring.getNameTableIndex(
address, getNameTableMirroringType());
int nameTableOffset = NameTableMirroring.getNameTableOffset(address);
return readNameTable(nameTableIndex, nameTableOffset, extNTRAM);
}
@Override
public void writeNameTable(int address, int value, int[][] extNTRAM) {
NameTableIndex nameTableIndex = NameTableMirroring.getNameTableIndex(
address, getNameTableMirroringType());
int nameTableOffset = NameTableMirroring.getNameTableOffset(address);
writeNameTable(nameTableIndex, nameTableOffset, value, extNTRAM);
}
protected NameTableMirroringType getNameTableMirroringType() {
return _cartridgeMirroringType;
}
protected int readNameTable(NameTableIndex nameTableIndex,
int nameTableOffset, int[][] extNTRAM) {
switch (nameTableIndex) {
case A: return extNTRAM[0][nameTableOffset];
case B: return extNTRAM[1][nameTableOffset];
case C: assert false; break; //TODO
case D: assert false; break; //TODO
}
return 0;
}
protected void writeNameTable(NameTableIndex nameTableIndex,
int nameTableOffset, int value, int[][] extNTRAM) {
switch (nameTableIndex) {
case A: extNTRAM[0][nameTableOffset] = value;
case B: extNTRAM[1][nameTableOffset] = value;
case C: assert false; break; //TODO
case D: assert false; break; //TODO
}
}