Added a new debugging channel io to allow read/writes on a port to be
logged to a file.

diff --git a/documentation/ioport-trace-hints b/documentation/ioport-trace-hints
new file mode 100644
index 0000000..9c6521a
--- /dev/null
+++ b/documentation/ioport-trace-hints
@@ -0,0 +1,223 @@
+cat > /dev/null <<EOF
+The above line is necessary, leave it alone!!
+--------------------------------------------------------------------
+
+DOING A HARDWARE TRACE IN WINE
+------------------------------
+
+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.
+
+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.
+
+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 writting programs
+to set things like battery save options under Linux on Toshiba laptops, ans as
+such I don't have any spare time for writting a driver for a parallel port
+scanner etc.
+
+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
+wine.conf (usually in /usr/local/etc) and in the ports section add the
+following two lines
+
+read=0x378,0x379,0x37a,0x37c,0x77a
+write=0x378,x379,0x37a,0x37c,0x77a
+
+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.
+
+When starting wine use the following command line, where XXXX is the program
+you need to run in order to access your scanner, and YYYY is the file your
+trace will be stored in:
+
+    wine -debugmsg +io XXXX 2> >(sed 's/^[^:]*:io:[^ ]* //' > YYYY)
+
+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.
+
+You might well find the log compression program that David Campbell
+<campbell@torque.net> wrote helpfull in reducing the size of the log files.
+This can be obtained by the following command:
+
+    sh ioport-trace-hints
+
+This should extract shrink.c (which is located at the end of this file. Compile
+the log compression program by:
+
+    cc shrink.c -o shrink
+
+Use the shrink program to reduce the physical size of the raw log as follows:
+
+    cat log | shrink > log2
+
+The trace has the basic form of
+
+    XXXX > YY @ ZZZZ:ZZZZ
+
+where XXXX is the port in hexidecimal being accessed, YY is the data written
+(or read) from the port, and ZZZZ:ZZZZ 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.
+
+    > data was written to the port
+    < data was read from the port
+
+
+My basic tip for interperating these logs is to pay close attention to the
+addresses of the IO instructions. There 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
+
+    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
+
+As you can see their is a repeating structure starting at address 0297:01ec
+that consists of four io access 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 0x05 to the control port, then a value which always seems to 0x8f is read
+from the status port at which point a byte 0x04 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.
+
+
+#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;
+}
+
+
+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.
+
+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)
+
+
+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
+
+From this it is easy to see that put routine is oftern gouped together in five
+sucessive 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 higest 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.
+
+
+Jonathan Buzzard
+<jab@hex.prestel.co.uk>
+
+
+--------------------------------------------------------------------
+The following is the shrink.c program.
+EOF
+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
diff --git a/include/debug.h b/include/debug.h
index 8a880c9..346a07b 100644
--- a/include/debug.h
+++ b/include/debug.h
@@ -68,90 +68,91 @@
 #define dbch_int19 60
 #define dbch_int21 61
 #define dbch_int31 62
