mirror of https://github.com/bsnes-emu/bsnes.git
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:
parent
2d83300235
commit
25eaaa82f4
|
@ -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/";
|
||||
|
|
|
@ -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/)
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
|
@ -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 {
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ struct Device {
|
|||
//expansion port devices
|
||||
Satellaview,
|
||||
SuperDisc,
|
||||
eBoot,
|
||||
S21FX,
|
||||
};
|
||||
|
||||
Device();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue