Results 1 to 11 of 11

Thread: Timed Loops to Simulate Hertz

  1. #1

    Thread Starter
    College Grad!!! Jacob Roman's Avatar
    Join Date
    Aug 2004
    Location
    Miami Beach, FL
    Posts
    5,299

    Timed Loops to Simulate Hertz

    Hello. I am currently simulating a 6502 CPU to run at an exact rate of 1.789773 MHz, which is 1789773 cycles per second. With my current code of locking the framerate, I can almost do it, as my computer can do 5 times that in a loop, but it does 1789773 cycles every 1.12 seconds, a whole .12 seconds off. I am at work right now so I cant show my current code yet, but is there a way to lock the rate of a Do loop just right so it does 1789773 cycles every exact second, or any speed for that matter? Thanks in advance. Also I am using QueryPerformanceCounters and QueryPerformanceFrequency APIs to help lock the framerate at a given speed. Almost works.

  2. #2
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    6,521

    Re: Timed Loops to Simulate Hertz

    I don't want to say it's impossible but my instinct is that it is impossible. I am assuming that you are running on Windows. Thing is, Windows is a pre-emptive multi-tasking operating system that is typically executing way more threads than it has cores. What this means is that the vast majority of threads are spending a lot of time not being executed and the entire process of time slicing is non-deterministic from the point of view of user mode applications. Quite frankly, I think it's a miracle you're only off by 1/10th of a second.

    All of this is just me speculating though. Perhaps there is someone here that knows a lot better than me.
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena

    Copy/move files using Windows Shell

    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. - jmcilhinney

    The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber

  3. #3
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    36,630

    Re: Timed Loops to Simulate Hertz

    I agree with Niya, and I think you can see this by making a loop that does some random, but short and repetitive, garbage, and timing it over and over. For example, you could do something like this:

    Code:
    Private i As Double 'Cause, why NOT use a private i?
    
    For x = 0 to 100000
      i = Math.sqr(x)
      i = i * x
      i = Math.sqr(x)
    Next
    That code might actually be TOO simple, but it gets the idea across: It should take the same amount of time every time, but if you time it, you find out that it will vary by small amounts most of the time, and occasionally by large amounts. That's because the OS will occasionally interrupt. In theory, it could also be lower level things interrupting, but it probably isn't.

    EDIT: I have a vague memory that there is an external clock you could add that would give you a predictable pulse. That memory is from at least one, and quite likely two, decades back, so it may not be true anymore, if it ever was. Still, it's something you might look into.
    My usual boring signature: Nothing

  4. #4

    Thread Starter
    College Grad!!! Jacob Roman's Avatar
    Join Date
    Aug 2004
    Location
    Miami Beach, FL
    Posts
    5,299

    Re: Timed Loops to Simulate Hertz

    What I have set up right now is a loop within the loop that helps lock the original loop at a certain speed. It does work but it almost works. You should be able to control the speed of the original loop. Should be home in about 3 hours or so to demonstrate this.

  5. #5
    Super Moderator dday9's Avatar
    Join Date
    Mar 2011
    Location
    South Louisiana
    Posts
    10,505

    Re: Timed Loops to Simulate Hertz

    I have a managed game loop timer codebank submission here: https://www.vbforums.com/showthread....aged-Game-Loop (this was written 7 years ago, might qualify for optimization).

    It also leverages QueryPerformanceCounters and QueryPerformanceFrequency, but the frequency is not a perfect 16.6ms elapse rate. It is usually off by a couple of couple hundreth to sometimes a full millisecond. I could never get it to consistently tick at 16.6ms and eventually gave up.

    My conclusion was that this was a limitation of VB.NET being a higher level programming language and WinForm technology being antiquated. I could be completely wrong, but I do remember spending significant time on trying to get this resolve only to come up short.

    Edit - Looking a little bit more, it seems as though QueryInteruptTimePrecise (documentation, only available Windows 10) may be more precise than QueryPerformanceCounters: https://docs.microsoft.com/en-us/win...on-time-stamps
    Last edited by dday9; Sep 21st, 2021 at 02:47 PM.
    "Code is like humor. When you have to explain it, it’s bad." - Cory House
    VbLessons | Code Tags | Sword of Fury - Jameram

  6. #6
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    6,521

    Re: Timed Loops to Simulate Hertz

    Quote Originally Posted by dday9 View Post
    My conclusion was that this was a limitation of VB.NET being a higher level programming language and WinForm technology being antiquated. I could be completely wrong, but I do remember spending significant time on trying to get this resolve only to come up short.
    I wouldn't go this far yet. While, the .Net GC is unpredictable and can cause unpredictable stutters, I think the OS itself also has a major role to play in this. The OS has to share the CPU between your program and the other thousands of threads running at any one time on Windows. This process is also unpredictable and mostly outside of your control. The only control you do have is to tell the OS to give your threads higher priority but the OS doesn't have to honor that request as faithfully as you might want. It doesn't even have to honor it at all.

    I'd even go as far as saying that being off by only a few milliseconds despite all this multi-tasking chaos is a testament to the superb software engineering skills of Microsoft and the engineering skills of modern CPU manufacturers.
    Last edited by Niya; Sep 21st, 2021 at 03:09 PM.
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena

    Copy/move files using Windows Shell

    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. - jmcilhinney

    The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber

  7. #7
    Fanatic Member Delaney's Avatar
    Join Date
    Nov 2019
    Location
    Paris, France
    Posts
    717

    Re: Timed Loops to Simulate Hertz

    At work to achieve this kind of precision and frequency, we use specific electronic cards who have their own clock and timer because windows in not reliable under the ms.
    The best friend of any programmer is a search engine
    "Don't wish it was easier, wish you were better. Don't wish for less problems, wish for more skills. Don't wish for less challenges, wish for more wisdom" (J. Rohn)
    “They did not know it was impossible so they did it” (Mark Twain)

  8. #8

    Thread Starter
    College Grad!!! Jacob Roman's Avatar
    Join Date
    Aug 2004
    Location
    Miami Beach, FL
    Posts
    5,299

    Re: Timed Loops to Simulate Hertz

    Finally home. I made a demonstration project for you all to try out. Just need to figure out how to possibly match the speed with exact seconds. I understand that there are background tasks that could interrupt it, but if I could harness the "speed limit" of the loop, then I got this by the buttcheeks


    vb Code:
    1. Option Explicit On
    2. Option Strict On
    3.  
    4. Imports System.Runtime.InteropServices
    5. Imports System.Security
    6.  
    7. Public Class Form1
    8.  
    9.     <DllImport("kernel32.dll"), SuppressUnmanagedCodeSecurity()>
    10.     Private Shared Function QueryPerformanceCounter(ByRef lpPerformanceCount As Long) As Boolean
    11.     End Function
    12.  
    13.     <DllImport("kernel32.dll"), SuppressUnmanagedCodeSecurity()>
    14.     Private Shared Function QueryPerformanceFrequency(ByRef lpPerformanceFreq As Long) As Boolean
    15.     End Function
    16.  
    17.  
    18.     Private loopy As Boolean
    19.     Private ticks_per_second As Long
    20.     Private cpu_clock_current_time As Long
    21.     Private cpu_clock_last_time As Long
    22.     Private Const CPU_CLOCK_SPEED_HZ As Integer = 1789773
    23.     Private cycles As Integer
    24.     Private cpucps As Double
    25.     Private time As Single
    26.     Private last_time As Single
    27.  
    28.  
    29.     Private Function Get_Elapsed_Time() As Single
    30.         Dim last_time As Long
    31.         Dim current_time As Long
    32.         Dim result As Single
    33.  
    34.         QueryPerformanceCounter(current_time)
    35.         result = Convert.ToSingle(current_time - last_time) / Convert.ToSingle(ticks_per_second)
    36.         QueryPerformanceCounter(last_time)
    37.  
    38.         Return result
    39.     End Function
    40.  
    41.  
    42.     Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    43.  
    44.         Show()
    45.         QueryPerformanceFrequency(ticks_per_second)
    46.         loopy = True
    47.         last_time = Get_Elapsed_Time()
    48.         Do While loopy = True
    49.  
    50.             Do
    51.                 QueryPerformanceCounter(cpu_clock_current_time)
    52.                 cpucps = Convert.ToDouble(ticks_per_second) / (Convert.ToDouble(cpu_clock_current_time - cpu_clock_last_time))
    53.             Loop While (cpucps >= CPU_CLOCK_SPEED_HZ)
    54.             QueryPerformanceCounter(cpu_clock_last_time)
    55.             cycles += 1
    56.             time = Get_Elapsed_Time() - last_time
    57.  
    58.             If (cycles >= CPU_CLOCK_SPEED_HZ) Then
    59.                 Label1.Text = Convert.ToString(cycles)
    60.                 Label4.Text = Convert.ToString(time) + " seconds"
    61.                 cycles = 0
    62.                 last_time = Get_Elapsed_Time()
    63.             End If
    64.             Application.DoEvents()
    65.         Loop
    66.     End Sub
    67.  
    68.     Private Sub Form1_Closed(sender As Object, e As EventArgs) Handles Me.Closed
    69.         loopy = False
    70.         Application.Exit()
    71.     End Sub
    72. End Class

  9. #9
    Sinecure devotee
    Join Date
    Aug 2013
    Location
    Southern Tier NY
    Posts
    6,384

    Re: Timed Loops to Simulate Hertz

    I didn't try the code or look at it in detail.

    Just looking at the Get_Elapsed_Time function, the code doesn't seem to make sense.
    You're subtracting last_time from current_time, but since last_time is declared local to the function, it will always be 0 when you do that subtraction.
    Perhaps you want it to be Static, rather than Dim.

    Also, when I don't want a clock or counter to drift, I don't read the clock/counter to get the value to put in the class level last_time. I base last_time on the last time the function should have run. Using the function to set last_time is going to include the time it takes for the function to run and the code that was run to get to the point of the function call.
    What I do is read the clock/counter at the beginning of the code to establish a basetime, and then add specific deltas to the basetime for the next trigger point.

    For example, assume you have a ticks per second of 3,553,232.
    If you want to leave your inner loop every second, which you appear to do in this example, then you read the time to establish the base value, and you add the 3,553,232 to it.
    When you reach that trigger point and exit your loop, you add 3,553,232 to the current base value to get the next trigger point. That way it doesn't matter how long the various function calls take to get the time, you are incrementing your trigger by a fixed amount each time so you don't accumulate small timing errors into your trigger points.
    "Anyone can do any amount of work, provided it isn't the work he is supposed to be doing at that moment" Robert Benchley, 1930

  10. #10

    Thread Starter
    College Grad!!! Jacob Roman's Avatar
    Join Date
    Aug 2004
    Location
    Miami Beach, FL
    Posts
    5,299

    Re: Timed Loops to Simulate Hertz

    Don't think I gave up on this just yet folks

    I honestly gotten a hell of a lot closer to my goal fiddling with the idea of setting a "speed limit" for loops at any iteration, even in the millions. I haven't ported this to VB.Net yet but I managed to pull it off in C++

    c++ Code:
    1. #include <windows.h>
    2. #include <math.h>
    3. #include <iostream>
    4. using namespace std;
    5.  
    6. int main()
    7. {
    8.     const int CPU_CLOCK_SPEED_HZ = 1789773;
    9.     const double ONE_OVER_CPU_CLOCK_SPEED_HZ = 1.0 / CPU_CLOCK_SPEED_HZ;
    10.     LARGE_INTEGER ticks;
    11.     double one_over_ticks = 0.0;
    12.     LARGE_INTEGER current_time;
    13.     LARGE_INTEGER last_time;
    14.     double time = 0.0;
    15.     int hertz = 0;
    16.     double result = 0.0;
    17.     int x = 0;
    18.     int cycles = 0;
    19.  
    20.     QueryPerformanceFrequency(&ticks);
    21.     one_over_ticks = 1.0 / static_cast<double>(ticks.QuadPart);
    22.     QueryPerformanceCounter(&last_time);
    23.  
    24.     do
    25.     {
    26.         hertz++;
    27.         QueryPerformanceCounter(&current_time);
    28.         time = static_cast<double>(current_time.QuadPart - last_time.QuadPart) * one_over_ticks;
    29.        
    30.     } while (time < 1.0);
    31.  
    32.     result = floor(static_cast<double>(hertz) * ONE_OVER_CPU_CLOCK_SPEED_HZ);
    33.     hertz = 0;
    34.     QueryPerformanceCounter(&last_time);
    35.  
    36.     while (1)
    37.     {
    38.         do
    39.         {
    40.             hertz++;
    41.             x++;
    42.         } while (x <= result);
    43.  
    44.         x = 0;
    45.         cycles++;
    46.  
    47.         QueryPerformanceCounter(&current_time);
    48.         time = static_cast<double>(current_time.QuadPart - last_time.QuadPart) * one_over_ticks;
    49.  
    50.         if (time >= 1.0)
    51.         {
    52.             cout << "hertz : " << hertz << endl;
    53.             cout << "cycles: " << cycles << endl;
    54.             cout << "goal  : " << CPU_CLOCK_SPEED_HZ << endl << endl;
    55.             result = floor(static_cast<double>(hertz) * ONE_OVER_CPU_CLOCK_SPEED_HZ);
    56.             hertz = 0;
    57.             cycles = 0;
    58.             QueryPerformanceCounter(&last_time);
    59.         }
    60.     };
    61.  
    62.  
    63.     cout << result << endl;
    64.  
    65.     return 0;
    66. }

    I have been getting values for the variable cycles that nearly range around my goal of 1789773, which is quite an improvement from my old code. It basically has been averaging around 1780000. Sometimes it'll once in awhile overshoot to 1800000. But that all depends where the timer ends up once the one second ends. I'm pretty certain when it comes to CPU's such as 6502 processors which is what I am emulating, it is not consistently 1.78 MHz, but probably ranges around there and only averages 1.78 MHz. And if you play around with the CPU_CLOCK_SPEED_HZ with any value, such as 60 or 100, it does a solid job on trying to stick with it. Gotta goto bed right now, but tomorrow I will port this over to VB.Net and see if I get similar results. I'm actually pretty psyched that I've gotten closer.
    Attached Images Attached Images  

  11. #11
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    36,630

    Re: Timed Loops to Simulate Hertz

    My impression was that the timer in those early chips was a quartz crystal oscillator (might have the wrong crystal), which was very consistent. Not sure what is used, now, but it it's really just a crystal oscillator, there may not be much variation around the frequency at all. Oscillating systems don't seem to vary all that much.
    My usual boring signature: Nothing

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width