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 {
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/";

View File

@ -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;

View File

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

View File

@ -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;

View File

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

View File

@ -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;

View File

@ -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();

View File

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

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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;
}

View File

@ -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;

View File

@ -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);
}

View File

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

View File

@ -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;

View File

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

View File

@ -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;

View File

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

View File

@ -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)};
}

View File

@ -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;

View File

@ -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 {

View File

@ -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();
}

View File

@ -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;

View File

@ -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();

View File

@ -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;

View File

@ -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();

View File

@ -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;

View File

@ -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) {

View File

@ -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;

View File

@ -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;

View File

@ -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;
}

View File

@ -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;

View File

@ -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 {

View File

@ -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;

View File

@ -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);

View File

@ -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;
}
}
}

View File

@ -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;
};

View File

@ -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);
}

View File

@ -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) {