CauseWay for Assembly Language pre-release documentation --- Copyright 1992-1993 by Michael Devore and John Wildsmith. All rights reserved. All company and/or product names are trademarks and/or registered trademarks of their respective holders. Last document revision 11/13/93, Michael Devore. Information contained within this document is subject to change without notice. CAUSEWAY LICENSE AGREEMENT ========================== Michael Devore and John Wildsmith, the CauseWay authors, authorize you to make archival copies of CauseWay only for the purpose of backing up your software and thus protecting your investment. You are bound by this agreement to ensure that no unauthorized copies of CauseWay are distributed, installed, or used in any manner. Treat your copy of CauseWay much like a book; you can use it anywhere but only in one place at a time. CauseWay carries a copyright (Copyright 1992-1993) by Michael Devore and John Wildsmith. Use of code or proprietary algorithms within CauseWay, or disassembly of CauseWay code for purposes of using its code or determining the nature of its algorithms for use in competition with CauseWay, is an infringement of CauseWay's copyright. All copyright infringements will be prosecuted to the fullest extent of the law. CAUSEWAY WARRANTY ================= Michael Devore and John Wildsmith warrant the CauseWay material to be free from physical defects in material and workmanship for a period of ninety (90) days from date of purchase. If a defect occurs during this period, you may return the faulty material and it will be replaced free of charge. Michael Devore and John Wildsmith grant no other warranties, express or implied, regarding CauseWay's fitness for any application, use, or purpose. Under no conditions will Michael Devore or John Wildsmith be held liabl for any special or consequential damages, either in contract or tort, arising out of use of, or inability to use, CauseWay. This license agreement and warranty is governed by the laws of the State of Illinois. RETURN POLICY ============= Should you experience a problem using CauseWay, the developers want to know. If they cannot resolved the problem to your satisfaction, they do not expect you to spend money for a product you cannot use. If you are dissatisfied with the operation of CauseWay for any reason and have not redistributed applications which use CauseWay, you may return the CauseWay package for a full refund of purchase price. The developers would rather lose a sale than have a dissatisfied customer. ========== OVERVIEW ========== INTRODUCTION ============ What Is CauseWay? CauseWay is a 386 DOS extender package for use with assembly language programs. It consists of a DOS extender and a protected mode linker, bound together in one executable file (WL32.EXE), as well as several support utilities. CauseWay provides a simple method of allowing 16-bit DOS environments to support very large memory models for applications on PC compatibles with an 80386SX processor or above. To do this, the DOS extender runs CauseWay processed applications in protected mode, rather than the real mode normally used in the DOS environment. Support for both 16-bit and 32-bit protected mode applications operating under a DOS environment is provided. CauseWay makes full use of 386-level chip capabilities including demand paging of code and data, variable-sized segments up to 4G in length, mixed 16- and 32-bit segments within the same program, as well as supporting the flat, nonsegmented, or NEAR addressing model. Applications created using CauseWay are compatible with the VCPI & DPMI standards, as well as with systems with no other protected mode programs running. This means that CauseWay applications will work with DesqView, Windows 3.0 and above in all three modes, as well as DOS windows within OS/2 2.0 and above. CauseWay allocates extended memory from DPMI, VCPI, XMS, and INT 15h. Any available conventional memory is also allocated. The created application can then allocate memory using CauseWay's services without the need to detect or use any other memory type. The primary concern during development of CauseWay was to ensure minimum effort would be needed by programmers to adapt their code to work with the CauseWay DOS extender. As a result, many real mode programs only need to be re-linked with WL32 to produce a 16-bit protected mode version. Full advantage of CauseWay's capabilities can only be realized by producing 32-bit programs, but CauseWay's ability to mix 16 and 32-bit code should ensure that this transition is as painless as possible. MINIMUM SYSTEM REQUIREMENTS =========================== CauseWay requires a 386SX based computer or better and 250K free conventional memory. The required operating environment is MS-DOS or PC-DOS 3.1 or higher, Windows 3.0 or higher, or OS/2 2.0 within a DOS window. ENVIRONMENT VARIABLES ===================== CauseWay can make use of four environment variables. The WL32 protected mode linker will use two environment variables when linking: OBJ and LIB. OBJ Environment Variable ------------------------ The OBJ environment variable contains the name of one or more directories that are used if WL32 does not find an object module in the current directory. The OBJ environment variable is identical to the LIB environment variable except WL32 searches for object modules with the OBJ environment variable instead of searching library files. For example, assume your current directory is C:\ASMWORK. The following command links DISPLAY.OBJ and DRIVER.OBJ with SA.LIB: WL32 DISPLAY DRIVER,,,SA Without an OBJ environment variable, if DISPLAY.OBJ or DRIVER.OBJ are not in the C:\ASMWORK directory, WL32 issues a DOS error 2 (file not found) error. To specify that object modules may also be found in the \ASMWORK\SUPPORT directory on the current drive or the \HARDWARE directory on the F drive, set the OBJ environment variable as follows: SET OBJ=\ASMWORK\SUPPORT;F:\HARDWARE If DISPLAY.OBJ or DRIVER.OBJ are not found in the C:\ASMWORK directory, WL32 will also search the current drive's \ASMWORK\SUPPORT directory and the F drive's \HARDWARE directory for the object modules. LIB Environment Variable ------------------------ The LIB environment variable contains the name of one or more directories that are used if WL32 does not find a library file in the current directory. The LIB environment variable is identical to the OBJ environment variable except WL32 searches for library files with the LIB environment variable instead of searching library files. To use the previous OBJ environment variable example, assume your current directory is C:\ASMWORK. The following command links DISPLAY.OBJ and DRIVER.OBJ with SA.LIB: WL32 DISPLAY DRIVER,,,SA Without a LIB environment variable, if SA.LIB is not in the C:\ASMWORK directory, WL32 issues a DOS error 2 (file not found) error. To specify that library files may also be found in the \SA or \LIB directories, set the LIB environment variable as follows: SET LIB=\SA;\LIB If SA.LIB is not found in the C:\ASMWORK directory, WL32 will also search the current drive's \SA and \LIB directories for the object modules. TEMP and TMP Environment Variables ---------------------------------- The DOS extender can make use of the TEMP or TMP environment variable when a CauseWay application is run. This environment variable determines the directory, and optionally the drive, where a swap file is built by the virtual memory manager (VMM). The path indicated by TEMP will be used if both are present. For example: SET TMP=C:\SWAP directs the DOS extender to create its swap file, if any, in the C:\SWAP directory. Since WL32 itself contains the CauseWay DOS extender, you may use the TEMP or TMP environment variable to specify a swapfile directory when linking a program. If neither of these environment variables are present then the current drive is used. If free drive space is less than physical memory available at startup then the VMM will be automatically disabled. OPERATING CAUSEWAY ================== Use of CauseWay follows the standard edit-compile/assemble-link programming cycle. CauseWay applications are created by first assembling or compiling the CauseWay-compliant source code to produce object modules. These object modules, and optionally support libraries, are then linked using the WL32 protected mode linker. The linker automatically adds the DOS extender to the executable file created. When your application is executed, CauseWay is loaded first by DOS. Once CauseWay has established the protected mode environment, your application is retrieved from the same .EXE file and executed. ============== DOS EXTENDER ============== WRITING CAUSEWAY-COMPLIANT PROGRAMS =================================== Switching from programming in real mode to protected mode using CauseWay should be a simple process. Most aspects of the DOS enviroment are retained and for simple programs little or no alteration to source code will be required. With the exception of segment arithmetic and code segment variables you won't need to change the way you program unless you need to allocate memory in >1Meg chunks or install your own hardware interrupt handlers. Because the segment registers in protected mode are actually an index into a table which holds the real segment information, protected mode segment register values cannot be used directly in math operations. There are API services to obtain the real address referenced by a segment register should this information be needed. The most common alteration needed to get an existing application working under CauseWay is removal of the code to shrink the application's memory block. Because this needs segment arithmetic to work in real mode it cannot be used in protected mode. Fortunately you only need to remove the code, not rewrite it because CauseWay allocates a block of memory that contains your application exactly and does not need shrinking. Segments in protected mode have several attributes which control their use. The most important aspect of this is whether the segment is code or data. Unlike real mode, a code segment can only be executed or read from and a data segment can be read only or read/write. This means that code segment variables will need special consideration before your application will work with CauseWay. You can either move all the variables into a data segment or use the API AliasSel service to allocate a data selector that referances the same memory region as the code segment and use the selector allocted to access your code segment variables instead of CS. CauseWay supports several programing models, the simplest being 16-bit multi segment programs much the same as you would produce for real mode. A 32-bit version of the multi segment model is also supported which offers more efficient code if you use the extended registers a lot or need very large segments. The final model supported is refered to as 'NEAR', also commonly known as the 'flat' model. In this model only a single segment is used to contain code, data and stack. Combined with the related API services this model removes the complication of segment registers almost completely with all relevant API dynamic memory manipulation being specified using 32-bit near pointers. It should be pointed out here that there are no instructions available to 32-bit applications that cannot also be used in a 16-bit application. The distinction between the two types of application relates to the default operation size of segments and the stack frame for interrupt handlers. In a 16-bit code segment use of the extended registers or dword variables requires a prefix byte to be added to the instruction code generated by the assembler, which both increases memory usage and slows the code down. It is still possible to form addresses >64k in 16-bit mode. Use of 16-bit registers or memory variables in a 32-bit segment has the same effect on code generation and speed. String instructions such as MOVSB and the LOOP instruction behave differently in 16 and 32-bit code segments. In a 16-bit code segment SI, DI and CX are the default registers used whereas the defaults in a 32-bit code segment are ESI, EDI and ECX. These defaults can be changed for a single instruction using manual prefix byte generation. A "db 67h" before a MOVSB will change the address size (SI,DI or ESI,EDI) and a "db 66h" before a LOOP will change the iteration size (CX or ECX). Manual use of these prefix bytes makes it possible to perform the same operations in both modes and this difference between 16 and 32-bit code segments is another common cause of problems. The CauseWay functions provided are based on the DPMI 1.0 specification and additionally offer enhancements not provided by DPMI. This means that %99 of the DPMI 1.0 functions are available to the CauseWay programmer in all systems providing access to lower level functions than the CauseWay API should they ever be needed. Hardware interrupts are always reflected to protected mode handlers even when signaled during real mode operations ensuring that your protected mode applications always retain control without resorting to patching real mode interrupt vectors yourself. The remaining interrupts will be serviced via the vector table appropriate to the mode but the real to protected mode Call Back services can be used to provide real mode code with access to protected mode code, effectively allowing any interrupt to be re-signaled in protected mode. With the exception of int's that require segment&|offset pointers as parameters, all interrupts can be issued as normal. The most common INT ?? API's that require segment&|offset pointers are intercepted by CauseWay to provide normal access to these services. Any other real mode int services that require segment&|offset pointers can still be acessed using CauseWay's simulate real mode interrupt/far call services. Unhandled exceptions will terminate the program with a register display dump to screen and a text file called CW.ERR. If you add your own hardware interrupt handlers, such as the timer tick at vector 08h, any memory touched by the handler, including its code, must reside in locked memory (a locked stack will be provided by CauseWay). This limitation is imposed by DOS not being re-entrant and the simple fact that hardware interrupts can occur at any time, including during DOS activity which would prevent CauseWay's virtual memory manager accessing its swap file. Locked memory will never be moved to the swap file. CAUSEWAY SERVICES ================= CauseWay provides an API for assembly language programmers as an extension of the DPMI API. Programmers may include the file CW.INC in their program for easy access to this API through appropriately named functions. Additional modules provide access to the CauseWay API for other languages. CauseWay also provides a large number of the DPMI 1.0 API services on systems without a true DPMI server. The function numbers supported are as follows: 0000h-0001h, 0003h, 0006h-000Ch, 0100h-0102h, 0200h-0205h, 0300h-0301h, 0303h-0304h, 0400h, 0500h-0503h, 0600h-0604h, 0702h-0703h, 0800h-0801h, 0900h-0902h. CauseWay provides an API for asm programmers as an extension of the DPMI API. Additional modules provide access to the CauseWay API for other languages. Some of the API services require pointers to a "real mode register list". The format of this list is as follows: dword EDI dword ESI dword EBP dword Reserved dword EBX dword EDX dword ECX dword EAX word Flags word ES word DS word FS word GS word IP word CS word SP word SS The values will be passed to the target routine without any interpretation of its contents so there is no need to set the high words of the extended register entries unless the target routine requires them. Stack frames for 16 bit interrupts are the same as for real mode. Stack frames for 32-bit interrupts use dword entries, eg, dword EFlags dword CS, high word undefined. dword EIP The default CauseWay API is as follows: Info Get system selectors/flags. Inputs: AX= 0ff00h Outputs: AX= Selector for real mode segment address of 00000h, 4G limit. BX= Selector for current PSP segment. 100h limit. [E]CX= Dos transfer buffer size. Always <64k. DX= Dos transfer buffer real mode segment address. ES:[E]SI= Dos transfer buffer address. ESI+ECX ALWAYS <64k. EDI= System flags. Bits significant if set. 0 - 32 bit code default. 1 - Virtual memory manager functional. 2 \ Mode, 0 - raw, 1 - VCPI, 2 - DPMI. 3 / 4 - DPMI available. 5 - VCPI available. 6 - No memory managers. 7 - Descriptor table type. 0 - GDT, 1 - LDT. 15 - Debug engine present. Errors: None Notes: Bits 1-2 of DI indicate the interface type being used by CauseWay. Bits 4-5 indicate the interface types that are available. Bit 7 indicates the descriptor table being used to allocate selectors to the application when on a raw/vcpi system. The Dos transfer buffer is the area CauseWay uses to transfer data between conventional and extended memory during DOS interrupts. This memory can be used as temporary work space for your own access to real mode code as long as you remember it may be over written the next time you issue an INT in protected mode that requires segment&|offset pointers. IntXX Simulate real mode interrupt. Inputs: AX= 0ff01h BL= Interupt number. ES:[E]DI= Real mode register list. Outputs: Register list updated. Errors: None. Notes: The real mode register list referenced by ES:[E]DI should contain the register values you want passing to the real mode interrupt handler. The SS:SP & Flags entries are filled in by CauseWay to ensure legal values are used and the CS:IP entries are ignored. This function bypasses protected mode interrupt handlers and provides access to INT API's that require segment &| offset pointers. FarCallReal Simulate real mode far call. Inputs: AX= 0ff02h ES:[E]DI= Real mode register list. Outputs: Register list updated. Errors: None. Notes: This function works much the same as IntXX but provides a 16 bit FAR stack frame and the CS:IP values are used to pass control to the real mode code. GetCallBack Allocate real mode call back address. Inputs: AX= 0303h DS:[E]SI= Call address. ES:[E]DI= Real mode register structure. Outputs: Carry set on error, else, CX:DX= Real mode address to trigger mode switch. Errors: CallBack's are a limited resource. There will normaly only be 16 available per virtual machine so they should be used carefully and released as soon as they are no longer required. Call-Back: Interrupts disabled. DS:[E]SI = Selector:Offset of real mode SS:SP. ES:[E]DI = Selector:Offset of real mode call structure. SS:[E]SP = Locked protected mode stack. All other registers undefined To return from Call-Back Procedure, Execute an IRET to return ES:[E]DI = Selector:Offset of real mode call structure to restore. Notes: Real mode CallBack's provide a means of switching from real mode to protected mode. The address returned by this function is a unique real mode address that when given control in real mode will switch to protected mode and pass control to the protected mode routine supplied at entry to this function. On entry to the protected mode code the "Real mode register structure" will contain all the real mode register values. RelCallBack Release a real mode call back entry. Inputs: AX= 0304h CX:DX= Real mode address returned by GetCallBack Outputs: None. Notes: This function should be used to release CallBack addresses once they are no longer needed. GetVect Get Protected mode interrupt handler address. Inputs: AX= 0204h BL= Interupt vector number. Outputs: CX:[E]DX= selector:offset of handler. Errors: None. SetVect Set Protected mode interrupt handler address. Inputs: AX= 0205h BL= Interupt vector number. CX:[E]DX= selector:offset of new handler. Outputs: None. Errors: None. GetRVect Get real mode interrupt handler address. Inputs: AX= 0200h BL= Interupt vector number. Outputs: CX:DX= selector:offset of handler. Errors: None. SetRVect Set real mode interrupt handler address. Inputs: AX= 0201h BL= Interupt vector number. CX:DX= selector:offset of new handler. Outputs: None. Errors: None. GetEVect Get Protected mode exception handler address. Inputs: AX= 0202h BL= Exception vector number. Outputs: Carry set on error else, CX:[E]DX= selector:offset of handler. Errors: The number in BL must be in the range 0-1Fh. Anything outside this range will return carry set. SetEVect Set Protected mode exception handler address. Inputs: AX= 0203h BL= Exception vector number. CX:[E]DX= selector:offset of new handler. Outputs: Carry set on error. Errors: The number in BL must be in the range 0-1Fh. Anything outside this range will return carry set. GetSel Allocate a new selector. Inputs: AX= 0ff03h Outputs: Carry set on error else, BX= Selector. Errors: Approximatly 8192 selectors are initially available. This is quite a lot but it is obviously possible to run out. Notes: A selector is allocated and initialised with a base of 0, a limit of 0 and as read/write expand up data. Use SetSelDet to make the selector useful. RelSel Release a selector. Inputs: AX= 0ff04h BX= Selector. Outputs: Carry set on error. Errors: If an invalid selector is passed in BX this function will return with carry set. Notes: Use this function to release selectors allocated by GetSel or AliasSel. CodeSel Make a selector execute/read type. Inputs: AX= 0ff05h BX= Selector. CL= Default operation size. (0=16 bit,1=32 bit) Outputs: Carry set on error. Errors: If an invalid selector is passed in BX this function will return with carry set. Notes: Allows a selector to be converted to a type suitable for execution. AliasSel Create a read/write data selector from source selector. Inputs: AX= 0ff06h BX= Source selector Outputs: Carry set on error else, AX= New data selector Errors: If an invalid selector is passed in BX this function will return with carry set. Notes: This function always creates a read/write data selector regardless of the source selector's type. Can be used to provide access to variables in a code segment. GetSelDet Get selector linear base and limit. Inputs: Carry set on error else, AX= 0ff07h BX= Selector Outputs: Carry set on error else, CX:DX= Linear base. SI:DI= Byte granular limit. Errors: If an invalid selector is passed in BX this function will return with carry set. GetSelDet32 Get selector linear base and limit. Inputs: AX= 0ff08h BX= Selector Outputs: Carry set on error else, EDX= Linear base. ECX= Byte granular limit. Errors: If an invalid selector is passed in BX this function will return with carry set. SetSelDet Set selector linear base and limit. Inputs: AX= 0ff09h BX= Selector. CX:DX= Linear base. SI:DI= Byte granular limit. Outputs: Carry set on error. Errors: If an invalid selector is passed in BX this function will return with carry set. SetSelDet32 Set selector linear base and limit. Inputs: AX= 0ff0ah BX= Selector. EDX= Linear base. ECX= Byte granular limit. Outputs: Carry set on error. Errors: If an invalid selector is passed in BX this function will return with carry set. GetMem Allocate a block of memory. Inputs: AX= 0ff0bh CX:DX= Size of block required in bytes. (-1:-1 to get maximum memory size) Outputs: Carry set on error else, BX= Selector to access the block with. or if CX:DX was -1, CX:DX= size of largest block available. Errors: The amount of memory available is limited by physical memory present and free disk space of the drive being used by the VMM. If CauseWay is unable to find a large enough block this function will return carry set. Notes: This function allocates a block of extended (application) memory, and also a selector with a suitable base & limit. GetMem32 Allocate a block of memory. Inputs: AX= 0ff0ch ECX= Size of block required in bytes. (-1 to get maximum memory size) Outputs: Carry set on error else, BX= Selector to access the block with. or if ECX was -1, ECX= size of largest block available. Errors: See GetMem Notes: This function allocates a block of extended (application) memory, and also a selector with a suitable base & limit. ResMem Resize a previously allocated block of memory. Inputs: AX= 0ff0dh BX= Selector for block. CX:DX= New size of block required in bytes. Outputs: Carry set on error. Errors: If an invalid selector is passed in BX or not enough memory is available when increasing the block size then this function will return carry set. Notes: If the memory block can't be resized in its current location, but a free block of memory of the new size exists, the memory will be copied to a new block and the old one released. This is transparent to the application as long as only the selector origionaly allocated with GetMem is being used to access the memory. ResMem32 Resize a previously allocated block of memory. Inputs: AX= 0ff0eh BX= Selector for block. ECX= New size of block required in bytes. Outputs: Carry set on error. Errors: See ResMem Notes: If the memory block can't be resized in its current location, but a free block of memory of the new size exists, the memory will be copied to a new block and the old one released. This is transparent to the application as long as only the selector originally allocated with GetMem is being used to access the memory. RelMem Release memory allocated by either GetMem or GetMem32. Inputs: AX= 0ff0fh BX= Selector for block to release. Outputs: Carry set on error. Errors: If an invalid selector is passed in BX or the memory was not allocated via GetMem or GetMem32 this function will return carry set. GetMemLinear Allocate a block of memory without a selector. Inputs: AX= 0ff10h CX:DX= Size of block required in bytes. Outputs: Carry set on error else, SI:DI= Linear address of block allocated. Errors: If not enough memory is available to satisfy the request then this function will return carry set. Notes: Addresses returned by this function may be >16M GetMemLinear32 Allocate a block of memory without a selector. Inputs: AX= 0ff11h ECX= Size of block required in bytes. Outputs: Carry seet on error else, ESI= Linear address of block allocated. Errors: See GetMemLinear Notes: Addresses returned by this function may be >16M ResMemLinear Resize a previously allocated block of memory without a selector. Inputs: AX= 0ff12h SI:DI= Linear address of block to resize. CX:DX= Size of block required in bytes. Outputs: Carry set on error else, SI:DI= New linear address of block. Errors: If not enough memory is available when extending the block size this function will return carry set. Notes: If the memory block cannot be expanded to the desired size, and a free block of sufficient size exists, the existing memory will be copied to the free block and released, the new block then being alocated in place of the old. ResMemLinear32 Resize a previously allocated block of memory without a selector. Inputs: AX= 0ff13h ESI= Linear address of block to resize. ECX= Size of block required in bytes. Outputs: Carry set on error else, ESI= New linear address of block. Errors: See ResMemLinear Notes: If the memory block cannot be expanded to the desired size, and a free block of sufficient size exists, the existing memory will be copied to the free block and released, the new block then being alocated in place of the old. RelMemLinear Release previously allocated block of memory (linear address). Inputs: AX= 0ff14h SI:DI= Linear address of block to release. Outputs: Carry set on error. Errors: If the address passed in SI:DI is not a valid memory block this function will return carry set. RelMemLinear32 Release previously allocated block of memory (linear address). Inputs: AX= 0ff15h ESI= Linear address of block to release. Outputs: Carry set on error. Errors: See RelMemLinear GetMemNear Allocate an application relative block of memory. Inputs: AX= 0ff16h EBX= Size of block required in bytes. Outputs: Carry set on error else, ESI= Application relative linear address of block allocated. Errors: If there is not enough memory available to satisfy the request then this function will return carry set. Notes: Addresses returned by this function are as an offset from the start of the applications memory and is intended to be used with the "NEAR" memory model. ResMemNear Resize a previously allocated application relative block of memory. Inputs: AX= 0ff17h EBX= Size of block required in bytes. ESI= application relative linear address of block to resize. Outputs: Carry set on error else, ESI= New application relative linear address of block. Errors: If the address passed in ESI is not a valid memory block or there is not enough memory available when expanding the block this function will return carry set. Notes: If the memory block cannot be expanded to the desired size, and a free block of sufficient size exists, the existing memory will be copied to the free block and released, the new block then being allocated in place of the old. RelMemNear Release previously allocated application relative block of memory. Inputs: AX= 0ff18h ESI= Application relative linear address of block to release. Outputs: Carry set on error. Errors: If the address passed in ESI is not a valid memory block this function will return carry set. Linear2Near Convert linear address to application relative address. Inputs: AX= 0ff19h ESI= Linear address to convert. Outputs: ESI= Application relative linear address. Errors: None Near2Linear Convert application relative address to linear address. Inputs: AX= 0ff1ah ESI= Application relative linear address. Outputs: ESI= Linear address. Errors: None LockMem Lock a region of memory. Inputs: AX= 0ff1bh BX:CX= Starting linear address of memory to lock. SI:DI= Size of region to lock in bytes. Outputs: Carry set on error. Errors: If any of the region specified is invalid or if not enough physical memory is available to fill the region specified then none of the memory will be locked and this function will return carry set. Notes: Memory that is locked cannot be swapped to disk by the VMM. You should note that locking is applied to memory on 4k boundaries, so areas of memory below and above the memory being locked will also be locked if the specified region is not 4K aligned. LockMem32 Lock a region of memory. Inputs: AX= 0ff1ch ESI= Starting linear address of memory to lock. ECX= Size of region to lock in bytes. Outputs: Carry set on error. Errors: See LockMem. Notes: Memory that is locked cannot be swapped to disk by the VMM. You should note that locking is applied to memory on 4k boundaries, so areas of memory below and above the memory being locked will also be locked if the specified region is not 4K aligned. UnLockMem Unlock a region of memory. Inputs: AX= 0ff1dh BX:CX= Starting linear address of memory to unlock SI:DI= Size of region to unlock in bytes Outputs: Carry set on error. Errors: If any of the memory region specified is invalid this function will return carry set. Notes: This will allow the memory to be swapped to disk by the VMM if necessary. Areas below and above the specified memory will also be unlocked if the specified region is not page aligned. UnLockMem32 Unlock a region of memory. Inputs: AX= 0ff1eh ESI= Starting linear address of memory to unlock ECX= Size of region to unlock in bytes Outputs: Carry set on error. Errors: See UnLockMem Notes: This will allow the memory to be swapped to disk by the VMM if necessary. Areas below and above the specified memory will also be unlocked if the specified region is not page aligned. LockMemNear Lock a region of memory using application relative address. Inputs: AX= 0ff1fh ESI= Starting linear address of memory to lock. EBX= Size of region to lock in bytes. Outputs: Carry set on error. Errors: See LockMem Notes: Memory that is locked cannot be swapped to disk by the VMM. You should note that locking is applied to memory on 4k boundaries, so areas of memory below and above the memory being locked will also be locked if the specified region is not 4K aligned. UnLockMemNear Unlock a region of memory using application relative address. Inputs: AX= 0ff20h ESI= Starting linear address of memory to unlock EBX= Size of region to unlock in bytes Outputs: Carry set on error. Errors: See UnLockMem Notes: This will allow the memory to be swapped to disk by the VMM if necessary. Areas below and above the specified memory will also be unlocked if the specified region is not page aligned. GetMemDOS Allocate a region of DOS (conventional) memory. Inputs: AX= 0ff21h BX= Number of paragraphs (16 byte blocks) required. Outputs: Carry set on error and BX= largest block size, AX=DOS error else, AX= Initial real mode segment of allocated block DX= Initial selector for allocated block Errors: If there are not enough selectors or memory available then this function will return carry set. Notes: If the size of the block requested is greater than 64K bytes (BX > 1000h) then contiguous descriptors will be allocated. If more than one descriptor is allocated under 32-bit applications, the limit of the first descriptor will be set to the size of the entire block. All subsequent descriptors will have a limit of 64K except for the final descriptor which will have a limit of Block size MOD 64K. 16-bit applications will always set the limit of the first descriptor to 64K. ResMemDOS Resize a block of DOS (conventional) memory allocated with GetMemDOS. Inputs: AX= 0ff22h BX= New block size in paragraphs DX= Selector of block to modify Outputs: Carry set on error, AX= DOS error code, BX= Maximum para block size. Errors: If an invalid block is passed or if not enough selectors or memory are available when expanding the block this function will return carry set. Notes: Growing a memory block is often likely to fail since other DOS block allocations will prevent increasing the size of the block. Also, if the size of a block grows past a 64K boundary then the allocation will fail if the next descriptor in the LDT is not free. RelMemDOS Release a block of DOS (conventional) memory allocated by GetMemDOS. Inputs: AX= 0ff23h DX= Selector of block to free. Outputs: Carry set on error and AX= DOS error code. Errors: If an invalid block is passed this function will return carry set. Notes: All descriptors allocated for the memory block are automatically freed and therefore should not be accessed once the block is freed by this function. ExecOverlay Load, relocate and optionally execute application code. Inputs: AX= 0fffdh EBX= Flags, bit significant if set. 0 - Don't execute. 1 - Don't preserve segment/relocation memory. ES:EDX= File name. FS:ESI= Command line. ESI=0 for none. First byte is length. GS:EDI= Name (/o:? option in CW). EDI=0 for none (Not used yet). Outputs: Carry set on error and AX= Error code else, If bit 1 of EBX was set on entry then: CX:EDX = Entry CS:EIP SI= PSP segment. if bit 0 set & bit 1 not set of EBX on entry: BX:EAX= entry SS:ESP EDI= High word - base segment, low word - number of segments. EBP= Start of segment definitions. Errors: 1 - DOS file access error. 2 - Not a CauseWay 3P file. 3 - Not enough memory. GetDOSTrans Get current address and size of the buffer used for DOS memory transfers. Inputs: AX = 0ff25h Outputs: BX = Real mode segment of buffer. DX = Protected mode selector for buffer. ECX = Buffer size Errors: None Notes: This buffer is used by the built in INT API translation services, e.g., INT 21h, AH=40h (write to file). The default buffer is 8k and uses memory that would otherwise be wasted. This default is sufficient for most file I/O but if you are writing a program that reads/writes large amounts of data you should consider allocating your own larger buffer and pass the address to CauseWay to speed this file I/O. SetDOSTrans Set new address and size of the buffer used for DOS memory transfers. Inputs: AX = 0ff26h BX = Real mode segment of buffer. DX = Protected mode selector for buffer. ECX = Buffer size (should be <=64K) Outputs: None Errors: None Notes: The buffer must be in conventional memory and only the first 64K will be used even if a bigger buffer is specified. CauseWay will automatically restore the previous buffer setting when the application terminates but GetDOSTrans can be used to get the current buffer's settings if you only want the change to be temporary. You can still use the default buffer for your own puposes even after setting a new address. NOTES ----- A fixed segment selector at 40h is always available to the application. This selector maps the real mode memory at 400h where most of the BIOS variables can be found. Their is a possibility of this selector being dropped in future DPMI specifications so complete compatibility with DPMI can only be ensured by allocating your own selector for this memory region. When not running under a DPMI server CauseWay provides selectors at addresses 0B000h, 0B800h and 0A000h to ease conversion of real mode code. DS & ES are set to the programs PSP at entry. The environment variable in the PSP is a valid protected mode selector. CauseWay allocates a block of memory that contains the application and its PSP exactly. It is not necesary to shrink the programs memory. The application should terminate using int 21h function 4Ch. The error level passed to this function will be returned to any parent programs in the normal way. If the application needs to TSR you can use int 21h function 31h but no memory value is required, all memory owned by the application when the TSR is issued will remain the programs property. You should note that there is currently no way of removing the application from memory once it is TSR. Interrupt handlers in a 32-bit application will need to do an IRETD rather than the normal IRET. I've not come across an assembler that automatically generates the right size from a normal IRET instruction yet. INTERRUPT SERVICES EXTENDED OR ALTERED BY CAUSEWAY ================================================== The size of register used will depend on the limit of the selector supplied. Unspecified registers should be set up in the normal way. INT API's not listed here that require segment &| offset pointers will need to be handled using IntXX or your own interrupt translation code. INT 10h ------- 10h sub function 02h, [E]DX instead of DX. 10h sub function 09h, [E]DX instead of DX. 10h sub function 12h, [E]DX instead of DX. 10h sub function 17h, [E]DX instead of DX. 13h [E]BP instead of BP. 1Ch sub function 01h, [E]BX instead of BX. 1Ch sub function 02h, [E]BX instead of BX. INT 21h ------- 09h [E]DX instead of DX 0Ah [E]DX instead of DX 1Ah [E]DX instead of DX 25h [E]DX instead of DX. Protected mode vector will be set. 2Fh [E]BX instead of BX 31h No value is required in DX. 35h [E]BX instead of BX. Protected mode vector will be returned. 39h [E]DX instead of DX 3Ah [E]DX instead of DX 3Bh [E]DX instead of DX 3Ch [E]DX instead of DX 3Dh [E]DX instead of DX 3Fh [E]DX instead of DX 40h [E]DX instead of DX 41h [E]DX instead of DX 43h [E]DX instead of DX 44h sub function 02h, use [E]DX instead. 44h sub function 03h, use [E]DX instead. 44h sub function 04h, use [E]DX instead. 44h sub function 05h, use [E]DX instead. 47h [E]SI instead of SI 48h Protected mode memory will be allocated. 49h Protected mode memory will be released. 4Ah Protected mode memory will be resized. 4Bh [E]DX & [E]BX instead of DX & BX. Parameter block offset entries are w|d. 4Eh [E]DX instead of DX 56h [E]DX & [E]DI instead of DX & DI 5Ah [E]DX instead of DX 5Bh [E]DX instead of DX 62h Protected mode selector will be returned. 6Ch [E]SI instead of SI INT 33h ------- 09h [E]DX instead of DX. 0Ch [E]DX instead of DX. 16h [E]DX instead of DX. 17h [E]DX instead of DX. The size of register used will depend on the limit of the selector supplied. Unspecified registers should be set up in the normal way. INT 23h ------- This interrupt is always reflected back to the protected mode handler to ensure your program can deal with it correctly. The default handler will abort your application in the same manner as DOS. If you need to terminate your application in your own handler, perform an int 21h, ah=4ch as normal. INT 24h ------- This interrupt is always reflected back to the protected mode handler to ensure your program can deal with it correctly. The default handler behaves in the same way as the DOS handler, aborting your application if appropriate. If you install your own handler, all memory touched by this interrupt must be locked. The register values normally placed on the stack by DOS before entry to the interrupt handler will not be present in protected mode, only the register values will be valid. You may terminate your application from within this interrupt with int 21h, ah=4ch as normal. USING THE FLAT OR NEAR MODEL ============================ If you declare a single 32-bit segment within your program and specify a class of NEAR, any segment name references will assume a selector of DATA is required. If you should need to reference the CODE selector, load it from CS. The segment will be extended by either the default stack size of 1K or the value specified by WL32's /ST option. The program's entry ESP value will be set to the end of this segment with the entry SS value using the segment defined as 'near'. This means that SS/ESP/EBP addressing will also be near. For example, A_SEGMENT segment para 'near' mov ax,A_SEGMENT ;A selector with a type of DATA will be loaded. mov ds,ax ;Could get this value from SS instead though. mov es,ax mov fs,ax ;Now all segment registers will map the same mov gs,ax ;region of memory and have a limit of 4G. A_SEGMENT ends Only a single segment definition is allowed when a class of NEAR is used. ============== WL32 LINKER ============== USING WL32 ========== Operate WL32 from the DOS command line or a batch or make file. There are no menus and operating modes. You simply type WL32 followed by your specific command options and one or more filenames. WL32 will be very familiar to users of Borland's TLink or Microsoft Link. Use the following format for running WL32 from the DOS prompt: WL32 [command options] OBJECT_FILES,[program_file],[map_file],[library_files] WL32 uses default extensions for the object files, program file, map file, and library files. You may omit a file extension and WL32 will look for a file by using the name given with the appropriate default extension. The default extension for various files are: Program File .EXE Library File .LIB Map File .MAP Object File .OBJ You can override the default extension by the explicit use of a different extension in the file name. If no executable file name is listed, WL32 will use the first .OBJ file name with a .EXE extension. If no map file name is listed and the /m option is specified, Wl32 will use the .EXE file name with a .MAP extension. RESPONSE FILES ============== You can store command options and file names that you entered on the DOS command in a response file. Response files are often referred to as link files, command scripts, or script files. Use response files to ease repetitive linking tasks and to allow longer commands than can be typed on the DOS command line. See also the Configuration File chapter for more information on response files. Response File Format -------------------- Use the following format for using a response file with WL32: WL32 @FILENAME.EXT The @ symbol designates the file as a response file instead of a .OBJ file or .LIB file. You may list more than one response file when linking and can nest one response file inside another (this is most useful when linking in freeformat mode described in the Configuration File chapter). The line length for a response file must be 125 characters or less. When using response files, you must specify the file extension if one is desired. WL32 does not assume a .LNK or .RSP file extension. Using A Response File --------------------- A response file contains exactly what you would type after WL32 on the DOS command line. To break up a response file into several lines in default linker behavior (nonfreeformat), use a plus sign at the end of the line for lists of .OBJ and .LIB files. Comments In A Response File --------------------------- You can add comments to WL32's response files to help make them clearer or for documentation purposes. Use the pound sign (#) in a line to insert comments into a response file. All characters on a line after the pound sign will be ignored by WL32. CONFIGURATION FILES =================== WL32 supports linker script "morphing" commands. Morphing capability allows WL32 to read link files and scripts of any linker without changing those files. This includes both positional and freeformat link files and scripts. To use this feature, place the morphing commands in a linker definitions file and specify the file through the /lc: option. With all morphing commands, case is significant and the linker will return an error for a lowercase command. However, case is not significant for the or