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:
Tim Allen 2016-03-17 22:28:15 +11:00
parent b586471562
commit a7f7985581
27 changed files with 444 additions and 312 deletions

View File

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

View File

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

View File

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

View File

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

View File

@ -1,2 +1,2 @@
system name:WonderSwan Color
eeprom name=internal.rom size=2048
eeprom name=internal.ram size=2048

View File

@ -1,2 +1,2 @@
system name:WonderSwan
eeprom name=internal.rom size=128
eeprom name=internal.ram size=128

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

17
higan/ws/apu/channel5.cpp Normal file
View File

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

29
higan/ws/apu/dma.cpp Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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