USING ASSEMBLY LANGUAGE WITH VISUAL C/C++ FOR 32-BIT APPLICATIONS
A major difference exists between l6-bit and 32-bit applications. The 32-bit applications are written using Microsoft Visual C/C++ Express for Windows and the l6-bit applications are written using Microsoft C++ for DOS. The main difference is that Visual C/C++ Express for Windows is more common today, but Visual C/C++ Express cannot easily call DOS functions such as INT 2lH. It is suggested that embedded applications that do not require a visual interface be written in l6-bit C or C++, and applications that incorporate Microsoft Windows or Windows CE (available for use on a ROM or Flash1 device for embedded applications) use 32-bit Visual C/C++ Express for Windows.
A 32-bit application is written by using any of the 32-bit registers, and the memory space is essentially limited to 2G bytes for Windows. The free version of Visual C++ Express does not support 64-bit applications written in assembly language at this time. The only difference is that you may not use the DOS function calls; instead use the console getch() or getche() and putch C/C++ language functions available for use with DOS console applications. Embedded applications use direct assembly language instructions to access I/O devices in an embedded system. In the Visual interface, all I/O is handled by the Windows operating system framework.
Console applications in WIN32 run in native mode, which allow assembly language to be included in the program without anything other than the _asm keyword. Windows forms appli- cations are more challenging because they operate in the managed mode, which does not run in the native mode of the microprocessor. Managed applications operate in a pseudo mode that does not generate native code.
An Example that Uses Console I/O to Access the Keyboard and Display
Example 7–7 illustrates a simple console application that uses the console I/O commands to read and write data from the console. To enter this application (assuming Visual Studio .NET 2003 or Visual C++ Express is available), select a WIN32 console application in the new project option (see Figure 7–1). Notice that instead of using the customary stdio.h library, we use the conio.h library in this application. This example program displays any number between 0 and 1000 in all number bases between base 2 and base 16. Notice that the main program is not called main as it was in ear- lier versions of C/C++, but is called _tmain in the current version of Visual C/C++ Express when used with a console application. The argc is the argument count passed to the _tmain procedure from the command line, and the argv[] is an array that contains the command line argument strings.
This example presents a mixture of assembly language and C/C++ language commands. The procedure disps (base,data) does most of the work for this program. It allows any integer (unsigned) to be displayed in any number base, which can be any value between base 2 and base 36. The upper limit occurs because letters of the alphabet only extend to the letter Z. If you need to convert larger number bases, a new scheme for bases over 36 must be developed. Perhaps the lowercase letters a through z can be used for base 37 to 52. Example 7–7 only displays the number that is entered in base 2 through base 16.
Directly Addressing I/O Ports
If a program is written that must access an actual port number, we can use console I/O commands such as the _inp(port) command to input byte data, and the _outp(port,byte_data) command to out- put byte data. When writing software for the personal computer, it is rare to directly address an I/O port, but when software is written for an embedded system, we often directly address an I/O port. An alternate to using the _inp and _outp commands is assembly language, which is more efficient in most cases. Be aware that I/O ports may not be accessed in the Windows environment if you are using Windows NT, Windows 2000, Windows XP, or Windows Vista. The only way to access the I/O ports in these modern operating systems is to develop a kernel driver. At this point in the text it would not be practical to develop such a driver. If you are using Windows 98 or even Windows 95, you can use inp and outp instructions in C/C++ to access the I/O ports directly.
Developing a Visual C++ Application for Windows
This section of the text shows how to use Visual C++ Express to develop a dialog-based application for the Microsoft Foundation Classes library. The Microsoft Foundation Classes (MFC) is a collection of classes that allows us to use the Windows interface without a great deal of difficulty. The MFC has been renamed to the common language runtime (CLR) in Visual C++ Express. The easiest application to learn and develop is a program that uses a forms application as presented here. This basic application type is used to program and test all of the software examples in this textbook written in the Visual C++ Express programming environment.
To create a Visual C++ form-based application, start Visual C++ Express and click on Create Project near the upper left corner of the start screen. (If you do not have the Visual C++ Express program, it is available for free from Microsoft at http://msdn.com.) Download and install the latest version, even of it is a beta version. Figure 7–2 illustrates what is displayed
when the CLR Windows Forms application type is selected under Visual C++ Express Projects. Enter a name for the project and select an appropriate path for the project, then click on OK.
After a few moments the design screen should appear as in Figure 7–3. In the middle section is the form created by this application. To test the application, as it appears, just find the green arrow located somewhere above the form and below the Windows menu bar at the top of the screen and click on it to compile, link, and execute the dialog application. (Answer yes to “Would you like to build the application?”). Click on the X in the title bar to close the application. You have just created and tested your very first Visual C++ Express application.
When looking at the screen shot in Figure 7–3, several items are located in the image that are important to program creation and development. The right margin of the screen contains a Properties window, which contains the properties of the form. The left margin contains Solution Explorer. The tabs, located at the bottom of the Solution Explorer win- dow, allow other views to be displayed such as a class view and so forth in this area. The tabs at the bottom of the Properties window allow the classes, properties, dynamic help, or output to be displayed in this window. Your screen may or may not appear as the one illustrated in Figure 7–3 because it can be modified and probably will be modified as you use the program.
To create a simple application, select the toolbox by clicking on Tools at the top of the screen or by opening the View dropdown menu and selecting Toolbox from the list. Windows is
an events-driven system so an object or a control is needed on the form to initiate an event. The control could be a button or almost any control object selected from the toolbox. Click on the button control near the top of the toolbox, which selects the button. Now move the mouse pointer (do not drag the button) over to the dialog application in the middle of the screen and draw, by left-clicking and resizing the button near the center (see Figure 7–4).
Once the button is placed on the screen, an event handler must be added to the application so that the act of pressing or clicking on the button can be handled. The event handlers are selected by going to the Properties window and clicking on the yellow lightning bolt
. Make sure that the item selected for events is the button1 object. To switch back to the Properties window from the event window, click on the icon just to the left of the lightning bolt. Locate the Click event (should be the first event) and then double-click on the textbox to the right to install the event handler for Click. The view will now switch to the code view and change the location of the button click software.
The software currently in view is the button1_Click function, which is called when the user clicks on the button. This procedure is illustrated in Example 7–8. To test the button, change the software in Example 7–8 to the software in Example 7–9(a). Click on the green arrow to compile, link, and execute the dialog application and click on button1 when it is running. The label on button1 will change to “Wow, Hello” if the button has been made wide enough. This is the first working application, but it does not use any assembly code. Example 7–9(a) uses the Text member property of the button1 object to change the text displayed on button1. A variant that uses a character string object (String^) appears in Example 7–9(b) to display “Wow, Hello World.”
Now that a simple application has been written, we can modify it to illustrate a more complicated application as shown in Figure 7–5. The caption on the button has been changed to the word “Convert.” To return to the design screen, select the tab at the top of the program window that is labeled Form1.h[design]*. When in the Design window, change the caption on the button1 object by clicking the button and then finding the Text property from the properties of button1 in the Properties window. Change the Text property to “Convert.” In Figure 7–5 notice that there are three Label controls and three textbox controls in the illustration below and to the left of the Convert but- ton. These controls are located in the toolbox. Draw them on the screen in approximately the same
place as in Figure 7–5. The labels are changed in properties for each label control. Change the text for each label as indicated.
Our goal in this example is to display any decimal number entered in the Decimal Number box as a number with any radix (number base) as selected by the number entered in the Radix box. The result appears in the Result box when the Convert button is clicked. To switch the view to the program view, click on the Form1.h tab at the top of the Design window.
To obtain the value from an edit control, use Text property to obtain a string version of the number. The problem is that in this case an integer is needed and not a string. The string must be converted to an integer. The Convert class provided in C++ performs conversion from most data types to most data types. In this case, the Convert class member function ToInt32 is used to trans- form the string into an integer. The difficult portion of this example is the conversion from base 10 to any number base. Example 7–10 shows how the Convert class is used to convert the string from the textbox into an integer. This will only function correctly if the number entered into textbox1 is an integer. If a letter or anything else is entered, the program will crash and display an error.
The remainder of the application appears in the button1_Click function of Example 7–12. This program uses the Horner’s algorithm to convert to any radix from 2 through 36. This con- version algorithm divides the number to be converted by the desired radix until the result is zero. After each division, the remainder is saved as a significant digit in the result and the quotient is divided again by the radix. Note that Windows does not use ASCII code, it uses Unicode so the Char (16-bit Unicode) is needed in place of the 8-bit char. Notice how the order of the remainders is placed into the result string by concatenating each digit to the left of the string. A 0x30 is added to each digit to convert to ASCII code in the example.
Horner’s algorithm:
1. Divide the number by the desired radix.
2. Save the remainder and replace the number with the quotient.
3. Repeat steps 1 and 2 until the quotient is zero.
Since this is an assembly language text, the Convert class is not going to be used for good reason; the function is quite large. To see just how large, you can put a breakpoint in the software to the left of a Convert function by clicking on the gray bar to the left of the line of code. A brown circle, a breakpoint, will appear. If you run the program, it will break (stop) at this point and enter the debugging mode so it can be viewed in assembly language form. To display the disassembled code, run the program until it breaks, and then go to the Debug menu and select Windows. In the Windows menu, near the bottom, find “Disassembly.” The registers can also be displayed to step through a program in assembly language.
As you can see, if the program is debugged as described, this is a substantial amount of code that can be significantly reduced if it is rewritten using the inline assembler. Example 7–13 depicts the assembly language version of Convert::ToInt32 function. This function is considerably shorter (if debugged and viewed in the Disassemble window) and executes many times faster than the Convert in Example 7–12. This example points out the ineffi- ciency of the code generated by a high-level language, which may not always be important, but many cases require tight and efficient code, and that can only be written in assembly language. My guess is that as a plateau is reached on processor speed, more things will be written in assembly language. In addition, the new instructions such as MMX and SSE are not available in high-level languages. They require a very good working knowledge of assembly code.
The main problem with using inline assembly code is that the code cannot be placed into a Windows-managed forms application in a managed class. In order to use the assembler, the
function must be placed before the managed class in order for it to compile. Therefore, in the project properties, Common Runtime Support must also be changed to /clr from the default set- ting of /clr:pure so it will compile successfully. (Refer to Figure 7–6 for a screen shot of how to change Common Language Runtime support to /clr.) A managed program runs under the virtual machine called .net and an unmanaged application operated in the native mode of the computer. The inline assembler generates native code for the microprocessor so it must be unmanaged and reside before the managed class in a program.
Example 7–13 illustrates how to replace part of the Horner’s algorithm with assembly code in a function called Adjust. The adjust function tests the number for 9, and if it’s greater than 9, it adds 0x07 and then 0x30 to convert it to ASCII, which is returned. Notice in the example that the assembly code is placed immediately following the using statements at the top of the program. This is where any assembly functions must be placed so a program can function correctly. The application starts in native mode and switches to managed mode when it encounters the managed class. By placing the assembly code before the managed class, it is available to the entire application and it executes in unmanaged or native mode.
At the end of Example 7–13 an alternative version of Adjust appears that is more efficient. The alternative version does not have a return instruction, so how can it function? What does not appear is that any assembly language function returns the value in AL for a byte, AX for a word or short, and EAX for an int. Note that the return value dictates the size of the value returned.
Figure 7–14 shows the modification to the button1_click function so that Adjust is called in place of the code that appears in Example 7–12. The code used to set up the form application that appears between Examples 7–13 and 7–14 is not shown. Notice that the assembly function uses short in place of character. A short is a 16-bit number used in unmanaged mode and a Char is a 16-bit number used in managed mode to represent a Unicode character. Here a cast is used to convert to a Char because without it, the numeric values are displayed instead of ASCII code.