2296 lines
39 KiB
C++
2296 lines
39 KiB
C++
/******************************************************************************/
|
|
/* Mednafen - Multi-system Emulator */
|
|
/******************************************************************************/
|
|
/* m68k.cpp - Motorola 68000 CPU Emulator
|
|
** Copyright (C) 2015-2016 Mednafen Team
|
|
**
|
|
** This program is free software; you can redistribute it and/or
|
|
** modify it under the terms of the GNU General Public License
|
|
** as published by the Free Software Foundation; either version 2
|
|
** of the License, or (at your option) any later version.
|
|
**
|
|
** This program is distributed in the hope that it will be useful,
|
|
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
** GNU General Public License for more details.
|
|
**
|
|
** You should have received a copy of the GNU General Public License
|
|
** along with this program; if not, write to the Free Software Foundation, Inc.,
|
|
** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
// TODO: Check CHK
|
|
//
|
|
// TODO: Address errors(or just cheap out and mask off the lower bit on 16-bit memory accesses).
|
|
//
|
|
// TODO: Predec, postinc order for same address register.
|
|
//
|
|
// TODO: Fix instruction timings(currently execute too fast).
|
|
//
|
|
// TODO: Fix multiplication and division timing, and make sure flags are ok for divide by zero.
|
|
//
|
|
// FIXME: Handle NMI differently; how to test? Maybe MOVEM to interrupt control registers...
|
|
//
|
|
// TODO: Test MOVEM
|
|
//
|
|
/*
|
|
Be sure to test the following thoroughly:
|
|
SUBA -(a0), a0
|
|
SUBX -(a0),-(a0)
|
|
CMPM (a0)+,(a0)+
|
|
|
|
SUBA -(a7), a7
|
|
SUBX -(a7),-(a7)
|
|
CMPM (a7)+,(a7)+
|
|
*/
|
|
|
|
#include "ss.h"
|
|
#include "m68k.h"
|
|
|
|
#include <tuple>
|
|
|
|
#pragma GCC optimize ("no-crossjumping,no-gcse")
|
|
|
|
static MDFN_FASTCALL void Dummy_BusRESET(bool state)
|
|
{
|
|
|
|
}
|
|
|
|
static void DummyDBG(const char* format, ...) noexcept
|
|
{
|
|
|
|
}
|
|
|
|
M68K::M68K(const bool rev_e) : Revision_E(rev_e),
|
|
BusReadInstr(nullptr), BusRead8(nullptr), BusRead16(nullptr),
|
|
BusWrite8(nullptr), BusWrite16(nullptr),
|
|
BusRMW(nullptr),
|
|
BusIntAck(nullptr),
|
|
BusRESET(Dummy_BusRESET),
|
|
DBG_Warning(DummyDBG),
|
|
DBG_Verbose(DummyDBG)
|
|
{
|
|
timestamp = 0;
|
|
XPending = 0;
|
|
IPL = 0;
|
|
Reset(true);
|
|
}
|
|
|
|
M68K::~M68K()
|
|
{
|
|
|
|
}
|
|
|
|
INLINE void M68K::RecalcInt(void)
|
|
{
|
|
XPending &= ~XPENDING_MASK_INT;
|
|
|
|
if(IPL > (SRHB & 0x7))
|
|
XPending |= XPENDING_MASK_INT;
|
|
}
|
|
|
|
void M68K::SetIPL(uint8 ipl_new)
|
|
{
|
|
if(IPL < 0x7 && ipl_new == 0x7)
|
|
XPending |= XPENDING_MASK_NMI;
|
|
else if(ipl_new < 0x7)
|
|
XPending &= ~XPENDING_MASK_NMI;
|
|
|
|
IPL = ipl_new;
|
|
RecalcInt();
|
|
}
|
|
|
|
void M68K::SetExtHalted(bool state)
|
|
{
|
|
XPending &= ~XPENDING_MASK_EXTHALTED;
|
|
if(state)
|
|
XPending |= XPENDING_MASK_EXTHALTED;
|
|
}
|
|
|
|
template<typename T>
|
|
INLINE T M68K::Read(uint32 addr)
|
|
{
|
|
if(sizeof(T) == 4)
|
|
{
|
|
uint32 ret;
|
|
|
|
ret = BusRead16(addr) << 16;
|
|
ret |= BusRead16(addr + 2);
|
|
|
|
return ret;
|
|
}
|
|
else if(sizeof(T) == 2)
|
|
return BusRead16(addr);
|
|
else
|
|
return BusRead8(addr);
|
|
}
|
|
|
|
INLINE uint16 M68K::ReadOp(void)
|
|
{
|
|
uint16 ret;
|
|
|
|
ret = BusReadInstr(PC);
|
|
PC += 2;
|
|
|
|
return ret;
|
|
}
|
|
|
|
template<typename T, bool long_dec>
|
|
INLINE void M68K::Write(uint32 addr, const T val)
|
|
{
|
|
if(sizeof(T) == 4)
|
|
{
|
|
if(long_dec)
|
|
{
|
|
BusWrite16(addr + 2, val);
|
|
BusWrite16(addr, val >> 16);
|
|
}
|
|
else
|
|
{
|
|
BusWrite16(addr, val >> 16);
|
|
BusWrite16(addr + 2, val);
|
|
}
|
|
}
|
|
else if(sizeof(T) == 2)
|
|
BusWrite16(addr, val);
|
|
else
|
|
BusWrite8(addr, val);
|
|
}
|
|
|
|
template<typename T>
|
|
INLINE void M68K::Push(const T value)
|
|
{
|
|
static_assert(sizeof(T) != 1, "Wrong type.");
|
|
A[7] -= sizeof(T);
|
|
Write<T, true>(A[7], value);
|
|
}
|
|
|
|
template<typename T>
|
|
INLINE T M68K::Pull(void)
|
|
{
|
|
static_assert(sizeof(T) != 1, "Wrong type.");
|
|
|
|
T ret;
|
|
|
|
ret = Read<T>(A[7]);
|
|
|
|
A[7] += sizeof(T);
|
|
|
|
return ret;
|
|
}
|
|
|
|
//
|
|
// MOVE byte and word: instructions, 2 cycle penalty for source predecrement only
|
|
// 2 cycle penalty for (d8, An, Xn) for both source and dest ams
|
|
// 2 cycle penalty for (d8, PC, Xn) for dest am
|
|
//
|
|
|
|
//
|
|
// Careful on declaration order of HAM objects(needs to be source then dest).
|
|
//
|
|
template<typename T, M68K::AddressMode am>
|
|
struct M68K::HAM
|
|
{
|
|
INLINE HAM(M68K* z) : zptr(z), reg(0), have_ea(false)
|
|
{
|
|
static_assert(am == PC_DISP || am == PC_INDEX || am == ABS_SHORT || am == ABS_LONG || am == IMMEDIATE, "Wrong arg count.");
|
|
|
|
switch(am)
|
|
{
|
|
case PC_DISP: // (d16, PC)
|
|
case PC_INDEX: // PC with index
|
|
ea = zptr->PC;
|
|
ext = zptr->ReadOp();
|
|
break;
|
|
|
|
case ABS_SHORT: // (xxxx).W
|
|
ext = zptr->ReadOp();
|
|
break;
|
|
|
|
case ABS_LONG: // (xxxx).L
|
|
ext = zptr->ReadOp() << 16;
|
|
ext |= zptr->ReadOp();
|
|
break;
|
|
|
|
case IMMEDIATE: // Immediate
|
|
if(sizeof(T) == 4)
|
|
{
|
|
ext = zptr->ReadOp() << 16;
|
|
ext |= zptr->ReadOp();
|
|
}
|
|
else
|
|
{
|
|
ext = zptr->ReadOp();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
INLINE HAM(M68K* z, uint32 arg) : zptr(z), reg(arg), have_ea(false)
|
|
{
|
|
static_assert(am != PC_DISP && am != PC_INDEX && am != ABS_SHORT && am != ABS_LONG, "Wrong arg count.");
|
|
|
|
static_assert(am != ADDR_REG_DIR || sizeof(T) != 1, "Wrong size for address reg direct read");
|
|
|
|
switch(am)
|
|
{
|
|
case DATA_REG_DIR:
|
|
case ADDR_REG_DIR:
|
|
case ADDR_REG_INDIR:
|
|
case ADDR_REG_INDIR_POST:
|
|
case ADDR_REG_INDIR_PRE:
|
|
break;
|
|
|
|
case ADDR_REG_INDIR_DISP: // (d16, An)
|
|
case ADDR_REG_INDIR_INDX: // (d8, An, Xn)
|
|
ext = zptr->ReadOp();
|
|
break;
|
|
|
|
case IMMEDIATE: // Immediate (quick)
|
|
ext = arg;
|
|
break;
|
|
}
|
|
}
|
|
|
|
private:
|
|
INLINE void calcea(const int predec_penalty)
|
|
{
|
|
if(have_ea)
|
|
return;
|
|
|
|
have_ea = true;
|
|
|
|
switch(am)
|
|
{
|
|
default:
|
|
break;
|
|
|
|
case ADDR_REG_INDIR:
|
|
ea = zptr->A[reg];
|
|
break;
|
|
|
|
case ADDR_REG_INDIR_POST:
|
|
ea = zptr->A[reg];
|
|
zptr->A[reg] += (sizeof(T) == 1 && reg == 0x7) ? 2 : sizeof(T);
|
|
break;
|
|
|
|
case ADDR_REG_INDIR_PRE:
|
|
zptr->timestamp += predec_penalty;
|
|
zptr->A[reg] -= (sizeof(T) == 1 && reg == 0x7) ? 2 : sizeof(T);
|
|
ea = zptr->A[reg];
|
|
break;
|
|
|
|
case ADDR_REG_INDIR_DISP:
|
|
ea = zptr->A[reg] + (int16)ext;
|
|
break;
|
|
|
|
case ADDR_REG_INDIR_INDX:
|
|
zptr->timestamp += 2;
|
|
ea = zptr->A[reg] + (int8)ext + ((ext & 0x800) ? zptr->DA[ext >> 12] : (int16)zptr->DA[ext >> 12]);
|
|
break;
|
|
|
|
case ABS_SHORT:
|
|
ea = (int16)ext;
|
|
break;
|
|
|
|
case ABS_LONG:
|
|
ea = ext;
|
|
break;
|
|
|
|
case PC_DISP:
|
|
ea = ea + (int16)ext;
|
|
break;
|
|
|
|
case PC_INDEX:
|
|
zptr->timestamp += 2;
|
|
ea = ea + (int8)ext + ((ext & 0x800) ? zptr->DA[ext >> 12] : (int16)zptr->DA[ext >> 12]);
|
|
break;
|
|
}
|
|
}
|
|
public:
|
|
|
|
//
|
|
// TODO: check pre-decrement 32-bit->2x 16-bit write order
|
|
//
|
|
|
|
INLINE void write(const T val, const int predec_penalty = 2)
|
|
{
|
|
static_assert(am != PC_DISP && am != PC_INDEX && am != IMMEDIATE, "What");
|
|
|
|
static_assert(am != ADDR_REG_DIR || sizeof(T) == 4, "Wrong size for address reg direct write");
|
|
|
|
switch(am)
|
|
{
|
|
case ADDR_REG_DIR:
|
|
zptr->A[reg] = val;
|
|
break;
|
|
|
|
case DATA_REG_DIR:
|
|
#ifdef MSB_FIRST
|
|
memcpy((uint8*)&zptr->D[reg] + (4 - sizeof(T)), &val, sizeof(T));
|
|
#else
|
|
memcpy((uint8*)&zptr->D[reg] + 0, &val, sizeof(T));
|
|
#endif
|
|
break;
|
|
|
|
case ADDR_REG_INDIR:
|
|
case ADDR_REG_INDIR_POST:
|
|
case ADDR_REG_INDIR_PRE:
|
|
case ADDR_REG_INDIR_DISP:
|
|
case ADDR_REG_INDIR_INDX:
|
|
case ABS_SHORT:
|
|
case ABS_LONG:
|
|
calcea(predec_penalty);
|
|
zptr->Write<T, am == ADDR_REG_INDIR_PRE>(ea, val);
|
|
break;
|
|
}
|
|
}
|
|
|
|
INLINE T read(void)
|
|
{
|
|
switch(am)
|
|
{
|
|
case DATA_REG_DIR:
|
|
return zptr->D[reg];
|
|
|
|
case ADDR_REG_DIR:
|
|
return zptr->A[reg];
|
|
|
|
case IMMEDIATE:
|
|
return ext;
|
|
|
|
case ADDR_REG_INDIR:
|
|
case ADDR_REG_INDIR_POST:
|
|
case ADDR_REG_INDIR_PRE:
|
|
case ADDR_REG_INDIR_DISP:
|
|
case ADDR_REG_INDIR_INDX:
|
|
case ABS_SHORT:
|
|
case ABS_LONG:
|
|
case PC_DISP:
|
|
case PC_INDEX:
|
|
calcea(2);
|
|
return zptr->Read<T>(ea);
|
|
}
|
|
}
|
|
|
|
INLINE void rmw(T (MDFN_FASTCALL *cb)(M68K*, T))
|
|
{
|
|
static_assert(am != PC_DISP && am != PC_INDEX && am != IMMEDIATE, "What");
|
|
|
|
switch(am)
|
|
{
|
|
case DATA_REG_DIR:
|
|
{
|
|
T tmp = cb(zptr, zptr->D[reg]);
|
|
#ifdef MSB_FIRST
|
|
memcpy((uint8*)&zptr->D[reg] + (4 - sizeof(T)), &tmp, sizeof(T));
|
|
#else
|
|
memcpy((uint8*)&zptr->D[reg] + 0, &tmp, sizeof(T));
|
|
#endif
|
|
}
|
|
break;
|
|
|
|
case ADDR_REG_INDIR:
|
|
case ADDR_REG_INDIR_POST:
|
|
case ADDR_REG_INDIR_PRE:
|
|
case ADDR_REG_INDIR_DISP:
|
|
case ADDR_REG_INDIR_INDX:
|
|
case ABS_SHORT:
|
|
case ABS_LONG:
|
|
calcea(2);
|
|
|
|
zptr->BusRMW(ea, cb);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
INLINE void jump(void)
|
|
{
|
|
calcea(0);
|
|
zptr->PC = ea;
|
|
}
|
|
|
|
INLINE uint32 getea(void)
|
|
{
|
|
static_assert(am == ADDR_REG_INDIR || am == ADDR_REG_INDIR_DISP || am == ADDR_REG_INDIR_INDX || am == ABS_SHORT || am == ABS_LONG || am == PC_DISP || am == PC_INDEX, "Wrong addressing mode");
|
|
calcea(0);
|
|
return ea;
|
|
}
|
|
|
|
M68K* zptr;
|
|
|
|
uint32 ea;
|
|
uint32 ext;
|
|
const unsigned reg;
|
|
|
|
private:
|
|
bool have_ea;
|
|
};
|
|
|
|
|
|
|
|
INLINE void M68K::SetC(bool val) { Flag_C = val; }
|
|
INLINE void M68K::SetV(bool val) { Flag_V = val; }
|
|
INLINE void M68K::SetZ(bool val) { Flag_Z = val; }
|
|
INLINE void M68K::SetN(bool val) { Flag_N = val; }
|
|
INLINE void M68K::SetX(bool val) { Flag_X = val; }
|
|
|
|
INLINE bool M68K::GetC(void) { return Flag_C; }
|
|
INLINE bool M68K::GetV(void) { return Flag_V; }
|
|
INLINE bool M68K::GetZ(void) { return Flag_Z; }
|
|
INLINE bool M68K::GetN(void) { return Flag_N; }
|
|
INLINE bool M68K::GetX(void) { return Flag_X; }
|
|
|
|
|
|
INLINE void M68K::SetCX(bool val)
|
|
{
|
|
SetC(val);
|
|
SetX(val);
|
|
}
|
|
|
|
//
|
|
// Z_OnlyClear should be true for ADDX, SUBX, NEGX, ABCD, SBCD, NBCD.
|
|
//
|
|
template<typename T, bool Z_OnlyClear>
|
|
INLINE void M68K::CalcZN(const T val)
|
|
{
|
|
if(Z_OnlyClear)
|
|
{
|
|
if(val != 0)
|
|
SetZ(false);
|
|
}
|
|
else
|
|
SetZ(val == 0);
|
|
|
|
SetN(static_cast<typename std::make_signed<T>::type>(val) < 0);
|
|
}
|
|
|
|
template<typename T>
|
|
INLINE void M68K::CalcCX(const uint64& val)
|
|
{
|
|
SetCX((val >> (sizeof(T) * 8)) & 1);
|
|
}
|
|
|
|
INLINE uint8 M68K::GetCCR(void)
|
|
{
|
|
return (GetC() << 0) | (GetV() << 1) | (GetZ() << 2) | (GetN() << 3) | (GetX() << 4);
|
|
}
|
|
|
|
INLINE void M68K::SetCCR(uint8 val)
|
|
{
|
|
SetC((val >> 0) & 1);
|
|
SetV((val >> 1) & 1);
|
|
SetZ((val >> 2) & 1);
|
|
SetN((val >> 3) & 1);
|
|
SetX((val >> 4) & 1);
|
|
}
|
|
|
|
INLINE uint16 M68K::GetSR(void)
|
|
{
|
|
return GetCCR() | (SRHB << 8);
|
|
}
|
|
|
|
INLINE void M68K::SetSR(uint16 val)
|
|
{
|
|
const uint8 new_srhb = (val >> 8) & 0xA7;
|
|
|
|
SetCCR(val);
|
|
|
|
if((SRHB ^ new_srhb) & 0x20) // Supervisor mode change
|
|
{
|
|
std::swap(A[7], SP_Inactive);
|
|
}
|
|
|
|
SRHB = new_srhb;
|
|
RecalcInt();
|
|
}
|
|
|
|
INLINE uint8 M68K::GetIMask(void)
|
|
{
|
|
return (GetSR() >> 8) & 0x7;
|
|
}
|
|
|
|
INLINE void M68K::SetIMask(uint8 val)
|
|
{
|
|
SetSR((GetSR() & ~0x0700) | ((val & 0x7) << 8));
|
|
}
|
|
|
|
INLINE bool M68K::GetSVisor(void)
|
|
{
|
|
return (bool)(GetSR() & 0x2000);
|
|
}
|
|
|
|
INLINE void M68K::SetSVisor(bool value)
|
|
{
|
|
SetSR((GetSR() & ~0x2000) | (value << 13));
|
|
}
|
|
|
|
INLINE bool M68K::GetTrace(void)
|
|
{
|
|
return (bool)(GetSR() & 0x8000);
|
|
}
|
|
|
|
INLINE void M68K::SetTrace(bool value)
|
|
{
|
|
SetSR((GetSR() & ~0x8000) | (value << 15));
|
|
}
|
|
|
|
//
|
|
//
|
|
//
|
|
enum
|
|
{
|
|
VECNUM_RESET_SSP = 0,
|
|
VECNUM_RESET_PC = 1,
|
|
VECNUM_BUS_ERROR = 2,
|
|
VECNUM_ADDRESS_ERROR = 3,
|
|
VECNUM_ILLEGAL = 4,
|
|
VECNUM_ZERO_DIVIDE = 5,
|
|
VECNUM_CHK = 6,
|
|
VECNUM_TRAPV = 7,
|
|
VECNUM_PRIVILEGE = 8,
|
|
VECNUM_TRACE = 9,
|
|
VECNUM_LINEA = 10,
|
|
VECNUM_LINEF = 11,
|
|
|
|
VECNUM_UNINI_INT = 15,
|
|
|
|
VECNUM_SPURIOUS_INT = 24,
|
|
VECNUM_INT_BASE = 24,
|
|
|
|
VECNUM_TRAP_BASE = 32
|
|
};
|
|
|
|
enum
|
|
{
|
|
EXCEPTION_RESET = 0,
|
|
EXCEPTION_BUS_ERROR,
|
|
EXCEPTION_ADDRESS_ERROR,
|
|
EXCEPTION_ILLEGAL,
|
|
EXCEPTION_ZERO_DIVIDE,
|
|
EXCEPTION_CHK,
|
|
EXCEPTION_TRAPV,
|
|
EXCEPTION_PRIVILEGE,
|
|
EXCEPTION_TRACE,
|
|
|
|
EXCEPTION_INT,
|
|
EXCEPTION_TRAP
|
|
};
|
|
|
|
//
|
|
// Instruction traps(TRAP, TRAPV, CHK, DIVS, DIVU):
|
|
// Saved PC points to the instruction after the instruction that triggered the exception.
|
|
//
|
|
// Illegal instructions:
|
|
//
|
|
//
|
|
// Privilege violation:
|
|
// Saved PC points to the instruction that generated the privilege violation.
|
|
//
|
|
// Base exception timing is 34 cycles?
|
|
void NO_INLINE M68K::Exception(unsigned which, unsigned vecnum)
|
|
{
|
|
const uint32 PC_save = PC;
|
|
const uint16 SR_save = GetSR();
|
|
|
|
SetSVisor(true);
|
|
SetTrace(false);
|
|
|
|
if(which == EXCEPTION_INT)
|
|
{
|
|
unsigned evn;
|
|
|
|
timestamp += 4;
|
|
|
|
SetIMask(IPL);
|
|
|
|
evn = BusIntAck(IPL);
|
|
|
|
if(evn > 255)
|
|
vecnum = vecnum + IPL;
|
|
else
|
|
vecnum = evn;
|
|
|
|
timestamp += 2;
|
|
}
|
|
|
|
Push<uint32>(PC_save);
|
|
Push<uint16>(SR_save);
|
|
PC = Read<uint32>(vecnum << 2);
|
|
|
|
//
|
|
{
|
|
auto dbgw = DBG_Verbose;
|
|
|
|
if(which != EXCEPTION_INT || vecnum == VECNUM_UNINI_INT || vecnum == VECNUM_SPURIOUS_INT)
|
|
dbgw = DBG_Warning;
|
|
|
|
dbgw("[M68K] Exception %u(vec=%u) @PC=0x%08x SR=0x%04x ---> PC=0x%08x, SR=0x%04x\n", which, vecnum, PC_save, SR_save, PC, GetSR());
|
|
}
|
|
//
|
|
|
|
// TODO: Prefetch
|
|
ReadOp();
|
|
ReadOp();
|
|
PC -= 4;
|
|
}
|
|
|
|
//
|
|
//
|
|
//
|
|
|
|
//
|
|
// ADD
|
|
//
|
|
template<typename T, typename DT, M68K::AddressMode SAM, M68K::AddressMode DAM>
|
|
INLINE void M68K::ADD(HAM<T, SAM> &src, HAM<DT, DAM> &dst)
|
|
{
|
|
static_assert(DAM == ADDR_REG_DIR || std::is_same<T, DT>::value, "Type mismatch");
|
|
|
|
uint32 const src_data = (DT)static_cast<typename std::make_signed<T>::type>(src.read());
|
|
uint32 const dst_data = dst.read();
|
|
uint64 const result = (uint64)dst_data + src_data;
|
|
|
|
if(DAM == ADDR_REG_DIR)
|
|
{
|
|
if(sizeof(T) != 4 || SAM == DATA_REG_DIR || SAM == ADDR_REG_DIR || SAM == IMMEDIATE)
|
|
timestamp += 4;
|
|
else
|
|
timestamp += 2;
|
|
}
|
|
else if(DAM == DATA_REG_DIR && sizeof(DT) == 4)
|
|
{
|
|
if(SAM == DATA_REG_DIR || SAM == IMMEDIATE)
|
|
timestamp += 4;
|
|
else
|
|
timestamp += 2;
|
|
}
|
|
|
|
if(DAM != ADDR_REG_DIR)
|
|
{
|
|
CalcZN<DT>(result);
|
|
SetCX((result >> (sizeof(DT) * 8)) & 1);
|
|
SetV((((~(dst_data ^ src_data)) & (dst_data ^ result)) >> (sizeof(DT) * 8 - 1)) & 1);
|
|
}
|
|
|
|
dst.write(result);
|
|
}
|
|
|
|
|
|
//
|
|
// ADDX
|
|
//
|
|
template<typename T, M68K::AddressMode SAM, M68K::AddressMode DAM>
|
|
INLINE void M68K::ADDX(HAM<T, SAM> &src, HAM<T, DAM> &dst)
|
|
{
|
|
uint32 const src_data = src.read();
|
|
uint32 const dst_data = dst.read();
|
|
uint64 const result = (uint64)dst_data + src_data + GetX();
|
|
|
|
if(DAM != DATA_REG_DIR)
|
|
{
|
|
timestamp += 2;
|
|
}
|
|
else
|
|
{
|
|
if(sizeof(T) == 4)
|
|
timestamp += 4;
|
|
}
|
|
|
|
CalcZN<T, true>(result);
|
|
SetCX((result >> (sizeof(T) * 8)) & 1);
|
|
SetV((((~(dst_data ^ src_data)) & (dst_data ^ result)) >> (sizeof(T) * 8 - 1)) & 1);
|
|
|
|
dst.write(result);
|
|
}
|
|
|
|
|
|
//
|
|
// Used to implement SUB, SUBA, SUBX, NEG, NEGX
|
|
//
|
|
template<bool X_form, typename T, typename DT, M68K::AddressMode SAM, M68K::AddressMode DAM>
|
|
INLINE DT M68K::Subtract(HAM<T, SAM> &src, HAM<DT, DAM> &dst)
|
|
{
|
|
static_assert(DAM == ADDR_REG_DIR || std::is_same<T, DT>::value, "Type mismatch");
|
|
static_assert(DAM == ADDR_REG_DIR || DAM == DATA_REG_DIR || DAM == IMMEDIATE || SAM == ADDR_REG_DIR || SAM == DATA_REG_DIR || SAM == IMMEDIATE || X_form, "Wrong addressing modes.");
|
|
|
|
uint32 const src_data = (DT)static_cast<typename std::make_signed<T>::type>(src.read());
|
|
uint32 const dst_data = dst.read();
|
|
const uint64 result = (uint64)dst_data - src_data - (X_form ? GetX() : 0);
|
|
|
|
if(DAM == ADDR_REG_DIR) // SUBA, SUBQ(A) only.
|
|
{
|
|
if(sizeof(T) != 4 || SAM == DATA_REG_DIR || SAM == ADDR_REG_DIR || SAM == IMMEDIATE)
|
|
timestamp += 4;
|
|
else
|
|
timestamp += 2;
|
|
}
|
|
else if(DAM == DATA_REG_DIR) // SUB, SUBQ, SUBX only.
|
|
{
|
|
if(sizeof(DT) == 4)
|
|
{
|
|
if(SAM == DATA_REG_DIR || SAM == IMMEDIATE)
|
|
timestamp += 4;
|
|
else
|
|
timestamp += 2;
|
|
}
|
|
}
|
|
else if(DAM == IMMEDIATE) // NEG, NEGX only and always.
|
|
{
|
|
if(sizeof(T) == 4)
|
|
{
|
|
timestamp += 2;
|
|
}
|
|
}
|
|
else if(SAM != IMMEDIATE && SAM != ADDR_REG_DIR && SAM != DATA_REG_DIR) // SUBX m,m
|
|
{
|
|
timestamp += 2;
|
|
}
|
|
|
|
|
|
if(DAM != ADDR_REG_DIR)
|
|
{
|
|
CalcZN<DT, X_form>(result);
|
|
SetCX((result >> (sizeof(DT) * 8)) & 1);
|
|
SetV(((((dst_data ^ src_data)) & (dst_data ^ result)) >> (sizeof(DT) * 8 - 1)) & 1);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
//
|
|
// SUB
|
|
//
|
|
template<typename T, typename DT, M68K::AddressMode SAM, M68K::AddressMode DAM>
|
|
INLINE void M68K::SUB(HAM<T, SAM> &src, HAM<DT, DAM> &dst)
|
|
{
|
|
dst.write(Subtract<false>(src, dst));
|
|
}
|
|
|
|
|
|
//
|
|
// SUBX
|
|
//
|
|
template<typename T, typename DT, M68K::AddressMode SAM, M68K::AddressMode DAM>
|
|
INLINE void M68K::SUBX(HAM<T, SAM> &src, HAM<DT, DAM> &dst)
|
|
{
|
|
dst.write(Subtract<true>(src, dst));
|
|
}
|
|
|
|
|
|
//
|
|
// NEG
|
|
//
|
|
template<typename DT, M68K::AddressMode DAM>
|
|
INLINE void M68K::NEG(HAM<DT, DAM> &dst)
|
|
{
|
|
HAM<DT, IMMEDIATE> dummy_zero(this, 0);
|
|
|
|
dst.write(Subtract<false>(dst, dummy_zero));
|
|
}
|
|
|
|
|
|
//
|
|
// NEGX
|
|
//
|
|
template<typename DT, M68K::AddressMode DAM>
|
|
INLINE void M68K::NEGX(HAM<DT, DAM> &dst)
|
|
{
|
|
HAM<DT, IMMEDIATE> dummy_zero(this, 0);
|
|
|
|
dst.write(Subtract<true>(dst, dummy_zero));
|
|
}
|
|
|
|
|
|
//
|
|
// CMP
|
|
//
|
|
template<typename T, typename DT, M68K::AddressMode SAM, M68K::AddressMode DAM>
|
|
INLINE void M68K::CMP(HAM<T, SAM> &src, HAM<DT, DAM> &dst)
|
|
{
|
|
static_assert(DAM == ADDR_REG_DIR || std::is_same<T, DT>::value, "Type mismatch");
|
|
|
|
// Doesn't affect X flag
|
|
uint32 const src_data = (DT)static_cast<typename std::make_signed<T>::type>(src.read());
|
|
uint32 const dst_data = dst.read();
|
|
uint64 const result = (uint64)dst_data - src_data;
|
|
|
|
CalcZN<DT>(result);
|
|
SetC((result >> (sizeof(DT) * 8)) & 1);
|
|
SetV(((((dst_data ^ src_data)) & (dst_data ^ result)) >> (sizeof(DT) * 8 - 1)) & 1);
|
|
}
|
|
|
|
|
|
//
|
|
// CHK
|
|
//
|
|
// Exception on dst < 0 || dst > src
|
|
template<typename T, M68K::AddressMode SAM, M68K::AddressMode DAM>
|
|
INLINE void M68K::CHK(HAM<T, SAM> &src, HAM<T, DAM> &dst)
|
|
{
|
|
uint32 const src_data = src.read();
|
|
uint32 const dst_data = dst.read();
|
|
|
|
timestamp += 6;
|
|
|
|
CalcZN<T>(dst_data);
|
|
if(GetN())
|
|
{
|
|
Exception(EXCEPTION_CHK, VECNUM_CHK);
|
|
}
|
|
else
|
|
{
|
|
// 7 - 1
|
|
uint64 const result = (uint64)dst_data - src_data;
|
|
|
|
CalcZN<T>(result);
|
|
SetC((result >> (sizeof(T) * 8)) & 1);
|
|
SetV(((((dst_data ^ src_data)) & (dst_data ^ result)) >> (sizeof(T) * 8 - 1)) & 1);
|
|
|
|
if(GetN() == GetV() && !GetZ())
|
|
{
|
|
Exception(EXCEPTION_CHK, VECNUM_CHK);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// OR
|
|
//
|
|
template<typename T, M68K::AddressMode SAM, M68K::AddressMode DAM>
|
|
INLINE void M68K::OR(HAM<T, SAM> &src, HAM<T, DAM> &dst)
|
|
{
|
|
T const src_data = src.read();
|
|
T const dst_data = dst.read();
|
|
T const result = dst_data | src_data;
|
|
|
|
if(sizeof(T) == 4 && DAM == DATA_REG_DIR)
|
|
{
|
|
if(SAM == IMMEDIATE || SAM == DATA_REG_DIR)
|
|
timestamp += 4;
|
|
else
|
|
timestamp += 2;
|
|
}
|
|
|
|
CalcZN<T>(result);
|
|
SetC(false);
|
|
SetV(false);
|
|
|
|
dst.write(result);
|
|
}
|
|
|
|
|
|
//
|
|
// EOR
|
|
//
|
|
template<typename T, M68K::AddressMode SAM, M68K::AddressMode DAM>
|
|
INLINE void M68K::EOR(HAM<T, SAM> &src, HAM<T, DAM> &dst)
|
|
{
|
|
T const src_data = src.read();
|
|
T const dst_data = dst.read();
|
|
T const result = dst_data ^ src_data;
|
|
|
|
if(sizeof(T) == 4 && DAM == DATA_REG_DIR)
|
|
{
|
|
if(SAM == IMMEDIATE || SAM == DATA_REG_DIR)
|
|
timestamp += 4;
|
|
else
|
|
timestamp += 2;
|
|
}
|
|
|
|
CalcZN<T>(result);
|
|
SetC(false);
|
|
SetV(false);
|
|
|
|
dst.write(result);
|
|
}
|
|
|
|
|
|
//
|
|
// AND
|
|
//
|
|
template<typename T, M68K::AddressMode SAM, M68K::AddressMode DAM>
|
|
INLINE void M68K::AND(HAM<T, SAM> &src, HAM<T, DAM> &dst)
|
|
{
|
|
T const src_data = src.read();
|
|
T const dst_data = dst.read();
|
|
T const result = dst_data & src_data;
|
|
|
|
if(sizeof(T) == 4 && DAM == DATA_REG_DIR)
|
|
{
|
|
if(SAM == IMMEDIATE || SAM == DATA_REG_DIR)
|
|
timestamp += 4;
|
|
else
|
|
timestamp += 2;
|
|
}
|
|
|
|
CalcZN<T>(result);
|
|
SetC(false);
|
|
SetV(false);
|
|
|
|
dst.write(result);
|
|
}
|
|
|
|
|
|
//
|
|
// ORI CCR
|
|
//
|
|
INLINE void M68K::ORI_CCR(void)
|
|
{
|
|
const uint8 imm = ReadOp();
|
|
|
|
SetCCR(GetCCR() | imm);
|
|
|
|
//
|
|
//
|
|
timestamp += 8;
|
|
ReadOp();
|
|
PC -= 2;
|
|
}
|
|
|
|
|
|
//
|
|
// ORI SR
|
|
//
|
|
INLINE void M68K::ORI_SR(void)
|
|
{
|
|
const uint16 imm = ReadOp();
|
|
|
|
SetSR(GetSR() | imm);
|
|
|
|
//
|
|
//
|
|
timestamp += 8;
|
|
ReadOp();
|
|
PC -= 2;
|
|
}
|
|
|
|
|
|
//
|
|
// ANDI CCR
|
|
//
|
|
INLINE void M68K::ANDI_CCR(void)
|
|
{
|
|
const uint8 imm = ReadOp();
|
|
|
|
SetCCR(GetCCR() & imm);
|
|
|
|
//
|
|
//
|
|
timestamp += 8;
|
|
ReadOp();
|
|
PC -= 2;
|
|
}
|
|
|
|
|
|
//
|
|
// ANDI SR
|
|
//
|
|
INLINE void M68K::ANDI_SR(void)
|
|
{
|
|
const uint16 imm = ReadOp();
|
|
|
|
SetSR(GetSR() & imm);
|
|
|
|
//
|
|
//
|
|
timestamp += 8;
|
|
ReadOp();
|
|
PC -= 2;
|
|
}
|
|
|
|
|
|
//
|
|
// EORI CCR
|
|
//
|
|
INLINE void M68K::EORI_CCR(void)
|
|
{
|
|
const uint8 imm = ReadOp();
|
|
|
|
SetCCR(GetCCR() ^ imm);
|
|
|
|
//
|
|
//
|
|
timestamp += 8;
|
|
ReadOp();
|
|
PC -= 2;
|
|
}
|
|
|
|
|
|
//
|
|
// EORI SR
|
|
//
|
|
INLINE void M68K::EORI_SR(void)
|
|
{
|
|
const uint16 imm = ReadOp();
|
|
|
|
SetSR(GetSR() ^ imm);
|
|
|
|
//
|
|
//
|
|
timestamp += 8;
|
|
ReadOp();
|
|
PC -= 2;
|
|
}
|
|
|
|
|
|
//
|
|
// MULU
|
|
//
|
|
template<typename T, M68K::AddressMode SAM>
|
|
INLINE void M68K::MULU(HAM<T, SAM> &src, const unsigned dr)
|
|
{
|
|
// Doesn't affect X flag
|
|
static_assert(sizeof(T) == 2, "Wrong type.");
|
|
|
|
T const src_data = src.read();
|
|
uint32 const result = (uint32)(uint16)D[dr] * (uint32)src_data;
|
|
|
|
CalcZN<uint32>(result);
|
|
SetC(false);
|
|
SetV(false);
|
|
|
|
D[dr] = result;
|
|
}
|
|
|
|
|
|
//
|
|
// MULS
|
|
//
|
|
template<typename T, M68K::AddressMode SAM>
|
|
INLINE void M68K::MULS(HAM<T, SAM> &src, const unsigned dr)
|
|
{
|
|
// Doesn't affect X flag
|
|
static_assert(sizeof(T) == 2, "Wrong type.");
|
|
|
|
T const src_data = src.read();
|
|
uint32 const result = (int16)D[dr] * (int16)src_data;
|
|
|
|
CalcZN<uint32>(result);
|
|
SetC(false);
|
|
SetV(false);
|
|
|
|
D[dr] = result;
|
|
}
|
|
|
|
|
|
template<bool sdiv>
|
|
INLINE void M68K::Divide(uint16 divisor, const unsigned dr)
|
|
{
|
|
uint32 dividend = D[dr];
|
|
uint32 tmp;
|
|
bool neg_quotient = false;
|
|
bool neg_remainder = false;
|
|
bool oflow = false;
|
|
|
|
if(!divisor)
|
|
{
|
|
Exception(EXCEPTION_ZERO_DIVIDE, VECNUM_ZERO_DIVIDE);
|
|
return;
|
|
}
|
|
|
|
if(sdiv)
|
|
{
|
|
neg_quotient = (dividend >> 31) ^ (divisor >> 15);
|
|
if(dividend & 0x80000000)
|
|
{
|
|
dividend = -dividend;
|
|
neg_remainder = true;
|
|
}
|
|
|
|
if(divisor & 0x8000)
|
|
divisor = -divisor;
|
|
}
|
|
|
|
tmp = dividend;
|
|
|
|
for(int i = 0; i < 16; i++)
|
|
{
|
|
bool lb = false;
|
|
bool ob;
|
|
|
|
if(tmp >= ((uint32)divisor << 15))
|
|
{
|
|
tmp -= divisor << 15;
|
|
lb = true;
|
|
}
|
|
|
|
ob = tmp >> 31;
|
|
tmp = (tmp << 1) | lb;
|
|
|
|
if(ob)
|
|
{
|
|
oflow = true;
|
|
//puts("OVERFLOW");
|
|
//break;
|
|
}
|
|
}
|
|
|
|
if(sdiv)
|
|
{
|
|
if((tmp & 0xFFFF) > (uint32)(0x7FFF + neg_quotient))
|
|
oflow = true;
|
|
}
|
|
|
|
if((uint32)(tmp >> 16) >= divisor)
|
|
oflow = true;
|
|
|
|
if(sdiv && !oflow)
|
|
{
|
|
if(neg_quotient)
|
|
tmp = ((-tmp) & 0xFFFF) | (tmp & 0xFFFF0000);
|
|
|
|
if(neg_remainder)
|
|
tmp = (((-(tmp >> 16)) << 16) & 0xFFFF0000) | (tmp & 0xFFFF);
|
|
}
|
|
|
|
//
|
|
// Doesn't affect X flag
|
|
//
|
|
CalcZN<uint16>(tmp);
|
|
SetC(false);
|
|
SetV(oflow);
|
|
|
|
if(!oflow)
|
|
D[dr] = tmp;
|
|
}
|
|
|
|
|
|
//
|
|
// DIVU
|
|
//
|
|
template<typename T, M68K::AddressMode SAM>
|
|
INLINE void M68K::DIVU(HAM<T, SAM> &src, const unsigned dr)
|
|
{
|
|
static_assert(sizeof(T) == 2, "Wrong type.");
|
|
|
|
T const src_data = src.read();
|
|
|
|
Divide<false>(src_data, dr);
|
|
}
|
|
|
|
|
|
//
|
|
// DIVS
|
|
//
|
|
template<typename T, M68K::AddressMode SAM>
|
|
INLINE void M68K::DIVS(HAM<T, SAM> &src, const unsigned dr)
|
|
{
|
|
// Doesn't affect X flag
|
|
static_assert(sizeof(T) == 2, "Wrong type.");
|
|
|
|
T const src_data = src.read();
|
|
|
|
Divide<true>(src_data, dr);
|
|
}
|
|
|
|
|
|
//
|
|
// ABCD
|
|
//
|
|
template<typename T, M68K::AddressMode SAM, M68K::AddressMode DAM>
|
|
INLINE void M68K::ABCD(HAM<T, SAM> &src, HAM<T, DAM> &dst) // ...XYZ, now I know my ABCs~
|
|
{
|
|
static_assert(sizeof(T) == 1, "Wrong size.");
|
|
|
|
bool V = false;
|
|
uint8 const src_data = src.read();
|
|
uint8 const dst_data = dst.read();
|
|
uint32 tmp;
|
|
|
|
tmp = dst_data + src_data + GetX();
|
|
|
|
if(((dst_data ^ src_data ^ tmp) & 0x10) || (tmp & 0xF) >= 0x0A)
|
|
{
|
|
uint8 prev_tmp = tmp;
|
|
tmp += 0x06;
|
|
V |= ((~prev_tmp & 0x80) & (tmp & 0x80));
|
|
}
|
|
|
|
if(tmp >= 0xA0)
|
|
{
|
|
uint8 prev_tmp = tmp;
|
|
tmp += 0x60;
|
|
V |= ((~prev_tmp & 0x80) & (tmp & 0x80));
|
|
}
|
|
|
|
CalcZN<uint8, true>(tmp);
|
|
SetCX((bool)(tmp >> 8));
|
|
SetV(V);
|
|
|
|
if(DAM == DATA_REG_DIR)
|
|
timestamp += 2;
|
|
else
|
|
timestamp += 4;
|
|
|
|
dst.write(tmp);
|
|
}
|
|
|
|
|
|
INLINE uint8 M68K::DecimalSubtractX(const uint8 src_data, const uint8 dst_data)
|
|
{
|
|
bool V = false;
|
|
uint32 tmp;
|
|
|
|
tmp = dst_data - src_data - GetX();
|
|
|
|
const bool adj0 = ((dst_data ^ src_data ^ tmp) & 0x10);
|
|
const bool adj1 = (tmp & 0x100);
|
|
|
|
if(adj0)
|
|
{
|
|
uint8 prev_tmp = tmp;
|
|
tmp -= 0x06;
|
|
V |= (prev_tmp & 0x80) & (~tmp & 0x80);
|
|
}
|
|
|
|
if(adj1)
|
|
{
|
|
uint8 prev_tmp = tmp;
|
|
tmp -= 0x60;
|
|
V |= (prev_tmp & 0x80) & (~tmp & 0x80);
|
|
}
|
|
|
|
SetV(V);
|
|
CalcZN<uint8, true>(tmp);
|
|
SetCX((bool)(tmp >> 8));
|
|
|
|
return tmp;
|
|
}
|
|
|
|
//
|
|
// SBCD
|
|
//
|
|
template<typename T, M68K::AddressMode SAM, M68K::AddressMode DAM>
|
|
INLINE void M68K::SBCD(HAM<T, SAM> &src, HAM<T, DAM> &dst)
|
|
{
|
|
static_assert(sizeof(T) == 1, "Wrong size.");
|
|
uint8 const src_data = src.read();
|
|
uint8 const dst_data = dst.read();
|
|
|
|
if(DAM == DATA_REG_DIR)
|
|
timestamp += 2;
|
|
else
|
|
timestamp += 4;
|
|
|
|
dst.write(DecimalSubtractX(src_data, dst_data));
|
|
}
|
|
|
|
|
|
//
|
|
// NBCD
|
|
//
|
|
template<typename T, M68K::AddressMode DAM>
|
|
INLINE void M68K::NBCD(HAM<T, DAM> &dst)
|
|
{
|
|
static_assert(sizeof(T) == 1, "Wrong size.");
|
|
uint8 const dst_data = dst.read();
|
|
|
|
timestamp += 2;
|
|
|
|
dst.write(DecimalSubtractX(dst_data, 0));
|
|
}
|
|
|
|
//
|
|
// MOVEP
|
|
//
|
|
template<typename T, bool reg_to_mem>
|
|
INLINE void M68K::MOVEP(const unsigned ar, const unsigned dr)
|
|
{
|
|
const int16 ext = ReadOp();
|
|
uint32 ea = A[ar] + (int16)ext;
|
|
unsigned shift = (sizeof(T) - 1) << 3;
|
|
|
|
for(unsigned i = 0; i < sizeof(T); i++)
|
|
{
|
|
if(reg_to_mem)
|
|
Write<uint8>(ea, D[dr] >> shift);
|
|
else
|
|
{
|
|
D[dr] &= ~(0xFF << shift);
|
|
D[dr] |= Read<uint8>(ea) << shift;
|
|
}
|
|
ea += 2;
|
|
shift -= 8;
|
|
}
|
|
}
|
|
|
|
|
|
template<typename T, M68K::AddressMode TAM>
|
|
INLINE void M68K::BTST(HAM<T, TAM> &targ, unsigned wb)
|
|
{
|
|
T const src_data = targ.read();
|
|
wb &= (sizeof(T) << 3) - 1;
|
|
|
|
SetZ(((src_data >> wb) & 1) == 0);
|
|
}
|
|
|
|
template<typename T, M68K::AddressMode TAM>
|
|
INLINE void M68K::BCHG(HAM<T, TAM> &targ, unsigned wb)
|
|
{
|
|
T const src_data = targ.read();
|
|
wb &= (sizeof(T) << 3) - 1;
|
|
|
|
SetZ(((src_data >> wb) & 1) == 0);
|
|
|
|
targ.write(src_data ^ (1U << wb));
|
|
}
|
|
|
|
template<typename T, M68K::AddressMode TAM>
|
|
INLINE void M68K::BCLR(HAM<T, TAM> &targ, unsigned wb)
|
|
{
|
|
T const src_data = targ.read();
|
|
wb &= (sizeof(T) << 3) - 1;
|
|
|
|
SetZ(((src_data >> wb) & 1) == 0);
|
|
|
|
targ.write(src_data & ~(1U << wb));
|
|
}
|
|
|
|
template<typename T, M68K::AddressMode TAM>
|
|
INLINE void M68K::BSET(HAM<T, TAM> &targ, unsigned wb)
|
|
{
|
|
T const src_data = targ.read();
|
|
wb &= (sizeof(T) << 3) - 1;
|
|
|
|
SetZ(((src_data >> wb) & 1) == 0);
|
|
|
|
targ.write(src_data | (1U << wb));
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// MOVE
|
|
//
|
|
template<typename T, M68K::AddressMode SAM, M68K::AddressMode DAM>
|
|
INLINE void M68K::MOVE(HAM<T, SAM> &src, HAM<T, DAM> &dst)
|
|
{
|
|
T const tmp = src.read();
|
|
|
|
if(DAM != ADDR_REG_DIR)
|
|
{
|
|
CalcZN<T>(tmp);
|
|
SetV(false);
|
|
SetC(false);
|
|
}
|
|
|
|
dst.write(tmp);
|
|
}
|
|
|
|
template<typename T, M68K::AddressMode SAM>
|
|
INLINE void M68K::MOVEA(HAM<T, SAM> &src, const unsigned ar)
|
|
{
|
|
uint32 const src_data = static_cast<typename std::make_signed<T>::type>(src.read());
|
|
|
|
A[ar] = src_data;
|
|
}
|
|
|
|
|
|
//
|
|
// MOVEM to memory
|
|
//
|
|
template<bool pseudo_predec, typename T, M68K::AddressMode DAM>
|
|
INLINE void M68K::MOVEM_to_MEM(const uint16 reglist, HAM<T, DAM> &dst)
|
|
{
|
|
static_assert(DAM != ADDR_REG_INDIR_PRE && DAM != ADDR_REG_INDIR_POST, "Wrong address mode.");
|
|
static_assert(!pseudo_predec || DAM == ADDR_REG_INDIR, "Wrong address mode.");
|
|
|
|
uint32 ea = dst.getea();
|
|
|
|
for(unsigned i = 0; i < 16; i++)
|
|
{
|
|
if(reglist & (1U << i))
|
|
{
|
|
if(pseudo_predec)
|
|
ea -= sizeof(T);
|
|
|
|
Write<T, pseudo_predec>(ea, DA[(pseudo_predec ? (15 - i) : i)]);
|
|
|
|
if(!pseudo_predec)
|
|
ea += sizeof(T);
|
|
}
|
|
}
|
|
|
|
if(pseudo_predec)
|
|
A[dst.reg] = ea;
|
|
}
|
|
|
|
|
|
//
|
|
// MOVEM to regs(from memory)
|
|
//
|
|
template<bool pseudo_postinc, typename T, M68K::AddressMode SAM>
|
|
INLINE void M68K::MOVEM_to_REGS(HAM<T, SAM> &src, const uint16 reglist)
|
|
{
|
|
static_assert(SAM != ADDR_REG_INDIR_PRE && SAM != ADDR_REG_INDIR_POST, "Wrong address mode.");
|
|
static_assert(!pseudo_postinc || SAM == ADDR_REG_INDIR, "Wrong address mode.");
|
|
|
|
uint32 ea = src.getea();
|
|
|
|
for(unsigned i = 0; i < 16; i++)
|
|
{
|
|
if(reglist & (1U << i))
|
|
{
|
|
T tmp = Read<T>(ea);
|
|
|
|
DA[i] = static_cast<typename std::make_signed<T>::type>(tmp);
|
|
|
|
ea += sizeof(T);
|
|
}
|
|
}
|
|
|
|
Read<uint16>(ea); // or should be <T> ?
|
|
|
|
if(pseudo_postinc)
|
|
A[src.reg] = ea;
|
|
}
|
|
|
|
|
|
template<typename T, M68K::AddressMode TAM, bool Arithmetic, bool ShiftLeft>
|
|
INLINE void M68K::ShiftBase(HAM<T, TAM> &targ, unsigned count)
|
|
{
|
|
T vchange = 0;
|
|
T result = targ.read();
|
|
count &= 0x3F;
|
|
|
|
if(TAM == DATA_REG_DIR)
|
|
timestamp += (sizeof(T) == 4) ? 4 : 2;
|
|
|
|
if(count == 0)
|
|
{
|
|
// X is unaffected with a shift count of 0!
|
|
SetC(false);
|
|
}
|
|
else
|
|
{
|
|
bool shifted_out = false;
|
|
|
|
do
|
|
{
|
|
if(TAM == DATA_REG_DIR)
|
|
timestamp += 2;
|
|
|
|
if(ShiftLeft)
|
|
shifted_out = (result >> (sizeof(T) * 8 - 1)) & 1;
|
|
else
|
|
shifted_out = result & 1;
|
|
|
|
if(Arithmetic)
|
|
{
|
|
const T prev = result;
|
|
|
|
if(ShiftLeft)
|
|
result = result << 1;
|
|
else
|
|
result = static_cast<typename std::make_signed<T>::type>(result) >> 1;
|
|
|
|
vchange |= prev ^ result;
|
|
}
|
|
else
|
|
{
|
|
if(ShiftLeft)
|
|
result = result << 1;
|
|
else
|
|
result = result >> 1;
|
|
}
|
|
} while(--count != 0);
|
|
|
|
SetCX(shifted_out);
|
|
}
|
|
|
|
CalcZN<T>(result);
|
|
|
|
if(Arithmetic)
|
|
SetV((vchange >> (sizeof(T) * 8 - 1)) & 1);
|
|
else
|
|
SetV(false);
|
|
|
|
targ.write(result);
|
|
}
|
|
|
|
template<typename T, M68K::AddressMode TAM>
|
|
INLINE void M68K::ASL(HAM<T, TAM> &targ, unsigned count)
|
|
{
|
|
ShiftBase<T, TAM, true, true>(targ, count);
|
|
}
|
|
|
|
template<typename T, M68K::AddressMode TAM>
|
|
INLINE void M68K::ASR(HAM<T, TAM> &targ, unsigned count)
|
|
{
|
|
ShiftBase<T, TAM, true, false>(targ, count);
|
|
}
|
|
|
|
template<typename T, M68K::AddressMode TAM>
|
|
INLINE void M68K::LSL(HAM<T, TAM> &targ, unsigned count)
|
|
{
|
|
ShiftBase<T, TAM, false, true>(targ, count);
|
|
}
|
|
|
|
template<typename T, M68K::AddressMode TAM>
|
|
INLINE void M68K::LSR(HAM<T, TAM> &targ, unsigned count)
|
|
{
|
|
ShiftBase<T, TAM, false, false>(targ, count);
|
|
}
|
|
|
|
template<typename T, M68K::AddressMode TAM, bool X_Form, bool ShiftLeft>
|
|
INLINE void M68K::RotateBase(HAM<T, TAM> &targ, unsigned count)
|
|
{
|
|
T result = targ.read();
|
|
count &= 0x3F;
|
|
|
|
if(TAM == DATA_REG_DIR)
|
|
timestamp += (sizeof(T) == 4) ? 4 : 2;
|
|
|
|
if(count == 0)
|
|
{
|
|
if(X_Form)
|
|
SetC(GetX());
|
|
else
|
|
SetC(false);
|
|
}
|
|
else
|
|
{
|
|
bool shifted_out = GetX();
|
|
|
|
do
|
|
{
|
|
const bool shift_in = shifted_out;
|
|
|
|
if(TAM == DATA_REG_DIR)
|
|
timestamp += 2;
|
|
|
|
if(ShiftLeft)
|
|
{
|
|
shifted_out = (result >> (sizeof(T) * 8 - 1)) & 1;
|
|
result <<= 1;
|
|
result |= (X_Form ? shift_in : shifted_out);
|
|
}
|
|
else
|
|
{
|
|
shifted_out = (result & 1);
|
|
result >>= 1;
|
|
result |= (T)(X_Form ? shift_in : shifted_out) << (sizeof(T) * 8 - 1);
|
|
}
|
|
} while(--count != 0);
|
|
|
|
SetC(shifted_out);
|
|
if(X_Form)
|
|
SetX(shifted_out);
|
|
}
|
|
|
|
CalcZN<T>(result);
|
|
SetV(false);
|
|
|
|
targ.write(result);
|
|
}
|
|
|
|
template<typename T, M68K::AddressMode TAM>
|
|
INLINE void M68K::ROL(HAM<T, TAM> &targ, unsigned count)
|
|
{
|
|
RotateBase<T, TAM, false, true>(targ, count);
|
|
}
|
|
|
|
template<typename T, M68K::AddressMode TAM>
|
|
INLINE void M68K::ROR(HAM<T, TAM> &targ, unsigned count)
|
|
{
|
|
RotateBase<T, TAM, false, false>(targ, count);
|
|
}
|
|
|
|
template<typename T, M68K::AddressMode TAM>
|
|
INLINE void M68K::ROXL(HAM<T, TAM> &targ, unsigned count)
|
|
{
|
|
RotateBase<T, TAM, true, true>(targ, count);
|
|
}
|
|
|
|
template<typename T, M68K::AddressMode TAM>
|
|
INLINE void M68K::ROXR(HAM<T, TAM> &targ, unsigned count)
|
|
{
|
|
RotateBase<T, TAM, true, false>(targ, count);
|
|
}
|
|
|
|
//
|
|
// TAS
|
|
//
|
|
static MDFN_FASTCALL uint8 TAS_Callback(M68K* zptr, uint8 data)
|
|
{
|
|
zptr->CalcZN<uint8>(data);
|
|
zptr->SetC(false);
|
|
zptr->SetV(false);
|
|
|
|
data |= 0x80;
|
|
return data;
|
|
}
|
|
|
|
template<typename T, M68K::AddressMode DAM>
|
|
INLINE void M68K::TAS(HAM<T, DAM> &dst)
|
|
{
|
|
static_assert(std::is_same<T, uint8>::value, "Wrong type");
|
|
|
|
dst.rmw(TAS_Callback);
|
|
}
|
|
|
|
//
|
|
// TST
|
|
//
|
|
template<typename T, M68K::AddressMode DAM>
|
|
INLINE void M68K::TST(HAM<T, DAM> &dst)
|
|
{
|
|
T const dst_data = dst.read();
|
|
|
|
CalcZN<T>(dst_data);
|
|
|
|
SetC(false);
|
|
SetV(false);
|
|
}
|
|
|
|
|
|
//
|
|
// CLR
|
|
//
|
|
template<typename T, M68K::AddressMode DAM>
|
|
INLINE void M68K::CLR(HAM<T, DAM> &dst)
|
|
{
|
|
dst.read();
|
|
|
|
if(sizeof(T) == 4 && DAM == DATA_REG_DIR)
|
|
timestamp += 2;
|
|
|
|
SetZ(true);
|
|
SetN(false);
|
|
|
|
SetC(false);
|
|
SetV(false);
|
|
|
|
dst.write(0);
|
|
}
|
|
|
|
//
|
|
// NOT
|
|
//
|
|
template<typename T, M68K::AddressMode DAM>
|
|
INLINE void M68K::NOT(HAM<T, DAM> &dst)
|
|
{
|
|
T result = dst.read();
|
|
|
|
if(sizeof(T) == 4 && DAM == DATA_REG_DIR)
|
|
timestamp += 2;
|
|
|
|
result = ~result;
|
|
|
|
CalcZN<T>(result);
|
|
SetC(false);
|
|
SetV(false);
|
|
|
|
dst.write(result);
|
|
}
|
|
|
|
|
|
//
|
|
// EXT
|
|
//
|
|
template<typename T, M68K::AddressMode DAM>
|
|
INLINE void M68K::EXT(HAM<T, DAM> &dst)
|
|
{
|
|
static_assert(std::is_same<T, uint16>::value || std::is_same<T, uint32>::value, "Wrong type");
|
|
T result = dst.read();
|
|
|
|
if(std::is_same<T, uint16>::value)
|
|
result = (int8)result;
|
|
else
|
|
result = (int16)result;
|
|
|
|
CalcZN<T>(result);
|
|
SetC(false);
|
|
SetV(false);
|
|
|
|
dst.write(result);
|
|
}
|
|
|
|
//
|
|
// SWAP
|
|
//
|
|
INLINE void M68K::SWAP(const unsigned dr)
|
|
{
|
|
D[dr] = (D[dr] << 16) | (D[dr] >> 16);
|
|
|
|
CalcZN<uint32>(D[dr]);
|
|
SetC(false);
|
|
SetV(false);
|
|
}
|
|
|
|
|
|
//
|
|
// EXG (doesn't affect flags)
|
|
//
|
|
INLINE void M68K::EXG(uint32* a, uint32* b)
|
|
{
|
|
timestamp += 2;
|
|
|
|
std::swap(*a, *b);
|
|
}
|
|
|
|
//
|
|
//
|
|
//
|
|
|
|
template<unsigned cc>
|
|
INLINE bool M68K::TestCond(void)
|
|
{
|
|
static_assert(cc < 0x10, "Invalid CC");
|
|
|
|
switch(cc)
|
|
{
|
|
case 0x00: // TRUE
|
|
return true;
|
|
|
|
case 0x01: // FALSE
|
|
return false;
|
|
|
|
case 0x02: // HI
|
|
return !GetC() && !GetZ();
|
|
|
|
case 0x03: // LS
|
|
return GetC() || GetZ();
|
|
|
|
case 0x04: // CC/HS
|
|
return !GetC();
|
|
|
|
case 0x05: // CS/LO
|
|
return GetC();
|
|
|
|
case 0x06: // NE
|
|
return !GetZ();
|
|
|
|
case 0x07: // EQ
|
|
return GetZ();
|
|
|
|
case 0x08: // VC
|
|
return !GetV();
|
|
|
|
case 0x09: // VS
|
|
return GetV();
|
|
|
|
case 0x0A: // PL
|
|
return !GetN();
|
|
|
|
case 0x0B: // MI
|
|
return GetN();
|
|
|
|
case 0x0C: // GE
|
|
return GetN() == GetV();
|
|
|
|
case 0x0D: // LT
|
|
return GetN() != GetV();
|
|
|
|
case 0x0E: // GT
|
|
return GetN() == GetV() && !GetZ();
|
|
|
|
case 0x0F: // LE
|
|
return GetN() != GetV() || GetZ();
|
|
}
|
|
}
|
|
|
|
//
|
|
// Bcc, BRA, BSR
|
|
//
|
|
// (caller of this function should sign-extend the 8-bit displacement)
|
|
//
|
|
template<unsigned cc>
|
|
INLINE void M68K::Bxx(uint32 disp)
|
|
{
|
|
const uint32 BPC = PC;
|
|
|
|
if(TestCond<(cc == 0x01) ? 0x00 : cc>())
|
|
{
|
|
const uint16 disp16 = (int16)ReadOp();
|
|
|
|
if(!disp)
|
|
disp = (int16)disp16;
|
|
else
|
|
PC -= 2;
|
|
|
|
if(cc == 0x01)
|
|
Push<uint32>(PC);
|
|
|
|
timestamp += 2;
|
|
PC = BPC + disp;
|
|
}
|
|
else
|
|
{
|
|
if(!disp)
|
|
ReadOp();
|
|
|
|
timestamp += 4;
|
|
}
|
|
}
|
|
|
|
template<unsigned cc>
|
|
INLINE void M68K::DBcc(const unsigned dr)
|
|
{
|
|
const uint32 BPC = PC;
|
|
uint32 disp;
|
|
|
|
disp = (int16)ReadOp();
|
|
|
|
if(!TestCond<cc>())
|
|
{
|
|
const uint16 result = D[dr] - 1;
|
|
|
|
timestamp += 2;
|
|
D[dr] = (D[dr] & 0xFFFF0000) | result;
|
|
|
|
if(result != 0xFFFF)
|
|
PC = BPC + disp;
|
|
else
|
|
timestamp += 4;
|
|
}
|
|
else
|
|
timestamp += 4;
|
|
}
|
|
|
|
|
|
//
|
|
// Scc
|
|
//
|
|
template<unsigned cc, typename T, M68K::AddressMode DAM>
|
|
INLINE void M68K::Scc(HAM<T, DAM> &dst)
|
|
{
|
|
static_assert(std::is_same<T, uint8>::value, "Wrong type");
|
|
|
|
T const result = TestCond<cc>() ? ~(T)0 : 0;
|
|
|
|
if(DAM == DATA_REG_DIR && result)
|
|
timestamp += 2;
|
|
|
|
dst.write(result);
|
|
}
|
|
|
|
|
|
//
|
|
// JSR
|
|
//
|
|
template<typename T, M68K::AddressMode TAM>
|
|
INLINE void M68K::JSR(HAM<T, TAM> &targ)
|
|
{
|
|
Push<uint32>(PC);
|
|
targ.jump();
|
|
}
|
|
|
|
|
|
//
|
|
// JMP
|
|
//
|
|
template<typename T, M68K::AddressMode TAM>
|
|
INLINE void M68K::JMP(HAM<T, TAM> &targ)
|
|
{
|
|
targ.jump();
|
|
}
|
|
|
|
|
|
//
|
|
// MOVE from SR
|
|
//
|
|
template <typename T, M68K::AddressMode DAM>
|
|
INLINE void M68K::MOVE_from_SR(HAM<T, DAM> &dst)
|
|
{
|
|
static_assert(std::is_same<T, uint16>::value, "Wrong type");
|
|
|
|
dst.read();
|
|
|
|
if(DAM == DATA_REG_DIR)
|
|
timestamp += 2;
|
|
|
|
dst.write(GetSR());
|
|
}
|
|
|
|
|
|
//
|
|
// MOVE to CCR
|
|
//
|
|
template<typename T, M68K::AddressMode SAM>
|
|
INLINE void M68K::MOVE_to_CCR(HAM<T, SAM> &src)
|
|
{
|
|
static_assert(std::is_same<T, uint16>::value, "Wrong type");
|
|
|
|
SetCCR(src.read());
|
|
|
|
timestamp += 8;
|
|
}
|
|
|
|
//
|
|
// MOVE to SR
|
|
//
|
|
template<typename T, M68K::AddressMode SAM>
|
|
INLINE void M68K::MOVE_to_SR(HAM<T, SAM> &src)
|
|
{
|
|
static_assert(std::is_same<T, uint16>::value, "Wrong type");
|
|
|
|
SetSR(src.read());
|
|
|
|
timestamp += 8;
|
|
}
|
|
|
|
|
|
//
|
|
// MOVE to/from USP
|
|
//
|
|
template<bool direction>
|
|
INLINE void M68K::MOVE_USP(const unsigned ar)
|
|
{
|
|
if(!direction)
|
|
SP_Inactive = A[ar];
|
|
else
|
|
A[ar] = SP_Inactive;
|
|
}
|
|
|
|
|
|
//
|
|
// LEA
|
|
//
|
|
template<typename T, M68K::AddressMode SAM>
|
|
INLINE void M68K::LEA(HAM<T, SAM> &src, const unsigned ar)
|
|
{
|
|
const uint32 ea = src.getea();
|
|
|
|
A[ar] = ea;
|
|
}
|
|
|
|
|
|
//
|
|
// PEA
|
|
//
|
|
template<typename T, M68K::AddressMode SAM>
|
|
INLINE void M68K::PEA(HAM<T, SAM> &src)
|
|
{
|
|
const uint32 ea = src.getea();
|
|
|
|
Push<uint32>(ea);
|
|
}
|
|
|
|
//
|
|
// UNLK
|
|
//
|
|
INLINE void M68K::UNLK(const unsigned ar)
|
|
{
|
|
A[7] = A[ar];
|
|
A[ar] = Pull<uint32>();
|
|
}
|
|
|
|
|
|
//
|
|
// LINK
|
|
//
|
|
INLINE void M68K::LINK(const unsigned ar)
|
|
{
|
|
const uint32 disp = (int16)ReadOp();
|
|
|
|
Push<uint32>(A[ar]);
|
|
A[ar] = A[7];
|
|
A[7] += disp;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
// RTE
|
|
//
|
|
INLINE void M68K::RTE(void)
|
|
{
|
|
uint16 new_SR;
|
|
|
|
new_SR = Pull<uint16>();
|
|
PC = Pull<uint32>();
|
|
|
|
SetSR(new_SR);
|
|
}
|
|
|
|
|
|
//
|
|
// RTR
|
|
//
|
|
INLINE void M68K::RTR(void)
|
|
{
|
|
SetCCR(Pull<uint16>());
|
|
PC = Pull<uint32>();
|
|
}
|
|
|
|
|
|
//
|
|
// RTS
|
|
//
|
|
INLINE void M68K::RTS(void)
|
|
{
|
|
PC = Pull<uint32>();
|
|
}
|
|
|
|
|
|
//
|
|
// TRAP
|
|
//
|
|
INLINE void M68K::TRAP(const unsigned vf)
|
|
{
|
|
Exception(EXCEPTION_TRAP, VECNUM_TRAP_BASE + vf);
|
|
}
|
|
|
|
|
|
//
|
|
// TRAPV
|
|
//
|
|
INLINE void M68K::TRAPV(void)
|
|
{
|
|
if(GetV())
|
|
Exception(EXCEPTION_TRAPV, VECNUM_TRAPV);
|
|
}
|
|
|
|
|
|
//
|
|
// ILLEGAL
|
|
//
|
|
INLINE void M68K::ILLEGAL(const uint16 instr)
|
|
{
|
|
//printf("ILLEGAL: %04x\n", instr);
|
|
|
|
PC -= 2;
|
|
Exception(EXCEPTION_ILLEGAL, VECNUM_ILLEGAL);
|
|
}
|
|
|
|
|
|
INLINE void M68K::LINEA(void)
|
|
{
|
|
PC -= 2;
|
|
Exception(EXCEPTION_ILLEGAL, VECNUM_LINEA);
|
|
}
|
|
|
|
INLINE void M68K::LINEF(void)
|
|
{
|
|
PC -= 2;
|
|
Exception(EXCEPTION_ILLEGAL, VECNUM_LINEF);
|
|
}
|
|
|
|
|
|
//
|
|
// NOP
|
|
//
|
|
INLINE void M68K::NOP(void)
|
|
{
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// RESET
|
|
//
|
|
INLINE void M68K::RESET(void)
|
|
{
|
|
timestamp += 2;
|
|
//
|
|
BusRESET(true);
|
|
timestamp += 124;
|
|
BusRESET(false);
|
|
//
|
|
timestamp += 2;
|
|
}
|
|
|
|
|
|
//
|
|
// STOP
|
|
//
|
|
INLINE void M68K::STOP(void)
|
|
{
|
|
uint16 new_SR = ReadOp();
|
|
|
|
SetSR(new_SR);
|
|
XPending |= XPENDING_MASK_STOPPED;
|
|
}
|
|
|
|
|
|
INLINE bool M68K::CheckPrivilege(void)
|
|
{
|
|
if(MDFN_UNLIKELY(!GetSVisor()))
|
|
{
|
|
PC -= 2;
|
|
Exception(EXCEPTION_PRIVILEGE, VECNUM_PRIVILEGE);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//
|
|
//
|
|
INLINE void M68K::InternalStep(void)
|
|
{
|
|
if(MDFN_UNLIKELY(XPending))
|
|
{
|
|
if(MDFN_LIKELY(!(XPending & XPENDING_MASK_EXTHALTED)))
|
|
{
|
|
if(MDFN_UNLIKELY(XPending & XPENDING_MASK_RESET))
|
|
{
|
|
XPending &= ~XPENDING_MASK_RESET;
|
|
|
|
SetSVisor(true);
|
|
SetTrace(false);
|
|
SetIMask(0x7);
|
|
|
|
A[7] = Read<uint32>(VECNUM_RESET_SSP << 2);
|
|
PC = Read<uint32>(VECNUM_RESET_PC << 2);
|
|
|
|
return;
|
|
}
|
|
else if(XPending & (XPENDING_MASK_INT | XPENDING_MASK_NMI))
|
|
{
|
|
assert(IPL == 0x7 || IPL > ((GetSR() >> 8) & 0x7));
|
|
XPending &= ~(XPENDING_MASK_STOPPED | XPENDING_MASK_INT | XPENDING_MASK_NMI);
|
|
|
|
Exception(EXCEPTION_INT, VECNUM_INT_BASE);
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
// STOP and ExtHalted fallthrough:
|
|
timestamp += 4;
|
|
return;
|
|
}
|
|
//
|
|
//
|
|
//
|
|
uint16 instr = ReadOp();
|
|
const unsigned instr_b11_b9 = (instr >> 9) & 0x7;
|
|
const unsigned instr_b2_b0 = instr & 0x7;
|
|
|
|
#if 0
|
|
printf("PC=%08x: %04x ---", PC - 2, instr);
|
|
|
|
for(unsigned i = 0; i < 8; i++)
|
|
printf(" A%u=0x%08x", i, A[i]);
|
|
|
|
for(unsigned i = 0; i < 8; i++)
|
|
printf(" D%u=0x%08x", i, D[i]);
|
|
|
|
printf("\n");
|
|
#endif
|
|
switch(instr)
|
|
{
|
|
default: ILLEGAL(instr); break;
|
|
#include "m68k_instr.inc"
|
|
}
|
|
}
|
|
|
|
|
|
void NO_INLINE M68K::Run(int32 run_until_time)
|
|
{
|
|
while(MDFN_LIKELY(timestamp < run_until_time))
|
|
InternalStep();
|
|
}
|
|
|
|
void NO_INLINE M68K::Step(void)
|
|
{
|
|
//printf("%08x\n", PC);
|
|
InternalStep();
|
|
}
|
|
|
|
//
|
|
// Reset() may be called from BusRESET, which is called from RESET, so ensure it continues working for that case.
|
|
//
|
|
void M68K::Reset(bool powering_up)
|
|
{
|
|
if(powering_up)
|
|
{
|
|
for(unsigned i = 0; i < 8; i++)
|
|
D[i] = 0;
|
|
|
|
for(unsigned i = 0; i < 8; i++)
|
|
A[i] = 0;
|
|
|
|
SP_Inactive = 0;
|
|
|
|
SetSR(0);
|
|
}
|
|
XPending = (XPending & ~XPENDING_MASK_STOPPED) | XPENDING_MASK_RESET;
|
|
}
|
|
|
|
|
|
//
|
|
//
|
|
//
|
|
uint32 M68K::GetRegister(unsigned which, char* special, const uint32 special_len)
|
|
{
|
|
switch(which)
|
|
{
|
|
default:
|
|
return 0xDEADBEEF;
|
|
|
|
case GSREG_D0: case GSREG_D1: case GSREG_D2: case GSREG_D3:
|
|
case GSREG_D4: case GSREG_D5: case GSREG_D6: case GSREG_D7:
|
|
return D[which - GSREG_D0];
|
|
|
|
case GSREG_A0: case GSREG_A1: case GSREG_A2: case GSREG_A3:
|
|
case GSREG_A4: case GSREG_A5: case GSREG_A6: case GSREG_A7:
|
|
return A[which - GSREG_A0];
|
|
|
|
case GSREG_PC:
|
|
return PC;
|
|
|
|
case GSREG_SR:
|
|
return GetSR();
|
|
|
|
case GSREG_SSP:
|
|
if(GetSVisor())
|
|
return A[7];
|
|
else
|
|
return SP_Inactive;
|
|
|
|
case GSREG_USP:
|
|
if(!GetSVisor())
|
|
return A[7];
|
|
else
|
|
return SP_Inactive;
|
|
}
|
|
}
|
|
|
|
void M68K::SetRegister(unsigned which, uint32 value)
|
|
{
|
|
switch(which)
|
|
{
|
|
case GSREG_D0: case GSREG_D1: case GSREG_D2: case GSREG_D3:
|
|
case GSREG_D4: case GSREG_D5: case GSREG_D6: case GSREG_D7:
|
|
D[which - GSREG_D0] = value;
|
|
break;
|
|
|
|
case GSREG_A0: case GSREG_A1: case GSREG_A2: case GSREG_A3:
|
|
case GSREG_A4: case GSREG_A5: case GSREG_A6: case GSREG_A7:
|
|
A[which - GSREG_A0] = value;
|
|
break;
|
|
|
|
case GSREG_PC:
|
|
PC = value;
|
|
break;
|
|
|
|
case GSREG_SR:
|
|
SetSR(value);
|
|
break;
|
|
|
|
case GSREG_SSP:
|
|
if(GetSVisor())
|
|
A[7] = value;
|
|
else
|
|
SP_Inactive = value;
|
|
break;
|
|
|
|
case GSREG_USP:
|
|
if(!GetSVisor())
|
|
A[7] = value;
|
|
else
|
|
SP_Inactive = value;
|
|
break;
|
|
}
|
|
}
|
|
|