Exemplo n.º 1
0
        private void InvokeTraceCallback(IDbConnection connection, string queryClass, string cmd, object parameters, Stopwatch sw)
        {
            IEnumerable <QueryTrace.Parameter> traceParams = parameters?.GetType()
                                                             .GetProperties()
                                                             .Where(pi => ((pi.GetIndexParameters()?.Length ?? 0) == 0)) // exclude indexer properties
                                                             .Select(pi => new QueryTrace.Parameter(pi, parameters));

            TraceCallback?.Invoke(connection, new QueryTrace(queryClass, UserName, cmd, traceParams, sw.ElapsedMilliseconds, null));
        }
Exemplo n.º 2
0
        private void Dispatch(string req)
        {
            var request = JsonConvert.DeserializeObject <DispatcherRequest>(req);

            if (request != null && request.type == "request")
            {
                TraceCallback?.Invoke(string.Format("C {0}: {1}", request.command, JsonConvert.SerializeObject(request.arguments)));

                if (_callback != null)
                {
                    try
                    {
                        lock (_lock)
                        {
                            _isDispatchingData = true;
                        }

                        var response  = new DispatcherResponse(request.seq, request.command);
                        var responder = new DispatchResponder(this, response);

                        _callback(request.command, request.arguments, responder);

                        SendMessage(response);
                    }
                    finally
                    {
                        lock (_lock)
                        {
                            _isDispatchingData = false;
                        }

                        DispatcherEvent e;
                        while (_queuedEvent.TryDequeue(out e))
                        {
                            SendMessage(e);
                        }
                    }
                }
            }
        }
Exemplo n.º 3
0
        // Execute instructions
        public void ExecuteOne()
        {
            switch (instr_table[instr_pntr++])
            {
            case IDLE:
                // do nothing
                break;

            case OP:
                // Read the opcode of the next instruction
                if (EI_pending > 0 && !CB_prefix)
                {
                    EI_pending--;
                    if (EI_pending == 0)
                    {
                        interrupts_enabled = true;
                    }
                }

                if (I_use && interrupts_enabled && !CB_prefix && !jammed)
                {
                    interrupts_enabled = false;

                    TraceCallback?.Invoke(new TraceInfo
                    {
                        Disassembly  = "====IRQ====",
                        RegisterInfo = ""
                    });

                    // call interrupt processor
                    // lowest bit set is highest priority
                    instr_pntr = 256 * 60 * 2 + 60 * 6;                             // point to Interrupt
                }
                else
                {
                    OnExecFetch?.Invoke(RegPC);
                    if (TraceCallback != null && !CB_prefix)
                    {
                        TraceCallback(State());
                    }
                    CDLCallback?.Invoke(RegPC, eCDLogMemFlags.FetchFirst);
                    FetchInstruction(ReadMemory(RegPC++));
                }
                I_use = false;
                break;

            case RD:
                Read_Func(instr_table[instr_pntr], instr_table[instr_pntr + 1], instr_table[instr_pntr + 2]);
                instr_pntr += 3;
                break;

            case WR:
                Write_Func(instr_table[instr_pntr], instr_table[instr_pntr + 1], instr_table[instr_pntr + 2]);
                instr_pntr += 3;
                break;

            case TR:
                TR_Func(instr_table[instr_pntr], instr_table[instr_pntr + 1]);
                instr_pntr += 2;
                break;

            case ADD16:
                ADD16_Func(instr_table[instr_pntr], instr_table[instr_pntr + 1], instr_table[instr_pntr + 2], instr_table[instr_pntr + 3]);
                instr_pntr += 4;
                break;

            case ADD8:
                ADD8_Func(instr_table[instr_pntr], instr_table[instr_pntr + 1]);
                instr_pntr += 2;
                break;

            case SUB8:
                SUB8_Func(instr_table[instr_pntr], instr_table[instr_pntr + 1]);
                instr_pntr += 2;
                break;

            case ADC8:
                ADC8_Func(instr_table[instr_pntr], instr_table[instr_pntr + 1]);
                instr_pntr += 2;
                break;

            case SBC8:
                SBC8_Func(instr_table[instr_pntr], instr_table[instr_pntr + 1]);
                instr_pntr += 2;
                break;

            case INC16:
                INC16_Func(instr_table[instr_pntr], instr_table[instr_pntr + 1]);
                instr_pntr += 2;
                break;

            case INC8:
                INC8_Func(instr_table[instr_pntr++]);
                break;

            case DEC16:
                DEC16_Func(instr_table[instr_pntr], instr_table[instr_pntr + 1]);
                instr_pntr += 2;
                break;

            case DEC8:
                DEC8_Func(instr_table[instr_pntr++]);
                break;

            case RLC:
                RLC_Func(instr_table[instr_pntr++]);
                break;

            case RL:
                RL_Func(instr_table[instr_pntr++]);
                break;

            case RRC:
                RRC_Func(instr_table[instr_pntr++]);
                break;

            case RR:
                RR_Func(instr_table[instr_pntr++]);
                break;

            case CPL:
                CPL_Func(instr_table[instr_pntr++]);
                break;

            case DA:
                DA_Func(instr_table[instr_pntr++]);
                break;

            case SCF:
                SCF_Func(instr_table[instr_pntr++]);
                break;

            case CCF:
                CCF_Func(instr_table[instr_pntr++]);
                break;

            case AND8:
                AND8_Func(instr_table[instr_pntr], instr_table[instr_pntr + 1]);
                instr_pntr += 2;
                break;

            case XOR8:
                XOR8_Func(instr_table[instr_pntr], instr_table[instr_pntr + 1]);
                instr_pntr += 2;
                break;

            case OR8:
                OR8_Func(instr_table[instr_pntr], instr_table[instr_pntr + 1]);
                instr_pntr += 2;
                break;

            case CP8:
                CP8_Func(instr_table[instr_pntr], instr_table[instr_pntr + 1]);
                instr_pntr += 2;
                break;

            case SLA:
                SLA_Func(instr_table[instr_pntr++]);
                break;

            case SRA:
                SRA_Func(instr_table[instr_pntr++]);
                break;

            case SRL:
                SRL_Func(instr_table[instr_pntr++]);
                break;

            case SWAP:
                SWAP_Func(instr_table[instr_pntr++]);
                break;

            case BIT:
                BIT_Func(instr_table[instr_pntr], instr_table[instr_pntr + 1]);
                instr_pntr += 2;
                break;

            case RES:
                RES_Func(instr_table[instr_pntr], instr_table[instr_pntr + 1]);
                instr_pntr += 2;
                break;

            case SET:
                SET_Func(instr_table[instr_pntr], instr_table[instr_pntr + 1]);
                instr_pntr += 2;
                break;

            case EI:
                if (EI_pending == 0)
                {
                    EI_pending = 2;
                }
                break;

            case DI:
                interrupts_enabled = false;
                EI_pending         = 0;
                break;

            case HALT:
                halted = true;

                bool temp = false;

                if (instr_table[instr_pntr++] == 1)
                {
                    temp = FlagI;
                }
                else
                {
                    temp = I_use;
                }

                if (EI_pending > 0 && !CB_prefix)
                {
                    EI_pending--;
                    if (EI_pending == 0)
                    {
                        interrupts_enabled = true;
                    }
                }

                // if the I flag is asserted at the time of halt, don't halt
                if (Halt_bug_5)
                {
                    Halt_bug_5 = Halt_bug_3 = halted = skip_once = false;

                    if (interrupts_enabled)
                    {
                        interrupts_enabled = false;

                        TraceCallback?.Invoke(new TraceInfo
                        {
                            Disassembly  = "====IRQ====",
                            RegisterInfo = ""
                        });

                        RegPC--;

                        // TODO: If interrupt priotrity is checked differently in GBC, then this is incorrect
                        // a new interrupt vector would be needed
                        instr_pntr = 256 * 60 * 2 + 60 * 6;                                 // point to Interrupt
                    }
                    else
                    {
                        TraceCallback?.Invoke(new TraceInfo
                        {
                            Disassembly  = "====un-halted====",
                            RegisterInfo = ""
                        });

                        OnExecFetch?.Invoke(RegPC);
                        if (TraceCallback != null && !CB_prefix)
                        {
                            TraceCallback(State());
                        }
                        CDLCallback?.Invoke(RegPC, eCDLogMemFlags.FetchFirst);

                        FetchInstruction(ReadMemory(RegPC));
                    }
                }
                else if (temp && interrupts_enabled)
                {
                    interrupts_enabled = false;

                    TraceCallback?.Invoke(new TraceInfo
                    {
                        Disassembly  = "====IRQ====",
                        RegisterInfo = ""
                    });
                    halted = false;

                    if (Halt_bug_4)
                    {
                        // TODO: If interrupt priotrity is checked differently in GBC, then this is incorrect
                        // a new interrupt vector would be needed
                        DEC16_Func(PCl, PCh);
                        instr_pntr = 256 * 60 * 2 + 60 * 6;                                 // point to Interrupt
                        Halt_bug_4 = false;
                        skip_once  = false;
                        Halt_bug_3 = false;
                    }
                    else if (is_GBC)
                    {
                        // call the interrupt processor after 4 extra cycles
                        if (!Halt_bug_3)
                        {
                            instr_pntr = 256 * 60 * 2 + 60 * 7;                                     // point to Interrupt for GBC
                        }
                        else
                        {
                            // TODO: If interrupt priotrity is checked differently in GBC, then this is incorrect
                            // a new interrupt vector would be needed
                            instr_pntr = 256 * 60 * 2 + 60 * 6;                                     // point to Interrupt
                            Halt_bug_3 = false;
                            //Console.WriteLine("Hit INT");
                        }
                    }
                    else
                    {
                        // call interrupt processor
                        instr_pntr = 256 * 60 * 2 + 60 * 6;                                 // point to Interrupt
                        Halt_bug_3 = false;
                    }
                }
                else if (temp)
                {
                    // even if interrupt servicing is disabled, any interrupt flag raised still resumes execution
                    TraceCallback?.Invoke(new TraceInfo
                    {
                        Disassembly  = "====un-halted====",
                        RegisterInfo = ""
                    });
                    halted = false;

                    if (is_GBC)
                    {
                        // extra 4 cycles for GBC
                        if (Halt_bug_3)
                        {
                            OnExecFetch?.Invoke(RegPC);
                            if (TraceCallback != null && !CB_prefix)
                            {
                                TraceCallback(State());
                            }
                            CDLCallback?.Invoke(RegPC, eCDLogMemFlags.FetchFirst);

                            RegPC++;
                            FetchInstruction(ReadMemory(RegPC));
                            Halt_bug_3 = false;
                            //Console.WriteLine("Hit un");
                        }
                        else
                        {
                            instr_pntr = 256 * 60 * 2 + 60;                                     // exit halt loop
                        }
                    }
                    else
                    {
                        OnExecFetch?.Invoke(RegPC);
                        if (TraceCallback != null && !CB_prefix)
                        {
                            TraceCallback(State());
                        }
                        CDLCallback?.Invoke(RegPC, eCDLogMemFlags.FetchFirst);

                        if (Halt_bug_3)
                        {
                            //special variant of halt bug where RegPC also isn't incremented post fetch
                            RegPC++;
                            FetchInstruction(ReadMemory(RegPC));
                            Halt_bug_3 = false;
                        }
                        else
                        {
                            FetchInstruction(ReadMemory(RegPC++));
                        }
                    }
                }
                else
                {
                    if (skip_once)
                    {
                        instr_pntr = 256 * 60 * 2 + 60 * 2;                                 // point to skipped loop
                        skip_once  = false;
                    }
                    else
                    {
                        if (is_GBC)
                        {
                            instr_pntr = 256 * 60 * 2 + 60 * 3;                                     // point to GBC Halt loop
                        }
                        else
                        {
                            instr_pntr = 256 * 60 * 2 + 60 * 4;                                     // point to spec Halt loop
                        }
                    }
                }
                I_use = false;
                break;

            case STOP:
                stopped = true;
                if (!stop_check)
                {
                    // Z contains the second stop byte, not sure if it's useful at all
                    stop_time  = SpeedFunc(0);
                    stop_check = true;
                }

                interrupt_src_reg = GetIntRegs(0);

                if (stop_time > 0)
                {
                    // Timer interrupts can prematurely terminate a speedchange, nt sure about other sources
                    // NOTE: some testing around the edge case of where the speed actually changes is needed
                    if (I_use && interrupts_enabled)
                    {
                        interrupts_enabled = false;
                        I_use = false;

                        TraceCallback?.Invoke(new TraceInfo
                        {
                            Disassembly  = "====un-stop====",
                            RegisterInfo = ""
                        });

                        stopped    = false;
                        stop_check = false;
                        stop_time  = 0;

                        TraceCallback?.Invoke(new TraceInfo
                        {
                            Disassembly  = "====IRQ====",
                            RegisterInfo = ""
                        });

                        // call interrupt processor
                        // lowest bit set is highest priority
                        instr_pntr = 256 * 60 * 2 + 60 * 6;                                 // point to Interrupt
                        break;
                    }

                    if (stop_time == (32769))
                    {
                        SpeedFunc(1);
                    }

                    stop_time--;

                    if (stop_time == 0)
                    {
                        TraceCallback?.Invoke(new TraceInfo
                        {
                            Disassembly  = "====un-stop====",
                            RegisterInfo = ""
                        });

                        stopped = false;

                        // it takes the CPU 4 cycles longer to restart then the rest of the system.
                        instr_pntr = 256 * 60 * 2 + 60;

                        stop_check = false;

                        break;
                    }

                    // If a button is pressed during speed change, the processor will jam
                    if (interrupt_src_reg.Bit(4))
                    {
                        stop_time++;
                        break;
                    }
                }

                // Button press will exit stop loop even if speed change in progress, even without interrupts enabled
                if (interrupt_src_reg.Bit(4))
                {
                    // TODO: On a gameboy, you can only un-STOP once, needs further testing
                    TraceCallback?.Invoke(new TraceInfo
                    {
                        Disassembly  = "====un-stop====",
                        RegisterInfo = ""
                    });

                    stopped = false;
                    OnExecFetch?.Invoke(RegPC);
                    if (TraceCallback != null && !CB_prefix)
                    {
                        TraceCallback(State());
                    }
                    CDLCallback?.Invoke(RegPC, eCDLogMemFlags.FetchFirst);
                    FetchInstruction(ReadMemory(RegPC++));

                    stop_check = false;
                }
                else
                {
                    instr_pntr = 256 * 60 * 2 + 60 * 5;                             // point to stop loop
                }
                break;

            case PREFIX:
                CB_prefix = true;
                break;

            case ASGN:
                ASGN_Func(instr_table[instr_pntr], instr_table[instr_pntr + 1]);
                instr_pntr += 2;
                break;

            case ADDS:
                ADDS_Func(instr_table[instr_pntr], instr_table[instr_pntr + 1], instr_table[instr_pntr + 2], instr_table[instr_pntr + 3]);
                instr_pntr += 4;
                break;

            case OP_G:
                OnExecFetch?.Invoke(RegPC);
                TraceCallback?.Invoke(State());
                CDLCallback?.Invoke(RegPC, eCDLogMemFlags.FetchFirst);

                FetchInstruction(ReadMemory(RegPC));                         // note no increment
                break;

            case JAM:
                jammed = true;
                instr_pntr--;
                break;

            case RD_F:
                Read_Func_F(instr_table[instr_pntr], instr_table[instr_pntr + 1], instr_table[instr_pntr + 2]);
                instr_pntr += 3;
                break;

            case EI_RETI:
                EI_pending = 1;
                break;

            case INT_GET:
                // check if any interrupts got cancelled along the way
                // interrupt src = 5 sets the PC to zero as observed
                // also the triggering interrupt seems like it is held low (i.e. cannot trigger I flag) until the interrupt is serviced
                ushort bit_check = instr_table[instr_pntr++];
                //Console.WriteLine(interrupt_src + " " + interrupt_enable + " " + TotalExecutedCycles);

                interrupt_src_reg    = GetIntRegs(0);
                interrupt_enable_reg = GetIntRegs(1);

                if (interrupt_src_reg.Bit(bit_check) && interrupt_enable_reg.Bit(bit_check))
                {
                    int_src = bit_check; int_clear = (byte)(1 << bit_check);
                }

                /*
                 * if (interrupt_src.Bit(0) && interrupt_enable.Bit(0)) { int_src = 0; int_clear = 1; }
                 * else if (interrupt_src.Bit(1) && interrupt_enable.Bit(1)) { int_src = 1; int_clear = 2; }
                 * else if (interrupt_src.Bit(2) && interrupt_enable.Bit(2)) { int_src = 2; int_clear = 4; }
                 * else if (interrupt_src.Bit(3) && interrupt_enable.Bit(3)) { int_src = 3; int_clear = 8; }
                 * else if (interrupt_src.Bit(4) && interrupt_enable.Bit(4)) { int_src = 4; int_clear = 16; }
                 * else { int_src = 5; int_clear = 0; }
                 */
                Regs[instr_table[instr_pntr++]] = INT_vectors[int_src];
                break;

            case HALT_CHK:
                I_use = FlagI;
                if (Halt_bug_2 && I_use)
                {
                    RegPC--;
                    Halt_bug_3 = true;
                    //Console.WriteLine("Halt_bug_3");
                    //Console.WriteLine(TotalExecutedCycles);
                }

                Halt_bug_2 = false;
                break;

            case HALT_CHK_2:
                if (FlagI && !I_use)
                {
                    Halt_bug_5 = true;
                }
                break;

            case IRQ_CLEAR:
                interrupt_src_reg    = GetIntRegs(0);
                interrupt_enable_reg = GetIntRegs(1);

                if (interrupt_src_reg.Bit(int_src))
                {
                    interrupt_src_reg -= int_clear;
                }

                SetIntRegs(interrupt_src_reg);

                if ((interrupt_src_reg & interrupt_enable_reg) == 0)
                {
                    FlagI = false;
                }

                // reset back to default state
                int_src   = 5;
                int_clear = 0;
                break;

            case COND_CHECK:
                checker = false;
                switch (instr_table[instr_pntr++])
                {
                case ALWAYS_T:
                    checker = true;
                    break;

                case ALWAYS_F:
                    checker = false;
                    break;

                case FLAG_Z:
                    checker = FlagZ;
                    break;

                case FLAG_NZ:
                    checker = !FlagZ;
                    break;

                case FLAG_C:
                    checker = FlagC;
                    break;

                case FLAG_NC:
                    checker = !FlagC;
                    break;
                }

                // true condition is what is represented in the instruction vectors
                // jump ahead if false
                if (checker)
                {
                    instr_pntr++;
                }
                else
                {
                    // 0 = JR COND, 1 = JP COND, 2 = RET COND, 3 = CALL
                    switch (instr_table[instr_pntr++])
                    {
                    case 0:
                        instr_pntr += 10;
                        break;

                    case 1:
                        instr_pntr += 8;
                        break;

                    case 2:
                        instr_pntr += 22;
                        break;

                    case 3:
                        instr_pntr += 26;
                        break;
                    }
                }
                break;

            case HALT_FUNC:
                if (was_FlagI && (EI_pending == 0) && !interrupts_enabled)
                {
                    // in GBC mode, the HALT bug is worked around by simply adding a NOP
                    // so it just takes 4 cycles longer to reach the next instruction

                    // otherwise, if interrupts are disabled,
                    // a glitchy decrement to the program counter happens

                    // either way, nothing needs to be done here
                }
                else
                {
                    instr_pntr += 3;

                    if (!is_GBC)
                    {
                        skip_once = true;
                    }
                    // If the interrupt flag is not currently set, but it does get set in the first check
                    // then a bug is triggered
                    // With interrupts enabled, this runs the halt command twice
                    // when they are disabled, it reads the next byte twice
                    if (!was_FlagI || (was_FlagI && !interrupts_enabled))
                    {
                        Halt_bug_2 = true;
                    }

                    // If the I flag was set right before hitting this point, then there is no extra cycle for the halt
                    // also there is a glitched increment to the program counter
                    if (was_FlagI && interrupts_enabled)
                    {
                        Halt_bug_4 = true;
                    }
                }
                break;
            }
            TotalExecutedCycles++;
        }
