Win32 Assembler Tutorial Chapter 0.5
All you need for this tute is an Assembler and Win95/98/NT. Of course you should also know a little about ASM programming. The sources are included in the bonuspack (zip). Since I don't know what else I should write in this introduction, let's start with the contents:
1. Win32 Coding
If you have coded in DPMI, you won't have any problems porting your code to Win32. Win32 programs run in the 32bit Protected Mode with a FLAT memory model. That means:
- Code, Data and Stack are in the same memory area and start at the same base address (yippieh).
- The limit for Code+Data+Stack is 4GB, in theory. At the moment the maximum space is either 2GB (W95/98) or 3GB (NT), but that's enough, isn't it?
- We will NEVER need segment registers any more - they point to the same area in the memory anyway. Always mind this rule: DS is equal to ES, but CS is not equal to DS but a so-called alias (blah, blah, blah).
- You can't modify the code using CS but you can do it using DS, ES or SS (okay, that's not that important either).
- If you try to access an area in memory which you haven't reserved for your program, Windows will kick your program out with a cool error message (nice for detecting bugs in your program).
- The stack can be as large as it or you want (well, until you get a memory overflow). If you know something about descriptors, you also know how that's realized. (And the stack always works - so forget the rest.)
What makes Win32 coding that damned c00l?
- Video memory, Code, Data, File-I/O, Sound output: Everything is located in the same segment - never care about 64k limits any more.
- All registers can be used as 32bit pointers to memory.
- Standardized functions instead of special routines for special hardware.
2. What you need:
- Assembler+Linker. For this tute: NASM+ALINK, TASM+TLINK or MASM+LINK. MASM and TASM are almost identical in their usage, NASM has its own syntax. I attached extra source files for NASM.
- As soon as you code Windows proggies yourself: A Win32 API manual called Platform SDK. It's a necessary encyclopedia for coding in Windows.
3.1 Let's start
First, let's tell our Assembler that we want to write 32bit code. For TASM or MASM, use the following:
If you use NASM, you have to specify this with the segments (see below).
The Windows functions use a lot of constant values which are described in the SDK. They are defined in windows.inc or win32n.inc, so let's put these files into the code:
%include "win32n.inc" (NASM)
include windows.inc (TASM/MASM)
The Windows functions are in DLLs. They are accessed using NEAR calls (not interrupts) and are hence EXTERN. MASM uses different names for these functions than TASM and NASM. The following code for TASM/MASM solves the problem:
ExitProcess equ _ExitProcess@4
This must be done for every function called in MASM.
Now let's insert the function:
extrn ExitProcess : near
NASM needs a special invitiation for this, which has to include the name of the DLL:
IMPORT ExitProcess kernel32.dll
Now it's time to implement the data. Both Code and Data fit into a segment. Still it's better to define these segments separately. Then you only have to specify the relevant segment in order to mix Code and Data in the Code. The linker will split them again:
segment .data USE32 (NASM)
The Data itself is yet missing:
ErrorCode dd 0
Same goes for the Code segment:
segment .code USE32 (NASM)
Here the program starts. The entry point does not necessarily have to be called main or WinMain, in ASM you can also call it _YerMothersFace (great insider movie in my area, btw) if you want. But _beginning is okay, too:
And for NASM:
Wow, now we are right in a working Win32 program! Before you wet your pants with excitement, I'd better stop. But, wait a moment... YES, the function to quit the program must be called, too.
3.2 Win32 Functions
All functions with a constant number of parameters (I know only one exception) are called using the stdcall convention. That is, all parameters are PUSHed onto the stack from left to right and then the function is called. The function clears the stack and returns its value in the EAX register.
In general, the parameters are passed as DWORDs.
The code looks like that:
Finished. Now you only have to implement the program.
4. Compiling and Linking
The following configuration is essential:
- Case-sensitive symbols
- Windowing compatible (-aa or SUBSYSTEM:WINDOWS)
- Win32-EXE file
Look up the exact command line parameters in the zip.
5. Hey, the program doesn't do anything!
That's right. That's why I extended the finished file with a standard Windows dialogue. You could evaluate the return value here and e.g. call the function again when the wrong button has been pressed...
6. Other basic topics
Ressources are data which is linked in the program file. All kinds of data are allowed. This feature is mainly used for icons, menus, and multiple language support (Windows loads the right language automatically!)...
In order to create resources, you need a resource script (*.RC). It describes the resources that are to be linked in your .exe file. You can create resource scripts using a text editor or a special resource editor. It's compiled together with the data to a *.res or *.obj file, which then gets passed to the linker. A resource compiler and an icon are included in the bonus archive, too.
To use Win32 functions, you have to include the required *.lib files in the program. While TASM stores all functions in import32.lib, MASM has a separate LIB for every DLL. That means that if you use MASM, you have to check what DLL contains the function you need.
BTW, Strings are always passed as pointers. They are either terminated by zero or their length is passed as a separate parameter. The registers EBX, ESI, EDI, and EBP remain unchanged, the DF flag has to be cleared before calling a function.
In Windows NT, all string functions are available twice: once for the ANSI and once for the UNICODE charset. Apart from a few exceptions, Win95 only contains the ANSI functions. Therefore the SDK function CreateFile actually exists twice: once as CreateFileA for ANSI and once as CreateFileW for UNICODE. In Assembler, you have to specify the exact type.
6.3. Functions in DLLs for which you have no LIBs
No problem at all: If you have no LIB, you create one yourself.
For TASM, you first use IMPDEF in order to retrieve a *.def file from the DLL and then use the *.def file with IMPLIB to create the *.lib.
MASM users: Use DUMPBIN /exports with the DLL, remove everything in the *.def except the lines with the functions, decorate the functions like the following,
to the head of the file and then have LIB /def do the rest about the *.def.
Attention: LIB.EXE automatically adds an underscore _ to all functions!
If you use NASM, take a glance at the DOCs of the linker.
6.4 How do these LIBs work?
LIBs contain information on what DLLs and what functions are used. Windows loads these and inserts their addresses in the places which are specified in our code by the LIBs. If we call a function, the CALL first goes to the LIB, then there is a JMP with the right address. MASM (and partly also NASM) users can also access the targets of the JMPs using labels, so instead of
you can write
and in this way the execution of the JMP instruction is removed.
7. -> Argh, I'm confused... I'd thought this would be simple.
Well, it's all a matter of getting accustomed. Your first attempts at programming weren't that simple either, were they?
But don't worry: The sourcecode in asmtut0.zip in the bonus pack should be easy to understand, and using the SDK you can experiment a little with it without great problems.
It was a lot of theory. The most important thing for now is how to get the compiler/linker to work and get an overview.
In the next chapter the actual stuff starts: windows, message/input handling,...
T$ - firstname.lastname@example.org