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