Exemplo n.º 4
0
        /// <summary>
        /// Runs a single CPU clock cycle
        /// </summary>
        public void ExecuteOne()
        {
            switch (cur_instr[instr_pntr++])
            {
            // always the last tick within an opcode instruction cycle
            case END:
                OnExecFetch?.Invoke(RegPC0);
                TraceCallback?.Invoke(State());
                opcode     = (byte)Regs[DB];
                instr_pntr = 0;
                FetchInstruction();
                break;

            // used as timing 'padding'
            case IDLE:
                break;

            // load one register into another (or databus)
            case OP_LR8:
                LR_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            // load DB into A (as a part of an IN or INS instruction)
            case OP_LR_A_DB_IO:
                LR_A_IO_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            // loads supplied index value into register
            case OP_LIS:
                Regs[ALU1] = (byte)(cur_instr[instr_pntr++] & 0x0F);
                LR_Func(A, ALU1);
                break;

            // Shift register n bit positions to the right (zero fill)
            case OP_SHFT_R:
                SR_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            // Shift register n bit positions to the left (zero fill)
            case OP_SHFT_L:
                SL_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            // x <- (x) ADD y
            case OP_ADD8:
                ADD_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            // x <- (x) MINUS y
            case OP_SUB8:
                SUB_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            // x <- (x) ADD y (decimal)
            case OP_ADD8D:
                ADDD_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            // A <- (A) + (C)
            case OP_LNK:
                Regs[ALU0] = (byte)(FlagC ? 1 : 0);
                ADD_Func(A, ALU0);
                break;

            // Clear ICB status bit
            case OP_DI:
                FlagICB = false;
                break;

            // Set ICB status bit
            case OP_EI:
                FlagICB = true;
                break;

            // x <- (y) XOR DB
            case OP_XOR8:
                XOR_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            // x <- (x) + 1
            case OP_INC8:
                ADD_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            // x <- (y) & DB
            case OP_AND8:
                AND_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            // x <- (y) | DB
            case OP_OR8:
                OR_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            // DB + (x) + 1 (modify flags without saving result)
            case OP_CI:
                CI_Func();
                break;

            // ISAR is incremented
            case OP_IS_INC:
                Regs[ISAR] = (byte)((Regs[ISAR] & 0x38) | ((Regs[ISAR] + 1) & 0x07));
                break;

            // ISAR is decremented
            case OP_IS_DEC:
                Regs[ISAR] = (byte)((Regs[ISAR] & 0x38) | ((Regs[ISAR] - 1) & 0x07));
                break;

            // set the upper octal ISAR bits (b3,b4,b5)
            case OP_LISU:
                Regs[ISAR] = (byte)((((Regs[ISAR] & 0x07) | (cur_instr[instr_pntr++] & 0x07) << 3)) & 0x3F);
                break;

            // set the lower octal ISAR bits (b0,b1,b2)
            case OP_LISL:
                Regs[ISAR] = (byte)(((Regs[ISAR] & 0x38) | (cur_instr[instr_pntr++] & 0x07)) & 0x3F);
                break;

            // decrement scratchpad byte
            //case OP_DS:
            //SUB_Func(cur_instr[instr_pntr++], ONE);
            //break;

            // Branch on TRUE
            case OP_BT:
                bool branchBT = false;
                switch (cur_instr[instr_pntr++])
                {
                case 0:
                    // do not branch
                    break;

                case 1:
                    // branch if positive (sign bit is set)
                    if (FlagS)
                    {
                        branchBT = true;
                    }
                    break;

                case 2:
                    // branch on carry (carry bit is set)
                    if (FlagC)
                    {
                        branchBT = true;
                    }
                    break;

                case 3:
                    // branch if positive or on carry
                    if (FlagS || FlagC)
                    {
                        branchBT = true;
                    }
                    break;

                case 4:
                    // branch if zero (zero bit is set)
                    if (FlagZ)
                    {
                        branchBT = true;
                    }
                    break;

                case 5:
                    // branch if positive and zero
                    if (FlagS || FlagZ)
                    {
                        branchBT = true;
                    }
                    break;

                case 6:
                    // branch if zero or on carry
                    if (FlagZ || FlagC)
                    {
                        branchBT = true;
                    }
                    break;

                case 7:
                    // branch if positive or on carry or zero
                    if (FlagS || FlagC || FlagZ)
                    {
                        branchBT = true;
                    }
                    break;
                }

                instr_pntr = 0;
                if (branchBT)
                {
                    DO_BRANCH();
                }
                else
                {
                    DONT_BRANCH();
                }
                break;

            // Branch on ISARL
            case OP_BR7:
                instr_pntr = 1;                                 // lose a cycle
                if (Regs[ISAR].Bit(0) && Regs[ISAR].Bit(1) && Regs[ISAR].Bit(2))
                {
                    DONT_BRANCH();
                }
                else
                {
                    DO_BRANCH();
                }
                break;

            //  Branch on FALSE
            case OP_BF:
                bool branchBF = false;
                switch (cur_instr[instr_pntr++])
                {
                case 0:
                    // unconditional branch relative
                    branchBF = true;
                    break;

                case 1:
                    // branch on negative (sign bit is reset)
                    if (!FlagS)
                    {
                        branchBF = true;
                    }
                    break;

                case 2:
                    // branch if no carry (carry bit is reset)
                    if (!FlagC)
                    {
                        branchBF = true;
                    }
                    break;

                case 3:
                    // branch if no carry and negative
                    if (!FlagC && !FlagS)
                    {
                        branchBF = true;
                    }
                    break;

                case 4:
                    // branch if not zero (zero bit is reset)
                    if (!FlagZ)
                    {
                        branchBF = true;
                    }
                    break;

                case 5:
                    // branch if not zero and negative
                    if (!FlagS && !FlagZ)
                    {
                        branchBF = true;
                    }
                    break;

                case 6:
                    // branch if no carry and result is no zero
                    if (!FlagC && !FlagZ)
                    {
                        branchBF = true;
                    }
                    break;

                case 7:
                    // branch if not zero, carry and sign
                    if (!FlagS && !FlagC && !FlagZ)
                    {
                        branchBF = true;
                    }
                    break;

                case 8:
                    // branch if there is no overflow (OVF bit is reset)
                    if (!FlagO)
                    {
                        branchBF = true;
                    }
                    break;

                case 9:
                    // branch if negative and no overflow
                    if (!FlagS && !FlagO)
                    {
                        branchBF = true;
                    }
                    break;

                case 0xA:
                    // branch if no overflow and no carry
                    if (!FlagO && !FlagC)
                    {
                        branchBF = true;
                    }
                    break;

                case 0xB:
                    // branch if no overflow, no carry & negative
                    if (!FlagO && !FlagC && !FlagS)
                    {
                        branchBF = true;
                    }
                    break;

                case 0xC:
                    // branch if no overflow and not zero
                    if (!FlagO && !FlagZ)
                    {
                        branchBF = true;
                    }
                    break;

                case 0xD:
                    // branch if no overflow, not zero and neg
                    if (!FlagS && !FlagO && !FlagZ)
                    {
                        branchBF = true;
                    }
                    break;

                case 0xE:
                    // branch if no overflow, no carry & not zero
                    if (!FlagO && !FlagC && !FlagZ)
                    {
                        branchBF = true;
                    }
                    break;

                case 0xF:
                    // all neg
                    if (!FlagO && !FlagC && !FlagS && FlagZ)
                    {
                        branchBF = true;
                    }
                    break;
                }

                instr_pntr = 0;
                if (branchBF)
                {
                    DO_BRANCH();
                }
                else
                {
                    DONT_BRANCH();
                }
                break;

            // A <- (I/O Port 0 or 1)
            case OP_IN:
                instr_pntr++;                         // dest == A
                Regs[ALU0] = cur_instr[instr_pntr++]; // src
                IN_Func(A, ALU0);
                break;

            // I/O Port 0 or 1 <- (A)
            case OP_OUT:
                WriteHardware(cur_instr[instr_pntr++], (byte)Regs[cur_instr[instr_pntr++]]);
                break;

            // instruction fetch
            // The device whose address space includes the contents of the PC0 register must place on the data bus the op code addressed by PC0;
            // then all devices increments the content of PC0.
            // CYCLE LENGTH: S
            case ROMC_00_S:
                Read_Func(DB, PC0l, PC0h);
                RegPC0++;
                break;

            // instruction fetch
            // The device whose address space includes the contents of the PC0 register must place on the data bus the op code addressed by PC0;
            // then all devices increments the content of PC0.
            // CYCLE LENGTH: L
            case ROMC_00_L:
                Read_Func(DB, PC0l, PC0h);
                RegPC0++;
                break;

            // The device whose address space includes the contents of the PC0 register must place on the data bus the contents of the memory location
            // addressed by by PC0; then all devices add the 8-bit value on the data bus, as a signed binary number, to PC0
            // CYCLE LENGTH: L
            case ROMC_01:
                Read_Func(DB, PC0l, PC0h);
                RegPC0 += (ushort)((SByte)Regs[DB]);
                break;

            // The device whose DC0 address addresses a memory word within the address space of that device must place on the data bus the contents
            // of the memory location addressed by DC0; then all devices increment DC0
            // CYCLE LENGTH: L
            case ROMC_02:
                Read_Func(DB, DC0l, DC0h);
                RegDC0++;
                break;

            // Similar to 0x00, except that it is used for Immediate Operand fetches (using PC0) instead of instruction fetches
            // CYCLE LENGTH: S
            case ROMC_03_S:
                Read_Func(DB, PC0l, PC0h);
                RegPC0++;
                Regs[IO] = Regs[DB];
                break;

            // Similar to 0x00, except that it is used for Immediate Operand fetches (using PC0) instead of instruction fetches
            // CYCLE LENGTH: L
            case ROMC_03_L:
                Read_Func(DB, PC0l, PC0h);
                RegPC0++;
                Regs[IO] = Regs[DB];
                break;

            // Copy the contents of PC1 into PC0
            // CYCLE LENGTH: S
            case ROMC_04:
                RegPC0 = RegPC1;
                break;

            // Store the data bus contents into the memory location pointed to by DC0; increment DC0
            // CYCLE LENGTH: L
            case ROMC_05:
                Write_Func(DC0l, DC0h, DB);
                break;

            // Place the high order byte of DC0 on the data bus
            // CYCLE LENGTH: L
            case ROMC_06:
                Regs[DB] = (byte)Regs[DC0h];
                break;

            // Place the high order byte of PC1 on the data bus
            // CYCLE LENGTH: L
            case ROMC_07:
                Regs[DB] = (byte)Regs[PC1h];
                break;

            // All devices copy the contents of PC0 into PC1. The CPU outputs zero on the data bus in this ROMC state.
            // Load the data bus into both halves of PC0, this clearing the register.
            // CYCLE LENGTH: L
            case ROMC_08:
                RegPC1     = RegPC0;
                Regs[DB]   = 0;
                Regs[PC0h] = 0;
                Regs[PC0l] = 0;
                break;

            // The device whose address space includes the contents of the DC0 register must place the low order byte of DC0 onto the data bus
            // CYCLE LENGTH: L
            case ROMC_09:
                Regs[DB] = (byte)Regs[DC0l];
                break;

            // All devices add the 8-bit value on the data bus, treated as a signed binary number, to the data counter
            // CYCLE LENGTH: L
            case ROMC_0A:
                RegDC0 += (ushort)((sbyte)Regs[DB]);
                break;

            // The device whose address space includes the value in PC1 must place the low order byte of PC1 on the data bus
            // CYCLE LENGTH: L
            case ROMC_0B:
                Regs[DB] = (byte)Regs[PC1l];
                break;

            // The device whose address space includes the contents of the PC0 register must place the contents of the memory word addressed by PC0
            // onto the data bus; then all devices move the value that has just been placed on the data bus into the low order byte of PC0
            // CYCLE LENGTH: L
            case ROMC_0C:
                Read_Func(DB, PC0l, PC0h);
                Regs[PC0l] = Regs[DB];
                break;

            // All devices store in PC1 the current contents of PC0, incremented by 1; PC0 is unaltered
            // CYCLE LENGTH: S
            case ROMC_0D:
                RegPC1 = (ushort)(RegPC0 + 1);
                break;

            // The device whose address space includes the contents of PC0 must place the contents of the word addressed by PC0 onto the data bus.
            // The value on the data bus is then moved to the low order byte of DC0 by all devices
            // CYCLE LENGTH: L
            case ROMC_0E:
                Read_Func(DB, PC0l, PC0h);
                Regs[DC0l] = Regs[DB];
                break;

            // The interrupting device with the highest priority must place the low order byte of the interrupt vector on the data bus.
            // All devices must copy the contents of PC0 into PC1. All devices must move the contents of the data bus into the low order byte of PC0
            // CYCLE LENGTH: L
            case ROMC_0F:
                throw new NotImplementedException("ROMC 0x0F not implemented");

            // Inhibit any modification to the interrupt priority logic
            // CYCLE LENGTH: L
            case ROMC_10:
                throw new NotImplementedException("ROMC 0x10 not implemented");

            // The device whose memory space includes the contents of PC0 must place the contents of the addressed memory word on the data bus.
            // All devices must then move the contents of the data bus to the upper byte of DC0
            // CYCLE LENGTH: L
            case ROMC_11:
                Read_Func(DB, PC0l, PC0h);
                Regs[DC0h] = Regs[DB];
                break;

            // All devices copy the contents of PC0 into PC1. All devices then move the contents of the data bus into the low order byte of PC0
            // CYCLE LENGTH: L
            case ROMC_12:
                RegPC1     = RegPC0;
                Regs[PC0l] = Regs[DB];
                break;

            // The interrupting device with the highest priority must move the high order half of the interrupt vector onto the data bus.
            // All devices must move the conetnts of the data bus into the high order byte of of PC0. The interrupting device resets its
            // interrupt circuitry (so that it is no longer requesting CPU servicing and can respond to another interrupt)
            // CYCLE LENGTH: L
            case ROMC_13:
                throw new NotImplementedException("ROMC 0x13 not implemented");

            // All devices move the contents of the data bus into the high order byte of PC0
            // CYCLE LENGTH: L
            case ROMC_14:
                Regs[PC0h] = Regs[DB];
                break;

            // All devices move the contents of the data bus into the high order byte of PC1
            // CYCLE LENGTH: L
            case ROMC_15:
                Regs[PC1h] = Regs[DB];
                break;

            // All devices move the contents of the data bus into the high order byte of DC0
            // CYCLE LENGTH: L
            case ROMC_16:
                Regs[DC0h] = Regs[DB];
                break;

            // All devices move the contents of the data bus into the low order byte of PC0
            // CYCLE LENGTH: L
            case ROMC_17:
                Regs[PC0l] = Regs[DB];
                break;

            // All devices move the contents of the data bus into the low order byte of PC1
            // CYCLE LENGTH: L
            case ROMC_18:
                Regs[PC1l] = Regs[DB];
                break;

            // All devices move the contents of the data bus into the low order byte of DC0
            // CYCLE LENGTH: L
            case ROMC_19:
                Regs[DC0l] = Regs[DB];
                break;

            // During the prior cycle, an I/O port timer or interrupt control register was addressed; the device containing the addressed
            // port must move the current contents of the data bus into the addressed port
            // CYCLE LENGTH: L
            case ROMC_1A:
                WriteHardware(Regs[IO], (byte)Regs[DB]);
                break;

            // During the prior cycle, the data bus specified the address of an I/O port. The device containing the addressed I/O port
            // must place the contents of the I/O port on the data bus. (Note that the contents of the timer and interrupt control
            // registers cannot be read back onto the data bus)
            // CYCLE LENGTH: L
            case ROMC_1B:
                IN_Func(DB, IO);
                //Regs[DB] = ReadHardware(Regs[IO]);
                break;

            // None
            // CYCLE LENGTH: S
            case ROMC_1C_S:
                break;

            // None
            // CYCLE LENGTH: L
            case ROMC_1C_L:
                break;

            // Devices with DC0 and DC1 registers must switch registers. Devices without a DC1 register perform no operation
            // CYCLE LENGTH: S
            case ROMC_1D:
                // we have no DC1 in this implementation
                break;

            // The device whose address space includes the contents of PC0 must place the low order byte of PC0 onto the data bus
            // CYCLE LENGTH: L
            case ROMC_1E:
                Regs[DB] = (byte)Regs[PC0l];
                break;

            // The device whose address space includes the contents of PC0 must place the high order byte of PC0 onto the data bus
            // CYCLE LENGTH: L
            case ROMC_1F:
                Regs[DB] = (byte)Regs[PC0h];
                break;
            }

            TotalExecutedCycles++;
        }
