Acmlm's Board - I2 Archive - Programming - Thinking of getting serious with C.
User | Post |
Dish
Posts: 238/596 |
Originally posted by HyperHacker Too slow. If I checked every frame it slowed down the emulation drastically, and if I checked every second or so the window became very unresponsive.
Not really. The time you spend processing windows messages is next to nothing. Besides... the CPU divies up processing time as needed for every running thread. You're still telling it to do the same amount of work.... making it divie up its attention to 2 seperate threads doesn't somehow make the processor run faster... if anything it makes it slower (more threads = more CPU split time).
The basic idea is:
Process all pending messages... Do a frame... and sleep if time allows (if you don't spend time sleeping, your program will gobble 100% of the CPU's time). For a demonstration:
----
while( runprogram ) {
while( PeekMessage( wnd, &msg, 0,0, PM_REMOVE ) ) { TranslateMessage(&msg); DispatchMessage(&msg); }
if( emulationpaused ) { Sleep(10); // if emulation isn't running for some reason, don't hog CPU time... sleep continue; // and go back to pump }
DoAnEmulationFrame();
// check time here, if it's too early to do another emulation frame, Sleep for a while
}
----
that runs suprisingly fast... hardly any CPU time will be spent on the PeekMessage loop. And if your time checking code is good... it can be smooth. The Demo NES Emu I made a while back does something like this. You can check the winmain cpp file in the source of said emu (or whatever the file is named) for a demonstration. That emu is very quick on my machine (typically no more than 0-1% CPU time)
Running multiple threads won't speed anything up... they may actually slow things down a bit. Not to mention they severly complicate things and cause lots of weird quirks (like your prior window problem). |
HyperLamer
Posts: 2985/8210 |
Originally posted by Disch I don't even see why you'd need a seperate thread at all (you could just check for messages periodically in your main thread... like say every frame or something).
Too slow. If I checked every frame it slowed down the emulation drastically, and if I checked every second or so the window became very unresponsive.
if hVideoWindow is a valid window handle... PeekMessage WILL return true (unless that thread thing is a problem).
This would appear to be the case, but according to MSDN, it has to be called by the thread that created the window. Hence why I had to move the window creation to the other thread as well.
Are you sure it's not returning true? I'm not exactly sure how printf() fits in here... since this isn't a console program (where are you expecting it to print to?)
It does have a console window for debug output. If it returned true, a message would be printed to the console window.
declare vars that MUST be "thread safe" as volatile
I was using this, but removed it when trying to fix something. I probably should put it back though.
bah... way to make my post useless XD
|
Dish
Posts: 237/596 |
I'm not sure if creating a window in one thread and running the message pump in another thread is a good idea. I don't even see why you'd need a seperate thread at all (you could just check for messages periodically in your main thread... like say every frame or something).
But that aside... if hVideoWindow is a valid window handle... PeekMessage WILL return true (unless that thread thing is a problem). Are you sure it's not returning true? I'm not exactly sure how printf() fits in here... since this isn't a console program (where are you expecting it to print to?)
As for other potential problems... sometimes when vars are only used within a small area of code (like your StopWindowUpdateThread var in this example), the processor will keep the value in a register and not write it back to RAM. Basically... this will cause problems when you rely on your second thread to react to a change made to the var from your first thread. To avoid this... declare vars that MUST be "thread safe" as volatile:
volatile int StopWindowUpdateThread;
When you write to it in 1 thread... the second thread will be sure to pick up the changes on the next read. However this removes the occasional optimization which will make slower code... so only use volatile where needed (and try to keep the number of inter-thread variables to a minimum)
But like I said... multithreading is not recommended unless necessary.
- edit -
bah... way to make my post useless XD |
HyperLamer
Posts: 2984/8210 |
[edit] Pooh, I can't have one thread create the window and a different one handle the messages? That's a rather stupid restriction. I'll just have to have the update thread create it then. |
neotransotaku
Posts: 1947/4016 |
Originally posted by HyperHacker Yes, I always use them in cases like that. I just didn't know that (*var) meant something different than *var on its own. (Though seeing how it's essentially the same as in assembly, I'm not that surprised. )
Well *var++ and (*var)++ are two different things because of the order of precedence in C. So, when you are unsure what is going to happen first, just use parenthesis to ensure what you want to happen is going to happen. |
HyperLamer
Posts: 2983/8210 |
Yes, I always use them in cases like that. I just didn't know that (*var) meant something different than *var on its own. (Though seeing how it's essentially the same as in assembly, I'm not that surprised. )
Haha, here's some nice statistics for ya: Actual Game Boy CPU: 4,194,304 instructions per second. Emulator core written in VB: 75,000 instructions per second. In C, updating window on each instruction: 900,000 instructions per second. In C, updating window once per second: 11,000,000 instructions per second.
Of course once per second is far too slow to update the window, but hey, this is C. I can make another thread do it. (Now we see one of the major reasons VB's so slow.) |
Parasyte
Posts: 211/514 |
Parantheses are so incredibly useful. I would suggest using them as often as you can. (Though it's kind of silly to go overboard with it.)
Good: if ((foo == bar) && ((tmp = iCoolDude(man))))
Bad: if (foo == bar && tmp = iCoolDude(man)) |
HyperLamer
Posts: 2982/8210 |
Ah, didn't know the brackets made a difference. It's working now, thanks! |
Parasyte
Posts: 210/514 |
void Inc16(BYTE *reg1, BYTE *reg2) { (*reg2)++; if((*reg2) == 0) { (*reg1)++; } }
//... BYTE val1 = 8, val2 = 120;
printf("Inc16(0x%02X, 0x%02X) -> ",val1,val2); Inc16(&val1,&val2); printf("0x%02X, 0x%02X\n",val1,val2); //...
Learning how to use pointers properly is rather important. Read what I said once again, about dereferencing pointers in C. |
HyperLamer
Posts: 2972/8210 |
Well, you write how you want then. The only part I don't like about it is that they have to be capitalized, but a quick '#define true TRUE' and '#define false FALSE' fixes that.
I'm having a bit of a problem with pointers, though. This function in particular: void Inc16(BYTE reg1, BYTE reg2) { printf("Inc16(%X, %X) -> ",reg1,reg2); reg2++; if(reg2 == 0) { reg1++; } printf("%X, %X\n",reg1,reg2); }
What I'm trying to do is pass it two variables, and have it modify the variables themselves. If I call it like "Inc16(regB, regC);" where regB = 0x13 and regC = 0x37, the function does indeed show that it's incremented reg2, but regC is still 0x37. I also tried using pointers, the best I could manage was to increment the pointer itself.
[edit] Bleh, no tabs. Also I know that if doesn't need braces but I'm gonna have to add to it later. |
Dish
Posts: 234/596 |
'true', 'false', 'TRUE', and 'FALSE' are like the stupidest keywords ever.
Why the hell are 0 and 1 so inconvienient to type? =P I mean really... they're easier to type.. .and it's not like they're confusing. |
neotransotaku
Posts: 1946/4016 |
well if you didn't know, your false outside the function error is because false is a C++ keyword, not a C keyword. However, one of the C headers #define BOOL, TRUE, and FALSE so you can use it. |
Parasyte
Posts: 209/514 |
C99 is a C 'standard' that warns you about doing stupid crap that C++ supports. Just like declaring variables within functions. It's bad code. When you think about it, declaring variables within a loop seems like it would be re-declaring on each iteration, right? (That's not what actually happens, but it should be viewed in that manner.) Same goes for declaring within a for loop. The for loop is set up so it goes (assign; test; action) If you try to break the rules with something even as simple as (declare, assign; test; action) the compiler will obviously complain. If you really want to write such nonsense code, though, you can switch to the C++ compiler. Which is generally g++. |
HyperLamer
Posts: 2971/8210 |
I've been using gcc -o file.exe file.c, seems to be doing the job. The only problem I've encountered so far is with the statement 'BOOL StopEmulation = false;' - it raises an error that false is undefined outside a function. If I don't give it a default assignment, it works, but then I don't know what the value will be.
And yay for being able to have a console window with a Win32 program. Sure beats MessageBox() for debug output.
[edit] Pooh, I just have to capitalize it. Where's that resource editor anyway? Do I have to download that entire program? Also what's this 'C99 mode'? I get the error "'for' loop initial declaration used outside C99 mode" when I try to do a for loop with the variable defined in it, like "for(int i=0;i<48;i++)". (This works if i is defined outside the loop, though.) |
Parasyte
Posts: 207/514 |
gcc -o file.exe -c file.c |
HyperLamer
Posts: 2958/8210 |
Oh yeah, I see how that works now. Read the wrong thing. |
Dish
Posts: 233/596 |
.o files are object files. You still need to run the linker to make an executable.
.cpp --> [compiler] --> .o --> [linker] --> .exe |
HyperLamer
Posts: 2957/8210 |
How exaclty do I work this? I compiled it with 'gcc -c' like the doc says, and it didn't output anything except a .o file in \bin, even though the source file was in a completely different folder. |
Parasyte
Posts: 205/514 |
There are many, many, many resource editors available that can be used with MinGW. MinGW does not come bundled with a resource editor, but it does include a resource compiler. (windres.exe) A free resource editor can be downloaded with the LCC-Win32 package - http://www.cs.virginia.edu/~lcc-win32/
There are also at LEAST 11 levels of optimization you can use with MinGW. Perhaps even more. The optimatation levels range from speed-oriented (-O0 to -O9 for slower to faster executables) to size-oriented (-Os to create the smallest file the compiler can manage). Each optimization level will optimize certain portions of code (such as switch statements) in many different ways. You may notice that speed-optimized executables tend to be a bit larger than non optimized builds. That's usually due to unrolling loops and inlining functions to reduce overhead. Anything you write in C will be fast. VERY fast, compared to Visual Basic. But just how fast it will be depends on how it's written. There are a lot of interesting tricks you can use to write fast code. So you should keep that in mind, and not rely on the compiler to do all the work for you. |
HyperLamer
Posts: 2954/8210 |
I'll look into that. Does it come with a resource builder? If not, what should I use for that? Also does it optimize big switch statements into jump tables? I was hoping to write an emulator (big project I know, but I already wrote the essentials in VB, it's just too slow) and I'd like it to be as fast as possible. (Even though it's only emulating a 4Mhz Z80. ) |
This is a long thread. Click here to view it. |
|