2016-06-05 05:03:21 +00:00
|
|
|
auto SuperFX::readIO(uint24 addr, uint8) -> uint8 {
|
Update to v100r14 release.
byuu says:
(Windows: compile with -fpermissive to silence an annoying error. I'll
fix it in the next WIP.)
I completely replaced the time management system in higan and overhauled
the scheduler.
Before, processor threads would have "int64 clock"; and there would
be a 1:1 relationship between two threads. When thread A ran for X
cycles, it'd subtract X * B.Frequency from clock; and when thread B ran
for Y cycles, it'd add Y * A.Frequency from clock. This worked well
and allowed perfect precision; but it doesn't work when you have more
complicated relationships: eg the 68K can sync to the Z80 and PSG; the
Z80 to the 68K and PSG; so the PSG needs two counters.
The new system instead uses a "uint64 clock" variable that represents
time in attoseconds. Every time the scheduler exits, it subtracts
the smallest clock count from all threads, to prevent an overflow
scenario. The only real downside is that rounding errors mean that
roughly every 20 minutes, we have a rounding error of one clock cycle
(one 20,000,000th of a second.) However, this only applies to systems
with multiple oscillators, like the SNES. And when you're in that
situation ... there's no such thing as a perfect oscillator anyway. A
real SNES will be thousands of times less out of spec than 1hz per 20
minutes.
The advantages are pretty immense. First, we obviously can now support
more complex relationships between threads. Second, we can build a
much more abstracted scheduler. All of libco is now abstracted away
completely, which may permit a state-machine / coroutine version of
Thread in the future. We've basically gone from this:
auto SMP::step(uint clocks) -> void {
clock += clocks * (uint64)cpu.frequency;
dsp.clock -= clocks;
if(dsp.clock < 0 && !scheduler.synchronizing()) co_switch(dsp.thread);
if(clock >= 0 && !scheduler.synchronizing()) co_switch(cpu.thread);
}
To this:
auto SMP::step(uint clocks) -> void {
Thread::step(clocks);
synchronize(dsp);
synchronize(cpu);
}
As you can see, we don't have to do multiple clock adjustments anymore.
This is a huge win for the SNES CPU that had to update the SMP, DSP, all
peripherals and all coprocessors. Likewise, we don't have to synchronize
all coprocessors when one runs, now we can just synchronize the active
one to the CPU.
Third, when changing the frequencies of threads (think SGB speed setting
modes, GBC double-speed mode, etc), it no longer causes the "int64
clock" value to be erroneous.
Fourth, this results in a fairly decent speedup, mostly across the
board. Aside from the GBA being mostly a wash (for unknown reasons),
it's about an 8% - 12% speedup in every other emulation core.
Now, all of this said ... this was an unbelievably massive change, so
... you know what that means >_> If anyone can help test all types of
SNES coprocessors, and some other system games, it'd be appreciated.
----
Lastly, we have a bitchin' new about screen. It unfortunately adds
~200KiB onto the binary size, because the PNG->C++ header file
transformation doesn't compress very well, and I want to keep the
original resource files in with the higan archive. I might try some
things to work around this file size increase in the future, but for now
... yeah, slightly larger archive sizes, sorry.
The logo's a bit busted on Windows (the Label control's background
transparency and alignment settings aren't working), but works well on
GTK. I'll have to fix Windows before the next official release. For now,
look on my Twitter feed if you want to see what it's supposed to look
like.
----
EDIT: forgot about ICD2::Enter. It's doing some weird inverse
run-to-save thing that I need to implement support for somehow. So, save
states on the SGB core probably won't work with this WIP.
2016-07-30 03:56:12 +00:00
|
|
|
cpu.synchronize(*this);
|
2016-06-05 05:03:21 +00:00
|
|
|
addr = 0x3000 | addr.bits(0,9);
|
2010-08-09 13:28:56 +00:00
|
|
|
|
|
|
|
if(addr >= 0x3100 && addr <= 0x32ff) {
|
2016-06-05 22:10:01 +00:00
|
|
|
return readCache(addr - 0x3100);
|
2010-08-09 13:28:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if(addr >= 0x3000 && addr <= 0x301f) {
|
|
|
|
return regs.r[(addr >> 1) & 15] >> ((addr & 1) << 3);
|
|
|
|
}
|
|
|
|
|
|
|
|
switch(addr) {
|
2013-05-05 09:21:30 +00:00
|
|
|
case 0x3030: {
|
|
|
|
return regs.sfr >> 0;
|
|
|
|
}
|
2010-08-09 13:28:56 +00:00
|
|
|
|
2013-05-05 09:21:30 +00:00
|
|
|
case 0x3031: {
|
|
|
|
uint8 r = regs.sfr >> 8;
|
|
|
|
regs.sfr.irq = 0;
|
Update to v098r11 release.
byuu says:
Changelog:
- fixed nall/path.hpp compilation issue
- fixed ruby/audio/xaudio header declaration compilation issue (again)
- cleaned up xaudio2.hpp file to match my coding syntax (12.5% of the
file was whitespace overkill)
- added null terminator entry to nall/windows/utf8.hpp argc[] array
- nall/windows/guid.hpp uses the Windows API for generating the GUID
- this should stop all the bug reports where two nall users were
generating GUIDs at the exact same second
- fixed hiro/cocoa compilation issue with uint# types
- fixed major higan/sfc Super Game Boy audio latency issue
- fixed higan/sfc CPU core bug with pei, [dp], [dp]+y instructions
- major cleanups to higan/processor/r65816 core
- merged emulation/native-mode opcodes
- use camel-case naming on memory.hpp functions
- simplify address masking code for memory.hpp functions
- simplify a few opcodes themselves (avoid redundant copies, etc)
- rename regs.* to r.* to match modern convention of other CPU cores
- removed device.order<> concept from Emulator::Interface
- cores will now do the translation to make the job of the UI easier
- fixed plurality naming of arrays in Emulator::Interface
- example: emulator.ports[p].devices[d].inputs[i]
- example: vector<Medium> media
- probably more surprises
Major show-stoppers to the next official release:
- we need to work on GB core improvements: LY=153/0 case, multiple STAT
IRQs case, GBC audio output regs, etc.
- we need to re-add software cursors for light guns (Super Scope,
Justifier)
- after the above, we need to fix the turbo button for the Super Scope
I really have no idea how I want to implement the light guns. Ideally,
we'd want it in higan/video, so we can support the NES Zapper with the
same code. But this isn't going to be easy, because only the SNES knows
when its output is interlaced, and its resolutions can vary as
{256,512}x{224,240,448,480} which requires pixel doubling that was
hard-coded to the SNES-specific behavior, but isn't appropriate to be
exposed in higan/video.
2016-05-25 11:13:02 +00:00
|
|
|
cpu.r.irq = 0;
|
2013-05-05 09:21:30 +00:00
|
|
|
return r;
|
|
|
|
}
|
2010-08-09 13:28:56 +00:00
|
|
|
|
2013-05-05 09:21:30 +00:00
|
|
|
case 0x3034: {
|
|
|
|
return regs.pbr;
|
|
|
|
}
|
2010-08-09 13:28:56 +00:00
|
|
|
|
2013-05-05 09:21:30 +00:00
|
|
|
case 0x3036: {
|
|
|
|
return regs.rombr;
|
|
|
|
}
|
2010-08-09 13:28:56 +00:00
|
|
|
|
2013-05-05 09:21:30 +00:00
|
|
|
case 0x303b: {
|
|
|
|
return regs.vcr;
|
|
|
|
}
|
2010-08-09 13:28:56 +00:00
|
|
|
|
2013-05-05 09:21:30 +00:00
|
|
|
case 0x303c: {
|
|
|
|
return regs.rambr;
|
|
|
|
}
|
2010-08-09 13:28:56 +00:00
|
|
|
|
2013-05-05 09:21:30 +00:00
|
|
|
case 0x303e: {
|
|
|
|
return regs.cbr >> 0;
|
|
|
|
}
|
2010-08-09 13:28:56 +00:00
|
|
|
|
2013-05-05 09:21:30 +00:00
|
|
|
case 0x303f: {
|
|
|
|
return regs.cbr >> 8;
|
|
|
|
}
|
2010-08-09 13:28:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0x00;
|
|
|
|
}
|
|
|
|
|
2016-06-05 05:03:21 +00:00
|
|
|
auto SuperFX::writeIO(uint24 addr, uint8 data) -> void {
|
Update to v100r14 release.
byuu says:
(Windows: compile with -fpermissive to silence an annoying error. I'll
fix it in the next WIP.)
I completely replaced the time management system in higan and overhauled
the scheduler.
Before, processor threads would have "int64 clock"; and there would
be a 1:1 relationship between two threads. When thread A ran for X
cycles, it'd subtract X * B.Frequency from clock; and when thread B ran
for Y cycles, it'd add Y * A.Frequency from clock. This worked well
and allowed perfect precision; but it doesn't work when you have more
complicated relationships: eg the 68K can sync to the Z80 and PSG; the
Z80 to the 68K and PSG; so the PSG needs two counters.
The new system instead uses a "uint64 clock" variable that represents
time in attoseconds. Every time the scheduler exits, it subtracts
the smallest clock count from all threads, to prevent an overflow
scenario. The only real downside is that rounding errors mean that
roughly every 20 minutes, we have a rounding error of one clock cycle
(one 20,000,000th of a second.) However, this only applies to systems
with multiple oscillators, like the SNES. And when you're in that
situation ... there's no such thing as a perfect oscillator anyway. A
real SNES will be thousands of times less out of spec than 1hz per 20
minutes.
The advantages are pretty immense. First, we obviously can now support
more complex relationships between threads. Second, we can build a
much more abstracted scheduler. All of libco is now abstracted away
completely, which may permit a state-machine / coroutine version of
Thread in the future. We've basically gone from this:
auto SMP::step(uint clocks) -> void {
clock += clocks * (uint64)cpu.frequency;
dsp.clock -= clocks;
if(dsp.clock < 0 && !scheduler.synchronizing()) co_switch(dsp.thread);
if(clock >= 0 && !scheduler.synchronizing()) co_switch(cpu.thread);
}
To this:
auto SMP::step(uint clocks) -> void {
Thread::step(clocks);
synchronize(dsp);
synchronize(cpu);
}
As you can see, we don't have to do multiple clock adjustments anymore.
This is a huge win for the SNES CPU that had to update the SMP, DSP, all
peripherals and all coprocessors. Likewise, we don't have to synchronize
all coprocessors when one runs, now we can just synchronize the active
one to the CPU.
Third, when changing the frequencies of threads (think SGB speed setting
modes, GBC double-speed mode, etc), it no longer causes the "int64
clock" value to be erroneous.
Fourth, this results in a fairly decent speedup, mostly across the
board. Aside from the GBA being mostly a wash (for unknown reasons),
it's about an 8% - 12% speedup in every other emulation core.
Now, all of this said ... this was an unbelievably massive change, so
... you know what that means >_> If anyone can help test all types of
SNES coprocessors, and some other system games, it'd be appreciated.
----
Lastly, we have a bitchin' new about screen. It unfortunately adds
~200KiB onto the binary size, because the PNG->C++ header file
transformation doesn't compress very well, and I want to keep the
original resource files in with the higan archive. I might try some
things to work around this file size increase in the future, but for now
... yeah, slightly larger archive sizes, sorry.
The logo's a bit busted on Windows (the Label control's background
transparency and alignment settings aren't working), but works well on
GTK. I'll have to fix Windows before the next official release. For now,
look on my Twitter feed if you want to see what it's supposed to look
like.
----
EDIT: forgot about ICD2::Enter. It's doing some weird inverse
run-to-save thing that I need to implement support for somehow. So, save
states on the SGB core probably won't work with this WIP.
2016-07-30 03:56:12 +00:00
|
|
|
cpu.synchronize(*this);
|
2016-06-05 05:03:21 +00:00
|
|
|
addr = 0x3000 | addr.bits(0,9);
|
2010-08-09 13:28:56 +00:00
|
|
|
|
|
|
|
if(addr >= 0x3100 && addr <= 0x32ff) {
|
2016-06-05 22:10:01 +00:00
|
|
|
return writeCache(addr - 0x3100, data);
|
2010-08-09 13:28:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if(addr >= 0x3000 && addr <= 0x301f) {
|
2015-12-14 09:41:06 +00:00
|
|
|
uint n = (addr >> 1) & 15;
|
2010-08-09 13:28:56 +00:00
|
|
|
if((addr & 1) == 0) {
|
|
|
|
regs.r[n] = (regs.r[n] & 0xff00) | data;
|
|
|
|
} else {
|
|
|
|
regs.r[n] = (data << 8) | (regs.r[n] & 0xff);
|
|
|
|
}
|
2016-06-05 22:10:01 +00:00
|
|
|
if(n == 14) updateROMBuffer();
|
2010-08-09 13:28:56 +00:00
|
|
|
|
|
|
|
if(addr == 0x301f) regs.sfr.g = 1;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch(addr) {
|
2013-05-05 09:21:30 +00:00
|
|
|
case 0x3030: {
|
|
|
|
bool g = regs.sfr.g;
|
|
|
|
regs.sfr = (regs.sfr & 0xff00) | (data << 0);
|
|
|
|
if(g == 1 && regs.sfr.g == 0) {
|
|
|
|
regs.cbr = 0x0000;
|
2016-06-05 22:10:01 +00:00
|
|
|
flushCache();
|
2013-05-05 09:21:30 +00:00
|
|
|
}
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case 0x3031: {
|
|
|
|
regs.sfr = (data << 8) | (regs.sfr & 0x00ff);
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case 0x3033: {
|
2015-06-28 08:44:56 +00:00
|
|
|
regs.bramr = data & 0x01;
|
2013-05-05 09:21:30 +00:00
|
|
|
} break;
|
|
|
|
|
|
|
|
case 0x3034: {
|
|
|
|
regs.pbr = data & 0x7f;
|
2016-06-05 22:10:01 +00:00
|
|
|
flushCache();
|
2013-05-05 09:21:30 +00:00
|
|
|
} break;
|
|
|
|
|
|
|
|
case 0x3037: {
|
|
|
|
regs.cfgr = data;
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case 0x3038: {
|
|
|
|
regs.scbr = data;
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case 0x3039: {
|
2015-06-28 08:44:56 +00:00
|
|
|
regs.clsr = data & 0x01;
|
2013-05-05 09:21:30 +00:00
|
|
|
} break;
|
|
|
|
|
|
|
|
case 0x303a: {
|
|
|
|
regs.scmr = data;
|
|
|
|
} break;
|
2010-08-09 13:28:56 +00:00
|
|
|
}
|
|
|
|
}
|