pvbemu/core/cpu.c

1573 lines
44 KiB
C

/* This file is included into vb.c and cannot be compiled on its own. */
#ifdef VBAPI
/********************************* Constants *********************************/
/* Operation IDs */
#define CPU_EXCEPTION 0
#define CPU_HALTING 1
#define CPU_FATAL 2
#define CPU_FETCH 3
#define CPU_ILLEGAL 4
#define CPU_BITSTRING 5
#define CPU_FLOATENDO 6
#define CPU_ADD_IMM 7
#define CPU_ADD_REG 8
#define CPU_ADDF_S 9
#define CPU_ADDI 10
#define CPU_AND 11
#define CPU_ANDBSU 12
#define CPU_ANDI 13
#define CPU_ANDNBSU 14
#define CPU_BCOND 15
#define CPU_CAXI 16
#define CPU_CLI 17
#define CPU_CMP_IMM 18
#define CPU_CMP_REG 19
#define CPU_CMPF_S 20
#define CPU_CVT_SW 21
#define CPU_CVT_WS 22
#define CPU_DIV 23
#define CPU_DIVF_S 24
#define CPU_DIVU 25
#define CPU_HALT 26
#define CPU_IN_B 27
#define CPU_IN_H 28
#define CPU_IN_W 29
#define CPU_JAL 30
#define CPU_JMP 31
#define CPU_JR 32
#define CPU_LD_B 33
#define CPU_LD_H 34
#define CPU_LD_W 35
#define CPU_LDSR 36
#define CPU_MOV_IMM 37
#define CPU_MOV_REG 38
#define CPU_MOVBSU 39
#define CPU_MOVEA 40
#define CPU_MOVHI 41
#define CPU_MPYHW 42
#define CPU_MUL 43
#define CPU_MULF_S 44
#define CPU_MULU 45
#define CPU_NOT 46
#define CPU_NOTBSU 47
#define CPU_OR 48
#define CPU_ORBSU 49
#define CPU_ORI 50
#define CPU_ORNBSU 51
#define CPU_OUT_B 52
#define CPU_OUT_H 53
#define CPU_OUT_W 54
#define CPU_RETI 55
#define CPU_REV 56
#define CPU_SAR_IMM 57
#define CPU_SAR_REG 58
#define CPU_SCH0BSD 59
#define CPU_SCH0BSU 60
#define CPU_SCH1BSD 61
#define CPU_SCH1BSU 62
#define CPU_SEI 63
#define CPU_SETF 64
#define CPU_SHL_IMM 65
#define CPU_SHL_REG 66
#define CPU_SHR_IMM 67
#define CPU_SHR_REG 68
#define CPU_ST_B 69
#define CPU_ST_H 70
#define CPU_ST_W 71
#define CPU_STSR 72
#define CPU_SUB 73
#define CPU_SUBF_S 74
#define CPU_TRAP 75
#define CPU_TRNC_SW 76
#define CPU_XB 77
#define CPU_XH 78
#define CPU_XOR 79
#define CPU_XORBSU 80
#define CPU_XORI 81
#define CPU_XORNBSU 82
/* Abstract operand types */
#define CPU_IMP(x) -x-1
#define CPU_DISP9 1
#define CPU_DISP26 2
#define CPU_IMM16S 3
#define CPU_IMM16U 4
#define CPU_IMM5S 5
#define CPU_IMM5U 6
#define CPU_MEM 7
#define CPU_REG1 8
#define CPU_REG2 9
/* Functional operand types */
#define CPU_LITERAL 0
#define CPU_MEMORY 1
#define CPU_REGISTER 2
/* Bit string operations */
#define CPU_AND_BS 0
#define CPU_ANDN_BS 1
#define CPU_MOV_BS 2
#define CPU_NOT_BS 3
#define CPU_OR_BS 4
#define CPU_ORN_BS 5
#define CPU_XOR_BS 6
#define CPU_XORN_BS 7
/* Instruction code lengths by opcode */
static const uint8_t INST_LENGTHS[] = {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 1, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2
};
/* Highest interrupt level by IRQ bit mask value */
static const int8_t IRQ_LEVELS[] = {
-1, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4
};
/********************************** Macros ***********************************/
/* Master clocks per CPU cycles */
#define cpuClocks(x) x
/* Shorthand */
#define auxData sim->cpu.aux.data
/******************************** Lookup Data ********************************/
/* Opdefs by opcode */
static const uint8_t OPDEFS[] = {
CPU_MOV_REG, CPU_ADD_REG, CPU_SUB , CPU_CMP_REG , /* 000000 */
CPU_SHL_REG, CPU_SHR_REG, CPU_JMP , CPU_SAR_REG ,
CPU_MUL , CPU_DIV , CPU_MULU , CPU_DIVU ,
CPU_OR , CPU_AND , CPU_XOR , CPU_NOT ,
CPU_MOV_IMM, CPU_ADD_IMM, CPU_SETF , CPU_CMP_IMM , /* 010000 */
CPU_SHL_IMM, CPU_SHR_IMM, CPU_CLI , CPU_SAR_IMM ,
CPU_TRAP , CPU_RETI , CPU_HALT , CPU_ILLEGAL ,
CPU_LDSR , CPU_STSR , CPU_SEI , CPU_BITSTRING,
CPU_BCOND , CPU_BCOND , CPU_BCOND , CPU_BCOND , /* 100000 */
CPU_BCOND , CPU_BCOND , CPU_BCOND , CPU_BCOND ,
CPU_MOVEA , CPU_ADDI , CPU_JR , CPU_JAL ,
CPU_ORI , CPU_ANDI , CPU_XORI , CPU_MOVHI ,
CPU_LD_B , CPU_LD_H , CPU_ILLEGAL , CPU_LD_W , /* 110000 */
CPU_ST_B , CPU_ST_H , CPU_ILLEGAL , CPU_ST_W ,
CPU_IN_B , CPU_IN_H , CPU_CAXI , CPU_IN_W ,
CPU_OUT_B , CPU_OUT_H , CPU_FLOATENDO, CPU_OUT_W
};
/* Opdefs by bit string sub-opcode */
static const uint8_t OPDEFS_BITSTRING[] = {
CPU_SCH0BSU, CPU_SCH0BSD, CPU_SCH1BSU, CPU_SCH1BSD, /* 00000 */
CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL,
CPU_ORBSU , CPU_ANDBSU , CPU_XORBSU , CPU_MOVBSU , /* 01000 */
CPU_ORNBSU , CPU_ANDNBSU, CPU_XORNBSU, CPU_NOTBSU ,
CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, /* 10000 */
CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL,
CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, /* 11000 */
CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL
};
/* Opdefs by floating-point/Nintendo sub-opcodes */
static const uint8_t OPDEFS_FLOATENDO[] = {
CPU_CMPF_S , CPU_ILLEGAL, CPU_CVT_WS , CPU_CVT_SW , /* 000000 */
CPU_ADDF_S , CPU_SUBF_S , CPU_MULF_S , CPU_DIVF_S ,
CPU_XB , CPU_XH , CPU_REV , CPU_TRNC_SW,
CPU_MPYHW , CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL,
CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, /* 010000 */
CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL,
CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL,
CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL,
CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, /* 100000 */
CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL,
CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL,
CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL,
CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, /* 110000 */
CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL,
CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL,
CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL
};
/***************************** Callback Handlers *****************************/
/* Prepare to handle an exception */
#ifndef VB_DIRECT_EXCEPTION
#define VB_ON_EXCEPTION sim->onException
#else
extern int vbxOnException(VB *, uint16_t *);
#define VB_ON_EXCEPTION sim->onException
#endif
static int cpuOnException(VB *sim, uint16_t *cause) {
return sim->onException != NULL && VB_ON_EXCEPTION(sim, cause);
}
#undef VB_ON_EXCEPTION
/* Prepare to execute an instruction */
#ifndef VB_DIRECT_EXECUTE
#define VB_ON_EXECUTE sim->onExecute
#else
extern int vbxOnExecute(VB *, uint32_t, const uint16_t *, int);
#define VB_ON_EXECUTE vbxOnExecute
#endif
static int cpuOnExecute(VB *sim) {
return
sim->onExecute != NULL &&
VB_ON_EXECUTE(sim, sim->cpu.pc, sim->cpu.code, sim->cpu.length)
;
}
#undef VB_ON_EXECUTE
/* Read a value from the memory bus */
#ifndef VB_DIRECT_READ
#define VB_ON_READ sim->onRead
#else
extern int vbxOnRead(VB *, uint32_t, int, int32_t *, uint32_t *);
#define VB_ON_READ vbxOnRead
#endif
static int cpuRead(VB *sim, uint32_t address, int type, int32_t *value) {
uint32_t cycles = 4; /* TODO: Research this */
/* Retrieve the value from the simulation state directly */
busRead(sim, address, type, value);
/* Invoke the callback if available */
if (
sim->onRead != NULL &&
VB_ON_READ(sim, address, type, value, &cycles)
) return 1;
/* Update state */
sim->cpu.clocks += cpuClocks(cycles);
return 0;
}
#undef VB_ON_READ
/* Fetch a code unit from the memory bus */
#ifndef VB_DIRECT_FETCH
#define VB_ON_FETCH sim->onFetch
#else
extern int vbxOnFetch(VB *, int, uint32_t, int32_t *, uint32_t *);
#define VB_ON_FETCH vbxOnFetch
#endif
static int cpuReadFetch(VB *sim, int fetch, uint32_t address, int32_t *value) {
uint32_t cycles = 0;
/* Retrieve the value from the simulation state directly */
busRead(sim, address, VB_U16, value);
/* Invoke the callback if available */
if (
sim->onFetch != NULL &&
VB_ON_FETCH(sim, fetch, address, value, &cycles)
) return 1;
/* Update state */
sim->cpu.clocks += cpuClocks(cycles);
return 0;
}
#undef VB_ON_FETCH
/* Write a value to the memory bus */
#ifndef VB_DIRECT_WRITE
#define VB_ON_WRITE sim->onWrite
#else
extern int vbxOnWrite(VB *, uint32_t, int, int32_t *, uint32_t *, int *);
#define VB_ON_WRITE vbxOnWrite
#endif
static int cpuWrite(VB *sim, uint32_t address, int type, int32_t value) {
int cancel = 0;
uint32_t cycles = 3; /* TODO: Research this */
/* Invoke the callback if available */
if (
sim->onWrite != NULL &&
VB_ON_WRITE(sim, address, type, &value, &cycles, &cancel)
) return 1;
/* Write the value to the simulation state directly */
if (!cancel)
busWrite(sim, address, type, value, 0);
/* Update state */
sim->cpu.clocks += cpuClocks(cycles);
return 0;
}
#undef VB_ON_WRITE
/**************************** Instruction Helpers ****************************/
/* Retrieve the value of a system register */
static uint32_t cpuGetSystemRegister(VB *sim, int index) {
switch (index) {
case VB_ADTRE: return sim->cpu.adtre;
case VB_CHCW : return sim->cpu.chcw.ice << 1;
case VB_ECR : return
(uint32_t) sim->cpu.ecr.fecc << 16 |
(uint32_t) sim->cpu.ecr.eicc;
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_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;
case VB_TKCW : return 0x000000E0;
case 29 : return sim->cpu.sr29;
case 30 : return 0x00000004;
case 31 : return sim->cpu.sr31;
}
return 0x00000000; /* All others */
}
/* Specify a new value for a system register */
static uint32_t cpuSetSystemRegister(VB*sim,
int index, uint32_t value, int debug) {
switch (index) {
case VB_ADTRE: return sim->cpu.adtre = value & 0xFFFFFFFE;
case VB_CHCW :
/* TODO: Configure instruction cache */
sim->cpu.chcw.ice = value >> 1 & 1;
return value & 0x00000002;
case VB_ECR :
if (debug) {
sim->cpu.ecr.fecc = value >> 16;
sim->cpu.ecr.eicc = value;
}
return
(uint32_t) sim->cpu.ecr.fecc << 16 |
(uint32_t) sim->cpu.ecr.eicc;
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_PSW :
sim->cpu.psw.i = value >> 16 & 0xF;
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;
case VB_TKCW : return 0x000000E0;
case 29 : return sim->cpu.sr29 = value;
case 30 : return 0x00000004;
case 31 : return sim->cpu.sr31 = debug ? value :
*(int32_t *) &value < 0 ? (uint32_t) -*(int32_t *) &value : value;
}
return 0x00000000; /* All others */
}
/* Raise an exception */
static void cpuThrow(VB *sim, uint16_t cause) {
sim->cpu.exception = cause;
sim->cpu.operation = CPU_EXCEPTION;
sim->cpu.step = 0;
}
/* Check for an interrupt request */
static int cpuIRQ(VB *sim) {
int level;
if (sim->cpu.psw.id | sim->cpu.psw.ep | sim->cpu.psw.np)
return 0;
level = IRQ_LEVELS[sim->cpu.irq];
if (level > sim->cpu.psw.i)
return 0;
cpuThrow(sim, 0xFE00 | level << 4);
return 1;
}
/****************************** Pipeline Stages ******************************/
/* Handle an exception */
static int cpuException(VB *sim) {
uint16_t cause = sim->cpu.exception;
/* Invoke the exception callback */
if (sim->cpu.step == 0 && cpuOnException(sim, &cause))
return 1;
/* Fatal exception */
if (sim->cpu.psw.np) {
switch (sim->cpu.step) {
case 0:
auxData.value = 0xFFFF0000 | cause;
/* Fallthrough */
case 1:
if (cpuWrite(sim, 0x00000000, VB_S32, auxData.value)) {
sim->cpu.step = 1;
return 1;
}
auxData.value = cpuGetSystemRegister(sim, VB_PSW);
/* Fallthrough */
case 2:
if (cpuWrite(sim, 0x00000004, VB_S32, auxData.value)) {
sim->cpu.step = 2;
return 1;
}
/* Fallthrough */
case 3:
if (cpuWrite(sim, 0x00000008, VB_S32, sim->cpu.pc)) {
sim->cpu.step = 3;
return 1;
}
}
/* Enter fatal halting state */
sim->cpu.halt = 1;
sim->cpu.operation = CPU_FATAL;
return 0;
}
/* Duplexed exception */
if (sim->cpu.psw.ep) {
sim->cpu.ecr.fecc = cause;
sim->cpu.fepsw = cpuGetSystemRegister(sim, VB_PSW);
sim->cpu.fepc = sim->cpu.pc;
sim->cpu.psw.np = 1;
sim->cpu.nextPC = 0xFFFFFFD0;
}
/* Regular exception */
else {
/* Interrupts only */
if ((cause & 0xFF00) == 0xFE00) {
sim->cpu.psw.i = (cause >> 4 & 7) + 1;
if (sim->cpu.halt) {
sim->cpu.halt = 0;
sim->cpu.pc += 2;
}
}
/* All exceptions */
sim->cpu.ecr.eicc = cause;
sim->cpu.eipsw = cpuGetSystemRegister(sim, VB_PSW);
sim->cpu.eipc = sim->cpu.pc;
sim->cpu.psw.ep = 1;
sim->cpu.nextPC = 0xFFFF0000 |
((cause & 0xFFF0) == 0xFF60 ? 0xFF60 : cause & 0xFFF0);
}
/* All exceptions */
sim->cpu.psw.ae = 0;
sim->cpu.psw.id = 1;
sim->cpu.operation = CPU_FETCH;
/* TODO: Research clocks */
return 0;
}
/* Fetch the code bits for the next instruction */
static int cpuFetch(VB *sim) {
int32_t value;
switch (sim->cpu.step) {
case 0:
sim->cpu.pc = sim->cpu.nextPC;
if (cpuIRQ(sim))
return 0;
/* Fallthrough */
case 1:
/* Retrieve the first code unit */
if (cpuReadFetch(sim, 0, sim->cpu.pc, &value)) {
sim->cpu.step = 1;
return 1;
}
/* Update state */
sim->cpu.code[0] = value;
sim->cpu.length = INST_LENGTHS[value >> 10 & 63];
sim->cpu.step = 3 - sim->cpu.length;
/* Wait any clocks taken */
if (sim->cpu.clocks != 0)
return 0;
/* Skip fetching a second code unit */
if (sim->cpu.length == 1)
goto Step3;
/* Fallthrough */
case 2:
/* Retrieve the second code unit */
if (cpuReadFetch(sim, 1, sim->cpu.pc + 2, &value))
return 1;
/* Update state */
sim->cpu.code[1] = value;
sim->cpu.step = 3;
/* Wait any clocks taken */
if (sim->cpu.clocks != 0)
return 0;
/* Fallthrough */
case 3: Step3:
/* Prepare to execute the instruction */
if (cpuOnExecute(sim))
return 1;
/* Select operation definition */
sim->cpu.operation = OPDEFS[sim->cpu.code[0] >> 10];
switch (sim->cpu.operation) {
case CPU_BITSTRING:
sim->cpu.operation =
OPDEFS_BITSTRING[sim->cpu.code[0] & 31];
break;
case CPU_FLOATENDO:
sim->cpu.operation =
OPDEFS_FLOATENDO[sim->cpu.code[1] >> 10];
}
/* Update state */
sim->cpu.step = 0;
}
return 0;
}
/* HALT instruction is pending */
static int cpuHalt(VB *sim) {
/* TODO: Research clocks */
return !cpuIRQ(sim);
}
/**************************** Instruction Helpers ****************************/
/* Parse the immediate 4-bit condition value */
static int32_t cpuGetCond(VB *sim) {
return sim->cpu.code[0] >> 9 & 15;
}
/* Parse the 9-bit displacement value */
static int32_t cpuGetDisp9(VB *sim) {
return SignExtend(sim->cpu.code[0], 9);
}
/* Parse the 26-bit displacement value */
static int32_t cpuGetDisp26(VB *sim) {
return SignExtend((int32_t) sim->cpu.code[0] << 16 | sim->cpu.code[1], 26);
}
/* Parse the immediate 5-bit sign-extended value */
static int32_t cpuGetImm5S(VB *sim) {
return SignExtend(sim->cpu.code[0], 5);
}
/* Parse the immediate 5-bit zero-filled value */
static int32_t cpuGetImm5U(VB *sim) {
return sim->cpu.code[0] & 31;
}
/* Parse the immediate 16-bit sign-extended value */
static int32_t cpuGetImm16S(VB *sim) {
return SignExtend(sim->cpu.code[1], 16);
}
/* Parse the immediate 16-bit zero-filled value */
static int32_t cpuGetImm16U(VB *sim) {
return sim->cpu.code[1];
}
/* Resolve the operand value for reg1 */
static int32_t cpuGetReg1(VB *sim) {
return sim->cpu.program[sim->cpu.code[0] & 31];
}
/* Resolve the operand value for reg2 */
static int32_t cpuGetReg2(VB *sim) {
return sim->cpu.program[sim->cpu.code[0] >> 5 & 31];
}
/* Supply an operand value for reg2 */
static void cpuSetReg2(VB *sim, int32_t value) {
int reg2 = sim->cpu.code[0] >> 5 & 31;
if (reg2 != 0)
sim->cpu.program[reg2] = value;
}
/* Advance to the next instruction */
static void cpuAdvance(VB *sim, uint32_t clocks) {
sim->cpu.clocks += clocks;
sim->cpu.operation = CPU_FETCH;
sim->cpu.nextPC = sim->cpu.pc + (sim->cpu.length << 1);
sim->cpu.step = 0;
}
/* Interpret floating short bits as word integer bits */
static int32_t cpuFloatToWord(float x) {
return *(int32_t *) &x;
}
/* Interpret word integer bits as floating short bits */
static float cpuWordToFloat(int32_t x) {
return *(float *) &x;
}
/* Addition common processing */
static int32_t cpuAdd(VB *sim, int32_t a, int32_t b) {
int32_t c = a + b;
sim->cpu.psw.cy = (uint32_t) c < (uint32_t) a;
sim->cpu.psw.ov = (~(a ^ b) & (a ^ c)) < 0;
sim->cpu.psw.s = c < 0;
sim->cpu.psw.z = c == 0;
return c;
}
/* Bitwise common processing */
static int32_t cpuBitwise(VB *sim, int32_t c) {
sim->cpu.psw.ov = 0;
sim->cpu.psw.s = c < 0;
sim->cpu.psw.z = c == 0;
return c;
}
/* Test a condition */
static int cpuCondition(VB *sim, int id) {
switch (id) {
case 0: return sim->cpu.psw.ov;
case 1: return sim->cpu.psw.cy;
case 2: return sim->cpu.psw.z;
case 3: return sim->cpu.psw.cy | sim->cpu.psw.z;
case 4: return sim->cpu.psw.s;
case 5: return 1;
case 6: return sim->cpu.psw.ov ^ sim->cpu.psw.s;
case 7: return (sim->cpu.psw.ov ^ sim->cpu.psw.s) | sim->cpu.psw.z;
}
return !cpuCondition(sim, id & 7);
}
/* Floating-point common processing */
static int cpuFloatCommon(VB *sim, double value, int32_t *bits) {
float floatVal;
/* Overflow */
if (value < -FLT_MAX || value > FLT_MAX) {
/* TODO: CPU does give reg2 some value */
sim->cpu.psw.fov = 1;
cpuThrow(sim, 0xFF64);
return 1;
}
/* Underflow */
if (value < 0 ? value > -FLT_MIN : value < FLT_MIN) {
sim->cpu.psw.fud = 1;
floatVal = 0;
/* TODO: Can this produce negative zero? */
}
/* Regular conversion */
else floatVal = value;
/* Precision degradation */
if (floatVal != value)
sim->cpu.psw.fpr = 1;
/* Update state */
*bits = cpuFloatToWord(floatVal);
sim->cpu.psw.cy = floatVal < 0;
sim->cpu.psw.ov = 0;
sim->cpu.psw.s = floatVal < 0;
sim->cpu.psw.z = floatVal == 0;
return 0;
}
/* Test whether a floating-point register value is a reserved operand */
static int cpuFloatReservedOne(int32_t bits) {
int32_t e = bits >> 23 & 0xFF;
return e ? (bits & 0x7FFFFFFF) != 0 : e == 0xFF;
}
/* Test whether floating-point register values are reserved operands */
static int cpuFloatReserved(VB *sim, int32_t a, int32_t b) {
if (!(cpuFloatReservedOne(a) || cpuFloatReservedOne(b)))
return 0;
sim->cpu.psw.fro = 1;
cpuThrow(sim, 0xFF60);
return 1;
}
/* Jump and branch common processing */
static void cpuJump(VB *sim, uint32_t address) {
sim->cpu.clocks += cpuClocks(3);
sim->cpu.operation = CPU_FETCH;
sim->cpu.nextPC = address & 0xFFFFFFFE;
sim->cpu.step = 0;
/* TODO: Clear prefetch buffer */
}
/* Arithmetic right shift common processing */
static int32_t cpuShiftArithmetic(VB *sim, int32_t b) {
int32_t a = cpuGetReg2(sim);
#ifndef VB_SIGNED_PROPAGATE
int32_t c = b == 0 ? a : SignExtend(a >> b, 32 - b);
#else
int32_t c = a >> b;
#endif
sim->cpu.psw.cy = b == 0 ? 0 : a >> (b - 1) & 1;
sim->cpu.psw.ov = 0;
sim->cpu.psw.s = c < 0;
sim->cpu.psw.z = c == 0;
return c;
}
/* Left shift common processing */
static int32_t cpuShiftLeft(VB *sim, int32_t b) {
int32_t a = cpuGetReg2(sim);
int32_t c = (uint32_t) a << b;
sim->cpu.psw.cy = b == 0 ? 0 : a >> (32 - b) & 1;
sim->cpu.psw.ov = 0;
sim->cpu.psw.s = c < 0;
sim->cpu.psw.z = c == 0;
return c;
}
/* Logical right shift common processing */
static int32_t cpuShiftRight(VB *sim, int32_t b) {
int32_t a = cpuGetReg2(sim);
int32_t c = b == 0 ? (uint32_t) a :
a >> b & ~((uint32_t) 0xFFFFFFFF << (32 - b));
sim->cpu.psw.cy = b == 0 ? 0 : a >> (b - 1) & 1;
sim->cpu.psw.ov = 0;
sim->cpu.psw.s = c < 0;
sim->cpu.psw.z = c == 0;
return c;
}
/* Subtraction common processing */
static int32_t cpuSubtract(VB *sim, int32_t a, int32_t b) {
int32_t c = a - b;
sim->cpu.psw.cy = (uint32_t) a < (uint32_t) b;
sim->cpu.psw.ov = ((a ^ b) & (a ^ c)) < 0;
sim->cpu.psw.s = c < 0;
sim->cpu.psw.z = c == 0;
return c;
}
/* Memory load/input instruction */
static int cpuLD_IN(VB *sim, int type) {
switch (sim->cpu.step) {
case 0:
auxData.address = cpuGetReg1(sim) + cpuGetImm16S(sim);
/* Fallthrough */
case 1:
/* Read the value from memory */
if (cpuRead(sim, auxData.address, type, &auxData.value)) {
sim->cpu.step = 1;
return 1;
}
/* Update state */
sim->cpu.clocks += cpuClocks(1);
/* Wait for clocks taken */
sim->cpu.step = 2;
return 0;
case 2:
cpuSetReg2(sim, auxData.value);
cpuAdvance(sim, cpuClocks(4)); /* TODO: Research */
}
return 0;
}
/* Memory store/output instruction */
static int cpuST_OUT(VB *sim, int type) {
switch (sim->cpu.step) {
case 0:
auxData.address = cpuGetReg1(sim) + cpuGetImm16S(sim);
/* Fallthrough */
case 1:
/* Write the value to memory */
if (cpuWrite(sim, auxData.address, type, cpuGetReg2(sim))) {
sim->cpu.step = 1;
return 1;
}
/* Update state */
sim->cpu.clocks += cpuClocks(1);
/* Wait for clocks taken */
sim->cpu.step = 2;
return 0;
case 2:
cpuSetReg2(sim, auxData.value);
cpuAdvance(sim, cpuClocks(3)); /* TODO: Research */
}
return 0;
}
/************************** Instruction Operations ***************************/
/* ADD immediate */
static void cpuADDImm(VB *sim) {
cpuSetReg2(sim, cpuAdd(sim, cpuGetReg2(sim), cpuGetImm5S(sim)));
cpuAdvance(sim, cpuClocks(1));
}
/* ADD register */
static void cpuADDReg(VB *sim) {
cpuSetReg2(sim, cpuAdd(sim, cpuGetReg2(sim), cpuGetReg1(sim)));
cpuAdvance(sim, cpuClocks(1));
}
/* ADDF.S */
static void cpuADDF_S(VB *sim) {
int32_t a = cpuGetReg2(sim);
int32_t b = cpuGetReg1(sim);
double c; /* Result */
int32_t d; /* Float bits for result */
if (cpuFloatReserved(sim, a, b))
return;
c = (double) cpuWordToFloat(a) + cpuWordToFloat(b);
if (cpuFloatCommon(sim, c, &d))
return;
cpuSetReg2(sim, d);
cpuAdvance(sim, cpuClocks(28)); /* TODO: Research */
}
/* ADDI */
static void cpuADDI(VB *sim) {
cpuSetReg2(sim, cpuAdd(sim, cpuGetReg1(sim), cpuGetImm16S(sim)));
cpuAdvance(sim, cpuClocks(1));
}
/* AND */
static void cpuAND(VB *sim) {
cpuSetReg2(sim, cpuBitwise(sim, cpuGetReg2(sim) & cpuGetReg1(sim)));
cpuAdvance(sim, cpuClocks(1));
}
/* ANDI */
static void cpuANDI(VB *sim) {
cpuSetReg2(sim, cpuBitwise(sim, cpuGetReg1(sim) & cpuGetImm16U(sim)));
cpuAdvance(sim, cpuClocks(1));
}
/* BCOND */
static void cpuBCOND(VB *sim) {
if (cpuCondition(sim, cpuGetCond(sim)))
cpuJump(sim, sim->cpu.pc + cpuGetDisp9(sim));
else cpuAdvance(sim, cpuClocks(1));
}
/* CAXI */
static int cpuCAXI(VB *sim) {
switch (sim->cpu.step) {
case 0:
auxData.address = cpuGetReg1(sim) + cpuGetImm16S(sim);
/* Fallthrough */
case 1:
/* Read the compare value */
if (cpuRead(sim, auxData.address, VB_S32, &auxData.value)) {
sim->cpu.step = 1;
return 1;
}
/* Update state */
sim->cpu.clocks += cpuClocks(1); /* TODO: Research */
cpuSubtract(sim, cpuGetReg2(sim), auxData.value);
if (sim->cpu.psw.z)
auxData.value = sim->cpu.program[30];
/* Wait for clocks taken */
sim->cpu.step = 2;
return 0;
case 2:
/* Write the exchange value */
if (cpuWrite(sim, auxData.address, VB_S32, auxData.value))
return 1;
/* Update state */
cpuSetReg2(sim, auxData.value);
cpuAdvance(sim, cpuClocks(25)); /* TODO: Research */
}
return 0;
}
/* CLI */
static void cpuCLI(VB *sim) {
sim->cpu.psw.id = 0;
cpuAdvance(sim, cpuClocks(12));
}
/* CMP immediate */
static void cpuCMPImm(VB *sim) {
cpuSubtract(sim, cpuGetReg2(sim), cpuGetImm5S(sim));
cpuAdvance(sim, cpuClocks(1));
}
/* CMP register */
static void cpuCMPReg(VB *sim) {
cpuSubtract(sim, cpuGetReg2(sim), cpuGetReg1(sim));
cpuAdvance(sim, cpuClocks(1));
}
/* CMPF.S */
static void cpuCMPF_S(VB *sim) {
int32_t a = cpuGetReg2(sim);
int32_t b = cpuGetReg1(sim);
double c; /* Result */
int32_t d; /* Float bits for result */
if (cpuFloatReserved(sim, a, b))
return;
c = (double) cpuWordToFloat(a) - cpuWordToFloat(b);
if (cpuFloatCommon(sim, c, &d))
return;
cpuAdvance(sim, cpuClocks(10)); /* TODO: Research */
}
/* CVT.SW */
static void cpuCVT_SW(VB *sim) {
double f;
int32_t w = cpuGetReg1(sim);
if (cpuFloatReservedOne(w)) {
cpuThrow(sim, 0xFF60);
return;
}
f = cpuWordToFloat(w);
if (f <= -2147483648.5 || f >= 2147483647.5) {
sim->cpu.psw.fiv = 1;
cpuThrow(sim, 0xFF70);
return;
}
w = f < 0 ? f - 0.5 : f + 0.5;
cpuSetReg2(sim, w);
if (w != f)
sim->cpu.psw.fpr = 1;
sim->cpu.psw.ov = 0;
sim->cpu.psw.s = w < 0;
sim->cpu.psw.z = w == 0;
cpuAdvance(sim, cpuClocks(14)); /* TODO: Research */
}
/* CVT.WS */
static void cpuCVT_WS(VB *sim) {
int32_t w = cpuGetReg1(sim);
float f = w;
if (f != w)
sim->cpu.psw.fpr = 1;
w = cpuFloatToWord(f);
cpuSetReg2(sim, w);
sim->cpu.psw.cy = w < 0;
sim->cpu.psw.ov = 0;
sim->cpu.psw.s = w < 0;
sim->cpu.psw.z = w == 0;
cpuAdvance(sim, cpuClocks(16)); /* TODO: Research */
}
/* DIV */
static void cpuDIV(VB *sim) {
int32_t a; /* Dividend */
int32_t b; /* Divisor */
int32_t c; /* Remainder */
int32_t d; /* Quotient */
#ifdef VB_DIV_GENERIC
int sa; /* Sign of a */
int sb; /* Sign of b */
#endif
/* Zero division */
b = cpuGetReg1(sim);
if (b == 0) {
cpuThrow(sim, 0xFF80);
return;
}
/* Compute results */
a = cpuGetReg2(sim);
sim->cpu.psw.ov = (uint32_t) a == (uint32_t) 0x80000000 && b == -1;
#ifndef VB_DIV_GENERIC
c = a % b;
d = a / b;
#else
/* Do not divide */
if (sim->cpu.psw.ov) {
c = 0;
d = a;
}
/* Perform division */
else {
/* Take signs and absolute values of operands */
sa = a < 0;
if (sa)
a = -a;
sb = b < 0;
if (sb)
b = -b;
/* Compute results */
c = (uint32_t) a % (uint32_t) b;
if (sa)
c = -c;
d = (uint32_t) a / (uint32_t) b;
if (sa ^ sb)
d = -d;
}
#endif
sim->cpu.program[30] = c;
cpuSetReg2(sim, d);
/* Flags */
sim->cpu.psw.s = d < 0;
sim->cpu.psw.z = d == 0;
cpuAdvance(sim, cpuClocks(38));
}
/* DIVF.S */
static void cpuDIVF_S(VB *sim) {
int32_t a = cpuGetReg2(sim);
int32_t b = cpuGetReg1(sim);
double c; /* Result */
int32_t d; /* Float bits for result */
if (cpuFloatReserved(sim, a, b))
return;
if ((b & 0x7FFFFFFF) == 0) {
cpuThrow(sim, (a & 0x7FFFFFFF) == 0 ? 0xFF70 : 0xFF68);
return;
}
c = (double) cpuWordToFloat(a) / cpuWordToFloat(b);
if (cpuFloatCommon(sim, c, &d))
return;
cpuSetReg2(sim, d);
cpuAdvance(sim, cpuClocks(44));
}
/* DIVU */
static void cpuDIVU(VB *sim) {
uint32_t a; /* Dividend */
uint32_t b; /* Divisor */
int32_t c; /* Remainder */
int32_t d; /* Quotient */
/* Zero division */
b = cpuGetReg1(sim);
if (b == 0) {
cpuThrow(sim, 0xFF80);
return;
}
/* Compute results */
a = cpuGetReg2(sim);
c = a % b;
d = a / b;
sim->cpu.program[30] = c;
cpuSetReg2(sim, d);
/* Flags */
sim->cpu.psw.ov = 0;
sim->cpu.psw.s = d < 0;
sim->cpu.psw.z = d == 0;
cpuAdvance(sim, cpuClocks(36));
}
/* HALT */
static void cpuHALT(VB *sim) {
sim->cpu.operation = CPU_HALTING;
/* TODO: Research clocks */
}
/* IN.B */
static int cpuIN_B(VB *sim) {
return cpuLD_IN(sim, VB_U8);
}
/* IN.H */
static int cpuIN_H(VB *sim) {
return cpuLD_IN(sim, VB_U16);
}
/* IN.W */
static int cpuIN_W(VB *sim) {
return cpuLD_IN(sim, VB_S32);
}
/* JAL */
static void cpuJAL(VB *sim) {
sim->cpu.program[31] = sim->cpu.pc + 4;
cpuJump(sim, sim->cpu.pc + cpuGetDisp26(sim));
}
/* JMP */
static void cpuJMP(VB *sim) {
cpuJump(sim, cpuGetReg1(sim));
}
/* JR */
static void cpuJR(VB *sim) {
cpuJump(sim, sim->cpu.pc + cpuGetDisp26(sim));
}
/* LD.B */
static int cpuLD_B(VB *sim) {
return cpuLD_IN(sim, VB_S8);
}
/* LD.H */
static int cpuLD_H(VB *sim) {
return cpuLD_IN(sim, VB_S16);
}
/* LD.W */
static int cpuLD_W(VB *sim) {
return cpuLD_IN(sim, VB_S32);
}
/* LDSR */
static void cpuLDSR(VB *sim) {
cpuSetSystemRegister(sim, cpuGetImm5U(sim), cpuGetReg2(sim), 0);
cpuAdvance(sim, cpuClocks(8));
}
/* MOV immediate */
static void cpuMOVImm(VB *sim) {
cpuSetReg2(sim, cpuGetImm5S(sim));
cpuAdvance(sim, cpuClocks(1));
}
/* MOV register */
static void cpuMOVReg(VB *sim) {
cpuSetReg2(sim, cpuGetReg1(sim));
cpuAdvance(sim, cpuClocks(1));
}
/* MOVEA */
static void cpuMOVEA(VB *sim) {
cpuSetReg2(sim, cpuGetReg1(sim) + cpuGetImm16S(sim));
cpuAdvance(sim, cpuClocks(1));
}
/* MOVHI */
static void cpuMOVHI(VB *sim) {
cpuSetReg2(sim, cpuGetReg1(sim) + (cpuGetImm16U(sim) << 16));
cpuAdvance(sim, cpuClocks(1));
}
/* MPYHW */
static void cpuMPYHW(VB *sim) {
cpuSetReg2(sim, cpuGetReg2(sim) * SignExtend(cpuGetReg1(sim), 17));
cpuAdvance(sim, cpuClocks(9));
}
/* MUL */
static void cpuMUL(VB *sim) {
int64_t a = cpuGetReg2(sim);
int64_t b = cpuGetReg1(sim);
int64_t c = a * b;
int32_t d = c;
sim->cpu.program[30] = c >> 32;
cpuSetReg2(sim, d);
sim->cpu.psw.ov = d != c;
sim->cpu.psw.s = d < 0;
sim->cpu.psw.z = d == 0;
cpuAdvance(sim, 13);
}
/* MULF.S */
static void cpuMULF_S(VB *sim) {
int32_t a = cpuGetReg2(sim);
int32_t b = cpuGetReg1(sim);
double c; /* Result */
int32_t d; /* Float bits for result */
if (cpuFloatReserved(sim, a, b))
return;
c = (double) cpuWordToFloat(a) * cpuWordToFloat(b);
if (cpuFloatCommon(sim, c, &d))
return;
cpuSetReg2(sim, d);
cpuAdvance(sim, cpuClocks(30)); /* TODO: Research */
}
/* MULU */
static void cpuMULU(VB *sim) {
uint64_t a = (uint32_t) cpuGetReg2(sim);
uint64_t b = (uint32_t) cpuGetReg1(sim);
uint64_t c = a * b;
uint32_t d = c;
sim->cpu.program[30] = c >> 32;
cpuSetReg2(sim, d);
sim->cpu.psw.ov = d != c;
sim->cpu.psw.s = (int32_t) d < 0;
sim->cpu.psw.z = d == 0;
cpuAdvance(sim, 13);
}
/* NOT */
static void cpuNOT(VB *sim) {
cpuSetReg2(sim, cpuBitwise(sim, ~cpuGetReg1(sim)));
cpuAdvance(sim, cpuClocks(1));
}
/* OR */
static void cpuOR(VB *sim) {
cpuSetReg2(sim, cpuBitwise(sim, cpuGetReg2(sim) | cpuGetReg1(sim)));
cpuAdvance(sim, cpuClocks(1));
}
/* ORI */
static void cpuORI(VB *sim) {
cpuSetReg2(sim, cpuBitwise(sim, cpuGetReg1(sim) | cpuGetImm16U(sim)));
cpuAdvance(sim, cpuClocks(1));
}
/* OUT.B */
static int cpuOUT_B(VB *sim) {
return cpuST_OUT(sim, VB_U8);
}
/* OUT.H */
static int cpuOUT_H(VB *sim) {
return cpuST_OUT(sim, VB_U16);
}
/* OUT.W */
static int cpuOUT_W(VB *sim) {
return cpuST_OUT(sim, VB_S32);
}
/* RETI */
static void cpuRETI(VB *sim) {
if (sim->cpu.psw.np) {
sim->cpu.nextPC = sim->cpu.fepc;
cpuSetSystemRegister(sim, VB_PSW, sim->cpu.fepsw, 0);
} else {
sim->cpu.nextPC = sim->cpu.eipc;
cpuSetSystemRegister(sim, VB_PSW, sim->cpu.eipsw, 0);
}
sim->cpu.clocks += cpuClocks(10);
sim->cpu.operation = CPU_FETCH;
}
/* REV */
static void cpuREV(VB *sim) {
uint32_t x = cpuGetReg1(sim);
x = (x << 16 & 0xFFFF0000) | (x >> 16 & 0x0000FFFF);
x = (x << 8 & 0xFF00FF00) | (x >> 8 & 0x00FF00FF);
x = (x << 4 & 0xF0F0F0F0) | (x >> 4 & 0x0F0F0F0F);
x = (x << 2 & 0xCCCCCCCC) | (x >> 2 & 0x33333333);
x = (x << 1 & 0xAAAAAAAA) | (x >> 1 & 0x55555555);
cpuSetReg2(sim, x);
}
/* SAR immediate */
static void cpuSARImm(VB *sim) {
cpuSetReg2(sim, cpuShiftArithmetic(sim, cpuGetImm5U(sim)));
cpuAdvance(sim, cpuClocks(1));
}
/* SAR register */
static void cpuSARReg(VB *sim) {
cpuSetReg2(sim, cpuShiftArithmetic(sim, cpuGetReg1(sim) & 31));
cpuAdvance(sim, cpuClocks(1));
}
/* SEI */
static void cpuSEI(VB *sim) {
sim->cpu.psw.id = 1;
cpuAdvance(sim, cpuClocks(12));
}
/* SETF */
static void cpuSETF(VB *sim) {
cpuSetReg2(sim, cpuCondition(sim, cpuGetImm5U(sim) & 15));
cpuAdvance(sim, cpuClocks(1));
}
/* SHL immediate */
static void cpuSHLImm(VB *sim) {
cpuSetReg2(sim, cpuShiftLeft(sim, cpuGetImm5U(sim)));
cpuAdvance(sim, cpuClocks(1));
}
/* SHL register */
static void cpuSHLReg(VB *sim) {
cpuSetReg2(sim, cpuShiftLeft(sim, cpuGetReg1(sim) & 31));
cpuAdvance(sim, cpuClocks(1));
}
/* SHR immediate */
static void cpuSHRImm(VB *sim) {
cpuSetReg2(sim, cpuShiftRight(sim, cpuGetImm5U(sim)));
cpuAdvance(sim, cpuClocks(1));
}
/* SHR register */
static void cpuSHRReg(VB *sim) {
cpuSetReg2(sim, cpuShiftRight(sim, cpuGetReg1(sim) & 31));
cpuAdvance(sim, cpuClocks(1));
}
/* ST.B */
static int cpuST_B(VB *sim) {
return cpuST_OUT(sim, VB_S8);
}
/* ST.H */
static int cpuST_H(VB *sim) {
return cpuST_OUT(sim, VB_S16);
}
/* ST.W */
static int cpuST_W(VB *sim) {
return cpuST_OUT(sim, VB_S32);
}
/* STSR */
static void cpuSTSR(VB *sim) {
cpuSetReg2(sim, cpuGetSystemRegister(sim, cpuGetImm5U(sim)));
cpuAdvance(sim, cpuClocks(8));
}
/* SUB */
static void cpuSUB(VB *sim) {
cpuSetReg2(sim, cpuSubtract(sim, cpuGetReg2(sim), cpuGetReg1(sim)));
cpuAdvance(sim, cpuClocks(1));
}
/* SUBF.S */
static void cpuSUBF_S(VB *sim) {
int32_t a = cpuGetReg2(sim);
int32_t b = cpuGetReg1(sim);
double c; /* Result */
int32_t d; /* Float bits for result */
if (cpuFloatReserved(sim, a, b))
return;
c = (double) cpuWordToFloat(a) - cpuWordToFloat(b);
if (cpuFloatCommon(sim, c, &d))
return;
cpuSetReg2(sim, d);
cpuAdvance(sim, cpuClocks(28)); /* TODO: Research */
}
/* TRAP */
static void cpuTRAP(VB *sim) {
sim->cpu.clocks += cpuClocks(15);
cpuThrow(sim, 0xFFA0 + cpuGetImm5U(sim));
}
/* TRNC.SW */
static void cpuTRNC_SW(VB *sim) {
double f;
int32_t w = cpuGetReg1(sim);
if (cpuFloatReservedOne(w)) {
cpuThrow(sim, 0xFF60);
return;
}
f = cpuWordToFloat(w);
if (f <= -2147483649.0 || f >= 2147483648.0) {
sim->cpu.psw.fiv = 1;
cpuThrow(sim, 0xFF70);
return;
}
w = f;
cpuSetReg2(sim, w);
if (w != f)
sim->cpu.psw.fpr = 1;
sim->cpu.psw.ov = 0;
sim->cpu.psw.s = w < 0;
sim->cpu.psw.z = w == 0;
cpuAdvance(sim, cpuClocks(14)); /* TODO: Research */
}
/* XB */
static void cpuXB(VB *sim) {
uint32_t x = cpuGetReg2(sim);
x = (x & 0xFFFF0000) | (x << 8 & 0x0000FF00) | (x >> 8 & 0x000000FF);
cpuSetReg2(sim, x);
}
/* XH */
static void cpuXH(VB *sim) {
uint32_t x = cpuGetReg2(sim);
x = (x << 16 & 0xFFFF0000) | (x >> 16 & 0x0000FFFF);
cpuSetReg2(sim, x);
}
/* XOR */
static void cpuXOR(VB *sim) {
cpuSetReg2(sim, cpuBitwise(sim, cpuGetReg2(sim) ^ cpuGetReg1(sim)));
cpuAdvance(sim, cpuClocks(1));
}
/* XORI */
static void cpuXORI(VB *sim) {
cpuSetReg2(sim, cpuBitwise(sim, cpuGetReg1(sim) ^ cpuGetImm16U(sim)));
cpuAdvance(sim, cpuClocks(1));
}
/***************************** Library Functions *****************************/
/* Process component */
static int cpuEmulate(VB *sim, uint32_t clocks) {
int brk = 0;
/* Process until there are clocks to wait */
for (;;) {
/* The next event is after the time remaining */
if (sim->cpu.clocks > clocks) {
sim->cpu.clocks -= clocks;
return 0;
}
/* Advance forward the CPU's number of clocks */
if (sim->cpu.clocks != 0) {
clocks -= sim->cpu.clocks;
sim->cpu.clocks = 0;
}
/* Processing by operation ID */
switch (sim->cpu.operation) {
case CPU_EXCEPTION: brk = cpuException(sim); break;
case CPU_FATAL : return 0;
case CPU_FETCH : brk = cpuFetch (sim); break;
case CPU_HALTING : if (cpuHalt(sim)) return 0; break;
case CPU_ADD_IMM: cpuADDImm (sim); break;
case CPU_ADD_REG: cpuADDReg (sim); break;
case CPU_ADDF_S : cpuADDF_S (sim); break;
case CPU_ADDI : cpuADDI (sim); break;
case CPU_AND : cpuAND (sim); break;
case CPU_ANDI : cpuANDI (sim); break;
case CPU_BCOND : cpuBCOND (sim); break;
case CPU_CAXI : brk = cpuCAXI (sim); break;
case CPU_CLI : cpuCLI (sim); break;
case CPU_CMP_IMM: cpuCMPImm (sim); break;
case CPU_CMP_REG: cpuCMPReg (sim); break;
case CPU_CMPF_S : cpuCMPF_S (sim); break;
case CPU_CVT_SW : cpuCVT_SW (sim); break;
case CPU_CVT_WS : cpuCVT_WS (sim); break;
case CPU_DIV : cpuDIV (sim); break;
case CPU_DIVF_S : cpuDIVF_S (sim); break;
case CPU_DIVU : cpuDIVU (sim); break;
case CPU_HALT : cpuHALT (sim); break;
case CPU_IN_B : brk = cpuIN_B (sim); break;
case CPU_IN_H : brk = cpuIN_H (sim); break;
case CPU_IN_W : brk = cpuIN_W (sim); break;
case CPU_JAL : cpuJAL (sim); break;
case CPU_JMP : cpuJMP (sim); break;
case CPU_JR : cpuJR (sim); break;
case CPU_LD_B : brk = cpuLD_B (sim); break;
case CPU_LD_H : brk = cpuLD_H (sim); break;
case CPU_LD_W : brk = cpuLD_W (sim); break;
case CPU_LDSR : cpuLDSR (sim); break;
case CPU_MOV_IMM: cpuMOVImm (sim); break;
case CPU_MOV_REG: cpuMOVReg (sim); break;
case CPU_MOVEA : cpuMOVEA (sim); break;
case CPU_MOVHI : cpuMOVHI (sim); break;
case CPU_MPYHW : cpuMPYHW (sim); break;
case CPU_MUL : cpuMUL (sim); break;
case CPU_MULF_S : cpuMULF_S (sim); break;
case CPU_MULU : cpuMULU (sim); break;
case CPU_NOT : cpuNOT (sim); break;
case CPU_OR : cpuOR (sim); break;
case CPU_ORI : cpuORI (sim); break;
case CPU_OUT_B : brk = cpuOUT_B (sim); break;
case CPU_OUT_H : brk = cpuOUT_H (sim); break;
case CPU_OUT_W : brk = cpuOUT_W (sim); break;
case CPU_RETI : cpuRETI (sim); break;
case CPU_REV : cpuREV (sim); break;
case CPU_SAR_IMM: cpuSARImm (sim); break;
case CPU_SAR_REG: cpuSARReg (sim); break;
case CPU_SEI : cpuSEI (sim); break;
case CPU_SETF : cpuSETF (sim); break;
case CPU_SHL_IMM: cpuSHLImm (sim); break;
case CPU_SHL_REG: cpuSHLReg (sim); break;
case CPU_SHR_IMM: cpuSHRImm (sim); break;
case CPU_SHR_REG: cpuSHRReg (sim); break;
case CPU_ST_B : brk = cpuST_B (sim); break;
case CPU_ST_H : brk = cpuST_H (sim); break;
case CPU_ST_W : brk = cpuST_W (sim); break;
case CPU_STSR : cpuSTSR (sim); break;
case CPU_SUB : cpuSUB (sim); break;
case CPU_SUBF_S : cpuSUBF_S (sim); break;
case CPU_TRAP : cpuTRAP (sim); break;
case CPU_TRNC_SW: cpuTRNC_SW(sim); break;
case CPU_XB : cpuXB (sim); break;
case CPU_XH : cpuXH (sim); break;
case CPU_XOR : cpuXOR (sim); break;
case CPU_XORI : cpuXORI (sim); break;
default: return -1; /* TODO: Temporary for debugging */
}
/* A callback requested a break */
if (brk)
return 1;
}
return 0;
}
/* Determine how many clocks are guaranteed to process */
static uint32_t cpuUntil(VB *sim, uint32_t clocks) {
return sim->cpu.halt || sim->cpu.clocks > clocks ?
clocks : sim->cpu.clocks;
}
#endif /* VBAPI */