Update to v097r31 release.

byuu says:

Changelog:
- WS: fixed sprite window clipping (again)
- WS: don't set IRQ status bits of IRQ enable bits are clear
- SFC: signed/unsigned -> int/uint for DSP core
- SFC: removed eBoot
- SFC: added 21fx (not the same as the old precursor to MSU1; just
  reusing the name)

Note: XI Little doesn't seem to be fixed after all ... but the other
three are. So I guess we're at 13 bugs :( And holy shit that music when
you choose a menu option is one of the worst sounds I've ever heard in
my life >_<
This commit is contained in:
Tim Allen 2016-03-29 20:15:01 +11:00
parent 2d83300235
commit 25eaaa82f4
24 changed files with 276 additions and 199 deletions

View File

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

View File

@ -3,7 +3,8 @@ processors += r65816 spc700 arm gsu hg51b upd96050
objects += sfc-interface sfc-system sfc-scheduler sfc-controller
objects += sfc-cartridge sfc-cheat
objects += sfc-memory sfc-cpu sfc-smp sfc-dsp sfc-ppu
objects += sfc-satellaview sfc-superdisc sfc-eboot
objects += sfc-satellaview sfc-superdisc
objects += sfc-21fx
objects += sfc-icd2 sfc-mcc sfc-nss sfc-event
objects += sfc-sa1 sfc-superfx
objects += sfc-armdsp sfc-hitachidsp sfc-necdsp
@ -48,7 +49,7 @@ obj/sfc-ppu.o: sfc/$(sfcppu)/ppu.cpp $(call rwildcard,sfc/$(sfcppu)/)
obj/sfc-satellaview.o: sfc/expansion/satellaview/satellaview.cpp $(call rwildcard,sfc/expansion/satellaview/)
obj/sfc-superdisc.o: sfc/expansion/superdisc/superdisc.cpp $(call rwildcard,sfc/expansion/superdisc/)
obj/sfc-eboot.o: sfc/expansion/eboot/eboot.cpp $(call rwildcard,sfc/expansion/eboot/)
obj/sfc-21fx.o: sfc/expansion/21fx/21fx.cpp $(call rwildcard,sfc/expansion/21fx/)
obj/sfc-icd2.o: sfc/coprocessor/icd2/icd2.cpp $(call rwildcard,sfc/coprocessor/icd2/)
obj/sfc-mcc.o: sfc/coprocessor/mcc/mcc.cpp $(call rwildcard,sfc/coprocessor/mcc/)

View File

