realmer@wet.UUCP (Sean B. Straw) (09/04/90)
Personally, I don't know what the fuss is with trying to identify the Intel CPUs from software - it's quite simple really. It's all in the prefetch queue (literally). I have assembler code that has successfully identified the iAPX80386SX from the iAPX80386DX (which I could already identify), and the iAPX80486 is identified as well through this means (although it is a rather particular creature). The Intel specified way is really ... *ssbackwards (as my personal engineering beliefs dictate - sorry to all of you Intel engineers), as, if you think about it, application software hasn't a hope of having control of the CPU right after a CPU RESET, and when it finally does gain control, the EAX register sure as hell isn't what the CPU set it to after RESET. The trick with the prefetch queue is changing a value in the code segment, that will affect how the CPU will perform it's duties. If the instruction is outside of the prefetch queue, then the CPU will act on the change you made. However, if the instruction is inside of the prefetch queue when you make the alteration, then the CPU will *not* act on the change (unless you entered the code again, at which time the CPU would have re-read the memory). The following assembler (and it must be written in assembler) real-mode code fragment demonstrates this function: QUEUESIZE EQU 12 ; Known size of the CPU you are ; testing for. This is the key ; value when determining the CPU ; type. You should experiment ; with this value yourself to ; determine which is accurrate for ; the CPU you are working with... ; I will make a later posting with ; the values for the various ; CPU types (this code is slightly ; different (better) than what I ; have put to use already). PUSHF ; Preserve Flags PUSH DS ; .. DS PUSH AX ; .. AX PUSH CS ; Get CS ... POP DS ; .. Into DS MOV AL,BYTE PTR MODIFY ; Get original value of ; modified instruction. CLI ; Disable software interrupts ; (you may wish to disable NMI and ; Watchdog timer as well...) CALL FAR PTR ID_CPU ; This should force a re-read of the ; prefetch queue. Used to be a JMP ; instruction would do it, but the ; IAPX80486 is a bit optimized... STI ; Reenable software interrupts MOV BYTE PTR MODIFY,AL ; Restore original value of ; modified instruction POP AX ; Restore AX POP DS ; .. DS JC SMALLQUEUE ; Branch if the CPU has a smaller ; queue. LARGEQUEUE: ; ; Set your flags for the condition of a large prefetch queue. ; ; (Your routine goes here) JMP ID_EXIT ; And leave this area. SMALLQUEUE: ; ; Set your flags for the condition of a small prefetch queue. ; ; (Your routine goes here) JMP ID_EXIT ; And leave this area. EVEN ; Align on EVEN address boundary ID_CPU PROC FAR ; ; (On entry here, the first instruction should be at the head of ; the prefetch queue). ; MOV BYTE PTR MODIFY,0F8h ; CLC instruction. ; ; The following should fill the remainder of the prefetch with ; NOP instructions, such that the last thing in the queue should ; be the carry modifying instruction. If the queue is smaller ; than the QUEUESIZE, then the CF modifying instruction will fall ; outside of the prefetch queue, such that the CPU will read the ; changed instruction into the prefetch, and act on it. If the ; instruction is inside of the queue, then the change will not be ; acted upon (although the memory will be changed). It is ; imperative that the preceeding instruction not use a CS: ; override, but that it use the DS: for its source. It is my ; belief (through testing) that the iAPX80486 understands when it's ; prefetch is being modified via CS: overrides. ; IF ((QUEUESIZE - 6) GT 0) DB (QUEUESIZE - 6) DUP (090h) ; NOP instructions ENDIF MODIFY LABEL BYTE STC ; Set CF (or be modified to CLC) RET ; Return to caller ID_CPU ENDP ID_EXIT: ; ; (Continue with your normal applcation) ; POPF ; Restore Flags It should be noted that this fragment of code relies on the professionally frowned-upon practice of self-modification of code. It has to be this way. I certainly don't write anything else this way, but the prefetch is an exception to the Cardinal Rule... This version of the routine can be called a multiple number of times as the original value of the MODIFY label (the CF modifier) is preserved and restored. This is relatively useless, as you should just set a CPU_ID value to indicate the CPU you have found, and never call into the routine again... Should you have a desire or need to obtain a fully-functional assembler module for the determination of CPUs (all 8086 compatable types I have ever managed to obtain...), send me email, and perhaps I shall post the module, or respond directly to you.