mirror of
https://github.com/ksherlock/mpw.git
synced 2024-10-11 11:23:47 +00:00
3744 lines
85 KiB
C
3744 lines
85 KiB
C
/*=========================================================================*/
|
|
/* Fellow */
|
|
/* CPU 68k functions */
|
|
/* */
|
|
/* Author: Petter Schau */
|
|
/* */
|
|
/* */
|
|
/* Copyright (C) 1991, 1992, 1996 Free Software Foundation, Inc. */
|
|
/* */
|
|
/* 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, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
|
|
/*=========================================================================*/
|
|
|
|
#include "defs.h"
|
|
#include "CpuModule_Memory.h"
|
|
#include "CpuModule.h"
|
|
#include "CpuModule_Internal.h"
|
|
|
|
|
|
/*============================================================================*/
|
|
/* profiling help functions */
|
|
/*============================================================================*/
|
|
|
|
#if 0
|
|
#ifndef X64
|
|
static __inline void cpuTscBefore(int64_t* a)
|
|
{
|
|
int64_t local_a = *a;
|
|
__asm
|
|
{
|
|
push eax
|
|
push edx
|
|
push ecx
|
|
mov ecx,10h
|
|
rdtsc
|
|
pop ecx
|
|
mov dword ptr [local_a], eax
|
|
mov dword ptr [local_a + 4], edx
|
|
pop edx
|
|
pop eax
|
|
}
|
|
*a = local_a;
|
|
}
|
|
|
|
static __inline void cpuTscAfter(int64_t* a, int64_t* b, int32_t* c)
|
|
{
|
|
int64_t local_a = *a;
|
|
int64_t local_b = *b;
|
|
uint32_t local_c = *c;
|
|
|
|
__asm
|
|
{
|
|
push eax
|
|
push edx
|
|
push ecx
|
|
mov ecx, 10h
|
|
rdtsc
|
|
pop ecx
|
|
sub eax, dword ptr [local_a]
|
|
sbb edx, dword ptr [local_a + 4]
|
|
add dword ptr [local_b], eax
|
|
adc dword ptr [local_b + 4], edx
|
|
inc dword ptr [local_c]
|
|
pop edx
|
|
pop eax
|
|
}
|
|
*a = local_a;
|
|
*b = local_b;
|
|
*c = local_c;
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
/* Maintains the integrity of the super/user state */
|
|
|
|
void cpuUpdateSr(uint32_t new_sr)
|
|
{
|
|
BOOLE supermode_was_set = cpuGetFlagSupervisor();
|
|
BOOLE master_was_set = (cpuGetModelMajor() >= 2) && cpuGetFlagMaster();
|
|
|
|
BOOLE supermode_is_set = !!(new_sr & 0x2000);
|
|
BOOLE master_is_set = (cpuGetModelMajor() >= 2) && !!(new_sr & 0x1000);
|
|
|
|
uint32_t runlevel_old = (cpuGetSR() >> 8) & 7;
|
|
uint32_t runlevel_new = (new_sr >> 8) & 7;
|
|
|
|
if (!supermode_was_set)
|
|
{
|
|
cpuSetUspDirect(cpuGetAReg(7));
|
|
}
|
|
else if (master_was_set)
|
|
{
|
|
cpuSetMspDirect(cpuGetAReg(7));
|
|
}
|
|
else
|
|
{
|
|
cpuSetSspDirect(cpuGetAReg(7));
|
|
}
|
|
|
|
if (!supermode_is_set)
|
|
{
|
|
cpuSetAReg(7, cpuGetUspDirect());
|
|
}
|
|
else if (master_is_set)
|
|
{
|
|
cpuSetAReg(7, cpuGetMspDirect());
|
|
}
|
|
else
|
|
{
|
|
cpuSetAReg(7, cpuGetSspDirect());
|
|
}
|
|
|
|
if (cpuGetModelMajor() < 2)
|
|
{
|
|
new_sr &= 0xa71f;
|
|
}
|
|
else
|
|
{
|
|
new_sr &= 0xf71f;
|
|
}
|
|
|
|
cpuSetSR(new_sr);
|
|
|
|
if (runlevel_old != runlevel_new)
|
|
{
|
|
cpuCheckPendingInterrupts();
|
|
}
|
|
}
|
|
|
|
static void cpuIllegal(void)
|
|
{
|
|
uint16_t opcode = memoryReadWord(cpuGetPC() - 2);
|
|
if ((opcode & 0xf000) == 0xf000)
|
|
{
|
|
cpuThrowFLineException();
|
|
}
|
|
else if ((opcode & 0xf000) == 0xa000)
|
|
{
|
|
#if 0
|
|
if ((cpuGetPC() & 0xff0000) == 0xf00000)
|
|
{
|
|
call_calltrap(opcode & 0xfff);
|
|
cpuInitializeFromNewPC(cpuGetPC());
|
|
cpuSetInstructionTime(512);
|
|
}
|
|
else
|
|
{
|
|
cpuThrowALineException();
|
|
}
|
|
#endif
|
|
// MPW - always do a-line exception (tool traps).
|
|
cpuThrowALineException();
|
|
}
|
|
else
|
|
{
|
|
cpuThrowIllegalInstructionException(FALSE);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Illegal instruction handler.
|
|
/// </summary>
|
|
static void cpuIllegalInstruction(uint32_t *opcode_data)
|
|
{
|
|
cpuIllegal();
|
|
}
|
|
|
|
/// <summary>
|
|
/// BKPT
|
|
/// </summary>
|
|
static void cpuBkpt(uint32_t vector)
|
|
{
|
|
cpuThrowIllegalInstructionExceptionFromBreakpoint();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds bytes src1 to src2. Sets all flags.
|
|
/// </summary>
|
|
/// <returns>The result</returns>
|
|
static uint8_t cpuAddB(uint8_t src2, uint8_t src1)
|
|
{
|
|
uint8_t res = src2 + src1;
|
|
cpuSetFlagsAdd(cpuIsZeroB(res), cpuMsbB(res), cpuMsbB(src2), cpuMsbB(src1));
|
|
return res;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds words src1 to src2. Sets all flags.
|
|
/// </summary>
|
|
/// <returns>The result</returns>
|
|
static uint16_t cpuAddW(uint16_t src2, uint16_t src1)
|
|
{
|
|
uint16_t res = src2 + src1;
|
|
cpuSetFlagsAdd(cpuIsZeroW(res), cpuMsbW(res), cpuMsbW(src2), cpuMsbW(src1));
|
|
return res;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds dwords src1 to src2. Sets all flags.
|
|
/// </summary>
|
|
/// <returns>The result</returns>
|
|
static uint32_t cpuAddL(uint32_t src2, uint32_t src1)
|
|
{
|
|
uint32_t res = src2 + src1;
|
|
cpuSetFlagsAdd(cpuIsZeroL(res), cpuMsbL(res), cpuMsbL(src2), cpuMsbL(src1));
|
|
return res;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds src1 to src2 (For address registers). No flags.
|
|
/// </summary>
|
|
/// <returns>The result</returns>
|
|
static uint32_t cpuAddaW(uint32_t src2, uint32_t src1)
|
|
{
|
|
return src2 + src1;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds src1 to src2 (For address registers). No flags.
|
|
/// </summary>
|
|
/// <returns>The result</returns>
|
|
static uint32_t cpuAddaL(uint32_t src2, uint32_t src1)
|
|
{
|
|
return src2 + src1;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Subtracts src1 from src2. Sets all flags.
|
|
/// </summary>
|
|
/// <returns>The result</returns>
|
|
static uint8_t cpuSubB(uint8_t src2, uint8_t src1)
|
|
{
|
|
uint8_t res = src2 - src1;
|
|
cpuSetFlagsSub(cpuIsZeroB(res), cpuMsbB(res), cpuMsbB(src2), cpuMsbB(src1));
|
|
return res;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Subtracts src1 from src2. Sets all flags.
|
|
/// </summary>
|
|
/// <returns>The result</returns>
|
|
static uint16_t cpuSubW(uint16_t src2, uint16_t src1)
|
|
{
|
|
uint16_t res = src2 - src1;
|
|
cpuSetFlagsSub(cpuIsZeroW(res), cpuMsbW(res), cpuMsbW(src2), cpuMsbW(src1));
|
|
return res;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Subtracts src1 from src2. Sets all flags.
|
|
/// </summary>
|
|
/// <returns>The result</returns>
|
|
static uint32_t cpuSubL(uint32_t src2, uint32_t src1)
|
|
{
|
|
uint32_t res = src2 - src1;
|
|
cpuSetFlagsSub(cpuIsZeroL(res), cpuMsbL(res), cpuMsbL(src2), cpuMsbL(src1));
|
|
return res;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Subtracts src1 from src2 (For address registers). No flags.
|
|
/// </summary>
|
|
/// <returns>The result</returns>
|
|
static uint32_t cpuSubaW(uint32_t src2, uint32_t src1)
|
|
{
|
|
return src2 - src1;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Subtracts src1 from src2 (For address registers). No flags.
|
|
/// </summary>
|
|
/// <returns>The result</returns>
|
|
static uint32_t cpuSubaL(uint32_t src2, uint32_t src1)
|
|
{
|
|
return src2 - src1;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Subtracts src1 from src2. Sets all flags.
|
|
/// </summary>
|
|
static void cpuCmpB(uint8_t src2, uint8_t src1)
|
|
{
|
|
uint8_t res = src2 - src1;
|
|
cpuSetFlagsCmp(cpuIsZeroB(res), cpuMsbB(res), cpuMsbB(src2), cpuMsbB(src1));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Subtracts src1 from src2. Sets all flags.
|
|
/// </summary>
|
|
static void cpuCmpW(uint16_t src2, uint16_t src1)
|
|
{
|
|
uint16_t res = src2 - src1;
|
|
cpuSetFlagsCmp(cpuIsZeroW(res), cpuMsbW(res), cpuMsbW(src2), cpuMsbW(src1));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Subtracts src1 from src2. Sets all flags.
|
|
/// </summary>
|
|
static void cpuCmpL(uint32_t src2, uint32_t src1)
|
|
{
|
|
uint32_t res = src2 - src1;
|
|
cpuSetFlagsCmp(cpuIsZeroL(res), cpuMsbL(res), cpuMsbL(src2), cpuMsbL(src1));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Ands src1 to src2. Sets NZ00 flags.
|
|
/// </summary>
|
|
/// <returns>The result</returns>
|
|
static uint8_t cpuAndB(uint8_t src2, uint8_t src1)
|
|
{
|
|
uint8_t res = src2 & src1;
|
|
cpuSetFlagsNZ00NewB(res);
|
|
return res;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Ands src1 to src2. Sets NZ00 flags.
|
|
/// </summary>
|
|
/// <returns>The result</returns>
|
|
static uint16_t cpuAndW(uint16_t src2, uint16_t src1)
|
|
{
|
|
uint16_t res = src2 & src1;
|
|
cpuSetFlagsNZ00NewW(res);
|
|
return res;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Ands src1 to src2. Sets NZ00 flags.
|
|
/// </summary>
|
|
/// <returns>The result</returns>
|
|
static uint32_t cpuAndL(uint32_t src2, uint32_t src1)
|
|
{
|
|
uint32_t res = src2 & src1;
|
|
cpuSetFlagsNZ00NewL(res);
|
|
return res;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Eors src1 to src2. Sets NZ00 flags.
|
|
/// </summary>
|
|
/// <returns>The result</returns>
|
|
static uint8_t cpuEorB(uint8_t src2, uint8_t src1)
|
|
{
|
|
uint8_t res = src2 ^ src1;
|
|
cpuSetFlagsNZ00NewB(res);
|
|
return res;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Eors src1 to src2. Sets NZ00 flags.
|
|
/// </summary>
|
|
/// <returns>The result</returns>
|
|
static uint16_t cpuEorW(uint16_t src2, uint16_t src1)
|
|
{
|
|
uint16_t res = src2 ^ src1;
|
|
cpuSetFlagsNZ00NewW(res);
|
|
return res;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Eors src1 to src2. Sets NZ00 flags.
|
|
/// </summary>
|
|
/// <returns>The result</returns>
|
|
static uint32_t cpuEorL(uint32_t src2, uint32_t src1)
|
|
{
|
|
uint32_t res = src2 ^ src1;
|
|
cpuSetFlagsNZ00NewL(res);
|
|
return res;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Ors src1 to src2. Sets NZ00 flags.
|
|
/// </summary>
|
|
/// <returns>The result</returns>
|
|
static uint8_t cpuOrB(uint8_t src2, uint8_t src1)
|
|
{
|
|
uint8_t res = src2 | src1;
|
|
cpuSetFlagsNZ00NewB(res);
|
|
return res;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Ors src1 to src2. Sets NZ00 flags.
|
|
/// </summary>
|
|
/// <returns>The result</returns>
|
|
static uint16_t cpuOrW(uint16_t src2, uint16_t src1)
|
|
{
|
|
uint16_t res = src2 | src1;
|
|
cpuSetFlagsNZ00NewW(res);
|
|
return res;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Ors src1 to src2. Sets NZ00 flags.
|
|
/// </summary>
|
|
/// <returns>The result</returns>
|
|
static uint32_t cpuOrL(uint32_t src2, uint32_t src1)
|
|
{
|
|
uint32_t res = src2 | src1;
|
|
cpuSetFlagsNZ00NewL(res);
|
|
return res;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Changes bit in src. Sets Z flag.
|
|
/// </summary>
|
|
/// <returns>The result</returns>
|
|
static uint8_t cpuBchgB(uint8_t src, uint8_t bit)
|
|
{
|
|
uint8_t bit_mask = 1 << (bit & 7);
|
|
cpuSetZFlagBitOpsB(src & bit_mask);
|
|
return src ^ bit_mask;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Changes bit in src. Sets Z flag.
|
|
/// </summary>
|
|
/// <returns>The result</returns>
|
|
static uint32_t cpuBchgL(uint32_t src, uint32_t bit)
|
|
{
|
|
uint32_t bit_mask = 1 << (bit & 31);
|
|
cpuSetZFlagBitOpsL(src & bit_mask);
|
|
return src ^ bit_mask;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Clears bit in src. Sets Z flag.
|
|
/// </summary>
|
|
/// <returns>The result</returns>
|
|
static uint8_t cpuBclrB(uint8_t src, uint8_t bit)
|
|
{
|
|
uint8_t bit_mask = 1 << (bit & 7);
|
|
cpuSetZFlagBitOpsB(src & bit_mask);
|
|
return src & ~bit_mask;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Clears bit in src. Sets Z flag.
|
|
/// </summary>
|
|
/// <returns>The result</returns>
|
|
static uint32_t cpuBclrL(uint32_t src, uint32_t bit)
|
|
{
|
|
uint32_t bit_mask = 1 << (bit & 31);
|
|
cpuSetZFlagBitOpsL(src & bit_mask);
|
|
return src & ~bit_mask;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets bit in src. Sets Z flag.
|
|
/// </summary>
|
|
/// <returns>The result</returns>
|
|
static uint8_t cpuBsetB(uint8_t src, uint8_t bit)
|
|
{
|
|
uint8_t bit_mask = 1 << (bit & 7);
|
|
cpuSetZFlagBitOpsB(src & bit_mask);
|
|
return src | bit_mask;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets bit in src. Sets Z flag.
|
|
/// </summary>
|
|
/// <returns>The result</returns>
|
|
static uint32_t cpuBsetL(uint32_t src, uint32_t bit)
|
|
{
|
|
uint32_t bit_mask = 1 << (bit & 31);
|
|
cpuSetZFlagBitOpsL(src & bit_mask);
|
|
return src | bit_mask;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Tests bit in src. Sets Z flag.
|
|
/// </summary>
|
|
static void cpuBtstB(uint8_t src, uint8_t bit)
|
|
{
|
|
uint8_t bit_mask = 1 << (bit & 7);
|
|
cpuSetZFlagBitOpsB(src & bit_mask);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Tests bit in src. Sets Z flag.
|
|
/// </summary>
|
|
static void cpuBtstL(uint32_t src, uint32_t bit)
|
|
{
|
|
uint32_t bit_mask = 1 << (bit & 31);
|
|
cpuSetZFlagBitOpsL(src & bit_mask);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set flags for clr operation. X0100.
|
|
/// </summary>
|
|
static void cpuClr(void)
|
|
{
|
|
cpuSetFlags0100();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Neg src1. Sets sub flags.
|
|
/// </summary>
|
|
/// <returns>The result</returns>
|
|
static uint8_t cpuNegB(uint8_t src1)
|
|
{
|
|
uint8_t res = (uint8_t)-(int8_t)src1;
|
|
cpuSetFlagsNeg(cpuIsZeroB(res), cpuMsbB(res), cpuMsbB(src1));
|
|
return res;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Neg src1. Sets sub flags.
|
|
/// </summary>
|
|
/// <returns>The result</returns>
|
|
static uint16_t cpuNegW(uint16_t src1)
|
|
{
|
|
uint16_t res = (uint16_t)-(int16_t)src1;
|
|
cpuSetFlagsNeg(cpuIsZeroW(res), cpuMsbW(res), cpuMsbW(src1));
|
|
return res;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Neg src1. Sets sub flags.
|
|
/// </summary>
|
|
/// <returns>The result</returns>
|
|
static uint32_t cpuNegL(uint32_t src1)
|
|
{
|
|
uint32_t res = (uint32_t)-(int32_t)src1;
|
|
cpuSetFlagsNeg(cpuIsZeroL(res), cpuMsbL(res), cpuMsbL(src1));
|
|
return res;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Negx src1.
|
|
/// </summary>
|
|
/// <returns>The result</returns>
|
|
static uint8_t cpuNegxB(uint8_t src1)
|
|
{
|
|
int8_t x = (cpuGetFlagX()) ? 1 : 0;
|
|
uint8_t res = (uint8_t)-(int8_t)src1 - x;
|
|
cpuSetFlagsNegx(cpuIsZeroB(res), cpuMsbB(res), cpuMsbB(src1));
|
|
return res;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Negx src1.
|
|
/// </summary>
|
|
/// <returns>The result</returns>
|
|
static uint16_t cpuNegxW(uint16_t src1)
|
|
{
|
|
int16_t x = (cpuGetFlagX()) ? 1 : 0;
|
|
uint16_t res = (uint16_t)-(int16_t)src1 - x;
|
|
cpuSetFlagsNegx(cpuIsZeroW(res), cpuMsbW(res), cpuMsbW(src1));
|
|
return res;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Negx src1.
|
|
/// </summary>
|
|
/// <returns>The result</returns>
|
|
static uint32_t cpuNegxL(uint32_t src1)
|
|
{
|
|
int32_t x = (cpuGetFlagX()) ? 1 : 0;
|
|
uint32_t res = (uint32_t)-(int32_t)src1 - x;
|
|
cpuSetFlagsNegx(cpuIsZeroL(res), cpuMsbL(res), cpuMsbL(src1));
|
|
return res;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Not src1.
|
|
/// </summary>
|
|
/// <returns>The result</returns>
|
|
static uint8_t cpuNotB(uint8_t src1)
|
|
{
|
|
uint8_t res = ~src1;
|
|
cpuSetFlagsNZ00NewB(res);
|
|
return res;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Not src1.
|
|
/// </summary>
|
|
/// <returns>The result</returns>
|
|
static uint16_t cpuNotW(uint16_t src1)
|
|
{
|
|
uint16_t res = ~src1;
|
|
cpuSetFlagsNZ00NewW(res);
|
|
return res;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Not src.
|
|
/// </summary>
|
|
/// <returns>The result</returns>
|
|
static uint32_t cpuNotL(uint32_t src)
|
|
{
|
|
uint32_t res = ~src;
|
|
cpuSetFlagsNZ00NewL(res);
|
|
return res;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Tas src.
|
|
/// </summary>
|
|
/// <returns>The result</returns>
|
|
static uint8_t cpuTas(uint8_t src)
|
|
{
|
|
cpuSetFlagsNZ00NewB(src);
|
|
return src | 0x80;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Tst res.
|
|
/// </summary>
|
|
static void cpuTestB(uint8_t res)
|
|
{
|
|
cpuSetFlagsNZ00NewB(res);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Tst res.
|
|
/// </summary>
|
|
static void cpuTestW(uint16_t res)
|
|
{
|
|
cpuSetFlagsNZ00NewW(res);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Tst res.
|
|
/// </summary>
|
|
static void cpuTestL(uint32_t res)
|
|
{
|
|
cpuSetFlagsNZ00NewL(res);
|
|
}
|
|
|
|
/// <summary>
|
|
/// PEA ea.
|
|
/// </summary>
|
|
static void cpuPeaL(uint32_t ea)
|
|
{
|
|
cpuSetAReg(7, cpuGetAReg(7) - 4);
|
|
memoryWriteLong(ea, cpuGetAReg(7));
|
|
}
|
|
|
|
/// <summary>
|
|
/// JMP ea.
|
|
/// </summary>
|
|
static void cpuJmp(uint32_t ea)
|
|
{
|
|
cpuInitializeFromNewPC(ea);
|
|
}
|
|
|
|
/// <summary>
|
|
/// JSR ea.
|
|
/// </summary>
|
|
static void cpuJsr(uint32_t ea)
|
|
{
|
|
cpuSetAReg(7, cpuGetAReg(7) - 4);
|
|
memoryWriteLong(cpuGetPC(), cpuGetAReg(7));
|
|
cpuInitializeFromNewPC(ea);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Move res
|
|
/// </summary>
|
|
/// <returns>The result</returns>
|
|
static void cpuMoveB(uint8_t res)
|
|
{
|
|
cpuSetFlagsNZ00NewB(res);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Move res
|
|
/// </summary>
|
|
/// <returns>The result</returns>
|
|
static void cpuMoveW(uint16_t res)
|
|
{
|
|
cpuSetFlagsNZ00NewW(res);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Move res
|
|
/// </summary>
|
|
/// <returns>The result</returns>
|
|
static void cpuMoveL(uint32_t res)
|
|
{
|
|
cpuSetFlagsNZ00NewL(res);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bra byte offset.
|
|
/// </summary>
|
|
static void cpuBraB(uint32_t offset)
|
|
{
|
|
cpuInitializeFromNewPC(cpuGetPC() + offset);
|
|
cpuSetInstructionTime(10);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bra word offset.
|
|
/// </summary>
|
|
static void cpuBraW(void)
|
|
{
|
|
uint32_t tmp_pc = cpuGetPC();
|
|
cpuInitializeFromNewPC(tmp_pc + cpuGetNextWordSignExt());
|
|
cpuSetInstructionTime(10);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bra long offset.
|
|
/// </summary>
|
|
static void cpuBraL(void)
|
|
{
|
|
if (cpuGetModelMajor() < 2) cpuBraB(0xffffffff);
|
|
else
|
|
{
|
|
uint32_t tmp_pc = cpuGetPC();
|
|
cpuInitializeFromNewPC(tmp_pc + cpuGetNextLong());
|
|
cpuSetInstructionTime(4);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bsr byte offset.
|
|
/// </summary>
|
|
static void cpuBsrB(uint32_t offset)
|
|
{
|
|
cpuSetAReg(7, cpuGetAReg(7) - 4);
|
|
memoryWriteLong(cpuGetPC(), cpuGetAReg(7));
|
|
cpuInitializeFromNewPC(cpuGetPC() + offset);
|
|
cpuSetInstructionTime(18);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bsr word offset.
|
|
/// </summary>
|
|
static void cpuBsrW(void)
|
|
{
|
|
uint32_t tmp_pc = cpuGetPC();
|
|
uint32_t offset = cpuGetNextWordSignExt();
|
|
cpuSetAReg(7, cpuGetAReg(7) - 4);
|
|
memoryWriteLong(cpuGetPC(), cpuGetAReg(7));
|
|
cpuInitializeFromNewPC(tmp_pc + offset);
|
|
cpuSetInstructionTime(18);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bsr long offset. (020+)
|
|
/// </summary>
|
|
static void cpuBsrL(void)
|
|
{
|
|
if (cpuGetModelMajor() < 2) cpuBsrB(0xffffffff);
|
|
else
|
|
{
|
|
uint32_t tmp_pc = cpuGetPC();
|
|
uint32_t offset = cpuGetNextLong();
|
|
cpuSetAReg(7, cpuGetAReg(7) - 4);
|
|
memoryWriteLong(cpuGetPC(), cpuGetAReg(7));
|
|
cpuInitializeFromNewPC(tmp_pc + offset);
|
|
cpuSetInstructionTime(4);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bcc byte offset.
|
|
/// </summary>
|
|
static void cpuBccB(BOOLE cc, uint32_t offset)
|
|
{
|
|
if (cc)
|
|
{
|
|
cpuInitializeFromNewPC(cpuGetPC() + offset);
|
|
cpuSetInstructionTime(10);
|
|
}
|
|
else
|
|
{
|
|
cpuSetInstructionTime(8);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bcc word offset.
|
|
/// </summary>
|
|
static void cpuBccW(BOOLE cc)
|
|
{
|
|
if (cc)
|
|
{
|
|
uint32_t tmp_pc = cpuGetPC();
|
|
cpuInitializeFromNewPC(tmp_pc + cpuGetNextWordSignExt());
|
|
cpuSetInstructionTime(10);
|
|
}
|
|
else
|
|
{
|
|
cpuSkipNextWord();
|
|
cpuSetInstructionTime(12);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bcc long offset. (020+)
|
|
/// </summary>
|
|
static void cpuBccL(BOOLE cc)
|
|
{
|
|
if (cpuGetModelMajor() < 2) cpuBccB(cc, 0xffffffff);
|
|
else
|
|
{
|
|
if (cc)
|
|
{
|
|
uint32_t tmp_pc = cpuGetPC();
|
|
cpuInitializeFromNewPC(tmp_pc + cpuGetNextLong());
|
|
}
|
|
else
|
|
{
|
|
cpuSkipNextLong();
|
|
}
|
|
cpuSetInstructionTime(4);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// DBcc word offset.
|
|
/// </summary>
|
|
static void cpuDbcc(BOOLE cc, uint32_t reg)
|
|
{
|
|
if (!cc)
|
|
{
|
|
int16_t val = (int16_t)cpuGetDRegWord(reg);
|
|
val--;
|
|
cpuSetDRegWord(reg, val);
|
|
if (val != -1)
|
|
{
|
|
uint32_t tmp_pc = cpuGetPC();
|
|
cpuInitializeFromNewPC(tmp_pc + cpuGetNextWordSignExt());
|
|
cpuSetInstructionTime(10);
|
|
}
|
|
else
|
|
{
|
|
cpuSkipNextWord();
|
|
cpuSetInstructionTime(14);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
cpuSkipNextWord();
|
|
cpuSetInstructionTime(12);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// And #imm, ccr
|
|
/// </summary>
|
|
static void cpuAndCcrB(void)
|
|
{
|
|
uint16_t imm = cpuGetNextWord();
|
|
cpuSetSR(cpuGetSR() & (0xffe0 | (imm & 0x1f)));
|
|
cpuSetInstructionTime(20);
|
|
}
|
|
|
|
/// <summary>
|
|
/// And #imm, sr
|
|
/// </summary>
|
|
static void cpuAndSrW(void)
|
|
{
|
|
if (cpuGetFlagSupervisor())
|
|
{
|
|
uint16_t imm = cpuGetNextWord();
|
|
cpuUpdateSr(cpuGetSR() & imm);
|
|
cpuSetInstructionTime(20);
|
|
}
|
|
else
|
|
{
|
|
cpuThrowPrivilegeViolationException();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Or #imm, ccr
|
|
/// </summary>
|
|
static void cpuOrCcrB(void)
|
|
{
|
|
uint16_t imm = cpuGetNextWord();
|
|
cpuSetSR(cpuGetSR() | (imm & 0x1f));
|
|
cpuSetInstructionTime(20);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Or #imm, sr
|
|
/// </summary>
|
|
static void cpuOrSrW(void)
|
|
{
|
|
if (cpuGetFlagSupervisor())
|
|
{
|
|
uint16_t imm = cpuGetNextWord();
|
|
cpuUpdateSr(cpuGetSR() | imm);
|
|
cpuSetInstructionTime(20);
|
|
}
|
|
else
|
|
{
|
|
cpuThrowPrivilegeViolationException();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Eor #imm, ccr
|
|
/// </summary>
|
|
static void cpuEorCcrB(void)
|
|
{
|
|
uint16_t imm = cpuGetNextWord();
|
|
cpuSetSR(cpuGetSR() ^ (imm & 0x1f));
|
|
cpuSetInstructionTime(20);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Eor #imm, sr
|
|
/// </summary>
|
|
static void cpuEorSrW(void)
|
|
{
|
|
if (cpuGetFlagSupervisor())
|
|
{
|
|
uint16_t imm = cpuGetNextWord();
|
|
cpuUpdateSr(cpuGetSR() ^ imm);
|
|
cpuSetInstructionTime(20);
|
|
}
|
|
else
|
|
{
|
|
cpuThrowPrivilegeViolationException();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Move ea, ccr
|
|
/// </summary>
|
|
static void cpuMoveToCcr(uint16_t src)
|
|
{
|
|
cpuSetSR((cpuGetSR() & 0xff00) | (src & 0x1f));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Move <ea>, sr
|
|
/// </summary>
|
|
static void cpuMoveToSr(uint16_t src)
|
|
{
|
|
// Supervisor privilege for move to sr is checked in the instruction wrappers
|
|
cpuUpdateSr(src);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Move ccr, ea
|
|
/// </summary>
|
|
static uint16_t cpuMoveFromCcr(void)
|
|
{
|
|
return cpuGetSR() & 0x1f;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Move <ea>, sr
|
|
/// </summary>
|
|
static uint16_t cpuMoveFromSr(void)
|
|
{
|
|
// Supervisor privilege for move from sr is checked in the instruction wrappers, privileged on 010+
|
|
return (uint16_t) cpuGetSR();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Scc byte.
|
|
/// </summary>
|
|
static uint8_t cpuScc(uint32_t cc)
|
|
{
|
|
return (cpuCalculateConditionCode(cc)) ? 0xff : 0;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Rts
|
|
/// </summary>
|
|
static void cpuRts(void)
|
|
{
|
|
cpuInitializeFromNewPC(memoryReadLong(cpuGetAReg(7)));
|
|
cpuSetAReg(7, cpuGetAReg(7) + 4);
|
|
cpuSetInstructionTime(16);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Rtr
|
|
/// </summary>
|
|
static void cpuRtr(void)
|
|
{
|
|
cpuSetSR((cpuGetSR() & 0xffe0) | (memoryReadWord(cpuGetAReg(7)) & 0x1f));
|
|
cpuSetAReg(7, cpuGetAReg(7) + 2);
|
|
cpuInitializeFromNewPC(memoryReadLong(cpuGetAReg(7)));
|
|
cpuSetAReg(7, cpuGetAReg(7) + 4);
|
|
cpuSetInstructionTime(20);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Nop
|
|
/// </summary>
|
|
static void cpuNop(void)
|
|
{
|
|
cpuSetInstructionTime(4);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Trapv
|
|
/// </summary>
|
|
static void cpuTrapv(void)
|
|
{
|
|
if (cpuGetFlagV())
|
|
{
|
|
cpuThrowTrapVException();
|
|
return;
|
|
}
|
|
cpuSetInstructionTime(4);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Muls/u.l
|
|
/// </summary>
|
|
static void cpuMulL(uint32_t src1, uint16_t extension)
|
|
{
|
|
uint32_t dl = (extension >> 12) & 7;
|
|
if (extension & 0x0800) // muls.l
|
|
{
|
|
int64_t result = ((int64_t)(int32_t) src1) * ((int64_t)(int32_t)cpuGetDReg(dl));
|
|
if (extension & 0x0400) // 32bx32b=64b
|
|
{
|
|
uint32_t dh = extension & 7;
|
|
cpuSetDReg(dl, (uint32_t)result);
|
|
cpuSetDReg(dh, (uint32_t)(result >> 32));
|
|
cpuSetFlagsNZ00New64(result);
|
|
}
|
|
else // 32bx32b=32b
|
|
{
|
|
BOOLE o;
|
|
if (result >= 0)
|
|
o = (result & 0xffffffff80000000) != 0;
|
|
else
|
|
o = (result & 0xffffffff80000000) != 0xffffffff80000000;
|
|
cpuSetDReg(dl, (uint32_t)result);
|
|
cpuSetFlagsNZVC(((uint32_t)result) == 0, !!(result & 0x80000000), o, FALSE);
|
|
}
|
|
}
|
|
else // mulu.l
|
|
{
|
|
uint64_t result = ((uint64_t) src1) * ((uint64_t) cpuGetDReg(dl));
|
|
if (extension & 0x0400) // 32bx32b=64b
|
|
{
|
|
uint32_t dh = extension & 7;
|
|
cpuSetDReg(dl, (uint32_t)result);
|
|
cpuSetDReg(dh, (uint32_t)(result >> 32));
|
|
cpuSetFlagsNZ00New64(result);
|
|
}
|
|
else // 32bx32b=32b
|
|
{
|
|
cpuSetDReg(dl, (uint32_t)result);
|
|
cpuSetFlagsNZVC((uint32_t)result == 0, !!(result & 0x80000000), (result >> 32) != 0, FALSE);
|
|
}
|
|
}
|
|
cpuSetInstructionTime(4);
|
|
}
|
|
|
|
uint8_t cpuMuluTime[256];
|
|
uint8_t cpuMulsTime[512];
|
|
|
|
void cpuCreateMuluTimeTable(void)
|
|
{
|
|
for (uint32_t i = 0; i < 256; i++)
|
|
{
|
|
uint32_t j = 0;
|
|
for (uint32_t k = 0; k < 8; k++)
|
|
if (((i>>k) & 1) == 1)
|
|
j++;
|
|
cpuMuluTime[i] = (uint8_t) j*2;
|
|
}
|
|
}
|
|
|
|
void cpuCreateMulsTimeTable(void)
|
|
{
|
|
for (uint32_t i = 0; i < 512; i++)
|
|
{
|
|
uint32_t j = 0;
|
|
for (uint32_t k = 0; k < 9; k++)
|
|
if ((((i>>k) & 3) == 1) || (((i>>k) & 3) == 2))
|
|
j++;
|
|
cpuMulsTime[i] = (uint8_t) j*2;
|
|
}
|
|
}
|
|
|
|
void cpuCreateMulTimeTables(void)
|
|
{
|
|
cpuCreateMuluTimeTable();
|
|
cpuCreateMulsTimeTable();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Muls.w
|
|
/// </summary>
|
|
static uint32_t cpuMulsW(uint16_t src2, uint16_t src1, uint32_t eatime)
|
|
{
|
|
uint32_t res = (uint32_t)(((int32_t)(int16_t)src2)*((int32_t)(int16_t)src1));
|
|
cpuSetFlagsNZ00NewL(res);
|
|
cpuSetInstructionTime(38 + eatime + cpuMulsTime[(src1 << 1) & 0x1ff] + cpuMulsTime[src1 >> 7]);
|
|
return res;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Mulu.w
|
|
/// </summary>
|
|
static uint32_t cpuMuluW(uint16_t src2, uint16_t src1, uint32_t eatime)
|
|
{
|
|
uint32_t res = ((uint32_t)src2)*((uint32_t)src1);
|
|
cpuSetFlagsNZ00NewL(res);
|
|
cpuSetInstructionTime(38 + eatime + cpuMuluTime[src1 & 0xff] + cpuMuluTime[src1 >> 8]);
|
|
return res;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Divsw, src1 / src2
|
|
/// </summary>
|
|
static void cpuDivsW(uint32_t dst, uint16_t src1, uint32_t destination_reg, uint32_t instruction_time)
|
|
{
|
|
if (src1 == 0)
|
|
{
|
|
// Alcatraz odyssey assumes that PC in this exception points after the instruction.
|
|
cpuSetFlagsNZVC(TRUE, FALSE, FALSE, FALSE);
|
|
cpuThrowDivisionByZeroException();
|
|
}
|
|
else
|
|
{
|
|
uint32_t result;
|
|
int32_t x = (int32_t) dst;
|
|
int32_t y = (int32_t)(int16_t) src1;
|
|
int32_t res = x / y;
|
|
int32_t rem = x % y;
|
|
if (res > 32767 || res < -32768)
|
|
{
|
|
result = dst;
|
|
cpuSetFlagsNZVC(FALSE, TRUE, TRUE, FALSE);
|
|
}
|
|
else
|
|
{
|
|
result = (rem << 16) | (res & 0xffff);
|
|
cpuSetFlagsNZVC(cpuIsZeroW((uint16_t) res), cpuMsbW((uint16_t) res), FALSE, FALSE);
|
|
}
|
|
cpuSetDReg(destination_reg, result);
|
|
cpuSetInstructionTime(instruction_time);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Divuw, src1 / src2
|
|
/// </summary>
|
|
static void cpuDivuW(uint32_t dst, uint16_t src1, uint32_t destination_reg, uint32_t instruction_time)
|
|
{
|
|
if (src1 == 0)
|
|
{
|
|
// Alcatraz odyssey assumes that PC in this exception points after the instruction.
|
|
cpuSetFlagsNZVC((dst & 0xffff0000) == 0, (dst & 0x80000000) == 0x80000000, FALSE, FALSE);
|
|
cpuThrowDivisionByZeroException();
|
|
}
|
|
else
|
|
{
|
|
uint32_t result;
|
|
uint32_t x = dst;
|
|
uint32_t y = (uint32_t) src1;
|
|
uint32_t res = x / y;
|
|
uint32_t rem = x % y;
|
|
if (res > 65535)
|
|
{
|
|
result = dst;
|
|
cpuSetFlagsNZVC(FALSE, TRUE, TRUE, FALSE);
|
|
}
|
|
else
|
|
{
|
|
result = (rem << 16) | (res & 0xffff);
|
|
cpuSetFlagsNZVC(cpuIsZeroW((uint16_t) res), cpuMsbW((uint16_t) res), FALSE, FALSE);
|
|
}
|
|
cpuSetDReg(destination_reg, result);
|
|
cpuSetInstructionTime(instruction_time);
|
|
}
|
|
}
|
|
|
|
static void cpuDivL(uint32_t divisor, uint32_t ext, uint32_t instruction_time)
|
|
{
|
|
if (divisor != 0)
|
|
{
|
|
uint32_t dq_reg = (ext>>12) & 7; /* Get operand registers, size and sign */
|
|
uint32_t dr_reg = ext & 7;
|
|
BOOLE size64 = (ext>>10) & 1;
|
|
BOOLE sign = (ext>>11) & 1;
|
|
BOOLE resultsigned = FALSE, restsigned = FALSE;
|
|
uint64_t x, y;
|
|
int64_t x_signed;
|
|
|
|
if (sign)
|
|
{
|
|
if (size64) x_signed = (int64_t) ((uint64_t) cpuGetDReg(dq_reg)) | (((int64_t) cpuGetDReg(dr_reg))<<32);
|
|
else x_signed = (int64_t) (int32_t) cpuGetDReg(dq_reg);
|
|
int64_t y_signed = (int64_t)(int32_t)divisor;
|
|
|
|
if (y_signed < 0)
|
|
{
|
|
y = (uint64_t) -y_signed;
|
|
resultsigned = !resultsigned;
|
|
}
|
|
else y = y_signed;
|
|
if (x_signed < 0)
|
|
{
|
|
x = (uint64_t) -x_signed;
|
|
resultsigned = !resultsigned;
|
|
restsigned = TRUE;
|
|
}
|
|
else x = (uint64_t) x_signed;
|
|
}
|
|
else
|
|
{
|
|
if (size64) x = ((uint64_t) cpuGetDReg(dq_reg)) | (((uint64_t) cpuGetDReg(dr_reg))<<32);
|
|
else x = (uint64_t) cpuGetDReg(dq_reg);
|
|
y = (uint64_t) divisor;
|
|
}
|
|
|
|
uint64_t result = x / y;
|
|
uint64_t rest = x % y;
|
|
|
|
if (sign)
|
|
{
|
|
if ((resultsigned && result > 0x80000000) || (!resultsigned && result > 0x7fffffff))
|
|
{
|
|
/* Overflow */
|
|
cpuSetFlagsVC(TRUE, FALSE);
|
|
}
|
|
else
|
|
{
|
|
int64_t result_signed = (resultsigned) ? (-(int64_t)result) : ((int64_t)result);
|
|
int64_t rest_signed = (restsigned) ? (-(int64_t)rest) : ((int64_t)rest);
|
|
cpuSetDReg(dr_reg, (uint32_t) rest_signed);
|
|
cpuSetDReg(dq_reg, (uint32_t) result_signed);
|
|
cpuSetFlagsNZ00NewL((uint32_t) result_signed);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (result > 0xffffffff)
|
|
{
|
|
/* Overflow */
|
|
cpuSetFlagsVC(TRUE, FALSE);
|
|
}
|
|
else
|
|
{
|
|
cpuSetDReg(dr_reg, (uint32_t) rest);
|
|
cpuSetDReg(dq_reg, (uint32_t) result);
|
|
cpuSetFlagsNZ00NewL((uint32_t) result);
|
|
}
|
|
}
|
|
cpuSetInstructionTime(instruction_time);
|
|
}
|
|
else
|
|
{
|
|
cpuThrowDivisionByZeroException();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Lslb
|
|
/// </summary>
|
|
static uint8_t cpuLslB(uint8_t dst, uint32_t sh, uint32_t cycles)
|
|
{
|
|
uint8_t res;
|
|
sh &= 0x3f;
|
|
if (sh == 0)
|
|
{
|
|
cpuSetFlagsShiftZero(cpuIsZeroB(dst), cpuMsbB(dst));
|
|
res = dst;
|
|
}
|
|
else if (sh >= 8)
|
|
{
|
|
res = 0;
|
|
cpuSetFlagsShift(TRUE, FALSE, (sh == 8) ? (dst & 1) : FALSE, FALSE);
|
|
}
|
|
else
|
|
{
|
|
res = dst << sh;
|
|
cpuSetFlagsShift(cpuIsZeroB(res), cpuMsbB(res), dst & (0x80>>(sh-1)), FALSE);
|
|
}
|
|
cpuSetInstructionTime(cycles + sh*2);
|
|
return res;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Lslw
|
|
/// </summary>
|
|
static uint16_t cpuLslW(uint16_t dst, uint32_t sh, uint32_t cycles)
|
|
{
|
|
uint16_t res;
|
|
sh &= 0x3f;
|
|
if (sh == 0)
|
|
{
|
|
cpuSetFlagsShiftZero(cpuIsZeroW(dst), cpuMsbW(dst));
|
|
res = dst;
|
|
}
|
|
else if (sh >= 16)
|
|
{
|
|
res = 0;
|
|
cpuSetFlagsShift(TRUE, FALSE, (sh == 16) ? (dst & 1) : FALSE, FALSE);
|
|
}
|
|
else
|
|
{
|
|
res = dst << sh;
|
|
cpuSetFlagsShift(cpuIsZeroW(res), cpuMsbW(res), dst & (0x8000>>(sh-1)), FALSE);
|
|
}
|
|
cpuSetInstructionTime(cycles + sh*2);
|
|
return res;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Lsll
|
|
/// </summary>
|
|
static uint32_t cpuLslL(uint32_t dst, uint32_t sh, uint32_t cycles)
|
|
{
|
|
uint32_t res;
|
|
sh &= 0x3f;
|
|
if (sh == 0)
|
|
{
|
|
cpuSetFlagsShiftZero(cpuIsZeroL(dst), cpuMsbL(dst));
|
|
res = dst;
|
|
}
|
|
else if (sh >= 32)
|
|
{
|
|
res = 0;
|
|
cpuSetFlagsShift(TRUE, FALSE, (sh == 32) ? (dst & 1) : FALSE, FALSE);
|
|
}
|
|
else
|
|
{
|
|
res = dst << sh;
|
|
cpuSetFlagsShift(cpuIsZeroL(res), cpuMsbL(res), dst & (0x80000000>>(sh-1)), FALSE);
|
|
}
|
|
cpuSetInstructionTime(cycles + sh*2);
|
|
return res;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Lsrb
|
|
/// </summary>
|
|
static uint8_t cpuLsrB(uint8_t dst, uint32_t sh, uint32_t cycles)
|
|
{
|
|
uint8_t res;
|
|
sh &= 0x3f;
|
|
if (sh == 0)
|
|
{
|
|
cpuSetFlagsShiftZero(cpuIsZeroB(dst), cpuMsbB(dst));
|
|
res = dst;
|
|
}
|
|
else if (sh >= 8)
|
|
{
|
|
res = 0;
|
|
cpuSetFlagsShift(TRUE, FALSE, (sh == 8) ? cpuMsbB(dst) : FALSE, FALSE);
|
|
}
|
|
else
|
|
{
|
|
res = dst >> sh;
|
|
cpuSetFlagsShift(cpuIsZeroB(res), FALSE, dst & (1<<(sh-1)), FALSE);
|
|
}
|
|
cpuSetInstructionTime(cycles + sh*2);
|
|
return res;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Lsrw
|
|
/// </summary>
|
|
static uint16_t cpuLsrW(uint16_t dst, uint32_t sh, uint32_t cycles)
|
|
{
|
|
uint16_t res;
|
|
sh &= 0x3f;
|
|
if (sh == 0)
|
|
{
|
|
cpuSetFlagsShiftZero(cpuIsZeroW(dst), cpuMsbW(dst));
|
|
res = dst;
|
|
}
|
|
else if (sh >= 16)
|
|
{
|
|
res = 0;
|
|
cpuSetFlagsShift(TRUE, FALSE, (sh == 16) ? cpuMsbW(dst) : FALSE, FALSE);
|
|
}
|
|
else
|
|
{
|
|
res = dst >> sh;
|
|
cpuSetFlagsShift(cpuIsZeroW(res), FALSE, dst & (1<<(sh-1)), FALSE);
|
|
}
|
|
cpuSetInstructionTime(cycles + sh*2);
|
|
return res;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Lsrl
|
|
/// </summary>
|
|
static uint32_t cpuLsrL(uint32_t dst, uint32_t sh, uint32_t cycles)
|
|
{
|
|
uint32_t res;
|
|
sh &= 0x3f;
|
|
if (sh == 0)
|
|
{
|
|
cpuSetFlagsShiftZero(cpuIsZeroL(dst), cpuMsbL(dst));
|
|
res = dst;
|
|
}
|
|
else if (sh >= 32)
|
|
{
|
|
res = 0;
|
|
cpuSetFlagsShift(TRUE, FALSE, (sh == 32) ? cpuMsbL(dst) : FALSE, FALSE);
|
|
}
|
|
else
|
|
{
|
|
res = dst >> sh;
|
|
cpuSetFlagsShift(cpuIsZeroL(res), FALSE, dst & (1<<(sh-1)), FALSE);
|
|
}
|
|
cpuSetInstructionTime(cycles + sh*2);
|
|
return res;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Aslb
|
|
/// </summary>
|
|
static uint8_t cpuAslB(uint8_t dst, uint32_t sh, uint32_t cycles)
|
|
{
|
|
int8_t res;
|
|
sh &= 0x3f;
|
|
if (sh == 0)
|
|
{
|
|
cpuSetFlagsShiftZero(cpuIsZeroB(dst), cpuMsbB(dst));
|
|
res = (int8_t) dst;
|
|
}
|
|
else if (sh >= 8)
|
|
{
|
|
res = 0;
|
|
cpuSetFlagsShift(TRUE, FALSE, (sh == 8) ? (dst & 1) : FALSE, dst != 0);
|
|
}
|
|
else
|
|
{
|
|
uint8_t mask = 0xff << (7-sh);
|
|
uint8_t out = dst & mask;
|
|
res = ((int8_t)dst) << sh;
|
|
|
|
// Overflow calculation:
|
|
// 1. The msb of the result and original are different
|
|
// 2. Or the bits shifted out were not all the same as the msb of the original
|
|
BOOLE n_result = cpuMsbB(res);
|
|
BOOLE n_original = cpuMsbB(dst);
|
|
BOOLE msb_changed = (n_result != n_original) || ((n_original) ? (out != mask) : (out != 0));
|
|
|
|
cpuSetFlagsShift(cpuIsZeroB(res), n_result, dst & (0x80>>(sh-1)), msb_changed);
|
|
}
|
|
cpuSetInstructionTime(cycles + sh*2);
|
|
return (uint8_t) res;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Aslw
|
|
/// </summary>
|
|
static uint16_t cpuAslW(uint16_t dst, uint32_t sh, uint32_t cycles)
|
|
{
|
|
int16_t res;
|
|
sh &= 0x3f;
|
|
if (sh == 0)
|
|
{
|
|
cpuSetFlagsShiftZero(cpuIsZeroW(dst), cpuMsbW(dst));
|
|
res = (int16_t) dst;
|
|
}
|
|
else if (sh >= 16)
|
|
{
|
|
res = 0;
|
|
cpuSetFlagsShift(TRUE, FALSE, (sh == 16) ? (dst & 1) : FALSE, dst != 0);
|
|
}
|
|
else
|
|
{
|
|
uint16_t mask = 0xffff << (15-sh);
|
|
uint16_t out = dst & mask;
|
|
res = ((int16_t)dst) << sh;
|
|
|
|
// Overflow calculation:
|
|
// 1. The msb of the result and original are different
|
|
// 2. Or the bits shifted out were not all the same as the msb of the original
|
|
BOOLE n_result = cpuMsbW(res);
|
|
BOOLE n_original = cpuMsbW(dst);
|
|
BOOLE msb_changed = (n_result != n_original) || ((n_original) ? (out != mask) : (out != 0));
|
|
|
|
cpuSetFlagsShift(cpuIsZeroW(res), n_result, dst & (0x8000>>(sh-1)), msb_changed);
|
|
}
|
|
cpuSetInstructionTime(cycles + sh*2);
|
|
return (uint16_t) res;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Asll
|
|
/// </summary>
|
|
static uint32_t cpuAslL(uint32_t dst, uint32_t sh, uint32_t cycles)
|
|
{
|
|
int32_t res;
|
|
sh &= 0x3f;
|
|
if (sh == 0)
|
|
{
|
|
cpuSetFlagsShiftZero(cpuIsZeroL(dst), cpuMsbL(dst));
|
|
res = (int32_t) dst;
|
|
}
|
|
else if (sh >= 32)
|
|
{
|
|
res = 0;
|
|
cpuSetFlagsShift(TRUE, FALSE, (sh == 32) ? (dst & 1) : FALSE, dst != 0);
|
|
}
|
|
else
|
|
{
|
|
uint32_t mask = 0xffffffff << (31-sh);
|
|
uint32_t out = dst & mask;
|
|
res = ((int32_t)dst) << sh;
|
|
|
|
// Overflow calculation:
|
|
// 1. The msb of the result and original are different
|
|
// 2. Or the bits shifted out were not all the same as the msb of the original
|
|
BOOLE n_result = cpuMsbL(res);
|
|
BOOLE n_original = cpuMsbL(dst);
|
|
BOOLE msb_changed = (n_result != n_original) || ((n_original) ? (out != mask) : (out != 0));
|
|
|
|
cpuSetFlagsShift(cpuIsZeroL(res), n_result, dst & (0x80000000>>(sh-1)), msb_changed);
|
|
}
|
|
cpuSetInstructionTime(cycles + sh*2);
|
|
return (uint32_t) res;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Asrb
|
|
/// </summary>
|
|
static uint8_t cpuAsrB(uint8_t dst, uint32_t sh, uint32_t cycles)
|
|
{
|
|
int8_t res;
|
|
sh &= 0x3f;
|
|
if (sh == 0)
|
|
{
|
|
cpuSetFlagsShiftZero(cpuIsZeroB(dst), cpuMsbB(dst));
|
|
res = (int8_t) dst;
|
|
}
|
|
else if (sh >= 8)
|
|
{
|
|
res = (cpuMsbB(dst)) ? 0xff : 0;
|
|
cpuSetFlagsShift(cpuIsZeroB(res), cpuMsbB(res), cpuMsbB(res), FALSE);
|
|
}
|
|
else
|
|
{
|
|
res = ((int8_t)dst) >> sh;
|
|
cpuSetFlagsShift(cpuIsZeroB(res), cpuMsbB(res), dst & (1<<(sh-1)), FALSE);
|
|
}
|
|
cpuSetInstructionTime(cycles + sh*2);
|
|
return (uint8_t) res;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Asrw
|
|
/// </summary>
|
|
static uint16_t cpuAsrW(uint16_t dst, uint32_t sh, uint32_t cycles)
|
|
{
|
|
int16_t res;
|
|
sh &= 0x3f;
|
|
if (sh == 0)
|
|
{
|
|
cpuSetFlagsShiftZero(cpuIsZeroW(dst), cpuMsbW(dst));
|
|
res = (int16_t) dst;
|
|
}
|
|
else if (sh >= 16)
|
|
{
|
|
res = (cpuMsbW(dst)) ? 0xffff : 0;
|
|
cpuSetFlagsShift(cpuIsZeroW(res), cpuMsbW(res), cpuMsbW(res), FALSE);
|
|
}
|
|
else
|
|
{
|
|
res = ((int16_t)dst) >> sh;
|
|
cpuSetFlagsShift(cpuIsZeroW(res), cpuMsbW(res), dst & (1<<(sh-1)), FALSE);
|
|
}
|
|
cpuSetInstructionTime(cycles + sh*2);
|
|
return (uint16_t) res;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Asrl
|
|
/// </summary>
|
|
static uint32_t cpuAsrL(uint32_t dst, uint32_t sh, uint32_t cycles)
|
|
{
|
|
int32_t res;
|
|
sh &= 0x3f;
|
|
|
|
if (sh == 0)
|
|
{
|
|
cpuSetFlagsShiftZero(cpuIsZeroL(dst), cpuMsbL(dst));
|
|
res = (int32_t) dst;
|
|
}
|
|
else if (sh >= 32)
|
|
{
|
|
res = (cpuMsbL(dst)) ? 0xffffffff : 0;
|
|
cpuSetFlagsShift(cpuIsZeroL(res), cpuMsbL(res), cpuMsbL(res), FALSE);
|
|
}
|
|
else
|
|
{
|
|
res = ((int32_t)dst) >> sh;
|
|
cpuSetFlagsShift(cpuIsZeroL(res), cpuMsbL(res), dst & (1<<(sh-1)), FALSE);
|
|
}
|
|
cpuSetInstructionTime(cycles + sh*2);
|
|
return (uint32_t) res;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Rolb
|
|
/// </summary>
|
|
static uint8_t cpuRolB(uint8_t dst, uint32_t sh, uint32_t cycles)
|
|
{
|
|
uint8_t res;
|
|
sh &= 0x3f;
|
|
cycles += sh*2;
|
|
if (sh == 0)
|
|
{
|
|
cpuSetFlagsShiftZero(cpuIsZeroB(dst), cpuMsbB(dst));
|
|
res = dst;
|
|
}
|
|
else
|
|
{
|
|
sh &= 7;
|
|
res = (dst << sh) | (dst >> (8-sh));
|
|
cpuSetFlagsRotate(cpuIsZeroB(res), cpuMsbB(res), res & 1);
|
|
}
|
|
cpuSetInstructionTime(cycles);
|
|
return res;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Rolw
|
|
/// </summary>
|
|
static uint16_t cpuRolW(uint16_t dst, uint32_t sh, uint32_t cycles)
|
|
{
|
|
uint16_t res;
|
|
sh &= 0x3f;
|
|
cycles += sh*2;
|
|
if (sh == 0)
|
|
{
|
|
cpuSetFlagsShiftZero(cpuIsZeroW(dst), cpuMsbW(dst));
|
|
res = dst;
|
|
}
|
|
else
|
|
{
|
|
sh &= 15;
|
|
res = (dst << sh) | (dst >> (16-sh));
|
|
cpuSetFlagsRotate(cpuIsZeroW(res), cpuMsbW(res), res & 1);
|
|
}
|
|
cpuSetInstructionTime(cycles);
|
|
return res;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Roll
|
|
/// </summary>
|
|
static uint32_t cpuRolL(uint32_t dst, uint32_t sh, uint32_t cycles)
|
|
{
|
|
uint32_t res;
|
|
sh &= 0x3f;
|
|
cycles += sh*2;
|
|
if (sh == 0)
|
|
{
|
|
cpuSetFlagsShiftZero(cpuIsZeroL(dst), cpuMsbL(dst));
|
|
res = dst;
|
|
}
|
|
else
|
|
{
|
|
sh &= 31;
|
|
res = (dst << sh) | (dst >> (32-sh));
|
|
cpuSetFlagsRotate(cpuIsZeroL(res), cpuMsbL(res), res & 1);
|
|
}
|
|
cpuSetInstructionTime(cycles);
|
|
return res;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Rorb
|
|
/// </summary>
|
|
static uint8_t cpuRorB(uint8_t dst, uint32_t sh, uint32_t cycles)
|
|
{
|
|
uint8_t res;
|
|
sh &= 0x3f;
|
|
cycles += sh*2;
|
|
if (sh == 0)
|
|
{
|
|
cpuSetFlagsShiftZero(cpuIsZeroB(dst), cpuMsbB(dst));
|
|
res = dst;
|
|
}
|
|
else
|
|
{
|
|
sh &= 7;
|
|
res = (dst >> sh) | (dst << (8-sh));
|
|
cpuSetFlagsRotate(cpuIsZeroB(res), cpuMsbB(res), cpuMsbB(res));
|
|
}
|
|
cpuSetInstructionTime(cycles);
|
|
return res;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Rorw
|
|
/// </summary>
|
|
static uint16_t cpuRorW(uint16_t dst, uint32_t sh, uint32_t cycles)
|
|
{
|
|
uint16_t res;
|
|
sh &= 0x3f;
|
|
cycles += sh*2;
|
|
if (sh == 0)
|
|
{
|
|
cpuSetFlagsShiftZero(cpuIsZeroW(dst), cpuMsbW(dst));
|
|
res = dst;
|
|
}
|
|
else
|
|
{
|
|
sh &= 15;
|
|
res = (dst >> sh) | (dst << (16-sh));
|
|
cpuSetFlagsRotate(cpuIsZeroW(res), cpuMsbW(res), cpuMsbW(res));
|
|
}
|
|
cpuSetInstructionTime(cycles);
|
|
return res;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Rorl
|
|
/// </summary>
|
|
static uint32_t cpuRorL(uint32_t dst, uint32_t sh, uint32_t cycles)
|
|
{
|
|
uint32_t res;
|
|
sh &= 0x3f;
|
|
cycles += sh*2;
|
|
if (sh == 0)
|
|
{
|
|
cpuSetFlagsShiftZero(cpuIsZeroL(dst), cpuMsbL(dst));
|
|
res = dst;
|
|
}
|
|
else
|
|
{
|
|
sh &= 31;
|
|
res = (dst >> sh) | (dst << (32-sh));
|
|
cpuSetFlagsRotate(cpuIsZeroL(res), cpuMsbL(res), cpuMsbL(res));
|
|
}
|
|
cpuSetInstructionTime(cycles);
|
|
return res;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Roxlb
|
|
/// </summary>
|
|
static uint8_t cpuRoxlB(uint8_t dst, uint32_t sh, uint32_t cycles)
|
|
{
|
|
BOOLE x = cpuGetFlagX();
|
|
uint8_t res = dst;
|
|
sh &= 0x3f;
|
|
for (uint32_t i = 0; i < sh; ++i)
|
|
{
|
|
BOOLE x_temp = cpuMsbB(res);
|
|
res = (res << 1) | ((x) ? 1:0);
|
|
x = x_temp;
|
|
}
|
|
cpuSetFlagsRotateX(cpuGetZFlagB(res), cpuGetNFlagB(res), (x) ? 0x11 : 0);
|
|
cpuSetInstructionTime(cycles + sh*2);
|
|
return res;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Roxlw
|
|
/// </summary>
|
|
static uint16_t cpuRoxlW(uint16_t dst, uint32_t sh, uint32_t cycles)
|
|
{
|
|
BOOLE x = cpuGetFlagX();
|
|
uint16_t res = dst;
|
|
sh &= 0x3f;
|
|
for (uint32_t i = 0; i < sh; ++i)
|
|
{
|
|
BOOLE x_temp = cpuMsbW(res);
|
|
res = (res << 1) | ((x) ? 1:0);
|
|
x = x_temp;
|
|
}
|
|
cpuSetFlagsRotateX(cpuGetZFlagW(res), cpuGetNFlagW(res), (x) ? 0x11 : 0);
|
|
cpuSetInstructionTime(cycles + sh*2);
|
|
return res;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Roxll
|
|
/// </summary>
|
|
static uint32_t cpuRoxlL(uint32_t dst, uint32_t sh, uint32_t cycles)
|
|
{
|
|
BOOLE x = cpuGetFlagX();
|
|
uint32_t res = dst;
|
|
sh &= 0x3f;
|
|
for (uint32_t i = 0; i < sh; ++i)
|
|
{
|
|
BOOLE x_temp = cpuMsbL(res);
|
|
res = (res << 1) | ((x) ? 1:0);
|
|
x = x_temp;
|
|
}
|
|
cpuSetFlagsRotateX(cpuGetZFlagL(res), cpuGetNFlagL(res), (x) ? 0x11 : 0);
|
|
cpuSetInstructionTime(cycles + sh*2);
|
|
return res;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Roxrb
|
|
/// </summary>
|
|
static uint8_t cpuRoxrB(uint8_t dst, uint32_t sh, uint32_t cycles)
|
|
{
|
|
BOOLE x = cpuGetFlagX();
|
|
uint8_t res = dst;
|
|
sh &= 0x3f;
|
|
for (uint32_t i = 0; i < sh; ++i)
|
|
{
|
|
BOOLE x_temp = res & 1;
|
|
res = (res >> 1) | ((x) ? 0x80:0);
|
|
x = x_temp;
|
|
}
|
|
cpuSetFlagsRotateX(cpuGetZFlagB(res), cpuGetNFlagB(res), (x) ? 0x11 : 0);
|
|
cpuSetInstructionTime(cycles + sh*2);
|
|
return res;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Roxrw
|
|
/// </summary>
|
|
static uint16_t cpuRoxrW(uint16_t dst, uint32_t sh, uint32_t cycles)
|
|
{
|
|
BOOLE x = cpuGetFlagX();
|
|
uint16_t res = dst;
|
|
sh &= 0x3f;
|
|
for (uint32_t i = 0; i < sh; ++i)
|
|
{
|
|
BOOLE x_temp = res & 1;
|
|
res = (res >> 1) | ((x) ? 0x8000:0);
|
|
x = x_temp;
|
|
}
|
|
cpuSetFlagsRotateX(cpuGetZFlagW(res), cpuGetNFlagW(res), (x) ? 0x11 : 0);
|
|
cpuSetInstructionTime(cycles + sh*2);
|
|
return res;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Roxrl
|
|
/// </summary>
|
|
static uint32_t cpuRoxrL(uint32_t dst, uint32_t sh, uint32_t cycles)
|
|
{
|
|
BOOLE x = cpuGetFlagX();
|
|
uint32_t res = dst;
|
|
sh &= 0x3f;
|
|
for (uint32_t i = 0; i < sh; ++i)
|
|
{
|
|
BOOLE x_temp = res & 1;
|
|
res = (res >> 1) | ((x) ? 0x80000000:0);
|
|
x = x_temp;
|
|
}
|
|
cpuSetFlagsRotateX(cpuGetZFlagL(res), cpuGetNFlagL(res), (x) ? 0x11 : 0);
|
|
cpuSetInstructionTime(cycles + sh*2);
|
|
return res;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Stop
|
|
/// </summary>
|
|
static void cpuStop(uint16_t flags)
|
|
{
|
|
if (cpuGetFlagSupervisor())
|
|
{
|
|
cpuSetStop(TRUE);
|
|
cpuUpdateSr(flags);
|
|
cpuSetInstructionTime(4);
|
|
}
|
|
else
|
|
{
|
|
cpuThrowPrivilegeViolationException();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reset
|
|
/// </summary>
|
|
static void cpuReset(void)
|
|
{
|
|
cpuCallResetExceptionFunc();
|
|
cpuSetInstructionTime(132);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Rtd
|
|
/// </summary>
|
|
static void cpuRtd(void)
|
|
{
|
|
uint32_t displacement = cpuGetNextWordSignExt();
|
|
cpuInitializeFromNewPC(memoryReadLong(cpuGetAReg(7)));
|
|
cpuSetAReg(7, cpuGetAReg(7) + 4 + displacement);
|
|
cpuSetInstructionTime(4);
|
|
}
|
|
|
|
static uint32_t cpuRteStackInc[16] = {0, 0, 4, 4, 8, 0, 0, 52, 50, 10, 24, 84, 16, 18, 0, 0};
|
|
|
|
/// <summary>
|
|
/// Rte
|
|
/// </summary>
|
|
static void cpuRte(void)
|
|
{
|
|
if (cpuGetFlagSupervisor())
|
|
{
|
|
BOOLE redo = TRUE;
|
|
do
|
|
{
|
|
uint16_t newsr = memoryReadWord(cpuGetAReg(7));
|
|
cpuSetAReg(7, cpuGetAReg(7) + 2);
|
|
|
|
cpuInitializeFromNewPC(memoryReadLong(cpuGetAReg(7)));
|
|
cpuSetAReg(7, cpuGetAReg(7) + 4);
|
|
|
|
if (cpuGetModelMajor() > 0)
|
|
{
|
|
uint32_t frame_type = (memoryReadWord(cpuGetAReg(7)) >> 12) & 0xf;
|
|
cpuSetAReg(7, cpuGetAReg(7) + 2);
|
|
cpuSetAReg(7, cpuGetAReg(7) + cpuRteStackInc[frame_type]);
|
|
redo = (frame_type == 1 && cpuGetModelMajor() >= 2 && cpuGetModelMajor() < 6);
|
|
}
|
|
else redo = FALSE;
|
|
|
|
cpuUpdateSr(newsr); // Because we can go from isp to msp here.
|
|
|
|
} while (redo);
|
|
|
|
cpuSetInstructionTime(20);
|
|
}
|
|
else
|
|
{
|
|
cpuThrowPrivilegeViolationException();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Swap
|
|
/// </summary>
|
|
static void cpuSwap(uint32_t reg)
|
|
{
|
|
uint32_t res = cpuJoinWordToLong((uint16_t)cpuGetDReg(reg), (uint16_t) (cpuGetDReg(reg) >> 16));
|
|
cpuSetDReg(reg, res);
|
|
cpuSetFlagsNZ00NewL(res);
|
|
cpuSetInstructionTime(4);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Unlk
|
|
/// </summary>
|
|
static void cpuUnlk(uint32_t reg)
|
|
{
|
|
cpuSetAReg(7, cpuGetAReg(reg));
|
|
uint32_t value = memoryReadLong(cpuGetAReg(7));
|
|
cpuSetAReg(7, cpuGetAReg(7) + 4);
|
|
cpuSetAReg(reg, value);
|
|
cpuSetInstructionTime(12);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Link
|
|
/// </summary>
|
|
static void cpuLinkW(uint32_t reg)
|
|
{
|
|
uint32_t disp = cpuGetNextWordSignExt();
|
|
uint32_t value = cpuGetAReg(reg);
|
|
cpuSetAReg(7, cpuGetAReg(7) - 4);
|
|
memoryWriteLong(value, cpuGetAReg(7));
|
|
cpuSetAReg(reg, cpuGetAReg(7));
|
|
cpuSetAReg(7, cpuGetAReg(7) + disp);
|
|
cpuSetInstructionTime(16);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Link.
|
|
/// 68020, 68030 and 68040 only.
|
|
/// </summary>
|
|
static void cpuLinkL(uint32_t reg)
|
|
{
|
|
uint32_t disp = cpuGetNextLong();
|
|
uint32_t value = cpuGetAReg(reg);
|
|
cpuSetAReg(7, cpuGetAReg(7) - 4);
|
|
memoryWriteLong(value, cpuGetAReg(7));
|
|
cpuSetAReg(reg, cpuGetAReg(7));
|
|
cpuSetAReg(7, cpuGetAReg(7) + disp);
|
|
cpuSetInstructionTime(4);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Ext.w (byte to word)
|
|
/// </summary>
|
|
static void cpuExtW(uint32_t reg)
|
|
{
|
|
uint16_t res = cpuGetDRegByteSignExtWord(reg);
|
|
cpuSetDRegWord(reg, res);
|
|
cpuSetFlagsNZ00NewW(res);
|
|
cpuSetInstructionTime(4);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Ext.l (word to long)
|
|
/// </summary>
|
|
static void cpuExtL(uint32_t reg)
|
|
{
|
|
uint32_t res = cpuGetDRegWordSignExtLong(reg);
|
|
cpuSetDReg(reg, res);
|
|
cpuSetFlagsNZ00NewL(res);
|
|
cpuSetInstructionTime(4);
|
|
}
|
|
|
|
/// <summary>
|
|
/// ExtB.l (byte to long) (020+)
|
|
/// </summary>
|
|
static void cpuExtBL(uint32_t reg)
|
|
{
|
|
uint32_t res = cpuGetDRegByteSignExtLong(reg);
|
|
cpuSetDReg(reg, res);
|
|
cpuSetFlagsNZ00NewL(res);
|
|
cpuSetInstructionTime(4);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Exg Rx,Ry
|
|
/// </summary>
|
|
static void cpuExgAll(uint32_t reg1_type, uint32_t reg1, uint32_t reg2_type, uint32_t reg2)
|
|
{
|
|
uint32_t tmp = cpuGetReg(reg1_type, reg1);
|
|
cpuSetReg(reg1_type, reg1, cpuGetReg(reg2_type, reg2));
|
|
cpuSetReg(reg2_type, reg2, tmp);
|
|
cpuSetInstructionTime(6);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Exg Dx,Dy
|
|
/// </summary>
|
|
static void cpuExgDD(uint32_t reg1, uint32_t reg2)
|
|
{
|
|
cpuExgAll(0, reg1, 0, reg2);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Exg Ax,Ay
|
|
/// </summary>
|
|
static void cpuExgAA(uint32_t reg1, uint32_t reg2)
|
|
{
|
|
cpuExgAll(1, reg1, 1, reg2);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Exg Dx,Ay
|
|
/// </summary>
|
|
static void cpuExgDA(uint32_t reg1, uint32_t reg2)
|
|
{
|
|
cpuExgAll(0, reg1, 1, reg2);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Movem.w regs, -(Ax)
|
|
/// Order: d0-d7,a0-a7 a7 first
|
|
/// </summary>
|
|
static void cpuMovemwPre(uint16_t regs, uint32_t reg)
|
|
{
|
|
uint32_t cycles = 8;
|
|
uint32_t dstea = cpuGetAReg(reg);
|
|
uint32_t index = 1;
|
|
int32_t j;
|
|
|
|
int32_t i = 1;
|
|
for (j = 7; j >= 0; j--)
|
|
{
|
|
if (regs & index)
|
|
{
|
|
dstea -= 2;
|
|
if (cpuGetModelMajor() >= 2 && j == reg)
|
|
{
|
|
memoryWriteWord((uint16_t)dstea, dstea);
|
|
}
|
|
else
|
|
{
|
|
memoryWriteWord(cpuGetRegWord(i, j), dstea);
|
|
}
|
|
cycles += 4;
|
|
}
|
|
index = index << 1;
|
|
}
|
|
|
|
i = 0;
|
|
for (j = 7; j >= 0; j--)
|
|
{
|
|
if (regs & index)
|
|
{
|
|
dstea -= 2;
|
|
memoryWriteWord(cpuGetRegWord(i, j), dstea);
|
|
cycles += 4;
|
|
}
|
|
index = index << 1;
|
|
}
|
|
cpuSetAReg(reg, dstea);
|
|
cpuSetInstructionTime(cycles);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Movem.l regs, -(Ax)
|
|
/// Order: d0-d7,a0-a7 a7 first
|
|
/// </summary>
|
|
static void cpuMovemlPre(uint16_t regs, uint32_t reg)
|
|
{
|
|
uint32_t cycles = 8;
|
|
uint32_t dstea = cpuGetAReg(reg);
|
|
uint32_t index = 1;
|
|
int32_t j;
|
|
|
|
int32_t i = 1;
|
|
for (j = 7; j >= 0; j--)
|
|
{
|
|
if (regs & index)
|
|
{
|
|
dstea -= 4;
|
|
if (cpuGetModelMajor() >= 2 && j == reg)
|
|
{
|
|
memoryWriteLong(dstea, dstea);
|
|
}
|
|
else
|
|
{
|
|
memoryWriteLong(cpuGetReg(i, j), dstea);
|
|
}
|
|
cycles += 8;
|
|
}
|
|
index = index << 1;
|
|
}
|
|
|
|
i = 0;
|
|
for (j = 7; j >= 0; j--)
|
|
{
|
|
if (regs & index)
|
|
{
|
|
dstea -= 4;
|
|
memoryWriteLong(cpuGetReg(i, j), dstea);
|
|
cycles += 8;
|
|
}
|
|
index = index << 1;
|
|
}
|
|
|
|
cpuSetAReg(reg, dstea);
|
|
cpuSetInstructionTime(cycles);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Movem.w (Ax)+, regs
|
|
/// Order: a7-a0,d7-d0 d0 first
|
|
/// </summary>
|
|
static void cpuMovemwPost(uint16_t regs, uint32_t reg)
|
|
{
|
|
uint32_t cycles = 12;
|
|
uint32_t dstea = cpuGetAReg(reg);
|
|
uint32_t index = 1;
|
|
|
|
for (uint32_t i = 0; i < 2; ++i)
|
|
{
|
|
for (uint32_t j = 0; j < 8; ++j)
|
|
{
|
|
if (regs & index)
|
|
{
|
|
// Each word, for both data and address registers, is sign-extended before stored.
|
|
cpuSetReg(i, j, (uint32_t)(int32_t)(int16_t) memoryReadWord(dstea));
|
|
dstea += 2;
|
|
cycles += 4;
|
|
}
|
|
index = index << 1;
|
|
}
|
|
}
|
|
cpuSetAReg(reg, dstea);
|
|
cpuSetInstructionTime(cycles);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Movem.l (Ax)+, regs
|
|
/// Order: a7-a0,d7-d0 d0 first
|
|
/// </summary>
|
|
static void cpuMovemlPost(uint16_t regs, uint32_t reg)
|
|
{
|
|
uint32_t cycles = 12;
|
|
uint32_t dstea = cpuGetAReg(reg);
|
|
uint32_t index = 1;
|
|
|
|
for (uint32_t i = 0; i < 2; ++i)
|
|
{
|
|
for (uint32_t j = 0; j < 8; ++j)
|
|
{
|
|
if (regs & index)
|
|
{
|
|
cpuSetReg(i, j, memoryReadLong(dstea));
|
|
dstea += 4;
|
|
cycles += 8;
|
|
}
|
|
index = index << 1;
|
|
}
|
|
}
|
|
cpuSetAReg(reg, dstea);
|
|
cpuSetInstructionTime(cycles);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Movem.w <Control>, regs
|
|
/// Order: a7-a0,d7-d0 d0 first
|
|
/// </summary>
|
|
static void cpuMovemwEa2R(uint16_t regs, uint32_t ea, uint32_t eacycles)
|
|
{
|
|
uint32_t cycles = eacycles;
|
|
uint32_t dstea = ea;
|
|
uint32_t index = 1;
|
|
|
|
for (uint32_t i = 0; i < 2; ++i)
|
|
{
|
|
for (uint32_t j = 0; j < 8; ++j)
|
|
{
|
|
if (regs & index)
|
|
{
|
|
// Each word, for both data and address registers, is sign-extended before stored.
|
|
cpuSetReg(i, j, (uint32_t)(int32_t)(int16_t) memoryReadWord(dstea));
|
|
dstea += 2;
|
|
cycles += 4;
|
|
}
|
|
index = index << 1;
|
|
}
|
|
}
|
|
cpuSetInstructionTime(cycles);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Movem.l <Control>, regs
|
|
/// Order: a7-a0,d7-d0 d0 first
|
|
/// </summary>
|
|
static void cpuMovemlEa2R(uint16_t regs, uint32_t ea, uint32_t eacycles)
|
|
{
|
|
uint32_t cycles = eacycles;
|
|
uint32_t dstea = ea;
|
|
uint32_t index = 1;
|
|
|
|
for (uint32_t i = 0; i < 2; ++i)
|
|
{
|
|
for (uint32_t j = 0; j < 8; ++j)
|
|
{
|
|
if (regs & index)
|
|
{
|
|
cpuSetReg(i, j, memoryReadLong(dstea));
|
|
dstea += 4;
|
|
cycles += 8;
|
|
}
|
|
index = index << 1;
|
|
}
|
|
}
|
|
cpuSetInstructionTime(cycles);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Movem.w regs, <Control>
|
|
/// Order: a7-a0,d7-d0 d0 first
|
|
/// </summary>
|
|
static void cpuMovemwR2Ea(uint16_t regs, uint32_t ea, uint32_t eacycles)
|
|
{
|
|
uint32_t cycles = eacycles;
|
|
uint32_t dstea = ea;
|
|
uint32_t index = 1;
|
|
|
|
for (uint32_t i = 0; i < 2; ++i)
|
|
{
|
|
for (uint32_t j = 0; j < 8; ++j)
|
|
{
|
|
if (regs & index)
|
|
{
|
|
memoryWriteWord(cpuGetRegWord(i, j), dstea);
|
|
dstea += 2;
|
|
cycles += 4;
|
|
}
|
|
index = index << 1;
|
|
}
|
|
}
|
|
cpuSetInstructionTime(cycles);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Movem.l regs, <Control>
|
|
/// Order: a7-a0,d7-d0 d0 first
|
|
/// </summary>
|
|
static void cpuMovemlR2Ea(uint16_t regs, uint32_t ea, uint32_t eacycles)
|
|
{
|
|
uint32_t cycles = eacycles;
|
|
uint32_t dstea = ea;
|
|
uint32_t index = 1;
|
|
uint32_t i, j;
|
|
|
|
for (i = 0; i < 2; ++i)
|
|
{
|
|
for (j = 0; j < 8; ++j)
|
|
{
|
|
if (regs & index)
|
|
{
|
|
memoryWriteLong(cpuGetReg(i, j), dstea);
|
|
dstea += 4;
|
|
cycles += 8;
|
|
}
|
|
index = index << 1;
|
|
}
|
|
}
|
|
cpuSetInstructionTime(cycles);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Trap #vectorno
|
|
/// </summary>
|
|
static void cpuTrap(uint32_t vectorno)
|
|
{
|
|
// PC written to the exception frame must be pc + 2, the address of the next instruction.
|
|
cpuThrowTrapException(vectorno);
|
|
}
|
|
|
|
/// <summary>
|
|
/// move.l Ax,Usp
|
|
/// </summary>
|
|
static void cpuMoveToUsp(uint32_t reg)
|
|
{
|
|
if (cpuGetFlagSupervisor())
|
|
{
|
|
// In supervisor mode, usp does not affect a7
|
|
cpuSetUspDirect(cpuGetAReg(reg));
|
|
cpuSetInstructionTime(4);
|
|
}
|
|
else
|
|
{
|
|
cpuThrowPrivilegeViolationException();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// move.l Usp,Ax
|
|
/// </summary>
|
|
static void cpuMoveFromUsp(uint32_t reg)
|
|
{
|
|
if (cpuGetFlagSupervisor())
|
|
{
|
|
// In supervisor mode, usp is up to date
|
|
cpuSetAReg(reg, cpuGetUspDirect());
|
|
cpuSetInstructionTime(4);
|
|
}
|
|
else
|
|
{
|
|
cpuThrowPrivilegeViolationException();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// cmp.b (Ay)+,(Ax)+
|
|
/// </summary>
|
|
static void cpuCmpMB(uint32_t regx, uint32_t regy)
|
|
{
|
|
uint8_t src = memoryReadByte(cpuEA03(regy, 1));
|
|
uint8_t dst = memoryReadByte(cpuEA03(regx, 1));
|
|
uint8_t res = dst - src;
|
|
cpuSetFlagsCmp(cpuIsZeroB(res), cpuMsbB(res), cpuMsbB(dst), cpuMsbB(src));
|
|
cpuSetInstructionTime(12);
|
|
}
|
|
|
|
/// <summary>
|
|
/// cmp.w (Ay)+,(Ax)+
|
|
/// </summary>
|
|
static void cpuCmpMW(uint32_t regx, uint32_t regy)
|
|
{
|
|
uint16_t src = memoryReadWord(cpuEA03(regy, 2));
|
|
uint16_t dst = memoryReadWord(cpuEA03(regx, 2));
|
|
uint16_t res = dst - src;
|
|
cpuSetFlagsCmp(cpuIsZeroW(res), cpuMsbW(res), cpuMsbW(dst), cpuMsbW(src));
|
|
cpuSetInstructionTime(12);
|
|
}
|
|
|
|
/// <summary>
|
|
/// cmp.l (Ay)+,(Ax)+
|
|
/// </summary>
|
|
static void cpuCmpML(uint32_t regx, uint32_t regy)
|
|
{
|
|
uint32_t src = memoryReadLong(cpuEA03(regy, 4));
|
|
uint32_t dst = memoryReadLong(cpuEA03(regx, 4));
|
|
uint32_t res = dst - src;
|
|
cpuSetFlagsCmp(cpuIsZeroL(res), cpuMsbL(res), cpuMsbL(dst), cpuMsbL(src));
|
|
cpuSetInstructionTime(20);
|
|
}
|
|
|
|
/// <summary>
|
|
/// chk.w ea, dn (upper bound, value to test)
|
|
/// Undocumented features:
|
|
/// Z is set from the register operand,
|
|
/// V and C is always cleared.
|
|
/// </summary>
|
|
static void cpuChkW(uint16_t value, uint16_t ub, uint32_t instructionTime)
|
|
{
|
|
cpuSetFlagZ(value == 0);
|
|
cpuClearFlagsVC();
|
|
if (((int16_t)value) < 0)
|
|
{
|
|
cpuSetFlagN(TRUE);
|
|
cpuThrowChkException();
|
|
}
|
|
else if (((int16_t)value) > ((int16_t)ub))
|
|
{
|
|
cpuSetFlagN(FALSE);
|
|
cpuThrowChkException();
|
|
}
|
|
else
|
|
{
|
|
cpuSetFlagN(FALSE);
|
|
cpuSetInstructionTime(instructionTime);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// chk.l Dx, ea
|
|
/// 68020+
|
|
/// Undocumented features:
|
|
/// Z is set from the register operand,
|
|
/// V and C is always cleared.
|
|
/// </summary>
|
|
static void cpuChkL(uint32_t value, uint32_t ub, uint32_t instructionTime)
|
|
{
|
|
cpuSetFlagZ(value == 0);
|
|
cpuClearFlagsVC();
|
|
if (((int32_t)value) < 0)
|
|
{
|
|
cpuSetFlagN(TRUE);
|
|
cpuThrowChkException();
|
|
}
|
|
else if (((int32_t)value) > ((int32_t)ub))
|
|
{
|
|
cpuSetFlagN(FALSE);
|
|
cpuThrowChkException();
|
|
}
|
|
else
|
|
{
|
|
cpuSetInstructionTime(instructionTime);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// addx.b dx,dy
|
|
/// </summary>
|
|
static uint8_t cpuAddXB(uint8_t dst, uint8_t src)
|
|
{
|
|
uint8_t res = dst + src + ((cpuGetFlagX()) ? 1:0);
|
|
cpuSetFlagsAddX(cpuIsZeroB(res), cpuMsbB(res), cpuMsbB(dst), cpuMsbB(src));
|
|
return res;
|
|
}
|
|
|
|
/// <summary>
|
|
/// addx.w dx,dy
|
|
/// </summary>
|
|
static uint16_t cpuAddXW(uint16_t dst, uint16_t src)
|
|
{
|
|
uint16_t res = dst + src + ((cpuGetFlagX()) ? 1:0);
|
|
cpuSetFlagsAddX(cpuIsZeroW(res), cpuMsbW(res), cpuMsbW(dst), cpuMsbW(src));
|
|
return res;
|
|
}
|
|
|
|
/// <summary>
|
|
/// addx.l dx,dy
|
|
/// </summary>
|
|
static uint32_t cpuAddXL(uint32_t dst, uint32_t src)
|
|
{
|
|
uint32_t res = dst + src + ((cpuGetFlagX()) ? 1:0);
|
|
cpuSetFlagsAddX(cpuIsZeroL(res), cpuMsbL(res), cpuMsbL(dst), cpuMsbL(src));
|
|
return res;
|
|
}
|
|
|
|
/// <summary>
|
|
/// subx.b dx,dy
|
|
/// </summary>
|
|
static uint8_t cpuSubXB(uint8_t dst, uint8_t src)
|
|
{
|
|
uint8_t res = dst - src - ((cpuGetFlagX()) ? 1:0);
|
|
cpuSetFlagsSubX(cpuIsZeroB(res), cpuMsbB(res), cpuMsbB(dst), cpuMsbB(src));
|
|
return res;
|
|
}
|
|
|
|
/// <summary>
|
|
/// subx.w dx,dy
|
|
/// </summary>
|
|
static uint16_t cpuSubXW(uint16_t dst, uint16_t src)
|
|
{
|
|
uint16_t res = dst - src - ((cpuGetFlagX()) ? 1:0);
|
|
cpuSetFlagsSubX(cpuIsZeroW(res), cpuMsbW(res), cpuMsbW(dst), cpuMsbW(src));
|
|
return res;
|
|
}
|
|
|
|
/// <summary>
|
|
/// subx.l dx,dy
|
|
/// </summary>
|
|
static uint32_t cpuSubXL(uint32_t dst, uint32_t src)
|
|
{
|
|
uint32_t res = dst - src - ((cpuGetFlagX()) ? 1:0);
|
|
cpuSetFlagsSubX(cpuIsZeroL(res), cpuMsbL(res), cpuMsbL(dst), cpuMsbL(src));
|
|
return res;
|
|
}
|
|
|
|
/// <summary>
|
|
/// abcd.b src,dst
|
|
/// Implemented using the information from:
|
|
/// 68000 Undocumented Behavior Notes
|
|
/// Fourth Edition
|
|
/// by Bart Trzynadlowski, May 12, 2003
|
|
/// </summary>
|
|
static uint8_t cpuAbcdB(uint8_t dst, uint8_t src)
|
|
{
|
|
uint8_t xflag = (cpuGetFlagX()) ? 1 : 0;
|
|
uint16_t low_nibble = (dst & 0xf) + (src & 0xf) + xflag;
|
|
uint16_t high_nibble = ((uint16_t)(dst & 0xf0)) + ((uint16_t)(src & 0xf0));
|
|
uint16_t result_unadjusted = low_nibble + high_nibble;
|
|
uint16_t result_bcd = result_unadjusted;
|
|
|
|
if (low_nibble > 9)
|
|
{
|
|
result_bcd += 6;
|
|
}
|
|
|
|
BOOLE xc_flags = (result_bcd & 0xfff0) > 0x90;
|
|
cpuSetFlagXC(xc_flags);
|
|
if (xc_flags)
|
|
{
|
|
result_bcd += 0x60;
|
|
}
|
|
|
|
if (result_bcd & 0xff)
|
|
{
|
|
cpuSetFlagZ(FALSE);
|
|
}
|
|
|
|
if (cpuGetModelMajor() < 4) // 040 apparently does not set these flags
|
|
{
|
|
cpuSetFlagN(result_bcd & 0x80);
|
|
cpuSetFlagV(((result_unadjusted & 0x80) == 0) && (result_bcd & 0x80));
|
|
}
|
|
return (uint8_t)result_bcd;
|
|
}
|
|
|
|
/// <summary>
|
|
/// sbcd.b src,dst
|
|
/// nbcd.b src (set dst=0)
|
|
/// Implemented using the information from:
|
|
/// 68000 Undocumented Behavior Notes
|
|
/// Fourth Edition
|
|
/// by Bart Trzynadlowski, May 12, 2003
|
|
/// </summary>
|
|
static uint8_t cpuSbcdB(uint8_t dst, uint8_t src)
|
|
{
|
|
uint16_t xflag = (cpuGetFlagX()) ? 1:0;
|
|
uint16_t result_plain_binary = (uint16_t)dst - (uint16_t)src - xflag;
|
|
uint16_t low_nibble = (uint16_t)(dst & 0xf) - (uint16_t)(src & 0xf) - xflag;
|
|
uint16_t high_nibble = ((uint16_t)(dst & 0xf0)) - ((uint16_t)(src & 0xf0));
|
|
uint16_t result_unadjusted = low_nibble + high_nibble;
|
|
uint16_t result_bcd = result_unadjusted;
|
|
|
|
if ((int16_t)result_plain_binary < 0)
|
|
{
|
|
result_bcd -= 0x60;
|
|
}
|
|
|
|
if (((int16_t)low_nibble) < 0)
|
|
{
|
|
result_bcd -= 6;
|
|
result_plain_binary -= 6;
|
|
}
|
|
|
|
BOOLE borrow = ((int16_t)result_plain_binary < 0);
|
|
cpuSetFlagXC(borrow);
|
|
|
|
if (result_bcd & 0xff)
|
|
{
|
|
cpuSetFlagZ(FALSE);
|
|
}
|
|
|
|
if (cpuGetModelMajor() < 4)
|
|
{
|
|
cpuSetFlagN(result_bcd & 0x80);
|
|
cpuSetFlagV(((result_unadjusted & 0x80) == 0x80) && !(result_bcd & 0x80));
|
|
}
|
|
return (uint8_t) result_bcd;
|
|
}
|
|
|
|
/// <summary>
|
|
/// nbcd.b dst
|
|
/// </summary>
|
|
static uint8_t cpuNbcdB(uint8_t dst)
|
|
{
|
|
return cpuSbcdB(0, dst);
|
|
}
|
|
|
|
// Bit field functions
|
|
typedef struct cpuBfData
|
|
{
|
|
int32_t offset;
|
|
uint32_t width;
|
|
uint32_t normalized_offset;
|
|
|
|
uint32_t base_address;
|
|
int32_t base_address_byte_offset;
|
|
uint32_t base_address_byte_count;
|
|
|
|
uint32_t field;
|
|
uint64_t field_mask;
|
|
uint32_t dn;
|
|
uint64_t field_memory;
|
|
} cpuBfData;
|
|
|
|
static int32_t cpuGetBfOffset(uint16_t ext, bool offsetIsDataRegister)
|
|
{
|
|
int32_t offset = (ext >> 6) & 0x1f;
|
|
if (offsetIsDataRegister)
|
|
{
|
|
offset = (int32_t) cpuGetDReg(offset & 7);
|
|
}
|
|
return offset;
|
|
}
|
|
|
|
static uint32_t cpuGetBfWidth(uint16_t ext, bool widthIsDataRegister)
|
|
{
|
|
uint32_t width = (ext & 0x1f);
|
|
if (widthIsDataRegister)
|
|
{
|
|
width = cpuGetDReg(width & 7) & 0x1f;
|
|
}
|
|
if (width == 0)
|
|
{
|
|
width = 32;
|
|
}
|
|
return width;
|
|
}
|
|
|
|
static uint32_t cpuRotateLeft(uint32_t value, uint32_t shift)
|
|
{
|
|
return (value << shift) | (value >> (32 - shift));
|
|
}
|
|
|
|
static uint32_t cpuRotateRight(uint32_t value, uint32_t shift)
|
|
{
|
|
return (value >> shift) | (value << (32 - shift));
|
|
}
|
|
|
|
static void cpuSetBfField(cpuBfData *bf_data, uint32_t ea_or_reg, bool has_ea)
|
|
{
|
|
if (has_ea)
|
|
{
|
|
uint32_t shift = bf_data->base_address_byte_count*8 - bf_data->normalized_offset - bf_data->width;
|
|
uint64_t field_value = (bf_data->field_memory & ~(bf_data->field_mask << shift)) | (((uint64_t)bf_data->field) << shift);
|
|
uint32_t address = bf_data->base_address + bf_data->base_address_byte_offset;
|
|
|
|
for (int i = bf_data->base_address_byte_count - 1; i >= 0; --i)
|
|
{
|
|
uint8_t field_byte = (field_value >> (i*8)) & 0xff;
|
|
memoryWriteByte(field_byte, address);
|
|
++address;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Field from Dn wraps around if offset + width > 32
|
|
uint32_t offsetPlusWidth = (bf_data->offset & 0x1f) + bf_data->width;
|
|
if (offsetPlusWidth > 32)
|
|
{
|
|
uint32_t rotate = offsetPlusWidth - 32;
|
|
uint32_t reg_value = (cpuGetDReg(ea_or_reg) & ~cpuRotateRight((uint32_t)bf_data->field_mask, rotate)) | cpuRotateRight(bf_data->field, rotate);
|
|
cpuSetDReg(ea_or_reg, reg_value);
|
|
}
|
|
else
|
|
{
|
|
uint32_t reg_shift = 32 - offsetPlusWidth;
|
|
uint32_t reg_value = (cpuGetDReg(ea_or_reg) & ~(bf_data->field_mask << reg_shift)) | (bf_data->field << reg_shift);
|
|
cpuSetDReg(ea_or_reg, reg_value);
|
|
}
|
|
}
|
|
}
|
|
|
|
void cpuBfDecodeExtWordAndGetField(cpuBfData *bf_data, uint32_t ea_or_reg, bool has_dn, bool has_ea, uint16_t ext)
|
|
{
|
|
bool offsetIsDataRegister = ((ext & 0x0800) == 0x0800);
|
|
bool widthIsDataRegister = ((ext & 0x0020) == 0x0020);
|
|
|
|
bf_data->offset = cpuGetBfOffset(ext, offsetIsDataRegister);
|
|
bf_data->width = cpuGetBfWidth(ext, widthIsDataRegister);
|
|
bf_data->field_mask = 0xffffffff >> (32 - bf_data->width);
|
|
|
|
if (has_dn)
|
|
{
|
|
bf_data->dn = (ext & 0x7000) >> 12;
|
|
}
|
|
|
|
if (has_ea)
|
|
{
|
|
bf_data->base_address = ea_or_reg; // Base address of the field, before offset is applied
|
|
bf_data->base_address_byte_offset = (bf_data->offset >> 3); // The first byte in the field
|
|
bf_data->normalized_offset = bf_data->offset - (bf_data->base_address_byte_offset)*8; // Offset relative to the first byte in the field
|
|
bf_data->base_address_byte_count = (bf_data->normalized_offset + bf_data->width + 7) / 8;
|
|
|
|
uint32_t field = 0;
|
|
uint64_t field_memory = 0;
|
|
uint32_t address = bf_data->base_address + bf_data->base_address_byte_offset;
|
|
uint32_t shift = (8 - bf_data->normalized_offset - bf_data->width) & 7;
|
|
for (int i = bf_data->base_address_byte_count - 1; i >= 0; --i)
|
|
{
|
|
uint64_t value = ((uint64_t)memoryReadByte(address)) << (8 * i);
|
|
field_memory |= value;
|
|
field |= (value >> shift);
|
|
++address;
|
|
}
|
|
|
|
bf_data->field_memory = field_memory;
|
|
bf_data->field = field;
|
|
}
|
|
else
|
|
{
|
|
// Field from Dn wraps around if offset + width > 32
|
|
const uint32_t offsetPlusWidth = (bf_data->offset & 0x1f) + bf_data->width;
|
|
if (offsetPlusWidth > 32)
|
|
{
|
|
bf_data->field = cpuRotateLeft(cpuGetDReg(ea_or_reg), offsetPlusWidth - 32);
|
|
}
|
|
else
|
|
{
|
|
bf_data->field = cpuGetDReg(ea_or_reg) >> (32 - offsetPlusWidth);
|
|
}
|
|
}
|
|
bf_data->field &= bf_data->field_mask;
|
|
}
|
|
|
|
/// <summary>
|
|
/// bfchg common logic
|
|
/// </summary>
|
|
static void cpuBfChgCommon(uint32_t ea_or_reg, bool has_ea, uint16_t ext)
|
|
{
|
|
cpuBfData bf_data;
|
|
cpuBfDecodeExtWordAndGetField(&bf_data, ea_or_reg, false, has_ea, ext);
|
|
cpuSetFlagsNZVC(bf_data.field == 0, bf_data.field & (1 << (bf_data.width - 1)), FALSE, FALSE);
|
|
|
|
bf_data.field = (~bf_data.field) & (uint32_t)bf_data.field_mask;
|
|
|
|
cpuSetBfField(&bf_data, ea_or_reg, has_ea);
|
|
}
|
|
|
|
// bfchg dx {offset:width}
|
|
static void cpuBfChgReg(uint32_t regno, uint16_t ext)
|
|
{
|
|
cpuBfChgCommon(regno, false, ext);
|
|
}
|
|
|
|
/// <summary>
|
|
/// bfchg ea {offset:width}
|
|
/// </summary>
|
|
static void cpuBfChgEa(uint32_t ea, uint16_t ext)
|
|
{
|
|
cpuBfChgCommon(ea, true, ext);
|
|
}
|
|
|
|
/// <summary>
|
|
/// bfclr common logic
|
|
/// </summary>
|
|
static void cpuBfClrCommon(uint32_t ea_or_reg, bool has_ea, uint16_t ext)
|
|
{
|
|
cpuBfData bf_data;
|
|
cpuBfDecodeExtWordAndGetField(&bf_data, ea_or_reg, false, has_ea, ext);
|
|
|
|
cpuSetFlagsNZVC(bf_data.field == 0, bf_data.field & (1 << (bf_data.width - 1)), FALSE, FALSE);
|
|
|
|
bf_data.field = 0;
|
|
|
|
cpuSetBfField(&bf_data, ea_or_reg, has_ea);
|
|
}
|
|
|
|
/// <summary>
|
|
/// bfclr dx {offset:width}
|
|
/// </summary>
|
|
static void cpuBfClrReg(uint32_t regno, uint16_t ext)
|
|
{
|
|
cpuBfClrCommon(regno, false, ext);
|
|
}
|
|
|
|
/// <summary>
|
|
/// bfclr ea {offset:width}
|
|
/// </summary>
|
|
static void cpuBfClrEa(uint32_t ea, uint16_t ext)
|
|
{
|
|
cpuBfClrCommon(ea, true, ext);
|
|
}
|
|
|
|
/// <summary>
|
|
/// bfexts common logic
|
|
/// </summary>
|
|
static void cpuBfExtsCommon(uint32_t ea_or_reg, bool has_ea, uint16_t ext)
|
|
{
|
|
cpuBfData bf_data;
|
|
cpuBfDecodeExtWordAndGetField(&bf_data, ea_or_reg, true, has_ea, ext);
|
|
|
|
BOOLE n_flag = bf_data.field & (1 << (bf_data.width - 1));
|
|
|
|
cpuSetFlagsNZVC(bf_data.field == 0, n_flag, FALSE, FALSE);
|
|
|
|
if (n_flag)
|
|
{
|
|
bf_data.field = (uint32_t)((~bf_data.field_mask) | bf_data.field);
|
|
}
|
|
// Destination is always Dn
|
|
cpuSetDReg(bf_data.dn, bf_data.field);
|
|
}
|
|
|
|
/// <summary>
|
|
/// bfexts dx {offset:width}, Dn
|
|
/// </summary>
|
|
static void cpuBfExtsReg(uint32_t regno, uint16_t ext)
|
|
{
|
|
cpuBfExtsCommon(regno, false, ext);
|
|
}
|
|
|
|
/// <summary>
|
|
/// bfexts ea {offset:width}, Dn
|
|
/// </summary>
|
|
static void cpuBfExtsEa(uint32_t ea, uint16_t ext)
|
|
{
|
|
cpuBfExtsCommon(ea, true, ext);
|
|
}
|
|
|
|
/// <summary>
|
|
/// bfextu ea {offset:width}, Dn
|
|
/// </summary>
|
|
static void cpuBfExtuCommon(uint32_t ea_or_reg, bool has_ea, uint16_t ext)
|
|
{
|
|
cpuBfData bf_data;
|
|
cpuBfDecodeExtWordAndGetField(&bf_data, ea_or_reg, true, has_ea, ext);
|
|
cpuSetFlagsNZVC(bf_data.field == 0, bf_data.field & (1 << (bf_data.width - 1)), FALSE, FALSE);
|
|
// Destination is always Dn
|
|
cpuSetDReg(bf_data.dn, bf_data.field);
|
|
}
|
|
|
|
/// <summary>
|
|
/// bfextu dx {offset:width}, Dn
|
|
/// </summary>
|
|
static void cpuBfExtuReg(uint32_t regno, uint16_t ext)
|
|
{
|
|
cpuBfExtuCommon(regno, false, ext);
|
|
}
|
|
|
|
/// <summary>
|
|
/// bfextu ea {offset:width}, Dn
|
|
/// </summary>
|
|
static void cpuBfExtuEa(uint32_t ea, uint16_t ext)
|
|
{
|
|
cpuBfExtuCommon(ea, true, ext);
|
|
}
|
|
|
|
/// <summary>
|
|
/// bfffo common logic
|
|
/// </summary>
|
|
static void cpuBfFfoCommon(uint32_t val, bool has_ea, uint16_t ext)
|
|
{
|
|
cpuBfData bf_data;
|
|
uint32_t i;
|
|
cpuBfDecodeExtWordAndGetField(&bf_data, val, true, has_ea, ext);
|
|
cpuSetFlagsNZVC(bf_data.field == 0, bf_data.field & (1 << (bf_data.width - 1)), FALSE, FALSE);
|
|
|
|
for (i = 0; i < bf_data.width; ++i)
|
|
{
|
|
if (bf_data.field & (1 << (bf_data.width - i - 1)))
|
|
break;
|
|
}
|
|
cpuSetDReg(bf_data.dn, bf_data.offset + i);
|
|
}
|
|
|
|
/// <summary>
|
|
/// bfffo dx {offset:width}, Dn
|
|
/// </summary>
|
|
static void cpuBfFfoReg(uint32_t regno, uint16_t ext)
|