using System; using BizHawk.Emulation.Cores.Consoles.O2Hawk; using BizHawk.Common; using BizHawk.Emulation.Common; // Intel Corp 8048 namespace BizHawk.Emulation.Cores.Components.I8048 { public sealed partial class I8048 { public O2Hawk Core { get; set; } // 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 INC11 = 5; public const ushort ADD8 = 6; public const ushort ADC8 = 7; public const ushort AND8 = 8; public const ushort XOR8 = 9; public const ushort OR8 = 10; public const ushort INC8 = 11; public const ushort INCA = 12; public const ushort DEC8 = 13; public const ushort DECA = 14; public const ushort ROL = 15; public const ushort ROR = 16; public const ushort RLC = 17; public const ushort RRC = 18; public const ushort CLRA = 19; public const ushort SWP = 20; public const ushort COMA = 21; public const ushort CMC = 22; public const ushort CM0 = 23; public const ushort CM1 = 24; public const ushort DA = 25; public const ushort SET_ADDR = 26; public const ushort CLC = 27; public const ushort CL0 = 28; public const ushort CL1 = 29; public const ushort EI = 30; public const ushort EN = 31; public const ushort DI = 32; public const ushort DN = 33; public const ushort MSK = 34; public const ushort CLK_OUT = 35; public const ushort XCH = 36; public const ushort XCH_RAM = 37; public const ushort XCHD_RAM = 38; public const ushort SEL_MB0 = 39; public const ushort SEL_MB1 = 40; public const ushort SEL_RB0 = 41; public const ushort SEL_RB1 = 42; public const ushort INC_RAM = 43; public const ushort RES_TF = 44; public const ushort SET_ADDR_M3 = 45; public const ushort MOVT_RAM_D = 46; public const ushort MOV = 47; public const ushort MOVT = 48; public const ushort MOVAR = 49; public const ushort MOVT_RAM = 50; public const ushort ST_CNT = 51; public const ushort STP_CNT = 52; public const ushort ST_T = 53; public const ushort SET_ADDR_8 = 54; public const ushort MEM_ALU = 55; public const ushort PUSH = 56; public const ushort PULL = 57; public const ushort PULL_PC = 58; public const ushort EEA = 59; public const ushort DEA = 60; public const ushort RD_P = 61; public const ushort WR_P = 62; public const ushort EM = 63; public const ushort DM = 64; public const ushort TEST_COND = 65; public I8048() { Reset(); } public void Reset() { ResetRegisters(); ResetInterrupts(); TotalExecutedCycles = 0; Regs[PC] = 0x0; PopulateCURINSTR(IDLE, IDLE, IDLE, IDLE, IDLE); IRQS = 5; instr_pntr = irq_pntr = 0; Regs[PX + 1] = 0xFF; Regs[PX + 2] = 0xFF; } // Memory Access public Func ReadMemory; public Action WriteMemory; public Func PeekMemory; public Func DummyReadMemory; // Port Access public Func ReadPort; public Action WritePort; //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, I8048.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])); Regs[ALU2] = (ushort)(Regs[PC] & 0x800); Regs[PC] = (ushort)(((Regs[PC] + 1) & 0x7FF) | Regs[ALU2]); instr_pntr = 0; irq_pntr = -1; break; case RD: Read_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case WR: Write_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case TR: TR_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case INC11: reg_d_ad = cur_instr[instr_pntr++]; Regs[ALU2] = (ushort)(Regs[reg_d_ad] & 0x800); Regs[reg_d_ad] = (ushort)(((Regs[reg_d_ad] + 1) & 0x7FF) | Regs[ALU2]); 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 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 INC8: INC8_Func(cur_instr[instr_pntr++]); break; case INCA: INC8_Func(A); break; case DEC8: DEC8_Func(cur_instr[instr_pntr++]); break; case DECA: DEC8_Func(A); break; case ROL: ROL_Func(A); break; case ROR: ROR_Func(A); break; case RLC: RLC_Func(A); break; case RRC: RRC_Func(A); break; case CLRA: Regs[A] = 0; break; case SWP: reg_d_ad = Regs[A]; Regs[A] = (ushort)(Regs[A] >> 4); Regs[A] |= (ushort)((reg_d_ad << 4) & 0xF0); break; case COMA: Regs[A] = (ushort)((~Regs[A]) & 0xFF); break; case CMC: FlagC = !FlagC; break; case CM0: FlagF0 = !FlagF0; break; case CM1: F1 = !F1; break; case DA: DA_Func(A); break; case SET_ADDR: reg_d_ad = cur_instr[instr_pntr++]; reg_l_ad = cur_instr[instr_pntr++]; reg_h_ad = cur_instr[instr_pntr++]; // direct value // bit 11 held low during interrupt if (INT_MSTR) { Regs[reg_d_ad] = (ushort)(MB | (reg_h_ad << 8) | Regs[reg_l_ad]); } else { Regs[reg_d_ad] = (ushort)((reg_h_ad << 8) | Regs[reg_l_ad]); } break; case CLC: FlagC = false; break; case CL0: FlagF0 = false; break; case CL1: F1 = false; break; case EI: IntEn = true; break; case EN: TimIntEn = true; break; case DI: IntEn = false; break; case DN: TimIntEn = false; TIRQPending = false; break; case MSK: break; case CLK_OUT: break; case XCH: Regs[ALU] = Regs[cur_instr[instr_pntr]]; Regs[cur_instr[instr_pntr++]] = Regs[cur_instr[instr_pntr]]; Regs[cur_instr[instr_pntr++]] = Regs[ALU]; break; case XCH_RAM: reg_d_ad = cur_instr[instr_pntr++]; reg_d_ad = (ushort)(Regs[reg_d_ad] & 0x3F); Regs[ALU] = Regs[reg_d_ad]; Regs[reg_d_ad] = Regs[A]; Regs[A] = Regs[ALU]; break; case XCHD_RAM: reg_d_ad = cur_instr[instr_pntr++]; reg_d_ad = (ushort)(Regs[reg_d_ad] & 0x3F); Regs[ALU] = Regs[reg_d_ad]; Regs[reg_d_ad] = (ushort)((Regs[reg_d_ad] & 0xF0) | (Regs[A] & 0xF)); Regs[A] = (ushort)((Regs[A] & 0xF0) | (Regs[ALU] & 0xF)); break; case SEL_MB0: MB = 0; break; case SEL_MB1: MB = 1 << 11; break; case SEL_RB0: FlagBS = false; // register bank also changed here break; case SEL_RB1: FlagBS = true; // register bank also changed here break; case INC_RAM: reg_d_ad = cur_instr[instr_pntr++]; reg_d_ad = (ushort)(Regs[reg_d_ad] & 0x3F); Regs[reg_d_ad] = (ushort)((Regs[reg_d_ad] + 1) & 0xFF); break; case RES_TF: TF = false; break; case SET_ADDR_M3: Regs[ALU] &= 0xFF; Regs[ALU] |= 0x300; break; case MOVT_RAM_D: reg_d_ad = cur_instr[instr_pntr++]; reg_d_ad = (ushort)(Regs[reg_d_ad] & 0x3F); Regs[reg_d_ad] = Regs[cur_instr[instr_pntr++]]; //Console.WriteLine(reg_d_ad + " " + Regs[reg_d_ad] + " " + Regs[ALU] + " " + TotalExecutedCycles); break; case MOV: reg_d_ad = cur_instr[instr_pntr++]; Regs[reg_d_ad] = Regs[cur_instr[instr_pntr++]]; break; case MOVT: reg_d_ad = cur_instr[instr_pntr++]; Regs[reg_d_ad] = Regs[cur_instr[instr_pntr++]]; break; case MOVAR: Regs[cur_instr[instr_pntr++]] = Regs[A]; break; case MOVT_RAM: reg_d_ad = cur_instr[instr_pntr++]; reg_d_ad = (ushort)(Regs[reg_d_ad] & 0x3F); Regs[reg_d_ad] = Regs[A]; break; case ST_CNT: counter_en = true; break; case STP_CNT: counter_en = timer_en = false; break; case ST_T: timer_en = true; timer_prescale = 0; break; case SET_ADDR_8: reg_d_ad = cur_instr[instr_pntr++]; Regs[reg_d_ad] &= 0xFF00; Regs[reg_d_ad] |= Regs[cur_instr[instr_pntr++]]; break; case MEM_ALU: Regs[ALU] = Regs[(ushort)(Regs[cur_instr[instr_pntr++]] & 0x3F)]; break; case PUSH: Regs[(Regs[PSW] & 0x7) * 2 + 8] = (ushort)(Regs[PC] & 0xFF); Regs[(Regs[PSW] & 0x7) * 2 + 8 + 1] = (ushort)(((Regs[PC] >> 8) & 0xF) | (Regs[PSW] & 0xF0)); Regs[PSW] = (ushort)((((Regs[PSW] & 0x7) + 1) & 0x7) | (Regs[PSW] & 0xF8)); break; case PULL: Regs[PSW] = (ushort)((((Regs[PSW] & 0x7) - 1) & 0x7) | (Regs[PSW] & 0xF8)); Regs[PC] = (ushort)(Regs[(Regs[PSW] & 0x7) * 2 + 8] & 0xFF); Regs[PC] |= (ushort)((Regs[(Regs[PSW] & 0x7) * 2 + 8 + 1] & 0xF) << 8); Regs[PSW] &= 0xF; Regs[PSW] |= (ushort)(Regs[(Regs[PSW] & 0x7) * 2 + 8 + 1] & 0xF0); RB = (ushort)(FlagBS ? 24 : 0); break; case PULL_PC: Regs[PSW] = (ushort)((((Regs[PSW] & 0x7) - 1) & 0x7) | (Regs[PSW] & 0xF8)); Regs[PC] = (ushort)(Regs[(Regs[PSW] & 0x7) * 2 + 8] & 0xFF); Regs[PC] |= (ushort)((Regs[(Regs[PSW] & 0x7) * 2 + 8 + 1] & 0xF) << 8); break; case EEA: EA = true; break; case DEA: EA = false; break; case RD_P: reg_d_ad = cur_instr[instr_pntr++]; reg_l_ad = cur_instr[instr_pntr++]; Regs[reg_d_ad] = ReadPort(reg_l_ad); Regs[PX + reg_l_ad] = Regs[reg_d_ad]; break; case WR_P: reg_d_ad = cur_instr[instr_pntr++]; reg_l_ad = cur_instr[instr_pntr++]; WritePort(reg_d_ad, (byte)Regs[reg_l_ad]); Regs[PX + reg_d_ad] = Regs[reg_l_ad]; break; case EM: INT_MSTR = true; break; case DM: INT_MSTR = false; break; case TEST_COND: reg_d_ad = cur_instr[instr_pntr++]; if ((reg_d_ad == 0) && !TF) { cur_instr[instr_pntr + 9] = IDLE; } if ((reg_d_ad == 1) && T0) { cur_instr[instr_pntr + 9] = IDLE; } if ((reg_d_ad == 2) && !T0) { cur_instr[instr_pntr + 9] = IDLE; } if ((reg_d_ad == 3) && T1) { cur_instr[instr_pntr + 9] = IDLE; } if ((reg_d_ad == 4) && !T1) { cur_instr[instr_pntr + 9] = IDLE; } if ((reg_d_ad == 5) && !F1) { cur_instr[instr_pntr + 9] = IDLE; } if ((reg_d_ad == 6) && IRQPending) { cur_instr[instr_pntr + 9] = IDLE; } if ((reg_d_ad == 7) && (Regs[A] == 0)) { cur_instr[instr_pntr + 9] = IDLE; } if ((reg_d_ad == 8) && !FlagF0) { cur_instr[instr_pntr + 9] = IDLE; } if ((reg_d_ad == 9) && (Regs[A] != 0)) { cur_instr[instr_pntr + 9] = IDLE; } if ((reg_d_ad == 10) && FlagC) { cur_instr[instr_pntr + 9] = IDLE; } if ((reg_d_ad == 11) && !FlagC) { cur_instr[instr_pntr + 9] = IDLE; } break; } if (++irq_pntr == IRQS) { // then regular IRQ if (IRQPending && IntEn && INT_MSTR) { IRQPending = false; if (TraceCallback != null) { TraceCallback(new TraceInfo { Disassembly = "====IRQ====", RegisterInfo = "" }); } IRQ_(0); IRQCallback(); instr_pntr = irq_pntr = 0; } else if (TIRQPending && TimIntEn && INT_MSTR) { TIRQPending = false; if (TraceCallback != null) { TraceCallback(new TraceInfo { Disassembly = "====TIRQ====", RegisterInfo = "" }); } IRQ_(1); IRQCallback(); instr_pntr = irq_pntr = 0; } // otherwise start the next instruction else { PopulateCURINSTR(OP); instr_pntr = irq_pntr = 0; IRQS = -1; } } TotalExecutedCycles++; if (timer_en) { timer_prescale++; if (timer_prescale == 32) { timer_prescale = 0; if (Regs[TIM] == 255) { TF = true; if (TimIntEn) { TIRQPending = true; } } Regs[TIM] = (ushort)((Regs[TIM] + 1) & 0xFF); } } if (counter_en) { if (!T1 && T1_old) { if (Regs[TIM] == 255) { TF = true; if (TimIntEn) { TIRQPending = true; //Console.WriteLine(TotalExecutedCycles); } } Regs[TIM] = (ushort)((Regs[TIM] + 1) & 0xFF); } } T1_old = T1; } // tracer stuff public Action TraceCallback; public string TraceHeader { get { return "MC6809: PC, machine code, mnemonic, operands, registers (A, B, X, Y, US, SP, DP, CC), Cy, flags (CAFBIFTTR)"; } } 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} R0:{1:X2} R1:{2:X2} R2:{3:X2} R3:{4:X2} R4:{5:X2} R5:{6:X2} R6:{7:X2} R7:{8:X2} PSW:{9:X4} Cy:{10} LY:{11} {12}{13}{14}{15}{16}{17}{18}{19}{20}{21}", Regs[A], Regs[(ushort)(R0 + RB)], Regs[(ushort)(R1 + RB)], Regs[(ushort)(R2 + RB)], Regs[(ushort)(R3 + RB)], Regs[(ushort)(R4 + RB)], Regs[(ushort)(R5 + RB)], Regs[(ushort)(R6 + RB)], Regs[(ushort)(R7 + RB)], Regs[PSW], TotalExecutedCycles, Core.ppu.LY, FlagC ? "C" : "c", FlagAC ? "A" : "a", FlagF0 ? "F" : "f", FlagBS ? "B" : "b", IntEn ? "I" : "i", TimIntEn ? "N" : "n", F1 ? "F" : "f", T0 ? "T" : "t", T1 ? "T" : "t", RB > 0 ? "R" : "r" ) }; } /// /// 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) { 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; } // State Save/Load public void SyncState(Serializer ser) { ser.BeginSection("I8048"); ser.Sync(nameof(IntEn), ref IntEn); ser.Sync(nameof(TimIntEn), ref TimIntEn); ser.Sync(nameof(IRQPending), ref IRQPending); ser.Sync(nameof(TIRQPending), ref TIRQPending); ser.Sync(nameof(INT_MSTR), ref INT_MSTR); 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(EA), ref EA); ser.Sync(nameof(TF), ref TF); ser.Sync(nameof(timer_en), ref timer_en); ser.Sync(nameof(counter_en), ref counter_en); ser.Sync(nameof(timer_prescale), ref timer_prescale); ser.Sync(nameof(RB), ref RB); ser.Sync(nameof(MB), ref MB); ser.Sync(nameof(Regs), ref Regs, false); ser.Sync(nameof(F1), ref F1); ser.Sync(nameof(T0), ref T0); ser.Sync(nameof(T1), ref T1); ser.Sync(nameof(T1_old), ref T1_old); ser.Sync(nameof(TotalExecutedCycles), ref TotalExecutedCycles); ser.EndSection(); } } }