2011-01-16 21:23:03 +00:00
|
|
|
//============================================================================
|
|
|
|
//
|
|
|
|
// SSSS tt lll lll
|
|
|
|
// SS SS tt ll ll
|
|
|
|
// SS tttttt eeee ll ll aaaa
|
|
|
|
// SSSS tt ee ee ll ll aa
|
|
|
|
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
|
|
|
|
// SS SS tt ee ll ll aa aa
|
|
|
|
// SSSS ttt eeeee llll llll aaaaa
|
|
|
|
//
|
2021-12-31 19:37:17 +00:00
|
|
|
// Copyright (c) 1995-2022 by Bradford W. Mott, Stephen Anthony
|
2011-01-16 21:23:03 +00:00
|
|
|
// and the Stella Team
|
|
|
|
//
|
|
|
|
// See the file "License.txt" for information on usage and redistribution of
|
|
|
|
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
|
|
|
//============================================================================
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
// This class provides Thumb emulation code ("Thumbulator")
|
|
|
|
// by David Welch (dwelch@dwelch.com)
|
|
|
|
// Modified by Fred Quimby
|
|
|
|
// Code is public domain and used with the author's consent
|
|
|
|
//============================================================================
|
|
|
|
|
|
|
|
#include "bspf.hxx"
|
2013-07-27 22:28:41 +00:00
|
|
|
#include "Base.hxx"
|
2017-11-20 18:57:05 +00:00
|
|
|
#include "Cart.hxx"
|
2011-01-16 21:23:03 +00:00
|
|
|
#include "Thumbulator.hxx"
|
2016-05-24 16:55:45 +00:00
|
|
|
using Common::Base;
|
2011-01-16 21:23:03 +00:00
|
|
|
|
2012-12-26 21:16:34 +00:00
|
|
|
// Uncomment the following to enable specific functionality
|
|
|
|
// WARNING!!! This slows the runtime to a crawl
|
2019-02-19 23:32:48 +00:00
|
|
|
// #define THUMB_DISS
|
2021-06-24 14:21:00 +00:00
|
|
|
// #define THUMB_DBUG
|
2012-12-26 21:16:34 +00:00
|
|
|
|
|
|
|
#if defined(THUMB_DISS)
|
|
|
|
#define DO_DISS(statement) statement
|
|
|
|
#else
|
|
|
|
#define DO_DISS(statement)
|
|
|
|
#endif
|
|
|
|
#if defined(THUMB_DBUG)
|
|
|
|
#define DO_DBUG(statement) statement
|
|
|
|
#else
|
|
|
|
#define DO_DBUG(statement)
|
|
|
|
#endif
|
2011-01-16 21:23:03 +00:00
|
|
|
|
2015-08-09 21:19:10 +00:00
|
|
|
#ifdef __BIG_ENDIAN__
|
2022-08-21 22:03:08 +00:00
|
|
|
#define CONV_DATA(d) ((((d) & 0xFFFF)>>8) | (((d) & 0xffff)<<8)) & 0xffff
|
|
|
|
#define CONV_RAMROM(d) (((d)>>8) | ((d)<<8)) & 0xffff
|
2015-08-09 21:19:10 +00:00
|
|
|
#else
|
2022-08-21 22:03:08 +00:00
|
|
|
#define CONV_DATA(d) ((d) & 0xFFFF)
|
2019-02-19 23:32:48 +00:00
|
|
|
#define CONV_RAMROM(d) (d)
|
2015-08-09 21:19:10 +00:00
|
|
|
#endif
|
|
|
|
|
2021-06-24 14:21:00 +00:00
|
|
|
#ifdef THUMB_CYCLE_COUNT
|
2021-07-01 13:40:49 +00:00
|
|
|
#define MERGE_I_S
|
2021-06-24 14:21:00 +00:00
|
|
|
#define INC_S_CYCLES(addr, accessType) \
|
|
|
|
if(_countCycles) \
|
|
|
|
incSCycles(addr, accessType)
|
|
|
|
#define INC_N_CYCLES(addr, accessType) \
|
|
|
|
if(_countCycles) \
|
|
|
|
incNCycles(addr, accessType)
|
|
|
|
#define INC_I_CYCLES \
|
|
|
|
if(_countCycles) \
|
|
|
|
incICycles()
|
|
|
|
#define INC_I_CYCLES_M(m) \
|
|
|
|
if(_countCycles) \
|
|
|
|
incICycles(m)
|
2021-07-08 13:46:41 +00:00
|
|
|
|
|
|
|
#define INC_SHIFT_CYCLES \
|
2021-10-22 21:44:40 +00:00
|
|
|
INC_I_CYCLES \
|
2021-07-08 13:46:41 +00:00
|
|
|
//FETCH_TYPE(CycleType::S, AccessType::data)
|
|
|
|
|
|
|
|
#define INC_LDR_CYCLES \
|
|
|
|
INC_N_CYCLES(rb, AccessType::data); \
|
2021-10-22 21:44:40 +00:00
|
|
|
INC_I_CYCLES \
|
2021-08-26 21:47:18 +00:00
|
|
|
/*FETCH_TYPE(CycleType::N, AccessType::data); \
|
|
|
|
FETCH_TYPE_N;*/
|
2021-07-08 13:46:41 +00:00
|
|
|
#define INC_LDRB_CYCLES \
|
|
|
|
INC_N_CYCLES(rb & (~1U), AccessType::data); \
|
2021-10-22 21:44:40 +00:00
|
|
|
INC_I_CYCLES \
|
2021-08-26 21:47:18 +00:00
|
|
|
/*FETCH_TYPE(CycleType::N, AccessType::data); \
|
|
|
|
FETCH_TYPE_N;*/
|
2021-07-08 13:46:41 +00:00
|
|
|
|
|
|
|
#define INC_STR_CYCLES \
|
|
|
|
INC_N_CYCLES(rb, AccessType::data); \
|
2021-10-22 21:44:40 +00:00
|
|
|
FETCH_TYPE_N \
|
2021-07-08 13:46:41 +00:00
|
|
|
//INC_N_CYCLES(rb, AccessType::data);
|
|
|
|
#define INC_STRB_CYCLES \
|
|
|
|
INC_N_CYCLES(rb & (~1U), AccessType::data); \
|
2021-10-22 21:44:40 +00:00
|
|
|
FETCH_TYPE_N \
|
2021-07-08 13:46:41 +00:00
|
|
|
//INC_N_CYCLES(rb & (~1U), AccessType::data);
|
|
|
|
|
2021-08-26 21:47:18 +00:00
|
|
|
#if 0 // unused for now
|
2021-07-08 13:46:41 +00:00
|
|
|
#define FETCH_TYPE(cycleType, accessType) \
|
|
|
|
_prefetchCycleType[_pipeIdx] = cycleType; \
|
|
|
|
_prefetchAccessType[_pipeIdx] = accessType
|
2021-08-26 21:47:18 +00:00
|
|
|
#endif
|
2021-07-08 13:46:41 +00:00
|
|
|
#define FETCH_TYPE_N \
|
|
|
|
_prefetchCycleType[_pipeIdx] = CycleType::N
|
|
|
|
|
|
|
|
// ARM cycles
|
|
|
|
#define INC_ARM_CYCLES(m) \
|
2021-06-27 07:44:42 +00:00
|
|
|
_totalCycles += m
|
2021-06-24 14:21:00 +00:00
|
|
|
#else
|
|
|
|
#define INC_S_CYCLES(addr, accessType)
|
|
|
|
#define INC_N_CYCLES(addr, accessType)
|
|
|
|
#define INC_I_CYCLES
|
|
|
|
#define INC_I_CYCLES_M(m)
|
2021-07-11 06:47:45 +00:00
|
|
|
|
|
|
|
#define INC_SHIFT_CYCLES
|
|
|
|
|
2021-07-08 13:46:41 +00:00
|
|
|
#define INC_LDR_CYCLES
|
|
|
|
#define INC_LDRB_CYCLES
|
2021-07-11 06:47:45 +00:00
|
|
|
|
|
|
|
#define INC_STR_CYCLES
|
|
|
|
#define INC_STRB_CYCLES
|
|
|
|
|
|
|
|
#define FETCH_TYPE(cycleType, accessType)
|
2021-06-24 14:21:00 +00:00
|
|
|
#define FETCH_TYPE_N
|
2021-07-11 06:47:45 +00:00
|
|
|
|
2021-07-08 13:46:41 +00:00
|
|
|
// ARM cycles
|
|
|
|
#define INC_ARM_CYCLES(m)
|
2021-06-24 14:21:00 +00:00
|
|
|
#endif
|
2021-06-13 10:18:44 +00:00
|
|
|
|
2022-11-28 21:12:48 +00:00
|
|
|
#define do_znflags(x) znFlags=(x)
|
|
|
|
#define do_cflag_bit(x) cFlag = (x)
|
2022-12-01 22:01:31 +00:00
|
|
|
#define do_vflag_bit(x) vFlag = (x)
|
2022-11-28 21:12:48 +00:00
|
|
|
|
2011-01-16 21:23:03 +00:00
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
2020-10-19 21:37:20 +00:00
|
|
|
Thumbulator::Thumbulator(const uInt16* rom_ptr, uInt16* ram_ptr, uInt32 rom_size,
|
2020-09-13 20:16:34 +00:00
|
|
|
const uInt32 c_base, const uInt32 c_start, const uInt32 c_stack,
|
2021-06-13 10:18:44 +00:00
|
|
|
bool traponfatal, double cyclefactor,
|
|
|
|
Thumbulator::ConfigureFor configurefor,
|
2019-03-02 15:36:08 +00:00
|
|
|
Cartridge* cartridge)
|
2020-12-20 21:44:28 +00:00
|
|
|
: rom{rom_ptr},
|
|
|
|
romSize{rom_size},
|
|
|
|
cBase{c_base},
|
|
|
|
cStart{c_start},
|
|
|
|
cStack{c_stack},
|
|
|
|
decodedRom{make_unique<Op[]>(romSize / 2)}, // NOLINT
|
2022-11-27 12:10:02 +00:00
|
|
|
decodedParam{make_unique<uInt32[]>(romSize / 2)}, // NOLINT
|
2020-12-20 21:44:28 +00:00
|
|
|
ram{ram_ptr},
|
|
|
|
configuration{configurefor},
|
|
|
|
myCartridge{cartridge}
|
2011-01-16 21:23:03 +00:00
|
|
|
{
|
2020-10-19 21:37:20 +00:00
|
|
|
for(uInt32 i = 0; i < romSize / 2; ++i)
|
2022-11-27 12:10:02 +00:00
|
|
|
decodedRom[i] = decodeInstructionWord(CONV_RAMROM(rom[i]), i * 2);
|
2019-02-19 23:32:48 +00:00
|
|
|
|
2017-03-21 02:55:27 +00:00
|
|
|
setConsoleTiming(ConsoleTiming::ntsc);
|
2019-02-23 15:09:00 +00:00
|
|
|
#ifndef UNSAFE_OPTIMIZATIONS
|
2011-11-07 22:50:23 +00:00
|
|
|
trapFatalErrors(traponfatal);
|
2021-06-13 10:18:44 +00:00
|
|
|
#endif
|
2021-06-24 14:21:00 +00:00
|
|
|
#ifdef DEBUGGER_SUPPORT
|
2021-06-13 10:18:44 +00:00
|
|
|
cycleFactor(cyclefactor);
|
2019-02-23 15:09:00 +00:00
|
|
|
#endif
|
2016-02-27 19:58:20 +00:00
|
|
|
reset();
|
2011-01-16 21:23:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
2021-06-25 07:18:26 +00:00
|
|
|
string Thumbulator::doRun(uInt32& cycles, bool irqDrivenAudio)
|
2011-01-16 21:23:03 +00:00
|
|
|
{
|
2021-06-25 07:18:26 +00:00
|
|
|
_irqDrivenAudio = irqDrivenAudio;
|
2011-03-09 14:21:32 +00:00
|
|
|
reset();
|
|
|
|
for(;;)
|
|
|
|
{
|
2016-02-27 19:58:20 +00:00
|
|
|
if(execute()) break;
|
2019-02-23 15:09:00 +00:00
|
|
|
#ifndef UNSAFE_OPTIMIZATIONS
|
2021-06-24 14:21:00 +00:00
|
|
|
if(_stats.instructions > 500000) // way more than would otherwise be possible
|
2015-06-12 20:44:09 +00:00
|
|
|
throw runtime_error("instructions > 500000");
|
2019-02-23 15:09:00 +00:00
|
|
|
#endif
|
2011-03-09 14:21:32 +00:00
|
|
|
}
|
2021-06-24 14:21:00 +00:00
|
|
|
#ifdef THUMB_CYCLE_COUNT
|
|
|
|
_totalCycles *= _armCyclesFactor;
|
2021-06-25 07:18:26 +00:00
|
|
|
|
|
|
|
// assuming 10% per scanline is spend for audio updates
|
|
|
|
// (equals 5 cycles 6507 code + ~130-155 cycles ARM code)
|
|
|
|
if(_irqDrivenAudio)
|
|
|
|
_totalCycles *= 1.10;
|
|
|
|
|
2021-06-24 14:21:00 +00:00
|
|
|
//_totalCycles = 127148; // VB during Turbo start sequence
|
|
|
|
cycles = _totalCycles / timing_factor;
|
2021-06-13 10:18:44 +00:00
|
|
|
#else
|
|
|
|
cycles = 0;
|
|
|
|
#endif
|
2012-12-26 21:16:34 +00:00
|
|
|
#if defined(THUMB_DISS) || defined(THUMB_DBUG)
|
|
|
|
dump_counters();
|
|
|
|
cout << statusMsg.str() << endl;
|
2011-05-24 16:04:48 +00:00
|
|
|
return statusMsg.str();
|
2019-02-24 08:23:57 +00:00
|
|
|
#else
|
|
|
|
return "";
|
|
|
|
#endif
|
2011-05-24 16:04:48 +00:00
|
|
|
}
|
|
|
|
|
2017-03-24 20:32:08 +00:00
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
2017-03-21 02:55:27 +00:00
|
|
|
void Thumbulator::setConsoleTiming(ConsoleTiming timing)
|
|
|
|
{
|
|
|
|
// this sets how many ticks of the Harmony/Melody clock
|
|
|
|
// will occur per tick of the 6507 clock
|
2021-06-25 07:18:26 +00:00
|
|
|
constexpr double NTSC = 1.19318166666667; // NTSC 6507 clock rate
|
|
|
|
constexpr double PAL = 1.182298; // PAL 6507 clock rate
|
|
|
|
constexpr double SECAM = 1.187500; // SECAM 6507 clock rate
|
2017-03-24 20:32:08 +00:00
|
|
|
|
2021-06-24 14:21:00 +00:00
|
|
|
_consoleTiming = timing;
|
2017-10-11 14:53:54 +00:00
|
|
|
switch(timing)
|
2017-03-21 02:55:27 +00:00
|
|
|
{
|
2021-06-24 14:21:00 +00:00
|
|
|
case ConsoleTiming::ntsc: timing_factor = _MHz / NTSC; break;
|
|
|
|
case ConsoleTiming::pal: timing_factor = _MHz / PAL; break;
|
|
|
|
case ConsoleTiming::secam: timing_factor = _MHz / SECAM; break;
|
2020-05-11 17:27:01 +00:00
|
|
|
default: break; // satisfy compiler
|
2017-03-21 02:55:27 +00:00
|
|
|
}
|
|
|
|
}
|
2019-02-23 15:09:00 +00:00
|
|
|
|
2017-03-21 02:55:27 +00:00
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
void Thumbulator::updateTimer(uInt32 cycles)
|
|
|
|
{
|
2021-06-13 10:18:44 +00:00
|
|
|
#ifdef TIMER_0
|
|
|
|
if(T0TCR & 1) // bit 0 controls timer on/off
|
2021-06-15 16:55:32 +00:00
|
|
|
{
|
2021-06-24 14:21:00 +00:00
|
|
|
T0TC += static_cast<uInt32>(cycles * timing_factor);
|
2022-02-15 11:33:46 +00:00
|
|
|
tim0Total = tim0Start = 0;
|
2021-06-15 16:55:32 +00:00
|
|
|
}
|
2021-06-13 10:18:44 +00:00
|
|
|
#endif
|
2021-06-15 16:55:32 +00:00
|
|
|
if(T1TCR & 1) // bit 0 controls timer on/off
|
|
|
|
{
|
2021-06-24 14:21:00 +00:00
|
|
|
T1TC += static_cast<uInt32>(cycles * timing_factor);
|
2022-02-15 11:33:46 +00:00
|
|
|
tim1Total = tim1Start = 0;
|
2021-06-15 16:55:32 +00:00
|
|
|
}
|
2017-03-21 02:55:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
2021-06-25 07:18:26 +00:00
|
|
|
string Thumbulator::run(uInt32& cycles, bool irqDrivenAudio)
|
2017-03-21 02:55:27 +00:00
|
|
|
{
|
|
|
|
updateTimer(cycles);
|
2021-06-25 07:18:26 +00:00
|
|
|
return doRun(cycles, irqDrivenAudio);
|
2017-03-21 02:55:27 +00:00
|
|
|
}
|
|
|
|
|
2019-02-23 15:09:00 +00:00
|
|
|
#ifndef UNSAFE_OPTIMIZATIONS
|
2011-05-24 16:04:48 +00:00
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
2022-11-29 09:53:31 +00:00
|
|
|
int Thumbulator::fatalError(const char* opcode, uInt32 v1, const char* msg)
|
2011-05-24 16:04:48 +00:00
|
|
|
{
|
2011-05-26 16:14:46 +00:00
|
|
|
statusMsg << "Thumb ARM emulation fatal error: " << endl
|
2013-07-27 22:28:41 +00:00
|
|
|
<< opcode << "(" << Base::HEX8 << v1 << "), " << msg << endl;
|
2011-05-24 16:04:48 +00:00
|
|
|
dump_regs();
|
2011-11-07 22:50:23 +00:00
|
|
|
if(trapOnFatal)
|
2015-06-12 20:44:09 +00:00
|
|
|
throw runtime_error(statusMsg.str());
|
2011-11-07 22:50:23 +00:00
|
|
|
return 0;
|
2011-05-24 16:04:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
2022-11-29 09:53:31 +00:00
|
|
|
int Thumbulator::fatalError(const char* opcode, uInt32 v1, uInt32 v2,
|
2017-07-02 23:16:27 +00:00
|
|
|
const char* msg)
|
2011-05-24 16:04:48 +00:00
|
|
|
{
|
2011-05-26 16:14:46 +00:00
|
|
|
statusMsg << "Thumb ARM emulation fatal error: " << endl
|
2013-07-27 22:28:41 +00:00
|
|
|
<< opcode << "(" << Base::HEX8 << v1 << "," << v2 << "), " << msg << endl;
|
2011-05-24 16:04:48 +00:00
|
|
|
dump_regs();
|
2021-08-25 21:15:52 +00:00
|
|
|
if(trapOnFatal)
|
2015-06-12 20:44:09 +00:00
|
|
|
throw runtime_error(statusMsg.str());
|
2011-11-07 22:50:23 +00:00
|
|
|
return 0;
|
2011-01-16 21:23:03 +00:00
|
|
|
}
|
|
|
|
|
2011-03-09 14:21:32 +00:00
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
2022-08-21 16:23:52 +00:00
|
|
|
void Thumbulator::dump_counters() const
|
2011-01-16 21:23:03 +00:00
|
|
|
{
|
2011-03-09 14:21:32 +00:00
|
|
|
cout << endl << endl
|
2021-06-24 14:21:00 +00:00
|
|
|
<< "instructions " << _stats.instructions << endl;
|
|
|
|
#ifdef THUMB_STATS
|
|
|
|
cout << "reads " << _stats.reads << endl
|
2021-02-09 18:47:50 +00:00
|
|
|
<< "writes " << _stats.writes << endl
|
2021-06-24 14:21:00 +00:00
|
|
|
<< "memcycles " << (_stats.instructions + _stats.reads + _stats.writes) << endl;
|
2021-02-09 18:47:50 +00:00
|
|
|
#endif
|
2011-01-16 21:23:03 +00:00
|
|
|
}
|
|
|
|
|
2011-05-24 16:04:48 +00:00
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
2016-02-27 19:58:20 +00:00
|
|
|
void Thumbulator::dump_regs()
|
2011-05-24 16:04:48 +00:00
|
|
|
{
|
2022-12-01 22:38:27 +00:00
|
|
|
for (int cnt = 0; cnt <= 12; cnt++)
|
2011-05-24 16:04:48 +00:00
|
|
|
{
|
2021-08-25 21:15:52 +00:00
|
|
|
statusMsg << "R" << std::dec << std::setfill(' ') << std::setw(2) << std::left << cnt
|
|
|
|
<< "= " << Base::HEX8 << reg_norm[cnt];
|
2019-12-21 10:59:07 +00:00
|
|
|
if((cnt + 1) % 4 == 0)
|
|
|
|
statusMsg << endl;
|
2021-08-25 21:15:52 +00:00
|
|
|
else
|
|
|
|
statusMsg << " ";
|
2011-05-24 16:04:48 +00:00
|
|
|
}
|
|
|
|
statusMsg << endl
|
2016-02-27 19:58:20 +00:00
|
|
|
<< "SP = " << Base::HEX8 << reg_norm[13] << " "
|
|
|
|
<< "LR = " << Base::HEX8 << reg_norm[14] << " "
|
2021-08-25 21:15:52 +00:00
|
|
|
<< "PC = " << Base::HEX8 << reg_norm[15] << endl;
|
2011-05-24 16:04:48 +00:00
|
|
|
}
|
2019-02-23 15:09:00 +00:00
|
|
|
#endif
|
2011-05-24 16:04:48 +00:00
|
|
|
|
2011-03-09 14:21:32 +00:00
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
2022-11-29 08:23:39 +00:00
|
|
|
FORCE_INLINE uInt32 Thumbulator::fetch16(uInt32 addr)
|
2011-01-16 21:23:03 +00:00
|
|
|
{
|
2019-02-24 13:39:52 +00:00
|
|
|
#ifndef UNSAFE_OPTIMIZATIONS
|
2022-03-29 00:30:26 +00:00
|
|
|
uInt32 data = 0;
|
2021-06-24 14:21:00 +00:00
|
|
|
|
|
|
|
#ifdef THUMB_CYCLE_COUNT
|
2021-08-26 21:47:18 +00:00
|
|
|
_pipeIdx = (_pipeIdx+1) % 3;
|
2021-07-08 13:46:41 +00:00
|
|
|
|
|
|
|
#ifdef MERGE_I_S
|
|
|
|
if(_lastCycleType[2] == CycleType::I)
|
|
|
|
//if(_lastCycleType[_pipeIdx] == CycleType::I)
|
|
|
|
--_totalCycles;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if(_prefetchCycleType[_pipeIdx] == CycleType::S)
|
2021-06-25 18:10:06 +00:00
|
|
|
{
|
2021-07-08 13:46:41 +00:00
|
|
|
//#ifdef MERGE_I_S
|
|
|
|
// //if(_lastCycleType[2] == CycleType::I)
|
|
|
|
// if(_lastCycleType[_pipeIdx] == CycleType::I)
|
|
|
|
// {
|
|
|
|
// --_totalCycles;
|
|
|
|
// INC_S_CYCLES(addr, AccessType::prefetch); // N?
|
|
|
|
// }
|
|
|
|
// else
|
|
|
|
//#endif
|
|
|
|
INC_S_CYCLES(addr, AccessType::prefetch);
|
|
|
|
//INC_S_CYCLES(addr, _prefetchAccessType[_pipeIdx]);
|
2021-06-25 18:10:06 +00:00
|
|
|
}
|
2021-06-24 14:21:00 +00:00
|
|
|
else
|
2021-06-25 18:10:06 +00:00
|
|
|
{
|
2021-07-08 13:46:41 +00:00
|
|
|
INC_N_CYCLES(addr, AccessType::prefetch); // or ::data ?
|
|
|
|
//INC_N_CYCLES(addr, _prefetchAccessType[_pipeIdx]);
|
2021-06-25 18:10:06 +00:00
|
|
|
}
|
2021-07-08 13:46:41 +00:00
|
|
|
_prefetchCycleType[_pipeIdx] = CycleType::S; // default
|
|
|
|
//_prefetchAccessType[_pipeIdx] = AccessType::prefetch; // default
|
2021-06-24 14:21:00 +00:00
|
|
|
#endif
|
|
|
|
|
2016-02-27 19:58:20 +00:00
|
|
|
switch(addr & 0xF0000000)
|
2011-03-09 14:21:32 +00:00
|
|
|
{
|
2021-12-11 22:31:10 +00:00
|
|
|
case 0x00000000: //ROM
|
2011-03-09 14:21:32 +00:00
|
|
|
addr &= ROMADDMASK;
|
2016-02-27 19:58:20 +00:00
|
|
|
if(addr < 0x50)
|
2011-05-24 16:04:48 +00:00
|
|
|
fatalError("fetch16", addr, "abort");
|
2016-02-27 19:58:20 +00:00
|
|
|
addr >>= 1;
|
|
|
|
data = CONV_RAMROM(rom[addr]);
|
2013-07-27 22:28:41 +00:00
|
|
|
DO_DBUG(statusMsg << "fetch16(" << Base::HEX8 << addr << ")=" << Base::HEX4 << data << endl);
|
2016-02-27 19:58:20 +00:00
|
|
|
return data;
|
2019-02-24 13:39:52 +00:00
|
|
|
|
2021-12-11 22:31:10 +00:00
|
|
|
case 0x40000000: //RAM
|
2011-03-09 14:21:32 +00:00
|
|
|
addr &= RAMADDMASK;
|
2016-02-27 19:58:20 +00:00
|
|
|
addr >>= 1;
|
2021-06-24 14:21:00 +00:00
|
|
|
data = CONV_RAMROM(ram[addr]);
|
2013-07-27 22:28:41 +00:00
|
|
|
DO_DBUG(statusMsg << "fetch16(" << Base::HEX8 << addr << ")=" << Base::HEX4 << data << endl);
|
2016-02-27 19:58:20 +00:00
|
|
|
return data;
|
2022-03-29 00:30:26 +00:00
|
|
|
|
|
|
|
default: // reserved
|
|
|
|
break;
|
2011-03-09 14:21:32 +00:00
|
|
|
}
|
2011-05-24 16:04:48 +00:00
|
|
|
return fatalError("fetch16", addr, "abort");
|
2019-02-24 13:39:52 +00:00
|
|
|
#else
|
|
|
|
addr &= ROMADDMASK;
|
|
|
|
addr >>= 1;
|
|
|
|
return CONV_RAMROM(rom[addr]);
|
2019-02-23 15:09:00 +00:00
|
|
|
#endif
|
2011-01-16 21:23:03 +00:00
|
|
|
}
|
|
|
|
|
2011-03-09 14:21:32 +00:00
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
2016-02-27 19:58:20 +00:00
|
|
|
void Thumbulator::write16(uInt32 addr, uInt32 data)
|
2011-01-16 21:23:03 +00:00
|
|
|
{
|
2019-02-23 15:09:00 +00:00
|
|
|
#ifndef UNSAFE_OPTIMIZATIONS
|
2016-02-27 19:58:20 +00:00
|
|
|
if(addr & 1)
|
2011-05-24 16:04:48 +00:00
|
|
|
fatalError("write16", addr, "abort - misaligned");
|
2019-02-23 15:09:00 +00:00
|
|
|
#endif
|
2021-06-24 14:21:00 +00:00
|
|
|
#ifdef THUMB_STATS
|
2021-02-09 18:47:50 +00:00
|
|
|
++_stats.writes;
|
2019-02-23 15:09:00 +00:00
|
|
|
#endif
|
2011-03-09 14:21:32 +00:00
|
|
|
|
2013-07-27 22:28:41 +00:00
|
|
|
DO_DBUG(statusMsg << "write16(" << Base::HEX8 << addr << "," << Base::HEX8 << data << ")" << endl);
|
2011-03-09 14:21:32 +00:00
|
|
|
|
2016-02-27 19:58:20 +00:00
|
|
|
switch(addr & 0xF0000000)
|
2011-03-09 14:21:32 +00:00
|
|
|
{
|
2021-12-11 22:31:10 +00:00
|
|
|
case 0x40000000: //RAM
|
2022-03-29 17:18:11 +00:00
|
|
|
#ifndef UNSAFE_OPTIMIZATIONS
|
|
|
|
if(isInvalidRAM(addr))
|
|
|
|
fatalError("write16", addr, "abort - out of range");
|
|
|
|
if(isProtectedRAM(addr))
|
|
|
|
fatalError("write16", addr, "to driver area");
|
|
|
|
#endif
|
2016-02-27 19:58:20 +00:00
|
|
|
addr &= RAMADDMASK;
|
|
|
|
addr >>= 1;
|
|
|
|
ram[addr] = CONV_DATA(data);
|
2011-03-09 14:21:32 +00:00
|
|
|
return;
|
2011-11-05 22:31:40 +00:00
|
|
|
|
2021-07-02 15:40:30 +00:00
|
|
|
#ifndef UNSAFE_OPTIMIZATIONS
|
|
|
|
case 0xE0000000: //MAMCR
|
|
|
|
#else
|
2019-02-23 15:09:00 +00:00
|
|
|
default:
|
2021-07-02 15:40:30 +00:00
|
|
|
#endif
|
|
|
|
if(addr == 0xE01FC000)
|
|
|
|
{
|
|
|
|
DO_DBUG(statusMsg << "write16(" << Base::HEX8 << "MAMCR" << "," << Base::HEX8 << data << ") *" << endl);
|
|
|
|
if(!_lockMamcr)
|
|
|
|
mamcr = static_cast<MamModeType>(data);
|
|
|
|
return;
|
|
|
|
}
|
2011-03-09 14:21:32 +00:00
|
|
|
}
|
2019-02-23 15:09:00 +00:00
|
|
|
#ifndef UNSAFE_OPTIMIZATIONS
|
2011-05-24 16:04:48 +00:00
|
|
|
fatalError("write16", addr, data, "abort");
|
2019-02-23 15:09:00 +00:00
|
|
|
#endif
|
2011-01-16 21:23:03 +00:00
|
|
|
}
|
|
|
|
|
2011-03-09 14:21:32 +00:00
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
2016-02-27 19:58:20 +00:00
|
|
|
void Thumbulator::write32(uInt32 addr, uInt32 data)
|
2011-01-16 21:23:03 +00:00
|
|
|
{
|
2019-02-23 15:09:00 +00:00
|
|
|
#ifndef UNSAFE_OPTIMIZATIONS
|
2016-02-27 19:58:20 +00:00
|
|
|
if(addr & 3)
|
2011-05-24 16:04:48 +00:00
|
|
|
fatalError("write32", addr, "abort - misaligned");
|
2019-02-23 15:09:00 +00:00
|
|
|
#endif
|
2013-07-27 22:28:41 +00:00
|
|
|
DO_DBUG(statusMsg << "write32(" << Base::HEX8 << addr << "," << Base::HEX8 << data << ")" << endl);
|
2011-03-09 14:21:32 +00:00
|
|
|
|
2016-02-27 19:58:20 +00:00
|
|
|
switch(addr & 0xF0000000)
|
2011-03-09 14:21:32 +00:00
|
|
|
{
|
2019-02-23 15:09:00 +00:00
|
|
|
#ifndef UNSAFE_OPTIMIZATIONS
|
2011-03-09 14:21:32 +00:00
|
|
|
case 0xF0000000: //halt
|
|
|
|
dump_counters();
|
2016-02-27 19:58:20 +00:00
|
|
|
throw runtime_error("HALT");
|
2019-02-23 15:09:00 +00:00
|
|
|
#endif
|
2011-03-09 14:21:32 +00:00
|
|
|
|
|
|
|
case 0xE0000000: //periph
|
|
|
|
switch(addr)
|
|
|
|
{
|
2019-02-25 10:36:42 +00:00
|
|
|
#ifndef UNSAFE_OPTIMIZATIONS
|
2011-03-09 14:21:32 +00:00
|
|
|
case 0xE0000000:
|
2012-12-26 21:16:34 +00:00
|
|
|
DO_DISS(statusMsg << "uart: [" << char(data&0xFF) << "]" << endl);
|
2011-03-09 14:21:32 +00:00
|
|
|
break;
|
2019-02-25 10:36:42 +00:00
|
|
|
#endif
|
2021-06-13 10:18:44 +00:00
|
|
|
#ifdef TIMER_0
|
|
|
|
case 0xE0004004: // T0TCR - Timer 0 Control Register
|
2021-07-01 13:40:49 +00:00
|
|
|
#ifdef THUMB_CYCLE_COUNT
|
|
|
|
if((T0TCR ^ data) & 1)
|
|
|
|
{
|
|
|
|
// timer changed counter state
|
|
|
|
if(data & 1)
|
|
|
|
// timer switched to counting
|
|
|
|
tim0Start = _totalCycles;
|
|
|
|
else
|
|
|
|
// timer switched to disabled
|
|
|
|
tim0Total += _totalCycles - tim0Start;
|
|
|
|
}
|
|
|
|
#endif
|
2021-06-13 10:18:44 +00:00
|
|
|
T0TCR = data;
|
|
|
|
break;
|
2017-03-24 20:32:08 +00:00
|
|
|
|
2021-06-13 10:18:44 +00:00
|
|
|
case 0xE0004008: // T0TC - Timer 0 Counter
|
2021-07-01 13:40:49 +00:00
|
|
|
#ifdef THUMB_CYCLE_COUNT
|
|
|
|
tim0Start = _totalCycles;
|
|
|
|
tim0Total = data / _armCyclesFactor;
|
|
|
|
#endif
|
2021-06-13 10:18:44 +00:00
|
|
|
T0TC = data;
|
|
|
|
break;
|
|
|
|
#endif
|
2017-03-21 02:55:27 +00:00
|
|
|
case 0xE0008004: // T1TCR - Timer 1 Control Register
|
2021-07-01 13:40:49 +00:00
|
|
|
#ifdef THUMB_CYCLE_COUNT
|
|
|
|
if((T1TCR ^ data) & 1)
|
|
|
|
{
|
|
|
|
// timer changed counter state
|
|
|
|
if(data & 1)
|
|
|
|
// timer switched to counting
|
|
|
|
tim1Start = _totalCycles;
|
|
|
|
else
|
|
|
|
// timer switched to disabled
|
|
|
|
tim1Total += _totalCycles - tim1Start;
|
|
|
|
}
|
|
|
|
#endif
|
2017-03-21 02:55:27 +00:00
|
|
|
T1TCR = data;
|
|
|
|
break;
|
2017-03-24 20:32:08 +00:00
|
|
|
|
2017-03-21 02:55:27 +00:00
|
|
|
case 0xE0008008: // T1TC - Timer 1 Counter
|
2021-06-24 14:21:00 +00:00
|
|
|
#ifdef THUMB_CYCLE_COUNT
|
2021-07-01 13:40:49 +00:00
|
|
|
tim1Start = _totalCycles;
|
|
|
|
tim1Total = data / _armCyclesFactor;
|
2021-06-24 14:21:00 +00:00
|
|
|
#endif
|
2021-07-01 13:40:49 +00:00
|
|
|
T1TC = data;
|
2017-03-21 02:55:27 +00:00
|
|
|
break;
|
2016-02-27 19:58:20 +00:00
|
|
|
|
|
|
|
case 0xE000E010:
|
|
|
|
{
|
2022-03-29 00:30:26 +00:00
|
|
|
const uInt32 old = systick_ctrl;
|
2016-02-27 19:58:20 +00:00
|
|
|
systick_ctrl = data & 0x00010007;
|
|
|
|
if(((old & 1) == 0) && (systick_ctrl & 1))
|
|
|
|
{
|
|
|
|
// timer started, load count
|
|
|
|
systick_count = systick_reload;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 0xE000E014:
|
|
|
|
systick_reload = data & 0x00FFFFFF;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xE000E018:
|
|
|
|
systick_count = data & 0x00FFFFFF;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xE000E01C:
|
|
|
|
systick_calibrate = data & 0x00FFFFFF;
|
|
|
|
break;
|
2019-12-28 04:15:09 +00:00
|
|
|
|
2021-07-01 13:40:49 +00:00
|
|
|
#ifdef THUMB_CYCLE_COUNT
|
|
|
|
case 0xE01FC000: //MAMCR
|
|
|
|
DO_DBUG(statusMsg << "write32(" << Base::HEX8 << "MAMCR" << ","
|
|
|
|
<< Base::HEX8 << data << ") *" << endl);
|
|
|
|
if(!_lockMamcr)
|
|
|
|
mamcr = static_cast<MamModeType>(data);
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
|
2019-12-28 04:15:09 +00:00
|
|
|
default:
|
|
|
|
break;
|
2011-03-09 14:21:32 +00:00
|
|
|
}
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0xD0000000: //debug
|
2019-02-23 15:09:00 +00:00
|
|
|
#ifndef UNSAFE_OPTIMIZATIONS
|
2016-02-27 19:58:20 +00:00
|
|
|
switch(addr & 0xFF)
|
|
|
|
{
|
|
|
|
case 0x00:
|
|
|
|
statusMsg << "[" << Base::HEX8 << read_register(14) << "]["
|
|
|
|
<< addr << "] " << data << endl;
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x10:
|
|
|
|
statusMsg << Base::HEX8 << data << endl;
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x20:
|
|
|
|
statusMsg << Base::HEX8 << data << endl;
|
|
|
|
return;
|
2019-12-28 04:15:09 +00:00
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
2016-02-27 19:58:20 +00:00
|
|
|
}
|
2019-02-23 15:09:00 +00:00
|
|
|
#endif
|
2011-03-09 14:21:32 +00:00
|
|
|
return;
|
2016-12-30 00:00:30 +00:00
|
|
|
|
2019-02-23 15:09:00 +00:00
|
|
|
#ifndef UNSAFE_OPTIMIZATIONS
|
2021-12-11 22:31:10 +00:00
|
|
|
case 0x40000000: //RAM
|
2019-02-23 15:09:00 +00:00
|
|
|
#else
|
|
|
|
default:
|
|
|
|
#endif
|
2016-02-27 19:58:20 +00:00
|
|
|
write16(addr+0, (data >> 0) & 0xFFFF);
|
|
|
|
write16(addr+2, (data >> 16) & 0xFFFF);
|
2011-03-09 14:21:32 +00:00
|
|
|
return;
|
|
|
|
}
|
2019-02-23 15:09:00 +00:00
|
|
|
#ifndef UNSAFE_OPTIMIZATIONS
|
2011-05-24 16:04:48 +00:00
|
|
|
fatalError("write32", addr, data, "abort");
|
2019-02-23 15:09:00 +00:00
|
|
|
#endif
|
2011-01-16 21:23:03 +00:00
|
|
|
}
|
|
|
|
|
2019-02-23 15:09:00 +00:00
|
|
|
#ifndef UNSAFE_OPTIMIZATIONS
|
2017-12-05 19:28:41 +00:00
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
2022-11-29 08:23:39 +00:00
|
|
|
FORCE_INLINE bool Thumbulator::isInvalidROM(uInt32 addr) const
|
2022-03-29 17:18:11 +00:00
|
|
|
{
|
|
|
|
const uInt32 romStart = configuration == ConfigureFor::DPCplus ? 0xc00 : 0x750; // was 0x800
|
|
|
|
|
2022-03-30 12:20:11 +00:00
|
|
|
return addr < romStart || addr >= romSize; // CDFJ+ allows ROM sizes larger than 32 KB
|
2022-03-29 17:18:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
2022-11-29 08:23:39 +00:00
|
|
|
FORCE_INLINE bool Thumbulator::isInvalidRAM(uInt32 addr) const
|
2022-03-29 17:18:11 +00:00
|
|
|
{
|
|
|
|
// Note: addr is already checked for RAM (0x4xxxxxxx)
|
2022-03-30 12:20:11 +00:00
|
|
|
switch(romSize) // CDFJ+ allows more than 8 KB RAM depending on ROM sizes
|
2022-03-29 17:18:11 +00:00
|
|
|
{
|
|
|
|
case 64_KB:
|
|
|
|
case 128_KB:
|
2022-03-30 12:20:11 +00:00
|
|
|
return addr > 0x40003fff; // 16 KB
|
2022-03-29 17:18:11 +00:00
|
|
|
|
|
|
|
case 256_KB:
|
|
|
|
case 512_KB:
|
2022-03-30 12:20:11 +00:00
|
|
|
return addr > 0x40007fff; // 32 KB
|
2022-03-29 17:18:11 +00:00
|
|
|
|
|
|
|
default: // assuming 32 KB
|
2022-03-30 12:20:11 +00:00
|
|
|
return addr > 0x40001fff; // 8 KB
|
2022-03-29 17:18:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
2022-11-29 08:23:39 +00:00
|
|
|
FORCE_INLINE bool Thumbulator::isProtectedRAM(uInt32 addr)
|
2017-12-05 19:28:41 +00:00
|
|
|
{
|
2022-02-24 06:59:29 +00:00
|
|
|
// Protected is within the driver RAM.
|
|
|
|
// For CDF variations parts of the driver RAM are reused to hold the
|
2022-03-29 17:18:11 +00:00
|
|
|
// datastream information, so are not write protected.
|
2022-02-24 06:59:29 +00:00
|
|
|
// Additionally for CDFJ+ the Fast Fetcher offset is not write protected.
|
2022-03-29 17:18:11 +00:00
|
|
|
|
2021-12-11 22:31:10 +00:00
|
|
|
if (addr < 0x40000000) return false;
|
|
|
|
addr -= 0x40000000;
|
2017-12-05 19:28:41 +00:00
|
|
|
|
2022-03-29 17:18:11 +00:00
|
|
|
switch(configuration) {
|
2017-12-05 19:28:41 +00:00
|
|
|
case ConfigureFor::DPCplus:
|
|
|
|
return (addr < 0x0c00) && (addr > 0x0028);
|
|
|
|
|
|
|
|
case ConfigureFor::CDF:
|
2022-03-29 17:18:11 +00:00
|
|
|
return (addr < 0x0800) && (addr > 0x0028) && !((addr >= 0x06e0) && (addr < (0x0e60 + 284)));
|
2017-12-05 19:28:41 +00:00
|
|
|
|
|
|
|
case ConfigureFor::CDF1:
|
2022-03-29 17:18:11 +00:00
|
|
|
return (addr < 0x0800) && (addr > 0x0028) && !((addr >= 0x00a0) && (addr < (0x00a0 + 284)));
|
2017-12-05 19:28:41 +00:00
|
|
|
|
2019-03-19 20:08:35 +00:00
|
|
|
case ConfigureFor::CDFJ:
|
2022-03-29 17:18:11 +00:00
|
|
|
return (addr < 0x0800) && (addr > 0x0028) && !((addr >= 0x0098) && (addr < (0x0098 + 292)));
|
2019-03-19 20:08:35 +00:00
|
|
|
|
2022-02-24 06:59:29 +00:00
|
|
|
case ConfigureFor::CDFJplus:
|
2022-03-29 17:18:11 +00:00
|
|
|
return (addr < 0x0800) && (addr > 0x0028) && !((addr >= 0x0098) && (addr < (0x0098 + 292))) && addr != 0x3E0;
|
2022-02-24 06:59:29 +00:00
|
|
|
|
2017-12-05 19:28:41 +00:00
|
|
|
case ConfigureFor::BUS:
|
2022-03-29 17:18:11 +00:00
|
|
|
return (addr < 0x06d8) && (addr > 0x0028);
|
2017-12-05 19:28:41 +00:00
|
|
|
}
|
2017-12-05 20:20:27 +00:00
|
|
|
|
|
|
|
return false;
|
2017-12-05 19:28:41 +00:00
|
|
|
}
|
2019-02-23 15:09:00 +00:00
|
|
|
#endif
|
2017-12-05 19:28:41 +00:00
|
|
|
|
2011-03-09 14:21:32 +00:00
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
2016-02-27 19:58:20 +00:00
|
|
|
uInt32 Thumbulator::read16(uInt32 addr)
|
2011-01-16 21:23:03 +00:00
|
|
|
{
|
2022-03-29 00:30:26 +00:00
|
|
|
uInt32 data = 0;
|
2019-02-23 15:09:00 +00:00
|
|
|
#ifndef UNSAFE_OPTIMIZATIONS
|
2016-02-27 19:58:20 +00:00
|
|
|
if(addr & 1)
|
2011-05-24 16:04:48 +00:00
|
|
|
fatalError("read16", addr, "abort - misaligned");
|
2019-02-23 15:09:00 +00:00
|
|
|
#endif
|
2021-06-24 14:21:00 +00:00
|
|
|
#ifdef THUMB_STATS
|
2021-02-09 18:47:50 +00:00
|
|
|
++_stats.reads;
|
2019-02-23 15:09:00 +00:00
|
|
|
#endif
|
2011-03-09 14:21:32 +00:00
|
|
|
|
2016-02-27 19:58:20 +00:00
|
|
|
switch(addr & 0xF0000000)
|
2011-03-09 14:21:32 +00:00
|
|
|
{
|
2021-12-11 22:31:10 +00:00
|
|
|
case 0x00000000: //ROM
|
2022-03-29 17:18:11 +00:00
|
|
|
#ifndef UNSAFE_OPTIMIZATIONS
|
|
|
|
if(isInvalidROM(addr))
|
|
|
|
fatalError("read16", addr, "abort - out of range");
|
|
|
|
#endif
|
2016-02-27 19:58:20 +00:00
|
|
|
addr &= ROMADDMASK;
|
|
|
|
addr >>= 1;
|
|
|
|
data = CONV_RAMROM(rom[addr]);
|
2013-07-27 22:28:41 +00:00
|
|
|
DO_DBUG(statusMsg << "read16(" << Base::HEX8 << addr << ")=" << Base::HEX4 << data << endl);
|
2016-02-27 19:58:20 +00:00
|
|
|
return data;
|
2011-03-09 14:21:32 +00:00
|
|
|
|
2021-12-11 22:31:10 +00:00
|
|
|
case 0x40000000: //RAM
|
2022-03-29 17:18:11 +00:00
|
|
|
#ifndef UNSAFE_OPTIMIZATIONS
|
|
|
|
if(isInvalidRAM(addr))
|
|
|
|
fatalError("read16", addr, "abort - out of range");
|
|
|
|
#endif
|
2016-02-27 19:58:20 +00:00
|
|
|
addr &= RAMADDMASK;
|
|
|
|
addr >>= 1;
|
|
|
|
data = CONV_RAMROM(ram[addr]);
|
2013-07-27 22:28:41 +00:00
|
|
|
DO_DBUG(statusMsg << "read16(" << Base::HEX8 << addr << ")=" << Base::HEX4 << data << endl);
|
2016-02-27 19:58:20 +00:00
|
|
|
return data;
|
2011-11-05 22:31:40 +00:00
|
|
|
|
2021-07-02 15:40:30 +00:00
|
|
|
case 0xe0000000: //peripherals
|
|
|
|
#ifdef THUMB_CYCLE_COUNT
|
|
|
|
if(addr == 0xE01FC000) //MAMCR
|
|
|
|
#else
|
|
|
|
default:
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
DO_DBUG(statusMsg << "read32(" << "MAMCR" << addr << ")=" << mamcr << " *");
|
|
|
|
data = static_cast<uInt32>(mamcr);
|
|
|
|
return data;
|
|
|
|
}
|
2011-03-09 14:21:32 +00:00
|
|
|
}
|
2019-02-23 15:09:00 +00:00
|
|
|
#ifndef UNSAFE_OPTIMIZATIONS
|
2011-05-24 16:04:48 +00:00
|
|
|
return fatalError("read16", addr, "abort");
|
2019-02-23 15:09:00 +00:00
|
|
|
#endif
|
2011-01-16 21:23:03 +00:00
|
|
|
}
|
|
|
|
|
2011-03-09 14:21:32 +00:00
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
2016-02-27 19:58:20 +00:00
|
|
|
uInt32 Thumbulator::read32(uInt32 addr)
|
2011-01-16 21:23:03 +00:00
|
|
|
{
|
2019-02-23 15:09:00 +00:00
|
|
|
#ifndef UNSAFE_OPTIMIZATIONS
|
2016-02-27 19:58:20 +00:00
|
|
|
if(addr & 3)
|
2011-05-24 16:04:48 +00:00
|
|
|
fatalError("read32", addr, "abort - misaligned");
|
2019-02-23 15:09:00 +00:00
|
|
|
#endif
|
2011-03-09 14:21:32 +00:00
|
|
|
|
2022-03-29 00:30:26 +00:00
|
|
|
uInt32 data = 0;
|
2016-02-27 19:58:20 +00:00
|
|
|
switch(addr & 0xF0000000)
|
2011-03-09 14:21:32 +00:00
|
|
|
{
|
2021-12-11 22:31:10 +00:00
|
|
|
case 0x00000000: //ROM
|
2022-03-29 17:18:11 +00:00
|
|
|
#ifndef UNSAFE_OPTIMIZATIONS
|
|
|
|
if(isInvalidROM(addr))
|
|
|
|
fatalError("read32", addr, "abort - out of range");
|
|
|
|
#endif
|
|
|
|
data = read16(addr+0);
|
2022-08-21 22:03:08 +00:00
|
|
|
data |= read16(addr+2) << 16;
|
2022-03-29 17:18:11 +00:00
|
|
|
DO_DBUG(statusMsg << "read32(" << Base::HEX8 << addr << ")=" << Base::HEX8 << data << endl);
|
|
|
|
return data;
|
|
|
|
|
2021-12-11 22:31:10 +00:00
|
|
|
case 0x40000000: //RAM
|
2022-03-29 17:18:11 +00:00
|
|
|
#ifndef UNSAFE_OPTIMIZATIONS
|
|
|
|
if(isInvalidRAM(addr))
|
|
|
|
fatalError("read32", addr, "abort - out of range");
|
|
|
|
#endif
|
2016-02-27 19:58:20 +00:00
|
|
|
data = read16(addr+0);
|
2022-08-21 22:03:08 +00:00
|
|
|
data |= read16(addr+2) << 16;
|
2013-07-27 22:28:41 +00:00
|
|
|
DO_DBUG(statusMsg << "read32(" << Base::HEX8 << addr << ")=" << Base::HEX8 << data << endl);
|
2016-02-27 19:58:20 +00:00
|
|
|
return data;
|
|
|
|
|
2019-02-23 15:09:00 +00:00
|
|
|
#ifndef UNSAFE_OPTIMIZATIONS
|
2016-02-27 19:58:20 +00:00
|
|
|
case 0xE0000000:
|
2019-02-23 15:09:00 +00:00
|
|
|
#else
|
|
|
|
default:
|
|
|
|
#endif
|
2016-02-27 19:58:20 +00:00
|
|
|
{
|
2022-08-20 18:02:15 +00:00
|
|
|
switch(addr) // NOLINT (FIXME: missing default)
|
2016-02-27 19:58:20 +00:00
|
|
|
{
|
2021-07-01 13:40:49 +00:00
|
|
|
#ifdef THUMB_CYCLE_COUNT
|
|
|
|
case 0xE01FC000: //MAMCR
|
|
|
|
DO_DBUG(statusMsg << "read32(" << "MAMCR" << addr << ")=" << mamcr << " *");
|
|
|
|
data = static_cast<uInt32>(mamcr);
|
|
|
|
return data;
|
|
|
|
#endif
|
|
|
|
|
2021-06-13 10:18:44 +00:00
|
|
|
#ifdef TIMER_0
|
|
|
|
case 0xE0004004: // T0TCR - Timer 0 Control Register
|
|
|
|
data = T0TCR;
|
|
|
|
return data;
|
|
|
|
|
|
|
|
case 0xE0004008: // T0TC - Timer 0 Counter
|
2021-06-24 14:21:00 +00:00
|
|
|
#ifdef THUMB_CYCLE_COUNT
|
2021-07-01 13:40:49 +00:00
|
|
|
if(T0TCR & 1)
|
|
|
|
// timer is counting
|
2022-08-20 18:02:15 +00:00
|
|
|
data = T0TC + (tim0Total + (_totalCycles - tim0Start)) * _armCyclesFactor; // NOLINT
|
2021-07-01 13:40:49 +00:00
|
|
|
else
|
|
|
|
// timer is disabled
|
2022-08-20 18:02:15 +00:00
|
|
|
data = T0TC + tim0Total * _armCyclesFactor; // NOLINT
|
2021-06-24 14:21:00 +00:00
|
|
|
#else
|
2022-08-20 18:02:15 +00:00
|
|
|
data = T0TC; // NOLINT
|
2021-06-24 14:21:00 +00:00
|
|
|
#endif
|
2021-06-13 10:18:44 +00:00
|
|
|
break;
|
|
|
|
#endif
|
2017-03-21 02:55:27 +00:00
|
|
|
case 0xE0008004: // T1TCR - Timer 1 Control Register
|
|
|
|
data = T1TCR;
|
|
|
|
return data;
|
2017-03-24 20:32:08 +00:00
|
|
|
|
2017-03-21 02:55:27 +00:00
|
|
|
case 0xE0008008: // T1TC - Timer 1 Counter
|
2021-06-24 14:21:00 +00:00
|
|
|
#ifdef THUMB_CYCLE_COUNT
|
2021-07-01 13:40:49 +00:00
|
|
|
if(T1TCR & 1)
|
|
|
|
// timer is counting
|
|
|
|
data = T1TC + (tim1Total + (_totalCycles - tim1Start)) * _armCyclesFactor;
|
|
|
|
else
|
|
|
|
// timer is disabled
|
|
|
|
data = T1TC + tim1Total * _armCyclesFactor;
|
2021-06-24 14:21:00 +00:00
|
|
|
#else
|
|
|
|
data = T1TC;
|
|
|
|
#endif
|
2017-03-21 02:55:27 +00:00
|
|
|
return data;
|
2017-03-24 20:32:08 +00:00
|
|
|
|
2016-02-27 19:58:20 +00:00
|
|
|
case 0xE000E010:
|
|
|
|
data = systick_ctrl;
|
|
|
|
systick_ctrl &= (~0x00010000);
|
|
|
|
return data;
|
|
|
|
|
|
|
|
case 0xE000E014:
|
|
|
|
data = systick_reload;
|
|
|
|
return data;
|
|
|
|
|
|
|
|
case 0xE000E018:
|
|
|
|
data = systick_count;
|
|
|
|
return data;
|
|
|
|
|
2019-02-23 15:09:00 +00:00
|
|
|
#ifndef UNSAFE_OPTIMIZATIONS
|
2016-02-27 19:58:20 +00:00
|
|
|
case 0xE000E01C:
|
2019-02-23 15:09:00 +00:00
|
|
|
#else
|
|
|
|
default:
|
|
|
|
#endif
|
2016-02-27 19:58:20 +00:00
|
|
|
data = systick_calibrate;
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
}
|
2011-03-09 14:21:32 +00:00
|
|
|
}
|
2019-02-23 15:09:00 +00:00
|
|
|
#ifndef UNSAFE_OPTIMIZATIONS
|
2011-05-24 16:04:48 +00:00
|
|
|
return fatalError("read32", addr, "abort");
|
2019-02-23 15:09:00 +00:00
|
|
|
#endif
|
2011-01-16 21:23:03 +00:00
|
|
|
}
|
|
|
|
|
2011-03-09 14:21:32 +00:00
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
2022-11-29 09:53:31 +00:00
|
|
|
FORCE_INLINE uInt32 Thumbulator::read_register(uInt32 reg)
|
2011-01-16 21:23:03 +00:00
|
|
|
{
|
2016-02-27 19:58:20 +00:00
|
|
|
reg &= 0xF;
|
2011-03-09 14:21:32 +00:00
|
|
|
|
2016-02-27 19:58:20 +00:00
|
|
|
uInt32 data = reg_norm[reg];
|
|
|
|
DO_DBUG(statusMsg << "read_register(" << dec << reg << ")=" << Base::HEX8 << data << endl);
|
2019-02-25 10:36:42 +00:00
|
|
|
#ifndef UNSAFE_OPTIMIZATIONS
|
2016-02-27 19:58:20 +00:00
|
|
|
if(reg == 15)
|
2011-03-09 14:21:32 +00:00
|
|
|
{
|
2016-02-27 19:58:20 +00:00
|
|
|
if(data & 1)
|
|
|
|
{
|
|
|
|
DO_DBUG(statusMsg << "pc has lsbit set 0x" << Base::HEX8 << data << endl);
|
2022-11-29 09:53:31 +00:00
|
|
|
data &= ~1;
|
2016-02-27 19:58:20 +00:00
|
|
|
}
|
2011-03-09 14:21:32 +00:00
|
|
|
}
|
2019-02-25 10:36:42 +00:00
|
|
|
#endif
|
2016-02-27 19:58:20 +00:00
|
|
|
return data;
|
2011-01-16 21:23:03 +00:00
|
|
|
}
|
|
|
|
|
2011-03-09 14:21:32 +00:00
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
2022-11-29 09:53:31 +00:00
|
|
|
FORCE_INLINE void Thumbulator::write_register(uInt32 reg, uInt32 data, bool isFlowBreak)
|
2011-01-16 21:23:03 +00:00
|
|
|
{
|
2016-02-27 19:58:20 +00:00
|
|
|
reg &= 0xF;
|
2011-03-09 14:21:32 +00:00
|
|
|
|
2013-07-27 22:28:41 +00:00
|
|
|
DO_DBUG(statusMsg << "write_register(" << dec << reg << "," << Base::HEX8 << data << ")" << endl);
|
2019-02-25 10:36:42 +00:00
|
|
|
//#ifndef UNSAFE_OPTIMIZATIONS // this fails when combined with read_register UNSAFE_OPTIMIZATIONS
|
2021-06-15 15:31:08 +00:00
|
|
|
if(reg == 15)
|
|
|
|
{
|
|
|
|
data &= ~1;
|
2021-06-24 14:21:00 +00:00
|
|
|
if(isFlowBreak)
|
|
|
|
{
|
2021-07-08 13:46:41 +00:00
|
|
|
#ifdef THUMB_STATS
|
|
|
|
++_stats.taken;
|
|
|
|
#endif
|
2021-06-24 14:21:00 +00:00
|
|
|
// dummy fetch + fill the pipeline
|
2021-07-08 13:46:41 +00:00
|
|
|
//INC_N_CYCLES(reg_norm[15] - 2, AccessType::prefetch);
|
|
|
|
//INC_S_CYCLES(data - 2, AccessType::branch);
|
|
|
|
INC_N_CYCLES(reg_norm[15] + 4, AccessType::prefetch);
|
|
|
|
INC_S_CYCLES(data, AccessType::branch);
|
2021-06-24 14:21:00 +00:00
|
|
|
}
|
2021-06-15 15:31:08 +00:00
|
|
|
}
|
2019-02-25 10:36:42 +00:00
|
|
|
//#endif
|
2016-02-27 19:58:20 +00:00
|
|
|
reg_norm[reg] = data;
|
2011-01-16 21:23:03 +00:00
|
|
|
}
|
|
|
|
|
2011-03-09 14:21:32 +00:00
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
2022-11-28 21:12:48 +00:00
|
|
|
void Thumbulator::do_cvflag(uInt32 a, uInt32 b, uInt32 c)
|
2011-01-16 21:23:03 +00:00
|
|
|
{
|
2022-03-29 00:30:26 +00:00
|
|
|
uInt32 rc = (a & 0x7FFFFFFF) + (b & 0x7FFFFFFF) + c; //carry in
|
2016-02-27 19:58:20 +00:00
|
|
|
rc >>= 31; //carry in in lsbit
|
2022-11-29 09:53:31 +00:00
|
|
|
a >>= 31;
|
|
|
|
b >>= 31;
|
|
|
|
uInt32 rd = (rc & 1) + (a & 1) + (b & 1); //carry out
|
2016-02-27 19:58:20 +00:00
|
|
|
rd >>= 1; //carry out in lsbit
|
2011-01-16 21:23:03 +00:00
|
|
|
|
2022-11-28 21:12:48 +00:00
|
|
|
vFlag = (rc ^ rd) & 1; //if carry in != carry out then signed overflow
|
2011-01-16 21:23:03 +00:00
|
|
|
|
2022-11-29 09:53:31 +00:00
|
|
|
rc += a + b; //carry out
|
2022-11-28 21:12:48 +00:00
|
|
|
cFlag = rc & 2;
|
2011-01-16 21:23:03 +00:00
|
|
|
}
|
|
|
|
|
2011-03-09 14:21:32 +00:00
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
2022-11-27 12:10:02 +00:00
|
|
|
Thumbulator::Op Thumbulator::decodeInstructionWord(uint16_t inst, uInt32 pc) {
|
2021-06-15 15:31:08 +00:00
|
|
|
//ADC add with carry
|
2019-02-22 19:11:19 +00:00
|
|
|
if((inst & 0xFFC0) == 0x4140) return Op::adc;
|
|
|
|
|
2011-03-09 14:21:32 +00:00
|
|
|
//ADD(1) small immediate two registers
|
2019-02-22 19:11:19 +00:00
|
|
|
if((inst & 0xFE00) == 0x1C00 && (inst >> 6) & 0x7) return Op::add1;
|
|
|
|
|
2011-03-09 14:21:32 +00:00
|
|
|
//ADD(2) big immediate one register
|
2019-02-22 19:11:19 +00:00
|
|
|
if((inst & 0xF800) == 0x3000) return Op::add2;
|
|
|
|
|
2011-03-09 14:21:32 +00:00
|
|
|
//ADD(3) three registers
|
2019-02-22 19:11:19 +00:00
|
|
|
if((inst & 0xFE00) == 0x1800) return Op::add3;
|
|
|
|
|
2011-03-09 14:21:32 +00:00
|
|
|
//ADD(4) two registers one or both high no flags
|
2019-02-22 19:11:19 +00:00
|
|
|
if((inst & 0xFF00) == 0x4400) return Op::add4;
|
|
|
|
|
2011-03-09 14:21:32 +00:00
|
|
|
//ADD(5) rd = pc plus immediate
|
2019-02-22 19:11:19 +00:00
|
|
|
if((inst & 0xF800) == 0xA000) return Op::add5;
|
|
|
|
|
2011-03-09 14:21:32 +00:00
|
|
|
//ADD(6) rd = sp plus immediate
|
2019-02-22 19:11:19 +00:00
|
|
|
if((inst & 0xF800) == 0xA800) return Op::add6;
|
|
|
|
|
2011-03-09 14:21:32 +00:00
|
|
|
//ADD(7) sp plus immediate
|
2019-02-22 19:11:19 +00:00
|
|
|
if((inst & 0xFF80) == 0xB000) return Op::add7;
|
|
|
|
|
2011-03-09 14:21:32 +00:00
|
|
|
//AND
|
2019-02-22 19:11:19 +00:00
|
|
|
if((inst & 0xFFC0) == 0x4000) return Op::and_;
|
|
|
|
|
2011-03-09 14:21:32 +00:00
|
|
|
//ASR(1) two register immediate
|
2019-02-22 19:11:19 +00:00
|
|
|
if((inst & 0xF800) == 0x1000) return Op::asr1;
|
|
|
|
|
2011-03-09 14:21:32 +00:00
|
|
|
//ASR(2) two register
|
2019-02-22 19:11:19 +00:00
|
|
|
if((inst & 0xFFC0) == 0x4100) return Op::asr2;
|
|
|
|
|
2022-11-27 12:10:02 +00:00
|
|
|
//B(1) conditional branch, decoded into its variants
|
|
|
|
if((inst & 0xF000) == 0xD000)
|
|
|
|
{
|
|
|
|
const uInt32 op = (inst >> 8) & 0xF;
|
|
|
|
uInt32 rb = inst & 0xFF;
|
|
|
|
|
|
|
|
if(rb & 0x80)
|
|
|
|
rb |= (~0U) << 8;
|
|
|
|
rb <<= 1;
|
|
|
|
rb += pc;
|
|
|
|
rb += 2;
|
|
|
|
decodedParam[pc / 2] = rb + 4;
|
|
|
|
|
|
|
|
switch(op)
|
|
|
|
{
|
|
|
|
case 0x0: //b eq z set
|
|
|
|
return Op::beq;
|
|
|
|
|
|
|
|
case 0x1: //b ne z clear
|
|
|
|
return Op::bne;
|
|
|
|
|
|
|
|
case 0x2: //b cs c set
|
|
|
|
return Op::bcs;
|
|
|
|
|
|
|
|
case 0x3: //b cc c clear
|
|
|
|
return Op::bcc;
|
|
|
|
|
|
|
|
case 0x4: //b mi n set
|
|
|
|
return Op::bmi;
|
|
|
|
|
|
|
|
case 0x5: //b pl n clear
|
|
|
|
return Op::bpl;
|
|
|
|
|
|
|
|
case 0x6: //b vs v set
|
|
|
|
return Op::bvs;
|
|
|
|
|
|
|
|
case 0x7: //b vc v clear
|
|
|
|
return Op::bvc;
|
|
|
|
|
|
|
|
case 0x8: //b hi c set z clear
|
|
|
|
return Op::bhi;
|
|
|
|
|
|
|
|
case 0x9: //b ls c clear or z set
|
|
|
|
return Op::bls;
|
|
|
|
|
|
|
|
case 0xA: //b ge N == V
|
|
|
|
return Op::bge;
|
|
|
|
|
|
|
|
case 0xB: //b lt N != V
|
|
|
|
return Op::blt;
|
|
|
|
|
|
|
|
case 0xC: //b gt Z==0 and N == V
|
|
|
|
return Op::bgt;
|
|
|
|
|
|
|
|
case 0xD: //b le Z==1 or N != V
|
|
|
|
return Op::ble;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return Op::invalid;
|
|
|
|
}
|
|
|
|
}
|
2019-02-22 19:11:19 +00:00
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//B(2) unconditional branch
|
2022-11-27 12:10:02 +00:00
|
|
|
if((inst & 0xF800) == 0xE000)
|
|
|
|
{
|
|
|
|
uInt32 rb = (inst >> 0) & 0x7FF;
|
|
|
|
|
|
|
|
if(rb & (1 << 10))
|
|
|
|
rb |= (~0U) << 11;
|
|
|
|
rb <<= 1;
|
|
|
|
rb += pc;
|
|
|
|
rb += 2;
|
|
|
|
decodedParam[pc / 2] = rb + 4;
|
|
|
|
|
|
|
|
return Op::b2;
|
|
|
|
}
|
2019-02-22 19:11:19 +00:00
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//BIC
|
2019-02-22 19:11:19 +00:00
|
|
|
if((inst & 0xFFC0) == 0x4380) return Op::bic;
|
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//BKPT
|
2019-02-22 19:11:19 +00:00
|
|
|
if((inst & 0xFF00) == 0xBE00) return Op::bkpt;
|
|
|
|
|
2022-11-27 12:10:02 +00:00
|
|
|
//BL/BLX(1) decoded into its variants
|
|
|
|
if((inst & 0xE000) == 0xE000)
|
|
|
|
{
|
|
|
|
if((inst & 0x1800) == 0x1000) return Op::bl;
|
|
|
|
else if((inst & 0x1800) == 0x1800) return Op::blx_thumb;
|
|
|
|
else if((inst & 0x1800) == 0x0800) return Op::blx_arm;
|
|
|
|
return Op::invalid;
|
|
|
|
}
|
2019-02-22 19:11:19 +00:00
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//BLX(2)
|
2019-02-22 19:11:19 +00:00
|
|
|
if((inst & 0xFF87) == 0x4780) return Op::blx2;
|
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//BX
|
2019-02-22 19:11:19 +00:00
|
|
|
if((inst & 0xFF87) == 0x4700) return Op::bx;
|
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//CMN
|
2019-02-22 19:11:19 +00:00
|
|
|
if((inst & 0xFFC0) == 0x42C0) return Op::cmn;
|
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//CMP(1) compare immediate
|
2019-02-22 19:11:19 +00:00
|
|
|
if((inst & 0xF800) == 0x2800) return Op::cmp1;
|
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//CMP(2) compare register
|
2019-02-22 19:11:19 +00:00
|
|
|
if((inst & 0xFFC0) == 0x4280) return Op::cmp2;
|
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//CMP(3) compare high register
|
2019-02-22 19:11:19 +00:00
|
|
|
if((inst & 0xFF00) == 0x4500) return Op::cmp3;
|
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//CPS
|
2019-02-22 19:11:19 +00:00
|
|
|
if((inst & 0xFFE8) == 0xB660) return Op::cps;
|
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//CPY copy high register
|
2019-02-22 19:11:19 +00:00
|
|
|
if((inst & 0xFFC0) == 0x4600) return Op::cpy;
|
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//EOR
|
2019-02-22 19:11:19 +00:00
|
|
|
if((inst & 0xFFC0) == 0x4040) return Op::eor;
|
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//LDMIA
|
2019-02-22 19:11:19 +00:00
|
|
|
if((inst & 0xF800) == 0xC800) return Op::ldmia;
|
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//LDR(1) two register immediate
|
2019-02-22 19:11:19 +00:00
|
|
|
if((inst & 0xF800) == 0x6800) return Op::ldr1;
|
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//LDR(2) three register
|
2019-02-22 19:11:19 +00:00
|
|
|
if((inst & 0xFE00) == 0x5800) return Op::ldr2;
|
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//LDR(3)
|
2019-02-22 19:11:19 +00:00
|
|
|
if((inst & 0xF800) == 0x4800) return Op::ldr3;
|
|
|
|
|
|
|
|
//LDR(4)
|
|
|
|
if((inst & 0xF800) == 0x9800) return Op::ldr4;
|
2011-04-16 16:53:41 +00:00
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//LDRB(1)
|
2019-02-22 19:11:19 +00:00
|
|
|
if((inst & 0xF800) == 0x7800) return Op::ldrb1;
|
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//LDRB(2)
|
2019-02-22 19:11:19 +00:00
|
|
|
if((inst & 0xFE00) == 0x5C00) return Op::ldrb2;
|
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//LDRH(1)
|
2019-02-22 19:11:19 +00:00
|
|
|
if((inst & 0xF800) == 0x8800) return Op::ldrh1;
|
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//LDRH(2)
|
2019-02-22 19:11:19 +00:00
|
|
|
if((inst & 0xFE00) == 0x5A00) return Op::ldrh2;
|
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//LDRSB
|
2019-02-22 19:11:19 +00:00
|
|
|
if((inst & 0xFE00) == 0x5600) return Op::ldrsb;
|
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//LDRSH
|
2019-02-22 19:11:19 +00:00
|
|
|
if((inst & 0xFE00) == 0x5E00) return Op::ldrsh;
|
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//LSL(1)
|
2019-02-22 19:11:19 +00:00
|
|
|
if((inst & 0xF800) == 0x0000) return Op::lsl1;
|
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//LSL(2) two register
|
2019-02-22 19:11:19 +00:00
|
|
|
if((inst & 0xFFC0) == 0x4080) return Op::lsl2;
|
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//LSR(1) two register immediate
|
2019-02-22 19:11:19 +00:00
|
|
|
if((inst & 0xF800) == 0x0800) return Op::lsr1;
|
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//LSR(2) two register
|
2019-02-22 19:11:19 +00:00
|
|
|
if((inst & 0xFFC0) == 0x40C0) return Op::lsr2;
|
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//MOV(1) immediate
|
2019-02-22 19:11:19 +00:00
|
|
|
if((inst & 0xF800) == 0x2000) return Op::mov1;
|
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//MOV(2) two low registers
|
2019-02-22 19:11:19 +00:00
|
|
|
if((inst & 0xFFC0) == 0x1C00) return Op::mov2;
|
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//MOV(3)
|
2019-02-22 19:11:19 +00:00
|
|
|
if((inst & 0xFF00) == 0x4600) return Op::mov3;
|
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//MUL
|
2019-02-22 19:11:19 +00:00
|
|
|
if((inst & 0xFFC0) == 0x4340) return Op::mul;
|
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//MVN
|
2019-02-22 19:11:19 +00:00
|
|
|
if((inst & 0xFFC0) == 0x43C0) return Op::mvn;
|
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//NEG
|
2019-02-22 19:11:19 +00:00
|
|
|
if((inst & 0xFFC0) == 0x4240) return Op::neg;
|
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//ORR
|
2019-02-22 19:11:19 +00:00
|
|
|
if((inst & 0xFFC0) == 0x4300) return Op::orr;
|
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//POP
|
2019-02-22 19:11:19 +00:00
|
|
|
if((inst & 0xFE00) == 0xBC00) return Op::pop;
|
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//PUSH
|
2019-02-22 19:11:19 +00:00
|
|
|
if((inst & 0xFE00) == 0xB400) return Op::push;
|
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//REV
|
2019-02-22 19:11:19 +00:00
|
|
|
if((inst & 0xFFC0) == 0xBA00) return Op::rev;
|
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//REV16
|
2019-02-22 19:11:19 +00:00
|
|
|
if((inst & 0xFFC0) == 0xBA40) return Op::rev16;
|
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//REVSH
|
2019-02-22 19:11:19 +00:00
|
|
|
if((inst & 0xFFC0) == 0xBAC0) return Op::revsh;
|
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//ROR
|
2019-02-22 19:11:19 +00:00
|
|
|
if((inst & 0xFFC0) == 0x41C0) return Op::ror;
|
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//SBC
|
2019-02-22 19:11:19 +00:00
|
|
|
if((inst & 0xFFC0) == 0x4180) return Op::sbc;
|
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//SETEND
|
2019-02-22 19:11:19 +00:00
|
|
|
if((inst & 0xFFF7) == 0xB650) return Op::setend;
|
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//STMIA
|
2019-02-22 19:11:19 +00:00
|
|
|
if((inst & 0xF800) == 0xC000) return Op::stmia;
|
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//STR(1)
|
2019-02-22 19:11:19 +00:00
|
|
|
if((inst & 0xF800) == 0x6000) return Op::str1;
|
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//STR(2)
|
2019-02-22 19:11:19 +00:00
|
|
|
if((inst & 0xFE00) == 0x5000) return Op::str2;
|
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//STR(3)
|
2019-02-22 19:11:19 +00:00
|
|
|
if((inst & 0xF800) == 0x9000) return Op::str3;
|
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//STRB(1)
|
2019-02-22 19:11:19 +00:00
|
|
|
if((inst & 0xF800) == 0x7000) return Op::strb1;
|
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//STRB(2)
|
2019-02-22 19:11:19 +00:00
|
|
|
if((inst & 0xFE00) == 0x5400) return Op::strb2;
|
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//STRH(1)
|
2019-02-22 19:11:19 +00:00
|
|
|
if((inst & 0xF800) == 0x8000) return Op::strh1;
|
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//STRH(2)
|
2019-02-22 19:11:19 +00:00
|
|
|
if((inst & 0xFE00) == 0x5200) return Op::strh2;
|
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//SUB(1)
|
2019-02-22 19:11:19 +00:00
|
|
|
if((inst & 0xFE00) == 0x1E00) return Op::sub1;
|
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//SUB(2)
|
2019-02-22 19:11:19 +00:00
|
|
|
if((inst & 0xF800) == 0x3800) return Op::sub2;
|
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//SUB(3)
|
2019-02-22 19:11:19 +00:00
|
|
|
if((inst & 0xFE00) == 0x1A00) return Op::sub3;
|
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//SUB(4)
|
2019-02-22 19:11:19 +00:00
|
|
|
if((inst & 0xFF80) == 0xB080) return Op::sub4;
|
|
|
|
|
2021-06-24 14:21:00 +00:00
|
|
|
//SWI SoftWare Interupt
|
2019-02-22 19:11:19 +00:00
|
|
|
if((inst & 0xFF00) == 0xDF00) return Op::swi;
|
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//SXTB
|
2019-02-22 19:11:19 +00:00
|
|
|
if((inst & 0xFFC0) == 0xB240) return Op::sxtb;
|
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//SXTH
|
2019-02-22 19:11:19 +00:00
|
|
|
if((inst & 0xFFC0) == 0xB200) return Op::sxth;
|
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//TST
|
2019-02-22 19:11:19 +00:00
|
|
|
if((inst & 0xFFC0) == 0x4200) return Op::tst;
|
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//UXTB
|
2019-02-22 19:11:19 +00:00
|
|
|
if((inst & 0xFFC0) == 0xB2C0) return Op::uxtb;
|
|
|
|
|
2021-06-15 15:31:08 +00:00
|
|
|
//UXTH Zero extend Halfword
|
2019-02-22 19:11:19 +00:00
|
|
|
if((inst & 0xFFC0) == 0xB280) return Op::uxth;
|
|
|
|
|
|
|
|
return Op::invalid;
|
2019-02-19 23:32:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
2022-11-29 08:23:39 +00:00
|
|
|
FORCE_INLINE int Thumbulator::execute() // NOLINT (readability-function-size)
|
2019-02-19 23:32:48 +00:00
|
|
|
{
|
2022-11-27 12:10:02 +00:00
|
|
|
uInt32 sp, inst, ra, rb, rc, rm, rd, rn, rs; // NOLINT
|
2019-02-19 23:32:48 +00:00
|
|
|
|
2022-08-20 18:02:15 +00:00
|
|
|
uInt32 pc = read_register(15);
|
2019-02-19 23:32:48 +00:00
|
|
|
|
2022-03-29 00:30:26 +00:00
|
|
|
const uInt32 instructionPtr = pc - 2;
|
2019-02-19 23:32:48 +00:00
|
|
|
inst = fetch16(instructionPtr);
|
|
|
|
|
|
|
|
pc += 2;
|
2021-06-24 14:21:00 +00:00
|
|
|
write_register(15, pc, false);
|
2019-02-19 23:32:48 +00:00
|
|
|
DO_DISS(statusMsg << Base::HEX8 << (pc-5) << ": " << Base::HEX4 << inst << " ");
|
|
|
|
|
2019-02-23 15:09:00 +00:00
|
|
|
#ifndef UNSAFE_OPTIMIZATIONS
|
2021-06-24 14:21:00 +00:00
|
|
|
++_stats.instructions;
|
2019-02-23 15:09:00 +00:00
|
|
|
#endif
|
2019-02-19 23:32:48 +00:00
|
|
|
|
2022-08-20 18:02:15 +00:00
|
|
|
Op decodedOp{};
|
2019-02-23 18:02:25 +00:00
|
|
|
#ifndef UNSAFE_OPTIMIZATIONS
|
2021-12-11 22:31:10 +00:00
|
|
|
if ((instructionPtr & 0xF0000000) == 0 && instructionPtr < romSize)
|
|
|
|
decodedOp = decodedRom[instructionPtr >> 1];
|
2019-02-23 11:05:53 +00:00
|
|
|
else
|
2022-11-27 12:10:02 +00:00
|
|
|
decodedOp = decodeInstructionWord(inst, pc);
|
2019-02-23 18:02:25 +00:00
|
|
|
#else
|
2019-02-24 08:23:57 +00:00
|
|
|
decodedOp = decodedRom[(instructionPtr & ROMADDMASK) >> 1];
|
2019-02-23 18:02:25 +00:00
|
|
|
#endif
|
2019-02-19 23:32:48 +00:00
|
|
|
|
2021-06-15 15:31:08 +00:00
|
|
|
#ifdef COUNT_OPS
|
2022-12-01 22:01:31 +00:00
|
|
|
++opCount[static_cast<int>(decodedOp)];
|
2021-06-15 15:31:08 +00:00
|
|
|
#endif
|
2019-02-22 19:11:19 +00:00
|
|
|
switch (decodedOp) {
|
2019-02-19 23:32:48 +00:00
|
|
|
//ADC
|
|
|
|
case Op::adc: {
|
2019-02-22 19:11:19 +00:00
|
|
|
rd = (inst >> 0) & 0x07;
|
|
|
|
rm = (inst >> 3) & 0x07;
|
2019-02-19 23:32:48 +00:00
|
|
|
DO_DISS(statusMsg << "adc r" << dec << rd << ",r" << dec << rm << endl);
|
|
|
|
ra = read_register(rd);
|
|
|
|
rb = read_register(rm);
|
|
|
|
rc = ra + rb;
|
2022-11-28 21:12:48 +00:00
|
|
|
if(cFlag)
|
2019-02-19 23:32:48 +00:00
|
|
|
++rc;
|
|
|
|
write_register(rd, rc);
|
2022-11-28 21:12:48 +00:00
|
|
|
do_znflags(rc);
|
|
|
|
if(cFlag) do_cvflag(ra, rb, 1);
|
|
|
|
else do_cvflag(ra, rb, 0);
|
2019-02-19 23:32:48 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//ADD(1) small immediate two registers
|
|
|
|
case Op::add1: {
|
2019-02-22 19:11:19 +00:00
|
|
|
rd = (inst >> 0) & 0x7;
|
|
|
|
rn = (inst >> 3) & 0x7;
|
|
|
|
rb = (inst >> 6) & 0x7;
|
2019-02-19 23:32:48 +00:00
|
|
|
if(rb)
|
2011-04-16 16:53:41 +00:00
|
|
|
{
|
2019-02-19 23:32:48 +00:00
|
|
|
DO_DISS(statusMsg << "adds r" << dec << rd << ",r" << dec << rn << ","
|
|
|
|
<< "#0x" << Base::HEX2 << rb << endl);
|
|
|
|
ra = read_register(rn);
|
|
|
|
rc = ra + rb;
|
|
|
|
//fprintf(stderr,"0x%08X = 0x%08X + 0x%08X\n",rc,ra,rb);
|
|
|
|
write_register(rd, rc);
|
2022-11-28 21:12:48 +00:00
|
|
|
do_znflags(rc);
|
|
|
|
do_cvflag(ra, rb, 0);
|
2019-02-19 23:32:48 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
//this is a mov
|
2011-04-16 16:53:41 +00:00
|
|
|
}
|
2019-02-19 23:32:48 +00:00
|
|
|
break;
|
2011-01-16 21:23:03 +00:00
|
|
|
}
|
2019-02-19 23:32:48 +00:00
|
|
|
|
|
|
|
//ADD(2) big immediate one register
|
|
|
|
case Op::add2: {
|
2019-02-22 19:11:19 +00:00
|
|
|
rb = (inst >> 0) & 0xFF;
|
|
|
|
rd = (inst >> 8) & 0x7;
|
2019-02-19 23:32:48 +00:00
|
|
|
DO_DISS(statusMsg << "adds r" << dec << rd << ",#0x" << Base::HEX2 << rb << endl);
|
|
|
|
ra = read_register(rd);
|
|
|
|
rc = ra + rb;
|
|
|
|
write_register(rd, rc);
|
2022-11-28 21:12:48 +00:00
|
|
|
do_znflags(rc);
|
|
|
|
do_cvflag(ra, rb, 0);
|
2019-02-19 23:32:48 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//ADD(3) three registers
|
|
|
|
case Op::add3: {
|
2019-02-22 19:11:19 +00:00
|
|
|
rd = (inst >> 0) & 0x7;
|
|
|
|
rn = (inst >> 3) & 0x7;
|
|
|
|
rm = (inst >> 6) & 0x7;
|
2019-02-19 23:32:48 +00:00
|
|
|
DO_DISS(statusMsg << "adds r" << dec << rd << ",r" << dec << rn << ",r" << rm << endl);
|
|
|
|
ra = read_register(rn);
|
|
|
|
rb = read_register(rm);
|
|
|
|
rc = ra + rb;
|
|
|
|
write_register(rd, rc);
|
2022-11-28 21:12:48 +00:00
|
|
|
do_znflags(rc);
|
|
|
|
do_cvflag(ra, rb, 0);
|
2019-02-19 23:32:48 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//ADD(4) two registers one or both high no flags
|
|
|
|
case Op::add4: {
|
2019-02-22 19:11:19 +00:00
|
|
|
if((inst >> 6) & 3)
|
|
|
|
{
|
|
|
|
//UNPREDICTABLE
|
|
|
|
}
|
|
|
|
rd = (inst >> 0) & 0x7;
|
|
|
|
rd |= (inst >> 4) & 0x8;
|
|
|
|
rm = (inst >> 3) & 0xF;
|
|
|
|
DO_DISS(statusMsg << "add r" << dec << rd << ",r" << dec << rm << endl);
|
2019-02-19 23:32:48 +00:00
|
|
|
ra = read_register(rd);
|
|
|
|
rb = read_register(rm);
|
|
|
|
rc = ra + rb;
|
|
|
|
if(rd == 15)
|
|
|
|
{
|
2019-02-23 15:09:00 +00:00
|
|
|
#ifndef UNSAFE_OPTIMIZATIONS
|
2019-02-19 23:32:48 +00:00
|
|
|
if((rc & 1) == 0)
|
|
|
|
fatalError("add pc", pc, rc, " produced an arm address");
|
2019-02-23 15:09:00 +00:00
|
|
|
#endif
|
2022-11-28 21:12:48 +00:00
|
|
|
//rc &= ~1; //write_register may f this as well
|
2019-02-19 23:32:48 +00:00
|
|
|
rc += 2; //The program counter is special
|
|
|
|
}
|
|
|
|
//fprintf(stderr,"0x%08X = 0x%08X + 0x%08X\n",rc,ra,rb);
|
|
|
|
write_register(rd, rc);
|
|
|
|
return 0;
|
2011-01-16 21:23:03 +00:00
|
|
|
}
|
2016-02-27 19:58:20 +00:00
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//ADD(5) rd = pc plus immediate
|
|
|
|
case Op::add5: {
|
2019-02-22 19:11:19 +00:00
|
|
|
rb = (inst >> 0) & 0xFF;
|
|
|
|
rd = (inst >> 8) & 0x7;
|
|
|
|
rb <<= 2;
|
2019-02-19 23:32:48 +00:00
|
|
|
DO_DISS(statusMsg << "add r" << dec << rd << ",PC,#0x" << Base::HEX2 << rb << endl);
|
|
|
|
ra = read_register(15);
|
2019-12-28 04:15:09 +00:00
|
|
|
rc = (ra & (~3U)) + rb;
|
2019-02-19 23:32:48 +00:00
|
|
|
write_register(rd, rc);
|
|
|
|
return 0;
|
|
|
|
}
|
2011-01-16 21:23:03 +00:00
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//ADD(6) rd = sp plus immediate
|
|
|
|
case Op::add6: {
|
2019-02-22 19:11:19 +00:00
|
|
|
rb = (inst >> 0) & 0xFF;
|
|
|
|
rd = (inst >> 8) & 0x7;
|
|
|
|
rb <<= 2;
|
2019-02-19 23:32:48 +00:00
|
|
|
DO_DISS(statusMsg << "add r" << dec << rd << ",SP,#0x" << Base::HEX2 << rb << endl);
|
|
|
|
ra = read_register(13);
|
|
|
|
rc = ra + rb;
|
|
|
|
write_register(rd, rc);
|
|
|
|
return 0;
|
|
|
|
}
|
2011-01-16 21:23:03 +00:00
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//ADD(7) sp plus immediate
|
|
|
|
case Op::add7: {
|
2019-02-22 19:11:19 +00:00
|
|
|
rb = (inst >> 0) & 0x7F;
|
|
|
|
rb <<= 2;
|
2019-02-19 23:32:48 +00:00
|
|
|
DO_DISS(statusMsg << "add SP,#0x" << Base::HEX2 << rb << endl);
|
|
|
|
ra = read_register(13);
|
|
|
|
rc = ra + rb;
|
|
|
|
write_register(13, rc);
|
|
|
|
return 0;
|
|
|
|
}
|
2011-01-16 21:23:03 +00:00
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//AND
|
|
|
|
case Op::and_: {
|
2019-02-22 19:11:19 +00:00
|
|
|
rd = (inst >> 0) & 0x7;
|
|
|
|
rm = (inst >> 3) & 0x7;
|
2019-02-19 23:32:48 +00:00
|
|
|
DO_DISS(statusMsg << "ands r" << dec << rd << ",r" << dec << rm << endl);
|
|
|
|
ra = read_register(rd);
|
|
|
|
rb = read_register(rm);
|
|
|
|
rc = ra & rb;
|
|
|
|
write_register(rd, rc);
|
2022-11-28 21:12:48 +00:00
|
|
|
do_znflags(rc);
|
2019-02-19 23:32:48 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2011-01-16 21:23:03 +00:00
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//ASR(1) two register immediate
|
|
|
|
case Op::asr1: {
|
2019-02-22 19:11:19 +00:00
|
|
|
rd = (inst >> 0) & 0x07;
|
|
|
|
rm = (inst >> 3) & 0x07;
|
|
|
|
rb = (inst >> 6) & 0x1F;
|
2019-02-19 23:32:48 +00:00
|
|
|
DO_DISS(statusMsg << "asrs r" << dec << rd << ",r" << dec << rm << ",#0x" << Base::HEX2 << rb << endl);
|
|
|
|
rc = read_register(rm);
|
|
|
|
if(rb == 0)
|
|
|
|
{
|
|
|
|
if(rc & 0x80000000)
|
|
|
|
{
|
|
|
|
do_cflag_bit(1);
|
2019-12-27 22:52:09 +00:00
|
|
|
rc = ~0U;
|
2019-02-19 23:32:48 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
do_cflag_bit(0);
|
|
|
|
rc = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
do_cflag_bit(rc & (1 << (rb-1)));
|
|
|
|
ra = rc & 0x80000000;
|
|
|
|
rc >>= rb;
|
|
|
|
if(ra) //asr, sign is shifted in
|
2019-12-27 22:52:09 +00:00
|
|
|
rc |= (~0U) << (32-rb);
|
2019-02-19 23:32:48 +00:00
|
|
|
}
|
|
|
|
write_register(rd, rc);
|
2022-11-28 21:12:48 +00:00
|
|
|
do_znflags(rc);
|
2021-07-08 13:46:41 +00:00
|
|
|
INC_SHIFT_CYCLES;
|
2019-02-19 23:32:48 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2011-01-16 21:23:03 +00:00
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//ASR(2) two register
|
|
|
|
case Op::asr2: {
|
2019-02-22 19:11:19 +00:00
|
|
|
rd = (inst >> 0) & 0x07;
|
|
|
|
rs = (inst >> 3) & 0x07;
|
2019-02-19 23:32:48 +00:00
|
|
|
DO_DISS(statusMsg << "asrs r" << dec << rd << ",r" << dec << rs << endl);
|
|
|
|
rc = read_register(rd);
|
|
|
|
rb = read_register(rs);
|
|
|
|
rb &= 0xFF;
|
|
|
|
if(rb == 0)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
else if(rb < 32)
|
|
|
|
{
|
|
|
|
do_cflag_bit(rc & (1 << (rb-1)));
|
|
|
|
ra = rc & 0x80000000;
|
|
|
|
rc >>= rb;
|
|
|
|
if(ra) //asr, sign is shifted in
|
|
|
|
{
|
2019-12-27 22:52:09 +00:00
|
|
|
rc |= (~0U) << (32-rb);
|
2019-02-19 23:32:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if(rc & 0x80000000)
|
|
|
|
{
|
|
|
|
do_cflag_bit(1);
|
2019-12-27 22:52:09 +00:00
|
|
|
rc = (~0U);
|
2019-02-19 23:32:48 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
do_cflag_bit(0);
|
|
|
|
rc = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
write_register(rd, rc);
|
2022-11-28 21:12:48 +00:00
|
|
|
do_znflags(rc);
|
2021-07-08 13:46:41 +00:00
|
|
|
INC_SHIFT_CYCLES;
|
2019-02-19 23:32:48 +00:00
|
|
|
return 0;
|
2011-01-16 21:23:03 +00:00
|
|
|
}
|
2019-02-19 23:32:48 +00:00
|
|
|
|
2022-11-27 12:10:02 +00:00
|
|
|
//B(1) conditional branch variants:
|
|
|
|
// (beq, bne, bcs, bcc, bmi, bpl, bvs, bvc, bhi, bls, bge, blt, bgt, ble)
|
|
|
|
case Op::beq: {
|
|
|
|
#ifdef THUMB_STATS
|
2021-07-08 13:46:41 +00:00
|
|
|
++_stats.branches;
|
2022-11-27 12:10:02 +00:00
|
|
|
#endif
|
|
|
|
rb = decodedParam[instructionPtr >> 1];
|
|
|
|
DO_DISS(statusMsg << "beq 0x" << Base::HEX8 << (rb-3) << endl);
|
2022-11-28 21:12:48 +00:00
|
|
|
if(!znFlags)
|
2022-11-27 12:10:02 +00:00
|
|
|
write_register(15, rb);
|
|
|
|
return 0;
|
|
|
|
}
|
2021-06-27 11:04:35 +00:00
|
|
|
|
2022-11-27 12:10:02 +00:00
|
|
|
case Op::bne: {
|
|
|
|
#ifdef THUMB_STATS
|
|
|
|
++_stats.branches;
|
|
|
|
#endif
|
|
|
|
rb = decodedParam[instructionPtr >> 1];
|
|
|
|
DO_DISS(statusMsg << "bne 0x" << Base::HEX8 << (rb-3) << endl);
|
2022-11-28 21:12:48 +00:00
|
|
|
if(znFlags)
|
2022-11-27 12:10:02 +00:00
|
|
|
write_register(15, rb);
|
|
|
|
return 0;
|
|
|
|
}
|
2019-02-19 23:32:48 +00:00
|
|
|
|
2022-11-27 12:10:02 +00:00
|
|
|
case Op::bcs: {
|
|
|
|
#ifdef THUMB_STATS
|
|
|
|
++_stats.branches;
|
|
|
|
#endif
|
|
|
|
rb = decodedParam[instructionPtr >> 1];
|
|
|
|
DO_DISS(statusMsg << "bcs 0x" << Base::HEX8 << (rb-3) << endl);
|
2022-11-28 21:12:48 +00:00
|
|
|
if(cFlag)
|
2022-11-27 12:10:02 +00:00
|
|
|
write_register(15, rb);
|
|
|
|
return 0;
|
|
|
|
}
|
2019-02-19 23:32:48 +00:00
|
|
|
|
2022-11-27 12:10:02 +00:00
|
|
|
case Op::bcc: {
|
|
|
|
#ifdef THUMB_STATS
|
|
|
|
++_stats.branches;
|
|
|
|
#endif
|
|
|
|
rb = decodedParam[instructionPtr >> 1];
|
|
|
|
DO_DISS(statusMsg << "bcc 0x" << Base::HEX8 << (rb-3) << endl);
|
2022-11-28 21:12:48 +00:00
|
|
|
if(!cFlag)
|
2022-11-27 12:10:02 +00:00
|
|
|
write_register(15, rb);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
case Op::bmi: {
|
|
|
|
#ifdef THUMB_STATS
|
|
|
|
++_stats.branches;
|
|
|
|
#endif
|
|
|
|
rb = decodedParam[instructionPtr >> 1];
|
|
|
|
DO_DISS(statusMsg << "bmi 0x" << Base::HEX8 << (rb-3) << endl);
|
2022-11-28 21:12:48 +00:00
|
|
|
if(znFlags & 0x80000000)
|
2022-11-27 12:10:02 +00:00
|
|
|
write_register(15, rb);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
case Op::bpl: {
|
|
|
|
#ifdef THUMB_STATS
|
|
|
|
++_stats.branches;
|
|
|
|
#endif
|
|
|
|
rb = decodedParam[instructionPtr >> 1];
|
|
|
|
DO_DISS(statusMsg << "bpl 0x" << Base::HEX8 << (rb-3) << endl);
|
2022-11-28 21:12:48 +00:00
|
|
|
if(!(znFlags & 0x80000000))
|
2022-11-27 12:10:02 +00:00
|
|
|
write_register(15, rb);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
case Op::bvs: {
|
|
|
|
#ifdef THUMB_STATS
|
|
|
|
++_stats.branches;
|
|
|
|
#endif
|
|
|
|
rb = decodedParam[instructionPtr >> 1];
|
|
|
|
DO_DISS(statusMsg << "bvs 0x" << Base::HEX8 << (rb-3) << endl);
|
2022-11-28 21:12:48 +00:00
|
|
|
if(vFlag)
|
2022-11-27 12:10:02 +00:00
|
|
|
write_register(15, rb);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
case Op::bvc: {
|
|
|
|
#ifdef THUMB_STATS
|
|
|
|
++_stats.branches;
|
|
|
|
#endif
|
|
|
|
rb = decodedParam[instructionPtr >> 1];
|
|
|
|
DO_DISS(statusMsg << "bvc 0x" << Base::HEX8 << (rb-3) << endl);
|
2022-11-28 21:12:48 +00:00
|
|
|
if(!vFlag)
|
2022-11-27 12:10:02 +00:00
|
|
|
write_register(15, rb);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
case Op::bhi: {
|
|
|
|
#ifdef THUMB_STATS
|
|
|
|
++_stats.branches;
|
|
|
|
#endif
|
|
|
|
rb = decodedParam[instructionPtr >> 1];
|
|
|
|
DO_DISS(statusMsg << "bhi 0x" << Base::HEX8 << (rb-3) << endl);
|
2022-11-28 21:12:48 +00:00
|
|
|
if(cFlag && znFlags)
|
2022-11-27 12:10:02 +00:00
|
|
|
write_register(15, rb);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
case Op::bls: {
|
|
|
|
#ifdef THUMB_STATS
|
|
|
|
++_stats.branches;
|
|
|
|
#endif
|
|
|
|
rb = decodedParam[instructionPtr >> 1];
|
|
|
|
DO_DISS(statusMsg << "bls 0x" << Base::HEX8 << (rb-3) << endl);
|
2022-11-28 21:12:48 +00:00
|
|
|
if(!znFlags || !cFlag)
|
2022-11-27 12:10:02 +00:00
|
|
|
write_register(15, rb);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
case Op::bge: {
|
|
|
|
#ifdef THUMB_STATS
|
|
|
|
++_stats.branches;
|
|
|
|
#endif
|
|
|
|
rb = decodedParam[instructionPtr >> 1];
|
|
|
|
DO_DISS(statusMsg << "bge 0x" << Base::HEX8 << (rb-3) << endl);
|
2022-11-28 21:12:48 +00:00
|
|
|
if(((znFlags & 0x80000000) && vFlag) ||
|
|
|
|
((!(znFlags & 0x80000000)) && !vFlag))
|
2022-11-27 12:10:02 +00:00
|
|
|
write_register(15, rb);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
case Op::blt: {
|
|
|
|
#ifdef THUMB_STATS
|
|
|
|
++_stats.branches;
|
|
|
|
#endif
|
|
|
|
rb = decodedParam[instructionPtr >> 1];
|
|
|
|
DO_DISS(statusMsg << "blt 0x" << Base::HEX8 << (rb-3) << endl);
|
2022-11-28 21:12:48 +00:00
|
|
|
if((!(znFlags & 0x80000000) && vFlag) ||
|
|
|
|
(((znFlags & 0x80000000)) && !vFlag))
|
2022-11-27 12:10:02 +00:00
|
|
|
write_register(15, rb);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
case Op::bgt: {
|
|
|
|
#ifdef THUMB_STATS
|
|
|
|
++_stats.branches;
|
|
|
|
#endif
|
|
|
|
rb = decodedParam[instructionPtr >> 1];
|
|
|
|
DO_DISS(statusMsg << "bgt 0x" << Base::HEX8 << (rb-3) << endl);
|
2022-11-28 21:12:48 +00:00
|
|
|
if(znFlags)
|
2022-11-27 12:10:02 +00:00
|
|
|
{
|
2022-11-28 21:12:48 +00:00
|
|
|
if(((znFlags & 0x80000000) && vFlag) ||
|
|
|
|
((!(znFlags & 0x80000000)) && !vFlag))
|
2022-11-27 12:10:02 +00:00
|
|
|
write_register(15, rb);
|
2019-12-28 18:31:28 +00:00
|
|
|
}
|
2022-11-27 12:10:02 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
case Op::ble: {
|
|
|
|
#ifdef THUMB_STATS
|
|
|
|
++_stats.branches;
|
|
|
|
#endif
|
|
|
|
rb = decodedParam[instructionPtr >> 1];
|
|
|
|
DO_DISS(statusMsg << "ble 0x" << Base::HEX8 << (rb-3) << endl);
|
2022-11-28 21:12:48 +00:00
|
|
|
if(!znFlags ||
|
|
|
|
(!(znFlags & 0x80000000) && vFlag) ||
|
|
|
|
(((znFlags & 0x80000000)) && !vFlag))
|
2022-11-27 12:10:02 +00:00
|
|
|
write_register(15, rb);
|
|
|
|
return 0;
|
2011-01-16 21:23:03 +00:00
|
|
|
}
|
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//B(2) unconditional branch
|
|
|
|
case Op::b2: {
|
2021-07-08 13:46:41 +00:00
|
|
|
#ifdef THUMB_STATS
|
|
|
|
++_stats.branches;
|
|
|
|
#endif
|
2022-11-27 12:10:02 +00:00
|
|
|
rb = decodedParam[instructionPtr >> 1];
|
2019-02-19 23:32:48 +00:00
|
|
|
DO_DISS(statusMsg << "B 0x" << Base::HEX8 << (rb-3) << endl);
|
|
|
|
write_register(15, rb);
|
|
|
|
return 0;
|
2011-01-16 21:23:03 +00:00
|
|
|
}
|
2019-02-19 23:32:48 +00:00
|
|
|
|
|
|
|
//BIC
|
|
|
|
case Op::bic: {
|
2019-02-22 19:11:19 +00:00
|
|
|
rd = (inst >> 0) & 0x7;
|
|
|
|
rm = (inst >> 3) & 0x7;
|
2019-02-19 23:32:48 +00:00
|
|
|
DO_DISS(statusMsg << "bics r" << dec << rd << ",r" << dec << rm << endl);
|
|
|
|
ra = read_register(rd);
|
|
|
|
rb = read_register(rm);
|
|
|
|
rc = ra & (~rb);
|
|
|
|
write_register(rd, rc);
|
2022-11-28 21:12:48 +00:00
|
|
|
do_znflags(rc);
|
2019-02-19 23:32:48 +00:00
|
|
|
return 0;
|
2011-01-16 21:23:03 +00:00
|
|
|
}
|
|
|
|
|
2019-02-23 15:09:00 +00:00
|
|
|
#ifndef UNSAFE_OPTIMIZATIONS
|
2019-02-19 23:32:48 +00:00
|
|
|
//BKPT
|
|
|
|
case Op::bkpt: {
|
2019-02-22 19:11:19 +00:00
|
|
|
rb = (inst >> 0) & 0xFF;
|
2019-02-19 23:32:48 +00:00
|
|
|
statusMsg << "bkpt 0x" << Base::HEX2 << rb << endl;
|
|
|
|
return 1;
|
|
|
|
}
|
2019-02-23 15:09:00 +00:00
|
|
|
#endif
|
2011-04-16 16:53:41 +00:00
|
|
|
|
2022-11-27 12:10:02 +00:00
|
|
|
//BL/BLX(1) variants
|
|
|
|
// (bl, blx_thumb, blx_arm)
|
|
|
|
case Op::bl: {
|
|
|
|
// branch to label
|
|
|
|
DO_DISS(statusMsg << endl);
|
|
|
|
rb = inst & ((1 << 11) - 1);
|
|
|
|
if(rb & 1 << 10) rb |= (~((1 << 11) - 1)); //sign extend
|
|
|
|
rb <<= 12;
|
|
|
|
rb += pc;
|
|
|
|
write_register(14, rb);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
case Op::blx_thumb: {
|
|
|
|
// branch to label, switch to thumb
|
|
|
|
rb = read_register(14);
|
|
|
|
rb += (inst & ((1 << 11) - 1)) << 1;
|
|
|
|
rb += 2;
|
|
|
|
DO_DISS(statusMsg << "bl 0x" << Base::HEX8 << (rb-3) << endl);
|
|
|
|
write_register(14, (pc-2) | 1);
|
|
|
|
write_register(15, rb);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
case Op::blx_arm: {
|
|
|
|
// branch to label, switch to arm
|
|
|
|
//fprintf(stderr,"cannot branch to arm 0x%08X 0x%04X\n",pc,inst);
|
|
|
|
// fxq: this should exit the code without having to detect it
|
|
|
|
// TJ: seems to be not used
|
|
|
|
rb = read_register(14);
|
|
|
|
rb += (inst & ((1 << 11) - 1)) << 1;
|
|
|
|
rb &= 0xFFFFFFFC;
|
|
|
|
rb += 2;
|
|
|
|
DO_DISS(statusMsg << "bl 0x" << Base::HEX8 << (rb-3) << endl);
|
|
|
|
write_register(14, (pc-2) | 1);
|
|
|
|
write_register(15, rb);
|
|
|
|
return 0;
|
2011-01-16 21:23:03 +00:00
|
|
|
}
|
2019-02-19 23:32:48 +00:00
|
|
|
|
|
|
|
//BLX(2)
|
|
|
|
case Op::blx2: {
|
2019-02-22 19:11:19 +00:00
|
|
|
rm = (inst >> 3) & 0xF;
|
2019-02-19 23:32:48 +00:00
|
|
|
DO_DISS(statusMsg << "blx r" << dec << rm << endl);
|
|
|
|
rc = read_register(rm);
|
|
|
|
//fprintf(stderr,"blx r%u 0x%X 0x%X\n",rm,rc,pc);
|
|
|
|
rc += 2;
|
|
|
|
if(rc & 1)
|
|
|
|
{
|
|
|
|
write_register(14, (pc-2) | 1);
|
2019-02-23 11:05:53 +00:00
|
|
|
//rc &= ~1;
|
2019-02-19 23:32:48 +00:00
|
|
|
write_register(15, rc);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
//fprintf(stderr,"cannot branch to arm 0x%08X 0x%04X\n",pc,inst);
|
|
|
|
// fxq: this could serve as exit code
|
|
|
|
return 1;
|
|
|
|
}
|
2011-01-16 21:23:03 +00:00
|
|
|
}
|
2011-04-16 16:53:41 +00:00
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//BX
|
|
|
|
case Op::bx: {
|
2019-02-22 19:11:19 +00:00
|
|
|
rm = (inst >> 3) & 0xF;
|
2019-02-19 23:32:48 +00:00
|
|
|
DO_DISS(statusMsg << "bx r" << dec << rm << endl);
|
|
|
|
rc = read_register(rm);
|
|
|
|
rc += 2;
|
|
|
|
//fprintf(stderr,"bx r%u 0x%X 0x%X\n",rm,rc,pc);
|
|
|
|
if(rc & 1)
|
|
|
|
{
|
|
|
|
// branch to odd address denotes 16 bit ARM code
|
2019-02-23 11:05:53 +00:00
|
|
|
//rc &= ~1;
|
2019-02-19 23:32:48 +00:00
|
|
|
write_register(15, rc);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// branch to even address denotes 32 bit ARM code, which the Thumbulator
|
2021-06-24 14:21:00 +00:00
|
|
|
// class does not support. So capture relevant information and hand it
|
2019-02-19 23:32:48 +00:00
|
|
|
// off to the Cartridge class for it to handle.
|
2011-01-16 21:23:03 +00:00
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
bool handled = false;
|
|
|
|
|
|
|
|
switch(configuration)
|
|
|
|
{
|
2022-12-01 22:16:53 +00:00
|
|
|
case ConfigureFor::BUS: {
|
2019-02-19 23:32:48 +00:00
|
|
|
// this subroutine interface is used in the BUS driver,
|
|
|
|
// it starts at address 0x000006d8
|
|
|
|
// _SetNote:
|
|
|
|
// ldr r4, =NoteStore
|
|
|
|
// bx r4 // bx instruction at 0x000006da
|
|
|
|
// _ResetWave:
|
|
|
|
// ldr r4, =ResetWaveStore
|
|
|
|
// bx r4 // bx instruction at 0x000006de
|
|
|
|
// _GetWavePtr:
|
|
|
|
// ldr r4, =WavePtrFetch
|
|
|
|
// bx r4 // bx instruction at 0x000006e2
|
|
|
|
// _SetWaveSize:
|
|
|
|
// ldr r4, =WaveSizeStore
|
|
|
|
// bx r4 // bx instruction at 0x000006e6
|
|
|
|
|
|
|
|
// address to test for is + 4 due to pipelining
|
2022-12-01 22:01:31 +00:00
|
|
|
static constexpr uInt32
|
|
|
|
BUS_SetNote = (0x000006da + 4),
|
|
|
|
BUS_ResetWave = (0x000006de + 4),
|
|
|
|
BUS_GetWavePtr = (0x000006e2 + 4),
|
|
|
|
BUS_SetWaveSize = (0x000006e6 + 4);
|
2019-02-19 23:32:48 +00:00
|
|
|
|
|
|
|
if (pc == BUS_SetNote)
|
|
|
|
{
|
|
|
|
myCartridge->thumbCallback(0, read_register(2), read_register(3));
|
|
|
|
handled = true;
|
|
|
|
}
|
|
|
|
else if (pc == BUS_ResetWave)
|
|
|
|
{
|
|
|
|
myCartridge->thumbCallback(1, read_register(2), 0);
|
|
|
|
handled = true;
|
|
|
|
}
|
|
|
|
else if (pc == BUS_GetWavePtr)
|
|
|
|
{
|
|
|
|
write_register(2, myCartridge->thumbCallback(2, read_register(2), 0));
|
|
|
|
handled = true;
|
|
|
|
}
|
|
|
|
else if (pc == BUS_SetWaveSize)
|
|
|
|
{
|
|
|
|
myCartridge->thumbCallback(3, read_register(2), read_register(3));
|
|
|
|
handled = true;
|
|
|
|
}
|
2021-12-11 22:31:10 +00:00
|
|
|
else if (pc == 0x0000083a)
|
2019-02-19 23:32:48 +00:00
|
|
|
{
|
|
|
|
// exiting Custom ARM code, returning to BUS Driver control
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
#if 0 // uncomment this for testing
|
|
|
|
uInt32 r0 = read_register(0);
|
|
|
|
uInt32 r1 = read_register(1);
|
|
|
|
uInt32 r2 = read_register(2);
|
|
|
|
uInt32 r3 = read_register(3);
|
|
|
|
uInt32 r4 = read_register(4);
|
|
|
|
#endif
|
|
|
|
myCartridge->thumbCallback(255, 0, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
2022-12-01 22:16:53 +00:00
|
|
|
}
|
2019-02-19 23:32:48 +00:00
|
|
|
|
2022-12-01 22:16:53 +00:00
|
|
|
case ConfigureFor::CDF: {
|
2019-02-19 23:32:48 +00:00
|
|
|
// this subroutine interface is used in the CDF driver,
|
|
|
|
// it starts at address 0x000006e0
|
|
|
|
// _SetNote:
|
|
|
|
// ldr r4, =NoteStore
|
|
|
|
// bx r4 // bx instruction at 0x000006e2
|
|
|
|
// _ResetWave:
|
|
|
|
// ldr r4, =ResetWaveStore
|
|
|
|
// bx r4 // bx instruction at 0x000006e6
|
|
|
|
// _GetWavePtr:
|
|
|
|
// ldr r4, =WavePtrFetch
|
|
|
|
// bx r4 // bx instruction at 0x000006ea
|
|
|
|
// _SetWaveSize:
|
|
|
|
// ldr r4, =WaveSizeStore
|
|
|
|
// bx r4 // bx instruction at 0x000006ee
|
|
|
|
|
|
|
|
// address to test for is + 4 due to pipelining
|
2022-12-01 22:01:31 +00:00
|
|
|
static constexpr uInt32
|
|
|
|
CDF_SetNote = (0x000006e2 + 4),
|
|
|
|
CDF_ResetWave = (0x000006e6 + 4),
|
|
|
|
CDF_GetWavePtr = (0x000006ea + 4),
|
|
|
|
CDF_SetWaveSize = (0x000006ee + 4);
|
2019-02-19 23:32:48 +00:00
|
|
|
|
|
|
|
if (pc == CDF_SetNote)
|
|
|
|
{
|
|
|
|
myCartridge->thumbCallback(0, read_register(2), read_register(3));
|
|
|
|
handled = true;
|
|
|
|
}
|
|
|
|
else if (pc == CDF_ResetWave)
|
|
|
|
{
|
|
|
|
myCartridge->thumbCallback(1, read_register(2), 0);
|
|
|
|
handled = true;
|
|
|
|
}
|
|
|
|
else if (pc == CDF_GetWavePtr)
|
|
|
|
{
|
|
|
|
write_register(2, myCartridge->thumbCallback(2, read_register(2), 0));
|
|
|
|
handled = true;
|
|
|
|
}
|
|
|
|
else if (pc == CDF_SetWaveSize)
|
|
|
|
{
|
|
|
|
myCartridge->thumbCallback(3, read_register(2), read_register(3));
|
|
|
|
handled = true;
|
|
|
|
}
|
2021-12-11 22:31:10 +00:00
|
|
|
else if (pc == 0x0000083a)
|
2019-02-19 23:32:48 +00:00
|
|
|
{
|
2021-06-27 07:44:42 +00:00
|
|
|
// exiting Custom ARM code, returning to CDF Driver control
|
2019-02-19 23:32:48 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
#if 0 // uncomment this for testing
|
|
|
|
uInt32 r0 = read_register(0);
|
|
|
|
uInt32 r1 = read_register(1);
|
|
|
|
uInt32 r2 = read_register(2);
|
|
|
|
uInt32 r3 = read_register(3);
|
|
|
|
uInt32 r4 = read_register(4);
|
|
|
|
#endif
|
|
|
|
myCartridge->thumbCallback(255, 0, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
2022-12-01 22:16:53 +00:00
|
|
|
}
|
2019-02-19 23:32:48 +00:00
|
|
|
|
|
|
|
case ConfigureFor::CDF1:
|
2019-03-19 20:08:35 +00:00
|
|
|
case ConfigureFor::CDFJ:
|
2022-12-01 22:16:53 +00:00
|
|
|
case ConfigureFor::CDFJplus: {
|
2019-02-19 23:32:48 +00:00
|
|
|
// this subroutine interface is used in the CDF driver,
|
|
|
|
// it starts at address 0x00000750
|
|
|
|
// _SetNote:
|
|
|
|
// ldr r4, =NoteStore
|
|
|
|
// bx r4 // bx instruction at 0x000006e2
|
|
|
|
// _ResetWave:
|
|
|
|
// ldr r4, =ResetWaveStore
|
|
|
|
// bx r4 // bx instruction at 0x000006e6
|
|
|
|
// _GetWavePtr:
|
|
|
|
// ldr r4, =WavePtrFetch
|
|
|
|
// bx r4 // bx instruction at 0x000006ea
|
|
|
|
// _SetWaveSize:
|
|
|
|
// ldr r4, =WaveSizeStore
|
|
|
|
// bx r4 // bx instruction at 0x000006ee
|
|
|
|
|
|
|
|
// address to test for is + 4 due to pipelining
|
2022-12-01 22:01:31 +00:00
|
|
|
static constexpr uInt32
|
|
|
|
CDF1_SetNote = (0x00000752 + 4),
|
|
|
|
CDF1_ResetWave = (0x00000756 + 4),
|
|
|
|
CDF1_GetWavePtr = (0x0000075a + 4),
|
|
|
|
CDF1_SetWaveSize = (0x0000075e + 4);
|
2019-02-19 23:32:48 +00:00
|
|
|
|
|
|
|
if (pc == CDF1_SetNote)
|
|
|
|
{
|
|
|
|
myCartridge->thumbCallback(0, read_register(2), read_register(3));
|
2021-06-27 07:44:42 +00:00
|
|
|
// approximated cycles
|
2021-07-08 13:46:41 +00:00
|
|
|
INC_ARM_CYCLES(_flashCycles + 1); // this instruction
|
|
|
|
INC_ARM_CYCLES(6); // ARM code NoteStore
|
|
|
|
INC_ARM_CYCLES(2 + _flashCycles + 2); // ARM code ReturnC
|
2019-02-19 23:32:48 +00:00
|
|
|
handled = true;
|
|
|
|
}
|
|
|
|
else if (pc == CDF1_ResetWave)
|
|
|
|
{
|
|
|
|
myCartridge->thumbCallback(1, read_register(2), 0);
|
2021-06-27 07:44:42 +00:00
|
|
|
// approximated cycles
|
2021-07-08 13:46:41 +00:00
|
|
|
INC_ARM_CYCLES(_flashCycles + 1); // this instruction
|
|
|
|
INC_ARM_CYCLES(6 + _flashCycles + 2); // ARM code ResetWaveStore
|
|
|
|
INC_ARM_CYCLES(2 + _flashCycles + 2); // ARM code ReturnC
|
2019-02-19 23:32:48 +00:00
|
|
|
handled = true;
|
|
|
|
}
|
|
|
|
else if (pc == CDF1_GetWavePtr)
|
|
|
|
{
|
|
|
|
write_register(2, myCartridge->thumbCallback(2, read_register(2), 0));
|
2021-06-27 07:44:42 +00:00
|
|
|
// approximated cycles
|
2021-07-08 13:46:41 +00:00
|
|
|
INC_ARM_CYCLES(_flashCycles + 1); // this instruction
|
|
|
|
INC_ARM_CYCLES(6 + _flashCycles + 2); // ARM code WavePtrFetch
|
|
|
|
INC_ARM_CYCLES(2 + _flashCycles + 2); // ARM code ReturnC
|
2019-02-19 23:32:48 +00:00
|
|
|
handled = true;
|
|
|
|
}
|
|
|
|
else if (pc == CDF1_SetWaveSize)
|
|
|
|
{
|
|
|
|
myCartridge->thumbCallback(3, read_register(2), read_register(3));
|
2021-06-27 07:44:42 +00:00
|
|
|
// approximated cycles
|
2021-07-08 13:46:41 +00:00
|
|
|
INC_ARM_CYCLES(_flashCycles + 1); // this instruction
|
|
|
|
INC_ARM_CYCLES(18 + _flashCycles * 3 + 2); // ARM code WaveSizeStore
|
|
|
|
INC_ARM_CYCLES(2 + _flashCycles + 2); // ARM code ReturnC
|
2019-02-19 23:32:48 +00:00
|
|
|
handled = true;
|
|
|
|
}
|
2021-12-11 22:31:10 +00:00
|
|
|
else if (pc == 0x0000083a)
|
2019-02-19 23:32:48 +00:00
|
|
|
{
|
2021-06-27 07:44:42 +00:00
|
|
|
// exiting Custom ARM code, returning to CDFJ Driver control
|
2019-02-19 23:32:48 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
#if 0 // uncomment this for testing
|
|
|
|
uInt32 r0 = read_register(0);
|
|
|
|
uInt32 r1 = read_register(1);
|
|
|
|
uInt32 r2 = read_register(2);
|
|
|
|
uInt32 r3 = read_register(3);
|
|
|
|
uInt32 r4 = read_register(4);
|
|
|
|
#endif
|
|
|
|
myCartridge->thumbCallback(255, 0, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
2022-12-01 22:16:53 +00:00
|
|
|
}
|
2019-02-19 23:32:48 +00:00
|
|
|
|
|
|
|
case ConfigureFor::DPCplus:
|
|
|
|
// no 32-bit subroutines in DPC+
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (handled)
|
|
|
|
{
|
|
|
|
rc = read_register(14); // lr
|
|
|
|
rc += 2;
|
2019-02-23 11:05:53 +00:00
|
|
|
//rc &= ~1;
|
2019-02-19 23:32:48 +00:00
|
|
|
write_register(15, rc);
|
2021-06-25 07:18:26 +00:00
|
|
|
//_totalCycles += 100; // just a wild guess
|
2019-02-19 23:32:48 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
2011-01-16 21:23:03 +00:00
|
|
|
}
|
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//CMN
|
|
|
|
case Op::cmn: {
|
2019-02-22 19:11:19 +00:00
|
|
|
rn = (inst >> 0) & 0x7;
|
|
|
|
rm = (inst >> 3) & 0x7;
|
2019-02-19 23:32:48 +00:00
|
|
|
DO_DISS(statusMsg << "cmns r" << dec << rn << ",r" << dec << rm << endl);
|
|
|
|
ra = read_register(rn);
|
|
|
|
rb = read_register(rm);
|
|
|
|
rc = ra + rb;
|
2022-11-28 21:12:48 +00:00
|
|
|
do_znflags(rc);
|
|
|
|
do_cvflag(ra, rb, 0);
|
2019-02-19 23:32:48 +00:00
|
|
|
return 0;
|
2011-01-16 21:23:03 +00:00
|
|
|
}
|
2019-02-19 23:32:48 +00:00
|
|
|
|
|
|
|
//CMP(1) compare immediate
|
|
|
|
case Op::cmp1: {
|
2019-02-22 19:11:19 +00:00
|
|
|
rb = (inst >> 0) & 0xFF;
|
|
|
|
rn = (inst >> 8) & 0x07;
|
2019-02-19 23:32:48 +00:00
|
|
|
DO_DISS(statusMsg << "cmp r" << dec << rn << ",#0x" << Base::HEX2 << rb << endl);
|
|
|
|
ra = read_register(rn);
|
|
|
|
rc = ra - rb;
|
|
|
|
//fprintf(stderr,"0x%08X 0x%08X\n",ra,rb);
|
2022-11-28 21:12:48 +00:00
|
|
|
do_znflags(rc);
|
|
|
|
do_cvflag(ra, ~rb, 1);
|
2019-02-19 23:32:48 +00:00
|
|
|
return 0;
|
2011-01-16 21:23:03 +00:00
|
|
|
}
|
2019-02-19 23:32:48 +00:00
|
|
|
|
|
|
|
//CMP(2) compare register
|
|
|
|
case Op::cmp2: {
|
2019-02-22 19:11:19 +00:00
|
|
|
rn = (inst >> 0) & 0x7;
|
|
|
|
rm = (inst >> 3) & 0x7;
|
2019-02-19 23:32:48 +00:00
|
|
|
DO_DISS(statusMsg << "cmps r" << dec << rn << ",r" << dec << rm << endl);
|
|
|
|
ra = read_register(rn);
|
|
|
|
rb = read_register(rm);
|
|
|
|
rc = ra - rb;
|
|
|
|
//fprintf(stderr,"0x%08X 0x%08X\n",ra,rb);
|
2022-11-28 21:12:48 +00:00
|
|
|
do_znflags(rc);
|
|
|
|
do_cvflag(ra, ~rb, 1);
|
2019-02-19 23:32:48 +00:00
|
|
|
return 0;
|
2011-01-16 21:23:03 +00:00
|
|
|
}
|
2019-02-19 23:32:48 +00:00
|
|
|
|
|
|
|
//CMP(3) compare high register
|
|
|
|
case Op::cmp3: {
|
2019-02-22 19:11:19 +00:00
|
|
|
if(((inst >> 6) & 3) == 0x0)
|
|
|
|
{
|
|
|
|
//UNPREDICTABLE
|
|
|
|
}
|
|
|
|
rn = (inst >> 0) & 0x7;
|
|
|
|
rn |= (inst >> 4) & 0x8;
|
|
|
|
if(rn == 0xF)
|
|
|
|
{
|
|
|
|
//UNPREDICTABLE
|
|
|
|
}
|
|
|
|
rm = (inst >> 3) & 0xF;
|
2019-02-19 23:32:48 +00:00
|
|
|
DO_DISS(statusMsg << "cmps r" << dec << rn << ",r" << dec << rm << endl);
|
|
|
|
ra = read_register(rn);
|
|
|
|
rb = read_register(rm);
|
|
|
|
rc = ra - rb;
|
2022-11-28 21:12:48 +00:00
|
|
|
do_znflags(rc);
|
|
|
|
do_cvflag(ra, ~rb, 1);
|
2019-02-19 23:32:48 +00:00
|
|
|
return 0;
|
2011-01-16 21:23:03 +00:00
|
|
|
}
|
|
|
|
|
2019-02-23 15:09:00 +00:00
|
|
|
#ifndef UNSAFE_OPTIMIZATIONS
|
2019-02-19 23:32:48 +00:00
|
|
|
//CPS
|
|
|
|
case Op::cps: {
|
|
|
|
DO_DISS(statusMsg << "cps TODO" << endl);
|
|
|
|
return 1;
|
2011-01-16 21:23:03 +00:00
|
|
|
}
|
2019-02-23 15:09:00 +00:00
|
|
|
#endif
|
2019-02-19 23:32:48 +00:00
|
|
|
|
|
|
|
//CPY copy high register
|
|
|
|
case Op::cpy: {
|
|
|
|
//same as mov except you can use both low registers
|
|
|
|
//going to let mov handle high registers
|
2019-02-22 19:11:19 +00:00
|
|
|
rd = (inst >> 0) & 0x7;
|
|
|
|
rm = (inst >> 3) & 0x7;
|
2019-02-19 23:32:48 +00:00
|
|
|
DO_DISS(statusMsg << "cpy r" << dec << rd << ",r" << dec << rm << endl);
|
|
|
|
rc = read_register(rm);
|
|
|
|
write_register(rd, rc);
|
|
|
|
return 0;
|
2011-04-16 16:53:41 +00:00
|
|
|
}
|
2011-01-16 21:23:03 +00:00
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//EOR
|
|
|
|
case Op::eor: {
|
2019-02-22 19:11:19 +00:00
|
|
|
rd = (inst >> 0) & 0x7;
|
|
|
|
rm = (inst >> 3) & 0x7;
|
2019-02-19 23:32:48 +00:00
|
|
|
DO_DISS(statusMsg << "eors r" << dec << rd << ",r" << dec << rm << endl);
|
|
|
|
ra = read_register(rd);
|
|
|
|
rb = read_register(rm);
|
|
|
|
rc = ra ^ rb;
|
|
|
|
write_register(rd, rc);
|
2022-11-28 21:12:48 +00:00
|
|
|
do_znflags(rc);
|
2019-02-19 23:32:48 +00:00
|
|
|
return 0;
|
2011-01-16 21:23:03 +00:00
|
|
|
}
|
2019-02-19 23:32:48 +00:00
|
|
|
|
|
|
|
//LDMIA
|
|
|
|
case Op::ldmia: {
|
2019-02-22 19:11:19 +00:00
|
|
|
rn = (inst >> 8) & 0x7;
|
2019-02-19 23:32:48 +00:00
|
|
|
#if defined(THUMB_DISS)
|
|
|
|
statusMsg << "ldmia r" << dec << rn << "!,{";
|
|
|
|
for(ra=0,rb=0x01,rc=0;rb;rb=(rb<<1)&0xFF,++ra)
|
|
|
|
{
|
|
|
|
if(inst&rb)
|
|
|
|
{
|
|
|
|
if(rc) statusMsg << ",";
|
|
|
|
statusMsg << "r" << dec << ra;
|
|
|
|
rc++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
statusMsg << "}" << endl;
|
|
|
|
#endif
|
2021-06-24 14:21:00 +00:00
|
|
|
bool first = true;
|
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
sp = read_register(rn);
|
|
|
|
for(ra = 0, rb = 0x01; rb; rb = (rb << 1) & 0xFF, ++ra)
|
|
|
|
{
|
|
|
|
if(inst & rb)
|
|
|
|
{
|
|
|
|
write_register(ra, read32(sp));
|
2021-06-24 14:21:00 +00:00
|
|
|
if(first)
|
|
|
|
{
|
|
|
|
INC_N_CYCLES(sp, AccessType::data);
|
|
|
|
first = false;
|
|
|
|
}
|
|
|
|
else
|
2021-10-24 00:21:13 +00:00
|
|
|
{
|
2021-06-24 14:21:00 +00:00
|
|
|
INC_S_CYCLES(sp, AccessType::data);
|
2021-10-24 00:21:13 +00:00
|
|
|
}
|
2019-02-19 23:32:48 +00:00
|
|
|
sp += 4;
|
|
|
|
}
|
|
|
|
}
|
2021-07-01 13:40:49 +00:00
|
|
|
INC_I_CYCLES; // Note: destination PC not possible, see pop instead
|
2019-02-19 23:32:48 +00:00
|
|
|
//there is a write back exception.
|
|
|
|
if((inst & (1 << rn)) == 0)
|
|
|
|
write_register(rn, sp);
|
|
|
|
return 0;
|
2011-04-16 16:53:41 +00:00
|
|
|
}
|
2019-02-19 23:32:48 +00:00
|
|
|
|
|
|
|
//LDR(1) two register immediate
|
|
|
|
case Op::ldr1: {
|
2019-02-22 19:11:19 +00:00
|
|
|
rd = (inst >> 0) & 0x07;
|
|
|
|
rn = (inst >> 3) & 0x07;
|
|
|
|
rb = (inst >> 6) & 0x1F;
|
|
|
|
rb <<= 2;
|
2019-02-19 23:32:48 +00:00
|
|
|
DO_DISS(statusMsg << "ldr r" << dec << rd << ",[r" << dec << rn << ",#0x" << Base::HEX2 << rb << "]" << endl);
|
|
|
|
rb = read_register(rn) + rb;
|
|
|
|
rc = read32(rb);
|
2021-07-01 13:40:49 +00:00
|
|
|
write_register(rd, rc);
|
2021-07-08 13:46:41 +00:00
|
|
|
INC_LDR_CYCLES;
|
2019-02-19 23:32:48 +00:00
|
|
|
return 0;
|
2011-04-16 16:53:41 +00:00
|
|
|
}
|
2019-02-19 23:32:48 +00:00
|
|
|
|
|
|
|
//LDR(2) three register
|
|
|
|
case Op::ldr2: {
|
2019-02-22 19:11:19 +00:00
|
|
|
rd = (inst >> 0) & 0x7;
|
|
|
|
rn = (inst >> 3) & 0x7;
|
|
|
|
rm = (inst >> 6) & 0x7;
|
2019-02-19 23:32:48 +00:00
|
|
|
DO_DISS(statusMsg << "ldr r" << dec << rd << ",[r" << dec << rn << ",r" << dec << "]" << endl);
|
|
|
|
rb = read_register(rn) + read_register(rm);
|
|
|
|
rc = read32(rb);
|
2021-07-01 13:40:49 +00:00
|
|
|
write_register(rd, rc);
|
2021-07-08 13:46:41 +00:00
|
|
|
INC_LDR_CYCLES;
|
2019-02-19 23:32:48 +00:00
|
|
|
return 0;
|
2011-04-16 16:53:41 +00:00
|
|
|
}
|
2011-01-16 21:23:03 +00:00
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//LDR(3)
|
|
|
|
case Op::ldr3: {
|
2019-02-22 19:11:19 +00:00
|
|
|
rb = (inst >> 0) & 0xFF;
|
|
|
|
rd = (inst >> 8) & 0x07;
|
|
|
|
rb <<= 2;
|
2019-02-19 23:32:48 +00:00
|
|
|
DO_DISS(statusMsg << "ldr r" << dec << rd << ",[PC+#0x" << Base::HEX2 << rb << "] ");
|
|
|
|
ra = read_register(15);
|
|
|
|
ra &= ~3;
|
|
|
|
rb += ra;
|
|
|
|
DO_DISS(statusMsg << ";@ 0x" << Base::HEX2 << rb << endl);
|
|
|
|
rc = read32(rb);
|
2021-07-01 13:40:49 +00:00
|
|
|
write_register(rd, rc);
|
2021-07-08 13:46:41 +00:00
|
|
|
INC_LDR_CYCLES;
|
2019-02-19 23:32:48 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2011-01-16 21:23:03 +00:00
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//LDR(4)
|
|
|
|
case Op::ldr4: {
|
2019-02-22 19:11:19 +00:00
|
|
|
rb = (inst >> 0) & 0xFF;
|
|
|
|
rd = (inst >> 8) & 0x07;
|
|
|
|
rb <<= 2;
|
2019-02-19 23:32:48 +00:00
|
|
|
DO_DISS(statusMsg << "ldr r" << dec << rd << ",[SP+#0x" << Base::HEX2 << rb << "]" << endl);
|
|
|
|
ra = read_register(13);
|
|
|
|
//ra&=~3;
|
|
|
|
rb += ra;
|
|
|
|
rc = read32(rb);
|
2021-07-01 13:40:49 +00:00
|
|
|
write_register(rd, rc);
|
2021-07-08 13:46:41 +00:00
|
|
|
INC_LDR_CYCLES;
|
2019-02-19 23:32:48 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2011-04-16 16:53:41 +00:00
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//LDRB(1)
|
|
|
|
case Op::ldrb1: {
|
2019-02-22 19:11:19 +00:00
|
|
|
rd = (inst >> 0) & 0x07;
|
|
|
|
rn = (inst >> 3) & 0x07;
|
|
|
|
rb = (inst >> 6) & 0x1F;
|
2019-02-19 23:32:48 +00:00
|
|
|
DO_DISS(statusMsg << "ldrb r" << dec << rd << ",[r" << dec << rn << ",#0x" << Base::HEX2 << rb << "]" << endl);
|
|
|
|
rb = read_register(rn) + rb;
|
2019-02-25 18:50:33 +00:00
|
|
|
#ifndef UNSAFE_OPTIMIZATIONS
|
2019-12-28 04:15:09 +00:00
|
|
|
rc = read16(rb & (~1U));
|
2019-02-25 18:50:33 +00:00
|
|
|
#else
|
|
|
|
rc = read16(rb);
|
|
|
|
#endif
|
2019-02-19 23:32:48 +00:00
|
|
|
if(rb & 1)
|
|
|
|
{
|
|
|
|
rc >>= 8;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
}
|
2021-07-01 13:40:49 +00:00
|
|
|
write_register(rd, rc & 0xFF);
|
2021-07-08 13:46:41 +00:00
|
|
|
INC_LDRB_CYCLES;
|
2019-02-19 23:32:48 +00:00
|
|
|
return 0;
|
2016-02-27 19:58:20 +00:00
|
|
|
}
|
2019-02-19 23:32:48 +00:00
|
|
|
|
|
|
|
//LDRB(2)
|
|
|
|
case Op::ldrb2: {
|
2019-02-22 19:11:19 +00:00
|
|
|
rd = (inst >> 0) & 0x7;
|
|
|
|
rn = (inst >> 3) & 0x7;
|
|
|
|
rm = (inst >> 6) & 0x7;
|
2019-02-19 23:32:48 +00:00
|
|
|
DO_DISS(statusMsg << "ldrb r" << dec << rd << ",[r" << dec << rn << ",r" << dec << rm << "]" << endl);
|
|
|
|
rb = read_register(rn) + read_register(rm);
|
2019-02-25 18:50:33 +00:00
|
|
|
#ifndef UNSAFE_OPTIMIZATIONS
|
2019-12-28 04:15:09 +00:00
|
|
|
rc = read16(rb & (~1U));
|
2019-02-25 18:50:33 +00:00
|
|
|
#else
|
|
|
|
rc = read16(rb);
|
|
|
|
#endif
|
2019-02-19 23:32:48 +00:00
|
|
|
if(rb & 1)
|
|
|
|
{
|
|
|
|
rc >>= 8;
|
|
|
|
}
|
2021-07-01 13:40:49 +00:00
|
|
|
write_register(rd, rc & 0xFF);
|
2021-07-08 13:46:41 +00:00
|
|
|
INC_LDRB_CYCLES;
|
2019-02-19 23:32:48 +00:00
|
|
|
return 0;
|
2016-02-27 19:58:20 +00:00
|
|
|
}
|
2011-04-16 16:53:41 +00:00
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//LDRH(1)
|
|
|
|
case Op::ldrh1: {
|
2019-02-22 19:11:19 +00:00
|
|
|
rd = (inst >> 0) & 0x07;
|
|
|
|
rn = (inst >> 3) & 0x07;
|
|
|
|
rb = (inst >> 6) & 0x1F;
|
|
|
|
rb <<= 1;
|
2019-02-19 23:32:48 +00:00
|
|
|
DO_DISS(statusMsg << "ldrh r" << dec << rd << ",[r" << dec << rn << ",#0x" << Base::HEX2 << rb << "]" << endl);
|
2019-02-25 18:50:33 +00:00
|
|
|
rb = read_register(rn) + rb;
|
2019-02-19 23:32:48 +00:00
|
|
|
rc = read16(rb);
|
2021-07-01 13:40:49 +00:00
|
|
|
write_register(rd, rc & 0xFFFF);
|
2021-07-08 13:46:41 +00:00
|
|
|
INC_LDR_CYCLES;
|
2019-02-19 23:32:48 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2011-04-16 16:53:41 +00:00
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//LDRH(2)
|
|
|
|
case Op::ldrh2: {
|
2019-02-22 19:11:19 +00:00
|
|
|
rd = (inst >> 0) & 0x7;
|
|
|
|
rn = (inst >> 3) & 0x7;
|
|
|
|
rm = (inst >> 6) & 0x7;
|
2019-02-19 23:32:48 +00:00
|
|
|
DO_DISS(statusMsg << "ldrh r" << dec << rd << ",[r" << dec << rn << ",r" << dec << rm << "]" << endl);
|
|
|
|
rb = read_register(rn) + read_register(rm);
|
|
|
|
rc = read16(rb);
|
|
|
|
write_register(rd, rc & 0xFFFF);
|
2021-07-08 13:46:41 +00:00
|
|
|
INC_LDR_CYCLES;
|
2019-02-19 23:32:48 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2011-04-16 16:53:41 +00:00
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//LDRSB
|
|
|
|
case Op::ldrsb: {
|
2019-02-22 19:11:19 +00:00
|
|
|
rd = (inst >> 0) & 0x7;
|
|
|
|
rn = (inst >> 3) & 0x7;
|
|
|
|
rm = (inst >> 6) & 0x7;
|
2019-02-19 23:32:48 +00:00
|
|
|
DO_DISS(statusMsg << "ldrsb r" << dec << rd << ",[r" << dec << rn << ",r" << dec << rm << "]" << endl);
|
|
|
|
rb = read_register(rn) + read_register(rm);
|
2019-02-25 18:50:33 +00:00
|
|
|
#ifndef UNSAFE_OPTIMIZATIONS
|
2019-12-28 04:15:09 +00:00
|
|
|
rc = read16(rb & (~1U));
|
2019-02-25 18:50:33 +00:00
|
|
|
#else
|
|
|
|
rc = read16(rb);
|
|
|
|
#endif
|
2019-02-19 23:32:48 +00:00
|
|
|
if(rb & 1)
|
|
|
|
{
|
|
|
|
rc >>= 8;
|
|
|
|
}
|
|
|
|
rc &= 0xFF;
|
|
|
|
if(rc & 0x80)
|
2019-12-27 22:52:09 +00:00
|
|
|
rc |= ((~0U) << 8);
|
2021-07-01 13:40:49 +00:00
|
|
|
write_register(rd, rc);
|
2021-07-08 13:46:41 +00:00
|
|
|
INC_LDRB_CYCLES;
|
2019-02-19 23:32:48 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2011-04-16 16:53:41 +00:00
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//LDRSH
|
|
|
|
case Op::ldrsh: {
|
2019-02-22 19:11:19 +00:00
|
|
|
rd = (inst >> 0) & 0x7;
|
|
|
|
rn = (inst >> 3) & 0x7;
|
|
|
|
rm = (inst >> 6) & 0x7;
|
2019-02-19 23:32:48 +00:00
|
|
|
DO_DISS(statusMsg << "ldrsh r" << dec << rd << ",[r" << dec << rn << ",r" << dec << rm << "]" << endl);
|
|
|
|
rb = read_register(rn) + read_register(rm);
|
|
|
|
rc = read16(rb);
|
|
|
|
rc &= 0xFFFF;
|
|
|
|
if(rc & 0x8000)
|
2019-12-27 22:52:09 +00:00
|
|
|
rc |= ((~0U) << 16);
|
2021-07-01 13:40:49 +00:00
|
|
|
write_register(rd, rc);
|
2021-07-08 13:46:41 +00:00
|
|
|
INC_LDR_CYCLES;
|
2019-02-19 23:32:48 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//LSL(1)
|
|
|
|
case Op::lsl1: {
|
2019-02-22 19:11:19 +00:00
|
|
|
rd = (inst >> 0) & 0x07;
|
|
|
|
rm = (inst >> 3) & 0x07;
|
|
|
|
rb = (inst >> 6) & 0x1F;
|
2019-02-19 23:32:48 +00:00
|
|
|
DO_DISS(statusMsg << "lsls r" << dec << rd << ",r" << dec << rm << ",#0x" << Base::HEX2 << rb << endl);
|
|
|
|
rc = read_register(rm);
|
|
|
|
if(rb == 0)
|
|
|
|
{
|
|
|
|
//if immed_5 == 0
|
|
|
|
//C unaffected
|
|
|
|
//result not shifted
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
//else immed_5 > 0
|
|
|
|
do_cflag_bit(rc & (1 << (32-rb)));
|
|
|
|
rc <<= rb;
|
|
|
|
}
|
|
|
|
write_register(rd, rc);
|
2022-11-28 21:12:48 +00:00
|
|
|
do_znflags(rc);
|
2021-07-08 13:46:41 +00:00
|
|
|
INC_SHIFT_CYCLES;
|
2019-02-19 23:32:48 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//LSL(2) two register
|
|
|
|
case Op::lsl2: {
|
2019-02-22 19:11:19 +00:00
|
|
|
rd = (inst >> 0) & 0x07;
|
|
|
|
rs = (inst >> 3) & 0x07;
|
2019-02-19 23:32:48 +00:00
|
|
|
DO_DISS(statusMsg << "lsls r" << dec << rd << ",r" << dec << rs << endl);
|
|
|
|
rc = read_register(rd);
|
|
|
|
rb = read_register(rs);
|
|
|
|
rb &= 0xFF;
|
|
|
|
if(rb == 0)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
else if(rb < 32)
|
|
|
|
{
|
|
|
|
do_cflag_bit(rc & (1 << (32-rb)));
|
|
|
|
rc <<= rb;
|
|
|
|
}
|
|
|
|
else if(rb == 32)
|
|
|
|
{
|
|
|
|
do_cflag_bit(rc & 1);
|
|
|
|
rc = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
do_cflag_bit(0);
|
|
|
|
rc = 0;
|
|
|
|
}
|
|
|
|
write_register(rd, rc);
|
2022-11-28 21:12:48 +00:00
|
|
|
do_znflags(rc);
|
2021-07-08 13:46:41 +00:00
|
|
|
INC_SHIFT_CYCLES;
|
2019-02-19 23:32:48 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//LSR(1) two register immediate
|
|
|
|
case Op::lsr1: {
|
2019-02-22 19:11:19 +00:00
|
|
|
rd = (inst >> 0) & 0x07;
|
|
|
|
rm = (inst >> 3) & 0x07;
|
|
|
|
rb = (inst >> 6) & 0x1F;
|
2019-02-19 23:32:48 +00:00
|
|
|
DO_DISS(statusMsg << "lsrs r" << dec << rd << ",r" << dec << rm << ",#0x" << Base::HEX2 << rb << endl);
|
|
|
|
rc = read_register(rm);
|
|
|
|
if(rb == 0)
|
|
|
|
{
|
|
|
|
do_cflag_bit(rc & 0x80000000);
|
|
|
|
rc = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
do_cflag_bit(rc & (1 << (rb-1)));
|
|
|
|
rc >>= rb;
|
|
|
|
}
|
|
|
|
write_register(rd, rc);
|
2022-11-28 21:12:48 +00:00
|
|
|
do_znflags(rc);
|
2021-07-08 13:46:41 +00:00
|
|
|
INC_SHIFT_CYCLES;
|
2019-02-19 23:32:48 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//LSR(2) two register
|
|
|
|
case Op::lsr2: {
|
2019-02-22 19:11:19 +00:00
|
|
|
rd = (inst >> 0) & 0x07;
|
|
|
|
rs = (inst >> 3) & 0x07;
|
2019-02-19 23:32:48 +00:00
|
|
|
DO_DISS(statusMsg << "lsrs r" << dec << rd << ",r" << dec << rs << endl);
|
|
|
|
rc = read_register(rd);
|
|
|
|
rb = read_register(rs);
|
|
|
|
rb &= 0xFF;
|
|
|
|
if(rb == 0)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
else if(rb < 32)
|
|
|
|
{
|
|
|
|
do_cflag_bit(rc & (1 << (rb-1)));
|
|
|
|
rc >>= rb;
|
|
|
|
}
|
|
|
|
else if(rb == 32)
|
|
|
|
{
|
|
|
|
do_cflag_bit(rc & 0x80000000);
|
|
|
|
rc = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
do_cflag_bit(0);
|
|
|
|
rc = 0;
|
|
|
|
}
|
|
|
|
write_register(rd, rc);
|
2022-11-28 21:12:48 +00:00
|
|
|
do_znflags(rc);
|
2021-07-08 13:46:41 +00:00
|
|
|
INC_SHIFT_CYCLES;
|
2019-02-19 23:32:48 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//MOV(1) immediate
|
|
|
|
case Op::mov1: {
|
2019-02-22 19:11:19 +00:00
|
|
|
rb = (inst >> 0) & 0xFF;
|
|
|
|
rd = (inst >> 8) & 0x07;
|
2019-02-19 23:32:48 +00:00
|
|
|
DO_DISS(statusMsg << "movs r" << dec << rd << ",#0x" << Base::HEX2 << rb << endl);
|
|
|
|
write_register(rd, rb);
|
2022-11-28 21:12:48 +00:00
|
|
|
do_znflags(rb);
|
2019-02-19 23:32:48 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//MOV(2) two low registers
|
|
|
|
case Op::mov2: {
|
2019-02-22 19:11:19 +00:00
|
|
|
rd = (inst >> 0) & 7;
|
|
|
|
rn = (inst >> 3) & 7;
|
2019-02-19 23:32:48 +00:00
|
|
|
DO_DISS(statusMsg << "movs r" << dec << rd << ",r" << dec << rn << endl);
|
|
|
|
rc = read_register(rn);
|
|
|
|
//fprintf(stderr,"0x%08X\n",rc);
|
|
|
|
write_register(rd, rc);
|
2022-11-28 21:12:48 +00:00
|
|
|
do_znflags(rc);
|
2019-02-19 23:32:48 +00:00
|
|
|
do_cflag_bit(0);
|
|
|
|
do_vflag_bit(0);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//MOV(3)
|
|
|
|
case Op::mov3: {
|
2019-02-22 19:11:19 +00:00
|
|
|
rd = (inst >> 0) & 0x7;
|
|
|
|
rd |= (inst >> 4) & 0x8;
|
|
|
|
rm = (inst >> 3) & 0xF;
|
2019-02-19 23:32:48 +00:00
|
|
|
DO_DISS(statusMsg << "mov r" << dec << rd << ",r" << dec << rm << endl);
|
|
|
|
rc = read_register(rm);
|
|
|
|
if((rd == 14) && (rm == 15))
|
|
|
|
{
|
|
|
|
//printf("mov lr,pc warning 0x%08X\n",pc-2);
|
|
|
|
//rc|=1;
|
|
|
|
}
|
|
|
|
if(rd == 15)
|
|
|
|
{
|
2019-02-23 11:05:53 +00:00
|
|
|
//rc &= ~1; //write_register may do this as well
|
2019-02-19 23:32:48 +00:00
|
|
|
rc += 2; //The program counter is special
|
|
|
|
}
|
|
|
|
write_register(rd, rc);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//MUL
|
|
|
|
case Op::mul: {
|
2019-02-22 19:11:19 +00:00
|
|
|
rd = (inst >> 0) & 0x7;
|
|
|
|
rm = (inst >> 3) & 0x7;
|
2019-02-19 23:32:48 +00:00
|
|
|
DO_DISS(statusMsg << "muls r" << dec << rd << ",r" << dec << rm << endl);
|
|
|
|
ra = read_register(rd);
|
|
|
|
rb = read_register(rm);
|
2021-06-24 14:21:00 +00:00
|
|
|
#ifdef DEBUGGER_SUPPORT
|
2021-06-15 16:55:32 +00:00
|
|
|
if((rb & 0xffffff00) == 0 || (rb & 0xffffff00) == 0xffffff00) // -2^8 <= rb < 2^8
|
2021-06-24 14:21:00 +00:00
|
|
|
{
|
|
|
|
INC_I_CYCLES;
|
|
|
|
}
|
2021-06-15 16:55:32 +00:00
|
|
|
else if((rb & 0xffff0000) == 0 || (rb & 0xffff0000) == 0xffff0000) // -2^16 <= rb < 2^16
|
2021-06-24 14:21:00 +00:00
|
|
|
{
|
|
|
|
INC_I_CYCLES_M(2);
|
|
|
|
}
|
2021-06-15 16:55:32 +00:00
|
|
|
else if((rb & 0xff000000) == 0 || (rb & 0xff000000) == 0xff000000) // -2^24 <= rb < 2^24
|
2021-06-24 14:21:00 +00:00
|
|
|
{
|
|
|
|
INC_I_CYCLES_M(3);
|
|
|
|
}
|
2021-06-15 16:55:32 +00:00
|
|
|
else
|
2021-06-24 14:21:00 +00:00
|
|
|
{
|
|
|
|
INC_I_CYCLES_M(4);
|
|
|
|
}
|
2021-06-15 16:55:32 +00:00
|
|
|
#endif
|
2019-02-19 23:32:48 +00:00
|
|
|
rc = ra * rb;
|
|
|
|
write_register(rd, rc);
|
2022-11-28 21:12:48 +00:00
|
|
|
do_znflags(rc);
|
2019-02-19 23:32:48 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//MVN
|
|
|
|
case Op::mvn: {
|
2019-02-22 19:11:19 +00:00
|
|
|
rd = (inst >> 0) & 0x7;
|
|
|
|
rm = (inst >> 3) & 0x7;
|
2019-02-19 23:32:48 +00:00
|
|
|
DO_DISS(statusMsg << "mvns r" << dec << rd << ",r" << dec << rm << endl);
|
|
|
|
ra = read_register(rm);
|
|
|
|
rc = (~ra);
|
|
|
|
write_register(rd, rc);
|
2022-11-28 21:12:48 +00:00
|
|
|
do_znflags(rc);
|
2019-02-19 23:32:48 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//NEG
|
|
|
|
case Op::neg: {
|
2019-02-22 19:11:19 +00:00
|
|
|
rd = (inst >> 0) & 0x7;
|
|
|
|
rm = (inst >> 3) & 0x7;
|
2019-02-19 23:32:48 +00:00
|
|
|
DO_DISS(statusMsg << "negs r" << dec << rd << ",r" << dec << rm << endl);
|
|
|
|
ra = read_register(rm);
|
|
|
|
rc = 0 - ra;
|
|
|
|
write_register(rd, rc);
|
2022-11-28 21:12:48 +00:00
|
|
|
do_znflags(rc);
|
|
|
|
do_cvflag(0, ~ra, 1);
|
2019-02-19 23:32:48 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//ORR
|
|
|
|
case Op::orr: {
|
2019-02-22 19:11:19 +00:00
|
|
|
rd = (inst >> 0) & 0x7;
|
|
|
|
rm = (inst >> 3) & 0x7;
|
2019-02-19 23:32:48 +00:00
|
|
|
DO_DISS(statusMsg << "orrs r" << dec << rd << ",r" << dec << rm << endl);
|
|
|
|
ra = read_register(rd);
|
|
|
|
rb = read_register(rm);
|
|
|
|
rc = ra | rb;
|
|
|
|
write_register(rd, rc);
|
2022-11-28 21:12:48 +00:00
|
|
|
do_znflags(rc);
|
2019-02-19 23:32:48 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2011-04-16 16:53:41 +00:00
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//POP
|
|
|
|
case Op::pop: {
|
|
|
|
#if defined(THUMB_DISS)
|
|
|
|
statusMsg << "pop {";
|
|
|
|
for(ra=0,rb=0x01,rc=0;rb;rb=(rb<<1)&0xFF,++ra)
|
|
|
|
{
|
|
|
|
if(inst&rb)
|
|
|
|
{
|
|
|
|
if(rc) statusMsg << ",";
|
|
|
|
statusMsg << "r" << dec << ra;
|
|
|
|
rc++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(inst&0x100)
|
2011-04-16 16:53:41 +00:00
|
|
|
{
|
2011-05-24 16:04:48 +00:00
|
|
|
if(rc) statusMsg << ",";
|
2019-02-19 23:32:48 +00:00
|
|
|
statusMsg << "pc";
|
2011-04-16 16:53:41 +00:00
|
|
|
}
|
2019-02-19 23:32:48 +00:00
|
|
|
statusMsg << "}" << endl;
|
|
|
|
#endif
|
2021-06-27 07:44:42 +00:00
|
|
|
bool first = true;
|
2011-01-16 21:23:03 +00:00
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
sp = read_register(13);
|
|
|
|
for(ra = 0, rb = 0x01; rb; rb = (rb << 1) & 0xFF, ++ra)
|
|
|
|
{
|
|
|
|
if(inst & rb)
|
|
|
|
{
|
|
|
|
write_register(ra, read32(sp));
|
2021-06-27 07:44:42 +00:00
|
|
|
if(first)
|
|
|
|
{
|
|
|
|
INC_N_CYCLES(sp, AccessType::data);
|
|
|
|
first = false;
|
|
|
|
}
|
|
|
|
else
|
2021-10-24 00:21:13 +00:00
|
|
|
{
|
2021-06-27 07:44:42 +00:00
|
|
|
INC_S_CYCLES(sp, AccessType::data);
|
2021-10-24 00:21:13 +00:00
|
|
|
}
|
2019-02-19 23:32:48 +00:00
|
|
|
sp += 4;
|
|
|
|
}
|
|
|
|
}
|
2021-07-01 13:40:49 +00:00
|
|
|
INC_I_CYCLES; // ??? (copied from ldmia)
|
2019-02-19 23:32:48 +00:00
|
|
|
if(inst & 0x100)
|
2011-04-16 16:53:41 +00:00
|
|
|
{
|
2019-02-19 23:32:48 +00:00
|
|
|
rc = read32(sp);
|
2021-06-27 07:44:42 +00:00
|
|
|
if(first)
|
2021-06-27 14:55:21 +00:00
|
|
|
{
|
2021-06-27 07:44:42 +00:00
|
|
|
INC_N_CYCLES(sp, AccessType::data);
|
2021-06-27 14:55:21 +00:00
|
|
|
}
|
2021-06-27 07:44:42 +00:00
|
|
|
else
|
2021-06-27 14:55:21 +00:00
|
|
|
{
|
2021-06-27 07:44:42 +00:00
|
|
|
INC_S_CYCLES(sp, AccessType::data);
|
2021-06-27 14:55:21 +00:00
|
|
|
}
|
2019-02-19 23:32:48 +00:00
|
|
|
rc += 2;
|
|
|
|
write_register(15, rc);
|
2016-02-27 19:58:20 +00:00
|
|
|
sp += 4;
|
2011-04-16 16:53:41 +00:00
|
|
|
}
|
2019-02-19 23:32:48 +00:00
|
|
|
write_register(13, sp);
|
|
|
|
return 0;
|
2011-01-16 21:23:03 +00:00
|
|
|
}
|
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//PUSH
|
|
|
|
case Op::push: {
|
|
|
|
#if defined(THUMB_DISS)
|
|
|
|
statusMsg << "push {";
|
|
|
|
for(ra=0,rb=0x01,rc=0;rb;rb=(rb<<1)&0xFF,++ra)
|
|
|
|
{
|
|
|
|
if(inst&rb)
|
|
|
|
{
|
|
|
|
if(rc) statusMsg << ",";
|
|
|
|
statusMsg << "r" << dec << ra;
|
|
|
|
rc++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(inst&0x100)
|
2011-04-16 16:53:41 +00:00
|
|
|
{
|
2011-05-24 16:04:48 +00:00
|
|
|
if(rc) statusMsg << ",";
|
2019-02-19 23:32:48 +00:00
|
|
|
statusMsg << "lr";
|
2011-04-16 16:53:41 +00:00
|
|
|
}
|
2019-02-19 23:32:48 +00:00
|
|
|
statusMsg << "}" << endl;
|
|
|
|
#endif
|
2011-01-16 21:23:03 +00:00
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
sp = read_register(13);
|
|
|
|
//fprintf(stderr,"sp 0x%08X\n",sp);
|
|
|
|
for(ra = 0, rb = 0x01, rc = 0; rb; rb = (rb << 1) & 0xFF, ++ra)
|
2011-04-16 16:53:41 +00:00
|
|
|
{
|
2019-02-19 23:32:48 +00:00
|
|
|
if(inst & rb)
|
|
|
|
{
|
|
|
|
++rc;
|
|
|
|
}
|
2011-04-16 16:53:41 +00:00
|
|
|
}
|
2019-02-19 23:32:48 +00:00
|
|
|
if(inst & 0x100) ++rc;
|
|
|
|
rc <<= 2;
|
|
|
|
sp -= rc;
|
2021-06-27 07:44:42 +00:00
|
|
|
bool first = true;
|
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
rd = sp;
|
|
|
|
for(ra = 0, rb = 0x01; rb; rb = (rb << 1) & 0xFF, ++ra)
|
2011-04-16 16:53:41 +00:00
|
|
|
{
|
2019-02-19 23:32:48 +00:00
|
|
|
if(inst & rb)
|
|
|
|
{
|
|
|
|
write32(rd, read_register(ra));
|
2021-06-27 07:44:42 +00:00
|
|
|
if(first)
|
|
|
|
{
|
|
|
|
INC_N_CYCLES(rd, AccessType::data);
|
|
|
|
first = false;
|
|
|
|
}
|
|
|
|
else
|
2021-10-24 00:21:13 +00:00
|
|
|
{
|
2021-06-27 07:44:42 +00:00
|
|
|
INC_S_CYCLES(rd, AccessType::data);
|
2021-10-24 00:21:13 +00:00
|
|
|
}
|
2019-02-19 23:32:48 +00:00
|
|
|
rd += 4;
|
|
|
|
}
|
2011-04-16 16:53:41 +00:00
|
|
|
}
|
2019-02-19 23:32:48 +00:00
|
|
|
if(inst & 0x100)
|
2016-02-27 19:58:20 +00:00
|
|
|
{
|
2019-02-19 23:32:48 +00:00
|
|
|
rc = read_register(14);
|
|
|
|
write32(rd, rc);
|
2021-06-27 07:44:42 +00:00
|
|
|
if(first)
|
2021-06-27 14:55:21 +00:00
|
|
|
{
|
2021-06-27 07:44:42 +00:00
|
|
|
INC_N_CYCLES(rd, AccessType::data);
|
2021-06-27 14:55:21 +00:00
|
|
|
}
|
2021-06-27 07:44:42 +00:00
|
|
|
else
|
2021-06-27 14:55:21 +00:00
|
|
|
{
|
2021-06-27 07:44:42 +00:00
|
|
|
INC_S_CYCLES(rd, AccessType::data);
|
2021-06-27 14:55:21 +00:00
|
|
|
}
|
2019-02-19 23:32:48 +00:00
|
|
|
if((rc & 1) == 0)
|
|
|
|
{
|
|
|
|
// FIXME fprintf(stderr,"push {lr} with an ARM address pc 0x%08X popped 0x%08X\n",pc,rc);
|
|
|
|
}
|
2016-02-27 19:58:20 +00:00
|
|
|
}
|
2019-02-19 23:32:48 +00:00
|
|
|
write_register(13, sp);
|
2021-07-01 13:40:49 +00:00
|
|
|
FETCH_TYPE_N; // ??? (copied from stmia)
|
2019-02-19 23:32:48 +00:00
|
|
|
return 0;
|
2011-01-16 21:23:03 +00:00
|
|
|
}
|
2011-04-16 16:53:41 +00:00
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//REV
|
|
|
|
case Op::rev: {
|
2019-02-22 19:11:19 +00:00
|
|
|
rd = (inst >> 0) & 0x7;
|
|
|
|
rn = (inst >> 3) & 0x7;
|
2019-02-19 23:32:48 +00:00
|
|
|
DO_DISS(statusMsg << "rev r" << dec << rd << ",r" << dec << rn << endl);
|
|
|
|
ra = read_register(rn);
|
|
|
|
rc = ((ra >> 0) & 0xFF) << 24;
|
|
|
|
rc |= ((ra >> 8) & 0xFF) << 16;
|
|
|
|
rc |= ((ra >> 16) & 0xFF) << 8;
|
|
|
|
rc |= ((ra >> 24) & 0xFF) << 0;
|
|
|
|
write_register(rd, rc);
|
|
|
|
return 0;
|
|
|
|
}
|
2011-01-16 21:23:03 +00:00
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//REV16
|
|
|
|
case Op::rev16: {
|
2019-02-22 19:11:19 +00:00
|
|
|
rd = (inst >> 0) & 0x7;
|
|
|
|
rn = (inst >> 3) & 0x7;
|
2019-02-19 23:32:48 +00:00
|
|
|
DO_DISS(statusMsg << "rev16 r" << dec << rd << ",r" << dec << rn << endl);
|
|
|
|
ra = read_register(rn);
|
|
|
|
rc = ((ra >> 0) & 0xFF) << 8;
|
|
|
|
rc |= ((ra >> 8) & 0xFF) << 0;
|
|
|
|
rc |= ((ra >> 16) & 0xFF) << 24;
|
|
|
|
rc |= ((ra >> 24) & 0xFF) << 16;
|
|
|
|
write_register(rd, rc);
|
|
|
|
return 0;
|
|
|
|
}
|
2011-01-16 21:23:03 +00:00
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//REVSH
|
|
|
|
case Op::revsh: {
|
2019-02-22 19:11:19 +00:00
|
|
|
rd = (inst >> 0) & 0x7;
|
|
|
|
rn = (inst >> 3) & 0x7;
|
2019-02-19 23:32:48 +00:00
|
|
|
DO_DISS(statusMsg << "revsh r" << dec << rd << ",r" << dec << rn << endl);
|
|
|
|
ra = read_register(rn);
|
|
|
|
rc = ((ra >> 0) & 0xFF) << 8;
|
|
|
|
rc |= ((ra >> 8) & 0xFF) << 0;
|
|
|
|
if(rc & 0x8000) rc |= 0xFFFF0000;
|
|
|
|
else rc &= 0x0000FFFF;
|
|
|
|
write_register(rd, rc);
|
|
|
|
return 0;
|
2011-01-16 21:23:03 +00:00
|
|
|
}
|
2019-02-19 23:32:48 +00:00
|
|
|
|
|
|
|
//ROR
|
|
|
|
case Op::ror: {
|
2019-02-22 19:11:19 +00:00
|
|
|
rd = (inst >> 0) & 0x7;
|
|
|
|
rs = (inst >> 3) & 0x7;
|
2019-02-19 23:32:48 +00:00
|
|
|
DO_DISS(statusMsg << "rors r" << dec << rd << ",r" << dec << rs << endl);
|
|
|
|
rc = read_register(rd);
|
|
|
|
ra = read_register(rs);
|
|
|
|
ra &= 0xFF;
|
2016-02-27 19:58:20 +00:00
|
|
|
if(ra == 0)
|
2011-04-16 16:53:41 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2019-02-19 23:32:48 +00:00
|
|
|
ra &= 0x1F;
|
|
|
|
if(ra == 0)
|
|
|
|
{
|
|
|
|
do_cflag_bit(rc & 0x80000000);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
do_cflag_bit(rc & (1 << (ra-1)));
|
|
|
|
rb = rc << (32-ra);
|
|
|
|
rc >>= ra;
|
|
|
|
rc |= rb;
|
|
|
|
}
|
2011-04-16 16:53:41 +00:00
|
|
|
}
|
2019-02-19 23:32:48 +00:00
|
|
|
write_register(rd, rc);
|
2022-11-28 21:12:48 +00:00
|
|
|
do_znflags(rc);
|
2021-07-08 13:46:41 +00:00
|
|
|
INC_SHIFT_CYCLES;
|
2019-02-19 23:32:48 +00:00
|
|
|
return 0;
|
2011-01-16 21:23:03 +00:00
|
|
|
}
|
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//SBC
|
|
|
|
case Op::sbc: {
|
2019-02-22 19:11:19 +00:00
|
|
|
rd = (inst >> 0) & 0x7;
|
|
|
|
rm = (inst >> 3) & 0x7;
|
2019-02-19 23:32:48 +00:00
|
|
|
DO_DISS(statusMsg << "sbc r" << dec << rd << ",r" << dec << rm << endl);
|
|
|
|
ra = read_register(rd);
|
|
|
|
rb = read_register(rm);
|
|
|
|
rc = ra - rb;
|
2022-11-28 21:12:48 +00:00
|
|
|
if(!cFlag) --rc;
|
2019-02-19 23:32:48 +00:00
|
|
|
write_register(rd, rc);
|
2022-11-28 21:12:48 +00:00
|
|
|
do_znflags(rc);
|
|
|
|
if(cFlag) do_cvflag(ra, ~rb, 1);
|
|
|
|
else do_cvflag(ra, ~rb, 0);
|
2019-02-19 23:32:48 +00:00
|
|
|
return 0;
|
2016-02-27 19:58:20 +00:00
|
|
|
}
|
2011-01-16 21:23:03 +00:00
|
|
|
|
2019-02-23 15:09:00 +00:00
|
|
|
#ifndef UNSAFE_OPTIMIZATIONS
|
2019-02-19 23:32:48 +00:00
|
|
|
//SETEND
|
|
|
|
case Op::setend: {
|
|
|
|
statusMsg << "setend not implemented" << endl;
|
|
|
|
return 1;
|
|
|
|
}
|
2019-02-23 15:09:00 +00:00
|
|
|
#endif
|
2011-01-16 21:23:03 +00:00
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//STMIA
|
|
|
|
case Op::stmia: {
|
2019-02-22 19:11:19 +00:00
|
|
|
rn = (inst >> 8) & 0x7;
|
2019-02-19 23:32:48 +00:00
|
|
|
#if defined(THUMB_DISS)
|
|
|
|
statusMsg << "stmia r" << dec << rn << "!,{";
|
|
|
|
for(ra=0,rb=0x01,rc=0;rb;rb=(rb<<1)&0xFF,++ra)
|
2011-04-16 16:53:41 +00:00
|
|
|
{
|
2019-02-19 23:32:48 +00:00
|
|
|
if(inst & rb)
|
|
|
|
{
|
|
|
|
if(rc) statusMsg << ",";
|
|
|
|
statusMsg << "r" << dec << ra;
|
|
|
|
rc++;
|
|
|
|
}
|
2011-04-16 16:53:41 +00:00
|
|
|
}
|
2019-02-19 23:32:48 +00:00
|
|
|
statusMsg << "}" << endl;
|
|
|
|
#endif
|
2021-06-24 14:21:00 +00:00
|
|
|
bool first = true;
|
2012-12-26 21:16:34 +00:00
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
sp = read_register(rn);
|
|
|
|
for(ra = 0, rb = 0x01; rb; rb = (rb << 1) & 0xFF, ++ra)
|
2011-04-16 16:53:41 +00:00
|
|
|
{
|
2019-02-19 23:32:48 +00:00
|
|
|
if(inst & rb)
|
|
|
|
{
|
|
|
|
write32(sp, read_register(ra));
|
2021-06-24 14:21:00 +00:00
|
|
|
if(first)
|
|
|
|
{
|
2021-06-27 07:44:42 +00:00
|
|
|
INC_N_CYCLES(sp, AccessType::data);
|
2021-06-24 14:21:00 +00:00
|
|
|
first = false;
|
|
|
|
}
|
|
|
|
else
|
2021-10-24 00:21:13 +00:00
|
|
|
{
|
2021-06-27 07:44:42 +00:00
|
|
|
INC_S_CYCLES(sp, AccessType::data);
|
2021-10-24 00:21:13 +00:00
|
|
|
}
|
2019-02-19 23:32:48 +00:00
|
|
|
sp += 4;
|
|
|
|
}
|
2011-04-16 16:53:41 +00:00
|
|
|
}
|
2019-02-19 23:32:48 +00:00
|
|
|
write_register(rn, sp);
|
2021-06-24 14:21:00 +00:00
|
|
|
FETCH_TYPE_N;
|
2019-02-19 23:32:48 +00:00
|
|
|
return 0;
|
2011-01-16 21:23:03 +00:00
|
|
|
}
|
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//STR(1)
|
|
|
|
case Op::str1: {
|
2019-02-22 19:11:19 +00:00
|
|
|
rd = (inst >> 0) & 0x07;
|
|
|
|
rn = (inst >> 3) & 0x07;
|
|
|
|
rb = (inst >> 6) & 0x1F;
|
|
|
|
rb <<= 2;
|
2019-02-19 23:32:48 +00:00
|
|
|
DO_DISS(statusMsg << "str r" << dec << rd << ",[r" << dec << rn << ",#0x" << Base::HEX2 << rb << "]" << endl);
|
|
|
|
rb = read_register(rn) + rb;
|
|
|
|
rc = read_register(rd);
|
|
|
|
write32(rb, rc);
|
2021-07-08 13:46:41 +00:00
|
|
|
INC_STR_CYCLES;
|
2019-02-19 23:32:48 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2011-01-16 21:23:03 +00:00
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//STR(2)
|
|
|
|
case Op::str2: {
|
2019-02-22 19:11:19 +00:00
|
|
|
rd = (inst >> 0) & 0x7;
|
|
|
|
rn = (inst >> 3) & 0x7;
|
|
|
|
rm = (inst >> 6) & 0x7;
|
2019-02-19 23:32:48 +00:00
|
|
|
DO_DISS(statusMsg << "str r" << dec << rd << ",[r" << dec << rn << ",r" << dec << rm << "]" << endl);
|
|
|
|
rb = read_register(rn) + read_register(rm);
|
|
|
|
rc = read_register(rd);
|
|
|
|
write32(rb, rc);
|
2021-07-08 13:46:41 +00:00
|
|
|
INC_STR_CYCLES;
|
2019-02-19 23:32:48 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2011-01-16 21:23:03 +00:00
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//STR(3)
|
|
|
|
case Op::str3: {
|
2019-02-22 19:11:19 +00:00
|
|
|
rb = (inst >> 0) & 0xFF;
|
|
|
|
rd = (inst >> 8) & 0x07;
|
|
|
|
rb <<= 2;
|
2019-02-19 23:32:48 +00:00
|
|
|
DO_DISS(statusMsg << "str r" << dec << rd << ",[SP,#0x" << Base::HEX2 << rb << "]" << endl);
|
|
|
|
rb = read_register(13) + rb;
|
|
|
|
//fprintf(stderr,"0x%08X\n",rb);
|
|
|
|
rc = read_register(rd);
|
|
|
|
write32(rb, rc);
|
2021-07-08 13:46:41 +00:00
|
|
|
INC_STR_CYCLES;
|
2019-02-19 23:32:48 +00:00
|
|
|
return 0;
|
2011-01-16 21:23:03 +00:00
|
|
|
}
|
2019-02-19 23:32:48 +00:00
|
|
|
|
|
|
|
//STRB(1)
|
|
|
|
case Op::strb1: {
|
2019-02-22 19:11:19 +00:00
|
|
|
rd = (inst >> 0) & 0x07;
|
|
|
|
rn = (inst >> 3) & 0x07;
|
|
|
|
rb = (inst >> 6) & 0x1F;
|
2019-02-19 23:32:48 +00:00
|
|
|
DO_DISS(statusMsg << "strb r" << dec << rd << ",[r" << dec << rn << ",#0x" << Base::HEX8 << rb << "]" << endl);
|
|
|
|
rb = read_register(rn) + rb;
|
|
|
|
rc = read_register(rd);
|
2019-02-25 18:50:33 +00:00
|
|
|
#ifndef UNSAFE_OPTIMIZATIONS
|
2019-12-28 04:15:09 +00:00
|
|
|
ra = read16(rb & (~1U));
|
2019-02-25 18:50:33 +00:00
|
|
|
#else
|
|
|
|
ra = read16(rb);
|
|
|
|
#endif
|
2019-02-19 23:32:48 +00:00
|
|
|
if(rb & 1)
|
|
|
|
{
|
|
|
|
ra &= 0x00FF;
|
|
|
|
ra |= rc << 8;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ra &= 0xFF00;
|
|
|
|
ra |= rc & 0x00FF;
|
|
|
|
}
|
2019-12-28 04:15:09 +00:00
|
|
|
write16(rb & (~1U), ra & 0xFFFF);
|
2021-07-08 13:46:41 +00:00
|
|
|
INC_STRB_CYCLES;
|
2019-02-19 23:32:48 +00:00
|
|
|
return 0;
|
2011-01-16 21:23:03 +00:00
|
|
|
}
|
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//STRB(2)
|
|
|
|
case Op::strb2: {
|
2019-02-22 19:11:19 +00:00
|
|
|
rd = (inst >> 0) & 0x7;
|
|
|
|
rn = (inst >> 3) & 0x7;
|
|
|
|
rm = (inst >> 6) & 0x7;
|
2019-02-19 23:32:48 +00:00
|
|
|
DO_DISS(statusMsg << "strb r" << dec << rd << ",[r" << dec << rn << ",r" << rm << "]" << endl);
|
|
|
|
rb = read_register(rn) + read_register(rm);
|
|
|
|
rc = read_register(rd);
|
2019-02-25 18:50:33 +00:00
|
|
|
#ifndef UNSAFE_OPTIMIZATIONS
|
2019-12-28 04:15:09 +00:00
|
|
|
ra = read16(rb & (~1U));
|
2019-02-25 18:50:33 +00:00
|
|
|
#else
|
|
|
|
ra = read16(rb);
|
|
|
|
#endif
|
2019-02-19 23:32:48 +00:00
|
|
|
if(rb & 1)
|
|
|
|
{
|
|
|
|
ra &= 0x00FF;
|
|
|
|
ra |= rc << 8;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ra &= 0xFF00;
|
|
|
|
ra |= rc & 0x00FF;
|
|
|
|
}
|
2019-12-28 04:15:09 +00:00
|
|
|
write16(rb & (~1U), ra & 0xFFFF);
|
2021-07-08 13:46:41 +00:00
|
|
|
INC_STRB_CYCLES;
|
2019-02-19 23:32:48 +00:00
|
|
|
return 0;
|
2011-01-16 21:23:03 +00:00
|
|
|
}
|
2019-02-19 23:32:48 +00:00
|
|
|
|
|
|
|
//STRH(1)
|
|
|
|
case Op::strh1: {
|
2019-02-22 19:11:19 +00:00
|
|
|
rd = (inst >> 0) & 0x07;
|
|
|
|
rn = (inst >> 3) & 0x07;
|
|
|
|
rb = (inst >> 6) & 0x1F;
|
|
|
|
rb <<= 1;
|
2019-02-19 23:32:48 +00:00
|
|
|
DO_DISS(statusMsg << "strh r" << dec << rd << ",[r" << dec << rn << ",#0x" << Base::HEX2 << rb << "]" << endl);
|
|
|
|
rb = read_register(rn) + rb;
|
|
|
|
rc= read_register(rd);
|
|
|
|
write16(rb, rc & 0xFFFF);
|
2021-07-08 13:46:41 +00:00
|
|
|
INC_STR_CYCLES;
|
2019-02-19 23:32:48 +00:00
|
|
|
return 0;
|
2011-01-16 21:23:03 +00:00
|
|
|
}
|
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//STRH(2)
|
|
|
|
case Op::strh2: {
|
2019-02-22 19:11:19 +00:00
|
|
|
rd = (inst >> 0) & 0x7;
|
|
|
|
rn = (inst >> 3) & 0x7;
|
|
|
|
rm = (inst >> 6) & 0x7;
|
2019-02-19 23:32:48 +00:00
|
|
|
DO_DISS(statusMsg << "strh r" << dec << rd << ",[r" << dec << rn << ",r" << dec << rm << "]" << endl);
|
|
|
|
rb = read_register(rn) + read_register(rm);
|
|
|
|
rc = read_register(rd);
|
|
|
|
write16(rb, rc & 0xFFFF);
|
2021-07-08 13:46:41 +00:00
|
|
|
INC_STR_CYCLES;
|
2019-02-19 23:32:48 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2011-01-16 21:23:03 +00:00
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//SUB(1)
|
|
|
|
case Op::sub1: {
|
2019-02-22 19:11:19 +00:00
|
|
|
rd = (inst >> 0) & 0x7;
|
|
|
|
rn = (inst >> 3) & 0x7;
|
|
|
|
rb = (inst >> 6) & 0x7;
|
2019-02-19 23:32:48 +00:00
|
|
|
DO_DISS(statusMsg << "subs r" << dec << rd << ",r" << dec << rn << ",#0x" << Base::HEX2 << rb << endl);
|
|
|
|
ra = read_register(rn);
|
|
|
|
rc = ra - rb;
|
|
|
|
write_register(rd, rc);
|
2022-11-28 21:12:48 +00:00
|
|
|
do_znflags(rc);
|
|
|
|
do_cvflag(ra, ~rb, 1);
|
2019-02-19 23:32:48 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2011-01-16 21:23:03 +00:00
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//SUB(2)
|
|
|
|
case Op::sub2: {
|
2019-02-22 19:11:19 +00:00
|
|
|
rb = (inst >> 0) & 0xFF;
|
|
|
|
rd = (inst >> 8) & 0x07;
|
2019-02-19 23:32:48 +00:00
|
|
|
DO_DISS(statusMsg << "subs r" << dec << rd << ",#0x" << Base::HEX2 << rb << endl);
|
|
|
|
ra = read_register(rd);
|
|
|
|
rc = ra - rb;
|
|
|
|
write_register(rd, rc);
|
2022-11-28 21:12:48 +00:00
|
|
|
do_znflags(rc);
|
|
|
|
do_cvflag(ra, ~rb, 1);
|
2019-02-19 23:32:48 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2011-01-16 21:23:03 +00:00
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//SUB(3)
|
|
|
|
case Op::sub3: {
|
2019-02-22 19:11:19 +00:00
|
|
|
rd = (inst >> 0) & 0x7;
|
|
|
|
rn = (inst >> 3) & 0x7;
|
|
|
|
rm = (inst >> 6) & 0x7;
|
2019-02-19 23:32:48 +00:00
|
|
|
DO_DISS(statusMsg << "subs r" << dec << rd << ",r" << dec << rn << ",r" << dec << rm << endl);
|
|
|
|
ra = read_register(rn);
|
|
|
|
rb = read_register(rm);
|
|
|
|
rc = ra - rb;
|
|
|
|
write_register(rd, rc);
|
2022-11-28 21:12:48 +00:00
|
|
|
do_znflags(rc);
|
|
|
|
do_cvflag(ra, ~rb, 1);
|
2019-02-19 23:32:48 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2011-01-16 21:23:03 +00:00
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//SUB(4)
|
|
|
|
case Op::sub4: {
|
2019-02-22 19:11:19 +00:00
|
|
|
rb = inst & 0x7F;
|
|
|
|
rb <<= 2;
|
2019-02-19 23:32:48 +00:00
|
|
|
DO_DISS(statusMsg << "sub SP,#0x" << Base::HEX2 << rb << endl);
|
|
|
|
ra = read_register(13);
|
|
|
|
ra -= rb;
|
|
|
|
write_register(13, ra);
|
|
|
|
return 0;
|
|
|
|
}
|
2011-01-16 21:23:03 +00:00
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//SWI
|
2022-11-28 21:12:48 +00:00
|
|
|
case Op::swi: { // never used
|
2019-02-22 19:11:19 +00:00
|
|
|
rb = inst & 0xFF;
|
2019-02-19 23:32:48 +00:00
|
|
|
DO_DISS(statusMsg << "swi 0x" << Base::HEX2 << rb << endl);
|
2011-01-16 21:23:03 +00:00
|
|
|
|
2022-11-28 21:12:48 +00:00
|
|
|
//if(rb == 0xCC)
|
|
|
|
//{
|
|
|
|
// write_register(0, cpsr);
|
|
|
|
// return 0;
|
|
|
|
//}
|
|
|
|
//else
|
2019-02-19 23:32:48 +00:00
|
|
|
{
|
2019-02-24 08:23:57 +00:00
|
|
|
#if defined(THUMB_DISS)
|
2019-02-19 23:32:48 +00:00
|
|
|
statusMsg << endl << endl << "swi 0x" << Base::HEX2 << rb << endl;
|
2019-02-24 08:23:57 +00:00
|
|
|
#endif
|
2019-02-19 23:32:48 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
2016-02-27 19:58:20 +00:00
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//SXTB
|
|
|
|
case Op::sxtb: {
|
2019-02-22 19:11:19 +00:00
|
|
|
rd = (inst >> 0) & 0x7;
|
|
|
|
rm = (inst >> 3) & 0x7;
|
2019-02-19 23:32:48 +00:00
|
|
|
DO_DISS(statusMsg << "sxtb r" << dec << rd << ",r" << dec << rm << endl);
|
|
|
|
ra = read_register(rm);
|
|
|
|
rc = ra & 0xFF;
|
|
|
|
if(rc & 0x80)
|
2019-12-27 22:52:09 +00:00
|
|
|
rc |= (~0U) << 8;
|
2019-02-19 23:32:48 +00:00
|
|
|
write_register(rd, rc);
|
2016-02-27 19:58:20 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2011-01-16 21:23:03 +00:00
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//SXTH
|
|
|
|
case Op::sxth: {
|
2019-02-22 19:11:19 +00:00
|
|
|
rd = (inst >> 0) & 0x7;
|
|
|
|
rm = (inst >> 3) & 0x7;
|
2019-02-19 23:32:48 +00:00
|
|
|
DO_DISS(statusMsg << "sxth r" << dec << rd << ",r" << dec << rm << endl);
|
|
|
|
ra = read_register(rm);
|
|
|
|
rc = ra & 0xFFFF;
|
|
|
|
if(rc & 0x8000)
|
2019-12-27 22:52:09 +00:00
|
|
|
rc |= (~0U) << 16;
|
2019-02-19 23:32:48 +00:00
|
|
|
write_register(rd, rc);
|
|
|
|
return 0;
|
|
|
|
}
|
2011-01-16 21:23:03 +00:00
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//TST
|
|
|
|
case Op::tst: {
|
2019-02-22 19:11:19 +00:00
|
|
|
rn = (inst >> 0) & 0x7;
|
|
|
|
rm = (inst >> 3) & 0x7;
|
2019-02-19 23:32:48 +00:00
|
|
|
DO_DISS(statusMsg << "tst r" << dec << rn << ",r" << dec << rm << endl);
|
|
|
|
ra = read_register(rn);
|
|
|
|
rb = read_register(rm);
|
|
|
|
rc = ra & rb;
|
2022-11-28 21:12:48 +00:00
|
|
|
do_znflags(rc);
|
2019-02-19 23:32:48 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2011-01-16 21:23:03 +00:00
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//UXTB
|
|
|
|
case Op::uxtb: {
|
2019-02-22 19:11:19 +00:00
|
|
|
rd = (inst >> 0) & 0x7;
|
|
|
|
rm = (inst >> 3) & 0x7;
|
2019-02-19 23:32:48 +00:00
|
|
|
DO_DISS(statusMsg << "uxtb r" << dec << rd << ",r" << dec << rm << endl);
|
|
|
|
ra = read_register(rm);
|
|
|
|
rc = ra & 0xFF;
|
|
|
|
write_register(rd, rc);
|
|
|
|
return 0;
|
|
|
|
}
|
2011-04-16 16:53:41 +00:00
|
|
|
|
2019-02-19 23:32:48 +00:00
|
|
|
//UXTH
|
|
|
|
case Op::uxth: {
|
2019-02-22 19:11:19 +00:00
|
|
|
rd = (inst >> 0) & 0x7;
|
|
|
|
rm = (inst >> 3) & 0x7;
|
2019-02-19 23:32:48 +00:00
|
|
|
DO_DISS(statusMsg << "uxth r" << dec << rd << ",r" << dec << rm << endl);
|
|
|
|
ra = read_register(rm);
|
|
|
|
rc = ra & 0xFFFF;
|
|
|
|
write_register(rd, rc);
|
|
|
|
return 0;
|
|
|
|
}
|
2011-04-16 16:53:41 +00:00
|
|
|
|
2021-06-25 18:10:06 +00:00
|
|
|
// Silence compiler
|
|
|
|
case Op::numOps:
|
|
|
|
break;
|
|
|
|
|
2019-03-10 14:29:07 +00:00
|
|
|
#ifndef UNSAFE_OPTIMIZATIONS
|
2019-02-19 23:32:48 +00:00
|
|
|
case Op::invalid:
|
|
|
|
break;
|
2019-03-10 14:29:07 +00:00
|
|
|
#else
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
#endif
|
2011-04-16 16:53:41 +00:00
|
|
|
}
|
|
|
|
|
2019-02-23 15:09:00 +00:00
|
|
|
#ifndef UNSAFE_OPTIMIZATIONS
|
2013-07-27 22:28:41 +00:00
|
|
|
statusMsg << "invalid instruction " << Base::HEX8 << pc << " " << Base::HEX4 << inst << endl;
|
2019-02-23 15:09:00 +00:00
|
|
|
#endif
|
2016-02-27 19:58:20 +00:00
|
|
|
return 1;
|
2011-01-16 21:23:03 +00:00
|
|
|
}
|
|
|
|
|
2011-03-09 14:21:32 +00:00
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
2016-02-27 19:58:20 +00:00
|
|
|
int Thumbulator::reset()
|
2011-01-16 21:23:03 +00:00
|
|
|
{
|
2019-12-27 22:52:09 +00:00
|
|
|
reg_norm.fill(0);
|
2011-01-16 21:23:03 +00:00
|
|
|
|
2020-09-13 20:16:34 +00:00
|
|
|
reg_norm[13] = cStack; // SP
|
|
|
|
reg_norm[14] = cBase; // LR
|
|
|
|
reg_norm[15] = (cStart + 2) | 1; // PC (+2 for pipeline, lower bit for THUMB)
|
2017-03-24 20:32:08 +00:00
|
|
|
|
2022-11-28 21:12:48 +00:00
|
|
|
znFlags = cFlag = vFlag = 0;
|
2016-02-27 19:58:20 +00:00
|
|
|
handler_mode = false;
|
|
|
|
|
2021-12-11 22:31:10 +00:00
|
|
|
systick_ctrl = 0x00000004;
|
|
|
|
systick_reload = 0x00000000;
|
|
|
|
systick_count = 0x00000000;
|
2016-02-27 19:58:20 +00:00
|
|
|
systick_calibrate = 0x00ABCDEF;
|
2011-01-16 21:23:03 +00:00
|
|
|
|
2011-04-16 16:53:41 +00:00
|
|
|
// fxq: don't care about below so much (maybe to guess timing???)
|
2019-02-23 15:09:00 +00:00
|
|
|
#ifndef UNSAFE_OPTIMIZATIONS
|
2021-06-24 14:21:00 +00:00
|
|
|
_stats.instructions = 0;
|
2019-03-09 23:10:45 +00:00
|
|
|
statusMsg.str("");
|
2019-02-23 15:09:00 +00:00
|
|
|
#endif
|
2021-06-24 14:21:00 +00:00
|
|
|
#ifdef THUMB_STATS
|
2021-07-08 13:46:41 +00:00
|
|
|
_stats.reads = _stats.writes
|
|
|
|
= _stats.nCylces = _stats.sCylces = _stats.iCylces
|
|
|
|
= _stats.branches = _stats.taken
|
|
|
|
= _stats.mamPrefetchHits = _stats.mamPrefetchMisses
|
|
|
|
= _stats.mamBranchHits = _stats.mamBranchMisses
|
|
|
|
= _stats.mamDataHits = _stats.mamDataMisses = 0;
|
2021-06-24 14:21:00 +00:00
|
|
|
#endif
|
|
|
|
#ifdef THUMB_CYCLE_COUNT
|
|
|
|
_totalCycles = 0;
|
|
|
|
#ifdef EMULATE_PIPELINE
|
|
|
|
_fetchPipeline = _memory0Pipeline = _memory1Pipeline = 0;
|
|
|
|
#endif
|
2021-06-15 15:31:08 +00:00
|
|
|
#endif
|
|
|
|
#ifdef COUNT_OPS
|
|
|
|
//memset(opCount, 0, sizeof(opCount));
|
2019-02-23 15:09:00 +00:00
|
|
|
#endif
|
2016-02-27 19:58:20 +00:00
|
|
|
return 0;
|
2011-01-16 21:23:03 +00:00
|
|
|
}
|
2011-04-16 16:53:41 +00:00
|
|
|
|
2011-11-07 22:50:23 +00:00
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
2021-06-27 07:44:42 +00:00
|
|
|
Thumbulator::ChipPropsType Thumbulator::setChipType(ChipType type)
|
2021-06-24 14:21:00 +00:00
|
|
|
{
|
2022-02-15 11:33:46 +00:00
|
|
|
if(type == ChipType::AUTO)
|
|
|
|
{
|
|
|
|
if(_chipType != ChipType::AUTO)
|
|
|
|
type = _chipType;
|
|
|
|
else if(searchPattern(0x3016E5C0, 3)) // alternate bus location (standard = 0x3015E5C0)
|
|
|
|
type = ChipType::LPC213x;
|
|
|
|
else if(romSize <= 0x8000) // LPC2104.. is always > 32K
|
|
|
|
type = ChipType::LPC2101;
|
|
|
|
else if(searchPattern(0x1026E3A0)) // 70 MHz pattern (60 MHZ = 0x1025E3A0)
|
|
|
|
type = ChipType::LPC2104_OC;
|
|
|
|
else
|
|
|
|
type = ChipType::LPC2104;
|
|
|
|
}
|
|
|
|
|
2021-06-27 07:44:42 +00:00
|
|
|
ChipPropsType props = ChipProps[static_cast<uInt32>(type)];
|
|
|
|
|
2021-06-24 14:21:00 +00:00
|
|
|
_chipType = type;
|
2021-06-27 07:44:42 +00:00
|
|
|
_MHz = props.MHz;
|
2021-06-24 14:21:00 +00:00
|
|
|
#ifdef THUMB_CYCLE_COUNT
|
2021-06-27 07:44:42 +00:00
|
|
|
_flashCycles = props.flashCycles;
|
|
|
|
_flashBanks = props.flashBanks;
|
2021-06-24 14:21:00 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
setConsoleTiming(_consoleTiming);
|
2021-06-27 07:44:42 +00:00
|
|
|
|
|
|
|
return props;
|
2021-06-24 14:21:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef THUMB_CYCLE_COUNT
|
|
|
|
// Notes:
|
|
|
|
// For exact cylce counting we have to
|
|
|
|
// - emulate the LPC21xx prefetch, branch and data buffers
|
|
|
|
// - differentiate between S and N cycles
|
|
|
|
// - simulation the pipeline (including stalls)
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
/*
|
|
|
|
This simulates the MAM of the LPC2101_02_03. It has three 128-bits buffers:
|
|
|
|
- prefetch buffer
|
|
|
|
- branch trail buffer
|
|
|
|
- data buffer
|
|
|
|
|
|
|
|
Instruction prefetches are checking the prefetch and the branch trail buffer.
|
|
|
|
- If a prefetch cannot be found in the two buffers a new line of 128 bits is read from memory
|
|
|
|
and put into the prefetch buffer. This causes wait states.
|
|
|
|
- If a branch target cannot be found in the two buffers a new line of 128 bits is read from
|
|
|
|
memory and put into the branch trail buffer. This causes wait states.
|
|
|
|
|
2021-07-08 13:46:41 +00:00
|
|
|
Data access is only checking and updating the data buffer.
|
2021-06-24 14:21:00 +00:00
|
|
|
|
|
|
|
The function returns true in case of a buffer hit.
|
|
|
|
*/
|
|
|
|
bool Thumbulator::isMamBuffered(uInt32 addr, AccessType accessType)
|
|
|
|
{
|
|
|
|
if(_flashBanks == 1) // e.g. LPC2101_02_03
|
|
|
|
{
|
|
|
|
// single Flash bank
|
|
|
|
addr &= ~0x7F; // 128-bit address line
|
|
|
|
|
|
|
|
switch(accessType)
|
|
|
|
{
|
|
|
|
case AccessType::prefetch:
|
|
|
|
if(addr != _prefetchBufferAddr[0] && addr != _branchBufferAddr[0])
|
|
|
|
{
|
2021-07-08 13:46:41 +00:00
|
|
|
#ifdef THUMB_STATS
|
|
|
|
++_stats.mamPrefetchMisses;
|
|
|
|
#endif
|
2021-06-24 14:21:00 +00:00
|
|
|
_prefetchBufferAddr[0] = addr;
|
|
|
|
return false;
|
|
|
|
}
|
2021-07-08 13:46:41 +00:00
|
|
|
#ifdef THUMB_STATS
|
|
|
|
++_stats.mamPrefetchHits;
|
|
|
|
#endif
|
2021-06-24 14:21:00 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case AccessType::branch:
|
|
|
|
if(addr != _prefetchBufferAddr[0] && addr != _branchBufferAddr[0])
|
|
|
|
{
|
2021-07-08 13:46:41 +00:00
|
|
|
#ifdef THUMB_STATS
|
|
|
|
++_stats.mamBranchMisses;
|
|
|
|
#endif
|
2021-06-24 14:21:00 +00:00
|
|
|
_branchBufferAddr[0] = addr;
|
|
|
|
return false;
|
|
|
|
}
|
2021-07-08 13:46:41 +00:00
|
|
|
#ifdef THUMB_STATS
|
|
|
|
++_stats.mamBranchHits;
|
|
|
|
#endif
|
|
|
|
|
2021-06-24 14:21:00 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
default: // AccessType::data
|
|
|
|
if(addr != _dataBufferAddr)
|
|
|
|
{
|
2021-07-08 13:46:41 +00:00
|
|
|
#ifdef THUMB_STATS
|
|
|
|
++_stats.mamDataMisses;
|
|
|
|
#endif
|
2021-06-24 14:21:00 +00:00
|
|
|
_dataBufferAddr = addr;
|
|
|
|
return false;
|
|
|
|
}
|
2021-07-08 13:46:41 +00:00
|
|
|
#ifdef THUMB_STATS
|
|
|
|
++_stats.mamDataHits;
|
|
|
|
#endif
|
2021-06-24 14:21:00 +00:00
|
|
|
break;
|
2021-10-22 21:44:40 +00:00
|
|
|
}
|
2021-06-24 14:21:00 +00:00
|
|
|
}
|
|
|
|
else // e.g. LPC2104_05_06
|
|
|
|
{
|
|
|
|
// dual Flash bank
|
2022-09-14 01:22:59 +00:00
|
|
|
const uInt32 bank = (addr & 0x80) ? 1 : 0;
|
2021-06-24 14:21:00 +00:00
|
|
|
|
|
|
|
addr &= ~0x7F; // 128-bit address line
|
|
|
|
|
|
|
|
switch(accessType)
|
|
|
|
{
|
|
|
|
case AccessType::prefetch:
|
|
|
|
// speculative load, executed after last instrucution has been executed
|
|
|
|
_prefetchBufferAddr[bank ^ 1] = addr + 0x80;
|
|
|
|
if(addr != _prefetchBufferAddr[bank] && addr != _branchBufferAddr[bank])
|
|
|
|
{
|
2021-07-08 13:46:41 +00:00
|
|
|
#ifdef THUMB_STATS
|
|
|
|
++_stats.mamPrefetchMisses;
|
|
|
|
#endif
|
2021-06-24 14:21:00 +00:00
|
|
|
_prefetchBufferAddr[bank] = addr;
|
|
|
|
return false;
|
|
|
|
}
|
2021-07-08 13:46:41 +00:00
|
|
|
#ifdef THUMB_STATS
|
|
|
|
++_stats.mamPrefetchHits;
|
|
|
|
#endif
|
2021-06-24 14:21:00 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case AccessType::branch:
|
|
|
|
if(addr != _prefetchBufferAddr[bank] && addr != _branchBufferAddr[bank])
|
|
|
|
{
|
2021-07-08 13:46:41 +00:00
|
|
|
#ifdef THUMB_STATS
|
|
|
|
++_stats.mamBranchMisses;
|
|
|
|
#endif
|
2021-06-24 14:21:00 +00:00
|
|
|
// load both branch trail buffers at once
|
|
|
|
_branchBufferAddr[bank] = addr;
|
|
|
|
_branchBufferAddr[bank ^ 1] = addr + 0x80;
|
|
|
|
return false;
|
|
|
|
}
|
2021-07-08 13:46:41 +00:00
|
|
|
#ifdef THUMB_STATS
|
|
|
|
++_stats.mamBranchHits;
|
|
|
|
#endif
|
2021-06-24 14:21:00 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
default: // AccessType::data
|
|
|
|
if(addr != _dataBufferAddr)
|
|
|
|
{
|
2021-07-08 13:46:41 +00:00
|
|
|
#ifdef THUMB_STATS
|
|
|
|
++_stats.mamDataMisses;
|
|
|
|
#endif
|
2021-06-24 14:21:00 +00:00
|
|
|
_dataBufferAddr = addr;
|
|
|
|
return false;
|
|
|
|
}
|
2021-07-08 13:46:41 +00:00
|
|
|
#ifdef THUMB_STATS
|
|
|
|
++_stats.mamDataHits;
|
|
|
|
#endif
|
2021-06-24 14:21:00 +00:00
|
|
|
break;
|
2021-10-22 21:44:40 +00:00
|
|
|
}
|
2021-06-24 14:21:00 +00:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
void Thumbulator::incCycles(AccessType accessType, uInt32 cycles)
|
|
|
|
{
|
|
|
|
#ifdef EMULATE_PIPELINE
|
|
|
|
// simulate the pipeline effects
|
2021-06-27 07:44:42 +00:00
|
|
|
//if(_memory0Pipeline)
|
|
|
|
//{
|
|
|
|
// --_memory0Pipeline; // == 0
|
|
|
|
// ++_fetchPipeline;
|
|
|
|
//}
|
2021-06-24 14:21:00 +00:00
|
|
|
if(_memory1Pipeline)
|
|
|
|
{
|
|
|
|
--_memory1Pipeline;
|
|
|
|
++_memory0Pipeline; // == 1
|
|
|
|
}
|
|
|
|
|
|
|
|
switch(accessType)
|
|
|
|
{
|
|
|
|
case AccessType::branch:
|
|
|
|
_fetchPipeline = _memory0Pipeline = _memory1Pipeline = 0; // flush pipeline
|
|
|
|
break;
|
|
|
|
|
|
|
|
case AccessType::data:
|
|
|
|
if(cycles == 1) // no Flash access
|
|
|
|
++_fetchPipeline;
|
|
|
|
else
|
|
|
|
_memory1Pipeline += cycles;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default: // AccessType::prefetch
|
|
|
|
{
|
|
|
|
// Reduce cycles by pipelined cycles
|
|
|
|
// Cart (Turbo start sequence): 1F0AC
|
|
|
|
// None: 1FF2E @ 90% (22989 @ 100%)
|
2021-06-27 07:44:42 +00:00
|
|
|
#if 1
|
2021-06-24 14:21:00 +00:00
|
|
|
// Version 1: 1ECFC @ 90% (223C3 @ 100%)
|
2021-06-27 07:44:42 +00:00
|
|
|
if(cycles == _flashCycles)
|
|
|
|
{
|
|
|
|
if(!_memory1Pipeline) // there must be no pending memory access
|
|
|
|
{
|
|
|
|
uInt32 newCycles = std::max(1, Int32(cycles - _fetchPipeline));
|
2021-06-24 14:21:00 +00:00
|
|
|
|
2021-06-27 07:44:42 +00:00
|
|
|
_fetchPipeline -= (cycles - newCycles);
|
|
|
|
cycles = newCycles;
|
|
|
|
}
|
|
|
|
}
|
2021-06-24 14:21:00 +00:00
|
|
|
#endif
|
2021-06-27 07:44:42 +00:00
|
|
|
#if 0
|
2021-06-24 14:21:00 +00:00
|
|
|
// Version 2: 1ED23 @ 90% (223EF @ 100%)
|
|
|
|
// considers that partial fetches are not allowed
|
|
|
|
if(cycles == _flashCycles)
|
|
|
|
{
|
2021-06-27 07:44:42 +00:00
|
|
|
//_memory0Pipeline = _memory1Pipeline = 0;
|
|
|
|
if(!_memory1Pipeline && _fetchPipeline >= _flashCycles)
|
2021-06-24 14:21:00 +00:00
|
|
|
{
|
|
|
|
_fetchPipeline -= (_flashCycles - 1);
|
|
|
|
cycles = 1;
|
|
|
|
}
|
|
|
|
//else
|
|
|
|
// _fetchPipeline = 0; // Flash prefetch abort (makes 0 difference!)
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
};
|
2019-02-23 15:09:00 +00:00
|
|
|
#endif
|
2021-07-02 15:40:30 +00:00
|
|
|
|
|
|
|
//#ifdef MERGE_I_S
|
|
|
|
// TODO
|
|
|
|
// if(accessType == AccessType::branch)
|
|
|
|
// _lastCycleType[2] = _lastCycleType[1] = _lastCycleType[0] = CycleType::S;
|
|
|
|
//#endif
|
|
|
|
|
2021-06-24 14:21:00 +00:00
|
|
|
_totalCycles += cycles;
|
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
void Thumbulator::incSCycles(uInt32 addr, AccessType accessType)
|
|
|
|
{
|
2021-07-08 13:46:41 +00:00
|
|
|
#ifdef THUMB_STATS
|
|
|
|
++_stats.sCylces;
|
|
|
|
#endif
|
|
|
|
|
2022-03-29 00:30:26 +00:00
|
|
|
uInt32 cycles = 0;
|
2021-07-08 13:46:41 +00:00
|
|
|
|
2021-06-29 15:44:08 +00:00
|
|
|
if(addr & 0xC0000000) // RAM, peripherals
|
2021-06-24 14:21:00 +00:00
|
|
|
cycles = 1;
|
|
|
|
else // Flash
|
|
|
|
{
|
2021-07-08 13:46:41 +00:00
|
|
|
if(mamcr == MamModeType::mode0 ||
|
|
|
|
(mamcr == MamModeType::mode1 && accessType == AccessType::data))
|
|
|
|
{
|
2021-06-27 07:44:42 +00:00
|
|
|
cycles = _flashCycles; // 3|4
|
2021-07-08 13:46:41 +00:00
|
|
|
}
|
2021-06-24 14:21:00 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
if(isMamBuffered(addr, accessType) || mamcr == MamModeType::modeX)
|
|
|
|
cycles = 1;
|
|
|
|
else
|
|
|
|
cycles = _flashCycles;
|
|
|
|
}
|
|
|
|
}
|
2021-07-01 13:40:49 +00:00
|
|
|
|
|
|
|
#ifdef MERGE_I_S
|
2021-07-08 13:46:41 +00:00
|
|
|
//if(accessType != AccessType::prefetch)
|
|
|
|
//{
|
|
|
|
// if(_lastCycleType[0] == CycleType::I)
|
|
|
|
// {
|
|
|
|
// _lastCycleType[0] = CycleType::S; // merge cannot be used twice!
|
|
|
|
// --cycles;
|
|
|
|
// }
|
|
|
|
//}
|
|
|
|
//else if(_lastCycleType[2] == CycleType::I)
|
|
|
|
// --cycles;
|
|
|
|
|
|
|
|
//if(accessType == AccessType::prefetch &&
|
|
|
|
// _lastCycleType[_pipeIdx] == CycleType::I)
|
|
|
|
// --cycles;
|
|
|
|
|
2021-07-01 13:40:49 +00:00
|
|
|
|
2021-07-02 15:40:30 +00:00
|
|
|
_lastCycleType[2] = _lastCycleType[1];
|
2021-07-01 13:40:49 +00:00
|
|
|
_lastCycleType[1] = _lastCycleType[0];
|
|
|
|
_lastCycleType[0] = CycleType::S;
|
2021-07-08 13:46:41 +00:00
|
|
|
//_lastCycleType[_pipeIdx] = CycleType::S;
|
2021-07-01 13:40:49 +00:00
|
|
|
#endif
|
2021-07-02 15:40:30 +00:00
|
|
|
incCycles(accessType, cycles);
|
2021-07-08 13:46:41 +00:00
|
|
|
|
|
|
|
|
|
|
|
#ifdef MERGE_I_S
|
|
|
|
if(accessType == AccessType::branch)
|
|
|
|
{
|
|
|
|
if(_lastCycleType[1] == CycleType::I || _lastCycleType[2] == CycleType::I)
|
|
|
|
_lastCycleType[1] = _lastCycleType[2] = CycleType::S;
|
|
|
|
//if(_lastCycleType[_pipeIdx ^ 1] == CycleType::I || _lastCycleType[_pipeIdx ^ 2] == CycleType::I)
|
|
|
|
// _lastCycleType[_pipeIdx ^ 1] = _lastCycleType[_pipeIdx ^ 2] = CycleType::S;
|
|
|
|
}
|
|
|
|
#endif // MERGE_I_S
|
2021-06-24 14:21:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
void Thumbulator::incNCycles(uInt32 addr, AccessType accessType)
|
|
|
|
{
|
2021-07-08 13:46:41 +00:00
|
|
|
#ifdef THUMB_STATS
|
|
|
|
++_stats.nCylces;
|
|
|
|
#endif
|
|
|
|
|
2022-03-29 00:30:26 +00:00
|
|
|
uInt32 cycles = 0;
|
2021-06-24 14:21:00 +00:00
|
|
|
|
2021-06-29 15:44:08 +00:00
|
|
|
if(addr & 0xC0000000) // RAM, peripherals
|
2021-06-24 14:21:00 +00:00
|
|
|
cycles = 1;
|
|
|
|
else // Flash
|
|
|
|
{
|
|
|
|
if(mamcr < MamModeType::mode2)
|
2021-06-27 07:44:42 +00:00
|
|
|
cycles = _flashCycles; // 3|4
|
2021-06-24 14:21:00 +00:00
|
|
|
else
|
|
|
|
if(isMamBuffered(addr, accessType) || mamcr == MamModeType::modeX)
|
|
|
|
cycles = 1;
|
|
|
|
else
|
|
|
|
cycles = _flashCycles;
|
|
|
|
}
|
2021-07-01 13:40:49 +00:00
|
|
|
#ifdef MERGE_I_S
|
2021-07-02 15:40:30 +00:00
|
|
|
_lastCycleType[2] = _lastCycleType[1];
|
2021-07-01 13:40:49 +00:00
|
|
|
_lastCycleType[1] = _lastCycleType[0];
|
|
|
|
_lastCycleType[0] = CycleType::N;
|
2021-07-08 13:46:41 +00:00
|
|
|
//_lastCycleType[_pipeIdx] = CycleType::N;
|
2021-07-01 13:40:49 +00:00
|
|
|
#endif
|
2021-07-02 15:40:30 +00:00
|
|
|
incCycles(accessType, cycles);
|
2021-06-24 14:21:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
void Thumbulator::incICycles(uInt32 m)
|
|
|
|
{
|
2021-07-08 13:46:41 +00:00
|
|
|
#ifdef THUMB_STATS
|
|
|
|
++_stats.iCylces;
|
|
|
|
#endif
|
|
|
|
|
2021-06-24 14:21:00 +00:00
|
|
|
#ifdef EMULATE_PIPELINE
|
|
|
|
_fetchPipeline += m;
|
2021-06-27 07:44:42 +00:00
|
|
|
//if(_memory0Pipeline)
|
|
|
|
//{
|
|
|
|
// --_memory0Pipeline; // == 0
|
|
|
|
// ++_fetchPipeline;
|
|
|
|
//}
|
|
|
|
|
|
|
|
// TODO: m!
|
2021-06-24 14:21:00 +00:00
|
|
|
if(_memory1Pipeline)
|
|
|
|
{
|
|
|
|
--_memory1Pipeline;
|
|
|
|
++_memory0Pipeline; // == 1
|
|
|
|
}
|
|
|
|
#endif
|
2021-07-01 13:40:49 +00:00
|
|
|
#ifdef MERGE_I_S
|
2021-07-02 15:40:30 +00:00
|
|
|
_lastCycleType[2] = _lastCycleType[1];
|
2021-07-01 13:40:49 +00:00
|
|
|
_lastCycleType[1] = _lastCycleType[0];
|
|
|
|
_lastCycleType[0] = CycleType::I;
|
2021-07-08 13:46:41 +00:00
|
|
|
//_lastCycleType[_pipeIdx] = CycleType::I;
|
2021-07-01 13:40:49 +00:00
|
|
|
#endif
|
2021-07-02 15:40:30 +00:00
|
|
|
_totalCycles += m;
|
2021-06-24 14:21:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#endif // THUMB_CYCLE_COUNT
|
2022-02-15 11:33:46 +00:00
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
bool Thumbulator::searchPattern(uInt32 pattern, uInt32 repeats) const
|
|
|
|
{
|
|
|
|
// Note: The pattern is defined in 1-0-3-2 byte order!
|
|
|
|
const uInt16 patternLo = pattern >> 16;
|
|
|
|
const uInt16 patternHi = pattern & 0xffff;
|
|
|
|
uInt32 count = 0;
|
|
|
|
|
|
|
|
// The pattern is always aligned to 4
|
|
|
|
for(uInt32 i = 0; i < romSize/2 - 2; i += 2)
|
|
|
|
{
|
|
|
|
if(rom[i] == patternLo && rom[i + 1] == patternHi)
|
|
|
|
if(++count == repeats)
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|