]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - board/MAI/bios_emulator/scitech/src/pm/cpuinfo.c
* Patch by Thomas Frieden, 13 Nov 2002:
[karo-tx-uboot.git] / board / MAI / bios_emulator / scitech / src / pm / cpuinfo.c
1 /****************************************************************************
2 *
3 *                   SciTech OS Portability Manager Library
4 *
5 *  ========================================================================
6 *
7 *    The contents of this file are subject to the SciTech MGL Public
8 *    License Version 1.0 (the "License"); you may not use this file
9 *    except in compliance with the License. You may obtain a copy of
10 *    the License at http://www.scitechsoft.com/mgl-license.txt
11 *
12 *    Software distributed under the License is distributed on an
13 *    "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
14 *    implied. See the License for the specific language governing
15 *    rights and limitations under the License.
16 *
17 *    The Original Code is Copyright (C) 1991-1998 SciTech Software, Inc.
18 *
19 *    The Initial Developer of the Original Code is SciTech Software, Inc.
20 *    All Rights Reserved.
21 *
22 *  ========================================================================
23 *
24 * Language:     ANSI C
25 * Environment:  Any
26 *
27 * Description:  Main module to implement the Zen Timer support functions.
28 *
29 ****************************************************************************/
30
31 #include "ztimer.h"
32 #include "pmapi.h"
33 #include "oshdr.h"
34 #if !defined(__WIN32_VXD__) && !defined(__OS2_VDD__) && !defined(__NT_DRIVER__)
35 #include <stdio.h>
36 #include <string.h>
37 #endif
38
39 /*----------------------------- Implementation ----------------------------*/
40
41 /* External Intel assembler functions */
42 #ifdef  __INTEL__
43 /* {secret} */
44 ibool   _ASMAPI _CPU_haveCPUID(void);
45 /* {secret} */
46 ibool   _ASMAPI _CPU_check80386(void);
47 /* {secret} */
48 ibool   _ASMAPI _CPU_check80486(void);
49 /* {secret} */
50 uint    _ASMAPI _CPU_checkCPUID(void);
51 /* {secret} */
52 uint    _ASMAPI _CPU_getCPUIDModel(void);
53 /* {secret} */
54 uint    _ASMAPI _CPU_getCPUIDStepping(void);
55 /* {secret} */
56 uint    _ASMAPI _CPU_getCPUIDFeatures(void);
57 /* {secret} */
58 uint    _ASMAPI _CPU_getCacheSize(void);
59 /* {secret} */
60 uint    _ASMAPI _CPU_have3DNow(void);
61 /* {secret} */
62 ibool   _ASMAPI _CPU_checkClone(void);
63 /* {secret} */
64 void    _ASMAPI _CPU_readTimeStamp(CPU_largeInteger *time);
65 /* {secret} */
66 void    _ASMAPI _CPU_runBSFLoop(ulong iterations);
67 /* {secret} */
68 ulong   _ASMAPI _CPU_mulDiv(ulong a,ulong b,ulong c);
69 /* {secret} */
70 void ZTimerQuickInit(void);
71 #define CPU_HaveMMX     0x00800000
72 #define CPU_HaveRDTSC   0x00000010
73 #define CPU_HaveSSE     0x02000000
74 #endif
75
76 #if     defined(__SMX32__)
77 #include "smx/cpuinfo.c"
78 #elif   defined(__RTTARGET__)
79 #include "rttarget/cpuinfo.c"
80 #elif   defined(__REALDOS__)
81 #include "dos/cpuinfo.c"
82 #elif   defined(__NT_DRIVER__)
83 #include "ntdrv/cpuinfo.c"
84 #elif   defined(__WIN32_VXD__)
85 #include "vxd/cpuinfo.c"
86 #elif   defined(__WINDOWS32__)
87 #include "win32/cpuinfo.c"
88 #elif   defined(__OS2_VDD__)
89 #include "vdd/cpuinfo.c"
90 #elif   defined(__OS2__)
91 #include "os2/cpuinfo.c"
92 #elif   defined(__LINUX__)
93 #include "linux/cpuinfo.c"
94 #elif   defined(__QNX__)
95 #include "qnx/cpuinfo.c"
96 #elif   defined(__BEOS__)
97 #include "beos/cpuinfo.c"
98 #else
99 #error  CPU library not ported to this platform yet!
100 #endif
101
102 /*------------------------ Public interface routines ----------------------*/
103
104 /****************************************************************************
105 REMARKS:
106 Read an I/O port location.
107 ****************************************************************************/
108 static uchar rdinx(
109     int port,
110     int index)
111 {
112     PM_outpb(port,(uchar)index);
113     return PM_inpb(port+1);
114 }
115
116 /****************************************************************************
117 REMARKS:
118 Write an I/O port location.
119 ****************************************************************************/
120 static void wrinx(
121     ushort port,
122     ushort index,
123     ushort value)
124 {
125     PM_outpb(port,(uchar)index);
126     PM_outpb(port+1,(uchar)value);
127 }
128
129 /****************************************************************************
130 REMARKS:
131 Enables the Cyrix CPUID instruction to properly detect MediaGX and 6x86
132 processors.
133 ****************************************************************************/
134 static void _CPU_enableCyrixCPUID(void)
135 {
136     uchar   ccr3;
137
138     PM_init();
139     ccr3 = rdinx(0x22,0xC3);
140     wrinx(0x22,0xC3,(uchar)(ccr3 | 0x10));
141     wrinx(0x22,0xE8,(uchar)(rdinx(0x22,0xE8) | 0x80));
142     wrinx(0x22,0xC3,ccr3);
143 }
144
145 /****************************************************************************
146 DESCRIPTION:
147 Returns the type of processor in the system.
148
149 HEADER:
150 ztimer.h
151
152 RETURNS:
153 Numerical identifier for the installed processor
154
155 REMARKS:
156 Returns the type of processor in the system. Note that if the CPU is an
157 unknown Pentium family processor that we don't have an enumeration for,
158 the return value will be greater than or equal to the value of CPU_UnkPentium
159 (depending on the value returned by the CPUID instruction).
160
161 SEE ALSO:
162 CPU_getProcessorSpeed, CPU_haveMMX, CPU_getProcessorName
163 ****************************************************************************/
164 uint ZAPI CPU_getProcessorType(void)
165 {
166 #if     defined(__INTEL__)
167     uint            cpu,vendor,model,cacheSize;
168     static ibool    firstTime = true;
169
170     if (_CPU_haveCPUID()) {
171         cpu = _CPU_checkCPUID();
172         vendor = cpu & ~CPU_mask;
173         if (vendor == CPU_Intel) {
174             /* Check for Intel processors */
175             switch (cpu & CPU_mask) {
176                 case 4: cpu = CPU_i486;         break;
177                 case 5: cpu = CPU_Pentium;      break;
178                 case 6:
179                     if ((model = _CPU_getCPUIDModel()) == 1)
180                         cpu = CPU_PentiumPro;
181                     else if (model <= 6) {
182                         cacheSize = _CPU_getCacheSize();
183                         if ((model == 5 && cacheSize == 0) ||
184                             (model == 5 && cacheSize == 256) ||
185                             (model == 6 && cacheSize == 128))
186                             cpu = CPU_Celeron;
187                         else
188                             cpu = CPU_PentiumII;
189                         }
190                     else if (model >= 7) {
191                         /* Model 7 == Pentium III */
192                         /* Model 8 == Celeron/Pentium III Coppermine */
193                         cacheSize = _CPU_getCacheSize();
194                         if ((model == 8 && cacheSize == 128))
195                             cpu = CPU_Celeron;
196                         else
197                             cpu = CPU_PentiumIII;
198                         }
199                     break;
200                 default:
201                     cpu = CPU_UnkIntel;
202                 }
203             }
204         else if (vendor == CPU_Cyrix) {
205             /* Check for Cyrix processors */
206             switch (cpu & CPU_mask) {
207                 case 4:
208                     if ((model = _CPU_getCPUIDModel()) == 4)
209                         cpu = CPU_CyrixMediaGX;
210                     else
211                         cpu = CPU_UnkCyrix;
212                     break;
213                 case 5:
214                     if ((model = _CPU_getCPUIDModel()) == 2)
215                         cpu = CPU_Cyrix6x86;
216                     else if (model == 4)
217                         cpu = CPU_CyrixMediaGXm;
218                     else
219                         cpu = CPU_UnkCyrix;
220                     break;
221                 case 6:
222                     if ((model = _CPU_getCPUIDModel()) <= 1)
223                         cpu = CPU_Cyrix6x86MX;
224                     else
225                         cpu = CPU_UnkCyrix;
226                     break;
227                 default:
228                     cpu = CPU_UnkCyrix;
229                 }
230             }
231         else if (vendor == CPU_AMD) {
232             /* Check for AMD processors */
233             switch (cpu & CPU_mask) {
234                 case 4:
235                     if ((model = _CPU_getCPUIDModel()) == 0)
236                         cpu = CPU_AMDAm5x86;
237                     else
238                         cpu = CPU_AMDAm486;
239                     break;
240                 case 5:
241                     if ((model = _CPU_getCPUIDModel()) <= 3)
242                         cpu = CPU_AMDK5;
243                     else if (model <= 7)
244                         cpu = CPU_AMDK6;
245                     else if (model == 8)
246                         cpu = CPU_AMDK6_2;
247                     else if (model == 9)
248                         cpu = CPU_AMDK6_III;
249                     else if (model == 13) {
250                         if (_CPU_getCPUIDStepping() <= 3)
251                             cpu = CPU_AMDK6_IIIplus;
252                         else
253                             cpu = CPU_AMDK6_2plus;
254                         }
255                     else
256                         cpu = CPU_UnkAMD;
257                     break;
258                 case 6:
259                     if ((model = _CPU_getCPUIDModel()) == 3)
260                         cpu = CPU_AMDDuron;
261                     else
262                         cpu = CPU_AMDAthlon;
263                     break;
264                 default:
265                     cpu = CPU_UnkAMD;
266                 }
267             }
268         else if (vendor == CPU_IDT) {
269             /* Check for IDT WinChip processors */
270             switch (cpu & CPU_mask) {
271                 case 5:
272                     if ((model = _CPU_getCPUIDModel()) <= 4)
273                         cpu = CPU_WinChipC6;
274                     else if (model == 8)
275                         cpu = CPU_WinChip2;
276                     else
277                         cpu = CPU_UnkIDT;
278                     break;
279                 default:
280                     cpu = CPU_UnkIDT;
281                 }
282             }
283         else {
284             /* Assume a Pentium compatible Intel clone */
285             cpu = CPU_Pentium;
286             }
287         return cpu | vendor | (_CPU_getCPUIDStepping() << CPU_steppingShift);
288         }
289     else {
290         if (_CPU_check80386())
291             cpu = CPU_i386;
292         else  if (_CPU_check80486()) {
293             /* If we get here we may have a Cyrix processor so we can try
294              * enabling the CPUID instruction and trying again.
295              */
296             if (firstTime) {
297                 firstTime = false;
298                 _CPU_enableCyrixCPUID();
299                 return CPU_getProcessorType();
300                 }
301             cpu = CPU_i486;
302             }
303         else
304             cpu = CPU_Pentium;
305         if (!_CPU_checkClone())
306             return cpu | CPU_Intel;
307         return cpu;
308         }
309 #elif   defined(__ALPHA__)
310     return CPU_Alpha;
311 #elif   defined(__MIPS__)
312     return CPU_Mips;
313 #elif   defined(__PPC__)
314     return CPU_PowerPC;
315 #endif
316 }
317
318 /****************************************************************************
319 DESCRIPTION:
320 Returns true if the processor supports Intel MMX extensions.
321
322 HEADER:
323 ztimer.h
324
325 RETURNS:
326 True if MMX is available, false if not.
327
328 REMARKS:
329 This function determines if the processor supports the Intel MMX extended
330 instruction set.
331
332 SEE ALSO:
333 CPU_getProcessorType, CPU_getProcessorSpeed, CPU_have3DNow, CPU_haveSSE,
334 CPU_getProcessorName
335 ****************************************************************************/
336 ibool ZAPI CPU_haveMMX(void)
337 {
338 #ifdef  __INTEL__
339     if (_CPU_haveCPUID())
340         return (_CPU_getCPUIDFeatures() & CPU_HaveMMX) != 0;
341     return false;
342 #else
343     return false;
344 #endif
345 }
346
347 /****************************************************************************
348 DESCRIPTION:
349 Returns true if the processor supports AMD 3DNow! extensions.
350
351 HEADER:
352 ztimer.h
353
354 RETURNS:
355 True if 3DNow! is available, false if not.
356
357 REMARKS:
358 This function determines if the processor supports the AMD 3DNow! extended
359 instruction set.
360
361 SEE ALSO:
362 CPU_getProcessorType, CPU_getProcessorSpeed, CPU_haveMMX, CPU_haveSSE,
363 CPU_getProcessorName
364 ****************************************************************************/
365 ibool ZAPI CPU_have3DNow(void)
366 {
367 #ifdef  __INTEL__
368     if (_CPU_haveCPUID())
369         return _CPU_have3DNow();
370     return false;
371 #else
372     return false;
373 #endif
374 }
375
376 /****************************************************************************
377 DESCRIPTION:
378 Returns true if the processor supports Intel KNI extensions.
379
380 HEADER:
381 ztimer.h
382
383 RETURNS:
384 True if Intel KNI is available, false if not.
385
386 REMARKS:
387 This function determines if the processor supports the Intel KNI extended
388 instruction set.
389
390 SEE ALSO:
391 CPU_getProcessorType, CPU_getProcessorSpeed, CPU_haveMMX, CPU_have3DNow,
392 CPU_getProcessorName
393 ****************************************************************************/
394 ibool ZAPI CPU_haveSSE(void)
395 {
396 #ifdef  __INTEL__
397     if (_CPU_haveCPUID())
398         return (_CPU_getCPUIDFeatures() & CPU_HaveSSE) != 0;
399     return false;
400 #else
401     return false;
402 #endif
403 }
404
405 /****************************************************************************
406 RETURNS:
407 True if the RTSC instruction is available, false if not.
408
409 REMARKS:
410 This function determines if the processor supports the Intel RDTSC
411 instruction, for high precision timing. If the processor is not an Intel or
412 Intel clone CPU, this function will always return false.
413
414 DESCRIPTION:
415 Returns true if the processor supports RDTSC extensions.
416
417 HEADER:
418 ztimer.h
419
420 RETURNS:
421 True if RTSC is available, false if not.
422
423 REMARKS:
424 This function determines if the processor supports the RDTSC instruction
425 for reading the processor time stamp counter.
426
427 SEE ALSO:
428 CPU_getProcessorType, CPU_getProcessorSpeed, CPU_haveMMX, CPU_have3DNow,
429 CPU_getProcessorName
430 ****************************************************************************/
431 ibool ZAPI CPU_haveRDTSC(void)
432 {
433 #ifdef  __INTEL__
434     if (_CPU_haveCPUID())
435         return (_CPU_getCPUIDFeatures() & CPU_HaveRDTSC) != 0;
436     return false;
437 #else
438     return false;
439 #endif
440 }
441
442 #ifdef  __INTEL__
443
444 #define ITERATIONS      16000
445 #define SAMPLINGS       2
446 #define INNER_LOOPS     400
447
448 /****************************************************************************
449 REMARKS:
450 If processor does not support time stamp reading, but is at least a 386 or
451 above, utilize method of timing a loop of BSF instructions which take a
452 known number of cycles to run on i386(tm), i486(tm), and Pentium(R)
453 processors.
454 ****************************************************************************/
455 static ulong GetBSFCpuSpeed(
456     ulong cycles)
457 {
458     CPU_largeInteger t0,t1,count_freq;
459     ulong   ticks;              /* Microseconds elapsed during test     */
460     ulong   current;            /* Variable to store time elapsed       */
461     int     i,j,iPriority;
462     ulong   lowest  = (ulong)-1;
463
464     iPriority = SetMaxThreadPriority();
465     GetCounterFrequency(&count_freq);
466     for (i = 0; i < SAMPLINGS; i++) {
467         GetCounter(&t0);
468         for (j = 0; j < INNER_LOOPS; j++)
469             _CPU_runBSFLoop(ITERATIONS);
470         GetCounter(&t1);
471         current = t1.low - t0.low;
472         if (current < lowest)
473             lowest = current;
474         }
475     RestoreThreadPriority(iPriority);
476
477     /* Compute frequency */
478     ticks = _CPU_mulDiv(lowest,1000000,count_freq.low);
479     if ((ticks % count_freq.low) > (count_freq.low/2))
480         ticks++;            /* Round up if necessary */
481     if (ticks == 0)
482         return 0;
483     return ((cycles*INNER_LOOPS)/ticks);
484 }
485
486 #define TOLERANCE       1
487
488 /****************************************************************************
489 REMARKS:
490 On processors supporting the Read Time Stamp opcode, compare elapsed
491 time on the High-Resolution Counter with elapsed cycles on the Time
492 Stamp Register.
493
494 The inner loop runs up to 20 times oruntil the average of the previous
495 three calculated frequencies is within 1 MHz of each of the individual
496 calculated frequencies. This resampling increases the accuracy of the
497 results since outside factors could affect this calculation.
498 ****************************************************************************/
499 static ulong GetRDTSCCpuSpeed(
500     ibool accurate)
501 {
502     CPU_largeInteger    t0,t1,s0,s1,count_freq;
503     u64                 stamp0, stamp1, ticks0, ticks1;
504     u64                 total_cycles, cycles, hz, freq;
505     u64                 total_ticks, ticks;
506     int                 tries,iPriority;
507     ulong               maxCount;
508
509     PM_set64_32(total_cycles,0);
510     PM_set64_32(total_ticks,0);
511     maxCount = accurate ? 600000 : 30000;
512     iPriority = SetMaxThreadPriority();
513     GetCounterFrequency(&count_freq);
514     PM_set64(freq,count_freq.high,count_freq.low);
515     for (tries = 0; tries < 3; tries++) {
516         /* Loop until 100 ticks have passed since last read of hi-res
517          * counter. This accounts for overhead later.
518          */
519         GetCounter(&t0);
520         t1.low = t0.low;
521         t1.high = t0.high;
522         while ((t1.low - t0.low) < 100) {
523             GetCounter(&t1);
524             _CPU_readTimeStamp(&s0);
525             }
526
527         /* Loop until 30000 ticks have passed since last read of hi-res counter.
528          * This allows for elapsed time for sampling. For a hi-res frequency
529          * of 1MHz, this is about 0.03 of a second. The frequency reported
530          * by the OS dependent code should be tuned to provide a good
531          * sample period depending on the accuracy of the OS timers (ie:
532          * if the accuracy is lower, lower the frequency to spend more time
533          * in the inner loop to get better accuracy).
534          */
535         t0.low = t1.low;
536         t0.high = t1.high;
537         while ((t1.low - t0.low) < maxCount) {
538             GetCounter(&t1);
539             _CPU_readTimeStamp(&s1);
540             }
541
542         /* Find the difference during the timing loop */
543         PM_set64(stamp0,s0.high,s0.low);
544         PM_set64(stamp1,s1.high,s1.low);
545         PM_set64(ticks0,t0.high,t0.low);
546         PM_set64(ticks1,t1.high,t1.low);
547         PM_sub64(cycles,stamp1,stamp0);
548         PM_sub64(ticks,ticks1,ticks0);
549
550         /* Sum up the results */
551         PM_add64(total_ticks,total_ticks,ticks);
552         PM_add64(total_cycles,total_cycles,cycles);
553         }
554     RestoreThreadPriority(iPriority);
555
556     /* Compute frequency in Hz */
557     PM_mul64(hz,total_cycles,freq);
558     PM_div64(hz,hz,total_ticks);
559     return PM_64to32(hz);
560 }
561
562 #endif  /* __INTEL__ */
563
564 /****************************************************************************
565 DESCRIPTION:
566 Returns the speed of the processor in MHz.
567
568 HEADER:
569 ztimer.h
570
571 PARAMETERS:
572 accurate    - True of the speed should be measured accurately
573
574 RETURNS:
575 Processor speed in MHz.
576
577 REMARKS:
578 This function returns the speed of the CPU in MHz. Note that if the speed
579 cannot be determined, this function will return 0.
580
581 If the accurate parameter is set to true, this function will spend longer
582 profiling the speed of the CPU, and will not round the CPU speed that is
583 reported. This is important for highly accurate timing using the Pentium
584 RDTSC instruction, but it does take a lot longer for the profiling to
585 produce accurate results.
586
587 SEE ALSO:
588 CPU_getProcessorSpeedInHz, CPU_getProcessorType, CPU_haveMMX,
589 CPU_getProcessorName
590 ****************************************************************************/
591 ulong ZAPI CPU_getProcessorSpeed(
592     ibool accurate)
593 {
594 #if defined(__INTEL__)
595     /* Number of cycles needed to execute a single BSF instruction on i386+
596      * processors.
597      */
598     ulong   cpuSpeed;
599     uint    i;
600     static  ulong intel_cycles[] = {
601         115,47,43,
602         };
603     static  ulong cyrix_cycles[] = {
604         38,38,52,52,
605         };
606     static  ulong amd_cycles[] = {
607         49,
608         };
609     static  ulong known_speeds[] = {
610         1000,950,900,850,800,750,700,650,600,550,500,450,433,400,350,
611         333,300,266,233,200,166,150,133,120,100,90,75,66,60,50,33,20,0,
612         };
613
614     if (CPU_haveRDTSC()) {
615         cpuSpeed = (GetRDTSCCpuSpeed(accurate) + 500000) / 1000000;
616         }
617     else {
618         int type = CPU_getProcessorType();
619         int processor = type & CPU_mask;
620         int vendor = type & CPU_familyMask;
621         if (vendor == CPU_Intel)
622             cpuSpeed = GetBSFCpuSpeed(ITERATIONS * intel_cycles[processor - CPU_i386]);
623         else if (vendor == CPU_Cyrix)
624             cpuSpeed = GetBSFCpuSpeed(ITERATIONS * cyrix_cycles[processor - CPU_Cyrix6x86]);
625         else if (vendor == CPU_AMD)
626             cpuSpeed = GetBSFCpuSpeed(ITERATIONS * amd_cycles[0]);
627         else
628             return 0;
629         }
630
631     /* Now normalise the results given known processors speeds, if the
632      * speed we measure is within 2MHz of the expected values
633      */
634     if (!accurate) {
635         for (i = 0; known_speeds[i] != 0; i++) {
636             if (cpuSpeed >= (known_speeds[i]-3) && cpuSpeed <= (known_speeds[i]+3)) {
637                 return known_speeds[i];
638                 }
639             }
640         }
641     return cpuSpeed;
642 #else
643     return 0;
644 #endif
645 }
646
647 /****************************************************************************
648 DESCRIPTION:
649 Returns the speed of the processor in Hz.
650
651 HEADER:
652 ztimer.h
653
654 RETURNS:
655 Accurate processor speed in Hz.
656
657 REMARKS:
658 This function returns the accurate speed of the CPU in Hz. Note that if the
659 speed cannot be determined, this function will return 0.
660
661 This function is similar to the CPU_getProcessorSpeed function, except that
662 it attempts to accurately measure the CPU speed in Hz. This is used
663 internally in the Zen Timer libraries to provide accurate real world timing
664 information. This is important for highly accurate timing using the Pentium
665 RDTSC instruction, but it does take a lot longer for the profiling to
666 produce accurate results.
667
668 SEE ALSO:
669 CPU_getProcessorSpeed, CPU_getProcessorType, CPU_haveMMX,
670 CPU_getProcessorName
671 ****************************************************************************/
672 ulong ZAPI CPU_getProcessorSpeedInHZ(
673     ibool accurate)
674 {
675 #if defined(__INTEL__)
676     if (CPU_haveRDTSC()) {
677         return GetRDTSCCpuSpeed(accurate);
678         }
679     return CPU_getProcessorSpeed(false) * 1000000;
680 #else
681     return 0;
682 #endif
683 }
684
685 /****************************************************************************
686 DESCRIPTION:
687 Returns a string defining the speed and name of the processor.
688
689 HEADER:
690 ztimer.h
691
692 RETURNS:
693 Processor name string.
694
695 REMARKS:
696 This function returns an English string describing the speed and name of the
697 CPU.
698
699 SEE ALSO:
700 CPU_getProcessorType, CPU_haveMMX, CPU_getProcessorName
701 ****************************************************************************/
702 char * ZAPI CPU_getProcessorName(void)
703 {
704 #if defined(__INTEL__)
705     static int  cpu,speed = -1;
706     static char name[80];
707
708     if (speed == -1) {
709         cpu = CPU_getProcessorType();
710         speed = CPU_getProcessorSpeed(false);
711         }
712     sprintf(name,"%d MHz ", speed);
713     switch (cpu & CPU_mask) {
714         case CPU_i386:
715             strcat(name,"Intel i386 processor");
716             break;
717         case CPU_i486:
718             strcat(name,"Intel i486 processor");
719             break;
720         case CPU_Pentium:
721             strcat(name,"Intel Pentium processor");
722             break;
723         case CPU_PentiumPro:
724             strcat(name,"Intel Pentium Pro processor");
725             break;
726         case CPU_PentiumII:
727             strcat(name,"Intel Pentium II processor");
728             break;
729         case CPU_Celeron:
730             strcat(name,"Intel Celeron processor");
731             break;
732         case CPU_PentiumIII:
733             strcat(name,"Intel Pentium III processor");
734             break;
735         case CPU_UnkIntel:
736             strcat(name,"Unknown Intel processor");
737             break;
738         case CPU_Cyrix6x86:
739             strcat(name,"Cyrix 6x86 processor");
740             break;
741         case CPU_Cyrix6x86MX:
742             strcat(name,"Cyrix 6x86MX processor");
743             break;
744         case CPU_CyrixMediaGX:
745             strcat(name,"Cyrix MediaGX processor");
746             break;
747         case CPU_CyrixMediaGXm:
748             strcat(name,"Cyrix MediaGXm processor");
749             break;
750         case CPU_UnkCyrix:
751             strcat(name,"Unknown Cyrix processor");
752             break;
753         case CPU_AMDAm486:
754             strcat(name,"AMD Am486 processor");
755             break;
756         case CPU_AMDAm5x86:
757             strcat(name,"AMD Am5x86 processor");
758             break;
759         case CPU_AMDK5:
760             strcat(name,"AMD K5 processor");
761             break;
762         case CPU_AMDK6:
763             strcat(name,"AMD K6 processor");
764             break;
765         case CPU_AMDK6_2:
766             strcat(name,"AMD K6-2 processor");
767             break;
768         case CPU_AMDK6_III:
769             strcat(name,"AMD K6-III processor");
770             break;
771         case CPU_AMDK6_2plus:
772             strcat(name,"AMD K6-2+ processor");
773             break;
774         case CPU_AMDK6_IIIplus:
775             strcat(name,"AMD K6-III+ processor");
776             break;
777         case CPU_UnkAMD:
778             strcat(name,"Unknown AMD processor");
779             break;
780         case CPU_AMDAthlon:
781             strcat(name,"AMD Athlon processor");
782             break;
783         case CPU_AMDDuron:
784             strcat(name,"AMD Duron processor");
785             break;
786         case CPU_WinChipC6:
787             strcat(name,"IDT WinChip C6 processor");
788             break;
789         case CPU_WinChip2:
790             strcat(name,"IDT WinChip 2 processor");
791             break;
792         case CPU_UnkIDT:
793             strcat(name,"Unknown IDT processor");
794             break;
795         default:
796             strcat(name,"Unknown processor");
797         }
798     if (CPU_haveMMX())
799         strcat(name," with MMX(R)");
800     if (CPU_have3DNow())
801         strcat(name,", 3DNow!(R)");
802     if (CPU_haveSSE())
803         strcat(name,", SSE(R)");
804     return name;
805 #else
806     return "Unknown";
807 #endif
808 }