Chapter 4: 16-Bit Programming Overview

History deserves to be learned! Although this chapter presents information of only historical interest, and although those who remember Windows 3.1 are few in number, I won't delete it even from future editions of my book. This chapter contains material that is valuable from a historical point of view. As a rule, when providing basic information about Windows programming in Assembly language, most authors start by describing 16-bit programming. In my opinion, it is obsolete and can no longer serve as an introduction to Windows programming. [i] Since it is extremely unlikely that you'll need to develop a 16-bit application, I'll limit myself to providing a simple example. If you are interested in this topic, I recommend that you read other books [1, 7] in addition to this chapter.

The Idea of 16-Bit Windows Programming

Let me start by considering the differences between 16-bit and 32-bit Assembly programming. You'll discover that programming has become considerably easier over time.

  • In contrast to the Windows 9x model, the memory model in Windows 3.1x was segmented. Accordingly, in the program, the data, the stack, and the code relate to different segments. [ii] As with MS-DOS, the DS register pointed to the data segment and CS pointed to the code segment.

  • In the 16-bit model, addressing is accomplished according to the segment/offset method. Accordingly, the address is defined by two 16-bit components . For example, to load the address of variable M into the stack, you'll need at least two commands: PUSH DS/PUSH OFFSET M. At the same time, the segment address must be loaded into the cell with the address higher than that of the offset (the stack grows in the direction of lower addresses).

  • For developing 16-bit applications, MASM 6.1 is the most convenient tool. Because this chapter provides only a historical overview, I won't concentrate attention on the development of 16-bit applications using Turbo Assembler. For linking the applications, you will use the LIBW.LIB library supplied with MASM 6.1. A feature of API calls, in this case, is the inverse order of loading parameters into the stack, as compared to all examples considered previously. In this chapter, and only here, I will use the following principle: from left to rightfrom top to bottom.

  • After starting the program, it is necessary to initialize startup. For this purpose, it is necessary to call the following API functions: INITTASK , WAITEVENT , and INITAPP .

    • INITTASK initializes registers, the command line, and memory. This function doesn't require any parameters and is the first one to be called. Return values of registers are as follows : AX = 1 (0 = error), CX = the stack size , DI = the unique number for the current task, DX = NCMDSHOW (described later), ES = the segment address (selector) PSP, ES:BX = the command line address, and SI = the unique number of the previous copy of the same application started earlier. In Windows 3.1, when starting an application, only a part of the registers is loaded into the memory each time and a part of the segments is the common resource. This was the way to economize on the memory. The developers of Windows 95 abandoned this approach. Every task running in the system is isolated and independent. In Windows 95, SI is always zero. Furthermore, this procedure fills the reserved header of the data segment.

    • WAITEVENT checks whether there are events intended for the specified application. If there is such an event in the queue, it is removed from the queue. The call looks as follows:

       PUSH AX ; AX --- Application number           ; If it is set to 0, this is the current application   CALL WAITEVENT 
    • INITAPP initializes the event queue for the current application. The call looks as follows:

       PUSH DI ; Unique number of the task   CALL INITAPP 

      In case of an error, this function returns 0; otherwise , it returns a nonzero value.

  • Some parameters of the API functions of a 16-bit application have a size of 2 bytes. In particular, the WPARAM and HWND parameters of the window procedure are 2 bytes long. When you need to operate with 4-byte parameters, they must be processed in two passes .

  • Consider the last difference: the data segment in the program start must contain the reserved 16-byte block.

  • An interesting fact is that in a 16-bit application, you can use normal MS-DOS interrupts by means of INT 21H. You only have to bear in mind which functions make sense with Windows.

An Example of 16-bit Windows Application

To illustrate the idea explained in the preceding section, I'll provide a simple program. Program translation is carried out using MASM 6.1:

 ML /c prog.asm LINK prog, prog,  libw 

The question about the DEF file can be ignored.

