[Home]

MIPS.COM benchmark program

MIPS.COM is a popular MS-DOS and IBM PC benchmark program from 1980s.

The version of my binary is v1.10, 13312 bytes and the file time stamp is 1987-03-28 (md5sum 2ca6ecb0dbacfb78da35b13ae73aaf8d). No source code is known to be available. However, like with many programs from this era, the disassembly is almost human readable code.

The program has couple annoying cosmetic problems when it's run on a modern PC today, and I have attempted to fix them.

Bug #1: Time and date in banner messages

This a classic Y2K problem. If the year is >= 2000 then time and date are displayed incorrectly. Example:

Expected output:

              +---------------------------------------------------+
              |   MIPS v1.10 CPU Benchmark and Performance Test   |
          +-----------------------------------------------------------+
          | 08/03/24 |   Million Instructions Per Second   | 12:07:37 |
          +-----------------------------------------------------------+

Actual output:

          +-----------------------------------------------------------+
          |          |   Million Instructions Per Second   | 12:07:37X0
          +-----------------------------------------------------------+

What's interesting is that the corruption looks different depending on what day it is. On many days, the output looks basically fine (just the date is blank and time is correct without garbage characters).

Bug #2: Unreadable table formatting

This happens when your PC is running too fast.

Correct output should look like:

          +-----------------------------------------------------------+
          |      BENCHMARK:      |  IBM/XT   IBM/AT   COMPAQ  |       |
          |                      |  4.7Mhz    8Mhz      386   |  MIPS |
          |-----------------------------------------------------------|
          | General Instructions |   26.17     7.61     3.84  |  4.34 |
          | Integer Instructions |   38.66     6.04     2.65  |  6.51 |
          | Memory to Memory     |   31.21     9.62     5.36  |  7.43 |
          | Register to Register |   38.28     4.97     2.08  |  6.89 |
          | Register to Memory   |   21.71     6.52     3.60  |  6.69 |
          |                      |                            |       |
          | Performance Rating   |   28.86     6.96     3.42  |  6.37 |
          +-----------------------------------------------------------+

On a fast machine, when the multiplier or rating gets >= 100 the output is unreadable/corrupted:

          +-----------------------------------------------------------+
          |      BENCHMARK:      |  IBM/XT   IBM/AT   COMPAQ  |       |
          |                      |  4.7Mhz    8Mhz      386   |  MIPS |
          |-----------------------------------------------------------|
          | General Instructions |   71.87    20.90    10.55  | 11.93 |
          | Integer Instructions |    7.20     1.13     0.49  |  1.21 |
          | Memory to Memory     |   94.64    29.15    16.27  | 22.55 |
          | Register to Register |            34.03    14.2661.847.16 |
          | Register to Memory   |            13.45     7.4244.813.82 |
          |                      |                            |       |
          | Performance Rating   |             3.09     1.5212.819.33 |
          +-----------------------------------------------------------+

Bug #3: Counter wraparound

The counter wraps on a fast machine, sometimes making benchmark to indicate a poor performance.

Analysis

The root cause for the both output problems is the same. The routine at CS:0A42 that converts the number in AX to ASCII is expected to write only 2 bytes. If the value is bigger than 99, an unexpected extra byte gets written that breaks the data (buffer overflow).

The counter at DS:018E used for benchmarks is 16-bits wide, so it can wrap around easily on a modern machine.

Fix

First, let's take a copy so we don't lose the original program:

A:\>copy mips.com newmips.com
        1 file(s) copied

Then start hacking with DEBUG.EXE:

A:\>debug newmips.com
-

Add a new routine for ensuring the year is always a 2-digit number:

-a 3500
1624:3500 cmp al,64
1624:3502 jge 3507
1624:3504 jmp a42
1624:3507 sub al,64
1624:3509 jmp a42
1624:350C
-r cx
CX 3400
:340C
-w
Writing 0340C bytes

Then we modify the call after the date check to use that routine instead:

-u 49d
1624:049D B42A          MOV     AH,2A
1624:049F CD21          INT     21
1624:04A1 8BC1          MOV     AX,CX
1624:04A3 2D6C07        SUB     AX,076C
1624:04A6 8D1E0702      LEA     BX,[0207]
1624:04AA 8D7F0E        LEA     DI,[BX+0E]
1624:04AD E89205        CALL    0A42
-a 4ad
1624:04AD call 3500
1624:04B0
-w
Writing 0340C bytes

To fix the counter wrapping and multiplier getting too large, we can add a function that limits the counter to some upper value. 0x3333 is chosen as it seems still to provide sensible results (and the maximum displayed benchmark result is roughly 4 times that of Compaq 386):

-a 350c
1624:350C cmp byte ptr [018f],33
1624:3511 jne 351b
1624:3513 cmp byte ptr [018e],33
1624:3518 jne 351b
1624:351A ret
1624:351B inc word ptr [018e]
1624:351F ret
1624:3520
-r cx
CX 340C
:3420
-w
Writing 03420 bytes

Then each benchmark is modified to use that routine instead of just incrementing the counter. There should be 5 places in the binary where the counter is incremented:

-u 1014
1624:1014 FF068E01      INC     WORD PTR [018E]
[...]
-a 1014
1624:1014 call 350c
1624:1017 nop
1624:1018
-u 119d
1624:119D FF068E01      INC     WORD PTR [018E]
[...]
-a 119d
1624:119D call 350c
1624:11A0 nop
1624:11A1
-u 1a80
1624:1A80 FF068E01      INC     WORD PTR [018E]
[...]
-a 1a80
1624:1A80 call 350c
1624:1A83 nop
1624:1A84
-u 1b78
1624:1B78 FF068E01      INC     WORD PTR [018E]
[...]
-a 1b78
1624:1B78 call 350c
1624:1B7B nop
1624:1B7C
-u 2063
1624:2063 FF068E01      INC     WORD PTR [018E]
[...]
-a 2063
1624:2063 call 350c
1624:2066 nop
1624:2067
-w
Writing 03420 bytes

Note that the added extra function call and instructions are executed during the benchmark itself so it may skew the results a bit. However, testing on an original 8086 hardware shows a little or no difference (1% at maximum).

The resulting MIPS.COM binary size using these instructions is 13344 bytes and the md5sum is 62c6444119ba7799edebe354da43f638.


Last updated: 2024-08-05 23:04 (EEST)