added command line options to include (coarse) ARM cycles into system cycles

This commit is contained in:
Thomas Jentzsch 2021-06-13 12:18:44 +02:00
parent c02fc531aa
commit 0c511805c7
10 changed files with 99 additions and 15 deletions

View File

@ -3541,6 +3541,16 @@
fatal errors are simply logged, and emulation continues. Do not use this fatal errors are simply logged, and emulation continues. Do not use this
unless you know exactly what you're doing, as it changes the behaviour as unless you know exactly what you're doing, as it changes the behaviour as
compared to real hardware.</td> compared to real hardware.</td>
</tr><tr>
<td><pre>-dev.thumb.incycles &lt;1|0&gt;</pre></td>
<td>When enabled, ARM emulation cycles are added to system cycles. This
allows detecting timer overruns.</br>
Note: The ARM emulation cycles are only a coarse approximation.
</td>
</tr><tr>
<td><pre>-dev.thumb.cyclefactor &lt;float&gt;</pre></td>
<td>Adjust ARM emulation cycles by a factor (1.25 = default).
</td>
</tr><tr> </tr><tr>
<td><pre>-&lt;plr.|dev.&gt;eepromaccess &lt;1|0&gt;</pre></td> <td><pre>-&lt;plr.|dev.&gt;eepromaccess &lt;1|0&gt;</pre></td>
<td>When enabled, each read or write access to the AtariVox/SaveKey EEPROM is <td>When enabled, each read or write access to the AtariVox/SaveKey EEPROM is

View File