@ -1,15 +1,15 @@
auto DSP::brrDecode(Voice& v) -> void {
//state.t_brr_byte = ram[v.brr_addr + v.brr_offset] cached from previous clock cycle
signed nybbles = (state._brrByte << 8) + smp.apuram[(uint16)(v.brrAddress + v.brrOffset + 1)];
int nybbles = (state._brrByte << 8) + smp.apuram[(uint16)(v.brrAddress + v.brrOffset + 1)];
const signed filter = (state._brrHeader >> 2) & 3;
const signed scale = (state._brrHeader >> 4);
const int filter = (state._brrHeader >> 2) & 3;
const int scale = (state._brrHeader >> 4);
//decode four samples
for(auto n : range(4)) {
//bits 12-15 = current nybble; sign extend, then shift right to 4-bit precision
//result: s = 4-bit sign-extended sample value
signed s = (int16)nybbles >> 12;
int s = (int16)nybbles >> 12;
nybbles <<= 4; //slide nybble so that on next loop iteration, bits 12-15 = current nybble
if(scale <= 12) {
@ -20,8 +20,8 @@ auto DSP::brrDecode(Voice& v) -> void {
}
//apply IIR filter (2 is the most commonly used)
const signed p1 = v.buffer[v.bufferOffset - 1];
const signed p2 = v.buffer[v.bufferOffset - 2] >> 1;
const int p1 = v.buffer[v.bufferOffset - 1];
const int p2 = v.buffer[v.bufferOffset - 2] >> 1;
switch(filter) {
case 0:

View File

@ -42,7 +42,7 @@ inline auto DSP::counterTick() -> void {
//return true if counter event should trigger
inline auto DSP::counterPoll(unsigned rate) -> bool {
inline auto DSP::counterPoll(uint rate) -> bool {
if(rate == 0) return false;
return (((unsigned)state.counter + CounterOffset[rate]) % CounterRate[rate]) == 0;
return (((uint)state.counter + CounterOffset[rate]) % CounterRate[rate]) == 0;
}

View File

@ -1,26 +1,26 @@
auto DSP::calculateFIR(signed i, bool channel) -> signed {
signed s = state.echoHistory[channel][state.echoHistoryOffset + i + 1];
auto DSP::calculateFIR(int i, bool channel) -> int {
int s = state.echoHistory[channel][state.echoHistoryOffset + i + 1];
return (s * (int8)REG(FIR + i * 0x10)) >> 6;
}
auto DSP::echoOutput(bool channel) -> signed {
signed output = (int16)((state._mainOut[channel] * (int8)REG(MVOLL + channel * 0x10)) >> 7)
auto DSP::echoOutput(bool channel) -> int {
int output = (int16)((state._mainOut[channel] * (int8)REG(MVOLL + channel * 0x10)) >> 7)
+ (int16)((state._echoIn [channel] * (int8)REG(EVOLL + channel * 0x10)) >> 7);
return sclamp<16>(output);
}
auto DSP::echoRead(bool channel) -> void {
unsigned addr = state._echoPointer + channel * 2;
uint addr = state._echoPointer + channel * 2;
uint8 lo = smp.apuram[(uint16)(addr + 0)];
uint8 hi = smp.apuram[(uint16)(addr + 1)];
signed s = (int16)((hi << 8) + lo);
int s = (int16)((hi << 8) + lo);
state.echoHistory[channel].write(state.echoHistoryOffset, s >> 1);
}
auto DSP::echoWrite(bool channel) -> void {
if(!(state._echoDisabled & 0x20)) {
unsigned addr = state._echoPointer + channel * 2;
signed s = state._echoOut[channel];
uint addr = state._echoPointer + channel * 2;
int s = state._echoOut[channel];
smp.apuram[(uint16)(addr + 0)] = s;
smp.apuram[(uint16)(addr + 1)] = s >> 8;
}
@ -37,16 +37,16 @@ auto DSP::echo22() -> void {
echoRead(0);
//FIR
signed l = calculateFIR(0, 0);
signed r = calculateFIR(0, 1);
int l = calculateFIR(0, 0);
int r = calculateFIR(0, 1);
state._echoIn[0] = l;
state._echoIn[1] = r;
}
auto DSP::echo23() -> void {
signed l = calculateFIR(1, 0) + calculateFIR(2, 0);
signed r = calculateFIR(1, 1) + calculateFIR(2, 1);
int l = calculateFIR(1, 0) + calculateFIR(2, 0);
int r = calculateFIR(1, 1) + calculateFIR(2, 1);
state._echoIn[0] += l;
state._echoIn[1] += r;
@ -55,16 +55,16 @@ auto DSP::echo23() -> void {
}
auto DSP::echo24() -> void {
signed l = calculateFIR(3, 0) + calculateFIR(4, 0) + calculateFIR(5, 0);
signed r = calculateFIR(3, 1) + calculateFIR(4, 1) + calculateFIR(5, 1);
int l = calculateFIR(3, 0) + calculateFIR(4, 0) + calculateFIR(5, 0);
int r = calculateFIR(3, 1) + calculateFIR(4, 1) + calculateFIR(5, 1);
state._echoIn[0] += l;
state._echoIn[1] += r;
}
auto DSP::echo25() -> void {
signed l = state._echoIn[0] + calculateFIR(6, 0);
signed r = state._echoIn[1] + calculateFIR(6, 1);
int l = state._echoIn[0] + calculateFIR(6, 0);
int r = state._echoIn[1] + calculateFIR(6, 1);
l = (int16)l;
r = (int16)r;
@ -82,8 +82,8 @@ auto DSP::echo26() -> void {
state._mainOut[0] = echoOutput(0);
//echo feedback
signed l = state._echoOut[0] + (int16)((state._echoIn[0] * (int8)REG(EFB)) >> 7);
signed r = state._echoOut[1] + (int16)((state._echoIn[1] * (int8)REG(EFB)) >> 7);
int l = state._echoOut[0] + (int16)((state._echoIn[0] * (int8)REG(EFB)) >> 7);
int r = state._echoOut[1] + (int16)((state._echoIn[1] * (int8)REG(EFB)) >> 7);
state._echoOut[0] = sclamp<16>(l) & ~1;
state._echoOut[1] = sclamp<16>(r) & ~1;
@ -91,12 +91,12 @@ auto DSP::echo26() -> void {
auto DSP::echo27() -> void {
//output
signed outl = state._mainOut[0];
signed outr = echoOutput(1);
int outl = state._mainOut[0];
int outr = echoOutput(1);
state._mainOut[0] = 0;
state._mainOut[1] = 0;
//TODO: global muting isn't this simple
//todo: global muting isn't this simple
//(turns DAC on and off or something, causing small ~37-sample pulse when first muted)
if(REG(FLG) & 0x40) {
outl = 0;

View File

@ -1,5 +1,5 @@
auto DSP::envelopeRun(Voice& v) -> void {
signed envelope = v.envelope;
int envelope = v.envelope;
if(v.envelopeMode == EnvelopeRelease) { //60%
envelope -= 0x8;
@ -8,8 +8,8 @@ auto DSP::envelopeRun(Voice& v) -> void {
return;
}
signed rate;
signed envelopeData = VREG(ADSR1);
int rate;
int envelopeData = VREG(ADSR1);
if(state._adsr0 & 0x80) { //99% ADSR
if(v.envelopeMode >= EnvelopeDecay) { //99%
envelope--;
@ -24,7 +24,7 @@ auto DSP::envelopeRun(Voice& v) -> void {
}
} else { //GAIN
envelopeData = VREG(GAIN);
signed mode = envelopeData >> 5;
int mode = envelopeData >> 5;
if(mode < 4) { //direct
envelope = envelopeData << 4;
rate = 31;
@ -37,7 +37,7 @@ auto DSP::envelopeRun(Voice& v) -> void {
envelope -= envelope >> 8;
} else { //6, 7: linear increase
envelope += 0x20;
if(mode > 6 && (unsigned)v.hiddenEnvelope >= 0x600) {
if(mode > 6 && (uint)v.hiddenEnvelope >= 0x600) {
envelope += 0x8 - 0x20; //7: two-slope linear increase
}
}
@ -48,8 +48,8 @@ auto DSP::envelopeRun(Voice& v) -> void {
if((envelope >> 8) == (envelopeData >> 5) && v.envelopeMode == EnvelopeDecay) v.envelopeMode = EnvelopeSustain;
v.hiddenEnvelope = envelope;
//unsigned cast because linear decrease underflowing also triggers this
if((unsigned)envelope > 0x7ff) {
//uint cast because linear decrease underflowing also triggers this
if((uint)envelope > 0x7ff) {
envelope = (envelope < 0 ? 0 : 0x7ff);
if(v.envelopeMode == EnvelopeAttack) v.envelopeMode = EnvelopeDecay;
}

View File

@ -33,14 +33,14 @@ const int16 DSP::GaussianTable[512] = {
1299, 1300, 1300, 1301, 1302, 1302, 1303, 1303, 1303, 1304, 1304, 1304, 1304, 1304, 1305, 1305,
};
auto DSP::gaussianInterpolate(const Voice& v) -> signed {
auto DSP::gaussianInterpolate(const Voice& v) -> int {
//make pointers into gaussian table based on fractional position between samples
signed offset = (v.gaussianOffset >> 4) & 0xff;
int offset = (v.gaussianOffset >> 4) & 0xff;
const int16* forward = GaussianTable + 255 - offset;
const int16* reverse = GaussianTable + offset; //mirror left half of gaussian table
offset = v.bufferOffset + (v.gaussianOffset >> 12);
signed output;
int output;
output = (forward[ 0] * v.buffer[offset + 0]) >> 11;
output += (forward[256] * v.buffer[offset + 1]) >> 11;
output += (reverse[256] * v.buffer[offset + 2]) >> 11;

View File

@ -25,7 +25,7 @@ auto DSP::misc30() -> void {
//noise
if(counterPoll(REG(FLG) & 0x1f)) {
signed feedback = (state.noise << 13) ^ (state.noise << 14);
int feedback = (state.noise << 13) ^ (state.noise << 14);
state.noise = (feedback & 0x4000) ^ (state.noise >> 1);
}
}

View File

@ -1,14 +1,14 @@
template<typename T, unsigned size>
template<typename T, uint size>
struct ModuloArray {
inline auto operator[](signed index) const -> T {
inline auto operator[](int index) const -> T {
return buffer[size + index];
}
inline auto read(signed index) const -> T {
inline auto read(int index) const -> T {
return buffer[size + index];
}
inline auto write(unsigned index, const T value) -> void {
inline auto write(uint index, const T value) -> void {
buffer[index] =
buffer[index + size] =
buffer[index + size + size] = value;

View File

@ -1,6 +1,6 @@
inline auto DSP::voiceOutput(Voice& v, bool channel) -> void {
//apply left/right volume
signed amp = (state._output * (int8)VREG(VOLL + channel)) >> 7;
int amp = (state._output * (int8)VREG(VOLL + channel)) >> 7;
//add to output total
state._mainOut[channel] += amp;
@ -77,7 +77,7 @@ auto DSP::voice3c(Voice& v) -> void {
}
//gaussian interpolation
signed output = gaussianInterpolate(v);
int output = gaussianInterpolate(v);
//noise
if(state._non & v.vbit) {

View File

@ -0,0 +1,143 @@
#include <sfc/sfc.hpp>
namespace SuperFamicom {
S21FX s21fx;
auto S21FX::Enter() -> void {
while(true) scheduler.synchronize(), s21fx.main();
}
auto S21FX::main() -> void {
if(linkInit) linkInit(
{&S21FX::quit, this},
{&S21FX::usleep, this},
{&S21FX::readable, this},
{&S21FX::writable, this},
{&S21FX::read, this},
{&S21FX::write, this}
);
if(linkMain) linkMain({});
while(true) step(10'000'000);
}
auto S21FX::step(uint clocks) -> void {
clock += clocks * (uint64)cpu.frequency;
if(CPU::Threaded) {
if(clock >= 0 && !scheduler.synchronizing()) co_switch(cpu.thread);
} else {
while(clock >= 0) cpu.main();
}
}
auto S21FX::init() -> void {
}
auto S21FX::load() -> void {
resetVector.byte(0) = bus.read(0xfffc, 0x00);
resetVector.byte(1) = bus.read(0xfffd, 0x00);
for(auto& byte : ram) byte = 0xdb; //stp
ram[0] = 0x6c; //jmp ($fffc)
ram[1] = 0xfc;
ram[2] = 0xff;
bus.map({&S21FX::read, &s21fx}, {&S21FX::write, &s21fx}, 0x00, 0x00, 0xfffc, 0xfffd);
bus.map({&S21FX::read, &s21fx}, {&S21FX::write, &s21fx}, 0x00, 0x3f, 0x2184, 0x21ff);
bus.map({&S21FX::read, &s21fx}, {&S21FX::write, &s21fx}, 0x80, 0xbf, 0x2184, 0x21ff);
if(auto buffer = file::read({interface->path(ID::System), "21fx.rom"})) {
memory::copy(ram, sizeof(ram), buffer.data(), buffer.size());
}
string filename{interface->path(ID::SuperFamicom), "21fx.so"};
if(link.openAbsolute(filename)) {
linkInit = link.sym("fx_init");
linkMain = link.sym("fx_main");
}
}
auto S21FX::unload() -> void {
if(link.open()) link.close();
linkInit.reset();
linkMain.reset();
}
auto S21FX::power() -> void {
}
auto S21FX::reset() -> void {
create(S21FX::Enter, 10'000'000);
booted = false;
}
auto S21FX::read(uint24 addr, uint8 data) -> uint8 {
addr &= 0x40ffff;
if(addr == 0xfffc) return booted ? resetVector.byte(0) : (uint8)0x84;
if(addr == 0xfffd) return booted ? resetVector.byte(1) : (booted = true, (uint8)0x21);
if(addr >= 0x2184 && addr <= 0x21fd) return ram[addr - 0x2184];
if(addr == 0x21fe) return (
(linkBuffer.size() > 0) << 7 //1 = readable
| (snesBuffer.size() < 65536) << 6 //1 = writable
| (link.open()) << 5 //1 = connected
);
if(addr == 0x21ff) {
if(linkBuffer.size() > 0) {
data = linkBuffer.takeFirst();
}
}
return data;
}
auto S21FX::write(uint24 addr, uint8 data) -> void {
addr &= 0x40ffff;
if(addr == 0x21ff) {
if(snesBuffer.size() < 65536) {
snesBuffer.append(data);
}
}
}
auto S21FX::quit() -> bool {
step(1);
return false;
}
auto S21FX::usleep(uint microseconds) -> void {
step(10 * microseconds);
}
auto S21FX::readable() -> bool {
step(1);
return snesBuffer.size() > 0;
}
auto S21FX::writable() -> bool {
step(1);
return linkBuffer.size() < 65536;
}
//SNES -> Link
auto S21FX::read() -> uint8 {
step(1);
if(snesBuffer.size() > 0) {
return snesBuffer.takeFirst();
}
return 0x00;
}
//Link -> SNES
auto S21FX::write(uint8 data) -> void {
step(1);
if(linkBuffer.size() < 65536) {
linkBuffer.append(data);
}
}
}

View File

@ -0,0 +1,41 @@
struct S21FX : Thread, Memory {
static auto Enter() -> void;
auto main() -> void;
auto step(uint clocks) -> void;
auto init() -> void;
auto load() -> void;
auto unload() -> void;
auto power() -> void;
auto reset() -> void;
auto read(uint24 addr, uint8 data) -> uint8;
auto write(uint24 addr, uint8 data) -> void;
private:
auto quit() -> bool;
auto usleep(uint) -> void;
auto readable() -> bool;
auto writable() -> bool;
auto read() -> uint8;
auto write(uint8) -> void;
bool booted = false;
uint16 resetVector;
uint8 ram[122];
nall::library link;
function<void (
function<bool ()>, //quit
function<void (uint)>, //usleep
function<bool ()>, //readable
function<bool ()>, //writable
function<uint8 ()>, //read
function<void (uint8)> //write
)> linkInit;
function<void (lstring)> linkMain;
vector<uint8> snesBuffer; //SNES -> Link
vector<uint8> linkBuffer; //Link -> SNES
};
extern S21FX s21fx;

View File

@ -1,49 +0,0 @@
#include <sfc/sfc.hpp>
namespace SuperFamicom {
eBoot eboot;
auto eBoot::init() -> void {
}
auto eBoot::load() -> void {
resetVector.byte(0) = bus.read(0xfffc, 0x00);
resetVector.byte(1) = bus.read(0xfffd, 0x00);
for(auto& byte : ram) byte = 0xdb; //stp
ram[0] = 0x6c; //jmp ($fffc)
ram[1] = 0xfc;
ram[2] = 0xff;
bus.map({&eBoot::read, &eboot}, {&eBoot::write, &eboot}, 0x00, 0x00, 0xfffc, 0xfffd);
bus.map({&eBoot::read, &eboot}, {&eBoot::write, &eboot}, 0x00, 0x3f, 0x2184, 0x21ff);
bus.map({&eBoot::read, &eboot}, {&eBoot::write, &eboot}, 0x80, 0xbf, 0x2184, 0x21ff);
if(auto buffer = file::read({interface->path(ID::System), "eboot.rom"})) {
memory::copy(ram, sizeof(ram), buffer.data(), buffer.size());
}
}
auto eBoot::unload() -> void {
}
auto eBoot::power() -> void {
}
auto eBoot::reset() -> void {
booted = false;
}
auto eBoot::read(uint24 addr, uint8 data) -> uint8 {
addr &= 0x40ffff;
if(addr == 0xfffc) return booted ? resetVector.byte(0) : (uint8)0x84;
if(addr == 0xfffd) return booted ? resetVector.byte(1) : (booted = true, (uint8)0x21);
if(addr >= 0x2184 && addr <= 0x21ff) return ram[addr - 0x2184];
return data;
}
auto eBoot::write(uint24 addr, uint8 data) -> void {
}
}

View File

@ -1,17 +0,0 @@
struct eBoot : Memory {
auto init() -> void;
auto load() -> void;
auto unload() -> void;
auto power() -> void;
auto reset() -> void;
auto read(uint24 addr, uint8 data) -> uint8;
auto write(uint24 addr, uint8 data) -> void;
private:
bool booted = false;
uint16 resetVector;
uint8 ram[124];
};
extern eBoot eboot;

View File

@ -1,3 +1,3 @@
#include <sfc/expansion/satellaview/satellaview.hpp>
#include <sfc/expansion/superdisc/superdisc.hpp>
#include <sfc/expansion/eboot/eboot.hpp>
#include <sfc/expansion/21fx/21fx.hpp>

View File

@ -121,7 +121,7 @@ Interface::Interface() {
this->device.append(device);
}
{ Device device{10, ID::ExpansionPort, "eBoot"};
{ Device device{10, ID::ExpansionPort, "21fx"};
this->device.append(device);
}

View File

@ -20,7 +20,7 @@ struct Device {
//expansion port devices
Satellaview,
SuperDisc,
eBoot,
S21FX,
};
Device();

View File

@ -33,7 +33,7 @@ auto System::init() -> void {
satellaview.init();
superdisc.init();
eboot.init();
s21fx.init();
icd2.init();
mcc.init();
@ -83,7 +83,7 @@ auto System::load() -> void {
if(expansionPort() == Device::ID::Satellaview) satellaview.load();
if(expansionPort() == Device::ID::SuperDisc) superdisc.load();
if(expansionPort() == Device::ID::eBoot) eboot.load();
if(expansionPort() == Device::ID::S21FX) s21fx.load();
if(cartridge.hasICD2()) icd2.load();
if(cartridge.hasMCC()) mcc.load();
@ -112,7 +112,7 @@ auto System::unload() -> void {
if(!loaded()) return;
if(expansionPort() == Device::ID::Satellaview) satellaview.unload();
if(expansionPort() == Device::ID::SuperDisc) superdisc.unload();
if(expansionPort() == Device::ID::eBoot) eboot.unload();
if(expansionPort() == Device::ID::S21FX) s21fx.unload();
if(cartridge.hasICD2()) icd2.unload();
if(cartridge.hasMCC()) mcc.unload();
@ -147,7 +147,7 @@ auto System::power() -> void {
if(expansionPort() == Device::ID::Satellaview) satellaview.power();
if(expansionPort() == Device::ID::SuperDisc) superdisc.power();
if(expansionPort() == Device::ID::eBoot) eboot.power();
if(expansionPort() == Device::ID::S21FX) s21fx.power();
if(cartridge.hasICD2()) icd2.power();
if(cartridge.hasMCC()) mcc.power();
@ -178,7 +178,7 @@ auto System::reset() -> void {
if(expansionPort() == Device::ID::Satellaview) satellaview.reset();
if(expansionPort() == Device::ID::SuperDisc) superdisc.reset();
if(expansionPort() == Device::ID::eBoot) eboot.reset();
if(expansionPort() == Device::ID::S21FX) s21fx.reset();
if(cartridge.hasICD2()) icd2.reset();
if(cartridge.hasMCC()) mcc.reset();
@ -210,6 +210,7 @@ auto System::reset() -> void {
if(cartridge.hasSPC7110()) cpu.coprocessors.append(&spc7110);
if(cartridge.hasMSU1()) cpu.coprocessors.append(&msu1);
if(expansionPort() == Device::ID::SuperDisc) cpu.coprocessors.append(&superdisc);
if(expansionPort() == Device::ID::S21FX) cpu.coprocessors.append(&s21fx);
scheduler.reset();
device.connect(0, (Device::ID)settings.controllerPort1);

View File

@ -11,6 +11,7 @@ auto CPU::poll() -> void {
}
auto CPU::raise(Interrupt irq) -> void {
if(!r.interruptEnable.bit((uint)irq)) return;
r.interruptStatus.bit((uint)irq) = 1;
}

View File

@ -50,18 +50,17 @@ auto CPU::portRead(uint16 addr) -> uint8 {
if(addr == 0x00a0) {
bool model = system.model() != Model::WonderSwan;
return (
1 << 7 //1 = built-in self-test passed
| 1 << 2 //0 = 8-bit bus width; 1 = 16-bit bus width
1 << 0 //0 = BIOS mapped; 1 = cartridge mapped
| model << 1 //0 = WonderSwan; 1 = WonderSwan Color or SwanCrystal
| 1 << 0 //0 = BIOS mapped; 1 = cartridge mapped
| 1 << 2 //0 = 8-bit bus width; 1 = 16-bit bus width
| 1 << 7 //1 = built-in self-test passed
);
}
//INT_BASE
if(addr == 0x00b0) {
if(system.model() == Model::WonderSwan) return r.interruptBase | 3;
return r.interruptBase;
}
if(addr == 0x00b0) return (
r.interruptBase | (system.model() == Model::WonderSwan ? 3 : 0)
);
//SER_DATA
if(addr == 0x00b1) return r.serialData;
@ -92,42 +91,38 @@ auto CPU::portRead(uint16 addr) -> uint8 {
auto CPU::portWrite(uint16 addr, uint8 data) -> void {
//DMA_SRC
if(addr == 0x0040) { r.dmaSource.byte(0) = data & ~1; return; }
if(addr == 0x0041) { r.dmaSource.byte(1) = data; return; }
if(addr == 0x0042) { r.dmaSource.byte(2) = data; return; }
if(addr == 0x0040) r.dmaSource.byte(0) = data & ~1;
if(addr == 0x0041) r.dmaSource.byte(1) = data;
if(addr == 0x0042) r.dmaSource.byte(2) = data;
//DMA_DST
if(addr == 0x0044) { r.dmaTarget.byte(0) = data & ~1; return; }
if(addr == 0x0045) { r.dmaTarget.byte(1) = data; return; }
if(addr == 0x0044) r.dmaTarget.byte(0) = data & ~1;
if(addr == 0x0045) r.dmaTarget.byte(1) = data;
//DMA_LEN
if(addr == 0x0046) { r.dmaLength.byte(0) = data & ~1; return; }
if(addr == 0x0047) { r.dmaLength.byte(1) = data; return; }
if(addr == 0x0046) r.dmaLength.byte(0) = data & ~1;
if(addr == 0x0047) r.dmaLength.byte(1) = data;
//DMA_CTRL
if(addr == 0x0048) {
r.dmaEnable = data.bit(7);
r.dmaMode = data.bit(0);
r.dmaEnable = data.bit(7);
if(r.dmaEnable) dmaTransfer();
return;
}
//WSC_SYSTEM
if(addr == 0x0062) {
//todo: d0 = 1 powers off system
return;
}
//HW_FLAGS
if(addr == 0x00a0) {
//todo: d2 (bus width) bit is writable; but ... it will do very bad things
return;
}
//INT_BASE
if(addr == 0x00b0) {
r.interruptBase = (system.model() == Model::WonderSwan) ? data & ~7 : data & ~1;
return;
}
//SER_DATA
@ -136,7 +131,7 @@ auto CPU::portWrite(uint16 addr, uint8 data) -> void {
//INT_ENABLE
if(addr == 0x00b2) {
r.interruptEnable = data;
return;
r.interruptStatus &= ~r.interruptEnable;
}
//SER_STATUS
@ -156,6 +151,5 @@ auto CPU::portWrite(uint16 addr, uint8 data) -> void {
if(addr == 0x00b6) {
//acknowledge only edge-sensitive interrupts
r.interruptStatus &= ~(data & 0b11110010);
return;
}
}

View File

@ -28,25 +28,10 @@ auto PPU::latchRegisters() -> void {
auto PPU::latchSprites() -> void {
l.spriteCount = 0;
if(!l.spriteEnable) return;
uint offset = 0;
bool windowInside = s.vclk >= l.spriteWindowY0 && s.vclk <= l.spriteWindowY1;
for(auto index : range(l.oamCount)) {
uint32 attributes = l.oam[!s.field][index];
auto& sprite = l.sprite[l.spriteCount];
sprite.x = attributes.bits(24,31);
if(sprite.x > 224 && sprite.x < 249) continue;
sprite.y = attributes.bits(16,23);
if((uint8)(s.vclk - sprite.y) > 7) continue;
sprite.vflip = attributes.bit(15);
sprite.hflip = attributes.bit(14);
sprite.priority = attributes.bit(13);
sprite.window = attributes.bit(12);
if(l.spriteWindowEnable && !sprite.window && !windowInside) continue;
sprite.palette = 8 + attributes.bits(9,11);
sprite.tile = attributes.bits(0,8);
if((uint8)(s.vclk - attributes.bits(16,23)) > 7) continue;
l.sprite[l.spriteCount] = attributes;
if(++l.spriteCount >= 32) break;
}
}

View File

@ -36,17 +36,6 @@ struct PPU : Thread, IO {
uint12 color;
};
struct Sprite {
uint8 x;
uint8 y;
uint1 vflip;
uint1 hflip;
uint1 priority;
uint1 window;
uint4 palette; //latchSprites() always sets bit3
uint9 tile;
};
uint12 output[224 * 144];
struct State {
@ -84,7 +73,7 @@ struct PPU : Thread, IO {
uint8 spriteWindowY1;
//latchSprites()
Sprite sprite[32];
uint32 sprite[32];
uint spriteCount;
//latchOAM()

View File

@ -96,19 +96,20 @@ auto PPU::renderScreenTwo() -> void {
}
auto PPU::renderSprite() -> void {
bool windowInside = s.hclk >= l.spriteWindowX0 && s.hclk <= l.spriteWindowY0;
bool windowInside = s.vclk >= l.spriteWindowY0 && s.vclk <= l.spriteWindowY1
&& s.hclk >= l.spriteWindowX0 && s.hclk <= l.spriteWindowX1;
for(auto index : range(l.spriteCount)) {
auto& sprite = l.sprite[index];
if(l.spriteWindowEnable && !sprite.window && !windowInside) continue;
if((uint8)(s.hclk - sprite.x) > 7) continue;
auto sprite = l.sprite[index];
if(l.spriteWindowEnable && sprite.bit(12) == windowInside) continue;
if((uint8)(s.hclk - sprite.bits(24,31)) > 7) continue;
uint3 tileY = (s.vclk - sprite.y) ^ sprite.vflip * 7;
uint3 tileX = (s.hclk - sprite.x) ^ sprite.hflip * 7;
uint4 tileColor = renderFetch(sprite.tile, tileY, tileX);
if(renderTransparent(sprite.palette.bit(2), tileColor)) continue;
if(!sprite.priority && s.pixel.source == Pixel::Source::ScreenTwo) continue;
uint3 tileY = (s.vclk - sprite.bits(16,23)) ^ sprite.bit(15) * 7;
uint3 tileX = (s.hclk - sprite.bits(24,31)) ^ sprite.bit(14) * 7;
uint4 tileColor = renderFetch(sprite.bits(0,8), tileY, tileX);
if(renderTransparent(sprite.bit(11), tileColor)) continue;
if(!sprite.bit(13) && s.pixel.source == Pixel::Source::ScreenTwo) continue;
s.pixel = {Pixel::Source::Sprite, renderPalette(sprite.palette, tileColor)};
s.pixel = {Pixel::Source::Sprite, renderPalette(8 + sprite.bits(9,11), tileColor)};
break;
}
}

View File

@ -29,21 +29,10 @@ auto PPU::serialize(serializer& s) -> void {
s.integer(l.spriteWindowX1);
s.integer(l.spriteWindowY1);
for(uint n : range(32)) {
s.integer(l.sprite[n].x);
s.integer(l.sprite[n].y);
s.integer(l.sprite[n].vflip);
s.integer(l.sprite[n].hflip);
s.integer(l.sprite[n].priority);
s.integer(l.sprite[n].window);
s.integer(l.sprite[n].palette);
s.integer(l.sprite[n].tile);
}
s.array(l.sprite);
s.integer(l.spriteCount);
for(uint n : range(2)) {
s.array(l.oam[n]);
}
for(uint n : range(2)) s.array(l.oam[n]);
s.integer(l.oamCount);
s.integer(r.screenOneEnable);
@ -83,9 +72,7 @@ auto PPU::serialize(serializer& s) -> void {
s.integer(r.vtotal);
s.integer(r.vblank);
s.array(r.pool);
for(uint n : range(16)) {
s.array(r.palette[n].color);
}
for(uint n : range(16)) s.array(r.palette[n].color);
s.integer(r.htimerEnable);
s.integer(r.htimerRepeat);
s.integer(r.vtimerEnable);