From 94486ecf0248c5021b9be16183b50e50a48d09a1 Mon Sep 17 00:00:00 2001 From: Guy Perfect Date: Sun, 19 Sep 2021 01:31:40 +0000 Subject: [PATCH] Preliminary CPU implementation --- app/_boot.js | 2 +- core/bus.c | 62 +- core/cpu.c | 1589 +++++++++++++++++++++++++++++++++++++++++++++++++- core/vb.c | 144 +++-- core/vb.h | 76 ++- license.txt | 2 +- makefile | 2 +- 7 files changed, 1792 insertions(+), 85 deletions(-) diff --git a/app/_boot.js b/app/_boot.js index 5b7c836..0453cf8 100644 --- a/app/_boot.js +++ b/app/_boot.js @@ -291,7 +291,7 @@ let run = async function() { if (!file.name.endsWith(".woff2")) continue; let family = "/" + file.name; - family = family.substring(family.lastIndexOf("/") + 1, family.length - 6); + family = family.substring(family.lastIndexOf("/")+1, family.length-6); let font = new FontFace(family, file.data); await font.load(); document.fonts.add(font); diff --git a/core/bus.c b/core/bus.c index 8b51b32..f08b6e5 100644 --- a/core/bus.c +++ b/core/bus.c @@ -1,6 +1,10 @@ /* This file is included into vb.c and cannot be compiled on its own. */ #ifdef VBAPI + + +/***************************** Utility Functions *****************************/ + /* Read a data unit from a memory buffer */ static int32_t busReadMemory(uint8_t *mem, int type) { @@ -28,31 +32,6 @@ static int32_t busReadMemory(uint8_t *mem, int type) { } -/* Read a data unit from the bus */ -static int32_t busRead(VB *emu, uint32_t address, int type, int debug) { - - /* Force address alignment */ - address &= ~((uint32_t) TYPE_SIZES[type] - 1); - - /* Process by address range */ - switch (address >> 24 & 7) { - case 0 : return 0; /* VIP */ - case 1 : debug = debug; return 0; /* VSU */ - case 2 : return 0; /* Miscellaneous hardware */ - case 3 : return 0; /* Unmapped */ - case 4 : return 0; /* Game pak expansion */ - case 5 : return /* WRAM */ - busReadMemory(&emu->wram[address & 0xFFFF], type); - case 6 : return emu->cart.sram == NULL ? 0 : /* Game pak RAM */ - busReadMemory(&emu->cart.sram - [address & (emu->cart.sramSize - 1)], type); - default: return emu->cart.rom == NULL ? 0 : /* Game pak ROM */ - busReadMemory(&emu->cart.rom - [address & (emu->cart.romSize - 1)], type); - } - -} - /* Write a data unit to a memory buffer */ static void busWriteMemory(uint8_t *mem, int type, int32_t value) { @@ -77,7 +56,36 @@ static void busWriteMemory(uint8_t *mem, int type, int32_t value) { } mem[0] = value; #endif - + +} + + + +/***************************** Module Functions ******************************/ + +/* Read a data unit from the bus */ +static int32_t busRead(VB *emu, uint32_t address, int type, int debug) { + + /* Force address alignment */ + address &= ~((uint32_t) TYPE_SIZES[type] - 1); + + /* Process by address range */ + switch (address >> 24 & 7) { + case 0 : return 0; /* VIP */ + case 1 : debug = debug; return 0; /* VSU */ + case 2 : return 0; /* Miscellaneous hardware */ + case 3 : return 0; /* Unmapped */ + case 4 : return 0; /* Game pak expansion */ + case 5 : return /* WRAM */ + busReadMemory(&emu->wram[address & 0xFFFF], type); + case 6 : return emu->cart.sram == NULL ? 0 : /* Game pak RAM */ + busReadMemory(&emu->cart.sram + [address & (emu->cart.sramSize - 1)], type); + default: return emu->cart.rom == NULL ? 0 : /* Game pak ROM */ + busReadMemory(&emu->cart.rom + [address & (emu->cart.romSize - 1)], type); + } + } /* Write a data unit to the bus */ @@ -110,4 +118,6 @@ static void busWrite( } + + #endif /* VBAPI */ diff --git a/core/cpu.c b/core/cpu.c index 8a37bc0..f8f28d9 100644 --- a/core/cpu.c +++ b/core/cpu.c @@ -5,6 +5,13 @@ /********************************* Constants *********************************/ +/* Operations states */ +#define CPU_FETCH 0 +#define CPU_EXECUTE 1 +#define CPU_EXCEPTION 2 +#define CPU_HALTED 3 +#define CPU_FATAL 4 + /* Instruction IDs */ #define CPU_ILLEGAL -1 #define CPU_ADD_IMM 0 @@ -86,6 +93,26 @@ #define CPU_BITSTRING 76 #define CPU_FLOATENDO 77 +/* Mapping for opcodes to operation IDs */ +static const int8_t CPU_OPCODES[] = { + CPU_MOV_REG, CPU_ADD_REG, CPU_SUB , CPU_CMP_REG , + 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 , + 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 , + 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 , + 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 +}; + /* Mapping for bit string sub-opcodes */ static const int8_t CPU_BITSTRINGS[] = { CPU_SCH0BSU, CPU_SCH0BSD, CPU_SCH1BSU, CPU_SCH1BSD, @@ -99,7 +126,7 @@ static const int8_t CPU_BITSTRINGS[] = { }; /* Mapping for floating-point/Nintendo sub-opcodes */ -static const int8_t CPU_FLOATENDO[] = { +static const int8_t CPU_FLOATENDOS[] = { CPU_CMPF_S , CPU_ILLEGAL, CPU_CVT_WS , CPU_CVT_SW , CPU_ADDF_S , CPU_SUBF_S , CPU_MULF_S , CPU_DIVF_S , CPU_XB , CPU_XH , CPU_REV , CPU_TRNC_SW, @@ -118,4 +145,1564 @@ static const int8_t CPU_FLOATENDO[] = { CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL }; +/* Instruction sizes by opcode */ +static const uint8_t CPU_SIZES[] = { + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 2, 4, 4, 4, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4 +}; + + + +/***************************** Utility Functions *****************************/ + +/* Read a data unit from the bus */ +static int cpuRead(VB *emu, uint32_t address, int8_t type) { + + /* TODO: Determine clock count here */ + emu->cpu.access.clocks = 0; + + /* Not using a breakpoint callback */ + if (emu->onRead == NULL) { + emu->cpu.access.value = busRead(emu, address, type, 0); + return 0; + } + + /* Invoke the breakpoint callback */ + emu->cpu.access.address = address; + emu->cpu.access.type = type; + return emu->onRead(emu, &emu->cpu.access); +} + +/* Read a data unit from the bus for the purpose of an instruction fetch */ +static int cpuReadFetch(VB *emu, uint32_t address) { + + /* TODO: Determine clock count here */ + emu->cpu.access.clocks = 0; + + /* Not using a breakpoint callback */ + if (emu->onFetch == NULL) { + emu->cpu.access.value = busRead(emu, address, VB_U16, 0); + return 0; + } + + /* Invoke the breakpoint callback */ + emu->cpu.access.address = address; + emu->cpu.access.type = VB_U16; + return emu->onFetch(emu, emu->cpu.fetch, &emu->cpu.access); +} + +/* Write a data unit to the bus */ +static int cpuWrite(VB *emu, uint32_t address, int8_t type, int32_t value) { + + /* TODO: Determine clock count here */ + emu->cpu.access.clocks = 0; + emu->cpu.access.type = type; + + /* Using a breakpoint callback */ + if (emu->onWrite != NULL) { + emu->cpu.access.value = value; + if (emu->onWrite(emu, &emu->cpu.access)) + return 1; + } + + /* Write the value if the operation wasn't cancelled */ + if (emu->cpu.access.type != VB_CANCEL) + busWrite(emu, address, type, value, 0); + return 0; +} + + + +/**************************** Instruction Helpers ****************************/ + +/* Add two numbers and update the flags */ +static int32_t cpuAdd(VB *emu, int32_t left, int32_t right) { + int32_t result = left + right; + emu->cpu.clocks = 1; + emu->cpu.psw.cy = (uint32_t) result < (uint32_t) left; + emu->cpu.psw.ov = (~(left ^ right) & (left ^ result)) >> 31 & 1; + emu->cpu.psw.s = result < 0; + emu->cpu.psw.z = result == 0; + return result; +} + +/* Bit string search */ +static int cpuBitSearch(VB *emu, int bit, int dir) { + int32_t offset; /* Bit offset in source word */ + int32_t value; /* Alias of source word */ + + /* The bit string is of zero length */ + if (emu->cpu.program[28] == 0) { + emu->cpu.clocks = dir == 1 ? 13 : 15; + return 0; + } + + /* Read the source word */ + if (!emu->cpu.busWait && !emu->cpu.substring) { + + /* Initialize state */ + emu->cpu.program[30] &= 0xFFFFFFFC; + emu->cpu.program[27] &= 0x0000001F; + emu->cpu.psw.z = 1; + + /* Read the data unit from the bus */ + if (cpuRead(emu, emu->cpu.program[30], VB_S32)) + return 1; + + /* Update state */ + emu->cpu.busWait = 1; + emu->cpu.clocks = emu->cpu.access.clocks; + + /* Wait for the bus access to complete */ + if (emu->cpu.clocks > 0) + return 0; + } + + /* Search the bit string */ + for ( + offset = emu->cpu.program[27], value = emu->cpu.access.value; + emu->cpu.program[28] != 0 && (offset & 31) == offset; + offset += dir, emu->cpu.program[28]--, emu->cpu.program[29]++ + ) { + + /* The current bit does not match */ + if ((value >> bit & 1) != bit) + continue; + + /* The current bit matches */ + emu->cpu.substring = 0; + emu->cpu.clocks = dir == 1 ? 46 : 51; + emu->cpu.psw.z = 0; + return 0; + } + + /* No bit in the current word matches */ + emu->cpu.substring = 1; + emu->cpu.clocks = 5; + emu->cpu.program[30] += dir << 2; + emu->cpu.program[27] &= 0x0000001F; + return 0; +} + +/* Bit string bitwise operation */ +static int cpuBitString(VB *emu, VB_INSTRUCTION *inst) { + int32_t dest; /* Destination word value */ + int32_t src; /* Source word value */ + + /* Initial invocation */ + if (emu->cpu.busWait == 0 && !emu->cpu.substring) { + + /* Initialize state */ + emu->cpu.program[30] &= 0xFFFFFFFC; + emu->cpu.program[29] &= 0xFFFFFFFC; + emu->cpu.program[27] &= 0x0000001F; + emu->cpu.program[26] &= 0x0000001F; + + /* The bit string is of zero length */ + if (emu->cpu.program[28] == 0) { + emu->cpu.clocks = 20; + return 0; + } + + /* Read the data unit from the bus */ + if (cpuRead(emu, emu->cpu.program[30], VB_S32)) + return 1; + + /* Update state */ + inst->aux[0] = emu->cpu.access.value; + emu->cpu.busWait = 1; + emu->cpu.clocks = emu->cpu.access.clocks; + + /* Wait for the bus access to complete */ + if (emu->cpu.clocks > 0) + return 0; + } + + /* Read the next source word */ + if (emu->cpu.busWait == 1) { + + /* Read the data unit from the bus */ + if (cpuRead(emu, emu->cpu.program[30] + 4, VB_S32)) + return 1; + + /* Update state */ + inst->aux[1] = emu->cpu.access.value; + emu->cpu.busWait = 2; + emu->cpu.clocks = emu->cpu.access.clocks; + + /* Wait for the bus access to complete */ + if (emu->cpu.clocks > 0) + return 0; + } + + /* Read the destination word */ + if (emu->cpu.busWait == 2) { + + /* Read the data unit from the bus */ + if (cpuRead(emu, emu->cpu.program[29], VB_S32)) + return 1; + + /* Update state */ + emu->cpu.busWait = 3; + emu->cpu.clocks = emu->cpu.access.clocks; + + /* Wait for the bus access to complete */ + if (emu->cpu.clocks > 0) + return 0; + } + + /* Compute and store the destination word */ + if (emu->cpu.busWait == 3) { + dest = emu->cpu.access.value; + src = ((int64_t) inst->aux[1] << 32 | (uint32_t) inst->aux[0]) >> + (32 + emu->cpu.program[27] - emu->cpu.program[26]); + + /* Perform the operation */ + switch (inst->bits[0] & 7) { + case 0 : dest |= src; break; /* ORBSU */ + case 1 : dest &= src; break; /* ANDBSU */ + case 2 : dest ^= src; break; /* XORBSU */ + case 3 : dest = src; break; /* MOVBSU */ + case 4 : dest |= ~src; break; /* ORNBSU */ + case 5 : dest &= ~src; break; /* ANDNBSU */ + case 6 : dest ^= ~src; break; /* XORNBSU */ + default: dest = ~src; break; /* NOTBSU */ + } + + /* Incorporate only the bits occupied by the bit string */ + src = (int32_t) 0xFFFFFFFF << emu->cpu.program[26]; + if ((uint32_t)emu->cpu.program[28] < (uint32_t)32-emu->cpu.program[26]) + src &= (1 << (emu->cpu.program[28] + emu->cpu.program[26])) - 1; + dest = (dest & src) | (emu->cpu.access.value & ~src); + + /* Write the data unit to the bus */ + if (cpuWrite(emu, emu->cpu.program[30], VB_S32, dest)) + return 1; + + /* Update state */ + emu->cpu.busWait = 4; + emu->cpu.clocks = emu->cpu.access.clocks; + + /* Wait for the bus access to complete */ + if (emu->cpu.clocks > 0) + return 0; + } + + /* Working variables */ + dest = 32 - emu->cpu.program[26]; /* Bits processed this invocation */ + if ((uint32_t) emu->cpu.program[28] < (uint32_t) dest) + dest = emu->cpu.program[28]; + src = emu->cpu.program[30] + dest; /* New source bit offset */ + + /* Update state */ + emu->cpu.busWait = 0; + emu->cpu.substring = dest != emu->cpu.program[28]; + if (src >= 32) { + inst->aux[0] = inst->aux[1]; + emu->cpu.program[30] += 4; + } + emu->cpu.program[29] += 4; + emu->cpu.program[28] -= dest; + emu->cpu.program[27] = src & 0x1F; + emu->cpu.program[26] = 0; + emu->cpu.clocks = emu->cpu.substring ? 6 : 36; + return 0; +} + +/* Common processing for most bitwise operations */ +static int32_t cpuBitwise(VB *emu, int32_t result) { + emu->cpu.clocks = 1; + emu->cpu.psw.ov = 0; + emu->cpu.psw.s = result < 0; + emu->cpu.psw.z = result == 0; + return result; +} + +/* Test a condition code */ +static int cpuCondition(VB *emu, int cond) { + + /* Falsey condition */ + if (cond > 7) + return !cpuCondition(emu, cond & 7); + + /* Truthy condition */ + switch (cond) { + case 0: return emu->cpu.psw.ov; /* V */ + case 1: return emu->cpu.psw.cy; /* L */ + case 2: return emu->cpu.psw.z; /* Z */ + case 3: return emu->cpu.psw.cy | emu->cpu.psw.z; /* NH */ + case 4: return emu->cpu.psw.s; /* N */ + case 5: return 1; /* T */ + case 6: return emu->cpu.psw.ov ^ emu->cpu.psw.s; /* LT */ + default:return(emu->cpu.psw.ov^emu->cpu.psw.s)|emu->cpu.psw.z; /* LE */ + } + +} + +/* Check for a floating-point reserved operand */ +static int cpuFloatReserved(VB *emu, int32_t bits) { + uint8_t e = bits >> 23; /* Exponent field */ + + /* Not reserved */ + if (e != 0xFF && (e != 0x00 || (bits & 0x007FFFFF) == 0)) + return 0; + + /* Reserved */ + emu->cpu.causeCode = 0xFF60; + emu->cpu.psw.fro = 1; + return 1; +} + +/* Convert a 32-bit integer to the represented floating-point value */ +static double cpuFloatOperand(VB *emu, int32_t bits) { + return cpuFloatReserved(emu, bits) ? 0 : (double) *(float *) &bits; +} + +/* Convert a floating-point result to bits as a 32-bit integer */ +static int32_t cpuFloatResult(VB *emu, double resultd) { + float resultf; /* 32-bit conversion of result */ + int32_t ret; /* Output value */ + + /* Overflow */ + if (resultd < -FLT_MAX || resultd > FLT_MAX) { + emu->cpu.causeCode = 0xFF64; + emu->cpu.psw.fov = 1; + return 0; + } + + /* Underflow */ + if (resultd > -FLT_MIN && resultd < FLT_MIN) { + emu->cpu.psw.fud = 1; + ret = 0; + } + + /* Normalized */ + else { + resultf = (float) resultd; + ret = *(int32_t *) &resultf; + if (ret == INT32_MIN) + ret = 0; /* Prevent negative zero */ + + /* Precision degradation */ + if (resultf != resultd) + emu->cpu.psw.fpr = 1; + } + + /* Update state */ + emu->cpu.psw.cy = ret < 0; + emu->cpu.psw.ov = 0; + emu->cpu.psw.s = ret < 0; + emu->cpu.psw.z = ret == 0; + return ret; +} + +/* Convert a floating-point value to a word value */ +static void cpuFloatToWord(VB *emu, VB_INSTRUCTION *inst, int truncate) { + int32_t bits = emu->cpu.program[inst->bits[0] & 0x1F]; + int32_t result; /* Output value */ + int32_t x; /* Working variable field */ + + /* Zero */ + if ((bits & 0x7FFFFFFF) == 0) + result = 0; + + /* Minimum value */ + else if (bits == (int32_t) 0xCF000000) + result = INT32_MIN; + + /* Conversion */ + else { + x = bits >> 23 & 0xFF; /* Exponent field */ + + /* Reserved operand */ + if (x == 0xFF || x == 0x00) { + emu->cpu.causeCode = 0xFF60; + emu->cpu.psw.fro = 1; + return; + } + + /* Invalid operation */ + if (x >= 135) { + emu->cpu.causeCode = 0xFF70; + emu->cpu.psw.fiv = 1; + return; + } + + /* Parse significand bits */ + result = (bits & 0x007FFFFF) | 0x00800000; + + /* Left shift */ + if (x >= 127) + result <<= x - 127; + + /* Right shift */ + else { + x = 127 - x; /* Number of bits to shift */ + result >>= x; /* Update state */ + x = 1 << x; /* Position of the "one halfths" bit */ + + /* Precision degradation */ + if ((bits & (x - 1)) != 0) { + emu->cpu.psw.fpr = 1; + + /* Apply rounding */ + if (!truncate && (bits & x) != 0) + result += x; + } + + } + + /* Incorporate sign */ + if (bits < 0) + result = -result; + } + + /* Update state */ + emu->cpu.program[inst->bits[0] >> 5 & 0x1F] = result; + emu->cpu.psw.ov = 0; + emu->cpu.psw.s = result < 0; + emu->cpu.psw.z = result == 0; + emu->cpu.clocks = 14; +} + +/* Perform a jump */ +static void cpuJump(VB *emu, VB_INSTRUCTION *inst, uint32_t address) { + emu->cpu.pcFrom = emu->cpu.pc; + emu->cpu.pc = address & 0xFFFFFFFE; + emu->cpu.pcTo = emu->cpu.pc; + emu->cpu.clocks = 3; + inst->size = 0; +} + +/* Perform an input or load */ +static int cpuLoad(VB *emu,VB_INSTRUCTION *inst,uint8_t type,uint32_t clocks) { + + /* Initiate the read */ + if (!emu->cpu.busWait) { + + /* Read the data unit from the bus */ + if (cpuRead( + emu, + emu->cpu.program[inst->bits[0] & 0x1F] + + SignExtend((int32_t) inst->bits[1], 16), + type + )) return 1; + + /* Update state */ + emu->cpu.busWait = 1; + emu->cpu.clocks = emu->cpu.access.clocks; + + /* Wait for the bus access to complete */ + if (emu->cpu.clocks > 0) + return 0; + } + + /* Complete the read */ + emu->cpu.busWait = 0; + emu->cpu.clocks = clocks; + emu->cpu.program[inst->bits[0] >> 5 & 0x1F] = emu->cpu.access.value; + return 0; +} + +/* Specify a new value for a system register */ +static uint32_t cpuSetSystemRegister(VB *emu,int id,uint32_t value,int debug) { + switch (id) { + case VB_ADTRE: return emu->cpu.adtre = value & 0xFFFFFFFE; + case VB_EIPC : return emu->cpu.eipc = value & 0xFFFFFFFE; + case VB_EIPSW: return emu->cpu.eipsw = value & 0x000FF3FF; + case VB_FEPC : return emu->cpu.fepc = value & 0xFFFFFFFE; + case VB_FEPSW: return emu->cpu.fepsw = value & 0x000FF3FF; + case VB_PIR : return 0x00005346; + case VB_TKCW : return 0x000000E0; + case 29 : return emu->cpu.sr29 = value & 0x00000001; + case 31 : + if (!debug && (int32_t) value < 0) + value = ~value + 1; + return emu->cpu.sr31 = value; + case VB_CHCW : + emu->cpu.chcw.ice = value >> 1 & 1; + /* TODO: Perform dump/restore operations */ + return value & 0x00000002; + case VB_ECR : + if (debug) { + emu->cpu.ecr.fecc = value >> 16; + emu->cpu.ecr.eicc = value; + return value; + } + return vbGetSystemRegister(emu, id); + case VB_PSW : + emu->cpu.psw.i = value >> 16 & 15; + emu->cpu.psw.np = value >> 15 & 1; + emu->cpu.psw.ep = value >> 14 & 1; + emu->cpu.psw.ae = value >> 13 & 1; + emu->cpu.psw.id = value >> 12 & 1; + emu->cpu.psw.fro = value >> 9 & 1; + emu->cpu.psw.fiv = value >> 8 & 1; + emu->cpu.psw.fzd = value >> 7 & 1; + emu->cpu.psw.fov = value >> 6 & 1; + emu->cpu.psw.fud = value >> 5 & 1; + emu->cpu.psw.fpr = value >> 4 & 1; + emu->cpu.psw.cy = value >> 3 & 1; + emu->cpu.psw.ov = value >> 2 & 1; + emu->cpu.psw.s = value >> 1 & 1; + emu->cpu.psw.z = value & 1; + return value & 0x000FF3FF; + } + return 0; +} + +/* Perform a right shift */ +static int cpuShiftRight(VB *emu, int32_t value, int bits, int arithmetic) { + if (bits != 0) { + emu->cpu.psw.cy = (value >> (bits - 1)) & 1; + value = value >> bits & (int32_t) 0xFFFFFFFF << bits; + if (arithmetic) + value = SignExtend(value, 32 - bits); + } else emu->cpu.psw.cy = 0; + return cpuBitwise(emu, value); +} + +/* Perform an output or store */ +static int cpuStore(VB *emu,VB_INSTRUCTION *inst,uint8_t type,uint32_t clocks){ + + /* Initiate the write */ + if (!emu->cpu.busWait) { + + /* Read the data unit from the bus */ + if (cpuWrite( + emu, + emu->cpu.program[inst->bits[0] & 0x1F] + + SignExtend((int32_t) inst->bits[1], 16), + type, + emu->cpu.program[inst->bits[0] >> 5 & 0x1F] + )) return 1; + + /* Update state */ + emu->cpu.busWait = 1; + emu->cpu.clocks = emu->cpu.access.clocks; + + /* Wait for the bus access to complete */ + if (emu->cpu.clocks > 0) + return 0; + } + + /* Complete the write */ + emu->cpu.busWait = 0; + emu->cpu.clocks = clocks; + return 0; +} + +/* Subtract two numbers and update the flags */ +static int32_t cpuSubtract(VB *emu, int32_t left, int32_t right) { + int32_t result = left - right; + emu->cpu.clocks = 1; + emu->cpu.psw.cy = (uint32_t) result > (uint32_t) left; + emu->cpu.psw.ov = ((left ^ right) & (left ^ result)) >> 31 & 1; + emu->cpu.psw.s = result < 0; + emu->cpu.psw.z = result == 0; + return result; +} + + + +/************************ Instruction Implementations ************************/ + +/* Add Immediate */ +static void cpuADD_IMM(VB *emu, VB_INSTRUCTION *inst) { + int32_t *reg2 = &emu->cpu.program[inst->bits[0] >> 5 & 0x1F]; + int32_t right = inst->bits[0] & 0x1F; + *reg2 = cpuAdd(emu, *reg2, SignExtend(right, 5)); +} + +/* Add Register */ +static void cpuADD_REG(VB *emu, VB_INSTRUCTION *inst) { + int32_t *reg2 = &emu->cpu.program[inst->bits[0] >> 5 & 0x1F]; + *reg2 = cpuAdd(emu, *reg2, emu->cpu.program[inst->bits[0] & 0x1F]); +} + +/* Add Floating Short */ +static void cpuADDF_S(VB *emu, VB_INSTRUCTION *inst) { + int32_t *reg2 = &emu->cpu.program[inst->bits[0] >> 5 & 0x1F]; + double left = cpuFloatOperand(emu, *reg2); + double right = cpuFloatOperand(emu,emu->cpu.program[inst->bits[0]&0x1F]); + int32_t result; /* Output bits */ + + /* Perform the operation */ + if (emu->cpu.causeCode == 0) + result = cpuFloatResult(emu, left + right); + if (emu->cpu.causeCode != 0) + return; + + /* Update state */ + *reg2 = result; + emu->cpu.clocks = 28; +} + +/* Add Immediate */ +static void cpuADDI(VB *emu, VB_INSTRUCTION *inst) { + int32_t right = inst->bits[1]; + emu->cpu.program[inst->bits[0] >> 5 & 0x1F] = cpuAdd(emu, + emu->cpu.program[inst->bits[0] & 0x1F], SignExtend(right, 16)); +} + +/* And */ +static void cpuAND(VB *emu, VB_INSTRUCTION *inst) { + int32_t *reg2 = &emu->cpu.program[inst->bits[0] >> 5 & 0x1F]; + *reg2 = cpuBitwise(emu, *reg2 & emu->cpu.program[inst->bits[0] & 0x1F]); +} + +/* And Bit String Upward */ +#define cpuANDBSU(emu, inst) cpuBitString(emu, inst) + +/* And Not Bit String Upward */ +#define cpuANDNBSU(emu, inst) cpuBitString(emu, inst) + +/* And Immediate */ +static void cpuANDI(VB *emu, VB_INSTRUCTION *inst) { + emu->cpu.program[inst->bits[0] >> 5 & 0x1F] = cpuBitwise(emu, + emu->cpu.program[inst->bits[0] & 0x1F] & inst->bits[1]); +} + +/* Conditional Branch */ +static void cpuBCOND(VB *emu, VB_INSTRUCTION *inst) { + int32_t disp; /* Target address displacement */ + + /* Branch to the target address */ + if (cpuCondition(emu, inst->bits[0] >> 9 & 15)) { + disp = inst->bits[0] & 0x1FF; + cpuJump(emu, inst, emu->cpu.pc + SignExtend(disp, 9)); + } + + /* Do not branch */ + else emu->cpu.clocks = 1; +} + +/* Compare And Exchahge Interlocked */ +static int cpuCAXI(VB *emu, VB_INSTRUCTION *inst) { + int32_t *reg2; /* Program register reg2 */ + + /* Read the lock word */ + if (emu->cpu.busWait == 0) { + + /* Compute the address of the lock word */ + inst->aux[0] = emu->cpu.program[inst->bits[0] & 0x1F] + + SignExtend((int32_t) inst->bits[1], 16); + + /* Read the data unit from the bus */ + if (cpuRead(emu, inst->aux[0], VB_S32)) + return 1; + + /* Update state */ + emu->cpu.busWait = 1; + emu->cpu.clocks = emu->cpu.access.clocks; + + /* Wait for the bus access to complete */ + if (emu->cpu.clocks > 0) + return 0; + } + + /* Compare and exchange */ + if (emu->cpu.busWait == 1) { + + /* Process the lock word */ + reg2 = &emu->cpu.program[inst->bits[0] >> 5 & 0x1F]; + cpuSubtract(emu, *reg2, emu->cpu.access.value); + *reg2 = emu->cpu.access.value; + + /* Store the exchange value to the bus */ + if (cpuWrite(emu, inst->aux[0], VB_S32, + emu->cpu.psw.z ? emu->cpu.program[30] : emu->cpu.access.value)) + return 1; + + /* Update state */ + emu->cpu.clocks = emu->cpu.access.clocks; + emu->cpu.busWait = 2; + + /* Wait for the bus access to complete */ + if (emu->cpu.clocks > 0) + return 0; + } + + /* Update state */ + emu->cpu.busWait = 0; + emu->cpu.clocks = 26; + return 0; +} + +/* Clear Interrupt Disable Flag */ +static void cpuCLI(VB *emu) { + emu->cpu.psw.id = 0; + emu->cpu.clocks = 12; +} + +/* Compare Immediate */ +static void cpuCMP_IMM(VB *emu, VB_INSTRUCTION *inst) { + int32_t right = inst->bits[0] & 0x1F; + cpuSubtract(emu, emu->cpu.program[inst->bits[0] >> 5 & 0x1F], + SignExtend(right, 5)); +} + +/* Compare Register */ +static void cpuCMP_REG(VB *emu, VB_INSTRUCTION *inst) { + cpuSubtract(emu, emu->cpu.program[inst->bits[0] >> 5 & 0x1F], + emu->cpu.program[inst->bits[0] & 0x1F]); +} + +/* Compare Floating Short */ +static void cpuCMPF_S(VB *emu, VB_INSTRUCTION *inst) { + double left =cpuFloatOperand(emu,emu->cpu.program[inst->bits[0]>>5&0x1F]); + double right=cpuFloatOperand(emu,emu->cpu.program[inst->bits[0] &0x1F]); + + /* Perform the operation */ + if (emu->cpu.causeCode == 0) + cpuFloatResult(emu, left - right); + if (emu->cpu.causeCode != 0) + return; + + /* Update state */ + emu->cpu.clocks = 10; +} + +/* Convert Short Floating to Word Integer */ +#define cpuCVT_SW(emu, inst) cpuFloatToWord(emu, inst, 0) + +/* Convert Word Integer to Short Floating */ +static void cpuCVT_WS(VB *emu, VB_INSTRUCTION *inst) { + int32_t value = emu->cpu.program[inst->bits[0] & 0x1F]; + float result = (float) value; + emu->cpu.program[inst->bits[0] >> 5 & 0x1F] = *(int32_t *) &result; + if (result != value) + emu->cpu.psw.fpr = 1; + emu->cpu.psw.cy = result < 0; + emu->cpu.psw.ov = 0; + emu->cpu.psw.s = result < 0; + emu->cpu.psw.z = result == 0; + emu->cpu.clocks = 16; +} + +/* Divide */ +static void cpuDIV(VB *emu, VB_INSTRUCTION *inst) { + int32_t right = emu->cpu.program[inst->bits[0] & 0x1F]; + int32_t *reg2; /* Program register reg2 */ + + /* Zero division */ + if (right == 0) { + emu->cpu.causeCode = 0xFF80; + return; + } + + /* Special case */ + reg2 = &emu->cpu.program[inst->bits[0] >> 5 & 0x1F]; + if (*reg2 == INT32_MIN && right == -1) { + emu->cpu.program[30] = 0; + emu->cpu.psw.ov = 1; + } + + /* Perform the operation */ + else { + emu->cpu.program[30] = *reg2 % right; + *reg2 /= right; + emu->cpu.psw.ov = 0; + } + + /* Update state */ + emu->cpu.psw.s = *reg2 < 0; + emu->cpu.psw.z = *reg2 == 0; + emu->cpu.clocks = 38; +} + +/* Divide Floating Short */ +static void cpuDIVF_S(VB *emu, VB_INSTRUCTION *inst) { + int32_t *reg2 = &emu->cpu.program[inst->bits[0] >> 5 & 0x1F]; + double left = cpuFloatOperand(emu, *reg2); + double right = cpuFloatOperand(emu,emu->cpu.program[inst->bits[0]&0x1F]); + int32_t result; /* Output bits */ + + /* Reserved operand */ + if (emu->cpu.causeCode != 0) + return; + + /* Zero division */ + if (right == 0) { + + /* Invalid operation */ + if (left == 0) { + emu->cpu.causeCode = 0xFF70; + emu->cpu.psw.fiv = 1; + } + + /* Zero division */ + else { + emu->cpu.causeCode = 0xFF68; + emu->cpu.psw.fzd = 1; + } + + return; + } + + /* Perform the operation */ + result = cpuFloatResult(emu, left / right); + if (emu->cpu.causeCode != 0) + return; + + /* Update state */ + *reg2 = result; + emu->cpu.clocks = 44; +} + +/* Divide Unsigned */ +static void cpuDIVU(VB *emu, VB_INSTRUCTION *inst) { + uint32_t right = emu->cpu.program[inst->bits[0] & 0x1F]; + uint32_t *reg2; /* Program register reg2 */ + + /* Zero division */ + if (right == 0) { + emu->cpu.causeCode = 0xFF80; + return; + } + + /* Perform the operation */ + reg2 = (uint32_t *) &emu->cpu.program[inst->bits[0] >> 5 & 0x1F]; + emu->cpu.program[30] = *reg2 % right; + *reg2 /= right; + + /* Update state */ + emu->cpu.psw.ov = 0; + emu->cpu.psw.s = *reg2 >> 31 & 1; + emu->cpu.psw.z = *reg2 == 0; + emu->cpu.clocks = 36; +} + +/* Halt */ +static void cpuHALT(VB *emu, VB_INSTRUCTION *inst) { + emu->cpu.state = CPU_HALTED; + inst->size = 0; + /* TODO: Find out how many clocks this takes */ +} + +/* Input Byte */ +#define cpuIN_B(emu, inst) cpuLoad(emu, inst, VB_U8, 5) + +/* Input Halfword */ +#define cpuIN_H(emu, inst) cpuLoad(emu, inst, VB_U16, 5) + +/* Input Word */ +#define cpuIN_W(emu, inst) cpuLoad(emu, inst, VB_S32, 5) + +/* Jump and Link */ +static void cpuJAL(VB *emu, VB_INSTRUCTION *inst) { + int32_t disp = ((int32_t) inst->bits[0]<<16 | inst->bits[1]) & 0x03FFFFFF; + emu->cpu.program[31] = emu->cpu.pc + 4; + cpuJump(emu, inst, emu->cpu.pc + SignExtend(disp, 26)); +} + +/* Jump Register */ +#define cpuJMP(emu,inst) cpuJump(emu,inst,emu->cpu.program[inst->bits[0]&0x1F]) + +/* Jump Relative */ +static void cpuJR(VB *emu, VB_INSTRUCTION *inst) { + int32_t disp = ((int32_t) inst->bits[0]<<16 | inst->bits[1]) & 0x03FFFFFF; + cpuJump(emu, inst, emu->cpu.pc + SignExtend(disp, 26)); +} + +/* Load Byte */ +#define cpuLD_B(emu, inst) cpuLoad(emu, inst, VB_S8, 5) + +/* Load Halfword */ +#define cpuLD_H(emu, inst) cpuLoad(emu, inst, VB_S16, 5) + +/* Load Word */ +#define cpuLD_W(emu, inst) cpuLoad(emu, inst, VB_S32, 5) + +/* Load to System Register */ +static void cpuLDSR(VB *emu, VB_INSTRUCTION *inst) { + cpuSetSystemRegister(emu, inst->bits[0] & 0x1F, + emu->cpu.program[inst->bits[0] >> 5 & 0x1F], 0); + emu->cpu.clocks = 8; +} + +/* Move Immediate */ +static void cpuMOV_IMM(VB *emu, VB_INSTRUCTION *inst) { + int32_t value = inst->bits[0] & 0x1F; + emu->cpu.program[inst->bits[0] >> 5 & 0x1F] = SignExtend(value, 5); + emu->cpu.clocks = 1; +} + +/* Move Register */ +static void cpuMOV_REG(VB *emu, VB_INSTRUCTION *inst) { + emu->cpu.program[inst->bits[0] >> 5 & 0x1F] = + emu->cpu.program[inst->bits[0] & 0x1F]; + emu->cpu.clocks = 1; +} + +/* Move Bit String Upward */ +#define cpuMOVBSU(emu, inst) cpuBitString(emu, inst) + +/* Add */ +static void cpuMOVEA(VB *emu, VB_INSTRUCTION *inst) { + int32_t right = inst->bits[1]; + emu->cpu.program[inst->bits[0] >> 5 & 0x1F] = + emu->cpu.program[inst->bits[0] & 0x1F] + SignExtend(right, 16); + emu->cpu.clocks = 1; +} + +/* Add */ +static void cpuMOVHI(VB *emu, VB_INSTRUCTION *inst) { + emu->cpu.program[inst->bits[0] >> 5 & 0x1F] = + emu->cpu.program[inst->bits[0] & 0x1F] + ((int32_t)inst->bits[1]<<16); + emu->cpu.clocks = 1; +} + +/* Multiply Halfword */ +static void cpuMPYHW(VB *emu, VB_INSTRUCTION *inst) { + int32_t right = emu->cpu.program[inst->bits[0] & 0x1F] & 0x0001FFFF; + emu->cpu.program[inst->bits[0] >> 5 & 0x1F] *= SignExtend(right, 17); + emu->cpu.clocks = 9; +} + +/* Multiply */ +static void cpuMUL(VB *emu, VB_INSTRUCTION *inst) { + int32_t *reg2 = &emu->cpu.program[inst->bits[0] >> 5 & 0xFF]; + int64_t result = (int64_t) *reg2 * + emu->cpu.program[inst->bits[0] & 0x1F]; + emu->cpu.program[30] = result >> 32; + *reg2 = result; + emu->cpu.psw.ov = *reg2 != result; + emu->cpu.psw.s = *reg2 < 0; + emu->cpu.psw.z = *reg2 == 0; + emu->cpu.clocks = 13; +} + +/* Multiply Floating Short */ +static void cpuMULF_S(VB *emu, VB_INSTRUCTION *inst) { + int32_t *reg2 = &emu->cpu.program[inst->bits[0] >> 5 & 0x1F]; + double left = cpuFloatOperand(emu, *reg2); + double right = cpuFloatOperand(emu,emu->cpu.program[inst->bits[0]&0x1F]); + int32_t result; /* Output bits */ + + /* Perform the operation */ + if (emu->cpu.causeCode == 0) + result = cpuFloatResult(emu, left * right); + if (emu->cpu.causeCode != 0) + return; + + /* Update state */ + *reg2 = result; + emu->cpu.clocks = 30; +} + +/* Multiply Unsigned */ +static void cpuMULU(VB *emu, VB_INSTRUCTION *inst) { + uint32_t *reg2 = (uint32_t *) &emu->cpu.program[inst->bits[0]>>5&0xFF]; + uint64_t result = (uint64_t) *reg2 * + (uint32_t) emu->cpu.program[inst->bits[0] & 0x1F]; + emu->cpu.program[30] = result >> 32; + *reg2 = result; + emu->cpu.psw.ov = *reg2 != result; + emu->cpu.psw.s = *reg2 >> 31 & 1; + emu->cpu.psw.z = *reg2 == 0; + emu->cpu.clocks = 13; +} + +/* Not */ +static void cpuNOT(VB *emu, VB_INSTRUCTION *inst) { + emu->cpu.program[inst->bits[0] >> 5 & 0x1F] = + cpuBitwise(emu, ~emu->cpu.program[inst->bits[0] & 0x1F]); +} + +/* Not Bit String Upward */ +#define cpuNOTBSU(emu, inst) cpuBitString(emu, inst) + +/* Or */ +static void cpuOR(VB *emu, VB_INSTRUCTION *inst) { + int32_t *reg2 = &emu->cpu.program[inst->bits[0] >> 5 & 0x1F]; + *reg2 = cpuBitwise(emu, *reg2 | emu->cpu.program[inst->bits[0] & 0x1F]); +} + +/* Or Bit String Upward */ +#define cpuORBSU(emu, inst) cpuBitString(emu, inst) + +/* Or Not Bit String Upward */ +#define cpuORNBSU(emu, inst) cpuBitString(emu, inst) + +/* Or Immediate */ +static void cpuORI(VB *emu, VB_INSTRUCTION *inst) { + emu->cpu.program[inst->bits[0] >> 5 & 0x1F] = cpuBitwise(emu, + emu->cpu.program[inst->bits[0] & 0x1F] | inst->bits[1]); +} + +/* Output Byte */ +#define cpuOUT_B(emu, inst) cpuStore(emu, inst, VB_U8, 4) + +/* Output Halfword */ +#define cpuOUT_H(emu, inst) cpuStore(emu, inst, VB_U16, 4) + +/* Output Word */ +#define cpuOUT_W(emu, inst) cpuStore(emu, inst, VB_S32, 4) + +/* Return from Trap or Interrupt */ +static void cpuRETI(VB *emu, VB_INSTRUCTION *inst) { + + /* Duplexed exception */ + if (emu->cpu.psw.np) { + emu->cpu.pc = emu->cpu.fepc; + emu->cpu.pcFrom = emu->cpu.fepcFrom; + emu->cpu.pcTo = emu->cpu.fepcTo; + cpuSetSystemRegister(emu, VB_PSW, emu->cpu.fepsw, 0); + } + + /* Non-duplexed exception */ + else { + + /* Exception */ + if (emu->cpu.psw.ep) { + emu->cpu.pc = emu->cpu.eipc; + emu->cpu.pcFrom = emu->cpu.eipcFrom; + emu->cpu.pcTo = emu->cpu.eipcTo; + } + + /* No exception */ + else { + emu->cpu.pcFrom = emu->cpu.pc; + emu->cpu.pc = emu->cpu.eipc; + emu->cpu.pcTo = emu->cpu.pc; + } + + /* Update state */ + cpuSetSystemRegister(emu, VB_PSW, emu->cpu.eipsw, 0); + } + + /* Update state */ + emu->cpu.clocks = 10; + inst->size = 0; +} + +/* Reverse Bits in Word */ +static void cpuREV(VB *emu, VB_INSTRUCTION *inst) { + int32_t value = emu->cpu.program[inst->bits[0] & 0x1F]; + value = (value >> 8 & 0x00FF00FF) | (value << 8 & (int32_t) 0xFF00FF00); + value = (value >> 4 & 0x0F0F0F0F) | (value << 4 & (int32_t) 0xF0F0F0F0); + value = (value >> 2 & 0x33333333) | (value << 2 & (int32_t) 0xCCCCCCCC); + emu->cpu.program[inst->bits[0] >> 5 & 0x1F] = + (value >> 1 & 0x55555555) | (value << 1 & (int32_t) 0xAAAAAAAA); + emu->cpu.clocks = 22; +} + +/* Shift Arithmetic Right by Immediate */ +static void cpuSAR_IMM(VB *emu, VB_INSTRUCTION *inst) { + int32_t *reg2 = &emu->cpu.program[inst->bits[0] >> 5 & 0x1F]; + *reg2 = cpuShiftRight(emu, *reg2, inst->bits[0] & 0x1F, 1); +} + +/* Shift Arithmetic Right by Register */ +static void cpuSAR_REG(VB *emu, VB_INSTRUCTION *inst) { + int32_t *reg2 = &emu->cpu.program[inst->bits[0] >> 5 & 0x1F]; + *reg2 = cpuShiftRight(emu, *reg2, + emu->cpu.program[inst->bits[0] & 0x1F] & 0x1F, 1); +} + +/* Search Bit 0 Downward */ +#define cpuSCH0BSD(emu) cpuBitSearch(emu, 0, -1) + +/* Search Bit 0 Upward */ +#define cpuSCH0BSU(emu) cpuBitSearch(emu, 0, 1) + +/* Search Bit 1 Downward */ +#define cpuSCH1BSD(emu) cpuBitSearch(emu, 1, -1) + +/* Search Bit 1 Upward */ +#define cpuSCH1BSU(emu) cpuBitSearch(emu, 1, 1) + +/* Set Interrupt Disable Flag */ +static void cpuSEI(VB *emu) { + emu->cpu.psw.id = 1; + emu->cpu.clocks = 12; +} + +/* Set Flag Condition */ +static void cpuSETF(VB *emu, VB_INSTRUCTION *inst) { + emu->cpu.program[inst->bits[0] >> 5 & 0x1F] = + cpuCondition(emu, inst->bits[0] & 15); + emu->cpu.clocks = 1; +} + +/* Shift Logical Left by Immediate */ +static void cpuSHL_IMM(VB *emu, VB_INSTRUCTION *inst) { + int32_t *reg2 = &emu->cpu.program[inst->bits[0] >> 5 & 0x1F]; + int32_t bits = inst->bits[0] & 0x1F; + emu->cpu.psw.cy = bits == 0 ? 0 : *reg2 >> (32 - bits) & 1; + *reg2 = cpuBitwise(emu, *reg2 << bits); +} + +/* Shift Logical Left by Register */ +static void cpuSHL_REG(VB *emu, VB_INSTRUCTION *inst) { + int32_t *reg2 = &emu->cpu.program[inst->bits[0] >> 5 & 0x1F]; + int32_t bits = emu->cpu.program[inst->bits[0] & 0x1F] & 0x1F; + emu->cpu.psw.cy = bits == 0 ? 0 : *reg2 >> (32 - bits) & 1; + *reg2 = cpuBitwise(emu, *reg2 << bits); +} + +/* Shift Logical Right by Immediate */ +static void cpuSHR_IMM(VB *emu, VB_INSTRUCTION *inst) { + int32_t *reg2 = &emu->cpu.program[inst->bits[0] >> 5 & 0x1F]; + *reg2 = cpuShiftRight(emu, *reg2, inst->bits[0] & 0x1F, 0); +} + +/* Shift Logical Right by Register */ +static void cpuSHR_REG(VB *emu, VB_INSTRUCTION *inst) { + int32_t *reg2 = &emu->cpu.program[inst->bits[0] >> 5 & 0x1F]; + *reg2 = cpuShiftRight(emu, *reg2, + emu->cpu.program[inst->bits[0] & 0x1F] & 0x1F, 0); +} + +/* Store Byte */ +#define cpuST_B(emu, inst) cpuStore(emu, inst, VB_S8, 4) + +/* Store Halfword */ +#define cpuST_H(emu, inst) cpuStore(emu, inst, VB_S16, 4) + +/* Store Word */ +#define cpuST_W(emu, inst) cpuStore(emu, inst, VB_S32, 4) + +/* Store Contents of System Register */ +static void cpuSTSR(VB *emu, VB_INSTRUCTION *inst) { + emu->cpu.program[inst->bits[0] >> 5 & 0x1F] = + vbGetSystemRegister(emu, inst->bits[0] & 0x1F); + emu->cpu.clocks = 8; +} + +/* Subtract */ +static void cpuSUB(VB *emu, VB_INSTRUCTION *inst) { + int32_t *reg2 = &emu->cpu.program[inst->bits[0] >> 5 & 0x1F]; + *reg2 = cpuSubtract(emu, *reg2, emu->cpu.program[inst->bits[0] & 0x1F]); +} + +/* Subtract Floating Short */ +static void cpuSUBF_S(VB *emu, VB_INSTRUCTION *inst) { + int32_t *reg2 = &emu->cpu.program[inst->bits[0] >> 5 & 0x1F]; + double left = cpuFloatOperand(emu, *reg2); + double right = cpuFloatOperand(emu,emu->cpu.program[inst->bits[0]&0x1F]); + int32_t result; /* Output bits */ + + /* Perform the operation */ + if (emu->cpu.causeCode == 0) + result = cpuFloatResult(emu, left - right); + if (emu->cpu.causeCode != 0) + return; + + /* Update state */ + *reg2 = result; + emu->cpu.clocks = 28; +} + +/* Trap */ +static void cpuTRAP(VB *emu, VB_INSTRUCTION *inst) { + emu->cpu.causeCode = 0xFFA0 + (inst->bits[0] & 0x1F); + emu->cpu.clocks = 15; + emu->cpu.pc += 2; +} + +/* Truncate Short Floating to Word Integer */ +#define cpuTRNC_SW(emu, inst) cpuFloatToWord(emu, inst, 1) + +/* Exchange Byte */ +static void cpuXB(VB *emu, VB_INSTRUCTION *inst) { + int32_t *reg2 = &emu->cpu.program[inst->bits[0] >> 5 & 0x1F]; + *reg2 = + (*reg2 >> 8 & (int32_t) 0x00FF0000) | + (*reg2 << 8 & (int32_t) 0xFF000000) | + (*reg2 & (int32_t) 0x0000FFFF) + ; + emu->cpu.clocks = 6; +} + +/* Exchange Halfword */ +static void cpuXH(VB *emu, VB_INSTRUCTION *inst) { + int32_t *reg2 = &emu->cpu.program[inst->bits[0] >> 5 & 0x1F]; + *reg2 = (*reg2 >> 16 & 0x0000FFFF) | *reg2 << 16; + emu->cpu.clocks = 1; +} + +/* Exclusive Or */ +static void cpuXOR(VB *emu, VB_INSTRUCTION *inst) { + int32_t *reg2 = &emu->cpu.program[inst->bits[0] >> 5 & 0x1F]; + *reg2 = cpuBitwise(emu, *reg2 ^ emu->cpu.program[inst->bits[0] & 0x1F]); +} + +/* Exclusive Or Bit String Upward */ +#define cpuXORBSU(emu, inst) cpuBitString(emu, inst) + +/* Exclusive Or Not Bit String Upward */ +#define cpuXORNBSU(emu, inst) cpuBitString(emu, inst) + +/* Exclusive Or Immediate */ +static void cpuXORI(VB *emu, VB_INSTRUCTION *inst) { + emu->cpu.program[inst->bits[0] >> 5 & 0x1F] = cpuBitwise(emu, + emu->cpu.program[inst->bits[0] & 0x1F] ^ inst->bits[1]); +} + + + +/***************************** Module Functions ******************************/ + +/* Check for interrupts */ +static int cpuCheckIRQs(VB *emu) { + int x; /* Iterator */ + + /* Interrupts are masked */ + if (emu->cpu.psw.np || emu->cpu.psw.ep || emu->cpu.psw.id) + return 0; + + /* Check for interrupts */ + for (x = 4; x >= emu->cpu.psw.i; x--) { + + /* The interrupt request line is low */ + if (!emu->cpu.irq[x]) + continue; + + /* Cause a pending HALT instruction to complete */ + if (emu->cpu.state == CPU_HALTED) + emu->cpu.pc += 2; + + /* Trigger an interrupt */ + emu->cpu.causeCode = 0xFE00 | x << 4; + emu->cpu.state = CPU_EXCEPTION; + return 1; + } + + /* No interrupt */ + return 0; +} + +/* Perform instruction execute operations */ +static int cpuExecute(VB *emu) { + int broke = 0; /* Application break occurred */ + VB_INSTRUCTION *inst = &emu->cpu.inst; /* Shorthand reference */ + + /* Check for application break */ + if (emu->onExecute != NULL && emu->onExecute(emu, inst)) + return 1; + + /* Update state */ + emu->cpu.causeCode = 0; + + /* Processing by ID */ + switch (inst->id) { + case CPU_ADD_IMM: cpuADD_IMM(emu, inst); break; + case CPU_ADD_REG: cpuADD_REG(emu, inst); break; + case CPU_ADDF_S : cpuADDF_S (emu, inst); break; + case CPU_ADDI : cpuADDI (emu, inst); break; + case CPU_AND : cpuAND (emu, inst); break; + case CPU_ANDBSU : broke = cpuANDBSU (emu, inst); break; + case CPU_ANDI : cpuANDI (emu, inst); break; + case CPU_ANDNBSU: broke = cpuANDNBSU(emu, inst); break; + case CPU_BCOND : cpuBCOND (emu, inst); break; + case CPU_CAXI : broke = cpuCAXI (emu, inst); break; + case CPU_CLI : cpuCLI (emu ); break; + case CPU_CMP_IMM: cpuCMP_IMM(emu, inst); break; + case CPU_CMP_REG: cpuCMP_REG(emu, inst); break; + case CPU_CMPF_S : cpuCMPF_S (emu, inst); break; + case CPU_CVT_SW : cpuCVT_SW (emu, inst); break; + case CPU_CVT_WS : cpuCVT_WS (emu, inst); break; + case CPU_DIV : cpuDIV (emu, inst); break; + case CPU_DIVF_S : cpuDIVF_S (emu, inst); break; + case CPU_DIVU : cpuDIVU (emu, inst); break; + case CPU_HALT : cpuHALT (emu, inst); break; + case CPU_IN_B : broke = cpuIN_B (emu, inst); break; + case CPU_IN_H : broke = cpuIN_H (emu, inst); break; + case CPU_IN_W : broke = cpuIN_W (emu, inst); break; + case CPU_JAL : cpuJAL (emu, inst); break; + case CPU_JMP : cpuJMP (emu, inst); break; + case CPU_JR : cpuJR (emu, inst); break; + case CPU_LD_B : broke = cpuLD_B (emu, inst); break; + case CPU_LD_H : broke = cpuLD_H (emu, inst); break; + case CPU_LD_W : broke = cpuLD_W (emu, inst); break; + case CPU_LDSR : cpuLDSR (emu, inst); break; + case CPU_MOV_IMM: cpuMOV_IMM(emu, inst); break; + case CPU_MOV_REG: cpuMOV_REG(emu, inst); break; + case CPU_MOVBSU : broke = cpuMOVBSU (emu, inst); break; + case CPU_MOVEA : cpuMOVEA (emu, inst); break; + case CPU_MOVHI : cpuMOVHI (emu, inst); break; + case CPU_MPYHW : cpuMPYHW (emu, inst); break; + case CPU_MUL : cpuMUL (emu, inst); break; + case CPU_MULF_S : cpuMULF_S (emu, inst); break; + case CPU_MULU : cpuMULU (emu, inst); break; + case CPU_NOT : cpuNOT (emu, inst); break; + case CPU_NOTBSU : broke = cpuNOTBSU (emu, inst); break; + case CPU_OR : cpuOR (emu, inst); break; + case CPU_ORBSU : broke = cpuORBSU (emu, inst); break; + case CPU_ORI : cpuORI (emu, inst); break; + case CPU_ORNBSU : broke = cpuORNBSU (emu, inst); break; + case CPU_OUT_B : broke = cpuOUT_B (emu, inst); break; + case CPU_OUT_H : broke = cpuOUT_H (emu, inst); break; + case CPU_OUT_W : broke = cpuOUT_W (emu, inst); break; + case CPU_RETI : cpuRETI (emu, inst); break; + case CPU_REV : cpuREV (emu, inst); break; + case CPU_SAR_IMM: cpuSAR_IMM(emu, inst); break; + case CPU_SAR_REG: cpuSAR_REG(emu, inst); break; + case CPU_SCH0BSD: cpuSCH0BSD(emu ); break; + case CPU_SCH0BSU: cpuSCH0BSU(emu ); break; + case CPU_SCH1BSD: cpuSCH1BSD(emu ); break; + case CPU_SCH1BSU: cpuSCH1BSU(emu ); break; + case CPU_SEI : cpuSEI (emu ); break; + case CPU_SETF : cpuSETF (emu, inst); break; + case CPU_SHL_IMM: cpuSHL_IMM(emu, inst); break; + case CPU_SHL_REG: cpuSHL_REG(emu, inst); break; + case CPU_SHR_IMM: cpuSHR_IMM(emu, inst); break; + case CPU_SHR_REG: cpuSHR_REG(emu, inst); break; + case CPU_ST_B : broke = cpuST_B (emu, inst); break; + case CPU_ST_H : broke = cpuST_H (emu, inst); break; + case CPU_ST_W : broke = cpuST_W (emu, inst); break; + case CPU_STSR : cpuSTSR (emu, inst); break; + case CPU_SUB : cpuSUB (emu, inst); break; + case CPU_SUBF_S : cpuSUBF_S (emu, inst); break; + case CPU_TRAP : cpuTRAP (emu, inst); break; + case CPU_TRNC_SW: cpuTRNC_SW(emu, inst); break; + case CPU_XB : cpuXB (emu, inst); break; + case CPU_XH : cpuXH (emu, inst); break; + case CPU_XOR : cpuXOR (emu, inst); break; + case CPU_XORBSU : broke = cpuXORBSU (emu, inst); break; + case CPU_XORI : cpuXORI (emu, inst); break; + case CPU_XORNBSU: broke = cpuXORNBSU(emu, inst); break; + default: /* CPU_ILLEGAL */ emu->cpu.causeCode = 0xFF90; + } + + /* Instructions cannot modify r0 */ + emu->cpu.program[0] = 0x00000000; + + /* An application break was requested */ + if (broke) + return 1; + + /* Post-instruction tasks */ + if (emu->cpu.causeCode == 0 && !emu->cpu.busWait) { + + /* Advance to next instruction if not processing a bit string */ + if (!emu->cpu.substring) + emu->cpu.pc += inst->size; + + /* Check for interrupts */ + cpuCheckIRQs(emu); + } + + /* An exception or interrupt occurred */ + if (emu->cpu.causeCode != 0) { + emu->cpu.state = CPU_EXCEPTION; + emu->cpu.substring = 0; + } + + /* Switch to fetch mode if not halting and execution has completed */ + else if (emu->cpu.state != CPU_HALTED && + !emu->cpu.busWait && !emu->cpu.substring) { + emu->cpu.state = CPU_FETCH; + emu->cpu.fetch = 0; + } + + return 0; +} + +/* Enter an exception state */ +static int cpuException(VB *emu) { + + /* Fatal exception */ + if (emu->cpu.psw.np) { + + /* Write the cause code for debugging */ + if (emu->cpu.busWait == 0) { + if (cpuWrite(emu, 0x00000000, VB_S32, + 0xFFFF0000 | emu->cpu.causeCode)) + return 1; + + /* Update state */ + emu->cpu.busWait = 1; + emu->cpu.clocks = emu->cpu.access.clocks; + + /* Wait for the bus access to complete */ + if (emu->cpu.clocks > 0) + return 0; + } + + /* Write PSW for debugging */ + if (emu->cpu.busWait == 1) { + if (cpuWrite(emu, 0x00000000, VB_S32, + vbGetSystemRegister(emu, VB_PSW))) + return 1; + + /* Update state */ + emu->cpu.busWait = 2; + emu->cpu.clocks = emu->cpu.access.clocks; + + /* Wait for the bus access to complete */ + if (emu->cpu.clocks > 0) + return 0; + } + + /* Write PC for debugging */ + if (emu->cpu.busWait == 2) { + if (cpuWrite(emu, 0x00000000, VB_S32, emu->cpu.pc)) + return 1; + + /* Update state */ + emu->cpu.busWait = 3; + emu->cpu.clocks = emu->cpu.access.clocks; + + /* Wait for the bus access to complete */ + if (emu->cpu.clocks > 0) + return 0; + } + + /* Update state */ + emu->cpu.busWait = 0; + emu->cpu.causeCode = 0; + emu->cpu.state = CPU_FATAL; + return 0; + } + + /* Duplexed exception */ + if (emu->cpu.psw.ep) { + emu->cpu.ecr.fecc = emu->cpu.causeCode; + emu->cpu.fepsw = vbGetSystemRegister(emu, VB_PSW); + emu->cpu.fepc = emu->cpu.pc; + emu->cpu.fepcFrom = emu->cpu.pcFrom; + emu->cpu.fepcTo = emu->cpu.pcTo; + emu->cpu.psw.np = 1; + emu->cpu.pc = 0xFFFFFFD0; + } + + /* Exception or interrupt */ + else { + emu->cpu.ecr.eicc = emu->cpu.causeCode; + emu->cpu.eipsw = vbGetSystemRegister(emu, VB_PSW); + emu->cpu.eipc = emu->cpu.pc; + emu->cpu.psw.ep = 1; + emu->cpu.pc = (emu->cpu.causeCode & 0x0040) != 0 ? + 0xFFFFFF60 : ((uint32_t) 0xFFFF0000 | emu->cpu.causeCode); + } + + /* Interrupt */ + if (emu->cpu.causeCode < 0xFF00) + emu->cpu.psw.i = emu->cpu.psw.i == 15 ? 15 : emu->cpu.psw.i + 1; + + /* Update state */ + emu->cpu.causeCode = 0; + emu->cpu.state = CPU_FETCH; + emu->cpu.psw.id = 1; + emu->cpu.psw.ae = 0; + emu->cpu.pcFrom = emu->cpu.pc; + emu->cpu.pcTo = emu->cpu.pc; + /* emu->cpu.clocks = ? */ + return 0; +} + +/* Perform instruction fetch operations */ +static int cpuFetch(VB *emu) { + VB_INSTRUCTION *inst; /* Reference to emu->cpu.inst */ + uint8_t opcode; /* 6-bit instruction opcode */ + + /* Need to read a data unit */ + if (!emu->cpu.busWait) { + + /* Read the data unit from the bus */ + if (cpuReadFetch(emu, emu->cpu.pc + (emu->cpu.fetch << 1))) + return 1; + + /* Update state */ + emu->cpu.busWait = 1; + emu->cpu.clocks = emu->cpu.access.clocks; + + /* Wait for the bus access to complete */ + if (emu->cpu.clocks > 0) + return 0; + } + + /* Update state */ + inst = &emu->cpu.inst; + inst->bits[emu->cpu.fetch] = emu->cpu.access.value; + emu->cpu.busWait = 0; + + /* Working variables */ + opcode = inst->bits[0] >> 10 & 0x3F; + + /* First fetch */ + if (emu->cpu.fetch == 0) { + + /* Update state */ + inst->size = CPU_SIZES[opcode]; + + /* A second fetch is needed */ + if (inst->size == 4) { + emu->cpu.fetch = 1; + return 0; + } + + } + + /* Determine the internal ID of the instruction */ + inst->id = CPU_OPCODES[opcode]; + switch (inst->id) { + case CPU_BITSTRING: inst->id = CPU_BITSTRINGS[inst->id & 0x1F]; break; + case CPU_FLOATENDO: inst->id = CPU_FLOATENDOS[inst->bits[1]>>10&0x3F]; + } + + /* Update state */ + emu->cpu.fetch = -1; + emu->cpu.state = CPU_EXECUTE; + return 0; +} + +/* Process the simulation for some number of clocks */ +static int cpuEmulate(VB *emu, uint32_t clocks) { + + /* Fatal halt: cannot break */ + if (emu->cpu.state == CPU_FATAL) + return 0; + + /* Process all clocks */ + do { + + /* The next operation is after the remaining clocks */ + if (clocks < emu->cpu.clocks) { + emu->cpu.clocks -= clocks; + break; + } + + /* Update remaining clocks */ + clocks -= emu->cpu.clocks; + emu->cpu.clocks = 0; + + /* Processing by operations state */ + switch (emu->cpu.state) { + case CPU_FETCH : if (cpuFetch (emu)) return 1; break; + case CPU_EXECUTE : if (cpuExecute (emu)) return 1; break; + case CPU_EXCEPTION: if (cpuException(emu)) return 1; break; + case CPU_HALTED : if (cpuCheckIRQs(emu)) return 0; break; + } + + } while (clocks > 0); + + /* No break occurred */ + return 0; +} + +/* Determine the number of clocks before a break condititon could occur */ +static uint32_t cpuUntil(VB *emu, uint32_t clocks) { + + /* Cannot break */ + if ( + emu->cpu.state == CPU_HALTED || + emu->cpu.state == CPU_FATAL || ( + emu->onException == NULL && + emu->onExecute == NULL && + emu->onRead == NULL && + emu->onWrite == NULL + )) return clocks; + + /* Will not break before next operation */ + return emu->cpu.clocks < clocks ? emu->cpu.clocks : clocks; +} + + + #endif /* VBAPI */ diff --git a/core/vb.c b/core/vb.c index 5d0e2be..abd0b90 100644 --- a/core/vb.c +++ b/core/vb.c @@ -1,6 +1,7 @@ #define VBAPI /* Header includes */ +#include #include @@ -15,7 +16,7 @@ static const uint8_t TYPE_SIZES[] = { 1, 1, 2, 2, 4 }; /********************************** Macros ***********************************/ /* Sign-extend a value of some number of bits to 32 bits */ -#define SignExtend(v,b) ( (v) | (((v) & 1 << b - 1) ? ~(int32_t)0 << b : 0) ) +#define SignExtend(v,b) ((v) | (((v) & ((1<<(b)) - 1)) ? ~(int32_t)0<<(b) : 0)) @@ -23,11 +24,78 @@ static const uint8_t TYPE_SIZES[] = { 1, 1, 2, 2, 4 }; /* Component includes */ #include "bus.c" +#include "cpu.c" + + + +/***************************** Module Functions ******************************/ + +/* Process a simulation for some number of clocks */ +static int sysEmulate(VB *emu, uint32_t clocks) { + int broke; + broke = cpuEmulate(emu, clocks); + return broke; +} + +/* Determine the number of clocks before a break condititon could occur */ +static uint32_t sysUntil(VB *emu, uint32_t clocks) { + clocks = cpuUntil(emu, clocks); + return clocks; +} /******************************* API Functions *******************************/ +/* Associate two simulations as peers */ +void vbConnect(VB *emu1, VB *emu2) { + + /* Disconnect any existing link associations */ + if (emu1->peer != NULL && emu1->peer != emu2) + emu1->peer->peer = NULL; + if (emu2->peer != NULL && emu2->peer != emu1) + emu2->peer->peer = NULL; + + /* Link the two simulations */ + emu1->peer = emu2; + emu2->peer = emu1; +} + +/* Disassociate linked peers */ +void vbDisconnect(VB *emu) { + if (emu->peer != NULL) + emu->peer->peer = NULL; + emu->peer = NULL; +} + +/* Process one or two simulations */ +int vbEmulate(VB *emu1, VB *emu2, uint32_t *clocks) { + int broke; /* The simulation requested an application break */ + uint32_t until; /* Maximum clocks before a break could happen */ + + /* Processing one simulaiton */ + if (emu2 == NULL) { + do { + until = sysUntil (emu1, *clocks); + broke = sysEmulate(emu1, until ); + *clocks -= until; + } while (!broke && *clocks > 0); + } + + /* Processing two simulations */ + else { + do { + until = sysUntil (emu1, *clocks); + until = sysUntil (emu2, until ); + broke = sysEmulate(emu1, until ); + broke |= sysEmulate(emu2, until ); + *clocks -= until; + } while (!broke && *clocks > 0); + } + + return broke; +} + /* Retrieve the value of PC */ uint32_t vbGetProgramCounter(VB *emu, int type) { switch (type) { @@ -97,13 +165,23 @@ uint32_t vbGetSystemRegister(VB *emu, int id) { /* Prepare a simulation state instance for use */ void vbInit(VB *emu) { + /* Breakpoint callbacks */ + emu->onException = NULL; + emu->onExecute = NULL; + emu->onFetch = NULL; + emu->onRead = NULL; + emu->onWrite = NULL; + + /* System */ + emu->peer = NULL; + /* Cartridge */ emu->cart.rom = NULL; emu->cart.romSize = 0; emu->cart.sram = NULL; emu->cart.sramSize = 0; - /* All others */ + /* Everything else */ vbReset(emu); } @@ -118,7 +196,7 @@ void vbReset(VB *emu) { uint32_t x; /* Iterator */ /* CPU registers */ - emu->cpu.pc = 0xFFFFFFF0; + vbSetProgramCounter(emu, 0xFFFFFFF0); vbSetSystemRegister(emu, VB_ECR, 0x0000FFF0); vbSetSystemRegister(emu, VB_PSW, 0x00008000); @@ -132,8 +210,19 @@ void vbReset(VB *emu) { emu->cpu.fepsw = 0x00000000; emu->cpu.sr29 = 0x00000000; emu->cpu.sr31 = 0x00000000; - emu->cpu.pcFrom = 0xFFFFFFF0; - emu->cpu.pcTo = 0xFFFFFFF0; + + /* History tracking */ + emu->cpu.eipcFrom = 0xFFFFFFF0; + emu->cpu.eipcTo = 0xFFFFFFF0; + emu->cpu.fepcFrom = 0xFFFFFFF0; + emu->cpu.fepcTo = 0xFFFFFFF0; + emu->cpu.pcFrom = 0xFFFFFFF0; + emu->cpu.pcTo = 0xFFFFFFF0; + + /* Other CPU state */ + emu->cpu.state = CPU_FETCH; + for (x = 0; x < 5; x++) + emu->cpu.irq[x] = 0; /* WRAM (the hardware does not do this) */ for (x = 0; x < 0x10000; x++) @@ -142,9 +231,11 @@ void vbReset(VB *emu) { /* Specify a new value for PC */ uint32_t vbSetProgramCounter(VB *emu, uint32_t value) { - value &= 0xFFFFFFFE; - emu->cpu.pc = value; - /* Set stage to fecth=0 */ + value &= 0xFFFFFFFE; + emu->cpu.fetch = 0; + emu->cpu.pc = value; + emu->cpu.state = CPU_FETCH; + emu->cpu.substring = 0; return value; } @@ -181,42 +272,7 @@ int vbSetSRAM(VB *emu, void *sram, uint32_t size) { /* Specify a new value for a system register */ uint32_t vbSetSystemRegister(VB *emu, int id, uint32_t value) { - switch (id) { - case VB_ADTRE: return emu->cpu.adtre = value & 0xFFFFFFFE; - case VB_EIPC : return emu->cpu.eipc = value & 0xFFFFFFFE; - case VB_EIPSW: return emu->cpu.eipsw = value & 0x000FF3FF; - case VB_FEPC : return emu->cpu.fepc = value & 0xFFFFFFFE; - case VB_FEPSW: return emu->cpu.fepsw = value & 0x000FF3FF; - case VB_PIR : return 0x00005346; - case VB_TKCW : return 0x000000E0; - case 29 : return emu->cpu.sr29 = value & 0x00000001; - case 31 : return emu->cpu.sr31 = value; - case VB_CHCW : - emu->cpu.chcw.ice = value >> 1 & 1; - return value & 0x00000002; - case VB_ECR : - emu->cpu.ecr.fecc = value >> 16; - emu->cpu.ecr.eicc = value; - return value; - case VB_PSW : - emu->cpu.psw.i = value >> 16 & 15; - emu->cpu.psw.np = value >> 15 & 1; - emu->cpu.psw.ep = value >> 14 & 1; - emu->cpu.psw.ae = value >> 13 & 1; - emu->cpu.psw.id = value >> 12 & 1; - emu->cpu.psw.fro = value >> 9 & 1; - emu->cpu.psw.fiv = value >> 8 & 1; - emu->cpu.psw.fzd = value >> 7 & 1; - emu->cpu.psw.fov = value >> 6 & 1; - emu->cpu.psw.fud = value >> 5 & 1; - emu->cpu.psw.fpr = value >> 4 & 1; - emu->cpu.psw.cy = value >> 3 & 1; - emu->cpu.psw.ov = value >> 2 & 1; - emu->cpu.psw.s = value >> 1 & 1; - emu->cpu.psw.z = value & 1; - return value & 0x000FF3FF; - } - return 0; + return cpuSetSystemRegister(emu, id, value, 1); } /* Write a data unit to the bus */ diff --git a/core/vb.h b/core/vb.h index 874cd1c..abc6caa 100644 --- a/core/vb.h +++ b/core/vb.h @@ -18,11 +18,12 @@ extern "C" { /********************************* Constants *********************************/ /* Memory access types */ -#define VB_S8 0 -#define VB_U8 1 -#define VB_S16 2 -#define VB_U16 3 -#define VB_S32 4 +#define VB_CANCEL -1 +#define VB_S8 0 +#define VB_U8 1 +#define VB_S16 2 +#define VB_U16 3 +#define VB_S32 4 /* System register IDs */ #define VB_ADTRE 25 @@ -45,8 +46,38 @@ extern "C" { /*********************************** Types ***********************************/ -/* Simulation state */ +/* Forward references */ typedef struct VB VB; + +/* Memory access */ +typedef struct { + uint32_t address; /* Bus address being accessed */ + uint32_t clocks; /* Number of clocks required to complete */ + int32_t value; /* Value read (callback's responsibility) or to write */ + int8_t type; /* Data type of value */ +} VB_ACCESS; + +/* CPU instruction */ +typedef struct { + + /* Public fields */ + uint32_t address; /* Bus address */ + uint16_t bits[2]; /* Binary instruction code */ + uint8_t size; /* Size in bytes of the instruction */ + + /* Implementation fields */ + int32_t aux[2]; /* Auxiliary storage for CAXI and bit strings */ + uint8_t id; /* Internal operation ID */ +} VB_INSTRUCTION; + +/* Breakpoint callbacks */ +typedef int (*VB_EXCEPTIONPROC)(VB *, uint16_t); +typedef int (*VB_EXECUTEPROC )(VB *, VB_INSTRUCTION *); +typedef int (*VB_FETCHPROC )(VB *, int, VB_ACCESS *); +typedef int (*VB_READPROC )(VB *, VB_ACCESS *); +typedef int (*VB_WRITEPROC )(VB *, VB_ACCESS *); + +/* Simulation state */ struct VB { /* Game pak */ @@ -101,17 +132,37 @@ struct VB { /* Other registers */ uint32_t pc; /* Program counter */ - uint32_t pcFrom; /* Source of most recent jump */ - uint32_t pcTo; /* Destination of most recent jump */ int32_t program[32]; /* program registers */ + /* History tracking */ + uint32_t eipcFrom; /* Source of most recent jump */ + uint32_t eipcTo; /* Destination of most recent jump */ + uint32_t fepcFrom; /* Source of most recent jump */ + uint32_t fepcTo; /* Destination of most recent jump */ + uint32_t pcFrom; /* Source of most recent jump */ + uint32_t pcTo; /* Destination of most recent jump */ + /* Other fields */ - uint32_t clocks; /* Clocks until next action */ - uint8_t fetch; /* Index of fetch unit */ - uint8_t state; /* Operations state */ + VB_ACCESS access; /* Memory access descriptor */ + VB_INSTRUCTION inst; /* Instruction descriptor */ + uint8_t irq[5]; /* Interrupt request lines */ + uint8_t busWait; /* Waiting on a memory access */ + uint16_t causeCode; /* Exception cause code */ + uint32_t clocks; /* Clocks until next action */ + int16_t fetch; /* Index of fetch unit */ + uint8_t state; /* Operations state */ + uint8_t substring; /* A bit string operation is in progress */ } cpu; + /* Breakpoint callbacks */ + VB_EXCEPTIONPROC onException; /* CPU exception */ + VB_EXECUTEPROC onExecute; /* Instruction execute */ + VB_FETCHPROC onFetch; /* Instruction fetch */ + VB_READPROC onRead; /* Memory read */ + VB_WRITEPROC onWrite; /* Memory write */ + /* Other fields */ + VB *peer; /* Communications peer */ uint8_t wram[0x10000]; /* Main memory */ }; @@ -119,6 +170,9 @@ struct VB { /**************************** Function Prototypes ****************************/ +VBAPI void vbConnect (VB *emu1, VB *emu2); +VBAPI void vbDisconnect (VB *emu); +VBAPI int vbEmulate (VB *emu1, VB *emu2, uint32_t *clocks); VBAPI uint32_t vbGetProgramCounter (VB *emu, int type); VBAPI int32_t vbGetProgramRegister (VB *emu, int id); VBAPI void* vbGetROM (VB *emu, uint32_t *size); diff --git a/license.txt b/license.txt index 76323fa..546ca0a 100644 --- a/license.txt +++ b/license.txt @@ -1,4 +1,4 @@ -Copyright (C) 2021 Planet Virtual Boy +Copyright (C) 2021 Guy Perfect This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff --git a/makefile b/makefile index d98aaad..4e862cd 100644 --- a/makefile +++ b/makefile @@ -1,7 +1,7 @@ .PHONY: help help: @echo - @echo "Virtual Boy Emulator - September 10, 2021" + @echo "Virtual Boy Emulator - September 18, 2021" @echo @echo "Target build environment is any Debian with the following packages:" @echo " emscripten"