winedos: Improve PIT emulation.

Florian Tobias Schandinat FlorianSchandinat at gmx.de
Tue Feb 3 11:26:04 CST 2009


---
 dlls/winedos/ioports.c |  193 ++++++++++++++++++++++++++++++++++-------------
 1 files changed, 139 insertions(+), 54 deletions(-)

diff --git a/dlls/winedos/ioports.c b/dlls/winedos/ioports.c
index 2de1b15..cde1599 100644
--- a/dlls/winedos/ioports.c
+++ b/dlls/winedos/ioports.c
@@ -50,18 +50,26 @@ WINE_DEFAULT_DEBUG_CHANNEL(int);
 
 static struct {
     WORD	countmax;
-    BOOL16	byte_toggle; /* if TRUE, then hi byte has already been written */
     WORD	latch;
-    BOOL16	latched;
     BYTE	ctrlbyte_ch;
-    WORD	oldval;
+    BYTE	flags;
+    LONG64	start_time;
 } tmr_8253[3] = {
-    {0xFFFF,	FALSE,	0,	FALSE,	0x36,	0},
-    {0x0012,	FALSE,	0,	FALSE,	0x74,	0},
-    {0x0001,	FALSE,	0,	FALSE,	0xB6,	0},
+    {0xFFFF,	0,	0x36,	0,	0},
+    {0x0012,	0,	0x74,	0,	0},
+    {0x0001,	0,	0xB6,	0,	0},
 };
+/* two byte read in progress */
+#define TMR_RTOGGLE 0x01
+/* two byte write in progress */
+#define TMR_WTOGGLE 0x02
+/* latch contains data */
+#define TMR_LATCHED 0x04
+/* counter is in update phase */
+#define TMR_UPDATE  0x08
+/* readback status request */
+#define TMR_STATUS  0x10
 
-static int dummy_ctr = 0;
 
 static BYTE parport_8255[4] = {0x4f, 0x20, 0xff, 0xff};
 
@@ -165,11 +173,26 @@ static int do_pp_port_access = -1; /* -1: uninitialized, 1: not available
 				       0: available);*/
 #endif
 
-static void set_timer_maxval(unsigned timer, unsigned maxval)
+#define BCD2BIN(a) \
+((a)%10 + ((a)>>4)%10*10 + ((a)>>8)%10*100 + ((a)>>12)%10*1000)
+#define BIN2BCD(a) \
+((a)%10 | (a)/10%10<<4 | (a)/100%10<<8 | (a)/1000%10<<12)
+
+
+static void set_timer(unsigned timer)
 {
+    DWORD val = tmr_8253[timer].countmax;
+
+    if (tmr_8253[timer].ctrlbyte_ch & 0x01)
+        val = BCD2BIN(val);
+
+    tmr_8253[timer].flags &= ~TMR_UPDATE;
+    if (!QueryPerformanceCounter((LARGE_INTEGER*)&tmr_8253[timer].start_time))
+        WARN("QueryPerformanceCounter should not fail!\n");
+
     switch (timer) {
         case 0: /* System timer counter divisor */
-            DOSVM_SetTimer(maxval);
+            DOSVM_SetTimer(val);
             break;
         case 1: /* RAM refresh */
             FIXME("RAM refresh counter handling not implemented !\n");
@@ -178,14 +201,60 @@ static void set_timer_maxval(unsigned timer, unsigned maxval)
             /* speaker on ? */
             if ((parport_8255[1] & 3) == 3)
             {
-                TRACE("Beep (freq: %d) !\n", 1193180 / maxval );
-                Beep(1193180 / maxval, 20);
+                TRACE("Beep (freq: %d) !\n", 1193180 / val);
+                Beep(1193180 / val, 20);
             }
             break;
     }
 }
 
 
