コード例 #1
0
 /// <summary>
 /// Initializes a new instance of the <see cref="DecodedBlock" /> class.
 /// </summary>
 /// <param name="operations">The operations.</param>
 /// <param name="timings">The timings.</param>
 /// <param name="halt">if set to <c>true</c> [ends on a HALT instruction].</param>
 /// <param name="stop">if set to <c>true</c> [ends on a STOP instruction].</param>
 /// <param name="length">The total block length including literals, displacements etc.</param>
 /// <param name="address">The address.</param>
 public DecodedBlock(ushort address, int length, ICollection <Operation> operations, InstructionTimings timings, bool halt, bool stop)
 {
     Address    = address;
     Length     = length;
     Operations = operations;
     Timings    = timings;
     Halt       = halt;
     Stop       = stop;
 }
コード例 #2
0
 public DecodeFixture(int expectedMachineCycles, int expectedThrottlingStates, params object[] data)
 {
     _expectedTimings = new InstructionTimings(expectedMachineCycles, expectedThrottlingStates);
     _data            = data;
     _address         = Rng.Word(0x6000, 0xaaaa);
     Expected         = new OperationFactory(_address);
     _cpuModes        = Enum.GetValues(typeof(CpuMode)).Cast <CpuMode>().ToArray();
     _throwOn         = Array.Empty <CpuMode>();
     _halt            = true;
 }
コード例 #3
0
 /// <summary>
 /// Uses the configured instruction timings to sync real time to the CPU.
 /// </summary>
 /// <param name="timings">The timings.</param>
 public void SyncToTimings(InstructionTimings timings)
 {
     // Check if we need to call the sync event.
     _cyclesSinceLastEventSync += timings.MachineCycles;
     if (_cyclesSinceLastEventSync > CyclesPerSyncEvent)
     {
         TimingSync?.Invoke(new InstructionTimings(_cyclesSinceLastEventSync));
         _timer.Block((long)(_ticksPerCycle * _cyclesSinceLastEventSync));
         _cyclesSinceLastEventSync = _cyclesSinceLastEventSync - CyclesPerSyncEvent;
     }
 }
コード例 #4
0
 /// <summary>
 /// Creates a DMA copy operation.
 /// </summary>
 /// <param name="sourceAddress">The source address.</param>
 /// <param name="destinationAddress">The destination address.</param>
 /// <param name="length">The length.</param>
 /// <param name="timings">The cpu cycles required to execute this operation.</param>
 /// <param name="lockedAddressesRanges">The address ranges to lock during the copy operation.</param>
 public void Copy(ushort sourceAddress,
                  ushort destinationAddress,
                  int length,
                  InstructionTimings timings,
                  IEnumerable <AddressRange> lockedAddressesRanges)
 => Task.Run(() =>
             _source.TrySetResult(new DmaCopyOperation(sourceAddress,
                                                       destinationAddress,
                                                       length,
                                                       timings,
                                                       lockedAddressesRanges)));
コード例 #5
0
ファイル: DmaCopyOperation.cs プロジェクト: jrp7/Alizarin
 /// <summary>
 /// Initializes a new instance of the <see cref="DmaCopyOperation" /> class.
 /// </summary>
 /// <param name="sourceAddress">The source address.</param>
 /// <param name="destinationAddress">The destination address.</param>
 /// <param name="length">The length.</param>
 /// <param name="timings">The timings.</param>
 /// <param name="lockedAddressesRanges">The locked addresses ranges.</param>
 public DmaCopyOperation(ushort sourceAddress,
                         ushort destinationAddress,
                         int length,
                         InstructionTimings timings,
                         IEnumerable <AddressRange> lockedAddressesRanges)
 {
     SourceAddress         = sourceAddress;
     DestinationAddress    = destinationAddress;
     Length                = length;
     Timings               = timings;
     LockedAddressesRanges = lockedAddressesRanges;
 }
