Merge pull request #109 from SpiceWare/master

ARM Timer 1 support
This commit is contained in:
sa666666 2017-03-21 10:07:22 -02:30 committed by GitHub
commit ee1fdfa565
4 changed files with 97 additions and 2 deletions

View File

@ -21,6 +21,7 @@
#include "System.hxx" #include "System.hxx"
#include "Thumbulator.hxx" #include "Thumbulator.hxx"
#include "CartDPCPlus.hxx" #include "CartDPCPlus.hxx"
#include "TIA.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CartridgeDPCPlus::CartridgeDPCPlus(const uInt8* image, uInt32 size, CartridgeDPCPlus::CartridgeDPCPlus(const uInt8* image, uInt32 size,
@ -31,6 +32,7 @@ CartridgeDPCPlus::CartridgeDPCPlus(const uInt8* image, uInt32 size,
myParameterPointer(0), myParameterPointer(0),
mySystemCycles(0), mySystemCycles(0),
myFractionalClocks(0.0), myFractionalClocks(0.0),
myARMCycles(0),
myCurrentBank(0) myCurrentBank(0)
{ {
// Store image, making sure it's at least 29KB // Store image, making sure it's at least 29KB
@ -72,6 +74,7 @@ void CartridgeDPCPlus::reset()
{ {
// Update cycles to the current system cycles // Update cycles to the current system cycles
mySystemCycles = mySystem->cycles(); mySystemCycles = mySystem->cycles();
myARMCycles = mySystem->cycles();
myFractionalClocks = 0.0; myFractionalClocks = 0.0;
setInitialState(); setInitialState();
@ -106,6 +109,7 @@ void CartridgeDPCPlus::systemCyclesReset()
{ {
// Adjust the cycle counter so that it reflects the new value // Adjust the cycle counter so that it reflects the new value
mySystemCycles -= mySystem->cycles(); mySystemCycles -= mySystem->cycles();
myARMCycles -= mySystem->cycles();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -188,7 +192,20 @@ inline void CartridgeDPCPlus::callFunction(uInt8 value)
case 255: case 255:
// Call user written ARM code (most likely be C compiled for ARM) // Call user written ARM code (most likely be C compiled for ARM)
try { 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) { catch(const runtime_error& e) {
if(!mySystem->autodetectMode()) if(!mySystem->autodetectMode())
@ -680,8 +697,12 @@ bool CartridgeDPCPlus::save(Serializer& out) const
// The random number generator register // The random number generator register
out.putInt(myRandomNumber); out.putInt(myRandomNumber);
// Get system cycles and fractional clocks
out.putInt(mySystemCycles); out.putInt(mySystemCycles);
out.putInt(uInt32(myFractionalClocks * 100000000.0)); out.putInt(uInt32(myFractionalClocks * 100000000.0));
// clock info for Thumbulator
out.putInt(myARMCycles);
} }
catch(...) catch(...)
{ {
@ -743,6 +764,9 @@ bool CartridgeDPCPlus::load(Serializer& in)
// Get system cycles and fractional clocks // Get system cycles and fractional clocks
mySystemCycles = in.getInt(); mySystemCycles = in.getInt();
myFractionalClocks = double(in.getInt()) / 100000000.0; myFractionalClocks = double(in.getInt()) / 100000000.0;
// clock info for Thumbulator
myARMCycles = (Int32)in.getInt();
} }
catch(...) catch(...)
{ {

View File

@ -260,6 +260,9 @@ class CartridgeDPCPlus : public Cartridge
// Fractional DPC music OSC clocks unused during the last update // Fractional DPC music OSC clocks unused during the last update
double myFractionalClocks; double myFractionalClocks;
// System cycle count when the last Thumbulator->Run() occurred
Int32 myARMCycles;
// Indicates which bank is currently active // Indicates which bank is currently active
uInt16 myCurrentBank; uInt16 myCurrentBank;

View File

@ -56,8 +56,11 @@ using Common::Base;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Thumbulator::Thumbulator(const uInt16* rom_ptr, uInt16* ram_ptr, bool traponfatal) Thumbulator::Thumbulator(const uInt16* rom_ptr, uInt16* ram_ptr, bool traponfatal)
: rom(rom_ptr), : rom(rom_ptr),
ram(ram_ptr) ram(ram_ptr),
T1TCR(0),
T1TC(0)
{ {
setConsoleTiming(ConsoleTiming::ntsc);
trapFatalErrors(traponfatal); trapFatalErrors(traponfatal);
reset(); reset();
} }
@ -79,6 +82,43 @@ string Thumbulator::run()
return statusMsg.str(); 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) inline int Thumbulator::fatalError(const char* opcode, uInt32 v1, const char* msg)
{ {
@ -236,6 +276,14 @@ 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;
case 0xE0008004: // T1TCR - Timer 1 Control Register
T1TCR = data;
break;
case 0xE0008008: // T1TC - Timer 1 Counter
T1TC = data;
break;
case 0xE000E010: case 0xE000E010:
{ {
uInt32 old = systick_ctrl; uInt32 old = systick_ctrl;
@ -348,6 +396,14 @@ uInt32 Thumbulator::read32(uInt32 addr)
{ {
switch(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: case 0xE000E010:
data = systick_ctrl; data = systick_ctrl;
systick_ctrl &= (~0x00010000); systick_ctrl &= (~0x00010000);

View File

@ -28,6 +28,7 @@
#define THUMBULATOR_HXX #define THUMBULATOR_HXX
#include "bspf.hxx" #include "bspf.hxx"
#include "Console.hxx"
#define ROMADDMASK 0x7FFF #define ROMADDMASK 0x7FFF
#define RAMADDMASK 0x1FFF #define RAMADDMASK 0x1FFF
@ -54,6 +55,7 @@ class Thumbulator
otherwise an empty string otherwise an empty string
*/ */
string run(); string run();
string run(uInt32 cycles);
/** /**
Normally when a fatal error is encountered, the ARM emulation Normally when a fatal error is encountered, the ARM emulation
@ -69,6 +71,8 @@ class Thumbulator
*/ */
static void trapFatalErrors(bool enable) { trapOnFatal = enable; } static void trapFatalErrors(bool enable) { trapOnFatal = enable; }
void setConsoleTiming(ConsoleTiming timing);
private: private:
uInt32 read_register(uInt32 reg); uInt32 read_register(uInt32 reg);
void write_register(uInt32 reg, uInt32 data); void write_register(uInt32 reg, uInt32 data);
@ -78,6 +82,7 @@ class Thumbulator
uInt32 read32(uInt32 addr); uInt32 read32(uInt32 addr);
void write16(uInt32 addr, uInt32 data); void write16(uInt32 addr, uInt32 data);
void write32(uInt32 addr, uInt32 data); void write32(uInt32 addr, uInt32 data);
void updateTimer(uInt32 cycles);
void do_zflag(uInt32 x); void do_zflag(uInt32 x);
void do_nflag(uInt32 x); void do_nflag(uInt32 x);
@ -107,6 +112,13 @@ class Thumbulator
uInt32 systick_ctrl, systick_reload, systick_count, systick_calibrate; uInt32 systick_ctrl, systick_reload, systick_count, systick_calibrate;
uInt64 instructions, fetches, reads, writes, systick_ints; 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; ostringstream statusMsg;
static bool trapOnFatal; static bool trapOnFatal;