2008-09-21 18 views
22

Si hubiera leído mi otro question, sabrá que he pasado este fin de semana armando un emulador de CPU 6502 como ejercicio de programación.CPU Emulación y bloqueo a una velocidad de reloj específica

El emulador de CPU está casi completo, y parece ser bastante preciso a partir de mis pruebas limitadas, sin embargo, funciona increíblemente rápido y quiero reducirlo a la velocidad de reloj real de la máquina.

Mi bucle de prueba actual es la siguiente:

// Just loop infinitely. 
    while (1 == 1) 
    {     
     CPU.ClockCyclesBeforeNext--; 

     if (CPU.ClockCyclesBeforeNext <= 0) 
     { 
      // Find out how many clock cycles this instruction will take 
      CPU.ClockCyclesBeforeNext = CPU.OpcodeMapper.Map[CPU.Memory[CPU.PC]].CpuCycles; 

      // Run the instruction 
      CPU.ExecuteInstruction(CPU.Memory[CPU.PC]); 

      // Debugging Info 
      CPU.DumpDebug(); 
      Console.WriteLine(CPU.OpcodeMapper.Map[CPU.Memory[CPU.PC]].ArgumentLength); 

      // Move to next instruction 
      CPU.PC += 1 + CPU.OpcodeMapper.Map[CPU.Memory[CPU.PC]].ArgumentLength;           
     } 
    } 

Como se puede ver, cada código de operación tiene una cantidad específica de tiempo para completar, así que no ejecute la siguiente instrucción hasta que la cuenta regresiva del reloj de ciclo de la CPU . Esto proporciona la sincronización adecuada entre los códigos de operación, es solo que todo funciona rápido.

La velocidad de CPU objetivo es de 1,79 mhz, sin embargo, me gustaría que cualquier solución al problema del reloj mantenga la velocidad en 1,79 mhz incluso cuando agrego complejidad, por lo que no tengo que ajustarla.

¿Alguna idea?

Respuesta

4

me gustaría utilizar los ciclos de reloj para calcular el tiempo y les dormir la diferencia en el tiempo. Por supuesto, para hacer esto, necesitas un reloj de alta resolución. La manera en que lo haces va a disparar la CPU en bucles giratorios.

7

Tome un vistazo a la documentación de QuickTime original para la inspiración.

que fue escrito hace mucho tiempo, cuando la visualización de vídeo significaba simplemente el intercambio de imágenes fijas a gran velocidad suficiente, pero los chicos de Apple decidieron que necesitaban un marco completo de gestión del tiempo. El diseño al principio parece excesivamente diseñado, pero les permite lidiar con requisitos de velocidad ampliamente diferentes y mantenerlos estrechamente sincronizados.

tiene la suerte de que 6502 tiene un comportamiento de tiempo determinista, el tiempo exacto que tarda cada instrucción está bien documentado; pero no es constante. algunas instrucciones toman 2 ciclos, otras 3. Al igual que los fotogramas en QuickTime, un video no tiene un parámetro 'fotogramas por segundo', cada fotograma dice cuánto tiempo quiere estar en la pantalla.

Dado que las CPU modernas son tan poco deterministas, y los sistemas operativos multitarea pueden congelarse durante algunos milisegundos (memoria virtual), debe mantener una pestaña si está retrasado, o si puede tomar unos pocos microseps. .

9

Escribí un emulador Z80 hace muchos años, y para realizar una ejecución precisa del ciclo, dividí la frecuencia del reloj en varios bloques pequeños y el núcleo ejecutó ese número de ciclos de reloj. En mi caso, lo até a la velocidad de cuadro del sistema de juego que estaba emulando. Cada código de operación sabía cuántos ciclos tardaba en ejecutarse y el núcleo seguiría ejecutando códigos de operación hasta que se hubiera ejecutado el número especificado de ciclos. Tenía un bucle de ejecución externo que ejecutaba el núcleo de la CPU, y ejecutaba otras partes del sistema emulado y luego dormía hasta la hora de inicio de la siguiente iteración.

EDIT: Adición de ejemplo de bucle de ejecución.

