Chapter 1: Your First Program
At the end of this Chapter, you will be able to produce your first assembly language program with EasyCode and to describe the key points supporting the script produced.
To enable you to do this, you must be able to:
- a. Create a new project.
- b. Alter project settings.
- c. Change the properties of a window.
- d. Save a project.
- e. Compile your project to both debug and release versions.
- f. Switch to the code view.
- g. Identify the key sections in a typical assembler program.
- h. Describe the function of event handlers.
- j. Explain function of the main event loop.
The idea behind this series of articles
There is no doubt that programming in assembler is somewhat more difficult than doing the same thing using a high-level language like C or VB. The benefits, however, are significant - smaller, faster programs and complete control over what's happening. But the learning curve is steep, indeed, and that's the reason for this article and those I propose to produce over the coming months.
But let's get one thing clear - I am not an expert on assembler. I have a long experience writing programs in high level languages but I am new to low-level work like this. I see that as a strength, however. I know about programming but I can understand the frustration and problems surrounding the acquisition of the skills needed to produce assembler. What I propose to do is to write a bunch of articles which provide you with the survival skills needed to get writing code quickly by helping you avoid the pitfalls normally encountered by beginners. I will not be diving into every last detail of assembler - there are plenty of books for that and the goAsm documentation is fairly complete in that respect, although it's not really geared towards the newcomer. My aim is that, at the end of these chapters, you'll be fit to fight and learn more and more programming knowledge. It's the initial 'hump' that these articles are intended to help you over.
Normally, a programmer would write his code in a text editor (even just Notepad) and then use batch files to compile and link his code into an executable. But there's a lot of work to do, especially since I will be concentrating purely on Windows programming and ignoring console DOS-type applications entirely. But, when we use EasyCode, a great deal of work is done for us, leaving us free to concentrate on those aspects that really matter - getting the code and the appearance of our Windows just right.
Of course, there's nothing to stop you coding the whole program from scratch, and I will cover that angle in a later chapter, but EasyCode will make the initial process of learning considerably easier. However, in programming - as in life, generally - there's no such thing as a free lunch. EasyCode programs will be larger than those created entirely by hand (20K as opposed to 8K, or less, for the examples in this chapter) but this is trifling when offset against the productivity benefits of a visual coding environment.
I am assuming that you have installed the goAsm suite of programs and EasyCode itself (if you haven't, you can read my guide here) so, let's launch EasyCode by double-clicking its icon on the desktop. You should be presented with the - New Project - dialog shown here. Since we are always going to be using the visual environment, select the first option - Visual Win32 executable file (exe) - ensuring that the Common controls checkbox is ticked, as shown. It won't do any harm if you also select RichEdit but, since we will not be discussing this feature until a later chapter, it's a waste of resources right now. Leave it unchecked.
You should now be in the EasyCode environment proper. The illustration shown here points out the main features of the interface. Note, however, that this is not a book on EasyCode. I do not plan on delving into every nook and cranny of the thing - it comes with its own help files. We will examine its features as and when they are of use to us in learning how to program in assembly.
It may surprise you to know that we could actually compile and run what you currently have in the designer window. It won't do very much but it is complete. But before we do that, let's learn some things about projects.
If you had not taken the decision to use EasyCode, you would have written your assembly program with some sort of text editor and then compiled it into an object file with goAsm. In addition, this program of yours might also have used a special icon or bitmaps or dialogs. These are called resources and they have to be gathered together using a resource compiler. In our case, that would be goRC. The compiled resources and our object files are then linked together using goLink to make our executable. A project, then, is all of that - the code and the resources all considered as one entity. As an entity, a project has its own properties, as we are about to see.
As I said, when you start EasyCode, you will see the default project dialog but you can also, at any time, select File|New Project from the menu and the same dialog will appear.
There are a number of items you will need to configure in order to get the results you want:
- Project Type - select this from the Type list. We will always want to create a Windows program, so select the first of these - Visual Executable File (exe).
- Configuration - by now, this should indicate the assembler and whether it is 32- or 64- bits. In the illustration, you can see I have selected my goAsm64 assembler but you need to check it's the one you need.
- Enable the use of - all are selected by defalt except for RichEdit. It won't do any harm to select this but we're not going to be using it for now. Leave it unchecked.
- Location - this is where you will be saving the project and storing the compiled results. You will remember that you created this directory when you installed EasyCode. If this is not showing correctly browse to it.
- Folder - this is the container for the project. It will be stored in the projects folder we've just been talking about. It defaults to Project1 but you can change it to whatever you want.
- Project Name - again, it defaults to Project1. I would suggest helloWorld. This is a bit of a cliche from the early days of programming.
- Startup window name - all Windows programs have an initial window that is used during startup. You many have many windows in your project but you need to name them all and this field allows you to name, in advance, the one that will be used as the "launch pad". Again, it defaults to Project1 and I definitely suggest you change that - winMain is the one I'm going for.
Once you have entered the above configuration items, click the OK button and you'll see your new (blank) project appear in the main EasyCode workspace. You'll see from the EasyCode title bar that the project is called helloWorld and the window's caption bar is set, by default, to its name - winMain. I'd like to change the latter to make the project more "ours".
You should be able to see in the bottom right corner of Easy Code, the Properties window for the currently selected object - in this case, winMain. Scroll down this window until you see the property Text. It should still be set to winMain. Double-click this value and change the text to Hello, World!.
We're good to go.
Before we can compile our program, we need to save it. Do that by selecting the File menu and choosing Save Project. You can also use the buttons in the toolbar, of course.
Compiling the Code
We're now ready to build the project. At the right hand end of the main toolbar, you will see a couple of groups, as shown here. The first button allows you to compile the currently selected window - winMain, in this case. The second, is to compile the project. The third one (Rebuild all) does everything - compile, link, the whole shooting match. That's the one we want, so click it now. Since we have done absolutely no coding, you should have no problems. If you do, it's most likely because EasyCode cannot find the compiler or other components that it needs. Check that you completed the settings properly. But if everything went well, you should see the following message in the debug window.
You can now run your program by selecting the fourth button in the group above - the one that looks like an exclamation mark. Your first program should appear. Yes, it's just a little window with the caption bar reading Hello, World! but it works. Congratulations.
Your new program is a true executable. You can give it proudly to friends and they can run it on their computer. The problem is that we don't know how we did it. This is the time to look at the assembly code generated for us by EasyCode, and we'll do that in a moment or two. But before we move on, you might want to play around with your window. Just for fun. For example, go to the properties window and select the BackColor property. It will display an ellipsis button (one with three dots). Click this button and the current style palette will appear. If you don't want one of the attractive shades of grey, you can select the Palette button at the bottom of that dialog. This will bring up the Windows colour chooser. Select a colour of your choice and the main colour of the window will change accordingly. Play around like this with some of the other attributes of the window, remembering to rebuild your project each time before selecting the Run button. This is how you become familiar with the EasyCode interface - play with it.
OK, settle back down again. Look back at what the Debug window said - helloWorld - Debug and a file size of around 144,000 bytes. Using Windows Explorer take a look at the directory where you saved the project. You should see 3 folders that you never created. These are the Debug, Release and Res sub-directories. It's in the first of these that you will find your program and supporting code. The reason EasyCode put it there lies in the settings you accepted in your project properties dialog - the Symbolic information checkbox, as shown in the illustration.
What this means is that EasyCode arranges with goAsm for extra information to be added into your program to assist any debugger you might be using. Once you have gone through a number of iterations of coding, compiling, running and identifying bugs you would be confident that your application was fit to be released to the world at large. You wouldn't need the symbolic information any longer and you would be expected not to include it any more. So, how do we remove it? Bring up the project properties dialog, uncheck the symbolic information option and then rebuild the project. Now you will see that the code has shrunk to 130,000 bytes. Well, it's a saving, but still not great, is it?
Well, go back again to the project properties dialog but, this time, select the Options "option" under General. The second checkbox from the top says Compress the executable file (Res only). If you check this, EasyCode will use a fantastic exectuable packer called UPX. This will compress your exectuable but it will still run exactly as advertised. You pay a little bit in terms of performance during startup but this is completely un-noticeable for most programs. So, click it, OK the dialog and rebuild. Look at the difference! It now says 38912 bytes. From 144,000! The other thing you should note is that your program is now in the Release folder because you removed the debug code.
Dissecting the Code
It's about time, then, that we looked at the assembly which EasyCode generated for us. Much of this will make little sense to you at this point in the game. But what I want to point out here is not the commands but the structure of assembly language programs. Look at the toolbar inside the design window. At the left hand end of the bar, you will see three buttons, as shown here. The first of these displays your editable helloWorld window. The second displays the code used to create it and the third, which looks like a little toggle switch, allows you to test it out without actually compiling it. Click the View code button and you will see the assembly code which EasyCode generated for you.
There are four main sections to an assembly program:
- Uninitialised Data.
You are required to declare the first three of these in your program (EasyCode does it for you most of the time) but the fourth is created by goAsm automatically if you make use of any uninitialised data. The assembler also names this section for you, so we can forget it for the moment.
Why do we have these sections? They are required by the assembler to allow it to compile the code correctly. Material in the Constants section is taken to be like the speed of light - a value which can be read and used in calculations but cannot be changed. Code in the Data section declares values which can be read and used but also changed, like the rate of income tax, for example, which goes up and down (mostly up) with changing government legislation. Uninitialised data represents variables which have no specific values at the point at which the code is compiled. Values will be inserted into these when the program is running (run-time) and goAsm treats these differently, as we now know. The remaining section is Code. As you can imagine, any text in here is taken as something which is meant to be executable. These are the commands which your CPU will follow.
Naming these sections is simple. goAsm accepts more than one naming convention in this area but EasyCode uses .Const, .Data and .Code. Stick with those. In a later chapter, I will show you how to write assembly programs and add them to your project. These are called Modules. It's important to note, therefore, that when you write your own assembly code, you can have as many of these sections in your program as you want. If you have, say, ten .Data sections scattered throughout your code, goAsm will pick them up and stick them together into one big one before compiling. This also applies to the other section types.
What is Data?
You can see from your code that you have no constants in this program but there is something in the data section. What we need to do now, therefore, is to discuss what we mean by the word data.
Imagine a vast automated warehouse, a bit like the one at the end of the first Indiana Jones movie, which contains row upon row of storage boxes. These ones have no "backs" to them, so whenever anyone "stores" something in them, it pushes out anything that was already in there. Consider this to be like Windows memory. If we want to retrieve some information (the bubbles in my illustration) from this warehouse, we will have to have some way of telling the system where, in this vast warehouse, it was stored in the first place. Obviously, there would be some numbering system for the boxes. In programming, this is done by means of variable names (called labels in the context of assembler). The operating system finds the first available box, sticks the variable name you have provided on to it and then inserts your value (the bubble). To retrieve the bubble, you provide the variable name, the operating system looks up exactly where the box with that name is located and the bubble gets retrieved.
But there's something else. To make the best use of the warehouse space (even although it is enormous), the storage boxes are of different sizes and shapes to best suit the types of things that can be stored there. Thus, if we give the warehouse a postcard to store, we don't want it to place it in a box large enough for the Ark of the Covenant (and vice versa!). So, when we first ask the operating system to assign a storage box for us (known as declaring the variable), we must also tell it what type of thing will be stored in it.
When we are programming in assembler, we're talking about two warehouses - the huge one which is the computer's memory and a smaller one for fast FedEx-type storage - the CPU's registers. I'll begin discussing these in the next chapter but I want to talk about the types right now so that we can understand the code we're currently looking at.
Look at our data section again. We have one data item and it's called MESSAGES. This variable is made up of 4 double words (a double word is made up of 4 bytes and is indicated by the mnemonic DD). The first of these is the Windows message WM_CREATE and the second is the location of the code which must react to that message. The third byte is the value for the Windows message WM_CLOSE and the fourth is the code to handle that message. Note how you can split these over two lines for readability. We could have placed them all on one line, separated by commas:
MESSAGES DD WM_CREATE, OnCreate, WM_CLOSE, OnClose
Note that we must remove the second DD or it will be an illegal line. Try this out for yourself in EasyCode and then re-build. You'll see that it works perfectly well. In fact, goAsm offers you a range of ways in which you can express your data needs. We will not be covering all of them or I would simply be re-creating goAsm's own documentation. I'll deal with what we need when we need it. And that will be in the next chapter. For now, we should understand what the messages are for.
Ever gone on a treasure hunt? You know the sort of thing - you all start off with the family in the car from a given point. You have a set of clues which you can follow in pretty much any order and you must all end up at the place indicated by those clues. Usually, a barbeque ensues - quite often family arguments, divorce and separation, too, particularly if the weather is hot. DOS programming is a bit like that. We start our program at the top and we go down the statements (the clues) one by one but there are points when we can make decisions - will we go this way or that way? In assembler, we implement these decisions with the "Jump" statements, something we will deal with in its own chapter. In concept, however, it's very simple. The point is that there are only so many ways in which you can get from start to finish, as you can see from the illustration, and they were all known to the programmer when he wrote the code. In Windows, however, things are little more fluid.
The Windows operating system is a good example of an event-driven environment but what exactly does that mean? I always try to relate computer concepts to parts of the body if I have to explain it to someone. Your brain sits on top of your spine and responds to stimuli from the outside world. For example, a noise occurs and the sound waves hit your ears, get transformed into electrical impulses and are then fed into the centre of hearing via the auditory nerve. When that happens, the brain processes the sound to see if it can make sense of it and to decide if it needs to do something as a result. The stimulus (the sound, in this case) is the event and the processing done in the brain as a result of the sound is the event handler. The important thing to note is that the centre of sight is not affected by the sound, nor is the area for processing the sense of touch. Only that part of the brain responsible for dealing with sound is immediately affected. So, we must determine which events we are interested in (and there are many to choose from) and to write a handler for each. If we are not interested in a particular event in this particular program, we simply ignore it completely. Dealing with messages appropriately is an important task, for your Windows program would be very boring indeed if it could not react to user handling. And that's the reason for our declaring the messages and their associated handlers right up there in the .Data section of our assembler script. In the next chapter, we'll see how to set up data items to hold other types of information than just messages.
OK, so how does our program deal with these messages? Inside Windows, it's just like a brain being bombarded with all the sensations of the outside world. There are messages flying everywhere. Our program has to be able to "hear" these messages but it needs to react only to those ones for which we have stated an interest and for which we have bits of code (the event handlers) to deal with them. That means we have to examine the first message to arrive, check if it's one of the ones in our list (WM_CREATE, WM_CLOSE) and, if it is, to jump to that bit of the code we have declared is waiting to handle it (OnCreate, OnClose) and then come back to wait for the next message. If it is not one of these messages, our code should simply throw it away and then go and get the next message from the queue waiting for processing. This is called the event processing loop. If you look in the .Code section of the program, you'll see something called winMainProcedure. This is the event processing loop for your main window and performs the actions pretty much as described above. We'll look at the commands (the mnemonics) in more detail in subsequent chapters.
Below the processing loop, you'll see the two event handlers - OnCreate and OnClose. The names as fairly self-explanatory. The first is activated when the operating system notices that the window has been created and the second when the window is being closed down. You can call these handlers anything you want. For example, you could change OnCreate to Startup, for example, as long as you remember to do the same in the .Data section. But these are sensible names and you should name your own handlers along these lines.
You'll notice that the OnCreate handler doesn't really do anything. It just "returns" to the main event loop. But the OnClose handler has some work to do and asks the operating system to close your main window down gracefully:
Invoke EndModal, [hWnd], IDCANCEL
In this chapter, we have discovered how to create and compile the simplest Windows program possible using EasyCode. Using the interface, we have manipulated the properties of both a window and the project itself. Furthermore, we have discussed some of the key concepts behind a typical Windows program, including messages and event handlers, and dissected the code generated by EasyCode to begin developing a better understanding of what's going on behind the scenes. What you should do now is to play around with what you have learned and to experiment a little. Believe me - it's the best way to learn. Catch you next time.
Coming Up ...
In the next chapter, we'll build on what you have just learned. I'm going to show you how to handle the Windows resize event and to manipulate data using pointers. In addition, we'll learn about the registers in our CPU and how they can be used by assembler commands.
You should now attempt the Post Test. Read through the Chapter once again if you are unsure of any area and try not to refer back while attempting the test. Check your answers with those contained in Annex A. Pay particular attention to wrong answers. Read the paragraphs relating to these again and re attempt the problem questions when you feel you have mastered the subject. Do not proceed to the next Chapter until you are satisfied that you are fully conversant with the material contained in this one.
Select your next chapter