using System; using System.IO; using BizHawk.Common; using BizHawk.Emulation.Common; namespace BizHawk.Emulation.Cores.Components.M6502 { public sealed partial class MOS6502X where TLink : IMOS6502XLink { private readonly TLink _link; public MOS6502X(TLink link) { _link = link; InitOpcodeHandlers(); Reset(); } public bool BCD_Enabled = true; public bool debug = false; public bool throw_unhandled; public void Reset() { A = 0; X = 0; Y = 0; P = 0x20; // 5th bit always set S = 0; PC = 0; TotalExecutedCycles = 0; mi = 0; opcode = VOP_RESET; iflag_pending = true; RDY = true; } public void NESSoftReset() { opcode = VOP_RESET; mi = 0; iflag_pending = true; FlagI = true; } public string TraceHeader { get { return "6502: PC, machine code, mnemonic, operands, registers (A, X, Y, P, SP), flags (NVTBDIZCR)"; } } public TraceInfo State(bool disassemble = true) { if (!disassemble) { return new TraceInfo { Disassembly = "", RegisterInfo = "" }; } int length; string rawbytes = ""; string disasm = Disassemble(PC, out length); for (int i = 0; i < length; i++) { rawbytes += string.Format(" {0:X2}", _link.PeekMemory((ushort)(PC + i))); } return new TraceInfo { Disassembly = string.Format( "{0:X4}: {1,-9} {2} ", PC, rawbytes, disasm).PadRight(32), RegisterInfo = string.Format( "A:{0:X2} X:{1:X2} Y:{2:X2} SP:{4:X2} P:{3:X2} {6}{7}{8}{9}{10}{11}{12}{13} Cy:{5} PPU-Cy:{15}", A, X, Y, P, S, TotalExecutedCycles, FlagN ? "N" : "n", FlagV ? "V" : "v", FlagT ? "T" : "t", FlagB ? "B" : "b", FlagD ? "D" : "d", FlagI ? "I" : "i", FlagZ ? "Z" : "z", FlagC ? "C" : "c", !RDY ? "R" : "r", ext_ppu_cycle) }; } public bool AtStart { get { return opcode == VOP_Fetch1 || Microcode[opcode][mi] >= Uop.End; } } public TraceInfo TraceState() { // only disassemble when we're at the beginning of an opcode return State(AtStart); } public const ushort NMIVector = 0xFFFA; public const ushort ResetVector = 0xFFFC; public const ushort BRKVector = 0xFFFE; public const ushort IRQVector = 0xFFFE; enum ExceptionType { BRK, NMI, IRQ } // ==== CPU State ==== public byte A; public byte X; public byte Y; public byte P; public ushort PC; public byte S; public bool IRQ; public bool NMI; public bool RDY; // ppu cycle (used with SubNESHawk) public int ext_ppu_cycle = 0; public void SyncState(Serializer ser) { ser.BeginSection(nameof(MOS6502X)); ser.Sync(nameof(A), ref A); ser.Sync(nameof(X), ref X); ser.Sync(nameof(Y), ref Y); ser.Sync(nameof(P), ref P); ser.Sync(nameof(PC), ref PC); ser.Sync(nameof(S), ref S); ser.Sync(nameof(NMI), ref NMI); ser.Sync(nameof(IRQ), ref IRQ); ser.Sync(nameof(RDY), ref RDY); ser.Sync(nameof(TotalExecutedCycles), ref TotalExecutedCycles); ser.Sync(nameof(opcode), ref opcode); ser.Sync(nameof(opcode2), ref opcode2); ser.Sync(nameof(opcode3), ref opcode3); ser.Sync(nameof(ea), ref ea); ser.Sync(nameof(alu_temp), ref alu_temp); ser.Sync(nameof(mi), ref mi); ser.Sync(nameof(iflag_pending), ref iflag_pending); ser.Sync(nameof(interrupt_pending), ref interrupt_pending); ser.Sync(nameof(branch_irq_hack), ref branch_irq_hack); ser.Sync(nameof(rdy_freeze), ref rdy_freeze); ser.Sync(nameof(ext_ppu_cycle), ref ext_ppu_cycle); ser.EndSection(); } public void SaveStateBinary(BinaryWriter writer) { SyncState(Serializer.CreateBinaryWriter(writer)); } public void LoadStateBinary(BinaryReader reader) { SyncState(Serializer.CreateBinaryReader(reader)); } // ==== End State ==== /// Carry Flag public bool FlagC { get { return (P & 0x01) != 0; } private set { P = (byte)((P & ~0x01) | (value ? 0x01 : 0x00)); } } /// Zero Flag public bool FlagZ { get { return (P & 0x02) != 0; } private set { P = (byte)((P & ~0x02) | (value ? 0x02 : 0x00)); } } /// Interrupt Disable Flag public bool FlagI { get { return (P & 0x04) != 0; } set { P = (byte)((P & ~0x04) | (value ? 0x04 : 0x00)); } } /// Decimal Mode Flag public bool FlagD { get { return (P & 0x08) != 0; } private set { P = (byte)((P & ~0x08) | (value ? 0x08 : 0x00)); } } /// Break Flag public bool FlagB { get { return (P & 0x10) != 0; } private set { P = (byte)((P & ~0x10) | (value ? 0x10 : 0x00)); } } /// T... Flag public bool FlagT { get { return (P & 0x20) != 0; } private set { P = (byte)((P & ~0x20) | (value ? 0x20 : 0x00)); } } /// Overflow Flag public bool FlagV { get { return (P & 0x40) != 0; } private set { P = (byte)((P & ~0x40) | (value ? 0x40 : 0x00)); } } /// Negative Flag public bool FlagN { get { return (P & 0x80) != 0; } private set { P = (byte)((P & ~0x80) | (value ? 0x80 : 0x00)); } } public long TotalExecutedCycles; public ushort ReadWord(ushort address) { byte l = _link.ReadMemory(address); byte h = _link.ReadMemory(++address); return (ushort)((h << 8) | l); } public ushort PeekWord(ushort address) { byte l = _link.PeekMemory(address); byte h = _link.PeekMemory(++address); return (ushort)((h << 8) | l); } private void WriteWord(ushort address, ushort value) { byte l = (byte)(value & 0xFF); byte h = (byte)(value >> 8); _link.WriteMemory(address, l); _link.WriteMemory(++address, h); } private ushort ReadWordPageWrap(ushort address) { ushort highAddress = (ushort)((address & 0xFF00) + ((address + 1) & 0xFF)); return (ushort)(_link.ReadMemory(address) | (_link.ReadMemory(highAddress) << 8)); } // SO pin public void SetOverflow() { FlagV = true; } private static readonly byte[] TableNZ = { 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 }; } }