Exemplo n.º 5
0
        // Execute instructions
        public void ExecuteOne(ref byte interrupt_src, byte interrupt_enable)
        {
            switch (cur_instr[instr_pntr++])
            {
            case IDLE:
                // do nothing
                break;

            case OP:
                // Read the opcode of the next instruction
                if (EI_pending > 0 && !CB_prefix)
                {
                    EI_pending--;
                    if (EI_pending == 0)
                    {
                        interrupts_enabled = true;
                    }
                }

                if (I_use && interrupts_enabled && !CB_prefix && !jammed)
                {
                    interrupts_enabled = false;

                    TraceCallback?.Invoke(new TraceInfo
                    {
                        Disassembly  = "====IRQ====",
                        RegisterInfo = ""
                    });

                    // call interrupt processor
                    // lowest bit set is highest priority
                    INTERRUPT_();
                }
                else
                {
                    OnExecFetch?.Invoke(RegPC);
                    if (TraceCallback != null && !CB_prefix)
                    {
                        TraceCallback(State());
                    }
                    CDLCallback?.Invoke(RegPC, eCDLogMemFlags.FetchFirst);
                    FetchInstruction(ReadMemory(RegPC++));
                }
                instr_pntr = 0;
                I_use      = false;
                break;

            case RD:
                Read_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            case WR:
                Write_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            case TR:
                TR_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            case ADD16:
                ADD16_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], 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 INC16:
                INC16_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            case INC8:
                INC8_Func(cur_instr[instr_pntr++]);
                break;

            case DEC16:
                DEC16_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            case DEC8:
                DEC8_Func(cur_instr[instr_pntr++]);
                break;

            case RLC:
                RLC_Func(cur_instr[instr_pntr++]);
                break;

            case RL:
                RL_Func(cur_instr[instr_pntr++]);
                break;

            case RRC:
                RRC_Func(cur_instr[instr_pntr++]);
                break;

            case RR:
                RR_Func(cur_instr[instr_pntr++]);
                break;

            case CPL:
                CPL_Func(cur_instr[instr_pntr++]);
                break;

            case DA:
                DA_Func(cur_instr[instr_pntr++]);
                break;

            case SCF:
                SCF_Func(cur_instr[instr_pntr++]);
                break;

            case CCF:
                CCF_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 CP8:
                CP8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            case SLA:
                SLA_Func(cur_instr[instr_pntr++]);
                break;

            case SRA:
                SRA_Func(cur_instr[instr_pntr++]);
                break;

            case SRL:
                SRL_Func(cur_instr[instr_pntr++]);
                break;

            case SWAP:
                SWAP_Func(cur_instr[instr_pntr++]);
                break;

            case BIT:
                BIT_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            case RES:
                RES_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            case SET:
                SET_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            case EI:
                if (EI_pending == 0)
                {
                    EI_pending = 2;
                }
                break;

            case DI:
                interrupts_enabled = false;
                EI_pending         = 0;
                break;

            case HALT:
                halted = true;

                bool temp = false;

                if (cur_instr[instr_pntr++] == 1)
                {
                    temp = FlagI;
                }
                else
                {
                    temp = I_use;
                }

                if (EI_pending > 0 && !CB_prefix)
                {
                    EI_pending--;
                    if (EI_pending == 0)
                    {
                        interrupts_enabled = true;
                    }
                }

                // if the I flag is asserted at the time of halt, don't halt
                if (temp && interrupts_enabled && !CB_prefix && !jammed)
                {
                    interrupts_enabled = false;

                    TraceCallback?.Invoke(new TraceInfo
                    {
                        Disassembly  = "====IRQ====",
                        RegisterInfo = ""
                    });
                    halted = false;

                    if (is_GBC)
                    {
                        // call the interrupt processor after 4 extra cycles
                        if (!Halt_bug_3)
                        {
                            INTERRUPT_GBC_NOP();
                        }
                        else
                        {
                            INTERRUPT_();
                            Halt_bug_3 = false;
                            //Console.WriteLine("Hit INT");
                        }
                    }
                    else
                    {
                        // call interrupt processor
                        INTERRUPT_();
                        Halt_bug_3 = false;
                    }
                }
                else if (temp)
                {
                    // even if interrupt servicing is disabled, any interrupt flag raised still resumes execution
                    TraceCallback?.Invoke(new TraceInfo
                    {
                        Disassembly  = "====un-halted====",
                        RegisterInfo = ""
                    });
                    halted = false;

                    if (is_GBC)
                    {
                        // extra 4 cycles for GBC
                        if (Halt_bug_3)
                        {
                            OnExecFetch?.Invoke(RegPC);
                            if (TraceCallback != null && !CB_prefix)
                            {
                                TraceCallback(State());
                            }
                            CDLCallback?.Invoke(RegPC, eCDLogMemFlags.FetchFirst);

                            RegPC++;
                            FetchInstruction(ReadMemory(RegPC));
                            Halt_bug_3 = false;
                            //Console.WriteLine("Hit un");
                        }
                        else
                        {
                            cur_instr = new[]
                            {
                                IDLE,
                                IDLE,
                                IDLE,
                                OP
                            };
                        }
                    }
                    else
                    {
                        OnExecFetch?.Invoke(RegPC);
                        if (TraceCallback != null && !CB_prefix)
                        {
                            TraceCallback(State());
                        }
                        CDLCallback?.Invoke(RegPC, eCDLogMemFlags.FetchFirst);

                        if (Halt_bug_3)
                        {
                            //special variant of halt bug where RegPC also isn't incremented post fetch
                            RegPC++;
                            FetchInstruction(ReadMemory(RegPC));
                            Halt_bug_3 = false;
                        }
                        else
                        {
                            FetchInstruction(ReadMemory(RegPC++));
                        }
                    }
                }
                else
                {
                    if (skip_once)
                    {
                        cur_instr = new ushort[]
                        { IDLE,
                          IDLE,
                          IDLE,
                          HALT, 0 };

                        skip_once = false;
                    }
                    else
                    {
                        if (is_GBC)
                        {
                            cur_instr = new ushort[]
                            { IDLE,
                              IDLE,
                              HALT_CHK,
                              HALT, 0 };
                        }
                        else
                        {
                            cur_instr = new ushort[]
                            { HALT_CHK,
                              IDLE,
                              IDLE,
                              HALT, 0 };
                        }
                    }
                }
                I_use      = false;
                instr_pntr = 0;
                break;

            case STOP:
                stopped = true;
                if (!stop_check)
                {
                    stop_time  = SpeedFunc(0);
                    stop_check = true;
                }

                if (stop_time > 0)
                {
                    stop_time--;
                    if (stop_time == 0)
                    {
                        TraceCallback?.Invoke(new TraceInfo
                        {
                            Disassembly  = "====un-stop====",
                            RegisterInfo = ""
                        });

                        stopped = false;
                        OnExecFetch?.Invoke(RegPC);
                        if (TraceCallback != null && !CB_prefix)
                        {
                            TraceCallback(State());
                        }
                        CDLCallback?.Invoke(RegPC, eCDLogMemFlags.FetchFirst);
                        FetchInstruction(ReadMemory(RegPC++));
                        instr_pntr = 0;

                        stop_check = false;
                    }
                    else
                    {
                        instr_pntr = 0;
                        cur_instr  = new ushort[]
                        { IDLE,
                          IDLE,
                          IDLE,
                          STOP };
                    }
                }
                else if (interrupt_src.Bit(4))                         // button pressed, not actually an interrupt though
                {
                    TraceCallback?.Invoke(new TraceInfo
                    {
                        Disassembly  = "====un-stop====",
                        RegisterInfo = ""
                    });

                    stopped = false;
                    OnExecFetch?.Invoke(RegPC);
                    if (TraceCallback != null && !CB_prefix)
                    {
                        TraceCallback(State());
                    }
                    CDLCallback?.Invoke(RegPC, eCDLogMemFlags.FetchFirst);
                    FetchInstruction(ReadMemory(RegPC++));
                    instr_pntr = 0;

                    stop_check = false;
                }
                else
                {
                    instr_pntr = 0;
                    cur_instr  = new[]
                    {
                        IDLE,
                        IDLE,
                        IDLE,
                        STOP
                    };
                }
                break;

            case PREFIX:
                CB_prefix = true;
                break;

            case ASGN:
                ASGN_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            case ADDS:
                ADDS_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            case OP_G:
                OnExecFetch?.Invoke(RegPC);
                TraceCallback?.Invoke(State());
                CDLCallback?.Invoke(RegPC, eCDLogMemFlags.FetchFirst);

                FetchInstruction(ReadMemory(RegPC));                         // note no increment

                instr_pntr = 0;
                break;

            case JAM:
                jammed = true;
                instr_pntr--;
                break;

            case RD_F:
                Read_Func_F(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            case EI_RETI:
                EI_pending = 1;
                break;

            case INT_GET:
                // check if any interrupts got cancelled along the way
                // interrupt src = 5 sets the PC to zero as observed
                // also the triggering interrupt seems like it is held low (i.e. annot trigger I flag) until the interrupt is serviced

                if (interrupt_src.Bit(0) && interrupt_enable.Bit(0))
                {
                    int_src = 0; int_clear = 1;
                }
                else if (interrupt_src.Bit(1) && interrupt_enable.Bit(1))
                {
                    int_src = 1; int_clear = 2;
                }
                else if (interrupt_src.Bit(2) && interrupt_enable.Bit(2))
                {
                    int_src = 2; int_clear = 4;
                }
                else if (interrupt_src.Bit(3) && interrupt_enable.Bit(3))
                {
                    int_src = 3; int_clear = 8;
                }
                else if (interrupt_src.Bit(4) && interrupt_enable.Bit(4))
                {
                    int_src = 4; int_clear = 16;
                }
                else
                {
                    int_src = 5; int_clear = 0;
                }

                Regs[cur_instr[instr_pntr++]] = INT_vectors[int_src];
                break;

            case HALT_CHK:
                I_use = FlagI;
                if (Halt_bug_2 && I_use)
                {
                    RegPC--;
                    Halt_bug_3 = true;
                    //Console.WriteLine("Halt_bug_3");
                    //Console.WriteLine(TotalExecutedCycles);
                }

                Halt_bug_2 = false;
                break;

            case IRQ_CLEAR:
                if (interrupt_src.Bit(int_src))
                {
                    interrupt_src -= int_clear;
                }

                if ((interrupt_src & interrupt_enable) == 0)
                {
                    FlagI = false;
                }

                break;
            }
            TotalExecutedCycles++;
        }
