Update to v102r22 release.

byuu says:

Changelog:

  - higan: Emulator::Interface::videoSize() renamed to videoResolution()
  - higan: Emulator::Interface::rtcsync() renamed to rtcSynchronize()
  - higan: added video display rotation support to Video
  - GBA: substantially improved audio mixing
      - fixed bug with FIFO 50%/100% volume setting
      - now properly using SOUNDBIAS amplitude to control output
        frequencies
      - reduced quantization noise
      - corrected relative volumes between PSG and FIFO channels
      - both PSG and FIFO values cached based on amplitude; resulting in
        cleaner PCM samples
      - treating PSG volume=3 as 200% volume instead of 0% volume now
        (unverified: to match mGBA)
  - GBA: properly initialize ALL CPU state; including the vital
    prefetch.wait=1 (fixes Classic NES series games)
  - GBA: added video rotation with automatic key translation support
  - PCE: reduced output resolution scalar from 285x242 to 285x240
      - the extra two scanlines won't be visible on most TVs; and they
        make all other cores look worse
      - this is because all other cores output at 240p or less; so they
        were all receiving black bars in windowed mode
  - tomoko: added "Rotate Display" hotkey setting
  - tomoko: changed hotkey multi-key logic to OR instead of AND
      - left support for flipping it back inside the core; for those so
        inclined; by uncommenting one line in input.hpp
  - tomoko: when choosing Settings→Configuration, it will
    automatically select the currently loaded system
      - for instance, if you're playing a Game Gear game, it'll take you
        to the Game Gear input settings
      - if no games are loaded, it will take you to the hotkeys panel
        instead
  - WS(C): merged "Hardware-Vertical", "Hardware-Horizontal" controls
    into combined "Hardware"
  - WS(C): converted rotation support from being inside the core to
    using Emulator::Video
      - this lets WS(C) video content scale larger now that it's not
        bounded by a 224x224 square box
  - WS(C): added automatic key rotation support
  - WS(C): removed emulator "Rotate" key (use the general hotkey
    instead; I recommend F8 for this)
  - nall: added serializer support for nall::Boolean (boolean) types
      - although I will probably prefer the usage of uint1 in most cases
This commit is contained in:
Tim Allen 2017-06-09 00:05:48 +10:00
parent a4629e1f64
commit 8af3e4a6e2
43 changed files with 252 additions and 126 deletions

View File

