Upcoming Events
Unite 2010
11/10 - 11/12 @ Montréal, Canada

GDC China
12/5 - 12/7 @ Shanghai, China

Asia Game Show 2010
12/24 - 12/27  

GDC 2011
2/28 - 3/4 @ San Francisco, CA

More events...
Quick Stats
100 people currently visiting GDNet.
2406 articles in the reference section.

Help us fight cancer!
Join SETI Team GDNet!
Link to us Events 4 Gamers
Intel sponsors gamedev.net search:

  Contents

 Introduction
 MASM's HL Syntax
 Getting A Game
 Loop Running

 Connecting to
 Direct Draw

 Our DDraw Library
 Our Bitmap Library
 A Game... Kinda'
 Until Next Time

 Printable version

 


  The Series

 Part 1
 Part 2
 Part 3
 Part 4
 Part 5
 Part 6

 

Getting A Game Loop Running

Now that we all know how to use our assembler, and the features contained in it, lets get a basic game shell up and running.

The first thing we need to do is get setup to enter into WinMain(). You may be wondering why the code doesn't start at WinMain() like in C/C++. The answer is: in C/C++ it doesn't start there either. The code that we will write is generated for you by the compiler, therefore it is completely transparent to you. We will most likely do it differently than the compiler, but the premise will be the same. So here is what we will code to get into the WinMain() function...

Popup : Source Listing 1: Setup

The only thing that may seem a little confusing is why we MOV EAX into a variable at the end of a INVOKE. The reason is all Windows functions, and C functions for that matter, place the return value of a function/procedure in EAX. So we are effectively doing an assignment statement with a function when we move a value from EAX into something. This code above is going to be the same for every Windows application that you write. At least, I have never had need to change it. The code simply sets everything up and ends it when we are finished.

If you follow the code you will see that it calls WinMain() for us. This is where things can get a bit confusing ... so let's have a look at the code first.

Popup : Source Listing 2: WinMain

This is quite a bit of code and is rather daunting at first glance. But, let's examine it a piece at a time. First we enter the function, notice that the local variables ( in this case a WNDCLASS variable ) get placed on the stack without your having to code anything. The code is generated for you ... you can declare local variables like in C. Thus, at the end of the procedure we don't need to tell the assembler how much to pop off of the stack ... it is done for us also. Then, we fill in this structure with various values and variables. Note the use of m2m. This is because in ASM you are not allowed to move a memory value to another memory location w/o placing it in a register, or on the stack first.

Next, we make some calls to register our window class and create a new window. Then, we hide the cursor. You may want the cursor ... but for our game we do not. Now we can show our window and try to initialize our game. We check for an error after calling the Game_Init() procedure. If there was an error the function would not return true and this would cause our program to jump to the shutdown label. It is important that we jump over the main message loop. If we do not, the program will continue executing. Also, make sure that you do not just return out of the code ... there still may be some things that need to be shutdown. It is good practice in ASM, just as in all other languages, to have one entry point and one exit point in each of your procedures -- this makes debugging easier.

Now for the meat of WinMain(): the message loop. For those of you that have never seen a Windows message loop before here is a quick explanation. Windows maintains a queue of messages that the application receives -- whether from other applications, user generated, or internal. In order to do ANYTHING an application must process messages. These tell you that a key has been pressed, the mouse button clicked, or the user wants to exit your program. If this were a normal program, and not a high performance game, we would use GetMessage() to retrieve a message from the queue and act upon it.

The problem however is, if there are no messages, the function WAITS until it receives one. This is totally unacceptable for a game. We need to be constantly performing our loop, no matter what messages we receive. So, one way around this, is to use PeekMessage() instead. PeekMessage() will return zero if it has no messages, otherwise it will grab it off of the queue.

What this means is, if we have a message, it will get translated and dispatched to our callback function. Furthermore, if we do not, then the main game loop will be called instead. Now here is the trick, by arranging the code just right, the main game loop will be called -- even if we process a message. If we did not do this, then Windows could process 1,000's of messages while our game loop wouldn't execute once!

Finally, when a quit message is passed to the queue we will jump out of our loop and execute the shutdown code. And that ... is the basic game loop.





Next : Connecting to Direct Draw