int execute_run_loop(int cycles) 
{ 
    int n = 0; 
    while(n < cycles) 
    { 
     /* Returns number of cycles executed */ 
     n += execute_next_opcode(); 
    } 

    return n; 
} 

Espero que esto ayude.

4

Como dice jfk, la forma más común de hacer esto es vincula la velocidad de la CPU a la actualización vertical de la salida de video (emulada).

elegir un número de ciclos a ejecutar por cada trama de vídeo.Esto será a menudo específica de la máquina, pero se puede calcular que por algo como:

cycles = clock speed in Hz/required frames-per-second 

A continuación, también se llega a hacer un sueño hasta que se golpeó la actualización de vídeo, momento en el que se inician los siguientes n ciclos de la emulación de la CPU .

Si está emulando algo en particular, entonces solo necesita buscar la velocidad de fps y la velocidad del procesador para obtener esto aproximadamente a la derecha.

EDITAR: Si no tiene ningún requisito de temporización externo, es normal que un emulador ejecute tan rápido como sea posible. Algunas veces este es un efecto deseado y otras veces no :)

0

Otra opción está disponible si se implementa la emulación de audio y si la salida de audio está vinculada al reloj del sistema/CPU. En particular, sé que este es el caso con las computadoras Apple de 8 bits.

Normalmente, el sonido se genera en búferes de un tamaño fijo (que es un tiempo fijo), por lo que el funcionamiento (generación de datos, etc.) de estos búferes puede vincularse al rendimiento de la CPU a través de primitivas de sincronización.

1

Sí, como se ha dicho antes, la mayoría de las veces no necesita un emulador de CPU para emular instrucciones a la misma velocidad que la real. Lo que el usuario percibe es la salida del cálculo (es decir, salidas de audio y video) por lo que solo necesita estar sincronizado con tales salidas, lo que no significa que debe tener necesariamente una velocidad de emulación de CPU exacta.

En otras palabras, si la velocidad de cuadro de la entrada de video es, digamos, 50Hz, deje que el emulador de CPU funcione lo más rápido posible para dibujar la pantalla, pero asegúrese de imprimir los cuadros de la pantalla a la velocidad correcta (50 Hz). Desde un punto de vista externo, su emulador está emulando a la velocidad correcta.

Intentar ser exacto de ciclo incluso en el tiempo de ejecución no tiene sentido en un sistema operativo multitarea como Windows o Linux porque el tiempo de instrucción del emulador (típicamente 1uS para CPUs vintage 80s) y el intervalo de tiempo de programación de la moderna OS son comparables.

Tratando de demostrar algo a una velocidad de 50 Hz es una tarea mucho más sencilla que puede hacer muy bien en cualquier máquina moderna

0

estoy en el proceso de hacer algo un poco caso de uso más general basa, tales como la capacidad para convertir el tiempo a una cantidad estimada de instrucciones y viceversa.

La página principal del proyecto es @http://net7mma.codeplex.com

El código comienza así: (creo)

#region Copyright 
/* 
This file came from Managed Media Aggregation, You can always find the latest version @ https://net7mma.codeplex.com/ 

[email protected]/(SR. Software Engineer ASTI Transportation Inc. http://www.asti-trans.com) 

Permission is hereby granted, free of charge, 
* to any person obtaining a copy of this software and associated documentation files (the "Software"), 
* to deal in the Software without restriction, 
* including without limitation the rights to : 
* use, 
* copy, 
* modify, 
* merge, 
* publish, 
* distribute, 
* sublicense, 
* and/or sell copies of the Software, 
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 
* 
* 
* [email protected] should be contacted for further details. 

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 
* 
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 
* TORT OR OTHERWISE, 
* ARISING FROM, 
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 
* 
* v// 
*/ 
#endregion 
namespace Media.Concepts.Classes 
{ 
    //Windows.Media.Clock has a fairly complex but complete API 

    /// <summary> 
    /// Provides a clock with a given offset and calendar. 
    /// </summary> 
    public class Clock : Media.Common.BaseDisposable 
    { 
     static bool GC = false; 

     #region Fields 

     /// <summary> 
     /// Indicates when the clock was created 
     /// </summary> 
     public readonly System.DateTimeOffset Created; 

