mpw/cpu/CpuModule_Instructions.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)