28 DASM: A 32-Bit Intel-Based Assembler
664 | Part V ■ Additional Software ® DASM is an Intel-based, 32-bit assembler designed for the development of the MMURTL operating system. It is also used to develop software to run on MMURTL. The version included with this book runs in MS-DOS. The source code is included. To de- velop an operating system, you must run the assembler in another environment. Running in MS-DOS will make DASM easy for you to use as a development tool for your own system. Of course, better assemblers exist, but none are as inexpensive, and few come with source code. How you design your memory-management and loading techniques will determine how much you must modify DASM to suit your needs. Unlike most other Intel-based assemblers, DASM combines the functions of an assembler and a linker. It produces an executable file called a RUN file (along with DLLs and device drivers). A RUN file is analogous to the MS-DOS executable file (.EXE). It can be modified quite easily to output object modules compatible with 32-bit linkers on other systems, if that is what you need. DASM Concepts Most assemblers and compilers produce what is called object code. This is an intermediate form of machine code, and binary data stored in a file is called an object module. Object modules are usually combined to produce executable code that will be ready to load and run by an operat- ing system. Using object code modules has these advantages: ■ You can place modules of object code in a library and search the library for what you need (unresolved externals). ■ You can break your project (the program you are working on) into smaller pieces and compile them separately as you change them. When you want to produce an execut- able file, you “link” all of these object modules together with a program called a linker. ■ Local variables, labels, and procedures may be hidden from other modules. Separate, relocatable modules are also required when you are programming with a “segmented” memory model. DASM eliminates the need for object code. This means you don't need a separate linker. DASM, however, still provides separate compilation of source code modules and library search func- tions— advantages normally associated with a linker and object code. For example, in systems that use a separate linker, with three C source files, you do this: 1 . Compile Module 1 into object module. 2. Compile Module 2 into object module.
Chapter 28 ■ DASM: A 32-Bit Intel-Based Assembler 3. Compile Module 3 into object module. 4. Place object modules in library (if desired). 5. Link object module, making an executable file. With MMURTL and three C source files, using DASM you do this: 1 . Compile Module 1 into assembly language. 2. Compile Module 2 into assembly language. 3. Compile Module 3 into assembly language. 4. Assemble (make an executable file). The difference between these two systems is in what the compiler produces. In other systems, the compilers and assemblers produce object modules. With MMURTL, the CM32 compiler produces assembly language, and the assembler produces the executable file (the RUN file). The previously mentioned advantages for systems producing object code are not lost in the MMURTL development system. You can still edit and compile separate modules to speed the development cycle, provide organization, and assist in code reuse. External library code can still be included in your project, and local (nonpublic or static) variables, labels, and functions are still hidden in each module. The one obvious difference is that all of the library code is in assembler source format. To eliminate the need for a linker, each of the public variables and code labels in your source libraries are listed in a text file that is searched by the assembler. A small utility is included that automatically searches and indexes them into the required text format for you. This is your librarian. Because there is no linking, the development cycle is further reduced. Using DASM DASM combines the functions of an assembler and a linker. Because of this combination, you don't specify an assembler source file for DASM on the command line. Instead, you provide the name of an assembly language template file (.ATF). The template file is actually an assem- bler source file that defines the structure of your program. It provides complete control of how your program is assembled and linked. The template file may contain standard INCLUDE files for MMURTL entry code and exit code required by compilers as well as .ASM file names. The template file also contains statements to set stack size, statements to set virtual memory offsets, and more.
sss Part V ■ Additional Software The following is a template file for the Hello World program. Listing 28.1. Simple application template file. ; STANDARD APPLICATION TEMPLATE FILE ; SYSTEM Entry/Exit module /commands go here .DATA ; Start data segment .VIRTUAL 0h .CODE .VIRTUAL 0h ; USER modules Begin here .INCLUDE Hello. ASM ;Your assembly module .INCLUDE \CM32\CM32.AS ;Standard Entry/Exit code ; USER Library Search files begin here .SEARCH \CM32\LIB\CM32.PUB .SEARCH \MMURTL\LIB\OS.PUB END The DOT commands (commands preceded by a period), INCLUDE files, and library code search file lists are all DASM needs to build your complete application and turn it into a run file. To build the application, compile Hello. c, which produces the Hello.ASM file that con- tains code and data assembly language sections. Then at the command prompt you execute DASM providing the name of the template file:
DASM Hello. ATF Hello. ATF is the assembly language template file shown previously. A run file named Hello.RUN is produced if no errors occurred during the assembly and link processes. The ATF file in Listing 28.2 is for a seemingly more complicated program including more modules. Listing 28.2. A more complicated template file. ; STANDARD APPLICATION TEMPLATE FILE ; - Commands that affect the entire program go here .DATA ;Start data segment .STACK 1000 ;This will default to 1 Page (4K)
Chapter 28 ■ DASM: A 32-Bit Intel-Based Assembler H .INCLUDE Search. ASM .INCLUDE LoadSave.ASM .INCLUDE BufMgmt.ASM ; - USER Library Search files begin here .SEARCH \CM32\LIB\CM32.PUB .SEARCH \MMURTL\LIB\OS.PUB As you can see, this ATF file is not much more complicated than Hello. ATF; it just has a few more modules. Command Line Options The format of the DASM command line is as follows: DASM TemplateFile [RunFile] /L /S /E /D /V Table 28.1 presents the command-line options (switches) available with DASM. Table 28.1 . The DASM command-line options. Description I L Complete list file generated /S Include SYMBOLS (only in complete list file) /E List file for errors/warnings only /D Process as dynamic link library (.DLL built) /V Process as device driver (.DRV built) These options are not case-sensitive and are explained in the following sections. List File The /L option produces a complete list file of all actions the assembler performs on your source code. The list file is automatically named SourceName.LIS. It has the same prefix as your source file with the .LIS extension. The list file format is as follows: Line Address Action/Code/Data Source Code 00001 0000000 <- DSeg begins .DATA The line number is the line for the current module you are assembling. The address is the off- set in the segment you are currently in (CSeg or DSeg). The Action/Code/Data column shows what action DASM took based on the source code for that line. The list file may show instruc- tions, data storage, or command options. Your source code is shown in the far right column.
668 Part V ■ Additional Software Generating a complete list file takes considerable time and disk space. You should use the / L option only when necessary. Errors Only The /E option also produces a list containing only the error statements. If no list file is speci- fied because you didn't use the /L or /E options, all of the errors are displayed on the screen. Approximately 70 errors are generated by DASM. They are explained in text form but also contain a reference number; you can use the number to refer to the section at the end of this chapter for more detailed information about errors. Dynamic Link Libraries The /D option causes DASM to produce a MMURTL-compatible dynamic link library (.DLL) instead of a .RUN file. A DLL in MMURTL may not contain any data. If the DSeg offset is not 0 at the end of the compile, an error is produced. DLLs in MMURTL may contain only code and stack-based variables. DLLs must be fully re-entrant. See Chapter 10, “Systems Program- ming.” Device Drivers The /V option causes DASM to produce a MMURTL-compatible device driver (.DRV) in- stead of a .RUN file. A device driver in MMURTL may contain code and data. Device drivers in MMURTL do not necessarily have to be fully re-entrant. See Chapter 10, “Systems Pro- gramming,” for more information on device drivers. DASM Source Files DASM isn't a fully parameterized macro assembler. It does, however, support simple macro substitution through the use of the EQU statement. DASM operates with DOT commands. DOT commands are reserved command words that begin with a period and are the first non-whitespace data on a line.
Chapter 28 ■ DASM: A 32-Bit Intel-Based Assemble Local (Nonpublic) Declarations DASM allows nesting up to four levels oflNCLUDE files (five if you include the level 0 tem- plate file). When each of the INCLUDE files listed in the .ATF file are opened, they cause DASM to clear all nonpublic labels and variables from the symbol table. The next level and subsequent levels do not do this. You can still write .ASM files that use INCLUDE files for shared local declarations. In the first example (Hello. c and Hello. ASM), if there were an INCLUDE statement in Hello. ASM, it would not clear local names from the symbol table. However, if Hello. ASM were broken into two files (Hellol.ASM and Hello2.ASM) and both were listed in the .ATF file, when DASM closed Hellol.ASM and then opened Hello2.ASM, all of the local labels and variables listed in Hellol.ASM would be “forgotten.” Memory Model DASM is designed specifically for the MMURTL operating system. MMURTL is a paged- memory OS. If you are familiar with the Intel processor segmentation model, you know that code and data may reside in multiple segments. This was a requirement when programs were restricted to a 64K segment. You may also be familiar with the infamous small memory model: a program was made of a single code segment, and the data and stack shared another segment. This is effectively MMURTL's memory model for all programs. Code and data segments in a MMURTL application are not limited to 64K. In theory, the segments can be up to one gigabyte. A program's stack resides in the data segment just as in the DOS small memory model. The code, data, and stack of your program are logically separated in memory on 4096 byte pages. What all this means to the programmer is ease of use. DASM does all the work by lining up the code and data from different ASM source modules. Address offsets are completely resolved at assemble time, and DLL offsets are resolved at load time. DASM Program Structure A stand-alone DASM program is made of code and data segment parts that may be broken up through one or more source files. Two DOT commands determine the current segment. DASM module structure in Listing 28.3 illustrates this: Listing 28.3. Alternating data and code in a template file. .STACK 4096 ;Minimum stack size in bytes .DATA ;Start data segment ;Variables, data, data EQUs, etc. are defined here
^7Q PartV ■ Additional Software Listing 28.3. continued .CODE . START .DATA ; Entry point code defined here ;Continue data segment ;More data defined here ;Continue code segment ;More code here .END DOT Commands A DOT command is a reserved command word preceded by a period ( . ) . This command must be the only active text on the line (trailing comments are allowed). Some DOT commands have a single parameter following the command word. Only a few DOT commands exist, because the memory model in MMURTL is so simple to use. The following DOT commands are recognized by DASM and are described in detail in the following paragraphs: .DATA .CODE . START .INCLUDE filename .ALIGN n .VIRTUAL n .END .SEARCH filename .STACK n .VERSION string .DATE .DEBUG n Each MMURTL program has only two segments. MMURTL uses segmented memory only for the purpose of protection, which is transparent to the programmer. As far as the program- mer is concerned, there is only code, data, and a stack. The following are detailed descriptions of the DOT commands and their parameters (if any): ■ .DATA This command indicates the start or continuation of the data segment. The data segment is where you define your data storage variables. No processor instruc- tions are allowed in the data segment. ■ .CODE This command indicates the start or continuation of the code segment where processor instructions and read-only storage may be defined. DASM is designed to recognize the complete 80386 instruction set. Instructions specific to the 80486 or Pentium processors should be coded in-line with DB statements if required.
Chapter 28 ■ DASM: A 32-Bit Intel- Based Assembler .START This command is placed just before the instruction you want as your program entry point (where it begins execution when the program is loaded). Only one .START command is allowed in your program, no matter how many separate modules you have linked together. It must be in a code segment; otherwise, an error will occur. If you are using one of the provided format files for use with the CM32 compiler, .start is already included in the \CM32\LIB\SUPPORT.ASM file. All you have to do is make sure you have defined main( ) in one of your modules. .STACK (n) The n parameter is the number of bytes in the initial program stack for the main program thread (first JOB task). If no stack command is found, the MMURTL loader automatically allocates a one-page stack (4096 bytes). The .STACK statements are additive. This enables you to add to the stack in a heavily recursive library module, if necessary. MMURTL allows true multithreading. Memory must be allocated (or used from your existing data segment) as a stack for each additional task that you create. The number of initial pages of stack is limited to 256 (1MB). Stack requirements are discussed in much greater detail in Chapter 9, “Application Pro- gramming.” .VIRTUAL xxxx The parameter xxxx is a value that tells the assembler where to assume the segment is loaded. This command is used in the data segment or code segment to set the default initial segment offset value the assembler works with. In virtual (paged) memory systems (such as MMURTL), there are advantages to specifying where the assembler assumes the segment will be loaded or relocated. This command should appear only once in each segment, and it must appear before any code, data, or labels are defined. Application programs don't need to use the .VIRTUAL command. The virtual offsets are 0 by default for the code segment and the data segment. Consider the following example: .DATA .VIRTUAL 40000000h These lines tell DASM that the data segment will begin at linear address 40000000h (1GB). Subsequent continuations of the segment in any of your source modules should nothzve a .VIRTUAL command in them. This does not mean that the module will actually load in memory at that linear address; the .VIRTUAL command simply tells DASM to assume it does. You should not assume this yourself. .ALIGN boundary where boundary is WORD, DWORD, or PARA. This command enables you to align the next storage declarations in the data segment on a memory address multiple of the specified size. This is used rarely, but it may be useful in situations such as optimizing file buffers for direct disk reads and writes. WORD means an even address. DWORD means an address that is modulo 4. PARA means an address that is modulo 16 (paragraph aligned). .INCLUDE filename This statement opens filename, and begins processing data statements and instructions. It also clears all local variables and labels from the symbol table if it is a “first level” include statement (listed in the .ATF file). Local variables are
0 Part V ■ Additional Software those not defined as PUBLIC or EXTRN, which is different from many assemblers, but provides the same functionality of separately linked modules to allow hidden local variables and code labels. In C, functions and variables defined as static will not be listed as PUBLIC in the assembler file. .SEARCH filename. pub This searches a public text index file for unresolved variables, code label names, and function label names. PUB files are specially formatted text files that list public code and variables in assembly language source files. The linker includes the assembly source files listed in the PUB file if they contain the public name of an unresolved external. The .PUB files may be read several times if there is included a new module that defined externals not already in the DASM's symbol table. .VERSION string This allows the definition of a string that will be included in the RUN file header so the program version can be easily obtained without running the program or without searching with a binary editor or viewer. The string can be up to 70 characters in length, begins after the first whitespace following the .VERSION command, and ends when the end of line is detected. All characters beyond 70 are truncated. . DATE This tells the assembler to place the current date and time in the RUN file header in text format. .DEBUG n DASM allows several debugging modes, and n is the number of the mode you want activated. This command causes additional code (and possibly data) to be added to your RUN file for debugging purposes, or causes a separate symbol table to be generated that a debugger can open, read, and use to find publics, line numbers, and so on. . END This tells the assembler you are done with all source modules. The . END command is used only at the end of the format file (or module, if this is a single-file assembly language program), not at the end of each segment piece or assembler source module. A segment type ends by default when a new segment type begins. The .END command indicates there is no more source text to process. DASM Versus DOS Assemblers DASM programs are generally very similar to DOS assembler programs. A major difference is the lack of SEGMENT commands and segment alignment options, for they aren't required with MMURTL. Remember, with MMURTL, your program sees a 2GB, 32-bit, linear ad- dress space. ASSUME commands are also absent from DASM. They aren't needed with “flat” application memory. DASM assumes that DS, ES, SS, FS, and GS, all refer to your data seg- ment. CS always refers to your code segment. If you need to read data from your code seg- ment, segment register prefixes work fine, although I can't imagine where you would need to do so. Code segment pages in MMURTL are read-only. Consider the following example:
Chapter 28 ■ DASM: A 32-Bit Intel-Based Assembler CS: [EBX+MyCodeLabel+5] Another difference is the lack of the PROC (Procedure) attribute. The only operational purpose PROC served was to tell the assembler what type of return instruction to use. Code labels in MMURTL serve as the PROC identifier, and you specify whether or not you want them public. Public labels can be referenced by code in other modules and will be resolved at assemble time. You also specify a RETN (near) or RETF (far) instruction manually. Only MMURTL operating system software uses the RETF instruction because all calls to your code will be 32-bit near calls (relative or indirect). In fact, the only far calls from your program will be to the operating system or its device driver interfaces, which are all predefined through processor call gates. As with some DOS assemblers, references to forward code labels and forward data are allowed, as long as DASM can figure out the size of the reference. You will receive an error if it can't. References to data or code outside the current module mart have an EXTRN declaration in advance. The assembler must be aware of its type to generate the correct instruction. DASM Addressing Modes DASM recognizes all valid 32-bit addressing modes for the 386/486 processors, with 1 6-bit addressing modes not allowed or recognized. DASM is a little fussier than most assemblers when it comes to address or memory operands. As you will see from the following examples, how- ever, use of memory operands is fairly easy to understand, and close to the DOS assemblers. It follows closely the definitions in the Intel 386/486 Programmer's Reference manuals. The following rules apply:
I
674
Part V ■ Additional Software
Table 28.1. Specifying memory operands.
Format [Brackets Required?]
[disp32]
optional
[Reg32]
required
[Reg32+disp32]
required
[Reg32+Reg32]
required
[Reg32Scale+dj
sp32]
required
[Reg32+Reg32Scale+d.
LSp32]
required
The following paragraphs describe items in Table 28.1 and provide examples.
The disp32 construct is a displacement value from the beginning of the segment. It is usually
entered in your program as a simple variable name such as MOV EAX, MyVar. It may also be a
number derived in a simple additive expression using a simple variable, such as [MyVar+5). In
the latter case, you should enclose the expression in square brackets to indicate that it is an
address and not an immediate value, for immediate values may also be constructed with ex-
pressions containing variable names and/or code labels. An example would be the following:
Label2-Labell
A value that is the difference between the addresses of the two labels would not be considered
an address after the calculation is performed. It becomes a constant in the code. No address
“fixup” will be applied by the loader. This type of statement might be used to find out how
large is a particular section of code.
Reg32 is any 32-bit register (EAX, EBX, ECX, EDX, EDI, ESI, EBP, or ESP).
Scale will be number 2, 4, or 8 and indicates a special scaled addressing mode on the 386 pro-
cessor. The *2, *4, or 8 must follow the Reg32 that you want to be scaled (multiplied) by that
value. An example of a scaled instruction is this:
MOV AX, [MyArray+ECX+EBX2]
IfMyArray were a two-dimensional array of WORDS (16-bit values), ECX could contain the offset
into the first dimension, while EBX had the index to the item you wanted in the second di-
mension. The *2 scale would effectively multiply the index by the size of a WORD to ensure you
accessed the correct second-dimension entry. This means high-level languages don't need to
do their own multiplication for access to WORD, DWORD ,or QWORD (64-bit) entries tor one- or two-
dimensional arrays.
The actual syntax for memory operands is the same as the constructs shown in Table 28. 1 above.
Square brackets are required for all memory operands except simple variable names. Here art-
some examples:
Chapter 28 ■ DASM: A 32-Bit Intel-Based Assembler .DATA MyVar DD 10 ;a DWORD variable MyBArray DB 100 DUP(0) ;a single dimension byte array MyWArray DW 1600 DUP(0) ;This is a 40 x 40 array of words .CODE MOV MyVar, 10 ; Immediate value into DWORD MyVar The next example moves 20h into the tenth WORD of the fifth element of MyWArray. (for example, MyWArray[5] [10] = 20h): MOV ECX, 5 ;Set up for the example MOV EBX, 10 ; " " MOV [MyArray+ECX+EBX2], 20h The next example does the same thing as the preceding example. It shows you that addressing elements are accepted in any order, but all must be inside the square brackets: MOV [EBX2+ECX+MyArray], 20h ;same as above Now we move 0 into an byte of MyBArray that is indexed by some value in EDX MOV [MyBArray+EDX], 0h .END In the preceding examples, DASM knew what size the immediate value was because a variable of known size was used in the address. Consider this invalid instruction: MOV [ECX], 20h ;Bad instruction DASM has no way to know which ECX points to, a BYTE, WORD, or DWORD! In these cases, you must use address modifiers to tell DASM the size of the destination memory variable. DASM will sign extend the value as required. Here's an example: MOV WORD PTR [ECX], 20h This line says ECX is pointing to a WORD. The immediate value would be sign-extended (to 16 bits) and moved to the address contained in ECX. Storage Declarations Inside the data or code segments of your program you can issue storage instructions for data to be accessed by your program. This is done with the DB, DW, and DD instructions. Modifiers can be used to duplicate storage values for arrays. Data in your code segment is assumed to be read- only. Labels (variable names) may precede any storage statement, as in this example: Count DB 0
Part V ■ Additional Software Dfi (Define Byte) The DB instruction defines one or more bytes of storage. Immediate numeric values or strings mav be defined with DB. Examples are as follows: EOL DB 0Ah ; creates one byte of labeled ; storage with 0Ah as the value DB 5, 4, 6, 8 ;4 bytes with the values shown DB 100 DUP(0) ;One hundred bytes filled with 0 DB The Quick Brown Fox’, 0Ah ;Creates ASCII bytes with the text ; followed by a 0Ah byte DW (Define Word) The DW instruction defines one or more WORDS of storage (two bytes). The syntax is the same as for DB except that string values are not allowed. DD (Define Double Word) The DD instruction defines one or more DWORDS of storage (four bytes). The syntax is the same for bytes except that quoted strings are not allowed. DF (Define Far Word) The DF instruction is used only to define a single 48-bit quantity (far pointer). It defines a six- byte quantity broken into two parts. The first part is the offset (four bytes), and the second is the selector (two bytes). The offset portion must be separated from the selector portion by a colon (:). An example follows: pFarProc DF 00000000h : 0000h The DF instruction is used for two things. First, it defines OS entry points in library module definitions. This allows you to hardcode the OS entry points. In other operating systems, this would not be a good idea, but because MMURTL uses 386/486 call gates, the OS code can move while its entry points will always stay the same. Second, the DF statement can also be used to define variables for use with the SIGT, LIDT, and LGDT, and other instructions that re- quire a 48-bit (6-byte) storage size. Application programmers will not generally use the DF statement except to access OS calls indirectly. DASM Peculiar Syntax DASM has some syntax differences from other assemblers you may be familiar with. The fol- lowing sections cover these differences.
Chapter 28 ■ DASM: A 32-Bit Intel-Based Assembler PUfii/C Declarations By default, variables and code labels are not visible to other modules that are included as a program. To make them visible (to resolve external use), you must precede the label with the reserved word PUBLIC. Here are some examples: .DATA PUBLIC MyVar DB 10h ;Will be visible to ALL modules ; specified in the .ATF file .CODE PUBLIC MyProc: ;Will be visible to ALL modules PUSH EBP ; specified in the .ATF file MOV EBP, SP .END Note that this is a little different from most DOS assemblers. They usually require the PUBLIC declaration on a separate line. EXTRN Declarations External references from your module must be presented to DASM before their use. If you call external procedures directly, you must declare them somewhere inside your code segment be- fore they are referenced. External variables must be declared in your data segment prior to use. There are three different types of externals in DASM: data, near code, and far code. EXTRN data declarations will always be used when you access public variables from separate ASM source modules. Due to re-entrancy requirements, dynamic link libraries will not contain data. EXTRN NEAR code declarations will always be used when you access code labels as CALL or JMP targets from separate ASM source modules or dynamic link libraries. The targets must be de- clared PUBLIC in the separate modules. EXTRN FAR code declarations will be used only to access operating system calls (procedures). Each external label must be declared on a single line with the keyword EXTRN, the name, and the type. The following are examples of external declarations: .DATA ; External variables in another module you created ;With external arrays you need ;only specify the size of the ; elements of the array 677 EXTRN MyVarl DD EXTRN bOne DB EXTRN bTwo DB EXTRN wOne DW EXTRN array DB
678 Part V ■ Additional Software .CODE ;Far procedures with absolute addresses ; resolved at link time are declared in the CODE segment EXTRN FAR AllocExch EXTRN FAR DeAllOCExch EXTRN FAR SendMsg EXTRN FAR Sleep ;Near code direct calls to one of your module or a ; routine in an object module from a library EXTRN NEAR MyProd , MyProc2 .END Note this is also a little different from most DOS assemblers. They usually require the EXTRN declaration on a separate line. Labels and Address In the following sections, I'll cover certain instructions and how they are used. An introduc- tion to the instruction comes first, followed by a discussion of the instruction and finally a coding example. Segment and Instruction Prefixes With the 386 processor, memory references refer to the data segment by default, which is the value contained in the DS register. There are only two exceptions: 1 . Instructions using the EBP and ESP registers (frame pointers and stack pointers) which use the SS segment register, and, 2. String instructions using the EDI register which uses the ES segment register. The 386 enables you to specify a different segment for each of your memory references. By default, MMURTL makes DS = ES = SS = FS = GS, and all are set to your data segment. Chances are slim that you will need segment prefixes, unless you want to access data contained in your code segment or you want make use of the two additional segment registers (FS and GS) on the 386. MMURTL uses the FS and GS registers internally, and quite frankly, I can't imagine why you would need them with flat memory space, but they are supported in DASM for OS use. To use the segment prefix in a memory reference, simply place the register name with a colon (:) after it before the open square bracket of the memory reference; the segment prefix will be properly coded in the instruction. Examples are as follows: MOV EAX, CS:[MyProc+10] MOV AL, CS:[AppCodel_abel+EBX] Instruction prefixes such as LOCK and REPNE, which may precede some instructions, are coded on the same line as the instruction they affect, which is typical of most 386/486 assemblers.
Chapter 28 ■ DASM: A 32-Bit Intel-Based Assemble The REP (repeat) series of prefixes deals specifically with the string instructions such as REP MOVSB. CALL and JUMP Addresses The CALL and JMP instructions (including JMP on condition) may make forward references into the local module's code segment. This section describes all the address types for calls and jumps, and explains what DASM assumes about them. Coding examples are provided for clarity. CALL LabelName - Call to Near Pointer JMP LabelName - Jump to Near Pointer Jc LabelName - Jump on Condition to Near Pointer These examples indicate a call or jump to a label anywhere in your program regardless of the module. All calls to code labels are considered NEAR and local to the current module unless previously defined EXTRN. This means they are assumed to be within a 32-bit offset from the call or jump instruction itself. If the called label were in a different module and not previously defined as external, then DASM assumes it is a forward reference and codes it as such, filling in the address/offset when it finds it. If the end of the module is reached and the label has not been found in the code segment, then an error is generated. If the label is defined EXTRN, then the label in the other module should have been defined as PUBLIC (for example, PUBLIC MyLabel :). A 32-bit offset relative to the next instruction is encoded as part of the instruction itself. The following are examples: .CODE EXTRN MyLabel ; Needed if in a different module CALL MyLabel ;NEAR PTR is default if left out JMP MyLabel JZ MyLabel CALL MyLabel JMP MyLabel JNZ MyLabel The next is a short jump: JMP SHORT - Jump Short (+/- 127 bytes) Jc SHORT - Jump on Condition (+/- 127 bytes) The two previous examples are a special case for jump instructions; they tell the assembler that the target label for the jump is less than 128 bytes away from the jump instruction. This form is three or four bytes smaller, and executes faster. If the label was already defined (preceding the jump instruction), DASM uses the short form of the instruction if possible without the SHORT modifier being present. The SHORT modifier is used if you jump to a label defined after the jump, but known to be close enough to be a short jump. If you use the SHORT modifier and the label turns out to be farther than 127 bytes, then an error is generated. An 8-bit offset rela- tive to the next instruction is encoded as part of the instruction itself. The instructions are these:
680 Part V ■ Additional Software JMP SHORT MyLabel JZ SHORT MyLabel Far code declarations are completely different from most other assemblers. The only far code you have is the operating system itself. CALL FAR PTR JMP FAR PTR
Chapter 28 ■ DASM: A 32-Bit Intel- Based Assembler The following code shows calling indirectly: CALL DWORD PTR — Call to a Near Indirect address JMP DWORD PTR — Jump to a Near Indirect address This code indicates an indirect call or jump to a near address. A DWORD variable in DSeg contains the address you are calling or jumping to. The following examples show the external declara- tion that would be in your data segment if required. If the label was not previously defined as either a local DD or an external DD, then an error is generated. The offset in the program's data segment of the variable containing the address is encoded as part of the instruction. Externals are fully resolved at assemble time. .DATA ;In data segment EXTRN DD pMyProc ;If not in this module pMyLabel DD OFFSET MyLabel ; Needed if it IS local .CODE ;In code segment CALL DWORD PTR MyLabel JMP DWORD PTR MyLabel You may also call far addresses indirectly. In fact, indirectly calling far addresses is the preferred way to call MMURTL procedures. CALL FWORD PTR — Call a Far address indirectly JMP FWORD PTR — Jump to Far address indirectly This code indicates an indirect call or jump to a far address. This means that an FWORD variable contains the address you are calling (4-byte offset, 2-byte segment). The following example shows an external declaration and two ways to define a local declaration in your data segment. If the label was not previously defined as a local DF, a DD/DW, or an external DF, then an error is generated. In MMURTL, CALL FWORD PTR is yet another way to reach the OS calls. A register pointing to, or the memory address of the FWORD variable containing, the target 48-bit address is encoded as part of the instruction. .DATA ; Somewhere in data segment EXTRN DF pFarProc ; Needed if not in this module pFarProc DF 00000000:0000 pFarProc DD Offset MyLabel DW CodeSeg ;Code Seg is a constant .CODE ; Somewhere in code segment CALL FWORD PTR pFarProc JMP FWORD PTR pFarProc ;Not used in MMURTL Apps
682 Part V ■ Additional Software 386 Instruction Set DASM supports all 32-bit 80386 instructions. The 386 instruction set is a subset of the 80486 and Pentium processors. Table 28.2 is an alphabetical listing of the instructions that DASM supports. To find the actual binary encoding for the instruction and timing information, you should refer to the documentation for the processor you are working with. Examples of almost all of these instructions can be found in the accompanying source code. Table 28.2. Supported instructions. Instruction Descripti AAD AAM AAS ADC ADD AND AR PI- BOUND BSF BSR BT BTC BTR BTS CALL CBW CWDE CLC CLD CLI CLTS CMC CMP ASCII adjust after addition ASCII adjust AX before division ASCII adjust AX after multiply ASCII adjust AL after subtraction Add w/ carry Add AND logically Adjust RPL field of selector Check array index against bounds Bit scan forward Bit scan reverse Bit test Bit test & clear Bit test & reverse Bit test & set Call a routine Convert Byte to word Convert word to dword (Extend) Clear carry Clear direction Clear interrupts Clear task switch flag in CRO Compliment carry flag Compare
Chapter 28 ■ DASM: A 32-Bit Intel-Based Assemble 683 Instruction Description CMPSB Compare strings of bytes CMPSW Compare strings of words CMPSD Compare strings of dwords CWD Convert word to dword CDQ Convert dword to Quad DM Decimal adjust accumulator DAS Decimal adjust AL after subtract DEC Decrement DIV Divide ENTER Make stack frame for procedure HLT Halt processor IDIV Integer divide IMUL Integer multiply IN Reads form port into AL, AX, EAX INC Increment INSB Read byte(s) from a port I NSW Read word(s) from a port INSD Read dword(s) from a port INT Call to Interrupt INTO Call to Interrupt 4 if Overflow IRET Return from interrupt 16 bit IRETD Return From Interrupt 32 bit JA Jump if above JNBE Jump not below or equal JAE Jump above or equal JNB Jump not below (Same as JAE) JNC Jump no carry JB Jump below JBE Jump below or equal JNA Jump not above JC Jump if carry
Part V ■ Additional Software Table 28.2. continued Instruction Description JNAE Jump not above or equal JCXZ Jump if CX=0 (Short only) JECXZ Jump if equal or CX=0 (Short only) JE Jump if equal (Same as JZ) JZ Jump if zero JG Jump if greater JGE Jump if greater or equal JNL Jump not less than JL Jump if less JNGE Jump not greater or equal JLE Jump less or equal JNG Jump if not greater JNE Jump if not equal (Same as JNZ) JNZ Jump if not zero JNLE Jump not less or equal J NO Jump not odd JNP Jump not parity JPO Jump parity odd JNS Jump not signed JO Jump parity odd JP Jump if parity JPE Jump parity even JS Jump if signed JMP Jump unconditionally LAHF (no params) LAR Load access rights (rRGW, rRMW) LEA Load effective address (rRGW, mem) LEAVE (no params) LGDT Load Global Descriptor Table register LIDT Load Interrupt Descriptor Table register LDS Load Far Ptr into DS:General Register
Chapter 28 DASM: A 32-Bit Intel-Based Assembler [683 Instruction Description LSS Load Far Ptr into SS:General Register LES Load Far Ptr into ES:General Register LFS Load Far Ptr into FSrGeneral Register LGS Load Far Ptr into GSrGeneral Register LLDT Load Local Descriptor Table Register LMSW Load Machine Status Word (obsolete) LOCK Lock bus (prefix) LODSB Load string byte LODSW Load string word LODSD Load string dword LOOP Loop to label if CX <> 0 LOOPE Loop to label if CX <> 0 and Zero bit set LOOPZ Same as LOOPE LOOPNE Loop to Label if CX <> 0 and Zero bit set LOOPNZ Same as LOOPE LSL Load Segment Limit (rRGW, rRMW) LTR1 Load Task Register (rml6) MOV Move value into register or memory MOV Segment into 1 6-bit register MOV 16-bit register into segment MOV Move 32-bit register into control register MOV Move control register into 32-bit register MOV Move 32-bit register into debug register MOV Move debug register into 32-bit register MOV Move 32-bit register into test register MOV Move test register into 32-bit register MOVSB Move Byte(s) MOVSW Move word(s) MOVSD Move dword(s) MOVSX Move Byte or word to dword and Sign extend MOVZX Move Byte or word to dword and Zero extend
686 Part V ■ Additional Software Table 28.2. continued Instruction Description MUL NEG NOP NOT OR OUT OUTSB OUTSW OUTSD POP POPAD POPFD PUSH PUSHAD PUSHFD RCL RCR ROL ROR REP REPE REPNE RETN RETF SAL SAR SHL SHR SBB SCASB SCASW Multiply Negate No Operation (Same as XCHG EAX.EAX) Logical NOT Logical OR Output byte/word/dword to Port Out String Bytes Out String words Out String dwords Pop a value from the stack (32-bit only) Pop all registers from stack Pop 32-bit flags from stack Push a value onto the stack Push all registers Push 32-bit flag register onto stack Logical roll w/carry left Logical roll w/carry right Logical roll left Logical roll right Repeat prefix (used with string instructions) Repeat if equal Repeat if not equal Return from NEAR call Return from FAR call Logical shift accumulator left Logical shift accumulator right Logical shift left Logical shift right Subtract w/borrow String compare and scan byte String compare and scan word
Chapter 28 DASM: A 32-Bit Intel-Based Assembler H Instruction Description SCASD String compare and scan d\ SETA Set if above SETAE Set if above or equal SETB Set if below SETBE Set if below or equal SETC Set if carry SETE Set if equal SETG Set if greater than SETGE Set if greater than or equal SETL Set if less than SETLE Set if less than or equal SETNA Set if not above SETNAE Set if not above or equal SETNB Set if not below SETNBE Set if not below or equal SETNC Set if no carry SETNE Set if not equal SETNG Set if not greater than SETNGE Set if greater than or equal SETNL Set if not less than SETNLE Set if not less than or equal SETNO Set if not odd SETNP Set if not parity- SETNS Set if not signed SETNZ Set if not zero SETO Set if odd SETP Set if parity SETPE Set if parity even SETPO Set if parity odd SETS Set if signed SETZ Set if zero
1
688
Part V ■ Additional Software
Table 28.2. continued
Instruction
Description
SGDT
Store global descriptor table
SIDT
Store interrupt descriptor table
SHLD
Shift left dword
SHRD
Shift right dword
SLDT
Store local descriptor table (Not used in MMURTL)
SMSW
Store machine status word (Obsolete)
STC
Set carry flag
STI
Set interrupt flag
STD
Set direction flag
STOSB
Store string of bytes
STOSW
Store string of words
STOSD
Store string of dwords
STR
Store task register
SUB
Subtract
TEST
Logical test (AND with results in flags)
VERR
Verify
VERW
Verify Word
WAIT
Wait for NCP
XCHG
Exchange reg/reg or reg/mem
XLAT
Translate
XLATB
Translate BL
XOR
Logical XOR
Executable File Format
The MMURTL RUN file is one of three types of executable files supported by the MMURTL
OS. The three types are these:
RUN A MMURTL standard executable
DLL A MMURTL dynamic link library
DDR A MMURTL device driver executable
Chapter 28 ■ DASM: A 32-Bit Intel-Based Assembler
The files for all three types of loadable or executable files are essentially the same. The files are
composed of tagged fields. The fields are in TLV format (Tag, Length, Value).
The format for the file is not fixed (meaning that it has no header with fixed-length fields) and
is therefore expandable for future requirements. Also, programs can read the file and look for
only the information they need and understand. Some fields are mandatory, whereas others
are optional.
The tags are single, unsigned bytes with values 80H and above. This allows 128 individual
pieces of information in the RUN file; which is far more than you should ever need for an
executable file in MMURTL (remember, simplicity was the goal from the beginning).
The length is a 4-byte (32-bit) unsigned number. It is always included after each tag and con-
tains the length of the data that follows it.
For example, the first tag in all RUN files is the begin tag (80h). It is followed by a 4-byte length
with a value of 1 (0000000 lh). A single byte follows the length as indicated by the value of 1 .
The byte describes the file type (RUN, DLL, or DDR).
If the file type value were 1 and you did a HEX dump of the RUN file, the first seven bytes
would look like this:
80h 01 h 00h 00h 00h 00h 01 h
The tagged fields must be included in numeric order in the file. This is mandatory and is for the
benefit of the tag reader (the operating system loader or applications that read these files). It is
also used for a consistency check. If the loader detects tags that are out of order, it will not load
the file. Table 28.3 describes each of the tags.
|
689
Table 28.3. Tag usage.
Description Use
80h FILE ID Mandatory
82h VERSION STRING Optional
83h DATE/TIME STRING Optional
84h COMMENT Optional
90h INITIAL SEGMENT SIZES Mandatory
92h ASSUMED DATA OFFSET Optional
94h ASSUMED CODE OFFSET Optional
96h STARTING OFFSET Mandatory
AOh DLL IDENTIFIER As Required
690 Part V ■ Additional Software Table 28.3. continued Tag Descripti Use BOh CODE SEGMENT Mandatory B2h DATA SEGMENT Mandatory COh CSEG DATA ADDRESS FDCUP As Required Clh CSEG CODE ADDRESS FIXUP As Required C2h DSEG DATA ADDRESS FDCUP As Required C3h DSEG CODE ADDRESS FDCUP As Required C5h DLL ADDRESS FDCUP As Required C8h DLL PUBLIC As Required FFh FILE END/CHECKSUM Mandatory Each of the tag types, length field use and the values for each tag are discussed in detail in the following sections. TAG 80h FILE ID (Mandatory) LEN 1 VAL 1 indicates a RUN file (standard executable) 2 indicates a DLL (dynamic link library) 3 indicates a DDR (device driver) TAG 82h VERSION STRING (Optional) LEN Length of version string VAL Version string TAG 83h DATE/TIME STRING (Optional) LEN Length of string VAL Date/Time string TAG 84h COMMENT (Optional) LEN Length of string VAL Comment string TAG 90h INITIAL SEGMENT SIZES (Mandatory) LEN 12 VAL Three unsigned dwords that contain the sizes of the initial stack, code segment, and data segment in that order. The values for Code and Data should match the totals found in tags BO and B2 (code and data) segments.
Chapter 28 ■ DASM: A 32-Bit Intel-Based Assembler TAG 92h ASSUMED DATA OFFSET (Optional) LEN 4 VAL An unsigned, 32-bit number containing the value the assembler assumed for the DSEG offset. If VAL is not included, the loader assumes 0. TAG 94h ASSUMED CODE OFFSET (Optional) LEN 4 VAL An unsigned, 32-bit number containing the value the assembler assumed for the CSEG offset. If VAL is not included, the loader assumes 0. TAG 96h STARTING OFFSET (Mandatory) LEN 4 VAL An unsigned, 32-bit number containing the offset in the code segment of the first instruction to execute after loading. TAG AOh DLL IDENTIFIER (As Required) LEN Length of DLL File Name VAL Full file specification of a DLL file, which may have to be loaded if not already resident. This must be included if you call public DLL procedures from this DLL in your program. TAG BOh CODE SEGMENT (Mandatory) LEN Length of code segment VAL Binary executable code of the length expressed in LEN. There may be one or more of these in a RUN file. If more than one is used, the order of the tags must be in the instruction sequence as required in the code segment. TAG B2h DATA SEGMENT (Mandatory) LEN Length of data segment VAL Binary data of the length expressed in LEN. For DLLs, the LEN must be 0, and no data will follow. There may be one or more of these in a RUN file. If more than one is used, the order of the tags must be in the data sequence as required in the data segment. TAG COh CSEG DATA ADDRESS FDCUP (As Required) LEN Multiple of 4 VAL One or more offset addresses (each address is 32 bits) in the code segment of a 32-bit value that refers to an address in the data segment which may change when the data segment is loaded (relocated). One or more of these tags may be in a single executable file. One tag may contain all the fixups, or multiple tags may be used.
692 Part V ■ Additional Software For example, MOV EAX, MyVar encodes the address of MyVar in the data segment as part of the instruction. This address is an offset from zero or the value specified by the virtual command. The data segment may not be located at that address, so the MMURTL loader has to know how to change the value in the instruction. TAG Clh CSEG CODE ADDRESS FDOJP (As Required) LEN Multiple of 4 VAL Offset of one or more addresses in the code segment of a 32-bit value that refers to an address in the code segment that may change when the code segment is loaded (relocated). As with TAG CO, there may be one or more of these tags. The following is an example of what would cause this: MOV EAX, OFFSET MyCodeLabel This encodes the address of MyCodelabel as part of the instruction. This address is an offset from the beginning of the code segment as understood by DASM. The code segment may not be located at that address, so the MMURTL loader has to know how to change the value in the instruction. TAG C2h DSEG DATA ADDRESS FDOJP (As Required) LEN Multiple of 4 VAL One or more offset addresses in the data segment of a 32-bit value that refers to an address in the data segment that may change when the data segment is loaded (relocated). As with TAG CO, there may be one or more of these tags. Here is an example: pMyData DD OFFSET MyVar This places the offset of a variable into another variable in the data segment. Once again, if the data segment is not located at the address DASM assumed, it must be changed after loading. TAG C3h DSEG CODE ADDRESS FDOJP (As Required) LEN 4 VAL Offset address in data segment of a 32-bit value that refers to an address in the code segment that may change when the code segment is loaded (relocated). As with TAG CO, there may be one or more of these tags. Consider the following example: pMyCode DD OFFSET MyCodelabel This places the offset of a code label (a procedure or function) into a variable in the data seg- ment. Once again, if the code segment is not located at the address DASM assumed, it must be changed after loading.
Chapter 28 ■ DASM: A 32-Bit Intel- Based Assembler TAG C5h DLL ADDRESS FLXUP (As Required) LEN 4 + length of DLL public name VAL Offset address in code segment of a 32-bit value that refers to a DLL PUBLIC procedure, followed by the DLL public name. DLL code references are near 32-bit calls. The value in the code segment will be zero until the loader resolves it. All DLL public names must be unique in MMURTL. See Chapter 10, “Systems Program- ming”, for details on DLLs. TAG C8h DLL PUBLIC (As Required) LEN 4 + length of DLL public name VAL Offset address in code segment of the entry point for a DLL public procedure, followed by the DLL public name. This tag type should only appear in DLL files. All DLL public names must be unique in MMURTL. See Chapter 10, “Systems Pro- gramming,” for details on DLLs. TAG FFh FILE END/CHECKSUM (Mandatory) LEN 4 VAL 32-bit simple check sum of the entire file prior to this tag. This means you simply total the value of each byte in a 32-bit variable while ignoring overflow. This value should match your total. If not, you can assume the file is corrupt. Error Codes From DASM The following errors can be returned from DASM. Each is assigned a number, followed by text that may be displayed, and a description of what may have caused it.
694 Part V ■ Additional Software 7. Unrecognized character More than likely, a letter was found in a number that doesn't fit its radix. 8. Invalid alignment specified Only WORD and DWORD are allowed in the .ALIGN statement. 9. Start command only allowed in CSEG .START was found in DSEG. 10. Virtual command must be It must also be in the first occurrence of •first in segment that segment. Check your ATF file. 11. Invalid virtual value Oh to 7FFFFFFFh is the limit. 12. Starting address not found You must have a . START statement some- where in your code or in included library files. 13. Reserved 14. Invalid command DOT What?? The following errors all deal with instruction format. You should look at the section of this chapter dealing with memory references to help understand the specific error. 15. Invalid operand 16. Invalid segment register use 17. Invalid scale value ‘Reg*?’ 18. Scale value expected (*2,*4,*8) 19. Too many address scale values 20. Invalid register for memory operand 21. Invalid memory operand 22. Offset must be from data segment 23. Nested brackets 24. Unbalanced brackets 25. Invalid operand size attribute 26-31. Reserved 32. Unknown token in operand array 33. Too many operands or extra character 34. Reserved 35. Invalid expression or numeric value 36. Operand expected before comma 37. Reserved
Chapter 28 DASM: A 32-Bit Intel-Based Assembler H 38. Invalid character or reserved ••word in operand 39. Relative jump out of range 40. Operand size NOT specified ••or implied 41. Instructions not allowed in ••data segment 42. Instruction expected after prefix 43. Mismatched sizes in operands. 44. Wrong operand type for instruction 45. Incorrect format for memory operand The following errors all deal with data storage: 46. Strings only valid for DB storage 47. Expected ‘(’ after ‘DUP’ 48. Storage expected between commas 49. ' : ' not expected 50. DWord storage required for OFFSET 51. Invalid storage value 52-53. Reserved 54. ' : ' expected after last label 55. Macro not allowed in lexical ••level 0 56. EQU or Storage expected 57-62. Reserved 63. Instruction expected before ••register name 64. Public symbol already defined 65. Local symbol already defined This indicates you have specified an operand size, and it didn't match the source or destination operand size, for example, MOV EAX, BYTE PTR Variable. DW “xxx” is not allowed. Duplicated storage values must be paren- thesized. DB 23,34,??, A colon is not allowed between data segment labels and storage definitions. All segment offsets are 32-bit in MMURTL. A semicolon is required after a label code segment. the
H Part V ■ Additional Software 66. Number not expected 67. New symbol must follow ••PUBLIC keyword 68. Label, Command, Instruction, «»or Storage expected 69. Inconsistent redeclaration Can't understand what's on the line! This label or variable is defined differently elsewhere. Usually the message is caused by an extern not being the same as the public when it's found. Your Own Assembler? If you're not using the Intel processors, this assembler won't do you much good, I'm afraid. On the other hand, you don't need to write one from scratch anyway. Cross-development is always an option for you. Cross-development is developing programs with tools and compilers under one operating system, targeted to run under a different operating system. This is really what you are doing with the DOS versions of the CM32 compiler and DASM. If you want to build a complete environment as I did, you will have to think about an assem- bler, a compiler, and all the utilities to go with them. One final note about the DASM assembler: The documentation was almost as difficult to write as the assembler. I recommend that you keep that in mind.
Powered by TurnKey Linux.