コード例 #1
0
        private ExitConditionException NotifyLifecycleEvent(LifecycleEventType eventType)
        {
            ExitConditionException pendingInterruption = null;

            try
            {
                switch (eventType)
                {
                case LifecycleEventType.HalfTState:
                    if (HalfTState != null)
                    {
                        HalfTState(new InternalState(this), eventType);
                    }
                    break;

                case LifecycleEventType.InstructionEnd:
                    if (InstructionEnd != null)
                    {
                        InstructionEnd(new InternalState(this), eventType);
                    }
                    break;

                case LifecycleEventType.InstructionStart:
                    if (InstructionStart != null)
                    {
                        InstructionStart(new InternalState(this), eventType);
                    }
                    break;

                case LifecycleEventType.MachineCycleEnd:
                    if (MachineCycleEnd != null)
                    {
                        MachineCycleEnd(new InternalState(this), eventType);
                    }
                    break;

                case LifecycleEventType.MachineCycleStart:
                    if (MachineCycleStart != null)
                    {
                        MachineCycleStart(new InternalState(this), eventType);
                    }
                    break;
                }
            }
            catch (ExitConditionException e)
            {
                pendingInterruption = e;
            }
            return(pendingInterruption);
        }
コード例 #2
0
        private ExitConditionException NotifyLifecycleEvent(LifecycleEventType eventType)
        {
            ExitConditionException pendingInterruption = null;

            try
            {
                switch (eventType)
                {
                case LifecycleEventType.ClockCycle:
                    if (ClockCycle != null)
                    {
                        ClockCycle(this, new InternalState(this), eventType);
                    }
                    break;
                }
            }
            catch (ExitConditionException e)
            {
                pendingInterruption = e;
            }
            return(pendingInterruption);
        }
コード例 #3
0
        // Main entry point : logic executed on each edge of the clock signal
        public void CLK_OnEdge()
        {
            // Count T States
            if (halfTStateIndex % 2 == 0)
            {
                tStateCounter++;
            }

            // Debug interface : breakpoints management
            ExitConditionException pendingExitException = null;

            // Start a new instruction if no instruction is currently in progress
            if (currentInstruction == null)
            {
                // If RESET Pin is active, execute a special reset "instruction"
                if (RESET == SignalState.LOW)
                {
                    ResetCPUControlState();
                    StartInstruction(Instruction.RESET);
                }
                // If a non maskable interrupt is pending, execute NMI instruction
                else if (nonMaskableInterruptPending)
                {
                    nonMaskableInterruptPending = false;
                    StartInstruction(Instruction.NMI);
                }
                // If a maskable interrupt is pending, execute INT instruction
                else if (maskableInterruptPending)
                {
                    maskableInterruptPending = false;
                    switch (IM)
                    {
                    case 0:
                        StartInstruction(Instruction.INT0);
                        break;

                    case 1:
                        StartInstruction(Instruction.INT1);
                        break;

                    case 2:
                        StartInstruction(Instruction.INT2);
                        break;
                    }
                }
                // If no external CPU control Pin is active, read the next instruction opcode in memory
                else
                {
                    StartInstruction(Instruction.FetchNewInstruction);
                }

                // Debug notification
                if (pendingExitException == null)
                {
                    pendingExitException = NotifyLifecycleEvent(LifecycleEventType.InstructionStart);
                }
            }

            // Start a new machine cycle if no machine cycle is currently in progress
            if (currentMachineCycle == null)
            {
                // If a Bus Request is pending, start a special Bus Request Acknowledge machine cycle
                if (busRequestPending)
                {
                    busRequestPending = false;
                    StartMachineCycle(MachineCycle.BusRequestAcknowledge);
                }
                // The following machine cycles are defined by the documentation of the current instruction
                else
                {
                    MachineCycle nextMachineCycle = currentInstruction.ExecutionTimings.MachineCycles[machineCycleIndex];
                    StartMachineCycle(nextMachineCycle);
                }

                // Debug notification
                if (pendingExitException == null)
                {
                    pendingExitException = NotifyLifecycleEvent(LifecycleEventType.MachineCycleStart);
                }
            }

            // Execute the current machine cycle logic during one halfTState
            executeMachineCycle(halfTStateIndex);

            // Check if the current machine cycle was a bus request
            bool machineCycleWasBusRequest = currentMachineCycle.Type == MachineCycleType.BRQA;

            // Check if this halfTState is the rising edge of the last clock period of this machine cycle
            bool isRisingEdgeOfLastClockPeriodOfCurrentMachineCycle = (halfTStateIndex == currentMachineCycle.PenultimateHalfTStateIndex);

            // Check if this halfTState is the last one for the current machine cycle
            bool isLastHalfTStateOfCurrentMachineCycle = (halfTStateIndex == currentMachineCycle.LastHalfTStateIndex);

            // Check if the current machine cycle was the last machine cycle of the instruction
            bool isLastMachineCycleOfCurrentInstruction = !machineCycleWasBusRequest && (machineCycleIndex == currentInstruction.LastMachineCycleIndex);

            // Sample BUSREQ, NMI, and INT signals at the rising edge of the last clock period of any machine cycle
            if (isRisingEdgeOfLastClockPeriodOfCurrentMachineCycle)
            {
                SampleBusControlSignals();
                if (isLastMachineCycleOfCurrentInstruction)
                {
                    SampleInterruptControlSignals();
                }
            }

            // Debug notification
            if (pendingExitException == null)
            {
                pendingExitException = NotifyLifecycleEvent(LifecycleEventType.HalfTState);
            }

            // If machine cycle is finished
            if (isLastHalfTStateOfCurrentMachineCycle)
            {
                // Execute one last time the machine cycle logic
                // to release buses and controls signals before the next machine cycle
                executeMachineCycle((byte)(halfTStateIndex + 1));

                // Debug notification
                if (pendingExitException == null)
                {
                    pendingExitException = NotifyLifecycleEvent(LifecycleEventType.MachineCycleEnd);
                }

                EndMachineCycle();
            }

            // If instruction is finished
            if (isLastMachineCycleOfCurrentInstruction && isLastHalfTStateOfCurrentMachineCycle)
            {
                // Debug notification
                if (pendingExitException == null)
                {
                    pendingExitException = NotifyLifecycleEvent(LifecycleEventType.InstructionEnd);
                }

                EndInstruction();
            }

            // Count half T states whithin one machine cycle
            if (isLastHalfTStateOfCurrentMachineCycle)
            {
                halfTStateIndex = 0;
            }
            else
            {
                halfTStateIndex++;
            }

            // Count machine cycles within one instruction
            if (isLastHalfTStateOfCurrentMachineCycle)
            {
                if (isLastMachineCycleOfCurrentInstruction)
                {
                    machineCycleIndex = 0;
                    machineCycleCountAfterInstruction = 0;
                }
                else
                {
                    if (!machineCycleWasBusRequest) // Bus request "steals" machine cycles from the instruction
                    {
                        machineCycleIndex++;
                        if (currentInstruction != Instruction.FetchNewInstruction) // Incremented only after instruction decoding
                        {
                            machineCycleCountAfterInstruction++;
                        }
                    }
                }
            }

            // Debug : if a breakpoint was hit, notify the system clock via a specific exception
            if (pendingExitException != null)
            {
                throw pendingExitException;
            }
        }
