bsnes/higan/sfc/smp/smp.hpp
Tim Allen 16f736307e Update to v103r06 release.
byuu says:

Changelog:

  - processor/spc700: restored fetch/load/store/pull/push shorthand
    functions
  - processor/spc700: split functions that tested the algorithm used (`op
    != &SPC700:...`) to separate instructions
      - mostly for code clarity over code size: it was awkward having
        cycle counts change based on a function parameter
  - processor/spc700: implemented Overload's new findings on which
    cycles are truly internal (no bus reads)
  - sfc/smp: TEST register emulation has been vastly improved¹

¹: it turns out that TEST.d4,d5 is the external clock divider (used
when accessing RAM through the DSP), and TEST.d6,d7 is the internal
clock divider (used when accessing IPLROM, IO registers, or during idle
cycles.)

The DSP (24576khz) feeds its clock / 12 through to the SMP (2048khz).
The clock divider setting further divides the clock by 2, 4, 8, or 16.
Since 8 and 16 are not cleanly divislbe by 12, the SMP cycle count
glitches out and seems to take 10 and 2 clocks instead of 8 or 16. This
can on real hardware either cause the SMP to run very slowly, or more
likely, crash the SMP completely until reset.

What's even stranger is the timers aren't affected by this. They still
clock by 2, 4, 8, or 16.

Note that technically I could divide my own clock counters by 24 and
reduce these to {1,2,5,10} and {1,2,4,8}, I instead chose to divide by
12 to better illustrate this hardware issue and better model that the
SMP clock runs at 2048khz and not 1024khz.

Further, note that things aren't 100% perfect yet. This seems to throw
off some tests, such as blargg's `test_timer_speed`. I can't tell how
far off I am because blargg's test tragically doesn't print out fail
values. But you can see the improvements in that higan is now passing
all of Revenant's tests that were obviously completely wrong before.
2017-07-03 17:24:47 +10:00

95 lines
1.8 KiB
C++

//Sony CXP1100Q-1
struct SMP : Processor::SPC700, Thread {
uint8 iplrom[64];
//smp.cpp
auto synchronizing() const -> bool override;
auto portRead(uint2 port) const -> uint8;
auto portWrite(uint2 port, uint8 data) -> void;
auto main() -> void;
auto load(Markup::Node) -> bool;
auto power() -> void;
//serialization.cpp
auto serialize(serializer&) -> void;
private:
struct IO {
//timing
uint clockCounter;
uint dspCounter;
//external
uint8 apu0;
uint8 apu1;
uint8 apu2;
uint8 apu3;
//$00f0
uint1 timersDisable;
uint1 ramWritable;
uint1 ramDisable;
uint1 timersEnable;
uint2 externalWaitStates;
uint2 internalWaitStates;
//$00f1
bool iplromEnable;
//$00f2
uint8 dspAddr;
//$00f4-00f7
uint8 cpu0;
uint8 cpu1;
uint8 cpu2;
uint8 cpu3;
//$00f8-00f9
uint8 aux4;
uint8 aux5;
} io;
static auto Enter() -> void;
//memory.cpp
auto ramRead(uint16 addr) -> uint8;
auto ramWrite(uint16 addr, uint8 data) -> void;
auto busRead(uint16 addr) -> uint8;
auto busWrite(uint16 addr, uint8 data) -> void;
auto idle() -> void override;
auto read(uint16 addr) -> uint8 override;
auto write(uint16 addr, uint8 data) -> void override;
auto readDisassembler(uint16 addr) -> uint8 override;
//timing.cpp
template<uint Frequency> struct Timer {
uint8 stage0;
uint8 stage1;
uint8 stage2;
uint4 stage3;
bool line;
bool enable;
uint8 target;
auto step(uint clocks) -> void;
auto synchronizeStage1() -> void;
};
Timer<128> timer0;
Timer<128> timer1;
Timer< 16> timer2;
inline auto wait(maybe<uint16> address = nothing) -> void;
inline auto step(uint clocks) -> void;
inline auto stepTimers(uint clocks) -> void;
};
extern SMP smp;