@ -12,7 +12,7 @@ using namespace nall;
namespace Emulator { namespace Emulator {
static const string Name = "higan"; 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 Author = "byuu";
static const string License = "GPLv3"; static const string License = "GPLv3";
static const string Website = "http://byuu.org/"; static const string Website = "http://byuu.org/";

View File

@ -44,7 +44,7 @@ struct Interface {
//video information //video information
struct VideoSize { uint width, height; }; 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 videoSize(uint width, uint height, bool arc) -> VideoSize = 0;
virtual auto videoColors() -> uint32 = 0; virtual auto videoColors() -> uint32 = 0;
virtual auto videoColor(uint32 color) -> uint64 = 0; virtual auto videoColor(uint32 color) -> uint64 = 0;
@ -63,7 +63,7 @@ struct Interface {
//time functions //time functions
virtual auto rtc() -> bool { return false; } virtual auto rtc() -> bool { return false; }
virtual auto rtcsync() -> void {} virtual auto rtcSynchronize() -> void {}
//state functions //state functions
virtual auto serialize() -> serializer = 0; virtual auto serialize() -> serializer = 0;

View File

@ -44,7 +44,7 @@ auto Interface::title() -> string {
return cartridge.title(); return cartridge.title();
} }
auto Interface::videoSize() -> VideoSize { auto Interface::videoResolution() -> VideoSize {
return {256, 240}; return {256, 240};
} }

View File

@ -26,7 +26,7 @@ struct Interface : Emulator::Interface {
auto manifest() -> string override; auto manifest() -> string override;
auto title() -> 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 videoSize(uint width, uint height, bool arc) -> VideoSize override;
auto videoColors() -> uint32 override; auto videoColors() -> uint32 override;
auto videoColor(uint32 color) -> uint64 override; auto videoColor(uint32 color) -> uint64 override;

View File

@ -33,7 +33,7 @@ auto Interface::title() -> string {
return cartridge.title(); return cartridge.title();
} }
auto Interface::videoSize() -> VideoSize { auto Interface::videoResolution() -> VideoSize {
return {160, 144}; return {160, 144};
} }

View File

@ -23,7 +23,7 @@ struct Interface : Emulator::Interface {
auto manifest() -> string override; auto manifest() -> string override;
auto title() -> 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 videoSize(uint width, uint height, bool arc) -> VideoSize override;
auto loaded() -> bool override; auto loaded() -> bool override;

View File

@ -18,34 +18,36 @@ auto APU::Enter() -> void {
} }
auto APU::main() -> void { auto APU::main() -> void {
//GBA clock runs at 16777216hz
//GBA PSG channels run at 2097152hz
runsequencer(); 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 lsample = regs.bias.level - 0x0200;
int rsample = regs.bias.level - 0x0200; int rsample = regs.bias.level - 0x0200;
//(4-bit x 4 -> 6-bit) + 3-bit volume = 9-bit output //amplitude: 0 = 32768hz; 1 = 65536hz; 2 = 131072hz; 3 = 262144hz
if(sequencer.masterenable) { if((clock & (63 >> (3 - regs.bias.amplitude))) == 0) {
int lsequence = 0; sequencer.sample();
if(sequencer.lenable[0]) lsequence += square1.output; fifo[0].sample();
if(sequencer.lenable[1]) lsequence += square2.output; fifo[1].sample();
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);
}
} }
//(8-bit x 2 -> 7-bit) + 1-bit volume = 10-bit output lsample += sequencer.loutput;
int fifo0 = fifo[0].output + (1 << fifo[0].volume); rsample += sequencer.routput;
int fifo1 = fifo[1].output + (1 << fifo[1].volume);
int fifo0 = fifo[0].output << fifo[0].volume;
int fifo1 = fifo[1].output << fifo[1].volume;
if(fifo[0].lenable) lsample += fifo0; if(fifo[0].lenable) lsample += fifo0;
if(fifo[1].lenable) lsample += fifo1; if(fifo[1].lenable) lsample += fifo1;
@ -53,16 +55,18 @@ auto APU::main() -> void {
if(fifo[0].renable) rsample += fifo0; if(fifo[0].renable) rsample += fifo0;
if(fifo[1].renable) rsample += fifo1; if(fifo[1].renable) rsample += fifo1;
lsample = sclamp<10>(lsample); lsample = sclamp<11>(lsample);
rsample = sclamp<10>(rsample); rsample = sclamp<11>(rsample);
if(regs.bias.amplitude == 1) lsample &= ~3, rsample &= ~3; //clip 11-bit signed output to more limited output bit-rate
if(regs.bias.amplitude == 2) lsample &= ~7, rsample &= ~7; //note: leaving 2-bits more on output to prevent quantization noise
if(regs.bias.amplitude == 3) lsample &= ~15, rsample &= ~15; 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; 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 stream->sample((lsample << 5) / 32768.0, (rsample << 5) / 32768.0);
step(8);
} }
auto APU::step(uint clocks) -> void { auto APU::step(uint clocks) -> void {
@ -72,10 +76,11 @@ auto APU::step(uint clocks) -> void {
auto APU::power() -> void { auto APU::power() -> void {
create(APU::Enter, 16'777'216); 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->addLowPassFilter(20000.0, 3);
stream->addHighPassFilter(20.0, 3); stream->addHighPassFilter(20.0, 3);
clock = 0;
square1.power(); square1.power();
square2.power(); square2.power();
wave.power(); wave.power();

View File

@ -14,6 +14,8 @@ struct APU : Thread, IO {
auto runsequencer() -> void; auto runsequencer() -> void;
auto serialize(serializer&) -> void; auto serialize(serializer&) -> void;
uint clock;
}; };
extern APU apu; extern APU apu;

View File

@ -1,17 +1,22 @@
auto APU::FIFO::sample() -> void {
output = active;
}
auto APU::FIFO::read() -> void { auto APU::FIFO::read() -> void {
if(size == 0) return; if(size == 0) return;
size--; size--;
output = sample[rdoffset++]; active = samples[rdoffset++];
} }
auto APU::FIFO::write(int8 byte) -> void { auto APU::FIFO::write(int8 byte) -> void {
if(size == 32) rdoffset++; if(size == 32) rdoffset++;
else size++; else size++;
sample[wroffset++] = byte; samples[wroffset++] = byte;
} }
auto APU::FIFO::reset() -> void { auto APU::FIFO::reset() -> void {
for(auto& byte : sample) byte = 0; for(auto& byte : samples) byte = 0;
active = 0;
output = 0; output = 0;
rdoffset = 0; rdoffset = 0;

View File

@ -3,8 +3,6 @@ struct Registers {
uint10 level; uint10 level;
uint2 amplitude; uint2 amplitude;
} bias; } bias;
uint clock;
} regs; } regs;
struct Sweep { struct Sweep {
@ -128,13 +126,19 @@ struct Sequencer {
int16 lsample; int16 lsample;
int16 rsample; int16 rsample;
uint10 loutput;
uint10 routput;
auto sample() -> void;
auto read(uint addr) const -> uint8; auto read(uint addr) const -> uint8;
auto write(uint addr, uint8 byte) -> void; auto write(uint addr, uint8 byte) -> void;
auto power() -> void; auto power() -> void;
} sequencer; } sequencer;
struct FIFO { struct FIFO {
int8 sample[32]; int8 samples[32];
int8 active;
int8 output; int8 output;
uint5 rdoffset; uint5 rdoffset;
@ -146,6 +150,7 @@ struct FIFO {
uint1 renable; uint1 renable;
uint1 timer; uint1 timer;
auto sample() -> void;
auto read() -> void; auto read() -> void;
auto write(int8 byte) -> void; auto write(int8 byte) -> void;
auto reset() -> void; auto reset() -> void;

View File

@ -26,6 +26,28 @@ auto APU::runsequencer() -> void {
if(noise.enable) noise.run(); 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 { auto APU::Sequencer::read(uint addr) const -> uint8 {
switch(addr) { switch(addr) {
case 0: return (rvolume << 0) | (lvolume << 4); case 0: return (rvolume << 0) | (lvolume << 4);

View File

@ -1,9 +1,10 @@
auto APU::serialize(serializer& s) -> void { auto APU::serialize(serializer& s) -> void {
Thread::serialize(s); Thread::serialize(s);
s.integer(clock);
s.integer(regs.bias.level); s.integer(regs.bias.level);
s.integer(regs.bias.amplitude); s.integer(regs.bias.amplitude);
s.integer(regs.clock);
s.integer(square1.sweep.shift); s.integer(square1.sweep.shift);
s.integer(square1.sweep.direction); s.integer(square1.sweep.direction);
@ -91,9 +92,12 @@ auto APU::serialize(serializer& s) -> void {
s.integer(sequencer.step); s.integer(sequencer.step);
s.integer(sequencer.lsample); s.integer(sequencer.lsample);
s.integer(sequencer.rsample); s.integer(sequencer.rsample);
s.integer(sequencer.loutput);
s.integer(sequencer.routput);
for(auto& f : fifo) { 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.output);
s.integer(f.rdoffset); s.integer(f.rdoffset);
s.integer(f.wroffset); s.integer(f.wroffset);

View File

@ -138,9 +138,11 @@ auto CPU::power() -> void {
timer.control.irq = 0; timer.control.irq = 0;
timer.control.enable = 0; timer.control.enable = 0;
} }
regs.serial = {};
for(auto& flag : regs.keypad.control.flag) flag = 0; for(auto& flag : regs.keypad.control.flag) flag = 0;
regs.keypad.control.enable = 0; regs.keypad.control.enable = 0;
regs.keypad.control.condition = 0; regs.keypad.control.condition = 0;
regs.joybus = {};
regs.ime = 0; regs.ime = 0;
regs.irq.enable = 0; regs.irq.enable = 0;
regs.irq.flag = 0; regs.irq.flag = 0;
@ -149,14 +151,17 @@ auto CPU::power() -> void {
regs.wait.control.phi = 0; regs.wait.control.phi = 0;
regs.wait.control.prefetch = 0; regs.wait.control.prefetch = 0;
regs.wait.control.gametype = 0; //0 = GBA, 1 = GBC 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.disable = 0;
regs.memory.control.unknown1 = 0; regs.memory.control.unknown1 = 0;
regs.memory.control.ewram = 1; regs.memory.control.ewram = 1;
regs.memory.control.ewramwait = 13; regs.memory.control.ewramwait = 13;
regs.memory.control.unknown2 = 0; 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.vblank = 0;
pending.dma.hblank = 0; pending.dma.hblank = 0;

View File

@ -61,7 +61,9 @@ auto CPU::readIO(uint32 addr) -> uint8 {
//KEYINPUT //KEYINPUT
case 0x04000130: { 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; if(auto result = player.keyinput()) return result() >> 0;
uint8 result = 0; uint8 result = 0;
for(uint n = 0; n < 8; n++) result |= platform->inputPoll(0, 0, lookup[n]) << n; for(uint n = 0; n < 8; n++) result |= platform->inputPoll(0, 0, lookup[n]) << n;

View File

@ -39,13 +39,17 @@ auto Interface::title() -> string {
return cartridge.title(); return cartridge.title();
} }
auto Interface::videoSize() -> VideoSize { auto Interface::videoResolution() -> VideoSize {
return {240, 160}; if(!settings.rotateLeft) {
return {240, 160};
} else {
return {160, 240};
}
} }
auto Interface::videoSize(uint width, uint height, bool arc) -> VideoSize { auto Interface::videoSize(uint width, uint height, bool arc) -> VideoSize {
uint w = 240; uint w = videoResolution().width;
uint h = 160; uint h = videoResolution().height;
uint m = min(width / w, height / h); uint m = min(width / w, height / h);
return {w * m, h * m}; return {w * m, h * m};
} }
@ -113,12 +117,14 @@ auto Interface::unserialize(serializer& s) -> bool {
auto Interface::cap(const string& name) -> bool { auto Interface::cap(const string& name) -> bool {
if(name == "Blur Emulation") return true; if(name == "Blur Emulation") return true;
if(name == "Color Emulation") return true; if(name == "Color Emulation") return true;
if(name == "Rotate Display") return true;
return false; return false;
} }
auto Interface::get(const string& name) -> any { auto Interface::get(const string& name) -> any {
if(name == "Blur Emulation") return settings.blurEmulation; if(name == "Blur Emulation") return settings.blurEmulation;
if(name == "Color Emulation") return settings.colorEmulation; if(name == "Color Emulation") return settings.colorEmulation;
if(name == "Rotate Display") return settings.rotateLeft;
return {}; return {};
} }
@ -135,6 +141,12 @@ auto Interface::set(const string& name, const any& value) -> bool {
return true; return true;
} }
if(name == "Rotate Display" && value.is<bool>()) {
settings.rotateLeft = value.get<bool>();
system.configureVideoEffects();
return true;
}
return false; return false;
} }

View File

@ -23,7 +23,7 @@ struct Interface : Emulator::Interface {
auto manifest() -> string override; auto manifest() -> string override;
auto title() -> 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 videoSize(uint width, uint height, bool arc) -> VideoSize override;
auto videoColors() -> uint32 override; auto videoColors() -> uint32 override;
auto videoColor(uint32 color) -> uint64 override; auto videoColor(uint32 color) -> uint64 override;
@ -47,6 +47,7 @@ struct Interface : Emulator::Interface {
struct Settings { struct Settings {
bool blurEmulation = true; bool blurEmulation = true;
bool colorEmulation = true; bool colorEmulation = true;
bool rotateLeft = false;
}; };
extern Settings settings; extern Settings settings;

View File

@ -4,4 +4,5 @@ auto System::configureVideoPalette() -> void {
auto System::configureVideoEffects() -> void { auto System::configureVideoEffects() -> void {
Emulator::video.setEffect(Emulator::Video::Effect::InterframeBlending, settings.blurEmulation); Emulator::video.setEffect(Emulator::Video::Effect::InterframeBlending, settings.blurEmulation);
Emulator::video.setEffect(Emulator::Video::Effect::RotateLeft, settings.rotateLeft);
} }

View File

@ -55,7 +55,7 @@ auto Interface::title() -> string {
return cartridge.title(); return cartridge.title();
} }
auto Interface::videoSize() -> VideoSize { auto Interface::videoResolution() -> VideoSize {
return {1280, 480}; return {1280, 480};
} }

View File

@ -26,7 +26,7 @@ struct Interface : Emulator::Interface {
auto manifest() -> string override; auto manifest() -> string override;
auto title() -> 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 videoSize(uint width, uint height, bool arc) -> VideoSize override;
auto videoColors() -> uint32 override; auto videoColors() -> uint32 override;
auto videoColor(uint32 color) -> uint64 override; auto videoColor(uint32 color) -> uint64 override;

View File

@ -21,7 +21,7 @@ GameGearInterface::GameGearInterface() {
ports.append(move(hardware)); ports.append(move(hardware));
} }
auto GameGearInterface::videoSize() -> VideoSize { auto GameGearInterface::videoResolution() -> VideoSize {
return {160, 144}; return {160, 144};
} }

View File

@ -50,7 +50,7 @@ struct MasterSystemInterface : Interface {
MasterSystemInterface(); MasterSystemInterface();
auto videoSize() -> VideoSize override; auto videoResolution() -> VideoSize override;
auto videoSize(uint width, uint height, bool arc) -> VideoSize override; auto videoSize(uint width, uint height, bool arc) -> VideoSize override;
auto videoColors() -> uint32 override; auto videoColors() -> uint32 override;
auto videoColor(uint32 color) -> uint64 override; auto videoColor(uint32 color) -> uint64 override;
@ -63,7 +63,7 @@ struct GameGearInterface : Interface {
GameGearInterface(); GameGearInterface();
auto videoSize() -> VideoSize override; auto videoResolution() -> VideoSize override;
auto videoSize(uint width, uint height, bool arc) -> VideoSize override; auto videoSize(uint width, uint height, bool arc) -> VideoSize override;
auto videoColors() -> uint32 override; auto videoColors() -> uint32 override;
auto videoColor(uint32 color) -> uint64 override; auto videoColor(uint32 color) -> uint64 override;

View File

@ -36,7 +36,7 @@ MasterSystemInterface::MasterSystemInterface() {
ports.append(move(controllerPort2)); ports.append(move(controllerPort2));
} }
auto MasterSystemInterface::videoSize() -> VideoSize { auto MasterSystemInterface::videoResolution() -> VideoSize {
return {256, 240}; return {256, 240};
} }

View File

@ -39,14 +39,14 @@ auto Interface::title() -> string {
return cartridge.title(); return cartridge.title();
} }
auto Interface::videoSize() -> VideoSize { auto Interface::videoResolution() -> VideoSize {
return {1140, 242}; return {1140, 240};
} }
auto Interface::videoSize(uint width, uint height, bool arc) -> VideoSize { auto Interface::videoSize(uint width, uint height, bool arc) -> VideoSize {
auto a = arc ? 8.0 / 7.0 : 1.0; auto a = arc ? 8.0 / 7.0 : 1.0;
uint w = 285; uint w = 285;
uint h = 242; uint h = 240;
uint m = min(width / (w * a), height / h); uint m = min(width / (w * a), height / h);
return {uint(w * a * m), uint(h * m)}; return {uint(w * a * m), uint(h * m)};
} }

View File

@ -23,7 +23,7 @@ struct Interface : Emulator::Interface {
auto manifest() -> string override; auto manifest() -> string override;
auto title() -> 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 videoSize(uint width, uint height, bool arc) -> VideoSize override;
auto videoColors() -> uint32 override; auto videoColors() -> uint32 override;
auto videoColor(uint32 color) -> uint64 override; auto videoColor(uint32 color) -> uint64 override;

View File

@ -54,7 +54,7 @@ auto VCE::step(uint clocks) -> void {
} }
auto VCE::refresh() -> 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 { auto VCE::power() -> void {

View File

@ -118,7 +118,7 @@ auto Interface::title() -> string {
return cartridge.title(); return cartridge.title();
} }
auto Interface::videoSize() -> VideoSize { auto Interface::videoResolution() -> VideoSize {
return {512, 480}; return {512, 480};
} }
@ -204,7 +204,7 @@ auto Interface::rtc() -> bool {
return false; return false;
} }
auto Interface::rtcsync() -> void { auto Interface::rtcSynchronize() -> void {
if(cartridge.has.EpsonRTC) epsonrtc.sync(); if(cartridge.has.EpsonRTC) epsonrtc.sync();
if(cartridge.has.SharpRTC) sharprtc.sync(); if(cartridge.has.SharpRTC) sharprtc.sync();
} }

View File

@ -38,7 +38,7 @@ struct Interface : Emulator::Interface {
auto manifest() -> string override; auto manifest() -> string override;
auto title() -> 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 videoSize(uint width, uint height, bool arc) -> VideoSize override;
auto videoColors() -> uint32 override; auto videoColors() -> uint32 override;
auto videoColor(uint32 color) -> uint64 override; auto videoColor(uint32 color) -> uint64 override;
@ -54,7 +54,7 @@ struct Interface : Emulator::Interface {
auto run() -> void override; auto run() -> void override;
auto rtc() -> bool override; auto rtc() -> bool override;
auto rtcsync() -> void override; auto rtcSynchronize() -> void override;
auto serialize() -> serializer override; auto serialize() -> serializer override;
auto unserialize(serializer&) -> bool override; auto unserialize(serializer&) -> bool override;

View File

@ -60,6 +60,14 @@ auto InputManager::appendHotkeys() -> void {
hotkeys.append(hotkey); hotkeys.append(hotkey);
} }
{ auto hotkey = new InputHotkey;
hotkey->name = "Rotate Display";
hotkey->press = [] {
program->rotateDisplay();
};
hotkeys.append(hotkey);
}
for(auto& hotkey : hotkeys) { for(auto& hotkey : hotkeys) {
hotkey->path = string{"Hotkey/", hotkey->name}.replace(" ", ""); hotkey->path = string{"Hotkey/", hotkey->name}.replace(" ", "");
hotkey->assignment = settings(hotkey->path).text(); hotkey->assignment = settings(hotkey->path).text();

View File

@ -30,7 +30,7 @@ struct InputMapping {
}; };
struct InputHotkey : InputMapping { struct InputHotkey : InputMapping {
auto logic() const -> Logic override { return Logic::AND; } //auto logic() const -> Logic override { return Logic::AND; }
function<auto () -> void> press; function<auto () -> void> press;
function<auto () -> void> release; function<auto () -> void> release;

View File

@ -109,7 +109,20 @@ Presentation::Presentation() {
statusBar.setVisible(showStatusBar.checked()); statusBar.setVisible(showStatusBar.checked());
if(visible()) resizeViewport(); 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); toolsMenu.setText("Tools").setVisible(false);
saveStateMenu.setText("Save State"); saveStateMenu.setText("Save State");
@ -238,7 +251,7 @@ auto Presentation::resizeViewport() -> void {
bool aspectCorrection = true; bool aspectCorrection = true;
if(!fullScreen()) { if(!fullScreen()) {
windowWidth = 326 * scale; windowWidth = 326 * scale;
windowHeight = 242 * scale; windowHeight = 240 * scale;
aspectCorrection = settings["Video/AspectCorrection"].boolean(); aspectCorrection = settings["Video/AspectCorrection"].boolean();
} else { } else {
windowWidth = geometry().width(); windowWidth = geometry().width();

View File

@ -27,6 +27,7 @@ struct Program : Emulator::Platform {
//utility.cpp //utility.cpp
auto powerCycle() -> void; auto powerCycle() -> void;
auto rotateDisplay() -> void;
auto connectDevices() -> void; auto connectDevices() -> void;
auto showMessage(const string& text) -> void; auto showMessage(const string& text) -> void;
auto updateStatusText() -> void; auto updateStatusText() -> void;

View File

@ -4,6 +4,15 @@ auto Program::powerCycle() -> void {
showMessage("Power cycled"); 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<bool>());
presentation->resizeViewport();
showMessage("Display rotated");
}
auto Program::connectDevices() -> void { auto Program::connectDevices() -> void {
if(!emulator) return; if(!emulator) return;
for(auto& port : emulator->ports) { for(auto& port : emulator->ports) {

View File

@ -12,14 +12,17 @@ Video::~Video() {
auto Video::reset() -> void { auto Video::reset() -> void {
interface = nullptr; interface = nullptr;
sprites.reset(); sprites.reset();
delete output; delete buffer;
output = nullptr; buffer = nullptr;
delete rotate;
rotate = nullptr;
delete palette; delete palette;
palette = nullptr; palette = nullptr;
width = 0; width = 0;
height = 0; height = 0;
effects.colorBleed = false; effects.colorBleed = false;
effects.interframeBlending = false; effects.interframeBlending = false;
effects.rotateLeft = false;
} }
auto Video::setInterface(Interface* interface) -> void { 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<bool>()) { if(effect == Effect::InterframeBlending && value.is<bool>()) {
effects.interframeBlending = value.get<bool>(); effects.interframeBlending = value.get<bool>();
} }
if(effect == Effect::RotateLeft && value.is<bool>()) {
effects.rotateLeft = value.get<bool>();
}
} }
auto Video::createSprite(uint width, uint height) -> shared_pointer<Sprite> { auto Video::createSprite(uint width, uint height) -> shared_pointer<Sprite> {
@ -105,13 +112,17 @@ auto Video::removeSprite(shared_pointer<Sprite> sprite) -> bool {
auto Video::refresh(uint32* input, uint pitch, uint width, uint height) -> void { auto Video::refresh(uint32* input, uint pitch, uint width, uint height) -> void {
if(this->width != width || this->height != height) { if(this->width != width || this->height != height) {
delete output; delete buffer;
output = new uint32[width * height](); delete rotate;
buffer = new uint32[width * height]();
rotate = new uint32[height * width]();
this->width = width; this->width = width;
this->height = height; this->height = height;
} }
auto output = buffer;
pitch >>= 2; //bytes to words pitch >>= 2; //bytes to words
for(uint y : range(height)) { for(uint y : range(height)) {
auto source = input + y * pitch; auto source = input + y * pitch;
auto target = output + y * width; 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) { for(auto& sprite : sprites) {
if(!sprite->visible) continue; if(!sprite->visible) continue;

View File

@ -10,6 +10,7 @@ struct Video {
enum class Effect : uint { enum class Effect : uint {
ColorBleed, ColorBleed,
InterframeBlending, InterframeBlending,
RotateLeft,
}; };
~Video(); ~Video();
@ -33,7 +34,8 @@ private:
Interface* interface = nullptr; Interface* interface = nullptr;
vector<shared_pointer<Sprite>> sprites; vector<shared_pointer<Sprite>> sprites;
uint32* output = nullptr; uint32* buffer = nullptr;
uint32* rotate = nullptr;
uint32* palette = nullptr; uint32* palette = nullptr;
uint width = 0; uint width = 0;
@ -47,6 +49,7 @@ private:
struct Effects { struct Effects {
bool colorBleed = false; bool colorBleed = false;
bool interframeBlending = false; bool interframeBlending = false;
bool rotateLeft = false;
} effects; } effects;
friend class Sprite; friend class Sprite;

View File

@ -7,8 +7,7 @@ Settings settings;
#include "wonderswan-color.cpp" #include "wonderswan-color.cpp"
Interface::Interface() { Interface::Interface() {
Port hardwareHorizontalPort{ID::Port::HardwareHorizontal, "Hardware - Horizontal"}; Port hardwarePort{ID::Port::Hardware, "Hardware"};
Port hardwareVerticalPort{ID::Port::HardwareVertical, "Hardware - Vertical"};
{ Device device{ID::Device::Controls, "Controls"}; { Device device{ID::Device::Controls, "Controls"};
device.inputs.append({0, "Y1"}); device.inputs.append({0, "Y1"});
@ -22,13 +21,10 @@ Interface::Interface() {
device.inputs.append({0, "B"}); device.inputs.append({0, "B"});
device.inputs.append({0, "A"}); device.inputs.append({0, "A"});
device.inputs.append({0, "Start"}); device.inputs.append({0, "Start"});
device.inputs.append({0, "Rotate"}); hardwarePort.devices.append(device);
hardwareHorizontalPort.devices.append(device);
hardwareVerticalPort.devices.append(device);
} }
ports.append(move(hardwareHorizontalPort)); ports.append(move(hardwarePort));
ports.append(move(hardwareVerticalPort));
} }
auto Interface::manifest() -> string { auto Interface::manifest() -> string {
@ -39,13 +35,17 @@ auto Interface::title() -> string {
return cartridge.information.title; return cartridge.information.title;
} }
auto Interface::videoSize() -> VideoSize { auto Interface::videoResolution() -> VideoSize {
return {224, 224}; if(!settings.rotateLeft) {
return {224, 144};
} else {
return {144, 224};
}
} }
auto Interface::videoSize(uint width, uint height, bool arc) -> VideoSize { auto Interface::videoSize(uint width, uint height, bool arc) -> VideoSize {
uint w = 224; uint w = videoResolution().width;
uint h = 224; uint h = videoResolution().height;
uint m = min(width / w, height / h); uint m = min(width / w, height / h);
return {w * m, h * m}; return {w * m, h * m};
} }
@ -91,12 +91,14 @@ auto Interface::cheatSet(const string_vector& list) -> void {
auto Interface::cap(const string& name) -> bool { auto Interface::cap(const string& name) -> bool {
if(name == "Blur Emulation") return true; if(name == "Blur Emulation") return true;
if(name == "Color Emulation") return true; if(name == "Color Emulation") return true;
if(name == "Rotate Display") return true;
return false; return false;
} }
auto Interface::get(const string& name) -> any { auto Interface::get(const string& name) -> any {
if(name == "Blur Emulation") return settings.blurEmulation; if(name == "Blur Emulation") return settings.blurEmulation;
if(name == "Color Emulation") return settings.colorEmulation; if(name == "Color Emulation") return settings.colorEmulation;
if(name == "Rotate Display") return settings.rotateLeft;
return {}; return {};
} }
@ -113,6 +115,12 @@ auto Interface::set(const string& name, const any& value) -> bool {
return true; return true;
} }
if(name == "Rotate Display" && value.is<bool>()) {
settings.rotateLeft = value.get<bool>();
system.configureVideoEffects();
return true;
}
return false; return false;
} }

View File

@ -8,8 +8,7 @@ struct ID {
}; };
struct Port { enum : uint { struct Port { enum : uint {
HardwareHorizontal, Hardware,
HardwareVertical,
};}; };};
struct Device { enum : uint { struct Device { enum : uint {
@ -23,7 +22,7 @@ struct Interface : Emulator::Interface {
auto manifest() -> string override; auto manifest() -> string override;
auto title() -> 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 videoSize(uint width, uint height, bool arc) -> VideoSize override;
auto loaded() -> bool override; auto loaded() -> bool override;
@ -69,6 +68,7 @@ struct WonderSwanColorInterface : Interface {
struct Settings { struct Settings {
bool blurEmulation = true; bool blurEmulation = true;
bool colorEmulation = true; bool colorEmulation = true;
bool rotateLeft = false;
}; };
extern Settings settings; extern Settings settings;

View File

@ -28,10 +28,7 @@ auto PPU::main() -> void {
if(l.screenTwoEnable) renderScreenTwo(); if(l.screenTwoEnable) renderScreenTwo();
if(l.spriteEnable) renderSprite(); if(l.spriteEnable) renderSprite();
} }
switch(l.orientation) { output[s.vclk * 224 + x] = s.pixel.color;
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;
}
step(1); step(1);
} }
step(32); step(32);
@ -76,14 +73,10 @@ auto PPU::frame() -> void {
s.field = !s.field; s.field = !s.field;
s.vclk = 0; s.vclk = 0;
scheduler.exit(Scheduler::Event::Frame); 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 { 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 { auto PPU::step(uint clocks) -> void {

View File

@ -35,7 +35,7 @@ struct PPU : Thread, IO {
uint12 color; uint12 color;
}; };
uint32 output[224 * 224]; uint32 output[224 * 144];
struct State { struct State {
bool field; bool field;
@ -45,9 +45,6 @@ struct PPU : Thread, IO {
} s; } s;
struct Latches { struct Latches {
//frame(), power()
bool orientation;
//latchRegisters() //latchRegisters()
uint8 backColor; uint8 backColor;

View File

@ -7,7 +7,6 @@ auto PPU::serialize(serializer& s) -> void {
s.integer((uint&)this->s.pixel.source); s.integer((uint&)this->s.pixel.source);
s.integer(this->s.pixel.color); s.integer(this->s.pixel.color);
s.integer(l.orientation);
s.integer(l.backColor); s.integer(l.backColor);
s.integer(l.screenOneEnable); s.integer(l.screenOneEnable);
s.integer(l.screenOneMapBase); s.integer(l.screenOneMapBase);

View File

@ -41,7 +41,7 @@ auto System::load(Emulator::Interface* interface, Model model) -> bool {
if(!cartridge.load()) return false; if(!cartridge.load()) return false;
serializeInit(); serializeInit();
_orientation = cartridge.information.orientation; settings.rotateLeft = cartridge.information.orientation;
this->interface = interface; this->interface = interface;
return _loaded = true; return _loaded = true;
} }
@ -103,22 +103,24 @@ auto System::runToSave() -> void {
} }
auto System::pollKeypad() -> void { auto System::pollKeypad() -> void {
uint port = !_orientation ? ID::Port::HardwareHorizontal : ID::Port::HardwareVertical; const uint landscape[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
uint device = ID::Device::Controls; const uint portrait [] = {7, 4, 5, 6, 3, 0, 1, 2, 8, 9, 10};
bool rotate = keypad.rotate;
keypad.y1 = platform->inputPoll(port, device, 0); uint port = ID::Port::Hardware;
keypad.y2 = platform->inputPoll(port, device, 1); uint device = ID::Device::Controls;
keypad.y3 = platform->inputPoll(port, device, 2); auto id = !settings.rotateLeft ? landscape : portrait;
keypad.y4 = platform->inputPoll(port, device, 3);
keypad.x1 = platform->inputPoll(port, device, 4); keypad.y1 = platform->inputPoll(port, device, id[0]);
keypad.x2 = platform->inputPoll(port, device, 5); keypad.y2 = platform->inputPoll(port, device, id[1]);
keypad.x3 = platform->inputPoll(port, device, 6); keypad.y3 = platform->inputPoll(port, device, id[2]);
keypad.x4 = platform->inputPoll(port, device, 7); keypad.y4 = platform->inputPoll(port, device, id[3]);
keypad.b = platform->inputPoll(port, device, 8); keypad.x1 = platform->inputPoll(port, device, id[4]);
keypad.a = platform->inputPoll(port, device, 9); keypad.x2 = platform->inputPoll(port, device, id[5]);
keypad.start = platform->inputPoll(port, device, 10); keypad.x3 = platform->inputPoll(port, device, id[6]);
keypad.rotate = platform->inputPoll(port, device, 11); 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 if(keypad.y1 || keypad.y2 || keypad.y3 || keypad.y4
|| keypad.x1 || keypad.x2 || keypad.x3 || keypad.x4 || keypad.x1 || keypad.x2 || keypad.x3 || keypad.x4
@ -126,10 +128,6 @@ auto System::pollKeypad() -> void {
) { ) {
cpu.raise(CPU::Interrupt::Input); cpu.raise(CPU::Interrupt::Input);
} }
if(!rotate && keypad.rotate) {
_orientation = !_orientation;
}
} }
} }

View File

@ -1,7 +1,6 @@
struct System : IO { struct System : IO {
auto loaded() const -> bool { return _loaded; } auto loaded() const -> bool { return _loaded; }
auto model() const -> Model { return _model; } auto model() const -> Model { return _model; }
auto orientation() const -> bool { return _orientation; }
auto color() const -> bool { return r.color; } auto color() const -> bool { return r.color; }
auto planar() const -> bool { return r.format == 0; } auto planar() const -> bool { return r.format == 0; }
auto packed() const -> bool { return r.format == 1; } auto packed() const -> bool { return r.format == 1; }
@ -58,7 +57,6 @@ private:
bool _loaded = false; bool _loaded = false;
Model _model = Model::WonderSwan; Model _model = Model::WonderSwan;
bool _orientation = 0; //0 = horizontal, 1 = vertical
uint _serializeSize = 0; uint _serializeSize = 0;
}; };

View File

@ -4,4 +4,5 @@ auto System::configureVideoPalette() -> void {
auto System::configureVideoEffects() -> void { auto System::configureVideoEffects() -> void {
Emulator::video.setEffect(Emulator::Video::Effect::InterframeBlending, settings.blurEmulation); Emulator::video.setEffect(Emulator::Video::Effect::InterframeBlending, settings.blurEmulation);
Emulator::video.setEffect(Emulator::Video::Effect::RotateLeft, settings.rotateLeft);
} }

View File

@ -61,6 +61,17 @@ struct serializer {
return *this; return *this;
} }
template<typename T> 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<typename T> auto integer(T& value) -> serializer& { template<typename T> auto integer(T& value) -> serializer& {
enum : uint { size = std::is_same<bool, T>::value ? 1 : sizeof(T) }; enum : uint { size = std::is_same<bool, T>::value ? 1 : sizeof(T) };
if(_mode == Save) { if(_mode == Save) {