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