shrooms-vb-core/core/cpu.c

1876 lines
53 KiB
C
Raw Normal View History

2024-10-14 20:07:00 +00:00
/* 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
2024-10-23 21:29:11 +00:00
#define CPU_PHALT 4
#define CPU_ILLEGAL 5
#define CPU_BITSTRING 6
#define CPU_FLOATENDO 7
#define CPU_ADD_IMM 8
#define CPU_ADD_REG 9
#define CPU_ADDF_S 10
#define CPU_ADDI 11
#define CPU_AND 12
#define CPU_ANDI 13
#define CPU_BCOND 14
#define CPU_CAXI 15
#define CPU_CLI 16
#define CPU_CMP_IMM 17
#define CPU_CMP_REG 18
#define CPU_CMPF_S 19
#define CPU_CVT_SW 20
#define CPU_CVT_WS 21
#define CPU_DIV 22
#define CPU_DIVF_S 23
#define CPU_DIVU 24
#define CPU_HALT 25
#define CPU_IN_B 26
#define CPU_IN_H 27
#define CPU_IN_W 28
#define CPU_JAL 29
#define CPU_JMP 30
#define CPU_JR 31
#define CPU_LD_B 32
#define CPU_LD_H 33
#define CPU_LD_W 34
#define CPU_LDSR 35
#define CPU_MOV_IMM 36
#define CPU_MOV_REG 37
#define CPU_MOVEA 38
#define CPU_MOVHI 39
#define CPU_MPYHW 40
#define CPU_MUL 41
#define CPU_MULF_S 42
#define CPU_MULU 43
#define CPU_NOT 44
#define CPU_OR 45
#define CPU_ORI 46
#define CPU_OUT_B 47
#define CPU_OUT_H 48
#define CPU_OUT_W 49
#define CPU_RETI 50
#define CPU_REV 51
#define CPU_SAR_IMM 52
#define CPU_SAR_REG 53
#define CPU_SCH0BSD 54
#define CPU_SCH0BSU 55
#define CPU_SCH1BSD 56
#define CPU_SCH1BSU 57
#define CPU_SEI 58
#define CPU_SETF 59
#define CPU_SHL_IMM 60
#define CPU_SHL_REG 61
#define CPU_SHR_IMM 62
#define CPU_SHR_REG 63
#define CPU_ST_B 64
#define CPU_ST_H 65
#define CPU_ST_W 66
#define CPU_STSR 67
#define CPU_SUB 68
#define CPU_SUBF_S 69
#define CPU_TRAP 70
#define CPU_TRNC_SW 71
#define CPU_XB 72
#define CPU_XH 73
#define CPU_XOR 74
#define CPU_XORI 75
#define CPU_ANDBSU 76 /* Keep bit string ALU IDs consecutive */
#define CPU_ANDNBSU 77
#define CPU_MOVBSU 78
#define CPU_NOTBSU 79
#define CPU_ORBSU 80
#define CPU_ORNBSU 81
#define CPU_XORBSU 82
#define CPU_XORNBSU 83
2024-10-14 20:07:00 +00:00
/* 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
/********************************** Macros ***********************************/
/* Master clocks per CPU cycles */
#define cpuClocks(x) x
/* Shorthands */
#define auxBS sim->cpu.aux.bs
#define auxData sim->cpu.aux.data
#define bsDestAddr sim->cpu.program[29]
#define bsDestBit sim->cpu.program[26]
#define bsLength sim->cpu.program[28]
#define bsSkipped sim->cpu.program[29]
#define bsSrcAddr sim->cpu.program[30]
#define bsSrcBit sim->cpu.program[27]
/******************************** Lookup Data ********************************/
/* 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
};
/* 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
};
2024-10-23 21:29:11 +00:00
/**************************** Forward references *****************************/
static int phAssess (VB *, uint32_t, int, int32_t);
static uint32_t phUntil (VB *);
2024-10-14 20:07:00 +00:00
/***************************** Callback Handlers *****************************/
/* Prepare to handle an exception */
#ifndef VB_DIRECT_EXCEPTION
#define VB_ON_EXCEPTION sim->onException
#else
2024-10-15 19:11:29 +00:00
extern int VB_DIRECT_EXCEPTION(VB *, uint16_t *);
#define VB_ON_EXCEPTION VB_DIRECT_EXCEPTION
2024-10-14 20:07:00 +00:00
#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
2024-10-15 19:11:29 +00:00
extern int VB_DIRECT_EXECUTE(VB *, uint32_t, const uint16_t *, int);
#define VB_ON_EXECUTE VB_DIRECT_EXECUTE
2024-10-14 20:07:00 +00:00
#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
2024-10-15 19:11:29 +00:00
extern int VB_DIRECT_READ(VB *, uint32_t, int, int32_t *, uint32_t *);
#define VB_ON_READ VB_DIRECT_READ
2024-10-14 20:07:00 +00:00
#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);
2024-10-23 21:29:11 +00:00
/* Check for pseudo-halt */
if (sim->ph.enabled && phAssess(sim, address, type, *value))
return 0;
2024-10-14 20:07:00 +00:00
/* 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
2024-10-15 19:11:29 +00:00
extern int VB_DIRECT_FETCH(VB *, int, uint32_t, int32_t *, uint32_t *);
#define VB_ON_FETCH VB_DIRECT_FETCH
2024-10-14 20:07:00 +00:00
#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
2024-10-15 19:11:29 +00:00
extern int VB_DIRECT_WRITE(VB *,uint32_t,int,int32_t *,uint32_t *,int *);
#define VB_ON_WRITE VB_DIRECT_WRITE
2024-10-14 20:07:00 +00:00
#endif
static int cpuWrite(VB *sim, uint32_t address, int type, int32_t value) {
int cancel = 0;
uint32_t cycles = 3; /* TODO: Research this */
2024-10-23 21:29:11 +00:00
/* Reset pseudo-halt */
if (sim->ph.enabled)
sim->ph.step = 0;
2024-10-14 20:07:00 +00:00
/* 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 :
2024-10-19 22:01:36 +00:00
sim->cpu.psw.i = value >> 16 & 15;
2024-10-14 20:07:00 +00:00
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];
2024-10-19 22:01:36 +00:00
if (level < sim->cpu.psw.i)
2024-10-14 20:07:00 +00:00
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 {
2024-10-19 22:01:36 +00:00
/* All exceptions */
sim->cpu.eipsw = cpuGetSystemRegister(sim, VB_PSW);
2024-10-14 20:07:00 +00:00
/* Interrupts only */
if ((cause & 0xFF00) == 0xFE00) {
sim->cpu.psw.i = (cause >> 4 & 7) + 1;
2024-10-23 21:29:11 +00:00
/* HALT instruction */
2024-10-14 20:07:00 +00:00
if (sim->cpu.halt) {
sim->cpu.halt = 0;
sim->cpu.pc += 2;
}
2024-10-23 21:29:11 +00:00
2024-10-14 20:07:00 +00:00
}
/* All exceptions */
sim->cpu.ecr.eicc = cause;
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);
}
2024-10-23 21:29:11 +00:00
/* Pseudo-halt is pending */
static int cpuPHalt(VB *sim) {
int32_t value; /* Value read from memory */
/* An interrupt will be accepted */
if (cpuIRQ(sim)) {
sim->ph.step = 0;
return 0;
}
/* Monitor value has not changed */
busRead(sim, sim->ph.address, sim->ph.type, &value);
if (value == sim->ph.value) {
sim->cpu.clocks = phUntil(sim);
return 1;
}
/* Release pseudo-halt */
sim->cpu.operation = CPU_FETCH;
sim->cpu.step = 0;
sim->ph.step = 0;
return 0;
}
2024-10-14 20:07:00 +00:00
/**************************** 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;
}
/* Bit string arithmetic common processing */
static int cpuBSArithmetic(VB *sim) {
uint32_t dest;
uint32_t src;
switch (sim->cpu.step) {
case 0:
bsDestAddr &= 0xFFFFFFFC;
bsDestBit &= 0x0000001F;
bsSrcAddr &= 0xFFFFFFFC;
bsSrcBit &= 0x0000001F;
sim->cpu.clocks += cpuClocks(35); /* TODO: Research */
/* Nothing to do */
if (bsLength == 0)
break;
/* Fallthrough */
case 1:
/* Read the low source word */
if (cpuRead(sim, bsSrcAddr, VB_S32, (int32_t *) &src)) {
sim->cpu.step = 1;
return 1;
}
auxBS.src = src;
/* Fallthrough */
case 2:
/* Read the high source word */
if (cpuRead(sim, bsSrcAddr + 4, VB_S32, (int32_t *) &src)) {
sim->cpu.step = 2;
return 1;
}
auxBS.src = (uint32_t) auxBS.src | (uint64_t) src << 32;
/* Fallthrough */
case 3:
/* Read the destination word */
if (cpuRead(sim, bsDestAddr, VB_S32, (int32_t *) &auxBS.dest)) {
sim->cpu.step = 3;
return 1;
}
/* Perform the ALU operation */
dest = auxBS.dest;
src = bsSrcBit < bsDestBit ?
auxBS.src << (bsDestBit - bsSrcBit ) :
auxBS.src >> (bsSrcBit - bsDestBit)
;
switch (sim->cpu.operation) {
case CPU_ANDBSU : dest &= src; break;
case CPU_ANDNBSU: dest &= ~src; break;
case CPU_MOVBSU : dest = src; break;
case CPU_NOTBSU : dest = ~src; break;
case CPU_ORBSU : dest |= src; break;
case CPU_ORNBSU : dest |= ~src; break;
case CPU_XORBSU : dest ^= src; break;
case CPU_XORNBSU: dest ^= ~src; break;
}
/* Change only the bits that are part of the string */
src = 0xFFFFFFFF << bsDestBit;
if ((uint32_t) 32 - bsDestBit > (uint32_t) bsLength)
src &= ~(0xFFFFFFFF << (bsDestBit + bsLength));
auxBS.dest = (auxBS.dest & ~src) | (dest & src);
/* Fallthrough */
case 4:
/* Write the destination word */
if (cpuWrite(sim, bsDestAddr, VB_S32, auxBS.dest)) {
sim->cpu.step = 4;
return 1;
}
/* Select number of bits to advance */
src = 32 - bsDestBit;
if (src > (uint32_t) bsLength)
src = bsLength;
/* Advance to next output word */
if (bsSrcBit + src >= 32) {
bsSrcAddr += 4;
auxBS.src >>= 32;
sim->cpu.step = 2; /* Read high source word */
} else sim->cpu.step = 3; /* Read destination word */
if (bsDestBit + src >= 32)
bsDestAddr += 4;
bsSrcBit = (bsSrcBit + src) & 31;
bsDestBit = (bsDestBit + src) & 31;
bsLength -= src;
/* Update state */
sim->cpu.clocks += cpuClocks(12); /* TODO: Research */
}
/* Exit condition */
if (bsLength == 0)
cpuAdvance(sim, 0);
return 0;
}
/* Bit string search common processing */
static int cpuBSSearch(VB *sim, int bit, int dir) {
switch (sim->cpu.step) {
case 0:
bsSrcAddr &= 0xFFFFFFFC;
bsSrcBit &= 0x0000001F;
sim->cpu.clocks += cpuClocks(50); /* TODO: Research */
sim->cpu.psw.z = 1;
sim->cpu.step = 1;
/* Nothing to do */
if (bsLength == 0)
break;
/* Fallthrough */
case 1:
/* Read the source word */
if (cpuRead(sim, bsSrcAddr, VB_S32, &auxData.value))
return 1;
sim->cpu.clocks += cpuClocks(5); /* TODO: Research */
/* Process all remaining bits in source word */
do {
/* Match was found */
if ((auxData.value >> bsSrcBit & 1) == bit)
sim->cpu.psw.z = 0;
/* Match was not found */
else bsSkipped++;
/* Advance to next bit */
bsSrcBit = (bsSrcBit + dir) & 31;
bsLength--;
if (bsSrcBit != (dir == 1 ? 0 : 31))
continue;
/* Advance to next word */
bsSrcAddr += dir << 2;
break;
} while (sim->cpu.psw.z && bsLength != 0);
}
/* Exit condition */
if (!sim->cpu.psw.z || bsLength == 0)
cpuAdvance(sim, 0);
return 0;
}
/* 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 */
2024-10-20 23:52:19 +00:00
if (value != 0 && value > -FLT_MIN && value < FLT_MIN) {
2024-10-14 20:07:00 +00:00
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);
2024-10-20 23:52:19 +00:00
sim->cpu.psw.cy = floatVal < 0;
sim->cpu.psw.ov = 0;
sim->cpu.psw.s = floatVal < 0;
sim->cpu.psw.z = floatVal == 0;
2024-10-14 20:07:00 +00:00
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 == 0 ? (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 */
cpuAdvance(sim, cpuClocks(4)); /* 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));
}
/* ANDBSU */
static int cpuANDBSU(VB *sim) {
return cpuBSArithmetic(sim);
}
/* ANDI */
static void cpuANDI(VB *sim) {
cpuSetReg2(sim, cpuBitwise(sim, cpuGetReg1(sim) & cpuGetImm16U(sim)));
cpuAdvance(sim, cpuClocks(1));
}
/* ANDNBSU */
static int cpuANDNBSU(VB *sim) {
return cpuBSArithmetic(sim);
}
/* 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.halt = 1;
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));
}
/* MOVBSU */
static int cpuMOVBSU(VB *sim) {
return cpuBSArithmetic(sim);
}
/* 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));
}
/* NOTBSU */
static int cpuNOTBSU(VB *sim) {
return cpuBSArithmetic(sim);
}
/* OR */
static void cpuOR(VB *sim) {
cpuSetReg2(sim, cpuBitwise(sim, cpuGetReg2(sim) | cpuGetReg1(sim)));
cpuAdvance(sim, cpuClocks(1));
}
/* ORBSU */
static int cpuORBSU(VB *sim) {
return cpuBSArithmetic(sim);
}
/* ORI */
static void cpuORI(VB *sim) {
cpuSetReg2(sim, cpuBitwise(sim, cpuGetReg1(sim) | cpuGetImm16U(sim)));
cpuAdvance(sim, cpuClocks(1));
}
/* ORNBSU */
static int cpuORNBSU(VB *sim) {
return cpuBSArithmetic(sim);
}
/* 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);
cpuAdvance(sim, cpuClocks(22));
}
/* 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));
}
/* SCH0BSD */
static int cpuSCH0BSD(VB *sim) {
return cpuBSSearch(sim, 0, -1);
}
/* SCH0BSU */
static int cpuSCH0BSU(VB *sim) {
return cpuBSSearch(sim, 0, +1);
}
/* SCH1BSD */
static int cpuSCH1BSD(VB *sim) {
return cpuBSSearch(sim, 1, -1);
}
/* SCH1BSU */
static int cpuSCH1BSU(VB *sim) {
return cpuBSSearch(sim, 1, +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);
cpuAdvance(sim, cpuClocks(6));
}
/* XH */
static void cpuXH(VB *sim) {
uint32_t x = cpuGetReg2(sim);
x = (x << 16 & 0xFFFF0000) | (x >> 16 & 0x0000FFFF);
cpuSetReg2(sim, x);
cpuAdvance(sim, cpuClocks(1));
}
/* XOR */
static void cpuXOR(VB *sim) {
cpuSetReg2(sim, cpuBitwise(sim, cpuGetReg2(sim) ^ cpuGetReg1(sim)));
cpuAdvance(sim, cpuClocks(1));
}
/* XORBSU */
static int cpuXORBSU(VB *sim) {
return cpuBSArithmetic(sim);
}
/* XORI */
static void cpuXORI(VB *sim) {
cpuSetReg2(sim, cpuBitwise(sim, cpuGetReg1(sim) ^ cpuGetImm16U(sim)));
cpuAdvance(sim, cpuClocks(1));
}
/* XORNBSU */
static int cpuXORNBSU(VB *sim) {
return cpuBSArithmetic(sim);
}
/***************************** 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 */
2024-10-16 21:15:39 +00:00
clocks -= sim->cpu.clocks;
sim->cpu.clocks = 0;
2024-10-14 20:07:00 +00:00
/* Processing by operation ID */
switch (sim->cpu.operation) {
2024-10-23 21:29:11 +00:00
case CPU_EXCEPTION: brk = cpuException(sim); break;
2024-10-14 20:07:00 +00:00
case CPU_FATAL : return 0;
2024-10-23 21:29:11 +00:00
case CPU_FETCH : brk = cpuFetch (sim); break;
case CPU_HALTING : if (cpuHalt (sim)) return 0; break;
case CPU_PHALT : if (cpuPHalt(sim)) return 0; break;
2024-10-14 20:07:00 +00:00
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_ANDBSU : cpuANDBSU (sim); break;
case CPU_ANDI : cpuANDI (sim); break;
case CPU_ANDNBSU: cpuANDNBSU(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_MOVBSU : cpuMOVBSU (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_NOTBSU : cpuNOTBSU (sim); break;
case CPU_OR : cpuOR (sim); break;
case CPU_ORBSU : cpuORBSU (sim); break;
case CPU_ORI : cpuORI (sim); break;
case CPU_ORNBSU : cpuORNBSU (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_SCH0BSD: cpuSCH0BSD(sim); break;
case CPU_SCH0BSU: cpuSCH0BSU(sim); break;
case CPU_SCH1BSD: cpuSCH1BSD(sim); break;
case CPU_SCH1BSU: cpuSCH1BSU(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_XORBSU : cpuXORBSU (sim); break;
case CPU_XORI : cpuXORI (sim); break;
case CPU_XORNBSU: cpuXORNBSU(sim); break;
default: return -1; /* TODO: Temporary for debugging */
}
/* A callback requested a break */
if (brk)
return 1;
}
return 0;
}
2024-10-15 19:11:29 +00:00
/* Simulate a hardware reset */
static void cpuReset(VB *sim) {
uint32_t x; /* Iterator */
/* Normal */
sim->cpu.exception = 0;
sim->cpu.halt = 0;
sim->cpu.irq = 0;
sim->cpu.pc = 0xFFFFFFF0;
cpuSetSystemRegister(sim, VB_ECR, 0x0000FFF0, 1);
cpuSetSystemRegister(sim, VB_PSW, 0x00008000, 1);
/* Extra (the hardware does not do this) */
sim->cpu.adtre = 0x00000000;
sim->cpu.eipc = 0x00000000;
sim->cpu.eipsw = 0x00000000;
sim->cpu.fepc = 0x00000000;
sim->cpu.fepsw = 0x00000000;
sim->cpu.sr29 = 0x00000000;
sim->cpu.sr31 = 0x00000000;
cpuSetSystemRegister(sim, VB_CHCW, 0x00000000, 1);
for (x = 0; x < 32; x++)
sim->cpu.program[x] = 0x00000000;
/* Other */
sim->cpu.clocks = 0;
sim->cpu.nextPC = 0xFFFFFFF0;
sim->cpu.operation = CPU_FETCH;
sim->cpu.step = 0;
}
2024-10-14 20:07:00 +00:00
/* Determine how many clocks are guaranteed to process */
static uint32_t cpuUntil(VB *sim, uint32_t clocks) {
2024-10-20 01:35:56 +00:00
2024-10-23 21:29:11 +00:00
/* Pseudo-halting */
if (sim->cpu.operation == CPU_PHALT) {
2024-10-24 02:16:35 +00:00
if (sim->cpu.clocks == 0)
2024-10-23 21:29:11 +00:00
return clocks;
}
2024-10-20 01:35:56 +00:00
/* Halting */
2024-10-23 21:29:11 +00:00
else if (sim->cpu.halt) {
2024-10-20 01:35:56 +00:00
return
sim->cpu.operation == CPU_HALTING &&
!(sim->cpu.psw.id | sim->cpu.psw.ep | sim->cpu.psw.np) &&
2024-10-20 03:56:23 +00:00
IRQ_LEVELS[sim->cpu.irq] >= sim->cpu.psw.i
2024-10-20 01:35:56 +00:00
? 0 : clocks;
}
/* Not halting */
return sim->cpu.clocks > clocks ? clocks : sim->cpu.clocks;
2024-10-14 20:07:00 +00:00
}
#endif /* VBAPI */