;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Kandamun CPUID v1.0
;;; WARNING: This code is designed by using KICK
;;; KICK = Keep It Complicated Kandamun
;;; Why make it difficult when it can be complicated?
;;;
include 'include\win32a.inc'

;;; Define Pascal Style String - 1 byte length in front
struc DPS string {
local ..end
      . db (..end - (.))
	db string
      ..end:
}
format PE GUI

;;section  '.code' code executable readable writeable

Main:
	mov edi, szNoCpuid

	;;; check if processor has CPUID
	pushfd
	pop  eax
	mov  edx, eax
	xor  eax, 0x200000
	push eax
	popfd
	pushfd
	pop eax
	cmp eax, edx
	jnz has_cpuid
MsgBox:
	push MB_OK
	push szTitle
	push edi
	push 0
	call [MessageBox]
	ret

stdFuncs db fn00 - $ - 1
	 db fn01 - $ - 1, 6, 0Dh, 'Step:', 7, ' Model:', 8, ' Family:', 0
	 db 0
extFuncs db fn00 - $ - 1
	 db fn80 - $ - 1
	 ;db fnnull - $ - 1
	 db fn4regs - $ - 1
	 db fn4regs - $ - 1
	 db fn4regs - $ - 1
	 db 0

fn4regs:
	mov [edi], ebp
	mov eax, ebp
	stosd
	xchg ecx, edx
fn00:
	mov [edi], ebx
	mov [edi + 4], edx
	mov [edi + 8], ecx
	add edi, 12
fnnull:
	ret
fn01.loop:
	rep movsb
	mov eax, ebp
	and eax, 0Fh
	push edx
	call EaxToDecimal
	pop edx
	shr ebp, 4
fn01:
	sub ecx, ecx
	lodsb
	mov cl,al
	test al, al
	jnz fn01.loop
	mov al, 0Dh
	stosb

	mov  eax, fn0Features
	call DecodeFeatureBitMask

	ret
fn80:
	mov al, 0Dh
	stosb
	shr  edx, 11
	mov  eax, extFeatures
	call DecodeFeatureBitMask
	ret


has_cpuid:
	push edi	    ; save edi
	push 0		    ; fn index - local variable
	mov esi, stdFuncs   ; -> table w/ function handlers
	sub eax, eax
outter_loop:
	cpuid		    ; get the number of cpuid functions
	mov  [bFnNum], al
next_fn:
	mov  ebp, eax	    ; EBP saves EAX
	sub  eax, eax
	lodsb		    ; get next fn handler
	test al, al	    ;
	jz   .exit_fn
	add  eax, esi	   ; lookup in function table
	call eax	   ; call fn handler
	inc  dword [esp]   ; inc fn index
	mov  eax, [esp]    ; exec next cpuid fn
	cpuid
	dec  byte [bFnNum] ; while counter >= 0
	jns  next_fn
.exit_fn:
	pop  eax
	test eax, eax
	js   .exit
	mov esi, extFuncs
	mov  al, 1	  ; one way to make EAX=80000000h
	ror  eax, 1	  ; last bit 1 for extend CPUID functions
	push eax	  ; fn index again as local variable
	jmp  outter_loop
.exit:
	pop  edi
	jmp  MsgBox

; Converts a number to a string using radix 10
;INPUT  : EAX - number, EDI - points where to store string
;OUTPUT : EDI = EDI + strlen
;DESTROY: EAX, EBX, EDX
EaxToDecimal:
	push 10
	pop  ebx
.recursion:
	xor  edx, edx
	div  ebx
	or   eax, eax
	push edx
	je   .skip
	call .recursion
.skip:
	pop  eax
	add  al, '0'
	stosb
	ret

;; INPUT: EDX - feature flags
DecodeFeatureBitMask:
	push esi
	xchg eax, esi
	sub  ecx, ecx
.loop:
	lodsb				 ; get length of string from table
	dec  al 			 ; actually it is length + 1
	mov  cl, al			 ; save string length
	jz   .skip			 ; 1 marks reserved bits
	js   .skip			 ; 0 marks end of table
	test dl, 1
	jz   .skip
	mov  [edi], byte '+'		 ; '+' >> output
	inc  edi
	rep movsb			 ; copy string from table to output
	mov  [edi], byte 0Dh		 ; endl >> output
	inc  edi
.skip:
	add  esi, ecx			 ; skip string row by moving after it length
	shr  edx, 1			 ; next bit
	inc  al 			 ; 0 marks end of table
	jnz .loop
	pop  esi
	ret

bFnNum	    db	 0
;;;;; TABLE  ;; LEN+1 ; STRING ;;;;;;;;;;;;;;;;;;;;;;
fn0Features DPS  'Floating-Point Unit'
     .bit1  DPS  'Virtual Mode Extensions'
     .bit2  DPS  'Debugging Extensions' 		     ;; AMD only
     .bit3  DPS  'Page Size Extensions (4-Mbyte pages)'
     .bit4  DPS  'Time Stamp Counter (w/ RDTSC and CR4 disable bit)'
     .bit5  DPS  'AMD K86 Model-Specific Registers (RDMSR/WRMSR)'
     .bit6  DPS  'PAE (Page Address Extensions)'
     .bit7  DPS  'Machine Check Exception'
     .bit8  DPS  'CMPXCHG8B Instruction'
     .bit9  DPS  'APIC'
     .bit10 db	 1   ; RESERVED
     .bit11 DPS  'SYSENTER/SYSEXIT'			     ;; AMD only
     .bit12 DPS  'MTRR (Memory Type Range Registers)'
     .bit13 DPS  'Global Paging Extension'
     .bit14 DPS  'Machine Check Architecture'
     .bit15 DPS  'Conditional Move Instruction'
     .bit16 DPS  'PAT (Page Attribute Table)'
     .bit17 DPS  'PSE-36 (Page Size Extensions)'
     .bit18 DPS  'PPN (Physical Processor Number)'	     ;; intel only
     .bit19 DPS  'CLFLUSH Instruction'			     ;; AMD only
	    db	 1,1,1 ; RESERVED
     .bit23 DPS  'MMX™ Instructions'
     .bit24 DPS  'FXSAVE/FXRSTOR'
     .bit25 DPS  'SSE Instructions'
     .bit26 DPS  'SSE2' 				     ;; AMD only
	    db	 1,1,1 ; RESERVED
     .bit30 DPS  'Itanium architecture' 		     ;; intel only
	    db	 0					     ;; END
extFeatures:
     .bit11 DPS  'SYSCALL/SYSRET'			     ;; AMD only
     .bit12 db	 1,1,1,1,1,1,1,1 ; RESERVED
     .bit20 DPS  'No-execute page (NX)' 		     ;; AMD only
	    db	 1	    ; RESERVED
     .bit22 DPS  'MMX™ Extensions'
     .bit23 db	 1,1
     .bit25 DPS 'Fast FXSAVE/FXRSTOR'
	    db	 1,1,1
     .bit29 DPS  'Long Mode Capable (LM)'
     .bit30 DPS  '3DNow!™ Instruction Extensions'	     ;; AMD only
     .bit31 DPS  '3DNow! Instructions'
	    db	 0

szTitle     db 'Kandamun CPUID v1.0',0

;;section '.idata' import data readable
data import
  library user,'USER32.DLL'

  import user, MessageBox,'MessageBoxA'
end data

szHasCpuid  db 'Has Cpuid', 0
szNoCpuid   db 'No Cpuid',  0
