using System; using BizHawk.Common; // Motorola Corp 6800 namespace BizHawk.Emulation.Common.Components.MC6800 { public sealed partial class MC6800 { // operations that can take place in an instruction public const ushort IDLE = 0; public const ushort OP = 1; public const ushort RD = 2; public const ushort WR = 3; public const ushort TR = 4; public const ushort ADD8 = 6; public const ushort SUB8 = 7; public const ushort ADC8 = 8; public const ushort SBC8 = 9; public const ushort INC16 = 10; public const ushort INC8 = 11; public const ushort DEC16 = 12; public const ushort DEC8 = 13; public const ushort ROL = 14; public const ushort ROR = 15; public const ushort COM = 16; public const ushort DA = 17; public const ushort AND8 = 18; public const ushort XOR8 = 19; public const ushort OR8 = 20; public const ushort ASL = 21; public const ushort ASR = 22; public const ushort LSR = 23; public const ushort BIT = 24; public const ushort WAI = 25; public const ushort SYNC = 26; public const ushort RD_INC = 27; public const ushort RD_INC_OP = 28; public const ushort WR_DEC_LO = 29; public const ushort WR_DEC_HI = 30; public const ushort WR_HI = 31; public const ushort SET_ADDR = 32; public const ushort NEG = 33; public const ushort TST = 34; public const ushort CLR = 35; public const ushort ADD8BR = 41; public const ushort JPE = 44; public const ushort IDX_DCDE = 45; public const ushort IDX_OP_BLD = 46; public const ushort EA_8 = 47; public const ushort EA_16 = 48; public const ushort WR_DEC_LO_OP = 51; public const ushort WR_DEC_HI_OP = 52; public const ushort WR_HI_INC = 53; public const ushort SET_F_I = 55; public const ushort SET_I = 56; public const ushort SET_E = 57; public const ushort ANDCC = 58; public const ushort CMP8 = 59; public const ushort CMP16 = 62; public const ushort LD_8 = 64; public const ushort LD_16 = 65; public const ushort CLR_E = 67; public const ushort TAP = 68; public const ushort TPA = 69; public const ushort INX = 70; public const ushort DEX = 71; public const ushort CLV = 72; public const ushort SEV = 73; public const ushort CLC = 74; public const ushort SEC = 75; public const ushort CLI = 76; public const ushort SEI = 77; public const ushort SBA = 78; public const ushort CBA = 79; public const ushort TAB = 80; public const ushort TBA = 81; public const ushort ABA = 82; public const ushort TSX = 83; public const ushort INS = 84; public const ushort DES = 85; public const ushort TXS = 86; public MC6800() { Reset(); } public void Reset() { ResetRegisters(); ResetInterrupts(); TotalExecutedCycles = 0; Regs[PC] = 0xFFFE; PopulateCURINSTR(IDLE, IDLE, IDLE, RD_INC, ALU, PC, RD_INC, ALU2, PC, SET_ADDR, PC, ALU, ALU2); IRQS = 6; instr_pntr = irq_pntr = 0; } // Memory Access public Func ReadMemory; public Action WriteMemory; public Func PeekMemory; public Func DummyReadMemory; // Special Function for Speed switching executed on a STOP public Func SpeedFunc; //this only calls when the first byte of an instruction is fetched. public Action OnExecFetch; public void UnregisterMemoryMapper() { ReadMemory = null; ReadMemory = null; PeekMemory = null; DummyReadMemory = null; } public void SetCallbacks ( Func ReadMemory, Func DummyReadMemory, Func PeekMemory, Action WriteMemory ) { this.ReadMemory = ReadMemory; this.DummyReadMemory = DummyReadMemory; this.PeekMemory = PeekMemory; this.WriteMemory = WriteMemory; } //a little CDL related stuff public delegate void DoCDLCallbackType(ushort addr, MC6800.eCDLogMemFlags flags); public DoCDLCallbackType CDLCallback; public enum eCDLogMemFlags { FetchFirst = 1, FetchOperand = 2, Data = 4, Write = 8 }; // 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 if (OnExecFetch != null) OnExecFetch(PC); if (TraceCallback != null) TraceCallback(State()); if (CDLCallback != null) CDLCallback(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 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 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 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++]); instr_pntr++; break; case WR_DEC_HI_OP: Write_Dec_HI_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); 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 EA_16: Regs[IDX_EA] = (ushort)(Regs[indexed_reg] + Regs[ADDR]); 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_F_I: FlagI = 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 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; if (TraceCallback != null) { TraceCallback(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; if (TraceCallback != null) { TraceCallback(new TraceInfo { Disassembly = "====CWAI IRQ====", RegisterInfo = "" }); } } else { PopulateCURINSTR(WAI); irq_pntr = 0; IRQS = -1; } instr_pntr = 0; break; case SYNC: IN_SYNC = true; IRQS = 1; instr_pntr = irq_pntr = 0; PopulateCURINSTR(SYNC); break; } if (++irq_pntr == IRQS) { // NMI has priority if (NMIPending) { NMIPending = false; if (TraceCallback != null) { TraceCallback(new TraceInfo { Disassembly = "====NMI====", RegisterInfo = "" }); } IN_SYNC = false; NMI_(); NMICallback(); instr_pntr = irq_pntr = 0; } // then regular IRQ else if (IRQPending && !FlagI) { if (!FlagI) { IRQPending = false; if (TraceCallback != null) { TraceCallback(new TraceInfo { Disassembly = "====IRQ====", RegisterInfo = "" }); } IN_SYNC = false; IRQ_(); IRQCallback(); instr_pntr = irq_pntr = 0; } else if (IN_SYNC) { IRQPending = false; if (TraceCallback != null) { TraceCallback(new TraceInfo { Disassembly = "====SYNC====", RegisterInfo = "" }); } IN_SYNC = false; IRQS = 1; instr_pntr = irq_pntr = 0; PopulateCURINSTR(IDLE); } } // otherwise start the next instruction else { PopulateCURINSTR(OP); instr_pntr = irq_pntr = 0; IRQS = -1; } } TotalExecutedCycles++; } // tracer stuff public Action TraceCallback; public string TraceHeader { get { return "MC6809: PC, machine code, mnemonic, operands, registers (A, B, X, SP, CC), Cy, flags (EHINZVC)"; } } public TraceInfo State(bool disassemble = true) { ushort notused; return new TraceInfo { Disassembly = $"{(disassemble ? Disassemble(Regs[PC], ReadMemory, out notused) : "---")} ".PadRight(50), RegisterInfo = string.Format( "A:{0:X2} B:{1:X2} X:{2:X4} SP:{3:X4} CC:{4:X2} Cy:{5} {6}{7}{8}{9}{10}{11}{12}", Regs[A], Regs[B], Regs[X], Regs[SP], Regs[CC], TotalExecutedCycles, FlagE ? "E" : "e", FlagH ? "H" : "h", FlagI ? "I" : "i", FlagN ? "N" : "n", FlagZ ? "Z" : "z", FlagV ? "V" : "v", FlagC ? "C" : "c" ) }; } /// /// Optimization method to set cur_instr /// private void PopulateCURINSTR(ushort d0 = 0, ushort d1 = 0, ushort d2 = 0, ushort d3 = 0, ushort d4 = 0, ushort d5 = 0, ushort d6 = 0, ushort d7 = 0, ushort d8 = 0, ushort d9 = 0, ushort d10 = 0, ushort d11 = 0, ushort d12 = 0, ushort d13 = 0, ushort d14 = 0, ushort d15 = 0, ushort d16 = 0, ushort d17 = 0, ushort d18 = 0, ushort d19 = 0, ushort d20 = 0, ushort d21 = 0, ushort d22 = 0, ushort d23 = 0, ushort d24 = 0, ushort d25 = 0, ushort d26 = 0, ushort d27 = 0, ushort d28 = 0, ushort d29 = 0, ushort d30 = 0, ushort d31 = 0, ushort d32 = 0, ushort d33 = 0, ushort d34 = 0, ushort d35 = 0, ushort d36 = 0, ushort d37 = 0, ushort d38 = 0, ushort d39 = 0, ushort d40 = 0, ushort d41 = 0, ushort d42 = 0, ushort d43 = 0, ushort d44 = 0, ushort d45 = 0, ushort d46 = 0, ushort d47 = 0, ushort d48 = 0, ushort d49 = 0, ushort d50 = 0, ushort d51 = 0, ushort d52 = 0, ushort d53 = 0, ushort d54 = 0, ushort d55 = 0, ushort d56 = 0, ushort d57 = 0, ushort d58 = 0) { cur_instr[0] = d0; cur_instr[1] = d1; cur_instr[2] = d2; cur_instr[3] = d3; cur_instr[4] = d4; cur_instr[5] = d5; cur_instr[6] = d6; cur_instr[7] = d7; cur_instr[8] = d8; cur_instr[9] = d9; cur_instr[10] = d10; cur_instr[11] = d11; cur_instr[12] = d12; cur_instr[13] = d13; cur_instr[14] = d14; cur_instr[15] = d15; cur_instr[16] = d16; cur_instr[17] = d17; cur_instr[18] = d18; cur_instr[19] = d19; cur_instr[20] = d20; cur_instr[21] = d21; cur_instr[22] = d22; cur_instr[23] = d23; cur_instr[24] = d24; cur_instr[25] = d25; cur_instr[26] = d26; cur_instr[27] = d27; cur_instr[28] = d28; cur_instr[29] = d29; cur_instr[30] = d30; cur_instr[31] = d31; cur_instr[32] = d32; cur_instr[33] = d33; cur_instr[34] = d34; cur_instr[35] = d35; cur_instr[36] = d36; cur_instr[37] = d37; cur_instr[38] = d38; cur_instr[39] = d39; cur_instr[40] = d40; cur_instr[41] = d41; cur_instr[42] = d42; cur_instr[43] = d43; cur_instr[44] = d44; cur_instr[45] = d45; cur_instr[46] = d46; cur_instr[47] = d47; cur_instr[48] = d48; cur_instr[49] = d49; cur_instr[50] = d50; cur_instr[51] = d51; cur_instr[52] = d52; cur_instr[53] = d53; cur_instr[54] = d54; cur_instr[55] = d55; cur_instr[56] = d56; cur_instr[57] = d57; cur_instr[58] = d58; } // State Save/Load public void SyncState(Serializer ser) { ser.BeginSection("MC6809"); ser.Sync(nameof(IN_SYNC), ref IN_SYNC); ser.Sync(nameof(NMIPending), ref NMIPending); ser.Sync(nameof(IRQPending), ref IRQPending); ser.Sync(nameof(indexed_op), ref indexed_op); ser.Sync(nameof(indexed_reg), ref indexed_reg); ser.Sync(nameof(indexed_op_reg), ref indexed_op_reg); ser.Sync(nameof(instr_pntr), ref instr_pntr); ser.Sync(nameof(cur_instr), ref cur_instr, false); ser.Sync(nameof(opcode_see), ref opcode_see); ser.Sync(nameof(IRQS), ref IRQS); ser.Sync(nameof(irq_pntr), ref irq_pntr); ser.Sync(nameof(Regs), ref Regs, false); ser.Sync(nameof(TotalExecutedCycles), ref TotalExecutedCycles); ser.EndSection(); } } }