diff --git a/higan/GNUmakefile b/higan/GNUmakefile index 9d9af1a2..5307f340 100644 --- a/higan/GNUmakefile +++ b/higan/GNUmakefile @@ -1,23 +1,24 @@ include ../nall/GNUmakefile target := tomoko +objects := libco emulator audio video resource +build := release # console := true -flags += -I. -I.. -O3 -objects := libco emulator audio video resource +flags += -I. -I.. -# profile-guided optimization mode -# pgo := instrument -# pgo := optimize - -ifeq ($(pgo),instrument) - flags += -fprofile-generate +ifeq ($(build),release) + flags += -O3 + link += -s +else ifeq ($(build),debug) + flags += -g +else ifeq ($(build),instrument) + flags += -O3 -fprofile-generate link += -lgcov -else ifeq ($(pgo),optimize) - flags += -fprofile-use +else ifeq ($(build),optimize) + flags += -O3 -fprofile-use endif -# platform ifeq ($(platform),windows) ifeq ($(console),true) link += -mconsole diff --git a/higan/emulator/emulator.hpp b/higan/emulator/emulator.hpp index d16eb5ab..ce95e184 100644 --- a/higan/emulator/emulator.hpp +++ b/higan/emulator/emulator.hpp @@ -12,7 +12,7 @@ using namespace nall; namespace Emulator { static const string Name = "higan"; - static const string Version = "102.04"; + static const string Version = "102.05"; static const string Author = "byuu"; static const string License = "GPLv3"; static const string Website = "http://byuu.org/"; diff --git a/higan/pce/cpu/io.cpp b/higan/pce/cpu/io.cpp index f3772bdc..c821dbda 100644 --- a/higan/pce/cpu/io.cpp +++ b/higan/pce/cpu/io.cpp @@ -134,8 +134,7 @@ auto CPU::write(uint8 bank, uint13 addr, uint8 data) -> void { //$0800-0bff PSG if((addr & 0x1c00) == 0x0800) { - io.mdr = data; - return; + return psg.write(addr, io.mdr = data); } //$0c00-0fff Timer diff --git a/higan/pce/interface/interface.cpp b/higan/pce/interface/interface.cpp index 2cc12159..c65ed795 100644 --- a/higan/pce/interface/interface.cpp +++ b/higan/pce/interface/interface.cpp @@ -75,7 +75,7 @@ auto Interface::videoColor(uint32 color) -> uint64 { } auto Interface::audioFrequency() -> double { - return 44'100.0; //todo: not accurate + return 315.0 / 88.0 * 1'000'000.0; //3.57MHz } auto Interface::loaded() -> bool { diff --git a/higan/pce/psg/channel.cpp b/higan/pce/psg/channel.cpp new file mode 100644 index 00000000..33a05e8e --- /dev/null +++ b/higan/pce/psg/channel.cpp @@ -0,0 +1,31 @@ +auto PSG::Channel::power() -> void { + memory::fill(&io, sizeof(IO)); + memory::fill(&output, sizeof(Output)); +} + +auto PSG::Channel::run() -> void { + if(!io.enable) return sample(0); + + if(io.noiseEnable) { + if(--io.noisePeriod == 0) { + io.noisePeriod = ~io.noiseFrequency << 7; + //todo: this should be a square wave; PRNG algorithm is also unknown + io.noiseSample = nall::random(); + } + return sample(io.noiseSample); + } + + if(io.direct) return sample(io.waveDirect); + + if(--io.period == 0) { + io.period = io.frequency; + io.waveOffset++; + } + + return sample(io.waveData[io.waveOffset]); +} + +auto PSG::Channel::sample(uint5 sample) -> void { + output.left = sample << 8; //<< io.volume << io.volumeLeft; + output.right = sample << 8; //<< io.volume << io.volumeRight; +} diff --git a/higan/pce/psg/io.cpp b/higan/pce/psg/io.cpp new file mode 100644 index 00000000..9b7abb39 --- /dev/null +++ b/higan/pce/psg/io.cpp @@ -0,0 +1,81 @@ +auto PSG::write(uint4 addr, uint8 data) -> void { + if(addr == 0x00) { + io.channel = data.bits(0,2); + return; + } + + if(addr == 0x01) { + io.volumeRight = data.bits(0,3); + io.volumeLeft = data.bits(4,7); + return; + } + + uint3 C = io.channel; + + if(addr == 0x02) { + if(C == 6 || C == 7) return; + channel[C].io.frequency.bits(0,7) = data.bits(0,7); + return; + } + + if(addr == 0x03) { + if(C == 6 || C == 7) return; + channel[C].io.frequency.bits(8,11) = data.bits(0,3); + return; + } + + if(addr == 0x04) { + if(C == 6 || C == 7) return; + if(channel[C].io.direct && !data.bit(6)) { + channel[C].io.waveOffset = 0; + } + if(!channel[C].io.enable && data.bit(7)) { + channel[C].io.waveOffset = 0; + channel[C].io.period = channel[C].io.frequency; + } + channel[C].io.volume = data.bits(0,3); + channel[C].io.direct = data.bit(6); + channel[C].io.enable = data.bit(7); + return; + } + + if(addr == 0x05) { + if(C == 6 || C == 7) return; + channel[C].io.volumeRight = data.bits(0,3); + channel[C].io.volumeLeft = data.bits(4,7); + return; + } + + if(addr == 0x06) { + if(C == 6 || C == 7) return; + if(channel[C].io.direct) { + channel[C].io.waveDirect = data.bits(0,4); + } else if(!channel[C].io.enable) { + uint5 O = channel[C].io.waveOffset++; + channel[C].io.waveData[O] = data.bits(0,4); + } + return; + } + + if(addr == 0x07) { + if(C != 4 && C != 5) return; + if(!channel[C].io.noiseEnable && data.bit(7)) { + channel[C].io.noisePeriod = ~data.bits(0,4) << 7; + channel[C].io.noiseSample = 0; + } + channel[C].io.noiseFrequency = data.bits(0,4); + channel[C].io.noiseEnable = data.bit(7); + return; + } + + if(addr == 0x08) { + io.lfoFrequency = data; + return; + } + + if(addr == 0x09) { + io.lfoControl = data.bits(0,1); + io.lfoEnable = data.bit(7); + return; + } +} diff --git a/higan/pce/psg/psg.cpp b/higan/pce/psg/psg.cpp index 7922e163..123d760f 100644 --- a/higan/pce/psg/psg.cpp +++ b/higan/pce/psg/psg.cpp @@ -3,14 +3,28 @@ namespace PCEngine { PSG psg; +#include "io.cpp" +#include "channel.cpp" auto PSG::Enter() -> void { while(true) scheduler.synchronize(), psg.main(); } auto PSG::main() -> void { + uint left = 0, right = 0; + + for(auto C : range(6)) { + channel[C].run(); + if(C == 1 && io.lfoEnable) { + //todo: frequency modulation of channel 0 using channel 1's output + } else { + left += channel[C].output.left; + right += channel[C].output.right; + } + } + + stream->sample(left / 32768.0, right / 32768.0); step(1); - stream->sample(0.0, 0.0); } auto PSG::step(uint clocks) -> void { @@ -19,8 +33,11 @@ auto PSG::step(uint clocks) -> void { } auto PSG::power() -> void { - create(PSG::Enter, 44'100.0); - stream = Emulator::audio.createStream(2, 44'100.0); + create(PSG::Enter, system.colorburst()); + stream = Emulator::audio.createStream(2, system.colorburst()); + + memory::fill(&io, sizeof(IO)); + for(auto C : range(6)) channel[C].power(); } } diff --git a/higan/pce/psg/psg.hpp b/higan/pce/psg/psg.hpp index 234bb61f..575f595b 100644 --- a/higan/pce/psg/psg.hpp +++ b/higan/pce/psg/psg.hpp @@ -8,6 +8,49 @@ struct PSG : Thread { auto step(uint clocks) -> void; auto power() -> void; + + //io.cpp + auto write(uint4 addr, uint8 data) -> void; + +private: + struct IO { + uint3 channel; + uint4 volumeLeft; + uint4 volumeRight; + uint8 lfoFrequency; + uint2 lfoControl; + uint1 lfoEnable; + } io; + + struct Channel { + //channel.cpp + auto power() -> void; + auto run() -> void; + auto sample(uint5 sample) -> void; + + struct IO { + uint12 frequency; + uint4 volume; + uint1 direct; + uint1 enable; + uint4 volumeLeft; + uint4 volumeRight; + uint5 waveData[32]; + uint5 waveOffset; + uint5 waveDirect; + uint5 noiseFrequency; //channels 4 and 5 only + uint1 noiseEnable; //channels 4 and 5 only + + uint12 period; + uint12 noisePeriod; + uint5 noiseSample; + } io; + + struct Output { + uint left; + uint right; + } output; + } channel[6]; }; extern PSG psg;