+static WORD get_timer_val(unsigned timer)
+{
+    LONG64 time;
+    WORD maxval, val = tmr_8253[timer].countmax;
+    BYTE mode = tmr_8253[timer].ctrlbyte_ch >> 1 & 0x07;
+
+    /* This is not strictly correct. In most cases the old countdown should
+     * finish normally (by counting down to 0) or halt and not jump to 0.
+     * But we are calculating and not countig, so this seems to be a good
+     * solution and should work well with most (all?) programs
+     */
+    if (tmr_8253[timer].flags & TMR_UPDATE)
+        return 0;
+
+    if (!QueryPerformanceCounter((LARGE_INTEGER*)&time))
+        WARN("QueryPerformanceCounter should not fail!\n");
+
+    time -= tmr_8253[timer].start_time;
+    if (tmr_8253[timer].ctrlbyte_ch & 0x01)
+        val = BCD2BIN(val);
+
+    switch ( mode )
+    {
+        case 0:
+        case 1:
+        case 4:
+        case 5:
+            maxval = tmr_8253[timer].ctrlbyte_ch & 0x01 ? 9999 : 0xFFFF;
+            break;
+        case 2:
+        case 3:
+            maxval = val;
+            break;
+        default:
+            ERR("Invalid PIT mode: %d\n", mode);
+            return 0;
+    }
+
+    val = (val - time) % (maxval + 1);
+    if (tmr_8253[timer].ctrlbyte_ch & 0x01)
+        val = BIN2BCD(val);
+
+    return val;
+}
+
+
 /**********************************************************************
  *	    IO_port_init
  */
