; ====================================== ; mcpuinfo.asm ; ; Turning the cpuid3a.asm sample, with tweaks, from the Intel AP-485 manual ; into a usable program for the MS-DOS operating system. ; ; Compile: nasm mcpuinfo.asm -fbin -omcpuinfo.com ; Author: dd86k <dd@dax.moe> ; ====================================== BITS 16 ; Base is 16-bit code CPU 386 ; Contains mixed 16+32-bit code ORG 100h ; Origin of CS:0100 %define VERSION '0.1.0' %define NL 13,10 section .text start: mov si,81h ; Start index of command-line (in PSP) call skip_delim ; Skip to first token mov bx,si ; Save delim position mov di,opt_help ; Help switch string mov cx,opt_helplen ; Help switch string length repe cmpsb jcxz cli_help mov si,bx ; Reset to start of token mov di,opt_helpalt ; Alt help switch string mov cx,opt_helpaltlen ; Alt help switch string length repe cmpsb jcxz cli_help mov si,bx ; Reset to start of token mov di,opt_version ; Version switch string mov cx,opt_versionlen ; Version switch string length repe cmpsb jcxz cli_version mov si,bx ; Reset to start of token mov di,opt_ver ; Ver switch string mov cx,opt_verlen ; Ver switch string length repe cmpsb jcxz cli_ver jmp check_8086 ; Version string cli_ver: mov dx,page_ver call print jmp exit ; Version page cli_version: mov dx,page_version call print jmp exit ; Help page cli_help: mov dx,page_help call print jmp exit ; Detect i8086, snippet taken out of AP-485. ; The 8086 has FLAGS bits 15:12 always set and cannot be cleared. ; The 8086 also changes the value of SP after pushing. check_8086: pushf ; Push original FLAGS pop ax ; Get FLAGS into AX mov cx,ax ; Save original FLAGS and ax,0fffh ; Clear bits 15:12 push ax ; Push new value into stack popf ; Set FLAGS from value from stack pushf ; Get new FLAGS pop ax ; Save FLAGS value and ax,0f000h ; Keep bits 15:12 cmp ax,0f000h ; Anything in FLAGS[15:12]? jne check_286 ; If cleared, it's probably an i286 push sp ; Not an i286? Check for PUSH/SP diff then pop dx ; Save SP value cmp dx,sp ; If current SP value jne case_8086 ; jmp case_unknown ; ; Detect i286. ; The i286 has FLAGS bits 15:12 always cleared and cannot be set. check_286: or cx,0f000h ; Try to set bits 15:12 to saved FLAGS push cx popf ; Sets FLAGS from CX pushf pop dx ; Get new FLAGS into DX and dx,0f000h ; Clear FLAGS[11:0] jz case_286 ; Jump if FLAGS[15:12] is cleared ; Detect i386. check_386: pushfd ; Push original EFLAGS pop eax ; Get ELFAGS mov ecx,eax ; Save EFLAGS xor eax,40000h ; Flip EFLAGS[AC] push eax ; Save new value on stack popfd ; Replace EFLAGS pushfd ; Get new EFLAGS pop eax ; Store EFLAGS xor eax,ecx ; If can't toggle EFLAGS[AC], it is 80386 jz case_386 push ecx ; Restore EFLAGS popfd jmp case_486 ; Detect dedicated FPUs such as the 80287 and 80387. check_fpu: ; call print ; Print what's in DX since we're here fninit ; Resets FPU if present fnstsw ax ; Get FPU status word cmp al,0 ; Do we have anything? jne exit ; No FPU then ; Check control word ; NOTE: Certainly doesn't seem to work with DosBox-X ;check_fpu_cw: ; fnstcw [fpucw] ; Get FPU status word ; mov ax,fpucw ; and ax,103fh ; ; cmp ax,3fh ; ; jne exit ; Failsafe: Incorrect FPU word, no FPU check_8087: and ax,0xff7f ; Clear other bits mov word [_fpu_cw],ax fldcw [_fpu_cw] ; Load control word into FPU fdisi ; 8087-only instruction fstcw [_fpu_cw] ; Get Control Word test word [_fpu_cw],0x80 ; Did FDISI do anything? je check_fpu_inf ; FDISI did nothing, go check +INF/-INF jmp case_8087 ; FPU: 8087 ; Check infinity (-INF/+INF comparison test) check_fpu_inf: fld1 ; Push +1.0, this will be st0 fldz ; Push +0.0, this will be st1 fdiv ; (fdivp st1,st0) 1.0/0.0 = +INF, then pop, TOP=st0 fld st0 ; Push st0 value (+INF) again into stack (now st1) fchs ; Toggle sign to st0, making st0 -INF fcompp ; See if st0/st1 are the same, then pop both fstsw ax ; Get status word sahf ; Save AH into low FLAGS to see if infinites matched jz case_287 ; <= 80287: +inf == -inf jmp case_387 ; >= 80387: +inf != -inf case_8086: mov dx,str_i8086 call print jmp check_fpu case_286: mov dx,str_i286 call print jmp check_fpu case_386: mov dx,str_i386 call print jmp check_fpu case_486: mov dx,str_i486 call print jmp done case_8087: mov dx,str_fpu87 call print jmp done case_287: mov dx,str_fpu287 call print jmp done case_387: mov dx,str_fpu387 call print jmp done case_unknown: mov dx,str_unknown call print jmp done ; MS-DOS output string ; Params: DX = String pointer print: mov ah,9 int 21h ret ; Program finale done: mov dx,str_newln call print ; MS-DOS exit program exit: mov ah,4ch int 21h ; Sets zero flag if character a delimiter ; Params: AL = Character value test_delim: cmp al,32 ; Space jz delim cmp al,44 ; Comma jz delim cmp al,9 ; Hardware tab jz delim cmp al,59 ; Semi-colon jz delim cmp al,61 ; Equal sign jz delim cmp al,13 ; Carriage return, failsafe delim: ret ; Skip over leading delimiters ; Params: SI = Source Index skip_delim: ;; dec si cld ; ;cont: inc si ; mov al,ds:[si] cont: lodsb ; cmp al,0dh ; Don't skip if carriage return ; je end call test_delim jz cont dec si end: ret ; ; Data section ; section .data opt_version db '--version' opt_versionlen equ $-opt_version opt_ver db '--ver' opt_verlen equ $-opt_ver opt_help db '--help' opt_helplen equ $-opt_help opt_helpalt db '/?' opt_helpaltlen equ $-opt_helpalt ; DOS strings page_version db 'mcpuinfo v',VERSION,' (built: ',__DATE__,' ',__TIME__,')',NL,'$' page_ver db VERSION,NL,'$' page_help db 'Print pre-Pentium processor and co-processor type in use.',NL,\ 'Usage:',NL,\ ' MCPUINFO [OPTION]',NL,\ NL,\ 'OPTIONS',NL,\ ' --version Show version page and quit',NL,\ ' --ver Print version string and quit',NL,\ ' --help, /? Show this help page and quit',NL,'$' str_newln db NL,'$' ; \r\n str_i8086 db '8086$' str_i286 db '80286$' str_i386 db '80386$' str_i486 db '80486$' str_fpu87 db '+8087$' str_fpu287 db '+80287$' str_fpu387 db '+80387$' str_unknown db 'unknown$' ; ; "Stack" section ; section .bss _fpu_cw resw 1