diff --git a/src/emucore/CartDPCPlus.cxx b/src/emucore/CartDPCPlus.cxx index 21d44788c..7798a39e4 100644 --- a/src/emucore/CartDPCPlus.cxx +++ b/src/emucore/CartDPCPlus.cxx @@ -21,6 +21,7 @@ #include "System.hxx" #include "Thumbulator.hxx" #include "CartDPCPlus.hxx" +#include "TIA.hxx" // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CartridgeDPCPlus::CartridgeDPCPlus(const uInt8* image, uInt32 size, @@ -31,6 +32,7 @@ CartridgeDPCPlus::CartridgeDPCPlus(const uInt8* image, uInt32 size, myParameterPointer(0), mySystemCycles(0), myFractionalClocks(0.0), + myARMCycles(0), myCurrentBank(0) { // Store image, making sure it's at least 29KB @@ -72,6 +74,7 @@ void CartridgeDPCPlus::reset() { // Update cycles to the current system cycles mySystemCycles = mySystem->cycles(); + myARMCycles = mySystem->cycles(); myFractionalClocks = 0.0; setInitialState(); @@ -106,6 +109,7 @@ void CartridgeDPCPlus::systemCyclesReset() { // Adjust the cycle counter so that it reflects the new value mySystemCycles -= mySystem->cycles(); + myARMCycles -= mySystem->cycles(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -188,7 +192,20 @@ inline void CartridgeDPCPlus::callFunction(uInt8 value) case 255: // Call user written ARM code (most likely be C compiled for ARM) try { - myThumbEmulator->run(); + Int32 cycles = mySystem->cycles() - myARMCycles; + myARMCycles = mySystem->cycles(); + + // setConsoleTiming should go elsewhere for one-time-setup, but doesn't + // work in install() + // + // if put in setInitialState() Stella ends up crashing in System.hxx at + // TIA& tia() const { return myTIA; } + // with error + // "Thread 1: EXC_BAD_ACCESS (code=1, address=0x20)" + + myThumbEmulator->setConsoleTiming(mySystem->tia().consoleTiming()); + + myThumbEmulator->run(cycles); } catch(const runtime_error& e) { if(!mySystem->autodetectMode()) @@ -680,8 +697,12 @@ bool CartridgeDPCPlus::save(Serializer& out) const // The random number generator register out.putInt(myRandomNumber); + // Get system cycles and fractional clocks out.putInt(mySystemCycles); out.putInt(uInt32(myFractionalClocks * 100000000.0)); + + // clock info for Thumbulator + out.putInt(myARMCycles); } catch(...) { @@ -743,6 +764,9 @@ bool CartridgeDPCPlus::load(Serializer& in) // Get system cycles and fractional clocks mySystemCycles = in.getInt(); myFractionalClocks = double(in.getInt()) / 100000000.0; + + // clock info for Thumbulator + myARMCycles = (Int32)in.getInt(); } catch(...) { diff --git a/src/emucore/CartDPCPlus.hxx b/src/emucore/CartDPCPlus.hxx index 06092624f..939e93df6 100644 --- a/src/emucore/CartDPCPlus.hxx +++ b/src/emucore/CartDPCPlus.hxx @@ -259,6 +259,9 @@ class CartridgeDPCPlus : public Cartridge // Fractional DPC music OSC clocks unused during the last update double myFractionalClocks; + + // System cycle count when the last Thumbulator->Run() occurred + Int32 myARMCycles; // Indicates which bank is currently active uInt16 myCurrentBank; diff --git a/src/emucore/Thumbulator.cxx b/src/emucore/Thumbulator.cxx index ef4e95ac7..a20b4decb 100644 --- a/src/emucore/Thumbulator.cxx +++ b/src/emucore/Thumbulator.cxx @@ -56,8 +56,11 @@ using Common::Base; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Thumbulator::Thumbulator(const uInt16* rom_ptr, uInt16* ram_ptr, bool traponfatal) : rom(rom_ptr), - ram(ram_ptr) + ram(ram_ptr), + T1TCR(0), + T1TC(0) { + setConsoleTiming(ConsoleTiming::ntsc); trapFatalErrors(traponfatal); reset(); } @@ -79,6 +82,43 @@ string Thumbulator::run() return statusMsg.str(); } +void Thumbulator::setConsoleTiming(ConsoleTiming timing) +{ + // this sets how many ticks of the Harmony/Melody clock + // will occur per tick of the 6507 clock + constexpr double NTSC = 70.0 / 1.193182; // NTSC 6507 clock rate + constexpr double PAL = 70.0 / 1.182298; // PAL 6507 clock rate + constexpr double SECAM = 70.0 / 1.187500; // SECAM 6507 clock rate + + switch (timing) + { + case ConsoleTiming::ntsc: timing_factor = NTSC; break; + case ConsoleTiming::secam: timing_factor = SECAM; break; + case ConsoleTiming::pal: timing_factor = PAL; break; + default: timing_factor = NTSC; break; + } +} +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void Thumbulator::updateTimer(uInt32 cycles) +{ + double increment; + + increment = cycles * timing_factor; + + if (T1TCR & 1) // bit 0 controls timer on/off + { + + T1TC += uInt32(increment); + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +string Thumbulator::run(uInt32 cycles) +{ + updateTimer(cycles); + return this->run(); +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - inline int Thumbulator::fatalError(const char* opcode, uInt32 v1, const char* msg) { @@ -235,6 +275,14 @@ void Thumbulator::write32(uInt32 addr, uInt32 data) case 0xE0000000: DO_DISS(statusMsg << "uart: [" << char(data&0xFF) << "]" << endl); break; + + case 0xE0008004: // T1TCR - Timer 1 Control Register + T1TCR = data; + break; + + case 0xE0008008: // T1TC - Timer 1 Counter + T1TC = data; + break; case 0xE000E010: { @@ -348,6 +396,14 @@ uInt32 Thumbulator::read32(uInt32 addr) { switch(addr) { + case 0xE0008004: // T1TCR - Timer 1 Control Register + data = T1TCR; + return data; + + case 0xE0008008: // T1TC - Timer 1 Counter + data = T1TC; + return data; + case 0xE000E010: data = systick_ctrl; systick_ctrl &= (~0x00010000); diff --git a/src/emucore/Thumbulator.hxx b/src/emucore/Thumbulator.hxx index a36d3341d..78ee3c553 100644 --- a/src/emucore/Thumbulator.hxx +++ b/src/emucore/Thumbulator.hxx @@ -28,6 +28,7 @@ #define THUMBULATOR_HXX #include "bspf.hxx" +#include "Console.hxx" #define ROMADDMASK 0x7FFF #define RAMADDMASK 0x1FFF @@ -54,6 +55,7 @@ class Thumbulator otherwise an empty string */ string run(); + string run(uInt32 cycles); /** Normally when a fatal error is encountered, the ARM emulation @@ -68,6 +70,8 @@ class Thumbulator @param enable Enable (the default) or disable exceptions on fatal errors */ static void trapFatalErrors(bool enable) { trapOnFatal = enable; } + + void setConsoleTiming(ConsoleTiming timing); private: uInt32 read_register(uInt32 reg); @@ -78,6 +82,7 @@ class Thumbulator uInt32 read32(uInt32 addr); void write16(uInt32 addr, uInt32 data); void write32(uInt32 addr, uInt32 data); + void updateTimer(uInt32 cycles); void do_zflag(uInt32 x); void do_nflag(uInt32 x); @@ -106,6 +111,13 @@ class Thumbulator bool handler_mode; uInt32 systick_ctrl, systick_reload, systick_count, systick_calibrate; uInt64 instructions, fetches, reads, writes, systick_ints; + + // For emulation of LPC2103's timer 1, used for NTSC/PAL/SECAM detection. + // Register names from documentation: + // http://www.nxp.com/documents/user_manual/UM10161.pdf + uInt32 T1TCR; // Timer 1 Timer Control Register + uInt32 T1TC; // Timer 1 Timer Counter + double timing_factor; ostringstream statusMsg;