     /// <summary> 
     /// The calendar system of the clock 
     /// </summary> 
     public readonly System.Globalization.Calendar Calendar; 

     /// <summary> 
     /// The amount of ticks which occur per update of the <see cref="System.Environment.TickCount"/> member. 
     /// </summary> 
     public readonly long TicksPerUpdate; 

     /// <summary> 
     /// The amount of instructions which occured when synchronizing with the system clock. 
     /// </summary> 
     public readonly long InstructionsPerClockUpdate; 

     #endregion 

     #region Properties 

     /// <summary> 
     /// The TimeZone offset of the clock from UTC 
     /// </summary> 
     public System.TimeSpan Offset { get { return Created.Offset; } } 

     /// <summary> 
     /// The average amount of operations per tick. 
     /// </summary> 
     public long AverageOperationsPerTick { get { return InstructionsPerClockUpdate/TicksPerUpdate; } } 

     /// <summary> 
     /// The <see cref="System.TimeSpan"/> which represents <see cref="TicksPerUpdate"/> as an amount of time. 
     /// </summary> 
     public System.TimeSpan SystemClockResolution { get { return System.TimeSpan.FromTicks(TicksPerUpdate); } } 

     /// <summary> 
     /// Return the current system time in the TimeZone offset of this clock 
     /// </summary> 
     public System.DateTimeOffset Now { get { return System.DateTimeOffset.Now.ToOffset(Offset).Add(new System.TimeSpan((long)(AverageOperationsPerTick/System.TimeSpan.TicksPerMillisecond))); } } 

     /// <summary> 
     /// Return the current system time in the TimeZone offset of this clock converter to UniversalTime. 
     /// </summary> 
     public System.DateTimeOffset UtcNow { get { return Now.ToUniversalTime(); } } 

     //public bool IsUtc { get { return Offset == System.TimeSpan.Zero; } } 

     //public bool IsDaylightSavingTime { get { return Created.LocalDateTime.IsDaylightSavingTime(); } } 

     #endregion 

     #region Constructor 

     /// <summary> 
     /// Creates a clock using the system's current timezone and calendar. 
     /// The system clock is profiled to determine it's accuracy 
     /// <see cref="System.DateTimeOffset.Now.Offset"/> 
     /// <see cref="System.Globalization.CultureInfo.CurrentCulture.Calendar"/> 
     /// </summary> 
     public Clock(bool shouldDispose = true) 
      : this(System.DateTimeOffset.Now.Offset, System.Globalization.CultureInfo.CurrentCulture.Calendar, shouldDispose) 
     { 
      try { if (false == GC && System.Runtime.GCSettings.LatencyMode != System.Runtime.GCLatencyMode.NoGCRegion) GC = System.GC.TryStartNoGCRegion(0); } 
      catch { } 
      finally 
      { 

       System.Threading.Thread.BeginCriticalRegion(); 

       //Sample the TickCount 
       long ticksStart = System.Environment.TickCount, 
        ticksEnd; 

       //Continually sample the TickCount. while the value has not changed increment InstructionsPerClockUpdate 
       while ((ticksEnd = System.Environment.TickCount) == ticksStart) ++InstructionsPerClockUpdate; //+= 4; Read,Assign,Compare,Increment 

       //How many ticks occur per update of TickCount 
       TicksPerUpdate = ticksEnd - ticksStart; 

       System.Threading.Thread.EndCriticalRegion(); 
      } 
     } 

     /// <summary> 
     /// Constructs a new clock using the given TimeZone offset and Calendar system 
     /// </summary> 
     /// <param name="timeZoneOffset"></param> 
     /// <param name="calendar"></param> 
     /// <param name="shouldDispose">Indicates if the instace should be diposed when Dispose is called.</param> 
     public Clock(System.TimeSpan timeZoneOffset, System.Globalization.Calendar calendar, bool shouldDispose = true) 
     { 
      //Allow disposal 
      ShouldDispose = shouldDispose; 

      Calendar = System.Globalization.CultureInfo.CurrentCulture.Calendar; 

      Created = new System.DateTimeOffset(System.DateTime.Now, timeZoneOffset); 
     } 

