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.
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.
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
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.
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.
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.
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.
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
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)
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
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
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++
result = floor(static_cast<double>(hertz) * ONE_OVER_CPU_CLOCK_SPEED_HZ);
hertz = 0;
cycles = 0;
QueryPerformanceCounter(&last_time);
}
};
cout << result << endl;
return 0;
}
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.
Last edited by Jacob Roman; Oct 5th, 2021 at 10:46 PM.
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.