diff --git a/makefile b/makefile index e9d12fe..b9dc268 100644 --- a/makefile +++ b/makefile @@ -164,5 +164,5 @@ win64: win64_pre native_common native_common: @echo " Building native module $(name)" @$(prefix)gcc $(include) -Isrc/core/include $(gccargs) -s -shared -O2 \ - -fno-strict-aliasing -Werror \ + -fno-strict-aliasing -fPIC -Werror \ -o native/$(name)$(ext) src/desktop/vue/NativeVUE.c src/core/vue.c diff --git a/src/core/cpu.c b/src/core/cpu.c index 497da94..0dc79e1 100644 --- a/src/core/cpu.c +++ b/src/core/cpu.c @@ -8,14 +8,14 @@ *****************************************************************************/ /* Stages */ -#define CPU_FETCH 0 -#define CPU_EXECUTE 1 -#define CPU_HALT 2 -#define CPU_EXCEPTION 3 -#define CPU_FATAL 4 -#define CPU_CLEAR 5 -#define CPU_DUMP 6 -#define CPU_RESTORE 7 +#define CPU_FATAL -1 +#define CPU_FETCH 0 +#define CPU_EXECUTE 1 +#define CPU_EXCEPTION 2 +#define CPU_HALT 3 +#define CPU_CLEAR 4 +#define CPU_DUMP 5 +#define CPU_RESTORE 6 /* Intermediate instruction IDs */ #define BITSTRING -2 @@ -57,20 +57,51 @@ static const int8_t LOOKUP_FLOATENDO[] = { VUE_MPYHW , VUE_ILLEGAL, VUE_ILLEGAL, VUE_ILLEGAL }; +/* Instruction cycle counts */ +static const int8_t CYCLES[] = { + 1, 1, 28, 1, 1, 1, 1, 1, 1, 26, 12, 1, 1, 10, 14, 16, + 38, 44, 36, 1, 5, 5, 5, 3, 3, 3, 5, 5, 5, 8, 1, 1, + 1, 1, 1, 9, 13, 30, 13, 1, 1, 1, 1, 1, 1, 4, 4, 4, + 10, 22, 1, 1, 1, 1, 1, 1, 12, 1, 1, 1, 1, 1, 4, 4, + 4, 8, 1, 28, 15, 14, 6, 1, 1, 1, 1, 1 +}; + /***************************************************************************** - * Module Functions * + * Instruction Functions * + *****************************************************************************/ + +/* JMP */ +static void cpuJMP(VUE *vue) { + vue->cpu.pc = vue->cpu.program[vue->cpu.inst.reg1] - 2; +} + +/* MOVEA */ +static void cpuMOVEA(VUE *vue) { + vue->cpu.program[vue->cpu.inst.reg2] = + vue->cpu.program[vue->cpu.inst.reg1] + vue->cpu.inst.imm; +} + +/* MOVHI */ +static void cpuMOVHI(VUE *vue) { + vue->cpu.program[vue->cpu.inst.reg2] = + vue->cpu.program[vue->cpu.inst.reg1] + (vue->cpu.inst.imm << 16); +} + + + +/***************************************************************************** + * Stage Functions * *****************************************************************************/ /* Decode an instruction from its binary encoding */ -static void cpuDecode(VUE_INST *inst, int32_t bits) { +static void cpuDecode(VUE_INSTRUCTION *inst) { int8_t extend; /* Sign-extend the immediate operand */ int32_t x; /* Working variable */ /* Configure instance fields */ - inst->bits = bits; - inst->opcode = bits >> 26 & 63; + inst->opcode = inst->bits >> 26 & 63; x = inst->opcode << 1; inst->id = LOOKUP_OPCODE[x ]; extend = LOOKUP_OPCODE[x + 1]; @@ -80,42 +111,42 @@ static void cpuDecode(VUE_INST *inst, int32_t bits) { /* Decode by format */ switch (inst->format) { case 1: - inst->reg2 = bits >> 21 & 31; - inst->reg1 = bits >> 16 & 31; + inst->reg2 = inst->bits >> 21 & 31; + inst->reg1 = inst->bits >> 16 & 31; break; case 2: - x = bits >> 16 & 31; - inst->reg2 = bits >> 21 & 31; + x = inst->bits >> 16 & 31; + inst->reg2 = inst->bits >> 21 & 31; inst->imm = extend < 0 ? SIGN_EXTEND(5, x) : x; if (inst->id == BITSTRING) inst->id = x >= 16 ? VUE_ILLEGAL : LOOKUP_BITSTRING[x]; break; case 3: - x = bits >> 16 & 0x1FF; + x = inst->bits >> 16 & 0x1FF; inst->opcode = 0x20; - inst->cond = bits >> 25 & 15; + inst->cond = inst->bits >> 25 & 15; inst->disp = SIGN_EXTEND(9, x); break; case 4: - x = bits & 0x3FFFFFF; + x = inst->bits & 0x3FFFFFF; inst->disp = SIGN_EXTEND(26, x); break; case 5: - x = bits & 0xFFFF; - inst->reg2 = bits >> 21 & 31; - inst->reg1 = bits >> 16 & 31; + x = inst->bits & 0xFFFF; + inst->reg2 = inst->bits >> 21 & 31; + inst->reg1 = inst->bits >> 16 & 31; inst->imm = extend < 0 ? SIGN_EXTEND(16, x) : x; break; case 6: - x = bits & 0xFFFF; - inst->reg2 = bits >> 21 & 31; - inst->reg1 = bits >> 16 & 31; + x = inst->bits & 0xFFFF; + inst->reg2 = inst->bits >> 21 & 31; + inst->reg1 = inst->bits >> 16 & 31; inst->disp = SIGN_EXTEND(16, x); break; case 7: - inst->reg2 = bits >> 21 & 31; - inst->reg1 = bits >> 16 & 31; - inst->subopcode = bits >> 10 & 63; + inst->reg2 = inst->bits >> 21 & 31; + inst->reg1 = inst->bits >> 16 & 31; + inst->subopcode = inst->bits >> 10 & 63; inst->id = inst->subopcode >= 16 ? VUE_ILLEGAL : LOOKUP_FLOATENDO[inst->subopcode]; break; @@ -123,6 +154,178 @@ static void cpuDecode(VUE_INST *inst, int32_t bits) { } +/* Check for an exception or interrupt */ +static vbool cpuTestException(VUE *vue) { + int32_t level; /* Interrupt level */ + + /* Check for an interrupt */ + if (vue->cpu.irq != 0 && (vue->cpu.exception.code | + vue->cpu.psw_id | vue->cpu.psw_ep | vue->cpu.psw_np) == 0) { + for (level = 4; level >= 0; level--) + if ((vue->cpu.irq >> level & 1) != 0) + break; + vue->cpu.exception.code = 0xFE00 | level << 4; + } + + /* There is no exception */ + if (vue->cpu.exception.code == 0) /* No further processing */ + return vue->cpu.stage == CPU_HALT && vue->cpu.cycles == 0; + + /* An exception has occurred */ + vue->cpu.cycles = 0; + vue->cpu.stage = CPU_EXCEPTION; + return VUE_FALSE; +} + +/* Operations for execute stage */ +static vbool cpuExecute(VUE *vue) { + + /* Application callback */ + if (vue->onExecute != NULL) { + vue->breakCode = vue->onExecute(vue, &vue->cpu.inst); + if (vue->breakCode != 0) + return VUE_TRUE; + } + + /* Determine the default number of cycles for the instruction */ + if (vue->cpu.inst.id >= 0 && vue->cpu.inst.id <= 75) + vue->cpu.cycles = CYCLES[vue->cpu.inst.id]; + + /* Processing by instruction ID */ + switch (vue->cpu.inst.id) { + /*case VUE_ADD_IMM: cpuADD_IMM(vue); break;*/ + /*case VUE_ADD_REG: cpuADD_REG(vue); break;*/ + /*case VUE_ADDF_S : cpuADDF_S (vue); break;*/ + /*case VUE_ADDI : cpuADDI (vue); break;*/ + /*case VUE_AND : cpuAND (vue); break;*/ + /*case VUE_ANDBSU : cpuANDBSU (vue); break;*/ + /*case VUE_ANDI : cpuANDI (vue); break;*/ + /*case VUE_ANDNBSU: cpuANDNBSU(vue); break;*/ + /*case VUE_BCOND : cpuBCOND (vue); break;*/ + /*case VUE_CAXI : cpuCAXI (vue); break;*/ + /*case VUE_CLI : cpuCLI (vue); break;*/ + /*case VUE_CMP_IMM: cpuCMP_IMM(vue); break;*/ + /*case VUE_CMP_REG: cpuCMP_REG(vue); break;*/ + /*case VUE_CMPF_S : cpuCMPF_S (vue); break;*/ + /*case VUE_CVT_SW : cpuCVT_SW (vue); break;*/ + /*case VUE_CVT_WS : cpuCVT_WS (vue); break;*/ + /*case VUE_DIV : cpuDIV (vue); break;*/ + /*case VUE_DIVF_S : cpuDIVF_S (vue); break;*/ + /*case VUE_DIVU : cpuDIVU (vue); break;*/ + /*case VUE_HALT : cpuHALT (vue); break;*/ + /*case VUE_IN_B : cpuIN_B (vue); break;*/ + /*case VUE_IN_H : cpuIN_H (vue); break;*/ + /*case VUE_IN_W : cpuIN_W (vue); break;*/ + /*case VUE_JAL : cpuJAL (vue); break;*/ + case VUE_JMP : cpuJMP (vue); break; + /*case VUE.JR : cpuJR (vue); break;*/ + /*case VUE.LD_B : cpuLD_B (vue); break;*/ + /*case VUE.LD_H : cpuLD_H (vue); break;*/ + /*case VUE.LD_W : cpuLD_W (vue); break;*/ + /*case VUE.LDSR : cpuLDSR (vue); break;*/ + /*case VUE.MOV_IMM: cpuMOV_IMM(vue); break;*/ + /*case VUE.MOV_REG: cpuMOV_REG(vue); break;*/ + /*case VUE.MOVBSU : cpuMOVBSU (vue); break;*/ + case VUE_MOVEA : cpuMOVEA (vue); break; + case VUE_MOVHI : cpuMOVHI (vue); break; + /*case VUE.MPYHW : cpuMPYHW (vue); break;*/ + /*case VUE.MUL : cpuMUL (vue); break;*/ + /*case VUE.MULF_S : cpuMULF_S (vue); break;*/ + /*case VUE.MULU : cpuMULU (vue); break;*/ + /*case VUE.NOT : cpuNOT (vue); break;*/ + /*case VUE.NOTBSU : cpuNOTBSU (vue); break;*/ + /*case VUE.OR : cpuOR (vue); break;*/ + /*case VUE.ORBSU : cpuORBSU (vue); break;*/ + /*case VUE.ORI : cpuORI (vue); break;*/ + /*case VUE.ORNBSU : cpuORNBSU (vue); break;*/ + /*case VUE.OUT_B : cpuOUT_B (vue); break;*/ + /*case VUE.OUT_H : cpuOUT_H (vue); break;*/ + /*case VUE.OUT_W : cpuOUT_W (vue); break;*/ + /*case VUE.RETI : cpuRETI (vue); break;*/ + /*case VUE.REV : cpuREV (vue); break;*/ + /*case VUE.SAR_IMM: cpuSAR_IMM(vue); break;*/ + /*case VUE.SAR_REG: cpuSAR_REG(vue); break;*/ + /*case VUE.SCH0BSD: cpuSCH0BSD(vue); break;*/ + /*case VUE.SCH0BSU: cpuSCH0BSU(vue); break;*/ + /*case VUE.SCH1BSD: cpuSCH1BSD(vue); break;*/ + /*case VUE.SCH1BSU: cpuSCH1BSU(vue); break;*/ + /*case VUE.SEI : cpuSEI (vue); break;*/ + /*case VUE.SETF : cpuSETF (vue); break;*/ + /*case VUE.SHL_IMM: cpuSHL_IMM(vue); break;*/ + /*case VUE.SHL_REG: cpuSHL_REG(vue); break;*/ + /*case VUE.SHR_IMM: cpuSHR_IMM(vue); break;*/ + /*case VUE.SHR_REG: cpuSHR_REG(vue); break;*/ + /*case VUE.ST_B : cpuST_B (vue); break;*/ + /*case VUE.ST_H : cpuST_H (vue); break;*/ + /*case VUE.ST_W : cpuST_W (vue); break;*/ + /*case VUE.STSR : cpuSTSR (vue); break;*/ + /*case VUE.SUB : cpuSUB (vue); break;*/ + /*case VUE.SUBF_S : cpuSUBF_S (vue); break;*/ + /*case VUE.TRAP : cpuTRAP (vue); break;*/ + /*case VUE.TRNC_SW: cpuTRNC_SW(vue); break;*/ + /*case VUE.XB : cpuXB (vue); break;*/ + /*case VUE.XH : cpuXH (vue); break;*/ + /*case VUE.XOR : cpuXOR (vue); break;*/ + /*case VUE.XORBSU : cpuXORBSU (vue); break;*/ + /*case VUE.XORI : cpuXORI (vue); break;*/ + /*case VUE.XORNBSU: cpuXORNBSU(vue); break;*/ + default: /* Invalid instruction */ + vue->cpu.exception.code = 0xFF90; + vue->cpu.inst.size = 0; + } + + /* Common processing */ + vue->cpu.pc += vue->cpu.inst.size; + vue->cpu.program[0] = 0; + cpuTestException(vue); + if (vue->cpu.stage == CPU_EXECUTE) + vue->cpu.stage = CPU_FETCH; + return VUE_FALSE; +} + +/* Determine the size of an instruction given its opcode */ +static int32_t cpuSize(int32_t opcode) { + int32_t ret = LOOKUP_OPCODE[opcode << 1 | 1]; + return (ret < 0 ? -ret : ret) < 4 ? 2 : 4; +} + +/* Operations for fetch stage */ +static vbool cpuFetch(VUE *vue) { + + /* Read the bits from the bus */ + vue->cpu.access.address = vue->cpu.pc + (vue->cpu.fetch << 1); + vue->cpu.access.fetch = vue->cpu.fetch; + vue->cpu.access.type = VUE_U16; + vue->cpu.access.value = vueRead(vue, vue->cpu.access.address, VUE_U16); + + /* Application callback */ + if (vue->onRead != NULL) { + vue->breakCode = vue->onRead(vue, &vue->cpu.access); + if (vue->breakCode != 0) + return VUE_TRUE; + } + + /* First unit */ + if (vue->cpu.fetch == 0) { + vue->cpu.inst.bits = vue->cpu.access.value << 16; + if (cpuSize(vue->cpu.access.value >> 10 & 0x3F) == 4) { + vue->cpu.fetch = 1; + return VUE_FALSE; + } + } + + /* Second unit */ + else { + vue->cpu.inst.bits |= vue->cpu.access.value & 0xFFFF; + vue->cpu.fetch = 0; + } + + /* Decode the instruction and advance to execute stage */ + cpuDecode(&vue->cpu.inst); + vue->cpu.stage = CPU_EXECUTE; + return VUE_FALSE; +} + /* Read a system register */ static int32_t cpuGetSystemRegister(VUE *vue, int32_t index) { switch (index) { @@ -158,6 +361,97 @@ static int32_t cpuGetSystemRegister(VUE *vue, int32_t index) { return 1; /* Unreachable */ } +/* Operations for exception stage */ +static vbool cpuException(VUE *vue) { + vbool isIRQ; /* The exception is an interrupt */ + int32_t psw; /* Current value of PSW */ + + /* Application callback */ + if (vue->onException != NULL) { + vue->breakCode = vue->onException(vue, &vue->cpu.exception); + if (vue->breakCode != 0) + return VUE_TRUE; + } + + /* Configure working variables */ + vue->cpu.exception.code &= 0xFFFF; + isIRQ = (vue->cpu.exception.code & 0xFF00) == 0xFE00; + psw = cpuGetSystemRegister(vue, VUE_PSW); + + /* Fatal exception */ + if (vue->cpu.psw_np != 0) { + vueWrite(vue, 0x00000000, VUE_S32, 0xFFFF0000|vue->cpu.exception.code); + vueWrite(vue, 0x00000004, VUE_S32, psw); + vueWrite(vue, 0x00000008, VUE_S32, vue->cpu.pc); + vue->cpu.stage = CPU_FATAL; + return VUE_TRUE; + } + + /* Duplexed exception */ + if (vue->cpu.psw_ep != 0) { + vue->cpu.ecr_fecc = vue->cpu.exception.code; + vue->cpu.fepc = vue->cpu.pc; + vue->cpu.fepsw = psw; + vue->cpu.psw_np = 1; + vue->cpu.pc = 0xFFFFFFD0; + } + + /* Regular exception */ + else { + vue->cpu.ecr_eicc = vue->cpu.exception.code; + vue->cpu.eipc = vue->cpu.pc; + vue->cpu.eipsw = psw; + vue->cpu.psw_ep = 1; + vue->cpu.pc = 0xFFFF0000 | (vue->cpu.exception.code & 0xFFF0); + if (vue->cpu.pc == (int32_t) 0xFFFFFF70) /* FIV */ + vue->cpu.pc = 0xFFFFFF60; + } + + /* Interrupt */ + if (isIRQ) { + psw = vue->cpu.exception.code >> 4 & 15; + vue->cpu.psw_i = psw < 15 ? psw : 15; + } + + /* Common processing */ + vue->cpu.cycles = 0; /* TODO: Determine the actual number */ + vue->cpu.exception.code = 0; + vue->cpu.psw_ae = 0; + vue->cpu.psw_id = 1; + vue->cpu.stage = CPU_FETCH; + return VUE_FALSE; +} + +/* Process the simulation */ +static void cpuEmulate(VUE *vue, int32_t cycles) { + + /* The CPU is in permanent halt */ + if (vue->cpu.stage == CPU_FATAL) + return; + +vue->cpu.cycles = 0; /* DEBUG: Stop processing after execute */ + + /* Process for the given number of cycles */ + for (;;) { + + /* The next event occurs after the given number of cycles */ + if (vue->cpu.cycles > cycles) { + vue->cpu.cycles -= cycles; + return; + } + + /* Processing by stage */ + switch (vue->cpu.stage) { + case CPU_EXCEPTION: if (cpuException (vue)) return; break; + case CPU_EXECUTE : if (cpuExecute (vue)) return; break; + case CPU_FETCH : if (cpuFetch (vue)) return; break; + case CPU_HALT : if (cpuTestException(vue)) return; break; + } + + } + +} + /* Write a system register */ static int32_t cpuSetSystemRegister(VUE *vue, int32_t index, int32_t value, vbool debug) { @@ -230,9 +524,11 @@ static void cpuReset(VUE *vue) { int32_t x; /* Configure instance fields */ - vue->cpu.cycles = 0; /* Duration of first fetch */ - vue->cpu.fetch = 0; - vue->cpu.stage = CPU_FETCH; + vue->cpu.cycles = 0; + vue->cpu.exception.code = 0; + vue->cpu.fetch = 0; + vue->cpu.irq = 0; + vue->cpu.stage = CPU_FETCH; /* Clear all registers (hardware only sets ECR, PC and PSW) */ for (x = 0; x < 32; x++) { @@ -251,7 +547,7 @@ static void cpuReset(VUE *vue) { } /* Test a condition */ -static int8_t cpuTest(VUE *vue, int32_t condition) { +static int8_t cpuTestCondition(VUE *vue, int32_t condition) { switch (condition) { case 0: return vue->cpu.psw_ov; case 1: return vue->cpu.psw_cy; @@ -262,7 +558,17 @@ static int8_t cpuTest(VUE *vue, int32_t condition) { case 6: return vue->cpu.psw_ov | vue->cpu.psw_s; case 7: return (vue->cpu.psw_ov ^ vue->cpu.psw_s) | vue->cpu.psw_z; } - return cpuTest(vue, condition & 7) ^ 1; + return cpuTestCondition(vue, condition & 7) ^ 1; } +/* Determine the number of CPU cycles until something can happen */ +static int32_t cpuUntil(VUE *vue, int32_t cycles) { + if (vue->cpu.stage == CPU_FATAL || vue->cpu.stage == CPU_HALT) + return cycles; + return cycles < 0 ? vue->cpu.cycles : + cycles < vue->cpu.cycles ? cycles : vue->cpu.cycles; +} + + + #endif diff --git a/src/core/include/vue.h b/src/core/include/vue.h index 899b4cf..95b9149 100644 --- a/src/core/include/vue.h +++ b/src/core/include/vue.h @@ -139,9 +139,25 @@ extern "C" { * Types * *****************************************************************************/ +/* Forward references */ +typedef struct VUE VUE; + /* Boolean */ typedef int vbool; +/* Access state */ +typedef struct { + uint32_t address; /* CPU bus address */ + int32_t value; /* Value read/to write */ + int8_t fetch; /* Index of machine code unit */ + int8_t type; /* Data type */ +} VUE_ACCESS; + +/* Exception state */ +typedef struct { + uint16_t code; /* Exception code */ +} VUE_EXCEPTION; + /* Instruction state */ typedef struct { int32_t bits; /* Binary encoding */ @@ -155,21 +171,36 @@ typedef struct { uint8_t reg2; /* Destination/left register */ uint8_t size; /* Number of bytes in encoding */ uint8_t subopcode; /* Instruction subopcode */ -} VUE_INST; +} VUE_INSTRUCTION; + +/* Callbacks */ +typedef int32_t (*VUE_ONEXCEPTION)(VUE *, VUE_EXCEPTION *); +typedef int32_t (*VUE_ONEXECUTE )(VUE *, VUE_INSTRUCTION *); +typedef int32_t (*VUE_ONREAD )(VUE *, VUE_ACCESS *); +typedef int32_t (*VUE_ONWRITE )(VUE *, VUE_ACCESS *); /* Emulation state */ -typedef struct { +struct VUE { int32_t breakCode; /* Application break code */ uint8_t wram[0x10000]; /* System memory */ + /* Callback handlers */ + VUE_ONEXCEPTION onException; + VUE_ONEXECUTE onExecute; + VUE_ONREAD onRead; + VUE_ONWRITE onWrite; + /* CPU state */ struct { - VUE_INST inst; /* Instruction state */ - uint32_t cycles; /* Cycles until next stage */ - int32_t jumpFrom[3]; /* Source PCs of most recent jumps */ - int32_t jumpTo [3]; /* Destination PCs of most recent jumps */ - int fetch; /* Fetch unit index */ - int stage; /* Current processing stage */ + VUE_ACCESS access; /* Access state */ + VUE_EXCEPTION exception; /* Exception state */ + VUE_INSTRUCTION inst; /* Instruction state */ + int32_t cycles; /* Cycles until next stage */ + int32_t jumpFrom[3]; /* Source PCs of most recent jumps */ + int32_t jumpTo [3]; /* Destination PCs of most recent jumps */ + uint16_t irq; /* Interrupt lines */ + int fetch; /* Fetch unit index */ + int stage; /* Current processing stage */ /* Program registers */ int32_t program[32]; @@ -225,7 +256,7 @@ typedef struct { int8_t wcr_rom1w; /* ROM one-wait mode */ } pak; -} VUE; +}; @@ -240,8 +271,12 @@ VUEAPI void vueInitialize (VUE *vue); VUEAPI int32_t vueRead (VUE *vue, uint32_t address, int32_t type); VUEAPI vbool vueReadBytes (VUE *vue, uint32_t address, uint8_t *dest, uint32_t length); VUEAPI void vueReset (VUE *vue); +VUEAPI void vueSetException(VUE *vue, VUE_ONEXCEPTION callback); +VUEAPI void vueSetExecute (VUE *vue, VUE_ONEXECUTE callback); +VUEAPI void vueSetRead (VUE *vue, VUE_ONREAD callback); VUEAPI int32_t vueSetRegister (VUE *vue, int32_t index, vbool system, int32_t value); VUEAPI vbool vueSetROM (VUE *vue, uint8_t *rom, uint32_t size); +VUEAPI void vueSetWrite (VUE *vue, VUE_ONWRITE callback); VUEAPI void vueWrite (VUE *vue, uint32_t address, int32_t type, int32_t value); VUEAPI vbool vueWriteBytes (VUE *vue, uint32_t address, uint8_t *src, uint32_t length); diff --git a/src/core/vue.c b/src/core/vue.c index 5b98f32..ea8c85e 100644 --- a/src/core/vue.c +++ b/src/core/vue.c @@ -75,7 +75,7 @@ static int32_t readBuffer(uint8_t *data, uint32_t datlen, uint32_t address, } /* Sign-extend the value if appropriate */ - if (type & 1) { + if ((type & 1) == 0) { size <<= 3; value = SIGN_EXTEND(size, value); } @@ -167,23 +167,29 @@ int32_t vueEmulate(VUE *vue, int32_t maxCycles) { do { /* Determine the number of cycles during which nothing will happen */ - cycles = maxCycles; - /*cycles = cpuUntil (vue, cycles);*/ + cycles = -1; /*cycles = padUntil (vue, cycles);*/ /*cycles = linkUntil (vue, cycles);*/ /*cycles = timerUntil(vue, cycles);*/ /*cycles = vipUntil (vue, cycles);*/ /*cycles = vsuUntil (vue, cycles);*/ + cycles = cpuUntil (vue, cycles); + + /* Range checking */ + if (cycles == -1) /* No activity on any component */ + break; + if (maxCycles >= 0) /* Restrict to given number of cycles */ + cycles = cycles < maxCycles ? cycles : maxCycles; /* Process all system components */ vue->breakCode = 0; - /*gamePad.emulate(cycles);*/ - /*gamePak.emulate(cycles);*/ - /*link .emulate(cycles);*/ - /*timer .emulate(cycles);*/ - /*vip .emulate(cycles);*/ - /*vsu .emulate(cycles);*/ - /*cpu .emulate(cycles);*/ + /*padEmulate (vue, cycles);*/ + /*pakEmulate (vue, cycles);*/ + /*linkEmulate (vue, cycles);*/ + /*timerEmulate(vue, cycles);*/ + /*vipEmulate (vue, cycles);*/ + /*vsuEmulate (vue, cycles);*/ + cpuEmulate (vue, cycles); /* An application break was requested */ if (vue->breakCode != 0) @@ -231,12 +237,22 @@ int32_t vueGetRegister(VUE *vue, int32_t index, vbool system) { void vueInitialize(VUE *vue) { if (vue == NULL) return; - vue->pak.ram = NULL; - vue->pak.rom = NULL; + vue->onException = NULL; + vue->onExecute = NULL; + vue->onRead = NULL; + vue->onWrite = NULL; + vue->pak.ram = NULL; + vue->pak.rom = NULL; } /* Read a value from the CPU bus */ int32_t vueRead(VUE *vue, uint32_t address, int32_t type) { + + /* Error checking */ + if (vue == NULL) + return 0; + + /* Perform the operation */ switch (address >> 24 & 7) { case 5: return readBuffer(vue->wram , 0x10000,address,type); case 6: return readBuffer(vue->pak.ram,vue->pak.ramSize,address,type); @@ -292,6 +308,24 @@ void vueReset(VUE *vue) { vue->wram[x] = 0; } +/* Specify an exception breakpoint callback */ +void vueSetException(VUE *vue, VUE_ONEXCEPTION callback) { + if (vue != NULL) + vue->onException = callback; +} + +/* Specify an execute breakpoint callback */ +void vueSetExecute(VUE *vue, VUE_ONEXECUTE callback) { + if (vue != NULL) + vue->onExecute = callback; +} + +/* Specify a read breakpoint callback */ +void vueSetRead(VUE *vue, VUE_ONREAD callback) { + if (vue != NULL) + vue->onRead = callback; +} + /* Specify a value for a register */ int32_t vueSetRegister(VUE *vue, int32_t index, vbool system, int32_t value) { return vue == NULL ? 0 : @@ -319,8 +353,20 @@ vbool vueSetROM(VUE *vue, uint8_t *rom, uint32_t size) { return VUE_TRUE; } +/* Specify a write breakpoint callback */ +void vueSetWrite(VUE *vue, VUE_ONREAD callback) { + if (vue != NULL) + vue->onWrite = callback; +} + /* Write a value to the CPU bus */ void vueWrite(VUE *vue, uint32_t address, int32_t type, int32_t value) { + + /* Error checking */ + if (vue == NULL) + return; + + /* Perform the operation */ switch (address >> 24 & 7) { case 5: writeBuffer(vue->wram , 0x1000, address, type, value); break; @@ -329,6 +375,7 @@ void vueWrite(VUE *vue, uint32_t address, int32_t type, int32_t value) { case 7: writeBuffer(vue->pak.rom, vue->pak.romSize, address, type, value); break; } + } /* Write bytes to the CPU bus */ diff --git a/src/desktop/app/DisassemblerPane.java b/src/desktop/app/DisassemblerPane.java index 1159ed1..17cb4ec 100644 --- a/src/desktop/app/DisassemblerPane.java +++ b/src/desktop/app/DisassemblerPane.java @@ -115,12 +115,20 @@ class DisassemblerPane extends JScrollPane { return; } - // Seek + // Processing by key code switch (code) { case KeyEvent.VK_UP : seek(address, 1); break; case KeyEvent.VK_DOWN : seek(address, -1); break; case KeyEvent.VK_PAGE_UP : seek(address, count); break; case KeyEvent.VK_PAGE_DOWN: seek(address, -count); break; + + case KeyEvent.VK_F11: + parent.parent.vue.emulate(0); + parent.parent.refreshDebug(); + int pc = parent.parent.vue.getRegister(VUE.PC, true); + if (!isVisible(pc)) + seek(pc, count / 3); + break; } } diff --git a/src/desktop/vue/CPU.java b/src/desktop/vue/CPU.java index a162ee4..afc8696 100644 --- a/src/desktop/vue/CPU.java +++ b/src/desktop/vue/CPU.java @@ -12,7 +12,7 @@ class CPU { // Package fields Access access; // Access state int cycles; // Cycles until next stage - int exception; // Exception code + Ecxeption exception; // Exception code int fetch; // Fetch unit index Instruction inst; // Instruction state int irq; // Interrupt lines @@ -96,12 +96,13 @@ class CPU { // Default constructor CPU(JavaVUE vue) { - access = new Access(); - inst = new Instruction(); - jumpFrom = new int[3]; - jumpTo = new int[3]; - program = new int[32]; - this.vue = vue; + access = new Access(); + exception = new Ecxeption(); + inst = new Instruction(); + jumpFrom = new int[3]; + jumpTo = new int[3]; + program = new int[32]; + this.vue = vue; } @@ -117,8 +118,10 @@ class CPU { if (stage == FATAL) return; +this.cycles = 0; // DEBUG: Stop processing after execute + // Process for the given number of cycles - do { + for (;;) { // The next event occurs after the given number of cycles if (this.cycles > cycles) { @@ -128,13 +131,14 @@ class CPU { // Processing by stage switch (stage) { - case FETCH : if (fetch ()) return; - case EXECUTE : if (execute ()) return; - case HALT : testException(); break; - case EXCEPTION: if (exception()) return; + case EXCEPTION: if (exception ()) return; break; + case EXECUTE : if (execute ()) return; break; + case FETCH : if (fetch ()) return; break; + case HALT : if (testException()) return; break; } - } while (cycles > 0); + } + } // Read a system register @@ -172,11 +176,11 @@ class CPU { void reset() { // Configure instance fields - cycles = 0; // Duration of first fetch - exception = 0; - fetch = 0; - irq = 0; - stage = FETCH; + cycles = 0; + exception.code = 0; + fetch = 0; + irq = 0; + stage = FETCH; // Clear all registers (hardware only sets ECR, PC and PSW) for (int x = 0; x < 32; x++) { @@ -290,44 +294,52 @@ class CPU { return true; } - exception &= 0xFFFF; - boolean isIRQ = (exception & 0xFF00) == 0xFE00; - int psw = getSystemRegister(VUE.PSW); +System.out.printf("Exception %04X %d %d\n", exception.code, psw_ep, psw_np); + + // Configure working variables + exception.code &= 0xFFFF; + boolean isIRQ = (exception.code & 0xFF00) == 0xFE00; + int psw = getSystemRegister(VUE.PSW); // Fatal exception if (psw_np != 0) { - vue.write(0x00000000, VUE.S32, 0xFFFF0000 | exception); + vue.write(0x00000000, VUE.S32, 0xFFFF0000 | exception.code); vue.write(0x00000004, VUE.S32, psw); vue.write(0x00000008, VUE.S32, pc); + stage = FATAL; + return true; } // Duplexed exception if (psw_ep != 0) { - ecr_fecc = exception; + ecr_fecc = exception.code; fepc = pc; fepsw = psw; + psw_np = 1; pc = 0xFFFFFFD0; } // Regular exception else { - ecr_eicc = exception; + ecr_eicc = exception.code; eipc = pc; eipsw = psw; - pc = 0xFFFF0000 | exception & 0xFFF0; + psw_ep = 1; + pc = 0xFFFF0000 | exception.code & 0xFFF0; if (pc == 0xFFFFFF70) // FIV pc = 0xFFFFFF60; } // Interrupt if (isIRQ) - psw_i = Math.min(15, exception >> 4 & 15); + psw_i = Math.min(15, exception.code >> 4 & 15); // Common processing - exception = 0; - psw_ae = 0; - psw_id = 1; - stage = FETCH; + cycles = 0; // TODO: Determine the actual number + exception.code = 0; + psw_ae = 0; + psw_id = 1; + stage = FETCH; return false; } @@ -341,6 +353,8 @@ class CPU { return true; } +System.out.printf("Execute %08X %d\n", pc, inst.id); + // Determine the default number of cycles for the instruction if (inst.id >= 0 && inst.id <= 75) cycles = CYCLES[inst.id]; @@ -371,7 +385,7 @@ class CPU { //case VUE.IN_H : IN_H (); break; //case VUE.IN_W : IN_W (); break; //case VUE.JAL : JAL (); break; - //case VUE.JMP : JMP (); break; + case VUE.JMP : JMP (); break; //case VUE.JR : JR (); break; //case VUE.LD_B : LD_B (); break; //case VUE.LD_H : LD_H (); break; @@ -380,8 +394,8 @@ class CPU { //case VUE.MOV_IMM: MOV_IMM(); break; //case VUE.MOV_REG: MOV_REG(); break; //case VUE.MOVBSU : MOVBSU (); break; - //case VUE.MOVEA : MOVEA (); break; - //case VUE.MOVHI : MOVHI (); break; + case VUE.MOVEA : MOVEA (); break; + case VUE.MOVHI : MOVHI (); break; //case VUE.MPYHW : MPYHW (); break; //case VUE.MUL : MUL (); break; //case VUE.MULF_S : MULF_S (); break; @@ -423,13 +437,17 @@ class CPU { //case VUE.XORBSU : XORBSU (); break; //case VUE.XORI : XORI (); break; //case VUE.XORNBSU: XORNBSU(); break; - default: exception = 0xFF90; // Invalid instruction + default: // Invalid instruction + exception.code = 0xFF90; + inst.size = 0; } // Common processing pc += inst.size; program[0] = 0; testException(); + if (stage == EXECUTE) + stage = FETCH; return false; } @@ -442,6 +460,8 @@ class CPU { access.type = VUE.U16; access.value = vue.read(access.address, VUE.U16); +System.out.printf("Fetch %08X %04X\n", access.address, access.value); + // Application callback if (vue.onRead != null) { vue.breakCode = vue.onRead.call(vue, access); @@ -452,7 +472,7 @@ class CPU { // First unit if (fetch == 0) { inst.bits = access.value << 16; - if (Instruction.size(access.value & 0x3F) == 4) { + if (Instruction.size(access.value >> 10 & 0x3F) == 4) { fetch = 1; return false; } @@ -471,23 +491,46 @@ class CPU { } // Check for an exception or interrupt - private void testException() { + private boolean testException() { // Check for an interrupt - if (irq != 0 && (exception | psw_id | psw_ep | psw_np) == 0) { + if (irq != 0 && (exception.code | psw_id | psw_ep | psw_np) == 0) { int level; for (level = 4; level >= 0; level--) if ((irq >> level & 1) != 0) break; - exception = 0xFE00 | level << 4; + exception.code = 0xFE00 | level << 4; } - // Check for an exception - if (exception != 0) { - cycles = 0; - stage = EXCEPTION; - } + // There is no exception + if (exception.code == 0) + return stage == HALT && cycles == 0; // No further processing + // An exception has occurred + cycles = 0; + stage = EXCEPTION; + return false; + } + + + + /////////////////////////////////////////////////////////////////////////// + // Instruction Methods // + /////////////////////////////////////////////////////////////////////////// + + // JMP + private void JMP() { + pc = program[inst.reg1] - 2; + } + + // MOVEA + private void MOVEA() { + program[inst.reg2] = program[inst.reg1] + inst.imm; + } + + // MOVHI + private void MOVHI() { + program[inst.reg2] = program[inst.reg1] + (inst.imm << 16); } } diff --git a/src/desktop/vue/Ecxeption.java b/src/desktop/vue/Ecxeption.java new file mode 100644 index 0000000..76fb7d1 --- /dev/null +++ b/src/desktop/vue/Ecxeption.java @@ -0,0 +1,20 @@ +package vue; + +// Intentionally misspelled to avoid ambiguity with java.lang.Exception + +// Exception state +public class Ecxeption { + + // Instance fields + public int code; // Exception code + + + + /////////////////////////////////////////////////////////////////////////// + // Constructors // + /////////////////////////////////////////////////////////////////////////// + + // Default constructor + Ecxeption() { } + +} diff --git a/src/desktop/vue/JavaVUE.java b/src/desktop/vue/JavaVUE.java index f112a66..7f59c30 100644 --- a/src/desktop/vue/JavaVUE.java +++ b/src/desktop/vue/JavaVUE.java @@ -178,6 +178,21 @@ class JavaVUE extends VUE { Arrays.fill(wram, 0, 0x10000, (byte) 0); } + // Specify an exception breakpoint callback + public void setException(OnException callback) { + onException = callback; + } + + // Specify an execute breakpoint callback + public void setExecute(OnExecute callback) { + onExecute = callback; + } + + // Specify a read breakpoint callback + public void setRead(OnRead callback) { + onRead = callback; + } + // Specify a register value public int setRegister(int index, boolean system, int value) { return @@ -206,6 +221,11 @@ class JavaVUE extends VUE { return true; } + // Specify a write breakpoint callback + public void setWrite(OnWrite callback) { + onWrite = callback; + } + // Write a value to the CPU bus public void write(int address, int type, int value) { switch (address >> 24 & 7) { @@ -273,7 +293,7 @@ class JavaVUE extends VUE { } // Sign-extend the value if appropriate - if ((type & 1) == 1) { + if ((type & 1) == 0) { size = 32 - (size << 3); value = value << size >> size; } diff --git a/src/desktop/vue/NativeVUE.c b/src/desktop/vue/NativeVUE.c index fa0e732..16a9ff5 100644 --- a/src/desktop/vue/NativeVUE.c +++ b/src/desktop/vue/NativeVUE.c @@ -90,7 +90,7 @@ JNIEXPORT jint JNICALL Java_vue_NativeVUE_emulate } // Retrieve the application break code -JNIEXPORT jint JNICALL Java_vue_NativeVUE_getRegister +JNIEXPORT jint JNICALL Java_vue_NativeVUE_getBreakCode (JNIEnv *env, jobject vue) { CORE *core = GetCore(env, vue); return vueGetBreakCode(&core->vue); @@ -161,6 +161,24 @@ JNIEXPORT void JNICALL Java_vue_NativeVUE_reset vueReset(&core->vue); } +// Specify an exception breakpoint callback +JNIEXPORT void JNICALL Java_vue_NativeVUE_setException + (JNIEnv *env, jobject vue, jobject onException) { + CORE *core = GetCore(env, vue); +} + +// Specify an execute breakpoint callback +JNIEXPORT void JNICALL Java_vue_NativeVUE_setExecute + (JNIEnv *env, jobject vue, jobject onExecute) { + CORE *core = GetCore(env, vue); +} + +// Specify a read breakpoint callback +JNIEXPORT void JNICALL Java_vue_NativeVUE_setRead + (JNIEnv *env, jobject vue, jobject onRead) { + CORE *core = GetCore(env, vue); +} + // Specify a register value JNIEXPORT jint JNICALL Java_vue_NativeVUE_setRegister (JNIEnv *env, jobject vue, jint index, jboolean system, jint value) { @@ -195,6 +213,12 @@ JNIEXPORT jboolean JNICALL Java_vue_NativeVUE_setROM return JNI_TRUE; } +// Specify a write breakpoint callback +JNIEXPORT void JNICALL Java_vue_NativeVUE_setWrite + (JNIEnv *env, jobject vue, jobject onWrite) { + CORE *core = GetCore(env, vue); +} + // Write a value to the CPU bus JNIEXPORT void JNICALL Java_vue_NativeVUE_write (JNIEnv *env, jobject vue, jint address, jint type, jint value) { diff --git a/src/desktop/vue/NativeVUE.java b/src/desktop/vue/NativeVUE.java index 37cbb08..61fea85 100644 --- a/src/desktop/vue/NativeVUE.java +++ b/src/desktop/vue/NativeVUE.java @@ -51,12 +51,24 @@ class NativeVUE extends VUE { // Initialize all system components public native void reset(); + // Specify an exception breakpoint callback + public native void setException(OnException callback); + + // Specify an execute breakpoint callback + public native void setExecute(OnExecute callback); + + // Specify a read breakpoint callback + public native void setRead(OnRead callback); + // Specify a register value public native int setRegister(int index, boolean system, int value); // Provide new ROM data public native boolean setROM(byte[] data, int offset, int length); + // Specify a write breakpoint callback + public native void setWrite(OnWrite callback); + // Write a value to the CPU bus public native void write(int address, int type, int value); diff --git a/src/desktop/vue/VUE.java b/src/desktop/vue/VUE.java index 0213457..61be8df 100644 --- a/src/desktop/vue/VUE.java +++ b/src/desktop/vue/VUE.java @@ -12,7 +12,7 @@ public abstract class VUE { // Types // /////////////////////////////////////////////////////////////////////////// - public interface OnException { int call(VUE vue, int code ); } + public interface OnException { int call(VUE vue, Ecxeption exp ); } public interface OnExecute { int call(VUE vue, Instruction inst ); } public interface OnRead { int call(VUE vue, Access access); } public interface OnWrite { int call(VUE vue, Access access); } @@ -24,11 +24,12 @@ public abstract class VUE { /////////////////////////////////////////////////////////////////////////// // Memory access types - public static final int S8 = 0; - public static final int U8 = 1; - public static final int S16 = 2; - public static final int U16 = 3; - public static final int S32 = 4; + public static final int S8 = 0; + public static final int U8 = 1; + public static final int S16 = 2; + public static final int U16 = 3; + public static final int S32 = 4; + public static final int CANCEL = 5; // System register indexes public static final int ADTRE = 25; @@ -194,12 +195,24 @@ public abstract class VUE { // Initialize all system components public abstract void reset(); + // Specify an exception breakpoint callback + public abstract void setException(OnException callback); + + // Specify an execute breakpoint callback + public abstract void setExecute(OnExecute callback); + + // Specify a read breakpoint callback + public abstract void setRead(OnRead callback); + // Specify a register value public abstract int setRegister(int index, boolean system, int value); // Provide new ROM data public abstract boolean setROM(byte[] data, int offset, int length); + // Specify a write breakpoint callback + public abstract void setWrite(OnWrite callback); + // Write a value to the CPU bus public abstract void write(int address, int type, int value);