mirror of https://github.com/bsnes-emu/bsnes.git
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:
parent
a4629e1f64
commit
8af3e4a6e2
|
@ -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/";
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -39,13 +39,17 @@ auto Interface::title() -> string {
|
||||||
return cartridge.title();
|
return cartridge.title();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Interface::videoSize() -> VideoSize {
|
auto Interface::videoResolution() -> VideoSize {
|
||||||
|
if(!settings.rotateLeft) {
|
||||||
return {240, 160};
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)};
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
Loading…
Reference in New Issue