Jump to content

  • Log In with Google      Sign In   
  • Create Account


Like
0Likes
Dislike

Win32 Assembly Part 1

By Chris Hobbs | Published Oct 14 1999 06:19 AM in General Programming

If you find this article contains errors or problems rendering it unreadable (missing images or files, mangled code, improper text formatting, etc) please contact the editor so corrections can be made. Thank you for helping us improve this resource

This is the article that I am sure all of you have been waiting ever so patiently for ... a complete series on the development of a game, in pure Assembly Language of all things. I know all of you are as excited about this article as I am, so I will try and keep this introduction brief. Instead of laying every single thing out to you in black and white, I will try and answer a few questions that are asked most often, and the details will appear as we progress ( I am making this up as I go you know ).

What is this article about?

This article is actually part of a seven article series on the development of a complete game, SPACE-TRIS, in 100% assembly language. We will be covering any aspect of game development that I can think of ... from design and code framework to graphics and sound.

Who is this article for?

This series is meant for anybody who wishes to learn something that they may not have known before. Since the game is a relatively simple Tetris clone it is great for the beginner. Also, given the fact that not many people are even aware that it is completely possible to write for Windows in assembly language, it is great for the more advanced developers out there too.

What do I need?

The only requirement is the ability to read. However, if you wish to assemble the source code, or participate in the challenge at the end of the article series, you need a copy of MASM 6.12+. You can download a package called MASM32 that will have everything that you need, and then some. Here is the link: http://www.pbq.com.au/home/hutch/.


Why Assembly Language?

Many of you are probably wondering why anybody in their right mind would write in pure assembly language. Especially in the present, when optimizing compilers are the "in" thing and everybody knows that VC++ is bug free, right? Okay I think I answered that argument ... but what about assembly language being hard to read, non-portable, and extremely difficult to learn. In the days of DOS these arguments were very valid ones. In Windows though, they are simply myths left over from the good old days of DOS. I might as well approach these one at a time.

First, assembly language is hard to read. But for that matter so is C, or even VB. The readability results from the skill of the programmer and his/her thoroughness at commenting the code. This is especially true of C++. Which is easier to read: Assembly code which progress one step at a time ( e.g. move variable into a register, move a different variable into another register, multiply ), or C++ code which can go through multiple layers of Virtual Functions that were inherited? No matter what language you are in, commenting is essential ... use it and you won't have any troubles reading source code. Remember just because you know what it means doesn't mean that everybody else does also.

Second, the issue of portability. Granted assembly language is not portable to other platforms. There is a way around this, which allows you to write for any x86 platform, but that is way beyond the scope of this article series. A good 80-90% of the games written are for Windows. This means that the majority of your code is specific to DirectX or the Win32 API, therefore ... you won't be porting without a huge amount of work anyway. So, if you want a truly portable game, then don't bother with writing for DirectX at all ... go get a multi-platform development library.

Finally, there comes the issue of Assembly Language being extremely difficult to learn. Although there is no real way for me to prove to you that it is easy, I can offer you the basics, in a few pages, which have helped many people, who never saw a line of assembly language before, learn it. Writing Windows assembly code, especially with MASM, is very easy. It is almost like writing some C code. Give it a chance and I am certain that you won't be disappointed.


Win32 ASM Basics

If you are already familiar with assembly language in the windows platform, you may want to skip this section. For those of you who aren't, this may be a bit boring, but hang with it ... this is very important stuff. For this discussion I will presume that you are at least familiar with the x86 architecture.

The first thing you need to understand are the instructions. There aren't very many that you will be using often so I will simply cover the ones that we care about.

MOV

This instruction moves a value from one location to another. You can only move from a register to register, memory to register, or register to memory. You can not move from a memory location to another memory location.

Example:

MOV    EAX, 30
MOV    EBX, EAX
MOV    my_var1, EAX
MOV    DWORD PTR my_var, EAX

The first example moves the value 30 into the EAX register. The second example moves the value in EAX into the EBX register. The third example moves the value of EAX into the variable my_var1. The fourth example moves the value of EAX into the ADDRESS pointed to by my_var, we need to use the DWORD specifier so that the assembler knows how much memory to move -- 1 byte ( BYTE ), 2 bytes ( WORD ), or 4 bytes ( DWORD ).

ADD & SUB

These two instructions perform addition and subtraction.

Example:

ADD    EAX, 30
SUB    EBX, EAX

The examples simply add 30 to the EAX register and then subtract that value from the EBX register.

MUL & DIV

These two instructions perform multiplication and division.

Example:

MOV    EAX, 10
MOV    ECX, 30
MUL    ECX
XOR    EDX, EDX
MOV    ECX, 10
DIV    ECX

The examples above first load EAX with 10 and ECX with 30. EAX is always the default multiplicand, and you get to select the other multiplier. When performing a multiplication the answer is in EAX:EDX. It only goes into EDX if the value is larger than the EAX register. When performing a divide you must first clear the EDX register that is what the XOR instruction does by performing an Exclusive OR on itself. After the divide, the answer is in EAX, with the remainder in EDX, if any exists.

