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.
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).
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 | +-----------------------------------------------------------+
The counter wraps on a fast machine, sometimes making benchmark to indicate a poor performance.
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.
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)