#include namespace Famicom { Settings settings; auto Interface::information() -> Information { Information information; information.manufacturer = "Nintendo"; information.name = "Famicom"; information.extension = "fc"; information.resettable = true; return information; } auto Interface::displays() -> vector { Display display; display.type = Display::Type::CRT; display.colors = 1 << 9; display.width = 256; display.height = 240; display.internalWidth = 256; display.internalHeight = 240; display.aspectCorrection = 8.0 / 7.0; display.refreshRate = system.frequency() / (ppu.vlines() * ppu.rate() * 341.0); return {display}; } auto Interface::color(uint32 n) -> uint64 { double saturation = 2.0; double hue = 0.0; double contrast = 1.0; double brightness = 1.0; double gamma = settings.colorEmulation ? 1.8 : 2.2; int color = (n & 0x0f), level = color < 0xe ? (n >> 4) & 3 : 1; static const double black = 0.518, white = 1.962, attenuation = 0.746; static const double levels[8] = { 0.350, 0.518, 0.962, 1.550, 1.094, 1.506, 1.962, 1.962, }; double lo_and_hi[2] = { levels[level + 4 * (color == 0x0)], levels[level + 4 * (color < 0xd)], }; double y = 0.0, i = 0.0, q = 0.0; auto wave = [](int p, int color) { return (color + p + 8) % 12 < 6; }; for(int p : range(12)) { double spot = lo_and_hi[wave(p, color)]; if(((n & 0x040) && wave(p, 12)) || ((n & 0x080) && wave(p, 4)) || ((n & 0x100) && wave(p, 8)) ) spot *= attenuation; double v = (spot - black) / (white - black); v = (v - 0.5) * contrast + 0.5; v *= brightness / 12.0; y += v; i += v * cos((Math::Pi / 6.0) * (p + hue)); q += v * sin((Math::Pi / 6.0) * (p + hue)); } i *= saturation; q *= saturation; auto gammaAdjust = [=](double f) { return f < 0.0 ? 0.0 : pow(f, 2.2 / gamma); }; uint64 r = uclamp<16>(65535.0 * gammaAdjust(y + 0.946882 * i + 0.623557 * q)); uint64 g = uclamp<16>(65535.0 * gammaAdjust(y + -0.274788 * i + -0.635691 * q)); uint64 b = uclamp<16>(65535.0 * gammaAdjust(y + -1.108545 * i + 1.709007 * q)); return r << 32 | g << 16 | b << 0; } auto Interface::loaded() -> bool { return system.loaded(); } auto Interface::hashes() -> vector { return {cartridge.hash()}; } auto Interface::manifests() -> vector { return {cartridge.manifest()}; } auto Interface::titles() -> vector { return {cartridge.title()}; } auto Interface::load() -> bool { return system.load(this); } auto Interface::save() -> void { system.save(); } auto Interface::unload() -> void { save(); system.unload(); } auto Interface::ports() -> vector { return { {ID::Port::Controller1, "Controller Port 1"}, {ID::Port::Controller2, "Controller Port 2"}, {ID::Port::Expansion, "Expansion Port" }}; } auto Interface::devices(uint port) -> vector { if(port == ID::Port::Controller1) return { {ID::Device::None, "None" }, {ID::Device::Gamepad, "Gamepad"} }; if(port == ID::Port::Controller2) return { {ID::Device::None, "None" }, {ID::Device::Gamepad, "Gamepad"} }; if(port == ID::Port::Expansion) return { {ID::Device::None, "None"} }; return {}; } auto Interface::inputs(uint device) -> vector { using Type = Input::Type; if(device == ID::Device::None) return { }; if(device == ID::Device::Gamepad) return { {Type::Hat, "Up" }, {Type::Hat, "Down" }, {Type::Hat, "Left" }, {Type::Hat, "Right" }, {Type::Button, "B" }, {Type::Button, "A" }, {Type::Control, "Select"}, {Type::Control, "Start" } }; return {}; } auto Interface::connected(uint port) -> uint { if(port == ID::Port::Controller1) return settings.controllerPort1; if(port == ID::Port::Controller2) return settings.controllerPort2; if(port == ID::Port::Expansion) return settings.expansionPort; return 0; } auto Interface::connect(uint port, uint device) -> void { if(port == ID::Port::Controller1) controllerPort1.connect(settings.controllerPort1 = device); if(port == ID::Port::Controller2) controllerPort2.connect(settings.controllerPort2 = device); } auto Interface::power() -> void { system.power(/* reset = */ false); } auto Interface::reset() -> void { system.power(/* reset = */ true); } auto Interface::run() -> void { system.run(); } auto Interface::serialize() -> serializer { system.runToSave(); return system.serialize(); } auto Interface::unserialize(serializer& s) -> bool { return system.unserialize(s); } auto Interface::cheats(const vector& list) -> void { cheat.assign(list); } auto Interface::cap(const string& name) -> bool { if(name == "Color Emulation") return true; if(name == "Scanline Emulation") return true; return false; } auto Interface::get(const string& name) -> any { if(name == "Color Emulation") return settings.colorEmulation; if(name == "Scanline Emulation") return settings.scanlineEmulation; return {}; } auto Interface::set(const string& name, const any& value) -> bool { if(name == "Color Emulation" && value.is()) { settings.colorEmulation = value.get(); Emulator::video.setPalette(); return true; } if(name == "Scanline Emulation" && value.is()) return settings.scanlineEmulation = value.get(), true; return false; } }