DOS32 Version 3.3 Released on 27 November 1995 A 32 bit DOS extender package Copyright (C) 1994 Adam Seychell, All Rights Reserved. This document should explain everything about the DOS extender DOS32 V3.3. This program may only be of use to people who have some knowledge of assembly language programming. However, I have tried to make this document easy for a beginner to understand. Anyone who can no longer tolerate programming with pathetic 64Kb segments and is sick of the 640Kb barrier or just wants to program using the full power of their computer then use DOS32 for 32bit mode programming. AGREEMENT This program may not be sold other than the cost of the disk or physical handling. The use of this program is entirely at the users risk. In no event will the author be liable for any damages, including any lost profits, lost savings or other incidental or consequential damage arising out of the use of this program ,or for any claim by any other party. This program is distributed WITHOUT ANY WARRANTY. You can redistribute it only in the same form as you have received it with all files left unmodified. The exception to this rule is if you choose to develop an application that requires the file DOS32.EXE then you may freely distribute it with the application. If you would like to take separate files from this package and distribute them in your productions then please ask me first. All source code of examples and libraries that are distributed with the DOS32 package are not under any copyright except if stated by their author. If you are releasing the source code to your program then it may require DOS32 for compiling. Under these circumstances you may include an unmodified copy of the entire DOS32 package within your program. Use of the DOS32 dos-extender in any commercial software or any software whereby the author is selling requires written permission from me. If you wish to use DOS32 dos-extender for commercial software then please contact me at the address below so we can work out an agreement. Typically the fee for using DOS32 in a small program will be US$150, however this price may vary depending on the application type. * * * The DOS32 project has been developed entirely in my spare time and I've been slowly working on it for over two years now. It's only fair for me to ask that ALL programs that use the DOS32 dos-extender must include full credit for it's use. A simple way of doing this is to display the DOS32 logo by using the -w switch of the linker. If you really like this program then I would also enjoy receiving a postcard from you. Of course I have no real control on what people do with it so I am just relying on your honesty. So please, help support free software. Anyone may buy a copy of the complete source code to DOS32.EXE for a fee of $30 Australian or $25US and I will accept either cash or a cheque, whatever is more convenient (or cheapest) for you. The source code is over 200KB in size and may be compiled with either MASM (Microsoft Macro Assembler) or TASM (Turbo Assembler), it is also very well commented and so it should be easy to follow and learn from. Address all correspondence to the author at the following address; Adam Seychell 16 Avion Place Westmeadows VIC. 3049 AUSTRALIA internet: s921880@minyos.xx.rmit.edu.au Please ensure you enclose your return address! If you wish to purchase the DOS32 source code then write out your cheque in the name of Adam Seychell. WHAT IS A DOS EXTENDER ? The 80386 has been around since 1987 and to this day DOS is still running in real mode. Since nobody has bothered to design a protected mode DOS, programs called 'DOS extenders' were written. A DOS extender is basically a program that will switch the CPU into protected mode so that it can then execute a protected mode application under DOS. If you don't know the difference between real mode and protected mode then have a go at reading the file PMODE.DOC or better still get a proper book before reading to much further. I think that it is much easier to program in protected mode than it is to on real mode ( Intel 8086 architecture ) because you can use only one big segment for the entire application. You could say a DOS extender is a separate program that handles all the setting up needed for protected mode under DOS. You let the DOS extender worry about all of the protected mode initialising. So you write programs in plain and pure protected mode. Since DOS runs in real mode ( or V86 mode ) a protected mode program can no longer execute DOS calls directly. The DOS extender should have services that allow protected mode programs to call real mode code and will either enter V86 mode or return to real mode when doing so. Usually a DOS extender has many more services than just real mode calls. For example, setting interrupt vectors, memory allocation, disk I/O routines and possibly a debugger. DOS32 Introduction DOS32 V3.0 is a public domain 32bit DOS-extender package for the development of true 32bit DOS executable. The actual extender is a real mode DOS executable called DOS32.EXE. This tiny 8.5K file is the heart of the hole package. DOS32.EXE will initialise protected mode then load and execute a custom 32bit executable. To make one of these 32bit executable you must use the linker, DLINK, which is also distributed in this package. My main aims when developing DOS32 was to make a DOS-extender that is very easy to use, has powerful features and small in size. I have also tried to make the extender as close as possible to programming under conventional real mode DOS, and yet have full 32bit power. This should make it easier for people to start programming in protected mode. The debugger that is distributed with DOS32 should also help you become more familiar with the workings of protected mode. I have released my work so people can stop programming in a 8086 architecture, a CPU produced back in 1978. I hope that you find DOS32 as a very useful tool for developing 32bit software. What is DLINK? The program DLINK is a linker supporting the Intel standard Object Module Format (OMF). DLINK is distributed with the DOS32 package and is required to produce the customary 32bit executable file that's loaded by DOS32. The main difference between DLINK and a conventional DOS linker such as Borland Turbo Linker (TLINK.EXE) or Microsoft Link (LINK.EXE), is that DLINK produces a special executable format made specifically for flat memory model programs. When DLINK is first ran it will begin processing the object modules specified on the command line. The information inside the modules is used to create a 32bit executable file of your application, which may simply be executed under DOS. Exact details of the loading process are explained later in this document. Some of DOS32 main features * Complies with the following specifications; Dos Protected Mode Interface ( DPMI v0.9 ) Virtual Control Program Interface ( VCPI v1.0 ) Extended Memory Specification ( XMS v2.0 and v3.0 ) Int 15h Top-Down extended memory management * Executable file sizes upto 1Gb. * Supports Dynamic Loadable Libraies. * Paged memory enabling a true flat memory model. * Functions for allocating/freeing memory and DMA buffers. * Contains a complete set of 32bit DOS file I/O services (open/close/create/read/write/chdir/rename, ect). * Services for interfacing with Real Mode programs. * Capable of terminating and staying resident. * Uses only 60K _total_ memory and requires under 9K of disk space. * Distributed with a debugger ,basic C library and linker. Minor features * Supports up to 4Gb of RAM * Executable can be compressed * Can run with zero extended memory * Ctrl+Break aborts executable loading. * Handles all errors during initialising * Load time selector fixups in executable. * Not a single 4KB block is wasted on the machine * DOS32.EXE can be integrated in the main .EXE file * Hooks XMS driver to stop disabling of the A20 gate. * Detects VCPI with Microsoft's EMM386.EXE NOEMS * No overhead on hardware interrupts (expect under DPMI) * Runtime termination for CPU exceptions 0,1,6,13 & 14 * Uses VCPI if available even when a DPMI server is present * Hooks INT 23h for correct Ctrl+C/Ctrl+Break termination * Fast starting and exiting. Less than 0.3 sec total on a 386SX16 * Guards from returning to protected mode from a real mode switch when under a XMS or raw systems and the 386 is in V86 mode. * Stops DOS32 trying to be loaded into upper memory because the system resets when doing so. The DOS32 Applications Programming Interface The DOS32 extender supplies many useful functions for the application to use. The complete list of each service and programming details are described in the file API.DOC. I've put all the API documentation in a separate file so it may be printed as a handy reference. ( 65 lines per page). DOS32 OPERATION This is the main section of this document and will explain the workings of DOS32. This is a flat memory model DOS extender. Flat memory means that the entire application is operating in a single block of linear memory. You may think of it as the application gets loaded into memory in one hit and is located somewhere in the CPU address space. This location is called the program base address since it represents the first byte of the program. The CPU is set up for data and code type segments with their base addresses equal to the program base address. However, as these two segments are on top of each other in memory they can effectively be treated as one segment. This allows the applications data variables and code to be mixed together. The memory map below may help you understand what's happening. Figure (1). The system memory map demonstrating a protected mode application loaded into memory. ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ FFFFFFFF ³ ³ ³ ³ °°°°°°°°°°°°°°°°°°°°° ³ ³ ³ ³ ³ ³ ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ³ 32bit application is³ ³ loaded in here ³ ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ <-program base address ³ ³ (can be anywhere above 1Mb ) ³ ³ ³ ³ ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ 100000h ³ ROM area ³ ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ C0000h ³ VGA memory map ³ ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ A0000h ³ ³ ³ 640K base RAM ³ ³ ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ 0 The program's data and code segment limits are 4GB in size. This means the largest OFFSET address (relative to the program base address) can range from 0 to FFFFFFFF. An offset of FFFFFFFF will not address memory above 4Gb but rather rap around so it will address memory somewhere below the program base address. Therefore it's possible to access the entire CPU address space with only a single 32bit offset. It also means there is no need for your programs to ever again use multiple segment registers. These are the reasons why I wrote DOS32. DOS32 sets up three segments for the application. As explained above, two of these are the applications code and data segments. The third segment is another data segment with a fixed base address of zero and may be used to access physical addresses such as video memory, BIOS data area, ect. Most of the time the application will never need to use this secondary data segment because the entire CPU address space can already be accessed from the programs data segment using 32bit offsets. The selector value of this zero based data segment can be obtained from DOS32 function AX=EE00h INT 31h. The program base address value can be obtained from function AX=EE02h INT31h (see API.DOC for details). If you are unfamiliar and would like to know more about protected mode descriptors and segments then have a go at reading the files PMODE.DOC and PMODE2.DOC. These files will explain some basics of protected mode operation. So basically there are three different segments available to the application, and all have limits of FFFFFFFF. * data segment * code segment * zero base data segment Please note that the first 1 MB of linear address space is mapped to the lower 1Mb of physical address space. This means that accessing locations 0..100000h will actually access the first megabyte of physical memory. This allows the application to access physical memory anywhere in the lower 1 MB. The offset address relative to the program segment can be calculated to point at any location in the first 1Mb of physical memory. offset = Physical address in first MByte - Base address of segment. Alternatively you can also use the zero based segment to access memory in the first megabyte. In this case if the memory is below 1MB then the Offset = linear = physical. You will probably only need physical memory locations to access the video memory (i.e 0A0000h - 0BFFFFh). If your program dos not need the zero base segment then you can ignore this selector value and keep all of the segment registers ( DS,ES,FS,GS,SS ) loaded with the program data selector. This is what makes protected mode so powerful, you can access all of your data,code, video memory or whatever just by using the 32bit offset. If you are still not clear on what I am saying here then try having a look the programming examples distributed with this package. When the application is first executed at the program entry point the CPU registers will hold the following values. All data segment registers, DS,ES,FS,GS and SS, will contain the programs DATA selector. CS will contain the programs CODE selector. EIP will point to the program entry address. ESP will point to the last dword of the defined stack (as explained below). EAX,EBX,ECX,EDX,EDI,ESI and EBP are undefined. Ineterrupts are enabled and direction flag is clear. Executable format and loading process. The information here will explain how DOS32 and DLINK work together and exactly what happens when the executable is first ran from DOS to when the application's first instruction is executed. Please note that it is not essential for you to know these details but is described here for those of you who are interested. File format of a DOS32 executable (.EXE file produced from DLINK) Offset Size Description ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ 0 n A stub loader or DOS32.EXE of n bytes {no stub in DLL} n+0 4 Signature "Adam" { ="DLL " for DLL files } n+4 2 DLINK version in packed BCD format n+6 2 Minimum DOS32 version required (packed BCD) n+8 4 Number of bytes remaining after the stub file. The entire .EXE file size (after linking) will be equal to this value plus the stub file size. n+0Ch 4 Start of 32bit executable image relative to the 'Adam' signature. n+10h 4 Length of executable image ( see below ). n+14h 4 Initial memory required for application. This value must be larger than the size of the 32bit executable image. n+18h 4 Initial EIP { public varible address for DLL file } n+1Ch 4 Initial ESP { ignored in DLL header } n+20h 4 Number of entries in fixup table. Each entry in the table contains a 32bit offset relative to the start of the executable image of a 16bit WORD that must be set to the programs data selector at load time. The table immediately follows the executable image. n+24h 4 Flags ( function active when bit is set ) bit 0 = executable image is compressed bit 1 = display DOS32 logo when executed bits 2..31 reseved. The 32bit executable image (application binary data) can start anywhere after this point (i.e >= offset n+28h in the .EXE file). --- more entries can be added in future ---- At the front of the .EXE is the stub loader or DOS32.EXE file and must be a normal DOS executable ( i.e start with a 'MZ' signature ). When the .EXE is executed, DOS will only load and execute the 'MZ' executable at the front of the file and ignore the rest of the file. The stub loader is a small program that will simply execute DOS32.EXE. I have distributed a sample stub loader, STUB.ASM. This actual program is stored internally in DLINK and is automatically used as the stub if the -S option is not used. See later in this document for more information about the -S switch. When the program DOS32.EXE is executed it will first initialise for protected mode and try an load in the 32bit application. It does this by first checking at the end of the it's self for the 'Adam' signature. If found it will then assume that it is a DOS32 32bit executable and read the rest of the header and begin loading the application it into memory. If no 'Adam' signature was found it will then look at parent program which had executed DOS32.EXE. If the parent program has the 'Adam' signature then it will begin loading the executing the DOS32 executable. If the parent does not contain the signature then the following error will be displayed. DOS32 Version 3.xx 32bit DOS-extender and loader Copyright (c) Adam Seychell, 1994. All rights reserved. File name: C:\DOS32.EXE Error: Bad DOS32 executable With this set up it allows DOS32.EXE to be used in place of the stub loader. If a stub loader is used, and the user runs the .EXE, the stub file will be executed which in turn will execute DOS32.EXE. DOS32 will then begin it's work, by first initialising for protected mode operation and allocate a memory block for the application. The executable image is then loaded into the memory block followed by applying the selector fixups. Finally the stack pointer (SS:ESP) and instruction pointer (CS:EIP) are set to finally start the application up and running. PROGRAMMING The section explains, in general, on how to write protected mode applications. It will explain about the initial stack, entry point, selectors, segments definitions and examples of basic ASM file structure. Segment Definitions There may be some confusion with segments defined in the ASM file to the segments set up by the CPU. If you cannot grasp all this segment stuff then don't worry. It's only useful to know if you want to understand how the programs are stored in the executable. In real mode if you declared a segment that is not grouped to the main segment then a segment register must be loaded with the segment value if any data/code is to access it. For example, if the application had more than 64KB of code then multiple code segments would need to be defined and far calls would be required to call across the segments. The CPU segment registers have to be altered each time a the CPU accesses a new segment. In a flat memory things work completely different. Segments defined in the ASM file (or in modules from a C compiler) have nothing to do with segments defined in the CPU descriptor tables. As described earlier, flat memory is when only one segment in memory exists for the entire application. Segments defined in the ASM file are like secondary segments or sections within the actual program segment in memory. The object modules ( OBJ files) contain information of all the segments that have been defined in the source code. The linker will place all the code/data from a particular segment name and group it into one section in the program segment in memory. For example, all data/code that has been defined in the segments named 'blabla' will be grouped to together when stored in the executable. The Object Module Format ( format of OBJ files ) are capable of containing all sorts of information about a segment definition. Some of these include public or private, Alignment in memory and Group definitions. However for flat memory most of them can be ignored. Rules on how DLINK handles segments definitions. * DLINK can support up to 30 segment definitions. * Each segment is automatically made public. The public field in the segment definition is ignored. * The segment alignment may be set on the DLINK command line. The alignment field in the segment definition is ignored. * The occurrence of each segment definition in the executable will be same order as they are defined in the object modules. * Each segment will automatically be grouped together. The assembler GROUP directive is ignored. * The code/data defined in segments of the same name will be next to each other in the executable. It is possible to define data that is not initialised. In this case the actual segment size greater than the total size of code and data that is defined in it. In assembler, non-initialised data can be defined with the ? operator. The following two examples will define a 1Kb array of data that is not initialised to any value. My_Array DB 1024 DUP (?) ; Assembly language char My_Array[1024]; /* C language */ The non-initialised data does not matter what information it contains when the application is first loaded. Therefore if the end of the executable contains non-initialised data then there is no need for it to be loaded into memory and therefore not stored in the .EXE file. DLINK takes advantage this and will not include any non-initialised data in the executable file which has been declared at the end of the program segment. This is how the initial stack area is set up. A segment is defined with an array and the stack pointer will point to the last dword of the segment. Defining the program entry point The entry point of the program is defined exactly the same way as writing a conventional assembler program. The start address will equal to the label at the assembler END directive. For example, the following code shows how to identify a program's starting instruction with the label 'start'. .CODE ; define code segment using ; quick segment definitions. start: .. .. .. END start Please note that the program entry point will only be set to the first module to contain a starting address. Starting addresses defined in following modules will be ignored and the linker will report with a warning message. The program eventually terminates with INT 21h AH=4Ch, just as in good old DOS. Defining the initial stack When the program is first loaded the initial stack area is defined by the last segment with it's class name equal to 'STACK'. The initial value of ESP will point to the last dword of this segment. Please also note that DOS32 uses upto 128 bytes of the stack space. Add to this what ever stack size your program is going to use. Example: To define a 128Kb stack My_Stack SEGMENT 'STACK' DB 20000h DUP (?) My_Stack ENDS If you are using the assembler quick segment definitions ( e.g .model .code .data .stack ) then the stack may be defined with the assembler directive .STACK [size]. Reading the program data selector In a real mode program the segment value could be obtained by referencing a segment name. E.G MOV AX,_TEXT will load AX with the real mode segment value of the _TEXT segment. In protected mode only selectors are used so loading AX with a real mode segment value is useless not to mention impossible if it's above 1MB. Whenever a segment name is referenced (like in the above instruction ) the value of the programs data selector is read. It does not matter what segment name or group definition is referenced, the value read will *always* be the programs data selector. This feature is handy for interrupt handlers as the program data selector may be required. For example, the following code will set both DS and FS segment registers to the programs data selector. First_Seg SEGMENT First_Seg ENDS The_Seg SEGMENT mov ax,The_Seg mov ds,ax mov ax,First_Seg mov fs,ax The_Seg ENDS If you are using the assembler quick segment definitions ( e.g .model .code .data ) then you may reference the data selector using '_DATA'. This is because the .Model directive automatically defines _DATA as the name of the data segment. General Assembly Language file structure For most applications the programmer will only ever need to use the assembler quick definitions directives for setting up segments. Both TASM and MASM 6.x support the new FLAT memory model types and so have no problems compiling flat memory 32bit code. Flat memory model programming is easy since there is no limitation on what goes where. For example you could write all the code in the .DATA segment and place all data variables in the .CODE segment. Below is an example of a basic ASM file; .386 .MODEL FLAT .STACK .DATA .. .. code/data .. .CODE ... ... more code/data ... Start: ... ... ... code and data can go anywhere ... END Start The above example will create a starting module since it contains a symbol after the END directive. The .STACK should only need to be defined in one module only. If it's defined more than once then the linker will added all the stacks together to form one large stack area (assuming the assembler greats the same names for each stack segment definition). If other modules are to be linked with the above example then they should not define a stack and/or a starting point. For example; .386 .MODEL FLAT .CODE ... ... code/data ... END It couldn't get any simpler, really. DOS32 Loadable Libraries DOS32 also supports loadable libraries. A DOS32 Loadable-Library or if you prefer Dynamic Linkable-Library, DLL for short, is a special executable file containing code and data that may be loaded into memory and executed by a DOS32 application during run time. The words Loadable Library are used because these files are typically used as extra functions available to the application program. Of course the programmer may use them for any purpose. For example, using DLL may be convenient way for storing say GIF images or music data. Loadable libraries operate like loading a DOS32 executable file into an allocated memory block allowing the application using some or all of its code/data. Once finished with it may simply be released from memory freeing up unnecessary code/data that would otherwise be wasted if was directly linked into the main executable. Once the library is loaded into memory the application ( or program which loaded the DLL) is given an address of a special public variable in the library file. The symbol name of this public variable must be defined when the library file is created so DOS32 can return its address to the DLL loader (application). The address of this variable is the only way the application can get further address information of the contents in the library. The library's code/data will be loaded at a specific offset address in the program segment which in most cases will not be at the beginning of the segment (i.e offset 0). The library file also contains a segment relative fixup location table. Immediately after DOS32 loads the library file into memory it can apply any necessary fixups to the library's code and data making it fully relocatable, keeping DOS32 as a pure flat memory dos-extender. Programming DOS32 Loadable Libraries To create a Loadable Library you compile and link a program as normal specifying the -d switch in DLINK. See 'USING DLINK' below for details on using the -d switch. The library program must define a public symbol that is to be used as the location which the DLL loader 'sees' when the library is loaded into memory. It is up to the programmer how he/she decides to use this public variable in the library. For example, the library may define its public variable as an array of pointers which point to each function in the library. When the application has loaded the library it is given the address of its public variable ( which is the address of the array ). The application may then call each function using the array data. Another method would be to define the library's public symbol as a function which may be called to get addresses of other functions in the library. You may even want to use a DLL for storing data only. For example, an application may store graphics data in a separate DLL with the public variable referencing the data. Please note that any stack segments should NOT BE DEFINED in a Loadable Library. Defining a stack will only wast memory in a library becasue there is already a stack area with the main applicaion. See API.DOC for DOS32 functions EE10h (Setup Library File) and EE11h (Load Library). Examples distributed with DOS32 should also help you understand using these Loadable-Libraries. USING DLINK Since the DOS32 package is mainly for assembly language programming I will assume you are familiar and have used a conventional linker before. Remember DLINK has been designed specifically for flat memory model programs so there is no need to worry about all the modules and segments combining to produce one large executable. If you wish to stop DLINK while it is processing then press the CTRL+BREAK keys, it then should return to the command prompt. DLINK is operated entirely by the command line so you will need to know what each command line switch does. The format of the command line is; DLINK OBJ file list [, EXE/DLL file] [,MAP file] [, LIB file list] The OBJ file list is a list of the Object Modules that DLINK is required to link. Each file name is separated by spaces. If the executable file name is not specified then it will be equal to the name of the first OBJ file. The default file name extension of the Object Module file list and executable file are .OBJ and .EXE respectively. The MAP file is an small text file that the linker creates showing information of all the segments defined in the modules. I included this feature for compatibility with conventional linkers. If no MAP file is specified the file is not created. The LIB file list are library files that the linker is required to process. The format of these files must be in the Microsoft Library file format (.LIB extension). DLINK will also accept command line response files. The response file is a text file containing the input DLINK expects on the command line and has the exact same format as that of Turbo linker (TLINK.EXE) or the Microsoft linker (LINK.EXE) response files. All response files specified must precede the name with an @ character. The response file can be used to overcome the 128 character limit of the command line or is sometimes used by compiler make utilities. When using DLINK with 4DOS command interpreter a maximum command length of 1800 character is allowed. Another feature of DLINK is that it will ignore any module that is not required to run the executable. For example suppose a module contains a particular function, if there are no other modules that call this function then it will not be linked. DLINK will check all modules of the OBJ file list and modules contained in any library files. This will always create an optimized executable file without containing any unnecessary modules. DLINK Options The linker options can go anywhere on the command line and must start with - or / characters. You may turn a switch off my placing a another - or / character immediately after switch character. e.g -S- will turn switch S off as if it was never specified. If the environment variable "DLINK_OPTIONS" is available then DLINK will use this environment string as the beginning of the command line. You can use this environment variable to hold frequently used options, for example, specifying library search paths. Below is a detailed description of each option. Option Description @xx Where xx is the file name of a response file. Multiple response files are allowed and can be placed anywhere on the command line. Nested responce files are not allowed. /v Verbose linking information. This will display all sorts of information that the linker is currently processing. This switch was used for debugging purposes in the early stages DLINK. /m Create MAP file even if none is specified on the command line. /L[;][;]...ect Specify library search paths. This switch tells the linker to also search in the directory when loading library files specified on the command line. More than one path can be specified and this switch can also be used more than once. /c Case sensitive symbol matching. All external symbols that are to be matched with public symbols in other object modules will be compared case sensitive. When this switch is not used, matching is case insensitive. /Sx Use alternate stub file x. The stub loader which is placed at the front of the executable file will be the file x. If x is not specified then DOS32.EXE will be used as the stub loader. If no file extension is specified the extension '.EXE' is assumed. For example, -Sstuber will use the file stuber.exe as the stub loader. When this switch is not used, a default stub loader is used and is only 290 bytes in size. /t Disable all messages to screen if linking is successful. If an error occurs then messages are displayed as normal. /w Display the DOS32 copyright message when the application is first executed. This switch may be used to give a more professional touch to the application. /p Ignore checking of the same symbol name defined more than one object module. Setting this switch may speed up the linker when processing many hundreds of symbols but then has the risk of linking in the same symbol name more than once. /A=N Set segment alignment to N. The starting address of the data defined in each segment definition will be aligned on a N byte boundary. Valid values of N are 1,2,4,8,16,32,64,....,2048,4096 If this switch is not used then the segment alignment defaults to a four byte boundary (/A=4) which is all that is necessary for the 386,486 and Pentium CPUs. /C Compress final .EXE. This option will tell the linker to compress the 32bit executable image in the .EXE file. The stub loader, header and fixup table are kept uncompressed. See reference 10 for the algorithm I used. On average the compression will produces files about 1.2 to 1.6 times that of the ZIP compression. Decompression is done by the loader, DOS32.EXE, and can expand about 1MB/sec on a 486 33Mhz. For tightest compression, avoid creating executables with large arrays of the same byte. If possible, define all arrays with non-initialised data so they are not written directly into the .EXE file. /r["command line"] Create and run executable in memory only instead of writing the EXE file to disk. This option may help speed up developemnt time since there is no time wasted writting the .EXE file to disk. The programs command line may be specified between (") characters. A double ("") will be treated as a single (") character and upto 256 characters can be used. For example: dlink -R"-Iinfile.ext -O""outfile.ext""" will execute the program with the command line of; -Iinfile.ext -O"outfile.ext" /M This option will set the memory limit of DLINK to the number of Kbytes specified in . Use this option when running the executable from memory (switch /R) since the default is 4Gbytes and no memory will be availible for running the executable. /a Force all specified modules to be linked. It tells DLINK not to detect the modules which are not required to run the executable. This will cause linking of every single module thats specified on the command line including ALL modules contained in library files. /d[symbol] Tells DLINK to create a DOS32 Loadable Library file instead of an executable. Where [symbol] specifies the public varible in the Library that will be addressed by the application at load time. If [symbol] is not specified then it defaults to 'DOS32_DLL'. DLINK will match [symbol] with the library's public defeind symbol regardless if it is preceded by a "_" character or not. THE DEBUGGER This DOS extender comes with a complete debugger for the debugging your 32bit protected mode programs. The whole debugger is the file DEBUG.OBJ and must be to be linked in with the program that you wish to debug. The debugger is started by executing a near call to the external procedure named "Debug" or "Debug_Run". Calling "Debug" will immediately start the debugger directly after this call. Calling "Debug_Run" will only initialise the debugger and return to the program as normal. With this later call the debugger will only come into action when a CPU exception occurs (such as a Page Fault, General Protection, breakpoint) or if CTRL+BREAK keys are pressed. In effect, this call is identical as calling 'Debug' and pressing the F9 key to continue executing. You have probably already ran the debugger example program, DEBUG.EXE You will find that it's similar to a conventional debugger except it's for 32bit protected mode. The debugger will not debug when in V86 mode or in real mode. Whenever your program calls real mode (or V86) then the debugger will be disabled until it returns to the program in protected mode. The main feature of the debugger is single-stepping. Single- stepping allows the user to execute one instruction at time and able to see the values of each register. There are two types of stepping available. Pressing the F7 key will step on every instruction except for INT xx. Pressing the F8 key will step on all instructions except for CALL, INT xx, LOOP and instructions with the REP prefix. For these instructions that are not stepped through, the debugger will begin normal execution of the instruction and return back to debugging mode on the following instruction. When stepping through, the debugger saves the value of each register. The debugger has back stepping feature, which allows the user to load every register to the same values as in the previous step. The back stepping keys are ALT+F4 and can only go back up to 40 times. You can also insert breakpoints anywhere in the code simply by pressing F2. This will insert (or delete) an opcode 0CCh which is an single byte to cause an interrupt 3. You will see the instruction highlighted red. When the program is run ( F9 key ), it will stop at the breakpoint instruction and return to the debugger. You can also assemble your programs with an INT 3 instruction at any locations you wish to break. For example, if you have a procedure that hangs the system, you could assemble the program with an INT 3 somewhere inside the procedure. The program may then simply be executed ( F9) and the debugger will take charge soon as the INT 3 is executed. Another feature of the debugger is toggling between the user screen and debuggers screen by ALT+F5. When switching to the users screens, the video mode, palette, cursor location, font memory, text mode screen memory, and all of the _standard_ VGA registers are restored. Non standard VGA registers are not restored so you may have some problems in SVGA modes. The debugger also has it's own keyboard handler. This means the debugger is independent of the BIOS's keyboard handler. Programs can grab IRQ 1 for their own keyboard handlers and do not need to be redirect back to the BIOS. At any time the program is running you may return to the debugger by pressing CTRL+BREAK keys. When stepping through certain instructions a temporary switch to the users screen is made. The instructions that will cause a video switch include, LOOP, INT xx ,CALL, REP prefix, and any access to the VGA ports. This makes sure that the VGA card is properly programmed while debugging. This debugger directly uses the 386's debug registers so don't try running it under DPMI. Windows DPMI seems to handle single-stepping and the software breakpoints, but it's not very reliable and OS/2 hangs with blank screen, yes OS/2 can crash. I have tried using the DPMI services for setting the debug registers but have not had much luck. In my next version I might add support for DPMI but at the moment I cannot be bothered with it. As it is DEBUG.ASM is already over 140KB of assembler...( yuck ) Advanced Debugging As well as software breakpoints ( opcode 0CCh) and single stepping, the 386 has a set of special debug registers. There are four breakpoint registers each of which hold a 32bit linear breakpoint address ( registers DR0..DR3 ). Another debug register ( DR7 ) sets the type of breakpoint for each of the four breakpoint addresses. There is also a debug status register ( DR6 ) which contains information for the exception 1 handler. See the Intel documentation for a complete description of the debug registers. The debuggers exception 1 handler will read DR6 to see what was the caused of the exception. If the exception was caused from one of the four breakpoint addresses then a message will be displayed on the screen telling you which breakpoint it was and if it was an instruction or data breakpoint. At any time the user can hold down the CONTROL key to display the values of the four linear breakpoint addresses and the W, R, and LEN fields from DR7. The debugger does not set any of the debug registers however the program that you are debugging can. Your program can load any of the breakpoint addresses and control fields (e.g. Ri,Ei,LENi & Gi ) and the debugger will report the breakpoint so you can then analyse the program. 32bit Programming Notes If you are new to 32bit assembly or you are converting older 16bit programs into 32bit then there are a few important things you will need to know. First off, all memory refrences should always be using 32bit addrssing modes as opposed to 16bit addressing modes. For example, the instruction MOV AL,Varible[BX] is using 16bit addressing and consequently will have a 16bit displacment value. If this varible is located above 64Kb then the assembler cannot fix up the correct address. The above instruction should be MOV AL,Varible[EBX]. Never use 16bit addressing modes ! Secondly, avoid pushing and poping 16bit registers on the stack becuase these may take the stack pointer out of dword alignment. ESP should always be kept aligned on a 4 byte boundary or else your program may run slower. This is because in 32bit mode, the stack transfers in double words and the CPU will take extra clocks cycles for every misaligned memory reference. The Segment Registers (DS,ES,FS,GS,CS & SS) are always stored as double words on the stack in 32bit mode, so its ok to push and pop these. Using DMA with DOS32 To use the DMA contollers in protected mode a small buffer (16Kb or less) must be used to transfer the data. Direct DMA transfering within the program segment cannot be used because the phyisical address that it is located at is unknown or it could be above the 16Mb DMA address limit. The problem is solved by using a transfer buffer located at a known physical and linear address. For a memory->DMA transfer the data is first copied into the buffer followed by programming the DMA to read the buffer. The transfer of DMA->memory is simply in reverse order to this. The DMA is first programmed to fill the buffer area followed by copying the buffer contents to the destination address. DOS32 function (AX=EE41h INT31h) will allocate a 16KB contiguous memory block that can be used as DMA buffer. Exceptions ( with non DPMI systems ) DOS32 will handle the exceptions interrupts 0 to 31 by displaying a message on the screen of the type of exception, then it exits to DOS. Your program can hook any of these interrupts but it is usually not required. The allocate memory service will build page tables as the memory is allocated. This means that if you try to access memory that was not allocated you may end up with a page fault exception. When running under DPMI, all exceptions will be handled by the DPMI server. Hardware Interrupt Handlers All hardware interrupts IRQ's 0..15 can be hooked in protected mode by using the interrupt services. When you program starts DOS32 will initially handle all the hardware interrupt vectors (including the NMI) so they all will be redirected to the original real mode interrupts. For example, when ever you push a key it will be calling INT 9 in Real/V86 mode. IMPORTANT Never assume what will be in the segment registers in a hardware interrupt handler. You should always preserve the segment registers used and then load them with the desired selector. The interrupt handler may obtain the programs data selector by referencing any segment definition name. This was explained above in 'Reading the program Data Selector'. The other important thing to remember is that when operating under a DPMI server ALL modified interrupt vectors (INTs 0 .. 255) should be returned to their original values before the application terminates. When under Raw/XMS or VCPI this is not necessary to do but should always be done for compatibility with DPMI. bugs, bugs and more bugs A word about program bugs. Of course the more complicated a program is the more likely it's going to have bugs. This is by far the biggest assembler program I have ever written and so I am sure it will have a bug or two. So I would appreciate it if you would contact me on finding any bugs. A program like DOS32 or any DOS extender fiddles a lot around with drivers and hardware, which means compatibility problems across the thousands of different IBM compatible computers available. I don't have access to very many different types of PCs so it is hard for me to find these bugs. Of course it works flawlessly on my computer :-) Known bugs, problems and limitations * DOS32 will not run under Novell DOS 7.0 when using it's EMS driver. An error is reported because DOS32 can only handle interrupt vectors of 8 and 70h for PIC #1 and PIC #2 controllers respectively. The VCPI server in Novell EMS driver remaps the PIC interrupt vectors some other values. However DOS32 should work fine under the Novell DOS Raw, XMS and DPMI configurations. * The Debugger will not correctly write to the screen while single stepping through instructions that are accessing the video memory. * When under Raw/XMS systems and another application has been executed ( e.g using INT21h AH=4Bh ) which switches the 386 into V86 mode and the DOS32 application has hooked a protected mode IRQ or a real mode call back then this protected mode IRQ handler or Call Back procedure will not be called but rather a IRET or RETF will be executed. This avoids DOS32 trying to do a raw switch into protected mode while the 386 is already in control of another protected mode program and thus causing an exception to it. * When under raw/XMS/VCPI systems and the application has hooked interrupt vectors 13 or 14 (IRQs 5 or 6 ) then general exceptions and page faults will no longer be handled and may cause a "triple fault" ( CPU reset ) if one of these exceptions does occur. Limitations of DLINK Description Maximum limit ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Number of externals 1024 per module Number of publics limited by memory available Number of object modules 2048 Number of segments definitions 10 Size of response file 12Kb Size of executable 1Gb Memory required to link approx 200Kb + total size of OBJs and LIBs The continuation of DOS32 I wrote this DOS extender for myself and have customised it to my preferences. For example, I have found no reason to have descriptor allocation services, however some people may desperately need extra descriptors in their programs. The VESA BIOS Extension v2.0 interface supports protected mode applications. 32bit BUS video cards are also becoming more popular with linear memory mapping and so I included the physical memory mapping service, INT31h AX=800h. There are still some improvments to DLINK that I would like to do. The main one is support for unix COFF (Common Object File Format). This would allow DOS32 to be used in conjunction with GNU C/C++ 32bit compiler for DOS, known as DJGPP. It will allow the use of both conventional DOS compilers such as TASM or MASM which support OMF files and DJGPP. Another improvment to DLINK would be better executable file compression. Anyone who has a good compression algorithem and would like it used in DLINK then I can implement it. If there are lots of people who are interested in this DOS extender, it may end up becoming a big project. If people who are willing to improve DOS32 or help with libraries then we may even be able to get a team of people working on the project. I am getting tired of the DOS32 project and I probably will not release many more versions in future. I would really like for other people who are interested in improving DOS32 to take over and make it a bigger and better program for the public domain. Of course, if I decide to do this then I would have to release the entire DOS32 package source code. It depends on how many people are interested in helping out with DOS32. Remember DOS32 is freeware so it's here for everyone to use. Anyone who is interested then please contact me! THE END After reading all this doc, I hope you have made at least some sense out of it. I know my grammar is not the greatest but you cant complain if your getting it for free. I am also probably missing some very important notes about DOS32. So all in all I think ( I hope ) that this DOS extender works out to be a very useful program for developing your software. If you are new to 80x86 programming and what to jump straight into 32bit power (flat memory) without getting involved with the 386's architecture then this DOS extender is probably what your after. For people who have only ever used real mode assembler I bet that they will be hooked to protected mode programming once got the hang of it. The way I see it, there is no reason why people should ever again write real mode assembler programs when they can use DOS32. Best of luck, and remember to support free software ! Adam Seychell. References used in the development of DOS32; 1 Ralf Brown Interrupt listings, Release 41, 6/5/1994 2 DPMI Specification, Version 0.9, 26 July 1990 3 VCPI Specification, Version 1.0, 12 June 1989 4 eXtended Memory Specification (XMS), Version 3.0, Microsoft Corporation, Lotus Development Corporation, Intel Corporation,and AST Research, Inc. January 1991. 5 Microprocessors Vol 1 & 2, Intel Corporation, 1993 6 "Undocumented DOS" 7 S.P.Morse, E.J.Isaacson and D.J.Albert, "The 80386/387 Architecture", Jhon Wiley & Sons, Inc. 1987 8 Microsoft Products Support Services Application Note (Text file) SS0288: Relocatable Object Module Format. Revision 5/92. 9 HelpPC, quick reference utility, David Jurgens, 1991 10 LZSS.C source code of, Storer and Szymanski compression algorithm and was written in C by Haruhiko Okumura who distributed it as public domain in the archive LZ_C.ZIP. MASM & LINK are a trademark of Microsoft corporation. TASM & TLINK are a trademark of Borland International.