@ -72,9 +72,12 @@ CartridgeBUS::CartridgeBUS(const ByteBuffer& image, size_t size,
0x00000808, 0x00000808,
0x40001FDC, 0x40001FDC,
devSettings ? settings.getBool("dev.thumb.trapfatal") : false, devSettings ? settings.getBool("dev.thumb.trapfatal") : false,
devSettings ? settings.getFloat("dev.thumb.cyclefactor") : 1.0,
Thumbulator::ConfigureFor::BUS, Thumbulator::ConfigureFor::BUS,
this); this);
myIncCycles = devSettings ? settings.getBool("dev.thumb.inccycles") : false,
setInitialState(); setInitialState();
} }
@ -167,10 +170,12 @@ inline void CartridgeBUS::callFunction(uInt8 value)
// time for Stella as ARM code "runs in zero 6507 cycles". // time for Stella as ARM code "runs in zero 6507 cycles".
case 255: // call without IRQ driven audio case 255: // call without IRQ driven audio
try { try {
Int32 cycles = Int32(mySystem->cycles() - myARMCycles); uInt32 cycles = uInt32(mySystem->cycles() - myARMCycles);
myARMCycles = mySystem->cycles();
myARMCycles = mySystem->cycles();
myThumbEmulator->run(cycles); myThumbEmulator->run(cycles);
if(myIncCycles)
mySystem->incrementCycles(cycles);
} }
catch(const runtime_error& e) { catch(const runtime_error& e) {
if(!mySystem->autodetectMode()) if(!mySystem->autodetectMode())

View File

@ -299,6 +299,9 @@ class CartridgeBUS : public Cartridge
uInt8 myFastJumpActive{false}; uInt8 myFastJumpActive{false};
// ARM code increases 6507 cycles
bool myIncCycles{false};
private: private:
// Following constructors and assignment operators not supported // Following constructors and assignment operators not supported
CartridgeBUS() = delete; CartridgeBUS() = delete;

View File

@ -108,9 +108,12 @@ CartridgeCDF::CartridgeCDF(const ByteBuffer& image, size_t size,
static_cast<uInt32>(mySize), static_cast<uInt32>(mySize),
cBase, cStart, cStack, cBase, cStart, cStack,
devSettings ? settings.getBool("dev.thumb.trapfatal") : false, devSettings ? settings.getBool("dev.thumb.trapfatal") : false,
devSettings ? settings.getFloat("dev.thumb.cyclefactor") : 1.0,
thumulatorConfiguration(myCDFSubtype), thumulatorConfiguration(myCDFSubtype),
this); this);
myIncCycles = devSettings ? settings.getBool("dev.thumb.inccycles") : false,
setInitialState(); setInitialState();
} }
@ -195,10 +198,12 @@ inline void CartridgeCDF::callFunction(uInt8 value)
// time for Stella as ARM code "runs in zero 6507 cycles". // time for Stella as ARM code "runs in zero 6507 cycles".
case 255: // call without IRQ driven audio case 255: // call without IRQ driven audio
try { try {
Int32 cycles = Int32(mySystem->cycles() - myARMCycles); uInt32 cycles = uInt32(mySystem->cycles() - myARMCycles);
myARMCycles = mySystem->cycles();
myARMCycles = mySystem->cycles();
myThumbEmulator->run(cycles); myThumbEmulator->run(cycles);
if(myIncCycles)
mySystem->incrementCycles(cycles); // * ~1.79 is the limit for ZEVIOUZ title screen
} }
catch(const runtime_error& e) { catch(const runtime_error& e) {
if(!mySystem->autodetectMode()) if(!mySystem->autodetectMode())

View File

@ -369,6 +369,9 @@ class CartridgeCDF : public Cartridge
// CDF subtype // CDF subtype
CDFSubtype myCDFSubtype{CDFSubtype::CDF0}; CDFSubtype myCDFSubtype{CDFSubtype::CDF0};
// ARM code increases 6507 cycles
bool myIncCycles{false};
private: private:
// Following constructors and assignment operators not supported // Following constructors and assignment operators not supported
CartridgeCDF() = delete; CartridgeCDF() = delete;

View File

@ -58,6 +58,7 @@ CartridgeDPCPlus::CartridgeDPCPlus(const ByteBuffer& image, size_t size,
0x00000C08, 0x00000C08,
0x40001FDC, 0x40001FDC,
devSettings ? settings.getBool("dev.thumb.trapfatal") : false, devSettings ? settings.getBool("dev.thumb.trapfatal") : false,
devSettings ? settings.getFloat("dev.thumb.cyclefactor") : 1.0,
Thumbulator::ConfigureFor::DPCplus, Thumbulator::ConfigureFor::DPCplus,
this); this);
@ -77,6 +78,8 @@ CartridgeDPCPlus::CartridgeDPCPlus(const ByteBuffer& image, size_t size,
myDriverMD5 == "8dd73b44fd11c488326ce507cbeb19d1" ) myDriverMD5 == "8dd73b44fd11c488326ce507cbeb19d1" )
myFractionalLowMask = 0x0F0000; myFractionalLowMask = 0x0F0000;
myIncCycles = devSettings ? settings.getBool("dev.thumb.inccycles") : false,
setInitialState(); setInitialState();
} }
@ -200,10 +203,12 @@ inline void CartridgeDPCPlus::callFunction(uInt8 value)
// time for Stella as ARM code "runs in zero 6507 cycles". // time for Stella as ARM code "runs in zero 6507 cycles".
case 255: // call without IRQ driven audio case 255: // call without IRQ driven audio
try { try {
Int32 cycles = Int32(mySystem->cycles() - myARMCycles); uInt32 cycles = uInt32(mySystem->cycles() - myARMCycles);
myARMCycles = mySystem->cycles();
myARMCycles = mySystem->cycles();
myThumbEmulator->run(cycles); myThumbEmulator->run(cycles);
if(myIncCycles)
mySystem->incrementCycles(cycles);
} }
catch(const runtime_error& e) { catch(const runtime_error& e) {
if(!mySystem->autodetectMode()) if(!mySystem->autodetectMode())

View File

@ -312,6 +312,9 @@ class CartridgeDPCPlus : public Cartridge
// For current versions, this is 0x0F00FF; older versions need 0x0F0000 // For current versions, this is 0x0F00FF; older versions need 0x0F0000
uInt32 myFractionalLowMask{0x0F00FF}; uInt32 myFractionalLowMask{0x0F00FF};
// ARM code increases 6507 cycles
bool myIncCycles{false};
private: private:
// Following constructors and assignment operators not supported // Following constructors and assignment operators not supported
CartridgeDPCPlus() = delete; CartridgeDPCPlus() = delete;

View File

@ -242,10 +242,12 @@ Settings::Settings()
setPermanent("dev.tm.uncompressed", 600); setPermanent("dev.tm.uncompressed", 600);
setPermanent("dev.tm.interval", "1f"); // = 1 frame setPermanent("dev.tm.interval", "1f"); // = 1 frame
setPermanent("dev.tm.horizon", "30s"); // = ~30 seconds setPermanent("dev.tm.horizon", "30s"); // = ~30 seconds
// Thumb ARM emulation options
setPermanent("dev.thumb.trapfatal", "true");
setPermanent("dev.detectedinfo", "true"); setPermanent("dev.detectedinfo", "true");
setPermanent("dev.eepromaccess", "true"); setPermanent("dev.eepromaccess", "true");
// Thumb ARM emulation options
setPermanent("dev.thumb.trapfatal", "true");
setPermanent("dev.thumb.inccycles", "true");
setPermanent("dev.thumb.cyclefactor", "1.25");
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -683,6 +685,8 @@ void Settings::usage() const
#endif #endif
<< " -dev.thumb.trapfatal <1|0> Determines whether errors in ARM emulation\n" << " -dev.thumb.trapfatal <1|0> Determines whether errors in ARM emulation\n"
<< " throw an exception\n" << " throw an exception\n"
<< " -dev.thumb.inccycles <1|0> Determines whether ARM emulation cycles\n"
<< " increase system cycles\n"
<< " -dev.eepromaccess <1|0> Enable messages for AtariVox/SaveKey access\n" << " -dev.eepromaccess <1|0> Enable messages for AtariVox/SaveKey access\n"
<< " messages\n" << " messages\n"
<< " -dev.tia.type <standard|custom| Selects a TIA type\n" << " -dev.tia.type <standard|custom| Selects a TIA type\n"

View File

@ -52,10 +52,13 @@ using Common::Base;
#define CONV_RAMROM(d) (d) #define CONV_RAMROM(d) (d)
#endif #endif
#define CYCLE_FACTOR 1.25 // coarse ARM cycle multiplier
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Thumbulator::Thumbulator(const uInt16* rom_ptr, uInt16* ram_ptr, uInt32 rom_size, Thumbulator::Thumbulator(const uInt16* rom_ptr, uInt16* ram_ptr, uInt32 rom_size,
const uInt32 c_base, const uInt32 c_start, const uInt32 c_stack, const uInt32 c_base, const uInt32 c_start, const uInt32 c_stack,
bool traponfatal, Thumbulator::ConfigureFor configurefor, bool traponfatal, double cyclefactor,
Thumbulator::ConfigureFor configurefor,
Cartridge* cartridge) Cartridge* cartridge)
: rom{rom_ptr}, : rom{rom_ptr},
romSize{rom_size}, romSize{rom_size},
@ -73,12 +76,15 @@ Thumbulator::Thumbulator(const uInt16* rom_ptr, uInt16* ram_ptr, uInt32 rom_size
setConsoleTiming(ConsoleTiming::ntsc); setConsoleTiming(ConsoleTiming::ntsc);
#ifndef UNSAFE_OPTIMIZATIONS #ifndef UNSAFE_OPTIMIZATIONS
trapFatalErrors(traponfatal); trapFatalErrors(traponfatal);
#endif
#ifndef NO_THUMB_STATS
cycleFactor(cyclefactor);
#endif #endif
reset(); reset();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string Thumbulator::run() string Thumbulator::doRun(uInt32& cycles)
{ {
reset(); reset();
for(;;) for(;;)
@ -89,6 +95,11 @@ string Thumbulator::run()
throw runtime_error("instructions > 500000"); throw runtime_error("instructions > 500000");
#endif #endif
} }
#ifndef NO_THUMB_STATS
cycles = uInt32((_stats.fetches + _stats.reads + _stats.writes) * arm_cycle_factor / timing_factor);
#else
cycles = 0;
#endif
#if defined(THUMB_DISS) || defined(THUMB_DBUG) #if defined(THUMB_DISS) || defined(THUMB_DBUG)
dump_counters(); dump_counters();
cout << statusMsg.str() << endl; cout << statusMsg.str() << endl;
@ -119,15 +130,19 @@ void Thumbulator::setConsoleTiming(ConsoleTiming timing)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Thumbulator::updateTimer(uInt32 cycles) void Thumbulator::updateTimer(uInt32 cycles)
{ {
#ifdef TIMER_0
if(T0TCR & 1) // bit 0 controls timer on/off
T0TC += uInt32(cycles * timing_factor);
#endif
if (T1TCR & 1) // bit 0 controls timer on/off if (T1TCR & 1) // bit 0 controls timer on/off
T1TC += uInt32(cycles * timing_factor); T1TC += uInt32(cycles * timing_factor);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string Thumbulator::run(uInt32 cycles) string Thumbulator::run(uInt32& cycles)
{ {
updateTimer(cycles); updateTimer(cycles);
return run(); return doRun(cycles);
} }
#ifndef UNSAFE_OPTIMIZATIONS #ifndef UNSAFE_OPTIMIZATIONS
@ -289,7 +304,15 @@ void Thumbulator::write32(uInt32 addr, uInt32 data)
DO_DISS(statusMsg << "uart: [" << char(data&0xFF) << "]" << endl); DO_DISS(statusMsg << "uart: [" << char(data&0xFF) << "]" << endl);
break; break;
#endif #endif
#ifdef TIMER_0
case 0xE0004004: // T0TCR - Timer 0 Control Register
T0TCR = data;
break;
case 0xE0004008: // T0TC - Timer 0 Counter
T0TC = data;
break;
#endif
case 0xE0008004: // T1TCR - Timer 1 Control Register case 0xE0008004: // T1TCR - Timer 1 Control Register
T1TCR = data; T1TCR = data;
break; break;
@ -467,6 +490,15 @@ uInt32 Thumbulator::read32(uInt32 addr)
{ {
switch(addr) switch(addr)
{ {
#ifdef TIMER_0
case 0xE0004004: // T0TCR - Timer 0 Control Register
data = T0TCR;
return data;
case 0xE0004008: // T0TC - Timer 0 Counter
data = T0TC;
break;
#endif
case 0xE0008004: // T1TCR - Timer 1 Control Register case 0xE0008004: // T1TCR - Timer 1 Control Register
data = T1TCR; data = T1TCR;
return data; return data;
@ -2560,4 +2592,5 @@ int Thumbulator::reset()
#ifndef UNSAFE_OPTIMIZATIONS #ifndef UNSAFE_OPTIMIZATIONS
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool Thumbulator::trapOnFatal = true; bool Thumbulator::trapOnFatal = true;
double Thumbulator::arm_cycle_factor = 1.25;
#endif #endif

View File

@ -46,6 +46,8 @@ class Cartridge;
#define CPSR_C (1u<<29) #define CPSR_C (1u<<29)
#define CPSR_V (1u<<28) #define CPSR_V (1u<<28)
//#define TIMER_0 // enable timer 0 support
class Thumbulator class Thumbulator
{ {
public: public:
@ -68,7 +70,8 @@ class Thumbulator
Thumbulator(const uInt16* rom_ptr, uInt16* ram_ptr, uInt32 rom_size, Thumbulator(const uInt16* rom_ptr, uInt16* ram_ptr, uInt32 rom_size,
const uInt32 c_base, const uInt32 c_start, const uInt32 c_stack, const uInt32 c_base, const uInt32 c_start, const uInt32 c_stack,
bool traponfatal, Thumbulator::ConfigureFor configurefor, bool traponfatal, double cyclefactor,
Thumbulator::ConfigureFor configurefor,
Cartridge* cartridge); Cartridge* cartridge);
/** /**
@ -79,8 +82,8 @@ class Thumbulator
@return The results of any debugging output (if enabled), @return The results of any debugging output (if enabled),
otherwise an empty string otherwise an empty string
*/ */
string run(); string doRun(uInt32& cycles);
string run(uInt32 cycles); string run(uInt32& cycles);
const Stats& stats() const { return _stats; } const Stats& stats() const { return _stats; }
const Stats& prevStats() const { return _prevStats; } const Stats& prevStats() const { return _prevStats; }
@ -99,6 +102,9 @@ class Thumbulator
*/ */
static void trapFatalErrors(bool enable) { trapOnFatal = enable; } static void trapFatalErrors(bool enable) { trapOnFatal = enable; }
#endif #endif
#ifndef NO_THUMB_STATS
static void cycleFactor(double factor) { arm_cycle_factor = factor; }
#endif
/** /**
Inform the Thumbulator class about the console currently in use, Inform the Thumbulator class about the console currently in use,
@ -215,6 +221,10 @@ class Thumbulator
// For emulation of LPC2103's timer 1, used for NTSC/PAL/SECAM detection. // For emulation of LPC2103's timer 1, used for NTSC/PAL/SECAM detection.
// Register names from documentation: // Register names from documentation:
// http://www.nxp.com/documents/user_manual/UM10161.pdf // http://www.nxp.com/documents/user_manual/UM10161.pdf
#ifdef TIMER_0
uInt32 T0TCR{0}; // Timer 0 Timer Control Register
uInt32 T0TC{0}; // Timer 0 Timer Counter
#endif
uInt32 T1TCR{0}; // Timer 1 Timer Control Register uInt32 T1TCR{0}; // Timer 1 Timer Control Register
uInt32 T1TC{0}; // Timer 1 Timer Counter uInt32 T1TC{0}; // Timer 1 Timer Counter
double timing_factor{0.0}; double timing_factor{0.0};
@ -224,6 +234,9 @@ class Thumbulator
static bool trapOnFatal; static bool trapOnFatal;
#endif #endif
#ifndef NO_THUMB_STATS
static double arm_cycle_factor;
#endif
ConfigureFor configuration; ConfigureFor configuration;