Listing 4.1: An example of a 16-bit application
image from book
 .286 .DOSSEG ; Segment order according to the agreement with Microsoft DGROUP GROUP DATA, STA       ASSUME CS:CODE, DS:DGROUP ; Prototypes of external procedures EXTRN INITTASK:FAR EXTRN INITAPP:FAR EXTRN WAITEVENT:FAR EXTRN DOS3CALL:FAR EXTRN REGISTERCLASS:FAR EXTRN LOADCURSOR:FAR EXTRN GETSTOCKOBJECT:FAR EXTRN GETMESSAGE:FAR EXTRN TRANSLATEMESSAGE:FAR EXTRN DISPATCHMESSAGE:FAR EXTRN CREATEWINDOW:FAR EXTRN CREATEWINDOWEX:FAR EXTRN UPDATEWINDOW:FAR EXTRN SHOWWINDOW:FAR EXTRN POSTQUITMESSAGE:FAR EXTRN DEFWINDOWPROC:FAR ; Templates WNDCL          STRUCT    STYLE          DW 0 ; Window class style    LPFNWNDPROC    DD 0 ; Pointer to the handler procedure    CBCLSEXTRA     DW 0    CBWNDEXTRA     DW 0    HINSTANCE      DW 0    HICON          DW 0    HCURSOR        DW 0    HBRBACKGROUND  DW 0    LPSZMENUNAME   DD 0 ; Pointer to the string    LPSZCLASSNAME  DD 0 ; Pointer to the string WNDCL          ENDS ; MESSA          STRUCT    HWND           DW ?    MESSAGE        DW ?    WPARAM         DW ?    LPARAM         DD ?    TIME           DW ?    X              DW ?    Y              DW ? MESSA          ENDS ; Stack segment STA SEGMENT STACK 'STACK'    DW 2000 DUP(?) STA ENDS ; Data segment DATA SEGMENT WORD 'DATA' ; The 16 bits at the starting point provide the reserve ; required for a 16-bit application to correctly ; operate in the Windows environment     DWORD 0     WORD  5     WORD  5 DUP (0)     HPREV   DW ?     HINST   DW ?     LPSZCMD DD ?     CMDSHOW DW ? ; Structure for creating a class     WNDCLASS WNDCL <> ; Message structure     MSG      MESSA <> ; Window class name     CLASS_NAME DB 'HELLO', 0 ; Window header     APP_NAME   DB '16-bit program', 0 ; Cursor type    CURSOR      EQU 00007F00H ; Window style    STYLE       EQU 000CF0000H ; Window parameters    XSTART      DW 100    YSTART      DW 100    DXCLIENT    DW 300    DYCLIENT    DW 200 DATA ENDS ; Code segment CODE SEGMENT WORD 'CODE' _BEGIN: ; I. Initial code    CALL        INITTASK               ; Initialize the task    OR          AX, AX                 ; CX --- Stack boundaries    JZ          _ERR    MOV         HPREV, SI              ; Number of the previous application    MOV         HINST, DI              ; Number for the new task    MOV         WORD PTR LPSZCMD, BX   ; ES:BX - Address    MOV         WORD PTR LPSZCMD+2, ES ; of the command line    MOV         CMDSHOW DX             ; Screen parameter    PUSH        0                      ; Current task    CALL        WAITEVENT              ; Clear the event queue    PUSH        HINST    CALL        INITAPP                ; Initialize applications    OR          AX, AX    JZ          _ERR    CALL        MAIN                   ; Start the main procedure _TO_OS:    MOV         AH, 4CH    INT         21H                    ; Exit the program _ERR: ; Here, it is possible to place the error message    JMP         SHORT _TO_OS ; Main procedure ;***************************************************** MAIN PROC ; II. Registering the window class ; Window style NULL --- Standard window    MOV         WNDCLASS.STYLE, 0 ; Handler procedure    LEA         BX, WNDPROC    MOV         WORD PTR WNDCLASS.LPFNWNDPROC, BX    MOV         BX, CS    MOV         WORD PTR WNDCLASS.LPFNWNDPROC+2, BX ;----------------------------------------- ; Reserved bytes terminating the structure being reserved    MOV         WNDCLASS.CBCLSEXTRA, 0 ; Reserved bytes terminating the structure for each window    MOV         WNDCLASS.CBWNDEXTRA, 0 ; The window icon is missing    MOV         WNDCLASS.HICON, 0 ; The number of the task being started    MOV         AX, HINST    MOV         WNDCLASS.HINSTANCE, AX ; Get the standard cursor number    PUSH        0    PUSH        DS    PUSH        CURSOR    CALL        LOADCURSOR    MOV         WNDCLASS.HCURSOR, AX ; Get the number of the standard object    PUSH        0 ; WHITE_BRUSH    CALL        GETSTOCKOBJECT ; Background color    MOV         WNDCLASS.HBRBACKGROUND, AX ; Menu name from the resource file (NULL if missing)    MOV         WORD PTR WNDCLASS.LPSZMENUNAME, 0    MOV         WORD PTR WNDCLASS.LPSZMENUNAME+2, 0 ; Pointer to the string containing the class name    LEA         BX, CLASS_NAME    MOV         WORD PTR WNDCLASS.LPSZCLASSNAME, BX    MOV         WORD PTR WNDCLASS.LPSZCLASSNAME+2, DS ; Call the registration procedure    PUSH        DS; Pointer to    LEA         DI, WNDCLASS    PUSH        DI; WNDCLASS structures    CALL        REGISTERCLASS    CMP         AX, 0    JNZ         _OK1 ; Registration error    RET         ; Error in the course of registration _OK1 ; III. Creating the window ; Address of the window class name string    PUSH        DS    LEA         BX, CLAS_NAME    PUSH        BX ; Address of the window header string    PUSH        DS    LEA         BX, APP_NAME    PUSH        BX ; Window style    MOV         BX, HIGHWORD STYLE    PUSH        BX    MOV         BX, LOWWORD STYLE    PUSH        BX ; X coordinate of the window's top left corner    PUSH        XSTART ; Y coordinate of the window's top left corner    PUSH        YSTART ; Window width    PUSH        DXCLIENT ; Window height    PUSH        DYCLIENT ; Number of the parent window    PUSH        0 ; Number (identifier) of the window menu    PUSH        0 ; NULL ; Task number    PUSH        HINST ; Address of the block of window parameters (none)    PUSH        0    PUSH        0    CALL        CREATEWINDOW    CMP         AX, 0    JNZ         NO_NULL ; Window creation error    RET         ; Error when creating a window ; Set the window visibility state (window or icon) ; according to the CMDSHOW parameter and its display NO_NULL:    MOV         SI, AX    PUSH        SI    PUSH        CMDSHOW    CALL        SHOWWINDOW ; Send the command to redraw the window area ; (WM_PAINT command) ; The message is sent to the window directly    PUSH        SI    CALL        UPDATEWINDOW ; IV. Waiting loop    LOOP1: ; Retrieve the message from the queue    PUSH        DS    LEA         BX, MSG   ; Pointer to the message structure    PUSH        BX    PUSH        0    PUSH        0    PUSH        0    CALL        GETMESSAGE ; Check whether the "exit" message has been received    CMP         AX, 0    JZ          NO_LOOP1 ; Convert all received messages to the ANSI standard    PUSH        DS    LEA         BX, MSG    PUSH        BX    CALL        TRANSLATEMESSAGE ; Instruct Windows to pass this message ; to the appropriate window    PUSH        DS    LEA         BX, MSG    PUSH        BX    CALL        DISPATCHMESSAGE ; Close the message-processing loop    JMP         SHORT LOOP1 NO_LOOP1:    RET MAIN ENDP ; Procedure for the specified window class ; Windows passes the following parameters to this procedure: ; HWND - Window descriptor, WORD type ; MES --- Message number, WORD type ; WPARAM - Additional information, WORD type ; LPARAM --- Additional information, DWORD type WNDPROC PROC    PUSH        BP    MOV         BP, SP    MOV         AX, [BP+0CH] ; MES - Message number    CMP         AX, 2        ; Is this the WM_DESTROY message?    JNZ         NEXT ; Pass the message about application termination ; This message will be received by the message-handling ; loop, and the application will thus be terminated    PUSH        0    CALL        POSTQUITMESSAGE    JMP         _QUIT NEXT: ; Pass the message further to Windows ; This means that everything that wasn't processed ; by the procedure is provided to Windows for processing    PUSH        [BP+0EH] ; HWND    PUSH        [BP+0CH] ; MES - Message number    PUSH        [BP+0AH] ; WPARAM    PUSH        [BP+8]   ; HIGHWORD LPARAM    PUSH        [BP+6]   ; LOWWORD LPARAM    CALL        DEFWINDOWPROC ;****************************************************** _QUIT:    POP         BP ; The call to the window procedure is always FAR; therefore, you use RETF    RETF        10  ; Clear the stack from the parameters WNDPROC ENDP CODE    ENDS END     _BEGIN 
image from book
 

That's all. The compiled and linked program will start both in older versions of the operating system and in all newer versions of operating systems from the Windows family. I'd only like to draw your attention to the .DOSSEG directive. This directive specifies a certain order of segments in the EXE file. This order is as follows: The starting segment is the segment of the CODE class, it is followed by segments of classes other than CODE that do not belong to the group of segments ( GROUP ), then there are segments grouped using the GROUP directive. At the same time, the segments not from the BSS and STACK classes go first, then come the segments of the BSS class (if any), and the last segment is the segment of the STACK class.

For Assembly language, Windows 95 was a significant advance. Obviously, Assembly language programming became considerably easier.

Now, say good-bye to 16-bit programming. You'll never encounter it again in this book.

[i] As you'll see for yourself, 16-bit programming is slightly more difficult than 32-bit programming.

[ii] I hope that you remember that in 32-bit model division of data and code into segments is a matter of convention.



The Assembly Programming Master Book
The Assembly Programming Master Book
ISBN: 8170088178
EAN: 2147483647
Year: 2004
Pages: 140
Authors: Vlad Pirogov

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net