diff --git a/higan/emulator/emulator.hpp b/higan/emulator/emulator.hpp index ff693915..260b391e 100644 --- a/higan/emulator/emulator.hpp +++ b/higan/emulator/emulator.hpp @@ -6,7 +6,7 @@ using namespace nall; namespace Emulator { static const string Name = "higan"; - static const string Version = "097.24"; + static const string Version = "097.25"; static const string Author = "byuu"; static const string License = "GPLv3"; static const string Website = "http://byuu.org/"; diff --git a/higan/processor/v30mz/v30mz.cpp b/higan/processor/v30mz/v30mz.cpp index d784e59c..11bf214a 100644 --- a/higan/processor/v30mz/v30mz.cpp +++ b/higan/processor/v30mz/v30mz.cpp @@ -22,8 +22,8 @@ auto V30MZ::debug(string text) -> void { } auto V30MZ::power() -> void { - state.halt = false; - state.poll = true; + state.halt = false; + state.poll = true; state.prefix = false; prefixes.reset(); diff --git a/higan/ws/apu/apu.cpp b/higan/ws/apu/apu.cpp index bc79c129..c4a1801e 100644 --- a/higan/ws/apu/apu.cpp +++ b/higan/ws/apu/apu.cpp @@ -4,14 +4,61 @@ namespace WonderSwan { APU apu; #include "io.cpp" +#include "channel.cpp" +#include "channel0.cpp" +#include "channel1.cpp" +#include "channel2.cpp" +#include "channel3.cpp" +#include "channel4.cpp" auto APU::Enter() -> void { while(true) scheduler.synchronize(), apu.main(); } auto APU::main() -> void { - step(128); - interface->audioSample(0, 0); + channel0.run(); + channel1.run(); + channel2.run(); + channel3.run(); + + if(s.clock.bits(0,12) == 0) { + channel2.sweep(); + } + + if(s.clock.bits(0,6) == 0) { + channel4.run(); + dacRun(); + } + + s.clock++; + step(1); +} + +auto APU::dacRun() -> void { + int left = 0; + if(channel0.r.enable) left += channel0.o.left; + if(channel1.r.enable) left += channel1.o.left; + if(channel2.r.enable) left += channel2.o.left; + if(channel3.r.enable) left += channel3.o.left; + left = (left >> r.speakerShift) << 5; + if(channel4.r.enable) left += channel4.o.left; + left = sclamp<16>(left << 3); + + int right = 0; + if(channel0.r.enable) right += channel0.o.right; + if(channel1.r.enable) right += channel1.o.right; + if(channel2.r.enable) right += channel2.o.right; + if(channel3.r.enable) right += channel3.o.right; + right = (right >> r.speakerShift) << 5; + if(channel4.r.enable) right += channel4.o.right; + right = sclamp<16>(right << 3); + + if(!r.speakerEnable) { + left = 0; + right = 0; + } + + interface->audioSample(left, right); } auto APU::step(uint clocks) -> void { @@ -22,40 +69,74 @@ auto APU::step(uint clocks) -> void { auto APU::power() -> void { create(APU::Enter, 3'072'000); + for(uint n = 0x006a; n <= 0x006b; n++) iomap[n] = this; for(uint n = 0x0080; n <= 0x0094; n++) iomap[n] = this; - channel1.r.pitch = 0; - channel1.r.volumeLeft = 0; - channel1.r.volumeRight = 0; - channel1.r.enable = 0; - - channel2.r.pitch = 0; - channel2.r.volumeLeft = 0; - channel2.r.volumeRight = 0; - channel2.r.enable = 0; - channel2.r.voice = 0; - - channel3.r.pitch = 0; - channel3.r.volumeLeft = 0; - channel3.r.volumeRight = 0; - channel3.r.sweepValue = 0; - channel3.r.sweepTime = 0; - channel3.r.enable = 0; - channel3.r.sweep = 0; - - channel4.r.pitch = 0; - channel4.r.volumeLeft = 0; - channel4.r.volumeRight = 0; - channel4.r.noiseMode = 0; - channel4.r.enable = 0; - channel4.r.noise = 0; - + s.clock = 0; r.waveBase = 0; r.speakerEnable = 0; r.speakerShift = 0; r.headphoneEnable = 0; - r.voiceEnableLeft = 0; - r.voiceEnableRight = 0; + + channel0.o.left = 0; + channel0.o.right = 0; + channel0.s.period = 0; + channel0.s.sampleOffset = 0; + channel0.r.pitch = 0; + channel0.r.volumeLeft = 0; + channel0.r.volumeRight = 0; + channel0.r.enable = 0; + + channel1.o.left = 0; + channel1.o.right = 0; + channel1.s.period = 0; + channel1.s.sampleOffset = 0; + channel1.r.pitch = 0; + channel1.r.volumeLeft = 0; + channel1.r.volumeRight = 0; + channel1.r.enable = 0; + channel1.r.voice = 0; + channel1.r.voiceEnableLeft = 0; + channel1.r.voiceEnableRight = 0; + + channel2.o.left = 0; + channel2.o.right = 0; + channel2.s.period = 0; + channel2.s.sampleOffset = 0; + channel2.s.sweepCounter = 0; + channel2.r.pitch = 0; + channel2.r.volumeLeft = 0; + channel2.r.volumeRight = 0; + channel2.r.sweepValue = 0; + channel2.r.sweepTime = 0; + channel2.r.enable = 0; + channel2.r.sweep = 0; + + channel3.o.left = 0; + channel3.o.right = 0; + channel3.s.period = 0; + channel3.s.sampleOffset = 0; + channel3.s.noiseOutput = 0; + channel3.s.noiseLFSR = 0; + channel3.r.pitch = 0; + channel3.r.volumeLeft = 0; + channel3.r.volumeRight = 0; + channel3.r.noiseMode = 0; + channel3.r.noiseReset = 0; + channel3.r.noiseUpdate = 0; + channel3.r.enable = 0; + channel3.r.noise = 0; + + channel4.o.left = 0; + channel4.o.right = 0; + channel4.s.data = 0; + channel4.r.volume = 0; + channel4.r.scale = 0; + channel4.r.speed = 0; + channel4.r.enable = 0; + channel4.r.unknown = 0; + channel4.r.leftEnable = 0; + channel4.r.rightEnable = 0; } } diff --git a/higan/ws/apu/apu.hpp b/higan/ws/apu/apu.hpp index e737e6d3..641fbe04 100644 --- a/higan/ws/apu/apu.hpp +++ b/higan/ws/apu/apu.hpp @@ -1,6 +1,7 @@ struct APU : Thread, IO { static auto Enter() -> void; auto main() -> void; + auto dacRun() -> void; auto step(uint clocks) -> void; auto power() -> void; @@ -8,41 +9,98 @@ struct APU : Thread, IO { auto portRead(uint16 addr) -> uint8; auto portWrite(uint16 addr, uint8 data) -> void; - struct Channel1 { + struct State { + uint13 clock; + } s; + + struct Registers { + //$008f SND_WAVE_BASE + uint8 waveBase; + + //$0091 SND_OUTPUT + uint1 speakerEnable; + uint2 speakerShift; + uint1 headphoneEnable; + } r; + + struct Channel { + Channel(uint id); + auto sample(uint5 index) -> uint4; + + const uint id; + + struct Output { + int16 left; + int16 right; + } o; + }; + + struct Channel0 : Channel { + Channel0(); + auto run() -> void; + + struct State { + uint11 period; + uint5 sampleOffset; + } s; + struct Registers { - //$0080-0081 SND_CH1_PITCH + //$0080-0081 SND_CH0_PITCH uint11 pitch; - //$0088 SND_CH1_VOL + //$0088 SND_CH0_VOL uint4 volumeLeft; uint4 volumeRight; //$0090 SND_CTRL uint1 enable; } r; - } channel1; + } channel0; + + struct Channel1 : Channel { + Channel1(); + auto run() -> void; + + struct State { + uint11 period; + uint5 sampleOffset; + } s; - struct Channel2 { struct Registers { - //$0082-0083 SND_CH2_PITCH + //$0082-0083 SND_CH1_PITCH uint11 pitch; - //$0089 SND_CH2_VOL + //$0089 SND_CH1_VOL uint4 volumeLeft; uint4 volumeRight; //$0090 SND_CTRL uint1 enable; uint1 voice; - } r; - } channel2; - struct Channel3 { + //$0092 SND_VOICE_CTRL + uint2 voiceEnableLeft; + uint2 voiceEnableRight; + } r; + } channel1; + + struct Channel2 : Channel { + Channel2(); + auto sweep() -> void; + auto run() -> void; + + struct State { + uint11 period; + uint5 sampleOffset; + + int sweepCounter; + } s; + struct Registers { - //$0084-0085 SND_CH3_PITCH + //$0084-0085 SND_CH2_PITCH uint11 pitch; - //$008a SND_CH3_VOL + //$008a SND_CH2_VOL uint4 volumeLeft; uint4 volumeRight; @@ -56,39 +114,61 @@ struct APU : Thread, IO { uint1 enable; uint1 sweep; } r; - } channel3; + } channel2; + + struct Channel3 : Channel { + Channel3(); + auto noiseSample() -> uint4; + auto run() -> void; + + struct State { + uint11 period; + uint5 sampleOffset; + + uint1 noiseOutput; + uint15 noiseLFSR; + } s; - struct Channel4 { struct Registers { - //$0086-0087 SND_CH4_PITCH + //$0086-0087 SND_CH3_PITCH uint11 pitch; - //$008b SND_CH4_VOL + //$008b SND_CH3_VOL uint4 volumeLeft; uint4 volumeRight; //$008e SND_NOISE - uint5 noiseMode; + uint3 noiseMode; + uint1 noiseReset; + uint1 noiseUpdate; //$0090 SND_CTRL uint1 enable; uint1 noise; } r; + } channel3; + + struct Channel4 : Channel { + Channel4(); + auto run() -> void; + + struct State { + int8 data; + } s; + + struct Registers { + //$006a HYPER_CTRL + uint2 volume; + uint2 scale; + uint3 speed; + uint1 enable; + + //$006b HYPER_CHAN_CTRL + uint4 unknown; + uint1 leftEnable; + uint1 rightEnable; + } r; } channel4; - - struct Registers { - //$008f SND_WAVE_BASE - uint8 waveBase; - - //$0091 SND_OUTPUT - uint1 speakerEnable; - uint2 speakerShift; - uint1 headphoneEnable; - - //$0092 SND_VOICE_CTRL - uint2 voiceEnableLeft; - uint2 voiceEnableRight; - } r; }; extern APU apu; diff --git a/higan/ws/apu/channel.cpp b/higan/ws/apu/channel.cpp new file mode 100644 index 00000000..c0218fdc --- /dev/null +++ b/higan/ws/apu/channel.cpp @@ -0,0 +1,8 @@ +APU::Channel::Channel(uint id) : id(id) { +} + +auto APU::Channel::sample(uint5 index) -> uint4 { + auto data = iram.read((apu.r.waveBase << 6) + (id << 4) + (index >> 1)); + if(index.bit(0) == 0) return data.bits(0,3); + if(index.bit(0) == 1) return data.bits(4,7); +} diff --git a/higan/ws/apu/channel0.cpp b/higan/ws/apu/channel0.cpp new file mode 100644 index 00000000..548c1425 --- /dev/null +++ b/higan/ws/apu/channel0.cpp @@ -0,0 +1,11 @@ +APU::Channel0::Channel0() : Channel(0) { +} + +auto APU::Channel0::run() -> void { + if(--s.period == r.pitch) { + s.period = 0; + auto output = sample(s.sampleOffset++); + o.left = output * r.volumeLeft; + o.right = output * r.volumeRight; + } +} diff --git a/higan/ws/apu/channel1.cpp b/higan/ws/apu/channel1.cpp new file mode 100644 index 00000000..7c70a887 --- /dev/null +++ b/higan/ws/apu/channel1.cpp @@ -0,0 +1,15 @@ +APU::Channel1::Channel1() : Channel(1) { +} + +auto APU::Channel1::run() -> void { + if(r.voice) { + uint8 volume = r.volumeLeft << 4 | r.volumeRight << 0; + o.left = r.voiceEnableLeft ? volume : (uint8)0x80; + o.right = r.voiceEnableRight ? volume : (uint8)0x80; + } else if(--s.period == r.pitch) { + s.period = 0; + auto output = sample(s.sampleOffset++); + o.left = output * r.volumeLeft; + o.right = output * r.volumeRight; + } +} diff --git a/higan/ws/apu/channel2.cpp b/higan/ws/apu/channel2.cpp new file mode 100644 index 00000000..ec7110dd --- /dev/null +++ b/higan/ws/apu/channel2.cpp @@ -0,0 +1,18 @@ +APU::Channel2::Channel2() : Channel(2) { +} + +auto APU::Channel2::sweep() -> void { + if(r.sweep && --s.sweepCounter < 0) { + s.sweepCounter = r.sweepTime; + r.pitch += r.sweepTime; + } +} + +auto APU::Channel2::run() -> void { + if(--s.period == r.pitch) { + s.period = 0; + auto output = sample(s.sampleOffset++); + o.left = output * r.volumeLeft; + o.right = output * r.volumeRight; + } +} diff --git a/higan/ws/apu/channel3.cpp b/higan/ws/apu/channel3.cpp new file mode 100644 index 00000000..d628fad4 --- /dev/null +++ b/higan/ws/apu/channel3.cpp @@ -0,0 +1,30 @@ +APU::Channel3::Channel3() : Channel(3) { +} + +auto APU::Channel3::noiseSample() -> uint4 { + return s.noiseOutput ? 0xf : 0x0; +} + +auto APU::Channel3::run() -> void { + if(--s.period == r.pitch) { + s.period = 0; + + auto output = r.noise ? noiseSample() : sample(s.sampleOffset++); + o.left = output * r.volumeLeft; + o.right = output * r.volumeRight; + + if(r.noiseReset) { + r.noiseReset = 0; + s.noiseLFSR = 0; + s.noiseOutput = 0; + } + + if(r.noiseUpdate) { + static const int taps[8] = {14, 10, 13, 4, 8, 6, 9, 11}; + auto tap = taps[r.noiseMode]; + + s.noiseOutput = (1 ^ (s.noiseLFSR >> 7) ^ (s.noiseLFSR >> tap)) & 1; + s.noiseLFSR = s.noiseLFSR << 1 | s.noiseOutput; + } + } +} diff --git a/higan/ws/apu/channel4.cpp b/higan/ws/apu/channel4.cpp new file mode 100644 index 00000000..30366416 --- /dev/null +++ b/higan/ws/apu/channel4.cpp @@ -0,0 +1,11 @@ +APU::Channel4::Channel4() : Channel(4) { +} + +auto APU::Channel4::run() -> void { + int16 sample = s.data << 8; + if(r.scale != 3) sample >>= r.volume; + if(r.scale == 1) sample |= 0x70000 >> r.volume; + + o.left = r.leftEnable ? sample : (int16)0; + o.right = r.rightEnable ? sample : (int16)0; +} diff --git a/higan/ws/apu/io.cpp b/higan/ws/apu/io.cpp index 9f1a82ae..14b3ea0d 100644 --- a/higan/ws/apu/io.cpp +++ b/higan/ws/apu/io.cpp @@ -1,65 +1,84 @@ auto APU::portRead(uint16 addr) -> uint8 { + //SND_HYPER_CTRL + if(addr == 0x006a) return ( + channel4.r.volume << 0 + | channel4.r.scale << 2 + | channel4.r.speed << 4 + | channel4.r.enable << 7 + ); + + //SND_HYPER_CHAN_CTRL + if(addr == 0x006b) return ( + channel4.r.unknown << 0 + | channel4.r.leftEnable << 5 + | channel4.r.rightEnable << 6 + ); + + //SND_CH0_PITCH + if(addr == 0x0080) return channel0.r.pitch.bits(0, 7); + if(addr == 0x0081) return channel0.r.pitch.bits(8,11); + //SND_CH1_PITCH - if(addr == 0x0080) return channel1.r.pitch.bits(0, 7); - if(addr == 0x0081) return channel1.r.pitch.bits(8,11); + if(addr == 0x0082) return channel1.r.pitch.bits(0, 7); + if(addr == 0x0083) return channel1.r.pitch.bits(8,11); //SND_CH2_PITCH - if(addr == 0x0082) return channel2.r.pitch.bits(0, 7); - if(addr == 0x0083) return channel2.r.pitch.bits(8,11); + if(addr == 0x0084) return channel2.r.pitch.bits(0, 7); + if(addr == 0x0085) return channel2.r.pitch.bits(8,11); //SND_CH3_PITCH - if(addr == 0x0084) return channel3.r.pitch.bits(0, 7); - if(addr == 0x0085) return channel3.r.pitch.bits(8,11); + if(addr == 0x0086) return channel3.r.pitch.bits(0, 7); + if(addr == 0x0087) return channel3.r.pitch.bits(8,11); - //SND_CH4_PITCH - if(addr == 0x0086) return channel4.r.pitch.bits(0, 7); - if(addr == 0x0087) return channel4.r.pitch.bits(8,11); + //SND_CH0_VOL + if(addr == 0x0088) return ( + channel0.r.volumeRight << 0 + | channel0.r.volumeLeft << 4 + ); //SND_CH1_VOL - if(addr == 0x0088) return ( + if(addr == 0x0089) return ( channel1.r.volumeRight << 0 | channel1.r.volumeLeft << 4 ); //SND_CH2_VOL - if(addr == 0x0089) return ( + if(addr == 0x008a) return ( channel2.r.volumeRight << 0 | channel2.r.volumeLeft << 4 ); //SND_CH3_VOL - if(addr == 0x008a) return ( + if(addr == 0x008b) return ( channel3.r.volumeRight << 0 | channel3.r.volumeLeft << 4 ); - //SND_CH4_VOL - if(addr == 0x008b) return ( - channel4.r.volumeRight << 0 - | channel4.r.volumeLeft << 4 - ); - //SND_SWEEP_VALUE - if(addr == 0x008c) return channel3.r.sweepValue; + if(addr == 0x008c) return channel2.r.sweepValue; //SND_SWEEP_TIME - if(addr == 0x008d) return channel3.r.sweepTime; + if(addr == 0x008d) return channel2.r.sweepTime; //SND_NOISE - if(addr == 0x008e) return channel4.r.noiseMode; + //(noiseReset [bit 3] always reads as zero) + if(addr == 0x008e) return ( + channel3.r.noiseMode << 0 + | channel3.r.noiseUpdate << 4 + ); //SND_WAVE_BASE if(addr == 0x008f) return r.waveBase; //SND_CTRL if(addr == 0x0090) return ( - channel1.r.enable << 0 - | channel2.r.enable << 1 - | channel3.r.enable << 2 - | channel4.r.enable << 3 - | channel2.r.voice << 5 - | channel3.r.sweep << 6 - | channel4.r.noise << 7 + channel0.r.enable << 0 + | channel1.r.enable << 1 + | channel2.r.enable << 2 + | channel3.r.enable << 3 + | channel1.r.voice << 5 + | channel2.r.sweep << 6 + | channel3.r.noise << 7 ); //SND_OUTPUT @@ -71,78 +90,95 @@ auto APU::portRead(uint16 addr) -> uint8 { ); //SND_RANDOM - if(addr == 0x0092) return rand() & 0xff; - if(addr == 0x0093) return rand() & 0x7f; + if(addr == 0x0092) return channel3.s.noiseLFSR.bits(0, 7); + if(addr == 0x0093) return channel3.s.noiseLFSR.bits(8,14); //SND_VOICE_CTRL if(addr == 0x0094) return ( - r.voiceEnableRight << 0 - | r.voiceEnableLeft << 2 + channel1.r.voiceEnableRight << 0 + | channel1.r.voiceEnableLeft << 2 ); return 0x00; } auto APU::portWrite(uint16 addr, uint8 data) -> void { + //SND_HYPER_CTRL + if(addr == 0x006a) { + channel4.r.volume = data.bits(0,1); + channel4.r.scale = data.bits(2,3); + channel4.r.speed = data.bits(4,6); + channel4.r.enable = data.bit (7); + } + + //SND_HYPER_CHAN_CTRL + if(addr == 0x006b) { + channel4.r.unknown = data.bits(0,3); + channel4.r.leftEnable = data.bit (5); + channel4.r.rightEnable = data.bit (6); + } + + //SND_CH0_PITCH + if(addr == 0x0080) { channel0.r.pitch.bits(0, 7) = data.bits(0,7); return; } + if(addr == 0x0081) { channel0.r.pitch.bits(8,11) = data.bits(0,3); return; } + //SND_CH1_PITCH - if(addr == 0x0080) { channel1.r.pitch.bits(0, 7) = data.bits(0,7); return; } - if(addr == 0x0081) { channel1.r.pitch.bits(8,11) = data.bits(0,3); return; } + if(addr == 0x0082) { channel1.r.pitch.bits(0, 7) = data.bits(0,7); return; } + if(addr == 0x0083) { channel1.r.pitch.bits(8,11) = data.bits(0,3); return; } //SND_CH2_PITCH - if(addr == 0x0082) { channel2.r.pitch.bits(0, 7) = data.bits(0,7); return; } - if(addr == 0x0083) { channel2.r.pitch.bits(8,11) = data.bits(0,3); return; } + if(addr == 0x0084) { channel2.r.pitch.bits(0, 7) = data.bits(0,7); return; } + if(addr == 0x0085) { channel2.r.pitch.bits(8,11) = data.bits(0,3); return; } //SND_CH3_PITCH - if(addr == 0x0084) { channel3.r.pitch.bits(0, 7) = data.bits(0,7); return; } - if(addr == 0x0085) { channel3.r.pitch.bits(8,11) = data.bits(0,3); return; } + if(addr == 0x0086) { channel3.r.pitch.bits(0, 7) = data.bits(0,7); return; } + if(addr == 0x0087) { channel3.r.pitch.bits(8,11) = data.bits(0,3); return; } - //SND_CH4_PITCH - if(addr == 0x0086) { channel4.r.pitch.bits(0, 7) = data.bits(0,7); return; } - if(addr == 0x0087) { channel4.r.pitch.bits(8,11) = data.bits(0,3); return; } + //SND_CH0_VOL + if(addr == 0x0088) { + channel0.r.volumeRight = data.bits(0,3); + channel0.r.volumeLeft = data.bits(4,7); + return; + } //SND_CH1_VOL - if(addr == 0x0088) { + if(addr == 0x0089) { channel1.r.volumeRight = data.bits(0,3); channel1.r.volumeLeft = data.bits(4,7); return; } //SND_CH2_VOL - if(addr == 0x0089) { + if(addr == 0x008a) { channel2.r.volumeRight = data.bits(0,3); channel2.r.volumeLeft = data.bits(4,7); return; } //SND_CH3_VOL - if(addr == 0x008a) { + if(addr == 0x008b) { channel3.r.volumeRight = data.bits(0,3); channel3.r.volumeLeft = data.bits(4,7); return; } - //SND_CH4_VOL - if(addr == 0x008b) { - channel4.r.volumeRight = data.bits(0,3); - channel4.r.volumeLeft = data.bits(4,7); - return; - } - //SND_SWEEP_VALUE if(addr == 0x008c) { - channel3.r.sweepValue = data; + channel2.r.sweepValue = data; return; } //SND_SWEEP_TIME if(addr == 0x008d) { - channel3.r.sweepTime = data.bits(0,4); + channel2.r.sweepTime = data.bits(0,4); return; } //SND_NOISE if(addr == 0x008e) { - channel4.r.noiseMode = data.bits(0,4); + channel3.r.noiseMode = data.bits(0,2); + channel3.r.noiseReset = data.bit (3); + channel3.r.noiseUpdate = data.bit (4); return; } @@ -154,13 +190,13 @@ auto APU::portWrite(uint16 addr, uint8 data) -> void { //SND_CTRL if(addr == 0x0090) { - channel1.r.enable = data.bit(0); - channel2.r.enable = data.bit(1); - channel3.r.enable = data.bit(2); - channel4.r.enable = data.bit(3); - channel2.r.voice = data.bit(5); - channel3.r.sweep = data.bit(6); - channel4.r.noise = data.bit(7); + channel0.r.enable = data.bit(0); + channel1.r.enable = data.bit(1); + channel2.r.enable = data.bit(2); + channel3.r.enable = data.bit(3); + channel1.r.voice = data.bit(5); + channel2.r.sweep = data.bit(6); + channel3.r.noise = data.bit(7); return; } @@ -174,8 +210,8 @@ auto APU::portWrite(uint16 addr, uint8 data) -> void { //SND_VOICE_CTRL if(addr == 0x0094) { - r.voiceEnableRight = data.bits(0,1); - r.voiceEnableLeft = data.bits(2,3); + channel1.r.voiceEnableRight = data.bits(0,1); + channel1.r.voiceEnableLeft = data.bits(2,3); return; } } diff --git a/higan/ws/cpu/interrupt.cpp b/higan/ws/cpu/interrupt.cpp index d0cc3b61..858fd274 100644 --- a/higan/ws/cpu/interrupt.cpp +++ b/higan/ws/cpu/interrupt.cpp @@ -1,22 +1,17 @@ auto CPU::poll() -> void { - if(!state.poll || !(r.interruptStatus & r.interruptEnable)) return; - state.halt = false; - if(!V30MZ::r.f.i) return; + if(!V30MZ::r.f.i || !state.poll) return; - //find and execute first pending interrupt in order of priority (7-0) for(int n = 7; n >= 0; n--) { - if(r.interruptStatus & r.interruptEnable & (1 << n)) { - return interrupt(r.interruptBase + n); - } + if(!r.interruptEnable.bit(n)) continue; + if(!r.interruptStatus.bit(n)) continue; + return interrupt(r.interruptBase + n); } } -auto CPU::raise(Interrupt i) -> void { - auto mask = 1 << (uint)i; - r.interruptStatus |= mask; +auto CPU::raise(Interrupt irq) -> void { + r.interruptStatus.bit((uint)irq) = 1; } -auto CPU::lower(Interrupt i) -> void { - auto mask = 1 << (uint)i; - r.interruptStatus &= ~mask; +auto CPU::lower(Interrupt irq) -> void { + r.interruptStatus.bit((uint)irq) = 0; } diff --git a/higan/ws/ppu/io.cpp b/higan/ws/ppu/io.cpp index 596d1515..96f12825 100644 --- a/higan/ws/ppu/io.cpp +++ b/higan/ws/ppu/io.cpp @@ -123,6 +123,30 @@ auto PPU::portRead(uint16 addr) -> uint8 { ); } + //TMR_CTRL + if(addr == 0x00a2) return ( + r.htimerEnable << 0 + | r.htimerRepeat << 1 + | r.vtimerEnable << 2 + | r.vtimerRepeat << 3 + ); + + //HTMR_FREQ + if(addr == 0x00a4) return r.htimerFrequency.byte(0); + if(addr == 0x00a5) return r.htimerFrequency.byte(1); + + //VTMR_FREQ + if(addr == 0x00a6) return r.vtimerFrequency.byte(0); + if(addr == 0x00a7) return r.vtimerFrequency.byte(1); + + //HTMR_CTR + if(addr == 0x00a8) return r.htimerCounter.byte(0); + if(addr == 0x00a9) return r.htimerCounter.byte(1); + + //VTMR_CTR + if(addr == 0x00aa) return r.vtimerCounter.byte(0); + if(addr == 0x00ab) return r.vtimerCounter.byte(1); + return 0x00; } @@ -289,4 +313,20 @@ auto PPU::portWrite(uint16 addr, uint8 data) -> void { r.palette[addr.bits(4,1)].color[addr.bit(0) * 2 + 0] = data.bits(2,0); return; } + + //TMR_CTRL + if(addr == 0x00a2) { + r.htimerEnable = data.bit(0); + r.htimerRepeat = data.bit(1); + r.vtimerEnable = data.bit(2); + r.vtimerRepeat = data.bit(3); + } + + //HTMR_FREQ + if(addr == 0x00a4) r.htimerFrequency.byte(0) = data; + if(addr == 0x00a5) r.htimerFrequency.byte(1) = data; + + //VTMR_FREQ + if(addr == 0x00a6) r.vtimerFrequency.byte(0) = data; + if(addr == 0x00a7) r.vtimerFrequency.byte(1) = data; } diff --git a/higan/ws/ppu/ppu.cpp b/higan/ws/ppu/ppu.cpp index fbd28f5b..995d0a74 100644 --- a/higan/ws/ppu/ppu.cpp +++ b/higan/ws/ppu/ppu.cpp @@ -31,13 +31,21 @@ auto PPU::main() -> void { output[status.vclk * 224 + status.hclk] = pixel.color; step(1); } - for(uint x = 224; x < 256; x++) { - step(1); - } + step(32); } else { step(256); } scanline(); + if(r.htimerEnable && r.htimerCounter < r.htimerFrequency) { + if(++r.htimerCounter == r.htimerFrequency) { + if(r.htimerRepeat) { + r.htimerCounter = 0; + } else { + r.htimerEnable = false; + } + cpu.raise(CPU::Interrupt::HblankTimer); + } + } } auto PPU::scanline() -> void { @@ -48,6 +56,16 @@ auto PPU::scanline() -> void { } if(status.vclk == 144) { cpu.raise(CPU::Interrupt::Vblank); + if(r.vtimerEnable && r.vtimerCounter < r.vtimerFrequency) { + if(++r.vtimerCounter == r.vtimerFrequency) { + if(r.vtimerRepeat) { + r.vtimerCounter = 0; + } else { + r.vtimerEnable = false; + } + cpu.raise(CPU::Interrupt::VblankTimer); + } + } } if(status.vclk == 159) frame(); } @@ -70,6 +88,8 @@ auto PPU::power() -> void { for(uint n = 0x0000; n <= 0x0017; n++) iomap[n] = this; for(uint n = 0x001c; n <= 0x003f; n++) iomap[n] = this; + iomap[0x00a2] = this; + for(uint n = 0x00a4; n <= 0x00ab; n++) iomap[n] = this; for(auto& n : output) n = 0; @@ -110,6 +130,14 @@ auto PPU::power() -> void { r.iconSleep = 0; r.vtotal = 158; r.vblank = 155; + r.htimerEnable = 0; + r.htimerRepeat = 0; + r.vtimerEnable = 0; + r.vtimerRepeat = 0; + r.htimerFrequency = 0; + r.vtimerFrequency = 0; + r.htimerCounter = 0; + r.vtimerCounter = 0; for(auto& color : r.pool) color = 0; for(auto& p : r.palette) for(auto& color : p.color) color = 0; diff --git a/higan/ws/ppu/ppu.hpp b/higan/ws/ppu/ppu.hpp index 67804d06..b63cb23e 100644 --- a/higan/ws/ppu/ppu.hpp +++ b/higan/ws/ppu/ppu.hpp @@ -143,6 +143,24 @@ struct PPU : Thread, IO { struct Palette { uint3 color[4]; } palette[16]; + + //$00a2 TMR_CTRL + uint1 htimerEnable; + uint1 htimerRepeat; + uint1 vtimerEnable; + uint1 vtimerRepeat; + + //$00a4,$00a5 HTMR_FREQ + uint16 htimerFrequency; + + //$00a6,$00a7 VTMR_FREQ + uint16 vtimerFrequency; + + //$00a8,$00a9 HTMR_CTR + uint16 htimerCounter; + + //$00aa,$00ab VTMR_CTR + uint16 vtimerCounter; } r; };