1 module nes.console; 2 3 import std.base64; 4 import std.conv; 5 import std.file; 6 import std.zlib; 7 8 import nes.apu; 9 import nes.cartridge; 10 import nes.color; 11 import nes.controller; 12 import nes.cpu; 13 import nes.image; 14 import nes.ines; 15 import nes.mapper; 16 import nes.palette; 17 import nes.ppu; 18 19 class Console { 20 CPU cpu; 21 APU apu; 22 PPU ppu; 23 Cartridge cartridge; 24 Controller controller1; 25 Controller controller2; 26 Mapper mapper; 27 ubyte[] ram; 28 29 this(string path) { 30 this.cartridge = LoadNESFile(path); 31 32 this.ram = new ubyte[2048]; 33 this.controller1 = new Controller(); 34 this.controller2 = new Controller(); 35 36 this.mapper = NewMapper(this); 37 38 this.cpu = new CPU(this); 39 this.apu = new APU(this); 40 this.ppu = new PPU(this); 41 } 42 43 void reset() { 44 this.cpu.reset(); 45 this.ppu.reset(); 46 this.apu.reset(); 47 } 48 49 int step() { 50 auto prevCycles = this.cpu.cycles; 51 this.cpu.step(); 52 return cast(int)(this.cpu.cycles - prevCycles); 53 } 54 55 int stepFrame() { 56 auto cpuCycles = 0; 57 auto frame = this.ppu.frame; 58 59 while (frame == this.ppu.frame) { 60 cpuCycles += this.step(); 61 } 62 63 return cpuCycles; 64 } 65 66 void stepSeconds(double seconds) { 67 auto cycles = cast(int)(CPUFrequency * seconds); 68 69 while (cycles > 0) { 70 cycles -= this.step(); 71 } 72 } 73 74 ImageRGBA buffer() { 75 return this.ppu.front; 76 } 77 78 RGBA backgroundColor() { 79 return Palette[this.ppu.readPalette(0) % 64]; 80 } 81 82 void setButtons1(bool[8] buttons) { 83 this.controller1.setButtons(buttons); 84 } 85 86 void setButtons2(bool[8] buttons) { 87 this.controller2.setButtons(buttons); 88 } 89 90 void setAudioCallback(ApuCallbackFuncType callback) { 91 this.apu.callback = callback; 92 } 93 94 void setAudioSampleRate(double sampleRate) { 95 this.apu.setAudioSampleRate(sampleRate); 96 } 97 98 void saveState(string fileName) { 99 string[string] state = ["version": "2"]; 100 101 this.save(state); 102 103 auto stateText = to!string(state); 104 105 auto data = std.zlib.compress(cast(void[])stateText); 106 107 write(fileName, data); 108 } 109 110 void loadState(string fileName) { 111 auto stateData = read(fileName); 112 113 stateData = cast(ubyte[])std.zlib.uncompress(cast(void[])stateData); 114 115 string[string] state = to!(string[string])(cast(string)stateData); 116 117 if (state["version"] != "2") return; 118 119 load(state); 120 } 121 122 void saveBatteryBackedRam(string fileName) { 123 if (!this.cartridge.battery) return; 124 125 auto data = std.zlib.compress(cast(void[])this.cartridge.sram); 126 127 write(fileName, data); 128 } 129 130 void loadBatteryBackedRam(string fileName) { 131 if (!this.cartridge.battery) return; 132 133 auto data = read(fileName); 134 135 this.cartridge.sram = cast(ubyte[])std.zlib.uncompress(cast(void[])data); 136 } 137 138 private: 139 void save(string[string] state) { 140 state["console.ram"] = Base64.encode(this.ram); 141 142 this.cpu.save(state); 143 this.apu.save(state); 144 this.ppu.save(state); 145 this.cartridge.save(state); 146 this.mapper.save(state); 147 } 148 149 void load(string[string] state) { 150 this.ram = Base64.decode(state["console.ram"]); 151 152 this.cpu.load(state); 153 this.apu.load(state); 154 this.ppu.load(state); 155 this.cartridge.load(state); 156 this.mapper.load(state); 157 } 158 }