-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcpuid.asm
611 lines (560 loc) · 24.4 KB
/
cpuid.asm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
Page 60,132
;-----------------------------------------------------------------------------
;
; CPUID.ASM
;
; Copyright (c) 1995-Present Robert Collins
;
; You have my permission to copy and distribute this software for
; non-commercial purposes. Any commercial use of this software or
; source code is allowed, so long as the appropriate copyright
; attributions (to me) are intact, *AND* my email address is properly
; displayed.
;
; Basically, give me credit, where credit is due, and show my email
; address.
;
;-----------------------------------------------------------------------------
;
; Robert R. Collins email: [email protected]
;
;-----------------------------------------------------------------------------
;-----------------------------------------------------------------------------
; CPUID determines the CPU type in the system.
;-----------------------------------------------------------------------------
; This program will return the CPUID of an x86 processor. Virtually no
; attempt is made to distinguish between different processor vendors. If the
; processor supports the CPUID instruction, then the vendor ID will be
; printed. If the processor doesn't support CPUID, then you're out of luck.
; In this case, feel free to use this source code as your testbed to include
; such a test.
;-----------------------------------------------------------------------------
;
; *** NOTE ***
;
; The Intel386 processors use a different format for the processor
; signature than their Intel486 or Pentium contemporaries. Neither does
; the Intel386 processor support the CPUID instruction, hence the
; processor signature is only available upon RESET. Therefore, the
; earlier processors must have their processor signature "massaged"
; before being returned by this program.
;
; Maybe someday, if I have some time, I'll include a test which tries
; to grab the processor signature upon RESET from the Intel386 and
; early Intel486 processors which didn't support the CPUID instruction.
;
;-----------------------------------------------------------------------------
; Input: None
; Output: EDX = CPUID (if you already rely on CPUID being in EDX)
; EAX = CPUID (same as DX, used by high-level language calls)
; ERRORLEVEL = CPUID family (i.e. 5=Pentium, 4=Intel486...)
;
; CPUID defined as follows (as per Intel specifications)
;
; 3 ... 1 11 1100 0000 0000
; 1 ... 4 32 1098 7654 3210 Bit position
; +----------+--+----+----+----+
; | Reserved | | | | |
; +----------+--+----+----+----+
; ^^ ^^^^ ^^^^ ^^^^
; || |||| |||| ||||
; Processor Type -----------++ |||| |||| ||||
; Family ----------------------++++ |||| ||||
; Model ----------------------------++++ ||||
; Stepping ------------------------------++++
;
;
; Processor Type : 00 = Original OEM processor
; 01 = Overdrive processor
; 10 = Dual processor
; 11 = Not used (by Intel)
;
; Family: 0000 = 8086/8088
; 0001 = 80186/80188
; 0010 = 80286
; 0011 = Intel386
; 0100 = Intel486
; 0101 = Pentium
; 0110 = P6
;
; Model (family = 0101): 0001 = Pentium (510\66, 567\66)
; 0010 = Pentium P54C (735\90, 815\100)
; 0011 = Pentium overdrive processors
; 0101 = Pentium overdrive for IntelDX4
; 1111 = Unknown
;
; Model (family = 0100): 0000 = Intel486 DX
; 0001 = Intel486 DX
; 0010 = Intel486 SX
; 0011 = Intel487 (SX), or Intel486 DX
; 0100 = Intel486 SL
; 0101 = IntelSX2
; 0111 = IntelDX2 write-back enhanced
; 1000 = IntelDX4 and IntelDX4 overdrive
; 1111 = Unknown
;
; Model (family = 0011): 0000 = Intel386 DX
; 0010 = Intel386 SX, Intel386 CX, Intel386 EX
; 0100 = Intel386 SL
; 1111 = Unknown
;
;-----------------------------------------------------------------------------
;
; CPUID values for the following processors which don't support the CPUID
; instruction:
;
; 00FFh = 8086/8088 (model and stepping unknown)
; 10FFh = V20/V30 (model and stepping unknown)
; 01FFh = 80186/80188
; 020Fh = 80286
; 030Fh = 80386 DX
; 032Fh = 80386 SX
; 033Fh = 80376
; 034Fh = 80386 SL
; 040Fh = 80486 DX
; 042Fh = 80486 SX
; 043Fh = 80487 SX
;
;-----------------------------------------------------------------------------
; Register(s) modified: AX, BX, CX, EDX
;-----------------------------------------------------------------------------
;-----------------------------------------------------------------------------
; Compiler directives
;-----------------------------------------------------------------------------
.model small
.radix 16
.586p
;-----------------------------------------------------------------------------
; PUBLIC statements here
;-----------------------------------------------------------------------------
Public _CPUID_, Signature, OBJ_CPUID
;-----------------------------------------------------------------------------
; External declarations
;-----------------------------------------------------------------------------
Extern _CheckShutdownCPUID_: Near ; Defined in SHUTDOWN.ASM
Extern _ShutdownCPUID_ : Near ; Defined in SHUTDOWN.ASM
.data
;-----------------------------------------------------------------------------
; Local variable definitions
;-----------------------------------------------------------------------------
Signature dw ?
INT6 equ [bp-4]
FCW equ [bp-6] ; Floating point Control Word
;-----------------------------------------------------------------------------
; Interrupt vector segment
;-----------------------------------------------------------------------------
ABS0 segment at 0
org 6*4
Orig_INT6 dd ?
ABS0 ends
Align
.code
ASSUME CS:_TEXT, DS:_DATA, ES:_DATA, SS:NOTHING
OBJ_CPUID label word
ifdef RRC
org 000CAh
endif
;-----------------------------------------------------------------------------
_CPUID_ proc near
;-----------------------------------------------------------------------------
; Determine the CPU type by testing for differences in the CPU in the system.
;-----------------------------------------------------------------------------
; To determine if we are a 8086/8088, or 80186/80188, test the value of SP
; after it is placed on the stack. The algorithm for "PUSH SP" differs from
; 8086/80186 to 80286+. The algorithm difference is as follows:
;
; 8086/80186 80286+
; { {
; SP = SP - 2 TEMP = SP
; SS:SP = SP SP = SP - 2
; } SS:SP = TEMP
; }
;
; Thus for the 8086/80186, the value of SP that gets pushed on the stack is
; the value after SP is decremented. Hence, the value on the stack does not
; reflect the value of SP before the "PUSH" instruction. Therefore, all we
; have to do to categorize the CPU as 8086/8088 or 80186/80188 is to
; "PUSH SP" and compare the value on the stack image to the value in SP.
;-----------------------------------------------------------------------------
mov ax,00FFh ; clear CPU type return register
push sp ; save SP on stack to look at
pop bx ; get SP saved on stack
cmp bx,sp ; if 8086/8088, these values will differ
jz short @Not_8086 ; nope, must be other CPU type
;-----------------------------------------------------------------------------
; If this test passes, then we need some other means to differentiate between
; 8088/8088 and 80186/80188. This method I will use comes from "80186/188,
; 80C186/C188 Hardware Reference Manual" from Intel, PN# 270788, page
; A-2: "When a word write is performed at offset FFFFh in a segment, the
; 8086 will write one byte at offset FFFFh, and the other at offset 0, while
; an 80186 family processor will write one byte at offset FFFFh, and the
; other at offset 10000h (one byte beyond the end of the segment).
;-----------------------------------------------------------------------------
; Before we can blast a value out to FFFFh, we must save anything there, so
; we don't crash anybody else's data.
;-----------------------------------------------------------------------------
mov bx,ds:[0ffffh] ; get original data
mov word ptr ds:[0ffffh],0aaaah ; write signature at test location
cmp byte ptr ds:[0],0aah ; 8086?
mov ds:[0ffffh],bx
je short @Test_V20
inc ah
jmp short CPU_8086_Exit
;-----------------------------------------------------------------------------
; The V20 and 8086 differ with respect to the handling of FLAGS during a MUL
; instruction. The 8086 always sets ZF=0 (NZ). The V20 sets ZF according to
; the result.
;-----------------------------------------------------------------------------
@Test_V20:
push ax ; save results
xor al,al ; force ZF
mov al,40h ; multiplicand
mul al ; V20 doesn't affect ZF
pop ax ; restore results
jnz short CPU_8086_Exit ; Intel?
or ah,10h ; Set V20 flag
jmp short CPU_8086_Exit ; go split
;-----------------------------------------------------------------------------
; When we get here, we know that we aren't a 8086/80186. And since all
; subsequent processors will trap invalid opcodes via INT6, we will determine
; which CPU we are by trapping an invalid opcode.
; We are an 80486 if: XADD DL,DL executes correctly
; 80386 if: MOV EDX,EDX executes correctly
; 80286 if: SMSW DX executes correctly
;-----------------------------------------------------------------------------
; Setup INT6 handler
;-----------------------------------------------------------------------------
@Not_8086:
enter 6,0 ; create stack frame
mov word ptr INT6,offset INT6_handler
mov INT6[2],cs
call set_INT6_vector ; set pointer to our INT6 handler
mov ax,40Fh ; initialize CPU flag=4 (80486)
xor cx,cx ; initialize semaphore
;-----------------------------------------------------------------------------
; Now, try and determine which CPU we are by executing invalid opcodes.
; The instructions I chose to invoke invalid opcodes, are themselves rather
; benign. In each case, the chosen instruction modifies the DX register,
; and nothing else. No system parameters are changed, e.g. protected mode,
; or other CPU dependant features.
;-----------------------------------------------------------------------------
; The 80486 instruction 'XADD' xchanges the registers, then adds them.
;-----------------------------------------------------------------------------
xadd dl,dl ; 80486
jcxz CPU_486
dec ah ; set 80386 semaphore
inc cx ; CX=0
;-----------------------------------------------------------------------------
; For a description on the effects of the following instructions, look in
; the Intel Programmers Reference Manual's for the 80186, 80286, or 80386.
;-----------------------------------------------------------------------------
mov edx,edx ; 80386
jcxz CPU_386
dec ah ; set 80286 semaphore
inc cx ; CX=0
smsw dx ; 80286
jcxz CPU_exit
mov ax,-1 ; set UNKNOWN_CPU semaphore
CPU_exit:
mov dx,ax ; make a copy
call set_INT6_vector
leave
CPU_8086_Exit:
ret
@Found486:
jmp CPU_486
;-----------------------------------------------------------------------------
; Determine the difference between the 80386-DX and 80386-SX.
; The 80386-SX differs with the 386 in the CR0 register. Bit4 of CR0 is
; pulled high in the SX, but is latchable in the DX. This test attempts to
; clear bit4. If bit4 is still set, then we are an 80386-SX.
;-----------------------------------------------------------------------------
CPU_386:
mov Signature,ax ; save CPUID signature
call _CheckShutdownCPUID_ ; check to see if we'll be successful
; at getting the CPUID via shutdown
jc @F ; won't work, don't try
call _ShutdownCPUID_ ; try and get CPUID signature
jc @F ; didn't work...keep trying.
ror ah,4 ; swap nibble locations
shr ax,4 ; convert to standard CPUID format
jmp CPU_exit ; successful, go split
;-----------------------------------------------------------------------------
; When the reset method fails, we'll try and figure it out by "hand."
;-----------------------------------------------------------------------------
; The Intel386 DX CR0.bit4 is reserved, and can be set or cleared by
; software. All other Intel386 processors have this bit hard-wired to 1.
; If we can set this bit, then we're an Intel386 DX, otherwise, we have to
; keep testing to see whether or not we are an SX variant (80376, Intel386 SL).
;-----------------------------------------------------------------------------
@@: mov ax,Signature ; restore the CPUID signature
mov edx,cr0 ; Get CR0
push edx ; save CR0
and dl,not 10h ; clear bit4
mov cr0,edx ; set CR0
mov edx,cr0 ; and read CR0
and dl,10h ; bit4 forced high?
pop edx ; restore reg w/ CR0
mov cr0,edx ; restore CR0
jz short CPU_exit ; nope, must be 80386-DX.
;-----------------------------------------------------------------------------
; If we get here, we're at least an 80386 SX variant.
;-----------------------------------------------------------------------------
; Now we'll check for an 80376 processor. The 80376 is an Intel386 SX that
; is always in protected mode. Hence, CR0.PE always equals 1.
;-----------------------------------------------------------------------------
add al,20h ; set 80386-SX flag.
ifdef i376
push eax ; save CPUID
push edx ; save CR0 value
and dl,not 11h ; try and clear PE
mov cr0,edx ; store in CR0
mov eax,cr0 ; get CR0
xor edx,eax ; are we left w/ 11h? (80376)
cmp dl,11h ; 11h will indicate 80376 CPU
pop edx ; restore CR0 value
pop eax ; restore CPUID
mov cr0,edx ; now restore CR0 to original value
jne short @Try386SL ; must have been 80386 SX
add al,10h ; indicate 80376
jmp short CPU_exit ; go split
endif
;-----------------------------------------------------------------------------
; There are two ways to check for the Intel386 SL processor. The first
; method enters the processor into protected mode. The second method attempts
; to read the stepping information directly from the chip itself. The
; latter method sounds a whole lot easier, so that's what we'll try here.
; But, just in case it doesn't work, I had better explain the other method
; of detecting the Intel386 SL using protected mode.
;
; The Intel386 SL only has (logical) 25 address lines. This is an odd-ball
; amount of address lines, but it makes detecting this part, a snap. Go
; into protected mode, and set a 4G limit. First and store the dword at
; physical address 0. Write to address 1000000h a value of 55aa55aah. Do
; a near jump (JMP $+2) to flush the prefetch queue to guarantee that any
; capacitance on the data bus won't be 55aa55aa from our last memory write.
; Read the value at physical address 0. If the value you wrote to 1000000h
; was read back at 0, then chances are you're *NOT* an Intel386 SL, but an
; Intel386 SX. Try this test again, by writing 0aa55aa55 to physical
; address 2000000h. Read back at physical address 0. If you read back
; 0aa55aa55 at physical address 0, there's a near 100% chance you're an
; Intel386 SL.
;-----------------------------------------------------------------------------
; The other (easier) method of determining the processor stepping, is to read
; it from the on-board Signature register in the Intel386 SL.
;
; The 80386 SL has a register which allows reading the CPUID. This
; register is called the signature register, and lies in the On-board
; Memory Control Unit (OMCU) at register 0x30E. To read the signature
; register, first we must unlock access to the OMCU, read the signature,
; and relock access.
;-----------------------------------------------------------------------------
; To unlock access to the CPUPWRMODE register, we need to execute the
; following code sequence:
; write 00h to port(23h)
; write 80h to port(22h)
; write 0080h to port(22h) ; word write
;-----------------------------------------------------------------------------
@Try386SL:
call Check_x86SL ; check for SL processor
jmp CPU_exit ; go split
;-----------------------------------------------------------------------------
; Determine whether or not the processor supports the CPUID instruction.
; If it does, then we can use it, and split.
;-----------------------------------------------------------------------------
CPU_486:mov Signature,ax ; save CPUID signature
pushfd ; save eflags
pop eax ; save eflags
mov edx,eax ; make a copy
xor eax,200000h ; toggle the bit
push eax ; save it
popfd ; check if CPUID flag could toggle
pushfd ; save it, so we can check it
pop eax ; get it
xor eax,edx ; did bit toggle?
jz @F ; can't use CPUID instruction
push edx
popfd ; restore original EFLAGS value
mov eax,1 ; set CPUID operand
cpuid
jmp CPU_exit ; go split
;-----------------------------------------------------------------------------
; If we get here, then this processor doesn't support the CPUID instruction.
; So we have to perform the brute-force approach to detecting the CPUID.
;-----------------------------------------------------------------------------
; First, let's try and get the CPUID by the shutdown method. If that's
; unsuccessful, let's try and iterate through this until we figure out which
; processor we are.
;-----------------------------------------------------------------------------
@@: call _CheckShutdownCPUID_ ; check to see if we'll be successful
; at getting the CPUID via shutdown
jc @F ; won't work, don't try
call _ShutdownCPUID_ ; try and get CPUID signature
jnc CPU_exit ; successful, go split
;-----------------------------------------------------------------------------
; Determine the difference between the 80486DX, 80486SX, and 80487SX.
; The '486SX and '487SX differ with the DX in the CR0 register. Bit4 of CR0
; is pulled high in the SX, but is latchable in the DX. This test attempts to
; clear bit4. If bit4 is still set, then we are a '486SX or '487SX.
;-----------------------------------------------------------------------------
@@: mov ax,Signature ; restore signature
mov edx,cr0 ; Get CR0
and dl,not 10h ; clear bit4
mov cr0,edx ; set CR0
mov edx,cr0 ; and read CR0
and dl,10h ; bit4 forced high?
jz CPU_exit ; nope, must be 80486-DX.
add al,20h ; set 80486-SX flag.
;-----------------------------------------------------------------------------
; Now that we have determined to be a '48x SX, we need to detect the
; difference between a '486SX and '487SX. This is done by sensing the
; presence of the Numeric Processor eXtension (NPX). The algorithm comes
; from the Intel 80387 programmers reference manual section 6.2.3.
;-----------------------------------------------------------------------------
mov word ptr FCW,5a5ah ; initialize to non-zero value
fninit ; must use non wait form
fnstsw FCW ; store status
cmp byte ptr FCW,0 ; was the correct status w/ 0's read?
jne CPU_exit ; nope
fnstcw FCW ; save control word
mov bx,FCW ; get control word into AX
and bx,103fh ; mask proper status bits
cmp bx,3fh ; NPX installed?
jne CPU_exit ; nope
add al,10h ; set '487SX flag
jmp CPU_exit ; go split
;-----------------------------------------------------------------------------
; Set the INT6 vector by exchanging it with the one currently on the stack.
;-----------------------------------------------------------------------------
set_INT6_vector:
push dx
push ds
push ABS0 ; save interrupt vector segment
pop ds ; make DS=INT vector segment
ASSUME DS:ABS0
mov dx,word ptr ds:Orig_INT6; ; get offset if INT6 handler
xchg INT6,dx ; set new INT6 offset
mov word ptr ds:Orig_INT6,dx
mov dx,word ptr ds:Orig_INT6[2] ; get segment of INT6 handler
xchg INT6[2],dx ; set new INT6 segment
mov word ptr ds:Orig_INT6[2],dx
pop ds ; restore segment register
pop dx
ret ; split
ASSUME DS:NOTHING
;-----------------------------------------------------------------------------
Check_x86SL proc near ; Check for Intel386SL, or Intel486SL
; ; processors
;-----------------------------------------------------------------------------
; The Intel386 SL and Intel486 SL have a register which allows reading the
; CPUID. This register is called the signature register, and lies in the On-
; board Memory Control Unit (OMCU) at register 0x30E. To read the signature
; register, first we must unlock access to the OMCU, read the signature,
; and relock access.
;-----------------------------------------------------------------------------
; To unlock access to the CPUPWRMODE register, we need to execute the
; following code sequence:
; write 00h to port(23h)
; write 80h to port(22h)
; write 0080h to port(22h) ; word write
;-----------------------------------------------------------------------------
pushf
push ax ; save current CPUID
cli
in ax,22h ; get CPUPWRMODE register
xor ax,0ffffh ; all bits set?
jz @Not386SL ; yes, go split
in ax,22h ; get CPUPWRMODE register
test al,1 ; CPUPWRMODE unlocked?
jz @EnaPWRMODE ; nope, don't try and lock it.
;-----------------------------------------------------------------------------
; The safest way to determine whether or not this is a 386 SL is to attempt
; to lock and unlock the CPUPWRMODE register. If the register can be locked
; and unlocked as per 386 SL specifications, then there's a good chance that
; this isn't some chipset that amazingly supports the same enable/disable
; protocol. So if we can enable and disable the CPUPWRMODE register, then
; we'll proceed with reading the CPUID signature register.
;-----------------------------------------------------------------------------
; Lock the CPUPWRMODE register
;-----------------------------------------------------------------------------
mov al,00 ;
out 23h,al ;
mov ax,180h ; will lock CPUPWRMODE register
out 22h,al
out 22h,ax ; now CPUPWRMODE register should be
; locked.
in ax,22h ; get CPUPWRMODE register
test al,1 ; CPUPWRMODE unlocked?
jnz @Not386SL ; yes, go try and unlock it
;-----------------------------------------------------------------------------
; Unlock the CPUPWRMODE register
;-----------------------------------------------------------------------------
@EnaPWRMODE:
mov al,00 ;
out 23h,al ;
mov ax,80h ; will unlock CPUPWRMODE register
out 22h,al
out 22h,ax ; now CPUPWRMODE register should be
; unlocked.
in ax,22h ; get CPUPWRMODE register
test al,1 ; CPUPWRMODE unlocked?
jz @Not386SL ; yes, go try and unlock it
;-----------------------------------------------------------------------------
; Enable the On-board Memory Configuration Unit (OMCU). If this is an
; Intel486 SL, then bits [4-2] are the unit configuration select bits. If
; this is an Intel386 SL, then bits [3-2] are the unit configuration select
; bits.
;-----------------------------------------------------------------------------
and al,not 1100y ; clear configuration unit bits
mov dx,word ptr ss:[esp] ; get current CPUID
and dh,0fh ; only keep processor family bits
cmp dh,4 ; 486 SL?
jne @F ; nope
and al,not 11100y ; clear configuration unit bits
@@: or al,10y ; set unit enable bit
out 22h,ax ; now unit should be enabled
;-----------------------------------------------------------------------------
; Now read the CPUID signature register
;-----------------------------------------------------------------------------
mov dx,030Eh ; signature register
in ax,dx ; get CPUID signature
mov dx,ax ; make a copy
;-----------------------------------------------------------------------------
; Now as one final test, let's relock the CPUPWRMODE register and make sure
; it really gets locked. Otherwise, we'll ignore the value we just read.
;-----------------------------------------------------------------------------
mov al,00 ;
out 23h,al ;
mov ax,180h ; will lock CPUPWRMODE register
out 22h,al
out 22h,ax ; now CPUPWRMODE register should be
; locked.
in ax,22h ; get CPUPWRMODE register
test al,1 ; CPUPWRMODE unlocked?
jnz @Not386SL ; yes, go try and unlock it
;-----------------------------------------------------------------------------
; OK, must be 80386 SL, and we have CPUID in DX.
;-----------------------------------------------------------------------------
mov ax,dx ; restore copy of CPUID
ror ah,4 ; swap nibble locations
shr ax,4 ; convert to standard CPUID format
add sp,2 ; ignore value on stack
push ax
@Not386SL:
pop ax ; restore current CPUID
popf
ret ; split
Check_x86SL endp
;-----------------------------------------------------------------------------
; INT6 handler sets a semaphore (CX=FFFF) and adjusts the return address to
; point past the invalid opcode.
;-----------------------------------------------------------------------------
INT6_handler:
enter 0,0 ; create new stack frame
dec cx ; make CX=FFFF
add word ptr ss:[bp][2],3 ; point past invalid opcode
leave
iret
_CPUID_ endp
.stack 800h
end