     #endregion 

     #region Overrides 

     public override void Dispose() 
     { 

      if (false == ShouldDispose) return; 

      base.Dispose(); 

      try 
      { 
       if (System.Runtime.GCSettings.LatencyMode == System.Runtime.GCLatencyMode.NoGCRegion) 
       { 
        System.GC.EndNoGCRegion(); 

        GC = false; 
       } 
      } 
      catch { } 
     } 

     #endregion 

     //Methods or statics for OperationCountToTimeSpan? (Estimate) 
     public void NanoSleep(int nanos) 
     { 
      Clock.NanoSleep((long)nanos); 
     } 

     public static void NanoSleep(long nanos) 
     { 
      System.Threading.Thread.BeginCriticalRegion(); 

      NanoSleep(ref nanos); 

      System.Threading.Thread.EndCriticalRegion(); 
     } 

     static void NanoSleep(ref long nanos) 
     { 
      try 
      { 
       unchecked 
       { 
        while (Common.Binary.Clamp(--nanos, 0, 1) >= 2) 
        { 
         /* if(--nanos % 2 == 0) */ 
          NanoSleep(long.MinValue); //nanos -= 1 + (ops/(ulong)AverageOperationsPerTick);// *10; 
        } 
       } 
      } 
      catch 
      { 
       return; 
      } 
     } 
    } 
} 

Una vez que tenga algún tipo de aplicación de reloj laico que avanzar a algo así como un Timer

/// <summary> 
/// Provides a Timer implementation which can be used across all platforms and does not rely on the existing Timer implementation. 
/// </summary> 
public class Timer : Common.BaseDisposable 
{ 
    readonly System.Threading.Thread m_Counter; // m_Consumer, m_Producer 

    internal System.TimeSpan m_Frequency; 

    internal ulong m_Ops = 0, m_Ticks = 0; 

    bool m_Enabled; 

    internal System.DateTimeOffset m_Started; 

    public delegate void TickEvent(ref long ticks); 

    public event TickEvent Tick; 

    public bool Enabled { get { return m_Enabled; } set { m_Enabled = value; } } 

    public System.TimeSpan Frequency { get { return m_Frequency; } } 

    internal ulong m_Bias; 

    // 

    //Could just use a single int, 32 bits is more than enough. 

    //uint m_Flags; 

    // 

    readonly internal Clock m_Clock = new Clock(); 

    readonly internal System.Collections.Generic.Queue<long> Producer; 

    void Count() 
    { 

     System.Threading.Thread Event = new System.Threading.Thread(new System.Threading.ThreadStart(() => 
     { 
      System.Threading.Thread.BeginCriticalRegion(); 
      long sample; 
     AfterSample: 
      try 
      { 
      Top: 
       System.Threading.Thread.CurrentThread.Priority = System.Threading.ThreadPriority.Highest; 

       while (m_Enabled && Producer.Count >= 1) 
       { 
        sample = Producer.Dequeue(); 

        Tick(ref sample); 
       } 

       System.Threading.Thread.CurrentThread.Priority = System.Threading.ThreadPriority.Lowest; 

       if (false == m_Enabled) return; 

       while (m_Enabled && Producer.Count == 0) if(m_Counter.IsAlive) m_Counter.Join(0); //++m_Ops; 

       goto Top; 
      } 
      catch { if (false == m_Enabled) return; goto AfterSample; } 
      finally { System.Threading.Thread.EndCriticalRegion(); } 
     })) 
     { 
      IsBackground = false, 
      Priority = System.Threading.ThreadPriority.AboveNormal 
     }; 

     Event.TrySetApartmentState(System.Threading.ApartmentState.MTA); 

     Event.Start(); 

     Approximate: 

     ulong approximate = (ulong)Common.Binary.Clamp((m_Clock.AverageOperationsPerTick/(Frequency.Ticks + 1)), 1, ulong.MaxValue); 

     try 
     { 
      m_Started = m_Clock.Now; 

      System.Threading.Thread.BeginCriticalRegion(); 

      unchecked 
      { 
      Start: 

       if (IsDisposed) return; 

       switch (++m_Ops) 
       { 
        default: 
         { 
          if (m_Bias + ++m_Ops >= approximate) 
          { 
           System.Threading.Thread.CurrentThread.Priority = System.Threading.ThreadPriority.Highest; 

           Producer.Enqueue((long)m_Ticks++); 

           ulong x = ++m_Ops/approximate; 

           while (1 > --x /*&& Producer.Count <= m_Frequency.Ticks*/) Producer.Enqueue((long)++m_Ticks); 

           m_Ops = (++m_Ops * m_Ticks) - (m_Bias = ++m_Ops/approximate); 

           System.Threading.Thread.CurrentThread.Priority = System.Threading.ThreadPriority.Lowest; 
          } 

          if(Event != null) Event.Join(m_Frequency); 

          goto Start; 
         } 
       } 
      } 
     } 
     catch (System.Threading.ThreadAbortException) { if (m_Enabled) goto Approximate; System.Threading.Thread.ResetAbort(); } 
     catch (System.OutOfMemoryException) { if ((ulong)Producer.Count > approximate) Producer.Clear(); if (m_Enabled) goto Approximate; } 
     catch { if (m_Enabled) goto Approximate; } 
     finally 
     { 
      Event = null; 

      System.Threading.Thread.EndCriticalRegion(); 
     } 
    } 

