3634 lines
117 KiB
C++
3634 lines
117 KiB
C++
#pragma once
|
|
#include <utility>
|
|
#include <atomic>
|
|
#include <stdexcept>
|
|
#include <tuple>
|
|
#include <type_traits>
|
|
#include <unordered_map>
|
|
#include <unordered_set>
|
|
#include "bit.h"
|
|
#include "core_timing.h"
|
|
#include "crash.h"
|
|
#include "decoder.h"
|
|
#include "memory_interface.h"
|
|
#include "operand.h"
|
|
#include "register.h"
|
|
|
|
namespace Teakra {
|
|
|
|
class UnimplementedException : public std::runtime_error {
|
|
public:
|
|
UnimplementedException() : std::runtime_error("unimplemented") {}
|
|
};
|
|
|
|
class Interpreter {
|
|
public:
|
|
Interpreter(CoreTiming& core_timing, RegisterState& regs, MemoryInterface& mem)
|
|
: core_timing(core_timing), regs(regs), mem(mem) {}
|
|
|
|
void PushPC() {
|
|
u16 l = (u16)(regs.pc & 0xFFFF);
|
|
u16 h = (u16)(regs.pc >> 16);
|
|
if (regs.cpc == 1) {
|
|
mem.DataWrite(--regs.sp, h);
|
|
mem.DataWrite(--regs.sp, l);
|
|
} else {
|
|
mem.DataWrite(--regs.sp, l);
|
|
mem.DataWrite(--regs.sp, h);
|
|
}
|
|
}
|
|
|
|
void PopPC() {
|
|
u16 h, l;
|
|
if (regs.cpc == 1) {
|
|
l = mem.DataRead(regs.sp++);
|
|
h = mem.DataRead(regs.sp++);
|
|
} else {
|
|
h = mem.DataRead(regs.sp++);
|
|
l = mem.DataRead(regs.sp++);
|
|
}
|
|
SetPC(l | ((u32)h << 16));
|
|
}
|
|
|
|
void SetPC(u32 new_pc) {
|
|
ASSERT(new_pc < 0x40000);
|
|
regs.pc = new_pc;
|
|
}
|
|
|
|
void undefined(u16 opcode) {
|
|
UNREACHABLE();
|
|
}
|
|
|
|
void Run(u64 cycles) {
|
|
idle = false;
|
|
for (u64 i = 0; i < cycles; ++i) {
|
|
if (idle) {
|
|
u64 skipped = core_timing.Skip(cycles - i - 1);
|
|
i += skipped;
|
|
|
|
// Skip additional tick so to let components fire interrupts
|
|
if (i < cycles - 1) {
|
|
++i;
|
|
core_timing.Tick();
|
|
}
|
|
}
|
|
|
|
for (std::size_t i = 0; i < 3; ++i) {
|
|
if (interrupt_pending[i].exchange(false)) {
|
|
regs.ip[i] = 1;
|
|
}
|
|
}
|
|
|
|
if (vinterrupt_pending.exchange(false)) {
|
|
regs.ipv = 1;
|
|
}
|
|
|
|
u16 opcode = mem.ProgramRead((regs.pc++) | (regs.prpage << 18));
|
|
auto& decoder = decoders[opcode];
|
|
u16 expand_value = 0;
|
|
if (decoder.NeedExpansion()) {
|
|
expand_value = mem.ProgramRead((regs.pc++) | (regs.prpage << 18));
|
|
}
|
|
|
|
if (regs.rep) {
|
|
if (regs.repc == 0) {
|
|
regs.rep = false;
|
|
} else {
|
|
--regs.repc;
|
|
--regs.pc;
|
|
}
|
|
}
|
|
|
|
if (regs.lp && regs.bkrep_stack[regs.bcn - 1].end + 1 == regs.pc) {
|
|
if (regs.bkrep_stack[regs.bcn - 1].lc == 0) {
|
|
--regs.bcn;
|
|
regs.lp = regs.bcn != 0;
|
|
} else {
|
|
--regs.bkrep_stack[regs.bcn - 1].lc;
|
|
regs.pc = regs.bkrep_stack[regs.bcn - 1].start;
|
|
}
|
|
}
|
|
|
|
decoder.call(*this, opcode, expand_value);
|
|
|
|
// I am not sure if a single-instruction loop is interruptable and how it is handled,
|
|
// so just disable interrupt for it for now.
|
|
if (regs.ie && !regs.rep) {
|
|
bool interrupt_handled = false;
|
|
for (u32 i = 0; i < regs.im.size(); ++i) {
|
|
if (regs.im[i] && regs.ip[i]) {
|
|
regs.ip[i] = 0;
|
|
regs.ie = 0;
|
|
PushPC();
|
|
regs.pc = 0x0006 + i * 8;
|
|
idle = false;
|
|
interrupt_handled = true;
|
|
if (regs.ic[i]) {
|
|
ContextStore();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (!interrupt_handled && regs.imv && regs.ipv) {
|
|
regs.ipv = 0;
|
|
regs.ie = 0;
|
|
PushPC();
|
|
regs.pc = vinterrupt_address;
|
|
idle = false;
|
|
if (vinterrupt_context_switch) {
|
|
ContextStore();
|
|
}
|
|
}
|
|
}
|
|
|
|
core_timing.Tick();
|
|
}
|
|
}
|
|
|
|
void SignalInterrupt(u32 i) {
|
|
interrupt_pending[i] = true;
|
|
}
|
|
void SignalVectoredInterrupt(u32 address, bool context_switch) {
|
|
vinterrupt_address = address;
|
|
vinterrupt_pending = true;
|
|
vinterrupt_context_switch = context_switch;
|
|
}
|
|
|
|
using instruction_return_type = void;
|
|
|
|
void nop() {
|
|
// literally nothing
|
|
}
|
|
|
|
void norm(Ax a, Rn b, StepZIDS bs) {
|
|
if (regs.fn == 0) {
|
|
u64 value = GetAcc(a.GetName());
|
|
regs.fv = value != SignExtend<39>(value);
|
|
if (regs.fv) {
|
|
regs.fvl = 1;
|
|
}
|
|
value <<= 1;
|
|
regs.fc0 = (value & ((u64)1 << 40)) != 0;
|
|
value = SignExtend<40>(value);
|
|
SetAccAndFlag(a.GetName(), value);
|
|
u32 unit = b.Index();
|
|
RnAndModify(unit, bs.GetName());
|
|
regs.fr = regs.r[unit] == 0;
|
|
}
|
|
}
|
|
void swap(SwapType swap) {
|
|
RegName s0, d0, s1, d1;
|
|
u64 u, v;
|
|
switch (swap.GetName()) {
|
|
case SwapTypeValue::a0b0:
|
|
s0 = d1 = RegName::a0;
|
|
s1 = d0 = RegName::b0;
|
|
break;
|
|
case SwapTypeValue::a0b1:
|
|
s0 = d1 = RegName::a0;
|
|
s1 = d0 = RegName::b1;
|
|
break;
|
|
case SwapTypeValue::a1b0:
|
|
s0 = d1 = RegName::a1;
|
|
s1 = d0 = RegName::b0;
|
|
break;
|
|
case SwapTypeValue::a1b1:
|
|
s0 = d1 = RegName::a1;
|
|
s1 = d0 = RegName::b1;
|
|
break;
|
|
case SwapTypeValue::a0b0a1b1:
|
|
u = GetAcc(RegName::a1);
|
|
v = GetAcc(RegName::b1);
|
|
SatAndSetAccAndFlag(RegName::a1, v);
|
|
SatAndSetAccAndFlag(RegName::b1, u);
|
|
s0 = d1 = RegName::a0;
|
|
s1 = d0 = RegName::b0;
|
|
break;
|
|
case SwapTypeValue::a0b1a1b0:
|
|
u = GetAcc(RegName::a1);
|
|
v = GetAcc(RegName::b0);
|
|
SatAndSetAccAndFlag(RegName::a1, v);
|
|
SatAndSetAccAndFlag(RegName::b0, u);
|
|
s0 = d1 = RegName::a0;
|
|
s1 = d0 = RegName::b1;
|
|
break;
|
|
case SwapTypeValue::a0b0a1:
|
|
s0 = RegName::a0;
|
|
d0 = s1 = RegName::b0;
|
|
d1 = RegName::a1;
|
|
break;
|
|
case SwapTypeValue::a0b1a1:
|
|
s0 = RegName::a0;
|
|
d0 = s1 = RegName::b1;
|
|
d1 = RegName::a1;
|
|
break;
|
|
case SwapTypeValue::a1b0a0:
|
|
s0 = RegName::a1;
|
|
d0 = s1 = RegName::b0;
|
|
d1 = RegName::a0;
|
|
break;
|
|
case SwapTypeValue::a1b1a0:
|
|
s0 = RegName::a1;
|
|
d0 = s1 = RegName::b1;
|
|
d1 = RegName::a0;
|
|
break;
|
|
case SwapTypeValue::b0a0b1:
|
|
s0 = d1 = RegName::a0;
|
|
d0 = RegName::b1;
|
|
s1 = RegName::b0;
|
|
break;
|
|
case SwapTypeValue::b0a1b1:
|
|
s0 = d1 = RegName::a1;
|
|
d0 = RegName::b1;
|
|
s1 = RegName::b0;
|
|
break;
|
|
case SwapTypeValue::b1a0b0:
|
|
s0 = d1 = RegName::a0;
|
|
d0 = RegName::b0;
|
|
s1 = RegName::b1;
|
|
break;
|
|
case SwapTypeValue::b1a1b0:
|
|
s0 = d1 = RegName::a1;
|
|
d0 = RegName::b0;
|
|
s1 = RegName::b1;
|
|
break;
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
u = GetAcc(s0);
|
|
v = GetAcc(s1);
|
|
SatAndSetAccAndFlag(d0, u);
|
|
SatAndSetAccAndFlag(d1, v); // only this one affects flags (except for fl)
|
|
}
|
|
void trap() {
|
|
throw UnimplementedException();
|
|
}
|
|
|
|
void DoMultiplication(u32 unit, bool x_sign, bool y_sign) {
|
|
u32 x = regs.x[unit];
|
|
u32 y = regs.y[unit];
|
|
if (regs.hwm == 1 || (regs.hwm == 3 && unit == 0)) {
|
|
y >>= 8;
|
|
} else if (regs.hwm == 2 || (regs.hwm == 3 && unit == 1)) {
|
|
y &= 0xFF;
|
|
}
|
|
if (x_sign)
|
|
x = SignExtend<16>(x);
|
|
if (y_sign)
|
|
y = SignExtend<16>(y);
|
|
regs.p[unit] = x * y;
|
|
if (x_sign || y_sign)
|
|
regs.pe[unit] = regs.p[unit] >> 31;
|
|
else
|
|
regs.pe[unit] = 0;
|
|
}
|
|
|
|
u64 AddSub(u64 a, u64 b, bool sub) {
|
|
a &= 0xFF'FFFF'FFFF;
|
|
b &= 0xFF'FFFF'FFFF;
|
|
u64 result = sub ? a - b : a + b;
|
|
regs.fc0 = (result >> 40) & 1;
|
|
if (sub)
|
|
b = ~b;
|
|
regs.fv = ((~(a ^ b) & (a ^ result)) >> 39) & 1;
|
|
if (regs.fv) {
|
|
regs.fvl = 1;
|
|
}
|
|
return SignExtend<40>(result);
|
|
}
|
|
|
|
void ProductSum(SumBase base, RegName acc, bool sub_p0, bool p0_align, bool sub_p1,
|
|
bool p1_align) {
|
|
u64 value_a = ProductToBus40(Px{0});
|
|
u64 value_b = ProductToBus40(Px{1});
|
|
if (p0_align) {
|
|
value_a = SignExtend<24>(value_a >> 16);
|
|
}
|
|
if (p1_align) {
|
|
value_b = SignExtend<24>(value_b >> 16);
|
|
}
|
|
u64 value_c;
|
|
switch (base) {
|
|
case SumBase::Zero:
|
|
value_c = 0;
|
|
break;
|
|
case SumBase::Acc:
|
|
value_c = GetAcc(acc);
|
|
break;
|
|
case SumBase::Sv:
|
|
value_c = SignExtend<32, u64>((u64)regs.sv << 16);
|
|
break;
|
|
case SumBase::SvRnd:
|
|
value_c = SignExtend<32, u64>((u64)regs.sv << 16) | 0x8000;
|
|
break;
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
u64 result = AddSub(value_c, value_a, sub_p0);
|
|
u16 temp_c = regs.fc0;
|
|
u16 temp_v = regs.fv;
|
|
result = AddSub(result, value_b, sub_p1);
|
|
// Is this correct?
|
|
if (sub_p0 == sub_p1) {
|
|
regs.fc0 |= temp_c;
|
|
regs.fv |= temp_v;
|
|
} else {
|
|
regs.fc0 ^= temp_c;
|
|
regs.fv ^= temp_v;
|
|
}
|
|
SatAndSetAccAndFlag(acc, result);
|
|
}
|
|
|
|
void AlmGeneric(AlmOp op, u64 a, Ax b) {
|
|
switch (op) {
|
|
case AlmOp::Or: {
|
|
u64 value = GetAcc(b.GetName());
|
|
value |= a;
|
|
value = SignExtend<40>(value);
|
|
SetAccAndFlag(b.GetName(), value);
|
|
break;
|
|
}
|
|
case AlmOp::And: {
|
|
u64 value = GetAcc(b.GetName());
|
|
value &= a;
|
|
value = SignExtend<40>(value);
|
|
SetAccAndFlag(b.GetName(), value);
|
|
break;
|
|
}
|
|
case AlmOp::Xor: {
|
|
u64 value = GetAcc(b.GetName());
|
|
value ^= a;
|
|
value = SignExtend<40>(value);
|
|
SetAccAndFlag(b.GetName(), value);
|
|
break;
|
|
}
|
|
case AlmOp::Tst0: {
|
|
u64 value = GetAcc(b.GetName()) & 0xFFFF;
|
|
regs.fz = (value & a) == 0;
|
|
break;
|
|
}
|
|
case AlmOp::Tst1: {
|
|
u64 value = GetAcc(b.GetName()) & 0xFFFF;
|
|
regs.fz = (value & ~a) == 0;
|
|
break;
|
|
}
|
|
case AlmOp::Cmp:
|
|
case AlmOp::Cmpu:
|
|
case AlmOp::Sub:
|
|
case AlmOp::Subl:
|
|
case AlmOp::Subh:
|
|
case AlmOp::Add:
|
|
case AlmOp::Addl:
|
|
case AlmOp::Addh: {
|
|
u64 value = GetAcc(b.GetName());
|
|
bool sub = !(op == AlmOp::Add || op == AlmOp::Addl || op == AlmOp::Addh);
|
|
u64 result = AddSub(value, a, sub);
|
|
if (op == AlmOp::Cmp || op == AlmOp::Cmpu) {
|
|
SetAccFlag(result);
|
|
} else {
|
|
SatAndSetAccAndFlag(b.GetName(), result);
|
|
}
|
|
break;
|
|
}
|
|
case AlmOp::Msu: {
|
|
u64 value = GetAcc(b.GetName());
|
|
u64 product = ProductToBus40(Px{0});
|
|
u64 result = AddSub(value, product, true);
|
|
SatAndSetAccAndFlag(b.GetName(), result);
|
|
|
|
regs.x[0] = a & 0xFFFF;
|
|
DoMultiplication(0, true, true);
|
|
break;
|
|
}
|
|
case AlmOp::Sqra: {
|
|
u64 value = GetAcc(b.GetName());
|
|
u64 product = ProductToBus40(Px{0});
|
|
u64 result = AddSub(value, product, false);
|
|
SatAndSetAccAndFlag(b.GetName(), result);
|
|
}
|
|
[[fallthrough]];
|
|
case AlmOp::Sqr: {
|
|
regs.y[0] = regs.x[0] = a & 0xFFFF;
|
|
DoMultiplication(0, true, true);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
|
|
u64 ExtendOperandForAlm(AlmOp op, u16 a) {
|
|
switch (op) {
|
|
case AlmOp::Cmp:
|
|
case AlmOp::Sub:
|
|
case AlmOp::Add:
|
|
return SignExtend<16, u64>(a);
|
|
case AlmOp::Addh:
|
|
case AlmOp::Subh:
|
|
return SignExtend<32, u64>((u64)a << 16);
|
|
default:
|
|
return a;
|
|
}
|
|
}
|
|
|
|
void alm(Alm op, MemImm8 a, Ax b) {
|
|
u16 value = LoadFromMemory(a);
|
|
AlmGeneric(op.GetName(), ExtendOperandForAlm(op.GetName(), value), b);
|
|
}
|
|
void alm(Alm op, Rn a, StepZIDS as, Ax b) {
|
|
u16 address = RnAddressAndModify(a.Index(), as.GetName());
|
|
u16 value = mem.DataRead(address);
|
|
AlmGeneric(op.GetName(), ExtendOperandForAlm(op.GetName(), value), b);
|
|
}
|
|
void alm(Alm op, Register a, Ax b) {
|
|
u64 value;
|
|
auto CheckBus40OperandAllowed = [op] {
|
|
static const std::unordered_set<AlmOp> allowed_instruction{
|
|
AlmOp::Or, AlmOp::And, AlmOp::Xor, AlmOp::Add, AlmOp::Cmp, AlmOp::Sub,
|
|
};
|
|
if (allowed_instruction.count(op.GetName()) == 0)
|
|
throw UnimplementedException(); // weird effect. probably undefined
|
|
};
|
|
switch (a.GetName()) {
|
|
// need more test
|
|
case RegName::p:
|
|
CheckBus40OperandAllowed();
|
|
value = ProductToBus40(Px{0});
|
|
break;
|
|
case RegName::a0:
|
|
case RegName::a1:
|
|
CheckBus40OperandAllowed();
|
|
value = GetAcc(a.GetName());
|
|
break;
|
|
default:
|
|
value = ExtendOperandForAlm(op.GetName(), RegToBus16(a.GetName()));
|
|
break;
|
|
}
|
|
AlmGeneric(op.GetName(), value, b);
|
|
}
|
|
void alm_r6(Alm op, Ax b) {
|
|
u16 value = regs.r[6];
|
|
AlmGeneric(op.GetName(), ExtendOperandForAlm(op.GetName(), value), b);
|
|
}
|
|
|
|
void alu(Alu op, MemImm16 a, Ax b) {
|
|
u16 value = LoadFromMemory(a);
|
|
AlmGeneric(op.GetName(), ExtendOperandForAlm(op.GetName(), value), b);
|
|
}
|
|
void alu(Alu op, MemR7Imm16 a, Ax b) {
|
|
u16 value = LoadFromMemory(a);
|
|
AlmGeneric(op.GetName(), ExtendOperandForAlm(op.GetName(), value), b);
|
|
}
|
|
void alu(Alu op, Imm16 a, Ax b) {
|
|
u16 value = a.Unsigned16();
|
|
AlmGeneric(op.GetName(), ExtendOperandForAlm(op.GetName(), value), b);
|
|
}
|
|
void alu(Alu op, Imm8 a, Ax b) {
|
|
u16 value = a.Unsigned16();
|
|
u64 and_backup = 0;
|
|
if (op.GetName() == AlmOp::And) {
|
|
// AND instruction has a special treatment:
|
|
// bit 8~15 are unaffected in the accumulator, but the flags are set as if they are
|
|
// affected
|
|
and_backup = GetAcc(b.GetName()) & 0xFF00;
|
|
}
|
|
AlmGeneric(op.GetName(), ExtendOperandForAlm(op.GetName(), value), b);
|
|
if (op.GetName() == AlmOp::And) {
|
|
u64 and_new = GetAcc(b.GetName()) & 0xFFFF'FFFF'FFFF'00FF;
|
|
SetAcc(b.GetName(), and_backup | and_new);
|
|
}
|
|
}
|
|
void alu(Alu op, MemR7Imm7s a, Ax b) {
|
|
u16 value = LoadFromMemory(a);
|
|
AlmGeneric(op.GetName(), ExtendOperandForAlm(op.GetName(), value), b);
|
|
}
|
|
|
|
void or_(Ab a, Ax b, Ax c) {
|
|
u64 value = GetAcc(a.GetName()) | GetAcc(b.GetName());
|
|
SetAccAndFlag(c.GetName(), value);
|
|
}
|
|
void or_(Ax a, Bx b, Ax c) {
|
|
u64 value = GetAcc(a.GetName()) | GetAcc(b.GetName());
|
|
SetAccAndFlag(c.GetName(), value);
|
|
}
|
|
void or_(Bx a, Bx b, Ax c) {
|
|
u64 value = GetAcc(a.GetName()) | GetAcc(b.GetName());
|
|
SetAccAndFlag(c.GetName(), value);
|
|
}
|
|
|
|
u16 GenericAlb(Alb op, u16 a, u16 b) {
|
|
u16 result;
|
|
switch (op.GetName()) {
|
|
case AlbOp::Set: {
|
|
result = a | b;
|
|
regs.fm = result >> 15;
|
|
break;
|
|
}
|
|
case AlbOp::Rst: {
|
|
result = ~a & b;
|
|
regs.fm = result >> 15;
|
|
break;
|
|
}
|
|
case AlbOp::Chng: {
|
|
result = a ^ b;
|
|
regs.fm = result >> 15;
|
|
break;
|
|
}
|
|
case AlbOp::Addv: {
|
|
u32 r = a + b;
|
|
regs.fc0 = (r >> 16) != 0;
|
|
regs.fm = (SignExtend<16, u32>(b) + SignExtend<16, u32>(a)) >> 31; // !
|
|
result = r & 0xFFFF;
|
|
break;
|
|
}
|
|
case AlbOp::Tst0: {
|
|
result = (a & b) != 0;
|
|
break;
|
|
}
|
|
case AlbOp::Tst1: {
|
|
result = (a & ~b) != 0;
|
|
break;
|
|
}
|
|
case AlbOp::Cmpv:
|
|
case AlbOp::Subv: {
|
|
u32 r = b - a;
|
|
regs.fc0 = (r >> 16) != 0;
|
|
regs.fm = (SignExtend<16, u32>(b) - SignExtend<16, u32>(a)) >> 31; // !
|
|
result = r & 0xFFFF;
|
|
break;
|
|
}
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
regs.fz = result == 0;
|
|
return result;
|
|
}
|
|
|
|
static bool IsAlbModifying(Alb op) {
|
|
switch (op.GetName()) {
|
|
case AlbOp::Set:
|
|
case AlbOp::Rst:
|
|
case AlbOp::Chng:
|
|
case AlbOp::Addv:
|
|
case AlbOp::Subv:
|
|
return true;
|
|
case AlbOp::Tst0:
|
|
case AlbOp::Tst1:
|
|
case AlbOp::Cmpv:
|
|
return false;
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
|
|
void alb(Alb op, Imm16 a, MemImm8 b) {
|
|
u16 bv = LoadFromMemory(b);
|
|
u16 result = GenericAlb(op, a.Unsigned16(), bv);
|
|
if (IsAlbModifying(op))
|
|
StoreToMemory(b, result);
|
|
}
|
|
void alb(Alb op, Imm16 a, Rn b, StepZIDS bs) {
|
|
u16 address = RnAddressAndModify(b.Index(), bs.GetName());
|
|
u16 bv = mem.DataRead(address);
|
|
u16 result = GenericAlb(op, a.Unsigned16(), bv);
|
|
if (IsAlbModifying(op))
|
|
mem.DataWrite(address, result);
|
|
}
|
|
void alb(Alb op, Imm16 a, Register b) {
|
|
u16 bv;
|
|
if (b.GetName() == RegName::p) {
|
|
bv = (u16)(ProductToBus40(Px{0}) >> 16);
|
|
} else if (b.GetName() == RegName::a0 || b.GetName() == RegName::a1) {
|
|
throw UnimplementedException(); // weird effect;
|
|
} else {
|
|
bv = RegToBus16(b.GetName());
|
|
}
|
|
u16 result = GenericAlb(op, a.Unsigned16(), bv);
|
|
if (IsAlbModifying(op)) {
|
|
switch (b.GetName()) {
|
|
case RegName::a0:
|
|
case RegName::a1:
|
|
UNREACHABLE();
|
|
// operation on accumulators doesn't go through regular bus with flag and saturation
|
|
case RegName::a0l:
|
|
regs.a[0] = (regs.a[0] & 0xFFFF'FFFF'FFFF'0000) | result;
|
|
break;
|
|
case RegName::a1l:
|
|
regs.a[1] = (regs.a[1] & 0xFFFF'FFFF'FFFF'0000) | result;
|
|
break;
|
|
case RegName::b0l:
|
|
regs.b[0] = (regs.b[0] & 0xFFFF'FFFF'FFFF'0000) | result;
|
|
break;
|
|
case RegName::b1l:
|
|
regs.b[1] = (regs.b[1] & 0xFFFF'FFFF'FFFF'0000) | result;
|
|
break;
|
|
case RegName::a0h:
|
|
regs.a[0] = (regs.a[0] & 0xFFFF'FFFF'0000'FFFF) | ((u64)result << 16);
|
|
break;
|
|
case RegName::a1h:
|
|
regs.a[1] = (regs.a[1] & 0xFFFF'FFFF'0000'FFFF) | ((u64)result << 16);
|
|
break;
|
|
case RegName::b0h:
|
|
regs.b[0] = (regs.b[0] & 0xFFFF'FFFF'0000'FFFF) | ((u64)result << 16);
|
|
break;
|
|
case RegName::b1h:
|
|
regs.b[1] = (regs.b[1] & 0xFFFF'FFFF'0000'FFFF) | ((u64)result << 16);
|
|
break;
|
|
default:
|
|
RegFromBus16(b.GetName(), result); // including RegName:p (p0h)
|
|
}
|
|
}
|
|
}
|
|
void alb_r6(Alb op, Imm16 a) {
|
|
u16 bv = regs.r[6];
|
|
u16 result = GenericAlb(op, a.Unsigned16(), bv);
|
|
if (IsAlbModifying(op))
|
|
regs.r[6] = result;
|
|
}
|
|
void alb(Alb op, Imm16 a, SttMod b) {
|
|
u16 bv = RegToBus16(b.GetName());
|
|
u16 result = GenericAlb(op, a.Unsigned16(), bv);
|
|
if (IsAlbModifying(op))
|
|
RegFromBus16(b.GetName(), result);
|
|
}
|
|
|
|
void add(Ab a, Bx b) {
|
|
u64 value_a = GetAcc(a.GetName());
|
|
u64 value_b = GetAcc(b.GetName());
|
|
u64 result = AddSub(value_b, value_a, false);
|
|
SatAndSetAccAndFlag(b.GetName(), result);
|
|
}
|
|
void add(Bx a, Ax b) {
|
|
u64 value_a = GetAcc(a.GetName());
|
|
u64 value_b = GetAcc(b.GetName());
|
|
u64 result = AddSub(value_b, value_a, false);
|
|
SatAndSetAccAndFlag(b.GetName(), result);
|
|
}
|
|
void add_p1(Ax b) {
|
|
u64 value_a = ProductToBus40(Px{1});
|
|
u64 value_b = GetAcc(b.GetName());
|
|
u64 result = AddSub(value_b, value_a, false);
|
|
SatAndSetAccAndFlag(b.GetName(), result);
|
|
}
|
|
void add(Px a, Bx b) {
|
|
u64 value_a = ProductToBus40(a);
|
|
u64 value_b = GetAcc(b.GetName());
|
|
u64 result = AddSub(value_b, value_a, false);
|
|
SatAndSetAccAndFlag(b.GetName(), result);
|
|
}
|
|
|
|
void sub(Ab a, Bx b) {
|
|
u64 value_a = GetAcc(a.GetName());
|
|
u64 value_b = GetAcc(b.GetName());
|
|
u64 result = AddSub(value_b, value_a, true);
|
|
SatAndSetAccAndFlag(b.GetName(), result);
|
|
}
|
|
void sub(Bx a, Ax b) {
|
|
u64 value_a = GetAcc(a.GetName());
|
|
u64 value_b = GetAcc(b.GetName());
|
|
u64 result = AddSub(value_b, value_a, true);
|
|
SatAndSetAccAndFlag(b.GetName(), result);
|
|
}
|
|
void sub_p1(Ax b) {
|
|
u64 value_a = ProductToBus40(Px{1});
|
|
u64 value_b = GetAcc(b.GetName());
|
|
u64 result = AddSub(value_b, value_a, true);
|
|
SatAndSetAccAndFlag(b.GetName(), result);
|
|
}
|
|
void sub(Px a, Bx b) {
|
|
u64 value_a = ProductToBus40(a);
|
|
u64 value_b = GetAcc(b.GetName());
|
|
u64 result = AddSub(value_b, value_a, true);
|
|
SatAndSetAccAndFlag(b.GetName(), result);
|
|
}
|
|
|
|
void app(Ab c, SumBase base, bool sub_p0, bool p0_align, bool sub_p1, bool p1_align) {
|
|
ProductSum(base, c.GetName(), sub_p0, p0_align, sub_p1, p1_align);
|
|
}
|
|
|
|
void add_add(ArpRn1 a, ArpStep1 asi, ArpStep1 asj, Ab b) {
|
|
auto [ui, uj] = GetArpRnUnit(a);
|
|
auto [si, sj] = GetArpStep(asi, asj);
|
|
auto [oi, oj] = GetArpOffset(asi, asj);
|
|
u16 i = RnAddressAndModify(ui, si);
|
|
u16 j = RnAddressAndModify(uj, sj);
|
|
u64 high = SignExtend<16, u64>(mem.DataRead(j)) + SignExtend<16, u64>(mem.DataRead(i));
|
|
u16 low = mem.DataRead(OffsetAddress(uj, j, oj)) + mem.DataRead(OffsetAddress(ui, i, oi));
|
|
u64 result = (high << 16) | low;
|
|
SetAcc(b.GetName(), result);
|
|
}
|
|
void add_sub(ArpRn1 a, ArpStep1 asi, ArpStep1 asj, Ab b) {
|
|
auto [ui, uj] = GetArpRnUnit(a);
|
|
auto [si, sj] = GetArpStep(asi, asj);
|
|
auto [oi, oj] = GetArpOffset(asi, asj);
|
|
u16 i = RnAddressAndModify(ui, si);
|
|
u16 j = RnAddressAndModify(uj, sj);
|
|
u64 high = SignExtend<16, u64>(mem.DataRead(j)) + SignExtend<16, u64>(mem.DataRead(i));
|
|
u16 low = mem.DataRead(OffsetAddress(uj, j, oj)) - mem.DataRead(OffsetAddress(ui, i, oi));
|
|
u64 result = (high << 16) | low;
|
|
SetAcc(b.GetName(), result);
|
|
}
|
|
void sub_add(ArpRn1 a, ArpStep1 asi, ArpStep1 asj, Ab b) {
|
|
auto [ui, uj] = GetArpRnUnit(a);
|
|
auto [si, sj] = GetArpStep(asi, asj);
|
|
auto [oi, oj] = GetArpOffset(asi, asj);
|
|
u16 i = RnAddressAndModify(ui, si);
|
|
u16 j = RnAddressAndModify(uj, sj);
|
|
u64 high = SignExtend<16, u64>(mem.DataRead(j)) - SignExtend<16, u64>(mem.DataRead(i));
|
|
u16 low = mem.DataRead(OffsetAddress(uj, j, oj)) + mem.DataRead(OffsetAddress(ui, i, oi));
|
|
u64 result = (high << 16) | low;
|
|
SetAcc(b.GetName(), result);
|
|
}
|
|
void sub_sub(ArpRn1 a, ArpStep1 asi, ArpStep1 asj, Ab b) {
|
|
auto [ui, uj] = GetArpRnUnit(a);
|
|
auto [si, sj] = GetArpStep(asi, asj);
|
|
auto [oi, oj] = GetArpOffset(asi, asj);
|
|
u16 i = RnAddressAndModify(ui, si);
|
|
u16 j = RnAddressAndModify(uj, sj);
|
|
u64 high = SignExtend<16, u64>(mem.DataRead(j)) - SignExtend<16, u64>(mem.DataRead(i));
|
|
u16 low = mem.DataRead(OffsetAddress(uj, j, oj)) - mem.DataRead(OffsetAddress(ui, i, oi));
|
|
u64 result = (high << 16) | low;
|
|
SetAcc(b.GetName(), result);
|
|
}
|
|
void add_sub_sv(ArRn1 a, ArStep1 as, Ab b) {
|
|
u16 u = GetArRnUnit(a);
|
|
auto s = GetArStep(as);
|
|
auto o = GetArOffset(as);
|
|
u16 address = RnAddressAndModify(u, s);
|
|
u64 high = SignExtend<16, u64>(mem.DataRead(address)) + SignExtend<16, u64>(regs.sv);
|
|
u16 low = mem.DataRead(OffsetAddress(u, address, o)) - regs.sv;
|
|
u64 result = (high << 16) | low;
|
|
SetAcc(b.GetName(), result);
|
|
}
|
|
void sub_add_sv(ArRn1 a, ArStep1 as, Ab b) {
|
|
u16 u = GetArRnUnit(a);
|
|
auto s = GetArStep(as);
|
|
auto o = GetArOffset(as);
|
|
u16 address = RnAddressAndModify(u, s);
|
|
u64 high = SignExtend<16, u64>(mem.DataRead(address)) - SignExtend<16, u64>(regs.sv);
|
|
u16 low = mem.DataRead(OffsetAddress(u, address, o)) + regs.sv;
|
|
u64 result = (high << 16) | low;
|
|
SetAcc(b.GetName(), result);
|
|
}
|
|
void sub_add_i_mov_j_sv(ArpRn1 a, ArpStep1 asi, ArpStep1 asj, Ab b) {
|
|
auto [ui, uj] = GetArpRnUnit(a);
|
|
auto [si, sj] = GetArpStep(asi, asj);
|
|
OffsetValue oi;
|
|
std::tie(oi, std::ignore) = GetArpOffset(asi, asj);
|
|
u16 i = RnAddressAndModify(ui, si);
|
|
u16 j = RnAddressAndModify(uj, sj);
|
|
u64 high = SignExtend<16, u64>(mem.DataRead(i)) - SignExtend<16, u64>(regs.sv);
|
|
u16 low = mem.DataRead(OffsetAddress(ui, i, oi)) + regs.sv;
|
|
u64 result = (high << 16) | low;
|
|
SetAcc(b.GetName(), result);
|
|
regs.sv = mem.DataRead(j);
|
|
}
|
|
void sub_add_j_mov_i_sv(ArpRn1 a, ArpStep1 asi, ArpStep1 asj, Ab b) {
|
|
auto [ui, uj] = GetArpRnUnit(a);
|
|
auto [si, sj] = GetArpStep(asi, asj);
|
|
OffsetValue oj;
|
|
std::tie(std::ignore, oj) = GetArpOffset(asi, asj);
|
|
u16 i = RnAddressAndModify(ui, si);
|
|
u16 j = RnAddressAndModify(uj, sj);
|
|
u64 high = SignExtend<16, u64>(mem.DataRead(j)) - SignExtend<16, u64>(regs.sv);
|
|
u16 low = mem.DataRead(OffsetAddress(uj, j, oj)) + regs.sv;
|
|
u64 result = (high << 16) | low;
|
|
SetAcc(b.GetName(), result);
|
|
regs.sv = mem.DataRead(i);
|
|
}
|
|
void add_sub_i_mov_j(ArpRn1 a, ArpStep1 asi, ArpStep1 asj, Ab b) {
|
|
auto [ui, uj] = GetArpRnUnit(a);
|
|
auto [si, sj] = GetArpStep(asi, asj);
|
|
OffsetValue oi;
|
|
std::tie(oi, std::ignore) = GetArpOffset(asi, asj);
|
|
u16 i = RnAddressAndModify(ui, si);
|
|
u16 j = RnAddressAndModify(uj, sj);
|
|
u64 high = SignExtend<16, u64>(mem.DataRead(i)) + SignExtend<16, u64>(regs.sv);
|
|
u16 low = mem.DataRead(OffsetAddress(ui, i, oi)) - regs.sv;
|
|
u64 result = (high << 16) | low;
|
|
u16 exchange = (u16)(GetAndSatAccNoFlag(b.GetName()) & 0xFFFF);
|
|
SetAcc(b.GetName(), result);
|
|
mem.DataWrite(j, exchange);
|
|
}
|
|
void add_sub_j_mov_i(ArpRn1 a, ArpStep1 asi, ArpStep1 asj, Ab b) {
|
|
auto [ui, uj] = GetArpRnUnit(a);
|
|
auto [si, sj] = GetArpStep(asi, asj);
|
|
OffsetValue oj;
|
|
std::tie(std::ignore, oj) = GetArpOffset(asi, asj);
|
|
u16 i = RnAddressAndModify(ui, si);
|
|
u16 j = RnAddressAndModify(uj, sj);
|
|
u64 high = SignExtend<16, u64>(mem.DataRead(j)) + SignExtend<16, u64>(regs.sv);
|
|
u16 low = mem.DataRead(OffsetAddress(uj, j, oj)) - regs.sv;
|
|
u64 result = (high << 16) | low;
|
|
u16 exchange = (u16)(GetAndSatAccNoFlag(b.GetName()) & 0xFFFF);
|
|
SetAcc(b.GetName(), result);
|
|
mem.DataWrite(i, exchange);
|
|
}
|
|
|
|
void Moda(ModaOp op, RegName a, Cond cond) {
|
|
if (regs.ConditionPass(cond)) {
|
|
switch (op) {
|
|
case ModaOp::Shr: {
|
|
ShiftBus40(GetAcc(a), 0xFFFF, a);
|
|
break;
|
|
}
|
|
case ModaOp::Shr4: {
|
|
ShiftBus40(GetAcc(a), 0xFFFC, a);
|
|
break;
|
|
}
|
|
case ModaOp::Shl: {
|
|
ShiftBus40(GetAcc(a), 1, a);
|
|
break;
|
|
}
|
|
case ModaOp::Shl4: {
|
|
ShiftBus40(GetAcc(a), 4, a);
|
|
break;
|
|
}
|
|
case ModaOp::Ror: {
|
|
u64 value = GetAcc(a) & 0xFF'FFFF'FFFF;
|
|
u16 old_fc = regs.fc0;
|
|
regs.fc0 = value & 1;
|
|
value >>= 1;
|
|
value |= (u64)old_fc << 39;
|
|
value = SignExtend<40>(value);
|
|
SetAccAndFlag(a, value);
|
|
break;
|
|
}
|
|
case ModaOp::Rol: {
|
|
u64 value = GetAcc(a);
|
|
u16 old_fc = regs.fc0;
|
|
regs.fc0 = (value >> 39) & 1;
|
|
value <<= 1;
|
|
value |= old_fc;
|
|
value = SignExtend<40>(value);
|
|
SetAccAndFlag(a, value);
|
|
break;
|
|
}
|
|
case ModaOp::Clr: {
|
|
SatAndSetAccAndFlag(a, 0);
|
|
break;
|
|
}
|
|
case ModaOp::Not: {
|
|
u64 result = ~GetAcc(a);
|
|
SetAccAndFlag(a, result);
|
|
break;
|
|
}
|
|
case ModaOp::Neg: {
|
|
u64 value = GetAcc(a);
|
|
regs.fc0 = value != 0; // ?
|
|
regs.fv = value == 0xFFFF'FF80'0000'0000; // ?
|
|
if (regs.fv)
|
|
regs.fvl = 1;
|
|
u64 result = SignExtend<40, u64>(~GetAcc(a) + 1);
|
|
SatAndSetAccAndFlag(a, result);
|
|
break;
|
|
}
|
|
case ModaOp::Rnd: {
|
|
u64 value = GetAcc(a);
|
|
u64 result = AddSub(value, 0x8000, false);
|
|
SatAndSetAccAndFlag(a, result);
|
|
break;
|
|
}
|
|
case ModaOp::Pacr: {
|
|
u64 value = ProductToBus40(Px{0});
|
|
u64 result = AddSub(value, 0x8000, false);
|
|
SatAndSetAccAndFlag(a, result);
|
|
break;
|
|
}
|
|
case ModaOp::Clrr: {
|
|
SatAndSetAccAndFlag(a, 0x8000);
|
|
break;
|
|
}
|
|
case ModaOp::Inc: {
|
|
u64 value = GetAcc(a);
|
|
u64 result = AddSub(value, 1, false);
|
|
SatAndSetAccAndFlag(a, result);
|
|
break;
|
|
}
|
|
case ModaOp::Dec: {
|
|
u64 value = GetAcc(a);
|
|
u64 result = AddSub(value, 1, true);
|
|
SatAndSetAccAndFlag(a, result);
|
|
break;
|
|
}
|
|
case ModaOp::Copy: {
|
|
// note: bX doesn't support
|
|
u64 value = GetAcc(a == RegName::a0 ? RegName::a1 : RegName::a0);
|
|
SatAndSetAccAndFlag(a, value);
|
|
break;
|
|
}
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
}
|
|
|
|
void moda4(Moda4 op, Ax a, Cond cond) {
|
|
Moda(op.GetName(), a.GetName(), cond);
|
|
}
|
|
|
|
void moda3(Moda3 op, Bx a, Cond cond) {
|
|
Moda(op.GetName(), a.GetName(), cond);
|
|
}
|
|
|
|
void pacr1(Ax a) {
|
|
u64 value = ProductToBus40(Px{1});
|
|
u64 result = AddSub(value, 0x8000, false);
|
|
SatAndSetAccAndFlag(a.GetName(), result);
|
|
}
|
|
|
|
void FilterDoubleClr(RegName& a, RegName& b) {
|
|
if (a == RegName::b0) {
|
|
b = RegName::b1;
|
|
} else if (a == RegName::b1) {
|
|
b = RegName::b0;
|
|
} else if (a == RegName::a0) {
|
|
if (b == RegName::a0)
|
|
b = RegName::a1;
|
|
} else
|
|
b = b == RegName::b1 ? RegName::b1 : RegName::b0;
|
|
}
|
|
|
|
void clr(Ab a, Ab b) {
|
|
RegName a_name = a.GetName();
|
|
RegName b_name = b.GetName();
|
|
FilterDoubleClr(a_name, b_name);
|
|
SatAndSetAccAndFlag(a_name, 0);
|
|
SatAndSetAccAndFlag(b_name, 0);
|
|
}
|
|
void clrr(Ab a, Ab b) {
|
|
RegName a_name = a.GetName();
|
|
RegName b_name = b.GetName();
|
|
FilterDoubleClr(a_name, b_name);
|
|
SatAndSetAccAndFlag(a_name, 0x8000);
|
|
SatAndSetAccAndFlag(b_name, 0x8000);
|
|
}
|
|
|
|
void BlockRepeat(u16 lc, u32 address) {
|
|
ASSERT(regs.bcn <= 3);
|
|
regs.bkrep_stack[regs.bcn].start = regs.pc;
|
|
regs.bkrep_stack[regs.bcn].end = address;
|
|
regs.bkrep_stack[regs.bcn].lc = lc;
|
|
regs.lp = 1;
|
|
++regs.bcn;
|
|
}
|
|
|
|
void bkrep(Imm8 a, Address16 addr) {
|
|
u16 lc = a.Unsigned16();
|
|
u32 address = addr.Address32() | (regs.pc & 0x30000);
|
|
BlockRepeat(lc, address);
|
|
}
|
|
void bkrep(Register a, Address18_16 addr_low, Address18_2 addr_high) {
|
|
u16 lc = RegToBus16(a.GetName());
|
|
u32 address = Address32(addr_low, addr_high);
|
|
BlockRepeat(lc, address);
|
|
}
|
|
void bkrep_r6(Address18_16 addr_low, Address18_2 addr_high) {
|
|
u16 lc = regs.r[6];
|
|
u32 address = Address32(addr_low, addr_high);
|
|
BlockRepeat(lc, address);
|
|
}
|
|
|
|
void RestoreBlockRepeat(u16& address_reg) {
|
|
if (regs.lp) {
|
|
ASSERT(regs.bcn <= 3);
|
|
std::copy_backward(regs.bkrep_stack.begin(), regs.bkrep_stack.begin() + regs.bcn,
|
|
regs.bkrep_stack.begin() + regs.bcn + 1);
|
|
++regs.bcn;
|
|
}
|
|
u32 flag = mem.DataRead(address_reg++);
|
|
u16 valid = flag >> 15;
|
|
if (regs.lp) {
|
|
ASSERT(valid);
|
|
} else {
|
|
if (valid)
|
|
regs.lp = regs.bcn = 1;
|
|
}
|
|
regs.bkrep_stack[0].end = mem.DataRead(address_reg++) | (((flag >> 8) & 3) << 16);
|
|
regs.bkrep_stack[0].start = mem.DataRead(address_reg++) | ((flag & 3) << 16);
|
|
regs.bkrep_stack[0].lc = mem.DataRead(address_reg++);
|
|
}
|
|
void StoreBlockRepeat(u16& address_reg) {
|
|
mem.DataWrite(--address_reg, regs.bkrep_stack[0].lc);
|
|
mem.DataWrite(--address_reg, regs.bkrep_stack[0].start & 0xFFFF);
|
|
mem.DataWrite(--address_reg, regs.bkrep_stack[0].end & 0xFFFF);
|
|
u16 flag = regs.lp << 15;
|
|
flag |= regs.bkrep_stack[0].start >> 16;
|
|
flag |= (regs.bkrep_stack[0].end >> 16) << 8;
|
|
mem.DataWrite(--address_reg, flag);
|
|
if (regs.lp) {
|
|
std::copy(regs.bkrep_stack.begin() + 1, regs.bkrep_stack.begin() + regs.bcn,
|
|
regs.bkrep_stack.begin());
|
|
--regs.bcn;
|
|
if (regs.bcn == 0)
|
|
regs.lp = 0;
|
|
}
|
|
}
|
|
void bkreprst(ArRn2 a) {
|
|
RestoreBlockRepeat(regs.r[GetArRnUnit(a)]);
|
|
}
|
|
void bkreprst_memsp() {
|
|
RestoreBlockRepeat(regs.sp);
|
|
}
|
|
void bkrepsto(ArRn2 a) {
|
|
StoreBlockRepeat(regs.r[GetArRnUnit(a)]);
|
|
}
|
|
void bkrepsto_memsp() {
|
|
StoreBlockRepeat(regs.sp);
|
|
}
|
|
|
|
void banke(BankFlags flags) {
|
|
if (flags.Cfgi()) {
|
|
std::swap(regs.stepi, regs.stepib);
|
|
std::swap(regs.modi, regs.modib);
|
|
if (regs.stp16)
|
|
std::swap(regs.stepi0, regs.stepi0b);
|
|
}
|
|
if (flags.R4()) {
|
|
std::swap(regs.r[4], regs.r4b);
|
|
}
|
|
if (flags.R1()) {
|
|
std::swap(regs.r[1], regs.r1b);
|
|
}
|
|
if (flags.R0()) {
|
|
std::swap(regs.r[0], regs.r0b);
|
|
}
|
|
if (flags.R7()) {
|
|
std::swap(regs.r[7], regs.r7b);
|
|
}
|
|
if (flags.Cfgj()) {
|
|
std::swap(regs.stepj, regs.stepjb);
|
|
std::swap(regs.modj, regs.modjb);
|
|
if (regs.stp16)
|
|
std::swap(regs.stepj0, regs.stepj0b);
|
|
}
|
|
}
|
|
void bankr() {
|
|
regs.SwapAllArArp();
|
|
}
|
|
void bankr(Ar a) {
|
|
regs.SwapAr(a.Index());
|
|
}
|
|
void bankr(Ar a, Arp b) {
|
|
regs.SwapAr(a.Index());
|
|
regs.SwapArp(b.Index());
|
|
}
|
|
void bankr(Arp a) {
|
|
regs.SwapArp(a.Index());
|
|
}
|
|
|
|
void bitrev(Rn a) {
|
|
u32 unit = a.Index();
|
|
regs.r[unit] = BitReverse(regs.r[unit]);
|
|
}
|
|
void bitrev_dbrv(Rn a) {
|
|
u32 unit = a.Index();
|
|
regs.r[unit] = BitReverse(regs.r[unit]);
|
|
regs.br[unit] = 0;
|
|
}
|
|
void bitrev_ebrv(Rn a) {
|
|
u32 unit = a.Index();
|
|
regs.r[unit] = BitReverse(regs.r[unit]);
|
|
regs.br[unit] = 1;
|
|
}
|
|
|
|
void br(Address18_16 addr_low, Address18_2 addr_high, Cond cond) {
|
|
if (regs.ConditionPass(cond)) {
|
|
SetPC(Address32(addr_low, addr_high));
|
|
}
|
|
}
|
|
|
|
void brr(RelAddr7 addr, Cond cond) {
|
|
if (regs.ConditionPass(cond)) {
|
|
regs.pc += addr.Relative32(); // note: pc is the address of the NEXT instruction
|
|
if (addr.Relative32() == 0xFFFFFFFF) {
|
|
idle = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
void break_() {
|
|
ASSERT(regs.lp);
|
|
--regs.bcn;
|
|
regs.lp = regs.bcn != 0;
|
|
// Note: unlike one would expect, the "break" instruction doesn't jump out of the block
|
|
}
|
|
|
|
void call(Address18_16 addr_low, Address18_2 addr_high, Cond cond) {
|
|
if (regs.ConditionPass(cond)) {
|
|
PushPC();
|
|
SetPC(Address32(addr_low, addr_high));
|
|
}
|
|
}
|
|
void calla(Axl a) {
|
|
PushPC();
|
|
SetPC(RegToBus16(a.GetName())); // use pcmhi?
|
|
}
|
|
void calla(Ax a) {
|
|
PushPC();
|
|
SetPC(GetAcc(a.GetName()) & 0x3FFFF); // no saturation ?
|
|
}
|
|
void callr(RelAddr7 addr, Cond cond) {
|
|
if (regs.ConditionPass(cond)) {
|
|
PushPC();
|
|
regs.pc += addr.Relative32();
|
|
}
|
|
}
|
|
|
|
void ContextStore() {
|
|
regs.ShadowStore();
|
|
regs.ShadowSwap();
|
|
if (!regs.crep) {
|
|
regs.repcs = regs.repc;
|
|
}
|
|
if (!regs.ccnta) {
|
|
regs.a1s = regs.a[1];
|
|
regs.b1s = regs.b[1];
|
|
} else {
|
|
u64 a = regs.a[1];
|
|
u64 b = regs.b[1];
|
|
regs.b[1] = a;
|
|
SetAccAndFlag(RegName::a1, b); // Flag set on b1->a1
|
|
}
|
|
}
|
|
|
|
void ContextRestore() {
|
|
regs.ShadowRestore();
|
|
regs.ShadowSwap();
|
|
if (!regs.crep) {
|
|
regs.repc = regs.repcs;
|
|
}
|
|
if (!regs.ccnta) {
|
|
regs.a[1] = regs.a1s;
|
|
regs.b[1] = regs.b1s;
|
|
} else {
|
|
std::swap(regs.a[1], regs.b[1]);
|
|
}
|
|
}
|
|
|
|
void cntx_s() {
|
|
ContextStore();
|
|
}
|
|
void cntx_r() {
|
|
ContextRestore();
|
|
}
|
|
|
|
void ret(Cond c) {
|
|
if (regs.ConditionPass(c)) {
|
|
PopPC();
|
|
}
|
|
}
|
|
void retd() {
|
|
throw UnimplementedException();
|
|
}
|
|
void reti(Cond c) {
|
|
if (regs.ConditionPass(c)) {
|
|
PopPC();
|
|
regs.ie = 1;
|
|
}
|
|
}
|
|
void retic(Cond c) {
|
|
if (regs.ConditionPass(c)) {
|
|
PopPC();
|
|
regs.ie = 1;
|
|
ContextRestore();
|
|
}
|
|
}
|
|
void retid() {
|
|
UNREACHABLE();
|
|
}
|
|
void retidc() {
|
|
UNREACHABLE();
|
|
}
|
|
void rets(Imm8 a) {
|
|
PopPC();
|
|
regs.sp += a.Unsigned16();
|
|
}
|
|
|
|
void load_ps(Imm2 a) {
|
|
regs.ps[0] = a.Unsigned16();
|
|
}
|
|
void load_stepi(Imm7s a) {
|
|
// Although this is signed, we still only store the lower 7 bits
|
|
regs.stepi = a.Signed16() & 0x7F;
|
|
}
|
|
void load_stepj(Imm7s a) {
|
|
regs.stepj = a.Signed16() & 0x7F;
|
|
}
|
|
void load_page(Imm8 a) {
|
|
regs.page = a.Unsigned16();
|
|
}
|
|
void load_modi(Imm9 a) {
|
|
regs.modi = a.Unsigned16();
|
|
}
|
|
void load_modj(Imm9 a) {
|
|
regs.modj = a.Unsigned16();
|
|
}
|
|
void load_movpd(Imm2 a) {
|
|
regs.pcmhi = a.Unsigned16();
|
|
}
|
|
void load_ps01(Imm4 a) {
|
|
regs.ps[0] = a.Unsigned16() & 3;
|
|
regs.ps[1] = a.Unsigned16() >> 2;
|
|
}
|
|
|
|
void push(Imm16 a) {
|
|
mem.DataWrite(--regs.sp, a.Unsigned16());
|
|
}
|
|
void push(Register a) {
|
|
u16 value = RegToBus16(a.GetName(), true);
|
|
mem.DataWrite(--regs.sp, value);
|
|
}
|
|
void push(Abe a) {
|
|
u16 value = (GetAndSatAcc(a.GetName()) >> 32) & 0xFFFF;
|
|
mem.DataWrite(--regs.sp, value);
|
|
}
|
|
void push(ArArpSttMod a) {
|
|
u16 value = RegToBus16(a.GetName());
|
|
mem.DataWrite(--regs.sp, value);
|
|
}
|
|
void push_prpage() {
|
|
mem.DataWrite(--regs.sp, regs.prpage);
|
|
}
|
|
void push(Px a) {
|
|
u32 value = (u32)ProductToBus40(a);
|
|
u16 h = value >> 16;
|
|
u16 l = value & 0xFFFF;
|
|
mem.DataWrite(--regs.sp, l);
|
|
mem.DataWrite(--regs.sp, h);
|
|
}
|
|
void push_r6() {
|
|
u16 value = regs.r[6];
|
|
mem.DataWrite(--regs.sp, value);
|
|
}
|
|
void push_repc() {
|
|
u16 value = regs.repc;
|
|
mem.DataWrite(--regs.sp, value);
|
|
}
|
|
void push_x0() {
|
|
u16 value = regs.x[0];
|
|
mem.DataWrite(--regs.sp, value);
|
|
}
|
|
void push_x1() {
|
|
u16 value = regs.x[1];
|
|
mem.DataWrite(--regs.sp, value);
|
|
}
|
|
void push_y1() {
|
|
u16 value = regs.y[1];
|
|
mem.DataWrite(--regs.sp, value);
|
|
}
|
|
void pusha(Ax a) {
|
|
u32 value = GetAndSatAcc(a.GetName()) & 0xFFFF'FFFF;
|
|
u16 h = value >> 16;
|
|
u16 l = value & 0xFFFF;
|
|
mem.DataWrite(--regs.sp, l);
|
|
mem.DataWrite(--regs.sp, h);
|
|
}
|
|
void pusha(Bx a) {
|
|
u32 value = GetAndSatAcc(a.GetName()) & 0xFFFF'FFFF;
|
|
u16 h = value >> 16;
|
|
u16 l = value & 0xFFFF;
|
|
mem.DataWrite(--regs.sp, l);
|
|
mem.DataWrite(--regs.sp, h);
|
|
}
|
|
|
|
void pop(Register a) {
|
|
u16 value = mem.DataRead(regs.sp++);
|
|
RegFromBus16(a.GetName(), value);
|
|
}
|
|
void pop(Abe a) {
|
|
u32 value32 = SignExtend<8, u32>(mem.DataRead(regs.sp++) & 0xFF);
|
|
u64 acc = GetAcc(a.GetName());
|
|
SetAccAndFlag(a.GetName(), (acc & 0xFFFFFFFF) | (u64)value32 << 32);
|
|
}
|
|
void pop(ArArpSttMod a) {
|
|
u16 value = mem.DataRead(regs.sp++);
|
|
RegFromBus16(a.GetName(), value);
|
|
}
|
|
void pop(Bx a) {
|
|
u16 value = mem.DataRead(regs.sp++);
|
|
RegFromBus16(a.GetName(), value);
|
|
}
|
|
void pop_prpage() {
|
|
regs.prpage = mem.DataRead(regs.sp++);
|
|
}
|
|
void pop(Px a) {
|
|
u16 h = mem.DataRead(regs.sp++);
|
|
u16 l = mem.DataRead(regs.sp++);
|
|
u32 value = ((u32)h << 16) | l;
|
|
ProductFromBus32(a, value);
|
|
}
|
|
void pop_r6() {
|
|
u16 value = mem.DataRead(regs.sp++);
|
|
regs.r[6] = value;
|
|
}
|
|
void pop_repc() {
|
|
u16 value = mem.DataRead(regs.sp++);
|
|
regs.repc = value;
|
|
}
|
|
void pop_x0() {
|
|
u16 value = mem.DataRead(regs.sp++);
|
|
regs.x[0] = value;
|
|
}
|
|
void pop_x1() {
|
|
u16 value = mem.DataRead(regs.sp++);
|
|
regs.x[1] = value;
|
|
}
|
|
void pop_y1() {
|
|
u16 value = mem.DataRead(regs.sp++);
|
|
regs.y[1] = value;
|
|
}
|
|
void popa(Ab a) {
|
|
u16 h = mem.DataRead(regs.sp++);
|
|
u16 l = mem.DataRead(regs.sp++);
|
|
u64 value = SignExtend<32, u64>(((u64)h << 16) | l);
|
|
SetAccAndFlag(a.GetName(), value);
|
|
}
|
|
|
|
void Repeat(u16 repc) {
|
|
regs.repc = repc;
|
|
regs.rep = true;
|
|
}
|
|
|
|
void rep(Imm8 a) {
|
|
Repeat(a.Unsigned16());
|
|
}
|
|
void rep(Register a) {
|
|
Repeat(RegToBus16(a.GetName()));
|
|
}
|
|
void rep_r6() {
|
|
Repeat(regs.r[6]);
|
|
}
|
|
|
|
void shfc(Ab a, Ab b, Cond cond) {
|
|
if (regs.ConditionPass(cond)) {
|
|
u64 value = GetAcc(a.GetName());
|
|
u16 sv = regs.sv;
|
|
ShiftBus40(value, sv, b.GetName());
|
|
}
|
|
}
|
|
void shfi(Ab a, Ab b, Imm6s s) {
|
|
u64 value = GetAcc(a.GetName());
|
|
u16 sv = s.Signed16();
|
|
ShiftBus40(value, sv, b.GetName());
|
|
}
|
|
|
|
void tst4b(ArRn2 b, ArStep2 bs) {
|
|
u16 address = RnAddressAndModify(GetArRnUnit(b), GetArStep(bs));
|
|
u16 value = mem.DataRead(address);
|
|
u64 bit = GetAcc(RegName::a0) & 0xF;
|
|
// Is this correct? an why?
|
|
regs.fz = regs.fc0 = (value >> bit) & 1;
|
|
}
|
|
void tst4b(ArRn2 b, ArStep2 bs, Ax c) {
|
|
u64 a = GetAcc(RegName::a0);
|
|
u64 bit = a & 0xF;
|
|
u16 fv = regs.fv;
|
|
u16 fvl = regs.fvl;
|
|
u16 fm = regs.fm;
|
|
u16 fn = regs.fn;
|
|
u16 fe = regs.fe;
|
|
u16 sv = regs.sv;
|
|
ShiftBus40(a, sv, c.GetName());
|
|
regs.fc1 = regs.fc0;
|
|
regs.fv = fv;
|
|
regs.fvl = fvl;
|
|
regs.fm = fm;
|
|
regs.fn = fn;
|
|
regs.fe = fe;
|
|
u16 address = RnAddressAndModify(GetArRnUnit(b), GetArStep(bs));
|
|
u16 value = mem.DataRead(address);
|
|
regs.fz = regs.fc0 = (value >> bit) & 1;
|
|
}
|
|
void tstb(MemImm8 a, Imm4 b) {
|
|
u16 value = LoadFromMemory(a);
|
|
regs.fz = (value >> b.Unsigned16()) & 1;
|
|
}
|
|
void tstb(Rn a, StepZIDS as, Imm4 b) {
|
|
u16 address = RnAddressAndModify(a.Index(), as.GetName());
|
|
u16 value = mem.DataRead(address);
|
|
regs.fz = (value >> b.Unsigned16()) & 1;
|
|
}
|
|
void tstb(Register a, Imm4 b) {
|
|
u16 value = RegToBus16(a.GetName());
|
|
regs.fz = (value >> b.Unsigned16()) & 1;
|
|
}
|
|
void tstb_r6(Imm4 b) {
|
|
u16 value = regs.r[6];
|
|
regs.fz = (value >> b.Unsigned16()) & 1;
|
|
}
|
|
void tstb(SttMod a, Imm16 b) {
|
|
u16 value = RegToBus16(a.GetName());
|
|
regs.fz = (value >> b.Unsigned16()) & 1;
|
|
}
|
|
|
|
void and_(Ab a, Ab b, Ax c) {
|
|
u64 value = GetAcc(a.GetName()) & GetAcc(b.GetName());
|
|
SetAccAndFlag(c.GetName(), value);
|
|
}
|
|
|
|
void dint() {
|
|
regs.ie = 0;
|
|
}
|
|
void eint() {
|
|
regs.ie = 1;
|
|
}
|
|
|
|
void MulGeneric(MulOp op, Ax a) {
|
|
if (op != MulOp::Mpy && op != MulOp::Mpysu) {
|
|
u64 value = GetAcc(a.GetName());
|
|
u64 product = ProductToBus40(Px{0});
|
|
if (op == MulOp::Maa || op == MulOp::Maasu) {
|
|
product >>= 16;
|
|
product = SignExtend<24>(product);
|
|
}
|
|
u64 result = AddSub(value, product, false);
|
|
SatAndSetAccAndFlag(a.GetName(), result);
|
|
}
|
|
|
|
switch (op) {
|
|
case MulOp::Mpy:
|
|
case MulOp::Mac:
|
|
case MulOp::Maa:
|
|
DoMultiplication(0, true, true);
|
|
break;
|
|
case MulOp::Mpysu:
|
|
case MulOp::Macsu:
|
|
case MulOp::Maasu:
|
|
// Note: the naming conventin of "mpysu" is "multiply signed *y* by unsigned *x*"
|
|
DoMultiplication(0, false, true);
|
|
break;
|
|
case MulOp::Macus:
|
|
DoMultiplication(0, true, false);
|
|
break;
|
|
case MulOp::Macuu:
|
|
DoMultiplication(0, false, false);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void mul(Mul3 op, Rn y, StepZIDS ys, Imm16 x, Ax a) {
|
|
u16 address = RnAddressAndModify(y.Index(), ys.GetName());
|
|
regs.y[0] = mem.DataRead(address);
|
|
regs.x[0] = x.Unsigned16();
|
|
MulGeneric(op.GetName(), a);
|
|
}
|
|
void mul_y0(Mul3 op, Rn x, StepZIDS xs, Ax a) {
|
|
u16 address = RnAddressAndModify(x.Index(), xs.GetName());
|
|
regs.x[0] = mem.DataRead(address);
|
|
MulGeneric(op.GetName(), a);
|
|
}
|
|
void mul_y0(Mul3 op, Register x, Ax a) {
|
|
regs.x[0] = RegToBus16(x.GetName());
|
|
MulGeneric(op.GetName(), a);
|
|
}
|
|
void mul(Mul3 op, R45 y, StepZIDS ys, R0123 x, StepZIDS xs, Ax a) {
|
|
u16 address_y = RnAddressAndModify(y.Index(), ys.GetName());
|
|
u16 address_x = RnAddressAndModify(x.Index(), xs.GetName());
|
|
regs.y[0] = mem.DataRead(address_y);
|
|
regs.x[0] = mem.DataRead(address_x);
|
|
MulGeneric(op.GetName(), a);
|
|
}
|
|
void mul_y0_r6(Mul3 op, Ax a) {
|
|
regs.x[0] = regs.r[6];
|
|
MulGeneric(op.GetName(), a);
|
|
}
|
|
void mul_y0(Mul2 op, MemImm8 x, Ax a) {
|
|
regs.x[0] = LoadFromMemory(x);
|
|
MulGeneric(op.GetName(), a);
|
|
}
|
|
|
|
void mpyi(Imm8s x) {
|
|
regs.x[0] = x.Signed16();
|
|
DoMultiplication(0, true, true);
|
|
}
|
|
|
|
void msu(R45 y, StepZIDS ys, R0123 x, StepZIDS xs, Ax a) {
|
|
u16 yi = RnAddressAndModify(y.Index(), ys.GetName());
|
|
u16 xi = RnAddressAndModify(x.Index(), xs.GetName());
|
|
u64 value = GetAcc(a.GetName());
|
|
u64 product = ProductToBus40(Px{0});
|
|
u64 result = AddSub(value, product, true);
|
|
SatAndSetAccAndFlag(a.GetName(), result);
|
|
regs.y[0] = mem.DataRead(yi);
|
|
regs.x[0] = mem.DataRead(xi);
|
|
DoMultiplication(0, true, true);
|
|
}
|
|
void msu(Rn y, StepZIDS ys, Imm16 x, Ax a) {
|
|
u16 yi = RnAddressAndModify(y.Index(), ys.GetName());
|
|
u64 value = GetAcc(a.GetName());
|
|
u64 product = ProductToBus40(Px{0});
|
|
u64 result = AddSub(value, product, true);
|
|
SatAndSetAccAndFlag(a.GetName(), result);
|
|
regs.y[0] = mem.DataRead(yi);
|
|
regs.x[0] = x.Unsigned16();
|
|
DoMultiplication(0, true, true);
|
|
}
|
|
void msusu(ArRn2 x, ArStep2 xs, Ax a) {
|
|
u16 xi = RnAddressAndModify(GetArRnUnit(x), GetArStep(xs));
|
|
u64 value = GetAcc(a.GetName());
|
|
u64 product = ProductToBus40(Px{0});
|
|
u64 result = AddSub(value, product, true);
|
|
SatAndSetAccAndFlag(a.GetName(), result);
|
|
regs.x[0] = mem.DataRead(xi);
|
|
DoMultiplication(0, false, true);
|
|
}
|
|
void mac_x1to0(Ax a) {
|
|
u64 value = GetAcc(a.GetName());
|
|
u64 product = ProductToBus40(Px{0});
|
|
u64 result = AddSub(value, product, false);
|
|
SatAndSetAccAndFlag(a.GetName(), result);
|
|
regs.x[0] = regs.x[1];
|
|
DoMultiplication(0, true, true);
|
|
}
|
|
void mac1(ArpRn1 xy, ArpStep1 xis, ArpStep1 yjs, Ax a) {
|
|
auto [ui, uj] = GetArpRnUnit(xy);
|
|
auto [si, sj] = GetArpStep(xis, yjs);
|
|
u16 i = RnAddressAndModify(ui, si);
|
|
u16 j = RnAddressAndModify(uj, sj);
|
|
u64 value = GetAcc(a.GetName());
|
|
u64 product = ProductToBus40(Px{1});
|
|
u64 result = AddSub(value, product, false);
|
|
SatAndSetAccAndFlag(a.GetName(), result);
|
|
regs.x[1] = mem.DataRead(i);
|
|
regs.y[1] = mem.DataRead(j);
|
|
DoMultiplication(1, true, true);
|
|
}
|
|
|
|
void modr(Rn a, StepZIDS as) {
|
|
u32 unit = a.Index();
|
|
RnAndModify(unit, as.GetName());
|
|
regs.fr = regs.r[unit] == 0;
|
|
}
|
|
void modr_dmod(Rn a, StepZIDS as) {
|
|
u32 unit = a.Index();
|
|
RnAndModify(unit, as.GetName(), true);
|
|
regs.fr = regs.r[unit] == 0;
|
|
}
|
|
void modr_i2(Rn a) {
|
|
u32 unit = a.Index();
|
|
RnAndModify(unit, StepValue::Increase2Mode1);
|
|
regs.fr = regs.r[unit] == 0;
|
|
}
|
|
void modr_i2_dmod(Rn a) {
|
|
u32 unit = a.Index();
|
|
RnAndModify(unit, StepValue::Increase2Mode1, true);
|
|
regs.fr = regs.r[unit] == 0;
|
|
}
|
|
void modr_d2(Rn a) {
|
|
u32 unit = a.Index();
|
|
RnAndModify(unit, StepValue::Decrease2Mode1);
|
|
regs.fr = regs.r[unit] == 0;
|
|
}
|
|
void modr_d2_dmod(Rn a) {
|
|
u32 unit = a.Index();
|
|
RnAndModify(unit, StepValue::Decrease2Mode1, true);
|
|
regs.fr = regs.r[unit] == 0;
|
|
}
|
|
void modr_eemod(ArpRn2 a, ArpStep2 asi, ArpStep2 asj) {
|
|
u32 uniti, unitj;
|
|
StepValue stepi, stepj;
|
|
std::tie(uniti, unitj) = GetArpRnUnit(a);
|
|
std::tie(stepi, stepj) = GetArpStep(asi, asj);
|
|
RnAndModify(uniti, stepi);
|
|
RnAndModify(unitj, stepj);
|
|
}
|
|
void modr_edmod(ArpRn2 a, ArpStep2 asi, ArpStep2 asj) {
|
|
u32 uniti, unitj;
|
|
StepValue stepi, stepj;
|
|
std::tie(uniti, unitj) = GetArpRnUnit(a);
|
|
std::tie(stepi, stepj) = GetArpStep(asi, asj);
|
|
RnAndModify(uniti, stepi);
|
|
RnAndModify(unitj, stepj, true);
|
|
}
|
|
void modr_demod(ArpRn2 a, ArpStep2 asi, ArpStep2 asj) {
|
|
u32 uniti, unitj;
|
|
StepValue stepi, stepj;
|
|
std::tie(uniti, unitj) = GetArpRnUnit(a);
|
|
std::tie(stepi, stepj) = GetArpStep(asi, asj);
|
|
RnAndModify(uniti, stepi, true);
|
|
RnAndModify(unitj, stepj);
|
|
}
|
|
void modr_ddmod(ArpRn2 a, ArpStep2 asi, ArpStep2 asj) {
|
|
u32 uniti, unitj;
|
|
StepValue stepi, stepj;
|
|
std::tie(uniti, unitj) = GetArpRnUnit(a);
|
|
std::tie(stepi, stepj) = GetArpStep(asi, asj);
|
|
RnAndModify(uniti, stepi, true);
|
|
RnAndModify(unitj, stepj, true);
|
|
}
|
|
|
|
void movd(R0123 a, StepZIDS as, R45 b, StepZIDS bs) {
|
|
u16 address_s = RnAddressAndModify(a.Index(), as.GetName());
|
|
u32 address_d = RnAddressAndModify(b.Index(), bs.GetName());
|
|
address_d |= (u32)regs.pcmhi << 16;
|
|
mem.ProgramWrite(address_d, mem.DataRead(address_s));
|
|
}
|
|
void movp(Axl a, Register b) {
|
|
u32 address = RegToBus16(a.GetName());
|
|
address |= (u32)regs.pcmhi << 16;
|
|
u16 value = mem.ProgramRead(address);
|
|
RegFromBus16(b.GetName(), value);
|
|
}
|
|
void movp(Ax a, Register b) {
|
|
u32 address = GetAcc(a.GetName()) & 0x3FFFF; // no saturation
|
|
u16 value = mem.ProgramRead(address);
|
|
RegFromBus16(b.GetName(), value);
|
|
}
|
|
void movp(Rn a, StepZIDS as, R0123 b, StepZIDS bs) {
|
|
u32 address_s = RnAddressAndModify(a.Index(), as.GetName());
|
|
u16 address_d = RnAddressAndModify(b.Index(), bs.GetName());
|
|
address_s |= (u32)regs.pcmhi << 16;
|
|
mem.DataWrite(address_d, mem.ProgramRead(address_s));
|
|
}
|
|
void movpdw(Ax a) {
|
|
u32 address = GetAcc(a.GetName()) & 0x3FFFF; // no saturation
|
|
// the endianess doesn't seem to be affected by regs.cpc
|
|
u16 h = mem.ProgramRead(address);
|
|
u16 l = mem.ProgramRead(address + 1);
|
|
SetPC(l | ((u32)h << 16));
|
|
}
|
|
|
|
void mov(Ab a, Ab b) {
|
|
u64 value = GetAcc(a.GetName());
|
|
SatAndSetAccAndFlag(b.GetName(), value);
|
|
}
|
|
void mov_dvm(Abl a) {
|
|
UNREACHABLE();
|
|
}
|
|
void mov_x0(Abl a) {
|
|
u16 value16 = RegToBus16(a.GetName(), true);
|
|
regs.x[0] = value16;
|
|
}
|
|
void mov_x1(Abl a) {
|
|
u16 value16 = RegToBus16(a.GetName(), true);
|
|
regs.x[1] = value16;
|
|
}
|
|
void mov_y1(Abl a) {
|
|
u16 value16 = RegToBus16(a.GetName(), true);
|
|
regs.y[1] = value16;
|
|
}
|
|
|
|
void StoreToMemory(MemImm8 addr, u16 value) {
|
|
mem.DataWrite(addr.Unsigned16() + (regs.page << 8), value);
|
|
}
|
|
void StoreToMemory(MemImm16 addr, u16 value) {
|
|
mem.DataWrite(addr.Unsigned16(), value);
|
|
}
|
|
void StoreToMemory(MemR7Imm16 addr, u16 value) {
|
|
mem.DataWrite(addr.Unsigned16() + regs.r[7], value);
|
|
}
|
|
void StoreToMemory(MemR7Imm7s addr, u16 value) {
|
|
mem.DataWrite(addr.Signed16() + regs.r[7], value);
|
|
}
|
|
|
|
void mov(Ablh a, MemImm8 b) {
|
|
u16 value16 = RegToBus16(a.GetName(), true);
|
|
StoreToMemory(b, value16);
|
|
}
|
|
void mov(Axl a, MemImm16 b) {
|
|
u16 value16 = RegToBus16(a.GetName(), true);
|
|
StoreToMemory(b, value16);
|
|
}
|
|
void mov(Axl a, MemR7Imm16 b) {
|
|
u16 value16 = RegToBus16(a.GetName(), true);
|
|
StoreToMemory(b, value16);
|
|
}
|
|
void mov(Axl a, MemR7Imm7s b) {
|
|
u16 value16 = RegToBus16(a.GetName(), true);
|
|
StoreToMemory(b, value16);
|
|
}
|
|
|
|
u16 LoadFromMemory(MemImm8 addr) {
|
|
return mem.DataRead(addr.Unsigned16() + (regs.page << 8));
|
|
}
|
|
u16 LoadFromMemory(MemImm16 addr) {
|
|
return mem.DataRead(addr.Unsigned16());
|
|
}
|
|
u16 LoadFromMemory(MemR7Imm16 addr) {
|
|
return mem.DataRead(addr.Unsigned16() + regs.r[7]);
|
|
}
|
|
u16 LoadFromMemory(MemR7Imm7s addr) {
|
|
return mem.DataRead(addr.Signed16() + regs.r[7]);
|
|
}
|
|
|
|
void mov(MemImm16 a, Ax b) {
|
|
u16 value = LoadFromMemory(a);
|
|
RegFromBus16(b.GetName(), value);
|
|
}
|
|
void mov(MemImm8 a, Ab b) {
|
|
u16 value = LoadFromMemory(a);
|
|
RegFromBus16(b.GetName(), value);
|
|
}
|
|
void mov(MemImm8 a, Ablh b) {
|
|
u16 value = LoadFromMemory(a);
|
|
RegFromBus16(b.GetName(), value);
|
|
}
|
|
void mov_eu(MemImm8 a, Axh b) {
|
|
u16 value = LoadFromMemory(a);
|
|
u64 acc = GetAcc(b.GetName());
|
|
acc &= 0xFFFF'FFFF'0000'0000;
|
|
acc |= (u64)value << 16;
|
|
SetAccAndFlag(b.GetName(), acc); // ?
|
|
}
|
|
void mov(MemImm8 a, RnOld b) {
|
|
u16 value = LoadFromMemory(a);
|
|
RegFromBus16(b.GetName(), value);
|
|
}
|
|
void mov_sv(MemImm8 a) {
|
|
u16 value = LoadFromMemory(a);
|
|
regs.sv = value;
|
|
}
|
|
void mov_dvm_to(Ab b) {
|
|
UNREACHABLE();
|
|
}
|
|
void mov_icr_to(Ab b) {
|
|
u16 value = regs.Get<icr>();
|
|
RegFromBus16(b.GetName(), value);
|
|
}
|
|
void mov(Imm16 a, Bx b) {
|
|
u16 value = a.Unsigned16();
|
|
RegFromBus16(b.GetName(), value);
|
|
}
|
|
void mov(Imm16 a, Register b) {
|
|
u16 value = a.Unsigned16();
|
|
RegFromBus16(b.GetName(), value);
|
|
}
|
|
void mov_icr(Imm5 a) {
|
|
u16 value = regs.Get<icr>();
|
|
value &= ~0x1F;
|
|
value |= a.Unsigned16();
|
|
regs.Set<icr>(value);
|
|
}
|
|
void mov(Imm8s a, Axh b) {
|
|
u16 value = a.Signed16();
|
|
RegFromBus16(b.GetName(), value);
|
|
}
|
|
void mov(Imm8s a, RnOld b) {
|
|
u16 value = a.Signed16();
|
|
RegFromBus16(b.GetName(), value);
|
|
}
|
|
void mov_sv(Imm8s a) {
|
|
u16 value = a.Signed16();
|
|
regs.sv = value;
|
|
}
|
|
void mov(Imm8 a, Axl b) {
|
|
u16 value = a.Unsigned16();
|
|
RegFromBus16(b.GetName(), value);
|
|
}
|
|
void mov(MemR7Imm16 a, Ax b) {
|
|
u16 value = LoadFromMemory(a);
|
|
RegFromBus16(b.GetName(), value);
|
|
}
|
|
void mov(MemR7Imm7s a, Ax b) {
|
|
u16 value = LoadFromMemory(a);
|
|
RegFromBus16(b.GetName(), value);
|
|
}
|
|
void mov(Rn a, StepZIDS as, Bx b) {
|
|
u16 address = RnAddressAndModify(a.Index(), as.GetName());
|
|
u16 value = mem.DataRead(address);
|
|
RegFromBus16(b.GetName(), value);
|
|
}
|
|
void mov(Rn a, StepZIDS as, Register b) {
|
|
u16 address = RnAddressAndModify(a.Index(), as.GetName());
|
|
u16 value = mem.DataRead(address);
|
|
RegFromBus16(b.GetName(), value);
|
|
}
|
|
void mov_memsp_to(Register b) {
|
|
u16 value = mem.DataRead(regs.sp);
|
|
RegFromBus16(b.GetName(), value);
|
|
}
|
|
void mov_mixp_to(Register b) {
|
|
u16 value = regs.mixp;
|
|
RegFromBus16(b.GetName(), value);
|
|
}
|
|
void mov(RnOld a, MemImm8 b) {
|
|
u16 value = RegToBus16(a.GetName());
|
|
StoreToMemory(b, value);
|
|
}
|
|
void mov_icr(Register a) {
|
|
u16 value = RegToBus16(a.GetName(), true);
|
|
regs.Set<icr>(value);
|
|
}
|
|
void mov_mixp(Register a) {
|
|
u16 value = RegToBus16(a.GetName(), true);
|
|
regs.mixp = value;
|
|
}
|
|
void mov(Register a, Rn b, StepZIDS bs) {
|
|
// a = a0 or a1 is overrided
|
|
u16 value = RegToBus16(a.GetName(), true);
|
|
u16 address = RnAddressAndModify(b.Index(), bs.GetName());
|
|
mem.DataWrite(address, value);
|
|
}
|
|
void mov(Register a, Bx b) {
|
|
if (a.GetName() == RegName::p) {
|
|
u64 value = ProductToBus40(Px{0});
|
|
SatAndSetAccAndFlag(b.GetName(), value);
|
|
} else if (a.GetName() == RegName::a0 || a.GetName() == RegName::a1) {
|
|
// Is there any difference from the mov(Ab, Ab) instruction?
|
|
u64 value = GetAcc(a.GetName());
|
|
SatAndSetAccAndFlag(b.GetName(), value);
|
|
} else {
|
|
u16 value = RegToBus16(a.GetName(), true);
|
|
RegFromBus16(b.GetName(), value);
|
|
}
|
|
}
|
|
void mov(Register a, Register b) {
|
|
// a = a0 or a1 is overrided
|
|
if (a.GetName() == RegName::p) {
|
|
// b loses its typical meaning in this case
|
|
RegName b_name = b.GetNameForMovFromP();
|
|
u64 value = ProductToBus40(Px{0});
|
|
SatAndSetAccAndFlag(b_name, value);
|
|
} else if (a.GetName() == RegName::pc) {
|
|
if (b.GetName() == RegName::a0 || b.GetName() == RegName::a1) {
|
|
SatAndSetAccAndFlag(b.GetName(), regs.pc);
|
|
} else {
|
|
RegFromBus16(b.GetName(), regs.pc & 0xFFFF);
|
|
}
|
|
} else {
|
|
u16 value = RegToBus16(a.GetName(), true);
|
|
RegFromBus16(b.GetName(), value);
|
|
}
|
|
}
|
|
void mov_repc_to(Ab b) {
|
|
u16 value = regs.repc;
|
|
RegFromBus16(b.GetName(), value);
|
|
}
|
|
void mov_sv_to(MemImm8 b) {
|
|
u16 value = regs.sv;
|
|
StoreToMemory(b, value);
|
|
}
|
|
void mov_x0_to(Ab b) {
|
|
u16 value = regs.x[0];
|
|
RegFromBus16(b.GetName(), value);
|
|
}
|
|
void mov_x1_to(Ab b) {
|
|
u16 value = regs.x[1];
|
|
RegFromBus16(b.GetName(), value);
|
|
}
|
|
void mov_y1_to(Ab b) {
|
|
u16 value = regs.y[1];
|
|
RegFromBus16(b.GetName(), value);
|
|
}
|
|
void mov(Imm16 a, ArArp b) {
|
|
u16 value = a.Unsigned16();
|
|
RegFromBus16(b.GetName(), value);
|
|
}
|
|
void mov_r6(Imm16 a) {
|
|
u16 value = a.Unsigned16();
|
|
regs.r[6] = value;
|
|
}
|
|
void mov_repc(Imm16 a) {
|
|
u16 value = a.Unsigned16();
|
|
regs.repc = value;
|
|
}
|
|
void mov_stepi0(Imm16 a) {
|
|
u16 value = a.Unsigned16();
|
|
regs.stepi0 = value;
|
|
}
|
|
void mov_stepj0(Imm16 a) {
|
|
u16 value = a.Unsigned16();
|
|
regs.stepj0 = value;
|
|
}
|
|
void mov(Imm16 a, SttMod b) {
|
|
u16 value = a.Unsigned16();
|
|
RegFromBus16(b.GetName(), value);
|
|
}
|
|
void mov_prpage(Imm4 a) {
|
|
regs.prpage = a.Unsigned16();
|
|
}
|
|
|
|
void mov_a0h_stepi0() {
|
|
u16 value = RegToBus16(RegName::a0h, true);
|
|
regs.stepi0 = value;
|
|
}
|
|
void mov_a0h_stepj0() {
|
|
u16 value = RegToBus16(RegName::a0h, true);
|
|
regs.stepj0 = value;
|
|
}
|
|
void mov_stepi0_a0h() {
|
|
u16 value = regs.stepi0;
|
|
RegFromBus16(RegName::a0h, value);
|
|
}
|
|
void mov_stepj0_a0h() {
|
|
u16 value = regs.stepj0;
|
|
RegFromBus16(RegName::a0h, value);
|
|
}
|
|
|
|
void mov_prpage(Abl a) {
|
|
regs.prpage = (u16)(GetAcc(a.GetName()) & 0xF); // ?
|
|
}
|
|
void mov_repc(Abl a) {
|
|
u16 value = RegToBus16(a.GetName(), true);
|
|
regs.repc = value;
|
|
}
|
|
void mov(Abl a, ArArp b) {
|
|
u16 value = RegToBus16(a.GetName(), true);
|
|
RegFromBus16(b.GetName(), value);
|
|
}
|
|
void mov(Abl a, SttMod b) {
|
|
u16 value = RegToBus16(a.GetName(), true);
|
|
RegFromBus16(b.GetName(), value);
|
|
}
|
|
|
|
void mov_prpage_to(Abl b) {
|
|
RegFromBus16(b.GetName(), regs.prpage); // ?
|
|
}
|
|
void mov_repc_to(Abl b) {
|
|
u16 value = regs.repc;
|
|
RegFromBus16(b.GetName(), value);
|
|
}
|
|
void mov(ArArp a, Abl b) {
|
|
u16 value = RegToBus16(a.GetName());
|
|
RegFromBus16(b.GetName(), value);
|
|
}
|
|
void mov(SttMod a, Abl b) {
|
|
u16 value = RegToBus16(a.GetName());
|
|
RegFromBus16(b.GetName(), value);
|
|
}
|
|
|
|
void mov_repc_to(ArRn1 b, ArStep1 bs) {
|
|
u16 address = RnAddressAndModify(GetArRnUnit(b), GetArStep(bs));
|
|
u16 value = regs.repc;
|
|
mem.DataWrite(address, value);
|
|
}
|
|
void mov(ArArp a, ArRn1 b, ArStep1 bs) {
|
|
u16 address = RnAddressAndModify(GetArRnUnit(b), GetArStep(bs));
|
|
u16 value = RegToBus16(a.GetName());
|
|
mem.DataWrite(address, value);
|
|
}
|
|
void mov(SttMod a, ArRn1 b, ArStep1 bs) {
|
|
u16 address = RnAddressAndModify(GetArRnUnit(b), GetArStep(bs));
|
|
u16 value = RegToBus16(a.GetName());
|
|
mem.DataWrite(address, value);
|
|
}
|
|
|
|
void mov_repc(ArRn1 a, ArStep1 as) {
|
|
u16 address = RnAddressAndModify(GetArRnUnit(a), GetArStep(as));
|
|
u16 value = mem.DataRead(address);
|
|
regs.repc = value;
|
|
}
|
|
void mov(ArRn1 a, ArStep1 as, ArArp b) {
|
|
u16 address = RnAddressAndModify(GetArRnUnit(a), GetArStep(as));
|
|
u16 value = mem.DataRead(address);
|
|
RegFromBus16(b.GetName(), value);
|
|
}
|
|
void mov(ArRn1 a, ArStep1 as, SttMod b) {
|
|
u16 address = RnAddressAndModify(GetArRnUnit(a), GetArStep(as));
|
|
u16 value = mem.DataRead(address);
|
|
RegFromBus16(b.GetName(), value);
|
|
}
|
|
|
|
void mov_repc_to(MemR7Imm16 b) {
|
|
u16 value = regs.repc;
|
|
StoreToMemory(b, value);
|
|
}
|
|
void mov(ArArpSttMod a, MemR7Imm16 b) {
|
|
u16 value = RegToBus16(a.GetName());
|
|
StoreToMemory(b, value);
|
|
}
|
|
|
|
void mov_repc(MemR7Imm16 a) {
|
|
u16 value = LoadFromMemory(a);
|
|
regs.repc = value;
|
|
}
|
|
void mov(MemR7Imm16 a, ArArpSttMod b) {
|
|
u16 value = LoadFromMemory(a);
|
|
RegFromBus16(b.GetName(), value);
|
|
}
|
|
|
|
void mov_pc(Ax a) {
|
|
u64 value = GetAcc(a.GetName());
|
|
SetPC(value & 0xFFFFFFFF);
|
|
}
|
|
void mov_pc(Bx a) {
|
|
u64 value = GetAcc(a.GetName());
|
|
SetPC(value & 0xFFFFFFFF);
|
|
}
|
|
|
|
void mov_mixp_to(Bx b) {
|
|
u16 value = regs.mixp;
|
|
RegFromBus16(b.GetName(), value);
|
|
}
|
|
void mov_mixp_r6() {
|
|
u16 value = regs.mixp;
|
|
regs.r[6] = value;
|
|
}
|
|
void mov_p0h_to(Bx b) {
|
|
u16 value = (ProductToBus40(Px{0}) >> 16) & 0xFFFF;
|
|
RegFromBus16(b.GetName(), value);
|
|
}
|
|
void mov_p0h_r6() {
|
|
u16 value = (ProductToBus40(Px{0}) >> 16) & 0xFFFF;
|
|
regs.r[6] = value;
|
|
}
|
|
void mov_p0h_to(Register b) {
|
|
u16 value = (ProductToBus40(Px{0}) >> 16) & 0xFFFF;
|
|
RegFromBus16(b.GetName(), value);
|
|
}
|
|
void mov_p0(Ab a) {
|
|
u32 value = GetAndSatAcc(a.GetName()) & 0xFFFFFFFF;
|
|
ProductFromBus32(Px{0}, value);
|
|
}
|
|
void mov_p1_to(Ab b) {
|
|
u64 value = ProductToBus40(Px{1});
|
|
SatAndSetAccAndFlag(b.GetName(), value);
|
|
}
|
|
|
|
void mov2(Px a, ArRn2 b, ArStep2 bs) {
|
|
u32 value = ProductToBus32_NoShift(a);
|
|
u16 l = value & 0xFFFF;
|
|
u16 h = (value >> 16) & 0xFFFF;
|
|
u16 unit = GetArRnUnit(b);
|
|
u16 address = RnAddressAndModify(unit, GetArStep(bs));
|
|
u16 address2 = OffsetAddress(unit, address, GetArOffset(bs));
|
|
// NOTE: keep the write order exactly like this.
|
|
mem.DataWrite(address2, l);
|
|
mem.DataWrite(address, h);
|
|
}
|
|
void mov2s(Px a, ArRn2 b, ArStep2 bs) {
|
|
u64 value = ProductToBus40(a);
|
|
u16 l = value & 0xFFFF;
|
|
u16 h = (value >> 16) & 0xFFFF;
|
|
u16 unit = GetArRnUnit(b);
|
|
u16 address = RnAddressAndModify(unit, GetArStep(bs));
|
|
u16 address2 = OffsetAddress(unit, address, GetArOffset(bs));
|
|
// NOTE: keep the write order exactly like this.
|
|
mem.DataWrite(address2, l);
|
|
mem.DataWrite(address, h);
|
|
}
|
|
void mov2(ArRn2 a, ArStep2 as, Px b) {
|
|
u16 unit = GetArRnUnit(a);
|
|
u16 address = RnAddressAndModify(unit, GetArStep(as));
|
|
u16 address2 = OffsetAddress(unit, address, GetArOffset(as));
|
|
u16 l = mem.DataRead(address2);
|
|
u16 h = mem.DataRead(address);
|
|
u32 value = ((u32)h << 16) | l;
|
|
ProductFromBus32(b, value);
|
|
}
|
|
void mova(Ab a, ArRn2 b, ArStep2 bs) {
|
|
u64 value = GetAndSatAcc(a.GetName());
|
|
u16 l = value & 0xFFFF;
|
|
u16 h = (value >> 16) & 0xFFFF;
|
|
u16 unit = GetArRnUnit(b);
|
|
u16 address = RnAddressAndModify(unit, GetArStep(bs));
|
|
u16 address2 = OffsetAddress(unit, address, GetArOffset(bs));
|
|
// NOTE: keep the write order exactly like this. The second one overrides the first one if
|
|
// the offset is zero.
|
|
mem.DataWrite(address2, l);
|
|
mem.DataWrite(address, h);
|
|
}
|
|
void mova(ArRn2 a, ArStep2 as, Ab b) {
|
|
u16 unit = GetArRnUnit(a);
|
|
u16 address = RnAddressAndModify(unit, GetArStep(as));
|
|
u16 address2 = OffsetAddress(unit, address, GetArOffset(as));
|
|
u16 l = mem.DataRead(address2);
|
|
u16 h = mem.DataRead(address);
|
|
u64 value = SignExtend<32, u64>(((u64)h << 16) | l);
|
|
SatAndSetAccAndFlag(b.GetName(), value);
|
|
}
|
|
|
|
void mov_r6_to(Bx b) {
|
|
u16 value = regs.r[6];
|
|
RegFromBus16(b.GetName(), value);
|
|
}
|
|
void mov_r6_mixp() {
|
|
u16 value = regs.r[6];
|
|
regs.mixp = value;
|
|
}
|
|
void mov_r6_to(Register b) {
|
|
u16 value = regs.r[6];
|
|
RegFromBus16(b.GetName(), value);
|
|
}
|
|
void mov_r6(Register a) {
|
|
u16 value = RegToBus16(a.GetName(), true);
|
|
regs.r[6] = value;
|
|
}
|
|
void mov_memsp_r6() {
|
|
u16 value = mem.DataRead(regs.sp);
|
|
regs.r[6] = value;
|
|
}
|
|
void mov_r6_to(Rn b, StepZIDS bs) {
|
|
u16 value = regs.r[6];
|
|
u16 address = RnAddressAndModify(b.Index(), bs.GetName());
|
|
mem.DataWrite(address, value);
|
|
}
|
|
void mov_r6(Rn a, StepZIDS as) {
|
|
u16 address = RnAddressAndModify(a.Index(), as.GetName());
|
|
u16 value = mem.DataRead(address);
|
|
regs.r[6] = value;
|
|
}
|
|
|
|
void mov2_axh_m_y0_m(Axh a, ArRn2 b, ArStep2 bs) {
|
|
u16 u = (u16)((GetAndSatAccNoFlag(a.GetName()) >> 16) & 0xFFFF);
|
|
u16 v = regs.y[0];
|
|
u16 unit = GetArRnUnit(b);
|
|
u16 ua = RnAddressAndModify(unit, GetArStep(bs));
|
|
u16 va = OffsetAddress(unit, ua, GetArOffset(bs));
|
|
// keep the order
|
|
mem.DataWrite(va, v);
|
|
mem.DataWrite(ua, u);
|
|
}
|
|
|
|
void mov2_ax_mij(Ab a, ArpRn1 b, ArpStep1 bsi, ArpStep1 bsj) {
|
|
auto [ui, uj] = GetArpRnUnit(b);
|
|
auto [si, sj] = GetArpStep(bsi, bsj);
|
|
u16 i = RnAddressAndModify(ui, si);
|
|
u16 j = RnAddressAndModify(uj, sj);
|
|
u64 value = GetAndSatAccNoFlag(a.GetName());
|
|
mem.DataWrite(i, (u16)((value >> 16) & 0xFFFF));
|
|
mem.DataWrite(j, (u16)(value & 0xFFFF));
|
|
}
|
|
void mov2_ax_mji(Ab a, ArpRn1 b, ArpStep1 bsi, ArpStep1 bsj) {
|
|
auto [ui, uj] = GetArpRnUnit(b);
|
|
auto [si, sj] = GetArpStep(bsi, bsj);
|
|
u16 i = RnAddressAndModify(ui, si);
|
|
u16 j = RnAddressAndModify(uj, sj);
|
|
u64 value = GetAndSatAccNoFlag(a.GetName());
|
|
mem.DataWrite(j, (u16)((value >> 16) & 0xFFFF));
|
|
mem.DataWrite(i, (u16)(value & 0xFFFF));
|
|
}
|
|
void mov2_mij_ax(ArpRn1 a, ArpStep1 asi, ArpStep1 asj, Ab b) {
|
|
auto [ui, uj] = GetArpRnUnit(a);
|
|
auto [si, sj] = GetArpStep(asi, asj);
|
|
u16 h = mem.DataRead(RnAddressAndModify(ui, si));
|
|
u16 l = mem.DataRead(RnAddressAndModify(uj, sj));
|
|
u64 value = SignExtend<32, u64>(((u64)h << 16) | l);
|
|
SetAcc(b.GetName(), value);
|
|
}
|
|
void mov2_mji_ax(ArpRn1 a, ArpStep1 asi, ArpStep1 asj, Ab b) {
|
|
auto [ui, uj] = GetArpRnUnit(a);
|
|
auto [si, sj] = GetArpStep(asi, asj);
|
|
u16 l = mem.DataRead(RnAddressAndModify(ui, si));
|
|
u16 h = mem.DataRead(RnAddressAndModify(uj, sj));
|
|
u64 value = SignExtend<32, u64>(((u64)h << 16) | l);
|
|
SetAcc(b.GetName(), value);
|
|
}
|
|
void mov2_abh_m(Abh ax, Abh ay, ArRn1 b, ArStep1 bs) {
|
|
u16 u = (u16)((GetAndSatAccNoFlag(ax.GetName()) >> 16) & 0xFFFF);
|
|
u16 v = (u16)((GetAndSatAccNoFlag(ay.GetName()) >> 16) & 0xFFFF);
|
|
u16 unit = GetArRnUnit(b);
|
|
u16 ua = RnAddressAndModify(unit, GetArStep(bs));
|
|
u16 va = OffsetAddress(unit, ua, GetArOffset(bs));
|
|
// keep the order
|
|
mem.DataWrite(va, v);
|
|
mem.DataWrite(ua, u);
|
|
}
|
|
void exchange_iaj(Axh a, ArpRn2 b, ArpStep2 bsi, ArpStep2 bsj) {
|
|
auto [ui, uj] = GetArpRnUnit(b);
|
|
auto [si, sj] = GetArpStep(bsi, bsj);
|
|
u16 i = RnAddressAndModify(ui, si);
|
|
u16 j = RnAddressAndModify(uj, sj);
|
|
u64 value = GetAndSatAccNoFlag(a.GetName());
|
|
mem.DataWrite(j, (u16)((value >> 16) & 0xFFFF));
|
|
value = SignExtend<32, u64>((u64)mem.DataRead(i) << 16);
|
|
SetAcc(a.GetName(), value);
|
|
}
|
|
void exchange_riaj(Axh a, ArpRn2 b, ArpStep2 bsi, ArpStep2 bsj) {
|
|
auto [ui, uj] = GetArpRnUnit(b);
|
|
auto [si, sj] = GetArpStep(bsi, bsj);
|
|
u16 i = RnAddressAndModify(ui, si);
|
|
u16 j = RnAddressAndModify(uj, sj);
|
|
u64 value = GetAndSatAccNoFlag(a.GetName());
|
|
mem.DataWrite(j, (u16)((value >> 16) & 0xFFFF));
|
|
value = SignExtend<32, u64>(((u64)mem.DataRead(i) << 16) | 0x8000);
|
|
SetAcc(a.GetName(), value);
|
|
}
|
|
void exchange_jai(Axh a, ArpRn2 b, ArpStep2 bsi, ArpStep2 bsj) {
|
|
auto [ui, uj] = GetArpRnUnit(b);
|
|
auto [si, sj] = GetArpStep(bsi, bsj);
|
|
u16 i = RnAddressAndModify(ui, si);
|
|
u16 j = RnAddressAndModify(uj, sj);
|
|
u64 value = GetAndSatAccNoFlag(a.GetName());
|
|
mem.DataWrite(i, (u16)((value >> 16) & 0xFFFF));
|
|
value = SignExtend<32, u64>((u64)mem.DataRead(j) << 16);
|
|
SetAcc(a.GetName(), value);
|
|
}
|
|
void exchange_rjai(Axh a, ArpRn2 b, ArpStep2 bsi, ArpStep2 bsj) {
|
|
auto [ui, uj] = GetArpRnUnit(b);
|
|
auto [si, sj] = GetArpStep(bsi, bsj);
|
|
u16 i = RnAddressAndModify(ui, si);
|
|
u16 j = RnAddressAndModify(uj, sj);
|
|
u64 value = GetAndSatAccNoFlag(a.GetName());
|
|
mem.DataWrite(i, (u16)((value >> 16) & 0xFFFF));
|
|
value = SignExtend<32, u64>(((u64)mem.DataRead(j) << 16) | 0x8000);
|
|
SetAcc(a.GetName(), value);
|
|
}
|
|
|
|
void ShiftBus40(u64 value, u16 sv, RegName dest) {
|
|
value &= 0xFF'FFFF'FFFF;
|
|
u64 original_sign = value >> 39;
|
|
if ((sv >> 15) == 0) {
|
|
// left shift
|
|
if (sv >= 40) {
|
|
if (regs.s == 0) {
|
|
regs.fv = value != 0;
|
|
if (regs.fv) {
|
|
regs.fvl = 1;
|
|
}
|
|
}
|
|
value = 0;
|
|
regs.fc0 = 0;
|
|
} else {
|
|
if (regs.s == 0) {
|
|
regs.fv = SignExtend<40>(value) != SignExtend(value, 40 - sv);
|
|
if (regs.fv) {
|
|
regs.fvl = 1;
|
|
}
|
|
}
|
|
value <<= sv;
|
|
regs.fc0 = (value & ((u64)1 << 40)) != 0;
|
|
}
|
|
} else {
|
|
// right shift
|
|
u16 nsv = ~sv + 1;
|
|
if (nsv >= 40) {
|
|
if (regs.s == 0) {
|
|
regs.fc0 = (value >> 39) & 1;
|
|
value = regs.fc0 ? 0xFF'FFFF'FFFF : 0;
|
|
} else {
|
|
value = 0;
|
|
regs.fc0 = 0;
|
|
}
|
|
} else {
|
|
regs.fc0 = (value & ((u64)1 << (nsv - 1))) != 0;
|
|
value >>= nsv;
|
|
if (regs.s == 0) {
|
|
value = SignExtend(value, 40 - nsv);
|
|
}
|
|
}
|
|
|
|
if (regs.s == 0) {
|
|
regs.fv = 0;
|
|
}
|
|
}
|
|
|
|
value = SignExtend<40>(value);
|
|
SetAccFlag(value);
|
|
if (regs.s == 0 && regs.sata == 0) {
|
|
if (regs.fv || SignExtend<32>(value) != value) {
|
|
regs.flm = 1;
|
|
value = original_sign == 1 ? 0xFFFF'FFFF'8000'0000 : 0x7FFF'FFFF;
|
|
}
|
|
}
|
|
SetAcc(dest, value);
|
|
}
|
|
|
|
void movs(MemImm8 a, Ab b) {
|
|
u64 value = SignExtend<16, u64>(LoadFromMemory(a));
|
|
u16 sv = regs.sv;
|
|
ShiftBus40(value, sv, b.GetName());
|
|
}
|
|
void movs(Rn a, StepZIDS as, Ab b) {
|
|
u16 address = RnAddressAndModify(a.Index(), as.GetName());
|
|
u64 value = SignExtend<16, u64>(mem.DataRead(address));
|
|
u16 sv = regs.sv;
|
|
ShiftBus40(value, sv, b.GetName());
|
|
}
|
|
void movs(Register a, Ab b) {
|
|
u64 value = SignExtend<16, u64>(RegToBus16(a.GetName()));
|
|
u16 sv = regs.sv;
|
|
ShiftBus40(value, sv, b.GetName());
|
|
}
|
|
void movs_r6_to(Ax b) {
|
|
u64 value = SignExtend<16, u64>(regs.r[6]);
|
|
u16 sv = regs.sv;
|
|
ShiftBus40(value, sv, b.GetName());
|
|
}
|
|
void movsi(RnOld a, Ab b, Imm5s s) {
|
|
u64 value = SignExtend<16, u64>(RegToBus16(a.GetName()));
|
|
u16 sv = s.Signed16();
|
|
ShiftBus40(value, sv, b.GetName());
|
|
}
|
|
|
|
void movr(ArRn2 a, ArStep2 as, Abh b) {
|
|
u16 value16 = mem.DataRead(RnAddressAndModify(GetArRnUnit(a), GetArStep(as)));
|
|
u64 value = SignExtend<32, u64>((u64)value16 << 16);
|
|
u64 result = AddSub(value, 0x8000, false);
|
|
SatAndSetAccAndFlag(b.GetName(), result);
|
|
}
|
|
void movr(Rn a, StepZIDS as, Ax b) {
|
|
u16 value16 = mem.DataRead(RnAddressAndModify(a.Index(), as.GetName()));
|
|
// Do 16-bit arithmetic. Flag C is set according to bit 16 but Flag V is always cleared
|
|
// Looks like a hardware bug to me
|
|
u64 result = (u64)value16 + 0x8000;
|
|
regs.fc0 = (u16)(result >> 16);
|
|
regs.fv = 0;
|
|
result &= 0xFFFF;
|
|
SatAndSetAccAndFlag(b.GetName(), result);
|
|
}
|
|
void movr(Register a, Ax b) {
|
|
u64 result;
|
|
if (a.GetName() == RegName::a0 || a.GetName() == RegName::a1) {
|
|
u64 value = GetAcc(a.GetName());
|
|
result = AddSub(value, 0x8000, false);
|
|
} else if (a.GetName() == RegName::p) {
|
|
u64 value = ProductToBus40(Px{0});
|
|
result = AddSub(value, 0x8000, false);
|
|
} else {
|
|
u16 value16 = RegToBus16(a.GetName());
|
|
result = (u64)value16 + 0x8000;
|
|
regs.fc0 = (u16)(result >> 16);
|
|
regs.fv = 0;
|
|
result &= 0xFFFF;
|
|
}
|
|
SatAndSetAccAndFlag(b.GetName(), result);
|
|
}
|
|
void movr(Bx a, Ax b) {
|
|
u64 value = GetAcc(a.GetName());
|
|
u64 result = AddSub(value, 0x8000, false);
|
|
SatAndSetAccAndFlag(b.GetName(), result);
|
|
}
|
|
void movr_r6_to(Ax b) {
|
|
u16 value16 = regs.r[6];
|
|
u64 result = (u64)value16 + 0x8000;
|
|
regs.fc0 = (u16)(result >> 16);
|
|
regs.fv = 0;
|
|
result &= 0xFFFF;
|
|
SatAndSetAccAndFlag(b.GetName(), result);
|
|
}
|
|
|
|
u16 Exp(u64 value) {
|
|
u64 sign = (value >> 39) & 1;
|
|
u16 bit = 38, count = 0;
|
|
while (true) {
|
|
if (((value >> bit) & 1) != sign)
|
|
break;
|
|
++count;
|
|
if (bit == 0)
|
|
break;
|
|
--bit;
|
|
}
|
|
return count - 8;
|
|
}
|
|
|
|
void ExpStore(Ax b) {
|
|
SetAcc(b.GetName(), SignExtend<16, u64>(regs.sv));
|
|
}
|
|
|
|
void exp(Bx a) {
|
|
u64 value = GetAcc(a.GetName());
|
|
regs.sv = Exp(value);
|
|
}
|
|
void exp(Bx a, Ax b) {
|
|
exp(a);
|
|
ExpStore(b);
|
|
}
|
|
void exp(Rn a, StepZIDS as) {
|
|
u16 address = RnAddressAndModify(a.Index(), as.GetName());
|
|
u64 value = SignExtend<32>((u64)mem.DataRead(address) << 16);
|
|
regs.sv = Exp(value);
|
|
}
|
|
void exp(Rn a, StepZIDS as, Ax b) {
|
|
exp(a, as);
|
|
ExpStore(b);
|
|
}
|
|
void exp(Register a) {
|
|
u64 value;
|
|
if (a.GetName() == RegName::a0 || a.GetName() == RegName::a1) {
|
|
value = GetAcc(a.GetName());
|
|
} else {
|
|
// RegName::p follows the usual rule
|
|
value = SignExtend<32>((u64)RegToBus16(a.GetName()) << 16);
|
|
}
|
|
regs.sv = Exp(value);
|
|
}
|
|
void exp(Register a, Ax b) {
|
|
exp(a);
|
|
ExpStore(b);
|
|
}
|
|
void exp_r6() {
|
|
u64 value = SignExtend<32>((u64)RegToBus16(RegName::r6) << 16);
|
|
regs.sv = Exp(value);
|
|
}
|
|
void exp_r6(Ax b) {
|
|
exp_r6();
|
|
ExpStore(b);
|
|
}
|
|
|
|
void lim(Ax a, Ax b) {
|
|
u64 value = GetAcc(a.GetName());
|
|
value = SaturateAcc(value);
|
|
SetAccAndFlag(b.GetName(), value);
|
|
}
|
|
|
|
void vtrclr0() {
|
|
regs.vtr0 = 0;
|
|
}
|
|
void vtrclr1() {
|
|
regs.vtr1 = 0;
|
|
}
|
|
void vtrclr() {
|
|
regs.vtr0 = 0;
|
|
regs.vtr1 = 0;
|
|
}
|
|
void vtrmov0(Axl a) {
|
|
SatAndSetAccAndFlag(a.GetName(), regs.vtr0);
|
|
}
|
|
void vtrmov1(Axl a) {
|
|
SatAndSetAccAndFlag(a.GetName(), regs.vtr1);
|
|
}
|
|
void vtrmov(Axl a) {
|
|
SatAndSetAccAndFlag(a.GetName(), (regs.vtr1 & 0xFF00) | (regs.vtr0 >> 8));
|
|
}
|
|
void vtrshr() {
|
|
// TODO: This instruction has one cycle delay on vtr0, but not on vtr1
|
|
regs.vtr0 = (regs.vtr0 >> 1) | (regs.fc0 << 15);
|
|
regs.vtr1 = (regs.vtr1 >> 1) | (regs.fc1 << 15);
|
|
}
|
|
|
|
void clrp0() {
|
|
ProductFromBus32(Px{0}, 0);
|
|
}
|
|
void clrp1() {
|
|
ProductFromBus32(Px{1}, 0);
|
|
}
|
|
void clrp() {
|
|
ProductFromBus32(Px{0}, 0);
|
|
ProductFromBus32(Px{1}, 0);
|
|
}
|
|
|
|
void max_ge(Ax a, StepZIDS bs) {
|
|
u64 u = GetAcc(a.GetName());
|
|
u64 v = GetAcc(CounterAcc(a.GetName()));
|
|
u64 d = v - u;
|
|
u16 r0 = RnAndModify(0, bs.GetName());
|
|
if (((d >> 63) & 1) == 0) {
|
|
regs.fm = 1;
|
|
regs.mixp = r0;
|
|
SetAcc(a.GetName(), v);
|
|
} else {
|
|
regs.fm = 0;
|
|
}
|
|
}
|
|
void max_gt(Ax a, StepZIDS bs) {
|
|
u64 u = GetAcc(a.GetName());
|
|
u64 v = GetAcc(CounterAcc(a.GetName()));
|
|
u64 d = v - u;
|
|
u16 r0 = RnAndModify(0, bs.GetName());
|
|
if (((d >> 63) & 1) == 0 && d != 0) {
|
|
regs.fm = 1;
|
|
regs.mixp = r0;
|
|
SetAcc(a.GetName(), v);
|
|
} else {
|
|
regs.fm = 0;
|
|
}
|
|
}
|
|
void min_le(Ax a, StepZIDS bs) {
|
|
u64 u = GetAcc(a.GetName());
|
|
u64 v = GetAcc(CounterAcc(a.GetName()));
|
|
u64 d = v - u;
|
|
u16 r0 = RnAndModify(0, bs.GetName());
|
|
if (((d >> 63) & 1) == 1 || d == 0) {
|
|
regs.fm = 1;
|
|
regs.mixp = r0;
|
|
SetAcc(a.GetName(), v);
|
|
} else {
|
|
regs.fm = 0;
|
|
}
|
|
}
|
|
void min_lt(Ax a, StepZIDS bs) {
|
|
u64 u = GetAcc(a.GetName());
|
|
u64 v = GetAcc(CounterAcc(a.GetName()));
|
|
u64 d = v - u;
|
|
u16 r0 = RnAndModify(0, bs.GetName());
|
|
if (((d >> 63) & 1) == 1) {
|
|
regs.fm = 1;
|
|
regs.mixp = r0;
|
|
SetAcc(a.GetName(), v);
|
|
} else {
|
|
regs.fm = 0;
|
|
}
|
|
}
|
|
|
|
void max_ge_r0(Ax a, StepZIDS bs) {
|
|
u64 u = GetAcc(a.GetName());
|
|
u16 r0 = RnAndModify(0, bs.GetName());
|
|
u64 v = SignExtend<16, u64>(mem.DataRead(RnAddress(0, r0)));
|
|
u64 d = v - u;
|
|
if (((d >> 63) & 1) == 0) {
|
|
regs.fm = 1;
|
|
regs.mixp = r0;
|
|
SetAcc(a.GetName(), v);
|
|
} else {
|
|
regs.fm = 0;
|
|
}
|
|
}
|
|
void max_gt_r0(Ax a, StepZIDS bs) {
|
|
u64 u = GetAcc(a.GetName());
|
|
u16 r0 = RnAndModify(0, bs.GetName());
|
|
u64 v = SignExtend<16, u64>(mem.DataRead(RnAddress(0, r0)));
|
|
u64 d = v - u;
|
|
if (((d >> 63) & 1) == 0 && d != 0) {
|
|
regs.fm = 1;
|
|
regs.mixp = r0;
|
|
SetAcc(a.GetName(), v);
|
|
} else {
|
|
regs.fm = 0;
|
|
}
|
|
}
|
|
void min_le_r0(Ax a, StepZIDS bs) {
|
|
u64 u = GetAcc(a.GetName());
|
|
u16 r0 = RnAndModify(0, bs.GetName());
|
|
u64 v = SignExtend<16, u64>(mem.DataRead(RnAddress(0, r0)));
|
|
u64 d = v - u;
|
|
if (((d >> 63) & 1) == 1 || d == 0) {
|
|
regs.fm = 1;
|
|
regs.mixp = r0;
|
|
SetAcc(a.GetName(), v);
|
|
} else {
|
|
regs.fm = 0;
|
|
}
|
|
}
|
|
void min_lt_r0(Ax a, StepZIDS bs) {
|
|
u64 u = GetAcc(a.GetName());
|
|
u16 r0 = RnAndModify(0, bs.GetName());
|
|
u64 v = SignExtend<16, u64>(mem.DataRead(RnAddress(0, r0)));
|
|
u64 d = v - u;
|
|
if (((d >> 63) & 1) == 1) {
|
|
regs.fm = 1;
|
|
regs.mixp = r0;
|
|
SetAcc(a.GetName(), v);
|
|
} else {
|
|
regs.fm = 0;
|
|
}
|
|
}
|
|
|
|
void divs(MemImm8 a, Ax b) {
|
|
u16 da = LoadFromMemory(a);
|
|
u64 db = GetAcc(b.GetName());
|
|
u64 value = db - ((u64)da << 15);
|
|
if (value >> 63) {
|
|
SetAccAndFlag(b.GetName(), SignExtend<40>(db << 1));
|
|
} else {
|
|
SetAccAndFlag(b.GetName(), SignExtend<40>((value << 1) + 1));
|
|
}
|
|
}
|
|
|
|
void sqr_sqr_add3(Ab a, Ab b) {
|
|
u64 value = GetAcc(a.GetName());
|
|
app(b, SumBase::Acc, false, false, false, false);
|
|
regs.x[0] = regs.y[0] = (u16)((value >> 16) & 0xFFFF);
|
|
regs.x[1] = regs.y[1] = (u16)(value & 0xFFFF);
|
|
DoMultiplication(0, true, true);
|
|
DoMultiplication(1, true, true);
|
|
}
|
|
|
|
void sqr_sqr_add3(ArRn2 a, ArStep2 as, Ab b) {
|
|
app(b, SumBase::Acc, false, false, false, false);
|
|
u16 unit = GetArRnUnit(a);
|
|
u16 address0 = RnAddressAndModify(unit, GetArStep(as));
|
|
u16 address1 = OffsetAddress(unit, address0, GetArOffset(as));
|
|
regs.x[0] = regs.y[0] = mem.DataRead(address0);
|
|
regs.x[1] = regs.y[1] = mem.DataRead(address1);
|
|
DoMultiplication(0, true, true);
|
|
DoMultiplication(1, true, true);
|
|
}
|
|
|
|
void sqr_mpysu_add3a(Ab a, Ab b) {
|
|
u64 value = GetAcc(a.GetName());
|
|
app(b, SumBase::Acc, false, false, false, true);
|
|
regs.x[0] = regs.y[0] = regs.y[1] = (u16)((value >> 16) & 0xFFFF);
|
|
regs.x[1] = (u16)(value & 0xFFFF);
|
|
DoMultiplication(0, true, true);
|
|
DoMultiplication(1, false, true);
|
|
}
|
|
|
|
void cmp(Ax a, Bx b) {
|
|
u64 va = GetAcc(a.GetName());
|
|
u64 vb = GetAcc(b.GetName());
|
|
SetAccFlag(AddSub(vb, va, true));
|
|
}
|
|
void cmp_b0_b1() {
|
|
u64 va = GetAcc(RegName::b0);
|
|
u64 vb = GetAcc(RegName::b1);
|
|
SetAccFlag(AddSub(vb, va, true));
|
|
}
|
|
void cmp_b1_b0() {
|
|
u64 va = GetAcc(RegName::b1);
|
|
u64 vb = GetAcc(RegName::b0);
|
|
SetAccFlag(AddSub(vb, va, true));
|
|
}
|
|
void cmp(Bx a, Ax b) {
|
|
u64 va = GetAcc(a.GetName());
|
|
u64 vb = GetAcc(b.GetName());
|
|
SetAccFlag(AddSub(vb, va, true));
|
|
}
|
|
void cmp_p1_to(Ax b) {
|
|
u64 va = ProductToBus40(Px{1});
|
|
u64 vb = GetAcc(b.GetName());
|
|
SetAccFlag(AddSub(vb, va, true));
|
|
}
|
|
|
|
void MinMaxVtr(RegName a, RegName b, bool min) {
|
|
u64 u = GetAcc(a);
|
|
u64 v = GetAcc(b);
|
|
u64 uh = SignExtend<24, u64>(u >> 16);
|
|
u64 ul = SignExtend<16, u64>(u & 0xFFFF);
|
|
u64 vh = SignExtend<24, u64>(v >> 16);
|
|
u64 vl = SignExtend<16, u64>(v & 0xFFFF);
|
|
u64 wh = min ? uh - vh : vh - uh;
|
|
u64 wl = min ? ul - vl : vl - ul;
|
|
|
|
regs.fc0 = !(wh >> 63);
|
|
regs.fc1 = !(wl >> 63);
|
|
|
|
wh = regs.fc0 != 0 ? vh : uh;
|
|
wl = regs.fc1 != 0 ? vl : ul;
|
|
|
|
u64 w = (wh << 16) | (wl & 0xFFFF);
|
|
SetAcc(a, w);
|
|
vtrshr();
|
|
}
|
|
|
|
void max2_vtr(Ax a) {
|
|
MinMaxVtr(a.GetName(), CounterAcc(a.GetName()), false);
|
|
}
|
|
void min2_vtr(Ax a) {
|
|
MinMaxVtr(a.GetName(), CounterAcc(a.GetName()), true);
|
|
}
|
|
void max2_vtr(Ax a, Bx b) {
|
|
MinMaxVtr(a.GetName(), b.GetName(), false);
|
|
}
|
|
void min2_vtr(Ax a, Bx b) {
|
|
MinMaxVtr(a.GetName(), b.GetName(), true);
|
|
}
|
|
void max2_vtr_movl(Ax a, Bx b, ArRn1 c, ArStep1 cs) {
|
|
MinMaxVtr(a.GetName(), b.GetName(), false);
|
|
u64 value = GetAndSatAccNoFlag(CounterAcc(a.GetName()));
|
|
u16 address = RnAddressAndModify(GetArRnUnit(c), GetArStep(cs));
|
|
u16 value16 = (u16)(value & 0xFFFF);
|
|
mem.DataWrite(address, value16);
|
|
}
|
|
void max2_vtr_movh(Ax a, Bx b, ArRn1 c, ArStep1 cs) {
|
|
MinMaxVtr(a.GetName(), b.GetName(), false);
|
|
u64 value = GetAndSatAccNoFlag(CounterAcc(a.GetName()));
|
|
u16 address = RnAddressAndModify(GetArRnUnit(c), GetArStep(cs));
|
|
u16 value16 = (u16)((value >> 16) & 0xFFFF);
|
|
mem.DataWrite(address, value16);
|
|
}
|
|
void max2_vtr_movl(Bx a, Ax b, ArRn1 c, ArStep1 cs) {
|
|
MinMaxVtr(a.GetName(), b.GetName(), false);
|
|
u64 value = GetAndSatAccNoFlag(CounterAcc(a.GetName()));
|
|
u16 address = RnAddressAndModify(GetArRnUnit(c), GetArStep(cs));
|
|
u16 value16 = (u16)(value & 0xFFFF);
|
|
mem.DataWrite(address, value16);
|
|
}
|
|
void max2_vtr_movh(Bx a, Ax b, ArRn1 c, ArStep1 cs) {
|
|
MinMaxVtr(a.GetName(), b.GetName(), false);
|
|
u64 value = GetAndSatAccNoFlag(CounterAcc(a.GetName()));
|
|
u16 address = RnAddressAndModify(GetArRnUnit(c), GetArStep(cs));
|
|
u16 value16 = (u16)((value >> 16) & 0xFFFF);
|
|
mem.DataWrite(address, value16);
|
|
}
|
|
void min2_vtr_movl(Ax a, Bx b, ArRn1 c, ArStep1 cs) {
|
|
MinMaxVtr(a.GetName(), b.GetName(), true);
|
|
u64 value = GetAndSatAccNoFlag(CounterAcc(a.GetName()));
|
|
u16 address = RnAddressAndModify(GetArRnUnit(c), GetArStep(cs));
|
|
u16 value16 = (u16)(value & 0xFFFF);
|
|
mem.DataWrite(address, value16);
|
|
}
|
|
void min2_vtr_movh(Ax a, Bx b, ArRn1 c, ArStep1 cs) {
|
|
MinMaxVtr(a.GetName(), b.GetName(), true);
|
|
u64 value = GetAndSatAccNoFlag(CounterAcc(a.GetName()));
|
|
u16 address = RnAddressAndModify(GetArRnUnit(c), GetArStep(cs));
|
|
u16 value16 = (u16)((value >> 16) & 0xFFFF);
|
|
mem.DataWrite(address, value16);
|
|
}
|
|
void min2_vtr_movl(Bx a, Ax b, ArRn1 c, ArStep1 cs) {
|
|
MinMaxVtr(a.GetName(), b.GetName(), true);
|
|
u64 value = GetAndSatAccNoFlag(CounterAcc(a.GetName()));
|
|
u16 address = RnAddressAndModify(GetArRnUnit(c), GetArStep(cs));
|
|
u16 value16 = (u16)(value & 0xFFFF);
|
|
mem.DataWrite(address, value16);
|
|
}
|
|
void min2_vtr_movh(Bx a, Ax b, ArRn1 c, ArStep1 cs) {
|
|
MinMaxVtr(a.GetName(), b.GetName(), true);
|
|
u64 value = GetAndSatAccNoFlag(CounterAcc(a.GetName()));
|
|
u16 address = RnAddressAndModify(GetArRnUnit(c), GetArStep(cs));
|
|
u16 value16 = (u16)((value >> 16) & 0xFFFF);
|
|
mem.DataWrite(address, value16);
|
|
}
|
|
void max2_vtr_movij(Ax a, Bx b, ArpRn1 c, ArpStep1 csi, ArpStep1 csj) {
|
|
MinMaxVtr(a.GetName(), b.GetName(), false);
|
|
u64 value = GetAndSatAccNoFlag(CounterAcc(a.GetName()));
|
|
u16 h = (u16)((value >> 16) & 0xFFFF);
|
|
u16 l = (u16)(value & 0xFFFF);
|
|
auto [ui, uj] = GetArpRnUnit(c);
|
|
auto [si, sj] = GetArpStep(csi, csj);
|
|
u16 i = RnAddressAndModify(ui, si);
|
|
u16 j = RnAddressAndModify(uj, sj);
|
|
mem.DataWrite(i, h);
|
|
mem.DataWrite(j, l);
|
|
}
|
|
void max2_vtr_movji(Ax a, Bx b, ArpRn1 c, ArpStep1 csi, ArpStep1 csj) {
|
|
MinMaxVtr(a.GetName(), b.GetName(), false);
|
|
u64 value = GetAndSatAccNoFlag(CounterAcc(a.GetName()));
|
|
u16 h = (u16)((value >> 16) & 0xFFFF);
|
|
u16 l = (u16)(value & 0xFFFF);
|
|
auto [ui, uj] = GetArpRnUnit(c);
|
|
auto [si, sj] = GetArpStep(csi, csj);
|
|
u16 i = RnAddressAndModify(ui, si);
|
|
u16 j = RnAddressAndModify(uj, sj);
|
|
mem.DataWrite(i, l);
|
|
mem.DataWrite(j, h);
|
|
}
|
|
void min2_vtr_movij(Ax a, Bx b, ArpRn1 c, ArpStep1 csi, ArpStep1 csj) {
|
|
MinMaxVtr(a.GetName(), b.GetName(), true);
|
|
u64 value = GetAndSatAccNoFlag(CounterAcc(a.GetName()));
|
|
u16 h = (u16)((value >> 16) & 0xFFFF);
|
|
u16 l = (u16)(value & 0xFFFF);
|
|
auto [ui, uj] = GetArpRnUnit(c);
|
|
auto [si, sj] = GetArpStep(csi, csj);
|
|
u16 i = RnAddressAndModify(ui, si);
|
|
u16 j = RnAddressAndModify(uj, sj);
|
|
mem.DataWrite(i, h);
|
|
mem.DataWrite(j, l);
|
|
}
|
|
void min2_vtr_movji(Ax a, Bx b, ArpRn1 c, ArpStep1 csi, ArpStep1 csj) {
|
|
MinMaxVtr(a.GetName(), b.GetName(), true);
|
|
u64 value = GetAndSatAccNoFlag(CounterAcc(a.GetName()));
|
|
u16 h = (u16)((value >> 16) & 0xFFFF);
|
|
u16 l = (u16)(value & 0xFFFF);
|
|
auto [ui, uj] = GetArpRnUnit(c);
|
|
auto [si, sj] = GetArpStep(csi, csj);
|
|
u16 i = RnAddressAndModify(ui, si);
|
|
u16 j = RnAddressAndModify(uj, sj);
|
|
mem.DataWrite(i, l);
|
|
mem.DataWrite(j, h);
|
|
}
|
|
|
|
template <typename ArpStepX>
|
|
void mov_sv_app(ArRn1 a, ArpStepX as, Bx b, SumBase base, bool sub_p0, bool p0_align,
|
|
bool sub_p1, bool p1_align) {
|
|
regs.sv = mem.DataRead(RnAddressAndModify(GetArRnUnit(a), GetArStep(as)));
|
|
ProductSum(base, b.GetName(), sub_p0, p0_align, sub_p1, p1_align);
|
|
}
|
|
|
|
void CodebookSearch(u16 u, u16 v, u16 r, CbsCond c) {
|
|
u64 diff = ProductToBus40(Px{0}) - ProductToBus40(Px{1});
|
|
bool cond;
|
|
switch (c.GetName()) {
|
|
case CbsCondValue::Ge:
|
|
cond = !(diff >> 63);
|
|
break;
|
|
case CbsCondValue::Gt:
|
|
cond = !(diff >> 63) && diff != 0;
|
|
break;
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
if (cond) {
|
|
regs.x[1] = regs.p0h_cbs;
|
|
regs.x[0] = regs.y[1];
|
|
regs.mixp = r;
|
|
}
|
|
regs.y[0] = u;
|
|
u16 x0 = std::exchange(regs.x[0], regs.y[0]);
|
|
DoMultiplication(0, true, true);
|
|
regs.p0h_cbs = regs.y[0] = (u16)((ProductToBus40(Px{0}) >> 16) & 0xFFFF);
|
|
regs.x[0] = x0;
|
|
regs.y[1] = v;
|
|
DoMultiplication(0, true, true);
|
|
DoMultiplication(1, true, true);
|
|
}
|
|
|
|
void cbs(Axh a, CbsCond c) {
|
|
u16 u = (u16)((GetAcc(a.GetName()) >> 16) & 0xFFFF);
|
|
u16 v = (u16)((GetAcc(CounterAcc(a.GetName())) >> 16) & 0xFFFF);
|
|
u16 r = regs.r[0];
|
|
CodebookSearch(u, v, r, c);
|
|
}
|
|
void cbs(Axh a, Bxh b, CbsCond c) {
|
|
u16 u = (u16)((GetAcc(a.GetName()) >> 16) & 0xFFFF);
|
|
u16 v = (u16)((GetAcc(b.GetName()) >> 16) & 0xFFFF);
|
|
u16 r = regs.r[0];
|
|
CodebookSearch(u, v, r, c);
|
|
}
|
|
void cbs(ArpRn1 a, ArpStep1 asi, ArpStep1 asj, CbsCond c) {
|
|
auto [ui, uj] = GetArpRnUnit(a);
|
|
auto [si, sj] = GetArpStep(asi, asj);
|
|
u16 aip = RnAndModify(ui, si);
|
|
u16 ai = RnAddress(ui, aip);
|
|
u16 aj = RnAddressAndModify(uj, sj);
|
|
u16 u = mem.DataRead(ai);
|
|
u16 v = mem.DataRead(aj);
|
|
u16 r = aip;
|
|
CodebookSearch(u, v, r, c);
|
|
}
|
|
|
|
void mma(RegName a, bool x0_sign, bool y0_sign, bool x1_sign, bool y1_sign, SumBase base,
|
|
bool sub_p0, bool p0_align, bool sub_p1, bool p1_align) {
|
|
ProductSum(base, a, sub_p0, p0_align, sub_p1, p1_align);
|
|
std::swap(regs.x[0], regs.x[1]);
|
|
DoMultiplication(0, x0_sign, y0_sign);
|
|
DoMultiplication(1, x1_sign, y1_sign);
|
|
}
|
|
|
|
template <typename ArpRnX, typename ArpStepX>
|
|
void mma(ArpRnX xy, ArpStepX i, ArpStepX j, bool dmodi, bool dmodj, RegName a, bool x0_sign,
|
|
bool y0_sign, bool x1_sign, bool y1_sign, SumBase base, bool sub_p0, bool p0_align,
|
|
bool sub_p1, bool p1_align) {
|
|
ProductSum(base, a, sub_p0, p0_align, sub_p1, p1_align);
|
|
auto [ui, uj] = GetArpRnUnit(xy);
|
|
auto [si, sj] = GetArpStep(i, j);
|
|
auto [oi, oj] = GetArpOffset(i, j);
|
|
u16 x = RnAddressAndModify(ui, si, dmodi);
|
|
u16 y = RnAddressAndModify(uj, sj, dmodj);
|
|
regs.x[0] = mem.DataRead(x);
|
|
regs.y[0] = mem.DataRead(y);
|
|
regs.x[1] = mem.DataRead(OffsetAddress(ui, x, oi, dmodi));
|
|
regs.y[1] = mem.DataRead(OffsetAddress(uj, y, oj, dmodj));
|
|
DoMultiplication(0, x0_sign, y0_sign);
|
|
DoMultiplication(1, x1_sign, y1_sign);
|
|
}
|
|
|
|
void mma_mx_xy(ArRn1 y, ArStep1 ys, RegName a, bool x0_sign, bool y0_sign, bool x1_sign,
|
|
bool y1_sign, SumBase base, bool sub_p0, bool p0_align, bool sub_p1,
|
|
bool p1_align) {
|
|
ProductSum(base, a, sub_p0, p0_align, sub_p1, p1_align);
|
|
std::swap(regs.x[0], regs.x[1]);
|
|
regs.y[0] = mem.DataRead(RnAddressAndModify(GetArRnUnit(y), GetArStep(ys)));
|
|
DoMultiplication(0, x0_sign, y0_sign);
|
|
DoMultiplication(1, x1_sign, y1_sign);
|
|
}
|
|
|
|
void mma_xy_mx(ArRn1 y, ArStep1 ys, RegName a, bool x0_sign, bool y0_sign, bool x1_sign,
|
|
bool y1_sign, SumBase base, bool sub_p0, bool p0_align, bool sub_p1,
|
|
bool p1_align) {
|
|
ProductSum(base, a, sub_p0, p0_align, sub_p1, p1_align);
|
|
std::swap(regs.x[0], regs.x[1]);
|
|
regs.y[1] = mem.DataRead(RnAddressAndModify(GetArRnUnit(y), GetArStep(ys)));
|
|
DoMultiplication(0, x0_sign, y0_sign);
|
|
DoMultiplication(1, x1_sign, y1_sign);
|
|
}
|
|
|
|
void mma_my_my(ArRn1 x, ArStep1 xs, RegName a, bool x0_sign, bool y0_sign, bool x1_sign,
|
|
bool y1_sign, SumBase base, bool sub_p0, bool p0_align, bool sub_p1,
|
|
bool p1_align) {
|
|
ProductSum(base, a, sub_p0, p0_align, sub_p1, p1_align);
|
|
u16 unit = GetArRnUnit(x);
|
|
u16 address = RnAddressAndModify(unit, GetArStep(xs));
|
|
regs.x[0] = mem.DataRead(address);
|
|
regs.x[1] = mem.DataRead(OffsetAddress(unit, address, GetArOffset(xs)));
|
|
DoMultiplication(0, x0_sign, y0_sign);
|
|
DoMultiplication(1, x1_sign, y1_sign);
|
|
}
|
|
|
|
void mma_mov(Axh u, Bxh v, ArRn1 w, ArStep1 ws, RegName a, bool x0_sign, bool y0_sign,
|
|
bool x1_sign, bool y1_sign, SumBase base, bool sub_p0, bool p0_align, bool sub_p1,
|
|
bool p1_align) {
|
|
u16 unit = GetArRnUnit(w);
|
|
u16 address = RnAddressAndModify(unit, GetArStep(ws));
|
|
u16 u_value = (u16)((GetAndSatAccNoFlag(u.GetName()) >> 16) & 0xFFFF);
|
|
u16 v_value = (u16)((GetAndSatAccNoFlag(v.GetName()) >> 16) & 0xFFFF);
|
|
// keep the order like this
|
|
mem.DataWrite(OffsetAddress(unit, address, GetArOffset(ws)), v_value);
|
|
mem.DataWrite(address, u_value);
|
|
ProductSum(base, a, sub_p0, p0_align, sub_p1, p1_align);
|
|
std::swap(regs.x[0], regs.x[1]);
|
|
DoMultiplication(0, x0_sign, y0_sign);
|
|
DoMultiplication(1, x1_sign, y1_sign);
|
|
}
|
|
|
|
void mma_mov(ArRn2 w, ArStep1 ws, RegName a, bool x0_sign, bool y0_sign, bool x1_sign,
|
|
bool y1_sign, SumBase base, bool sub_p0, bool p0_align, bool sub_p1,
|
|
bool p1_align) {
|
|
u16 unit = GetArRnUnit(w);
|
|
u16 address = RnAddressAndModify(unit, GetArStep(ws));
|
|
u16 u_value = (u16)((GetAndSatAccNoFlag(a) >> 16) & 0xFFFF);
|
|
u16 v_value = (u16)((GetAndSatAccNoFlag(CounterAcc(a)) >> 16) & 0xFFFF);
|
|
// keep the order like this
|
|
mem.DataWrite(OffsetAddress(unit, address, GetArOffset(ws)), v_value);
|
|
mem.DataWrite(address, u_value);
|
|
ProductSum(base, a, sub_p0, p0_align, sub_p1, p1_align);
|
|
std::swap(regs.x[0], regs.x[1]);
|
|
DoMultiplication(0, x0_sign, y0_sign);
|
|
DoMultiplication(1, x1_sign, y1_sign);
|
|
}
|
|
|
|
void addhp(ArRn2 a, ArStep2 as, Px b, Ax c) {
|
|
u16 address = RnAddressAndModify(GetArRnUnit(a), GetArStep(as));
|
|
u64 value = SignExtend<32, u64>(((u64)mem.DataRead(address) << 16) | 0x8000);
|
|
u64 p = ProductToBus40(b);
|
|
u64 result = AddSub(value, p, false);
|
|
SatAndSetAccAndFlag(c.GetName(), result);
|
|
}
|
|
|
|
void mov_ext0(Imm8s a) {
|
|
regs.ext[0] = a.Signed16();
|
|
}
|
|
void mov_ext1(Imm8s a) {
|
|
regs.ext[1] = a.Signed16();
|
|
}
|
|
void mov_ext2(Imm8s a) {
|
|
regs.ext[2] = a.Signed16();
|
|
}
|
|
void mov_ext3(Imm8s a) {
|
|
regs.ext[3] = a.Signed16();
|
|
}
|
|
|
|
private:
|
|
CoreTiming& core_timing;
|
|
RegisterState& regs;
|
|
MemoryInterface& mem;
|
|
|
|
std::array<std::atomic<bool>, 3> interrupt_pending{{false, false, false}};
|
|
std::atomic<bool> vinterrupt_pending{false};
|
|
std::atomic<bool> vinterrupt_context_switch;
|
|
std::atomic<u32> vinterrupt_address;
|
|
|
|
bool idle = false;
|
|
|
|
u64 GetAcc(RegName name) const {
|
|
switch (name) {
|
|
case RegName::a0:
|
|
case RegName::a0h:
|
|
case RegName::a0l:
|
|
case RegName::a0e:
|
|
return regs.a[0];
|
|
case RegName::a1:
|
|
case RegName::a1h:
|
|
case RegName::a1l:
|
|
case RegName::a1e:
|
|
return regs.a[1];
|
|
case RegName::b0:
|
|
case RegName::b0h:
|
|
case RegName::b0l:
|
|
case RegName::b0e:
|
|
return regs.b[0];
|
|
case RegName::b1:
|
|
case RegName::b1h:
|
|
case RegName::b1l:
|
|
case RegName::b1e:
|
|
return regs.b[1];
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
|
|
u64 SaturateAccNoFlag(u64 value) const {
|
|
if (value != SignExtend<32>(value)) {
|
|
if ((value >> 39) != 0)
|
|
return 0xFFFF'FFFF'8000'0000;
|
|
else
|
|
return 0x0000'0000'7FFF'FFFF;
|
|
}
|
|
return value;
|
|
}
|
|
|
|
u64 SaturateAcc(u64 value) {
|
|
if (value != SignExtend<32>(value)) {
|
|
regs.flm = 1;
|
|
if ((value >> 39) != 0)
|
|
return 0xFFFF'FFFF'8000'0000;
|
|
else
|
|
return 0x0000'0000'7FFF'FFFF;
|
|
}
|
|
// note: flm doesn't change value otherwise
|
|
return value;
|
|
}
|
|
|
|
u64 GetAndSatAcc(RegName name) {
|
|
u64 value = GetAcc(name);
|
|
if (!regs.sat) {
|
|
return SaturateAcc(value);
|
|
}
|
|
return value;
|
|
}
|
|
|
|
u64 GetAndSatAccNoFlag(RegName name) const {
|
|
u64 value = GetAcc(name);
|
|
if (!regs.sat) {
|
|
return SaturateAccNoFlag(value);
|
|
}
|
|
return value;
|
|
}
|
|
|
|
u16 RegToBus16(RegName reg, bool enable_sat_for_mov = false) {
|
|
switch (reg) {
|
|
case RegName::a0:
|
|
case RegName::a1:
|
|
case RegName::b0:
|
|
case RegName::b1:
|
|
// get aXl, but unlike using RegName::aXl, this does never saturate.
|
|
// This only happen to insturctions using "Register" operand,
|
|
// and doesn't apply to all instructions. Need test and special check.
|
|
return GetAcc(reg) & 0xFFFF;
|
|
case RegName::a0l:
|
|
case RegName::a1l:
|
|
case RegName::b0l:
|
|
case RegName::b1l:
|
|
if (enable_sat_for_mov) {
|
|
return GetAndSatAcc(reg) & 0xFFFF;
|
|
}
|
|
return GetAcc(reg) & 0xFFFF;
|
|
case RegName::a0h:
|
|
case RegName::a1h:
|
|
case RegName::b0h:
|
|
case RegName::b1h:
|
|
if (enable_sat_for_mov) {
|
|
return (GetAndSatAcc(reg) >> 16) & 0xFFFF;
|
|
}
|
|
return (GetAcc(reg) >> 16) & 0xFFFF;
|
|
case RegName::a0e:
|
|
case RegName::a1e:
|
|
case RegName::b0e:
|
|
case RegName::b1e:
|
|
UNREACHABLE();
|
|
|
|
case RegName::r0:
|
|
return regs.r[0];
|
|
case RegName::r1:
|
|
return regs.r[1];
|
|
case RegName::r2:
|
|
return regs.r[2];
|
|
case RegName::r3:
|
|
return regs.r[3];
|
|
case RegName::r4:
|
|
return regs.r[4];
|
|
case RegName::r5:
|
|
return regs.r[5];
|
|
case RegName::r6:
|
|
return regs.r[6];
|
|
case RegName::r7:
|
|
return regs.r[7];
|
|
|
|
case RegName::y0:
|
|
return regs.y[0];
|
|
case RegName::p:
|
|
// This only happen to insturctions using "Register" operand,
|
|
// and doesn't apply to all instructions. Need test and special check.
|
|
return (ProductToBus40(Px{0}) >> 16) & 0xFFFF;
|
|
|
|
case RegName::pc:
|
|
UNREACHABLE();
|
|
case RegName::sp:
|
|
return regs.sp;
|
|
case RegName::sv:
|
|
return regs.sv;
|
|
case RegName::lc:
|
|
return regs.Lc();
|
|
|
|
case RegName::ar0:
|
|
return regs.Get<ar0>();
|
|
case RegName::ar1:
|
|
return regs.Get<ar1>();
|
|
|
|
case RegName::arp0:
|
|
return regs.Get<arp0>();
|
|
case RegName::arp1:
|
|
return regs.Get<arp1>();
|
|
case RegName::arp2:
|
|
return regs.Get<arp2>();
|
|
case RegName::arp3:
|
|
return regs.Get<arp3>();
|
|
|
|
case RegName::ext0:
|
|
return regs.ext[0];
|
|
case RegName::ext1:
|
|
return regs.ext[1];
|
|
case RegName::ext2:
|
|
return regs.ext[2];
|
|
case RegName::ext3:
|
|
return regs.ext[3];
|
|
|
|
case RegName::stt0:
|
|
return regs.Get<stt0>();
|
|
case RegName::stt1:
|
|
return regs.Get<stt1>();
|
|
case RegName::stt2:
|
|
return regs.Get<stt2>();
|
|
|
|
case RegName::st0:
|
|
return regs.Get<st0>();
|
|
case RegName::st1:
|
|
return regs.Get<st1>();
|
|
case RegName::st2:
|
|
return regs.Get<st2>();
|
|
|
|
case RegName::cfgi:
|
|
return regs.Get<cfgi>();
|
|
case RegName::cfgj:
|
|
return regs.Get<cfgj>();
|
|
|
|
case RegName::mod0:
|
|
return regs.Get<mod0>();
|
|
case RegName::mod1:
|
|
return regs.Get<mod1>();
|
|
case RegName::mod2:
|
|
return regs.Get<mod2>();
|
|
case RegName::mod3:
|
|
return regs.Get<mod3>();
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
|
|
void SetAccFlag(u64 value) {
|
|
regs.fz = value == 0;
|
|
regs.fm = (value >> 39) != 0;
|
|
regs.fe = value != SignExtend<32>(value);
|
|
u64 bit31 = (value >> 31) & 1;
|
|
u64 bit30 = (value >> 30) & 1;
|
|
regs.fn = regs.fz || (!regs.fe && (bit31 ^ bit30) != 0);
|
|
}
|
|
|
|
void SetAcc(RegName name, u64 value) {
|
|
switch (name) {
|
|
case RegName::a0:
|
|
case RegName::a0h:
|
|
case RegName::a0l:
|
|
case RegName::a0e:
|
|
regs.a[0] = value;
|
|
break;
|
|
case RegName::a1:
|
|
case RegName::a1h:
|
|
case RegName::a1l:
|
|
case RegName::a1e:
|
|
regs.a[1] = value;
|
|
break;
|
|
case RegName::b0:
|
|
case RegName::b0h:
|
|
case RegName::b0l:
|
|
case RegName::b0e:
|
|
regs.b[0] = value;
|
|
break;
|
|
case RegName::b1:
|
|
case RegName::b1h:
|
|
case RegName::b1l:
|
|
case RegName::b1e:
|
|
regs.b[1] = value;
|
|
break;
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
|
|
void SatAndSetAccAndFlag(RegName name, u64 value) {
|
|
SetAccFlag(value);
|
|
if (!regs.sata) {
|
|
value = SaturateAcc(value);
|
|
}
|
|
SetAcc(name, value);
|
|
}
|
|
|
|
void SetAccAndFlag(RegName name, u64 value) {
|
|
SetAccFlag(value);
|
|
SetAcc(name, value);
|
|
}
|
|
|
|
void RegFromBus16(RegName reg, u16 value) {
|
|
switch (reg) {
|
|
case RegName::a0:
|
|
case RegName::a1:
|
|
case RegName::b0:
|
|
case RegName::b1:
|
|
SatAndSetAccAndFlag(reg, SignExtend<16, u64>(value));
|
|
break;
|
|
case RegName::a0l:
|
|
case RegName::a1l:
|
|
case RegName::b0l:
|
|
case RegName::b1l:
|
|
SatAndSetAccAndFlag(reg, (u64)value);
|
|
break;
|
|
case RegName::a0h:
|
|
case RegName::a1h:
|
|
case RegName::b0h:
|
|
case RegName::b1h:
|
|
SatAndSetAccAndFlag(reg, SignExtend<32, u64>(value << 16));
|
|
break;
|
|
case RegName::a0e:
|
|
case RegName::a1e:
|
|
case RegName::b0e:
|
|
case RegName::b1e:
|
|
UNREACHABLE();
|
|
|
|
case RegName::r0:
|
|
regs.r[0] = value;
|
|
break;
|
|
case RegName::r1:
|
|
regs.r[1] = value;
|
|
break;
|
|
case RegName::r2:
|
|
regs.r[2] = value;
|
|
break;
|
|
case RegName::r3:
|
|
regs.r[3] = value;
|
|
break;
|
|
case RegName::r4:
|
|
regs.r[4] = value;
|
|
break;
|
|
case RegName::r5:
|
|
regs.r[5] = value;
|
|
break;
|
|
case RegName::r6:
|
|
regs.r[6] = value;
|
|
break;
|
|
case RegName::r7:
|
|
regs.r[7] = value;
|
|
break;
|
|
|
|
case RegName::y0:
|
|
regs.y[0] = value;
|
|
break;
|
|
case RegName::p: // p0h
|
|
regs.pe[0] = value > 0x7FFF;
|
|
regs.p[0] = (regs.p[0] & 0xFFFF) | (value << 16);
|
|
break;
|
|
|
|
case RegName::pc:
|
|
UNREACHABLE();
|
|
case RegName::sp:
|
|
regs.sp = value;
|
|
break;
|
|
case RegName::sv:
|
|
regs.sv = value;
|
|
break;
|
|
case RegName::lc:
|
|
regs.Lc() = value;
|
|
break;
|
|
|
|
case RegName::ar0:
|
|
regs.Set<ar0>(value);
|
|
break;
|
|
case RegName::ar1:
|
|
regs.Set<ar1>(value);
|
|
break;
|
|
|
|
case RegName::arp0:
|
|
regs.Set<arp0>(value);
|
|
break;
|
|
case RegName::arp1:
|
|
regs.Set<arp1>(value);
|
|
break;
|
|
case RegName::arp2:
|
|
regs.Set<arp2>(value);
|
|
break;
|
|
case RegName::arp3:
|
|
regs.Set<arp3>(value);
|
|
break;
|
|
|
|
case RegName::ext0:
|
|
regs.ext[0] = value;
|
|
break;
|
|
case RegName::ext1:
|
|
regs.ext[1] = value;
|
|
break;
|
|
case RegName::ext2:
|
|
regs.ext[2] = value;
|
|
break;
|
|
case RegName::ext3:
|
|
regs.ext[3] = value;
|
|
break;
|
|
|
|
case RegName::stt0:
|
|
regs.Set<stt0>(value);
|
|
break;
|
|
case RegName::stt1:
|
|
regs.Set<stt1>(value);
|
|
break;
|
|
case RegName::stt2:
|
|
regs.Set<stt2>(value);
|
|
break;
|
|
|
|
case RegName::st0:
|
|
regs.Set<st0>(value);
|
|
break;
|
|
case RegName::st1:
|
|
regs.Set<st1>(value);
|
|
break;
|
|
case RegName::st2:
|
|
regs.Set<st2>(value);
|
|
break;
|
|
|
|
case RegName::cfgi:
|
|
regs.Set<cfgi>(value);
|
|
break;
|
|
case RegName::cfgj:
|
|
regs.Set<cfgj>(value);
|
|
break;
|
|
|
|
case RegName::mod0:
|
|
regs.Set<mod0>(value);
|
|
break;
|
|
case RegName::mod1:
|
|
regs.Set<mod1>(value);
|
|
break;
|
|
case RegName::mod2:
|
|
regs.Set<mod2>(value);
|
|
break;
|
|
case RegName::mod3:
|
|
regs.Set<mod3>(value);
|
|
break;
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
|
|
template <typename ArRnX>
|
|
u16 GetArRnUnit(ArRnX arrn) const {
|
|
static_assert(std::is_same_v<ArRnX, ArRn1> || std::is_same_v<ArRnX, ArRn2>);
|
|
return regs.arrn[arrn.Index()];
|
|
}
|
|
|
|
template <typename ArpRnX>
|
|
std::tuple<u16, u16> GetArpRnUnit(ArpRnX arprn) const {
|
|
static_assert(std::is_same_v<ArpRnX, ArpRn1> || std::is_same_v<ArpRnX, ArpRn2>);
|
|
return std::make_tuple(regs.arprni[arprn.Index()], regs.arprnj[arprn.Index()] + 4);
|
|
}
|
|
|
|
static StepValue ConvertArStep(u16 arvalue) {
|
|
switch (arvalue) {
|
|
case 0:
|
|
return StepValue::Zero;
|
|
case 1:
|
|
return StepValue::Increase;
|
|
case 2:
|
|
return StepValue::Decrease;
|
|
case 3:
|
|
return StepValue::PlusStep;
|
|
case 4:
|
|
return StepValue::Increase2Mode1;
|
|
case 5:
|
|
return StepValue::Decrease2Mode1;
|
|
case 6:
|
|
return StepValue::Increase2Mode2;
|
|
case 7:
|
|
return StepValue::Decrease2Mode2;
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
|
|
template <typename ArStepX>
|
|
StepValue GetArStep(ArStepX arstep) const {
|
|
static_assert(std::is_same_v<ArStepX, ArStep1> || std::is_same_v<ArStepX, ArStep1Alt> ||
|
|
std::is_same_v<ArStepX, ArStep2>);
|
|
return ConvertArStep(regs.arstep[arstep.Index()]);
|
|
}
|
|
|
|
template <typename ArpStepX>
|
|
std::tuple<StepValue, StepValue> GetArpStep(ArpStepX arpstepi, ArpStepX arpstepj) const {
|
|
static_assert(std::is_same_v<ArpStepX, ArpStep1> || std::is_same_v<ArpStepX, ArpStep2>);
|
|
return std::make_tuple(ConvertArStep(regs.arpstepi[arpstepi.Index()]),
|
|
ConvertArStep(regs.arpstepj[arpstepj.Index()]));
|
|
}
|
|
|
|
enum class OffsetValue : u16 {
|
|
Zero = 0,
|
|
PlusOne = 1,
|
|
MinusOne = 2,
|
|
MinusOneDmod = 3,
|
|
};
|
|
|
|
template <typename ArStepX>
|
|
OffsetValue GetArOffset(ArStepX arstep) const {
|
|
static_assert(std::is_same_v<ArStepX, ArStep1> || std::is_same_v<ArStepX, ArStep2>);
|
|
return (OffsetValue)regs.aroffset[arstep.Index()];
|
|
}
|
|
|
|
template <typename ArpStepX>
|
|
std::tuple<OffsetValue, OffsetValue> GetArpOffset(ArpStepX arpstepi, ArpStepX arpstepj) const {
|
|
static_assert(std::is_same_v<ArpStepX, ArpStep1> || std::is_same_v<ArpStepX, ArpStep2>);
|
|
return std::make_tuple((OffsetValue)regs.arpoffseti[arpstepi.Index()],
|
|
(OffsetValue)regs.arpoffsetj[arpstepj.Index()]);
|
|
}
|
|
|
|
u16 RnAddress(unsigned unit, unsigned value) {
|
|
u16 ret = value;
|
|
if (regs.br[unit] && !regs.m[unit]) {
|
|
ret = BitReverse(ret);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
u16 RnAddressAndModify(unsigned unit, StepValue step, bool dmod = false) {
|
|
return RnAddress(unit, RnAndModify(unit, step, dmod));
|
|
}
|
|
|
|
u16 OffsetAddress(unsigned unit, u16 address, OffsetValue offset, bool dmod = false) {
|
|
if (offset == OffsetValue::Zero)
|
|
return address;
|
|
if (offset == OffsetValue::MinusOneDmod) {
|
|
return address - 1;
|
|
}
|
|
bool emod = regs.m[unit] & !regs.br[unit] & !dmod;
|
|
u16 mod = unit < 4 ? regs.modi : regs.modj;
|
|
u16 mask = 1; // mod = 0 still have one bit mask
|
|
for (unsigned i = 0; i < 9; ++i) {
|
|
mask |= mod >> i;
|
|
}
|
|
if (offset == OffsetValue::PlusOne) {
|
|
if (!emod)
|
|
return address + 1;
|
|
if ((address & mask) == mod)
|
|
return address & ~mask;
|
|
return address + 1;
|
|
} else { // OffsetValue::MinusOne
|
|
if (!emod)
|
|
return address - 1;
|
|
throw UnimplementedException();
|
|
// TODO: sometimes this would return two addresses,
|
|
// neither of which is the original Rn value.
|
|
// This only happens for memory writing, but not for memory reading.
|
|
// Might be some undefined behaviour.
|
|
if ((address & mask) == 0)
|
|
return address | mod;
|
|
return address - 1;
|
|
}
|
|
}
|
|
|
|
u16 StepAddress(unsigned unit, u16 address, StepValue step, bool dmod = false) {
|
|
u16 s;
|
|
bool legacy = regs.cmd;
|
|
bool step2_mode1 = false;
|
|
bool step2_mode2 = false;
|
|
switch (step) {
|
|
case StepValue::Zero:
|
|
s = 0;
|
|
break;
|
|
case StepValue::Increase:
|
|
s = 1;
|
|
break;
|
|
case StepValue::Decrease:
|
|
s = 0xFFFF;
|
|
break;
|
|
// TODO: Increase/Decrease2Mode1/2 sometimes have wrong result if Offset=+/-1.
|
|
// This however never happens with modr instruction.
|
|
case StepValue::Increase2Mode1:
|
|
s = 2;
|
|
step2_mode1 = !legacy;
|
|
break;
|
|
case StepValue::Decrease2Mode1:
|
|
s = 0xFFFE;
|
|
step2_mode1 = !legacy;
|
|
break;
|
|
case StepValue::Increase2Mode2:
|
|
s = 2;
|
|
step2_mode2 = !legacy;
|
|
break;
|
|
case StepValue::Decrease2Mode2:
|
|
s = 0xFFFE;
|
|
step2_mode2 = !legacy;
|
|
break;
|
|
case StepValue::PlusStep: {
|
|
if (regs.br[unit] && !regs.m[unit]) {
|
|
s = unit < 4 ? regs.stepi0 : regs.stepj0;
|
|
} else {
|
|
s = unit < 4 ? regs.stepi : regs.stepj;
|
|
s = SignExtend<7>(s);
|
|
}
|
|
if (regs.stp16 == 1 && !legacy) {
|
|
s = unit < 4 ? regs.stepi0 : regs.stepj0;
|
|
if (regs.m[unit]) {
|
|
s = SignExtend<9>(s);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
|
|
if (s == 0)
|
|
return address;
|
|
|
|
if (!dmod && !regs.br[unit] && regs.m[unit]) {
|
|
u16 mod = unit < 4 ? regs.modi : regs.modj;
|
|
|
|
if (mod == 0) {
|
|
return address;
|
|
}
|
|
|
|
if (mod == 1 && step2_mode2) {
|
|
return address;
|
|
}
|
|
|
|
unsigned iteration = 1;
|
|
if (step2_mode1) {
|
|
iteration = 2;
|
|
s = SignExtend<15, u16>(s >> 1);
|
|
}
|
|
|
|
for (unsigned i = 0; i < iteration; ++i) {
|
|
if (legacy || step2_mode2) {
|
|
bool negative = false;
|
|
u16 m = mod;
|
|
if (s >> 15) {
|
|
negative = true;
|
|
m |= ~s;
|
|
} else {
|
|
m |= s;
|
|
}
|
|
|
|
u16 mask = (1 << std20::log2p1(m)) - 1;
|
|
u16 next;
|
|
if (!negative) {
|
|
if ((address & mask) == mod && (!step2_mode2 || mod != mask)) {
|
|
next = 0;
|
|
} else {
|
|
next = (address + s) & mask;
|
|
}
|
|
} else {
|
|
if ((address & mask) == 0 && (!step2_mode2 || mod != mask)) {
|
|
next = mod;
|
|
} else {
|
|
next = (address + s) & mask;
|
|
}
|
|
}
|
|
address &= ~mask;
|
|
address |= next;
|
|
} else {
|
|
u16 mask = (1 << std20::log2p1(mod)) - 1;
|
|
u16 next;
|
|
if (s < 0x8000) {
|
|
next = (address + s) & mask;
|
|
if (next == ((mod + 1) & mask)) {
|
|
next = 0;
|
|
}
|
|
} else {
|
|
next = address & mask;
|
|
if (next == 0) {
|
|
next = mod + 1;
|
|
}
|
|
next += s;
|
|
next &= mask;
|
|
}
|
|
address &= ~mask;
|
|
address |= next;
|
|
}
|
|
}
|
|
} else {
|
|
address += s;
|
|
}
|
|
return address;
|
|
}
|
|
|
|
u16 RnAndModify(unsigned unit, StepValue step, bool dmod = false) {
|
|
u16 ret = regs.r[unit];
|
|
if ((unit == 3 && regs.epi) || (unit == 7 && regs.epj)) {
|
|
if (step != StepValue::Increase2Mode1 && step != StepValue::Decrease2Mode1 &&
|
|
step != StepValue::Increase2Mode2 && step != StepValue::Decrease2Mode2) {
|
|
regs.r[unit] = 0;
|
|
return ret;
|
|
}
|
|
}
|
|
regs.r[unit] = StepAddress(unit, regs.r[unit], step, dmod);
|
|
return ret;
|
|
}
|
|
|
|
u32 ProductToBus32_NoShift(Px reg) const {
|
|
return regs.p[reg.Index()];
|
|
}
|
|
|
|
u64 ProductToBus40(Px reg) const {
|
|
u16 unit = reg.Index();
|
|
u64 value = regs.p[unit] | ((u64)regs.pe[unit] << 32);
|
|
switch (regs.ps[unit]) {
|
|
case 0:
|
|
value = SignExtend<33>(value);
|
|
break;
|
|
case 1:
|
|
value >>= 1;
|
|
value = SignExtend<32>(value);
|
|
break;
|
|
case 2:
|
|
value <<= 1;
|
|
value = SignExtend<34>(value);
|
|
break;
|
|
case 3:
|
|
value <<= 2;
|
|
value = SignExtend<35>(value);
|
|
break;
|
|
}
|
|
return value;
|
|
}
|
|
|
|
void ProductFromBus32(Px reg, u32 value) {
|
|
u16 unit = reg.Index();
|
|
regs.p[unit] = value;
|
|
regs.pe[unit] = value >> 31;
|
|
}
|
|
|
|
static RegName CounterAcc(RegName in) {
|
|
static std::unordered_map<RegName, RegName> map{
|
|
{RegName::a0, RegName::a1}, {RegName::a1, RegName::a0},
|
|
{RegName::b0, RegName::b1}, {RegName::b1, RegName::b0},
|
|
{RegName::a0l, RegName::a1l}, {RegName::a1l, RegName::a0l},
|
|
{RegName::b0l, RegName::b1l}, {RegName::b1l, RegName::b0l},
|
|
{RegName::a0h, RegName::a1h}, {RegName::a1h, RegName::a0h},
|
|
{RegName::b0h, RegName::b1h}, {RegName::b1h, RegName::b0h},
|
|
{RegName::a0e, RegName::a1e}, {RegName::a1e, RegName::a0e},
|
|
{RegName::b0e, RegName::b1e}, {RegName::b1e, RegName::b0e},
|
|
};
|
|
return map.at(in);
|
|
}
|
|
|
|
const std::vector<Matcher<Interpreter>> decoders = GetDecoderTable<Interpreter>();
|
|
};
|
|
|
|
} // namespace Teakra
|