Of course, there are many more instructions, but those should be enough to get you started. We will probably only be using a few others, but they fairly easy to figure out once you have seen the main ones. Now we need to deal with the calling convention. We will be using the Standard Call calling convention since that is what the Win32 API uses. What this means is that we push parameters onto the stack in right to left order, but we aren't responsible for the clearing the stack afterwards. Everything will be completely transparent to you however as we will be using the pseudo-op INVOKE to make our calls.

Next, there is the issue of calling Windows functions. In order to use invoke, you must have a function prototype. There is a program that comes with MASM32 which builds include files ( equivalent to header files in C ) out of the VC++ libraries. Then, you include the needed libraries in your code and you are free to make calls as you wish. You do have to build a special include file by hand for access to Win32 structures and constants. However, this too is included in the MASM32 package, and I have even put together a special one for game programmers which will be included in the source code and built upon as needed.

The final thing that I need to inform you about is the high level syntax that MASM provides. These are constructs that allow you to create If-Then-Else and For loops in assembly with C-like expressions. They are easiest to show once we have some code to put in, therefore you won't see them until next time. But, they are there ... and they make life 100000 times easier than without them.

That is really about all you need to know. The rest will come together as we take a look at the source code and such. So, now that we have that out of the way, we can work on designing the game and creating a code framework for it.


The Design Document

Time for something a lot more fun ... designing the game. This is a process that is often neglected simply because people want to start writing code as soon as they have an idea. Although this approach can work for some people, it often does not. Or, if it does work, you end up re-coding a good portion of your game because of a simple oversight. So, we will cover exactly how to create a design document that you will be able to stick to, and will end up helping you with your game.

First, you need to have an idea of what you want the game to be, and how you want the game play. In our case this is a simple Tetris clone so there isn't too much we need to cover in the way of game play and such. In many cases though, you will need to describe the game play as thoroughly as possible. This will help you see if your ideas are feasible, or if you are neglecting something.

The easy part is finished, now we need to come up with as many details as we possibly can. Are we going to have a scoring system? Are we going to have load/save game options? How many levels are there? What happens at the end of a level? Is there an introductory screen? These are the kinds of questions that you should be asking yourself as you work on the design of the game. Another thing that may help you is to story board or flow chart the game on a piece of paper or your computer. This will allow you to see how the game is going to progress at each point.

Once you have all of the details complete, it is time to start sketching the levels out. How do you want the screens to appear? What will the interfaces look like? This doesn't have to be precise just yet ... but it should give you a realistic idea of what the final versions will look like. I tend to break out my calculator and estimate positions at this point also. I have actually ran out of room while creating the menu screen before. This was my own fault for not calculating the largest size my text could be and it took a few hours to re-do everything. Don't make the same mistake, plan ahead.

The final stage is just sort of a clean-up phase. I like to go back and make sure that everything is the way I want it to be. Take a few days break from your game beforehand. This will give you a fresh viewpoint when you come back to it later on. Often times, you will stare at the document for so long that something extraordinarily simple will be glanced over and not included in your plan -- for instance, how many points everything is worth and the maximum number of points they can get ( Not that I have ever found out halfway through the game that the player could obtain more points than the maximum score allowed for, or anything like that ).

Whether you choose to use the process I have outlined, or one of your own making, it is imperative that you complete this step. I have never been one for wasted effort -- I do it right the first time if possible, and learn from my mistakes, as well as the mistakes of others. If this weren't necessary I wouldn't do it. So, do yourself a favor and complete a design document no matter how simple you think your game is.


Code Framework

The final preparation step is something that I like to call code framework. This is where you lay out your blank source code modules and fill them with comments detailing the routines that will go into them and the basic idea behind how they operate. If you think you are perfect and have gotten every detail in your design document then you can probably skip this step. But, for those of you like me, who are cautious, then give this phase a whirl. It helps you see how all of the pieces will fit together and more importantly if something has been neglected or included that shouldn't have been.

Here is an example of the framework that I am speaking about from SPACE-TRIS. You can see that nothing much goes into it ... just an overview of the module more or less.

;###########################################################################
;###########################################################################
; ABOUT SPACE-TRIS:
;
;    This is the main portion of code. It has WinMain and performs all
;    of the management for the game.
;
;   	 - WinMain()
;   	 - WndProc()
;   	 - Main_Loop()
;   	 - Game_Init()
;   	 - Game_Main()
;   	 - Game_Shutdown()
;
;
;###########################################################################
;###########################################################################


;###########################################################################
;###########################################################################
; THE COMPILER OPTIONS
;###########################################################################
;###########################################################################

    .386
    .MODEL flat, stdcall
    OPTION CASEMAP :none   ; case sensitive