Exemplo n.º 6
0
        // 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
                OnExecFetch?.Invoke(PC);
                TraceCallback?.Invoke(State());
                CDLCallback?.Invoke(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 IDX_DCDE:
                    Index_decode();
                    break;

                case IDX_OP_BLD:
                    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;
                }
                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_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 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_I:
                FlagI = true;
                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;

                    TraceCallback?.Invoke(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;

                    TraceCallback?.Invoke(new TraceInfo {
                        Disassembly = "====CWAI IRQ====", RegisterInfo = ""
                    });
                }
                else
                {
                    PopulateCURINSTR(WAI);
                    irq_pntr = 0;
                    IRQS     = -1;
                }
                instr_pntr = 0;
                break;
            }

            if (++irq_pntr == IRQS)
            {
                // NMI has priority
                if (NMIPending)
                {
                    NMIPending = false;

                    TraceCallback?.Invoke(new TraceInfo {
                        Disassembly = "====NMI====", RegisterInfo = ""
                    });

                    NMI_();
                    NMICallback();
                    instr_pntr = irq_pntr = 0;
                }
                // then regular IRQ
                else if (IRQPending && !FlagI)
                {
                    IRQPending = false;

                    TraceCallback?.Invoke(new TraceInfo {
                        Disassembly = "====IRQ====", RegisterInfo = ""
                    });

                    IRQ_();
                    IRQCallback();
                    instr_pntr = irq_pntr = 0;
                }
                // otherwise start the next instruction
                else
                {
                    PopulateCURINSTR(OP);
                    instr_pntr = irq_pntr = 0;
                    IRQS       = -1;
                }
            }

            TotalExecutedCycles++;
        }