    public Timer(System.TimeSpan frequency) 
    { 
     Producer = new System.Collections.Generic.Queue<long>((int)(m_Frequency = frequency).Ticks * 10); 

     m_Counter = new System.Threading.Thread(new System.Threading.ThreadStart(Count)) 
     { 
      IsBackground = false, 
      Priority = System.Threading.ThreadPriority.AboveNormal 
     }; 

     m_Counter.TrySetApartmentState(System.Threading.ApartmentState.MTA); 

     Tick = delegate { m_Ops += 1 + m_Bias; }; 
    } 

    public void Start() 
    { 
     if (m_Enabled) return; 

     m_Enabled = true; 

     m_Counter.Start(); 

     var p = System.Threading.Thread.CurrentThread.Priority; 

     System.Threading.Thread.CurrentThread.Priority = System.Threading.ThreadPriority.Lowest; 

     while (m_Ops == 0) m_Counter.Join(0); //m_Clock.NanoSleep(0); 

     System.Threading.Thread.CurrentThread.Priority = p; 

    } 

    public void Stop() 
    { 
     m_Enabled = false; 
    } 

    void Change(System.TimeSpan interval, System.TimeSpan dueTime) 
    { 
     m_Enabled = false; 

     m_Frequency = interval; 

     m_Enabled = true; 
    } 

    delegate void ElapsedEvent(object sender, object args); 

    public override void Dispose() 
    { 
     if (IsDisposed) return;    

     base.Dispose(); 

     Stop(); 

     try { m_Counter.Abort(m_Frequency); } 
     catch (System.Threading.ThreadAbortException) { System.Threading.Thread.ResetAbort(); } 
     catch { } 

     Tick = null; 

     //Producer.Clear(); 
    } 

} 

Entonces realmente puedes replicar algo de lógica usando algo como

/// <summary> 
/// Provides a completely managed implementation of <see cref="System.Diagnostics.Stopwatch"/> which expresses time in the same units as <see cref="System.TimeSpan"/>. 
/// </summary> 
public class Stopwatch : Common.BaseDisposable 
{ 
    internal Timer Timer; 

    long Units; 

    public bool Enabled { get { return Timer != null && Timer.Enabled; } } 

    public double ElapsedMicroseconds { get { return Units * Media.Common.Extensions.TimeSpan.TimeSpanExtensions.TotalMicroseconds(Timer.Frequency); } } 

    public double ElapsedMilliseconds { get { return Units * Timer.Frequency.TotalMilliseconds; } } 

    public double ElapsedSeconds { get { return Units * Timer.Frequency.TotalSeconds; } } 

    //public System.TimeSpan Elapsed { get { return System.TimeSpan.FromMilliseconds(ElapsedMilliseconds/System.TimeSpan.TicksPerMillisecond); } } 

