2021-09-06 00:09:15 +00:00
|
|
|
/* This file is included into vb.c and cannot be compiled on its own. */
|
|
|
|
#ifdef VBAPI
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/********************************* Constants *********************************/
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Pipeline stages */
|
2021-09-19 01:31:40 +00:00
|
|
|
#define CPU_FETCH 0
|
2023-03-11 00:44:57 +00:00
|
|
|
#define CPU_EXECUTE_A 1
|
|
|
|
#define CPU_EXECUTE_B 2
|
|
|
|
#define CPU_HALT 3
|
2021-09-19 01:31:40 +00:00
|
|
|
#define CPU_FATAL 4
|
|
|
|
|
|
|
|
|
2021-09-06 00:09:15 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/*********************************** Types ***********************************/
|
2021-09-06 00:09:15 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Handler types */
|
|
|
|
typedef int (*ExecuteAProc )(VB *);
|
|
|
|
typedef void (*ExecuteBProc )(VB *);
|
|
|
|
typedef void (*OperationProc)(VB *, int32_t *, int32_t);
|
|
|
|
typedef int32_t (*OperandProc )(VB *);
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Opcode descriptor */
|
|
|
|
typedef struct {
|
|
|
|
ExecuteAProc executeA; /* Execute handler (no state change) */
|
|
|
|
ExecuteBProc executeB; /* Execute handler (update state) */
|
|
|
|
OperationProc operation; /* Operation handler */
|
|
|
|
OperandProc operand; /* Operand handler */
|
|
|
|
uint8_t size; /* Total size in halfwords */
|
|
|
|
uint8_t aux; /* Number of clocks or access data type */
|
|
|
|
} OpDef;
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Floating-point auxiliary memory */
|
|
|
|
typedef struct {
|
|
|
|
float f32; /* 32-bit result */
|
|
|
|
double f64; /* 64-bit result */
|
|
|
|
} FloatAux;
|
2021-09-19 01:31:40 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/***************************** Utility Functions *****************************/
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Check for an interrupt exception condition */
|
|
|
|
static int cpuCheckIRQs(VB *vb) {
|
|
|
|
int x; /* Iterator */
|
|
|
|
|
|
|
|
/* Interrupts are masked */
|
|
|
|
if (vb->cpu.psw.id || vb->cpu.psw.ep || vb->cpu.psw.np)
|
2021-09-19 01:31:40 +00:00
|
|
|
return 0;
|
2023-03-11 00:44:57 +00:00
|
|
|
|
|
|
|
/* Check for interrupt requests */
|
|
|
|
for (x = 4; x >= vb->cpu.psw.i; x--) {
|
|
|
|
if (!vb->cpu.irq[x])
|
|
|
|
continue;
|
|
|
|
vb->cpu.exception = 0xFE00 | x << 4;
|
|
|
|
return 1;
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* No interrupt */
|
|
|
|
return 0;
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Test a condition */
|
|
|
|
static int cpuCondition(VB *vb, int index) {
|
|
|
|
switch (index) {
|
|
|
|
case 0: return vb->cpu.psw.ov; /* V */
|
|
|
|
case 1: return vb->cpu.psw.cy; /* C, L */
|
|
|
|
case 2: return vb->cpu.psw.z; /* E, Z */
|
|
|
|
case 3: return vb->cpu.psw.cy | vb->cpu.psw.z; /* NH */
|
|
|
|
case 4: return vb->cpu.psw.s; /* N */
|
|
|
|
case 5: return 1; /* T */
|
|
|
|
case 6: return vb->cpu.psw.ov ^ vb->cpu.psw.s; /* LT */
|
|
|
|
case 7: return (vb->cpu.psw.ov^vb->cpu.psw.s)|vb->cpu.psw.z; /* LE */
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
2023-03-11 00:44:57 +00:00
|
|
|
return !cpuCondition(vb, index - 8);
|
|
|
|
}
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Retrieve the value of a system register */
|
|
|
|
static uint32_t cpuGetSystemRegister(VB *vb, int id) {
|
|
|
|
switch (id) {
|
|
|
|
case VB_ADTRE: return vb->cpu.adtre;
|
|
|
|
case VB_EIPC : return vb->cpu.eipc;
|
|
|
|
case VB_EIPSW: return vb->cpu.eipsw;
|
|
|
|
case VB_FEPC : return vb->cpu.fepc;
|
|
|
|
case VB_FEPSW: return vb->cpu.fepsw;
|
|
|
|
case VB_PIR : return 0x00005346;
|
|
|
|
case VB_TKCW : return 0x000000E0;
|
|
|
|
case 29 : return vb->cpu.sr29;
|
|
|
|
case 30 : return 0x00000004;
|
|
|
|
case 31 : return vb->cpu.sr31;
|
|
|
|
case VB_CHCW : return
|
|
|
|
(uint32_t) vb->cpu.chcw.ice << 1
|
|
|
|
;
|
|
|
|
case VB_ECR : return
|
|
|
|
(uint32_t) vb->cpu.ecr.fecc << 16 |
|
|
|
|
(uint32_t) vb->cpu.ecr.eicc
|
|
|
|
;
|
|
|
|
case VB_PSW : return
|
|
|
|
(uint32_t) vb->cpu.psw.i << 16 |
|
|
|
|
(uint32_t) vb->cpu.psw.np << 15 |
|
|
|
|
(uint32_t) vb->cpu.psw.ep << 14 |
|
|
|
|
(uint32_t) vb->cpu.psw.ae << 13 |
|
|
|
|
(uint32_t) vb->cpu.psw.id << 12 |
|
|
|
|
(uint32_t) vb->cpu.psw.fro << 9 |
|
|
|
|
(uint32_t) vb->cpu.psw.fiv << 8 |
|
|
|
|
(uint32_t) vb->cpu.psw.fzd << 7 |
|
|
|
|
(uint32_t) vb->cpu.psw.fov << 6 |
|
|
|
|
(uint32_t) vb->cpu.psw.fud << 5 |
|
|
|
|
(uint32_t) vb->cpu.psw.fpr << 4 |
|
|
|
|
(uint32_t) vb->cpu.psw.cy << 3 |
|
|
|
|
(uint32_t) vb->cpu.psw.ov << 2 |
|
|
|
|
(uint32_t) vb->cpu.psw.s << 1 |
|
|
|
|
(uint32_t) vb->cpu.psw.z
|
|
|
|
;
|
|
|
|
}
|
|
|
|
return 0; /* Invalid ID */
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Read a memory value from the bus */
|
|
|
|
static int cpuRead(VB *vb, uint32_t address, int type, int32_t *value) {
|
|
|
|
VBAccess access; /* Bus access descriptor */
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Retrieve the value from the simulation state */
|
|
|
|
access.clocks = 0; /* TODO: Needs research */
|
|
|
|
access.value = busRead(vb, address, type);
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Call the breakpoint handler */
|
|
|
|
if (vb->onRead != NULL) {
|
|
|
|
access.address = address;
|
|
|
|
access.type = type;
|
|
|
|
if (vb->onRead(vb, &access))
|
2021-09-19 01:31:40 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Post-processing */
|
|
|
|
vb->cpu.clocks += access.clocks;
|
|
|
|
*value = access.value;
|
2021-09-19 01:31:40 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Fetch an instruction code unit from the bus */
|
|
|
|
static int cpuReadFetch(VB *vb) {
|
|
|
|
VBAccess access; /* Bus access descriptor */
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Retrieve the value from the simulation state */
|
|
|
|
access.address = vb->cpu.pc + (vb->cpu.step << 1);
|
|
|
|
access.clocks = 0; /* TODO: Prefetch makes this tricky */
|
|
|
|
access.value = busRead(vb, access.address, VB_U16);
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Call the breakpoint handler */
|
|
|
|
if (vb->onFetch != NULL) {
|
|
|
|
access.type = VB_U16;
|
|
|
|
if (vb->onFetch(vb, vb->cpu.step, &access))
|
|
|
|
return 1;
|
|
|
|
}
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Post-processing */
|
|
|
|
vb->cpu.clocks += access.clocks;
|
|
|
|
vb->cpu.inst.code[vb->cpu.step++] = access.value;
|
|
|
|
return 0;
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Detect a floating-point reserved operand */
|
|
|
|
#define cpuFRO(x) ( \
|
|
|
|
( \
|
|
|
|
((x) & 0x007FFFFF) != 0x00000000 && /* Is not zero */ \
|
|
|
|
((x) & 0x7F800000) == 0x00000000 /* Is denormal */ \
|
|
|
|
) || ((x) & 0x7F800000) == 0x7F800000 /* Is indefinite/NaN */ \
|
|
|
|
)
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Specify a value for a system register */
|
|
|
|
static uint32_t cpuSetSystemRegister(VB *vb,int id,uint32_t value,int debug) {
|
|
|
|
switch (id) {
|
|
|
|
case VB_ADTRE: return vb->cpu.adtre = value & 0xFFFFFFFE;
|
|
|
|
case VB_EIPC : return vb->cpu.eipc = value & 0xFFFFFFFE;
|
|
|
|
case VB_EIPSW: return vb->cpu.eipsw = value & 0x000FF3FF;
|
|
|
|
case VB_FEPC : return vb->cpu.fepc = value & 0xFFFFFFFE;
|
|
|
|
case VB_FEPSW: return vb->cpu.fepsw = value & 0x000FF3FF;
|
|
|
|
case VB_PIR : return 0x00005346;
|
|
|
|
case VB_TKCW : return 0x000000E0;
|
|
|
|
case 29 : return vb->cpu.sr29 = value;
|
|
|
|
case 30 : return 0x00000004;
|
|
|
|
case 31 : return
|
|
|
|
vb->cpu.sr31 = debug || value < (uint32_t) 0x80000000 ?
|
|
|
|
value : (uint32_t) -(int32_t)value;
|
|
|
|
case VB_CHCW:
|
|
|
|
/* TODO: Manage cache functions */
|
|
|
|
vb->cpu.chcw.ice = value >> 1 & 1;
|
|
|
|
return value & 0x00000002;
|
|
|
|
case VB_ECR:
|
|
|
|
if (debug) {
|
|
|
|
vb->cpu.ecr.fecc = value >> 16 & 0xFFFF;
|
|
|
|
vb->cpu.ecr.eicc = value & 0xFFFF;
|
|
|
|
}
|
|
|
|
return (uint32_t) vb->cpu.ecr.fecc << 16 | vb->cpu.ecr.eicc;
|
|
|
|
case VB_PSW:
|
|
|
|
vb->cpu.psw.i = value >> 16 & 15;
|
|
|
|
vb->cpu.psw.np = value >> 15 & 1;
|
|
|
|
vb->cpu.psw.ep = value >> 14 & 1;
|
|
|
|
vb->cpu.psw.ae = value >> 13 & 1;
|
|
|
|
vb->cpu.psw.id = value >> 12 & 1;
|
|
|
|
vb->cpu.psw.fro = value >> 9 & 1;
|
|
|
|
vb->cpu.psw.fiv = value >> 8 & 1;
|
|
|
|
vb->cpu.psw.fzd = value >> 7 & 1;
|
|
|
|
vb->cpu.psw.fov = value >> 6 & 1;
|
|
|
|
vb->cpu.psw.fud = value >> 5 & 1;
|
|
|
|
vb->cpu.psw.fpr = value >> 4 & 1;
|
|
|
|
vb->cpu.psw.cy = value >> 3 & 1;
|
|
|
|
vb->cpu.psw.ov = value >> 2 & 1;
|
|
|
|
vb->cpu.psw.s = value >> 1 & 1;
|
|
|
|
vb->cpu.psw.z = value & 1;
|
|
|
|
return value & 0x000FF3FF;
|
|
|
|
}
|
|
|
|
return 0; /* Invalid ID */
|
|
|
|
}
|
2021-09-20 21:29:00 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Prepare to write a memory value to the bus */
|
|
|
|
static int cpuWritePre(VB *vb,uint32_t *address,int32_t *type,int32_t *value) {
|
|
|
|
VBAccess access; /* Bus access descriptor */
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Determine how many clocks the access will take */
|
|
|
|
access.clocks = 0; /* TODO: Needs research */
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Call the breakpoint handler */
|
|
|
|
if (vb->onWrite != NULL) {
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Query the application */
|
|
|
|
access.address = *address;
|
|
|
|
access.value = *value;
|
|
|
|
access.type = *type;
|
|
|
|
if (vb->onWrite(vb, &access))
|
|
|
|
return 1;
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Apply changes */
|
|
|
|
*address = access.address;
|
|
|
|
*value = access.value;
|
|
|
|
if (access.type <= VB_S32)
|
|
|
|
*type = access.type;
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Post-processing */
|
|
|
|
vb->cpu.clocks += access.clocks;
|
2021-09-19 01:31:40 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/**************************** Execute A Handlers *****************************/
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Standard two-operand instruction */
|
|
|
|
static int exaStdTwo(VB *vb) {
|
|
|
|
OpDef *def = (OpDef *) vb->cpu.inst.def;
|
|
|
|
vb->cpu.inst.aux[0] = def->operand(vb);
|
|
|
|
vb->cpu.clocks += def->aux;
|
|
|
|
return 0;
|
|
|
|
}
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Standard three-operand instruction */
|
|
|
|
static int exaStdThree(VB *vb) {
|
|
|
|
OpDef *def = (OpDef *) vb->cpu.inst.def;
|
|
|
|
vb->cpu.inst.aux[0] = def->operand(vb);
|
|
|
|
vb->cpu.inst.aux[1] = vb->cpu.program[vb->cpu.inst.code[0] & 31];
|
|
|
|
vb->cpu.clocks += def->aux;
|
|
|
|
return 0;
|
|
|
|
}
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Bit string bitwise */
|
|
|
|
static int exaBitBitwise(VB *vb) {
|
|
|
|
int32_t bits; /* Working shift amount and bit mask */
|
|
|
|
uint32_t length; /* Number of bits remaining in bit string */
|
|
|
|
int32_t offDest; /* Bit offset of destination bit string */
|
|
|
|
int32_t offSrc; /* Bit offset of source bit string */
|
|
|
|
uint32_t result; /* Output wrdvalue */
|
|
|
|
uint32_t valDest; /* Destination word value */
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Initial invocation */
|
|
|
|
if (vb->cpu.step == 0)
|
|
|
|
vb->cpu.step = vb->cpu.bitstring + 1;
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Read the low-order 32 source bits */
|
|
|
|
if (vb->cpu.step == 1) {
|
|
|
|
if (cpuRead(vb, vb->cpu.program[30], VB_S32, &vb->cpu.inst.aux[0]))
|
2021-09-19 01:31:40 +00:00
|
|
|
return 1;
|
2023-03-11 00:44:57 +00:00
|
|
|
vb->cpu.clocks += 4; /* TODO: Needs research */
|
|
|
|
vb->cpu.step = 2;
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Read the high-order 32 source bits */
|
|
|
|
if (vb->cpu.step == 2) {
|
|
|
|
if (cpuRead(vb, vb->cpu.program[30] + 4, VB_S32, &vb->cpu.inst.aux[1]))
|
2021-09-19 01:31:40 +00:00
|
|
|
return 1;
|
2023-03-11 00:44:57 +00:00
|
|
|
vb->cpu.clocks += 4; /* TODO: Needs research */
|
|
|
|
vb->cpu.step = 3;
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Read the destination bits */
|
|
|
|
if (vb->cpu.step == 3) {
|
|
|
|
if (cpuRead(vb, vb->cpu.program[29], VB_S32, &vb->cpu.inst.aux[2]))
|
|
|
|
return 1;
|
|
|
|
vb->cpu.clocks += 4; /* TODO: Needs research */
|
|
|
|
vb->cpu.step = 4;
|
|
|
|
}
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Compute the result */
|
|
|
|
if (vb->cpu.step == 4) {
|
|
|
|
length = vb->cpu.program[28];
|
|
|
|
offDest = vb->cpu.program[26] & 31;
|
|
|
|
offSrc = vb->cpu.program[27] & 31;
|
|
|
|
result = vb->cpu.inst.aux[0];
|
|
|
|
valDest = vb->cpu.inst.aux[2];
|
|
|
|
bits = offDest - offSrc;
|
|
|
|
|
|
|
|
/* Compose the source value */
|
|
|
|
if (bits > 0)
|
|
|
|
result <<= bits;
|
|
|
|
else if (bits < 0) {
|
|
|
|
result >>= -bits;
|
|
|
|
#ifndef VB_SIGNED_PROPAGATE
|
|
|
|
result &= ((uint32_t) 1 << (32 + bits)) - 1;
|
|
|
|
#endif
|
|
|
|
result |= vb->cpu.inst.aux[1] << (32 + bits);
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Compose the destination value */
|
|
|
|
((OpDef *) vb->cpu.inst.def)
|
|
|
|
->operation(vb, (int32_t *) &result, valDest);
|
|
|
|
bits = (1 << offDest) - 1;
|
|
|
|
if (length < 32 && offDest + length < 32)
|
|
|
|
bits |= (uint32_t) 0xFFFFFFFF << (offDest + length);
|
|
|
|
vb->cpu.inst.aux[2] = (result & ~bits) | (valDest & bits);
|
|
|
|
|
|
|
|
/* Prepare to write the result */
|
|
|
|
vb->cpu.inst.aux[3] = vb->cpu.program[29] & 0xFFFFFFFC;
|
|
|
|
vb->cpu.inst.aux[4] = VB_S32;
|
|
|
|
if (cpuWritePre(vb, (uint32_t *) &vb->cpu.inst.aux[3],
|
|
|
|
&vb->cpu.inst.aux[4], &vb->cpu.inst.aux[2]))
|
2021-09-19 01:31:40 +00:00
|
|
|
return 1;
|
2023-03-11 00:44:57 +00:00
|
|
|
vb->cpu.clocks += 4; /* TODO: Needs research */
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Bit string search */
|
|
|
|
static int exaBitSearch(VB *vb) {
|
|
|
|
if (cpuRead(vb, vb->cpu.program[30], VB_S32, &vb->cpu.inst.aux[0]))
|
|
|
|
return 1;
|
|
|
|
vb->cpu.clocks += 4; /* TODO: Needs research */
|
|
|
|
return 0;
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Branch on condition */
|
|
|
|
static int exaBCOND(VB *vb) {
|
|
|
|
if (cpuCondition(vb, vb->cpu.inst.code[0] >> 9 & 15)) {
|
|
|
|
vb->cpu.inst.aux[0] =
|
|
|
|
(vb->cpu.pc + SignExtend(vb->cpu.inst.code[0], 9)) & 0xFFFFFFFE;
|
|
|
|
vb->cpu.clocks += 3;
|
|
|
|
} else {
|
|
|
|
vb->cpu.inst.aux[0] = vb->cpu.pc + vb->cpu.inst.size;
|
|
|
|
vb->cpu.clocks += 1;
|
|
|
|
}
|
|
|
|
return 0;
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Compare and exchange interlocked */
|
|
|
|
static int exaCAXI(VB *vb) {
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* First invocation */
|
|
|
|
if (vb->cpu.step == 0) {
|
|
|
|
exaStdThree(vb);
|
|
|
|
vb->cpu.inst.aux[0] += vb->cpu.inst.aux[1]; /* Address */
|
|
|
|
vb->cpu.inst.aux[1] = VB_S32; /* Write type */
|
|
|
|
vb->cpu.inst.aux[3] = vb->cpu.program[vb->cpu.inst.code[0] >> 5 & 31];
|
|
|
|
vb->cpu.step = 1;
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Read the lock word and determine the exchange value */
|
|
|
|
if (vb->cpu.step == 1) {
|
|
|
|
if (cpuRead(vb, vb->cpu.inst.aux[0], VB_S32, &vb->cpu.inst.aux[4]))
|
|
|
|
return 1;
|
|
|
|
vb->cpu.inst.aux[2] = vb->cpu.inst.aux[3] == vb->cpu.inst.aux[4] ?
|
|
|
|
vb->cpu.program[30] : vb->cpu.inst.aux[4];
|
|
|
|
vb->cpu.step = 2;
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Prepare to write the exchange value */
|
|
|
|
if (vb->cpu.step == 2) {
|
|
|
|
if (cpuWritePre(vb, (uint32_t *) &vb->cpu.inst.aux[0],
|
|
|
|
&vb->cpu.inst.aux[1], &vb->cpu.inst.aux[2]))
|
|
|
|
return 1;
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
return 0;
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* No special action */
|
|
|
|
static int exaDefault(VB *vb) {
|
|
|
|
vb->cpu.clocks += ((OpDef *) vb->cpu.inst.def)->aux;
|
|
|
|
return 0;
|
|
|
|
}
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Division */
|
|
|
|
static int exaDivision(VB *vb) {
|
|
|
|
exaStdTwo(vb);
|
|
|
|
if (vb->cpu.inst.aux[0] == 0) {
|
|
|
|
vb->cpu.clocks = 0; /* exaStdTwo adds clocks */
|
|
|
|
vb->cpu.exception = 0xFF80; /* Zero division */
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Exception */
|
|
|
|
static int exaException(VB *vb) {
|
|
|
|
VBException exception; /* Exception descriptor */
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Initial invocation */
|
|
|
|
if (vb->cpu.step == 0) {
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Call the breakpoint handler */
|
|
|
|
if (vb->onException != NULL) {
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Query the application */
|
|
|
|
exception.address = vb->cpu.inst.aux[0];
|
|
|
|
exception.code = vb->cpu.exception;
|
|
|
|
exception.cancel = 0;
|
|
|
|
if (vb->onException(vb, &exception))
|
|
|
|
return 1;
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* The application canceled the exception */
|
|
|
|
if (exception.cancel) {
|
|
|
|
vb->cpu.exception = 0;
|
|
|
|
vb->cpu.stage = CPU_FETCH;
|
|
|
|
return 0;
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Apply changes */
|
|
|
|
vb->cpu.inst.aux[0] = exception.address;
|
|
|
|
vb->cpu.exception = exception.code;
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Fatal exception: stage values for writing */
|
|
|
|
if (vb->cpu.psw.np) {
|
|
|
|
vb->cpu.inst.aux[0] = 0x00000000;
|
|
|
|
vb->cpu.inst.aux[1] = VB_S32;
|
|
|
|
vb->cpu.inst.aux[2] = 0xFFFF0000 | vb->cpu.exception;
|
|
|
|
vb->cpu.inst.aux[3] = 0x00000004;
|
|
|
|
vb->cpu.inst.aux[4] = VB_S32;
|
|
|
|
vb->cpu.inst.aux[5] = cpuGetSystemRegister(vb, VB_PSW);
|
|
|
|
vb->cpu.inst.aux[6] = 0x00000008;
|
|
|
|
vb->cpu.inst.aux[7] = VB_S32;
|
|
|
|
vb->cpu.inst.aux[8] = vb->cpu.pc;
|
|
|
|
vb->cpu.step = 1;
|
|
|
|
}
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Other exception */
|
|
|
|
else vb->cpu.step = 10;
|
|
|
|
}
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Prepare to dump fatal exception diagnostic values to memory */
|
|
|
|
for (; vb->cpu.step < 10; vb->cpu.step += 3) {
|
|
|
|
if (cpuWritePre(vb, (uint32_t *)
|
|
|
|
&vb->cpu.inst.aux[vb->cpu.step - 1],
|
|
|
|
&vb->cpu.inst.aux[vb->cpu.step ],
|
|
|
|
&vb->cpu.inst.aux[vb->cpu.step + 1]
|
2021-09-19 01:31:40 +00:00
|
|
|
)) return 1;
|
|
|
|
}
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Common processing */
|
|
|
|
vb->cpu.bitstring = 0;
|
|
|
|
vb->cpu.clocks += 1; /* TODO: Needs research */
|
2021-09-19 01:31:40 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* One-operand floating-point instruction */
|
|
|
|
static int exaFloating1(VB *vb) {
|
|
|
|
int bits; /* Number of bits to shift */
|
|
|
|
int32_t reg1; /* Left operand */
|
|
|
|
int32_t result; /* Operation result */
|
|
|
|
int32_t subop; /* Sub-opcode */
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Reserved operand */
|
|
|
|
reg1 = vb->cpu.program[vb->cpu.inst.code[0] & 31];
|
|
|
|
if (cpuFRO(reg1)) {
|
|
|
|
vb->cpu.fp_flags = 0x00000200; /* FRO */
|
|
|
|
vb->cpu.exception = 0xFF60;
|
|
|
|
return 0;
|
|
|
|
}
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Working variables */
|
|
|
|
bits = (reg1 >> 23 & 0xFF) - 150;
|
|
|
|
result = (reg1 & 0x007FFFFF) | 0x00800000;
|
|
|
|
subop = vb->cpu.inst.code[1] >> 10 & 63;
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Zero */
|
|
|
|
if ((reg1 & 0x7FFFFFFF) == 0x00000000)
|
|
|
|
result = 0;
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Minimum negative value */
|
|
|
|
else if (bits == 8 && result == 0x00800000 && reg1 < 0)
|
|
|
|
result = INT32_MIN;
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Shifting left */
|
|
|
|
else if (bits > 0) {
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Invalid operation */
|
|
|
|
if (bits > 7) {
|
|
|
|
vb->cpu.fp_flags = 0x00000100; /* FIV */
|
|
|
|
vb->cpu.exception = 0xFF70;
|
2021-09-19 01:31:40 +00:00
|
|
|
return 0;
|
2023-03-11 00:44:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Compute result */
|
|
|
|
result <<= bits;
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Shifting right */
|
|
|
|
else if (bits < 0) {
|
|
|
|
result =
|
|
|
|
bits < -24 ? 0 : /* All bits shifted out */
|
|
|
|
subop == 0x0B ? result >> -bits : /* Truncate */
|
|
|
|
((result >> (-bits - 1)) + 1) >> 1 /* Round */
|
|
|
|
;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Result is negative */
|
|
|
|
if (reg1 < 0 && result != INT32_MIN)
|
|
|
|
result = -result;
|
|
|
|
|
|
|
|
/* Stage updates */
|
|
|
|
vb->cpu.fp_flags = result == *(float *)®1 ? 0 : 0x00000010; /* FPR */
|
|
|
|
vb->cpu.inst.aux[0] = result;
|
|
|
|
vb->cpu.inst.aux[1] = subop;
|
|
|
|
vb->cpu.clocks += ((OpDef *) vb->cpu.inst.def)->aux;
|
2021-09-19 01:31:40 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Two-operand floating-point instruction */
|
|
|
|
static int exaFloating2(VB *vb) {
|
|
|
|
FloatAux *aux; /* Floating-point auxiliary memory */
|
|
|
|
int32_t bits; /* Bits of testing value */
|
|
|
|
int32_t reg1; /* Right operand */
|
|
|
|
int32_t reg2; /* Left operand */
|
|
|
|
float test; /* Floating-point testing value */
|
|
|
|
OpDef *def = (OpDef *) vb->cpu.inst.def;
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Reserved operand */
|
|
|
|
reg1 = vb->cpu.program[vb->cpu.inst.code[0] & 31];
|
|
|
|
reg2 = vb->cpu.program[vb->cpu.inst.code[0] >> 5 & 31];
|
|
|
|
if (cpuFRO(reg1) || cpuFRO(reg2)) {
|
|
|
|
vb->cpu.fp_flags = 0x00000200; /* FRO */
|
|
|
|
vb->cpu.exception = 0xFF60;
|
|
|
|
return 0;
|
|
|
|
}
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Perform the operation */
|
|
|
|
def->operation(vb, ®2, reg1);
|
|
|
|
if (vb->cpu.exception != 0)
|
|
|
|
return 0; /* Handled in opDIVF_S() */
|
|
|
|
aux = (FloatAux *) &vb->cpu.inst.aux;
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Overflow */
|
|
|
|
bits = 0x7F7FFFFF; /* Maximum value */
|
|
|
|
test = *(float *)&bits;
|
|
|
|
if (aux->f64 > test || aux->f64 < -test) {
|
|
|
|
vb->cpu.fp_flags = 0x00000040; /* FOV */
|
|
|
|
vb->cpu.exception = 0xFF64;
|
|
|
|
return 0;
|
|
|
|
}
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Process result */
|
|
|
|
bits = *(int32_t *)&aux->f32;
|
|
|
|
vb->cpu.fp_flags = 0;
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Zero */
|
|
|
|
if ((bits & 0x7FFFFFFF) == 0x00000000)
|
|
|
|
aux->f32 = bits = 0;
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Underflow */
|
|
|
|
else if ((bits & 0x7F800000) == 0x00000000) {
|
|
|
|
vb->cpu.fp_flags = 0x00000020; /* FUD */
|
|
|
|
aux->f32 = bits = 0;
|
|
|
|
}
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Precision degradation */
|
|
|
|
if (aux->f32 != aux->f64)
|
|
|
|
vb->cpu.fp_flags |= 0x00000010; /* FPR */
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Other state */
|
|
|
|
vb->cpu.inst.aux[0] = bits;
|
|
|
|
vb->cpu.inst.aux[1] = vb->cpu.inst.code[1] >> 10 & 63;
|
|
|
|
vb->cpu.clocks += def->aux;
|
|
|
|
return 0;
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Illegal opcode */
|
|
|
|
static int exaIllegal(VB *vb) {
|
|
|
|
vb->cpu.exception = 0xFF90; /* Illegal opcode */
|
|
|
|
return 0;
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Jump relative, jump and link */
|
|
|
|
static int exaJR(VB *vb) {
|
|
|
|
vb->cpu.inst.aux[0] = 0xFFFFFFFE & (vb->cpu.pc + SignExtend(
|
|
|
|
(int32_t) vb->cpu.inst.code[0] << 16 | vb->cpu.inst.code[1], 26));
|
|
|
|
vb->cpu.clocks += 3;
|
|
|
|
return 0;
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Memory read */
|
|
|
|
static int exaRead(VB *vb) {
|
|
|
|
|
|
|
|
/* First invocation */
|
|
|
|
if (vb->cpu.step == 0) {
|
|
|
|
exaStdThree(vb);
|
|
|
|
vb->cpu.inst.aux[1] += vb->cpu.inst.aux[0]; /* Address */
|
|
|
|
vb->cpu.clocks += 5; /* TODO: Needs research */
|
|
|
|
vb->cpu.step = 1;
|
|
|
|
}
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Read the value */
|
|
|
|
return cpuRead(vb, vb->cpu.inst.aux[1],
|
|
|
|
((OpDef *) vb->cpu.inst.def)->aux, &vb->cpu.inst.aux[0]);
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Trap */
|
|
|
|
static int exaTRAP(VB *vb) {
|
|
|
|
/* TODO: Clocks is less 1 here because exaException adds 1 */
|
|
|
|
vb->cpu.clocks += ((OpDef *) vb->cpu.inst.def)->aux - 1;
|
|
|
|
vb->cpu.exception = 0xFFA0 | (vb->cpu.inst.code[0] & 31);
|
|
|
|
return 0;
|
|
|
|
}
|
2021-09-19 19:36:30 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Memory write */
|
|
|
|
static int exaWrite(VB *vb) {
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* First invocation */
|
|
|
|
if (vb->cpu.step == 0) {
|
|
|
|
exaStdThree(vb);
|
|
|
|
vb->cpu.inst.aux[0] += vb->cpu.inst.aux[1];
|
|
|
|
vb->cpu.inst.aux[1] = ((OpDef *) vb->cpu.inst.def)->aux; /* Type */
|
|
|
|
vb->cpu.inst.aux[2] = vb->cpu.program[vb->cpu.inst.code[0] >> 5 & 31];
|
|
|
|
vb->cpu.clocks += 4; /* TODO: Needs research */
|
|
|
|
vb->cpu.step = 1;
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Write the value */
|
|
|
|
return cpuWritePre(vb, (uint32_t *) &vb->cpu.inst.aux[0],
|
|
|
|
&vb->cpu.inst.aux[1], &vb->cpu.inst.aux[2]);
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/**************************** Execute B Handlers *****************************/
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Bit string bitwise */
|
|
|
|
static void exbBitBitwise(VB *vb) {
|
|
|
|
int32_t bits;
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Write the output value */
|
|
|
|
if (vb->cpu.program[28] != 0) {
|
|
|
|
busWrite(vb, vb->cpu.inst.aux[3],
|
|
|
|
vb->cpu.inst.aux[4], vb->cpu.inst.aux[2], 0);
|
|
|
|
}
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Prepare registers */
|
|
|
|
vb->cpu.program[26] &= 0x0000001F;
|
|
|
|
vb->cpu.program[27] &= 0x0000001F;
|
|
|
|
vb->cpu.program[29] &= 0xFFFFFFFC;
|
|
|
|
vb->cpu.program[30] &= 0xFFFFFFFC;
|
|
|
|
|
|
|
|
/* Determine how many bits of output have been processed */
|
|
|
|
bits = 32 - vb->cpu.program[26];
|
|
|
|
if ((uint32_t) vb->cpu.program[28] <= (uint32_t) bits)
|
|
|
|
bits = vb->cpu.program[28];
|
|
|
|
|
|
|
|
/* Update source */
|
|
|
|
vb->cpu.program[27] += bits;
|
|
|
|
if (vb->cpu.program[27] >= 32) {
|
|
|
|
vb->cpu.program[27] &= 31;
|
|
|
|
vb->cpu.program[30] += 4;
|
|
|
|
vb->cpu.inst.aux[0] = vb->cpu.inst.aux[1];
|
|
|
|
vb->cpu.bitstring = 1; /* Read next source word */
|
|
|
|
} else vb->cpu.bitstring = 2; /* Skip source reads */
|
|
|
|
|
|
|
|
/* Update destination */
|
|
|
|
vb->cpu.program[26] += bits;
|
|
|
|
if (vb->cpu.program[26] >= 32) {
|
|
|
|
vb->cpu.program[26] &= 31;
|
|
|
|
vb->cpu.program[29] += 4;
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Update length */
|
|
|
|
vb->cpu.program[28] -= bits;
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Advance to the next instruction */
|
|
|
|
if (vb->cpu.program[28] == 0) {
|
|
|
|
vb->cpu.bitstring = 0;
|
|
|
|
vb->cpu.pc += vb->cpu.inst.size;
|
|
|
|
}
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
}
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Bit string search */
|
|
|
|
static void exbBitSearch(VB *vb) {
|
|
|
|
int32_t dir = ((~vb->cpu.inst.code[0] & 1) << 1) - 1;
|
|
|
|
int32_t test = vb->cpu.inst.code[0] >> 1 & 1;
|
|
|
|
|
|
|
|
/* Prepare registers */
|
|
|
|
vb->cpu.program[27] &= 0x0000001F;
|
|
|
|
vb->cpu.program[30] &= 0xFFFFFFFC;
|
|
|
|
vb->cpu.psw.z = 1;
|
|
|
|
vb->cpu.bitstring = 1;
|
|
|
|
|
|
|
|
/* Process all remaining bits in the current word */
|
|
|
|
while (vb->cpu.psw.z && vb->cpu.program[28] != 0) {
|
|
|
|
|
|
|
|
/* The bit does not match */
|
|
|
|
if (
|
|
|
|
(vb->cpu.inst.aux[0] & 1 << vb->cpu.program[27]) !=
|
|
|
|
test << vb->cpu.program[27]
|
|
|
|
) vb->cpu.program[29]++;
|
|
|
|
|
|
|
|
/* A match was found */
|
|
|
|
else vb->cpu.psw.z = 0;
|
|
|
|
|
|
|
|
/* Advance to the next bit */
|
|
|
|
vb->cpu.program[28]--;
|
|
|
|
vb->cpu.program[27] += dir;
|
|
|
|
if (vb->cpu.program[27] & 0x00000020) {
|
|
|
|
vb->cpu.program[27] &= 0x0000001F;
|
|
|
|
vb->cpu.program[30] += dir << 2;
|
|
|
|
break;
|
|
|
|
}
|
2021-09-19 01:31:40 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Advance to the next instruction */
|
|
|
|
if (!vb->cpu.psw.z || vb->cpu.program[28] == 0) {
|
|
|
|
vb->cpu.bitstring = 0;
|
|
|
|
vb->cpu.pc += vb->cpu.inst.size;
|
|
|
|
}
|
2021-09-19 01:31:40 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Compare and exchange interlocked */
|
|
|
|
static void exbCAXI(VB *vb) {
|
|
|
|
int32_t left = vb->cpu.inst.aux[3];
|
|
|
|
int32_t right = vb->cpu.inst.aux[4];
|
|
|
|
int32_t result = left - right;
|
|
|
|
vb->cpu.psw.cy = (uint32_t) left < (uint32_t) right;
|
|
|
|
vb->cpu.psw.ov = (int32_t) ((left ^ right) & (left ^ result)) < 0;
|
|
|
|
vb->cpu.psw.s = result < 0;
|
|
|
|
vb->cpu.psw.z = result == 0;
|
|
|
|
vb->cpu.pc += vb->cpu.inst.size;
|
|
|
|
vb->cpu.program[vb->cpu.inst.code[0] >> 5 & 31] = right;
|
|
|
|
busWrite(vb, vb->cpu.inst.aux[0],
|
|
|
|
vb->cpu.inst.aux[1], vb->cpu.inst.aux[2], 0);
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Clear interrupt disable flag */
|
|
|
|
static void exbCLI(VB *vb) {
|
|
|
|
vb->cpu.pc += vb->cpu.inst.size;
|
|
|
|
vb->cpu.psw.id = 0;
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Exception */
|
|
|
|
static void exbException(VB *vb) {
|
|
|
|
int x; /* Iterator */
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Apply staged floating-point flags */
|
|
|
|
if (vb->cpu.fp_flags != 0) {
|
|
|
|
cpuSetSystemRegister(vb, VB_PSW, vb->cpu.fp_flags |
|
|
|
|
cpuGetSystemRegister(vb, VB_PSW), 0);
|
|
|
|
vb->cpu.fp_flags = 0;
|
|
|
|
}
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Fatal exception */
|
|
|
|
if (vb->cpu.psw.np) {
|
|
|
|
for (x = 0; x < 9; x += 3) {
|
|
|
|
busWrite(vb, vb->cpu.inst.aux[x],
|
|
|
|
vb->cpu.inst.aux[x + 1], vb->cpu.inst.aux[x + 2], 0);
|
|
|
|
}
|
|
|
|
vb->cpu.stage = CPU_FATAL;
|
2021-09-19 01:31:40 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Duplexed exception */
|
|
|
|
if (vb->cpu.psw.ep) {
|
|
|
|
vb->cpu.ecr.fecc = vb->cpu.exception;
|
|
|
|
vb->cpu.fepc = vb->cpu.pc;
|
|
|
|
vb->cpu.fepsw = cpuGetSystemRegister(vb, VB_PSW);
|
|
|
|
vb->cpu.pc = 0xFFFFFFD0;
|
|
|
|
vb->cpu.psw.np = 1;
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Regular exception */
|
2021-09-19 01:31:40 +00:00
|
|
|
else {
|
2023-03-11 00:44:57 +00:00
|
|
|
vb->cpu.ecr.eicc = vb->cpu.exception;
|
|
|
|
vb->cpu.eipc = vb->cpu.pc;
|
|
|
|
vb->cpu.eipsw = cpuGetSystemRegister(vb, VB_PSW);
|
|
|
|
vb->cpu.pc = vb->cpu.inst.aux[0];
|
|
|
|
vb->cpu.psw.ep = 1;
|
|
|
|
|
|
|
|
/* Interrupt */
|
|
|
|
if (vb->cpu.exception < 0xFF00) {
|
|
|
|
if ((vb->cpu.inst.code[0] & 0xFC00) == 0x6800) /* HALT */
|
|
|
|
vb->cpu.eipc += vb->cpu.inst.size;
|
|
|
|
vb->cpu.psw.i = Min(15, (vb->cpu.exception >> 4 & 15) + 1);
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* TRAP */
|
|
|
|
if ((vb->cpu.exception & 0xFFE0) == 0xFFA0)
|
|
|
|
vb->cpu.eipc += vb->cpu.inst.size;
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Common processing */
|
|
|
|
vb->cpu.psw.ae = 0;
|
|
|
|
vb->cpu.psw.id = 1;
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Floating-point instruction */
|
|
|
|
static void exbFloating(VB *vb) {
|
|
|
|
int32_t result = vb->cpu.inst.aux[0]; /* Operation result */
|
|
|
|
int32_t subop = vb->cpu.inst.aux[1]; /* Sub-opcode */
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Apply staged floating-point flags */
|
|
|
|
if (vb->cpu.fp_flags != 0) {
|
|
|
|
cpuSetSystemRegister(vb, VB_PSW, vb->cpu.fp_flags |
|
|
|
|
cpuGetSystemRegister(vb, VB_PSW), 0);
|
|
|
|
vb->cpu.fp_flags = 0;
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Update state */
|
2023-03-11 00:44:57 +00:00
|
|
|
vb->cpu.pc += vb->cpu.inst.size;
|
|
|
|
vb->cpu.psw.ov = 0;
|
|
|
|
vb->cpu.psw.s = result < 0;
|
|
|
|
vb->cpu.psw.z = result == 0;
|
|
|
|
if (subop != 0x03 && subop != 0x0B) /* CVT.SW, TRNC.SW */
|
|
|
|
vb->cpu.psw.cy = result < 0;
|
|
|
|
if (subop != 0x00) /* CMPF.S */
|
|
|
|
vb->cpu.program[vb->cpu.inst.code[0] >> 5 & 31] = result;
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Halt */
|
2023-03-11 00:44:57 +00:00
|
|
|
static void exbHALT(VB *vb) {
|
|
|
|
vb->cpu.stage = CPU_HALT;
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Jump and link */
|
|
|
|
static void exbJAL(VB *vb) {
|
|
|
|
vb->cpu.program[31] = vb->cpu.pc + vb->cpu.inst.size;
|
|
|
|
vb->cpu.pc = vb->cpu.inst.aux[0];
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Jump */
|
|
|
|
static void exbJMP(VB *vb) {
|
|
|
|
vb->cpu.pc = vb->cpu.inst.aux[0];
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Return from trap or interrupt */
|
|
|
|
static void exbRETI(VB *vb) {
|
|
|
|
if (vb->cpu.psw.np) {
|
|
|
|
vb->cpu.pc = vb->cpu.fepc;
|
|
|
|
cpuSetSystemRegister(vb, VB_PSW, vb->cpu.fepsw, 0);
|
|
|
|
} else {
|
|
|
|
vb->cpu.pc = vb->cpu.eipc;
|
|
|
|
cpuSetSystemRegister(vb, VB_PSW, vb->cpu.eipsw, 0);
|
|
|
|
}
|
|
|
|
}
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Set interrupt disable flag */
|
|
|
|
static void exbSEI(VB *vb) {
|
|
|
|
vb->cpu.pc += vb->cpu.inst.size;
|
|
|
|
vb->cpu.psw.id = 1;
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Standard two-operand instruction */
|
|
|
|
static void exbStdTwo(VB *vb) {
|
|
|
|
((OpDef *) vb->cpu.inst.def)->operation(vb,
|
|
|
|
&vb->cpu.program[vb->cpu.inst.code[0] >> 5 & 31],
|
|
|
|
vb->cpu.inst.aux[0]
|
|
|
|
);
|
|
|
|
vb->cpu.pc += vb->cpu.inst.size;
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Standard three-operand instruction */
|
|
|
|
static void exbStdThree(VB *vb) {
|
|
|
|
int32_t *reg2 = &vb->cpu.program[vb->cpu.inst.code[0] >> 5 & 31];
|
|
|
|
*reg2 = vb->cpu.inst.aux[1];
|
|
|
|
((OpDef *) vb->cpu.inst.def)->operation(vb, reg2, vb->cpu.inst.aux[0]);
|
|
|
|
vb->cpu.pc += vb->cpu.inst.size;
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/**************************** Operation Handlers *****************************/
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Add */
|
|
|
|
static void opADD(VB *vb, int32_t *dest, int32_t src) {
|
|
|
|
int32_t result = *dest + src;
|
|
|
|
vb->cpu.psw.cy = (uint32_t) result < (uint32_t) *dest;
|
|
|
|
vb->cpu.psw.ov = (int32_t) (~(*dest ^ src) & (*dest ^ result)) < 0;
|
|
|
|
vb->cpu.psw.s = result < 0;
|
|
|
|
vb->cpu.psw.z = result == 0;
|
|
|
|
*dest = result;
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Add Floating Short */
|
|
|
|
static void opADDF_S(VB *vb, int32_t *dest, int32_t src) {
|
|
|
|
FloatAux *aux = (FloatAux *) vb->cpu.inst.aux;
|
|
|
|
double left = *(float *)dest;
|
|
|
|
double right = *(float *)&src;
|
|
|
|
double result = left + right;
|
|
|
|
aux->f32 = result;
|
|
|
|
aux->f64 = result;
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* And */
|
|
|
|
static void opAND(VB *vb, int32_t *dest, int32_t src) {
|
|
|
|
int32_t result = *dest & src;
|
|
|
|
vb->cpu.psw.ov = 0;
|
|
|
|
vb->cpu.psw.s = result < 0;
|
|
|
|
vb->cpu.psw.z = result == 0;
|
|
|
|
*dest = result;
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* And Bit String Upward */
|
|
|
|
static void opANDBSU(VB *vb, int32_t *dest, int32_t src) {
|
|
|
|
(void) vb;
|
|
|
|
*dest &= src;
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* And Not Bit String Upward */
|
|
|
|
static void opANDNBSU(VB *vb, int32_t *dest, int32_t src) {
|
|
|
|
(void) vb;
|
|
|
|
*dest = ~*dest & src;
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Compare */
|
|
|
|
static void opCMP(VB *vb, int32_t *dest, int32_t src) {
|
|
|
|
int32_t result = *dest - src;
|
|
|
|
vb->cpu.psw.cy = (uint32_t) *dest < (uint32_t) src;
|
|
|
|
vb->cpu.psw.ov = (int32_t) ((*dest ^ src) & (*dest ^ result)) < 0;
|
|
|
|
vb->cpu.psw.s = result < 0;
|
|
|
|
vb->cpu.psw.z = result == 0;
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Convert Word Integer to Short Floating */
|
|
|
|
static void opCVT_WS(VB *vb, int32_t *dest, int32_t src) {
|
|
|
|
float value = (float) src;
|
|
|
|
*dest = *(int32_t *)&value;
|
|
|
|
if ((double) value != (double) src)
|
|
|
|
vb->cpu.psw.fpr = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Divide signed */
|
|
|
|
static void opDIV(VB *vb, int32_t *dest, int32_t src) {
|
|
|
|
int32_t result;
|
|
|
|
if (*dest == INT32_MIN && src == -1) {
|
|
|
|
vb->cpu.psw.ov = 1;
|
|
|
|
vb->cpu.psw.s = 1;
|
|
|
|
vb->cpu.psw.z = 0;
|
|
|
|
vb->cpu.program[30] = 0;
|
|
|
|
} else {
|
|
|
|
result = *dest / src;
|
|
|
|
vb->cpu.psw.ov = 0;
|
|
|
|
vb->cpu.psw.s = result < 0;
|
|
|
|
vb->cpu.psw.z = result == 0;
|
|
|
|
vb->cpu.program[30] = *dest % src;
|
|
|
|
*dest = result;
|
|
|
|
}
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Divide Floating Short */
|
|
|
|
static void opDIVF_S(VB *vb, int32_t *dest, int32_t src) {
|
|
|
|
FloatAux *aux = (FloatAux *) vb->cpu.inst.aux;
|
|
|
|
double left; /* Left operand */
|
|
|
|
double right; /* Right operand */
|
|
|
|
double result; /* Operation result */
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Divisor is zero */
|
|
|
|
if (*dest == 0) {
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Invalid operation */
|
|
|
|
if (src == 0) {
|
|
|
|
vb->cpu.fp_flags = 0x00000100; /* FIV */
|
|
|
|
vb->cpu.exception = 0xFF70;
|
|
|
|
}
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Zero division */
|
|
|
|
else {
|
|
|
|
vb->cpu.fp_flags = 0x00000080; /* FZD */
|
|
|
|
vb->cpu.exception = 0xFF68;
|
|
|
|
}
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
return;
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Perform the operation */
|
|
|
|
left = *(float *)dest;
|
|
|
|
right = *(float *)&src;
|
|
|
|
result = left / right;
|
|
|
|
aux->f32 = result;
|
|
|
|
aux->f64 = result;
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Divide unsigned */
|
|
|
|
static void opDIVU(VB *vb, int32_t *dest, int32_t src) {
|
|
|
|
uint32_t result = (uint32_t) *dest / (uint32_t) src;
|
|
|
|
vb->cpu.psw.ov = 0;
|
|
|
|
vb->cpu.psw.s = (int32_t) result < 0;
|
|
|
|
vb->cpu.psw.z = result == 0;
|
|
|
|
vb->cpu.program[30] = (int32_t) ((uint32_t) *dest % (uint32_t) src);
|
|
|
|
*dest = (int32_t) result;
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Load to system register */
|
|
|
|
static void opLDSR(VB *vb, int32_t *dest, int32_t src) {
|
|
|
|
cpuSetSystemRegister(vb, src, *dest, 0);
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Move */
|
|
|
|
static void opMOV(VB *vb, int32_t *dest, int32_t src) {
|
|
|
|
(void) vb;
|
|
|
|
*dest = src;
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Move Bit String Upward */
|
|
|
|
static void opMOVBSU(VB *vb, int32_t *dest, int32_t src) {
|
|
|
|
(void) vb;
|
|
|
|
(void) dest;
|
|
|
|
(void) src;
|
|
|
|
}
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Add Immediate */
|
|
|
|
static void opMOVEA(VB *vb, int32_t *dest, int32_t src) {
|
|
|
|
(void) vb;
|
|
|
|
*dest += src;
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Multiply Halfword */
|
|
|
|
static void opMPYHW(VB *vb, int32_t *dest, int32_t src) {
|
|
|
|
(void) vb;
|
|
|
|
*dest *= SignExtend(src, 17);
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Multiply signed */
|
|
|
|
static void opMUL(VB *vb, int32_t *dest, int32_t src) {
|
|
|
|
int64_t result = (int64_t) *dest * (int64_t) src;
|
|
|
|
int32_t resultLow = (int32_t) result;
|
|
|
|
vb->cpu.psw.ov = result != resultLow;
|
|
|
|
vb->cpu.psw.s = resultLow < 0;
|
|
|
|
vb->cpu.psw.z = resultLow == 0;
|
|
|
|
vb->cpu.program[30] = (int32_t) (result >> 32);
|
|
|
|
*dest = resultLow;
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Multiply Floating Short */
|
|
|
|
static void opMULF_S(VB *vb, int32_t *dest, int32_t src) {
|
|
|
|
FloatAux *aux = (FloatAux *) vb->cpu.inst.aux;
|
|
|
|
double left = *(float *)dest;
|
|
|
|
double right = *(float *)&src;
|
|
|
|
double result = left * right;
|
|
|
|
aux->f32 = result;
|
|
|
|
aux->f64 = result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Multiply unsigned */
|
|
|
|
static void opMULU(VB *vb, int32_t *dest, int32_t src) {
|
|
|
|
uint64_t result = (uint64_t)(uint32_t)*dest * (uint64_t)(uint32_t)src;
|
|
|
|
uint32_t resultLow = (uint32_t) result;
|
|
|
|
vb->cpu.psw.ov = result != resultLow;
|
|
|
|
vb->cpu.psw.s = (int32_t) resultLow < 0;
|
|
|
|
vb->cpu.psw.z = resultLow == 0;
|
|
|
|
vb->cpu.program[30] = (int32_t) (result >> 32);
|
|
|
|
*dest = (int32_t) resultLow;
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Not */
|
|
|
|
static void opNOT(VB *vb, int32_t *dest, int32_t src) {
|
|
|
|
int32_t result = ~src;
|
|
|
|
vb->cpu.psw.ov = 0;
|
|
|
|
vb->cpu.psw.s = result < 0;
|
|
|
|
vb->cpu.psw.z = result == 0;
|
|
|
|
*dest = result;
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Not Bit String Upward */
|
|
|
|
static void opNOTBSU(VB *vb, int32_t *dest, int32_t src) {
|
|
|
|
(void) vb;
|
|
|
|
(void) src;
|
|
|
|
*dest = ~*dest;
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Or */
|
|
|
|
static void opOR(VB *vb, int32_t *dest, int32_t src) {
|
|
|
|
int32_t result = *dest | src;
|
|
|
|
vb->cpu.psw.ov = 0;
|
|
|
|
vb->cpu.psw.s = result < 0;
|
|
|
|
vb->cpu.psw.z = result == 0;
|
|
|
|
*dest = result;
|
|
|
|
}
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Or Bit String Upward */
|
|
|
|
static void opORBSU(VB *vb, int32_t *dest, int32_t src) {
|
|
|
|
(void) vb;
|
|
|
|
*dest |= src;
|
|
|
|
}
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Or Not Bit String Upward */
|
|
|
|
static void opORNBSU(VB *vb, int32_t *dest, int32_t src) {
|
|
|
|
(void) vb;
|
|
|
|
*dest = ~*dest | src;
|
|
|
|
}
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Reverse Bits in Word */
|
|
|
|
static void opREV(VB *vb, int32_t *dest, int32_t src) {
|
|
|
|
(void) vb;
|
|
|
|
src = (src << 16 & 0xFFFF0000) | (src >> 16 & 0x0000FFFF);
|
|
|
|
src = (src << 8 & 0xFF00FF00) | (src >> 8 & 0x00FF00FF);
|
|
|
|
src = (src << 4 & 0xF0F0F0F0) | (src >> 4 & 0x0F0F0F0F);
|
|
|
|
src = (src << 2 & 0xCCCCCCCC) | (src >> 2 & 0x33333333);
|
|
|
|
*dest = (src << 1 & 0xAAAAAAAA) | (src >> 1 & 0x55555555);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set flag condition */
|
|
|
|
static void opSETF(VB *vb, int32_t *dest, int32_t src) {
|
|
|
|
*dest = cpuCondition(vb, src & 15);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Shift right arithmetic */
|
|
|
|
static void opSAR(VB *vb, int32_t *dest, int32_t src) {
|
|
|
|
int32_t result = *dest >> (src &= 31);
|
|
|
|
#ifndef VB_SIGNED_PROPAGATE
|
|
|
|
if (src != 0)
|
|
|
|
result = SignExtend(result, 32 - src);
|
|
|
|
#endif
|
|
|
|
vb->cpu.psw.cy = src != 0 && *dest & 1 << (src - 1);
|
|
|
|
vb->cpu.psw.ov = 0;
|
|
|
|
vb->cpu.psw.s = result < 0;
|
|
|
|
vb->cpu.psw.z = result == 0;
|
|
|
|
*dest = result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Shift left */
|
|
|
|
static void opSHL(VB *vb, int32_t *dest, int32_t src) {
|
|
|
|
int32_t result = *dest << (src &= 31);
|
|
|
|
vb->cpu.psw.cy = src != 0 && *dest & 1 << (32 - src);
|
|
|
|
vb->cpu.psw.ov = 0;
|
|
|
|
vb->cpu.psw.s = result < 0;
|
|
|
|
vb->cpu.psw.z = result == 0;
|
|
|
|
*dest = result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Shift right logical */
|
|
|
|
static void opSHR(VB *vb, int32_t *dest, int32_t src) {
|
|
|
|
int32_t result = (uint32_t) *dest >> (src &= 31);
|
|
|
|
#ifndef VB_SIGNED_PROPAGATE
|
|
|
|
if (src != 0)
|
|
|
|
result &= ((uint32_t) 1 << (32 - src)) - 1;
|
|
|
|
#endif
|
|
|
|
vb->cpu.psw.cy = src != 0 && *dest & 1 << (src - 1);
|
|
|
|
vb->cpu.psw.ov = 0;
|
|
|
|
vb->cpu.psw.s = result < 0;
|
|
|
|
vb->cpu.psw.z = result == 0;
|
|
|
|
*dest = result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Store */
|
|
|
|
static void opST(VB *vb, int32_t *dest, int32_t src) {
|
|
|
|
(void) dest, (void) src;
|
|
|
|
busWrite(vb, vb->cpu.inst.aux[0],
|
|
|
|
vb->cpu.inst.aux[1], vb->cpu.inst.aux[2], 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Store to system register */
|
|
|
|
static void opSTSR(VB *vb, int32_t *dest, int32_t src) {
|
|
|
|
*dest = cpuGetSystemRegister(vb, src);
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Subtract */
|
2023-03-11 00:44:57 +00:00
|
|
|
static void opSUB(VB *vb, int32_t *dest, int32_t src) {
|
|
|
|
int32_t result = *dest - src;
|
|
|
|
vb->cpu.psw.cy = (uint32_t) *dest < (uint32_t) src;
|
|
|
|
vb->cpu.psw.ov = (int32_t) ((*dest ^ src) & (*dest ^ result)) < 0;
|
|
|
|
vb->cpu.psw.s = result < 0;
|
|
|
|
vb->cpu.psw.z = result == 0;
|
|
|
|
*dest = result;
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Subtract Floating Short */
|
2023-03-11 00:44:57 +00:00
|
|
|
static void opSUBF_S(VB *vb, int32_t *dest, int32_t src) {
|
|
|
|
FloatAux *aux = (FloatAux *) vb->cpu.inst.aux;
|
|
|
|
double left = *(float *)dest;
|
|
|
|
double right = *(float *)&src;
|
|
|
|
double result = left - right;
|
|
|
|
aux->f32 = result;
|
|
|
|
aux->f64 = result;
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Exchange Byte */
|
2023-03-11 00:44:57 +00:00
|
|
|
static void opXB(VB *vb, int32_t *dest, int32_t src) {
|
|
|
|
(void) vb;
|
|
|
|
*dest = (src & 0xFFFF0000) | (src << 8 & 0xFF00) | (src >> 8 & 0x00FF);
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Exchange Halfword */
|
2023-03-11 00:44:57 +00:00
|
|
|
static void opXH(VB *vb, int32_t *dest, int32_t src) {
|
|
|
|
(void) vb;
|
|
|
|
*dest = (src << 16 & 0xFFFF0000) | (src >> 16 & 0x0000FFFF);
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Exclusive Or */
|
2023-03-11 00:44:57 +00:00
|
|
|
static void opXOR(VB *vb, int32_t *dest, int32_t src) {
|
|
|
|
int32_t result = *dest ^ src;
|
|
|
|
vb->cpu.psw.ov = 0;
|
|
|
|
vb->cpu.psw.s = result < 0;
|
|
|
|
vb->cpu.psw.z = result == 0;
|
|
|
|
*dest = result;
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Exclusive Or Bit String Upward */
|
2023-03-11 00:44:57 +00:00
|
|
|
static void opXORBSU(VB *vb, int32_t *dest, int32_t src) {
|
|
|
|
(void) vb;
|
|
|
|
*dest ^= src;
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
2021-09-19 19:36:30 +00:00
|
|
|
/* Exclusive Or Not Bit String Upward */
|
2023-03-11 00:44:57 +00:00
|
|
|
static void opXORNBSU(VB *vb, int32_t *dest, int32_t src) {
|
|
|
|
(void) vb;
|
|
|
|
*dest = ~*dest ^ src;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/***************************** Operand Handlers ******************************/
|
|
|
|
|
|
|
|
/* imm5 (sign-extended) */
|
|
|
|
static int32_t opImm5S(VB *vb) {
|
|
|
|
return SignExtend(vb->cpu.inst.code[0] & 31, 5);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* imm5 */
|
|
|
|
static int32_t opImm5U(VB *vb) {
|
|
|
|
return vb->cpu.inst.code[0] & 31;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* imm16 (shifted left by 16) */
|
|
|
|
static int32_t opImm16H(VB *vb) {
|
|
|
|
return (uint32_t) vb->cpu.inst.code[1] << 16;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* imm16 (sign-extended) */
|
|
|
|
static int32_t opImm16S(VB *vb) {
|
|
|
|
return SignExtend(vb->cpu.inst.code[1], 16);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* imm16 */
|
|
|
|
static int32_t opImm16U(VB *vb) {
|
|
|
|
return vb->cpu.inst.code[1];
|
|
|
|
}
|
|
|
|
|
|
|
|
/* reg1 */
|
|
|
|
static int32_t opReg1(VB *vb) {
|
|
|
|
return vb->cpu.program[vb->cpu.inst.code[0] & 31];
|
|
|
|
}
|
|
|
|
|
|
|
|
/* reg2 */
|
|
|
|
static int32_t opReg2(VB *vb) {
|
|
|
|
return vb->cpu.program[vb->cpu.inst.code[0] >> 5 & 31];
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**************************** Opcode Definitions *****************************/
|
|
|
|
|
|
|
|
/* Forward references */
|
|
|
|
static int exaBitString(VB *);
|
|
|
|
static int exaFloatendo(VB *);
|
|
|
|
|
|
|
|
/* Exception processing */
|
|
|
|
static const OpDef OPDEF_EXCEPTION =
|
|
|
|
{ &exaException, &exbException, NULL, NULL, 0, 0 };
|
|
|
|
|
|
|
|
/* Top-level opcode definitions */
|
|
|
|
static const OpDef OPDEFS[] = {
|
|
|
|
{ &exaStdTwo , &exbStdTwo , &opMOV , &opReg1 , 1, 1 }, /* 0x00 */
|
|
|
|
{ &exaStdTwo , &exbStdTwo , &opADD , &opReg1 , 1, 1 },
|
|
|
|
{ &exaStdTwo , &exbStdTwo , &opSUB , &opReg1 , 1, 1 },
|
|
|
|
{ &exaStdTwo , &exbStdTwo , &opCMP , &opReg1 , 1, 1 },
|
|
|
|
{ &exaStdTwo , &exbStdTwo , &opSHL , &opReg1 , 1, 1 },
|
|
|
|
{ &exaStdTwo , &exbStdTwo , &opSHR , &opReg1 , 1, 1 },
|
|
|
|
{ &exaStdTwo , &exbJMP , NULL , &opReg1 , 1, 3 },
|
|
|
|
{ &exaStdTwo , &exbStdTwo , &opSAR , &opReg1 , 1, 1 },
|
|
|
|
{ &exaStdTwo , &exbStdTwo , &opMUL , &opReg1 , 1, 13 },
|
|
|
|
{ &exaDivision , &exbStdTwo , &opDIV , &opReg1 , 1, 38 },
|
|
|
|
{ &exaStdTwo , &exbStdTwo , &opMULU , &opReg1 , 1, 13 },
|
|
|
|
{ &exaDivision , &exbStdTwo , &opDIVU , &opReg1 , 1, 36 },
|
|
|
|
{ &exaStdTwo , &exbStdTwo , &opOR , &opReg1 , 1, 1 },
|
|
|
|
{ &exaStdTwo , &exbStdTwo , &opAND , &opReg1 , 1, 1 },
|
|
|
|
{ &exaStdTwo , &exbStdTwo , &opXOR , &opReg1 , 1, 1 },
|
|
|
|
{ &exaStdTwo , &exbStdTwo , &opNOT , &opReg1 , 1, 1 },
|
|
|
|
{ &exaStdTwo , &exbStdTwo , &opMOV , &opImm5S , 1, 1 }, /* 0x10 */
|
|
|
|
{ &exaStdTwo , &exbStdTwo , &opADD , &opImm5S , 1, 1 },
|
|
|
|
{ &exaStdTwo , &exbStdTwo , &opSETF , &opImm5U , 1, 1 },
|
|
|
|
{ &exaStdTwo , &exbStdTwo , &opCMP , &opImm5S , 1, 1 },
|
|
|
|
{ &exaStdTwo , &exbStdTwo , &opSHL , &opImm5U , 1, 1 },
|
|
|
|
{ &exaStdTwo , &exbStdTwo , &opSHR , &opImm5U , 1, 1 },
|
|
|
|
{ &exaDefault , &exbCLI , NULL , NULL , 1, 12 },
|
|
|
|
{ &exaStdTwo , &exbStdTwo , &opSAR , &opImm5U , 1, 1 },
|
|
|
|
{ &exaTRAP , NULL , NULL , NULL , 1, 15 },
|
|
|
|
{ &exaDefault , &exbRETI , NULL , NULL , 1, 10 },
|
|
|
|
{ &exaDefault , &exbHALT , NULL , NULL , 1, 0 },
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL , 1, 0 },
|
|
|
|
{ &exaStdTwo , &exbStdTwo , &opLDSR , &opImm5U , 1, 8 },
|
|
|
|
{ &exaStdTwo , &exbStdTwo , &opSTSR , &opImm5U , 1, 8 },
|
|
|
|
{ &exaDefault , &exbSEI , NULL , NULL , 1, 12 },
|
|
|
|
{ &exaBitString, NULL , NULL , NULL , 1, 0 },
|
|
|
|
{ &exaBCOND , &exbJMP , NULL , NULL , 1, 0 }, /* 0x20 */
|
|
|
|
{ &exaBCOND , &exbJMP , NULL , NULL , 1, 0 },
|
|
|
|
{ &exaBCOND , &exbJMP , NULL , NULL , 1, 0 },
|
|
|
|
{ &exaBCOND , &exbJMP , NULL , NULL , 1, 0 },
|
|
|
|
{ &exaBCOND , &exbJMP , NULL , NULL , 1, 0 },
|
|
|
|
{ &exaBCOND , &exbJMP , NULL , NULL , 1, 0 },
|
|
|
|
{ &exaBCOND , &exbJMP , NULL , NULL , 1, 0 },
|
|
|
|
{ &exaBCOND , &exbJMP , NULL , NULL , 1, 0 },
|
|
|
|
{ &exaStdThree , &exbStdThree, &opMOVEA, &opImm16S, 2, 1 },
|
|
|
|
{ &exaStdThree , &exbStdThree, &opADD , &opImm16S, 2, 1 },
|
|
|
|
{ &exaJR , &exbJMP , NULL , NULL , 2, 0 },
|
|
|
|
{ &exaJR , &exbJAL , NULL , NULL , 2, 0 },
|
|
|
|
{ &exaStdThree , &exbStdThree, &opOR , &opImm16U, 2, 1 },
|
|
|
|
{ &exaStdThree , &exbStdThree, &opAND , &opImm16U, 2, 1 },
|
|
|
|
{ &exaStdThree , &exbStdThree, &opXOR , &opImm16U, 2, 1 },
|
|
|
|
{ &exaStdThree , &exbStdThree, &opMOVEA, &opImm16H, 2, 1 },
|
|
|
|
{ &exaRead , &exbStdTwo , &opMOV , &opImm16S, 2, VB_S8 }, /* 0x30 */
|
|
|
|
{ &exaRead , &exbStdTwo , &opMOV , &opImm16S, 2, VB_S16 },
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL , 1, 0 },
|
|
|
|
{ &exaRead , &exbStdTwo , &opMOV , &opImm16S, 2, VB_S32 },
|
|
|
|
{ &exaWrite , &exbStdTwo , &opST , &opImm16S, 2, VB_S8 },
|
|
|
|
{ &exaWrite , &exbStdTwo , &opST , &opImm16S, 2, VB_S16 },
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL , 1, 0 },
|
|
|
|
{ &exaWrite , &exbStdTwo , &opST , &opImm16S, 2, VB_S32 },
|
|
|
|
{ &exaRead , &exbStdTwo , &opMOV , &opImm16S, 2, VB_U8 },
|
|
|
|
{ &exaRead , &exbStdTwo , &opMOV , &opImm16S, 2, VB_U16 },
|
|
|
|
{ &exaCAXI , &exbCAXI , NULL , &opImm16S, 2, 26 },
|
|
|
|
{ &exaRead , &exbStdTwo , &opMOV , &opImm16S, 2, VB_S32 },
|
|
|
|
{ &exaWrite , &exbStdTwo , &opST , &opImm16S, 2, VB_U8 },
|
|
|
|
{ &exaWrite , &exbStdTwo , &opST , &opImm16S, 2, VB_U16 },
|
|
|
|
{ &exaFloatendo, NULL , NULL , NULL , 2, 0 },
|
|
|
|
{ &exaWrite , &exbStdTwo , &opST , &opImm16S, 2, VB_S32 }
|
|
|
|
};
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Bit string opcode definitions */
|
|
|
|
static const OpDef OPDEFS_BITSTRING[] = {
|
|
|
|
{ &exaBitSearch , &exbBitSearch , NULL , NULL, 1, 0 }, /* 0x00 */
|
|
|
|
{ &exaBitSearch , &exbBitSearch , NULL , NULL, 1, 0 },
|
|
|
|
{ &exaBitSearch , &exbBitSearch , NULL , NULL, 1, 0 },
|
|
|
|
{ &exaBitSearch , &exbBitSearch , NULL , NULL, 1, 0 },
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL, 1, 0 },
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL, 1, 0 },
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL, 1, 0 },
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL, 1, 0 },
|
|
|
|
{ &exaBitBitwise, &exbBitBitwise, &opORBSU , NULL, 1, 0 },
|
|
|
|
{ &exaBitBitwise, &exbBitBitwise, &opANDBSU , NULL, 1, 0 },
|
|
|
|
{ &exaBitBitwise, &exbBitBitwise, &opXORBSU , NULL, 1, 0 },
|
|
|
|
{ &exaBitBitwise, &exbBitBitwise, &opMOVBSU , NULL, 1, 0 },
|
|
|
|
{ &exaBitBitwise, &exbBitBitwise, &opORNBSU , NULL, 1, 0 },
|
|
|
|
{ &exaBitBitwise, &exbBitBitwise, &opANDNBSU, NULL, 1, 0 },
|
|
|
|
{ &exaBitBitwise, &exbBitBitwise, &opXORNBSU, NULL, 1, 0 },
|
|
|
|
{ &exaBitBitwise, &exbBitBitwise, &opNOTBSU , NULL, 1, 0 },
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL, 1, 0 }, /* 0x10 */
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL, 1, 0 },
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL, 1, 0 },
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL, 1, 0 },
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL, 1, 0 },
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL, 1, 0 },
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL, 1, 0 },
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL, 1, 0 },
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL, 1, 0 },
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL, 1, 0 },
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL, 1, 0 },
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL, 1, 0 },
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL, 1, 0 },
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL, 1, 0 },
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL, 1, 0 },
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL, 1, 0 }
|
|
|
|
};
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Floating-point/Nintendo opcode definitions */
|
|
|
|
static const OpDef OPDEFS_FLOATENDO[] = {
|
|
|
|
{ &exaFloating2, &exbFloating, &opSUBF_S, NULL , 2, 10 }, /* 0x00 */
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL , 2, 0 },
|
|
|
|
{ &exaStdTwo , &exbStdTwo , &opCVT_WS, &opReg1, 2, 16 },
|
|
|
|
{ &exaFloating1, &exbFloating, NULL , NULL , 2, 14 },
|
|
|
|
{ &exaFloating2, &exbFloating, &opADDF_S, NULL , 2, 28 },
|
|
|
|
{ &exaFloating2, &exbFloating, &opSUBF_S, NULL , 2, 30 },
|
|
|
|
{ &exaFloating2, &exbFloating, &opMULF_S, NULL , 2, 28 },
|
|
|
|
{ &exaFloating2, &exbFloating, &opDIVF_S, NULL , 2, 44 },
|
|
|
|
{ &exaStdTwo , &exbStdTwo , &opXB , &opReg2, 2, 6 },
|
|
|
|
{ &exaStdTwo , &exbStdTwo , &opXH , &opReg2, 2, 1 },
|
|
|
|
{ &exaStdTwo , &exbStdTwo , &opREV , &opReg1, 2, 22 },
|
|
|
|
{ &exaFloating1, &exbFloating, NULL , NULL , 2, 14 },
|
|
|
|
{ &exaStdTwo , &exbStdTwo , &opMPYHW , &opReg1, 2, 22 },
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL , 2, 0 },
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL , 2, 0 },
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL , 2, 0 },
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL , 2, 0 }, /* 0x10 */
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL , 2, 0 },
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL , 2, 0 },
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL , 2, 0 },
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL , 2, 0 },
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL , 2, 0 },
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL , 2, 0 },
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL , 2, 0 },
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL , 2, 0 },
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL , 2, 0 },
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL , 2, 0 },
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL , 2, 0 },
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL , 2, 0 },
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL , 2, 0 },
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL , 2, 0 },
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL , 2, 0 },
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL , 2, 0 }, /* 0x20 */
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL , 2, 0 },
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL , 2, 0 },
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL , 2, 0 },
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL , 2, 0 },
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL , 2, 0 },
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL , 2, 0 },
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL , 2, 0 },
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL , 2, 0 },
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL , 2, 0 },
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL , 2, 0 },
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL , 2, 0 },
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL , 2, 0 },
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL , 2, 0 },
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL , 2, 0 },
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL , 2, 0 },
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL , 2, 0 }, /* 0x30 */
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL , 2, 0 },
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL , 2, 0 },
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL , 2, 0 },
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL , 2, 0 },
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL , 2, 0 },
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL , 2, 0 },
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL , 2, 0 },
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL , 2, 0 },
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL , 2, 0 },
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL , 2, 0 },
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL , 2, 0 },
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL , 2, 0 },
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL , 2, 0 },
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL , 2, 0 },
|
|
|
|
{ &exaIllegal , NULL , NULL , NULL , 2, 0 },
|
|
|
|
};
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Bit string instructions */
|
|
|
|
static int exaBitString(VB *vb) {
|
|
|
|
OpDef *def; /* Opcode definition */
|
|
|
|
vb->cpu.inst.def = def =
|
|
|
|
(OpDef *) &OPDEFS_BITSTRING[vb->cpu.inst.code[0] & 31];
|
|
|
|
return def->executeA(vb);
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Floating-point and Nintendo instruction parser */
|
|
|
|
static int exaFloatendo(VB *vb) {
|
|
|
|
OpDef *def; /* Opcode definition */
|
|
|
|
vb->cpu.inst.def = def =
|
|
|
|
(OpDef *) &OPDEFS_FLOATENDO[vb->cpu.inst.code[1] >> 10 & 63];
|
|
|
|
return def->executeA(vb);
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/****************************** Pipeline Stages ******************************/
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Raise an exception */
|
|
|
|
static void cpuException(VB *vb) {
|
|
|
|
vb->cpu.inst.def = (OpDef *) &OPDEF_EXCEPTION;
|
|
|
|
vb->cpu.stage = CPU_EXECUTE_A;
|
|
|
|
vb->cpu.inst.aux[0] = 0xFFFF0000 | (vb->cpu.exception & 0xFFF0);
|
|
|
|
if (vb->cpu.inst.aux[0] == (int32_t) 0xFFFFFF70)
|
|
|
|
vb->cpu.inst.aux[0] = 0xFFFFFF60;
|
|
|
|
}
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Execute: Pre-processing, does not update state */
|
|
|
|
static int cpuExecuteA(VB *vb) {
|
|
|
|
OpDef *def; /* Opcode descriptor */
|
|
|
|
VBInstruction inst; /* Instruction descriptor */
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* First invocation */
|
|
|
|
if (vb->cpu.step == 0 && vb->cpu.exception == 0) {
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Call the breakpoint handler */
|
|
|
|
if (vb->onExecute != NULL) {
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Query the application */
|
|
|
|
inst.address = vb->cpu.pc;
|
|
|
|
inst.code[0] = vb->cpu.inst.code[0];
|
|
|
|
inst.code[1] = vb->cpu.inst.code[1];
|
|
|
|
inst.size = vb->cpu.inst.size;
|
|
|
|
if (vb->onExecute(vb, &inst))
|
2021-09-19 01:31:40 +00:00
|
|
|
return 1;
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Apply changes */
|
|
|
|
vb->cpu.inst.code[0] = inst.code[0];
|
|
|
|
vb->cpu.inst.code[1] = inst.code[1];
|
|
|
|
vb->cpu.inst.size = inst.size;
|
|
|
|
vb->cpu.inst.def =
|
|
|
|
(OpDef *) &OPDEFS[vb->cpu.inst.code[0] >> 10 & 63];
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Detect non-bit string instruction */
|
|
|
|
if ((vb->cpu.inst.code[0] & 0xFC00) != 0x7C00)
|
|
|
|
vb->cpu.bitstring = 0;
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Processing before updating simulation state */
|
|
|
|
def = vb->cpu.inst.def;
|
|
|
|
for (;;) {
|
|
|
|
if (def->executeA(vb))
|
|
|
|
return 1;
|
|
|
|
vb->cpu.step = 0;
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Advance to exception processing */
|
|
|
|
if (vb->cpu.exception == 0 || def == &OPDEF_EXCEPTION)
|
|
|
|
break;
|
|
|
|
def = (OpDef *) &OPDEF_EXCEPTION;
|
|
|
|
cpuException(vb);
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Advance to execute B */
|
|
|
|
vb->cpu.stage = CPU_EXECUTE_B;
|
|
|
|
return 0;
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Execute: Post-processing, updates state */
|
|
|
|
static void cpuExecuteB(VB *vb) {
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Perform the operation and update state */
|
|
|
|
((OpDef *) vb->cpu.inst.def)->executeB(vb);
|
|
|
|
vb->cpu.program[0] = 0;
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Advance to next pipeline stage */
|
|
|
|
if (vb->cpu.stage == CPU_EXECUTE_B) {
|
|
|
|
if (cpuCheckIRQs(vb))
|
|
|
|
cpuException(vb);
|
|
|
|
else if (vb->cpu.bitstring != 0)
|
|
|
|
vb->cpu.stage = CPU_EXECUTE_A;
|
|
|
|
else vb->cpu.stage = CPU_FETCH;
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
}
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Retrieve instruction data from the bus */
|
|
|
|
static int cpuFetch(VB *vb) {
|
|
|
|
OpDef *def; /* Opcode definition */
|
2021-09-19 01:31:40 +00:00
|
|
|
|
|
|
|
/* First fetch */
|
2023-03-11 00:44:57 +00:00
|
|
|
if (vb->cpu.step == 0) {
|
|
|
|
if (cpuReadFetch(vb))
|
|
|
|
return 1;
|
|
|
|
vb->cpu.inst.def = def =
|
|
|
|
(OpDef *) &OPDEFS[vb->cpu.inst.code[0] >> 10 & 0x003F];
|
|
|
|
vb->cpu.inst.size = def->size << 1;
|
|
|
|
vb->cpu.step = 1;
|
|
|
|
} else def = (OpDef *) vb->cpu.inst.def;
|
|
|
|
|
|
|
|
/* Second fetch */
|
|
|
|
for (; vb->cpu.step < def->size; vb->cpu.step++) {
|
|
|
|
if (cpuReadFetch(vb))
|
|
|
|
return 1;
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Advance to execute A */
|
|
|
|
vb->cpu.stage = CPU_EXECUTE_A;
|
|
|
|
vb->cpu.step = 0;
|
2021-09-19 01:31:40 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/***************************** Module Functions ******************************/
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Process a simulation for a given number of clocks */
|
|
|
|
static int cpuEmulate(VB *vb, uint32_t clocks) {
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Process all clocks */
|
|
|
|
for (;;) {
|
|
|
|
|
|
|
|
/* Processing by pipeline stage */
|
|
|
|
switch (vb->cpu.stage) {
|
|
|
|
|
|
|
|
/* Fetch: Retrive instruction code from memory */
|
|
|
|
case CPU_FETCH:
|
|
|
|
if (cpuFetch(vb))
|
|
|
|
return 1;
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* Execute A: Check for exceptions, configure CPU clocks */
|
|
|
|
case CPU_EXECUTE_A:
|
|
|
|
if (cpuExecuteA(vb))
|
|
|
|
return 1;
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* Execute B: Wait clocks and update state */
|
|
|
|
case CPU_EXECUTE_B:
|
|
|
|
|
|
|
|
/* Clocks remaining exceeds emulation clocks */
|
|
|
|
if (clocks < vb->cpu.clocks) {
|
|
|
|
vb->cpu.clocks -= clocks;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Update simulation state */
|
|
|
|
clocks -= vb->cpu.clocks;
|
|
|
|
vb->cpu.clocks = 0;
|
|
|
|
cpuExecuteB(vb);
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* Halt: Wait for an interrupt */
|
|
|
|
case CPU_HALT:
|
|
|
|
if (!cpuCheckIRQs(vb))
|
|
|
|
return 0;
|
|
|
|
cpuException(vb);
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* Fatal exception: Cannot recover */
|
|
|
|
case CPU_FATAL:
|
|
|
|
return 0;
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
};
|
2021-09-19 01:31:40 +00:00
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Unreachable */
|
2021-09-19 01:31:40 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2023-03-11 00:44:57 +00:00
|
|
|
/* Compute clocks without breakpoint or state change */
|
|
|
|
static int cpuUntil(VB *vb, uint32_t clocks) {
|
|
|
|
return vb->cpu.stage == CPU_HALT || vb->cpu.stage == CPU_FATAL ?
|
|
|
|
clocks : Min(vb->cpu.clocks, clocks);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#endif /* VBAPI */
|