@@ -401,24 +470,22 @@ DWORD WINAPI DOSVM_inport( int port, int size )
     case 0x42:
         {
             BYTE chan = port & 3;
-            WORD tempval = 0;
-            if (tmr_8253[chan].latched)
-                tempval = tmr_8253[chan].latch;
-            else
+            WORD tempval = tmr_8253[chan].flags & TMR_LATCHED
+                ? tmr_8253[chan].latch : get_timer_val(chan);
+
+            if (tmr_8253[chan].flags & TMR_STATUS)
             {
-                dummy_ctr -= 1 + (int)(10.0 * rand() / (RAND_MAX + 1.0));
-                if (chan == 0) /* System timer counter divisor */
-                {
-                    /* FIXME: DOSVM_GetTimer() returns quite rigid values */
-                    tempval = dummy_ctr + (WORD)DOSVM_GetTimer();
-                }
-                else
-                {
-                    /* FIXME: intelligent hardware timer emulation needed */
-                    tempval = dummy_ctr;
-                }
+                WARN("Read-back status\n");
+                /* We differ slightly from the spec:
+                 * - TMR_UPDATE is already set with the first write
+                 *   of a two byte counter update
+                 * - 0x80 should be set if OUT signal is 1 (high)
+                 */
+                tmr_8253[chan].flags &= ~TMR_STATUS;
+                res = (tmr_8253[chan].ctrlbyte_ch & 0x3F) |
+                    (tmr_8253[chan].flags & TMR_UPDATE ? 0x40 : 0x00);
+                break;
             }
-
             switch ((tmr_8253[chan].ctrlbyte_ch & 0x30) >> 4)
             {
             case 0:
@@ -426,11 +493,11 @@ DWORD WINAPI DOSVM_inport( int port, int size )
                 break;
             case 1: /* read lo byte */
                 res = (BYTE)tempval;
-                tmr_8253[chan].latched = FALSE;
+                tmr_8253[chan].flags &= ~TMR_LATCHED;
                 break;
             case 3: /* read lo byte, then hi byte */
-                tmr_8253[chan].byte_toggle ^= 1; /* toggle */
-                if (tmr_8253[chan].byte_toggle)
+                tmr_8253[chan].flags ^= TMR_RTOGGLE; /* toggle */
+                if (tmr_8253[chan].flags & TMR_RTOGGLE)
                 {
                     res = (BYTE)tempval;
                     break;
@@ -438,7 +505,7 @@ DWORD WINAPI DOSVM_inport( int port, int size )
                 /* else [fall through if read hi byte !] */
             case 2: /* read hi byte */
                 res = (BYTE)(tempval >> 8);
-                tmr_8253[chan].latched = FALSE;
+                tmr_8253[chan].flags &= ~TMR_LATCHED;
                 break;
             }
         }
@@ -601,10 +668,7 @@ void WINAPI DOSVM_outport( int port, int size, DWORD value )
         {
             BYTE chan = port & 3;
 
-            /* we need to get the oldval before any lo/hi byte change has been made */
-            if (((tmr_8253[chan].ctrlbyte_ch & 0x30) != 0x30) ||
-                !tmr_8253[chan].byte_toggle)
-                tmr_8253[chan].oldval = tmr_8253[chan].countmax;
+            tmr_8253[chan].flags |= TMR_UPDATE;
             switch ((tmr_8253[chan].ctrlbyte_ch & 0x30) >> 4)
             {
             case 0:
@@ -614,8 +678,8 @@ void WINAPI DOSVM_outport( int port, int size, DWORD value )
                     (tmr_8253[chan].countmax & 0xff00) | (BYTE)value;
                 break;
             case 3: /* write lo byte, then hi byte */
-                tmr_8253[chan].byte_toggle ^= TRUE; /* toggle */
-                if (tmr_8253[chan].byte_toggle)
+                tmr_8253[chan].flags ^= TMR_WTOGGLE; /* toggle */
+                if (tmr_8253[chan].flags & TMR_WTOGGLE)
                 {
                     tmr_8253[chan].countmax =
                         (tmr_8253[chan].countmax & 0xff00) | (BYTE)value;
@@ -627,12 +691,10 @@ void WINAPI DOSVM_outport( int port, int size, DWORD value )
                     (tmr_8253[chan].countmax & 0x00ff) | ((BYTE)value << 8);
                 break;
             }
-            /* if programming is finished and value has changed
-               then update to new value */
-            if ((((tmr_8253[chan].ctrlbyte_ch & 0x30) != 0x30) ||
-                 !tmr_8253[chan].byte_toggle) &&
-                (tmr_8253[chan].countmax != tmr_8253[chan].oldval))
-                set_timer_maxval(chan, tmr_8253[chan].countmax);
+            /* if programming is finished, update to new value */
+            if ((tmr_8253[chan].ctrlbyte_ch & 0x30) &&
+                !(tmr_8253[chan].flags & TMR_WTOGGLE))
+                set_timer(chan);
         }
         break;
     case 0x43:
@@ -641,28 +703,51 @@ void WINAPI DOSVM_outport( int port, int size, DWORD value )
            /* ctrl byte for specific timer channel */
            if (chan == 3)
            {
-               FIXME("8254 timer readback not implemented yet\n");
+               if ( !(value & 0x20) )
+               {
+                   if (value & 0x02 && !(tmr_8253[0].flags & TMR_LATCHED))
+                   {
+                       tmr_8253[0].flags |= TMR_LATCHED;
+                       tmr_8253[0].latch = get_timer_val(chan);
+                   }
+                   if (value & 0x04 && !(tmr_8253[1].flags & TMR_LATCHED))
+                   {
+                       tmr_8253[1].flags |= TMR_LATCHED;
+                       tmr_8253[1].latch = get_timer_val(chan);
+                   }
+                   if (value & 0x08 && !(tmr_8253[2].flags & TMR_LATCHED))
+                   {
+                       tmr_8253[2].flags |= TMR_LATCHED;
+                       tmr_8253[2].latch = get_timer_val(chan);
+                   }
+               }
+
+               if ( !(value & 0x10) )
+               {
+                   if (value & 0x02)
+                       tmr_8253[0].flags |= TMR_STATUS;
+                   if (value & 0x04)
+                       tmr_8253[1].flags |= TMR_STATUS;
+                   if (value & 0x08)
+                       tmr_8253[2].flags |= TMR_STATUS;
+               }
                break;
            }
            switch (((BYTE)value & 0x30) >> 4)
            {
            case 0:	/* latch timer */
-               tmr_8253[chan].latched = TRUE;
-               dummy_ctr -= 1 + (int)(10.0 * rand() / (RAND_MAX + 1.0));
-               if (chan == 0) /* System timer divisor */
-                   tmr_8253[chan].latch = dummy_ctr + (WORD)DOSVM_GetTimer();
-               else
+               if ( !(tmr_8253[chan].flags & TMR_LATCHED) )
                {
-                   /* FIXME: intelligent hardware timer emulation needed */
-                   tmr_8253[chan].latch = dummy_ctr;
+                   tmr_8253[chan].flags |= TMR_LATCHED;
+                   tmr_8253[chan].latch = get_timer_val(chan);
                }
                break;
-           case 3:	/* write lo byte, then hi byte */
-               tmr_8253[chan].byte_toggle = FALSE; /* init */
-               /* fall through */
            case 1:	/* write lo byte only */
            case 2:	/* write hi byte only */
+           case 3:	/* write lo byte, then hi byte */
                tmr_8253[chan].ctrlbyte_ch = (BYTE)value;
+               tmr_8253[chan].countmax = 0;
+               tmr_8253[chan].flags = TMR_UPDATE;
                break;
            }
        }
-- 
1.4.4.4


--------------040008020207040104040808--



More information about the wine-patches mailing list