1 module nes.memory; 2 3 import std.experimental.logger; 4 import std.format; 5 6 import nes.console; 7 8 interface Memory { 9 ubyte read(ushort address); 10 void write(ushort address, ubyte value); 11 } 12 13 class MemoryException : Exception 14 { 15 this(string msg, string file = __FILE__, size_t line = __LINE__) { 16 super(msg, file, line); 17 } 18 } 19 20 class CPUMemory : Memory { 21 this(Console console) { 22 this.console = console; 23 } 24 25 ubyte read(ushort address) { 26 if (address < 0x2000) { 27 return this.console.ram[address % 0x0800]; 28 } 29 else if (address < 0x4000) { 30 return this.console.ppu.readRegister(0x2000 + address % 8); 31 } 32 else if (address == 0x4014) { 33 return this.console.ppu.readRegister(address); 34 } 35 else if (address == 0x4015) { 36 return this.console.apu.readRegister(address); 37 } 38 else if (address == 0x4016) { 39 return this.console.controller1.read(); 40 } 41 else if (address == 0x4017) { 42 return this.console.controller2.read(); 43 } 44 else if (address < 0x6000) { 45 // TODO: I/O registers 46 } 47 else if (address >= 0x6000) { 48 return this.console.mapper.read(address); 49 } 50 else { 51 throw new MemoryException(format("unhandled cpu memory read at address: 0x%04X", address)); 52 } 53 54 return 0; 55 } 56 57 void write(ushort address, ubyte value) { 58 if (address < 0x2000) { 59 this.console.ram[address % 0x0800] = value; 60 } 61 else if (address < 0x4000) { 62 this.console.ppu.writeRegister(0x2000 + address % 8, value); 63 } 64 else if (address < 0x4014) { 65 this.console.apu.writeRegister(address, value); 66 } 67 else if (address == 0x4014) { 68 this.console.ppu.writeRegister(address, value); 69 } 70 else if (address == 0x4015) { 71 this.console.apu.writeRegister(address, value); 72 } 73 else if (address == 0x4016) { 74 this.console.controller1.write(value); 75 this.console.controller2.write(value); 76 } 77 else if (address == 0x4017) { 78 this.console.apu.writeRegister(address, value); 79 } 80 else if (address < 0x6000) { 81 // TODO: I/O registers 82 } 83 else if (address >= 0x6000) { 84 this.console.mapper.write(address, value); 85 } 86 else { 87 throw new MemoryException(format("unhandled cpu memory write at address: 0x%04X", address)); 88 } 89 } 90 91 private Console console; 92 } 93 94 class PPUMemory : Memory { 95 this(Console console) { 96 this.console = console; 97 } 98 99 ubyte read(ushort address) { 100 address = address % 0x4000; 101 102 if (address < 0x2000) { 103 return this.console.mapper.read(address); 104 } 105 else if (address < 0x3F00) { 106 auto mode = this.console.cartridge.mirror; 107 return this.console.ppu.nameTableData[MirrorAddress(mode, address) % 2048]; 108 } 109 else if (address < 0x4000) { 110 return this.console.ppu.readPalette(address % 32); 111 } 112 else { 113 throw new MemoryException(format("unhandled ppu memory read at address: 0x%04X", address)); 114 } 115 } 116 117 void write(ushort address, ubyte value) { 118 address = address % 0x4000; 119 120 if (address < 0x2000) { 121 this.console.mapper.write(address, value); 122 } 123 else if (address < 0x3F00) { 124 auto mode = this.console.cartridge.mirror; 125 this.console.ppu.nameTableData[MirrorAddress(mode, address) % 2048] = value; 126 } 127 else if (address < 0x4000) { 128 this.console.ppu.writePalette(address % 32, value); 129 } 130 else { 131 throw new MemoryException(format("unhandled ppu memory write at address: 0x%04X", address)); 132 } 133 } 134 135 private Console console; 136 } 137 138 // Mirroring Modes 139 140 enum { 141 MirrorHorizontal = 0, 142 MirrorVertical = 1, 143 MirrorSingle0 = 2, 144 MirrorSingle1 = 3, 145 MirrorFour = 4 146 } 147 148 ushort[4][] MirrorLookup = [ 149 [0, 0, 1, 1], 150 [0, 1, 0, 1], 151 [0, 0, 0, 0], 152 [1, 1, 1, 1], 153 [0, 1, 2, 3] 154 ]; 155 156 ushort MirrorAddress(ubyte mode, ushort address) { 157 address = cast(ushort)(address - 0x2000) % 0x1000; 158 auto table = address / 0x0400; 159 auto offset = address % 0x0400; 160 161 return cast(ushort)(0x2000 + MirrorLookup[mode][table] * 0x0400 + offset); 162 }