    public System.TimeSpan Elapsed 
    { 
     get 
     { 
      switch (Units) 
      { 
       case 0: return System.TimeSpan.Zero; 
       default: 
        { 
         System.TimeSpan taken = System.DateTime.UtcNow - Timer.m_Started; 

         return taken.Add(new System.TimeSpan(Units * Timer.Frequency.Ticks)); 

         //System.TimeSpan additional = new System.TimeSpan(Media.Common.Extensions.Math.MathExtensions.Clamp(Units, 0, Timer.Frequency.Ticks)); 

         //return taken.Add(additional); 
        } 
      } 



      //////The maximum amount of times the timer can elapse in the given frequency 
      ////double maxCount = (taken.TotalMilliseconds/Timer.Frequency.TotalMilliseconds)/ElapsedMilliseconds; 

      ////if (Units > maxCount) 
      ////{ 
      //// //How many more times the event was fired than needed 
      //// double overage = (maxCount - Units); 

      //// additional = new System.TimeSpan(System.Convert.ToInt64(Media.Common.Extensions.Math.MathExtensions.Clamp(Units, overage, maxCount))); 

      //// //return taken.Add(new System.TimeSpan((long)Media.Common.Extensions.Math.MathExtensions.Clamp(Units, overage, maxCount))); 
      ////} 
      //////return taken.Add(new System.TimeSpan(Units)); 


     } 
    } 

    public void Start() 
    { 
     if (Enabled) return; 

     Units = 0; 

     //Create a Timer that will elapse every OneTick //`OneMicrosecond` 
     Timer = new Timer(Media.Common.Extensions.TimeSpan.TimeSpanExtensions.OneTick); 

     //Handle the event by incrementing count 
     Timer.Tick += Count; 

     Timer.Start(); 
    } 

    public void Stop() 
    { 
     if (false == Enabled) return; 

     Timer.Stop(); 

     Timer.Dispose();   
    } 

    void Count(ref long count) { ++Units; } 
} 

Finalmente, cree algo semi útil, p. Ej. un Bus y luego tal vez una pantalla virtual para emitir datos al bus ...

public abstract class Bus : Common.CommonDisposable 
    { 
     public readonly Timer Clock = new Timer(Common.Extensions.TimeSpan.TimeSpanExtensions.OneTick); 

     public Bus() : base(false) { Clock.Start(); } 
    } 

    public class ClockedBus : Bus 
    { 
     long FrequencyHz, Maximum, End; 

     readonly Queue<byte[]> Input = new Queue<byte[]>(), Output = new Queue<byte[]>(); 

     readonly double m_Bias; 

     public ClockedBus(long frequencyHz, double bias = 1.5) 
     { 
      m_Bias = bias; 

      cache = Clock.m_Clock.InstructionsPerClockUpdate/1000; 

      SetFrequency(frequencyHz); 

      Clock.Tick += Clock_Tick; 

      Clock.Start(); 
     } 

     public void SetFrequency(long frequencyHz) 
     { 
      FrequencyHz = frequencyHz; 

      //Clock.m_Frequency = new TimeSpan(Clock.m_Clock.InstructionsPerClockUpdate/1000); 

      //Maximum = System.TimeSpan.TicksPerSecond/Clock.m_Clock.InstructionsPerClockUpdate; 

      //Maximum = Clock.m_Clock.InstructionsPerClockUpdate/System.TimeSpan.TicksPerSecond; 

      Maximum = cache/(cache/FrequencyHz); 

      Maximum *= System.TimeSpan.TicksPerSecond; 

      Maximum = (cache/FrequencyHz); 

      End = Maximum * 2; 

      Clock.m_Frequency = new TimeSpan(Maximum); 

      if (cache < frequencyHz * m_Bias) throw new Exception("Cannot obtain stable clock"); 

      Clock.Producer.Clear(); 
     } 

     public override void Dispose() 
     { 
      ShouldDispose = true; 

      Clock.Tick -= Clock_Tick; 

      Clock.Stop(); 

      Clock.Dispose(); 

      base.Dispose(); 
     } 

     ~ClockedBus() { Dispose(); } 

     long sample = 0, steps = 0, count = 0, avg = 0, cache = 1; 

     void Clock_Tick(ref long ticks) 
     { 
      if (ShouldDispose == false && false == IsDisposed) 
      { 
       //Console.WriteLine("@ops=>" + Clock.m_Ops + " @ticks=>" + Clock.m_Ticks + " @Lticks=>" + ticks + "@=>" + Clock.m_Clock.Now.TimeOfDay + "@=>" + (Clock.m_Clock.Now - Clock.m_Clock.Created)); 

       steps = sample; 

       sample = ticks; 

       ++count; 

       System.ConsoleColor f = System.Console.ForegroundColor; 

       if (count <= Maximum) 
       { 
        System.Console.BackgroundColor = ConsoleColor.Yellow; 

        System.Console.ForegroundColor = ConsoleColor.Green; 

        Console.WriteLine("count=> " + count + "@=>" + Clock.m_Clock.Now.TimeOfDay + "@=>" + (Clock.m_Clock.Now - Clock.m_Clock.Created) + " - " + DateTime.UtcNow.ToString("MM/dd/yyyy hh:mm:ss.ffffff tt")); 

        avg = Maximum/count; 

        if (Clock.m_Clock.InstructionsPerClockUpdate/count > Maximum) 
        { 
         System.Console.ForegroundColor = ConsoleColor.Red; 

         Console.WriteLine("---- Over InstructionsPerClockUpdate ----" + FrequencyHz); 
        } 
       } 
       else if (count >= End) 
       { 
        System.Console.BackgroundColor = ConsoleColor.Black; 

        System.Console.ForegroundColor = ConsoleColor.Blue; 

        avg = Maximum/count; 

        Console.WriteLine("avg=> " + avg + "@=>" + FrequencyHz); 

        count = 0; 
       } 
      } 
     } 

     //Read, Write at Frequency 

    } 
