Update to v097r01 release.

byuu says:

A minor WIP to get us started.

Changelog:
- System::Video merged to PPU::Video
- System::Audio merged to DSP::Audio
- System::Configuration merged to Interface::Settings
- created emulator/emulator.cpp and accompanying object file for shared
  code between all cores

Currently, emulator.cpp just holds a videoColor() function that takes
R16G16B16, performs gamma/saturation/luma adjust, and outputs
(currently) A8R8G8B8. It's basically an internal function call for cores
to use when generating palette entries. This code used to exist inside
ui-tomoko/program/interface.cpp, but we have to move it internal for
software display emulation. But in the future, we could add other useful
cross-core functionality here.
This commit is contained in:
Tim Allen 2016-01-23 18:29:34 +11:00
parent 1fdd0582fc
commit f1ebef2ea8
32 changed files with 320 additions and 306 deletions

View File

@ -0,0 +1,3 @@
objects += emulator
obj/emulator.o: emulator/emulator.cpp $(call rwildcard,emulator/)

View File

@ -0,0 +1,34 @@
#include <emulator/emulator.hpp>
namespace Emulator {
auto Interface::videoColor(uint16 r, uint16 g, uint16 b) -> uint32 {
double saturation = 1.0;
double gamma = 1.0;
double luminance = 1.0;
if(saturation != 1.0) {
uint16 grayscale = uclamp<16>((r + g + b) / 3);
double inverse = max(0.0, 1.0 - saturation);
r = uclamp<16>(r * saturation + grayscale * inverse);
g = uclamp<16>(g * saturation + grayscale * inverse);
b = uclamp<16>(b * saturation + grayscale * inverse);
}
if(gamma != 1.0) {
double reciprocal = 1.0 / 32767.0;
r = r > 32767 ? r : 32767 * pow(r * reciprocal, gamma);
g = g > 32767 ? g : 32767 * pow(g * reciprocal, gamma);
b = b > 32767 ? b : 32767 * pow(b * reciprocal, gamma);
}
if(luminance != 1.0) {
r = uclamp<16>(r * luminance);
g = uclamp<16>(g * luminance);
b = uclamp<16>(b * luminance);
}
return 255 << 24 | (r >> 8) << 16 | (g >> 8) << 8 | (b >> 8) << 0;
}
}

View File

@ -6,7 +6,7 @@ using namespace nall;
namespace Emulator {
static const string Name = "higan";
static const string Version = "097";
static const string Version = "097.01";
static const string Author = "byuu";
static const string License = "GPLv3";
static const string Website = "http://byuu.org/";

View File

@ -108,6 +108,9 @@ struct Interface {
virtual auto cap(const string& name) -> bool { return false; }
virtual auto get(const string& name) -> any { return {}; }
virtual auto set(const string& name, const any& value) -> bool { return false; }
//shared functions
auto videoColor(uint16 r, uint16 g, uint16 b) -> uint32;
};
}

View File

@ -1,6 +1,3 @@
#ifndef PROCESSOR_HPP
#define PROCESSOR_HPP
#pragma once
#include <emulator/emulator.hpp>
#endif

View File