コード例 #4
0
ファイル: ULA.cs プロジェクト: m4k3r-org/ZxSpectrumSimulator
        /// <summary>
        /// All ULA timing states are synchronized to this internal 7MHz clock.
        /// </summary>
        public void ExecuteOnPixelClock()
        {
            // Debug interface : breakpoints management
            ExitConditionException pendingExitException = null;

            // Video signals generation is aligned on a 16 pixels pattern
            int videoMem16StepsAccessPatternIndex = column % 16;

            // --- CPU/ULA video memory contention

            // The ULA accesses video memory from pixels 8 to 15
            // outside the border generation periods. To make sure
            // no CPU memory or IO access can overlap this period
            // we must stop any CPU memory or IO instruction starting
            // after pixel 4 and halt the CPU clock until next pixel 0.
            switch (videoMem16StepsAccessPatternIndex)
            {
            case 0:
                videoMemAccessTimeFrame = false;
                break;

            case 4:
                if (!generateBorder)
                {
                    videoMemAccessTimeFrame = true;
                }
                break;
            }

            if (videoMemAccessTimeFrame)
            {
                if (!haltCpuClock) // No need to check for CPU memory or IO requests while it is already halted
                {
                    if (           // CPU is about to access the video memory
                        // CPU is about to access the ULA IO port
                        cpuMemoryOrIORequestHalfTState == 1)
                    {
                        haltCpuClock = true;
                    }
                }
            }
            else
            {
                if (haltCpuClock)
                {
                    haltCpuClock = false;
                }
            }

            // --- CPU interrupt
            if (line == 248)
            {
                if (column == 0 /* Offset : because of the time necessary to read the first pixels in video memory, color output begins only 13 cycles after the master counter */ + 13)
                {
                    CpuINT = SignalState.LOW;
                }
                else if (column == 32 /* Offset : because of the time necessary to read the first pixels in video memory, color output begins only 13 cycles after the master counter */ + 13)
                {
                    CpuINT = SignalState.HIGH;
                }
            }

            // Connect TapeInput and SoundOutput
            if (TapeInputSignal.Level == 1)
            {
                SpeakerSoundSignal.Level = 1;
            }
            else if (!soundOutputWasSetByTheCPU)
            {
                SpeakerSoundSignal.Level = 0;
            }

            if (!haltCpuClock)
            {
                // --- CPU clock
                CpuCLK = PixelCLK; // C0 is directly used to drive CPU clock, but CPU T state starts with High state while ULA counter should start with C0 low

                // Check for CPU memory or IO access that could interfere with ULA operations
                if (cpuMemoryOrIORequestHalfTState == 0)
                {
                    // CPU is about to access the video memory
                    bool cpuMemoryOrIORequestToVideoAddress = (Address.SampleValue() & A15A14) == VideoMem;
                    // CPU is about to access the ULA IO port
                    cpuIORequestToULA = (CpuIORQ == SignalState.LOW) && ((Address.SampleValue() & A0) == (ULAPort & A0));
                    if (cpuIORequestToULA)
                    {
                        // Check to see if it is a read operation
                        cpuIORequestToULAIsREAD = CpuWR == SignalState.HIGH;
                    }
                    if (cpuMemoryOrIORequestToVideoAddress || cpuIORequestToULA)
                    {
                        cpuMemoryOrIORequestHalfTState = 1;
                    }
                }
                else if (cpuMemoryOrIORequestHalfTState > 0)
                {
                    cpuMemoryOrIORequestHalfTState++;
                }

                // --- CPU IO requests to ULA port
                if (cpuIORequestToULA)
                {
                    if (cpuIORequestToULAIsREAD)
                    {
                        if (cpuMemoryOrIORequestHalfTState == 5)
                        {
                            byte inputData = 0;
                            // Read keyboard state
                            KeyboardRD = SignalState.LOW;
                            inputData  = (byte)(KeyboardData.SampleValue() & ULAPort_Read_Keyboard);
                            KeyboardRD = SignalState.HIGH;
                            // Load cassette
                            inputData |= (byte)(TapeInputSignal.Level << ULAPort_Read_EAR);
                            // Bits 5 and 7 as read by INning from Port 0xfe are always one
                            inputData |= ULAPort_Read_Bits5And7;
                            Data.SetValue(inputData);
                        }
                        else if (cpuMemoryOrIORequestHalfTState == 6)
                        {
                            Data.ReleaseValue();
                        }
                    }
                    else
                    {
                        if (cpuMemoryOrIORequestHalfTState == 3)
                        {
                            byte writeData = Data.SampleValue();
                            // Write borderColor register
                            borderColorRegister = (byte)(writeData & ULAPort_Write_Border);
                            // Output sound
                            SpeakerSoundSignal.Level = (byte)((writeData & ULAPort_Write_Speaker) >> 4);
                            if (SpeakerSoundSignal.Level == 1)
                            {
                                soundOutputWasSetByTheCPU = true;
                            }
                            else
                            {
                                soundOutputWasSetByTheCPU = false;
                            }
                            // Save cassette
                            TapeOuputSignal.Level = (byte)(writeData & ULAPort_Write_MIC);
                        }
                    }
                }
                else if (cpuMemoryOrIORequestHalfTState == 3)
                {
                    // CPU is about to access the ULA IO port
                    cpuIORequestToULA = (CpuIORQ == SignalState.LOW) && ((Address.SampleValue() & A0) == (ULAPort & A0));
                    if (cpuIORequestToULA)
                    {
                        // Check to see if it is a read operation
                        cpuIORequestToULAIsREAD = CpuWR == SignalState.HIGH;
                    }
                    if (cpuIORequestToULA)
                    {
                        cpuMemoryOrIORequestHalfTState = 1;
                    }
                }
            }

            if (cpuMemoryOrIORequestHalfTState == 6)
            {
                cpuMemoryOrIORequestHalfTState = 0;
            }

            // --- Compute pixel colors ---
            if (displayPixels) // First thing to do at the falling edge of the clock
            {
                switch (videoMem16StepsAccessPatternIndex)
                {
                case 5:
                    displayRegister = displayLatch;
                    break;

                case 13:
                    displayRegister = displayLatch;
                    break;
                }
            }
            switch (videoMem16StepsAccessPatternIndex)
            {
            case 5:
                MultiplexAttributeLatchWithBorderColor();
                break;

            case 13:
                MultiplexAttributeLatchWithBorderColor();
                break;
            }

            // --- Load two display and attribute bytes for 16 pixels from video memory ---
            if (!generateBorder)
            {
                switch (videoMem16StepsAccessPatternIndex)
                {
                case 7:
                    ComputeVideoAddresses();
                    break;

                case 8:
                    // display address -> address bus
                    VideoAddress.SetValue(displayAddress);
                    VideoMREQ = SignalState.LOW;
                    VideoRD   = SignalState.LOW;
                    break;

                case 9:
                    displayLatch = VideoData.SampleValue();
                    VideoRD      = SignalState.HIGH;
                    VideoMREQ    = SignalState.HIGH;
                    VideoAddress.ReleaseValue();
                    break;

                case 10:
                    // attribute adress -> address bus
                    VideoAddress.SetValue(attributeAddress);
                    VideoMREQ = SignalState.LOW;
                    VideoRD   = SignalState.LOW;
                    break;

                case 11:
                    attributeLatch = VideoData.SampleValue();
                    VideoRD        = SignalState.HIGH;
                    VideoMREQ      = SignalState.HIGH;
                    VideoAddress.ReleaseValue();
                    ComputeVideoAddresses();
                    break;

                case 12:
                    // display colum address -> address bus
                    VideoAddress.SetValue(displayAddress);
                    VideoMREQ = SignalState.LOW;
                    VideoRD   = SignalState.LOW;
                    break;

                case 13:
                    displayLatch = VideoData.SampleValue();
                    VideoRD      = SignalState.HIGH;
                    VideoMREQ    = SignalState.HIGH;
                    VideoAddress.ReleaseValue();
                    break;

                case 14:
                    // attribute adress -> address bus
                    VideoAddress.SetValue(attributeAddress);
                    VideoMREQ = SignalState.LOW;
                    VideoRD   = SignalState.LOW;
                    break;

                case 15:
                    attributeLatch = VideoData.SampleValue();
                    VideoRD        = SignalState.HIGH;
                    VideoMREQ      = SignalState.HIGH;
                    VideoAddress.ReleaseValue();
                    break;
                }
            }

            // --- For each pixel : Video output signals ---

            // Generate horizontal and vertical synchronization signals
            if (column == 320 /* Offset : because of the time necessary to read the first pixels in video memory, color output begins only 13 cycles after the master counter */ + 13)
            {
                HSync = SignalState.LOW;
            }
            else if (column == 416 /* Offset : because of the time necessary to read the first pixels in video memory, color output begins only 13 cycles after the master counter */ + 13)
            {
                HSync = SignalState.HIGH;
                if (line == 247)
                {
                    VSync = SignalState.LOW;
                }
                else if (line == 255)
                {
                    VSync = SignalState.HIGH;
                }
            }

            // Compute pixel color for video output
            if (HSync == SignalState.LOW || VSync == SignalState.LOW)
            {
                // Blanking
                ColorSignal.Level = 0;
            }
            else
            {
                MultiplexDisplayRegisterWithAttributeRegisterAndFlashClock();
            }

            // Debug notification
            if (pendingExitException == null)
            {
                pendingExitException = NotifyLifecycleEvent(LifecycleEventType.ClockCycle);
            }

            // --- Increment master counters and prepare next iteration ---

            // Shift display register bits
            displayRegister = (byte)(displayRegister << 1);

            column++;
            if (column == 8)
            {
                if (!generateBorder)
                {
                    displayPixels = true;
                }
            }
            else if (column == 256)
            {
                generateBorder          = true;
                videoMemAccessTimeFrame = false;
            }
            else if (column == 264)
            {
                if (displayPixels)
                {
                    displayPixels = false;
                }
            }
            else if (column == 448)
            {
                column = 0;
                line++;

                // At each end of line send sound sample signal to the speaker
                // => 7 Mhz pixel clock / 448 pixels per line = 15.6 Khz sound sampling frequency
                SoundSampleCLK = SoundSampleCLK == SignalState.LOW ? SignalState.HIGH : SignalState.LOW;

                if (line < 192)
                {
                    generateBorder = false;
                }

                if (line == 312)
                {
                    line           = 0;
                    generateBorder = false;
                    frame++;

                    // Each 16 frames, invert flash clock
                    if (frame == 16)
                    {
                        frame      = 0;
                        flashClock = !flashClock;
                    }
                }
            }

            // Debug : if a breakpoint was hit, notify the pixel clock via a specific exception
            if (pendingExitException != null)
            {
                throw pendingExitException;
            }
        }