private void InvokeTraceCallback(IDbConnection connection, string queryClass, string cmd, object parameters, Stopwatch sw) { IEnumerable <QueryTrace.Parameter> traceParams = parameters?.GetType() .GetProperties() .Where(pi => ((pi.GetIndexParameters()?.Length ?? 0) == 0)) // exclude indexer properties .Select(pi => new QueryTrace.Parameter(pi, parameters)); TraceCallback?.Invoke(connection, new QueryTrace(queryClass, UserName, cmd, traceParams, sw.ElapsedMilliseconds, null)); }
private void Dispatch(string req) { var request = JsonConvert.DeserializeObject <DispatcherRequest>(req); if (request != null && request.type == "request") { TraceCallback?.Invoke(string.Format("C {0}: {1}", request.command, JsonConvert.SerializeObject(request.arguments))); if (_callback != null) { try { lock (_lock) { _isDispatchingData = true; } var response = new DispatcherResponse(request.seq, request.command); var responder = new DispatchResponder(this, response); _callback(request.command, request.arguments, responder); SendMessage(response); } finally { lock (_lock) { _isDispatchingData = false; } DispatcherEvent e; while (_queuedEvent.TryDequeue(out e)) { SendMessage(e); } } } } }
// Execute instructions public void ExecuteOne() { switch (instr_table[instr_pntr++]) { case IDLE: // do nothing break; case OP: // Read the opcode of the next instruction if (EI_pending > 0 && !CB_prefix) { EI_pending--; if (EI_pending == 0) { interrupts_enabled = true; } } if (I_use && interrupts_enabled && !CB_prefix && !jammed) { interrupts_enabled = false; TraceCallback?.Invoke(new TraceInfo { Disassembly = "====IRQ====", RegisterInfo = "" }); // call interrupt processor // lowest bit set is highest priority instr_pntr = 256 * 60 * 2 + 60 * 6; // point to Interrupt } else { OnExecFetch?.Invoke(RegPC); if (TraceCallback != null && !CB_prefix) { TraceCallback(State()); } CDLCallback?.Invoke(RegPC, eCDLogMemFlags.FetchFirst); FetchInstruction(ReadMemory(RegPC++)); } I_use = false; break; case RD: Read_Func(instr_table[instr_pntr], instr_table[instr_pntr + 1], instr_table[instr_pntr + 2]); instr_pntr += 3; break; case WR: Write_Func(instr_table[instr_pntr], instr_table[instr_pntr + 1], instr_table[instr_pntr + 2]); instr_pntr += 3; break; case TR: TR_Func(instr_table[instr_pntr], instr_table[instr_pntr + 1]); instr_pntr += 2; break; case ADD16: ADD16_Func(instr_table[instr_pntr], instr_table[instr_pntr + 1], instr_table[instr_pntr + 2], instr_table[instr_pntr + 3]); instr_pntr += 4; break; case ADD8: ADD8_Func(instr_table[instr_pntr], instr_table[instr_pntr + 1]); instr_pntr += 2; break; case SUB8: SUB8_Func(instr_table[instr_pntr], instr_table[instr_pntr + 1]); instr_pntr += 2; break; case ADC8: ADC8_Func(instr_table[instr_pntr], instr_table[instr_pntr + 1]); instr_pntr += 2; break; case SBC8: SBC8_Func(instr_table[instr_pntr], instr_table[instr_pntr + 1]); instr_pntr += 2; break; case INC16: INC16_Func(instr_table[instr_pntr], instr_table[instr_pntr + 1]); instr_pntr += 2; break; case INC8: INC8_Func(instr_table[instr_pntr++]); break; case DEC16: DEC16_Func(instr_table[instr_pntr], instr_table[instr_pntr + 1]); instr_pntr += 2; break; case DEC8: DEC8_Func(instr_table[instr_pntr++]); break; case RLC: RLC_Func(instr_table[instr_pntr++]); break; case RL: RL_Func(instr_table[instr_pntr++]); break; case RRC: RRC_Func(instr_table[instr_pntr++]); break; case RR: RR_Func(instr_table[instr_pntr++]); break; case CPL: CPL_Func(instr_table[instr_pntr++]); break; case DA: DA_Func(instr_table[instr_pntr++]); break; case SCF: SCF_Func(instr_table[instr_pntr++]); break; case CCF: CCF_Func(instr_table[instr_pntr++]); break; case AND8: AND8_Func(instr_table[instr_pntr], instr_table[instr_pntr + 1]); instr_pntr += 2; break; case XOR8: XOR8_Func(instr_table[instr_pntr], instr_table[instr_pntr + 1]); instr_pntr += 2; break; case OR8: OR8_Func(instr_table[instr_pntr], instr_table[instr_pntr + 1]); instr_pntr += 2; break; case CP8: CP8_Func(instr_table[instr_pntr], instr_table[instr_pntr + 1]); instr_pntr += 2; break; case SLA: SLA_Func(instr_table[instr_pntr++]); break; case SRA: SRA_Func(instr_table[instr_pntr++]); break; case SRL: SRL_Func(instr_table[instr_pntr++]); break; case SWAP: SWAP_Func(instr_table[instr_pntr++]); break; case BIT: BIT_Func(instr_table[instr_pntr], instr_table[instr_pntr + 1]); instr_pntr += 2; break; case RES: RES_Func(instr_table[instr_pntr], instr_table[instr_pntr + 1]); instr_pntr += 2; break; case SET: SET_Func(instr_table[instr_pntr], instr_table[instr_pntr + 1]); instr_pntr += 2; break; case EI: if (EI_pending == 0) { EI_pending = 2; } break; case DI: interrupts_enabled = false; EI_pending = 0; break; case HALT: halted = true; bool temp = false; if (instr_table[instr_pntr++] == 1) { temp = FlagI; } else { temp = I_use; } if (EI_pending > 0 && !CB_prefix) { EI_pending--; if (EI_pending == 0) { interrupts_enabled = true; } } // if the I flag is asserted at the time of halt, don't halt if (Halt_bug_5) { Halt_bug_5 = Halt_bug_3 = halted = skip_once = false; if (interrupts_enabled) { interrupts_enabled = false; TraceCallback?.Invoke(new TraceInfo { Disassembly = "====IRQ====", RegisterInfo = "" }); RegPC--; // TODO: If interrupt priotrity is checked differently in GBC, then this is incorrect // a new interrupt vector would be needed instr_pntr = 256 * 60 * 2 + 60 * 6; // point to Interrupt } else { TraceCallback?.Invoke(new TraceInfo { Disassembly = "====un-halted====", RegisterInfo = "" }); OnExecFetch?.Invoke(RegPC); if (TraceCallback != null && !CB_prefix) { TraceCallback(State()); } CDLCallback?.Invoke(RegPC, eCDLogMemFlags.FetchFirst); FetchInstruction(ReadMemory(RegPC)); } } else if (temp && interrupts_enabled) { interrupts_enabled = false; TraceCallback?.Invoke(new TraceInfo { Disassembly = "====IRQ====", RegisterInfo = "" }); halted = false; if (Halt_bug_4) { // TODO: If interrupt priotrity is checked differently in GBC, then this is incorrect // a new interrupt vector would be needed DEC16_Func(PCl, PCh); instr_pntr = 256 * 60 * 2 + 60 * 6; // point to Interrupt Halt_bug_4 = false; skip_once = false; Halt_bug_3 = false; } else if (is_GBC) { // call the interrupt processor after 4 extra cycles if (!Halt_bug_3) { instr_pntr = 256 * 60 * 2 + 60 * 7; // point to Interrupt for GBC } else { // TODO: If interrupt priotrity is checked differently in GBC, then this is incorrect // a new interrupt vector would be needed instr_pntr = 256 * 60 * 2 + 60 * 6; // point to Interrupt Halt_bug_3 = false; //Console.WriteLine("Hit INT"); } } else { // call interrupt processor instr_pntr = 256 * 60 * 2 + 60 * 6; // point to Interrupt Halt_bug_3 = false; } } else if (temp) { // even if interrupt servicing is disabled, any interrupt flag raised still resumes execution TraceCallback?.Invoke(new TraceInfo { Disassembly = "====un-halted====", RegisterInfo = "" }); halted = false; if (is_GBC) { // extra 4 cycles for GBC if (Halt_bug_3) { OnExecFetch?.Invoke(RegPC); if (TraceCallback != null && !CB_prefix) { TraceCallback(State()); } CDLCallback?.Invoke(RegPC, eCDLogMemFlags.FetchFirst); RegPC++; FetchInstruction(ReadMemory(RegPC)); Halt_bug_3 = false; //Console.WriteLine("Hit un"); } else { instr_pntr = 256 * 60 * 2 + 60; // exit halt loop } } else { OnExecFetch?.Invoke(RegPC); if (TraceCallback != null && !CB_prefix) { TraceCallback(State()); } CDLCallback?.Invoke(RegPC, eCDLogMemFlags.FetchFirst); if (Halt_bug_3) { //special variant of halt bug where RegPC also isn't incremented post fetch RegPC++; FetchInstruction(ReadMemory(RegPC)); Halt_bug_3 = false; } else { FetchInstruction(ReadMemory(RegPC++)); } } } else { if (skip_once) { instr_pntr = 256 * 60 * 2 + 60 * 2; // point to skipped loop skip_once = false; } else { if (is_GBC) { instr_pntr = 256 * 60 * 2 + 60 * 3; // point to GBC Halt loop } else { instr_pntr = 256 * 60 * 2 + 60 * 4; // point to spec Halt loop } } } I_use = false; break; case STOP: stopped = true; if (!stop_check) { // Z contains the second stop byte, not sure if it's useful at all stop_time = SpeedFunc(0); stop_check = true; } interrupt_src_reg = GetIntRegs(0); if (stop_time > 0) { // Timer interrupts can prematurely terminate a speedchange, nt sure about other sources // NOTE: some testing around the edge case of where the speed actually changes is needed if (I_use && interrupts_enabled) { interrupts_enabled = false; I_use = false; TraceCallback?.Invoke(new TraceInfo { Disassembly = "====un-stop====", RegisterInfo = "" }); stopped = false; stop_check = false; stop_time = 0; TraceCallback?.Invoke(new TraceInfo { Disassembly = "====IRQ====", RegisterInfo = "" }); // call interrupt processor // lowest bit set is highest priority instr_pntr = 256 * 60 * 2 + 60 * 6; // point to Interrupt break; } if (stop_time == (32769)) { SpeedFunc(1); } stop_time--; if (stop_time == 0) { TraceCallback?.Invoke(new TraceInfo { Disassembly = "====un-stop====", RegisterInfo = "" }); stopped = false; // it takes the CPU 4 cycles longer to restart then the rest of the system. instr_pntr = 256 * 60 * 2 + 60; stop_check = false; break; } // If a button is pressed during speed change, the processor will jam if (interrupt_src_reg.Bit(4)) { stop_time++; break; } } // Button press will exit stop loop even if speed change in progress, even without interrupts enabled if (interrupt_src_reg.Bit(4)) { // TODO: On a gameboy, you can only un-STOP once, needs further testing TraceCallback?.Invoke(new TraceInfo { Disassembly = "====un-stop====", RegisterInfo = "" }); stopped = false; OnExecFetch?.Invoke(RegPC); if (TraceCallback != null && !CB_prefix) { TraceCallback(State()); } CDLCallback?.Invoke(RegPC, eCDLogMemFlags.FetchFirst); FetchInstruction(ReadMemory(RegPC++)); stop_check = false; } else { instr_pntr = 256 * 60 * 2 + 60 * 5; // point to stop loop } break; case PREFIX: CB_prefix = true; break; case ASGN: ASGN_Func(instr_table[instr_pntr], instr_table[instr_pntr + 1]); instr_pntr += 2; break; case ADDS: ADDS_Func(instr_table[instr_pntr], instr_table[instr_pntr + 1], instr_table[instr_pntr + 2], instr_table[instr_pntr + 3]); instr_pntr += 4; break; case OP_G: OnExecFetch?.Invoke(RegPC); TraceCallback?.Invoke(State()); CDLCallback?.Invoke(RegPC, eCDLogMemFlags.FetchFirst); FetchInstruction(ReadMemory(RegPC)); // note no increment break; case JAM: jammed = true; instr_pntr--; break; case RD_F: Read_Func_F(instr_table[instr_pntr], instr_table[instr_pntr + 1], instr_table[instr_pntr + 2]); instr_pntr += 3; break; case EI_RETI: EI_pending = 1; break; case INT_GET: // check if any interrupts got cancelled along the way // interrupt src = 5 sets the PC to zero as observed // also the triggering interrupt seems like it is held low (i.e. cannot trigger I flag) until the interrupt is serviced ushort bit_check = instr_table[instr_pntr++]; //Console.WriteLine(interrupt_src + " " + interrupt_enable + " " + TotalExecutedCycles); interrupt_src_reg = GetIntRegs(0); interrupt_enable_reg = GetIntRegs(1); if (interrupt_src_reg.Bit(bit_check) && interrupt_enable_reg.Bit(bit_check)) { int_src = bit_check; int_clear = (byte)(1 << bit_check); } /* * if (interrupt_src.Bit(0) && interrupt_enable.Bit(0)) { int_src = 0; int_clear = 1; } * else if (interrupt_src.Bit(1) && interrupt_enable.Bit(1)) { int_src = 1; int_clear = 2; } * else if (interrupt_src.Bit(2) && interrupt_enable.Bit(2)) { int_src = 2; int_clear = 4; } * else if (interrupt_src.Bit(3) && interrupt_enable.Bit(3)) { int_src = 3; int_clear = 8; } * else if (interrupt_src.Bit(4) && interrupt_enable.Bit(4)) { int_src = 4; int_clear = 16; } * else { int_src = 5; int_clear = 0; } */ Regs[instr_table[instr_pntr++]] = INT_vectors[int_src]; break; case HALT_CHK: I_use = FlagI; if (Halt_bug_2 && I_use) { RegPC--; Halt_bug_3 = true; //Console.WriteLine("Halt_bug_3"); //Console.WriteLine(TotalExecutedCycles); } Halt_bug_2 = false; break; case HALT_CHK_2: if (FlagI && !I_use) { Halt_bug_5 = true; } break; case IRQ_CLEAR: interrupt_src_reg = GetIntRegs(0); interrupt_enable_reg = GetIntRegs(1); if (interrupt_src_reg.Bit(int_src)) { interrupt_src_reg -= int_clear; } SetIntRegs(interrupt_src_reg); if ((interrupt_src_reg & interrupt_enable_reg) == 0) { FlagI = false; } // reset back to default state int_src = 5; int_clear = 0; break; case COND_CHECK: checker = false; switch (instr_table[instr_pntr++]) { case ALWAYS_T: checker = true; break; case ALWAYS_F: checker = false; break; case FLAG_Z: checker = FlagZ; break; case FLAG_NZ: checker = !FlagZ; break; case FLAG_C: checker = FlagC; break; case FLAG_NC: checker = !FlagC; break; } // true condition is what is represented in the instruction vectors // jump ahead if false if (checker) { instr_pntr++; } else { // 0 = JR COND, 1 = JP COND, 2 = RET COND, 3 = CALL switch (instr_table[instr_pntr++]) { case 0: instr_pntr += 10; break; case 1: instr_pntr += 8; break; case 2: instr_pntr += 22; break; case 3: instr_pntr += 26; break; } } break; case HALT_FUNC: if (was_FlagI && (EI_pending == 0) && !interrupts_enabled) { // in GBC mode, the HALT bug is worked around by simply adding a NOP // so it just takes 4 cycles longer to reach the next instruction // otherwise, if interrupts are disabled, // a glitchy decrement to the program counter happens // either way, nothing needs to be done here } else { instr_pntr += 3; if (!is_GBC) { skip_once = true; } // If the interrupt flag is not currently set, but it does get set in the first check // then a bug is triggered // With interrupts enabled, this runs the halt command twice // when they are disabled, it reads the next byte twice if (!was_FlagI || (was_FlagI && !interrupts_enabled)) { Halt_bug_2 = true; } // If the I flag was set right before hitting this point, then there is no extra cycle for the halt // also there is a glitched increment to the program counter if (was_FlagI && interrupts_enabled) { Halt_bug_4 = true; } } break; } TotalExecutedCycles++; }
/// <summary> /// Runs a single CPU clock cycle /// </summary> public void ExecuteOne() { switch (cur_instr[instr_pntr++]) { // always the last tick within an opcode instruction cycle case END: OnExecFetch?.Invoke(RegPC0); TraceCallback?.Invoke(State()); opcode = (byte)Regs[DB]; instr_pntr = 0; FetchInstruction(); break; // used as timing 'padding' case IDLE: break; // load one register into another (or databus) case OP_LR8: LR_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; // load DB into A (as a part of an IN or INS instruction) case OP_LR_A_DB_IO: LR_A_IO_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; // loads supplied index value into register case OP_LIS: Regs[ALU1] = (byte)(cur_instr[instr_pntr++] & 0x0F); LR_Func(A, ALU1); break; // Shift register n bit positions to the right (zero fill) case OP_SHFT_R: SR_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; // Shift register n bit positions to the left (zero fill) case OP_SHFT_L: SL_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; // x <- (x) ADD y case OP_ADD8: ADD_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; // x <- (x) MINUS y case OP_SUB8: SUB_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; // x <- (x) ADD y (decimal) case OP_ADD8D: ADDD_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; // A <- (A) + (C) case OP_LNK: Regs[ALU0] = (byte)(FlagC ? 1 : 0); ADD_Func(A, ALU0); break; // Clear ICB status bit case OP_DI: FlagICB = false; break; // Set ICB status bit case OP_EI: FlagICB = true; break; // x <- (y) XOR DB case OP_XOR8: XOR_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; // x <- (x) + 1 case OP_INC8: ADD_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; // x <- (y) & DB case OP_AND8: AND_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; // x <- (y) | DB case OP_OR8: OR_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; // DB + (x) + 1 (modify flags without saving result) case OP_CI: CI_Func(); break; // ISAR is incremented case OP_IS_INC: Regs[ISAR] = (byte)((Regs[ISAR] & 0x38) | ((Regs[ISAR] + 1) & 0x07)); break; // ISAR is decremented case OP_IS_DEC: Regs[ISAR] = (byte)((Regs[ISAR] & 0x38) | ((Regs[ISAR] - 1) & 0x07)); break; // set the upper octal ISAR bits (b3,b4,b5) case OP_LISU: Regs[ISAR] = (byte)((((Regs[ISAR] & 0x07) | (cur_instr[instr_pntr++] & 0x07) << 3)) & 0x3F); break; // set the lower octal ISAR bits (b0,b1,b2) case OP_LISL: Regs[ISAR] = (byte)(((Regs[ISAR] & 0x38) | (cur_instr[instr_pntr++] & 0x07)) & 0x3F); break; // decrement scratchpad byte //case OP_DS: //SUB_Func(cur_instr[instr_pntr++], ONE); //break; // Branch on TRUE case OP_BT: bool branchBT = false; switch (cur_instr[instr_pntr++]) { case 0: // do not branch break; case 1: // branch if positive (sign bit is set) if (FlagS) { branchBT = true; } break; case 2: // branch on carry (carry bit is set) if (FlagC) { branchBT = true; } break; case 3: // branch if positive or on carry if (FlagS || FlagC) { branchBT = true; } break; case 4: // branch if zero (zero bit is set) if (FlagZ) { branchBT = true; } break; case 5: // branch if positive and zero if (FlagS || FlagZ) { branchBT = true; } break; case 6: // branch if zero or on carry if (FlagZ || FlagC) { branchBT = true; } break; case 7: // branch if positive or on carry or zero if (FlagS || FlagC || FlagZ) { branchBT = true; } break; } instr_pntr = 0; if (branchBT) { DO_BRANCH(); } else { DONT_BRANCH(); } break; // Branch on ISARL case OP_BR7: instr_pntr = 1; // lose a cycle if (Regs[ISAR].Bit(0) && Regs[ISAR].Bit(1) && Regs[ISAR].Bit(2)) { DONT_BRANCH(); } else { DO_BRANCH(); } break; // Branch on FALSE case OP_BF: bool branchBF = false; switch (cur_instr[instr_pntr++]) { case 0: // unconditional branch relative branchBF = true; break; case 1: // branch on negative (sign bit is reset) if (!FlagS) { branchBF = true; } break; case 2: // branch if no carry (carry bit is reset) if (!FlagC) { branchBF = true; } break; case 3: // branch if no carry and negative if (!FlagC && !FlagS) { branchBF = true; } break; case 4: // branch if not zero (zero bit is reset) if (!FlagZ) { branchBF = true; } break; case 5: // branch if not zero and negative if (!FlagS && !FlagZ) { branchBF = true; } break; case 6: // branch if no carry and result is no zero if (!FlagC && !FlagZ) { branchBF = true; } break; case 7: // branch if not zero, carry and sign if (!FlagS && !FlagC && !FlagZ) { branchBF = true; } break; case 8: // branch if there is no overflow (OVF bit is reset) if (!FlagO) { branchBF = true; } break; case 9: // branch if negative and no overflow if (!FlagS && !FlagO) { branchBF = true; } break; case 0xA: // branch if no overflow and no carry if (!FlagO && !FlagC) { branchBF = true; } break; case 0xB: // branch if no overflow, no carry & negative if (!FlagO && !FlagC && !FlagS) { branchBF = true; } break; case 0xC: // branch if no overflow and not zero if (!FlagO && !FlagZ) { branchBF = true; } break; case 0xD: // branch if no overflow, not zero and neg if (!FlagS && !FlagO && !FlagZ) { branchBF = true; } break; case 0xE: // branch if no overflow, no carry & not zero if (!FlagO && !FlagC && !FlagZ) { branchBF = true; } break; case 0xF: // all neg if (!FlagO && !FlagC && !FlagS && FlagZ) { branchBF = true; } break; } instr_pntr = 0; if (branchBF) { DO_BRANCH(); } else { DONT_BRANCH(); } break; // A <- (I/O Port 0 or 1) case OP_IN: instr_pntr++; // dest == A Regs[ALU0] = cur_instr[instr_pntr++]; // src IN_Func(A, ALU0); break; // I/O Port 0 or 1 <- (A) case OP_OUT: WriteHardware(cur_instr[instr_pntr++], (byte)Regs[cur_instr[instr_pntr++]]); break; // instruction fetch // The device whose address space includes the contents of the PC0 register must place on the data bus the op code addressed by PC0; // then all devices increments the content of PC0. // CYCLE LENGTH: S case ROMC_00_S: Read_Func(DB, PC0l, PC0h); RegPC0++; break; // instruction fetch // The device whose address space includes the contents of the PC0 register must place on the data bus the op code addressed by PC0; // then all devices increments the content of PC0. // CYCLE LENGTH: L case ROMC_00_L: Read_Func(DB, PC0l, PC0h); RegPC0++; break; // The device whose address space includes the contents of the PC0 register must place on the data bus the contents of the memory location // addressed by by PC0; then all devices add the 8-bit value on the data bus, as a signed binary number, to PC0 // CYCLE LENGTH: L case ROMC_01: Read_Func(DB, PC0l, PC0h); RegPC0 += (ushort)((SByte)Regs[DB]); break; // The device whose DC0 address addresses a memory word within the address space of that device must place on the data bus the contents // of the memory location addressed by DC0; then all devices increment DC0 // CYCLE LENGTH: L case ROMC_02: Read_Func(DB, DC0l, DC0h); RegDC0++; break; // Similar to 0x00, except that it is used for Immediate Operand fetches (using PC0) instead of instruction fetches // CYCLE LENGTH: S case ROMC_03_S: Read_Func(DB, PC0l, PC0h); RegPC0++; Regs[IO] = Regs[DB]; break; // Similar to 0x00, except that it is used for Immediate Operand fetches (using PC0) instead of instruction fetches // CYCLE LENGTH: L case ROMC_03_L: Read_Func(DB, PC0l, PC0h); RegPC0++; Regs[IO] = Regs[DB]; break; // Copy the contents of PC1 into PC0 // CYCLE LENGTH: S case ROMC_04: RegPC0 = RegPC1; break; // Store the data bus contents into the memory location pointed to by DC0; increment DC0 // CYCLE LENGTH: L case ROMC_05: Write_Func(DC0l, DC0h, DB); break; // Place the high order byte of DC0 on the data bus // CYCLE LENGTH: L case ROMC_06: Regs[DB] = (byte)Regs[DC0h]; break; // Place the high order byte of PC1 on the data bus // CYCLE LENGTH: L case ROMC_07: Regs[DB] = (byte)Regs[PC1h]; break; // All devices copy the contents of PC0 into PC1. The CPU outputs zero on the data bus in this ROMC state. // Load the data bus into both halves of PC0, this clearing the register. // CYCLE LENGTH: L case ROMC_08: RegPC1 = RegPC0; Regs[DB] = 0; Regs[PC0h] = 0; Regs[PC0l] = 0; break; // The device whose address space includes the contents of the DC0 register must place the low order byte of DC0 onto the data bus // CYCLE LENGTH: L case ROMC_09: Regs[DB] = (byte)Regs[DC0l]; break; // All devices add the 8-bit value on the data bus, treated as a signed binary number, to the data counter // CYCLE LENGTH: L case ROMC_0A: RegDC0 += (ushort)((sbyte)Regs[DB]); break; // The device whose address space includes the value in PC1 must place the low order byte of PC1 on the data bus // CYCLE LENGTH: L case ROMC_0B: Regs[DB] = (byte)Regs[PC1l]; break; // The device whose address space includes the contents of the PC0 register must place the contents of the memory word addressed by PC0 // onto the data bus; then all devices move the value that has just been placed on the data bus into the low order byte of PC0 // CYCLE LENGTH: L case ROMC_0C: Read_Func(DB, PC0l, PC0h); Regs[PC0l] = Regs[DB]; break; // All devices store in PC1 the current contents of PC0, incremented by 1; PC0 is unaltered // CYCLE LENGTH: S case ROMC_0D: RegPC1 = (ushort)(RegPC0 + 1); break; // The device whose address space includes the contents of PC0 must place the contents of the word addressed by PC0 onto the data bus. // The value on the data bus is then moved to the low order byte of DC0 by all devices // CYCLE LENGTH: L case ROMC_0E: Read_Func(DB, PC0l, PC0h); Regs[DC0l] = Regs[DB]; break; // The interrupting device with the highest priority must place the low order byte of the interrupt vector on the data bus. // All devices must copy the contents of PC0 into PC1. All devices must move the contents of the data bus into the low order byte of PC0 // CYCLE LENGTH: L case ROMC_0F: throw new NotImplementedException("ROMC 0x0F not implemented"); // Inhibit any modification to the interrupt priority logic // CYCLE LENGTH: L case ROMC_10: throw new NotImplementedException("ROMC 0x10 not implemented"); // The device whose memory space includes the contents of PC0 must place the contents of the addressed memory word on the data bus. // All devices must then move the contents of the data bus to the upper byte of DC0 // CYCLE LENGTH: L case ROMC_11: Read_Func(DB, PC0l, PC0h); Regs[DC0h] = Regs[DB]; break; // All devices copy the contents of PC0 into PC1. All devices then move the contents of the data bus into the low order byte of PC0 // CYCLE LENGTH: L case ROMC_12: RegPC1 = RegPC0; Regs[PC0l] = Regs[DB]; break; // The interrupting device with the highest priority must move the high order half of the interrupt vector onto the data bus. // All devices must move the conetnts of the data bus into the high order byte of of PC0. The interrupting device resets its // interrupt circuitry (so that it is no longer requesting CPU servicing and can respond to another interrupt) // CYCLE LENGTH: L case ROMC_13: throw new NotImplementedException("ROMC 0x13 not implemented"); // All devices move the contents of the data bus into the high order byte of PC0 // CYCLE LENGTH: L case ROMC_14: Regs[PC0h] = Regs[DB]; break; // All devices move the contents of the data bus into the high order byte of PC1 // CYCLE LENGTH: L case ROMC_15: Regs[PC1h] = Regs[DB]; break; // All devices move the contents of the data bus into the high order byte of DC0 // CYCLE LENGTH: L case ROMC_16: Regs[DC0h] = Regs[DB]; break; // All devices move the contents of the data bus into the low order byte of PC0 // CYCLE LENGTH: L case ROMC_17: Regs[PC0l] = Regs[DB]; break; // All devices move the contents of the data bus into the low order byte of PC1 // CYCLE LENGTH: L case ROMC_18: Regs[PC1l] = Regs[DB]; break; // All devices move the contents of the data bus into the low order byte of DC0 // CYCLE LENGTH: L case ROMC_19: Regs[DC0l] = Regs[DB]; break; // During the prior cycle, an I/O port timer or interrupt control register was addressed; the device containing the addressed // port must move the current contents of the data bus into the addressed port // CYCLE LENGTH: L case ROMC_1A: WriteHardware(Regs[IO], (byte)Regs[DB]); break; // During the prior cycle, the data bus specified the address of an I/O port. The device containing the addressed I/O port // must place the contents of the I/O port on the data bus. (Note that the contents of the timer and interrupt control // registers cannot be read back onto the data bus) // CYCLE LENGTH: L case ROMC_1B: IN_Func(DB, IO); //Regs[DB] = ReadHardware(Regs[IO]); break; // None // CYCLE LENGTH: S case ROMC_1C_S: break; // None // CYCLE LENGTH: L case ROMC_1C_L: break; // Devices with DC0 and DC1 registers must switch registers. Devices without a DC1 register perform no operation // CYCLE LENGTH: S case ROMC_1D: // we have no DC1 in this implementation break; // The device whose address space includes the contents of PC0 must place the low order byte of PC0 onto the data bus // CYCLE LENGTH: L case ROMC_1E: Regs[DB] = (byte)Regs[PC0l]; break; // The device whose address space includes the contents of PC0 must place the high order byte of PC0 onto the data bus // CYCLE LENGTH: L case ROMC_1F: Regs[DB] = (byte)Regs[PC0h]; break; } TotalExecutedCycles++; }
// Execute instructions public void ExecuteOne(ref byte interrupt_src, byte interrupt_enable) { switch (cur_instr[instr_pntr++]) { case IDLE: // do nothing break; case OP: // Read the opcode of the next instruction if (EI_pending > 0 && !CB_prefix) { EI_pending--; if (EI_pending == 0) { interrupts_enabled = true; } } if (I_use && interrupts_enabled && !CB_prefix && !jammed) { interrupts_enabled = false; TraceCallback?.Invoke(new TraceInfo { Disassembly = "====IRQ====", RegisterInfo = "" }); // call interrupt processor // lowest bit set is highest priority INTERRUPT_(); } else { OnExecFetch?.Invoke(RegPC); if (TraceCallback != null && !CB_prefix) { TraceCallback(State()); } CDLCallback?.Invoke(RegPC, eCDLogMemFlags.FetchFirst); FetchInstruction(ReadMemory(RegPC++)); } instr_pntr = 0; I_use = false; break; case RD: Read_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case WR: Write_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case TR: TR_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case ADD16: ADD16_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case ADD8: ADD8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case SUB8: SUB8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case ADC8: ADC8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case SBC8: SBC8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case INC16: INC16_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case INC8: INC8_Func(cur_instr[instr_pntr++]); break; case DEC16: DEC16_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case DEC8: DEC8_Func(cur_instr[instr_pntr++]); break; case RLC: RLC_Func(cur_instr[instr_pntr++]); break; case RL: RL_Func(cur_instr[instr_pntr++]); break; case RRC: RRC_Func(cur_instr[instr_pntr++]); break; case RR: RR_Func(cur_instr[instr_pntr++]); break; case CPL: CPL_Func(cur_instr[instr_pntr++]); break; case DA: DA_Func(cur_instr[instr_pntr++]); break; case SCF: SCF_Func(cur_instr[instr_pntr++]); break; case CCF: CCF_Func(cur_instr[instr_pntr++]); break; case AND8: AND8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case XOR8: XOR8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case OR8: OR8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case CP8: CP8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case SLA: SLA_Func(cur_instr[instr_pntr++]); break; case SRA: SRA_Func(cur_instr[instr_pntr++]); break; case SRL: SRL_Func(cur_instr[instr_pntr++]); break; case SWAP: SWAP_Func(cur_instr[instr_pntr++]); break; case BIT: BIT_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case RES: RES_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case SET: SET_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case EI: if (EI_pending == 0) { EI_pending = 2; } break; case DI: interrupts_enabled = false; EI_pending = 0; break; case HALT: halted = true; bool temp = false; if (cur_instr[instr_pntr++] == 1) { temp = FlagI; } else { temp = I_use; } if (EI_pending > 0 && !CB_prefix) { EI_pending--; if (EI_pending == 0) { interrupts_enabled = true; } } // if the I flag is asserted at the time of halt, don't halt if (temp && interrupts_enabled && !CB_prefix && !jammed) { interrupts_enabled = false; TraceCallback?.Invoke(new TraceInfo { Disassembly = "====IRQ====", RegisterInfo = "" }); halted = false; if (is_GBC) { // call the interrupt processor after 4 extra cycles if (!Halt_bug_3) { INTERRUPT_GBC_NOP(); } else { INTERRUPT_(); Halt_bug_3 = false; //Console.WriteLine("Hit INT"); } } else { // call interrupt processor INTERRUPT_(); Halt_bug_3 = false; } } else if (temp) { // even if interrupt servicing is disabled, any interrupt flag raised still resumes execution TraceCallback?.Invoke(new TraceInfo { Disassembly = "====un-halted====", RegisterInfo = "" }); halted = false; if (is_GBC) { // extra 4 cycles for GBC if (Halt_bug_3) { OnExecFetch?.Invoke(RegPC); if (TraceCallback != null && !CB_prefix) { TraceCallback(State()); } CDLCallback?.Invoke(RegPC, eCDLogMemFlags.FetchFirst); RegPC++; FetchInstruction(ReadMemory(RegPC)); Halt_bug_3 = false; //Console.WriteLine("Hit un"); } else { cur_instr = new[] { IDLE, IDLE, IDLE, OP }; } } else { OnExecFetch?.Invoke(RegPC); if (TraceCallback != null && !CB_prefix) { TraceCallback(State()); } CDLCallback?.Invoke(RegPC, eCDLogMemFlags.FetchFirst); if (Halt_bug_3) { //special variant of halt bug where RegPC also isn't incremented post fetch RegPC++; FetchInstruction(ReadMemory(RegPC)); Halt_bug_3 = false; } else { FetchInstruction(ReadMemory(RegPC++)); } } } else { if (skip_once) { cur_instr = new ushort[] { IDLE, IDLE, IDLE, HALT, 0 }; skip_once = false; } else { if (is_GBC) { cur_instr = new ushort[] { IDLE, IDLE, HALT_CHK, HALT, 0 }; } else { cur_instr = new ushort[] { HALT_CHK, IDLE, IDLE, HALT, 0 }; } } } I_use = false; instr_pntr = 0; break; case STOP: stopped = true; if (!stop_check) { stop_time = SpeedFunc(0); stop_check = true; } if (stop_time > 0) { stop_time--; if (stop_time == 0) { TraceCallback?.Invoke(new TraceInfo { Disassembly = "====un-stop====", RegisterInfo = "" }); stopped = false; OnExecFetch?.Invoke(RegPC); if (TraceCallback != null && !CB_prefix) { TraceCallback(State()); } CDLCallback?.Invoke(RegPC, eCDLogMemFlags.FetchFirst); FetchInstruction(ReadMemory(RegPC++)); instr_pntr = 0; stop_check = false; } else { instr_pntr = 0; cur_instr = new ushort[] { IDLE, IDLE, IDLE, STOP }; } } else if (interrupt_src.Bit(4)) // button pressed, not actually an interrupt though { TraceCallback?.Invoke(new TraceInfo { Disassembly = "====un-stop====", RegisterInfo = "" }); stopped = false; OnExecFetch?.Invoke(RegPC); if (TraceCallback != null && !CB_prefix) { TraceCallback(State()); } CDLCallback?.Invoke(RegPC, eCDLogMemFlags.FetchFirst); FetchInstruction(ReadMemory(RegPC++)); instr_pntr = 0; stop_check = false; } else { instr_pntr = 0; cur_instr = new[] { IDLE, IDLE, IDLE, STOP }; } break; case PREFIX: CB_prefix = true; break; case ASGN: ASGN_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case ADDS: ADDS_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case OP_G: OnExecFetch?.Invoke(RegPC); TraceCallback?.Invoke(State()); CDLCallback?.Invoke(RegPC, eCDLogMemFlags.FetchFirst); FetchInstruction(ReadMemory(RegPC)); // note no increment instr_pntr = 0; break; case JAM: jammed = true; instr_pntr--; break; case RD_F: Read_Func_F(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case EI_RETI: EI_pending = 1; break; case INT_GET: // check if any interrupts got cancelled along the way // interrupt src = 5 sets the PC to zero as observed // also the triggering interrupt seems like it is held low (i.e. annot trigger I flag) until the interrupt is serviced if (interrupt_src.Bit(0) && interrupt_enable.Bit(0)) { int_src = 0; int_clear = 1; } else if (interrupt_src.Bit(1) && interrupt_enable.Bit(1)) { int_src = 1; int_clear = 2; } else if (interrupt_src.Bit(2) && interrupt_enable.Bit(2)) { int_src = 2; int_clear = 4; } else if (interrupt_src.Bit(3) && interrupt_enable.Bit(3)) { int_src = 3; int_clear = 8; } else if (interrupt_src.Bit(4) && interrupt_enable.Bit(4)) { int_src = 4; int_clear = 16; } else { int_src = 5; int_clear = 0; } Regs[cur_instr[instr_pntr++]] = INT_vectors[int_src]; break; case HALT_CHK: I_use = FlagI; if (Halt_bug_2 && I_use) { RegPC--; Halt_bug_3 = true; //Console.WriteLine("Halt_bug_3"); //Console.WriteLine(TotalExecutedCycles); } Halt_bug_2 = false; break; case IRQ_CLEAR: if (interrupt_src.Bit(int_src)) { interrupt_src -= int_clear; } if ((interrupt_src & interrupt_enable) == 0) { FlagI = false; } break; } TotalExecutedCycles++; }
// Execute instructions public void ExecuteOne() { //Console.Write(opcode_see + " "); //Console.WriteLine(Regs[PC] + " "); switch (cur_instr[instr_pntr++]) { case IDLE: // do nothing break; case OP: // Read the opcode of the next instruction OnExecFetch?.Invoke(PC); TraceCallback?.Invoke(State()); CDLCallback?.Invoke(PC, eCDLogMemFlags.FetchFirst); FetchInstruction(ReadMemory(Regs[PC]++)); instr_pntr = 0; irq_pntr = -1; break; case RD: Read_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case RD_INC: Read_Inc_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case RD_INC_OP: Read_Inc_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); switch (cur_instr[instr_pntr++]) { case AND8: AND8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case ADD8: ADD8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case ADC8: ADC8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case OR8: OR8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case XOR8: XOR8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case BIT: BIT_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case SUB8: SUB8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case SBC8: SBC8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case CMP8: CMP8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case DEC16: DEC16_Func(cur_instr[instr_pntr++]); break; case ADD8BR: ADD8BR_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case TR: TR_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case SET_ADDR: reg_d_ad = cur_instr[instr_pntr++]; reg_h_ad = cur_instr[instr_pntr++]; reg_l_ad = cur_instr[instr_pntr++]; Regs[reg_d_ad] = (ushort)((Regs[reg_h_ad] << 8) | Regs[reg_l_ad]); break; case IDX_DCDE: Index_decode(); break; case IDX_OP_BLD: Index_Op_Builder(); break; case LD_8: LD_8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case LD_16: LD_16_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; } break; case WR: Write_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case WR_DEC_LO: Write_Dec_Lo_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case WR_DEC_HI: Write_Dec_HI_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case WR_HI: Write_Hi_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case WR_HI_INC: Write_Hi_Inc_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case TR: TR_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case LD_8: LD_8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case LD_16: LD_16_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case IDX_OP_BLD: Index_Op_Builder(); break; case SET_ADDR: reg_d_ad = cur_instr[instr_pntr++]; reg_h_ad = cur_instr[instr_pntr++]; reg_l_ad = cur_instr[instr_pntr++]; // Console.WriteLine(reg_d_ad + " " + reg_h_ad + " " + reg_l_ad); // Console.WriteLine(Regs[reg_d_ad] + " " + Regs[reg_h_ad] + " " + Regs[reg_l_ad]); Regs[reg_d_ad] = (ushort)((Regs[reg_h_ad] << 8) | Regs[reg_l_ad]); break; case NEG: NEG_8_Func(cur_instr[instr_pntr++]); break; case TST: TST_Func(cur_instr[instr_pntr++]); break; case CLR: CLR_Func(cur_instr[instr_pntr++]); break; case SET_I: FlagI = true; break; case ADD8BR: ADD8BR_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case ADD8: ADD8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case SUB8: SUB8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case ADC8: ADC8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case SBC8: SBC8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case CMP8: CMP8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case INC16: INC16_Func(cur_instr[instr_pntr++]); break; case INC8: INC8_Func(cur_instr[instr_pntr++]); break; case DEC16: DEC16_Func(cur_instr[instr_pntr++]); break; case CMP16: CMP16_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case DEC8: DEC8_Func(cur_instr[instr_pntr++]); break; case ROL: ROL_Func(cur_instr[instr_pntr++]); break; case ROR: ROR_Func(cur_instr[instr_pntr++]); break; case COM: COM_Func(cur_instr[instr_pntr++]); break; case DA: DA_Func(cur_instr[instr_pntr++]); break; case AND8: AND8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case XOR8: XOR8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case OR8: OR8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case ASL: ASL_Func(cur_instr[instr_pntr++]); break; case ASR: ASR_Func(cur_instr[instr_pntr++]); break; case LSR: LSR_Func(cur_instr[instr_pntr++]); break; case TAP: instr_pntr++; Regs[CC] = (ushort)((Regs[A] & 0x3F) | 0xC0); // last 2 bits always 1 break; case TPA: instr_pntr++; Regs[A] = Regs[CC]; break; case INX: instr_pntr++; Regs[X] = (ushort)(Regs[X] + 1); FlagZ = Regs[X] == 0; break; case DEX: instr_pntr++; Regs[X] = (ushort)(Regs[X] - 1); FlagZ = Regs[X] == 0; break; case CLV: instr_pntr++; FlagV = false; break; case SEV: instr_pntr++; FlagV = true; break; case CLC: instr_pntr++; FlagC = false; break; case SEC: instr_pntr++; FlagC = true; break; case CLI: instr_pntr++; FlagI = false; break; case SEI: instr_pntr++; FlagI = true; break; case SBA: instr_pntr++; SBC8_Func(A, B); break; case CBA: instr_pntr++; CMP8_Func(A, B); break; case TAB: instr_pntr++; Regs[B] = Regs[A]; break; case TBA: instr_pntr++; Regs[A] = Regs[B]; break; case ABA: instr_pntr++; ADD8_Func(A, B); break; case TSX: instr_pntr++; Regs[X] = (ushort)(Regs[SP] + 1); break; case INS: instr_pntr++; Regs[SP] = (ushort)(Regs[SP] + 1); break; case DES: instr_pntr++; Regs[SP] = (ushort)(Regs[SP] - 1); break; case TXS: instr_pntr++; Regs[SP] = (ushort)(Regs[X] - 1); break; case BIT: BIT_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case WAI: if (NMIPending) { NMIPending = false; Regs[ADDR] = 0xFFFC; PopulateCURINSTR(RD_INC, ALU, ADDR, RD_INC, ALU2, ADDR, SET_ADDR, PC, ALU, ALU2); irq_pntr = -1; IRQS = 3; TraceCallback?.Invoke(new TraceInfo { Disassembly = "====CWAI NMI====", RegisterInfo = "" }); } else if (IRQPending && !FlagI) { IRQPending = false; Regs[ADDR] = 0xFFF8; PopulateCURINSTR(RD_INC, ALU, ADDR, RD_INC, ALU2, ADDR, SET_ADDR, PC, ALU, ALU2); irq_pntr = -1; IRQS = 3; TraceCallback?.Invoke(new TraceInfo { Disassembly = "====CWAI IRQ====", RegisterInfo = "" }); } else { PopulateCURINSTR(WAI); irq_pntr = 0; IRQS = -1; } instr_pntr = 0; break; } if (++irq_pntr == IRQS) { // NMI has priority if (NMIPending) { NMIPending = false; TraceCallback?.Invoke(new TraceInfo { Disassembly = "====NMI====", RegisterInfo = "" }); NMI_(); NMICallback(); instr_pntr = irq_pntr = 0; } // then regular IRQ else if (IRQPending && !FlagI) { IRQPending = false; TraceCallback?.Invoke(new TraceInfo { Disassembly = "====IRQ====", RegisterInfo = "" }); IRQ_(); IRQCallback(); instr_pntr = irq_pntr = 0; } // otherwise start the next instruction else { PopulateCURINSTR(OP); instr_pntr = irq_pntr = 0; IRQS = -1; } } TotalExecutedCycles++; }
/// <summary> /// Runs a single CPU clock cycle /// </summary> public void ExecuteOne() { if (Regs[ISAR] > 0x3F) { } if (Regs[W] > 0x1F) { } switch (cur_instr[instr_pntr++]) { // always the last tick within an opcode instruction cycle case END: OnExecFetch?.Invoke(RegPC0); TraceCallback?.Invoke(State()); opcode = (byte)Regs[DB]; instr_pntr = 0; FetchInstruction(); break; // used as timing 'padding' case IDLE: break; // load one register into another (or databus) case OP_LR8: LR_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; // load DB into A (as a part of an IN or INS instruction) case OP_LR_A_DB_IO: LR_A_IO_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; // loads supplied index value into the bottom 4 bits of a register (upper bits are set to 0) case OP_LIS: Regs[ALU1] = (byte)(cur_instr[instr_pntr++] & 0x0F); LR_Func(A, ALU1); break; // Shift register n bit positions to the right (zero fill) case OP_SHFT_R: SR_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; // Shift register n bit positions to the left (zero fill) case OP_SHFT_L: SL_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; // x <- (x) ADD y case OP_ADD8: ADD_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; // x <- (x) MINUS y case OP_SUB8: SUB_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; // x <- (x) ADD y (decimal) case OP_ADD8D: ADDD_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; // A <- (A) + (C) case OP_LNK: Regs[ALU0] = (byte)(FlagC ? 1 : 0); ADD_Func(A, ALU0); break; // Clear ICB status bit case OP_DI: FlagICB = false; break; // Set ICB status bit case OP_EI: FlagICB = true; break; // x <- (y) XOR DB case OP_XOR8: XOR_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; // The accumulator is loaded with its one's complement case OP_COM: XOR_Func(A, BYTE); //Regs[A] = (byte)(Regs[A] ^ 0xFF); break; // x <- (x) + 1 case OP_INC8: ADD_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; // x <- (y) & DB case OP_AND8: AND_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; // x <- (y) | DB case OP_OR8: OR_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; // DB + (x) + 1 (modify flags without saving result) case OP_CI: CI_Func(); break; // ISAR is incremented case OP_IS_INC: Regs[ISAR] = (byte)((Regs[ISAR] & 0x38) | ((Regs[ISAR] + 1) & 0x07)); break; // ISAR is decremented case OP_IS_DEC: Regs[ISAR] = (byte)((Regs[ISAR] & 0x38) | ((Regs[ISAR] - 1) & 0x07)); break; // set the upper octal ISAR bits (b3,b4,b5) but do not alter the three least significant bits case OP_LISU: //Regs[ISAR] = (byte)(((Regs[ISAR] & 0x07) | (cur_instr[instr_pntr++] & 0x07) << 3) & 0x3F); Regs[ISAR] = (byte)((Regs[ISAR] & 0x07) | cur_instr[instr_pntr++]); break; // set the lower octal ISAR bits (b0,b1,b2) but do not alter the three most significant bits case OP_LISL: //Regs[ISAR] = (byte) (((Regs[ISAR] & 0x38) | (cur_instr[instr_pntr++] & 0x07)) & 0x3F); Regs[ISAR] = (byte)((Regs[ISAR] & 0x38) | cur_instr[instr_pntr++]); break; // Branch on TRUE case OP_BT: if ((Regs[W] & cur_instr[instr_pntr++]) > 0) { DO_BRANCH(0); } else { DONT_BRANCH(0); } break; // Branch on ISARL - if any of the low 3 bits of ISAR are reset case OP_BR7: if (!Regs[ISAR].Bit(0) || !Regs[ISAR].Bit(1) || !Regs[ISAR].Bit(2)) { DO_BRANCH(1); } else { DONT_BRANCH(1); } /* * if Regs[ISAR] & 7) == 7) * DONT_BRANCH(1); * else * DO_BRANCH(1); */ break; // Branch on FALSE case OP_BF: if ((Regs[W] & cur_instr[instr_pntr++]) > 0) { DONT_BRANCH(0); } else { DO_BRANCH(0); } break; /* * * // Unconditional Branch (relative) * case OP_BR: * DO_BF_BRANCH(0); * break; * * // Branch on Negative * case OP_BM: * if (!FlagS) * DO_BF_BRANCH(0); * else * DONT_BF_BRANCH(0); * break; * * // Branch if no carry * case OP_BNC: * if (!FlagC) * DO_BF_BRANCH(0); * else * DONT_BF_BRANCH(0); * break; * * // Branch if negative and no carry * case OP_BF_CS: * if (!FlagS && !FlagC) * DO_BF_BRANCH(0); * else * DONT_BF_BRANCH(0); * break; * * // Branch if not zero * case OP_BNZ: * instr_pntr = 0; * if (!FlagZ) * DO_BF_BRANCH(0); * else * DONT_BF_BRANCH(0); * break; * * // Branch on Negative and not Zero (same thing as branch on negative) * case OP_BF_ZS: * if (!FlagS && !FlagZ) * DO_BF_BRANCH(0); * else * DONT_BF_BRANCH(0); * break; * * // Branch on no Carry and not Zero * case OP_BF_ZC: * if (!FlagC && !FlagZ) * DO_BF_BRANCH(0); * else * DONT_BF_BRANCH(0); * break; * * // Branch on no Carry and not Zero and Negative * case OP_BF_ZCS: * if (!FlagC && !FlagZ && !FlagS) * DO_BF_BRANCH(0); * else * DONT_BF_BRANCH(0); * break; * * // Branch on no Overflow * case OP_BNO: * if (!FlagO) * DO_BF_BRANCH(0); * else * DONT_BF_BRANCH(0); * break; * * // Branch on no overflow and Negative * case OP_BF_OS: * if (!FlagO && !FlagS) * DO_BF_BRANCH(0); * else * DONT_BF_BRANCH(0); * break; * * // Branch on no overflow and Negative * case OP_BF_OC: * if (!FlagO && !FlagC) * DO_BF_BRANCH(0); * else * DONT_BF_BRANCH(0); * break; * * // Branch on no overflow and no carry and Negative * case OP_BF_OCS: * if (!FlagO && !FlagC && !FlagS) * DO_BF_BRANCH(0); * else * DONT_BF_BRANCH(0); * break; * * // Branch on no overflow and not zero * case OP_BF_OZ: * if (!FlagO && !FlagZ) * DO_BF_BRANCH(0); * else * DONT_BF_BRANCH(0); * break; * * // Branch on no overflow and not zero and negative * case OP_BF_OZS: * if (!FlagO && !FlagZ && !FlagS) * DO_BF_BRANCH(0); * else * DONT_BF_BRANCH(0); * break; * * // Branch on no overflow and not zero and no carry * case OP_BF_OZC: * if (!FlagO && !FlagZ && !FlagC) * DO_BF_BRANCH(0); * else * DONT_BF_BRANCH(0); * break; * * // Branch on no overflow and not zero and no carry and negative * case OP_BF_OZCS: * if (!FlagO && !FlagZ && !FlagC && FlagS) * DO_BF_BRANCH(0); * else * DONT_BF_BRANCH(0); * break; */ /* * // Branch on true - no branch * case OP_BTN: * DONT_BT_BRANCH(0); * break; * * // Branch if positive * case OP_BP: * if (FlagS) * DO_BT_BRANCH(0); * else * DONT_BT_BRANCH(0); * break; * * // Branch on carry * case OP_BC: * if (FlagC) * DO_BT_BRANCH(0); * else * DONT_BT_BRANCH(0); * break; * * // Branch on carry or positive * case OP_BT_CS: * if (FlagC || FlagS) * DO_BT_BRANCH(0); * else * DONT_BT_BRANCH(0); * break; * * // Branch if zero * case OP_BZ: * if (FlagZ) * DO_BT_BRANCH(0); * else * DONT_BT_BRANCH(0); * break; * * // Branch if zero or positive * case OP_BT_ZS: * if (FlagZ || FlagS) * DO_BT_BRANCH(0); * else * DONT_BT_BRANCH(0); * break; * * // Branch if zero or carry * case OP_BT_ZC: * if (FlagZ || FlagC) * DO_BT_BRANCH(0); * else * DONT_BT_BRANCH(0); * break; * * // Branch if zero or carry or positive * case OP_BT_ZCS: * if (FlagZ || FlagC || FlagS) * DO_BT_BRANCH(0); * else * DONT_BT_BRANCH(0); * break; */ // A <- (I/O Port 0 or 1) case OP_IN: IN_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; // I/O Port 0 or 1 <- (A) case OP_OUT: OUT_Func(IO, A); break; // instruction fetch // The device whose address space includes the contents of the PC0 register must place on the data bus the op code addressed by PC0; // then all devices increments the content of PC0. // CYCLE LENGTH: S case ROMC_00_S: Read_Func(DB, PC0l, PC0h); RegPC0++; break; // instruction fetch // The device whose address space includes the contents of the PC0 register must place on the data bus the op code addressed by PC0; // then all devices increments the content of PC0. // CYCLE LENGTH: L case ROMC_00_L: Read_Func(DB, PC0l, PC0h); RegPC0++; break; // The device whose address space includes the contents of the PC0 register must place on the data bus the contents of the memory location // addressed by by PC0; then all devices add the 8-bit value on the data bus, as a signed binary number, to PC0 // CYCLE LENGTH: L case ROMC_01: Read_Func(DB, PC0l, PC0h); RegPC0 = Regs[DB].Bit(7) ? (ushort)(RegPC0 - (byte)((Regs[DB] ^ 0xFF) + 1)) : (ushort)(RegPC0 + Regs[DB]); break; // The device whose DC0 address addresses a memory word within the address space of that device must place on the data bus the contents // of the memory location addressed by DC0; then all devices increment DC0 // CYCLE LENGTH: L case ROMC_02: Read_Func(DB, DC0l, DC0h); RegDC0++; break; // Similar to 0x00, except that it is used for Immediate Operand fetches (using PC0) instead of instruction fetches // CYCLE LENGTH: S case ROMC_03_S: Read_Func(DB, PC0l, PC0h); RegPC0++; Regs[IO] = Regs[DB]; break; // Similar to 0x00, except that it is used for Immediate Operand fetches (using PC0) instead of instruction fetches // CYCLE LENGTH: L case ROMC_03_L: Read_Func(DB, PC0l, PC0h); RegPC0++; Regs[IO] = Regs[DB]; break; // Copy the contents of PC1 into PC0 // CYCLE LENGTH: S case ROMC_04: RegPC0 = RegPC1; break; // Store the data bus contents into the memory location pointed to by DC0; increment DC0 // CYCLE LENGTH: L case ROMC_05: Write_Func(DC0l, DC0h, DB); RegDC0++; break; // Place the high order byte of DC0 on the data bus // CYCLE LENGTH: L case ROMC_06: Regs[DB] = (byte)Regs[DC0h]; break; // Place the high order byte of PC1 on the data bus // CYCLE LENGTH: L case ROMC_07: Regs[DB] = (byte)Regs[PC1h]; break; // All devices copy the contents of PC0 into PC1. The CPU outputs zero on the data bus in this ROMC state. // Load the data bus into both halves of PC0, this clearing the register. // CYCLE LENGTH: L case ROMC_08: RegPC1 = RegPC0; Regs[DB] = 0; Regs[PC0h] = 0; Regs[PC0l] = 0; break; // The device whose address space includes the contents of the DC0 register must place the low order byte of DC0 onto the data bus // CYCLE LENGTH: L case ROMC_09: Regs[DB] = (byte)Regs[DC0l]; break; // All devices add the 8-bit value on the data bus, treated as a signed binary number, to the data counter // CYCLE LENGTH: L case ROMC_0A: // The contents of the accumulator are treated as a signed binary number, and are added to the contents of every DCO register. RegDC0 = Regs[DB].Bit(7) ? (ushort)(RegDC0 - (byte)((Regs[DB] ^ 0xFF) + 1)) : (ushort)(RegDC0 + Regs[DB]); break; // The device whose address space includes the value in PC1 must place the low order byte of PC1 on the data bus // CYCLE LENGTH: L case ROMC_0B: Regs[DB] = (byte)Regs[PC1l]; break; // The device whose address space includes the contents of the PC0 register must place the contents of the memory word addressed by PC0 // onto the data bus; then all devices move the value that has just been placed on the data bus into the low order byte of PC0 // CYCLE LENGTH: L case ROMC_0C: Read_Func(DB, PC0l, PC0h); Regs[PC0l] = Regs[DB]; break; // All devices store in PC1 the current contents of PC0, incremented by 1; PC0 is unaltered // CYCLE LENGTH: S case ROMC_0D: RegPC1 = (ushort)(RegPC0 + 1); break; // The device whose address space includes the contents of PC0 must place the contents of the word addressed by PC0 onto the data bus. // The value on the data bus is then moved to the low order byte of DC0 by all devices // CYCLE LENGTH: L case ROMC_0E: Read_Func(DB, PC0l, PC0h); Regs[DC0l] = Regs[DB]; break; // The interrupting device with the highest priority must place the low order byte of the interrupt vector on the data bus. // All devices must copy the contents of PC0 into PC1. All devices must move the contents of the data bus into the low order byte of PC0 // CYCLE LENGTH: L case ROMC_0F: throw new NotImplementedException("ROMC 0x0F not implemented"); // Inhibit any modification to the interrupt priority logic // CYCLE LENGTH: L case ROMC_10: throw new NotImplementedException("ROMC 0x10 not implemented"); // The device whose memory space includes the contents of PC0 must place the contents of the addressed memory word on the data bus. // All devices must then move the contents of the data bus to the upper byte of DC0 // CYCLE LENGTH: L case ROMC_11: Read_Func(DB, PC0l, PC0h); Regs[DC0h] = Regs[DB]; break; // All devices copy the contents of PC0 into PC1. All devices then move the contents of the data bus into the low order byte of PC0 // CYCLE LENGTH: L case ROMC_12: RegPC1 = RegPC0; Regs[PC0l] = Regs[DB]; break; // The interrupting device with the highest priority must move the high order half of the interrupt vector onto the data bus. // All devices must move the conetnts of the data bus into the high order byte of of PC0. The interrupting device resets its // interrupt circuitry (so that it is no longer requesting CPU servicing and can respond to another interrupt) // CYCLE LENGTH: L case ROMC_13: throw new NotImplementedException("ROMC 0x13 not implemented"); // All devices move the contents of the data bus into the high order byte of PC0 // CYCLE LENGTH: L case ROMC_14: Regs[PC0h] = Regs[DB]; break; // All devices move the contents of the data bus into the high order byte of PC1 // CYCLE LENGTH: L case ROMC_15: Regs[PC1h] = Regs[DB]; break; // All devices move the contents of the data bus into the high order byte of DC0 // CYCLE LENGTH: L case ROMC_16: Regs[DC0h] = Regs[DB]; break; // All devices move the contents of the data bus into the low order byte of PC0 // CYCLE LENGTH: L case ROMC_17: Regs[PC0l] = Regs[DB]; break; // All devices move the contents of the data bus into the low order byte of PC1 // CYCLE LENGTH: L case ROMC_18: Regs[PC1l] = Regs[DB]; break; // All devices move the contents of the data bus into the low order byte of DC0 // CYCLE LENGTH: L case ROMC_19: Regs[DC0l] = Regs[DB]; break; // During the prior cycle, an I/O port timer or interrupt control register was addressed; the device containing the addressed // port must move the current contents of the data bus into the addressed port // CYCLE LENGTH: L case ROMC_1A: OUT_Func(IO, DB); break; // During the prior cycle, the data bus specified the address of an I/O port. The device containing the addressed I/O port // must place the contents of the I/O port on the data bus. (Note that the contents of the timer and interrupt control // registers cannot be read back onto the data bus) // CYCLE LENGTH: L case ROMC_1B: IN_Func(DB, IO); break; // None // CYCLE LENGTH: S case ROMC_1C_S: break; // None // CYCLE LENGTH: L case ROMC_1C_L: break; // Devices with DC0 and DC1 registers must switch registers. Devices without a DC1 register perform no operation // CYCLE LENGTH: S case ROMC_1D: ushort temp = RegDC0; RegDC0 = RegDC1; RegDC1 = temp; break; // The device whose address space includes the contents of PC0 must place the low order byte of PC0 onto the data bus // CYCLE LENGTH: L case ROMC_1E: Regs[DB] = (byte)Regs[PC0l]; break; // The device whose address space includes the contents of PC0 must place the high order byte of PC0 onto the data bus // CYCLE LENGTH: L case ROMC_1F: Regs[DB] = (byte)Regs[PC0h]; break; } TotalExecutedCycles++; }
// Execute instructions public void ExecuteOne() { //Console.Write(opcode_see + " "); //Console.WriteLine(Regs[PC] + " "); switch (cur_instr[instr_pntr++]) { case IDLE: // do nothing break; case OP: // Read the opcode of the next instruction OnExecFetch?.Invoke(PC); TraceCallback?.Invoke(State()); CDLCallback?.Invoke(PC, eCDLogMemFlags.FetchFirst); FetchInstruction(ReadMemory(Regs[PC])); Regs[ALU2] = (ushort)(Regs[PC] & 0x800); Regs[PC] = (ushort)(((Regs[PC] + 1) & 0x7FF) | Regs[ALU2]); instr_pntr = 0; irq_pntr = -1; break; case RD: Read_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case WR: Write_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case TR: TR_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case INC11: reg_d_ad = cur_instr[instr_pntr++]; Regs[ALU2] = (ushort)(Regs[reg_d_ad] & 0x800); Regs[reg_d_ad] = (ushort)(((Regs[reg_d_ad] + 1) & 0x7FF) | Regs[ALU2]); break; case ADD8: ADD8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case ADC8: ADC8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case AND8: AND8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case XOR8: XOR8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case OR8: OR8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case INC8: INC8_Func(cur_instr[instr_pntr++]); break; case INCA: INC8_Func(A); break; case DEC8: DEC8_Func(cur_instr[instr_pntr++]); break; case DECA: DEC8_Func(A); break; case ROL: ROL_Func(A); break; case ROR: ROR_Func(A); break; case RLC: RLC_Func(A); break; case RRC: RRC_Func(A); break; case CLRA: Regs[A] = 0; break; case SWP: reg_d_ad = Regs[A]; Regs[A] = (ushort)(Regs[A] >> 4); Regs[A] |= (ushort)((reg_d_ad << 4) & 0xF0); break; case COMA: Regs[A] = (ushort)((~Regs[A]) & 0xFF); break; case CMC: FlagC = !FlagC; break; case CM0: FlagF0 = !FlagF0; break; case CM1: F1 = !F1; break; case DA: DA_Func(A); break; case SET_ADDR: reg_d_ad = cur_instr[instr_pntr++]; reg_l_ad = cur_instr[instr_pntr++]; reg_h_ad = cur_instr[instr_pntr++]; // direct value // bit 11 held low during interrupt if (INT_MSTR) { Regs[reg_d_ad] = (ushort)(MB | (reg_h_ad << 8) | Regs[reg_l_ad]); } else { Regs[reg_d_ad] = (ushort)((reg_h_ad << 8) | Regs[reg_l_ad]); } break; case CLC: FlagC = false; break; case CL0: FlagF0 = false; break; case CL1: F1 = false; break; case EI: IntEn = true; break; case EN: TimIntEn = true; break; case DI: IntEn = false; break; case DN: TimIntEn = false; TIRQPending = false; break; case MSK: break; case CLK_OUT: break; case XCH: Regs[ALU] = Regs[cur_instr[instr_pntr]]; Regs[cur_instr[instr_pntr++]] = Regs[cur_instr[instr_pntr]]; Regs[cur_instr[instr_pntr++]] = Regs[ALU]; break; case XCH_RAM: reg_d_ad = cur_instr[instr_pntr++]; reg_d_ad = (ushort)(Regs[reg_d_ad] & 0x3F); Regs[ALU] = Regs[reg_d_ad]; Regs[reg_d_ad] = Regs[A]; Regs[A] = Regs[ALU]; break; case XCHD_RAM: reg_d_ad = cur_instr[instr_pntr++]; reg_d_ad = (ushort)(Regs[reg_d_ad] & 0x3F); Regs[ALU] = Regs[reg_d_ad]; Regs[reg_d_ad] = (ushort)((Regs[reg_d_ad] & 0xF0) | (Regs[A] & 0xF)); Regs[A] = (ushort)((Regs[A] & 0xF0) | (Regs[ALU] & 0xF)); break; case SEL_MB0: MB = 0; break; case SEL_MB1: MB = 1 << 11; break; case SEL_RB0: FlagBS = false; // register bank also changed here break; case SEL_RB1: FlagBS = true; // register bank also changed here break; case INC_RAM: reg_d_ad = cur_instr[instr_pntr++]; reg_d_ad = (ushort)(Regs[reg_d_ad] & 0x3F); Regs[reg_d_ad] = (ushort)((Regs[reg_d_ad] + 1) & 0xFF); break; case RES_TF: TF = false; break; case SET_ADDR_M3: Regs[ALU] &= 0xFF; Regs[ALU] |= 0x300; break; case MOVT_RAM_D: reg_d_ad = cur_instr[instr_pntr++]; reg_d_ad = (ushort)(Regs[reg_d_ad] & 0x3F); Regs[reg_d_ad] = Regs[cur_instr[instr_pntr++]]; //Console.WriteLine(reg_d_ad + " " + Regs[reg_d_ad] + " " + Regs[ALU] + " " + TotalExecutedCycles); break; case MOV: reg_d_ad = cur_instr[instr_pntr++]; Regs[reg_d_ad] = Regs[cur_instr[instr_pntr++]]; break; case MOVT: reg_d_ad = cur_instr[instr_pntr++]; Regs[reg_d_ad] = Regs[cur_instr[instr_pntr++]]; break; case MOVAR: Regs[cur_instr[instr_pntr++]] = Regs[A]; break; case MOVT_RAM: reg_d_ad = cur_instr[instr_pntr++]; reg_d_ad = (ushort)(Regs[reg_d_ad] & 0x3F); Regs[reg_d_ad] = Regs[A]; break; case ST_CNT: counter_en = true; next_T1_check = TotalExecutedCycles + 20; break; case STP_CNT: counter_en = timer_en = false; break; case ST_T: timer_en = true; timer_prescale = 0; break; case SET_ADDR_8: reg_d_ad = cur_instr[instr_pntr++]; Regs[reg_d_ad] &= 0xFF00; Regs[reg_d_ad] |= Regs[cur_instr[instr_pntr++]]; break; case MEM_ALU: Regs[ALU] = Regs[(ushort)(Regs[cur_instr[instr_pntr++]] & 0x3F)]; break; case PUSH: Regs[(Regs[PSW] & 0x7) * 2 + 8] = (ushort)(Regs[PC] & 0xFF); Regs[(Regs[PSW] & 0x7) * 2 + 8 + 1] = (ushort)(((Regs[PC] >> 8) & 0xF) | (Regs[PSW] & 0xF0)); Regs[PSW] = (ushort)((((Regs[PSW] & 0x7) + 1) & 0x7) | (Regs[PSW] & 0xF8)); break; case PULL: Regs[PSW] = (ushort)((((Regs[PSW] & 0x7) - 1) & 0x7) | (Regs[PSW] & 0xF8)); Regs[PC] = (ushort)(Regs[(Regs[PSW] & 0x7) * 2 + 8] & 0xFF); Regs[PC] |= (ushort)((Regs[(Regs[PSW] & 0x7) * 2 + 8 + 1] & 0xF) << 8); Regs[PSW] &= 0xF; Regs[PSW] |= (ushort)(Regs[(Regs[PSW] & 0x7) * 2 + 8 + 1] & 0xF0); RB = (ushort)(FlagBS ? 24 : 0); break; case PULL_PC: Regs[PSW] = (ushort)((((Regs[PSW] & 0x7) - 1) & 0x7) | (Regs[PSW] & 0xF8)); Regs[PC] = (ushort)(Regs[(Regs[PSW] & 0x7) * 2 + 8] & 0xFF); Regs[PC] |= (ushort)((Regs[(Regs[PSW] & 0x7) * 2 + 8 + 1] & 0xF) << 8); break; case EEA: EA = true; break; case DEA: EA = false; break; case RD_P: reg_d_ad = cur_instr[instr_pntr++]; reg_l_ad = cur_instr[instr_pntr++]; Regs[reg_d_ad] = ReadPort(reg_l_ad); Regs[PX + reg_l_ad] = Regs[reg_d_ad]; break; case WR_P: reg_d_ad = cur_instr[instr_pntr++]; reg_l_ad = cur_instr[instr_pntr++]; WritePort(reg_d_ad, (byte)Regs[reg_l_ad]); Regs[PX + reg_d_ad] = Regs[reg_l_ad]; break; case EM: INT_MSTR = true; break; case DM: INT_MSTR = false; break; case TEST_COND: reg_d_ad = cur_instr[instr_pntr++]; bool test_pass = true; if ((reg_d_ad == 0) && !TF) { test_pass = false; } if ((reg_d_ad == 1) && T0) { test_pass = false; } if ((reg_d_ad == 2) && !T0) { test_pass = false; } if ((reg_d_ad == 3) && T1) { test_pass = false; } if ((reg_d_ad == 4) && !T1) { test_pass = false; } if ((reg_d_ad == 5) && !F1) { test_pass = false; } if ((reg_d_ad == 6) && IRQPending) { test_pass = false; } if ((reg_d_ad == 7) && (Regs[A] == 0)) { test_pass = false; } if ((reg_d_ad == 8) && !FlagF0) { test_pass = false; } if ((reg_d_ad == 9) && (Regs[A] != 0)) { test_pass = false; } if ((reg_d_ad == 10) && FlagC) { test_pass = false; } if ((reg_d_ad == 11) && !FlagC) { test_pass = false; } if (test_pass) { cur_instr[instr_pntr + 6] = ALU2; } else { cur_instr[instr_pntr + 9] = IDLE; } break; } if (++irq_pntr == IRQS) { // then regular IRQ if (IRQPending && IntEn && INT_MSTR) { IRQPending = false; TraceCallback?.Invoke(new TraceInfo { Disassembly = "====IRQ====", RegisterInfo = "" }); IRQ_(0); IRQCallback(); instr_pntr = irq_pntr = 0; } else if (TIRQPending && TimIntEn && INT_MSTR) { TIRQPending = false; TraceCallback?.Invoke(new TraceInfo { Disassembly = "====TIRQ====", RegisterInfo = "" }); IRQ_(1); IRQCallback(); instr_pntr = irq_pntr = 0; } // otherwise start the next instruction else { PopulateCURINSTR(OP); instr_pntr = irq_pntr = 0; IRQS = -1; } } TotalExecutedCycles++; if (timer_en) { timer_prescale++; if (timer_prescale == 32) { timer_prescale = 0; if (Regs[TIM] == 255) { TF = true; if (TimIntEn) { TIRQPending = true; } } Regs[TIM] = (ushort)((Regs[TIM] + 1) & 0xFF); } } if (counter_en) { // NOTE: Odyssey 2 games tend to enable the counter within a few cycles of a falling edge and expect to count to take place if (!T1 && T1_old && (TotalExecutedCycles > next_T1_check)) { if (Regs[TIM] == 255) { TF = true; if (TimIntEn) { TIRQPending = true; //Console.WriteLine(TotalExecutedCycles); } } Regs[TIM] = (ushort)((Regs[TIM] + 1) & 0xFF); } } T1_old = T1; }
/// <summary> /// Runs a single CPU clock cycle /// </summary> public void ExecuteOne() { switch (cur_instr[instr_pntr++]) { // always the last tick within an opcode instruction cycle case END: OnExecFetch?.Invoke(RegPC0); TraceCallback?.Invoke(State()); opcode = (byte)Regs[DB]; instr_pntr = 0; FetchInstruction(); break; // used as timing 'padding' case IDLE: break; // load one register into another (or databus) case OP_LR8: LR8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; // Shift register n bit positions to the right (zero fill) case OP_SHFT_R: SR_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; // Shift register n bit positions to the left (zero fill) case OP_SHFT_L: SL_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; // x <- (x) ADD y case OP_ADD8: ADD8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; // x <- (x) ADD y (decimal) case OP_ADD8D: ADD8D_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; // A <- (A) + (C) case OP_LNK: ADD8_Func(Regs[A], (ushort)(FlagC ? 1 : 0)); break; // Clear ICB status bit case OP_DI: FlagICB = false; break; // Set ICB status bit case OP_EI: FlagICB = true; break; // x <- (y) XOR DB case OP_XOR8: XOR8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; // x <- (y) XOR DB (complement accumulator) case OP_XOR8C: XOR8C_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; // x <- (x) + 1 case OP_INC8: INC8_Func(cur_instr[instr_pntr++]); break; // x <- (y) & DB case OP_AND8: AND8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; // x <- (y) | DB case OP_OR8: OR8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; // DB + (x) + 1 (modify flags without saving result) case OP_CI: var tmpX = cur_instr[instr_pntr++]; var tmpOperand = Regs[DB]; INC8_Func(tmpX); ADD8_Func(tmpOperand, tmpX); break; // load one register into another (or databus) // ALU also runs flag status checking case OP_LR8_IO: LR8_IO_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; // DS op performed indirectly on the ScratchPad register pointed to by the ISAR case OP_DS_IS: SUB8_Func(Regs[ISAR], ONE); break; // ISAR is incremented case OP_IS_INC: Regs[ISAR] = (ushort)((Regs[ISAR] & 0x38) | ((Regs[ISAR] + 1) & 0x07)); break; // ISAR is decremented case OP_IS_DEC: Regs[ISAR] = (ushort)((Regs[ISAR] & 0x38) | ((Regs[ISAR] - 1) & 0x07)); break; // x <- (SR) (as pointed to by ISAR) case OP_LR_A_IS: LR8_Func(cur_instr[instr_pntr++], Regs[ISAR]); break; // x <- (SR) (as pointed to by ISAR) case OP_LR_IS_A: LR8_Func(Regs[ISAR], cur_instr[instr_pntr++]); break; // set the upper octal ISAR bits (b3,b4,b5) case OP_LISU: var isVala = (Regs[ISAR] & 0x07) | cur_instr[instr_pntr++]; Regs[ISAR] = (ushort)(isVala & 0x3F); break; // set the lower octal ISAR bits (b0,b1,b2) case OP_LISL: var isValb = (Regs[ISAR] & 0x38) | cur_instr[instr_pntr++]; Regs[ISAR] = (ushort)(isValb & 0x3F); break; // test operand against status register case OP_BT: instr_pntr = 0; if ((Regs[W] & cur_instr[instr_pntr++]) != 0) { PopulateCURINSTR( ROMC_01, // L IDLE, IDLE, IDLE, IDLE, IDLE, ROMC_00_S, // S IDLE, IDLE, END); } else { PopulateCURINSTR( ROMC_03_S, // S IDLE, IDLE, IDLE, ROMC_00_S, // S IDLE, IDLE, END); } break; // DC0 - A - set status only case OP_CM: var tmpDB = Regs[DB]; var tmpA = Regs[A]; SUB8_Func(tmpDB, tmpA); break; // Branch based on ISARL case OP_BR7: instr_pntr = 0; if ((Regs[ISAR] & 7) == 7) { PopulateCURINSTR( ROMC_03_S, // S //IDLE, <- lose a cycle that was stolen in the table IDLE, IDLE, ROMC_00_S, // S IDLE, IDLE, END); } else { PopulateCURINSTR( ROMC_01, // L //IDLE, <- lose a cycle that was stolen in the table IDLE, IDLE, IDLE, IDLE, ROMC_00_S, // S IDLE, IDLE, END); } break; // PC0 <- PC0+n+1 case OP_BF: instr_pntr = 0; if ((Regs[W] & cur_instr[instr_pntr++]) != 0) { PopulateCURINSTR( ROMC_03_S, // S IDLE, IDLE, IDLE, ROMC_00_S, // S IDLE, IDLE, END); } else { PopulateCURINSTR( ROMC_01, // L IDLE, IDLE, IDLE, IDLE, IDLE, ROMC_00_S, // S IDLE, IDLE, END); } break; // A <- (I/O Port 0 or 1) case OP_IN: Regs[cur_instr[instr_pntr++]] = ReadHardware(cur_instr[instr_pntr++]); //IN_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; // I/O Port 0 or 1 <- (A) case OP_OUT: WriteHardware(cur_instr[instr_pntr++], (byte)cur_instr[instr_pntr++]); //OUT_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; // Add the content of the SR register addressed by ISAR to A (Binary) case OP_AS_IS: ADD8_Func(A, Regs[ISAR]); break; // Add the content of the SR register addressed by ISAR to A (Decimal) case OP_ASD_IS: ADD8D_Func(A, Regs[ISAR]); break; // XOR the content of the SR register addressed by ISAR to A case OP_XS_IS: XOR8_Func(A, Regs[ISAR]); break; // AND the content of the SR register addressed by ISAR to A case OP_NS_IS: AND8_Func(A, Regs[ISAR]); break; // Set flags based on accumulator case OP_AFTEST: break; // subtraction case OP_SUB8: SUB8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; // instruction fetch // The device whose address space includes the contents of the PC0 register must place on the data bus the op code addressed by PC0; // then all devices increments the content of PC0. // CYCLE LENGTH: S case ROMC_00_S: Regs[DB] = ReadMemory(RegPC0++); break; // instruction fetch // The device whose address space includes the contents of the PC0 register must place on the data bus the op code addressed by PC0; // then all devices increments the content of PC0. // CYCLE LENGTH: L case ROMC_00_L: Regs[DB] = ReadMemory(RegPC0++); break; // The device whose address space includes the contents of the PC0 register must place on the data bus the contents of the memory location // addressed by by PC0; then all devices add the 8-bit value on the data bus, as a signed binary number, to PC0 // CYCLE LENGTH: L case ROMC_01: Regs[DB] = ReadMemory(RegPC0); ADDS_Func(PC0l, PC0h, DB, ZERO); break; // The device whose DC0 address addresses a memory word within the address space of that device must place on the data bus the contents // of the memory location addressed by DC0; then all devices increment DC0 // CYCLE LENGTH: L case ROMC_02: Regs[DB] = ReadMemory(RegDC0++); break; // Similar to 0x00, except that it is used for Immediate Operand fetches (using PC0) instead of instruction fetches // CYCLE LENGTH: S case ROMC_03_S: Regs[DB] = ReadMemory(RegPC0++); Regs[IO] = Regs[DB]; break; // Similar to 0x00, except that it is used for Immediate Operand fetches (using PC0) instead of instruction fetches // CYCLE LENGTH: L case ROMC_03_L: Regs[DB] = ReadMemory(RegPC0++); Regs[IO] = Regs[DB]; break; // Copy the contents of PC1 into PC0 // CYCLE LENGTH: S case ROMC_04: RegPC0 = RegPC1; break; // Store the data bus contents into the memory location pointed to by DC0; increment DC0 // CYCLE LENGTH: L case ROMC_05: WriteMemory(RegDC0++, (byte)Regs[DB]); break; // Place the high order byte of DC0 on the data bus // CYCLE LENGTH: L case ROMC_06: Regs[DB] = (byte)Regs[DC0h]; break; // Place the high order byte of PC1 on the data bus // CYCLE LENGTH: L case ROMC_07: Regs[DB] = (byte)Regs[PC1h]; break; // All devices copy the contents of PC0 into PC1. The CPU outputs zero on the data bus in this ROMC state. // Load the data bus into both halves of PC0, this clearing the register. // CYCLE LENGTH: L case ROMC_08: RegPC1 = RegPC0; Regs[DB] = 0; Regs[PC0h] = 0; Regs[PC0l] = 0; break; // The device whose address space includes the contents of the DC0 register must place the low order byte of DC0 onto the data bus // CYCLE LENGTH: L case ROMC_09: Regs[DB] = (byte)Regs[DC0l]; break; // All devices add the 8-bit value on the data bus, treated as a signed binary number, to the data counter // CYCLE LENGTH: L case ROMC_0A: ADDS_Func(DC0l, DC0h, DB, ZERO); break; // The device whose address space includes the value in PC1 must place the low order byte of PC1 on the data bus // CYCLE LENGTH: L case ROMC_0B: Regs[DB] = (byte)Regs[PC1l]; break; // The device whose address space includes the contents of the PC0 register must place the contents of the memory word addressed by PC0 // onto the data bus; then all devices move the value that has just been placed on the data bus into the low order byte of PC0 // CYCLE LENGTH: L case ROMC_0C: Regs[DB] = ReadMemory(RegPC0); Regs[PC0l] = Regs[DB]; break; // All devices store in PC1 the current contents of PC0, incremented by 1; PC0 is unaltered // CYCLE LENGTH: S case ROMC_0D: RegPC1 = (ushort)(RegPC0 + 1); break; // The device whose address space includes the contents of PC0 must place the contents of the word addressed by PC0 onto the data bus. // The value on the data bus is then moved to the low order byte of DC0 by all devices // CYCLE LENGTH: L case ROMC_0E: Regs[DB] = ReadMemory(RegPC0); Regs[DC0l] = Regs[DB]; break; // The interrupting device with the highest priority must place the low order byte of the interrupt vector on the data bus. // All devices must copy the contents of PC0 into PC1. All devices must move the contents of the data bus into the low order byte of PC0 // CYCLE LENGTH: L case ROMC_0F: throw new NotImplementedException("ROMC 0x0F not implemented"); // Inhibit any modification to the interrupt priority logic // CYCLE LENGTH: L case ROMC_10: throw new NotImplementedException("ROMC 0x10 not implemented"); // The device whose memory space includes the contents of PC0 must place the contents of the addressed memory word on the data bus. // All devices must then move the contents of the data bus to the upper byte of DC0 // CYCLE LENGTH: L case ROMC_11: Regs[DB] = ReadMemory(RegPC0); Regs[DC0h] = Regs[DB]; break; // All devices copy the contents of PC0 into PC1. All devices then move the contents of the data bus into the low order byte of PC0 // CYCLE LENGTH: L case ROMC_12: RegPC1 = RegPC0; Regs[PC0l] = Regs[DB]; break; // The interrupting device with the highest priority must move the high order half of the interrupt vector onto the data bus. // All devices must move the conetnts of the data bus into the high order byte of of PC0. The interrupting device resets its // interrupt circuitry (so that it is no longer requesting CPU servicing and can respond to another interrupt) // CYCLE LENGTH: L case ROMC_13: throw new NotImplementedException("ROMC 0x13 not implemented"); // All devices move the contents of the data bus into the high order byte of PC0 // CYCLE LENGTH: L case ROMC_14: Regs[PC0h] = Regs[DB]; break; // All devices move the contents of the data bus into the high order byte of PC1 // CYCLE LENGTH: L case ROMC_15: Regs[PC1h] = Regs[DB]; break; // All devices move the contents of the data bus into the high order byte of DC0 // CYCLE LENGTH: L case ROMC_16: Regs[DC0h] = Regs[DB]; break; // All devices move the contents of the data bus into the low order byte of PC0 // CYCLE LENGTH: L case ROMC_17: Regs[PC0l] = Regs[DB]; break; // All devices move the contents of the data bus into the low order byte of PC1 // CYCLE LENGTH: L case ROMC_18: Regs[PC1l] = Regs[DB]; break; // All devices move the contents of the data bus into the low order byte of DC0 // CYCLE LENGTH: L case ROMC_19: Regs[DC0l] = Regs[DB]; break; // During the prior cycle, an I/O port timer or interrupt control register was addressed; the device containing the addressed // port must move the current contents of the data bus into the addressed port // CYCLE LENGTH: L case ROMC_1A: WriteHardware(Regs[IO], (byte)Regs[DB]); break; // During the prior cycle, the data bus specified the address of an I/O port. The device containing the addressed I/O port // must place the contents of the I/O port on the data bus. (Note that the contents of the timer and interrupt control // registers cannot be read back onto the data bus) // CYCLE LENGTH: L case ROMC_1B: //Regs[DB] = ReadHardware(Regs[IO]); Regs[DB] = ReadHardware(Regs[IO]); break; // None // CYCLE LENGTH: S case ROMC_1C_S: break; // None // CYCLE LENGTH: L case ROMC_1C_L: break; // Devices with DC0 and DC1 registers must switch registers. Devices without a DC1 register perform no operation // CYCLE LENGTH: S case ROMC_1D: // we have no DC1 in this implementation break; // The device whose address space includes the contents of PC0 must place the low order byte of PC0 onto the data bus // CYCLE LENGTH: L case ROMC_1E: Regs[DB] = (byte)Regs[PC0l]; break; // The device whose address space includes the contents of PC0 must place the high order byte of PC0 onto the data bus // CYCLE LENGTH: L case ROMC_1F: Regs[DB] = (byte)Regs[PC0h]; break; } TotalExecutedCycles++; }
// Execute instructions public void ExecuteOne() { //Console.Write(opcode_see + " "); //Console.WriteLine(Regs[PC] + " "); switch (cur_instr[instr_pntr++]) { case IDLE: // do nothing break; case OP: // Read the opcode of the next instruction OnExecFetch?.Invoke(PC); TraceCallback?.Invoke(State()); CDLCallback?.Invoke(PC, eCDLogMemFlags.FetchFirst); FetchInstruction(ReadMemory(Regs[PC]++)); instr_pntr = 0; irq_pntr = -1; break; case OP_PG_2: FetchInstruction2(ReadMemory(Regs[PC]++)); instr_pntr = 0; irq_pntr = -1; break; case OP_PG_3: FetchInstruction3(ReadMemory(Regs[PC]++)); instr_pntr = 0; irq_pntr = -1; break; case RD: Read_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case RD_INC: Read_Inc_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case RD_INC_OP: Read_Inc_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); switch (cur_instr[instr_pntr++]) { case AND8: AND8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case ADD8: ADD8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case ADC8: ADC8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case OR8: OR8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case XOR8: XOR8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case BIT: BIT_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case SUB8: SUB8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case SBC8: SBC8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case CMP8: CMP8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case DEC16: DEC16_Func(cur_instr[instr_pntr++]); break; case ADD8BR: ADD8BR_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case TR: TR_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case SET_ADDR: reg_d_ad = cur_instr[instr_pntr++]; reg_h_ad = cur_instr[instr_pntr++]; reg_l_ad = cur_instr[instr_pntr++]; Regs[reg_d_ad] = (ushort)((Regs[reg_h_ad] << 8) | Regs[reg_l_ad]); break; case JPE: if (!FlagE) { instr_pntr = 44; irq_pntr = 10; } break; case IDX_DCDE: Index_decode(); break; case IDX_OP_BLD: Index_Op_Builder(); break; case EA_8: Regs[IDX_EA] = (ushort)(Regs[indexed_reg] + (((Regs[ALU2] & 0x80) == 0x80) ? (Regs[ALU2] | 0xFF00) : Regs[ALU2])); Index_Op_Builder(); break; case PUL_n: PUL_n_BLD(cur_instr[instr_pntr++]); break; case SET_ADDR_PUL: Regs[cur_instr[instr_pntr++]] = (ushort)((Regs[ALU2] << 8) + Regs[ADDR]); PUL_n_BLD(cur_instr[instr_pntr++]); break; case LD_8: LD_8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case LD_16: LD_16_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case LEA: LEA_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case ANDCC: Regs[CC] &= Regs[instr_pntr++]; break; } break; case WR: Write_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case WR_DEC_LO: Write_Dec_Lo_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case WR_DEC_HI: Write_Dec_HI_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case WR_DEC_LO_OP: Write_Dec_Lo_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); switch (cur_instr[instr_pntr++]) { case PSH_n: PSH_n_BLD(cur_instr[instr_pntr++]); break; } break; case WR_DEC_HI_OP: Write_Dec_HI_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); switch (cur_instr[instr_pntr++]) { case PSH_n: PSH_n_BLD(cur_instr[instr_pntr++]); break; } break; case WR_HI: Write_Hi_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case WR_HI_INC: Write_Hi_Inc_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case WR_LO_INC: Write_Lo_Inc_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case TR: TR_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case LD_8: LD_8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case LD_16: LD_16_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case ST_8: ST_8_Func(cur_instr[instr_pntr++]); break; case ST_16: ST_16_Func(cur_instr[instr_pntr++]); break; case LEA: LEA_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case EXG: EXG_Func(cur_instr[instr_pntr++]); break; case IDX_OP_BLD: Index_Op_Builder(); break; case EA_16: Regs[IDX_EA] = (ushort)(Regs[indexed_reg] + Regs[ADDR]); Index_Op_Builder(); break; case TFR: TFR_Func(cur_instr[instr_pntr++]); break; case SET_ADDR: reg_d_ad = cur_instr[instr_pntr++]; reg_h_ad = cur_instr[instr_pntr++]; reg_l_ad = cur_instr[instr_pntr++]; // Console.WriteLine(reg_d_ad + " " + reg_h_ad + " " + reg_l_ad); // Console.WriteLine(Regs[reg_d_ad] + " " + Regs[reg_h_ad] + " " + Regs[reg_l_ad]); Regs[reg_d_ad] = (ushort)((Regs[reg_h_ad] << 8) | Regs[reg_l_ad]); break; case NEG: NEG_8_Func(cur_instr[instr_pntr++]); break; case TST: TST_Func(cur_instr[instr_pntr++]); break; case CLR: CLR_Func(cur_instr[instr_pntr++]); break; case SEX: SEX_Func(cur_instr[instr_pntr++]); break; case ABX: Regs[X] += Regs[B]; break; case MUL: Mul_Func(); break; case SET_F_I: FlagI = true; FlagF = true; break; case SET_I: FlagI = true; break; case SET_E: FlagE = true; break; case CLR_E: FlagE = false; break; case ANDCC: Regs[CC] &= Regs[instr_pntr++]; break; case PSH_n: PSH_n_BLD(cur_instr[instr_pntr++]); break; case PUL_n: PUL_n_BLD(cur_instr[instr_pntr++]); break; case ADD16BR: ADD16BR_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case ADD8BR: ADD8BR_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case ADD8: ADD8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case SUB8: SUB8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case ADC8: ADC8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case SBC8: SBC8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case CMP8: CMP8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case INC16: INC16_Func(cur_instr[instr_pntr++]); break; case INC8: INC8_Func(cur_instr[instr_pntr++]); break; case DEC16: DEC16_Func(cur_instr[instr_pntr++]); break; case SUB16: SUB16_Func(cur_instr[instr_pntr++]); break; case ADD16: ADD16_Func(cur_instr[instr_pntr++]); break; case CMP16: CMP16_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case CMP16D: CMP16D_Func(cur_instr[instr_pntr++]); break; case DEC8: DEC8_Func(cur_instr[instr_pntr++]); break; case ROL: ROL_Func(cur_instr[instr_pntr++]); break; case ROR: ROR_Func(cur_instr[instr_pntr++]); break; case COM: COM_Func(cur_instr[instr_pntr++]); break; case DA: DA_Func(cur_instr[instr_pntr++]); break; case AND8: AND8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case XOR8: XOR8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case OR8: OR8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case ASL: ASL_Func(cur_instr[instr_pntr++]); break; case ASR: ASR_Func(cur_instr[instr_pntr++]); break; case LSR: LSR_Func(cur_instr[instr_pntr++]); break; case BIT: BIT_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case CWAI: if (NMIPending) { NMIPending = false; Regs[ADDR] = 0xFFFC; PopulateCURINSTR(RD_INC, ALU, ADDR, RD_INC, ALU2, ADDR, SET_ADDR, PC, ALU, ALU2); irq_pntr = -1; IRQS = 3; TraceCallback?.Invoke(new(disassembly: "====CWAI NMI====", registerInfo: string.Empty)); } else if (FIRQPending && !FlagF) { FIRQPending = false; Regs[ADDR] = 0xFFF6; PopulateCURINSTR(RD_INC, ALU, ADDR, RD_INC, ALU2, ADDR, SET_ADDR, PC, ALU, ALU2); irq_pntr = -1; IRQS = 3; TraceCallback?.Invoke(new(disassembly: "====CWAI FIRQ====", registerInfo: string.Empty)); } else if (IRQPending && !FlagI) { IRQPending = false; Regs[ADDR] = 0xFFF8; PopulateCURINSTR(RD_INC, ALU, ADDR, RD_INC, ALU2, ADDR, SET_ADDR, PC, ALU, ALU2); irq_pntr = -1; IRQS = 3; TraceCallback?.Invoke(new(disassembly: "====CWAI IRQ====", registerInfo: string.Empty)); } else { PopulateCURINSTR(CWAI); irq_pntr = instr_pntr = 0; IRQS = -1; } instr_pntr = 0; break; case SYNC: if (NMIPending) { NMIPending = false; IN_SYNC = false; Regs[ADDR] = 0xFFFC; PopulateCURINSTR(RD_INC, ALU, ADDR, RD_INC, ALU2, ADDR, SET_ADDR, PC, ALU, ALU2); irq_pntr = -1; IRQS = 3; TraceCallback?.Invoke(new(disassembly: "====SYNC NMI====", registerInfo: string.Empty)); } else if (FIRQPending) { if (!FlagF) { FIRQPending = false; Regs[ADDR] = 0xFFF6; PopulateCURINSTR(RD_INC, ALU, ADDR, RD_INC, ALU2, ADDR, SET_ADDR, PC, ALU, ALU2); irq_pntr = -1; IRQS = 3; TraceCallback?.Invoke(new(disassembly: "====SYNC FIRQ====", registerInfo: string.Empty)); } else { FIRQPending = false; IN_SYNC = false; IRQS = 2; instr_pntr = irq_pntr = 0; PopulateCURINSTR(IDLE, IDLE); } } else if (IRQPending) { if (!FlagI) { IRQPending = false; IN_SYNC = false; Regs[ADDR] = 0xFFF8; PopulateCURINSTR(RD_INC, ALU, ADDR, RD_INC, ALU2, ADDR, SET_ADDR, PC, ALU, ALU2); irq_pntr = -1; IRQS = 3; TraceCallback?.Invoke(new(disassembly: "====SYNC IRQ====", registerInfo: string.Empty)); } else { FIRQPending = false; IN_SYNC = false; IRQS = 2; instr_pntr = irq_pntr = 0; PopulateCURINSTR(IDLE, IDLE); } } else { IN_SYNC = true; IRQS = -1; instr_pntr = irq_pntr = 0; PopulateCURINSTR(SYNC); } break; } if (++irq_pntr == IRQS) { // NMI has priority if (NMIPending) { NMIPending = false; TraceCallback?.Invoke(new(disassembly: "====NMI====", registerInfo: string.Empty)); NMI_(); NMICallback(); instr_pntr = irq_pntr = 0; } // fast IRQ has next priority else if (FIRQPending && !FlagF) { FIRQPending = false; TraceCallback?.Invoke(new(disassembly: "====FIRQ====", registerInfo: string.Empty)); FIRQ_(); FIRQCallback(); instr_pntr = irq_pntr = 0; } // then regular IRQ else if (IRQPending && !FlagI) { IRQPending = false; TraceCallback?.Invoke(new(disassembly: "====IRQ====", registerInfo: string.Empty)); IRQ_(); IRQCallback(); instr_pntr = irq_pntr = 0; } // otherwise start the next instruction else { PopulateCURINSTR(OP); instr_pntr = irq_pntr = 0; IRQS = -1; } } TotalExecutedCycles++; }
// Execute instructions public void ExecuteOne() { bus_pntr++; mem_pntr++; switch (cur_instr[instr_pntr++]) { case IDLE: // do nothing break; case OP: // should never reach here break; case OP_F: // Read the opcode of the next instruction OnExecFetch?.Invoke(RegPC); TraceCallback?.Invoke(State()); opcode = FetchMemory(RegPC++); FetchInstruction(); temp_R = (byte)(Regs[R] & 0x7F); temp_R++; temp_R &= 0x7F; Regs[R] = (byte)((Regs[R] & 0x80) | temp_R); instr_pntr = bus_pntr = mem_pntr = irq_pntr = 0; I_skip = true; break; case HALT: halted = true; // NOTE: Check how halt state effects the DB Regs[DB] = 0xFF; temp_R = (byte)(Regs[R] & 0x7F); temp_R++; temp_R &= 0x7F; Regs[R] = (byte)((Regs[R] & 0x80) | temp_R); break; case RD: Read_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case WR: Write_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case RD_INC: Read_INC_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case RD_INC_TR_PC: Read_INC_TR_PC_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case RD_OP: if (cur_instr[instr_pntr++] == 1) { Read_INC_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); } else { Read_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); } switch (cur_instr[instr_pntr++]) { case ADD8: ADD8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case ADC8: ADC8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case SUB8: SUB8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case SBC8: SBC8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case AND8: AND8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case XOR8: XOR8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case OR8: OR8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case CP8: CP8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case TR: TR_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; } break; case WR_INC: Write_INC_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case WR_DEC: Write_DEC_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case WR_TR_PC: Write_TR_PC_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case WR_INC_WA: Write_INC_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); Regs[W] = Regs[A]; break; case TR: TR_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case TR16: TR16_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case ADD16: ADD16_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case ADD8: ADD8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case SUB8: SUB8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case ADC8: ADC8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case ADC16: ADC_16_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case SBC8: SBC8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case SBC16: SBC_16_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case INC16: INC16_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case INC8: INC8_Func(cur_instr[instr_pntr++]); break; case DEC16: DEC16_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case DEC8: DEC8_Func(cur_instr[instr_pntr++]); break; case RLC: RLC_Func(cur_instr[instr_pntr++]); break; case RL: RL_Func(cur_instr[instr_pntr++]); break; case RRC: RRC_Func(cur_instr[instr_pntr++]); break; case RR: RR_Func(cur_instr[instr_pntr++]); break; case CPL: CPL_Func(cur_instr[instr_pntr++]); break; case DA: DA_Func(cur_instr[instr_pntr++]); break; case SCF: SCF_Func(cur_instr[instr_pntr++]); break; case CCF: CCF_Func(cur_instr[instr_pntr++]); break; case AND8: AND8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case XOR8: XOR8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case OR8: OR8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case CP8: CP8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case SLA: SLA_Func(cur_instr[instr_pntr++]); break; case SRA: SRA_Func(cur_instr[instr_pntr++]); break; case SRL: SRL_Func(cur_instr[instr_pntr++]); break; case SLL: SLL_Func(cur_instr[instr_pntr++]); break; case BIT: BIT_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case I_BIT: I_BIT_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case RES: RES_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case SET: SET_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case EI: EI_pending = 2; break; case DI: IFF1 = IFF2 = false; break; case EXCH: EXCH_16_Func(F_s, A_s, F, A); break; case EXX: EXCH_16_Func(C_s, B_s, C, B); EXCH_16_Func(E_s, D_s, E, D); EXCH_16_Func(L_s, H_s, L, H); break; case EXCH_16: EXCH_16_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case PREFIX: ushort src_t = PRE_SRC; NO_prefix = false; if (PRE_SRC == CBpre) { CB_prefix = true; } if (PRE_SRC == EXTDpre) { EXTD_prefix = true; } if (PRE_SRC == IXpre) { IX_prefix = true; } if (PRE_SRC == IYpre) { IY_prefix = true; } if (PRE_SRC == IXCBpre) { IXCB_prefix = true; } if (PRE_SRC == IYCBpre) { IYCB_prefix = true; } // only the first prefix in a double prefix increases R, although I don't know how / why if (PRE_SRC < 4) { temp_R = (byte)(Regs[R] & 0x7F); temp_R++; temp_R &= 0x7F; Regs[R] = (byte)((Regs[R] & 0x80) | temp_R); } opcode = FetchMemory(RegPC++); FetchInstruction(); instr_pntr = bus_pntr = mem_pntr = irq_pntr = 0; I_skip = true; // for prefetched case, the PC stays on the BUS one cycle longer if ((src_t == IXCBpre) || (src_t == IYCBpre)) { BUSRQ[0] = PCh; } break; case ASGN: ASGN_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case ADDS: ADDS_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case EI_RETI: // NOTE: This is needed for systems using multiple interrupt sources, it triggers the next interrupt // Not currently implemented here iff1 = iff2; break; case EI_RETN: iff1 = iff2; break; case OUT: OUT_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case OUT_INC: OUT_INC_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case IN: IN_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case IN_INC: IN_INC_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case IN_A_N_INC: IN_A_N_INC_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case NEG: NEG_8_Func(cur_instr[instr_pntr++]); break; case INT_MODE: interruptMode = cur_instr[instr_pntr++]; break; case RRD: RRD_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case RLD: RLD_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case SET_FL_LD_R: DEC16_Func(C, B); SET_FL_LD_Func(); Ztemp1 = cur_instr[instr_pntr++]; Ztemp2 = cur_instr[instr_pntr++]; Ztemp3 = cur_instr[instr_pntr++]; if (((Regs[C] | (Regs[B] << 8)) != 0) && (Ztemp3 > 0)) { PopulateCURINSTR (DEC16, PCl, PCh, DEC16, PCl, PCh, TR16, Z, W, PCl, PCh, INC16, Z, W, Ztemp2, E, D); PopulateBUSRQ(D, D, D, D, D); PopulateMEMRQ(0, 0, 0, 0, 0); IRQS = 5; instr_pntr = mem_pntr = bus_pntr = irq_pntr = 0; I_skip = true; } else { if (Ztemp2 == INC16) { INC16_Func(E, D); } else { DEC16_Func(E, D); } } break; case SET_FL_CP_R: SET_FL_CP_Func(); Ztemp1 = cur_instr[instr_pntr++]; Ztemp2 = cur_instr[instr_pntr++]; Ztemp3 = cur_instr[instr_pntr++]; if (((Regs[C] | (Regs[B] << 8)) != 0) && (Ztemp3 > 0) && !FlagZ) { PopulateCURINSTR (DEC16, PCl, PCh, DEC16, PCl, PCh, TR16, Z, W, PCl, PCh, INC16, Z, W, Ztemp2, L, H); PopulateBUSRQ(H, H, H, H, H); PopulateMEMRQ(0, 0, 0, 0, 0); IRQS = 5; instr_pntr = mem_pntr = bus_pntr = irq_pntr = 0; I_skip = true; } else { if (Ztemp2 == INC16) { INC16_Func(L, H); } else { DEC16_Func(L, H); } } break; case SET_FL_IR: ushort dest_t = cur_instr[instr_pntr++]; TR_Func(dest_t, cur_instr[instr_pntr++]); SET_FL_IR_Func(dest_t); break; case FTCH_DB: FTCH_DB_Func(); break; case WAIT: if (FlagW) { instr_pntr--; bus_pntr--; mem_pntr--; I_skip = true; } break; case RST: Regs[Z] = cur_instr[instr_pntr++]; Regs[W] = 0; break; case REP_OP_I: Write_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); Ztemp4 = cur_instr[instr_pntr++]; if (Ztemp4 == DEC16) { TR16_Func(Z, W, C, B); DEC16_Func(Z, W); DEC8_Func(B); // take care of other flags // taken from 'undocumented z80 documented' and Fuse FlagN = Regs[ALU].Bit(7); FlagH = FlagC = ((Regs[ALU] + Regs[C] - 1) & 0xFF) < Regs[ALU]; FlagP = TableParity[((Regs[ALU] + Regs[C] - 1) & 7) ^ Regs[B]]; } else { TR16_Func(Z, W, C, B); INC16_Func(Z, W); DEC8_Func(B); // take care of other flags // taken from 'undocumented z80 documented' and Fuse FlagN = Regs[ALU].Bit(7); FlagH = FlagC = ((Regs[ALU] + Regs[C] + 1) & 0xFF) < Regs[ALU]; FlagP = TableParity[((Regs[ALU] + Regs[C] + 1) & 7) ^ Regs[B]]; } Ztemp1 = cur_instr[instr_pntr++]; Ztemp2 = cur_instr[instr_pntr++]; Ztemp3 = cur_instr[instr_pntr++]; if ((Regs[B] != 0) && (Ztemp3 > 0)) { PopulateCURINSTR (IDLE, IDLE, DEC16, PCl, PCh, DEC16, PCl, PCh, Ztemp2, L, H); PopulateBUSRQ(H, H, H, H, H); PopulateMEMRQ(0, 0, 0, 0, 0); IRQS = 5; instr_pntr = mem_pntr = bus_pntr = irq_pntr = 0; I_skip = true; } else { if (Ztemp2 == INC16) { INC16_Func(L, H); } else { DEC16_Func(L, H); } } break; case REP_OP_O: OUT_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); Ztemp4 = cur_instr[instr_pntr++]; if (Ztemp4 == DEC16) { DEC16_Func(L, H); DEC8_Func(B); TR16_Func(Z, W, C, B); DEC16_Func(Z, W); } else { INC16_Func(L, H); DEC8_Func(B); TR16_Func(Z, W, C, B); INC16_Func(Z, W); } // take care of other flags // taken from 'undocumented z80 documented' FlagN = Regs[ALU].Bit(7); FlagH = FlagC = (Regs[ALU] + Regs[L]) > 0xFF; FlagP = TableParity[((Regs[ALU] + Regs[L]) & 7) ^ (Regs[B])]; Ztemp1 = cur_instr[instr_pntr++]; Ztemp2 = cur_instr[instr_pntr++]; Ztemp3 = cur_instr[instr_pntr++]; if ((Regs[B] != 0) && (Ztemp3 > 0)) { PopulateCURINSTR (IDLE, IDLE, DEC16, PCl, PCh, DEC16, PCl, PCh, IDLE); PopulateBUSRQ(B, B, B, B, B); PopulateMEMRQ(0, 0, 0, 0, 0); IRQS = 5; instr_pntr = mem_pntr = bus_pntr = irq_pntr = 0; I_skip = true; } break; case IORQ: IRQACKCallback(); break; } if (I_skip) { I_skip = false; } else if (++irq_pntr == IRQS) { if (EI_pending > 0) { EI_pending--; if (EI_pending == 0) { IFF1 = IFF2 = true; } } // NMI has priority if (nonMaskableInterruptPending) { nonMaskableInterruptPending = false; TraceCallback?.Invoke(new(disassembly: "====NMI====", registerInfo: string.Empty)); iff2 = iff1; iff1 = false; NMI_(); NMICallback(); instr_pntr = mem_pntr = bus_pntr = irq_pntr = 0; temp_R = (byte)(Regs[R] & 0x7F); temp_R++; temp_R &= 0x7F; Regs[R] = (byte)((Regs[R] & 0x80) | temp_R); halted = false; } // if we are processing an interrrupt, we need to modify the instruction vector else if (iff1 && FlagI) { iff1 = iff2 = false; EI_pending = 0; TraceCallback?.Invoke(new(disassembly: "====IRQ====", registerInfo: string.Empty)); switch (interruptMode) { case 0: // Requires something to be pushed onto the data bus // we'll assume it's a zero for now INTERRUPT_0(0); break; case 1: INTERRUPT_1(); break; case 2: INTERRUPT_2(); break; } IRQCallback(); instr_pntr = mem_pntr = bus_pntr = irq_pntr = 0; temp_R = (byte)(Regs[R] & 0x7F); temp_R++; temp_R &= 0x7F; Regs[R] = (byte)((Regs[R] & 0x80) | temp_R); halted = false; } // otherwise start a new normal access else if (!halted) { PopulateCURINSTR (IDLE, WAIT, OP_F, OP); PopulateBUSRQ(PCh, 0, 0, 0); PopulateMEMRQ(PCh, 0, 0, 0); IRQS = 4; instr_pntr = mem_pntr = bus_pntr = irq_pntr = 0; } else { instr_pntr = mem_pntr = bus_pntr = irq_pntr = 0; } } TotalExecutedCycles++; }