private Instruction InterruptHandler(Interrupts interrupt) { // Handle interrupt with a CALL instruction to the interrupt handler Instruction instruction = new Instruction(); instruction.OpCode = 0xCD; // CALL! byte lowOpcode = (byte)instruction.OpCode; instruction.Length = CPUInstructionLengths.Get(lowOpcode); instruction.Literal = this.interruptHandlers[(Interrupts)interrupt]; instruction.Ticks = CPUInstructionPreClocks.Get(lowOpcode); instruction.Name = CPUInstructionNames.Get(lowOpcode); instruction.Description = CPUInstructionDescriptions.Get(lowOpcode); // Disable interrupts during interrupt handling and clear the current one this._interruptController.InterruptMasterEnable = false; byte IF = this._memory.LowLevelRead((ushort)MMR.IF); IF &= (byte)~(byte)interrupt; this._memory.LowLevelWrite((ushort)MMR.IF, IF); return(instruction); }
/// <summary> /// Poor man's dissambly (for now) /// </summary> /// <returns></returns> public IEnumerable <IInstruction> Disassamble(ushort startAddress, bool permissive = true) { // TODO(Cristian): Complete the on-demand disassembly var stoppers = GetShowStoppers(); var dirJumps = GetDirectJumps(); var relJumps = GetRelativeJumps(); var restarts = GetRestarts(); _disAddressToVisit.Push(startAddress); // Initial address while (_disAddressToVisit.Count > 0) { ushort address = _disAddressToVisit.Pop(); // If we already saw this address, we move on if (_disVisitedAddresses.Contains(address)) { continue; } _disVisitedAddresses.Add(address); // NOTE(Cristian): The 0xCB external opcodes all exists, so we just need to check // that the first byte is 0xCB to know that the instruction is valid if (CPUInstructionLengths.Get((byte)_memory.Read(address)) != 0) { try { // Get the instruction and added to the instruction list //var inst = _cpu.FetchAndDecode(address); // TODO(Cristian): This code will NOT work! Instruction inst = null; _disInstructions.Add(inst); // We get a list of possible next addresses to check var candidates = new List <ushort>(); // A show-stopper doesn't add any more instructions if (stoppers.Contains(inst.OpCode)) { continue; } if (dirJumps.ContainsKey(inst.OpCode)) { //if(dirJumps[inst.OpCode]) //{ // candidates.Add(inst.Literal); //} //// If permissive, we also permit conditional jumps (ant the next inst) //else if(permissive) //{ // candidates.Add(inst.Literal); // var next = (ushort)(address + inst.Length); // candidates.Add(next); //} } else if (relJumps.ContainsKey(inst.OpCode)) { //if(relJumps[inst.OpCode]) //{ // sbyte signedLiteral; // unchecked { signedLiteral = (sbyte)inst.Literal; } // ushort target = (ushort)(inst.Address + signedLiteral + inst.Length); // candidates.Add(target); //} //else if(permissive) //{ // sbyte signedLiteral; // unchecked { signedLiteral = (sbyte)inst.Literal; } // ushort target = (ushort)(inst.Address + signedLiteral + inst.Length); // candidates.Add(target); // var next = (ushort)(address + inst.Length); // candidates.Add(next); //} } else if (restarts.ContainsKey(inst.OpCode)) { candidates.Add(restarts[inst.OpCode]); } else // It's an instruction that continues { ushort target = (ushort)(address + inst.Length); candidates.Add(target); } // We add the candidate instructions into the list foreach (var candidateNextInstruction in candidates) { // If any of the candidates was already visited, we do not visit it if (_disVisitedAddresses.Contains(candidateNextInstruction)) { continue; } _disAddressToVisit.Push(candidateNextInstruction); } } catch (Exception) { } } } return(_disInstructions.OrderBy(i => i.Address)); }
/// <summary> /// Fetches and Decodes an instruction /// </summary> /// <param name="instruction"> /// As FetchAndDecode gets called *several* times a frame (and many times during /// disassembly), it is better to have a pre-allocated instruction and to replace /// the values, instead of getting the overhead of allocating a new Instrucion /// everytime. /// </param> /// <param name="instructionAddress"></param> /// <param name="haltLoad"></param> /// <returns></returns> internal Instruction FetchAndDecode(ref Instruction instruction, ushort instructionAddress, bool haltLoad = false) { instruction.Address = instructionAddress; byte opcode = this._memory.LowLevelRead(instructionAddress); instruction.OpCode = opcode; if (instruction.OpCode != 0xCB) { instruction.CB = false; byte lowOpcode = (byte)instruction.OpCode; if (_instructionHistogram[lowOpcode] < ushort.MaxValue) { _instructionHistogram[lowOpcode]++; } // Normal instructions instruction.Length = CPUInstructionLengths.Get(lowOpcode); // Extract literal if (instruction.Length == 2) { // 8 bit literal instruction.Operands[0] = this._memory.LowLevelRead((ushort)(instructionAddress + 1)); if (haltLoad) { instruction.Operands[0] = opcode; } instruction.Literal = (byte)instruction.Operands[0]; } else if (instruction.Length == 3) { // 16 bit literal, little endian instruction.Operands[0] = this._memory.LowLevelRead((ushort)(instructionAddress + 2)); instruction.Operands[1] = this._memory.LowLevelRead((ushort)(instructionAddress + 1)); if (haltLoad) { instruction.Operands[1] = instruction.Operands[0]; instruction.Operands[0] = opcode; } instruction.Literal = (byte)instruction.Operands[1]; instruction.Literal += (ushort)(instruction.Operands[0] << 8); } instruction.Ticks = CPUInstructionPreClocks.Get(lowOpcode); instruction.Name = CPUInstructionNames.Get(lowOpcode); instruction.Description = CPUInstructionDescriptions.Get(lowOpcode); } else { instruction.CB = true; // CB instructions block instruction.OpCode <<= 8; if (!haltLoad) { instruction.OpCode += this._memory.LowLevelRead((ushort)(instructionAddress + 1)); } else { instruction.OpCode += 0xCB; // The first byte is duplicated } byte lowOpcode = (byte)instruction.OpCode; if (_cbInstructionHistogram[lowOpcode] < ushort.MaxValue) { _cbInstructionHistogram[lowOpcode]++; } instruction.Length = CPUCBInstructionLengths.Get(lowOpcode); // There is no literal in CB instructions! //instruction.Lambda = this.CBInstructionLambdas[lowOpcode]; instruction.Ticks = CPUCBInstructionPreClocks.Get(lowOpcode); instruction.Name = CPUCBInstructionNames.Get(lowOpcode); instruction.Description = CPUCBInstructionDescriptions.Get(lowOpcode); } // NOTE(Cristian): On haltLoad (HALT with IME disabled), the next byte after the HALT opcode // is "duplicated". This is a hardware bug. if (haltLoad) { instruction.Length--; } return(instruction); }