@ -21,7 +21,7 @@ auto ICD2::enter() -> void {
step(GameBoy::system.clocks_executed);
GameBoy::system.clocks_executed = 0;
} else { //DMG halted
audio.coprocessor_sample(0x0000, 0x0000);
dsp.audio.coprocessorSample(0, 0);
step(1);
}
synchronizeCPU();
@ -44,8 +44,8 @@ auto ICD2::unload() -> void {
}
auto ICD2::power() -> void {
audio.coprocessor_enable(true);
audio.coprocessor_frequency(2 * 1024 * 1024);
dsp.audio.coprocessorEnable(true);
dsp.audio.coprocessorFrequency(2 * 1024 * 1024);
}
auto ICD2::reset() -> void {

View File

@ -93,7 +93,7 @@ auto ICD2::videoRefresh(const uint32* data, uint pitch, uint width, uint height)
}
auto ICD2::audioSample(int16 left, int16 right) -> void {
audio.coprocessor_sample(left, right);
dsp.audio.coprocessorSample(left, right);
}
auto ICD2::inputPoll(uint port, uint device, uint id) -> int16 {

View File

@ -41,7 +41,7 @@ auto MSU1::enter() -> void {
right = sclamp<16>(rchannel);
if(dsp.mute()) left = 0, right = 0;
audio.coprocessor_sample(left, right);
dsp.audio.coprocessorSample(left, right);
step(1);
synchronizeCPU();
}
@ -59,8 +59,8 @@ auto MSU1::unload() -> void {
}
auto MSU1::power() -> void {
audio.coprocessor_enable(true);
audio.coprocessor_frequency(44100.0);
dsp.audio.coprocessorEnable(true);
dsp.audio.coprocessorFrequency(44100.0);
}
auto MSU1::reset() -> void {

44
higan/sfc/dsp/audio.cpp Normal file
View File

@ -0,0 +1,44 @@
auto DSP::Audio::coprocessorEnable(bool enable) -> void {
mixer.clear();
mixerEnable = enable;
dsp.read = dsp.write = 0;
mix.read = mix.write = 0;
}
auto DSP::Audio::coprocessorFrequency(double frequency) -> void {
mixer.setFrequency(frequency);
mixer.setResampler(nall::DSP::ResampleEngine::Sinc);
mixer.setResamplerFrequency(system.apuFrequency() / 768.0);
}
auto DSP::Audio::sample(int16 left, int16 right) -> void {
if(!mixerEnable) return interface->audioSample(left, right);
dsp.left[dsp.write] = left;
dsp.right[dsp.write] = right;
dsp.write++;
flush();
}
auto DSP::Audio::coprocessorSample(int16 left, int16 right) -> void {
int samples[] = {left, right};
mixer.sample(samples);
while(mixer.pending()) {
mixer.read(samples);
mix.left[mix.write] = samples[0];
mix.right[mix.write] = samples[1];
mix.write++;
flush();
}
}
auto DSP::Audio::flush() -> void {
while(dsp.read != dsp.write && mix.read != mix.write) {
interface->audioSample(
sclamp<16>(dsp.left[dsp.read] + mix.left[mix.read]),
sclamp<16>(dsp.right[dsp.read] + mix.right[mix.read])
);
dsp.read++;
mix.read++;
}
}

18
higan/sfc/dsp/audio.hpp Normal file
View File

@ -0,0 +1,18 @@
struct Audio {
auto sample(int16 left, int16 right) -> void;
auto coprocessorEnable(bool enable) -> void;
auto coprocessorFrequency(double frequency) -> void;
auto coprocessorSample(int16 left, int16 right) -> void;
auto flush() -> void;
private:
nall::DSP mixer;
bool mixerEnable = false;
struct Buffer {
int16 left[256];
int16 right[256];
uint8 read;
uint8 write;
} dsp, mix;
};

View File

@ -7,7 +7,6 @@ DSP dsp;
#define REG(n) state.regs[n]
#define VREG(n) state.regs[v.vidx + n]
#include "serialization.cpp"
#include "gaussian.cpp"
#include "counter.cpp"
#include "envelope.cpp"
@ -15,6 +14,8 @@ DSP dsp;
#include "misc.cpp"
#include "voice.cpp"
#include "echo.cpp"
#include "serialization.cpp"
#include "audio.cpp"
DSP::DSP() {
static_assert(sizeof(signed) >= 32 / 8, "signed >= 32-bits");
@ -283,6 +284,8 @@ auto DSP::power() -> void {
voice[n].hiddenEnvelope = 0;
voice[n]._envxOut = 0;
}
audio.coprocessorEnable(false);
}
auto DSP::reset() -> void {

View File

@ -5,7 +5,7 @@ struct DSP : Thread {
DSP();
alwaysinline auto step(unsigned clocks) -> void;
alwaysinline auto step(uint clocks) -> void;
alwaysinline auto synchronizeSMP() -> void;
auto mute() const -> bool;
@ -18,10 +18,13 @@ struct DSP : Thread {
auto serialize(serializer&) -> void;
#include "audio.hpp"
Audio audio;
privileged:
#include "modulo-array.hpp"
enum GlobalRegister : unsigned {
enum GlobalRegister : uint {
MVOLL = 0x0c, MVOLR = 0x1c,
EVOLL = 0x2c, EVOLR = 0x3c,
KON = 0x4c, KOFF = 0x5c,
@ -32,7 +35,7 @@ privileged:
EDL = 0x7d, FIR = 0x0f, //8 coefficients at 0x0f, 0x1f, ... 0x7f
};
enum VoiceRegister : unsigned {
enum VoiceRegister : uint {
VOLL = 0x00, VOLR = 0x01,
PITCHL = 0x02, PITCHH = 0x03,
SRCN = 0x04, ADSR0 = 0x05,
@ -40,14 +43,14 @@ privileged:
ENVX = 0x08, OUTX = 0x09,
};
enum EnvelopeMode : unsigned {
enum EnvelopeMode : uint {
EnvelopeRelease,
EnvelopeAttack,
EnvelopeDecay,
EnvelopeSustain,
};
enum : unsigned {
enum : uint {
EchoHistorySize = 8,
BrrBufferSize = 12,
BrrBlockSize = 9,
@ -57,77 +60,77 @@ privileged:
struct State {
uint8 regs[128];
ModuloArray<signed, EchoHistorySize> echoHistory[2]; //echo history keeps most recent 8 samples
signed echoHistoryOffset;
ModuloArray<int, EchoHistorySize> echoHistory[2]; //echo history keeps most recent 8 samples
int echoHistoryOffset;
bool everyOtherSample; //toggles every sample
signed kon; //KON value when last checked
signed noise;
signed counter;
signed echoOffset; //offset from ESA in echo buffer
signed echoLength; //number of bytes that echo_offset will stop at
int kon; //KON value when last checked
int noise;
int counter;
int echoOffset; //offset from ESA in echo buffer
int echoLength; //number of bytes that echo_offset will stop at
//hidden registers also written to when main register is written to
signed konBuffer;
signed endxBuffer;
signed envxBuffer;
signed outxBuffer;
int konBuffer;
int endxBuffer;
int envxBuffer;
int outxBuffer;
//temporary state between clocks (prefixed with _)
//read once per sample
signed _pmon;
signed _non;
signed _eon;
signed _dir;
signed _koff;
int _pmon;
int _non;
int _eon;
int _dir;
int _koff;
//read a few clocks ahead before used
signed _brrNextAddress;
signed _adsr0;
signed _brrHeader;
signed _brrByte;
signed _srcn;
signed _esa;
signed _echoDisabled;
int _brrNextAddress;
int _adsr0;
int _brrHeader;
int _brrByte;
int _srcn;
int _esa;
int _echoDisabled;
//internal state that is recalculated every sample
signed _dirAddress;
signed _pitch;
signed _output;
signed _looped;
signed _echoPointer;
int _dirAddress;
int _pitch;
int _output;
int _looped;
int _echoPointer;
//left/right sums
signed _mainOut[2];
signed _echoOut[2];
signed _echoIn [2];
int _mainOut[2];
int _echoOut[2];
int _echoIn [2];
} state;
struct Voice {
ModuloArray<signed, BrrBufferSize> buffer; //decoded samples
signed bufferOffset; //place in buffer where next samples will be decoded
signed gaussianOffset; //relative fractional position in sample (0x1000 = 1.0)
signed brrAddress; //address of current BRR block
signed brrOffset; //current decoding offset in BRR block
signed vbit; //bitmask for voice: 0x01 for voice 0, 0x02 for voice 1, etc
signed vidx; //voice channel register index: 0x00 for voice 0, 0x10 for voice 1, etc
signed konDelay; //KON delay/current setup phase
signed envelopeMode;
signed envelope; //current envelope level
signed hiddenEnvelope; //used by GAIN mode 7, very obscure quirk
signed _envxOut;
ModuloArray<int, BrrBufferSize> buffer; //decoded samples
int bufferOffset; //place in buffer where next samples will be decoded
int gaussianOffset; //relative fractional position in sample (0x1000 = 1.0)
int brrAddress; //address of current BRR block
int brrOffset; //current decoding offset in BRR block
int vbit; //bitmask for voice: 0x01 for voice 0, 0x02 for voice 1, etc
int vidx; //voice channel register index: 0x00 for voice 0, 0x10 for voice 1, etc
int konDelay; //KON delay/current setup phase
int envelopeMode;
int envelope; //current envelope level
int hiddenEnvelope; //used by GAIN mode 7, very obscure quirk
int _envxOut;
} voice[8];
//gaussian
static const int16 GaussianTable[512];
auto gaussianInterpolate(const Voice& v) -> signed;
auto gaussianInterpolate(const Voice& v) -> int;
//counter
static const uint16 CounterRate[32];
static const uint16 CounterOffset[32];
auto counterTick() -> void;
auto counterPoll(unsigned rate) -> bool;
auto counterPoll(uint rate) -> bool;
//envelope
auto envelopeRun(Voice& v) -> void;
@ -157,8 +160,8 @@ privileged:
auto voice9 (Voice& v) -> void;
//echo
auto calculateFIR(signed i, bool channel) -> signed;
auto echoOutput(bool channel) -> signed;
auto calculateFIR(int i, bool channel) -> int;
auto echoOutput(bool channel) -> int;
auto echoRead(bool channel) -> void;
auto echoWrite(bool channel) -> void;
auto echo22() -> void;

View File

@ -128,6 +128,11 @@ struct Settings {
bool blurEmulation = true;
bool colorEmulation = true;
bool scanlineEmulation = true;
uint controllerPort1 = 0;
uint controllerPort2 = 0;
uint expansionPort = 0;
bool random = true;
};
extern Interface* interface;

View File

@ -10,6 +10,7 @@ PPU ppu;
#include "sprite/sprite.cpp"
#include "window/window.cpp"
#include "serialization.cpp"
#include "video.cpp"
PPU::PPU() :
bg1(*this, Background::ID::BG1),
@ -119,6 +120,7 @@ auto PPU::reset() -> void {
sprite.reset();
window.reset();
screen.reset();
video.reset();
frame();
}
@ -141,7 +143,7 @@ auto PPU::scanline() -> void {
screen.scanline();
if(vcounter() == 241) {
scheduler.exit(Scheduler::ExitReason::FrameEvent);
video.refresh();
}
}

View File

@ -38,6 +38,7 @@ privileged:
#include "screen/screen.hpp"
#include "sprite/sprite.hpp"
#include "window/window.hpp"
#include "video.hpp"
Background bg1;
Background bg2;
@ -46,6 +47,7 @@ privileged:
Sprite sprite;
Window window;
Screen screen;
Video video;
static auto Enter() -> void;
alwaysinline auto add_clocks(uint) -> void;
@ -57,7 +59,6 @@ privileged:
friend class PPU::Sprite;
friend class PPU::Window;
friend class PPU::Screen;
friend class Video;
struct Debugger {
hook<void (uint16, uint8)> vram_read;

View File

@ -1,22 +1,23 @@
Video video;
Video::Video() {
PPU::Video::Video() {
output = new uint32[512 * 512]();
output += 16 * 512; //overscan padding
paletteLiteral = new uint32[1 << 19];
paletteStandard = new uint32[1 << 19];
paletteEmulation = new uint32[1 << 19];
output += 16 * 512; //overscan padding
}
Video::~Video() {
PPU::Video::~Video() {
output -= 16 * 512;
delete[] output;
delete[] paletteLiteral;
delete[] paletteStandard;
delete[] paletteEmulation;
}
auto Video::reset() -> void {
memory::fill(output, 512 * 480 * sizeof(uint32)); //padding area already cleared
auto PPU::Video::reset() -> void {
memory::fill(output, 512 * 480 * sizeof(uint32));
for(auto color : range(1 << 19)) {
uint l = (uint4)(color >> 15);
@ -26,28 +27,31 @@ auto Video::reset() -> void {
double L = (1.0 + l) / 16.0 * (l ? 1.0 : 0.5);
{ uint R = L * image::normalize(r, 5, 8);
uint G = L * image::normalize(g, 5, 8);
uint B = L * image::normalize(b, 5, 8);
paletteStandard[color] = (255 << 24) | (R << 16) | (G << 8) | (B << 0);
{ paletteLiteral[color] = color;
}
{ uint R = L * gammaRamp[r];
uint G = L * gammaRamp[g];
uint B = L * gammaRamp[b];
paletteEmulation[color] = (255 << 24) | (R << 16) | (G << 8) | (B << 0);
{ uint R = L * image::normalize(r, 5, 16);
uint G = L * image::normalize(g, 5, 16);
uint B = L * image::normalize(b, 5, 16);
paletteStandard[color] = interface->videoColor(R, G, B);
}
}
for(auto color : range(1 << 19)) {
uint l = (uint4)(color >> 15);
uint b = (uint5)(color >> 10);
uint g = (uint5)(color >> 5);
uint r = (uint5)(color >> 0);
{ static const uint8 gammaRamp[32] = {
0x00, 0x01, 0x03, 0x06, 0x0a, 0x0f, 0x15, 0x1c,
0x24, 0x2d, 0x37, 0x42, 0x4e, 0x5b, 0x69, 0x78,
0x88, 0x90, 0x98, 0xa0, 0xa8, 0xb0, 0xb8, 0xc0,
0xc8, 0xd0, 0xd8, 0xe0, 0xe8, 0xf0, 0xf8, 0xff,
};
uint R = L * gammaRamp[r] * 0x0101;
uint G = L * gammaRamp[g] * 0x0101;
uint B = L * gammaRamp[b] * 0x0101;
paletteEmulation[color] = interface->videoColor(R, G, B);
}
}
}
auto Video::refresh() -> void {
auto PPU::Video::refresh() -> void {
auto palette = settings.colorEmulation ? paletteEmulation : paletteStandard;
if(settings.scanlineEmulation) {
@ -109,13 +113,29 @@ auto Video::refresh() -> void {
}
drawCursors();
interface->videoRefresh(output - (ppu.overscan() ? 0 : 7 * 1024), 512 * sizeof(uint32), 512, 480);
scheduler.exit(Scheduler::ExitReason::FrameEvent);
}
//internal
auto PPU::Video::drawCursor(uint32 color, int x, int y) -> void {
static const uint8 cursor[15 * 15] = {
0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,
0,0,0,0,1,1,2,2,2,1,1,0,0,0,0,
0,0,0,1,2,2,1,2,1,2,2,1,0,0,0,
0,0,1,2,1,1,0,1,0,1,1,2,1,0,0,
0,1,2,1,0,0,0,1,0,0,0,1,2,1,0,
0,1,2,1,0,0,1,2,1,0,0,1,2,1,0,
1,2,1,0,0,1,1,2,1,1,0,0,1,2,1,
1,2,2,1,1,2,2,2,2,2,1,1,2,2,1,
1,2,1,0,0,1,1,2,1,1,0,0,1,2,1,
0,1,2,1,0,0,1,2,1,0,0,1,2,1,0,
0,1,2,1,0,0,0,1,0,0,0,1,2,1,0,
0,0,1,2,1,1,0,1,0,1,1,2,1,0,0,
0,0,0,1,2,2,1,2,1,2,2,1,0,0,0,
0,0,0,0,1,1,2,2,2,1,1,0,0,0,0,
0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,
};
auto Video::drawCursor(uint32 color, int x, int y) -> void {
auto data = (uint32*)output;
if(ppu.interlace() && ppu.field()) data += 512;
@ -138,8 +158,8 @@ auto Video::drawCursor(uint32 color, int x, int y) -> void {
}
}
auto Video::drawCursors() -> void {
switch(configuration.controllerPort2) {
auto PPU::Video::drawCursors() -> void {
switch((Device::ID)settings.controllerPort2) {
case Device::ID::SuperScope:
if(dynamic_cast<SuperScope*>(device.controllerPort2)) {
auto& controller = (SuperScope&)*device.controllerPort2;
@ -157,28 +177,3 @@ auto Video::drawCursors() -> void {
break;
}
}
const uint8 Video::gammaRamp[32] = {
0x00, 0x01, 0x03, 0x06, 0x0a, 0x0f, 0x15, 0x1c,
0x24, 0x2d, 0x37, 0x42, 0x4e, 0x5b, 0x69, 0x78,
0x88, 0x90, 0x98, 0xa0, 0xa8, 0xb0, 0xb8, 0xc0,
0xc8, 0xd0, 0xd8, 0xe0, 0xe8, 0xf0, 0xf8, 0xff,
};
const uint8 Video::cursor[15 * 15] = {
0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,
0,0,0,0,1,1,2,2,2,1,1,0,0,0,0,
0,0,0,1,2,2,1,2,1,2,2,1,0,0,0,
0,0,1,2,1,1,0,1,0,1,1,2,1,0,0,
0,1,2,1,0,0,0,1,0,0,0,1,2,1,0,
0,1,2,1,0,0,1,2,1,0,0,1,2,1,0,
1,2,1,0,0,1,1,2,1,1,0,0,1,2,1,
1,2,2,1,1,2,2,2,2,2,1,1,2,2,1,
1,2,1,0,0,1,1,2,1,1,0,0,1,2,1,
0,1,2,1,0,0,1,2,1,0,0,1,2,1,0,
0,1,2,1,0,0,0,1,0,0,0,1,2,1,0,
0,0,1,2,1,1,0,1,0,1,1,2,1,0,0,
0,0,0,1,2,2,1,2,1,2,2,1,0,0,0,
0,0,0,0,1,1,2,2,2,1,1,0,0,0,0,
0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,
};

View File

@ -5,18 +5,12 @@ struct Video {
auto reset() -> void;
auto refresh() -> void;
uint32* output = nullptr;
uint32* paletteStandard = nullptr;
uint32* paletteEmulation = nullptr;
private:
auto drawCursor(uint32 color, int x, int y) -> void;
auto drawCursors() -> void;
static const uint8 gammaRamp[32];
static const uint8 cursor[15 * 15];
friend class System;
uint32* output = nullptr;
uint32* paletteLiteral = nullptr;
uint32* paletteStandard = nullptr;
uint32* paletteEmulation = nullptr;
};
extern Video video;

View File

@ -1,62 +0,0 @@
Audio audio;
auto Audio::coprocessor_enable(bool state) -> void {
coprocessor = state;
dspaudio.clear();
dsp_rdoffset = cop_rdoffset = 0;
dsp_wroffset = cop_wroffset = 0;
dsp_length = cop_length = 0;
}
auto Audio::coprocessor_frequency(double input_frequency) -> void {
dspaudio.setFrequency(input_frequency);
dspaudio.setResampler(nall::DSP::ResampleEngine::Sinc);
dspaudio.setResamplerFrequency(system.apuFrequency() / 768.0);
}
auto Audio::sample(int16 lsample, int16 rsample) -> void {
if(coprocessor == false) return interface->audioSample(lsample, rsample);
dsp_buffer[dsp_wroffset] = ((uint16)lsample << 0) + ((uint16)rsample << 16);
dsp_wroffset = (dsp_wroffset + 1) & buffer_mask;
dsp_length = (dsp_length + 1) & buffer_mask;
flush();
}
auto Audio::coprocessor_sample(int16 lsample, int16 rsample) -> void {
int samples[] = {lsample, rsample};
dspaudio.sample(samples);
while(dspaudio.pending()) {
dspaudio.read(samples);
cop_buffer[cop_wroffset] = ((uint16)samples[0] << 0) + ((uint16)samples[1] << 16);
cop_wroffset = (cop_wroffset + 1) & buffer_mask;
cop_length = (cop_length + 1) & buffer_mask;
flush();
}
}
auto Audio::flush() -> void {
while(dsp_length > 0 && cop_length > 0) {
uint32 dsp_sample = dsp_buffer[dsp_rdoffset];
uint32 cop_sample = cop_buffer[cop_rdoffset];
dsp_rdoffset = (dsp_rdoffset + 1) & buffer_mask;
cop_rdoffset = (cop_rdoffset + 1) & buffer_mask;
dsp_length--;
cop_length--;
int dsp_left = (int16)(dsp_sample >> 0);
int dsp_right = (int16)(dsp_sample >> 16);
int cop_left = (int16)(cop_sample >> 0);
int cop_right = (int16)(cop_sample >> 16);
interface->audioSample(
sclamp<16>(dsp_left + cop_left ),
sclamp<16>(dsp_right + cop_right)
);
}
}

View File

@ -1,19 +0,0 @@
struct Audio {
auto coprocessor_enable(bool state) -> void;
auto coprocessor_frequency(double frequency) -> void;
auto sample(int16 lsample, int16 rsample) -> void;
auto coprocessor_sample(int16 lsample, int16 rsample) -> void;
private:
auto flush() -> void;
nall::DSP dspaudio;
bool coprocessor;
enum : uint { buffer_size = 256, buffer_mask = buffer_size - 1 };
uint32 dsp_buffer[buffer_size], cop_buffer[buffer_size];
uint dsp_rdoffset, cop_rdoffset;
uint dsp_wroffset, cop_wroffset;
uint dsp_length, cop_length;
};
extern Audio audio;

View File

@ -32,12 +32,12 @@ auto Device::connect(uint port, Device::ID id) -> void {
}
switch(port) {
case 0: configuration.controllerPort1 = id; break;
case 1: configuration.controllerPort2 = id; break;
case 0: settings.controllerPort1 = (uint)id; break;
case 1: settings.controllerPort2 = (uint)id; break;
}
}
if(port == 2) {
configuration.expansionPort = id;
settings.expansionPort = (uint)id;
}
}

View File

@ -0,0 +1,14 @@
Random random;
auto Random::seed(uint seed) -> void {
iter = seed;
}
auto Random::operator()(uint result) -> uint {
if(!settings.random) return result;
return iter = (iter >> 1) ^ (((iter & 1) - 1) & 0xedb88320);
}
auto Random::serialize(serializer& s) -> void {
s.integer(iter);
}

View File

@ -1,5 +1,5 @@
auto System::serialize() -> serializer {
serializer s(serializeSize);
serializer s(_serializeSize);
uint signature = 0x31545342, version = Info::SerializerVersion;
char hash[64], description[512], profile[16];
@ -37,13 +37,11 @@ auto System::unserialize(serializer& s) -> bool {
return true;
}
//========
//internal
//========
auto System::serialize(serializer& s) -> void {
s.integer((uint&)region);
s.integer((uint&)expansionPort);
s.integer((uint&)_region);
s.integer((uint&)_expansionPort);
}
auto System::serializeAll(serializer& s) -> void {
@ -89,5 +87,5 @@ auto System::serializeInit() -> void {
s.array(description);
serializeAll(s);
serializeSize = s.size();
_serializeSize = s.size();
}

View File

@ -3,53 +3,46 @@
namespace SuperFamicom {
System system;
Configuration configuration;
Random random;
#include "video.cpp"
#include "audio.cpp"
#include "device.cpp"
#include "random.cpp"
#include "serialization.cpp"
#include <sfc/scheduler/scheduler.cpp>
System::System() {
region = Region::Autodetect;
expansionPort = Device::ID::eBoot;
}
auto System::region() const -> Region { return _region; }
auto System::expansionPort() const -> Device::ID { return _expansionPort; }
auto System::cpuFrequency() const -> uint { return _cpuFrequency; }
auto System::apuFrequency() const -> uint { return _apuFrequency; }
auto System::run() -> void {
scheduler.sync = Scheduler::SynchronizeMode::None;
scheduler.enter();
if(scheduler.exit_reason == Scheduler::ExitReason::FrameEvent) {
video.refresh();
}
}
auto System::runToSave() -> void {
if(CPU::Threaded == true) {
if(CPU::Threaded) {
scheduler.sync = Scheduler::SynchronizeMode::CPU;
runThreadToSave();
}
if(SMP::Threaded == true) {
if(SMP::Threaded) {
scheduler.thread = smp.thread;
runThreadToSave();
}
if(PPU::Threaded == true) {
if(PPU::Threaded) {
scheduler.thread = ppu.thread;
runThreadToSave();
}
if(DSP::Threaded == true) {
if(DSP::Threaded) {
scheduler.thread = dsp.thread;
runThreadToSave();
}
for(unsigned i = 0; i < cpu.coprocessors.size(); i++) {
auto& chip = *cpu.coprocessors[i];
for(uint n = 0; n < cpu.coprocessors.size(); n++) {
auto& chip = *cpu.coprocessors[n];
scheduler.thread = chip.thread;
runThreadToSave();
}
@ -59,9 +52,6 @@ auto System::runThreadToSave() -> void {
while(true) {
scheduler.enter();
if(scheduler.exit_reason == Scheduler::ExitReason::SynchronizeEvent) break;
if(scheduler.exit_reason == Scheduler::ExitReason::FrameEvent) {
video.refresh();
}
}
}
@ -89,8 +79,9 @@ auto System::init() -> void {
bsmemory.init();
device.connect(0, configuration.controllerPort1);
device.connect(1, configuration.controllerPort2);
device.connect(0, (Device::ID)settings.controllerPort1);
device.connect(1, (Device::ID)settings.controllerPort2);
device.connect(2, (Device::ID)settings.expansionPort);
}
auto System::term() -> void {
@ -104,15 +95,10 @@ auto System::load() -> void {
interface->loadRequest(ID::IPLROM, iplrom, true);
}
region = configuration.region;
if(region == Region::Autodetect) {
region = (cartridge.region() == Cartridge::Region::NTSC ? Region::NTSC : Region::PAL);
}
expansionPort = configuration.expansionPort;
cpuFrequency = region() == Region::NTSC ? 21477272 : 21281370;
apuFrequency = 24606720;
audio.coprocessor_enable(false);
_region = cartridge.region() == Cartridge::Region::NTSC ? Region::NTSC : Region::PAL;
_expansionPort = (Device::ID)settings.expansionPort;
_cpuFrequency = region() == Region::NTSC ? 21477272 : 21281370;
_apuFrequency = 24606720;
bus.reset();
bus.map();
@ -240,10 +226,10 @@ auto System::reset() -> void {
if(cartridge.hasSPC7110()) cpu.coprocessors.append(&spc7110);
if(cartridge.hasMSU1()) cpu.coprocessors.append(&msu1);
video.reset();
scheduler.init();
device.connect(0, configuration.controllerPort1);
device.connect(1, configuration.controllerPort2);
device.connect(0, (Device::ID)settings.controllerPort1);
device.connect(1, (Device::ID)settings.controllerPort2);
device.connect(2, (Device::ID)settings.expansionPort);
}
}

View File

@ -1,13 +1,14 @@
struct Interface;
#include "video.hpp"
#include "audio.hpp"
#include "device.hpp"
struct System : property<System> {
enum class Region : uint { NTSC = 0, PAL = 1, Autodetect = 2 };
struct System {
enum class Region : bool { NTSC = 0, PAL = 1 };
System();
auto region() const -> Region;
auto expansionPort() const -> Device::ID;
auto cpuFrequency() const -> uint;
auto apuFrequency() const -> uint;
auto run() -> void;
auto runToSave() -> void;
@ -19,14 +20,6 @@ struct System : property<System> {
auto power() -> void;
auto reset() -> void;
//return *active* system information (settings are cached upon power-on)
readonly<Region> region;
readonly<Device::ID> expansionPort;
readonly<uint> cpuFrequency;
readonly<uint> apuFrequency;
readonly<uint> serializeSize;
auto serialize() -> serializer;
auto unserialize(serializer&) -> bool;
@ -41,9 +34,13 @@ private:
auto serializeAll(serializer&) -> void;
auto serializeInit() -> void;
Region _region = Region::NTSC;
Device::ID _expansionPort = Device::ID::None;
uint _cpuFrequency = 0;
uint _apuFrequency = 0;
uint _serializeSize = 0;
friend class Cartridge;
friend class Video;
friend class Audio;
friend class Device;
};
@ -51,29 +48,10 @@ extern System system;
#include <sfc/scheduler/scheduler.hpp>
struct Configuration {
Device::ID controllerPort1 = Device::ID::None;
Device::ID controllerPort2 = Device::ID::None;
Device::ID expansionPort = Device::ID::None;
System::Region region = System::Region::Autodetect;
bool random = true;
};
extern Configuration configuration;
struct Random {
auto seed(uint seed) -> void {
iter = seed;
}
auto operator()(uint result) -> uint {
if(configuration.random == false) return result;
return iter = (iter >> 1) ^ (((iter & 1) - 1) & 0xedb88320);
}
auto serialize(serializer& s) -> void {
s.integer(iter);
}
auto seed(uint seed) -> void;
auto operator()(uint result) -> uint;
auto serialize(serializer& s) -> void;
private:
uint iter = 0;

View File

@ -3,6 +3,7 @@ name := higan
processors := arm gsu hg51b lr35902 r6502 r65816 spc700 upd96050
include processor/GNUmakefile
include emulator/GNUmakefile
include fc/GNUmakefile
include sfc/GNUmakefile
include gb/GNUmakefile

View File

@ -47,7 +47,11 @@ auto Icarus::famicomImport(vector<uint8>& buffer, string location) -> string {
uint chrrom = 0;
auto markup = famicomManifest(buffer, location, &prgrom, &chrrom);
if(!markup) return failure("failed to parse ROM image");
if(!directory::create(target)) return failure("library path unwritable");
if(file::exists({source, name, ".sav"}) && !file::exists({target, "save.ram"})) {
file::copy({source, name, ".sav"}, {target, "save.ram"});
}
if(settings["icarus/CreateManifests"].boolean()) file::write({target, "manifest.bml"}, markup);
file::write({target, "ines.rom"}, buffer.data(), 16);

View File

@ -39,7 +39,11 @@ auto Icarus::gameBoyAdvanceImport(vector<uint8>& buffer, string location) -> str
auto markup = gameBoyAdvanceManifest(buffer, location);
if(!markup) return failure("failed to parse ROM image");
if(!directory::create(target)) return failure("library path unwritable");
if(file::exists({source, name, ".sav"}) && !file::exists({target, "save.ram"})) {
file::copy({source, name, ".sav"}, {target, "save.ram"});
}
if(settings["icarus/CreateManifests"].boolean()) file::write({target, "manifest.bml"}, markup);
file::write({target, "program.rom"}, buffer);

View File

@ -39,7 +39,11 @@ auto Icarus::gameBoyColorImport(vector<uint8>& buffer, string location) -> strin
auto markup = gameBoyColorManifest(buffer, location);
if(!markup) return failure("failed to parse ROM image");
if(!directory::create(target)) return failure("library path unwritable");
if(file::exists({source, name, ".sav"}) && !file::exists({target, "save.ram"})) {
file::copy({source, name, ".sav"}, {target, "save.ram"});
}
if(settings["icarus/CreateManifests"].boolean()) file::write({target, "manifest.bml"}, markup);
file::write({target, "program.rom"}, buffer);

View File

@ -39,7 +39,11 @@ auto Icarus::gameBoyImport(vector<uint8>& buffer, string location) -> string {
auto markup = gameBoyManifest(buffer, location);
if(!markup) return failure("failed to parse ROM image");
if(!directory::create(target)) return failure("library path unwritable");
if(file::exists({source, name, ".sav"}) && !file::exists({target, "save.ram"})) {
file::copy({source, name, ".sav"}, {target, "save.ram"});
}
if(settings["icarus/CreateManifests"].boolean()) file::write({target, "manifest.bml"}, markup);
file::write({target, "program.rom"}, buffer);

View File

@ -66,6 +66,9 @@ auto Icarus::superFamicomImport(vector<uint8>& buffer, string location) -> strin
}
if(!directory::create(target)) return failure("library path unwritable");
if(file::exists({source, name, ".srm"}) && !file::exists({target, "save.ram"})) {
file::copy({source, name, ".srm"}, {target, "save.ram"});
}
if(settings["icarus/CreateManifests"].boolean()) file::write({target, "manifest.bml"}, markup);
uint offset = (buffer.size() & 0x7fff) == 512 ? 512 : 0; //skip header if present

View File

@ -114,12 +114,9 @@ template<typename... P> inline auto execute(const string& name, P&&... p) -> str
nullptr, nullptr, &si, &pi
)) return "";
while(true) {
DWORD exitCode;
GetExitCodeProcess(pi.hProcess, &exitCode);
Sleep(1);
if(exitCode != STILL_ACTIVE) break;
}
if(WaitForSingleObject(pi.hProcess, INFINITE)) return "";
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
string result;
while(true) {

View File

@ -6,7 +6,7 @@ auto activepath() -> string {
char path[PATH_MAX] = "";
auto unused = getcwd(path, PATH_MAX);
string result = path;
if(result.empty()) result = ".";
if(!result) result = ".";
result.transform("\\", "/");
if(result.endsWith("/") == false) result.append("/");
return result;
@ -16,7 +16,7 @@ auto realpath(rstring name) -> string {
string result;
char path[PATH_MAX] = "";
if(::realpath(name, path)) result = pathname(string{path}.transform("\\", "/"));
if(result.empty()) return activepath();
if(!result) return activepath();
result.transform("\\", "/");
if(result.endsWith("/") == false) result.append("/");
return result;
@ -48,7 +48,7 @@ auto userpath() -> string {
struct passwd* userinfo = getpwuid(getuid());
string result = userinfo->pw_dir;
#endif
if(result.empty()) result = ".";
if(!result) result = ".";
if(result.endsWith("/") == false) result.append("/");
return result;
}
@ -66,12 +66,12 @@ auto configpath() -> string {
#else
string result = {userpath(), ".config/"};
#endif
if(result.empty()) result = ".";
if(!result) result = ".";
if(result.endsWith("/") == false) result.append("/");
return result;
}
// /home/username/.local/
// /home/username/.local/share/
// c:/users/username/appdata/local/
auto localpath() -> string {
#if defined(PLATFORM_WINDOWS)
@ -82,9 +82,9 @@ auto localpath() -> string {
#elif defined(PLATFORM_MACOSX)
string result = {userpath(), "Library/Application Support/"};
#else
string result = {userpath(), ".local/"};
string result = {userpath(), ".local/share/"};
#endif
if(result.empty()) result = ".";
if(!result) result = ".";
if(result.endsWith("/") == false) result.append("/");
return result;
}
@ -103,7 +103,7 @@ auto sharedpath() -> string {
#else
string result = "/usr/share/";
#endif
if(result.empty()) result = ".";
if(!result) result = ".";
if(result.endsWith("/") == false) result.append("/");
return result;
}