Exemplo n.º 7
0
        /// <summary>
        /// Runs a single CPU clock cycle
        /// </summary>
        public void ExecuteOne()
        {
            if (Regs[ISAR] > 0x3F)
            {
            }
            if (Regs[W] > 0x1F)
            {
            }

            switch (cur_instr[instr_pntr++])
            {
            // always the last tick within an opcode instruction cycle
            case END:
                OnExecFetch?.Invoke(RegPC0);
                TraceCallback?.Invoke(State());
                opcode     = (byte)Regs[DB];
                instr_pntr = 0;
                FetchInstruction();
                break;

            // used as timing 'padding'
            case IDLE:
                break;

            // load one register into another (or databus)
            case OP_LR8:
                LR_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            // load DB into A (as a part of an IN or INS instruction)
            case OP_LR_A_DB_IO:
                LR_A_IO_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            // loads supplied index value into the bottom 4 bits of a register (upper bits are set to 0)
            case OP_LIS:
                Regs[ALU1] = (byte)(cur_instr[instr_pntr++] & 0x0F);
                LR_Func(A, ALU1);
                break;

            // Shift register n bit positions to the right (zero fill)
            case OP_SHFT_R:
                SR_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            // Shift register n bit positions to the left (zero fill)
            case OP_SHFT_L:
                SL_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            // x <- (x) ADD y
            case OP_ADD8:
                ADD_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            // x <- (x) MINUS y
            case OP_SUB8:
                SUB_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            // x <- (x) ADD y (decimal)
            case OP_ADD8D:
                ADDD_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            // A <- (A) + (C)
            case OP_LNK:
                Regs[ALU0] = (byte)(FlagC ? 1 : 0);
                ADD_Func(A, ALU0);
                break;

            // Clear ICB status bit
            case OP_DI:
                FlagICB = false;
                break;

            // Set ICB status bit
            case OP_EI:
                FlagICB = true;
                break;

            // x <- (y) XOR DB
            case OP_XOR8:
                XOR_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            // The accumulator is loaded with its one's complement
            case OP_COM:
                XOR_Func(A, BYTE);
                //Regs[A] = (byte)(Regs[A] ^ 0xFF);
                break;

            // x <- (x) + 1
            case OP_INC8:
                ADD_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            // x <- (y) & DB
            case OP_AND8:
                AND_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            // x <- (y) | DB
            case OP_OR8:
                OR_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            // DB + (x) + 1 (modify flags without saving result)
            case OP_CI:
                CI_Func();
                break;

            // ISAR is incremented
            case OP_IS_INC:
                Regs[ISAR] = (byte)((Regs[ISAR] & 0x38) | ((Regs[ISAR] + 1) & 0x07));
                break;

            // ISAR is decremented
            case OP_IS_DEC:
                Regs[ISAR] = (byte)((Regs[ISAR] & 0x38) | ((Regs[ISAR] - 1) & 0x07));
                break;

            // set the upper octal ISAR bits (b3,b4,b5) but do not alter the three least significant bits
            case OP_LISU:
                //Regs[ISAR] = (byte)(((Regs[ISAR] & 0x07) | (cur_instr[instr_pntr++] & 0x07) << 3) & 0x3F);
                Regs[ISAR] = (byte)((Regs[ISAR] & 0x07) | cur_instr[instr_pntr++]);
                break;

            // set the lower octal ISAR bits (b0,b1,b2) but do not alter the three most significant bits
            case OP_LISL:
                //Regs[ISAR] = (byte) (((Regs[ISAR] & 0x38) | (cur_instr[instr_pntr++] & 0x07)) & 0x3F);
                Regs[ISAR] = (byte)((Regs[ISAR] & 0x38) | cur_instr[instr_pntr++]);
                break;

            // Branch on TRUE
            case OP_BT:
                if ((Regs[W] & cur_instr[instr_pntr++]) > 0)
                {
                    DO_BRANCH(0);
                }
                else
                {
                    DONT_BRANCH(0);
                }
                break;

            // Branch on ISARL - if any of the low 3 bits of ISAR are reset
            case OP_BR7:

                if (!Regs[ISAR].Bit(0) || !Regs[ISAR].Bit(1) || !Regs[ISAR].Bit(2))
                {
                    DO_BRANCH(1);
                }
                else
                {
                    DONT_BRANCH(1);
                }

                /*
                 * if Regs[ISAR] & 7) == 7)
                 *      DONT_BRANCH(1);
                 * else
                 *      DO_BRANCH(1);
                 */
                break;

            // Branch on FALSE
            case OP_BF:
                if ((Regs[W] & cur_instr[instr_pntr++]) > 0)
                {
                    DONT_BRANCH(0);
                }
                else
                {
                    DO_BRANCH(0);
                }
                break;

            /*
             *
             * // Unconditional Branch (relative)
             * case OP_BR:
             * DO_BF_BRANCH(0);
             * break;
             *
             * // Branch on Negative
             * case OP_BM:
             * if (!FlagS)
             *      DO_BF_BRANCH(0);
             * else
             *      DONT_BF_BRANCH(0);
             * break;
             *
             * // Branch if no carry
             * case OP_BNC:
             * if (!FlagC)
             *      DO_BF_BRANCH(0);
             * else
             *      DONT_BF_BRANCH(0);
             * break;
             *
             * // Branch if negative and no carry
             * case OP_BF_CS:
             * if (!FlagS && !FlagC)
             *      DO_BF_BRANCH(0);
             * else
             *      DONT_BF_BRANCH(0);
             * break;
             *
             * // Branch if not zero
             * case OP_BNZ:
             * instr_pntr = 0;
             * if (!FlagZ)
             *      DO_BF_BRANCH(0);
             * else
             *      DONT_BF_BRANCH(0);
             * break;
             *
             * // Branch on Negative and not Zero (same thing as branch on negative)
             * case OP_BF_ZS:
             * if (!FlagS && !FlagZ)
             *      DO_BF_BRANCH(0);
             * else
             *      DONT_BF_BRANCH(0);
             * break;
             *
             * // Branch on no Carry and not Zero
             * case OP_BF_ZC:
             * if (!FlagC && !FlagZ)
             *      DO_BF_BRANCH(0);
             * else
             *      DONT_BF_BRANCH(0);
             * break;
             *
             * // Branch on no Carry and not Zero and Negative
             * case OP_BF_ZCS:
             * if (!FlagC && !FlagZ && !FlagS)
             *      DO_BF_BRANCH(0);
             * else
             *      DONT_BF_BRANCH(0);
             * break;
             *
             * // Branch on no Overflow
             * case OP_BNO:
             * if (!FlagO)
             *      DO_BF_BRANCH(0);
             * else
             *      DONT_BF_BRANCH(0);
             * break;
             *
             * // Branch on no overflow and Negative
             * case OP_BF_OS:
             * if (!FlagO && !FlagS)
             *      DO_BF_BRANCH(0);
             * else
             *      DONT_BF_BRANCH(0);
             * break;
             *
             * // Branch on no overflow and Negative
             * case OP_BF_OC:
             * if (!FlagO && !FlagC)
             *      DO_BF_BRANCH(0);
             * else
             *      DONT_BF_BRANCH(0);
             * break;
             *
             * // Branch on no overflow and no carry and Negative
             * case OP_BF_OCS:
             * if (!FlagO && !FlagC && !FlagS)
             *      DO_BF_BRANCH(0);
             * else
             *      DONT_BF_BRANCH(0);
             * break;
             *
             * // Branch on no overflow and not zero
             * case OP_BF_OZ:
             * if (!FlagO && !FlagZ)
             *      DO_BF_BRANCH(0);
             * else
             *      DONT_BF_BRANCH(0);
             * break;
             *
             * // Branch on no overflow and not zero and negative
             * case OP_BF_OZS:
             * if (!FlagO && !FlagZ && !FlagS)
             *      DO_BF_BRANCH(0);
             * else
             *      DONT_BF_BRANCH(0);
             * break;
             *
             * // Branch on no overflow and not zero and no carry
             * case OP_BF_OZC:
             * if (!FlagO && !FlagZ && !FlagC)
             *      DO_BF_BRANCH(0);
             * else
             *      DONT_BF_BRANCH(0);
             * break;
             *
             * // Branch on no overflow and not zero and no carry and negative
             * case OP_BF_OZCS:
             * if (!FlagO && !FlagZ && !FlagC && FlagS)
             *      DO_BF_BRANCH(0);
             * else
             *      DONT_BF_BRANCH(0);
             * break;
             */
/*
 *                              // Branch on true - no branch
 *                              case OP_BTN:
 *                                      DONT_BT_BRANCH(0);
 *                                      break;
 *
 *                              // Branch if positive
 *                              case OP_BP:
 *                                      if (FlagS)
 *                                              DO_BT_BRANCH(0);
 *                                      else
 *                                              DONT_BT_BRANCH(0);
 *                                      break;
 *
 *                              // Branch on carry
 *                              case OP_BC:
 *                                      if (FlagC)
 *                                              DO_BT_BRANCH(0);
 *                                      else
 *                                              DONT_BT_BRANCH(0);
 *                                      break;
 *
 *                              // Branch on carry or positive
 *                              case OP_BT_CS:
 *                                      if (FlagC || FlagS)
 *                                              DO_BT_BRANCH(0);
 *                                      else
 *                                              DONT_BT_BRANCH(0);
 *                                      break;
 *
 *                              // Branch if zero
 *                              case OP_BZ:
 *                                      if (FlagZ)
 *                                              DO_BT_BRANCH(0);
 *                                      else
 *                                              DONT_BT_BRANCH(0);
 *                                      break;
 *
 *                              // Branch if zero or positive
 *                              case OP_BT_ZS:
 *                                      if (FlagZ || FlagS)
 *                                              DO_BT_BRANCH(0);
 *                                      else
 *                                              DONT_BT_BRANCH(0);
 *                                      break;
 *
 *                              // Branch if zero or carry
 *                              case OP_BT_ZC:
 *                                      if (FlagZ || FlagC)
 *                                              DO_BT_BRANCH(0);
 *                                      else
 *                                              DONT_BT_BRANCH(0);
 *                                      break;
 *
 *                              // Branch if zero or carry or positive
 *                              case OP_BT_ZCS:
 *                                      if (FlagZ || FlagC || FlagS)
 *                                              DO_BT_BRANCH(0);
 *                                      else
 *                                              DONT_BT_BRANCH(0);
 *                                      break;
 */


            // A <- (I/O Port 0 or 1)
            case OP_IN:
                IN_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            // I/O Port 0 or 1 <- (A)
            case OP_OUT:
                OUT_Func(IO, A);
                break;

            // instruction fetch
            // The device whose address space includes the contents of the PC0 register must place on the data bus the op code addressed by PC0;
            // then all devices increments the content of PC0.
            // CYCLE LENGTH: S
            case ROMC_00_S:
                Read_Func(DB, PC0l, PC0h);
                RegPC0++;
                break;

            // instruction fetch
            // The device whose address space includes the contents of the PC0 register must place on the data bus the op code addressed by PC0;
            // then all devices increments the content of PC0.
            // CYCLE LENGTH: L
            case ROMC_00_L:
                Read_Func(DB, PC0l, PC0h);
                RegPC0++;
                break;

            // The device whose address space includes the contents of the PC0 register must place on the data bus the contents of the memory location
            // addressed by by PC0; then all devices add the 8-bit value on the data bus, as a signed binary number, to PC0
            // CYCLE LENGTH: L
            case ROMC_01:
                Read_Func(DB, PC0l, PC0h);
                RegPC0 = Regs[DB].Bit(7) ? (ushort)(RegPC0 - (byte)((Regs[DB] ^ 0xFF) + 1)) : (ushort)(RegPC0 + Regs[DB]);
                break;

            // The device whose DC0 address addresses a memory word within the address space of that device must place on the data bus the contents
            // of the memory location addressed by DC0; then all devices increment DC0
            // CYCLE LENGTH: L
            case ROMC_02:
                Read_Func(DB, DC0l, DC0h);
                RegDC0++;
                break;

            // Similar to 0x00, except that it is used for Immediate Operand fetches (using PC0) instead of instruction fetches
            // CYCLE LENGTH: S
            case ROMC_03_S:
                Read_Func(DB, PC0l, PC0h);
                RegPC0++;
                Regs[IO] = Regs[DB];
                break;

            // Similar to 0x00, except that it is used for Immediate Operand fetches (using PC0) instead of instruction fetches
            // CYCLE LENGTH: L
            case ROMC_03_L:
                Read_Func(DB, PC0l, PC0h);
                RegPC0++;
                Regs[IO] = Regs[DB];
                break;

            // Copy the contents of PC1 into PC0
            // CYCLE LENGTH: S
            case ROMC_04:
                RegPC0 = RegPC1;
                break;

            // Store the data bus contents into the memory location pointed to by DC0; increment DC0
            // CYCLE LENGTH: L
            case ROMC_05:
                Write_Func(DC0l, DC0h, DB);
                RegDC0++;
                break;

            // Place the high order byte of DC0 on the data bus
            // CYCLE LENGTH: L
            case ROMC_06:
                Regs[DB] = (byte)Regs[DC0h];
                break;

            // Place the high order byte of PC1 on the data bus
            // CYCLE LENGTH: L
            case ROMC_07:
                Regs[DB] = (byte)Regs[PC1h];
                break;

            // All devices copy the contents of PC0 into PC1. The CPU outputs zero on the data bus in this ROMC state.
            // Load the data bus into both halves of PC0, this clearing the register.
            // CYCLE LENGTH: L
            case ROMC_08:
                RegPC1     = RegPC0;
                Regs[DB]   = 0;
                Regs[PC0h] = 0;
                Regs[PC0l] = 0;
                break;

            // The device whose address space includes the contents of the DC0 register must place the low order byte of DC0 onto the data bus
            // CYCLE LENGTH: L
            case ROMC_09:
                Regs[DB] = (byte)Regs[DC0l];
                break;

            // All devices add the 8-bit value on the data bus, treated as a signed binary number, to the data counter
            // CYCLE LENGTH: L
            case ROMC_0A:
                // The contents of the accumulator are treated as a signed binary number, and are added to the contents of every DCO register.
                RegDC0 = Regs[DB].Bit(7) ? (ushort)(RegDC0 - (byte)((Regs[DB] ^ 0xFF) + 1)) : (ushort)(RegDC0 + Regs[DB]);
                break;

            // The device whose address space includes the value in PC1 must place the low order byte of PC1 on the data bus
            // CYCLE LENGTH: L
            case ROMC_0B:
                Regs[DB] = (byte)Regs[PC1l];
                break;

            // The device whose address space includes the contents of the PC0 register must place the contents of the memory word addressed by PC0
            // onto the data bus; then all devices move the value that has just been placed on the data bus into the low order byte of PC0
            // CYCLE LENGTH: L
            case ROMC_0C:
                Read_Func(DB, PC0l, PC0h);
                Regs[PC0l] = Regs[DB];
                break;

            // All devices store in PC1 the current contents of PC0, incremented by 1; PC0 is unaltered
            // CYCLE LENGTH: S
            case ROMC_0D:
                RegPC1 = (ushort)(RegPC0 + 1);
                break;

            // The device whose address space includes the contents of PC0 must place the contents of the word addressed by PC0 onto the data bus.
            // The value on the data bus is then moved to the low order byte of DC0 by all devices
            // CYCLE LENGTH: L
            case ROMC_0E:
                Read_Func(DB, PC0l, PC0h);
                Regs[DC0l] = Regs[DB];
                break;

            // The interrupting device with the highest priority must place the low order byte of the interrupt vector on the data bus.
            // All devices must copy the contents of PC0 into PC1. All devices must move the contents of the data bus into the low order byte of PC0
            // CYCLE LENGTH: L
            case ROMC_0F:
                throw new NotImplementedException("ROMC 0x0F not implemented");

            // Inhibit any modification to the interrupt priority logic
            // CYCLE LENGTH: L
            case ROMC_10:
                throw new NotImplementedException("ROMC 0x10 not implemented");

            // The device whose memory space includes the contents of PC0 must place the contents of the addressed memory word on the data bus.
            // All devices must then move the contents of the data bus to the upper byte of DC0
            // CYCLE LENGTH: L
            case ROMC_11:
                Read_Func(DB, PC0l, PC0h);
                Regs[DC0h] = Regs[DB];
                break;

            // All devices copy the contents of PC0 into PC1. All devices then move the contents of the data bus into the low order byte of PC0
            // CYCLE LENGTH: L
            case ROMC_12:
                RegPC1     = RegPC0;
                Regs[PC0l] = Regs[DB];
                break;

            // The interrupting device with the highest priority must move the high order half of the interrupt vector onto the data bus.
            // All devices must move the conetnts of the data bus into the high order byte of of PC0. The interrupting device resets its
            // interrupt circuitry (so that it is no longer requesting CPU servicing and can respond to another interrupt)
            // CYCLE LENGTH: L
            case ROMC_13:
                throw new NotImplementedException("ROMC 0x13 not implemented");

            // All devices move the contents of the data bus into the high order byte of PC0
            // CYCLE LENGTH: L
            case ROMC_14:
                Regs[PC0h] = Regs[DB];
                break;

            // All devices move the contents of the data bus into the high order byte of PC1
            // CYCLE LENGTH: L
            case ROMC_15:
                Regs[PC1h] = Regs[DB];
                break;

            // All devices move the contents of the data bus into the high order byte of DC0
            // CYCLE LENGTH: L
            case ROMC_16:
                Regs[DC0h] = Regs[DB];
                break;

            // All devices move the contents of the data bus into the low order byte of PC0
            // CYCLE LENGTH: L
            case ROMC_17:
                Regs[PC0l] = Regs[DB];
                break;

            // All devices move the contents of the data bus into the low order byte of PC1
            // CYCLE LENGTH: L
            case ROMC_18:
                Regs[PC1l] = Regs[DB];
                break;

            // All devices move the contents of the data bus into the low order byte of DC0
            // CYCLE LENGTH: L
            case ROMC_19:
                Regs[DC0l] = Regs[DB];
                break;

            // During the prior cycle, an I/O port timer or interrupt control register was addressed; the device containing the addressed
            // port must move the current contents of the data bus into the addressed port
            // CYCLE LENGTH: L
            case ROMC_1A:
                OUT_Func(IO, DB);
                break;

            // During the prior cycle, the data bus specified the address of an I/O port. The device containing the addressed I/O port
            // must place the contents of the I/O port on the data bus. (Note that the contents of the timer and interrupt control
            // registers cannot be read back onto the data bus)
            // CYCLE LENGTH: L
            case ROMC_1B:
                IN_Func(DB, IO);
                break;

            // None
            // CYCLE LENGTH: S
            case ROMC_1C_S:
                break;

            // None
            // CYCLE LENGTH: L
            case ROMC_1C_L:
                break;

            // Devices with DC0 and DC1 registers must switch registers. Devices without a DC1 register perform no operation
            // CYCLE LENGTH: S
            case ROMC_1D:
                ushort temp = RegDC0;
                RegDC0 = RegDC1;
                RegDC1 = temp;
                break;

            // The device whose address space includes the contents of PC0 must place the low order byte of PC0 onto the data bus
            // CYCLE LENGTH: L
            case ROMC_1E:
                Regs[DB] = (byte)Regs[PC0l];
                break;

            // The device whose address space includes the contents of PC0 must place the high order byte of PC0 onto the data bus
            // CYCLE LENGTH: L
            case ROMC_1F:
                Regs[DB] = (byte)Regs[PC0h];
                break;
            }

            TotalExecutedCycles++;
        }
