|
|
1.1 root 1: /
2: / FLG_PIQL Pre-fetch instruction queue length, 0 => 4-byte, 1 => 6-byte
3: / FLG_08 Intel 808x
4: / FLG_NEC NEC V20 or V30
5: / FLG_18 Intel 8018x
6: / FLG_28 Intel 8028x
7: / FLG_38 Intel 8038x
8: /
9: / FLG_87 Intel 8087
10: / FLG_287 Intel 80287
11: / FLG_387 Intel 80387
12: /
13: / FLG_1167 Weitek 1167
14: /
15: / $FLG_CERR Faulty CPU
16: / $FLG_NERR Faulty NDP switch setting
17:
18: FLG record $RSVD:6,$FLG_NERR:1,$FLG_CERR:1,$FLG_WTK:1,$FLG_NDP:3,$FLG_CPU:4
19:
20: / CPU-related flags
21:
22: FLG_PIQL equ 0001b shl $FLG_CPU
23: FLG_08 equ 0000b shl $FLG_CPU
24: FLG_NEC equ 0010b shl $FLG_CPU
25: FLG_18 equ 0100b shl $FLG_CPU
26: FLG_28 equ 0110b shl $FLG_CPU
27: FLG_38 equ 1000b shl $FLG_CPU
28:
29: FLG_8088 equ FLG_08
30: FLG_8086 equ FLG_08 or FLG_PIQL
31: FLG_V20 equ FLG_NEC
32: FLG_V30 equ FLG_NEC or FLG_PIQL
33: FLG_80188 equ FLG_18
34: FLG_80186 equ FLG_18 or FLG_PIQL
35: FLG_80286 equ FLG_28 or FLG_PIQL
36: FLG_80386 equ FLG_38 or FLG_PIQL
37:
38: / NDP-related flags
39:
40: FLG_NDPX equ 000b shl $FLG_NDP / Not present
41: FLG_NDPU equ 001b shl $FLG_NDP / Untested
42: FLG_87 equ 010b shl $FLG_NDP
43: FLG_287 equ 011b shl $FLG_NDP
44: FLG_387 equ 100b shl $FLG_NDP
45:
46: FLG_1167 equ mask $FLG_WTK
47:
48: CPUID proc near / Start CPUID procedure
49:
50: / This procedure determines the type of CPU and NDP (if any) in use.
51: /
52: / The possibilities include:
53: /
54: / Intel 8086
55: / Intel 8088
56: / NEC V20
57: / NEC V30
58: / Intel 80186
59: / Intel 80188
60: / Intel 80286
61: / Intel 80386
62: / Intel 8087
63: / Intel 80287
64: / Intel 80387
65: /
66: / Also checked is whether or not the CPU allows interrupts after
67: / changing the SS segment register. If the CPU does, it is faulty and
68: / should be replaced.
69: /
70: / Further, if an NDP is installed, non-AT machines should have a
71: / system board switch set correspondingly. Such a discrepancy is
72: / reported upon.
73: /
74: / On exit, BX contains flag settings (as defined in $FLG record) which
75: / the caller can check. For example, to test for an Intel 80286, use
76: /
77: / and bx,mask $FLAG_CPU
78: /
79: / cmp bx,FLG_80286
80: / je ITSA286
81: /
82:
83: push ax / Save registers
84: push cx
85: push di
86: push ds
87: push es
88:
89: / Test for 80286/386 -- these CPUs execute PUSH SP by first storing SP on stack,
90: / then decrementing it. Earlier CPUs first decrement then store.
91:
92: push sp / Only 286 pushes pre-push SP
93: pop ax / Get it back
94:
95: cmp ax,sp / Check for same
96: jne CHECK_18x / They aren't, try next class
97:
98: call DIST_286or386 / Distinguish a 286 from 386
99: jmp short CHECK_PIQL / Join common code
100:
101: / Test for 80186/80188 -- 18x and later CPUs mask shift/rotate operations
102: / mod 32/ earlier CPUs use all 8 bits of CL.
103:
104: CHECK_18x:
105: mov bx,FLG_18 / Assume it's an 8018x
106: mov cl,32+1 / 18x masks shift counts mod 32
107: / Note we can't use just 32 in CL
108: mov al,0FFh / Start with all bits set
109:
110: shl al,cl / Shift one position if 18x
111: jnz CHECK_PIQL / Some bits still on, so it's a 18x or later/
112: / check PIQL
113:
114: mov bx,FLG_NEC / Assume it's an NEC V-series CPU
115: call CHECK_NEC / See if it's an NEC chip
116: jcxz CHECK_PIQL / Good guess, check PIQL
117:
118: mov bx,FLG_08 / It's an 808x
119: / subttl Check Length Of Pre-fetch Instruction Queue
120: / page
121:
122: / Check the length of the pre-fetch instruction queue (PIQ).
123: /
124: / xxxx6 CPUs have a PIQ length of 6 bytes,
125: / xxxx8 CPUs " " " 4 "
126: /
127: / Self-modifying code is used to distinguish the two PIQ lengths.
128: /
129: / To overcome write pipelining in 286/386 chips, the largest value
130: / over several executions of the subroutine is used.
131:
132: CHECK_PIQL:
133: call PIQL_SUB / Handled via subroutine
134:
135: cmp cx,PIQL / Use the larger of the two
136: jbe CHECK_PIQL1 / CX is smaller
137:
138: mov PIQL,cx / Save to report on later
139: CHECK_PIQL1:
140: dec PIQL_CNT / One fewer times through the loop
141: jnz CHECK_PIQL / Jump if
142:
143: cmp PIQL,4 / Check PIQL
144: jbe CHECK_ERR / Jump if xxxx8
145:
146: or bx,FLG_PIQL / PIQ length is 5 or longer
147: / subttl Check For Allowing Interrupts After POP SS
148: / page
149:
150: / Test for faulty chip (allows interrupts after change to SS register)
151:
152: CHECK_ERR:
153: call ERR_SUB / Handled via subroutine
154: jcxz CHECK_NDP / If CX is 0, the DEC was executed,
155: / and the CPU is OK
156: or bx,mask $FLG_CERR / It's a faulty chip
157: / subttl Check For Numeric Data Processor
158: / page
159: CHECK_NDP:
160: call NDP_SUB / Handled via subroutine
161:
162: pop es / Restore registers
163: pop ds
164: pop di
165: pop cx
166: pop ax
167:
168: ret / Return to caller
169:
170: CPUID endp / End CPUID procedure
171: / subttl Distinguish A 286 From 386
172: / page
173: DIST_286or386 proc near
174:
175: / The test for 286 vs. 386 is done by attempting to set flag bits in
176: / the high-order nibble of the flag word. If that's successful, it's a
177: / 386/ otherwise it's a 286.
178:
179:
180: push ax / Save register
181:
182: pushf / Save flags for a moment
183:
184: mov ax,0F000h / Try to set high bits in flag register
185:
186: push ax / Move into flag register
187: popf
188:
189: pushf / Get flags back into AX
190: pop ax
191:
192: popf / Restore original flags
193:
194: test ax,0F000h / Any bits set?
195: jz ITSA286 / No, so it's a 286
196:
197: or bx,FLG_38 / It's a 38x
198: jmp short DIST_286or386_EXIT / Join common exit code
199: ITSA286:
200: or bx,FLG_28 / It's a 28x
201: DIST_286or386_EXIT:
202: pop ax / Restore
203:
204: ret / Return to caller
205:
206: DIST_286or386 endp / End DIST_286or386 procedure
207: / subttl Check For NEC V20/V30
208: / page
209: CHECK_NEC proc near
210: / The NEC V20/V30 CPUs are very compatible with the Intel 8086/8088.
211: / The only point of "incompatiblity" is that they do not contain a bug
212: / found in the Intel CPUs. Specifically, the NEC CPUs correctly restart
213: / an interrupted multi-prefix string instruction at the start of the
214: / instruction. The Intel CPUs incorrectly restart it in the middle of
215: / the instruction. This routine tests for that situation by executing
216: / such an instruction for a sufficiently long period of time for a timer
217: / interrupt to occur. If at the end of the instruction, CX is zero,
218: / it must be an NEC CPU/ if not, it's an Intel CPU.
219: /
220: / Note that we're counting on the timer interrupt to do its thing
221: / every 18.2 times per second.
222: /
223: / Here's a worst case analysis: An Intel 8086/8088 executes 65535
224: / iterations of LODSB ES:[SI] in 2+9+13*65535 = 851,966 clock ticks. If
225: / the Intel 8086/8088 is running at 15 MHz, each clock tick is 66.67
226: / nanoseconds, hence the entire operation takes 56.8 milliseconds. If the
227: / timer is running at normal speed, it interrupts the CPU every 55
228: / millseconds and so should interrupt the repeated string instruction at
229: / least once.
230:
231: mov cx,0FFFFh / Move a lot of data
232: sti / Ensure timer enabled
233:
234: / Execute multi-prefix instruction. Note that the value of ES as
235: / well as the direction flag setting is irrelevant.
236:
237: push ax / Save registers
238: push si
239: rep lods byte ptr es:[si]
240: pop si / Restore
241: pop ax
242:
243: / On exit, if CX is zero, it's an NEC CPU, otherwise it's an Intel CPU
244:
245: ret / Return to caller
246:
247: CHECK_NEC endp
248: / subttl Pre-fetch Instruction Queue Subroutine
249: / page
250: PIQL_SUB proc near
251: / This subroutine attempts to discern the length of the CPU's
252: / pre-fetch instruction queue (PIQ).
253: /
254: / It stores a new instruction into the instruction stream
255: / following the STOSB. The loop proceeds backwards from the end
256: / of the stream to the beginning. At the point the inserted
257: / instruction is not executed, we have found the last byte in
258: / the PIQ. The value in CX at that time is then the PIQ length.
259:
260: push ax / Save registers
261: push bx
262: push dx
263: push si
264: push di
265:
266: @REP equ 64 / Maximum length of PIQ
267: / we can handle
268:
269: std / Store backwards
270:
271: mov cx,@REP / Loop counter
272: lea di,LAB_NOP+@REP-1 / ES:DI ==> PIQL last byte
273: / in fill area
274:
275: mov al,ds:LAB_INC / Change to INC BX
276: mov ah,ds:LAB_NOP / Save a NOP here to restore
277: mov si,1 / Divisor
278: xor dx,dx / Zero high-order word for divide
279: cli / Ensure interrupts are disabled, otherwise
280: / a timer tick could disturb the PIQ filling
281: even / Ensure word alignment for LAB_FILL
282: nop
283: PIQL_SUB_NEXT:
284: xor bx,bx / Initialize flag
285: div si / Take up some time and
286: / refill the queue
287: stosb / Change the instruction
288:
289: / The PIQ begins filling here
290:
291: LAB_NOP label byte
292: rept @REP
293: nop // Fill byte
294: endm
295:
296: mov es:[di+1],ah / Restore the NOP
297:
298: and bx,bx / Did we execute it?
299: loopnz PIQL_SUB_NEXT / Go around again if we did
300: / and loop not finished
301: inc cx / Count in last byte
302:
303: sti / Restore interrupts
304: cld / Restore direction flag
305:
306: pop di / Restore
307: pop si
308: pop dx
309: pop bx
310: pop ax
311:
312: / At the end, CX has the length of the PIQ
313:
314: ret / Return to caller
315:
316: LAB_INC label byte
317: inc bx / Increment counter
318:
319: PIQL_SUB endp / End PIQL_SUB procedure
320: / subttl Check For Faulty Interrupts
321: / page
322: ERR_SUB proc near
323: / Test for faulty chip (allows interrupts after change to SS register).
324: / Setup a handler for INT 01h (single-step interrupt) and turn on that
325: / flag just before executing a POP SS. If the CPU allows a single-step
326: / interrupt after the POP SS, it's faulty.
327: /
328: / On exit:
329: /
330: / CX = 1 if CPU is faulty
331: / = 0 if OK
332:
333:
334:
335: push ax / Save registers
336: push ds
337:
338: xor ax,ax / Prepare to address interrupt vector segment
339: mov ds,ax / DS points to segment 0
340:
341: cli / Nobody move while we swap
342:
343: lea ax,INT01 / Point to our own handler
344: xchg ax,INT01_OFF / Get and swap offset
345: mov OLDINT01_OFF,ax / Save to restore later
346:
347: mov ax,cs / Our handler's segment
348: xchg ax,INT01_SEG / Get and swap segment
349: mov OLDINT01_SEG,ax / Save to restore later
350:
351: / Note we continue with interrupts disabled to avoid an external interrupt
352: / occurring during this test.
353:
354: mov cx,1 / Initialize a register
355: push ss / Save SS to store back into itself
356:
357: pushf / Move flags
358: pop ax / ...into AX
359: or ax,mask $TF / Set trap flag
360: push ax / Place onto stack
361: POPFF / ...and then into effect
362: / Some CPUs effect the trap flag immediately,
363: / some wait one instruction.
364: nop / Allow interrupt to take effect
365: POST_NOP:
366: pop ss / Change the stack segment register (to itself)
367: dec cx / Normal CPUs execute this instruction before
368: / recognizing the single-step interrupt
369: hlt / We never get here
370: INT01:
371:
372: / Note IF=TF=0
373:
374: / If we're stopped at or before POST_NOP, continue on
375:
376: push bp / Prepare to address the stack
377: mov bp,sp / Hello, Mr. Stack
378:
379: cmp [bp].ARG_OFF,offset cs:POST_NOP / Check offset
380: pop bp / Restore
381: ja INT01_DONE / We're done
382:
383: iret / Return to caller
384: INT01_DONE:
385:
386: / Restore old INT 01h handler
387:
388: push es / Save for a moment
389: les ax,OLDINT01_VEC / ES:AX ==> old INT 01h handler
390: mov INT01_OFF,ax / Restore offset
391: mov INT01_SEG,es / ...and segment
392: pop es / Restore
393:
394: sti / Allow interrupts again (IF=1)
395:
396: add sp,3*2 / Strip IP, CS, and Flags from stack
397:
398: pop ds / Restore
399: pop ax
400:
401: ret / Return to caller
402:
403: ERR_SUB endp / End ERR_SUB procedure
404: / subttl Check For Numeric Data Processor
405: / page
406: NDP_SUB proc near
407: / Test for a Numeric Data Processor -- Intel 8087, 80287, or 80387.
408: / An 8087 allows FDISI, an 80287/80387 ignores it. The 80287 and 80387
409: / can be distinguished through their different treatment of the infinity
410: / closure setting.
411: /
412: / In general, the technique used is passive -- it leaves the NDP in
413: / the same state in which it is found.
414: /
415: / Unfortunately, some IBM PC/ATs and 3270/ATs without an NDP don't
416: / handle floating-point instructions correctly. In particular, when
417: / no-WAIT NDP instruction is executed on those systems, they wipe out
418: / the memory location and all bytes following in the same segment. To
419: / overcome this bug, Dan Lewis has suggested a technique which computes
420: / the segment and offset of the location into which the store is made
421: / such that the offset is in the last paragraph of the segment. This
422: / way, the wipe out is harmless.
423: /
424: / On exit:
425: /
426: / BX = $FLG_NDP & $FLG_NERR bits set as appropriate.
427:
428: push ax / Save registers
429: push cx
430: push di
431:
432: call CHECK_1167 / See if there's a Weitek 1167 in the system
433:
434: / Because some IBM PC/ATs and 3270/ATs without an NDP don't handle
435: / floating-point instructions correctly, we check for a 286 explicitly
436: / and rely upon the equipment flags to tell us if there's an NDP installed.
437: / This behavior is also present on some 386s.
438:
439: test LCL_FLAGS,@LCL_REAL / Use real code or not?
440: jnz NDP_SUB1 / It's real
441:
442: mov ax,bx / Copy CPUID bits for destructive testing
443: and ax,(mask $FLG_CPU) and not FLG_PIQL / Isolate CPU bits
444:
445: cmp ax,FLG_28 / Izit a 28x?
446: je NDP_SUB0 / Yes, skip NDP instructions
447:
448: cmp ax,FLG_38 / Izit a 38x?
449: jne NDP_SUB1 / Not this time
450: NDP_SUB0:
451: test LCL_FLAGS,@LCL_I11H / Skip INT 11h test?
452: jnz NDP_SUB_UN / Yes
453:
454: int 11h / Get equipment flags into AX
455:
456: test ax,mask $I11_NDP / Check NDP-installed bit
457: jz NDP_SUB_EXIT0 / Not installed
458:
459: call DIST_287or387 / Distinguish a 287 from a 387
460: NDP_SUB_EXIT0:
461: jmp NDP_SUB_EXIT / Join common exit code
462:
463: NDP_SUB_UN:
464: or bx,FLG_NDPU / Mark as untested
465:
466: jmp NDP_SUB_EXIT / Join common exit code
467:
468: NDP_SUB1:
469: push es / Save for a moment
470:
471: lea di,NDP_ENV+(size NDP_ENV)-1 / Offset of end of environment
472: call MAX_OFFSET / Return with ES:DI ==> NDP_ENV and DI largest
473: sub di,(size NDP_ENV)-1 / Back off to start of NDP_ENV
474:
475: cli / Protect FNSTENV
476: fnstenv es:[di] / If NDP present, save current environment,
477: / otherwise, this instruction is ignored
478: sti / Allow interrupts
479:
480: pop es / Restore
481:
482: mov cx,50/7 / Cycle this many times
483: loop $ / Wait for result to be stored
484:
485: fninit / Initialize processor to known state
486: jmp short $+2 / Wait for initialization
487:
488: push es / Save for a moment
489:
490: lea di,NDP_CW+(size NDP_CW)-1 / Offset of end of control word
491: call MAX_OFFSET / Return with ES:DI ==> NDP_CW and DI largest
492: sub di,(size NDP_CW)-1 / Back off to start of control word
493:
494: fnstcw es:[di] / Save control word
495:
496: pop es / Restore
497:
498: jmp short $+2 / Wait for result to be stored
499: jmp short $+2
500:
501: and NDP_CW,not mask $IC / Turn off infinity control in case of 387
502:
503: cmp NDP_CW_HI,03h / Check for NDP initial control word
504: jne NDP_SUB_NONE / No NDP installed
505:
506: test LCL_FLAGS,@LCL_I11H / Skip INT 11h test?
507: jnz NDP_SUB2 / Yes
508:
509: int 11h / Get equipment flags into AX
510:
511: test ax,mask $I11_NDP / Check NDP-installed bit
512: jnz NDP_SUB2 / It's correctly set
513:
514: or bx,mask $FLG_NERR / Mark as in error
515: NDP_SUB2:
516: and NDP_CW,not mask $IEM / Enable interrupts (IEM=0, 8087 only)
517: fldcw NDP_CW / Reload control word
518: fdisi / Disable interrupts (IEM=1) on 8087,
519: / ignored by 80287/80387
520: fstcw NDP_CW / Save control word
521: fldenv NDP_ENV / Restore original NDP environment
522: / No need to wait for environment to be loaded
523:
524: test NDP_CW,mask $IEM / Check Interrupt Enable Mask (8087 only)
525: jnz NDP_SUB_8087 / It changed, hence NDP is an 8087
526:
527: call DIST_287or387 / Distinguish a 287 from a 387
528:
529: jmp short NDP_SUB_EXIT / Exit with flags in BX
530: NDP_SUB_8087:
531: or bx,FLG_87 / NDP is an 8087
532:
533: jmp short NDP_SUB_EXIT / Join common exit code
534: NDP_SUB_NONE:
535: test LCL_FLAGS,@LCL_I11H / Skip INT 11h test?
536: jnz NDP_SUB_EXIT / Yes
537:
538: int 11h / Get equipment flags into AX
539:
540: test ax,mask $I11_NDP / Check NDP-installed bit
541: jz NDP_SUB_EXIT / It's correctly set
542:
543: or bx,mask $FLG_NERR / Mark as in error
544: NDP_SUB_EXIT:
545: pop di / Restore
546: pop cx
547: pop ax
548:
549: ret / Return to caller
550:
551: NDP_SUB endp / End NDP_SUB procedure
552: / subttl Check For Weitek 1167
553: / page
554: CHECK_1167 proc near
555: / See if there's a Weitek 1167 in the system.
556: /
557: / To determine that, clear EAX, call INT 11h, and test bit 24
558: / in EAX. If set, the coprocessor is present/ if not, then
559: / it's not. Obviously, we must be running on a 386.
560:
561: test LCL_FLAGS,@LCL_I11H / Skip INT 11h test?
562: jnz CHECK_1167_EXIT / Yes, 1167 untested
563:
564: test bx,FLG_38 / Are we on a 38x?
565: jz CHECK_1167_EXIT / No, thus no 1167
566:
567: db 66h / Use EAX
568: push ax / Save for a moment
569:
570: db 66h / Use EAX
571: xor ax,ax / Clear entire register
572:
573: int 11h / Get equipment flags
574:
575: db 66h / Use EAX
576: test ax,0000h
577: dw 0100h / Test bit 24
578: jz CHECK_1167_EXIT0 / Not present
579:
580: or bx,FLG_1167 / Mark as present
581: CHECK_1167_EXIT0:
582: db 66h / Use EAX
583: pop ax / Restore
584: CHECK_1167_EXIT:
585: ret / Return to caller
586:
587: CHECK_1167 endp / End CHECK_1167 procedure
588: / subttl Distinguish A 287 From 387
589: / page
590: DIST_287or387 proc near
591: / Distinguish a 287 from a 387.
592: /
593: / Both the 80287 and 80387 are initialized with the infinity closure
594: / bit set to one. However, only the 80287 is sensitive to the value of
595: / this bit. Ordinarily when this bit is set to one, the chip uses
596: / projective closure/ when it is cleared to zero, the chip uses affine
597: / closure. Thus the 80287 is initialized to use projective closure, but
598: / that state can be changed through the infinity closure bit in the
599: / control word. On the other hand, the 80387 is initialized to use
600: / affine closure and remains in that state independent of the setting of
601: / the infinity closure bit. The two NDPs can be distinguished by
602: / executing code which is sensitive to the setting of the infinity
603: / closure bit.
604:
605: The algorithm used is based upon one published by Intel on how to
606: detect the 80387.
607:
608: On exit:
609:
610: BX = $FLG_NDP bits set as appropriate.
611:
612: |
613:
614: .287
615: fstenv NDP_ENV / Save current environment
616: finit / Initialize processor to known state
617: / The 80287 is using projective
618: / closure for arithmetic, the 80387 is
619: / using affine closure
620:
621: fld1 / Generate infinity
622: fldz / by dividing zero into one
623: fdiv / ST0 = +infinity
624: fld st(0) / Copy it
625: fchs / ST0 = -infinity, ST1 = +infinity
626: fcompp / Compare them and pop both from stack
627: fstsw ax / Get status word
628: fldenv NDP_ENV / Restore original NDP environment
629: sahf / Copy into flags
630: jz DIST_287or387_PROJ / Jump if the two are equal
631: / (projective closure)
632:
633: or bx,FLG_387 / NDP is an 80387
634:
635: jmp short DIST_287or387_EXIT / Join common exit code
636:
637: DIST_287or387_PROJ:
638: or bx,FLG_287 / NDP is an 80287
639: DIST_287or387_EXIT:
640: ret / Return to caller
641: .8087
642: DIST_287or387 endp / End DIST_287or387 procedure
643: / subttl Calculate Maximum Offset
644: / page
645: MAX_OFFSET proc near
646:
647: / On entry:
648: /
649: / ES:DI ==> memory offset
650: /
651: / On exit:
652: /
653: / ES:DI ==> same location but with DI as large as possible
654:
655:
656: MAXOFF equ 0FFF0h
657:
658: push ax / Save registers
659: push cx
660:
661: push di
662:
663: mov cl,4 / Shift amount
664: shr di,cl / Isolate para of control word
665: mov ax,es / Get current segment
666: add ax,di / Add in segment of control word
667: sub ax,MAXOFF shr 4 / Less a lot of paras
668: mov es,ax / Save as segment of control word
669:
670: pop di / Restore
671:
672: or di,MAXOFF / Include paras subtracted out above
673:
674: pop cx / Restore
675: pop ax
676:
677: ret / Return to caller
678:
679: MAX_OFFSET endp / End MAX_OFFSET procedure
680:
681: CODE ends / End CODE segment
682:
683: if1
684: %OUT Pass 1 complete
685: else
686: %OUT Pass 2 complete
687: endif
688:
689: end INITIAL / End CPUID module
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.