| <chapter id="implementation"> | |
| <title>Low-level Implementation</title> | |
| <para>Details of Wine's Low-level Implementation...</para> | |
| <sect1 id="builtin-dlls"> | |
| <title>Builtin DLLs</title> | |
| <para> | |
| Written by &name-juergen-schmied; <email>&email-juergen-schmied;</email> | |
| </para> | |
| <para> | |
| (Extracted from <filename>wine/documentation/internal-dll</filename>) | |
| </para> | |
| <para> | |
| This document describes some points you should know before | |
| implementing the internal counterparts to external DLL's. | |
| Only 32 bit DLL's are considered. | |
| </para> | |
| <sect2> | |
| <title>1. The LibMain function</title> | |
| <para> | |
| This is the way to do some initializing when a process or | |
| thread is attached to the dll. The function name is taken | |
| from a <filename>*.spec</filename> file line: | |
| </para> | |
| <programlisting> | |
| init YourFunctionName | |
| </programlisting> | |
| <para> | |
| Then, you have to implement the function: | |
| </para> | |
| <programlisting> | |
| BOOL32 WINAPI YourLibMain(HINSTANCE32 hinstDLL, | |
| DWORD fdwReason, LPVOID lpvReserved) | |
| { if (fdwReason==DLL_PROCESS_ATTACH) | |
| { ... | |
| } | |
| .... | |
| } | |
| </programlisting> | |
| </sect2> | |
| <sect2> | |
| <title>2. Using functions from other built-in DLL's</title> | |
| <para> | |
| The problem here is, that you can't know if you have to call | |
| the function from the internal or the external DLL. If you | |
| just call the function you will get the internal | |
| implementation. If the external DLL is loaded the executed | |
| program will use the external DLL and you the internal one. | |
| When you -as an example- fill an iconlist placed in the | |
| internal DLL the application won't get the icons from the | |
| external DLL. | |
| </para> | |
| <para> | |
| To work around this, you should always use a pointer to call | |
| such functions: | |
| </para> | |
| <programlisting> | |
| /* definition of the pointer type*/ | |
| void (CALLBACK* pDLLInitComctl)(); | |
| /* getting the function address this should be done in the | |
| LibMain function when called with DLL_PROCESS_ATTACH*/ | |
| BOOL32 WINAPI Shell32LibMain(HINSTANCE32 hinstDLL, DWORD fdwReason, | |
| LPVOID lpvReserved) | |
| { HINSTANCE32 hComctl32; | |
| if (fdwReason==DLL_PROCESS_ATTACH) | |
| { /* load the external / internal DLL*/ | |
| hComctl32 = LoadLibrary32A("COMCTL32.DLL"); | |
| if (hComctl32) | |
| { /* get the function pointer */ | |
| pDLLInitComctl=GetProcAddress32(hComctl32,"InitCommonControlsEx"); | |
| /* check it */ | |
| if (pDLLInitComctl) | |
| { /* use it */ | |
| pDLLInitComctl(); | |
| } | |
| /* free the DLL / decrease the ref count */ | |
| FreeLibrary32(hComctl32); | |
| } | |
| else | |
| { /* do some panic*/ | |
| ERR(shell,"P A N I C error getting functionpointers\n"); | |
| exit (1); | |
| } | |
| } | |
| .... | |
| </programlisting> | |
| </sect2> | |
| <sect2> | |
| <title>3. Getting resources from a <filename>*.rc</filename> file linked to the DLL</title> | |
| <para> | |
| < If you know how, write some lines> | |
| </para> | |
| </sect2> | |
| </sect1> | |
| <sect1 id="accel-impl"> | |
| <title>Accelerators</title> | |
| <para> | |
| Findings researched by Uwe Bonnes, Ulrich Weigand and Marcus Meissner. | |
| </para> | |
| <para> | |
| (Extracted from <filename>wine/documentation/accelerators</filename>) | |
| </para> | |
| <para> | |
| Some notes concerning accelerators. | |
| </para> | |
| <para> | |
| There are <emphasis>three</emphasis> differently sized | |
| accelerator structures exposed to the user. The general layout | |
| is: | |
| </para> | |
| <programlisting> | |
| BYTE fVirt; | |
| WORD key; | |
| WORD cmd; | |
| </programlisting> | |
| <para> | |
| We now have three different appearances: | |
| </para> | |
| <orderedlist> | |
| <listitem> | |
| <para> | |
| Accelerators in NE resources. These have a size of 5 byte | |
| and do not have any padding. This is also the internal | |
| layout of the global handle <type>HACCEL</type> (16 and | |
| 32) in Windows 95 and WINE. Exposed to the user as Win16 | |
| global handles <type>HACCEL16</type> and | |
| <type>HACCEL32</type> by the Win16/Win32 API. | |
| </para> | |
| </listitem> | |
| <listitem> | |
| <para> | |
| Accelerators in PE resources. These have a size of 8 byte. | |
| Layout is: | |
| </para> | |
| <programlisting> | |
| BYTE fVirt; | |
| BYTE pad0; | |
| WORD key; | |
| WORD cmd; | |
| WORD pad1; | |
| </programlisting> | |
| <para> | |
| They are exposed to the user only by direct accessing PE | |
| resources. | |
| </para> | |
| </listitem> | |
| <listitem> | |
| <para> | |
| Accelerators in the Win32 API. These have a size of 6 | |
| bytes. Layout is: | |
| </para> | |
| <programlisting> | |
| BYTE fVirt; | |
| BYTE pad0; | |
| WORD key; | |
| WORD cmd; | |
| </programlisting> | |
| <para> | |
| These are exposed to the user by the | |
| <function>CopyAcceleratorTable</function> and | |
| <function>CreateAcceleratorTable</function> functions in | |
| the Win32 API. | |
| </para> | |
| </listitem> | |
| </orderedlist> | |
| <para> | |
| Why two types of accelerators in the Win32 API? We can only | |
| guess, but my best bet is that the Win32 resource compiler | |
| can/does not handle struct packing. Win32 <type>ACCEL</type> | |
| is defined using <function>#pragma(2)</function> for the | |
| compiler but without any packing for RC, so it will assume | |
| <function>#pragma(4)</function>. | |
| </para> | |
| </sect1> | |
| <sect1 id="file-handles"> | |
| <title>File Handles</title> | |
| <para> | |
| Written by (???) | |
| </para> | |
| <para> | |
| (Extracted from <filename>wine/documentation/filehandles</filename>) | |
| </para> | |
| <para> | |
| DOS treats the first 5 file handles as special cases. They | |
| map directly to <filename>stdin</filename>, | |
| <filename>stdout</filename>, <filename>stderr</filename>, | |
| <filename>stdaux</filename> and <filename>stdprn</filename>. | |
| Windows 16 inherits this behavior, and in fact, win16 handles | |
| are interchangable with DOS handles. Some nasty windows | |
| programs even do this! | |
| </para> | |
| <para> | |
| Windows32 issues file handles starting from | |
| <literal>1</literal>, on the grounds that most GUI processes | |
| don't need a <filename>stdin</filename>, | |
| <filename>stdout</filename>, etc. | |
| </para> | |
| <para> | |
| The Wine handle code is implemented in the Win32 style, and | |
| the Win16 functions use two macros to convert to and from the | |
| two types. | |
| </para> | |
| <para> | |
| The macros are defined in <filename>file.h</filename> as follows: | |
| </para> | |
| <programlisting> | |
| #define HFILE16_TO_HFILE32(handle) \ | |
| (((handle)==0) ? GetStdHandle(STD_INPUT_HANDLE) : \ | |
| ((handle)==1) ? GetStdHandle(STD_OUTPUT_HANDLE) : \ | |
| ((handle)==2) ? GetStdHandle(STD_ERROR_HANDLE) : \ | |
| ((handle)>0x400) ? handle : \ | |
| (handle)-5) | |
| #define HFILE32_TO_HFILE16(handle) ({ HFILE32 hnd=handle; \ | |
| ((hnd==HFILE_ERROR32) ? HFILE_ERROR16 : \ | |
| ((handle>0x400) ? handle : \ | |
| (HFILE16)hnd+5); }) | |
| </programlisting> | |
| <warning> | |
| <para> | |
| Be careful not to use the macro | |
| <function>HFILE16_TO_HFILE32</function> on functions with | |
| side-effects, as it will cause them to be evaluated several | |
| times. This could be considered a bug, but the use of this | |
| macro is limited enough not to need a rewrite. | |
| </para> | |
| </warning> | |
| <note> | |
| <para> | |
| The <literal>0x400</literal> special case above deals with | |
| LZW filehandles (see <filename>misc/lzexpand.c</filename>). | |
| </para> | |
| </note> | |
| </sect1> | |
| <sect1 id="hardware-trace"> | |
| <title>Doing A Hardware Trace In Wine</title> | |
| <para> | |
| Written by &name-jonathan-buzzard; <email>&email-jonathan-buzzard;</email> | |
| </para> | |
| <para> | |
| (Extracted from <filename>wine/documentation/ioport-trace-hints</filename>) | |
| </para> | |
| <para> | |
| The primary reason to do this is to reverse engineer a | |
| hardware device for which you don't have documentation, but | |
| can get to work under Wine. | |
| </para> | |
| <para> | |
| This lot is aimed at parallel port devices, and in particular | |
| parallel port scanners which are now so cheap they are | |
| virtually being given away. The problem is that few | |
| manufactures will release any programming information which | |
| prevents drivers being written for Sane, and the traditional | |
| technique of using DOSemu to produce the traces does not work | |
| as the scanners invariably only have drivers for Windows. | |
| </para> | |
| <para> | |
| Please note that I have not been able to get my scanner | |
| working properly (a UMAX Astra 600P), but a couple of people | |
| have reported success with at least the Artec AS6e scanner. I | |
| am not in the process of developing any driver nor do I intend | |
| to, so don't bug me about it. My time is now spent writing | |
| programs to set things like battery save options under Linux | |
| on Toshiba laptops, and as such I don't have any spare time | |
| for writing a driver for a parallel port scanner etc. | |
| </para> | |
| <para> | |
| Presuming that you have compiled and installed wine the first | |
| thing to do is is to enable direct hardware access to your | |
| parallel port. To do this edit <filename>wine.conf</filename> | |
| (usually in <filename>/usr/local/etc</filename>) and in the | |
| ports section add the following two lines | |
| </para> | |
| <programlisting> | |
| read=0x378,0x379,0x37a,0x37c,0x77a | |
| write=0x378,x379,0x37a,0x37c,0x77a | |
| </programlisting> | |
| <para> | |
| This adds the necessary access required for SPP/PS2/EPP/ECP | |
| parallel port on LPT1. You will need to adjust these number | |
| accordingly if your parallel port is on LPT2 or LPT0. | |
| </para> | |
| <para> | |
| When starting wine use the following command line, where | |
| <literal>XXXX</literal> is the program you need to run in | |
| order to access your scanner, and <literal>YYYY</literal> is | |
| the file your trace will be stored in: | |
| </para> | |
| <programlisting> | |
| wine -debugmsg +io XXXX 2> >(sed 's/^[^:]*:io:[^ ]* //' > YYYY) | |
| </programlisting> | |
| <para> | |
| You will need large amounts of hard disk space (read hundreds | |
| of megabytes if you do a full page scan), and for reasonable | |
| performance a really fast processor and lots of RAM. | |
| </para> | |
| <para> | |
| You might well find the log compression program that <email>David | |
| Campbell campbell@torque.net</email> wrote helpful in | |
| reducing the size of the log files. This can be obtained by | |
| the following command: | |
| </para> | |
| <programlisting> | |
| sh ioport-trace-hints | |
| </programlisting> | |
| <para> | |
| This should extract <filename>shrink.c</filename> (which is | |
| located at the end of this file. Compile the log compression | |
| program by: | |
| </para> | |
| <programlisting> | |
| cc shrink.c -o shrink | |
| </programlisting> | |
| <para> | |
| Use the <command>shrink</command> program to reduce the | |
| physical size of the raw log as follows: | |
| </para> | |
| <programlisting> | |
| cat log | shrink > log2 | |
| </programlisting> | |
| <para> | |
| The trace has the basic form of | |
| </para> | |
| <programlisting> | |
| XXXX > YY @ ZZZZ:ZZZZ | |
| </programlisting> | |
| <para> | |
| where <literal>XXXX</literal> is the port in hexidecimal being | |
| accessed, <literal>YY</literal> is the data written (or read) | |
| from the port, and <literal>ZZZZ:ZZZZ</literal> is the address | |
| in memory of the instruction that accessed the port. The | |
| direction of the arrow indicates whether the data was written | |
| or read from the port. | |
| </para> | |
| <programlisting> | |
| > data was written to the port | |
| < data was read from the port | |
| </programlisting> | |
| <para> | |
| My basic tip for interperating these logs is to pay close | |
| attention to the addresses of the IO instructions. Their | |
| grouping and sometimes proximity should reveal the presence of | |
| subroutines in the driver. By studying the different versions | |
| you should be able to work them out. For example consider the | |
| following section of trace from my UMAX Astra 600P | |
| </para> | |
| <programlisting> | |
| 0x378 > 55 @ 0297:01ec | |
| 0x37a > 05 @ 0297:01f5 | |
| 0x379 < 8f @ 0297:01fa | |
| 0x37a > 04 @ 0297:0211 | |
| 0x378 > aa @ 0297:01ec | |
| 0x37a > 05 @ 0297:01f5 | |
| 0x379 < 8f @ 0297:01fa | |
| 0x37a > 04 @ 0297:0211 | |
| 0x378 > 00 @ 0297:01ec | |
| 0x37a > 05 @ 0297:01f5 | |
| 0x379 < 8f @ 0297:01fa | |
| 0x37a > 04 @ 0297:0211 | |
| 0x378 > 00 @ 0297:01ec | |
| 0x37a > 05 @ 0297:01f5 | |
| 0x379 < 8f @ 0297:01fa | |
| 0x37a > 04 @ 0297:0211 | |
| 0x378 > 00 @ 0297:01ec | |
| 0x37a > 05 @ 0297:01f5 | |
| 0x379 < 8f @ 0297:01fa | |
| 0x37a > 04 @ 0297:0211 | |
| 0x378 > 00 @ 0297:01ec | |
| 0x37a > 05 @ 0297:01f5 | |
| 0x379 < 8f @ 0297:01fa | |
| 0x37a > 04 @ 0297:0211 | |
| </programlisting> | |
| <para> | |
| As you can see their is a repeating structure starting at | |
| address <literal>0297:01ec</literal> that consists of four io | |
| accesses on the parallel port. Looking at it the first io | |
| access writes a changing byte to the data port the second | |
| always writes the byte <literal>0x05</literal> to the control | |
| port, then a value which always seems to | |
| <literal>0x8f</literal> is read from the status port at which | |
| point a byte <literal>0x04</literal> is written to the control | |
| port. By studying this and other sections of the trace we can | |
| write a C routine that emulates this, shown below with some | |
| macros to make reading/writing on the parallel port easier to | |
| read. | |
| </para> | |
| <programlisting> | |
| #define r_dtr(x) inb(x) | |
| #define r_str(x) inb(x+1) | |
| #define r_ctr(x) inb(x+2) | |
| #define w_dtr(x,y) outb(y, x) | |
| #define w_str(x,y) outb(y, x+1) | |
| #define w_ctr(x,y) outb(y, x+2) | |
| /* | |
| * Seems to be sending a command byte to the scanner | |
| * | |
| */ | |
| int udpp_put(int udpp_base, unsigned char command) | |
| { | |
| int loop,value; | |
| w_dtr(udpp_base, command); | |
| w_ctr(udpp_base, 0x05); | |
| for (loop=0;loop<10;loop++) | |
| if (((value=r_str(udpp_base)) & 0x80)!=0x00) { | |
| w_ctr(udpp_base, 0x04); | |
| return value & 0xf8; | |
| } | |
| return (value & 0xf8) | 0x01; | |
| } | |
| </programlisting> | |
| <para> | |
| For the UMAX Astra 600P only seven such routines exist (well | |
| 14 really, seven for SPP and seven for EPP). Whether you | |
| choose to disassemble the driver at this point to verify the | |
| routines is your own choice. If you do, the address from the | |
| trace should help in locating them in the disassembly. | |
| </para> | |
| <para> | |
| You will probably then find it useful to write a script/perl/C | |
| program to analyse the logfile and decode them futher as this | |
| can reveal higher level grouping of the low level routines. | |
| For example from the logs from my UMAX Astra 600P when decoded | |
| futher reveal (this is a small snippet) | |
| </para> | |
| <programlisting> | |
| start: | |
| put: 55 8f | |
| put: aa 8f | |
| put: 00 8f | |
| put: 00 8f | |
| put: 00 8f | |
| put: c2 8f | |
| wait: ff | |
| get: af,87 | |
| wait: ff | |
| get: af,87 | |
| end: cc | |
| start: | |
| put: 55 8f | |
| put: aa 8f | |
| put: 00 8f | |
| put: 03 8f | |
| put: 05 8f | |
| put: 84 8f | |
| wait: ff | |
| </programlisting> | |
| <para> | |
| From this it is easy to see that <varname>put</varname> | |
| routine is often grouped together in five successive calls | |
| sending information to the scanner. Once these are understood | |
| it should be possible to process the logs further to show the | |
| higher level routines in an easy to see format. Once the | |
| highest level format that you can derive from this process is | |
| understood, you then need to produce a series of scans varying | |
| only one parameter between them, so you can discover how to | |
| set the various parameters for the scanner. | |
| </para> | |
| <para> | |
| The following is the <filename>shrink.c</filename> program. | |
| </para> | |
| <programlisting> | |
| cat > shrink.c <<EOF | |
| #include <stdio.h> | |
| #include <string.h> | |
| void | |
| main (void) | |
| { | |
| char buff[256], lastline[256]; | |
| int count; | |
| count = 0; | |
| lastline[0] = 0; | |
| while (!feof (stdin)) | |
| { | |
| fgets (buff, sizeof (buff), stdin); | |
| if (strcmp (buff, lastline) == 0) | |
| { | |
| count++; | |
| } | |
| else | |
| { | |
| if (count > 1) | |
| fprintf (stdout, "# Last line repeated %i times #\n", count); | |
| fprintf (stdout, "%s", buff); | |
| strcpy (lastline, buff); | |
| count = 1; | |
| } | |
| } | |
| } | |
| EOF | |
| </programlisting> | |
| </sect1> | |
| </chapter> | |
| <!-- Keep this comment at the end of the file | |
| Local variables: | |
| mode: sgml | |
| sgml-parent-document:("wine-doc.sgml" "set" "book" "part" "chapter" "") | |
| End: | |
| --> |