The Assembly Programming Master Book

Now, I'll proceed with an explanation of the need to connect other object modules and libraries at the second stage of translation. First, it is necessary to mention that no matter how many object modules are linked, only one of them is the main module. The general idea is straightforward: this is the module, from which the program execution starts. This is the only difference between it and other modules. Also, agree that the main module will always contain the START label at the starting point of the segment. It is specified after the END directive because the translator must know the program's entry point to specify it in the header of the module to be loaded.

As a rule, all procedures that will be called from modules are placed into INCLUDE modules. Consider such a module in Listing 1.3.

Listing 1.3: The PROG2.ASM module containing PROC1 procedure that will be called from the main module

.586P ; The PROG2.ASM module ; Flat memory model .MODEL FLAT, STDCALL PUBLIC PROC1 _TEXT SEGMENT PROC1 PROC MOV EAX, 1000 RET PROC1 ENDP _TEXT ENDS END

 

First, notice that no label is specified after the END directive. Clearly, this is not the main module and its procedures will be called from other modules.

The second important aspect, to which I'd like to draw your attention, is that the procedure to be called must be declared as PUBLIC . Its name will be saved in the object module; later, it can be linked to calls from other modules.

Thus, you can issue the following command:

ML /coff /c PROG1.ASM

As a result, the PROG2.OBJ module will be created.

Now, carry out a small investigation. View the object module using some simple viewer, such as the built-in viewer of the Far.exe file manager. The following will be easily noticed: Instead of the PROC1 name, you'll see the name _PROCI@0. Now, pay attention, because the characters that I describe here are of special importance! First, the leading underscore (_) reflects the ANSI standard, which requires all public names (i.e., the names available to several modules) to add the underscore automatically. In this case, the assembler will act automatically; therefore, you don't need to worry about this.

The situation with the @0 suffix is somewhat more complicated. First, what does it mean? The digit that follows the @ character specifies the number of bytes that need to be passed to the stack as parameters when the procedure is called. In this case, the assembler thinks that the procedure doesn't require parameters. This was done for the convenience of using the INVOKE directive that will be described later. Now, try to construct the main module, PROG1.ASM.

Listing 1.4: The PROG1.ASM module, calling a procedure from PROG2.ASM

.586P ; Flat memory model .MODEL FLAT, STDCALL ;------------------------------------------ ; Prototype of the external procedure EXTERN PROC1@0:NEAR ; Data segment _DATA SEGMENT _DATA ENDS ; Code segment _TEXT SEGMENT START: CALL PROC1@0 RET ; Exit _TEXT ENDS END START

 

Obviously, the procedure called from another module is declared as EXTERN. Furthermore, instead of the name PROC1 , you must use the name PROCI@0. For the moment, nothing can be done about it. A question related to the NEAR type can arise. In MS-DOS, the NEAR type meant that the procedure call (or unconditional jump) would take place within the same segment. The FAR type meant that procedure (or jump) would be called from another segment. Since Windows implements the so-called flat memory model, the entire memory can be interpreted as one large segment. Thus, the use of the NEAR type is logical.

Issue the following command:

ML /coff /c PROG1.ASM

As a result, you'll get the PROG 1.OBJ object module. Now you can combine these modules to get the PROG1.EXE executable program:

LINK /SUBSYSTEM:WINDOWS PROG1.OBJ PROG2.OBJ

When linking several modules, the main module must be specified first followed by the other modules in an arbitrary order.

Категории