;--------------- ; HELLO32 Copyright 2001 Eric Isaacson, permissions in AWDOC.TXT ;--------------- ; HELLO32 is a template for a 32-bit Windows application, written entirely ; in A386 assembler. It is inspired by (and has most of the same features ; of) Steve Gibson's "Small Is Beautiful" program written for MASM, available ; at http://grc.com/smgassembly.htm . The resource script and system calls ; made are about the same as in Steve's program, but the source code is ; almost completely different. The major differences are: ; ; * I have a system for bringing the resource script into the assembler source, ; which automates the generation of the constants that denote resources. I ; also have brought the code common to most Win32 apps into A386 source ; library modules: source files INCLUDEd automatically by A386 during the ; assembly. You assemble, resource-compile, and link this program by issuing ; the command AW HELLO32 to the DOS prompt. ; ; * My callback control mechanism is table-driven, rather than the long ; sequence of compares and jumps generated by MASM's structured-assembler ; IF and ELSEIF directives. This allows me to place callback code for the ; individual messages into different library modules. ; ; * The callback mechanism also pops the passed parameters into the 386's ; registers, instead of being accessed on the stack via EBP addressing. ; Likewise, my internal routines pass parameters via registers instead of ; being pushed onto the stack. In my opinion this generally makes the code ; smaller, clearer, and easier to write. The EXE for my program is about ; 2K smaller than Steve's. ; ; * I do not have a giant INCLUDE file containing hundreds of Win32 system ; constants. Instead, I prefer to maintain a reference file W32DEFS.TXT with ; thousands of constants, look up a name in that file when I'm writing a ; program, and code it as a numeric value, with its name (and often its ; meaning) provided in the comment field. This reduces the number of lines ; assembled, and also simplifies the process of verifying the correctness ; of the code when single-stepping in a debugger. ; ; * The format of the source file is of a more old-fashioned style, instead ; of the structured-assembler format MASM uses. The MASM code generates ; a lot of wasted JMPs and produces a debugger disassembly that does not ; resemble the coded source (a sort of poor man's C compiler). My older ; format requires more lines for system-call parameter passing: I have a ; sequence of PUSH instructions before the CALL. Again, this makes the ; debugger disassembly much more recognizable, and it also gives me room ; in the source file to provide comments for each parameter. ; ; * My source-file indenting uses spaces instead of tabs. It does not make ; any sense to me to have instruction operands in their own column, so ; there is only a single space between the mnemonic and the operands. This ; usually makes more room on the line for a more descriptive comment. Most ; instructions are indented two spaces, to make the (non-indented) labels ; stick out. There is an extra indentation space for operand PUSHes ; preceding system calls, to make their association clearer, and to ; distinguish them from register-saving stack pushes (which will need ; later balancing POPs). Occasionally, for tighter code, I push a ; system-call operand prematurely; i.e., so far before its system call that ; there are one or more other system calls in-between. I flag this ; trickiness with a second extra indentation space, and a comment that ; names the later call. ; ; ; ; This HELLO32 program demonstrates the following Win32 features: ; ; * We have an icon built into the program. This is implemented in the ; resource script, by importing an icon file with the identifier IDI_ICON. ; Our library code in regclass.8 uses IDI_ICON when registering our program's ; class, so that the icon appears in our title bar and on the Windows task ; bar. The icon is also used in the "About" box, defined later in the ; resource script. ; ; * We have a menu bar with a few working menu items that bring up the standard ; Win32 dialogs for opening and saving files (although once the user has ; selected a file with those dialogs, this program does nothing other than ; put the file name in the title bar). The menu is implemeted via the ; "Menu:" array in this source file, which contains lines seen both by A386 ; to generate a callback control table, and by the resource compiler to ; create the menu itself. Our library's callback code directs menu ; invocations to our A386 library routines menuNew, menuOpen, etc. ; ; * We have some accelerator keys that call up menu items. This is implemented ; by defining the symbol IDA_ACCEL in the resource script, which triggers ; the enabling code in the library file w32main.8 . The resource script ; has the control tables that define the specific keys. ; ; * We have a toolbar with buttons that invoke the menu items. This is ; implemented by defining the symbol ID_TOOLBAR in the resource script, ; which triggers the enabling code the library file cbcreate.8, which in turn ; references the "tbButtons:" table provided in the data area of this ; source file. The tbButton macro used for table entries is defined in ; w32mac.8 . ; ; * We have tooltips: tiny little annotating windows that pop up when the user ; pauses the mouse cursor over toolbar buttons and Windows system buttons, ; before clicking on those buttons. These are implemented by activating ; the default Notify message handler in the MainCbActs table in this source ; file, with ID_TOOLBAR defined. This triggers our cbnotify.8 library ; code, which accesses the tooltip strings declared in resource script. ; We have simplified our code by manually declaring the tooltip string ; identifier constants to be 256 more than the menu-item resource constants ; associated with their buttons. ; ; * We have a status bar that provides a one-line annotation for menu items ; that have been selected but not yet invoked. This is implemented by ; defining the symbol ID_STATUSBAR in the resource script, which triggers ; the activating code in the library file cbcreate.8. We must also activate ; the default MenuSelect message handler in the MainCbActs table in this ; source file. That code, in statbar.8, references the STRINGTABLE resources ; in our script, using the identifiers IDS_SYSMENU and IDM_FILEMENU as ; indexes to identify the strings to display for each menu item. ; ; * We have an "About" window that identifies the program, implemented as a ; menu item that invokes a default library routine that brings up the ; IDD_ABOUT dialog box defined in our resource script. ; ; * Finally, we output "Hello, world!" to the main window display. The code ; for this is in the CB_PAINT routine in this source file, which is called ; by our default library code. ;----------------------------------------------------------------------------- ; ; The following macro call causes the A386 library source file w32mac.8 to be ; included in this spot. References within the following MainCbActs table ; will trigger the inclusion (after this file) of w32main.8, our standard A386 ; startup code for a Win32 GUI application. The code performs initializations, ; creates the main program window, and then enters a "message loop", processing ; events reported to us by the operating system. We define a "callback" ; procedure for the operating system to call when any event happens. One of ; the parameters passed is a "message number", identifying the event. The ; MainCbActs table defines which events we will respond to. Our library ; provides the handlers, named cbCreate, cbDestroy, etc., for many (in this ; simple template, all) events in the table. Those handlers in turn call ; subroutines provided in the application-specific part of the program, which ; is the last section of this source file. WIN32 "Hello32",,400,360,CENTERING,ONE_INSTANCE ; | ; v ; program name ; class name is the same ; main window width ; main window height ; flag triggering a window-centering mechanism ; flag inhibiting multiple program instances MainCbActs: ; table of callback actions and message numbers cb Create, 1 ; wmCreate: the main window is being created cb Destroy, 2 ; wmDestroy: the main window has been destroyed cb Size, 5 ; wmSize: our window has been resized cb Paint, 15 ; wmPaint: the window needs to be repainted cb Close, 16 ; wmClose: the main window is being closed cb Notify, 78 ; wmNotify: an event has occurred over a control cb Command, 273 ; wmCommand: user has double-clicked a menu item cb MenuSelect, 287 ; wmMenuSelect: user has single-clicked a menu item DD 0 ; table terminator ;----------------------------------------------------------------------------- ; ; Following is the resource script for our program. The AW tool will scan ; these lines and feed them to the resource compiler. The uncommented lines ; are also processed by A386: the rc and RCDEF lines define symbols that ; equate to resource-identifier constants. The MENUITEM lines generate ; entries for the control table to direct menu callbacks. These A386 lines ; are macro calls; their macro definitions are contained in the w32mac.8 ; library file. rc IDI_ICON,ICON "hello32.ico" RCDEF ID_STATUSBAR ; this symbol activates our status bar RCDEF ID_TOOLBAR ; this symbol activates the main toolbar Menu: ; A386-only line to declare the Menu control array rc IDM_MENU,MENU DISCARDABLE ;rc BEGIN ;rc POPUP "&File" ;rc BEGIN MENUITEM "&New\tCtrl+N", New MENUITEM "&Open...\tCtrl+O", Open MENUITEM "&Save\tCtrl+S", Save MENUITEM "Save &As...", SaveAs ;rc MENUITEM SEPARATOR MENUITEM "E&xit", Exit ;rc END ;rc POPUP "&Help" ;rc BEGIN MENUITEM "&Help Topics", HelpTopics ;rc MENUITEM SEPARATOR MENUITEM "&About Hello32", About ;rc END ;rc END nMenu EQU ($-Menu)/4 ; A386-only line to declare the size of the Menu array rc IDA_ACCEL,ACCELERATORS ;rc BEGIN ;rc "N", IDM_New, CONTROL, VIRTKEY ;rc "O", IDM_Open, CONTROL, VIRTKEY ;rc "S", IDM_Save, CONTROL, VIRTKEY ;rc END rc IDD_ABOUT,DIALOG DISCARDABLE 0, 0, 160, 60 ;rc STYLE 0x80C80080 // WS_POPUP | WS_CAPTION | WS_SYSMENU | DS_MODALFRAME ;rc CAPTION "About Hello32" ;rc FONT 8, "MS Sans Serif" ;rc BEGIN // id x y w h ;rc ICON IDI_ICON, -1, 8, 8, 21, 20 ;rc CTEXT "Hello32", -1, 30, 8, 100, 8 ;rc CTEXT "written in A386 assembler", -1, 30, 18, 100, 8 ;rc CTEXT "Copyright © 2001 Eric Isaacson",-1, 30, 28, 100, 8 ;rc DEFPUSHBUTTON "OK", 1, 101, 43, 50, 14 ;rc END ;rc STRINGTABLE ;rc BEGIN // for Status Bar rc IDS_SYSMENU,,"Contains commands for manipulating this window." ;rc 0xF000, "Resizes this window." //SC_SIZE ;rc 0xF010, "Moves this window." //SC_MOVE ;rc 0xF020, "Minimizes this window." //SC_MINIMIZE ;rc 0xF030, "Expands this window to fill the screen." //SC_MAXIMIZE ;rc 0xF060, "Closes this window." //SC_CLOSE ;rc 0xF120, "Restores this window to its selected size." //SC_RESTORE ;rc rc IDM_FILEMENU, ,"Contains commands for working with files." ;rc IDM_New ,"Creates a new file." ;rc IDM_Open ,"Opens an existing file." ;rc IDM_Save ,"Saves the file." ;rc IDM_SaveAs ,"Saves the file with a new name." ;rc IDM_Exit ,"Quits this program." ;rc rc IDM_HELPMENU, ,"Contains commands for displaying Help." ;rc IDM_HelpTopics ,"Displays Help Contents and Index (not implemented)." ;rc IDM_About ,"Displays program information and copyright." ;rc IDM_New+256 ,"New" // for ToolTips ;rc IDM_Open+256 ,"Open" ;rc IDM_Save+256 ,"Save" ;rc END ;----------------------------------------------------------------------------- ; ; Following are the main data tables and strings for this program. In the ; FLAT model, these items could just as well have been included in the same ; segment as the program code, since all segment registers address the same ; memory. But I am told that data access is faster if the items are in this ; declared DATA segment. tbButtons: ; button definitions for the main toolbar tbButton 6, IDM_New ; STD_FILENEW, IDM_New tbButton 7, IDM_Open ; STD_FILEOPEN, IDM_Open tbButton 8, IDM_Save ; STD_FILESAVE, IDM_Save osave_Newfn: ; our window title if no file is currently selected DB 'Untitled',0 osave_n EQU ($-osave_Newfn-1) SHL 16 osave_Filter: ; filter strings for the file-selection dialogs DB "Text Files (*.TXT)",0,"*.TXT",0 DB "All Files (*.*)",0,"*.*",0 DB 0 ; second null terminates the list osave_DefExt: DB "txt",0 ;----------------------------------------------------------------------------- ; ; The application-specific code for our program begins here. This code is ; called by the A386 library routines, provided as callbacks to respond to ; the various events reported to us by the operating system. _TEXT SEGMENT FLAT CB_CREATE: ; the main program window has been created JMP menuNew ; set up an "untitled" file state for initial usage EXIT_CAUTION: ; application-specific code for program exit JMP SAVE_CHANGES ; if user tries to exit then prompt him for file-save menuHelpTopics: ; "Help" menu item was selected JMP DEFAULT_CB ; we have no help implemented in this template ; CB_PAINT is called when the OS tells us our window needs to be repainted. ; Our calling library code opens the device context before calling us, and ; closes it after. We are called with EDI set to the device context handle, ; and other registers set to the callback parameters as described in the ; callback.8 library module. We can also access the static structure ps, ; located in w32main.8, to get information on the repainting area. DATA SEGMENT FLAT HELLO: ; our output message DB 'Hello, world!' N_HELLO EQU $-HELLO ; size of the output message DATA ENDS CB_PAINT: PUSHD N_HELLO ; size of display string PUSHD HELLO ; "Hello, world!" display string PUSHD 50,30 ; y and x pixel-coordinates within the window PUSH EDI ; device context handle CALL TextOutA ; output the "Hello, world!" message RET ; OPEN_OFN_FILE and SAVE_OFN_FILE are the application-specific routines for ; opening and saving files. In each case, we are called with EDX pointing ; to the full path name of the file to be opened or saved. Our caller is ; responsible for managing the dialog boxes that determined the name before ; calling us, and for placing the name of the file into the title bar, after ; calling us. We are responsible for actually opening, reading/writing, and ; closing the file. OPEN_OFN_FILE: ; ; place file-open code here (typically open, read, and generate a display) ; OR fFileStatus D,2 ; set "Changed" bit, just to demo the "Save Changes?" box RET SAVE_OFN_FILE: ; ; place file-save code here (typically open, modify/rewrite, and close) ; RET