#include namespace SuperFamicom { Interface* interface = nullptr; Settings settings; Interface::Interface() { interface = this; system.init(); information.manufacturer = "Nintendo"; information.name = "Super Famicom"; information.width = 256; information.height = 240; information.overscan = true; information.aspectRatio = 8.0 / 7.0; information.resettable = true; information.capability.states = true; information.capability.cheats = true; media.append({ID::SuperFamicom, "Super Famicom", "sfc", true }); media.append({ID::SuperFamicom, "Game Boy", "gb", false}); media.append({ID::SuperFamicom, "BS Memory", "bs", false}); media.append({ID::SuperFamicom, "Sufami Turbo", "st", false}); Port controllerPort1{ID::Port::Controller1, "Controller Port 1"}; Port controllerPort2{ID::Port::Controller2, "Controller Port 2"}; Port expansionPort{ID::Port::Expansion, "Expansion Port"}; { Device device{ID::Device::None, "None"}; controllerPort1.devices.append(device); controllerPort2.devices.append(device); expansionPort.devices.append(device); } { Device device{ID::Device::Gamepad, "Gamepad"}; device.inputs.append({0, "Up" }); device.inputs.append({0, "Down" }); device.inputs.append({0, "Left" }); device.inputs.append({0, "Right" }); device.inputs.append({0, "B" }); device.inputs.append({0, "A" }); device.inputs.append({0, "Y" }); device.inputs.append({0, "X" }); device.inputs.append({0, "L" }); device.inputs.append({0, "R" }); device.inputs.append({0, "Select"}); device.inputs.append({0, "Start" }); controllerPort1.devices.append(device); controllerPort2.devices.append(device); } { Device device{ID::Device::Multitap, "Multitap"}; for(uint p = 1, n = 0; p <= 4; p++, n += 12) { device.inputs.append({0, {"Port ", p, " - ", "Up" }}); device.inputs.append({0, {"Port ", p, " - ", "Down" }}); device.inputs.append({0, {"Port ", p, " - ", "Left" }}); device.inputs.append({0, {"Port ", p, " - ", "Right" }}); device.inputs.append({0, {"Port ", p, " - ", "B" }}); device.inputs.append({0, {"Port ", p, " - ", "A" }}); device.inputs.append({0, {"Port ", p, " - ", "Y" }}); device.inputs.append({0, {"Port ", p, " - ", "X" }}); device.inputs.append({0, {"Port ", p, " - ", "L" }}); device.inputs.append({0, {"Port ", p, " - ", "R" }}); device.inputs.append({0, {"Port ", p, " - ", "Select"}}); device.inputs.append({0, {"Port ", p, " - ", "Start" }}); } //controllerPort1.devices.append(device); //not used by any commercial software (only homebrew) controllerPort2.devices.append(device); } { Device device{ID::Device::Mouse, "Mouse"}; device.inputs.append({1, "X-axis"}); device.inputs.append({1, "Y-axis"}); device.inputs.append({0, "Left" }); device.inputs.append({0, "Right" }); controllerPort1.devices.append(device); controllerPort2.devices.append(device); } { Device device{ID::Device::SuperScope, "Super Scope"}; device.inputs.append({1, "X-axis" }); device.inputs.append({1, "Y-axis" }); device.inputs.append({0, "Trigger"}); device.inputs.append({0, "Cursor" }); device.inputs.append({0, "Turbo" }); device.inputs.append({0, "Pause" }); controllerPort2.devices.append(device); } { Device device{ID::Device::Justifier, "Justifier"}; device.inputs.append({1, "X-axis" }); device.inputs.append({1, "Y-axis" }); device.inputs.append({0, "Trigger"}); device.inputs.append({0, "Start" }); controllerPort2.devices.append(device); } { Device device{ID::Device::Justifiers, "Justifiers"}; device.inputs.append({1, "Port 1 - X-axis" }); device.inputs.append({1, "Port 1 - Y-axis" }); device.inputs.append({0, "Port 1 - Trigger"}); device.inputs.append({0, "Port 1 - Start" }); device.inputs.append({1, "Port 2 - X-axis" }); device.inputs.append({1, "Port 2 - Y-axis" }); device.inputs.append({0, "Port 2 - Trigger"}); device.inputs.append({0, "Port 2 - Start" }); controllerPort2.devices.append(device); } { Device device{ID::Device::Satellaview, "Satellaview"}; expansionPort.devices.append(device); } { Device device{ID::Device::SuperDisc, "Super Disc"}; expansionPort.devices.append(device); } { Device device{ID::Device::S21FX, "21fx"}; expansionPort.devices.append(device); } ports.append(move(controllerPort1)); ports.append(move(controllerPort2)); ports.append(move(expansionPort)); } auto Interface::manifest() -> string { return cartridge.manifest(); } auto Interface::title() -> string { return cartridge.title(); } auto Interface::videoFrequency() -> double { switch(system.region()) { default: case System::Region::NTSC: return system.cpuFrequency() / (262.0 * 1364.0 - 4.0); case System::Region::PAL: return system.cpuFrequency() / (312.0 * 1364.0); } } auto Interface::videoColors() -> uint32 { return 1 << 19; } auto Interface::videoColor(uint32 color) -> uint64 { uint r = color.bits( 0, 4); uint g = color.bits( 5, 9); uint b = color.bits(10,14); uint l = color.bits(15,18); double L = (1.0 + l) / 16.0 * (l ? 1.0 : 0.5); uint64 R = L * image::normalize(r, 5, 16); uint64 G = L * image::normalize(g, 5, 16); uint64 B = L * image::normalize(b, 5, 16); if(settings.colorEmulation) { static const uint8 gammaRamp[32] = { 0x00, 0x01, 0x03, 0x06, 0x0a, 0x0f, 0x15, 0x1c, 0x24, 0x2d, 0x37, 0x42, 0x4e, 0x5b, 0x69, 0x78, 0x88, 0x90, 0x98, 0xa0, 0xa8, 0xb0, 0xb8, 0xc0, 0xc8, 0xd0, 0xd8, 0xe0, 0xe8, 0xf0, 0xf8, 0xff, }; R = L * gammaRamp[r] * 0x0101; G = L * gammaRamp[g] * 0x0101; B = L * gammaRamp[b] * 0x0101; } return R << 32 | G << 16 | B << 0; } auto Interface::audioFrequency() -> double { return system.apuFrequency() / 768.0; } auto Interface::loaded() -> bool { return system.loaded(); } auto Interface::sha256() -> string { return cartridge.sha256(); } auto Interface::load(uint id) -> bool { if(id == ID::SuperFamicom) return system.load(); if(id == ID::BSMemory) return cartridge.loadBSMemory(); if(id == ID::SufamiTurboA) return cartridge.loadSufamiTurboA(); if(id == ID::SufamiTurboB) return cartridge.loadSufamiTurboB(); return false; } auto Interface::save() -> void { system.save(); } auto Interface::unload() -> void { save(); system.unload(); } auto Interface::connect(uint port, uint device) -> void { SuperFamicom::peripherals.connect(port, device); } auto Interface::power() -> void { system.power(); } auto Interface::reset() -> void { system.reset(); } auto Interface::run() -> void { system.run(); } auto Interface::rtc() -> bool { if(cartridge.has.EpsonRTC) return true; if(cartridge.has.SharpRTC) return true; return false; } auto Interface::rtcsync() -> void { if(cartridge.has.EpsonRTC) epsonrtc.sync(); if(cartridge.has.SharpRTC) sharprtc.sync(); } auto Interface::serialize() -> serializer { system.runToSave(); return system.serialize(); } auto Interface::unserialize(serializer& s) -> bool { return system.unserialize(s); } auto Interface::cheatSet(const lstring& list) -> void { cheat.reset(); #if defined(SFC_SUPERGAMEBOY) if(cartridge.has.ICD2) { GameBoy::cheat.reset(); for(auto& codeset : list) { lstring codes = codeset.split("+"); for(auto& code : codes) { lstring part = code.split("/"); if(part.size() == 2) GameBoy::cheat.append(hex(part[0]), hex(part[1])); if(part.size() == 3) GameBoy::cheat.append(hex(part[0]), hex(part[1]), hex(part[2])); } } return; } #endif for(auto& codeset : list) { lstring codes = codeset.split("+"); for(auto& code : codes) { lstring part = code.split("/"); if(part.size() == 2) cheat.append(hex(part[0]), hex(part[1])); if(part.size() == 3) cheat.append(hex(part[0]), hex(part[1]), hex(part[2])); } } } auto Interface::cap(const string& name) -> bool { if(name == "Blur Emulation") return true; if(name == "Color Emulation") return true; if(name == "Scanline Emulation") return true; return false; } auto Interface::get(const string& name) -> any { if(name == "Blur Emulation") return settings.blurEmulation; 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 == "Blur Emulation" && value.is()) { settings.blurEmulation = value.get(); system.configureVideoEffects(); return true; } if(name == "Color Emulation" && value.is()) { settings.colorEmulation = value.get(); system.configureVideoPalette(); return true; } if(name == "Scanline Emulation" && value.is()) return settings.scanlineEmulation = value.get(), true; return false; } }