diff --git a/higan/emulator/emulator.hpp b/higan/emulator/emulator.hpp index 1c01c7d7..9f0b68c5 100644 --- a/higan/emulator/emulator.hpp +++ b/higan/emulator/emulator.hpp @@ -12,7 +12,7 @@ using namespace nall; namespace Emulator { static const string Name = "higan"; - static const string Version = "102.21"; + static const string Version = "102.22"; static const string Author = "byuu"; static const string License = "GPLv3"; static const string Website = "http://byuu.org/"; diff --git a/higan/emulator/interface.hpp b/higan/emulator/interface.hpp index 621aa931..e76e5a67 100644 --- a/higan/emulator/interface.hpp +++ b/higan/emulator/interface.hpp @@ -44,7 +44,7 @@ struct Interface { //video information struct VideoSize { uint width, height; }; - virtual auto videoSize() -> VideoSize = 0; + virtual auto videoResolution() -> VideoSize = 0; virtual auto videoSize(uint width, uint height, bool arc) -> VideoSize = 0; virtual auto videoColors() -> uint32 = 0; virtual auto videoColor(uint32 color) -> uint64 = 0; @@ -63,7 +63,7 @@ struct Interface { //time functions virtual auto rtc() -> bool { return false; } - virtual auto rtcsync() -> void {} + virtual auto rtcSynchronize() -> void {} //state functions virtual auto serialize() -> serializer = 0; diff --git a/higan/fc/interface/interface.cpp b/higan/fc/interface/interface.cpp index 2071c439..b3a9bbf8 100644 --- a/higan/fc/interface/interface.cpp +++ b/higan/fc/interface/interface.cpp @@ -44,7 +44,7 @@ auto Interface::title() -> string { return cartridge.title(); } -auto Interface::videoSize() -> VideoSize { +auto Interface::videoResolution() -> VideoSize { return {256, 240}; } diff --git a/higan/fc/interface/interface.hpp b/higan/fc/interface/interface.hpp index 9e40734d..5ec7d0fd 100644 --- a/higan/fc/interface/interface.hpp +++ b/higan/fc/interface/interface.hpp @@ -26,7 +26,7 @@ struct Interface : Emulator::Interface { auto manifest() -> string override; auto title() -> string override; - auto videoSize() -> VideoSize override; + auto videoResolution() -> VideoSize override; auto videoSize(uint width, uint height, bool arc) -> VideoSize override; auto videoColors() -> uint32 override; auto videoColor(uint32 color) -> uint64 override; diff --git a/higan/gb/interface/interface.cpp b/higan/gb/interface/interface.cpp index cf8ba67e..30549e26 100644 --- a/higan/gb/interface/interface.cpp +++ b/higan/gb/interface/interface.cpp @@ -33,7 +33,7 @@ auto Interface::title() -> string { return cartridge.title(); } -auto Interface::videoSize() -> VideoSize { +auto Interface::videoResolution() -> VideoSize { return {160, 144}; } diff --git a/higan/gb/interface/interface.hpp b/higan/gb/interface/interface.hpp index 5555da31..0749c550 100644 --- a/higan/gb/interface/interface.hpp +++ b/higan/gb/interface/interface.hpp @@ -23,7 +23,7 @@ struct Interface : Emulator::Interface { auto manifest() -> string override; auto title() -> string override; - auto videoSize() -> VideoSize override; + auto videoResolution() -> VideoSize override; auto videoSize(uint width, uint height, bool arc) -> VideoSize override; auto loaded() -> bool override; diff --git a/higan/gba/apu/apu.cpp b/higan/gba/apu/apu.cpp index 4759afa9..33e5982f 100644 --- a/higan/gba/apu/apu.cpp +++ b/higan/gba/apu/apu.cpp @@ -18,34 +18,36 @@ auto APU::Enter() -> void { } auto APU::main() -> void { + //GBA clock runs at 16777216hz + //GBA PSG channels run at 2097152hz runsequencer(); + step(8); + + //audio PWM output frequency and bit-rate are dependent upon amplitude setting: + //0 = 9-bit @ 32768hz + //1 = 8-bit @ 65536hz + //2 = 7-bit @ 131072hz + //3 = 6-bit @ 262144hz + + //dynamically changing the output frequency under emulation would be difficult; + //always run the output frequency at the maximum 262144hz + if(++clock & 7) return; int lsample = regs.bias.level - 0x0200; int rsample = regs.bias.level - 0x0200; - //(4-bit x 4 -> 6-bit) + 3-bit volume = 9-bit output - if(sequencer.masterenable) { - int lsequence = 0; - if(sequencer.lenable[0]) lsequence += square1.output; - if(sequencer.lenable[1]) lsequence += square2.output; - if(sequencer.lenable[2]) lsequence += wave.output; - if(sequencer.lenable[3]) lsequence += noise.output; - - int rsequence = 0; - if(sequencer.renable[0]) rsequence += square1.output; - if(sequencer.renable[1]) rsequence += square2.output; - if(sequencer.renable[2]) rsequence += wave.output; - if(sequencer.renable[3]) rsequence += noise.output; - - if(sequencer.volume < 3) { - lsample += lsequence * (sequencer.lvolume + 1) >> (2 - sequencer.volume); - rsample += rsequence * (sequencer.rvolume + 1) >> (2 - sequencer.volume); - } + //amplitude: 0 = 32768hz; 1 = 65536hz; 2 = 131072hz; 3 = 262144hz + if((clock & (63 >> (3 - regs.bias.amplitude))) == 0) { + sequencer.sample(); + fifo[0].sample(); + fifo[1].sample(); } - //(8-bit x 2 -> 7-bit) + 1-bit volume = 10-bit output - int fifo0 = fifo[0].output + (1 << fifo[0].volume); - int fifo1 = fifo[1].output + (1 << fifo[1].volume); + lsample += sequencer.loutput; + rsample += sequencer.routput; + + int fifo0 = fifo[0].output << fifo[0].volume; + int fifo1 = fifo[1].output << fifo[1].volume; if(fifo[0].lenable) lsample += fifo0; if(fifo[1].lenable) lsample += fifo1; @@ -53,16 +55,18 @@ auto APU::main() -> void { if(fifo[0].renable) rsample += fifo0; if(fifo[1].renable) rsample += fifo1; - lsample = sclamp<10>(lsample); - rsample = sclamp<10>(rsample); + lsample = sclamp<11>(lsample); + rsample = sclamp<11>(rsample); - if(regs.bias.amplitude == 1) lsample &= ~3, rsample &= ~3; - if(regs.bias.amplitude == 2) lsample &= ~7, rsample &= ~7; - if(regs.bias.amplitude == 3) lsample &= ~15, rsample &= ~15; + //clip 11-bit signed output to more limited output bit-rate + //note: leaving 2-bits more on output to prevent quantization noise + if(regs.bias.amplitude == 0) lsample &= ~0, rsample &= ~0; //9-bit + if(regs.bias.amplitude == 1) lsample &= ~1, rsample &= ~1; //8-bit + if(regs.bias.amplitude == 2) lsample &= ~3, rsample &= ~3; //7-bit + if(regs.bias.amplitude == 3) lsample &= ~7, rsample &= ~7; //6-bit if(cpu.regs.mode == CPU::Registers::Mode::Stop) lsample = 0, rsample = 0; - stream->sample(sclamp<16>(lsample << 6) / 32768.0, sclamp<16>(rsample << 6) / 32768.0); //should be <<5; use <<6 for added volume - step(8); + stream->sample((lsample << 5) / 32768.0, (rsample << 5) / 32768.0); } auto APU::step(uint clocks) -> void { @@ -72,10 +76,11 @@ auto APU::step(uint clocks) -> void { auto APU::power() -> void { create(APU::Enter, 16'777'216); - stream = Emulator::audio.createStream(2, frequency() / 8.0); + stream = Emulator::audio.createStream(2, frequency() / 64.0); stream->addLowPassFilter(20000.0, 3); stream->addHighPassFilter(20.0, 3); + clock = 0; square1.power(); square2.power(); wave.power(); diff --git a/higan/gba/apu/apu.hpp b/higan/gba/apu/apu.hpp index 413a9c04..f2d958df 100644 --- a/higan/gba/apu/apu.hpp +++ b/higan/gba/apu/apu.hpp @@ -14,6 +14,8 @@ struct APU : Thread, IO { auto runsequencer() -> void; auto serialize(serializer&) -> void; + + uint clock; }; extern APU apu; diff --git a/higan/gba/apu/fifo.cpp b/higan/gba/apu/fifo.cpp index 52966c61..641abc3c 100644 --- a/higan/gba/apu/fifo.cpp +++ b/higan/gba/apu/fifo.cpp @@ -1,17 +1,22 @@ +auto APU::FIFO::sample() -> void { + output = active; +} + auto APU::FIFO::read() -> void { if(size == 0) return; size--; - output = sample[rdoffset++]; + active = samples[rdoffset++]; } auto APU::FIFO::write(int8 byte) -> void { if(size == 32) rdoffset++; else size++; - sample[wroffset++] = byte; + samples[wroffset++] = byte; } auto APU::FIFO::reset() -> void { - for(auto& byte : sample) byte = 0; + for(auto& byte : samples) byte = 0; + active = 0; output = 0; rdoffset = 0; diff --git a/higan/gba/apu/registers.hpp b/higan/gba/apu/registers.hpp index 1f9a45d6..1e574369 100644 --- a/higan/gba/apu/registers.hpp +++ b/higan/gba/apu/registers.hpp @@ -3,8 +3,6 @@ struct Registers { uint10 level; uint2 amplitude; } bias; - - uint clock; } regs; struct Sweep { @@ -128,13 +126,19 @@ struct Sequencer { int16 lsample; int16 rsample; + uint10 loutput; + uint10 routput; + + auto sample() -> void; + auto read(uint addr) const -> uint8; auto write(uint addr, uint8 byte) -> void; auto power() -> void; } sequencer; struct FIFO { - int8 sample[32]; + int8 samples[32]; + int8 active; int8 output; uint5 rdoffset; @@ -146,6 +150,7 @@ struct FIFO { uint1 renable; uint1 timer; + auto sample() -> void; auto read() -> void; auto write(int8 byte) -> void; auto reset() -> void; diff --git a/higan/gba/apu/sequencer.cpp b/higan/gba/apu/sequencer.cpp index 71eef05f..071c0149 100644 --- a/higan/gba/apu/sequencer.cpp +++ b/higan/gba/apu/sequencer.cpp @@ -26,6 +26,28 @@ auto APU::runsequencer() -> void { if(noise.enable) noise.run(); } +auto APU::Sequencer::sample() -> void { + loutput = 0; + routput = 0; + if(!masterenable) return; + + if(lenable[0]) loutput += apu.square1.output; + if(lenable[1]) loutput += apu.square2.output; + if(lenable[2]) loutput += apu.wave.output; + if(lenable[3]) loutput += apu.noise.output; + loutput *= 1 + lvolume; + loutput <<= 1; + loutput >>= 3 - volume; + + if(renable[0]) routput += apu.square1.output; + if(renable[1]) routput += apu.square2.output; + if(renable[2]) routput += apu.wave.output; + if(renable[3]) routput += apu.noise.output; + routput *= 1 + rvolume; + routput <<= 1; + routput >>= 3 - volume; +} + auto APU::Sequencer::read(uint addr) const -> uint8 { switch(addr) { case 0: return (rvolume << 0) | (lvolume << 4); diff --git a/higan/gba/apu/serialization.cpp b/higan/gba/apu/serialization.cpp index 0918cf05..496c4fae 100644 --- a/higan/gba/apu/serialization.cpp +++ b/higan/gba/apu/serialization.cpp @@ -1,9 +1,10 @@ auto APU::serialize(serializer& s) -> void { Thread::serialize(s); + s.integer(clock); + s.integer(regs.bias.level); s.integer(regs.bias.amplitude); - s.integer(regs.clock); s.integer(square1.sweep.shift); s.integer(square1.sweep.direction); @@ -91,9 +92,12 @@ auto APU::serialize(serializer& s) -> void { s.integer(sequencer.step); s.integer(sequencer.lsample); s.integer(sequencer.rsample); + s.integer(sequencer.loutput); + s.integer(sequencer.routput); for(auto& f : fifo) { - for(auto& value : f.sample) s.integer(value); + for(auto& value : f.samples) s.integer(value); + s.integer(f.active); s.integer(f.output); s.integer(f.rdoffset); s.integer(f.wroffset); diff --git a/higan/gba/cpu/cpu.cpp b/higan/gba/cpu/cpu.cpp index c1c78c5a..6f6878ed 100644 --- a/higan/gba/cpu/cpu.cpp +++ b/higan/gba/cpu/cpu.cpp @@ -138,9 +138,11 @@ auto CPU::power() -> void { timer.control.irq = 0; timer.control.enable = 0; } + regs.serial = {}; for(auto& flag : regs.keypad.control.flag) flag = 0; regs.keypad.control.enable = 0; regs.keypad.control.condition = 0; + regs.joybus = {}; regs.ime = 0; regs.irq.enable = 0; regs.irq.flag = 0; @@ -149,14 +151,17 @@ auto CPU::power() -> void { regs.wait.control.phi = 0; regs.wait.control.prefetch = 0; regs.wait.control.gametype = 0; //0 = GBA, 1 = GBC - regs.postboot = 0; - regs.mode = Registers::Mode::Normal; - regs.clock = 0; regs.memory.control.disable = 0; regs.memory.control.unknown1 = 0; regs.memory.control.ewram = 1; regs.memory.control.ewramwait = 13; regs.memory.control.unknown2 = 0; + regs.postboot = 0; + regs.mode = Registers::Mode::Normal; + regs.clock = 0; + + prefetch = {}; + prefetch.wait = 1; pending.dma.vblank = 0; pending.dma.hblank = 0; diff --git a/higan/gba/cpu/io.cpp b/higan/gba/cpu/io.cpp index 3140e3f1..ea380bd0 100644 --- a/higan/gba/cpu/io.cpp +++ b/higan/gba/cpu/io.cpp @@ -61,7 +61,9 @@ auto CPU::readIO(uint32 addr) -> uint8 { //KEYINPUT case 0x04000130: { - static const uint lookup[] = {5, 4, 8, 9, 3, 2, 0, 1}; + static const uint landscape[] = {5, 4, 8, 9, 3, 2, 0, 1}; + static const uint portrait[] = {5, 4, 8, 9, 0, 1, 2, 3}; + auto lookup = !settings.rotateLeft ? landscape : portrait; if(auto result = player.keyinput()) return result() >> 0; uint8 result = 0; for(uint n = 0; n < 8; n++) result |= platform->inputPoll(0, 0, lookup[n]) << n; diff --git a/higan/gba/interface/interface.cpp b/higan/gba/interface/interface.cpp index 0b20f54f..e797b274 100644 --- a/higan/gba/interface/interface.cpp +++ b/higan/gba/interface/interface.cpp @@ -39,13 +39,17 @@ auto Interface::title() -> string { return cartridge.title(); } -auto Interface::videoSize() -> VideoSize { - return {240, 160}; +auto Interface::videoResolution() -> VideoSize { + if(!settings.rotateLeft) { + return {240, 160}; + } else { + return {160, 240}; + } } auto Interface::videoSize(uint width, uint height, bool arc) -> VideoSize { - uint w = 240; - uint h = 160; + uint w = videoResolution().width; + uint h = videoResolution().height; uint m = min(width / w, height / h); return {w * m, h * m}; } @@ -113,12 +117,14 @@ auto Interface::unserialize(serializer& s) -> bool { auto Interface::cap(const string& name) -> bool { if(name == "Blur Emulation") return true; if(name == "Color Emulation") return true; + if(name == "Rotate Display") 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 == "Rotate Display") return settings.rotateLeft; return {}; } @@ -135,6 +141,12 @@ auto Interface::set(const string& name, const any& value) -> bool { return true; } + if(name == "Rotate Display" && value.is()) { + settings.rotateLeft = value.get(); + system.configureVideoEffects(); + return true; + } + return false; } diff --git a/higan/gba/interface/interface.hpp b/higan/gba/interface/interface.hpp index 610c2303..51daa379 100644 --- a/higan/gba/interface/interface.hpp +++ b/higan/gba/interface/interface.hpp @@ -23,7 +23,7 @@ struct Interface : Emulator::Interface { auto manifest() -> string override; auto title() -> string override; - auto videoSize() -> VideoSize override; + auto videoResolution() -> VideoSize override; auto videoSize(uint width, uint height, bool arc) -> VideoSize override; auto videoColors() -> uint32 override; auto videoColor(uint32 color) -> uint64 override; @@ -47,6 +47,7 @@ struct Interface : Emulator::Interface { struct Settings { bool blurEmulation = true; bool colorEmulation = true; + bool rotateLeft = false; }; extern Settings settings; diff --git a/higan/gba/system/video.cpp b/higan/gba/system/video.cpp index f77e7ae9..33c13fab 100644 --- a/higan/gba/system/video.cpp +++ b/higan/gba/system/video.cpp @@ -4,4 +4,5 @@ auto System::configureVideoPalette() -> void { auto System::configureVideoEffects() -> void { Emulator::video.setEffect(Emulator::Video::Effect::InterframeBlending, settings.blurEmulation); + Emulator::video.setEffect(Emulator::Video::Effect::RotateLeft, settings.rotateLeft); } diff --git a/higan/md/interface/interface.cpp b/higan/md/interface/interface.cpp index ec7fbfc4..80d003cd 100644 --- a/higan/md/interface/interface.cpp +++ b/higan/md/interface/interface.cpp @@ -55,7 +55,7 @@ auto Interface::title() -> string { return cartridge.title(); } -auto Interface::videoSize() -> VideoSize { +auto Interface::videoResolution() -> VideoSize { return {1280, 480}; } diff --git a/higan/md/interface/interface.hpp b/higan/md/interface/interface.hpp index a7217289..f47c0f7c 100644 --- a/higan/md/interface/interface.hpp +++ b/higan/md/interface/interface.hpp @@ -26,7 +26,7 @@ struct Interface : Emulator::Interface { auto manifest() -> string override; auto title() -> string override; - auto videoSize() -> VideoSize override; + auto videoResolution() -> VideoSize override; auto videoSize(uint width, uint height, bool arc) -> VideoSize override; auto videoColors() -> uint32 override; auto videoColor(uint32 color) -> uint64 override; diff --git a/higan/ms/interface/game-gear.cpp b/higan/ms/interface/game-gear.cpp index fa75feba..7bd1ff65 100644 --- a/higan/ms/interface/game-gear.cpp +++ b/higan/ms/interface/game-gear.cpp @@ -21,7 +21,7 @@ GameGearInterface::GameGearInterface() { ports.append(move(hardware)); } -auto GameGearInterface::videoSize() -> VideoSize { +auto GameGearInterface::videoResolution() -> VideoSize { return {160, 144}; } diff --git a/higan/ms/interface/interface.hpp b/higan/ms/interface/interface.hpp index f7c39339..e861942b 100644 --- a/higan/ms/interface/interface.hpp +++ b/higan/ms/interface/interface.hpp @@ -50,7 +50,7 @@ struct MasterSystemInterface : Interface { MasterSystemInterface(); - auto videoSize() -> VideoSize override; + auto videoResolution() -> VideoSize override; auto videoSize(uint width, uint height, bool arc) -> VideoSize override; auto videoColors() -> uint32 override; auto videoColor(uint32 color) -> uint64 override; @@ -63,7 +63,7 @@ struct GameGearInterface : Interface { GameGearInterface(); - auto videoSize() -> VideoSize override; + auto videoResolution() -> VideoSize override; auto videoSize(uint width, uint height, bool arc) -> VideoSize override; auto videoColors() -> uint32 override; auto videoColor(uint32 color) -> uint64 override; diff --git a/higan/ms/interface/master-system.cpp b/higan/ms/interface/master-system.cpp index 12f3c1a3..480a78b9 100644 --- a/higan/ms/interface/master-system.cpp +++ b/higan/ms/interface/master-system.cpp @@ -36,7 +36,7 @@ MasterSystemInterface::MasterSystemInterface() { ports.append(move(controllerPort2)); } -auto MasterSystemInterface::videoSize() -> VideoSize { +auto MasterSystemInterface::videoResolution() -> VideoSize { return {256, 240}; } diff --git a/higan/pce/interface/interface.cpp b/higan/pce/interface/interface.cpp index f0edf61b..ac495041 100644 --- a/higan/pce/interface/interface.cpp +++ b/higan/pce/interface/interface.cpp @@ -39,14 +39,14 @@ auto Interface::title() -> string { return cartridge.title(); } -auto Interface::videoSize() -> VideoSize { - return {1140, 242}; +auto Interface::videoResolution() -> VideoSize { + return {1140, 240}; } auto Interface::videoSize(uint width, uint height, bool arc) -> VideoSize { auto a = arc ? 8.0 / 7.0 : 1.0; uint w = 285; - uint h = 242; + uint h = 240; uint m = min(width / (w * a), height / h); return {uint(w * a * m), uint(h * m)}; } diff --git a/higan/pce/interface/interface.hpp b/higan/pce/interface/interface.hpp index 545a4162..8e2fd477 100644 --- a/higan/pce/interface/interface.hpp +++ b/higan/pce/interface/interface.hpp @@ -23,7 +23,7 @@ struct Interface : Emulator::Interface { auto manifest() -> string override; auto title() -> string override; - auto videoSize() -> VideoSize override; + auto videoResolution() -> VideoSize override; auto videoSize(uint width, uint height, bool arc) -> VideoSize override; auto videoColors() -> uint32 override; auto videoColor(uint32 color) -> uint64 override; diff --git a/higan/pce/vce/vce.cpp b/higan/pce/vce/vce.cpp index 80a97c35..9d63d7a3 100644 --- a/higan/pce/vce/vce.cpp +++ b/higan/pce/vce/vce.cpp @@ -54,7 +54,7 @@ auto VCE::step(uint clocks) -> void { } auto VCE::refresh() -> void { - Emulator::video.refresh(buffer + 1365 * 13, 1365 * sizeof(uint32), 1140, 242); + Emulator::video.refresh(buffer + 1365 * 13, 1365 * sizeof(uint32), 1140, 240); } auto VCE::power() -> void { diff --git a/higan/sfc/interface/interface.cpp b/higan/sfc/interface/interface.cpp index 83042276..eb3e9c19 100644 --- a/higan/sfc/interface/interface.cpp +++ b/higan/sfc/interface/interface.cpp @@ -118,7 +118,7 @@ auto Interface::title() -> string { return cartridge.title(); } -auto Interface::videoSize() -> VideoSize { +auto Interface::videoResolution() -> VideoSize { return {512, 480}; } @@ -204,7 +204,7 @@ auto Interface::rtc() -> bool { return false; } -auto Interface::rtcsync() -> void { +auto Interface::rtcSynchronize() -> void { if(cartridge.has.EpsonRTC) epsonrtc.sync(); if(cartridge.has.SharpRTC) sharprtc.sync(); } diff --git a/higan/sfc/interface/interface.hpp b/higan/sfc/interface/interface.hpp index 267c42ea..b8406a35 100644 --- a/higan/sfc/interface/interface.hpp +++ b/higan/sfc/interface/interface.hpp @@ -38,7 +38,7 @@ struct Interface : Emulator::Interface { auto manifest() -> string override; auto title() -> string override; - auto videoSize() -> VideoSize override; + auto videoResolution() -> VideoSize override; auto videoSize(uint width, uint height, bool arc) -> VideoSize override; auto videoColors() -> uint32 override; auto videoColor(uint32 color) -> uint64 override; @@ -54,7 +54,7 @@ struct Interface : Emulator::Interface { auto run() -> void override; auto rtc() -> bool override; - auto rtcsync() -> void override; + auto rtcSynchronize() -> void override; auto serialize() -> serializer override; auto unserialize(serializer&) -> bool override; diff --git a/higan/target-tomoko/input/hotkeys.cpp b/higan/target-tomoko/input/hotkeys.cpp index b1fc44a1..e86d92e5 100644 --- a/higan/target-tomoko/input/hotkeys.cpp +++ b/higan/target-tomoko/input/hotkeys.cpp @@ -60,6 +60,14 @@ auto InputManager::appendHotkeys() -> void { hotkeys.append(hotkey); } + { auto hotkey = new InputHotkey; + hotkey->name = "Rotate Display"; + hotkey->press = [] { + program->rotateDisplay(); + }; + hotkeys.append(hotkey); + } + for(auto& hotkey : hotkeys) { hotkey->path = string{"Hotkey/", hotkey->name}.replace(" ", ""); hotkey->assignment = settings(hotkey->path).text(); diff --git a/higan/target-tomoko/input/input.hpp b/higan/target-tomoko/input/input.hpp index 4a61d258..f1012fa8 100644 --- a/higan/target-tomoko/input/input.hpp +++ b/higan/target-tomoko/input/input.hpp @@ -30,7 +30,7 @@ struct InputMapping { }; struct InputHotkey : InputMapping { - auto logic() const -> Logic override { return Logic::AND; } +//auto logic() const -> Logic override { return Logic::AND; } function void> press; function void> release; diff --git a/higan/target-tomoko/presentation/presentation.cpp b/higan/target-tomoko/presentation/presentation.cpp index ce4e5f6b..ef6ee21f 100644 --- a/higan/target-tomoko/presentation/presentation.cpp +++ b/higan/target-tomoko/presentation/presentation.cpp @@ -109,7 +109,20 @@ Presentation::Presentation() { statusBar.setVisible(showStatusBar.checked()); if(visible()) resizeViewport(); }); - showConfiguration.setText("Configuration ...").onActivate([&] { settingsManager->show(2); }); + showConfiguration.setText("Configuration ...").onActivate([&] { + //if no emulation core active; default to hotkeys panel + if(!emulator) return settingsManager->show(3); + + //default to input panel with current core's input settings active + for(auto item : settingsManager->input.emulatorList.items()) { + if(systemMenu.text() == item.text()) { + item.setSelected(); + settingsManager->input.emulatorList.doChange(); + break; + } + } + settingsManager->show(2); + }); toolsMenu.setText("Tools").setVisible(false); saveStateMenu.setText("Save State"); @@ -238,7 +251,7 @@ auto Presentation::resizeViewport() -> void { bool aspectCorrection = true; if(!fullScreen()) { windowWidth = 326 * scale; - windowHeight = 242 * scale; + windowHeight = 240 * scale; aspectCorrection = settings["Video/AspectCorrection"].boolean(); } else { windowWidth = geometry().width(); diff --git a/higan/target-tomoko/program/program.hpp b/higan/target-tomoko/program/program.hpp index d23f67b9..7c1eae70 100644 --- a/higan/target-tomoko/program/program.hpp +++ b/higan/target-tomoko/program/program.hpp @@ -27,6 +27,7 @@ struct Program : Emulator::Platform { //utility.cpp auto powerCycle() -> void; + auto rotateDisplay() -> void; auto connectDevices() -> void; auto showMessage(const string& text) -> void; auto updateStatusText() -> void; diff --git a/higan/target-tomoko/program/utility.cpp b/higan/target-tomoko/program/utility.cpp index 3b6b9cbb..ae1c3114 100644 --- a/higan/target-tomoko/program/utility.cpp +++ b/higan/target-tomoko/program/utility.cpp @@ -4,6 +4,15 @@ auto Program::powerCycle() -> void { showMessage("Power cycled"); } +auto Program::rotateDisplay() -> void { + if(!emulator) return; + if(!emulator->cap("Rotate Display")) return showMessage("Display rotation not supported"); + auto rotate = emulator->get("Rotate Display"); + emulator->set("Rotate Display", !rotate.get()); + presentation->resizeViewport(); + showMessage("Display rotated"); +} + auto Program::connectDevices() -> void { if(!emulator) return; for(auto& port : emulator->ports) { diff --git a/higan/video/video.cpp b/higan/video/video.cpp index fe0af5ef..502a7741 100644 --- a/higan/video/video.cpp +++ b/higan/video/video.cpp @@ -12,14 +12,17 @@ Video::~Video() { auto Video::reset() -> void { interface = nullptr; sprites.reset(); - delete output; - output = nullptr; + delete buffer; + buffer = nullptr; + delete rotate; + rotate = nullptr; delete palette; palette = nullptr; width = 0; height = 0; effects.colorBleed = false; effects.interframeBlending = false; + effects.rotateLeft = false; } auto Video::setInterface(Interface* interface) -> void { @@ -85,6 +88,10 @@ auto Video::setEffect(Effect effect, const any& value) -> void { if(effect == Effect::InterframeBlending && value.is()) { effects.interframeBlending = value.get(); } + + if(effect == Effect::RotateLeft && value.is()) { + effects.rotateLeft = value.get(); + } } auto Video::createSprite(uint width, uint height) -> shared_pointer { @@ -105,13 +112,17 @@ auto Video::removeSprite(shared_pointer sprite) -> bool { auto Video::refresh(uint32* input, uint pitch, uint width, uint height) -> void { if(this->width != width || this->height != height) { - delete output; - output = new uint32[width * height](); + delete buffer; + delete rotate; + buffer = new uint32[width * height](); + rotate = new uint32[height * width](); this->width = width; this->height = height; } + auto output = buffer; pitch >>= 2; //bytes to words + for(uint y : range(height)) { auto source = input + y * pitch; auto target = output + y * width; @@ -141,6 +152,18 @@ auto Video::refresh(uint32* input, uint pitch, uint width, uint height) -> void } } + if(effects.rotateLeft) { + for(uint y : range(height)) { + auto source = buffer + y * width; + for(uint x : range(width)) { + auto target = rotate + (width - 1 - x) * height + y; + *target = *source++; + } + } + output = rotate; + swap(width, height); + } + for(auto& sprite : sprites) { if(!sprite->visible) continue; diff --git a/higan/video/video.hpp b/higan/video/video.hpp index 885b175b..dd97d505 100644 --- a/higan/video/video.hpp +++ b/higan/video/video.hpp @@ -10,6 +10,7 @@ struct Video { enum class Effect : uint { ColorBleed, InterframeBlending, + RotateLeft, }; ~Video(); @@ -33,7 +34,8 @@ private: Interface* interface = nullptr; vector> sprites; - uint32* output = nullptr; + uint32* buffer = nullptr; + uint32* rotate = nullptr; uint32* palette = nullptr; uint width = 0; @@ -47,6 +49,7 @@ private: struct Effects { bool colorBleed = false; bool interframeBlending = false; + bool rotateLeft = false; } effects; friend class Sprite; diff --git a/higan/ws/interface/interface.cpp b/higan/ws/interface/interface.cpp index ed5ac26f..3cd26103 100644 --- a/higan/ws/interface/interface.cpp +++ b/higan/ws/interface/interface.cpp @@ -7,8 +7,7 @@ Settings settings; #include "wonderswan-color.cpp" Interface::Interface() { - Port hardwareHorizontalPort{ID::Port::HardwareHorizontal, "Hardware - Horizontal"}; - Port hardwareVerticalPort{ID::Port::HardwareVertical, "Hardware - Vertical"}; + Port hardwarePort{ID::Port::Hardware, "Hardware"}; { Device device{ID::Device::Controls, "Controls"}; device.inputs.append({0, "Y1"}); @@ -22,13 +21,10 @@ Interface::Interface() { device.inputs.append({0, "B"}); device.inputs.append({0, "A"}); device.inputs.append({0, "Start"}); - device.inputs.append({0, "Rotate"}); - hardwareHorizontalPort.devices.append(device); - hardwareVerticalPort.devices.append(device); + hardwarePort.devices.append(device); } - ports.append(move(hardwareHorizontalPort)); - ports.append(move(hardwareVerticalPort)); + ports.append(move(hardwarePort)); } auto Interface::manifest() -> string { @@ -39,13 +35,17 @@ auto Interface::title() -> string { return cartridge.information.title; } -auto Interface::videoSize() -> VideoSize { - return {224, 224}; +auto Interface::videoResolution() -> VideoSize { + if(!settings.rotateLeft) { + return {224, 144}; + } else { + return {144, 224}; + } } auto Interface::videoSize(uint width, uint height, bool arc) -> VideoSize { - uint w = 224; - uint h = 224; + uint w = videoResolution().width; + uint h = videoResolution().height; uint m = min(width / w, height / h); return {w * m, h * m}; } @@ -91,12 +91,14 @@ auto Interface::cheatSet(const string_vector& list) -> void { auto Interface::cap(const string& name) -> bool { if(name == "Blur Emulation") return true; if(name == "Color Emulation") return true; + if(name == "Rotate Display") 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 == "Rotate Display") return settings.rotateLeft; return {}; } @@ -113,6 +115,12 @@ auto Interface::set(const string& name, const any& value) -> bool { return true; } + if(name == "Rotate Display" && value.is()) { + settings.rotateLeft = value.get(); + system.configureVideoEffects(); + return true; + } + return false; } diff --git a/higan/ws/interface/interface.hpp b/higan/ws/interface/interface.hpp index 2bb75f33..e85af6c1 100644 --- a/higan/ws/interface/interface.hpp +++ b/higan/ws/interface/interface.hpp @@ -8,8 +8,7 @@ struct ID { }; struct Port { enum : uint { - HardwareHorizontal, - HardwareVertical, + Hardware, };}; struct Device { enum : uint { @@ -23,7 +22,7 @@ struct Interface : Emulator::Interface { auto manifest() -> string override; auto title() -> string override; - auto videoSize() -> VideoSize override; + auto videoResolution() -> VideoSize override; auto videoSize(uint width, uint height, bool arc) -> VideoSize override; auto loaded() -> bool override; @@ -69,6 +68,7 @@ struct WonderSwanColorInterface : Interface { struct Settings { bool blurEmulation = true; bool colorEmulation = true; + bool rotateLeft = false; }; extern Settings settings; diff --git a/higan/ws/ppu/ppu.cpp b/higan/ws/ppu/ppu.cpp index 80279834..5286ae58 100644 --- a/higan/ws/ppu/ppu.cpp +++ b/higan/ws/ppu/ppu.cpp @@ -28,10 +28,7 @@ auto PPU::main() -> void { if(l.screenTwoEnable) renderScreenTwo(); if(l.spriteEnable) renderSprite(); } - switch(l.orientation) { - case 0: output[(s.vclk + 40) * 224 + s.hclk] = s.pixel.color; break; - case 1: output[(223 - s.hclk) * 224 + (s.vclk + 40)] = s.pixel.color; break; - } + output[s.vclk * 224 + x] = s.pixel.color; step(1); } step(32); @@ -76,14 +73,10 @@ auto PPU::frame() -> void { s.field = !s.field; s.vclk = 0; scheduler.exit(Scheduler::Event::Frame); - if(l.orientation != system.orientation()) { - l.orientation = system.orientation(); - memory::fill(output, 224 * 224 * sizeof(uint32)); - } } auto PPU::refresh() -> void { - Emulator::video.refresh(output, 224 * sizeof(uint32), 224, 224); + Emulator::video.refresh(output, 224 * sizeof(uint32), 224, 144); } auto PPU::step(uint clocks) -> void { diff --git a/higan/ws/ppu/ppu.hpp b/higan/ws/ppu/ppu.hpp index 4a2f94f1..15d432a7 100644 --- a/higan/ws/ppu/ppu.hpp +++ b/higan/ws/ppu/ppu.hpp @@ -35,7 +35,7 @@ struct PPU : Thread, IO { uint12 color; }; - uint32 output[224 * 224]; + uint32 output[224 * 144]; struct State { bool field; @@ -45,9 +45,6 @@ struct PPU : Thread, IO { } s; struct Latches { - //frame(), power() - bool orientation; - //latchRegisters() uint8 backColor; diff --git a/higan/ws/ppu/serialization.cpp b/higan/ws/ppu/serialization.cpp index 5ed28a14..e0f682c3 100644 --- a/higan/ws/ppu/serialization.cpp +++ b/higan/ws/ppu/serialization.cpp @@ -7,7 +7,6 @@ auto PPU::serialize(serializer& s) -> void { s.integer((uint&)this->s.pixel.source); s.integer(this->s.pixel.color); - s.integer(l.orientation); s.integer(l.backColor); s.integer(l.screenOneEnable); s.integer(l.screenOneMapBase); diff --git a/higan/ws/system/system.cpp b/higan/ws/system/system.cpp index 77f7e8c3..865615a3 100644 --- a/higan/ws/system/system.cpp +++ b/higan/ws/system/system.cpp @@ -41,7 +41,7 @@ auto System::load(Emulator::Interface* interface, Model model) -> bool { if(!cartridge.load()) return false; serializeInit(); - _orientation = cartridge.information.orientation; + settings.rotateLeft = cartridge.information.orientation; this->interface = interface; return _loaded = true; } @@ -103,22 +103,24 @@ auto System::runToSave() -> void { } auto System::pollKeypad() -> void { - uint port = !_orientation ? ID::Port::HardwareHorizontal : ID::Port::HardwareVertical; - uint device = ID::Device::Controls; - bool rotate = keypad.rotate; + const uint landscape[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + const uint portrait [] = {7, 4, 5, 6, 3, 0, 1, 2, 8, 9, 10}; - keypad.y1 = platform->inputPoll(port, device, 0); - keypad.y2 = platform->inputPoll(port, device, 1); - keypad.y3 = platform->inputPoll(port, device, 2); - keypad.y4 = platform->inputPoll(port, device, 3); - keypad.x1 = platform->inputPoll(port, device, 4); - keypad.x2 = platform->inputPoll(port, device, 5); - keypad.x3 = platform->inputPoll(port, device, 6); - keypad.x4 = platform->inputPoll(port, device, 7); - keypad.b = platform->inputPoll(port, device, 8); - keypad.a = platform->inputPoll(port, device, 9); - keypad.start = platform->inputPoll(port, device, 10); - keypad.rotate = platform->inputPoll(port, device, 11); + uint port = ID::Port::Hardware; + uint device = ID::Device::Controls; + auto id = !settings.rotateLeft ? landscape : portrait; + + keypad.y1 = platform->inputPoll(port, device, id[0]); + keypad.y2 = platform->inputPoll(port, device, id[1]); + keypad.y3 = platform->inputPoll(port, device, id[2]); + keypad.y4 = platform->inputPoll(port, device, id[3]); + keypad.x1 = platform->inputPoll(port, device, id[4]); + keypad.x2 = platform->inputPoll(port, device, id[5]); + keypad.x3 = platform->inputPoll(port, device, id[6]); + keypad.x4 = platform->inputPoll(port, device, id[7]); + keypad.b = platform->inputPoll(port, device, id[8]); + keypad.a = platform->inputPoll(port, device, id[9]); + keypad.start = platform->inputPoll(port, device, id[10]); if(keypad.y1 || keypad.y2 || keypad.y3 || keypad.y4 || keypad.x1 || keypad.x2 || keypad.x3 || keypad.x4 @@ -126,10 +128,6 @@ auto System::pollKeypad() -> void { ) { cpu.raise(CPU::Interrupt::Input); } - - if(!rotate && keypad.rotate) { - _orientation = !_orientation; - } } } diff --git a/higan/ws/system/system.hpp b/higan/ws/system/system.hpp index 1653ccc7..cace5ba6 100644 --- a/higan/ws/system/system.hpp +++ b/higan/ws/system/system.hpp @@ -1,7 +1,6 @@ struct System : IO { auto loaded() const -> bool { return _loaded; } auto model() const -> Model { return _model; } - auto orientation() const -> bool { return _orientation; } auto color() const -> bool { return r.color; } auto planar() const -> bool { return r.format == 0; } auto packed() const -> bool { return r.format == 1; } @@ -58,7 +57,6 @@ private: bool _loaded = false; Model _model = Model::WonderSwan; - bool _orientation = 0; //0 = horizontal, 1 = vertical uint _serializeSize = 0; }; diff --git a/higan/ws/system/video.cpp b/higan/ws/system/video.cpp index f77e7ae9..33c13fab 100644 --- a/higan/ws/system/video.cpp +++ b/higan/ws/system/video.cpp @@ -4,4 +4,5 @@ auto System::configureVideoPalette() -> void { auto System::configureVideoEffects() -> void { Emulator::video.setEffect(Emulator::Video::Effect::InterframeBlending, settings.blurEmulation); + Emulator::video.setEffect(Emulator::Video::Effect::RotateLeft, settings.rotateLeft); } diff --git a/nall/serializer.hpp b/nall/serializer.hpp index 0704b176..0f9c4547 100644 --- a/nall/serializer.hpp +++ b/nall/serializer.hpp @@ -61,6 +61,17 @@ struct serializer { return *this; } + template auto boolean(T& value) -> serializer& { + if(_mode == Save) { + _data[_size++] = (bool)value; + } else if(_mode == Load) { + value = (bool)_data[_size++]; + } else if(_mode == Size) { + _size += 1; + } + return *this; + } + template auto integer(T& value) -> serializer& { enum : uint { size = std::is_same::value ? 1 : sizeof(T) }; if(_mode == Save) {