Exemplo n.º 8
0
        // 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
                OnExecFetch?.Invoke(PC);
                TraceCallback?.Invoke(State());
                CDLCallback?.Invoke(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;
                next_T1_check = TotalExecutedCycles + 20;
                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++];

                bool test_pass = true;
                if ((reg_d_ad == 0) && !TF)
                {
                    test_pass = false;
                }
                if ((reg_d_ad == 1) && T0)
                {
                    test_pass = false;
                }
                if ((reg_d_ad == 2) && !T0)
                {
                    test_pass = false;
                }
                if ((reg_d_ad == 3) && T1)
                {
                    test_pass = false;
                }
                if ((reg_d_ad == 4) && !T1)
                {
                    test_pass = false;
                }
                if ((reg_d_ad == 5) && !F1)
                {
                    test_pass = false;
                }
                if ((reg_d_ad == 6) && IRQPending)
                {
                    test_pass = false;
                }
                if ((reg_d_ad == 7) && (Regs[A] == 0))
                {
                    test_pass = false;
                }
                if ((reg_d_ad == 8) && !FlagF0)
                {
                    test_pass = false;
                }
                if ((reg_d_ad == 9) && (Regs[A] != 0))
                {
                    test_pass = false;
                }
                if ((reg_d_ad == 10) && FlagC)
                {
                    test_pass = false;
                }
                if ((reg_d_ad == 11) && !FlagC)
                {
                    test_pass = false;
                }

                if (test_pass)
                {
                    cur_instr[instr_pntr + 6] = ALU2;
                }
                else
                {
                    cur_instr[instr_pntr + 9] = IDLE;
                }
                break;
            }

            if (++irq_pntr == IRQS)
            {
                // then regular IRQ
                if (IRQPending && IntEn && INT_MSTR)
                {
                    IRQPending = false;

                    TraceCallback?.Invoke(new TraceInfo {
                        Disassembly = "====IRQ====", RegisterInfo = ""
                    });

                    IRQ_(0);
                    IRQCallback();
                    instr_pntr = irq_pntr = 0;
                }
                else if (TIRQPending && TimIntEn && INT_MSTR)
                {
                    TIRQPending = false;

                    TraceCallback?.Invoke(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)
            {
                // NOTE: Odyssey 2 games tend to enable the counter within a few cycles of a falling edge and expect to count to take place
                if (!T1 && T1_old && (TotalExecutedCycles > next_T1_check))
                {
                    if (Regs[TIM] == 255)
                    {
                        TF = true;
                        if (TimIntEn)
                        {
                            TIRQPending = true;
                            //Console.WriteLine(TotalExecutedCycles);
                        }
                    }
                    Regs[TIM] = (ushort)((Regs[TIM] + 1) & 0xFF);
                }
            }

            T1_old = T1;
        }
Exemplo n.º 9
0
        /// <summary>
        /// Runs a single CPU clock cycle
        /// </summary>
        public void ExecuteOne()
        {
            switch (cur_instr[instr_pntr++])
            {
            // always the last tick within an opcode instruction cycle
            case END:
                OnExecFetch?.Invoke(RegPC0);
                TraceCallback?.Invoke(State());
                opcode     = (byte)Regs[DB];
                instr_pntr = 0;
                FetchInstruction();
                break;

            // used as timing 'padding'
            case IDLE:
                break;

            // load one register into another (or databus)
            case OP_LR8:
                LR8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            // Shift register n bit positions to the right (zero fill)
            case OP_SHFT_R:
                SR_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            // Shift register n bit positions to the left (zero fill)
            case OP_SHFT_L:
                SL_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            // x <- (x) ADD y
            case OP_ADD8:
                ADD8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            // x <- (x) ADD y (decimal)
            case OP_ADD8D:
                ADD8D_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            // A <- (A) + (C)
            case OP_LNK:
                ADD8_Func(Regs[A], (ushort)(FlagC ? 1 : 0));
                break;

            // Clear ICB status bit
            case OP_DI:
                FlagICB = false;
                break;

            // Set ICB status bit
            case OP_EI:
                FlagICB = true;
                break;

            // x <- (y) XOR DB
            case OP_XOR8:
                XOR8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            // x <- (y) XOR DB (complement accumulator)
            case OP_XOR8C:
                XOR8C_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            // x <- (x) + 1
            case OP_INC8:
                INC8_Func(cur_instr[instr_pntr++]);
                break;

            // x <- (y) & DB
            case OP_AND8:
                AND8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            // x <- (y) | DB
            case OP_OR8:
                OR8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            // DB + (x) + 1 (modify flags without saving result)
            case OP_CI:
                var tmpX       = cur_instr[instr_pntr++];
                var tmpOperand = Regs[DB];
                INC8_Func(tmpX);
                ADD8_Func(tmpOperand, tmpX);
                break;

            // load one register into another (or databus)
            // ALU also runs flag status checking
            case OP_LR8_IO:
                LR8_IO_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            // DS op performed indirectly on the ScratchPad register pointed to by the ISAR
            case OP_DS_IS:
                SUB8_Func(Regs[ISAR], ONE);
                break;

            // ISAR is incremented
            case OP_IS_INC:
                Regs[ISAR] = (ushort)((Regs[ISAR] & 0x38) | ((Regs[ISAR] + 1) & 0x07));
                break;

            // ISAR is decremented
            case OP_IS_DEC:
                Regs[ISAR] = (ushort)((Regs[ISAR] & 0x38) | ((Regs[ISAR] - 1) & 0x07));
                break;

            // x <- (SR) (as pointed to by ISAR)
            case OP_LR_A_IS:
                LR8_Func(cur_instr[instr_pntr++], Regs[ISAR]);
                break;

            // x <- (SR) (as pointed to by ISAR)
            case OP_LR_IS_A:
                LR8_Func(Regs[ISAR], cur_instr[instr_pntr++]);
                break;

            // set the upper octal ISAR bits (b3,b4,b5)
            case OP_LISU:
                var isVala = (Regs[ISAR] & 0x07) | cur_instr[instr_pntr++];
                Regs[ISAR] = (ushort)(isVala & 0x3F);
                break;

            // set the lower octal ISAR bits (b0,b1,b2)
            case OP_LISL:
                var isValb = (Regs[ISAR] & 0x38) | cur_instr[instr_pntr++];
                Regs[ISAR] = (ushort)(isValb & 0x3F);
                break;

            // test operand against status register
            case OP_BT:
                instr_pntr = 0;
                if ((Regs[W] & cur_instr[instr_pntr++]) != 0)
                {
                    PopulateCURINSTR(
                        ROMC_01,                                  // L
                        IDLE,
                        IDLE,
                        IDLE,
                        IDLE,
                        IDLE,
                        ROMC_00_S,                                      // S
                        IDLE,
                        IDLE,
                        END);
                }
                else
                {
                    PopulateCURINSTR(
                        ROMC_03_S,                                  // S
                        IDLE,
                        IDLE,
                        IDLE,
                        ROMC_00_S,                                  // S
                        IDLE,
                        IDLE,
                        END);
                }
                break;

            // DC0 - A - set status only
            case OP_CM:
                var tmpDB = Regs[DB];
                var tmpA  = Regs[A];
                SUB8_Func(tmpDB, tmpA);
                break;

            // Branch based on ISARL
            case OP_BR7:
                instr_pntr = 0;
                if ((Regs[ISAR] & 7) == 7)
                {
                    PopulateCURINSTR(
                        ROMC_03_S,                                  // S
                        //IDLE, <- lose a cycle that was stolen in the table
                        IDLE,
                        IDLE,
                        ROMC_00_S,                                  // S
                        IDLE,
                        IDLE,
                        END);
                }
                else
                {
                    PopulateCURINSTR(
                        ROMC_01,                                  // L
                        //IDLE, <- lose a cycle that was stolen in the table
                        IDLE,
                        IDLE,
                        IDLE,
                        IDLE,
                        ROMC_00_S,                                  // S
                        IDLE,
                        IDLE,
                        END);
                }
                break;

            //  PC0 <- PC0+n+1
            case OP_BF:
                instr_pntr = 0;
                if ((Regs[W] & cur_instr[instr_pntr++]) != 0)
                {
                    PopulateCURINSTR(
                        ROMC_03_S,                                  // S
                        IDLE,
                        IDLE,
                        IDLE,
                        ROMC_00_S,                                  // S
                        IDLE,
                        IDLE,
                        END);
                }
                else
                {
                    PopulateCURINSTR(
                        ROMC_01,                                  // L
                        IDLE,
                        IDLE,
                        IDLE,
                        IDLE,
                        IDLE,
                        ROMC_00_S,                                  // S
                        IDLE,
                        IDLE,
                        END);
                }
                break;

            // A <- (I/O Port 0 or 1)
            case OP_IN:
                Regs[cur_instr[instr_pntr++]] = ReadHardware(cur_instr[instr_pntr++]);
                //IN_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            // I/O Port 0 or 1 <- (A)
            case OP_OUT:
                WriteHardware(cur_instr[instr_pntr++], (byte)cur_instr[instr_pntr++]);
                //OUT_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            // Add the content of the SR register addressed by ISAR to A (Binary)
            case OP_AS_IS:
                ADD8_Func(A, Regs[ISAR]);
                break;

            // Add the content of the SR register addressed by ISAR to A (Decimal)
            case OP_ASD_IS:
                ADD8D_Func(A, Regs[ISAR]);
                break;

            // XOR the content of the SR register addressed by ISAR to A
            case OP_XS_IS:
                XOR8_Func(A, Regs[ISAR]);
                break;

            // AND the content of the SR register addressed by ISAR to A
            case OP_NS_IS:
                AND8_Func(A, Regs[ISAR]);
                break;

            // Set flags based on accumulator
            case OP_AFTEST:
                break;

            // subtraction
            case OP_SUB8:
                SUB8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;



            // instruction fetch
            // The device whose address space includes the contents of the PC0 register must place on the data bus the op code addressed by PC0;
            // then all devices increments the content of PC0.
            // CYCLE LENGTH: S
            case ROMC_00_S:
                Regs[DB] = ReadMemory(RegPC0++);
                break;

            // instruction fetch
            // The device whose address space includes the contents of the PC0 register must place on the data bus the op code addressed by PC0;
            // then all devices increments the content of PC0.
            // CYCLE LENGTH: L
            case ROMC_00_L:
                Regs[DB] = ReadMemory(RegPC0++);
                break;

            // The device whose address space includes the contents of the PC0 register must place on the data bus the contents of the memory location
            // addressed by by PC0; then all devices add the 8-bit value on the data bus, as a signed binary number, to PC0
            // CYCLE LENGTH: L
            case ROMC_01:
                Regs[DB] = ReadMemory(RegPC0);
                ADDS_Func(PC0l, PC0h, DB, ZERO);
                break;

            // The device whose DC0 address addresses a memory word within the address space of that device must place on the data bus the contents
            // of the memory location addressed by DC0; then all devices increment DC0
            // CYCLE LENGTH: L
            case ROMC_02:
                Regs[DB] = ReadMemory(RegDC0++);
                break;

            // Similar to 0x00, except that it is used for Immediate Operand fetches (using PC0) instead of instruction fetches
            // CYCLE LENGTH: S
            case ROMC_03_S:
                Regs[DB] = ReadMemory(RegPC0++);
                Regs[IO] = Regs[DB];
                break;

            // Similar to 0x00, except that it is used for Immediate Operand fetches (using PC0) instead of instruction fetches
            // CYCLE LENGTH: L
            case ROMC_03_L:
                Regs[DB] = ReadMemory(RegPC0++);
                Regs[IO] = Regs[DB];
                break;

            // Copy the contents of PC1 into PC0
            // CYCLE LENGTH: S
            case ROMC_04:
                RegPC0 = RegPC1;
                break;

            // Store the data bus contents into the memory location pointed to by DC0; increment DC0
            // CYCLE LENGTH: L
            case ROMC_05:
                WriteMemory(RegDC0++, (byte)Regs[DB]);
                break;

            // Place the high order byte of DC0 on the data bus
            // CYCLE LENGTH: L
            case ROMC_06:
                Regs[DB] = (byte)Regs[DC0h];
                break;

            // Place the high order byte of PC1 on the data bus
            // CYCLE LENGTH: L
            case ROMC_07:
                Regs[DB] = (byte)Regs[PC1h];
                break;

            // All devices copy the contents of PC0 into PC1. The CPU outputs zero on the data bus in this ROMC state.
            // Load the data bus into both halves of PC0, this clearing the register.
            // CYCLE LENGTH: L
            case ROMC_08:
                RegPC1     = RegPC0;
                Regs[DB]   = 0;
                Regs[PC0h] = 0;
                Regs[PC0l] = 0;
                break;

            // The device whose address space includes the contents of the DC0 register must place the low order byte of DC0 onto the data bus
            // CYCLE LENGTH: L
            case ROMC_09:
                Regs[DB] = (byte)Regs[DC0l];
                break;

            // All devices add the 8-bit value on the data bus, treated as a signed binary number, to the data counter
            // CYCLE LENGTH: L
            case ROMC_0A:
                ADDS_Func(DC0l, DC0h, DB, ZERO);
                break;

            // The device whose address space includes the value in PC1 must place the low order byte of PC1 on the data bus
            // CYCLE LENGTH: L
            case ROMC_0B:
                Regs[DB] = (byte)Regs[PC1l];
                break;

            // The device whose address space includes the contents of the PC0 register must place the contents of the memory word addressed by PC0
            // onto the data bus; then all devices move the value that has just been placed on the data bus into the low order byte of PC0
            // CYCLE LENGTH: L
            case ROMC_0C:
                Regs[DB]   = ReadMemory(RegPC0);
                Regs[PC0l] = Regs[DB];
                break;

            // All devices store in PC1 the current contents of PC0, incremented by 1; PC0 is unaltered
            // CYCLE LENGTH: S
            case ROMC_0D:
                RegPC1 = (ushort)(RegPC0 + 1);
                break;

            // The device whose address space includes the contents of PC0 must place the contents of the word addressed by PC0 onto the data bus.
            // The value on the data bus is then moved to the low order byte of DC0 by all devices
            // CYCLE LENGTH: L
            case ROMC_0E:
                Regs[DB]   = ReadMemory(RegPC0);
                Regs[DC0l] = Regs[DB];
                break;

            // The interrupting device with the highest priority must place the low order byte of the interrupt vector on the data bus.
            // All devices must copy the contents of PC0 into PC1. All devices must move the contents of the data bus into the low order byte of PC0
            // CYCLE LENGTH: L
            case ROMC_0F:
                throw new NotImplementedException("ROMC 0x0F not implemented");

            // Inhibit any modification to the interrupt priority logic
            // CYCLE LENGTH: L
            case ROMC_10:
                throw new NotImplementedException("ROMC 0x10 not implemented");

            // The device whose memory space includes the contents of PC0 must place the contents of the addressed memory word on the data bus.
            // All devices must then move the contents of the data bus to the upper byte of DC0
            // CYCLE LENGTH: L
            case ROMC_11:
                Regs[DB]   = ReadMemory(RegPC0);
                Regs[DC0h] = Regs[DB];
                break;

            // All devices copy the contents of PC0 into PC1. All devices then move the contents of the data bus into the low order byte of PC0
            // CYCLE LENGTH: L
            case ROMC_12:
                RegPC1     = RegPC0;
                Regs[PC0l] = Regs[DB];
                break;

            // The interrupting device with the highest priority must move the high order half of the interrupt vector onto the data bus.
            // All devices must move the conetnts of the data bus into the high order byte of of PC0. The interrupting device resets its
            // interrupt circuitry (so that it is no longer requesting CPU servicing and can respond to another interrupt)
            // CYCLE LENGTH: L
            case ROMC_13:
                throw new NotImplementedException("ROMC 0x13 not implemented");

            // All devices move the contents of the data bus into the high order byte of PC0
            // CYCLE LENGTH: L
            case ROMC_14:
                Regs[PC0h] = Regs[DB];
                break;

            // All devices move the contents of the data bus into the high order byte of PC1
            // CYCLE LENGTH: L
            case ROMC_15:
                Regs[PC1h] = Regs[DB];
                break;

            // All devices move the contents of the data bus into the high order byte of DC0
            // CYCLE LENGTH: L
            case ROMC_16:
                Regs[DC0h] = Regs[DB];
                break;

            // All devices move the contents of the data bus into the low order byte of PC0
            // CYCLE LENGTH: L
            case ROMC_17:
                Regs[PC0l] = Regs[DB];
                break;

            // All devices move the contents of the data bus into the low order byte of PC1
            // CYCLE LENGTH: L
            case ROMC_18:
                Regs[PC1l] = Regs[DB];
                break;

            // All devices move the contents of the data bus into the low order byte of DC0
            // CYCLE LENGTH: L
            case ROMC_19:
                Regs[DC0l] = Regs[DB];
                break;

            // During the prior cycle, an I/O port timer or interrupt control register was addressed; the device containing the addressed
            // port must move the current contents of the data bus into the addressed port
            // CYCLE LENGTH: L
            case ROMC_1A:
                WriteHardware(Regs[IO], (byte)Regs[DB]);
                break;

            // During the prior cycle, the data bus specified the address of an I/O port. The device containing the addressed I/O port
            // must place the contents of the I/O port on the data bus. (Note that the contents of the timer and interrupt control
            // registers cannot be read back onto the data bus)
            // CYCLE LENGTH: L
            case ROMC_1B:
                //Regs[DB] = ReadHardware(Regs[IO]);
                Regs[DB] = ReadHardware(Regs[IO]);
                break;

            // None
            // CYCLE LENGTH: S
            case ROMC_1C_S:
                break;

            // None
            // CYCLE LENGTH: L
            case ROMC_1C_L:
                break;

            // Devices with DC0 and DC1 registers must switch registers. Devices without a DC1 register perform no operation
            // CYCLE LENGTH: S
            case ROMC_1D:
                // we have no DC1 in this implementation
                break;

            // The device whose address space includes the contents of PC0 must place the low order byte of PC0 onto the data bus
            // CYCLE LENGTH: L
            case ROMC_1E:
                Regs[DB] = (byte)Regs[PC0l];
                break;

            // The device whose address space includes the contents of PC0 must place the high order byte of PC0 onto the data bus
            // CYCLE LENGTH: L
            case ROMC_1F:
                Regs[DB] = (byte)Regs[PC0h];
                break;
            }

            TotalExecutedCycles++;
        }
Exemplo n.º 10
0
        // 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
                OnExecFetch?.Invoke(PC);
                TraceCallback?.Invoke(State());
                CDLCallback?.Invoke(PC, eCDLogMemFlags.FetchFirst);
                FetchInstruction(ReadMemory(Regs[PC]++));
                instr_pntr = 0;
                irq_pntr   = -1;
                break;

            case OP_PG_2:
                FetchInstruction2(ReadMemory(Regs[PC]++));
                instr_pntr = 0;
                irq_pntr   = -1;
                break;

            case OP_PG_3:
                FetchInstruction3(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 PUL_n:
                    PUL_n_BLD(cur_instr[instr_pntr++]);
                    break;

                case SET_ADDR_PUL:
                    Regs[cur_instr[instr_pntr++]] = (ushort)((Regs[ALU2] << 8) + Regs[ADDR]);
                    PUL_n_BLD(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 LEA:
                    LEA_Func(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++]);
                switch (cur_instr[instr_pntr++])
                {
                case PSH_n:
                    PSH_n_BLD(cur_instr[instr_pntr++]);
                    break;
                }
                break;

            case WR_DEC_HI_OP:
                Write_Dec_HI_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                switch (cur_instr[instr_pntr++])
                {
                case PSH_n:
                    PSH_n_BLD(cur_instr[instr_pntr++]);
                    break;
                }
                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 WR_LO_INC:
                Write_Lo_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 ST_8:
                ST_8_Func(cur_instr[instr_pntr++]);
                break;

            case ST_16:
                ST_16_Func(cur_instr[instr_pntr++]);
                break;

            case LEA:
                LEA_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            case EXG:
                EXG_Func(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 TFR:
                TFR_Func(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++];

                // 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 SEX:
                SEX_Func(cur_instr[instr_pntr++]);
                break;

            case ABX:
                Regs[X] += Regs[B];
                break;

            case MUL:
                Mul_Func();
                break;

            case SET_F_I:
                FlagI = true; FlagF = 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 PSH_n:
                PSH_n_BLD(cur_instr[instr_pntr++]);
                break;

            case PUL_n:
                PUL_n_BLD(cur_instr[instr_pntr++]);
                break;

            case ADD16BR:
                ADD16BR_Func(cur_instr[instr_pntr++], cur_instr[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 SUB16:
                SUB16_Func(cur_instr[instr_pntr++]);
                break;

            case ADD16:
                ADD16_Func(cur_instr[instr_pntr++]);
                break;

            case CMP16:
                CMP16_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            case CMP16D:
                CMP16D_Func(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 BIT:
                BIT_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            case CWAI:
                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;

                    TraceCallback?.Invoke(new(disassembly: "====CWAI NMI====", registerInfo: string.Empty));
                }
                else if (FIRQPending && !FlagF)
                {
                    FIRQPending = false;

                    Regs[ADDR] = 0xFFF6;
                    PopulateCURINSTR(RD_INC, ALU, ADDR,
                                     RD_INC, ALU2, ADDR,
                                     SET_ADDR, PC, ALU, ALU2);
                    irq_pntr = -1;
                    IRQS     = 3;

                    TraceCallback?.Invoke(new(disassembly: "====CWAI FIRQ====", registerInfo: string.Empty));
                }
                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;

                    TraceCallback?.Invoke(new(disassembly: "====CWAI IRQ====", registerInfo: string.Empty));
                }
                else
                {
                    PopulateCURINSTR(CWAI);
                    irq_pntr = instr_pntr = 0;
                    IRQS     = -1;
                }
                instr_pntr = 0;
                break;

            case SYNC:
                if (NMIPending)
                {
                    NMIPending = false;
                    IN_SYNC    = false;

                    Regs[ADDR] = 0xFFFC;
                    PopulateCURINSTR(RD_INC, ALU, ADDR,
                                     RD_INC, ALU2, ADDR,
                                     SET_ADDR, PC, ALU, ALU2);
                    irq_pntr = -1;
                    IRQS     = 3;

                    TraceCallback?.Invoke(new(disassembly: "====SYNC NMI====", registerInfo: string.Empty));
                }
                else if (FIRQPending)
                {
                    if (!FlagF)
                    {
                        FIRQPending = false;

                        Regs[ADDR] = 0xFFF6;
                        PopulateCURINSTR(RD_INC, ALU, ADDR,
                                         RD_INC, ALU2, ADDR,
                                         SET_ADDR, PC, ALU, ALU2);
                        irq_pntr = -1;
                        IRQS     = 3;

                        TraceCallback?.Invoke(new(disassembly: "====SYNC FIRQ====", registerInfo: string.Empty));
                    }
                    else
                    {
                        FIRQPending = false;
                        IN_SYNC     = false;
                        IRQS        = 2;
                        instr_pntr  = irq_pntr = 0;
                        PopulateCURINSTR(IDLE,
                                         IDLE);
                    }
                }
                else if (IRQPending)
                {
                    if (!FlagI)
                    {
                        IRQPending = false;
                        IN_SYNC    = false;

                        Regs[ADDR] = 0xFFF8;
                        PopulateCURINSTR(RD_INC, ALU, ADDR,
                                         RD_INC, ALU2, ADDR,
                                         SET_ADDR, PC, ALU, ALU2);
                        irq_pntr = -1;
                        IRQS     = 3;

                        TraceCallback?.Invoke(new(disassembly: "====SYNC IRQ====", registerInfo: string.Empty));
                    }
                    else
                    {
                        FIRQPending = false;
                        IN_SYNC     = false;
                        IRQS        = 2;
                        instr_pntr  = irq_pntr = 0;
                        PopulateCURINSTR(IDLE,
                                         IDLE);
                    }
                }
                else
                {
                    IN_SYNC    = true;
                    IRQS       = -1;
                    instr_pntr = irq_pntr = 0;
                    PopulateCURINSTR(SYNC);
                }

                break;
            }

            if (++irq_pntr == IRQS)
            {
                // NMI has priority
                if (NMIPending)
                {
                    NMIPending = false;

                    TraceCallback?.Invoke(new(disassembly: "====NMI====", registerInfo: string.Empty));

                    NMI_();
                    NMICallback();
                    instr_pntr = irq_pntr = 0;
                }
                // fast IRQ has next priority
                else if (FIRQPending && !FlagF)
                {
                    FIRQPending = false;

                    TraceCallback?.Invoke(new(disassembly: "====FIRQ====", registerInfo: string.Empty));

                    FIRQ_();
                    FIRQCallback();
                    instr_pntr = irq_pntr = 0;
                }
                // then regular IRQ
                else if (IRQPending && !FlagI)
                {
                    IRQPending = false;

                    TraceCallback?.Invoke(new(disassembly: "====IRQ====", registerInfo: string.Empty));
                    IRQ_();
                    IRQCallback();
                    instr_pntr = irq_pntr = 0;
                }
                // otherwise start the next instruction
                else
                {
                    PopulateCURINSTR(OP);
                    instr_pntr = irq_pntr = 0;
                    IRQS       = -1;
                }
            }

            TotalExecutedCycles++;
        }
Exemplo n.º 11
0
        // Execute instructions
        public void ExecuteOne()
        {
            bus_pntr++; mem_pntr++;
            switch (cur_instr[instr_pntr++])
            {
            case IDLE:
                // do nothing
                break;

            case OP:
                // should never reach here

                break;

            case OP_F:
                // Read the opcode of the next instruction
                OnExecFetch?.Invoke(RegPC);
                TraceCallback?.Invoke(State());
                opcode = FetchMemory(RegPC++);
                FetchInstruction();

                temp_R = (byte)(Regs[R] & 0x7F);
                temp_R++;
                temp_R &= 0x7F;
                Regs[R] = (byte)((Regs[R] & 0x80) | temp_R);

                instr_pntr = bus_pntr = mem_pntr = irq_pntr = 0;
                I_skip     = true;
                break;

            case HALT:
                halted = true;
                // NOTE: Check how halt state effects the DB
                Regs[DB] = 0xFF;

                temp_R = (byte)(Regs[R] & 0x7F);
                temp_R++;
                temp_R &= 0x7F;
                Regs[R] = (byte)((Regs[R] & 0x80) | temp_R);
                break;

            case RD:
                Read_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            case WR:
                Write_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            case RD_INC:
                Read_INC_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            case RD_INC_TR_PC:
                Read_INC_TR_PC_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            case RD_OP:
                if (cur_instr[instr_pntr++] == 1)
                {
                    Read_INC_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                }
                else
                {
                    Read_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                }

                switch (cur_instr[instr_pntr++])
                {
                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 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 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 CP8:
                    CP8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                    break;

                case TR:
                    TR_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                    break;
                }
                break;

            case WR_INC:
                Write_INC_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            case WR_DEC:
                Write_DEC_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            case WR_TR_PC:
                Write_TR_PC_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            case WR_INC_WA:
                Write_INC_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                Regs[W] = Regs[A];
                break;

            case TR:
                TR_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            case TR16:
                TR16_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            case ADD16:
                ADD16_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], 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 ADC16:
                ADC_16_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            case SBC8:
                SBC8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            case SBC16:
                SBC_16_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            case INC16:
                INC16_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            case INC8:
                INC8_Func(cur_instr[instr_pntr++]);
                break;

            case DEC16:
                DEC16_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            case DEC8:
                DEC8_Func(cur_instr[instr_pntr++]);
                break;

            case RLC:
                RLC_Func(cur_instr[instr_pntr++]);
                break;

            case RL:
                RL_Func(cur_instr[instr_pntr++]);
                break;

            case RRC:
                RRC_Func(cur_instr[instr_pntr++]);
                break;

            case RR:
                RR_Func(cur_instr[instr_pntr++]);
                break;

            case CPL:
                CPL_Func(cur_instr[instr_pntr++]);
                break;

            case DA:
                DA_Func(cur_instr[instr_pntr++]);
                break;

            case SCF:
                SCF_Func(cur_instr[instr_pntr++]);
                break;

            case CCF:
                CCF_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 CP8:
                CP8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            case SLA:
                SLA_Func(cur_instr[instr_pntr++]);
                break;

            case SRA:
                SRA_Func(cur_instr[instr_pntr++]);
                break;

            case SRL:
                SRL_Func(cur_instr[instr_pntr++]);
                break;

            case SLL:
                SLL_Func(cur_instr[instr_pntr++]);
                break;

            case BIT:
                BIT_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            case I_BIT:
                I_BIT_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            case RES:
                RES_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            case SET:
                SET_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            case EI:
                EI_pending = 2;
                break;

            case DI:
                IFF1 = IFF2 = false;
                break;

            case EXCH:
                EXCH_16_Func(F_s, A_s, F, A);
                break;

            case EXX:
                EXCH_16_Func(C_s, B_s, C, B);
                EXCH_16_Func(E_s, D_s, E, D);
                EXCH_16_Func(L_s, H_s, L, H);
                break;

            case EXCH_16:
                EXCH_16_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            case PREFIX:
                ushort src_t = PRE_SRC;

                NO_prefix = false;
                if (PRE_SRC == CBpre)
                {
                    CB_prefix = true;
                }
                if (PRE_SRC == EXTDpre)
                {
                    EXTD_prefix = true;
                }
                if (PRE_SRC == IXpre)
                {
                    IX_prefix = true;
                }
                if (PRE_SRC == IYpre)
                {
                    IY_prefix = true;
                }
                if (PRE_SRC == IXCBpre)
                {
                    IXCB_prefix = true;
                }
                if (PRE_SRC == IYCBpre)
                {
                    IYCB_prefix = true;
                }

                // only the first prefix in a double prefix increases R, although I don't know how / why
                if (PRE_SRC < 4)
                {
                    temp_R = (byte)(Regs[R] & 0x7F);
                    temp_R++;
                    temp_R &= 0x7F;
                    Regs[R] = (byte)((Regs[R] & 0x80) | temp_R);
                }

                opcode = FetchMemory(RegPC++);
                FetchInstruction();
                instr_pntr = bus_pntr = mem_pntr = irq_pntr = 0;
                I_skip     = true;

                // for prefetched case, the PC stays on the BUS one cycle longer
                if ((src_t == IXCBpre) || (src_t == IYCBpre))
                {
                    BUSRQ[0] = PCh;
                }

                break;

            case ASGN:
                ASGN_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            case ADDS:
                ADDS_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            case EI_RETI:
                // NOTE: This is needed for systems using multiple interrupt sources, it triggers the next interrupt
                // Not currently implemented here
                iff1 = iff2;
                break;

            case EI_RETN:
                iff1 = iff2;
                break;

            case OUT:
                OUT_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            case OUT_INC:
                OUT_INC_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            case IN:
                IN_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            case IN_INC:
                IN_INC_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            case IN_A_N_INC:
                IN_A_N_INC_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            case NEG:
                NEG_8_Func(cur_instr[instr_pntr++]);
                break;

            case INT_MODE:
                interruptMode = cur_instr[instr_pntr++];
                break;

            case RRD:
                RRD_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            case RLD:
                RLD_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
                break;

            case SET_FL_LD_R:
                DEC16_Func(C, B);
                SET_FL_LD_Func();

                Ztemp1 = cur_instr[instr_pntr++];
                Ztemp2 = cur_instr[instr_pntr++];
                Ztemp3 = cur_instr[instr_pntr++];

                if (((Regs[C] | (Regs[B] << 8)) != 0) && (Ztemp3 > 0))
                {
                    PopulateCURINSTR
                        (DEC16, PCl, PCh,
                        DEC16, PCl, PCh,
                        TR16, Z, W, PCl, PCh,
                        INC16, Z, W,
                        Ztemp2, E, D);

                    PopulateBUSRQ(D, D, D, D, D);
                    PopulateMEMRQ(0, 0, 0, 0, 0);
                    IRQS = 5;

                    instr_pntr = mem_pntr = bus_pntr = irq_pntr = 0;
                    I_skip     = true;
                }
                else
                {
                    if (Ztemp2 == INC16)
                    {
                        INC16_Func(E, D);
                    }
                    else
                    {
                        DEC16_Func(E, D);
                    }
                }
                break;

            case SET_FL_CP_R:
                SET_FL_CP_Func();

                Ztemp1 = cur_instr[instr_pntr++];
                Ztemp2 = cur_instr[instr_pntr++];
                Ztemp3 = cur_instr[instr_pntr++];

                if (((Regs[C] | (Regs[B] << 8)) != 0) && (Ztemp3 > 0) && !FlagZ)
                {
                    PopulateCURINSTR
                        (DEC16, PCl, PCh,
                        DEC16, PCl, PCh,
                        TR16, Z, W, PCl, PCh,
                        INC16, Z, W,
                        Ztemp2, L, H);

                    PopulateBUSRQ(H, H, H, H, H);
                    PopulateMEMRQ(0, 0, 0, 0, 0);
                    IRQS = 5;

                    instr_pntr = mem_pntr = bus_pntr = irq_pntr = 0;
                    I_skip     = true;
                }
                else
                {
                    if (Ztemp2 == INC16)
                    {
                        INC16_Func(L, H);
                    }
                    else
                    {
                        DEC16_Func(L, H);
                    }
                }
                break;

            case SET_FL_IR:
                ushort dest_t = cur_instr[instr_pntr++];
                TR_Func(dest_t, cur_instr[instr_pntr++]);
                SET_FL_IR_Func(dest_t);
                break;

            case FTCH_DB:
                FTCH_DB_Func();
                break;

            case WAIT:
                if (FlagW)
                {
                    instr_pntr--; bus_pntr--; mem_pntr--;
                    I_skip = true;
                }
                break;

            case RST:
                Regs[Z] = cur_instr[instr_pntr++];
                Regs[W] = 0;
                break;

            case REP_OP_I:
                Write_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]);

                Ztemp4 = cur_instr[instr_pntr++];
                if (Ztemp4 == DEC16)
                {
                    TR16_Func(Z, W, C, B);
                    DEC16_Func(Z, W);
                    DEC8_Func(B);

                    // take care of other flags
                    // taken from 'undocumented z80 documented' and Fuse
                    FlagN = Regs[ALU].Bit(7);
                    FlagH = FlagC = ((Regs[ALU] + Regs[C] - 1) & 0xFF) < Regs[ALU];
                    FlagP = TableParity[((Regs[ALU] + Regs[C] - 1) & 7) ^ Regs[B]];
                }
                else
                {
                    TR16_Func(Z, W, C, B);
                    INC16_Func(Z, W);
                    DEC8_Func(B);

                    // take care of other flags
                    // taken from 'undocumented z80 documented' and Fuse
                    FlagN = Regs[ALU].Bit(7);
                    FlagH = FlagC = ((Regs[ALU] + Regs[C] + 1) & 0xFF) < Regs[ALU];
                    FlagP = TableParity[((Regs[ALU] + Regs[C] + 1) & 7) ^ Regs[B]];
                }

                Ztemp1 = cur_instr[instr_pntr++];
                Ztemp2 = cur_instr[instr_pntr++];
                Ztemp3 = cur_instr[instr_pntr++];

                if ((Regs[B] != 0) && (Ztemp3 > 0))
                {
                    PopulateCURINSTR
                        (IDLE,
                        IDLE,
                        DEC16, PCl, PCh,
                        DEC16, PCl, PCh,
                        Ztemp2, L, H);

                    PopulateBUSRQ(H, H, H, H, H);
                    PopulateMEMRQ(0, 0, 0, 0, 0);
                    IRQS = 5;

                    instr_pntr = mem_pntr = bus_pntr = irq_pntr = 0;
                    I_skip     = true;
                }
                else
                {
                    if (Ztemp2 == INC16)
                    {
                        INC16_Func(L, H);
                    }
                    else
                    {
                        DEC16_Func(L, H);
                    }
                }
                break;

            case REP_OP_O:
                OUT_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]);

                Ztemp4 = cur_instr[instr_pntr++];
                if (Ztemp4 == DEC16)
                {
                    DEC16_Func(L, H);
                    DEC8_Func(B);
                    TR16_Func(Z, W, C, B);
                    DEC16_Func(Z, W);
                }
                else
                {
                    INC16_Func(L, H);
                    DEC8_Func(B);
                    TR16_Func(Z, W, C, B);
                    INC16_Func(Z, W);
                }

                // take care of other flags
                // taken from 'undocumented z80 documented'
                FlagN = Regs[ALU].Bit(7);
                FlagH = FlagC = (Regs[ALU] + Regs[L]) > 0xFF;
                FlagP = TableParity[((Regs[ALU] + Regs[L]) & 7) ^ (Regs[B])];

                Ztemp1 = cur_instr[instr_pntr++];
                Ztemp2 = cur_instr[instr_pntr++];
                Ztemp3 = cur_instr[instr_pntr++];

                if ((Regs[B] != 0) && (Ztemp3 > 0))
                {
                    PopulateCURINSTR
                        (IDLE,
                        IDLE,
                        DEC16, PCl, PCh,
                        DEC16, PCl, PCh,
                        IDLE);

                    PopulateBUSRQ(B, B, B, B, B);
                    PopulateMEMRQ(0, 0, 0, 0, 0);
                    IRQS = 5;

                    instr_pntr = mem_pntr = bus_pntr = irq_pntr = 0;
                    I_skip     = true;
                }
                break;

            case IORQ:
                IRQACKCallback();
                break;
            }

            if (I_skip)
            {
                I_skip = false;
            }
            else if (++irq_pntr == IRQS)
            {
                if (EI_pending > 0)
                {
                    EI_pending--;
                    if (EI_pending == 0)
                    {
                        IFF1 = IFF2 = true;
                    }
                }

                // NMI has priority
                if (nonMaskableInterruptPending)
                {
                    nonMaskableInterruptPending = false;

                    TraceCallback?.Invoke(new(disassembly: "====NMI====", registerInfo: string.Empty));

                    iff2 = iff1;
                    iff1 = false;
                    NMI_();
                    NMICallback();
                    instr_pntr = mem_pntr = bus_pntr = irq_pntr = 0;

                    temp_R = (byte)(Regs[R] & 0x7F);
                    temp_R++;
                    temp_R &= 0x7F;
                    Regs[R] = (byte)((Regs[R] & 0x80) | temp_R);

                    halted = false;
                }
                // if we are processing an interrrupt, we need to modify the instruction vector
                else if (iff1 && FlagI)
                {
                    iff1       = iff2 = false;
                    EI_pending = 0;

                    TraceCallback?.Invoke(new(disassembly: "====IRQ====", registerInfo: string.Empty));

                    switch (interruptMode)
                    {
                    case 0:
                        // Requires something to be pushed onto the data bus
                        // we'll assume it's a zero for now
                        INTERRUPT_0(0);
                        break;

                    case 1:
                        INTERRUPT_1();
                        break;

                    case 2:
                        INTERRUPT_2();
                        break;
                    }
                    IRQCallback();
                    instr_pntr = mem_pntr = bus_pntr = irq_pntr = 0;

                    temp_R = (byte)(Regs[R] & 0x7F);
                    temp_R++;
                    temp_R &= 0x7F;
                    Regs[R] = (byte)((Regs[R] & 0x80) | temp_R);

                    halted = false;
                }
                // otherwise start a new normal access
                else if (!halted)
                {
                    PopulateCURINSTR
                        (IDLE,
                        WAIT,
                        OP_F,
                        OP);

                    PopulateBUSRQ(PCh, 0, 0, 0);
                    PopulateMEMRQ(PCh, 0, 0, 0);
                    IRQS = 4;

                    instr_pntr = mem_pntr = bus_pntr = irq_pntr = 0;
                }
                else
                {
                    instr_pntr = mem_pntr = bus_pntr = irq_pntr = 0;
                }
            }

            TotalExecutedCycles++;
        }