-#define dbch_ipaddress 63
-#define dbch_key 64
-#define dbch_keyboard 65
-#define dbch_ldt 66
-#define dbch_listbox 67
-#define dbch_listview 68
-#define dbch_local 69
-#define dbch_mci 70
-#define dbch_mcianim 71
-#define dbch_mciwave 72
-#define dbch_mdi 73
-#define dbch_menu 74
-#define dbch_message 75
-#define dbch_metafile 76
-#define dbch_midi 77
-#define dbch_mmaux 78
-#define dbch_mmio 79
-#define dbch_mmsys 80
-#define dbch_mmtime 81
-#define dbch_module 82
-#define dbch_mpr 83
-#define dbch_msacm 84
-#define dbch_msg 85
-#define dbch_nativefont 86
-#define dbch_nonclient 87
-#define dbch_ntdll 88
-#define dbch_ole 89
-#define dbch_pager 90
-#define dbch_palette 91
-#define dbch_pidl 92
-#define dbch_print 93
-#define dbch_process 94
-#define dbch_profile 95
-#define dbch_progress 96
-#define dbch_prop 97
-#define dbch_psapi 98
-#define dbch_psdrv 99
-#define dbch_rebar 100
-#define dbch_reg 101
-#define dbch_region 102
-#define dbch_relay 103
-#define dbch_resource 104
-#define dbch_s 105
-#define dbch_scroll 106
-#define dbch_security 107
-#define dbch_segment 108
-#define dbch_selector 109
-#define dbch_sem 110
-#define dbch_sendmsg 111
-#define dbch_shell 112
-#define dbch_shm 113
-#define dbch_snoop 114
-#define dbch_sound 115
-#define dbch_static 116
-#define dbch_statusbar 117
-#define dbch_stress 118
-#define dbch_string 119
-#define dbch_syscolor 120
-#define dbch_system 121
-#define dbch_tab 122
-#define dbch_task 123
-#define dbch_text 124
-#define dbch_thread 125
-#define dbch_thunk 126
-#define dbch_timer 127
-#define dbch_toolbar 128
-#define dbch_toolhelp 129
-#define dbch_tooltips 130
-#define dbch_trackbar 131
-#define dbch_treeview 132
-#define dbch_tweak 133
-#define dbch_uitools 134
-#define dbch_updown 135
-#define dbch_ver 136
-#define dbch_virtual 137
-#define dbch_vxd 138
-#define dbch_win 139
-#define dbch_win16drv 140
-#define dbch_win32 141
-#define dbch_wing 142
-#define dbch_winsock 143
-#define dbch_wnet 144
-#define dbch_x11 145
-#define dbch_x11drv 146
+#define dbch_io 63
+#define dbch_ipaddress 64
+#define dbch_key 65
+#define dbch_keyboard 66
+#define dbch_ldt 67
+#define dbch_listbox 68
+#define dbch_listview 69
+#define dbch_local 70
+#define dbch_mci 71
+#define dbch_mcianim 72
+#define dbch_mciwave 73
+#define dbch_mdi 74
+#define dbch_menu 75
+#define dbch_message 76
+#define dbch_metafile 77
+#define dbch_midi 78
+#define dbch_mmaux 79
+#define dbch_mmio 80
+#define dbch_mmsys 81
+#define dbch_mmtime 82
+#define dbch_module 83
+#define dbch_mpr 84
+#define dbch_msacm 85
+#define dbch_msg 86
+#define dbch_nativefont 87
+#define dbch_nonclient 88
+#define dbch_ntdll 89
+#define dbch_ole 90
+#define dbch_pager 91
+#define dbch_palette 92
+#define dbch_pidl 93
+#define dbch_print 94
+#define dbch_process 95
+#define dbch_profile 96
+#define dbch_progress 97
+#define dbch_prop 98
+#define dbch_psapi 99
+#define dbch_psdrv 100
+#define dbch_rebar 101
+#define dbch_reg 102
+#define dbch_region 103
+#define dbch_relay 104
+#define dbch_resource 105
+#define dbch_s 106
+#define dbch_scroll 107
+#define dbch_security 108
+#define dbch_segment 109
+#define dbch_selector 110
+#define dbch_sem 111
+#define dbch_sendmsg 112
+#define dbch_shell 113
+#define dbch_shm 114
+#define dbch_snoop 115
+#define dbch_sound 116
+#define dbch_static 117
+#define dbch_statusbar 118
+#define dbch_stress 119
+#define dbch_string 120
+#define dbch_syscolor 121
+#define dbch_system 122
+#define dbch_tab 123
+#define dbch_task 124
+#define dbch_text 125
+#define dbch_thread 126
+#define dbch_thunk 127
+#define dbch_timer 128
+#define dbch_toolbar 129
+#define dbch_toolhelp 130
+#define dbch_tooltips 131
+#define dbch_trackbar 132
+#define dbch_treeview 133
+#define dbch_tweak 134
+#define dbch_uitools 135
+#define dbch_updown 136
+#define dbch_ver 137
+#define dbch_virtual 138
+#define dbch_vxd 139
+#define dbch_win 140
+#define dbch_win16drv 141
+#define dbch_win32 142
+#define dbch_wing 143
+#define dbch_winsock 144
+#define dbch_wnet 145
+#define dbch_x11 146
+#define dbch_x11drv 147
 /* Definitions for classes identifiers */
 #define dbcl_fixme 0
 #define dbcl_err 1
