public byte GetFlag(FLAGS6502 f) { byte b = (byte)f; byte a = (byte)(status & b); return((byte)((a > 0) ? 1 : 0)); }
private void SetFlag(FLAGS6502 flag, bool value) { if (value == true) { StatusRegister |= ToByte((int)flag); } else { StatusRegister &= ToByte((int)~flag); } }
public void SetFlag(FLAGS6502 f, int v) { if (v > 0) { status |= (byte)f; } else { status &= (byte)(~f); } }
public void SetFlag(FLAGS6502 f, bool v) { if (v) { status |= (byte)f; } else { status &= (byte)~f; } }
private void SetFlag(FLAGS6502 flag, bool v) { if (v) { status = (byte)(status | (byte)flag); } else { status = (byte)(status & (byte)~flag); } }
/// <summary> /// A Non-Maskable Interrupt cannot be ignored. It behaves in exactly the /// same way as a regular IRQ, but reads the new program counter address /// form location 0xFFFA. /// </summary> public void NMI() { // Push current current PC to the stack (two pushes) PushStack((byte)((Pc >> 8) & 0x00FF)); // Store the high byte (& 0x00FF clears the high portion) PushStack((byte)(Pc & 0x00FF)); // Store the low byte // Then Push the status register to the stack Status |= FLAGS6502.B; Status |= FLAGS6502.U; Status |= FLAGS6502.I; PushStack((byte)Status); // Read new program counter location from fixed address Pc = ReadAsAddress(NMI_PC_START_ADDRESS); // NMIs take time cycles = NMI_CYCLE_COUNT; }
// EXTERNAL EVENTS /// <summary> /// Forces the 6502 into a known state. This is hard-wired inside the CPU. The /// registers are set to 0x00, the status register is cleared except for unused /// bit which remains at 1. An absolute address is read from location 0xFFFC /// which contains a second address that the program counter is set to. This /// allows the programmer to jump to a known and programmable location in the /// memory to start executing from. Typically the programmer would set the value /// </summary> public void Reset(bool hardReset = false) { // Get address to set program counter to // This is stored little indian starting at 0xFFFC Pc = ReadAsAddress(PC_START_ADDRESS); // Reset registers A = 0; X = 0; Y = 0; StkPtr = INITIAL_STACK_POINTER; Status = FLAGS6502.U; // Clear internals addr_abs = 0x0000; addr_rel = 0x0000; fetched = 0x00; // Reset takes time cycles = RESET_CYCLE_COUNT; clock_count = 0; }
/// <summary> /// Interrupt requests are a complex operation and only happen if the /// "disable interrupt" flag is 0. IRQs can happen at any time, but /// you dont want them to be destructive to the operation of the running /// program. Therefore the current instruction is allowed to finish /// (which I facilitate by doing the whole thing when cycles == 0) and /// then the current program counter is stored on the stack. Then the /// current status register is stored on the stack. When the routine /// that services the interrupt has finished, the status register /// and program counter can be restored to how they where before it /// occurred. This is impemented by the "RTI" instruction. Once the IRQ /// has happened, in a similar way to a reset, a programmable address /// is read form hard coded location 0xFFFE, which is subsequently /// set to the program counter. /// </summary> public void IRQ() { // Check if "Disable Interrupts" is set if (Status.HasFlag(FLAGS6502.I)) { return; } // Push current current PC to the stack (two pushes) PushPcOnStack(); // Then Push the status register to the stack Status |= FLAGS6502.B; Status |= FLAGS6502.U; Status |= FLAGS6502.I; PushStack((byte)Status); // Read new program counter location from fixed address Pc = ReadAsAddress(IRQ_PC_START_ADDRESS); // IRQs take time cycles = IRQ_CYCLE_COUNT; }
private void SetFlag(FLAGS6502 flag, int value) { SetFlag(flag, value != 0); }
private byte GetFlag(FLAGS6502 flag) { var flagValue = ((StatusRegister & ToByte((int)flag)) > 0) ? 1: 0; return(ToByte(flagValue)); }
public bool GetFlag(FLAGS6502 f) { return((status & (byte)f) > 0); }
} = 0x00; // Status Register public byte GetFlagByte(FLAGS6502 f) { return(GetFlag(f) ? (byte)1 : (byte)0); }
private byte GetFlag(FLAGS6502 flag) => status;
/// <summary> /// Set of unset a flag based on a condition /// </summary> /// <param name="flag"></param> /// <param name="isSet"></param> private void SetFlag(FLAGS6502 flag, bool isSet) { Status = isSet ? Status | flag : Status & ~flag; }
/// <summary> /// Perform one clock cycles worth of emulation /// </summary> public void Clock() { // Non clock cycle accurate emulation // The result is calculated immediatly based on an opperation cycle value if (cycles == 0) { if (DebugEnabled) { // Init logger logger = logger ??= File.CreateText("cpu.txt"); // Log Pc before incrementing logger.WriteLine($"{Pc:X4} A:{A:X2} X:{X:X2} Y:{Y:X2} P:{(byte)Status:X2} SP:{StkPtr:X2} CYC:{clock_count}"); // Usefull to break at a specific prg location if (DebugBreakPc != 0x0000 && Pc == DebugBreakPc) { Debugger.Break(); // Disable further breaks DebugBreakPc = 0; // force push logging buffer to file logger.Flush(); // Notify if defined DebugPcHitCallback?.Invoke(); } } // Read next instruction byte. This 8-bit value is used to index // the translation table to get the relevant information about // how to implement the instruction opcode = CpuRead(Pc, false); // Always set the unused status flag bit to 1 Status |= FLAGS6502.U; // Increment program counter, we read the opcode byte Pc++; // Get Starting number of cycles cycles = Lookup[opcode].Cycles; // Perform fetch of intermmediate data using the // required addressing mode byte extra_cycles1 = Lookup[opcode].AddressModeFunc(); // Perform the opperation byte extra_cycles2 = Lookup[opcode].OpcodeFunc(); // The addressmode and opcode may have altered the number // of cycles this instruction requires before its completed cycles += (byte)(extra_cycles1 & extra_cycles2); // Always set the unused status flag bit to 1 Status |= FLAGS6502.U; } // Increment global clock count - This is actually unused unless logging is enabled // but I've kept it in because its a handy watch variable for debugging clock_count++; // Decrement the number of cycles remaining for this instruction cycles--; }