// Cyrix 6x86 Configuration under Windows NT,  V 1.0 f. Visual C++ 2.0++
// c't 1/97, Matthias Witthopf, Andreas Stiller
// english Version 

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <winioctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <string.h>
#include "directnt.h"

const char *DirectNTDrvName = "\\\\.\\Dev_DirectNT";
ULONG dummy;

BOOL DrvExec(HANDLE Drv,ULONG OpCode,ULONG Par1,ULONG Par2,ULONG Par3,
             void *Result,ULONG ResultSize)
{
  TDirectNTInfo I = {OpCode,Par1,Par2,Par3};
  ULONG         ResultLen;
  if (DeviceIoControl(Drv,DWORD(IOCTL_DIRECTNT_CONTROL),&I,sizeof(I),
                      Result,ResultSize,&ResultLen,NULL))
    return TRUE;
  printf("Error in DirectNT.sys, Code = %d\n",GetLastError());
  return FALSE;
}

HANDLE Drv;

BYTE getreg(ULONG index)
{
  BYTE wert;
  DrvExec(Drv,OP_WritePortByte,0x22,index,0,&wert,sizeof(wert));
  DrvExec(Drv,OP_ReadPortByte,0x23,0,0,&wert,sizeof(wert));
  return wert;
}

void setreg(ULONG index, BYTE wert)
{
  DrvExec(Drv,OP_WritePortByte,0x22,index,0,&dummy,0);
  DrvExec(Drv,OP_WritePortByte,0x23,wert,0,&dummy,0);
}

ULONG SearchFrameBuffer ()
 {
  ULONG indword;
  for (ULONG i=0; i<0x20; i++)  // searches on  Bus 0, from Device 0 to Device 1Fh 
	{
	DrvExec(Drv,OP_ReadPCIDword,8,i,0,&indword,sizeof(indword));
	if  (indword >> 24 == 3) break; 
	if  (indword >> 16 == 1) break; 
	}
	if (i>=0x20) return 0; // not found 
	
	DrvExec(Drv,OP_ReadPCIDword,0x10,i,0,&indword,sizeof(indword));

	return indword & 0xFFFFFFFC;
	}

void setARRx (BYTE nr, ULONG adr, BYTE size, BYTE attr)
 {
  BYTE ARRx=0xC4+3*nr;
  BYTE RCRx=0xDC+nr; 
  setreg (ARRx,   (BYTE)(adr >> 24)); 
  setreg (ARRx+1, (BYTE)(adr >> 16 &0xFF));
  setreg (ARRx+2, (BYTE)(adr >> 8  &0xF0) | size);
  setreg (RCRx, attr);
 }   

ULONG getARRx (BYTE nr)
 {
  BYTE ARRx=0xC4+3*nr;
  return   ((ULONG)(getreg(ARRx))   << 24) 
          +((ULONG)(getreg(ARRx+1)) << 16) 
          +((ULONG)(getreg(ARRx+2)) <<  8);
 }           


void setframe(BYTE attribut)  // search framebuffer address in PCI configuration space
							  // search framebuffer in 6x86 ARR, if not found 
							  // search unused  6x86 Address Range Register 
   {  
	ULONG framebuffer=SearchFrameBuffer();
	if (!framebuffer) 
	 {
	  printf ("Framebuffer not found\n");
	  return;
	 }
	 printf ("Framebuffer found at %08Xh\n",framebuffer);
	
	BYTE CCR3= getreg (0xc3);
	setreg (0xc3,(CCR3 & 0xF) | 0x10); // enable Mapping Register
	// is framebuffer already in any ARRx ? 
	for (BYTE ARR=2; (ARR < 7) && ((getARRx(ARR) & 0xFFFFF000) != framebuffer);ARR++);
	if (ARR==7)	 // no, not found 
	  			 // then search unused ARR 
	for (BYTE ARR=2; (ARR < 7) && (getreg(0xC6+3*ARR) &0xF != 0);ARR++);   
	if (ARR==7) 
	   printf ("No Address Region Register free");
	else
	   {
	   setARRx (ARR,framebuffer,0xB,attribut); // Fixed Size   of 16 MByte 
	   printf ("New attribut %02Xh in Address Region Register %1X\n\n",attribut,ARR);
	   }
	
	setreg (0xc3,CCR3);                 // restore old value CCR3 
   }


BOOL IsCyrix(void)	 // Uses differences of unused flags after division
{					 // to distinguish between Cyrix and the rest 
  BYTE xah;
  _asm  {
    xor ax,ax
    sahf
    mov ax,5
    mov bx,2
    div bl
    lahf
    mov xah,ah
  }
  return (xah==2);
}

