pvbemu/core/cpu.c

1650 lines
54 KiB
C

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