;###########################################################################
;###########################################################################
; THE INCLUDES SECTION
;###########################################################################
;###########################################################################
    
    ;==================================================
    ; This is the include file for the Windows structs,
    ; unions, and constants
    ;==================================================
    INCLUDE Includes\Windows.inc

    ;================================================
    ; These are the Include files for Window calls
    ;================================================
    INCLUDE \masm32\include\comctl32.inc
    INCLUDE \masm32\include\comdlg32.inc
    INCLUDE \masm32\include\shell32.inc
    INCLUDE \masm32\include\user32.inc
    INCLUDE \masm32\include\kernel32.inc
    INCLUDE \masm32\include\gdi32.inc

    ;====================================
    ; The Direct Draw include file
    ;====================================
    INCLUDE Includes\DDraw.inc
    
    ;===============================================
    ; The Lib's for those included files
    ;================================================
    INCLUDELIB \masm32\lib\comctl32.lib
    INCLUDELIB \masm32\lib\comdlg32.lib
    INCLUDELIB \masm32\lib\shell32.lib
    INCLUDELIB \masm32\lib\gdi32.lib
    INCLUDELIB \masm32\lib\user32.lib
    INCLUDELIB \masm32\lib\kernel32.lib

    ;=================================================
    ; Include the file that has our prototypes
    ;=================================================
    INCLUDE Protos.inc

;###########################################################################
;###########################################################################
; LOCAL MACROS
;###########################################################################
;###########################################################################

    szText MACRO Name, Text:VARARG
   	 LOCAL lbl
   	 JMP lbl
   	 Name DB Text,0
   	 lbl:
    ENDM

    m2m MACRO M1, M2
   	 PUSH   	 M2
   	 POP   	 M1
    ENDM

    return MACRO arg
   	 MOV    EAX, arg
   		 RET
    ENDM

    RGB MACRO red, green, blue
   	 XOR    EAX,EAX
   	 MOV    AH,blue
   	 SHL    EAX,8
   	 MOV    AH,green
   	 MOV    AL,red
    ENDM

    hWrite MACRO handle, buffer, size
   	 MOV    EDI, handle
   	 ADD    EDI, Dest_index
   	 MOV    ECX, 0
   	 MOV    CX, size
   	 ADD    Dest_index, ECX
   	 MOV    ESI, buffer
   	 movsb
    ENDM

    hRead MACRO handle, buffer, size
   	 MOV    EDI, handle
   	 ADD    EDI, Spot
   	 MOV    ECX, 0
   	 MOV    CX, size
   	 ADD    Spot, ECX
   	 MOV    ESI, buffer
   	 movsb
    ENDM

;#################################################################################
;#################################################################################
; Variables we want to use in other modules
;#################################################################################
;#################################################################################


;#################################################################################
;#################################################################################
; External variables
;#################################################################################
;#################################################################################


;#################################################################################
;#################################################################################
; BEGIN INITIALIZED DATA
;#################################################################################
;#################################################################################

	.DATA

;#################################################################################
;#################################################################################
; BEGIN CONSTANTS
;#################################################################################
;#################################################################################


;#################################################################################
;#################################################################################
; BEGIN EQUATES
;#################################################################################
;#################################################################################

    ;=================
    ;Utility Equates
    ;=================
FALSE   	 EQU    0
TRUE   	 EQU    1


;#################################################################################
;#################################################################################
; BEGIN THE CODE SECTION
;#################################################################################
;#################################################################################

  .CODE

start:

;########################################################################
; WinMain Function
;########################################################################


;########################################################################
; End of WinMain Procedure
;########################################################################



;########################################################################
; Main Window Callback Procedure -- WndProc
;########################################################################


;########################################################################
; End of Main Windows Callback Procedure
;########################################################################




;========================================================================
;========================================================================
; THE GAME PROCEDURES
;========================================================================
;========================================================================


;########################################################################
; Game_Init Procedure
;########################################################################


;########################################################################
; END Game_Init
;########################################################################



;########################################################################
; Game_Main Procedure
;########################################################################


;########################################################################
; END Game_Main
;########################################################################



;########################################################################
; Game_Shutdown Procedure
;########################################################################


;########################################################################
; END Game_Shutdown
;########################################################################

;######################################
; THIS IS THE END OF THE PROGRAM CODE #
;######################################
END start


Until Next Time

Well, this is the end of the first article. The good news is all of the dry boring stuff is behind us. The bad news is you won't get to see any code until I complete the next article. In the meantime I would suggest brushing up on your assembly language and maybe searching on the Internet for some references on Win32 assembly language. You can find links to a lot of Win32 ASM resources at my website: Http://www.fastsoftware.com.

Researching more information isn't a must ... but for those of you that still think this might be difficult, I would suggest taking the time to do so. It isn't like you will be hindered by learning more. You may find another resource that helps you learn this stuff and that is ALWAYS a good thing.

In the next article we will get a skeleton version of SPACE-TRIS up and running along with coding our Direct Draw library functions. The goal is to get a bitmap up onto the screen and I think we can accomplish it next time. If everything goes as planned, you should see the work starting to pay off in a loading game screen. I know it doesn't sound like much ... but appreciate how slowly we are progressing before we get further along. Because once we have the basics down, we are going to pull out all of the stops and then you will be thankful we took the extra time to cover this stuff.

So young grasshoppers, until next time ... happy coding.





Comments

Note: Please offer only positive, constructive comments - we are looking to promote a positive atmosphere where collaboration is valued above all else.




PARTNERS