/// <summary> /// Actually executes the instruction that was determine by the Step phase. /// In most cases, the whole of the instrucftion is run, but for some opcodes, /// some additional code has to be run. /// It is possible for those cases (and conditional jumps) that some extra clock /// ticks have to run after execution. /// </summary> /// <returns>How many clock ticks have to run after the opcode execution</returns> internal byte ExecuteInstruction() { // Prepare for program counter movement, but wait for instruction execution. // Overwrite _state.NextPC in the instruction lambdas if you want to implement jumps. // NOTE(Cristian): If we don't differentiate this case, the CALL instruction of the // interrupt will be added to _state.NextPC, which will in turn be written // into the stack. This means that when we RET, we would have jumped // into the address we *should* have jumped plus some meaningless offset! if (!_state.InterruptInProgress) { this._state.NextPC = (ushort)(_state.Registers.PC + _state.CurrentInstruction.Length); } if (!_state.CurrentInstruction.CB) { CPUInstructions.RunInstruction(this, (byte)_state.CurrentInstruction.OpCode, _state.CurrentInstruction.Literal); } else { CPUCBInstructions.RunCBInstruction(this, (byte)_state.CurrentInstruction.OpCode, _state.CurrentInstruction.Literal); } // Push the next program counter value into the real program counter! _state.Registers.PC = this._state.NextPC; // We calculate how many more ticks have to run byte remainingSteps = GetPostTicks(); return(remainingSteps); }
public void Cycle() { mtx.WaitOne(); // Normal Cycle reg.CycleCount++; var totalClockM = 0; var totalClockT = 0; if (_halt) { totalClockM += 1; totalClockT += 4; } else { var op = memory.ReadByte(reg.PC); reg.PC++; CPUInstructions.opcodes[op](this); totalClockM += reg.lastClockM; totalClockT += reg.lastClockT; } // Check Interrupts if (reg.InterruptEnable && reg.EnabledInterrupts != 0 && reg.TriggerInterrupts != 0) { _halt = false; reg.InterruptEnable = false; var interruptsFired = reg.EnabledInterrupts & reg.TriggerInterrupts; if ((interruptsFired & Flags.INT_VBLANK) > 0) { reg.TriggerInterrupts &= (byte)~Flags.INT_VBLANK; CPUInstructions.RSTXX(this, Addresses.INT_VBLANK); // V-Blank totalClockM += reg.lastClockM; totalClockT += reg.lastClockT; } else if ((interruptsFired & Flags.INT_LCDSTAT) > 0) { Console.WriteLine("Handling LCDSTAT Int"); reg.TriggerInterrupts &= (byte)~Flags.INT_LCDSTAT; CPUInstructions.RSTXX(this, Addresses.INT_LCDSTAT); // LCD Stat totalClockM += reg.lastClockM; totalClockT += reg.lastClockT; } else if ((interruptsFired & Flags.INT_TIMER) > 0) { Console.WriteLine("Handling Timer Int"); reg.TriggerInterrupts &= (byte)~Flags.INT_TIMER; CPUInstructions.RSTXX(this, Addresses.INT_TIMER); // Timer totalClockM += reg.lastClockM; totalClockT += reg.lastClockT; } else if ((interruptsFired & Flags.INT_SERIAL) > 0) { Console.WriteLine("Handling Serial Int"); reg.TriggerInterrupts &= (byte)~Flags.INT_SERIAL; CPUInstructions.RSTXX(this, Addresses.INT_SERIAL); // Serial totalClockM += reg.lastClockM; totalClockT += reg.lastClockT; } else if ((interruptsFired & Flags.INT_JOYPAD) > 0) { reg.TriggerInterrupts &= (byte)~Flags.INT_JOYPAD; CPUInstructions.RSTXX(this, Addresses.INT_JOYPAD); // Joypad Interrupt totalClockM += reg.lastClockM; totalClockT += reg.lastClockT; } else { reg.InterruptEnable = true; } } clockM += totalClockM; clockT += totalClockT; if (!stopped) { // GPU gpu.Cycle(); // Timers timer.Increment(); } mtx.ReleaseMutex(); }