int __cdecl main(int argc,char *argv[])
{
  printf("Cyrix/IBM/ST 6x86 NT Configuration, ct 1/97 V1.0/Andreas Stiller \n");
  Drv = CreateFile(DirectNTDrvName,GENERIC_READ,FILE_SHARE_READ,
                   NULL,OPEN_EXISTING,0,NULL);
  if (Drv == INVALID_HANDLE_VALUE) {
    printf("Error: Can't open DirectNT.sys!\n");
    exit(1);
  }

  ULONG CR0;
  BOOL waittast=FALSE;
  if (!IsCyrix()) {
    printf ("Cyrix/IBM/ST-Prozessor not detected");
    exit (2);                   
  }
  BYTE DIR0=getreg(0xfe);       // CPU Model & CORE/Clock-Ratio
  BYTE DIR1=getreg(0xff);       // Mask Step, 16h => 2.6, 17h => 2.7 ..
  printf ("Cyrix/IBM/ST");
  switch (DIR0 >> 4) {
    case 0: printf(" 486SLC/DLC"); break;
    case 1: printf(" 486S/DX/DX2");break;
    case 2: printf(" 5x86");break;
    case 3: printf(" 6x86");break;
    case 4: printf(" M2");  break;
    case 8: printf(" TI DX4"); break;
    case 0xF:printf(" 486"); break;
    default: printf(" Type unknown");
  }
  printf(",CPU-ID=%02Xh, mask revision= %02Xh\n",DIR0,DIR1);

  BYTE CCR2=getreg(0xc2);                 // Read CCR2 
  BYTE CCR3=getreg(0xc3);				  // Read CCR3 
  BYTE CCR4=getreg(0xc4);				  // Read CCR4
  BYTE CCR5=getreg(0xc5);				  // Read CCR5

  if (!DrvExec(Drv,OP_GetCR0,0,0,0,&CR0,sizeof CR0))
    printf("Can't read CR0\n");
  
  printf("CCR2= %02Xh, CCR3= %02Xh, CCR4= %02Xh, CCR5= %02Xh, CR0=%08Xh\n\n",
          CCR2,CCR3,CCR4,CCR5,CR0);

  setreg(0xc2, CCR2 & ~4);                // unlock of NW-Bit in CR0
  
  for (int j=1; j<argc; j++) {            // all the parameters
    
    if (_stricmp(argv[j],"/WB")==0)
      if (!DrvExec(Drv,OP_SetCR0,CR0 & ~(1 << 29),0,0,&dummy,0))
        printf("Can't change CR0\n");
    
    if (_stricmp(argv[j],"/WT")==0)
      if (!DrvExec(Drv,OP_SetCR0,CR0 | (1 << 29),0,0,&dummy,0))
        printf("Can't change CR0\n");
    
    if (_stricmp(argv[j],"/SP")==0)       // Suspend on Halt
      CCR2 |=8;
    
    if (_stricmp(argv[j],"/NSP")==0)      // No Suspend on Halt
      CCR2 &=~8;
    
    
    if (_stricmp(argv[j],"/FF")==0)       // Fast Framebuffer
      setframe(0xb);                      
    
    if (_stricmp(argv[j],"/NFF")==0)      // Slow Framebuffer 
      setframe(0x1);   
      
    if (_stricmp(argv[j],"/ID")==0)       // CPUID on 
      CCR4 |=0x80;                      
    
    if (_stricmp(argv[j],"/NID")==0)      // CPUID off
      CCR4 &=~0x80;   

    if (_stricmp(argv[j],"/WA")==0)       // WT_alloc on 
      CCR5 |=0x1;                      
    
    if (_stricmp(argv[j],"/NWA")==0)      // WT_alloc off
      CCR5 &=~0x1;   

    if (_stricmp(argv[j],"/Wait")==0)     // Console waits for key 
      waittast=TRUE;
                     
  }
			  
  setreg(0xc2,CCR2);
  setreg(0xc3,CCR3);
  setreg(0xc4,CCR4);
  setreg(0xc5,CCR5);

  printf("Suspend on Halt : ");
  if (CCR2 & 8)
    printf("on (Powersaving  on)\n");
  else
    printf("off (Powersaving off)\n");
  if (!DrvExec(Drv,OP_GetCR0,0,0,0,&CR0,sizeof CR0))
    printf("Can't Read CR0\n");

  printf("WB Mode L1-Cache: ");
  if ((CR0 >> 29) & 1)
    printf("off\n");
  else
    printf("on\n");


  printf("CPUID           : ");
  if ((CCR4 >> 7) & 1)
    printf("on\n");
  else
    printf("off\n");

  printf("Write Allocation: ");
  if (CCR5  & 1)
    printf("fill on read/write miss\n");
  else
    printf("No fill on write miss\n");

  printf("Lin Burst       : ");
  if ((CCR3 >> 7) & 1)
    printf("on\n\n");
  else
    printf("off\n\n");


  if (argc==1)
    {
      printf("Parameter: /WB :L1-Cache Write Back,  /WT : L1-Cache Write Through \n");
      printf("           /SP :Suspend on HALT activ,/NSP: passive\n");
      printf("           /FF :Fast Framebuffer     ,/NFF: Slow\n");
	  printf("           /ID :CPUID on             ,/NID: CPUID off\n");
  	  printf("           /WA :Write Allocation on  ,/NWA: Write Allocation off\n"); 
      printf("           /wait :Console  waits  for Key \n");
   }
  if (!CloseHandle(Drv))
    printf("Error: Can't close DirectNT.sys!\n");
  if (waittast) {
    printf ("Go on with key");
    dummy= getch();
  }
  return 0;
}