コード例 #6
0
ファイル: InstructionBlock.cs プロジェクト: jrp7/Alizarin
 /// <summary>
 /// Initializes a new instance of the <see cref="InstructionBlock"/> class.
 /// </summary>
 /// <param name="address">The address.</param>
 /// <param name="length">The length.</param>
 /// <param name="action">The action.</param>
 /// <param name="staticTimings">The static timings.</param>
 /// <param name="halt">if set to <c>true</c> [halt].</param>
 /// <param name="stop">if set to <c>true</c> [stop].</param>
 /// <param name="debugInfo">The debug information.</param>
 public InstructionBlock(ushort address,
                         int length,
                         Func <IRegisters, IMmu, IAlu, IPeripheralManager, InstructionTimings> action,
                         InstructionTimings staticTimings,
                         bool halt,
                         bool stop,
                         string debugInfo = null)
 {
     _action         = action;
     _staticTimings  = staticTimings;
     Address         = address;
     Length          = length;
     HaltCpu         = halt || stop;
     HaltPeripherals = stop;
     DebugInfo       = debugInfo;
 }
コード例 #7
0
        private static IEnumerable <Action> Assertions(InstructionTimings observed, InstructionTimings expected)
        {
            yield return(() => observed.MachineCycles.ShouldBe(expected.MachineCycles, nameof(observed.MachineCycles)));

            yield return(() => observed.ThrottlingStates.ShouldBe(expected.ThrottlingStates, nameof(observed.ThrottlingStates)));
        }
コード例 #8
0
 public static void ShouldBe(this InstructionTimings observed, InstructionTimings expected) =>
 observed.ShouldSatisfyAllConditions($"[exptected: {expected}, observed: {observed}", Assertions(observed, expected).ToArray());
コード例 #9
0
        private IEnumerable <Action> Test(CpuMode cpuMode)
        {
            using (var mock = AutoMock.GetLoose())
            {
                var expected = Expected.Build();
                var data     = _halt ? _data.Concat(new object[] { PrimaryOpCode.HALT }).ToArray() : _data;

                var config = mock.Mock <IPlatformConfig>();
                config.Setup(x => x.UndefinedInstructionBehaviour).Returns(UndefinedInstructionBehaviour.Throw);
                config.Setup(x => x.CpuMode).Returns(cpuMode);

                // Simulate a prefetch queue from the specified data.
                var queue    = new Queue(data);
                var prefetch = mock.Mock <IPrefetchQueue>();
                prefetch.Setup(x => x.NextByte()).Returns(() =>
                {
                    var value = queue.Dequeue();
                    switch (value)
                    {
                    case byte b:
                        return(b);

                    case sbyte b:
                        return(unchecked ((byte)b));

                    case PrimaryOpCode op:
                        return((byte)op);

                    case PrefixCbOpCode op:
                        return((byte)op);

                    case PrefixEdOpCode op:
                        return((byte)op);

                    case GameBoyPrimaryOpCode op:
                        return((byte)op);

                    case GameBoyPrefixCbOpCode op:
                        return((byte)op);

                    default:
                        throw new ArgumentOutOfRangeException(nameof(value), value, $"{value.GetType()} not supported");
                    }
                });

                prefetch.Setup(x => x.NextWord()).Returns(() => (ushort)queue.Dequeue());

                var expectedWordsRead = data.Count(x => x is ushort);
                var expectedBytesRead = data.Length - expectedWordsRead; // everything else is a byte.
                var totalBytesRead    = expectedWordsRead * 2 + expectedBytesRead;

                prefetch.Setup(x => x.TotalBytesRead).Returns(() => totalBytesRead);

                // Run.
                var decoder = mock.Create <OpCodeDecoder>();
                if (_throwOn.Contains(cpuMode))
                {
                    yield return(() => Should.Throw <InvalidOperationException>(() => decoder.DecodeNextBlock(_address)));

                    yield break;
                }


                var block = decoder.DecodeNextBlock(_address);
                yield return(() => block.Address.ShouldBe(_address, nameof(block.Address)));

                yield return(() => block.Length.ShouldBe(totalBytesRead, nameof(block.Length)));

                yield return(() => block.Operations.FirstOrDefault().ShouldBe(expected));

                // Timings, adjusted for the extra HALT.
                var timings = block.Timings;
                if (_halt)
                {
                    // Remove tiumings generated by the HALT.
                    timings -= new InstructionTimings(1, 4);
                }
                yield return(() => timings.ShouldBe(_expectedTimings));

                // Make sure the correct mix of bytes and words were read.
                yield return(() => queue.Count.ShouldBe(0, () => $"Didn't read some data: {string.Join(", ", queue.ToArray())}"));

                yield return(() => prefetch.Verify(x => x.NextWord(), Times.Exactly(expectedWordsRead), $"Should have read {expectedWordsRead} words"));

                yield return(() => prefetch.Verify(x => x.NextByte(), Times.Exactly(expectedBytesRead), $"Should have read {expectedBytesRead} bytes"));

                if (_halt)
                {
                    yield return(() => block.Operations.Count.ShouldBe(2, "Should have a single operation and a HALT"));
                }
                else
                {
                    yield return(() => block.Operations.Count.ShouldBe(1, "Should have a single operation"));
                }
            }
        }