public class VirtualScreen 
    { 
     TimeSpan RefreshRate;  
     bool VerticalSync;  
     int Width, Height;    
     Common.MemorySegment DisplayMemory, BackBuffer, DisplayBuffer; 
    } 

Así es como he probado el StopWatch

internal class StopWatchTests 
    { 
     public void TestForOneMicrosecond() 
     { 
      System.Collections.Generic.List<System.Tuple<bool, System.TimeSpan, System.TimeSpan>> l = new System.Collections.Generic.List<System.Tuple<bool, System.TimeSpan, System.TimeSpan>>(); 

      //Create a Timer that will elapse every `OneMicrosecond` 
      for (int i = 0; i <= 250; ++i) using (Media.Concepts.Classes.Stopwatch sw = new Media.Concepts.Classes.Stopwatch()) 
      { 
       var started = System.DateTime.UtcNow; 

       System.Console.WriteLine("Started: " + started.ToString("MM/dd/yyyy hh:mm:ss.ffffff tt")); 

       //Define some amount of time 
       System.TimeSpan sleepTime = Media.Common.Extensions.TimeSpan.TimeSpanExtensions.OneMicrosecond; 

       System.Diagnostics.Stopwatch testSw = new System.Diagnostics.Stopwatch(); 

       //Start 
       testSw.Start(); 

       //Start 
       sw.Start(); 

       while (testSw.Elapsed.Ticks < sleepTime.Ticks - (Common.Extensions.TimeSpan.TimeSpanExtensions.OneTick + Common.Extensions.TimeSpan.TimeSpanExtensions.OneTick).Ticks) 
        sw.Timer.m_Clock.NanoSleep(0); //System.Threading.Thread.SpinWait(0); 

       //Sleep the desired amount 
       //System.Threading.Thread.Sleep(sleepTime); 

       //Stop 
       testSw.Stop(); 

       //Stop 
       sw.Stop(); 

       var finished = System.DateTime.UtcNow; 

       var taken = finished - started; 

       var cc = System.Console.ForegroundColor; 

       System.Console.WriteLine("Finished: " + finished.ToString("MM/dd/yyyy hh:mm:ss.ffffff tt")); 

       System.Console.WriteLine("Sleep Time: " + sleepTime.ToString()); 

       System.Console.WriteLine("Real Taken Total: " + taken.ToString()); 

       if (taken > sleepTime) 
       { 
        System.Console.ForegroundColor = System.ConsoleColor.Red; 
        System.Console.WriteLine("Missed by: " + (taken - sleepTime)); 
       } 
       else 
       { 
        System.Console.ForegroundColor = System.ConsoleColor.Green; 
        System.Console.WriteLine("Still have: " + (sleepTime - taken)); 
       } 

       System.Console.ForegroundColor = cc; 

       System.Console.WriteLine("Real Taken msec Total: " + taken.TotalMilliseconds.ToString()); 

       System.Console.WriteLine("Real Taken sec Total: " + taken.TotalSeconds.ToString()); 

       System.Console.WriteLine("Real Taken μs Total: " + Media.Common.Extensions.TimeSpan.TimeSpanExtensions.TotalMicroseconds(taken).ToString()); 

       System.Console.WriteLine("Managed Taken Total: " + sw.Elapsed.ToString()); 

       System.Console.WriteLine("Diagnostic Taken Total: " + testSw.Elapsed.ToString()); 

       System.Console.WriteLine("Diagnostic Elapsed Seconds Total: " + ((testSw.ElapsedTicks/(double)System.Diagnostics.Stopwatch.Frequency))); 

       //Write the rough amount of time taken in micro seconds 
       System.Console.WriteLine("Managed Time Estimated Taken: " + sw.ElapsedMicroseconds + "μs"); 

       //Write the rough amount of time taken in micro seconds 
       System.Console.WriteLine("Diagnostic Time Estimated Taken: " + Media.Common.Extensions.TimeSpan.TimeSpanExtensions.TotalMicroseconds(testSw.Elapsed) + "μs"); 

       System.Console.WriteLine("Managed Time Estimated Taken: " + sw.ElapsedMilliseconds); 

       System.Console.WriteLine("Diagnostic Time Estimated Taken: " + testSw.ElapsedMilliseconds); 

       System.Console.WriteLine("Managed Time Estimated Taken: " + sw.ElapsedSeconds); 

       System.Console.WriteLine("Diagnostic Time Estimated Taken: " + testSw.Elapsed.TotalSeconds); 

       if (sw.Elapsed < testSw.Elapsed) 
       { 
        System.Console.WriteLine("Faster than Diagnostic StopWatch"); 
        l.Add(new System.Tuple<bool, System.TimeSpan, System.TimeSpan>(true, sw.Elapsed, testSw.Elapsed)); 
       } 
       else if (sw.Elapsed > testSw.Elapsed) 
       { 
        System.Console.WriteLine("Slower than Diagnostic StopWatch"); 
        l.Add(new System.Tuple<bool, System.TimeSpan, System.TimeSpan>(false, sw.Elapsed, testSw.Elapsed)); 
       } 
       else 
       { 
        System.Console.WriteLine("Equal to Diagnostic StopWatch"); 
        l.Add(new System.Tuple<bool, System.TimeSpan, System.TimeSpan>(true, sw.Elapsed, testSw.Elapsed)); 
       } 
      } 

      int w = 0, f = 0; 

      var cc2 = System.Console.ForegroundColor; 

      foreach (var t in l) 
      { 
       if (t.Item1) 
       { 
        System.Console.ForegroundColor = System.ConsoleColor.Green; 
        ++w; System.Console.WriteLine("Faster than Diagnostic StopWatch by: " + (t.Item3 - t.Item2)); 
       } 
       else 
       { 
        System.Console.ForegroundColor = System.ConsoleColor.Red; 
        ++f; System.Console.WriteLine("Slower than Diagnostic StopWatch by: " + (t.Item2 - t.Item3)); 
       } 
      } 

      System.Console.ForegroundColor = System.ConsoleColor.Green; 
      System.Console.WriteLine("Wins = " + w); 

      System.Console.ForegroundColor = System.ConsoleColor.Red; 
      System.Console.WriteLine("Loss = " + f); 

      System.Console.ForegroundColor = cc2; 
     } 
    } 
Cuestiones relacionadas