diff --git a/include/debugdefs.h b/include/debugdefs.h
index 3313d2d..42e6c92 100644
--- a/include/debugdefs.h
+++ b/include/debugdefs.h
@@ -4,7 +4,7 @@
 #include "debugtools.h"
 #endif
 
-#define DEBUG_CHANNEL_COUNT 147
+#define DEBUG_CHANNEL_COUNT 148
 #ifdef DEBUG_RUNTIME
 short debug_msg_enabled[][DEBUG_CLASS_COUNT] = {
 {1, 1, 0, 0},
@@ -154,6 +154,7 @@
 {1, 1, 0, 0},
 {1, 1, 0, 0},
 {1, 1, 0, 0},
+{1, 1, 0, 0},
 };
 const char* debug_ch_name[] = {
 "1",
@@ -219,6 +220,7 @@
 "int19",
 "int21",
 "int31",
+"io",
 "ipaddress",
 "key",
 "keyboard",
diff --git a/miscemu/instr.c b/miscemu/instr.c
index 91d42b5..8d57e37 100644
--- a/miscemu/instr.c
+++ b/miscemu/instr.c
@@ -489,21 +489,41 @@
                   {
 		    case 0x6c:
 		      *((BYTE *)data) = IO_inport( DX_sig(context), 1);
+		      TRACE(io, "0x%x < %02x @ %04x:%04x\n", DX_sig(context),
+                        *((BYTE *)data), CS_sig(context), IP_sig(context));
 		      break;
 		    case 0x6d:
 		      if (long_op)
+                      {
 			*((DWORD *)data) = IO_inport( DX_sig(context), 4);
+                        TRACE(io, "0x%x < %08lx @ %04x:%04x\n", DX_sig(context),
+                          *((DWORD *)data), CS_sig(context), IP_sig(context));
+                      }
 		      else
+                      {
 			*((WORD *)data) = IO_inport( DX_sig(context), 2);
+                        TRACE(io, "0x%x < %04x @ %04x:%04x\n", DX_sig(context),
+                          *((WORD *)data), CS_sig(context), IP_sig(context));
+                      }
 		      break;
 		    case 0x6e:
                         IO_outport( DX_sig(context), 1, *((BYTE *)data));
+                        TRACE(io, "0x%x > %02x @ %04x:%04x\n", DX_sig(context),
+                          *((BYTE *)data), CS_sig(context), IP_sig(context));
                         break;
 		    case 0x6f:
                         if (long_op)
+                        {
                             IO_outport( DX_sig(context), 4, *((DWORD *)data));
+                            TRACE(io, "0x%x > %08lx @ %04x:%04x\n", DX_sig(context),
+                              *((DWORD *)data), CS_sig(context), IP_sig(context)); 
+                        }
                         else
+                        {
                             IO_outport( DX_sig(context), 2, *((WORD *)data));
+                            TRACE(io, "0x%x > %04x @ %04x:%04x\n", DX_sig(context),
+                              *((WORD *)data), CS_sig(context), IP_sig(context));
+                        }
                         break;
 		    }
 		}
@@ -609,45 +629,93 @@
 
         case 0xe4: /* inb al,XX */
             AL_sig(context) = IO_inport( instr[1], 1 );
+            TRACE(io, "0x%x < %02x @ %04x:%04x\n", instr[1],
+                AL_sig(context), CS_sig(context), IP_sig(context));
 	    EIP_sig(context) += prefixlen + 2;
             return TRUE;
 
         case 0xe5: /* in (e)ax,XX */
