182 lines
7.3 KiB
Plaintext
182 lines
7.3 KiB
Plaintext
This is intended to be a document to help new developers get started.
|
|
Existing developers should feel free to add their comments.
|
|
|
|
MEMORY AND SEGMENTS
|
|
===================
|
|
|
|
NE (Win16) executables consist of multiple segments. The Wine loader
|
|
loads each segment into a unique location in the Wine processes memory
|
|
and assigns a selector to that segment. Because of this, it's not
|
|
possible to exchange addresses freely between 16-bit and 32-bit code.
|
|
Addresses used by 16-bit code are segmented addresses (16:16), formed
|
|
by a 16-bit selector and a 16-bit offset. Those used by the Wine code
|
|
are regular 32-bit linear addresses.
|
|
|
|
There are four ways to obtain a segmented pointer:
|
|
- Use the SEGPTR_* macros in include/heap.h (recommended).
|
|
- Allocate a block of memory from the global heap and use
|
|
WIN16_GlobalLock to get its segmented address.
|
|
- Allocate a block of memory from a local heap, and build the
|
|
segmented address from the local heap selector (see the
|
|
USER_HEAP_* macros for an example of this).
|
|
- Declare the argument as 'segptr' instead of 'ptr' in the spec file
|
|
for a given API function.
|
|
|
|
Once you have a segmented pointer, it must be converted to a linear
|
|
pointer before you can use it from 32-bit code. This can be done with
|
|
the PTR_SEG_TO_LIN() and PTR_SEG_OFF_TO_LIN() macros. The linear
|
|
pointer can then be used freely with standard Unix functions like
|
|
memcpy() etc. without worrying about 64k boundaries. Note: there's no
|
|
easy way to convert back from a linear to a segmented address.
|
|
|
|
In most cases, you don't need to worry about segmented address, as the
|
|
conversion is made automatically by the callback code and the API
|
|
functions only see linear addresses. However, in some cases it is
|
|
necessary to manipulate segmented addresses; the most frequent cases
|
|
are:
|
|
- API functions that return a pointer
|
|
- lParam of Windows messages that point to a structure
|
|
- Pointers contained inside structures accessed by 16-bit code.
|
|
|
|
It is usually a good practice to used the type 'SEGPTR' for segmented
|
|
pointers, instead of something like 'LPSTR' or 'char *'. As SEGPTR is
|
|
defined as a DWORD, you'll get a compilation warning if you mistakenly
|
|
use it as a regular 32-bit pointer.
|
|
|
|
|
|
STRUCTURE PACKING
|
|
=================
|
|
|
|
Under Windows, data structures are tightly packed, i.e. there is no
|
|
padding between structure members. On the other hand, by default gcc
|
|
aligns structure members (e.g. WORDs are on a WORD boundary, etc.).
|
|
This means that a structure like
|
|
|
|
struct { BYTE x; WORD y; };
|
|
|
|
will take 3 bytes under Windows, but 4 with gcc, because gcc will add a
|
|
dummy byte between x and y. To have the correct layout for structures
|
|
used by Windows code, you need to use the WINE_PACKED attribute; so you
|
|
would declare the above structure like this:
|
|
|
|
struct { BYTE x; WORD y WINE_PACKED; };
|
|
|
|
You have to do this every time a structure member is not aligned
|
|
correctly under Windows (i.e. a WORD not on an even address, or a
|
|
DWORD on a address that is not a multiple of 4).
|
|
|
|
|
|
NAMING CONVENTIONS FOR API FUNCTIONS AND TYPES
|
|
==============================================
|
|
|
|
In order to support both Win16 and Win32 APIs within the same source
|
|
code, as well as share the include files between the emulator and the
|
|
library, the following convention must be used in naming all API
|
|
functions and types. If the Windows API uses the name 'xxx', the Wine
|
|
code must use:
|
|
|
|
- 'xxx16' for the 16-bit version,
|
|
- 'xxx32' for the 32-bit version when no ASCII/Unicode strings are
|
|
involved,
|
|
- 'xxx32A' for the 32-bit version with ASCII strings,
|
|
- 'xxx32W' for the 32-bit version with Unicode strings.
|
|
|
|
You should then use the macros WINELIB_NAME[_AW](xxx) or
|
|
DECL_WINELIB_TYPE[_AW](xxx) (defined in include/wintypes.h) to define
|
|
the correct 'xxx' function or type for Winelib. When compiling the
|
|
emulator, 'xxx' is _not_ defined, meaning that you must always specify
|
|
explicitly whether you want the 16-bit or 32-bit version.
|
|
|
|
Note: if 'xxx' is the same in Win16 and Win32, you can simply use the
|
|
same name as Windows.
|
|
|
|
Examples:
|
|
|
|
typedef short INT16;
|
|
typedef int INT32;
|
|
DECL_WINELIB_TYPE(INT);
|
|
|
|
typedef struct { /* Win32 ASCII data structure */ } WNDCLASS32A;
|
|
typedef struct { /* Win32 Unicode data structure */ } WNDCLASS32W;
|
|
typedef struct { /* Win16 data structure */ } WNDCLASS16;
|
|
DECL_WINELIB_TYPE_AW(WNDCLASS);
|
|
|
|
ATOM RegisterClass16( WNDCLASS16 * );
|
|
ATOM RegisterClass32A( WNDCLASS32A * );
|
|
ATOM RegisterClass32W( WNDCLASS32W * );
|
|
#define RegisterClass WINELIB_NAME_AW(RegisterClass)
|
|
|
|
The Winelib user can then say:
|
|
|
|
INT i;
|
|
WNDCLASS wc = { ... };
|
|
RegisterClass( &wc );
|
|
|
|
and this will use the correct declaration depending on the definition
|
|
of the symbols WINELIB16, WINELIB32 and UNICODE.
|
|
|
|
|
|
API ENTRY POINTS
|
|
================
|
|
|
|
Because Win16 programs use a 16-bit stack and because they can only
|
|
call 16:16 addressed functions, all API entry points must be at low
|
|
address offsets and must have the arguments translated and moved to
|
|
Wines 32-bit stack. This task is handled by the code in the "if1632"
|
|
directory. To define a new API entry point handler you must place a
|
|
new entry in the appropriate API specification file. These files are
|
|
named *.spec. For example, the API specification file for the USER
|
|
DLL is contained in the file user.spec. These entries are processed
|
|
by the "build" program to create an assembly file containing the entry
|
|
point code for each API call. The format of the *.spec files is
|
|
documented in the file "tools/build-spec.txt".
|
|
|
|
|
|
DEBUG MESSAGES
|
|
==============
|
|
|
|
To display a message only during debugging, you normally write something
|
|
like this:
|
|
|
|
#ifdef DEBUG_WIN
|
|
printf("abc...");
|
|
#endif
|
|
|
|
You can write this shorter (and better) in this way:
|
|
|
|
dprintf_win(stddeb,"abc...");
|
|
|
|
All symbols of the form dprintf_xxxx are macros defined in include/debug.h .
|
|
The macro-definitions are generated by the shell-script tools/make_debug. It
|
|
scans the source code for symbols of this forms and puts the necessary
|
|
macro definitions in include/debug.h and include/stddebug.h . These macros
|
|
test for the symbol DEBUG_XXXX (e.g. dprintf_win refers to DEBUG_WIN) being
|
|
defined and thus decided whether to actually display the text. If you want
|
|
to enable specific types of messages, simply put the corresponding
|
|
#define DEBUG_XXXX in include/stddebug.h . If you want to enable or disable
|
|
a specific type of message in just one c-source-file, put the corresponding
|
|
#define DEBUG_XXXX or #undefine DEBUG_XXXX between #include<stddebug.h> and
|
|
#include <debug.h> in that specific file. In addition you can change the
|
|
types of displayed messages by supplying the "-debugmsg" option to Wine.
|
|
If your debugging code is more complex than just printf, you can use the
|
|
symbols debugging_XXX as well. These are true when XXX is enabled, either
|
|
permanent or in the command line. So instead of writing
|
|
|
|
#ifdef DEBUG_WIN
|
|
DumpSomeStructure(&str);
|
|
#endif
|
|
|
|
write
|
|
if(debugging_win)DumpSomeStructure(&str);
|
|
Don't worry about the inefficiency of the test. If it is permanently
|
|
disabled (thus debugging_win is 0 at compile time), the compiler will
|
|
eliminate the dead code.
|
|
|
|
The file handle "stddeb" is intended for displaying standard informational
|
|
messages, whereas "stdnimp" is intended for displaying messages concerning
|
|
not yet implemented functions.
|
|
|
|
You have to start tools/make_debug only if you introduced a new macro,
|
|
e.g. dprintf_win32s - not if you just changed one of the #define
|
|
DEBUG_XXX's in include/stddebug.h or in a specific file.
|