mirror of https://github.com/bsnes-emu/bsnes.git
Update to v097r26 release.
byuu says: Changelog: - WS: fixed 8-bit sign-extended imul (fixes Star Hearts completely, Final Fantasy world map) - WS: fixed rcl/rcr carry shifting (fixes Crazy Climber, others) - WS: added sound DMA emulation (Star Hearts rain sound for one example) - WS: added OAM caching, but it's forced every line for now because otherwise there are too many sprite glitches - WS: use headphoneEnable bit instead of speakerEnable bit (fixes muted audio in games) - WS: various code cleanups (I/O mapping, audio channel naming, etc) The hypervoice channel doesn't sound all that great just yet. But I'm not sure how it's supposed to sound. I need a better example of some more complex music. What's left are some unknown register status bits (especially in the sound area), keypad interrupts, RTC emulation, CPU prefetch emulation. And then it's all just bugs. Lots and lots of bugs that need to be fixed. EDIT: oops, bad typo in the code. ws/ppu/ppu.cpp line 20: change range(256) to range(224). Also, delete the r.speed stuff from channel5.cpp to make the rain sound a lot better in Star Hearts. Apparently that's outdated and not what the bits really do.
This commit is contained in:
parent
b586471562
commit
a7f7985581
|
@ -6,7 +6,7 @@ using namespace nall;
|
|||
|
||||
namespace Emulator {
|
||||
static const string Name = "higan";
|
||||
static const string Version = "097.25";
|
||||
static const string Version = "097.26";
|
||||
static const string Author = "byuu";
|
||||
static const string License = "GPLv3";
|
||||
static const string Website = "http://byuu.org/";
|
||||
|
|
|
@ -119,7 +119,7 @@ auto V30MZ::alRcl(Size size, uint16 x, uint5 y) -> uint16 {
|
|||
uint16 result = x;
|
||||
for(uint n = 0; n < y; n++) {
|
||||
bool carry = result & sign;
|
||||
result = (result << 1) | carry;
|
||||
result = (result << 1) | r.f.c;
|
||||
r.f.c = carry;
|
||||
}
|
||||
r.f.v = (x ^ result) & sign;
|
||||
|
@ -130,7 +130,7 @@ auto V30MZ::alRcr(Size size, uint16 x, uint5 y) -> uint16 {
|
|||
uint16 result = x;
|
||||
for(uint n = 0; n < y; n++) {
|
||||
bool carry = result & 1;
|
||||
result = (carry ? sign : 0) | (result >> 1);
|
||||
result = (r.f.c ? sign : 0) | (result >> 1);
|
||||
r.f.c = carry;
|
||||
}
|
||||
r.f.v = (x ^ result) & sign;
|
||||
|
|
|
@ -134,7 +134,7 @@ auto V30MZ::opTestMemReg(Size size) {
|
|||
auto V30MZ::opMultiplySignedRegMemImm(Size size) {
|
||||
wait(2);
|
||||
modRM();
|
||||
setReg(size, alMuli(size, getMem(size), size == Word ? (int16_t)fetch(Word) : (int8_t)fetch(Byte)));
|
||||
setReg(Word, alMuli(Word, getMem(Word), size == Word ? (int16_t)fetch(Word) : (int8_t)fetch(Byte)));
|
||||
}
|
||||
|
||||
//40 inc ax
|
||||
|
|
|
@ -18,7 +18,7 @@ namespace Processor {
|
|||
#include "disassembler.cpp"
|
||||
|
||||
auto V30MZ::debug(string text) -> void {
|
||||
print(text, "\n");
|
||||
//print(text, "\n");
|
||||
}
|
||||
|
||||
auto V30MZ::power() -> void {
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
system name:WonderSwan Color
|
||||
eeprom name=internal.rom size=2048
|
||||
eeprom name=internal.ram size=2048
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
system name:WonderSwan
|
||||
eeprom name=internal.rom size=128
|
||||
eeprom name=internal.ram size=128
|
||||
|
|
|
@ -4,56 +4,54 @@ namespace WonderSwan {
|
|||
|
||||
APU apu;
|
||||
#include "io.cpp"
|
||||
#include "channel.cpp"
|
||||
#include "channel0.cpp"
|
||||
#include "dma.cpp"
|
||||
#include "channel1.cpp"
|
||||
#include "channel2.cpp"
|
||||
#include "channel3.cpp"
|
||||
#include "channel4.cpp"
|
||||
#include "channel5.cpp"
|
||||
|
||||
auto APU::Enter() -> void {
|
||||
while(true) scheduler.synchronize(), apu.main();
|
||||
}
|
||||
|
||||
auto APU::main() -> void {
|
||||
channel0.run();
|
||||
dma.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();
|
||||
}
|
||||
|
||||
channel5.run();
|
||||
if(s.clock.bits(0,12) == 0) channel3.sweep();
|
||||
if(s.clock.bits(0, 6) == 0) dacRun();
|
||||
s.clock++;
|
||||
step(1);
|
||||
}
|
||||
|
||||
auto APU::sample(uint channel, uint5 index) -> uint4 {
|
||||
uint8 data = iram.read((r.waveBase << 6) + (--channel << 4) + (index >> 1));
|
||||
if(index.bit(0) == 0) return data.bits(0,3);
|
||||
if(index.bit(0) == 1) return data.bits(4,7);
|
||||
}
|
||||
|
||||
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);
|
||||
if(channel5.r.enable) left += (int11)channel5.o.left >> 3;
|
||||
left = sclamp<16>(left << 5);
|
||||
|
||||
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(channel5.r.enable) right += (int11)channel5.o.right >> 3;
|
||||
right = sclamp<16>(right << 5);
|
||||
|
||||
if(!r.speakerEnable) {
|
||||
if(!r.headphoneEnable) {
|
||||
left = 0;
|
||||
right = 0;
|
||||
}
|
||||
|
@ -69,8 +67,11 @@ 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;
|
||||
bus.map(this, 0x004a, 0x004c);
|
||||
bus.map(this, 0x004e, 0x0050);
|
||||
bus.map(this, 0x0052);
|
||||
bus.map(this, 0x006a, 0x006b);
|
||||
bus.map(this, 0x0080, 0x0095);
|
||||
|
||||
s.clock = 0;
|
||||
r.waveBase = 0;
|
||||
|
@ -78,14 +79,17 @@ auto APU::power() -> void {
|
|||
r.speakerShift = 0;
|
||||
r.headphoneEnable = 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;
|
||||
dma.s.clock = 0;
|
||||
dma.s.source = 0;
|
||||
dma.s.length = 0;
|
||||
dma.r.source = 0;
|
||||
dma.r.length = 0;
|
||||
dma.r.rate = 0;
|
||||
dma.r.unknown = 0;
|
||||
dma.r.loop = 0;
|
||||
dma.r.target = 0;
|
||||
dma.r.direction = 0;
|
||||
dma.r.enable = 0;
|
||||
|
||||
channel1.o.left = 0;
|
||||
channel1.o.right = 0;
|
||||
|
@ -95,48 +99,58 @@ auto APU::power() -> void {
|
|||
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;
|
||||
channel2.r.voice = 0;
|
||||
channel2.r.voiceEnableLeft = 0;
|
||||
channel2.r.voiceEnableRight = 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.s.sweepCounter = 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.sweepValue = 0;
|
||||
channel3.r.sweepTime = 0;
|
||||
channel3.r.enable = 0;
|
||||
channel3.r.noise = 0;
|
||||
channel3.r.sweep = 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.s.period = 0;
|
||||
channel4.s.sampleOffset = 0;
|
||||
channel4.s.noiseOutput = 0;
|
||||
channel4.s.noiseLFSR = 0;
|
||||
channel4.r.pitch = 0;
|
||||
channel4.r.volumeLeft = 0;
|
||||
channel4.r.volumeRight = 0;
|
||||
channel4.r.noiseMode = 0;
|
||||
channel4.r.noiseReset = 0;
|
||||
channel4.r.noiseUpdate = 0;
|
||||
channel4.r.enable = 0;
|
||||
channel4.r.unknown = 0;
|
||||
channel4.r.leftEnable = 0;
|
||||
channel4.r.rightEnable = 0;
|
||||
channel4.r.noise = 0;
|
||||
|
||||
channel5.o.left = 0;
|
||||
channel5.o.right = 0;
|
||||
channel5.s.clock = 0;
|
||||
channel5.s.data = 0;
|
||||
channel5.r.volume = 0;
|
||||
channel5.r.scale = 0;
|
||||
channel5.r.speed = 0;
|
||||
channel5.r.enable = 0;
|
||||
channel5.r.unknown = 0;
|
||||
channel5.r.leftEnable = 0;
|
||||
channel5.r.rightEnable = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
struct APU : Thread, IO {
|
||||
static auto Enter() -> void;
|
||||
auto main() -> void;
|
||||
auto sample(uint channel, uint5 index) -> uint4;
|
||||
auto dacRun() -> void;
|
||||
auto step(uint clocks) -> void;
|
||||
auto power() -> void;
|
||||
|
@ -13,7 +14,47 @@ struct APU : Thread, IO {
|
|||
uint13 clock;
|
||||
} s;
|
||||
|
||||
struct DMA {
|
||||
auto run() -> void;
|
||||
|
||||
struct State {
|
||||
uint clock;
|
||||
uint20 source;
|
||||
uint20 length;
|
||||
} s;
|
||||
|
||||
struct Registers {
|
||||
//$004a-$004c SDMA_SRC
|
||||
uint20 source;
|
||||
|
||||
//$004e-$0050 SDMA_LEN
|
||||
uint20 length;
|
||||
|
||||
//$0052 SDMA_CTRL
|
||||
uint2 rate;
|
||||
uint1 unknown;
|
||||
uint1 loop;
|
||||
uint1 target;
|
||||
uint1 direction;
|
||||
uint1 enable;
|
||||
} r;
|
||||
} dma;
|
||||
|
||||
struct Registers {
|
||||
//$004a-$004c SDMA_SRC
|
||||
uint20 dmaSource;
|
||||
|
||||
//$004e-$0050 SDMA_LEN
|
||||
uint20 dmaLength;
|
||||
|
||||
//$0052 SDMA_CTRL
|
||||
uint2 dmaRate;
|
||||
uint1 dmaUnknown;
|
||||
uint1 dmaLoop;
|
||||
uint1 dmaTarget;
|
||||
uint1 dmaDirection;
|
||||
uint1 dmaEnable;
|
||||
|
||||
//$008f SND_WAVE_BASE
|
||||
uint8 waveBase;
|
||||
|
||||
|
@ -23,21 +64,13 @@ struct APU : Thread, IO {
|
|||
uint1 headphoneEnable;
|
||||
} r;
|
||||
|
||||
struct Channel {
|
||||
Channel(uint id);
|
||||
auto sample(uint5 index) -> uint4;
|
||||
|
||||
const uint id;
|
||||
struct Channel1 {
|
||||
auto run() -> void;
|
||||
|
||||
struct Output {
|
||||
int16 left;
|
||||
int16 right;
|
||||
uint8 left;
|
||||
uint8 right;
|
||||
} o;
|
||||
};
|
||||
|
||||
struct Channel0 : Channel {
|
||||
Channel0();
|
||||
auto run() -> void;
|
||||
|
||||
struct State {
|
||||
uint11 period;
|
||||
|
@ -45,32 +78,36 @@ struct APU : Thread, IO {
|
|||
} s;
|
||||
|
||||
struct Registers {
|
||||
//$0080-0081 SND_CH0_PITCH
|
||||
//$0080-0081 SND_CH1_PITCH
|
||||
uint11 pitch;
|
||||
|
||||
//$0088 SND_CH0_VOL
|
||||
//$0088 SND_CH1_VOL
|
||||
uint4 volumeLeft;
|
||||
uint4 volumeRight;
|
||||
|
||||
//$0090 SND_CTRL
|
||||
uint1 enable;
|
||||
} r;
|
||||
} channel0;
|
||||
} channel1;
|
||||
|
||||
struct Channel1 : Channel {
|
||||
Channel1();
|
||||
struct Channel2 {
|
||||
auto run() -> void;
|
||||
|
||||
struct Output {
|
||||
uint8 left;
|
||||
uint8 right;
|
||||
} o;
|
||||
|
||||
struct State {
|
||||
uint11 period;
|
||||
uint5 sampleOffset;
|
||||
} s;
|
||||
|
||||
struct Registers {
|
||||
//$0082-0083 SND_CH1_PITCH
|
||||
//$0082-0083 SND_CH2_PITCH
|
||||
uint11 pitch;
|
||||
|
||||
//$0089 SND_CH1_VOL
|
||||
//$0089 SND_CH2_VOL
|
||||
uint4 volumeLeft;
|
||||
uint4 volumeRight;
|
||||
|
||||
|
@ -78,17 +115,21 @@ struct APU : Thread, IO {
|
|||
uint1 enable;
|
||||
uint1 voice;
|
||||
|
||||
//$0092 SND_VOICE_CTRL
|
||||
//$0094 SND_VOICE_CTRL
|
||||
uint2 voiceEnableLeft;
|
||||
uint2 voiceEnableRight;
|
||||
} r;
|
||||
} channel1;
|
||||
} channel2;
|
||||
|
||||
struct Channel2 : Channel {
|
||||
Channel2();
|
||||
struct Channel3 {
|
||||
auto sweep() -> void;
|
||||
auto run() -> void;
|
||||
|
||||
struct Output {
|
||||
uint8 left;
|
||||
uint8 right;
|
||||
} o;
|
||||
|
||||
struct State {
|
||||
uint11 period;
|
||||
uint5 sampleOffset;
|
||||
|
@ -97,10 +138,10 @@ struct APU : Thread, IO {
|
|||
} s;
|
||||
|
||||
struct Registers {
|
||||
//$0084-0085 SND_CH2_PITCH
|
||||
//$0084-0085 SND_CH3_PITCH
|
||||
uint11 pitch;
|
||||
|
||||
//$008a SND_CH2_VOL
|
||||
//$008a SND_CH3_VOL
|
||||
uint4 volumeLeft;
|
||||
uint4 volumeRight;
|
||||
|
||||
|
@ -114,13 +155,17 @@ struct APU : Thread, IO {
|
|||
uint1 enable;
|
||||
uint1 sweep;
|
||||
} r;
|
||||
} channel2;
|
||||
} channel3;
|
||||
|
||||
struct Channel3 : Channel {
|
||||
Channel3();
|
||||
struct Channel4 {
|
||||
auto noiseSample() -> uint4;
|
||||
auto run() -> void;
|
||||
|
||||
struct Output {
|
||||
uint8 left;
|
||||
uint8 right;
|
||||
} o;
|
||||
|
||||
struct State {
|
||||
uint11 period;
|
||||
uint5 sampleOffset;
|
||||
|
@ -130,10 +175,10 @@ struct APU : Thread, IO {
|
|||
} s;
|
||||
|
||||
struct Registers {
|
||||
//$0086-0087 SND_CH3_PITCH
|
||||
//$0086-0087 SND_CH4_PITCH
|
||||
uint11 pitch;
|
||||
|
||||
//$008b SND_CH3_VOL
|
||||
//$008b SND_CH4_VOL
|
||||
uint4 volumeLeft;
|
||||
uint4 volumeRight;
|
||||
|
||||
|
@ -146,14 +191,19 @@ struct APU : Thread, IO {
|
|||
uint1 enable;
|
||||
uint1 noise;
|
||||
} r;
|
||||
} channel3;
|
||||
} channel4;
|
||||
|
||||
struct Channel4 : Channel {
|
||||
Channel4();
|
||||
struct Channel5 {
|
||||
auto run() -> void;
|
||||
|
||||
struct Output {
|
||||
uint11 left;
|
||||
uint11 right;
|
||||
} o;
|
||||
|
||||
struct State {
|
||||
int8 data;
|
||||
uint clock;
|
||||
uint8 data;
|
||||
} s;
|
||||
|
||||
struct Registers {
|
||||
|
@ -168,7 +218,7 @@ struct APU : Thread, IO {
|
|||
uint1 leftEnable;
|
||||
uint1 rightEnable;
|
||||
} r;
|
||||
} channel4;
|
||||
} channel5;
|
||||
};
|
||||
|
||||
extern APU apu;
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
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);
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
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;
|
||||
}
|
||||
}
|
|
@ -1,14 +1,7 @@
|
|||
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) {
|
||||
if(--s.period == r.pitch) {
|
||||
s.period = 0;
|
||||
auto output = sample(s.sampleOffset++);
|
||||
auto output = apu.sample(1, s.sampleOffset++);
|
||||
o.left = output * r.volumeLeft;
|
||||
o.right = output * r.volumeRight;
|
||||
}
|
||||
|
|
|
@ -1,17 +1,11 @@
|
|||
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) {
|
||||
if(r.voice) {
|
||||
uint8 volume = r.volumeLeft << 4 | r.volumeRight << 0;
|
||||
o.left = r.voiceEnableLeft ? volume : (uint8)0;
|
||||
o.right = r.voiceEnableRight ? volume : (uint8)0;
|
||||
} else if(--s.period == r.pitch) {
|
||||
s.period = 0;
|
||||
auto output = sample(s.sampleOffset++);
|
||||
auto output = apu.sample(2, s.sampleOffset++);
|
||||
o.left = output * r.volumeLeft;
|
||||
o.right = output * r.volumeRight;
|
||||
}
|
||||
|
|
|
@ -1,30 +1,15 @@
|
|||
APU::Channel3::Channel3() : Channel(3) {
|
||||
auto APU::Channel3::sweep() -> void {
|
||||
if(r.sweep && --s.sweepCounter < 0) {
|
||||
s.sweepCounter = r.sweepTime;
|
||||
r.pitch += r.sweepTime;
|
||||
}
|
||||
|
||||
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++);
|
||||
auto output = apu.sample(3, 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,27 @@
|
|||
APU::Channel4::Channel4() : Channel(4) {
|
||||
auto APU::Channel4::noiseSample() -> uint4 {
|
||||
return s.noiseOutput ? 0xf : 0x0;
|
||||
}
|
||||
|
||||
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;
|
||||
if(--s.period == r.pitch) {
|
||||
s.period = 0;
|
||||
|
||||
o.left = r.leftEnable ? sample : (int16)0;
|
||||
o.right = r.rightEnable ? sample : (int16)0;
|
||||
auto output = r.noise ? noiseSample() : apu.sample(4, 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
auto APU::Channel5::run() -> void {
|
||||
if(r.speed <= 5 && s.clock++ < 1536) return; //2000hz
|
||||
if(r.speed == 6 && s.clock++ < 2048) return; //1500hz
|
||||
if(r.speed == 7 && s.clock++ < 3072) return; //1000hz
|
||||
s.clock = 0;
|
||||
|
||||
uint11 output = s.data;
|
||||
switch(r.scale) {
|
||||
case 0: output <<= 3 - r.volume; break;
|
||||
case 1: output <<= 3 - r.volume; output |= -0x100 << (3 - r.volume); break;
|
||||
case 2: output <<= 3 - r.volume; break;
|
||||
case 3: output <<= r.volume; break;
|
||||
}
|
||||
|
||||
o.left = r.leftEnable ? output : (uint11)0;
|
||||
o.right = r.rightEnable ? output : (uint11)0;
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
auto APU::DMA::run() -> void {
|
||||
if(!r.enable) return;
|
||||
|
||||
if(r.rate == 0 && ++s.clock < 768) return; // 4000hz
|
||||
if(r.rate == 1 && ++s.clock < 512) return; // 6000hz
|
||||
if(r.rate == 2 && ++s.clock < 256) return; //12000hz
|
||||
if(r.rate == 3 && ++s.clock < 128) return; //24000hz
|
||||
s.clock = 0;
|
||||
|
||||
uint8 data = bus.read(s.source);
|
||||
if(r.direction == 0) s.source++;
|
||||
if(r.direction == 1) s.source--;
|
||||
|
||||
if(r.target == 0) {
|
||||
apu.channel2.r.volumeRight = data.bits(0,3);
|
||||
apu.channel2.r.volumeLeft = data.bits(4,7);
|
||||
} else {
|
||||
apu.channel5.s.data = data;
|
||||
}
|
||||
|
||||
if(--s.length) return;
|
||||
|
||||
if(r.loop) {
|
||||
s.source = r.source;
|
||||
s.length = r.length;
|
||||
} else {
|
||||
r.enable = false;
|
||||
}
|
||||
}
|
|
@ -1,70 +1,90 @@
|
|||
auto APU::portRead(uint16 addr) -> uint8 {
|
||||
//SDMA_SRC
|
||||
if(addr == 0x004a) return dma.s.source.bits( 0, 7);
|
||||
if(addr == 0x004b) return dma.s.source.bits( 8,15);
|
||||
if(addr == 0x004c) return dma.s.source.bits(16,19);
|
||||
|
||||
//SDMA_LEN
|
||||
if(addr == 0x004e) return dma.s.length.bits( 0, 7);
|
||||
if(addr == 0x004f) return dma.s.length.bits( 8,15);
|
||||
if(addr == 0x0050) return dma.s.length.bits(16,19);
|
||||
|
||||
//SDMA_CTRL
|
||||
if(addr == 0x0052) return (
|
||||
dma.r.rate << 0
|
||||
| dma.r.unknown << 2
|
||||
| dma.r.loop << 3
|
||||
| dma.r.target << 4
|
||||
| dma.r.direction << 6
|
||||
| dma.r.enable << 7
|
||||
);
|
||||
|
||||
//SND_HYPER_CTRL
|
||||
if(addr == 0x006a) return (
|
||||
channel4.r.volume << 0
|
||||
| channel4.r.scale << 2
|
||||
| channel4.r.speed << 4
|
||||
| channel4.r.enable << 7
|
||||
channel5.r.volume << 0
|
||||
| channel5.r.scale << 2
|
||||
| channel5.r.speed << 4
|
||||
| channel5.r.enable << 7
|
||||
);
|
||||
|
||||
//SND_HYPER_CHAN_CTRL
|
||||
if(addr == 0x006b) return (
|
||||
channel4.r.unknown << 0
|
||||
| channel4.r.leftEnable << 5
|
||||
| channel4.r.rightEnable << 6
|
||||
channel5.r.unknown << 0
|
||||
| channel5.r.leftEnable << 5
|
||||
| channel5.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 == 0x0082) return channel1.r.pitch.bits(0, 7);
|
||||
if(addr == 0x0083) return channel1.r.pitch.bits(8,11);
|
||||
if(addr == 0x0080) return channel1.r.pitch.bits(0, 7);
|
||||
if(addr == 0x0081) return channel1.r.pitch.bits(8,11);
|
||||
|
||||
//SND_CH2_PITCH
|
||||
if(addr == 0x0084) return channel2.r.pitch.bits(0, 7);
|
||||
if(addr == 0x0085) return channel2.r.pitch.bits(8,11);
|
||||
if(addr == 0x0082) return channel2.r.pitch.bits(0, 7);
|
||||
if(addr == 0x0083) return channel2.r.pitch.bits(8,11);
|
||||
|
||||
//SND_CH3_PITCH
|
||||
if(addr == 0x0086) return channel3.r.pitch.bits(0, 7);
|
||||
if(addr == 0x0087) return channel3.r.pitch.bits(8,11);
|
||||
if(addr == 0x0084) return channel3.r.pitch.bits(0, 7);
|
||||
if(addr == 0x0085) return channel3.r.pitch.bits(8,11);
|
||||
|
||||
//SND_CH0_VOL
|
||||
if(addr == 0x0088) return (
|
||||
channel0.r.volumeRight << 0
|
||||
| channel0.r.volumeLeft << 4
|
||||
);
|
||||
//SND_CH4_PITCH
|
||||
if(addr == 0x0086) return channel4.r.pitch.bits(0, 7);
|
||||
if(addr == 0x0087) return channel4.r.pitch.bits(8,11);
|
||||
|
||||
//SND_CH1_VOL
|
||||
if(addr == 0x0089) return (
|
||||
if(addr == 0x0088) return (
|
||||
channel1.r.volumeRight << 0
|
||||
| channel1.r.volumeLeft << 4
|
||||
);
|
||||
|
||||
//SND_CH2_VOL
|
||||
if(addr == 0x008a) return (
|
||||
if(addr == 0x0089) return (
|
||||
channel2.r.volumeRight << 0
|
||||
| channel2.r.volumeLeft << 4
|
||||
);
|
||||
|
||||
//SND_CH3_VOL
|
||||
if(addr == 0x008b) return (
|
||||
if(addr == 0x008a) 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 channel2.r.sweepValue;
|
||||
if(addr == 0x008c) return channel3.r.sweepValue;
|
||||
|
||||
//SND_SWEEP_TIME
|
||||
if(addr == 0x008d) return channel2.r.sweepTime;
|
||||
if(addr == 0x008d) return channel3.r.sweepTime;
|
||||
|
||||
//SND_NOISE
|
||||
//(noiseReset [bit 3] always reads as zero)
|
||||
if(addr == 0x008e) return (
|
||||
channel3.r.noiseMode << 0
|
||||
| channel3.r.noiseUpdate << 4
|
||||
channel4.r.noiseMode << 0
|
||||
| channel4.r.noiseUpdate << 4
|
||||
);
|
||||
|
||||
//SND_WAVE_BASE
|
||||
|
@ -72,13 +92,13 @@ auto APU::portRead(uint16 addr) -> uint8 {
|
|||
|
||||
//SND_CTRL
|
||||
if(addr == 0x0090) return (
|
||||
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
|
||||
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
|
||||
);
|
||||
|
||||
//SND_OUTPUT
|
||||
|
@ -90,114 +110,127 @@ auto APU::portRead(uint16 addr) -> uint8 {
|
|||
);
|
||||
|
||||
//SND_RANDOM
|
||||
if(addr == 0x0092) return channel3.s.noiseLFSR.bits(0, 7);
|
||||
if(addr == 0x0093) return channel3.s.noiseLFSR.bits(8,14);
|
||||
if(addr == 0x0092) return channel4.s.noiseLFSR.bits(0, 7);
|
||||
if(addr == 0x0093) return channel4.s.noiseLFSR.bits(8,14);
|
||||
|
||||
//SND_VOICE_CTRL
|
||||
if(addr == 0x0094) return (
|
||||
channel1.r.voiceEnableRight << 0
|
||||
| channel1.r.voiceEnableLeft << 2
|
||||
channel2.r.voiceEnableRight << 0
|
||||
| channel2.r.voiceEnableLeft << 2
|
||||
);
|
||||
|
||||
//SND_HYPERVOICE
|
||||
if(addr == 0x0095) return channel5.s.data;
|
||||
|
||||
return 0x00;
|
||||
}
|
||||
|
||||
auto APU::portWrite(uint16 addr, uint8 data) -> void {
|
||||
//SDMA_SRC
|
||||
if(addr == 0x004a) dma.r.source.bits( 0, 7) = data.bits(0,7);
|
||||
if(addr == 0x004b) dma.r.source.bits( 8,15) = data.bits(0,7);
|
||||
if(addr == 0x004c) dma.r.source.bits(16,19) = data.bits(0,3);
|
||||
|
||||
//SDMA_LEN
|
||||
if(addr == 0x004e) dma.r.length.bits( 0, 7) = data.bits(0,7);
|
||||
if(addr == 0x004f) dma.r.length.bits( 8,15) = data.bits(0,7);
|
||||
if(addr == 0x0050) dma.r.length.bits(16,19) = data.bits(0,3);
|
||||
|
||||
//SDMA_CTRL
|
||||
if(addr == 0x0052) {
|
||||
bool trigger = !dma.r.enable && data.bit(7);
|
||||
dma.r.rate = data.bits(0,1);
|
||||
dma.r.unknown = data.bit (2);
|
||||
dma.r.loop = data.bit (3);
|
||||
dma.r.target = data.bit (4);
|
||||
dma.r.direction = data.bit (6);
|
||||
dma.r.enable = data.bit (7);
|
||||
if(trigger) {
|
||||
dma.s.source = dma.r.source;
|
||||
dma.s.length = dma.r.length;
|
||||
}
|
||||
}
|
||||
|
||||
//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);
|
||||
channel5.r.volume = data.bits(0,1);
|
||||
channel5.r.scale = data.bits(2,3);
|
||||
channel5.r.speed = data.bits(4,6);
|
||||
channel5.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);
|
||||
channel5.r.unknown = data.bits(0,3);
|
||||
channel5.r.leftEnable = data.bit (5);
|
||||
channel5.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 == 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; }
|
||||
if(addr == 0x0080) channel1.r.pitch.bits(0, 7) = data.bits(0,7);
|
||||
if(addr == 0x0081) channel1.r.pitch.bits(8,11) = data.bits(0,3);
|
||||
|
||||
//SND_CH2_PITCH
|
||||
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; }
|
||||
if(addr == 0x0082) channel2.r.pitch.bits(0, 7) = data.bits(0,7);
|
||||
if(addr == 0x0083) channel2.r.pitch.bits(8,11) = data.bits(0,3);
|
||||
|
||||
//SND_CH3_PITCH
|
||||
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; }
|
||||
if(addr == 0x0084) channel3.r.pitch.bits(0, 7) = data.bits(0,7);
|
||||
if(addr == 0x0085) channel3.r.pitch.bits(8,11) = data.bits(0,3);
|
||||
|
||||
//SND_CH0_VOL
|
||||
if(addr == 0x0088) {
|
||||
channel0.r.volumeRight = data.bits(0,3);
|
||||
channel0.r.volumeLeft = data.bits(4,7);
|
||||
return;
|
||||
}
|
||||
//SND_CH4_PITCH
|
||||
if(addr == 0x0086) channel4.r.pitch.bits(0, 7) = data.bits(0,7);
|
||||
if(addr == 0x0087) channel4.r.pitch.bits(8,11) = data.bits(0,3);
|
||||
|
||||
//SND_CH1_VOL
|
||||
if(addr == 0x0089) {
|
||||
if(addr == 0x0088) {
|
||||
channel1.r.volumeRight = data.bits(0,3);
|
||||
channel1.r.volumeLeft = data.bits(4,7);
|
||||
return;
|
||||
}
|
||||
|
||||
//SND_CH2_VOL
|
||||
if(addr == 0x008a) {
|
||||
if(addr == 0x0089) {
|
||||
channel2.r.volumeRight = data.bits(0,3);
|
||||
channel2.r.volumeLeft = data.bits(4,7);
|
||||
return;
|
||||
}
|
||||
|
||||
//SND_CH3_VOL
|
||||
if(addr == 0x008b) {
|
||||
if(addr == 0x008a) {
|
||||
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);
|
||||
}
|
||||
|
||||
//SND_SWEEP_VALUE
|
||||
if(addr == 0x008c) {
|
||||
channel2.r.sweepValue = data;
|
||||
return;
|
||||
}
|
||||
if(addr == 0x008c) channel3.r.sweepValue = data;
|
||||
|
||||
//SND_SWEEP_TIME
|
||||
if(addr == 0x008d) {
|
||||
channel2.r.sweepTime = data.bits(0,4);
|
||||
return;
|
||||
}
|
||||
if(addr == 0x008d) channel3.r.sweepTime = data.bits(0,4);
|
||||
|
||||
//SND_NOISE
|
||||
if(addr == 0x008e) {
|
||||
channel3.r.noiseMode = data.bits(0,2);
|
||||
channel3.r.noiseReset = data.bit (3);
|
||||
channel3.r.noiseUpdate = data.bit (4);
|
||||
return;
|
||||
channel4.r.noiseMode = data.bits(0,2);
|
||||
channel4.r.noiseReset = data.bit (3);
|
||||
channel4.r.noiseUpdate = data.bit (4);
|
||||
}
|
||||
|
||||
//SND_WAVE_BASE
|
||||
if(addr == 0x008f) {
|
||||
r.waveBase = data;
|
||||
return;
|
||||
}
|
||||
if(addr == 0x008f) r.waveBase = data;
|
||||
|
||||
//SND_CTRL
|
||||
if(addr == 0x0090) {
|
||||
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;
|
||||
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);
|
||||
}
|
||||
|
||||
//SND_OUTPUT
|
||||
|
@ -205,13 +238,11 @@ auto APU::portWrite(uint16 addr, uint8 data) -> void {
|
|||
r.speakerEnable = data.bit (0);
|
||||
r.speakerShift = data.bits(1,2);
|
||||
r.headphoneEnable = data.bit (3);
|
||||
return;
|
||||
}
|
||||
|
||||
//SND_VOICE_CTRL
|
||||
if(addr == 0x0094) {
|
||||
channel1.r.voiceEnableRight = data.bits(0,1);
|
||||
channel1.r.voiceEnableLeft = data.bits(2,3);
|
||||
return;
|
||||
channel2.r.voiceEnableRight = data.bits(0,1);
|
||||
channel2.r.voiceEnableLeft = data.bits(2,3);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,7 +60,7 @@ auto Cartridge::unload() -> void {
|
|||
auto Cartridge::power() -> void {
|
||||
eeprom.power();
|
||||
|
||||
for(uint n = 0x00c0; n <= 0x00c8; n++) iomap[n] = this;
|
||||
bus.map(this, 0x00c0, 0x00c8);
|
||||
|
||||
r.bank_rom0 = 0xff;
|
||||
r.bank_rom1 = 0xff;
|
||||
|
|
|
@ -37,27 +37,27 @@ auto CPU::write(uint20 addr, uint8 data) -> void {
|
|||
}
|
||||
|
||||
auto CPU::in(uint16 port) -> uint8 {
|
||||
return iomap[port]->portRead(port);
|
||||
return bus.portRead(port);
|
||||
}
|
||||
|
||||
auto CPU::out(uint16 port, uint8 data) -> void {
|
||||
return iomap[port]->portWrite(port, data);
|
||||
return bus.portWrite(port, data);
|
||||
}
|
||||
|
||||
auto CPU::power() -> void {
|
||||
V30MZ::power();
|
||||
create(CPU::Enter, 3'072'000);
|
||||
|
||||
iomap[0x00a0] = this;
|
||||
iomap[0x00b0] = this;
|
||||
iomap[0x00b2] = this;
|
||||
iomap[0x00b4] = this;
|
||||
iomap[0x00b5] = this;
|
||||
iomap[0x00b6] = this;
|
||||
bus.map(this, 0x00a0);
|
||||
bus.map(this, 0x00b0);
|
||||
bus.map(this, 0x00b2);
|
||||
bus.map(this, 0x00b4);
|
||||
bus.map(this, 0x00b5);
|
||||
bus.map(this, 0x00b6);
|
||||
|
||||
if(system.model() != Model::WonderSwan) {
|
||||
for(uint p = 0x0040; p <= 0x0049; p++) iomap[p] = this;
|
||||
iomap[0x0062] = this;
|
||||
bus.map(this, 0x0040, 0x0049);
|
||||
bus.map(this, 0x0062);
|
||||
}
|
||||
|
||||
r.dmaSource = 0;
|
||||
|
|
|
@ -2,24 +2,9 @@
|
|||
|
||||
namespace WonderSwan {
|
||||
|
||||
IO* iomap[64 * 1024] = {nullptr};
|
||||
InternalRAM iram;
|
||||
Bus bus;
|
||||
|
||||
auto IO::power() -> void {
|
||||
static IO unmapped;
|
||||
for(auto& n : iomap) n = &unmapped;
|
||||
}
|
||||
|
||||
auto IO::portRead(uint16 addr) -> uint8 {
|
||||
//print("[", hex(addr, 4L), "]: port unmapped\n");
|
||||
return 0x00;
|
||||
}
|
||||
|
||||
auto IO::portWrite(uint16 addr, uint8 data) -> void {
|
||||
//print("[", hex(addr, 4L), "] = ", hex(data, 2L), ": port unmapped\n");
|
||||
}
|
||||
|
||||
auto InternalRAM::power() -> void {
|
||||
for(auto& byte : memory) byte = 0x00;
|
||||
}
|
||||
|
@ -37,6 +22,10 @@ auto InternalRAM::write(uint16 addr, uint8 data) -> void {
|
|||
memory[addr] = data;
|
||||
}
|
||||
|
||||
auto Bus::power() -> void {
|
||||
for(auto& io : port) io = nullptr;
|
||||
}
|
||||
|
||||
auto Bus::read(uint20 addr) -> uint8 {
|
||||
if(addr.bits(16,19) == 0) return iram.read(addr);
|
||||
if(addr.bits(16,19) == 1) return cartridge.ramRead(addr);
|
||||
|
@ -50,4 +39,17 @@ auto Bus::write(uint20 addr, uint8 data) -> void {
|
|||
if(addr.bits(16,19) >= 2) return cartridge.romWrite(addr, data);
|
||||
}
|
||||
|
||||
auto Bus::map(IO* io, uint16_t lo, maybe<uint16_t> hi) -> void {
|
||||
for(uint addr = lo; addr <= (hi ? hi() : lo); addr++) port[addr] = io;
|
||||
}
|
||||
|
||||
auto Bus::portRead(uint16 addr) -> uint8 {
|
||||
if(auto io = port[addr]) return io->portRead(addr);
|
||||
return 0x00;
|
||||
}
|
||||
|
||||
auto Bus::portWrite(uint16 addr, uint8 data) -> void {
|
||||
if(auto io = port[addr]) return io->portWrite(addr, data);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
struct IO {
|
||||
static auto power() -> void;
|
||||
|
||||
virtual auto portRead(uint16 addr) -> uint8;
|
||||
virtual auto portWrite(uint16 addr, uint8 data) -> void;
|
||||
virtual auto portRead(uint16 addr) -> uint8 = 0;
|
||||
virtual auto portWrite(uint16 addr, uint8 data) -> void = 0;
|
||||
};
|
||||
|
||||
struct InternalRAM {
|
||||
|
@ -16,10 +14,18 @@ private:
|
|||
};
|
||||
|
||||
struct Bus {
|
||||
auto power() -> void;
|
||||
|
||||
auto read(uint20 addr) -> uint8;
|
||||
auto write(uint20 addr, uint8 data) -> void;
|
||||
|
||||
auto map(IO* io, uint16_t lo, maybe<uint16_t> hi = nothing) -> void;
|
||||
auto portRead(uint16 addr) -> uint8;
|
||||
auto portWrite(uint16 addr, uint8 data) -> void;
|
||||
|
||||
private:
|
||||
IO* port[64 * 1024] = {nullptr};
|
||||
};
|
||||
|
||||
extern IO* iomap[64 * 1024];
|
||||
extern InternalRAM iram;
|
||||
extern Bus bus;
|
||||
|
|
|
@ -15,8 +15,9 @@ auto PPU::Enter() -> void {
|
|||
|
||||
auto PPU::main() -> void {
|
||||
if(status.vclk < 144) {
|
||||
renderSpriteFetch();
|
||||
renderSpriteDecode();
|
||||
for(uint x = 0; x < 224; x++) {
|
||||
for(auto x : range(256)) {
|
||||
if(!system.color()) {
|
||||
renderMonoBack();
|
||||
renderMonoScreenOne();
|
||||
|
@ -86,12 +87,13 @@ auto PPU::step(uint clocks) -> void {
|
|||
auto PPU::power() -> void {
|
||||
create(PPU::Enter, 3'072'000);
|
||||
|
||||
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;
|
||||
bus.map(this, 0x0000, 0x0017);
|
||||
bus.map(this, 0x001c, 0x003f);
|
||||
bus.map(this, 0x00a2);
|
||||
bus.map(this, 0x00a4, 0x00ab);
|
||||
|
||||
for(auto& n : output) n = 0;
|
||||
for(auto& n : oam) n = 0;
|
||||
|
||||
status.vclk = 0;
|
||||
status.hclk = 0;
|
||||
|
|
|
@ -13,10 +13,12 @@ struct PPU : Thread, IO {
|
|||
auto portWrite(uint16 addr, uint8 data) -> void override;
|
||||
|
||||
//render-sprite.cpp
|
||||
auto renderSpriteFetch() -> void;
|
||||
auto renderSpriteDecode() -> void;
|
||||
|
||||
//render-mono.cpp
|
||||
auto renderMonoFetch(uint14 offset, uint3 y, uint3 x) -> uint2;
|
||||
auto renderMonoPalette(uint4 palette, uint2 index) -> uint12;
|
||||
auto renderMonoBack() -> void;
|
||||
auto renderMonoScreenOne() -> void;
|
||||
auto renderMonoScreenTwo() -> void;
|
||||
|
@ -24,6 +26,7 @@ struct PPU : Thread, IO {
|
|||
|
||||
//render-color.cpp
|
||||
auto renderColorFetch(uint16 offset, uint3 y, uint3 x) -> uint4;
|
||||
auto renderColorPalette(uint4 palette, uint4 index) -> uint12;
|
||||
auto renderColorBack() -> void;
|
||||
auto renderColorScreenOne() -> void;
|
||||
auto renderColorScreenTwo() -> void;
|
||||
|
@ -31,6 +34,7 @@ struct PPU : Thread, IO {
|
|||
|
||||
//state
|
||||
uint12 output[224 * 144];
|
||||
uint32 oam[128];
|
||||
|
||||
struct Status {
|
||||
uint vclk;
|
||||
|
|
|
@ -17,6 +17,10 @@ auto PPU::renderColorFetch(uint16 offset, uint3 y, uint3 x) -> uint4 {
|
|||
return color;
|
||||
}
|
||||
|
||||
auto PPU::renderColorPalette(uint4 palette, uint4 index) -> uint12 {
|
||||
return iram.read(0xfe00 + (palette << 5) + (index << 1), Word);
|
||||
}
|
||||
|
||||
auto PPU::renderColorBack() -> void {
|
||||
uint12 color = iram.read(0xfe00 + (r.backColor << 1), Word);
|
||||
pixel = {Pixel::Source::Back, color};
|
||||
|
@ -39,8 +43,7 @@ auto PPU::renderColorScreenOne() -> void {
|
|||
uint4 tileColor = renderColorFetch(tileOffset, tileY, tileX);
|
||||
if(tileColor == 0) return;
|
||||
|
||||
uint12 color = iram.read(0xfe00 + (tile.bits(9, 12) << 5) + (tileColor << 1), Word);
|
||||
pixel = {Pixel::Source::ScreenOne, color};
|
||||
pixel = {Pixel::Source::ScreenOne, renderColorPalette(tile.bits(9, 12), tileColor)};
|
||||
}
|
||||
|
||||
auto PPU::renderColorScreenTwo() -> void {
|
||||
|
@ -65,8 +68,7 @@ auto PPU::renderColorScreenTwo() -> void {
|
|||
uint4 tileColor = renderColorFetch(tileOffset, tileY, tileX);
|
||||
if(tileColor == 0) return;
|
||||
|
||||
uint12 color = iram.read(0xfe00 + (tile.bits(9,12) << 5) + (tileColor << 1), Word);
|
||||
pixel = {Pixel::Source::ScreenTwo, color};
|
||||
pixel = {Pixel::Source::ScreenTwo, renderColorPalette(tile.bits(9, 12), tileColor)};
|
||||
}
|
||||
|
||||
auto PPU::renderColorSprite() -> void {
|
||||
|
@ -85,8 +87,7 @@ auto PPU::renderColorSprite() -> void {
|
|||
if(tileColor == 0) continue;
|
||||
if(!sprite.priority && pixel.source == Pixel::Source::ScreenTwo) continue;
|
||||
|
||||
uint12 color = iram.read(0xfe00 + (sprite.palette << 5) + (tileColor << 1), Word);
|
||||
pixel = {Pixel::Source::Sprite, color};
|
||||
pixel = {Pixel::Source::Sprite, renderColorPalette(sprite.palette, tileColor)};
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,12 @@ auto PPU::renderMonoFetch(uint14 offset, uint3 y, uint3 x) -> uint2 {
|
|||
return color;
|
||||
}
|
||||
|
||||
auto PPU::renderMonoPalette(uint4 palette, uint2 index) -> uint12 {
|
||||
uint3 paletteColor = r.palette[palette].color[index];
|
||||
uint4 poolColor = 15 - r.pool[paletteColor];
|
||||
return poolColor << 0 | poolColor << 4 | poolColor << 8;
|
||||
}
|
||||
|
||||
auto PPU::renderMonoBack() -> void {
|
||||
uint4 poolColor = 15 - r.pool[r.backColor.bits(0,2)];
|
||||
pixel = {Pixel::Source::Back, poolColor << 0 | poolColor << 4 | poolColor << 8};
|
||||
|
@ -37,9 +43,7 @@ auto PPU::renderMonoScreenOne() -> void {
|
|||
uint2 tileColor = renderMonoFetch(tileOffset, tileY, tileX);
|
||||
if(tile.bit(11) && tileColor == 0) return;
|
||||
|
||||
uint3 paletteColor = r.palette[tile.bits(9,12)].color[tileColor];
|
||||
uint4 poolColor = 15 - r.pool[paletteColor];
|
||||
pixel = {Pixel::Source::ScreenOne, poolColor << 0 | poolColor << 4 | poolColor << 8};
|
||||
pixel = {Pixel::Source::ScreenOne, renderMonoPalette(tile.bits(9,12), tileColor)};
|
||||
}
|
||||
|
||||
auto PPU::renderMonoScreenTwo() -> void {
|
||||
|
@ -50,8 +54,8 @@ auto PPU::renderMonoScreenTwo() -> void {
|
|||
windowInside ^= r.screenTwoWindowInvert;
|
||||
if(r.screenTwoWindowEnable && !windowInside) return;
|
||||
|
||||
uint8 scrollX = status.hclk + r.scrollTwoX;
|
||||
uint8 scrollY = status.vclk + r.scrollTwoY;
|
||||
uint8 scrollX = status.hclk + r.scrollTwoX;
|
||||
|
||||
uint14 tilemapOffset = r.screenTwoMapBase.bits(0,2) << 11;
|
||||
tilemapOffset += (scrollY >> 3) << 6;
|
||||
|
@ -64,9 +68,7 @@ auto PPU::renderMonoScreenTwo() -> void {
|
|||
uint2 tileColor = renderMonoFetch(tileOffset, tileY, tileX);
|
||||
if(tile.bit(11) && tileColor == 0) return;
|
||||
|
||||
uint3 paletteColor = r.palette[tile.bits(9,12)].color[tileColor];
|
||||
uint4 poolColor = 15 - r.pool[paletteColor];
|
||||
pixel = {Pixel::Source::ScreenTwo, poolColor << 0 | poolColor << 4 | poolColor << 8};
|
||||
pixel = {Pixel::Source::ScreenTwo, renderMonoPalette(tile.bits(9,12), tileColor)};
|
||||
}
|
||||
|
||||
auto PPU::renderMonoSprite() -> void {
|
||||
|
@ -85,9 +87,7 @@ auto PPU::renderMonoSprite() -> void {
|
|||
if(sprite.palette.bit(2) && tileColor == 0) continue;
|
||||
if(!sprite.priority && pixel.source == Pixel::Source::ScreenTwo) continue;
|
||||
|
||||
uint3 paletteColor = r.palette[sprite.palette].color[tileColor];
|
||||
uint4 poolColor = 15 - r.pool[paletteColor];
|
||||
pixel = {Pixel::Source::Sprite, poolColor << 0 | poolColor << 4 | poolColor << 8};
|
||||
pixel = {Pixel::Source::Sprite, renderMonoPalette(sprite.palette, tileColor)};
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,10 @@
|
|||
auto PPU::renderSpriteFetch() -> void {
|
||||
uint16 spriteBase = r.spriteBase.bits(0, 4 + system.depth()) << 9;
|
||||
for(auto spriteIndex : range(128)) {
|
||||
oam[spriteIndex] = iram.read(spriteBase + (spriteIndex << 2), Long);
|
||||
}
|
||||
}
|
||||
|
||||
auto PPU::renderSpriteDecode() -> void {
|
||||
sprites.reset();
|
||||
sprites.reserve(32);
|
||||
|
@ -9,7 +16,7 @@ auto PPU::renderSpriteDecode() -> void {
|
|||
uint7 spriteIndex = r.spriteFirst;
|
||||
uint8 spriteCount = min(128, (uint)r.spriteCount);
|
||||
while(spriteCount--) {
|
||||
uint32 attributes = iram.read(spriteBase + (spriteIndex++ << 2), Long);
|
||||
uint32 attributes = oam[spriteIndex++];
|
||||
|
||||
Sprite sprite;
|
||||
sprite.x = attributes.bits(24,31);
|
||||
|
|
|
@ -50,7 +50,7 @@ auto System::unload() -> void {
|
|||
}
|
||||
|
||||
auto System::power() -> void {
|
||||
IO::power();
|
||||
bus.power();
|
||||
iram.power();
|
||||
eeprom.power();
|
||||
cpu.power();
|
||||
|
@ -59,8 +59,8 @@ auto System::power() -> void {
|
|||
cartridge.power();
|
||||
scheduler.power();
|
||||
|
||||
iomap[0x0060] = this;
|
||||
for(uint n = 0x00ba; n <= 0x00be; n++) iomap[n] = this;
|
||||
bus.map(this, 0x0060);
|
||||
bus.map(this, 0x00ba, 0x00be);
|
||||
|
||||
r.depth = 0;
|
||||
r.color = 0;
|
||||
|
|
Loading…
Reference in New Issue