コード例 #10
0
ファイル: QueuingDmaController.cs プロジェクト: jrp7/Alizarin
 /// <summary>
 /// Creates a DMA copy operation.
 /// </summary>
 /// <param name="sourceAddress">The source address.</param>
 /// <param name="destinationAddress">The destination address.</param>
 /// <param name="length">The length.</param>
 /// <param name="timings">The cpu cycles required to execute this operation.</param>
 /// <param name="lockedAddressesRanges">The address ranges to lock during the copy operation.</param>
 public void Copy(ushort sourceAddress,
                  ushort destinationAddress,
                  int length,
                  InstructionTimings timings,
                  IEnumerable <AddressRange> lockedAddressesRanges)
 => _dmaOperations.Add(new DmaCopyOperation(sourceAddress, destinationAddress, length, timings, lockedAddressesRanges));
コード例 #11
0
 /// <summary>
 /// Returns a task that will complete in an amount of time according to the specified timings.
 /// </summary>
 /// <param name="timings">The timings.</param>
 /// <returns></returns>
 public async Task DelayAsync(InstructionTimings timings)
 {
     var blockFor = (long)_ticksPerCycle * timings.MachineCycles;
     await Task.Delay(new TimeSpan(blockFor)).ConfigureAwait(false);
 }
コード例 #12
0
ファイル: ExecuteFixture.cs プロジェクト: jrp7/Alizarin
 public ExecuteFixture RuntimeTiming(int machineCycles, int throttlingStates)
 {
     _runtimeTimings = new InstructionTimings(machineCycles, throttlingStates);
     return(this);
 }
コード例 #13
0
        /// <summary>
        /// Synchronizes the GPU thread and associated registers according to the specified instruction timings.
        /// </summary>
        /// <param name="instructionTimings">The instruction timings.</param>
        /// <exception cref="System.ArgumentOutOfRangeException"></exception>
        private void Sync(InstructionTimings instructionTimings)
        {
            if (!_gpuRegisters.LcdControlRegister.LcdOperation)
            {
                return;
            }

            _currentTimings += instructionTimings.MachineCycles;

            switch (_gpuRegisters.GpuMode)
            {
            case GpuMode.HorizontalBlank:
                if (_currentTimings >= HorizontalBlankCycles)
                {
                    _gpuRegisters.IncrementScanline();
                    _gpuRegisters.GpuMode = _gpuRegisters.CurrentScanlineRegister.Scanline == ScanLines - 1
                                                    ? GpuMode.VerticalBlank
                                                    : GpuMode.ReadingOam;
                    _currentTimings -= HorizontalBlankCycles;
                }
                break;

            case GpuMode.VerticalBlank:
                if (_currentTimings >= VerticalBlankCycles)
                {
                    if (_gpuRegisters.CurrentScanlineRegister.Scanline == VerticalBlankScanLines)
                    {
                        // Paint.
                        var painted = _paintingTaskCompletionSource?.TrySetResult(true);
                        if (!painted.GetValueOrDefault())
                        {
                            _frameSkip++;
                        }

                        // Reset
                        _gpuRegisters.CurrentScanlineRegister.Scanline = 0x00;
                        _gpuRegisters.GpuMode = GpuMode.ReadingOam;
                        _interruptFlagsRegister.UpdateInterrupts(InterruptFlag.VerticalBlank);
                        _currentTimings -= VerticalBlankCycles;
                        break;
                    }

                    _gpuRegisters.IncrementScanline();
                    _currentTimings -= VerticalBlankCycles;
                }
                break;

            case GpuMode.ReadingOam:
                if (_currentTimings >= ReadingOamCycles)
                {
                    _gpuRegisters.GpuMode = GpuMode.ReadingVram;
                    _currentTimings      -= ReadingOamCycles;
                }
                break;

            case GpuMode.ReadingVram:
                if (_currentTimings >= ReadingVramCycles)
                {
                    _gpuRegisters.GpuMode = GpuMode.HorizontalBlank;
                    _currentTimings      -= ReadingVramCycles;
                }
                break;

            default:
                throw new ArgumentOutOfRangeException();
            }
        }