-            if (long_op) EAX_sig(context) = IO_inport( instr[1], 4 );
-            else AX_sig(context) = IO_inport( instr[1], 2 );
+            if (long_op)
+            {
+                EAX_sig(context) = IO_inport( instr[1], 4 );
+                TRACE(io, "0x%x < %08lx @ %04x:%04x\n", instr[1],
+                    EAX_sig(context), CS_sig(context), IP_sig(context));
+            }
+            else
+            {
+                AX_sig(context) = IO_inport( instr[1], 2 );
+                TRACE(io, "0x%x < %04x @ %04x:%04x\n", instr[1],
+                    AX_sig(context), CS_sig(context), IP_sig(context));
+            }
 	    EIP_sig(context) += prefixlen + 2;
             return TRUE;
 
         case 0xe6: /* outb XX,al */
             IO_outport( instr[1], 1, AL_sig(context) );
+            TRACE(io, "0x%x > %02x @ %04x:%04x\n", instr[1],
+                AL_sig(context), CS_sig(context), IP_sig(context));
 	    EIP_sig(context) += prefixlen + 2;
             return TRUE;
 
         case 0xe7: /* out XX,(e)ax */
-            if (long_op) IO_outport( instr[1], 4, EAX_sig(context) );
-            else IO_outport( instr[1], 2, AX_sig(context) );
+            if (long_op)
+            {
+                IO_outport( instr[1], 4, EAX_sig(context) );
+                TRACE(io, "0x%x > %08lx @ %04x:%04x\n", instr[1],
+                    EAX_sig(context), CS_sig(context), IP_sig(context));
+            }
+            else
+            {
+                IO_outport( instr[1], 2, AX_sig(context) );
+                TRACE(io, "0x%x > %04x @ %04x:%04x\n", instr[1],
+                    AX_sig(context), CS_sig(context), IP_sig(context));
+            }
   	    EIP_sig(context) += prefixlen + 2;
             return TRUE;
 
         case 0xec: /* inb al,dx */
             AL_sig(context) = IO_inport( DX_sig(context), 1 );
+            TRACE(io, "0x%x < %02x @ %04x:%04x\n", DX_sig(context),
+                AL_sig(context), CS_sig(context), IP_sig(context));
   	    EIP_sig(context) += prefixlen + 1;
             return TRUE;
 
         case 0xed: /* in (e)ax,dx */
-            if (long_op) EAX_sig(context) = IO_inport( DX_sig(context), 4 );
-            else AX_sig(context) = IO_inport( DX_sig(context), 2 );
+            if (long_op)
+            {
+                EAX_sig(context) = IO_inport( DX_sig(context), 4 );
+                TRACE(io, "0x%x < %08lx @ %04x:%04x\n", DX_sig(context),
+                    EAX_sig(context), CS_sig(context), IP_sig(context));
+            }
+            else
+            {
+                AX_sig(context) = IO_inport( DX_sig(context), 2 );
+                TRACE(io, "0x%x < %04x @ %04x:%04x\n", DX_sig(context),
+                    AX_sig(context), CS_sig(context), IP_sig(context));
+            }
   	    EIP_sig(context) += prefixlen + 1;
             return TRUE;
 
         case 0xee: /* outb dx,al */
             IO_outport( DX_sig(context), 1, AL_sig(context) );
+            TRACE(io, "0x%x > %02x @ %04x:%04x\n", DX_sig(context),
+                AL_sig(context), CS_sig(context), IP_sig(context));
   	    EIP_sig(context) += prefixlen + 1;
             return TRUE;
       
         case 0xef: /* out dx,(e)ax */
-            if (long_op) IO_outport( DX_sig(context), 4, EAX_sig(context) );
-            else IO_outport( DX_sig(context), 2, AX_sig(context) );
+            if (long_op)
+            {
+                IO_outport( DX_sig(context), 4, EAX_sig(context) );
+                TRACE(io, "0x%x > %08lx @ %04x:%04x\n", DX_sig(context),
+                    EAX_sig(context), CS_sig(context), IP_sig(context));
+            }
+            else
+            {
+                IO_outport( DX_sig(context), 2, AX_sig(context) );
+                TRACE(io, "0x%x > %04x @ %04x:%04x\n", DX_sig(context),
+                    AX_sig(context), CS_sig(context), IP_sig(context));
+            }
   	    EIP_sig(context) += prefixlen + 1;
             return TRUE;