| 1 | package com.rox.cartridge; | |
| 2 | ||
| 3 | import com.rox.mem.RAM; | |
| 4 | ||
| 5 | import static com.rox.ByteUtil.BYTE_MASK; | |
| 6 | ||
| 7 | /** | |
| 8 | * iNES mapper 0 (NROM): PRG-ROM is fixed at {@code $8000-$FFFF} with no bank registers - a 16KB ROM | |
| 9 | * is mirrored into both halves of that window, a 32KB ROM fills it directly. Writes to {@code $8000+} | |
| 10 | * are no-ops (real NROM boards have nothing to switch). Always backed by 8KB of PRG-RAM at | |
| 11 | * {@code $6000-$7FFF} - not universal on real NROM boards, but needed here since that's exactly | |
| 12 | * where blargg's test ROMs report their pass/fail status. | |
| 13 | * | |
| 14 | * CHR is a single fixed 8KB bank, no banking registers to switch it (same "nothing to switch" | |
| 15 | * simplicity as PRG) - CHR-ROM if the ROM has any, else CHR-RAM (writable, for games that generate | |
| 16 | * their own tiles). Mirroring is whatever the iNES header says and never changes - NROM boards have | |
| 17 | * no register to override it, unlike MMC1. | |
| 18 | */ | |
| 19 | public final class NromMapper implements Mapper { | |
| 20 | private static final int PRG_RAM_SIZE = 0x2000; | |
| 21 | private static final int PRG_ROM_START_ADDRESS = 0x8000; | |
| 22 | private static final int SINGLE_BANK_SIZE = 0x4000; | |
| 23 | private static final int DOUBLE_BANK_SIZE = SINGLE_BANK_SIZE * 2; | |
| 24 | private static final int CHR_SIZE = 0x2000; | |
| 25 | ||
| 26 | private final byte[] prgRom; | |
| 27 | private final RAM prgRam = new RAM(PRG_RAM_SIZE, "PRG-RAM"); | |
| 28 | private final ChrMemory chr; | |
| 29 | private final Mirroring mirroring; | |
| 30 | ||
| 31 | public NromMapper(final INesRom rom){ | |
| 32 | this.prgRom = rom.prgRom(); | |
| 33 |
2
1. <init> : negated conditional → KILLED 2. <init> : negated conditional → KILLED |
if (prgRom.length != SINGLE_BANK_SIZE && prgRom.length != DOUBLE_BANK_SIZE){ |
| 34 | throw new IllegalArgumentException( | |
| 35 | "NROM expects 16KB or 32KB PRG-ROM, got " + prgRom.length + " bytes"); | |
| 36 | } | |
| 37 | this.chr = ChrMemory.of(rom, CHR_SIZE); | |
| 38 |
1
1. <init> : negated conditional → KILLED |
this.mirroring = rom.isVerticalMirroring() ? Mirroring.VERTICAL : Mirroring.HORIZONTAL; |
| 39 | } | |
| 40 | ||
| 41 | /** | |
| 42 | * Contract: only ever called with {@code address} in {@code $6000-$FFFF} (see class Javadoc). | |
| 43 | * PRG-ROM mirroring (16KB banks repeating to fill the 32KB window) falls out for free from | |
| 44 | * masking to {@code prgRom.length - 1} - both bank sizes are powers of two, same trick | |
| 45 | * {@link com.rox.mem.RAM} uses for its own address wraparound. | |
| 46 | */ | |
| 47 | @Override | |
| 48 | public int read(final int address){ | |
| 49 |
2
1. read : negated conditional → KILLED 2. read : changed conditional boundary → KILLED |
if (address >= PRG_ROM_START_ADDRESS){ |
| 50 |
4
1. read : Replaced bitwise AND with OR → KILLED 2. read : Replaced integer subtraction with addition → KILLED 3. read : replaced int return with 0 for com/rox/cartridge/NromMapper::read → KILLED 4. read : Replaced bitwise AND with OR → KILLED |
return prgRom[address & (prgRom.length - 1)] & BYTE_MASK; |
| 51 | } | |
| 52 |
1
1. read : replaced int return with 0 for com/rox/cartridge/NromMapper::read → KILLED |
return prgRam.read(address); |
| 53 | } | |
| 54 | ||
| 55 | /** Contract: only ever called with {@code address} in {@code $6000-$FFFF} (see class Javadoc). */ | |
| 56 | @Override | |
| 57 | public void write(final int address, final int value){ | |
| 58 |
2
1. write : negated conditional → KILLED 2. write : changed conditional boundary → KILLED |
if (address < PRG_ROM_START_ADDRESS){ |
| 59 |
1
1. write : removed call to com/rox/mem/RAM::write → KILLED |
prgRam.write(address, value); |
| 60 | } | |
| 61 | } | |
| 62 | ||
| 63 | @Override | |
| 64 | public int readChr(final int address){ | |
| 65 |
2
1. readChr : Replaced bitwise AND with OR → KILLED 2. readChr : replaced int return with 0 for com/rox/cartridge/NromMapper::readChr → KILLED |
return chr.read(address & (CHR_SIZE - 1)); |
| 66 | } | |
| 67 | ||
| 68 | /** No-op on CHR-ROM boards - real hardware has nowhere to write CHR-ROM writes to. */ | |
| 69 | @Override | |
| 70 | public void writeChr(final int address, final int value){ | |
| 71 |
2
1. writeChr : removed call to com/rox/cartridge/ChrMemory::write → KILLED 2. writeChr : Replaced bitwise AND with OR → KILLED |
chr.write(address & (CHR_SIZE - 1), value); |
| 72 | } | |
| 73 | ||
| 74 | @Override | |
| 75 | public Mirroring nametableMirroring(){ | |
| 76 |
1
1. nametableMirroring : replaced return value with null for com/rox/cartridge/NromMapper::nametableMirroring → KILLED |
return mirroring; |
| 77 | } | |
| 78 | ||
| 79 | @Override | |
| 80 | public int[] prgRam(){ | |
| 81 |
1
1. prgRam : replaced return value with null for com/rox/cartridge/NromMapper::prgRam → KILLED |
return prgRam.snapshot(); |
| 82 | } | |
| 83 | ||
| 84 | @Override | |
| 85 | public void restorePrgRam(final int[] prgRam){ | |
| 86 |
1
1. restorePrgRam : removed call to com/rox/mem/RAM::restore → KILLED |
this.prgRam.restore(prgRam); |
| 87 | } | |
| 88 | ||
| 89 | @Override | |
| 90 | public MapperSnapshot snapshot(){ | |
| 91 |
1
1. snapshot : replaced return value with null for com/rox/cartridge/NromMapper::snapshot → KILLED |
return new NromMapperSnapshot(prgRam.snapshot(), chr.snapshot()); |
| 92 | } | |
| 93 | ||
| 94 | @Override | |
| 95 | public void checkRestorable(final MapperSnapshot snapshot){ | |
| 96 | final NromMapperSnapshot nrom = SnapshotArrays.expect(snapshot, NromMapperSnapshot.class); | |
| 97 |
1
1. checkRestorable : removed call to com/rox/mem/RAM::checkRestorable → KILLED |
prgRam.checkRestorable(nrom.prgRam()); |
| 98 |
1
1. checkRestorable : removed call to com/rox/cartridge/ChrMemory::checkRestorable → KILLED |
chr.checkRestorable(nrom.chrRam()); |
| 99 | } | |
| 100 | ||
| 101 | @Override | |
| 102 | public void restore(final MapperSnapshot snapshot){ | |
| 103 |
1
1. restore : removed call to com/rox/cartridge/NromMapper::checkRestorable → KILLED |
checkRestorable(snapshot); |
| 104 | final NromMapperSnapshot nrom = (NromMapperSnapshot) snapshot; | |
| 105 |
1
1. restore : removed call to com/rox/mem/RAM::restore → KILLED |
prgRam.restore(nrom.prgRam()); |
| 106 |
1
1. restore : removed call to com/rox/cartridge/ChrMemory::restore → KILLED |
chr.restore(nrom.chrRam()); |
| 107 | } | |
| 108 | } | |
Mutations | ||
| 33 |
1.1 2.2 |
|
| 38 |
1.1 |
|
| 49 |
1.1 2.2 |
|
| 50 |
1.1 2.2 3.3 4.4 |
|
| 52 |
1.1 |
|
| 58 |
1.1 2.2 |
|
| 59 |
1.1 |
|
| 65 |
1.1 2.2 |
|
| 71 |
1.1 2.2 |
|
| 76 |
1.1 |
|
| 81 |
1.1 |
|
| 86 |
1.1 |
|
| 91 |
1.1 |
|
| 97 |
1.1 |
|
| 98 |
1.1 |
|
| 103 |
1.1 |
|
| 105 |
1.1 |
|
| 106 |
1.1 |