www.pudn.com > PCIConf.rar > PCICFG.CPP


 
#include  
#include  
#include  
#include  
#include  
#include  
#include  
 
#define VERSION "1.19" 
 
#define lengthof(x) ((sizeof(x))/(sizeof(x[0]))) 
 
#ifdef __TURBOC__ 
# pragma option -a-  /* byte alignment */ 
#endif /* __TURBOC__ */ 
 
#ifndef FALSE 
# define FALSE (0) 
#endif /* FALSE */ 
 
#ifndef TRUE 
# define TRUE (!FALSE) 
#endif /* TRUE */ 
 
/************************************************************************/ 
/*	Manifest Constants						*/ 
/************************************************************************/ 
 
#define CAPLIST_BIT 0x0010		// does device have capabilities list? 
 
#define MAX_LINE     512		// max length of a line in .PCI files 
#define MAX_VENDOR_NAME 50		// max length of vendor's name 
#define MAX_DEVICE_NAME 50		// max length of device name 
#define MAX_VENDOR_DATA 16384		// maximum data per vendor 
 
#define SIGNATURE "PCICFG"		// PCICFG.DAT signature at start of file 
#define SIGNATURE_LENGTH (sizeof(SIGNATURE)-1) 
 
/************************************************************************/ 
/*	Types								*/ 
/************************************************************************/ 
 
typedef unsigned char  BYTE ; 
typedef unsigned short WORD ; 
typedef unsigned long DWORD ; 
 
//---------------------------------------------------------------------- 
 
struct PCIcfg 
   { 
   WORD	 vendorID ; 
   WORD	 deviceID ; 
   WORD	 command_reg ; 
   WORD	 status_reg ; 
   BYTE	 revisionID ; 
   BYTE	 progIF ; 
   BYTE	 subclass ; 
   BYTE	 classcode ; 
   BYTE	 cacheline_size ; 
   BYTE	 latency ; 
   BYTE	 header_type ; 
   BYTE	 BIST ; 
   union 
      { 
      struct 
	 { 
	 DWORD base_address0 ; 
	 DWORD base_address1 ; 
	 DWORD base_address2 ; 
	 DWORD base_address3 ; 
	 DWORD base_address4 ; 
	 DWORD base_address5 ; 
	 DWORD CardBus_CIS ; 
	 WORD  subsystem_vendorID ; 
	 WORD  subsystem_deviceID ; 
	 DWORD expansion_ROM ; 
	 BYTE  cap_ptr ; 
	 BYTE  reserved1[3] ; 
	 DWORD reserved2[1] ; 
	 BYTE  interrupt_line ; 
	 BYTE  interrupt_pin ; 
	 BYTE  min_grant ; 
	 BYTE  max_latency ; 
	 DWORD device_specific[48] ; 
	 } nonbridge ; 
      struct 
	 { 
	 DWORD base_address0 ; 
	 DWORD base_address1 ; 
	 BYTE  primary_bus ; 
	 BYTE  secondary_bus ; 
	 BYTE  subordinate_bus ; 
	 BYTE  secondary_latency ; 
	 BYTE  IO_base_low ; 
	 BYTE  IO_limit_low ; 
	 WORD  secondary_status ; 
	 WORD  memory_base_low ; 
	 WORD  memory_limit_low ; 
	 WORD  prefetch_base_low ; 
	 WORD  prefetch_limit_low ; 
	 DWORD prefetch_base_high ; 
	 DWORD prefetch_limit_high ; 
	 WORD  IO_base_high ; 
	 WORD  IO_limit_high ; 
	 DWORD reserved2[1] ; 
	 DWORD expansion_ROM ; 
	 BYTE  interrupt_line ; 
	 BYTE  interrupt_pin ; 
	 WORD  bridge_control ; 
	 DWORD device_specific[48] ; 
	 } bridge ; 
      struct 
	 { 
	 DWORD ExCa_base ; 
	 BYTE  cap_ptr ; 
	 BYTE  reserved05 ; 
	 WORD  secondary_status ; 
	 BYTE  PCI_bus ; 
	 BYTE  CardBus_bus ; 
	 BYTE  subordinate_bus ; 
	 BYTE  latency_timer ; 
	 DWORD memory_base0 ; 
	 DWORD memory_limit0 ; 
	 DWORD memory_base1 ; 
	 DWORD memory_limit1 ; 
	 WORD  IObase_0low ; 
	 WORD  IObase_0high ; 
	 WORD  IOlimit_0low ; 
	 WORD  IOlimit_0high ; 
	 WORD  IObase_1low ; 
	 WORD  IObase_1high ; 
	 WORD  IOlimit_1low ; 
	 WORD  IOlimit_1high ; 
	 BYTE  interrupt_line ; 
	 BYTE  interrupt_pin ; 
	 WORD  bridge_control ; 
	 WORD  subsystem_vendorID ; 
	 WORD  subsystem_deviceID ; 
	 DWORD legacy_baseaddr ; 
	 DWORD cardbus_reserved[14] ; 
	 DWORD vendor_specific[32] ; 
	 } cardbus ; 
      } ; 
   } ; 
 
struct subclass_info 
   { 
   int subclass_code ; 
   const char *subclass_name ; 
   } ; 
 
/************************************************************************/ 
/*	Global Data							*/ 
/************************************************************************/ 
 
static const char * const class_names[] = 
   { 
    "reserved",		// 00 
    "disk",		// 01 
    "network",		// 02 
    "display",		// 03 
    "multimedia",	// 04 
    "memory",		// 05 
    "bridge",		// 06 
    "communication",	// 07 
    "system peripheral",// 08 
    "input",		// 09 
    "docking station",	// 0A 
    "CPU",		// 0B 
    "serial bus",	// 0C 
   } ; 
 
static const subclass_info subclass_info_01[] = 
   { 
     { 0x00, "SCSI" }, 
     { 0x01, "IDE" }, 
     { 0x02, "floppy" }, 
     { 0x03, "IPI"}, 
     { 0x04, "RAID" }, 
     { 0x80, "other" }, 
     { -1, 0 }, 
   } ; 
 
static const subclass_info subclass_info_02[] = 
   { 
     { 0x00, "Ethernet" }, 
     { 0x01, "TokenRing" }, 
     { 0x02, "FDDI" }, 
     { 0x03, "ATM" }, 
     { 0x80, "other" }, 
     { -1, 0 }, 
   } ; 
 
static const subclass_info subclass_info_03[] = 
   { 
     { 0x00, "VGA" }, 
     { 0x01, "SuperVGA" }, 
     { 0x02, "XGA" }, 
     { 0x80, "other" }, 
     { -1, 0 }, 
   } ; 
 
static const subclass_info subclass_info_04[] = 
   { 
     { 0x00, "video" }, 
     { 0x01, "audio" }, 
     { 0x80, "other" }, 
     { -1, 0 }, 
   } ; 
 
static const subclass_info subclass_info_05[] = 
   { 
     { 0x00, "RAM" }, 
     { 0x01, "Flash memory" }, 
     { 0x80, "other" }, 
     { -1, 0 }, 
   } ; 
 
static const subclass_info subclass_info_06[] = 
   { 
     { 0x00, "CPU/PCI" }, 
     { 0x01, "PCI/ISA" }, 
     { 0x02, "PCI/EISA" }, 
     { 0x03, "PCI/MCA" }, 
     { 0x04, "PCI/PCI" }, 
     { 0x05, "PCI/PCMCIA" }, 
     { 0x06, "PCI/NuBus" }, 
     { 0x07, "PCI/CardBus" }, 
     { 0x80, "other" }, 
     { -1, 0 }, 
   } ; 
 
static const subclass_info subclass_info_07[] = 
   { 
     { 0x00, "serial" }, 
     { 0x01, "parallel" }, 
     { 0x80, "other" }, 
     { -1, 0 }, 
   } ; 
 
static const subclass_info subclass_info_08[] = 
   { 
     { 0x00, "PIC" }, 
     { 0x01, "DMAC" }, 
     { 0x02, "timer" }, 
     { 0x03, "RTC" }, 
     { 0x80, "other" }, 
     { -1, 0 }, 
   } ; 
 
static const subclass_info subclass_info_09[] = 
   { 
     { 0x00, "keyboard" }, 
     { 0x01, "digitizer" }, 
     { 0x02, "mouse" }, 
     { 0x80, "other" }, 
     { -1, 0 }, 
   } ; 
 
static const subclass_info subclass_info_0A[] = 
   { 
     { 0x00, "generic" }, 
     { 0x80, "other" }, 
     { -1, 0 }, 
   } ; 
 
static const subclass_info subclass_info_0B[] = 
   { 
     { 0x00, "386" }, 
     { 0x01, "486" }, 
     { 0x02, "Pentium" }, 
     { 0x03, "P6" }, 
     { 0x10, "Alpha" }, 
     { 0x40, "coproc" }, 
     { 0x80, "other" }, 
     { -1, 0 }, 
   } ; 
 
static const subclass_info subclass_info_0C[] = 
   { 
     { 0x00, "Firewire" }, 
     { 0x01, "ACCESS.bus" }, 
     { 0x02, "SSA" }, 
     { 0x03, "USB" }, 
     { 0x04, "Fiber Channel" }, 
     { 0x80, "other" }, 
     { -1, 0 }, 
   } ; 
 
static const subclass_info *subclass_data[] = 
   { 
   0, subclass_info_01, subclass_info_02, 
   subclass_info_03, subclass_info_04, subclass_info_05, 
   subclass_info_06, subclass_info_07, subclass_info_08, 
   subclass_info_09, subclass_info_0A, subclass_info_0B, 
   subclass_info_0C, 
   } ; 
 
//---------------------------------------------------------------------- 
 
static const char *const command_bits[] = 
   { 
     "I/O-on", 
     "mem-on", 
     "busmstr", 
     "spec-cyc", 
     "invalidate", 
     "VGAsnoop", 
     "parity-err", 
     "wait-cyc", 
     "sys-err",                         // bit 8 
     "fast-trns",			// bit 9 
     0,					// bit 10 
     0,					// bit 11 
     0,					// bit 12 
     0,					// bit 13 
     0,					// bit 14 
     0,					// bit 15 
   } ; 
 
static const char *const status_bits[] = 
   { 
     0,					// bit 0 
     0,					// bit 1 
     0,					// bit 2 
     0,					// bit 3 
     "CapList",                         // bit 4 
     "66Mhz",				// bit 5 
     "UDF",				// bit 6 
     "fast-trns",			// bit 7 
     "parity-err",			// bit 8 
     0,					// bits 9-10 are select timing 
     0, 
     "sig-abort",			// bit 11 
     "rcv-abort",			// bit 12 
     "mst-abort",			// bit 13 
     "sig-serr",			// bit 14 
     "det-parity",			// bit 15 
   } ; 
 
static const char *const bctrl_bits[] = 
   { 
     "parity-resp",			// bit 0 
     0,					// bit 1 
     "ISA",				// bit 2 
     "VGA",				// bit 3 
     0,					// bit 4 
     "mst-abort",			// bit 5 
     "bus-reset",			// bit 6 
     "fast-b2b",			// bit 7 
   } ; 
 
static const char *const select_timing[] = 
   { 
    "fast", 
    "med", 
    "slow", 
    "???" 
   } ; 
 
static const char *const PMC_bits[] = 
   { 
     0, 				// bits 0-2: version (001) 
     0, 
     0, 
     "PME-Clk",				// bit 3: PM Event Clock 
     "aux-pwr-PME#",			// bit 4: need aux power for PME# 
     "DevSpec Init",                    // bit 5 
     0, 				// bits 6-7: DynClk 
     0, 
     "FullClk", 
     "D1-supp", 
     "D2-supp", 
     "PME#-D0",                         // bit 11 
     "PME#-D1",                         // bit 12 
     "PME#-D2",                         // bit 13 
     "PME#-D3hot",                      // bit 14 
     "PME#-D3cold",                     // bit 15 
   } ; 
 
/************************************************************************/ 
/*    global variables							*/ 
/************************************************************************/ 
 
static int verbose = FALSE ; 
static int terse = FALSE ; 
static int first_device = TRUE ; 
static int bypass_BIOS = FALSE ; 
static int cfg_mech = 1 ;		// PCI access mechanism: 1 or 2 
 
static char *exe_directory = "." ; 
 
static char *device_ID_data = 0 ; 
/* format of ID data once loaded: 
          char*  -> next vendor or 0 
	  WORD   vendor ID 
	  ASCIZ	 vendor name 
	  WORD   device ID 
	  ASCIZ	 device name 
	  ... 
	  char*	 -> next vendor or 0 
	  WORD   vendor ID 
	  ASCIZ	 vendor name 
	  ..... 
*/ 
 
/************************************************************************/ 
/*	Helper Functions						*/ 
/************************************************************************/ 
 
static void get_exe_directory(const char *argv0) 
{ 
   char *pathname = (char*)malloc(strlen(argv0)+2) ; 
   strcpy(pathname,argv0) ; 
   // strip off any existing extension 
   char *slash = strrchr(pathname,'/') ; 
   char *backslash = strrchr(pathname,'\\') ; 
   if (backslash && (!slash || backslash > slash)) 
      slash = backslash ; 
   if (slash) 
      *slash = '\0' ; 
   else 
      strcpy(pathname,".") ; 
   exe_directory = pathname ; 
} 
 
//---------------------------------------------------------------------- 
 
static const char *skip_whitespace(const char *line) 
{ 
   while (*line && isspace(*line)) 
      line++ ; 
   return line ; 
} 
 
//---------------------------------------------------------------------- 
 
inline char *skip_whitespace(char *line) 
{ 
   return (char*)skip_whitespace((const char *)line) ; 
} 
 
//---------------------------------------------------------------------- 
 
static int is_comment_line(const char *line,int pcicfg_format = 0) 
{ 
   // if in Craig Hart's format, we want to skip the subsystem-vendor-ID 
   //   lines (those starting with two tab characters) because PCICFG doesn't 
   //   support them 
   if (!pcicfg_format && line[0] == '\t' && line[1] == '\t') 
      return TRUE ; 
   line = skip_whitespace(line) ; 
   // blank lines and lines whose first nonwhite character is a semicolon 
   // can be skipped as comments 
   if (*line == '\0' || *line == '\n' || *line == ';') 
      return TRUE ; 
   else 
      return FALSE ; 
} 
 
//---------------------------------------------------------------------- 
 
static int read_nonblank_line(char *buf, int size, FILE *fp, int pcicfg_format) 
{ 
   do { 
      buf[0] = '\0' ; 
      if (!fgets(buf,size,fp) || feof(fp)) 
	 return FALSE ; 
      } while (is_comment_line(buf,pcicfg_format)) ; 
   return TRUE ; 
} 
 
//---------------------------------------------------------------------- 
 
static WORD hextoint(const char *&digits) 
{ 
   WORD hex = 0 ; 
   while (isxdigit(*digits)) 
      { 
      int digit = toupper(*digits++) - '0' ; 
      if (digit > 9) 
	 digit -= ('A'-'0'-10) ; 
      hex = 16*hex + digit ; 
      } 
   return hex ; 
} 
 
//---------------------------------------------------------------------- 
 
static FILE *open_PCICFG_DAT(const char *fopen_mode) 
{ 
   int dir_len = strlen(exe_directory) ; 
   char *datafile = (char*)malloc(dir_len+15) ; 
   if (!datafile) 
      { 
      fprintf(stderr,"Insufficient memory for PCICFG.DAT pathname\n") ; 
      return FALSE ; 
      } 
   sprintf(datafile,"%s/PCICFG.DAT",exe_directory) ; 
   FILE *fp = fopen(datafile,fopen_mode) ; 
   free(datafile) ; 
   if (!fp) 
      fprintf(stderr,"Unable to open PCICFG.DAT in mode \"%s\"\n",fopen_mode) ; 
   return fp ; 
} 
 
//---------------------------------------------------------------------- 
 
static int backup_PCICFG_DAT() 
{ 
   int dir_len = strlen(exe_directory) ; 
   char *datafile = (char*)malloc(dir_len+15) ; 
   if (!datafile) 
      { 
      fprintf(stderr,"Insufficient memory for PCICFG.DAT pathname\n") ; 
      return FALSE ; 
      } 
   sprintf(datafile,"%s/PCICFG.DAT",exe_directory) ; 
   FILE *fp = fopen(datafile,"r") ; 
   if (!fp) 
      { 
      free(datafile) ; 
      return FALSE ; 
      } 
   strcpy(datafile+strlen(datafile)-4,".BAK") ; 
   FILE *backup = fopen(datafile,"w") ; 
   if (!backup) 
      { 
      free(datafile) ; 
      return FALSE ; 
      } 
   char buffer[BUFSIZ] ; 
   int count ; 
   int success = TRUE ; 
   while ((count = fread(buffer,sizeof(char),sizeof(buffer),fp)) > 0) 
      { 
      if (fwrite(buffer,sizeof(char),count,backup) < count) 
	 success = FALSE ; 
      } 
   fclose(fp) ; 
   fclose(backup) ; 
   if (!success) 
      { 
      fprintf(stderr,"Backup of PCICFG.DAT failed!\n") ; 
      unlink(datafile) ; 
      } 
   free(datafile) ; 
   return success ; 
} 
 
/************************************************************************/ 
/************************************************************************/ 
 
#if defined(__WATCOMC__) && defined(__386__) 
extern DWORD inpd(int portnum) ; 
#pragma aux inpd = \ 
   "in eax,dx" \ 
   parm [edx] \ 
   value [eax] \ 
   modify exact [eax] ; 
 
#else 
static DWORD inpd(int portnum) 
{ 
   static DWORD value ; 
   asm mov dx,portnum ; 
   asm lea bx,value ; 
#if defined(__BORLANDC__) 
   __emit__(0x66,0x50,			// push EAX 
	    0x66,0xED,			// in EAX,DX 
	    0x66,0x89,0x07,		// mov [BX],EAX 
	    0x66,0x58) ;		// pop EAX 
#else 
   asm push eax 
   asm in eax,dx ; 
   asm mov [bx],eax ; 
   asm pop eax 
#endif 
   return value ; 
} 
#endif /* __WATCOMC__ */ 
 
//---------------------------------------------------------------------- 
 
#if defined(__WATCOMC__) && defined(__386__) 
extern void outpd(int portnum, DWORD value) ; 
#pragma aux outpd = \ 
   "out dx,eax" \ 
   parm [edx][eax] \ 
   modify exact [] ; 
    
#else 
static void outpd(int portnum, DWORD val) 
{ 
   static DWORD value = 0 ; 
 
   value = val ; 
   asm mov dx,portnum ; 
   asm lea bx,value ; 
#if defined(__BORLANDC__) 
   __emit__(0x66,0x50,			// push EAX 
	    0x66,0x8B,0x07,		// mov EAX,[BX] 
	    0x66,0xEF,			// out DX,EAX 
	    0x66,0x58) ;		// pop EAX 
#else 
   asm push eax 
   asm mov eax,[bx] ; 
   asm out dx,eax ; 
   asm pop eax 
#endif 
   return ; 
} 
#endif /* __WATCOMC__ */ 
 
//---------------------------------------------------------------------- 
 
#if defined(__WATCOMC__) 
extern void outp(short portnumber, BYTE value) ; 
#pragma aux outp = \ 
   "out dx,al" \ 
   parm [dx][al] \ 
   modify exact [] ; 
 
extern BYTE inp(short portnumber) ; 
#pragma aux inp = \ 
   "in al,dx" \ 
  parm [dx] \ 
  value [al] \ 
  modify exact [al] ; 
#endif /* __WATCOMC__ */ 
 
 
/************************************************************************/ 
/************************************************************************/ 
 
static int check_PCI_BIOS() 
{ 
   union REGS regs ; 
#if defined(__WATCOMC__) && defined(__386__) 
   regs.w.ax = 0xB101 ; 
   int386(0x1A,®s,®s) ; 
#else 
   regs.x.ax = 0xB101 ; 
   int86(0x1A,®s,®s) ; 
#endif /* __WATCOMC__ && __386__ */ 
   if (regs.h.ah == 0x00) 
      return regs.h.cl ;		// last PCI bus in system 
   else 
      return -1 ;			// no PCI BIOS detected 
} 
 
//---------------------------------------------------------------------- 
 
static void determine_cfg_mech() 
{ 
   union REGS regs ; 
#if defined(__WATCOMC__) && defined(__386__) 
   regs.w.ax = 0xB101 ; 
   int386(0x1A,®s,®s) ; 
#else 
   regs.x.ax = 0xB101 ; 
   int86(0x1A,®s,®s) ; 
#endif /* __WATCOMC__ && __386__ */ 
   if (regs.h.ah == 0x00) 
      { 
      // we have a PCI BIOS, so check which configuration mechanism(s) it 
      // supports 
      int mechs = regs.h.al & 0x03 ; 
      if (mechs & 0x01) 
	 cfg_mech = 1 ; 
      else if (mechs & 0x02) 
	 cfg_mech = 2 ; 
      else 
	 cfg_mech = 1 ;			// default is to assume mechanism #1 
      } 
   return ; 
} 
 
//---------------------------------------------------------------------- 
 
static int read_DWORD_register(int bus, int device, int func, int reg, 
			       WORD *lo, WORD *hi) 
{ 
   if (bypass_BIOS) 
      { 
      DWORD value ; 
      if (cfg_mech == 1) 
	 { 
	 DWORD addr = 0x80000000L | (((DWORD)(bus & 0xFF)) << 16) | 
		      ((((unsigned)device) & 0x1F) << 11) | 
		      ((((unsigned)func) & 0x07) << 8) | (reg & 0xFC) ; 
	 DWORD orig = inp(0xCF8) ;	// get current state 
	 outpd(0xCF8,addr) ;		// set up addressing to config data 
	 value = inpd(0xCFC) ;		// get requested DWORD of config data 
	 outpd(0xCF8,orig) ;		// restore configuration control 
	 } 
      else // cfg_mech == 2 
	 { 
	 if (device > 0x0F)		// mech#2 only supports 16 devices 
	    {				//   per bus 
	    *lo = 0xFFFF ; 
	    *hi = 0xFFFF ; 
	    return FALSE ; 
	    } 
	 BYTE oldenable = inp(0xCF8) ;	// store current state of config space 
	 BYTE oldbus = inp(0xCFA) ; 
	 outp(0xCFA,bus) ; 
	 outp(0xCF8,0x80) ;		// enable configuration space 
	 WORD addr = 0xC000 | ((device & 0x0F) << 8) | (reg & 0xFF) ; 
	 value = inpd(addr) ; 
	 outp(0xCFA,oldbus) ;		// restore configuration space 
	 outp(0xCF8,oldenable) ; 
	 } 
      *hi = (WORD)(value >> 16) ; 
      *lo = (WORD)(value & 0xFFFF) ; 
      return TRUE ; 
      } 
   else // use BIOS 
      { 
      union REGS regs, outregs ; 
      regs.h.bh = bus ; 
      regs.h.bl = (device<<3) | (func & 0x07) ; 
#if defined(__WATCOMC__) && defined(__386__) 
      regs.w.ax = 0xB109 ; 
      regs.w.di = reg ; 
      int386(0x1A,®s,&outregs) ; 
      if (outregs.x.cflag != 0) 
	 return FALSE ; 
      *lo = outregs.w.cx ; 
      regs.w.di += 2 ; 
      int386(0x1A,®s,&outregs) ; 
      if (outregs.x.cflag != 0) 
	 return FALSE ; 
      *hi = outregs.w.cx ; 
#else 
      regs.x.ax = 0xB109 ; 
      regs.x.di = reg ; 
      int86(0x1A,®s,&outregs) ; 
      if (outregs.x.cflag != 0) 
	 return FALSE ; 
      *lo = outregs.x.cx ; 
      regs.x.di += 2 ; 
      int86(0x1A,®s,&outregs) ; 
 
      if (outregs.x.cflag != 0) 
	 return FALSE ; 
      *hi = outregs.x.cx ; 
#endif /* __WATCOMC__ && __386__ */ 
      } 
   return TRUE ; 
} 
 
//---------------------------------------------------------------------- 
 
static void write_DWORD_register(int bus,int device,int func, 
				 int reg, WORD lo, WORD hi) 
{ 
   if (bypass_BIOS) 
      { 
      DWORD value = ((DWORD)hi << 16) | lo ; 
      if (cfg_mech == 1) 
	 { 
	 DWORD addr = 0x80000000L | ((DWORD)(bus & 0xFF) << 16) | 
		      ((device & 0x1F) << 11) | ((func & 0x07) << 8) | 
		      (reg & 0xFC) ; 
	 DWORD orig = inpd(0xCF8) ; 
	 outpd(0xCF8,addr) ; 
	 outpd(0xCFC,value) ; 
	 outpd(0xCF8,orig) ; 
	 } 
      else 
	 { 
	 if (device > 0x0F) 
	    return ; 
	 BYTE oldenable = inp(0xCF8) ; 
	 BYTE oldbus = inp(0xCFA) ; 
	 outp(0xCFA,bus) ; 
	 outp(0xCF8,0x80) ;		// enable configuration space 
	 WORD addr = 0xC000 | ((device & 0x0F) << 8) | reg ; 
	 outpd(addr,value) ; 
	 outp(0xCFA,oldbus) ; 
	 outp(0xCF8,oldenable) ; 
	 } 
      } 
   else 
      { 
      union REGS regs, outregs ; 
 
      regs.h.bh = bus ; 
      regs.h.bl = (device<<3) | (func & 0x07) ; 
#if defined(__WATCOMC__) && defined(__386__) 
      regs.w.ax = 0xB10D ;		// write configuration DWORD 
      regs.w.di = reg ; 
      regs.x.ecx = ((long)hi) << 16 | ((unsigned)lo) ; 
      int386(0x1A,®s,&outregs) ; 
#else 
      regs.x.ax = 0xB10C ;		// write configuration word 
      regs.x.di = reg ; 
      regs.x.cx = lo ; 
      int86(0x1A,®s,&outregs) ; 
      regs.x.di += 2 ; 
      regs.x.cx = hi ; 
      int86(0x1A,®s,&outregs) ; 
#endif /* __WATCOMC__ && __386__ */ 
      } 
   return ; 
} 
 
//---------------------------------------------------------------------- 
 
static PCIcfg *read_PCI_config(int bus, int device, int func) 
{ 
   static PCIcfg cfg ; 
 
   for (int i = 0 ; i < sizeof(cfg)/sizeof(DWORD) ; i++) 
      { 
      WORD hi, lo ; 
      if (!read_DWORD_register(bus,device,func,i*sizeof(DWORD),&lo,&hi)) 
	 return 0 ; 
      ((WORD*)&cfg)[2*i] = lo ; 
      ((WORD*)&cfg)[2*i+1] = hi ; 
      } 
   return &cfg ; 
} 
 
//---------------------------------------------------------------------- 
 
static const char *get_subclass_name(int classcode, int subclass) 
{ 
   if (classcode < 0 || classcode >= lengthof(subclass_data) || 
       subclass_data[classcode] == 0) 
      return "???" ; 
   const subclass_info *subinfo = subclass_data[classcode] ; 
   while (subinfo->subclass_code != -1) 
      { 
      if (subinfo->subclass_code == subclass) 
	 return subinfo->subclass_name ; 
      subinfo++ ; 
      } 
   return "???" ; 
} 
 
//---------------------------------------------------------------------- 
 
static const char *get_vendor_name(WORD vendorID) 
{ 
   if (vendorID == 0x0000 || vendorID == 0xFFFF) 
      return "Not Present" ; 
   char *next ; 
   for (char *data = device_ID_data ; data ; data = next) 
      { 
      next = *((char**)data) ; 
      data += sizeof(char*) ; 
      data += sizeof(WORD) ;		// skip the length field 
      WORD ID = *((WORD*)data) ; 
      data += sizeof(WORD) ; 
      if (ID == vendorID) 
	 return data ; 
      } 
   // if we get here, there was no matching ID in the file, 
   return "???" ; 
} 
 
//---------------------------------------------------------------------- 
 
static const char *get_device_name(WORD vendorID, WORD deviceID) 
{ 
   if (vendorID == 0x0000 || vendorID == 0xFFFF || deviceID == 0xFFFF) 
      return "Not Present" ; 
   char *data = device_ID_data ; 
   while (data) 
      { 
      char *next = *((char**)data) ; 
      data += sizeof(char*) ; 
      WORD length = *((WORD*)data) ; 
      data += sizeof(WORD) ; 
      char *end = data + length ; 
      WORD ID = *((WORD*)data) ; 
      data += sizeof(WORD) ; 
      if (ID == vendorID) 
	 { 
	 // OK, we've found the vendor, now scan for the device 
	 // 1. skip the vendor name 
	 while (*data) 
	    data++ ; 
	 if (data < end)		// skip the NUL 
	    data++ ; 
	 // 2. check each device ID in turn 
	 while (data < end) 
	    { 
	    ID = *((WORD*)data) ; 
	    data += sizeof(WORD) ; 
	    if (ID == deviceID) 
	       return data ; 
	    while (*data) 
	       data++ ; 
	    data++ ;			// skip the NUL 
	    } 
	 // if we get here, there was no match for the device ID 
	 break ; 
	 } 
      data = next ; 
      } 
   // if we get here, there was no matching ID in the file, 
   return "???" ; 
} 
 
//---------------------------------------------------------------------- 
 
static int load_device_info(FILE *fp, char * &format_string, 
			    char const * &enum_list) 
{ 
   if (!fp) 
      return FALSE ; 
   long int filesize = lseek(fileno(fp),0L,SEEK_END) ; 
   fseek(fp,0L,SEEK_SET) ;		// back to beginning of file 
   char line[MAX_LINE] ; 
   // read until we find the actual beginning of the device definition 
   do { 
      line[0] = '\0' ;			// catch EOF or read error 
      fgets(line,sizeof(line),fp) ; 
      } while (!feof(fp) && strncmp(line,"!begin",6) != 0) ; 
   int datasize = (int)(filesize - ftell(fp)) ; 
   char *buffer = (char*)malloc(datasize+3) ; 
   int readsize ; 
   char *newline ; 
   if (buffer && (readsize = fread(buffer,sizeof(char),datasize,fp)) > 0) 
      { 
      buffer[readsize] = '\0' ;		// ensure proper string termination 
      format_string = buffer ; 
      enum_list = format_string ; 
      if (*enum_list == '\n') 
	 enum_list++ ; 
      // look forward to end of format string, then chop into two strings 
      do { 
	 newline = strchr(enum_list,'\n') ; 
	 if (newline && strncmp(newline+1,"!end",4) == 0) 
	    { 
	    newline[1] = '\0' ; 
	    newline = strchr(newline+2,'\n') ; 
	    if (newline) 
	       enum_list = newline ; 
	    else 
	       enum_list = newline+2 ; 
	    break ; 
	    } 
	 if (newline) 
	    enum_list = newline+1 ; 
	 } while (newline) ; 
      } 
   else 
      return FALSE ; 
   // scan until we find the actual beginning of the 'enum' definition 
   char *result = (char*)enum_list ; 
   newline = (char*)enum_list ; 
   do { 
      newline = strchr(newline,'\n') ; 
      if (newline) 
	 { 
	 newline++ ; 
	 if (strncmp(newline,"!enum",5) == 0) 
	    break ; 
	 } 
      } while (newline) ; 
   if (newline) 
      { 
      newline++ ; 
      // format of the enum: 
      //    !enum enum_name 
      //     enumvalue0 
      //     enumvalue1 
      //     ... 
      //     enumvalueN 
      //    !end 
      do { 
	 // extract the enum's name 
	 //assert(strcmp(newline,"!enum",5) == 0) ; 
	 newline = skip_whitespace(newline+5) ; 
	 const char *end = strchr(newline,'\n') ; 
	 if (!end) 
	    break ; 
	 memcpy(result,newline,end-newline+1) ; 
	 result += (end-newline+1) ; 
	 newline = (char*)end+1 ; 
	 while (*newline && strncmp(newline,"!end",4) != 0) 
	    { 
	    newline = skip_whitespace(newline) ; 
	    char *end = strchr(newline,'\n') ; 
	    if (!end) 
	       end = strchr(newline,'\0') ; 
	    char *next = *end ? end+1 : end ; 
	    while (end > newline && isspace(end[-1])) 
	       end-- ; 
	    memcpy(result,newline,end-newline) ; 
	    result[end-newline] = '\n' ; 
	    result += (end-newline+1) ; 
	    newline = next ; 
	    } 
	 *result++ = '\0' ; 
	 // one enum is done, so scan for the next (if any) 
	 while ((newline = strchr(newline,'\n')) != 0) 
	    { 
	    newline++ ; 
	    if (strncmp(newline,"!enum",5) == 0) 
	       break ; 
	    } 
	 } while (newline && *newline) ; 
      } 
   *result++ = '\0' ; 
   return TRUE ; 
} 
 
//---------------------------------------------------------------------- 
 
static int know_device(WORD vendor, WORD device, 
		       char * &format_string, char const * &enum_list) 
{ 
   if (vendor == 0x0000 || vendor == 0xFFFF || device == 0xFFFF) 
      return FALSE ; 
   // see if there's a data file for this device 
   int dir_len = strlen(exe_directory) ; 
   char *device_file = (char*)malloc(dir_len+14) ; 
   if (device_file) 
      { 
      sprintf(device_file,"%s/%4.04X%4.04X.PCI",exe_directory,vendor,device) ; 
      device_file[dir_len+13] = '\0' ; 
      FILE *fp = fopen(device_file,"r") ; 
      free(device_file) ; 
      if (fp) 
	 { 
	 int success = load_device_info(fp,format_string,enum_list) ; 
	 fclose(fp) ; 
	 return success ; 
	 } 
      } 
   return FALSE ; 
} 
 
//---------------------------------------------------------------------- 
 
static void write_bits(WORD bitflags, const char *const *flagnames, int numbits) 
{ 
   for (int i = 0 ; i < numbits ; i++) 
      { 
      if ((bitflags & (1 << i)) != 0 && flagnames[i]) 
	 printf(" %s",flagnames[i]) ; 
      } 
} 
 
#define WRITE_CMD_BITS(x) write_bits((x),command_bits,lengthof(command_bits)) 
#define WRITE_STAT_BITS(x) write_bits((x),status_bits,lengthof(status_bits)) 
#define WRITE_BCTRL_BITS(x) write_bits((x),bctrl_bits,lengthof(bctrl_bits)) 
 
//---------------------------------------------------------------------- 
 
static DWORD extract_field(const char *&s, const char *cfg) 
{ 
   DWORD value = 0 ; 
   int addr = 0 ; 
   while (*s == '[' || *s == '|') 
      { 
      s++ ; 
      int lowbit = 0 ; 
      int highbit = 7 ; 
      while (*s && isxdigit(*s)) 
	 { 
	 int digit = *s - '0' ; 
	 if (digit > 9) 
	    digit -= ('A'-10-'0') ; 
	 addr = 16*addr + digit ; 
	 s++ ; 
	 } 
      addr &= 0x00FF ; 
      if (*s == ':') 
	 { 
	 s++ ; 
	 while (*s && isdigit(*s)) 
	    { 
	    lowbit = 10*lowbit + (*s-'0') ; 
	    s++ ; 
	    } 
	 if (*s == '-') 
	    { 
	    highbit = 0 ; 
	    s++ ; 
	    while (*s && isdigit(*s)) 
	       { 
	       highbit = 10*highbit + (*s-'0') ; 
	       s++ ; 
	       } 
	    } 
	 else 
	    highbit = lowbit ; 
	 if (highbit < lowbit) 
	    { 
	    int tmp = lowbit ; 
	    lowbit = highbit ; 
	    highbit = tmp ; 
	    } 
	 if (highbit > 31) 
	    { 
	    if (highbit - lowbit >= 32) 
	       highbit = 31 ; 
	    } 
	 if (lowbit/8) 
	    { 
	    int adj = lowbit/8 ; 
	    addr += adj ; 
	    adj *= 8 ; 
	    lowbit -= adj ; 
	    highbit -= adj ; 
	    } 
	 } 
      DWORD prev_value = value << (highbit-lowbit+1) ; 
      if (highbit > 16) 
	 { 
	 value = cfg[addr] + (cfg[addr+1] << 8) + ((DWORD)cfg[addr+2] << 16) + 
		 ((DWORD)cfg[addr+3] << 24) ; 
	 } 
      else if (highbit > 8) 
	 value = cfg[addr] + (cfg[addr+1] << 8) ; 
      else 
	 value = cfg[addr] ; 
      DWORD mask = 0 ; 
      for (int i = lowbit ; i <= highbit ; i++) 
	 mask |= (1L << i-lowbit) ; 
      while (lowbit-- > 0) 
         value >>= 1 ; 
      value &= mask ; 
      if (*s == '<') 
	 { 
	 s++ ; 
	 int shift = 0 ; 
	 while (*s && isdigit(*s)) 
	    { 
	    shift = 10*shift + (*s-'0') ; 
	    s++ ; 
	    } 
	 while (shift-- > 0) 
	    { 
	    value <<= 1 ; 
	    prev_value <<= 1 ; 
	    } 
	 } 
      if (*s == '*') 
	 { 
	 s++ ; 
	 unsigned mult = 0 ; 
	 while (*s && isdigit(*s)) 
	    { 
	    mult = 10*mult + (*s-'0') ; 
	    s++ ; 
	    } 
	 if (mult == 0) 
	    mult =1 ; 
	 value *= mult ; 
	 prev_value *= mult ; 
	 } 
      value |= prev_value ; 
      if (*s == '+') 
	 { 
	 *s++ ; 
	 int negative = 0 ; 
	 if (*s == '-') 
	    negative = 1 ; 
	 long offset = 0 ; 
	 while (*s && isdigit(*s)) 
	    { 
	    offset = 10*offset + (*s-'0') ; 
	    s++ ; 
	    } 
	 if (negative) 
	    offset = -offset ; 
	 value += offset ; 
	 } 
      if (*s == ']') 
	 { 
	 s++ ; 
	 break ; 
	 } 
      } 
   return value ; 
} 
 
//---------------------------------------------------------------------- 
 
static void format_number(FILE *out, DWORD val, int width, int base) 
{ 
   char buf[38] ;			// enough for DWORD, plus fudge factor 
   static char digits[] = "0123456789ABCDEF" ; 
   int count = 0 ; 
   do { 
      int digit = (int)(val % base) ; 
      val /= base ; 
      buf[count++] = digits[digit] ; 
      } while (val) ; 
   buf[count] = '\0' ; 
   for (int i = 0 ; i < count/2 ; i++) 
      { 
      char tmp = buf[count-i-1] ; 
      buf[count-i-1] = buf[i] ; 
      buf[i] = tmp ; 
      } 
   if (count < width) 
      { 
      for (int i = count ; i < width ; i++) 
	 fputc(base == 10 ? ' ' : '0',out) ; 
      } 
   fputs(buf,out) ; 
   return ; 
} 
 
//---------------------------------------------------------------------- 
 
static void format_enabled(FILE *out, DWORD value, int width) 
{ 
   int w = value ? 6 : 7 ; 
   for (int i = w ; i < width ; i++) 
      fputc(' ',out) ; 
   fputs(value ? "enable" : "disable",out) ; 
} 
 
//---------------------------------------------------------------------- 
 
static void format_flag(FILE *out, DWORD value, int width) 
{ 
   for (int i = 1 ; i < width ; i++) 
      fputc(' ',out) ; 
   fputc(value ? 'û' : '-',out) ; 
} 
 
 
//---------------------------------------------------------------------- 
 
static void format_yesno(FILE *out, DWORD value, int width) 
{ 
   for (int i = 1 ; i < width ; i++) 
      fputc(' ',out) ; 
   fputc(value ? 'Y' : 'N',out) ; 
} 
 
//---------------------------------------------------------------------- 
 
static void format_full_yesno(FILE *out, DWORD value, int width) 
{ 
   if (width < 3) 
      width = 3 ; 
   for (int i = (value ? 3 : 2) ; i < width ; i++) 
      fputc(' ',out) ; 
   fputs(value ? "Yes" : "No",out) ; 
} 
 
//---------------------------------------------------------------------- 
 
static void format_charlist(FILE *out, DWORD value, int width, 
			    const char *&chars) 
{ 
   for (int i = 1 ; i < width ; i++) 
      fputc(' ',out) ; 
   int n = 0 ; 
   chars++ ;				// skip opening brace 
   const char *ch = chars ; 
   while (*chars && *chars != '}') 
      { 
      n++ ; 
      chars++ ; 
      } 
   if (value >= n) 
      value = n-1 ; 
   fputc(ch[(size_t)value],out) ; 
} 
 
//---------------------------------------------------------------------- 
 
static const char *find_enum(const char *name, const char *name_end, 
			     const char *enums) 
{ 
   const char *e = 0 ; 
   if (name && enums) 
      { 
      size_t len = name_end - name ; 
      const char *enum_list = enums ; 
      while (*enums) 
	 { 
	 if (strncmp(name,enums,len) == 0 && enums[len] == '\n') 
	    return enums ; 
	 enums = strchr(enums,'\0') + 1 ; 
	 } 
      // OK, first pass didn't find any exact match, so try to find a prefix 
      enums = enum_list ; 
      while (*enums) 
	 { 
	 if (strncmp(name,enums,len) == 0) 
	    return enums ; 
	 enums = strchr(enums,'\0') + 1 ; 
	 } 
      } 
   return e ; 
} 
 
//---------------------------------------------------------------------- 
 
static const char *format_enum(FILE *out, DWORD value, int width, 
			       const char *name, const char *enums) 
{ 
   if (!out || !enums) 
      return name ; 
   //asssert(*name == '(') ; 
   name++ ;				// skip the open paren 
   const char *end = name ; 
   while (*end && *end != ')') 
      end++ ; 
   const char *e = find_enum(name,end,enums) ; 
   if (e) 
      { 
      // format of string pointed at by 'e': 
      //      name \n value0 \n value1 \n value2 \n ... \n valueN \0 
      e = strchr(e,'\n') ; 
      if (e) e++ ; 
//      size_t orig_value = value ; 
      while (value > 0) 
	 { 
	 const char *next = strchr(e,'\n') ; 
	 if (!next) 
#if 1 
	    break ; 
#else 
	    { 
	    fflush(stdout) ; 
	    fprintf(stderr,"undefined value %d given for enum '%.4s'\n", 
		    orig_value,name) ; 
	    return end ; 
	    } 
#endif /* 0/1 */ 
	 e = next+1 ; 
	 value-- ; 
	 } 
      // OK, we have the desired string, so output it 
      size_t count = 0 ; 
      while (*e && *e != '\n') 
	 { 
	 if (*e == '\\') 
	    { 
	    switch (*++e) 
	       { 
	       case '\n': 
		  // do nothing, this was just a dummy to retain trailing 
		  // white space 
		  break ; 
	       case 't': 
		  width -= (count%8) ; 
		  count = (8*((count+7)/8))-1 ; 
		  // fall through to default case 
	       default: 
		  fputc(*e++,out) ; 
	       } 
	    } 
	 else 
	    { 
	    fputc(*e++,out) ; 
	    count++ ; 
	    } 
	 width-- ; 
	 } 
      } 
   else 
      { 
      fputs("þenumþ",out) ; 
      width-- ; 
      } 
   for (int i = 0 ; i < width ; i++) 
      fputc(' ',out) ; 
   return end ; 
} 
 
//---------------------------------------------------------------------- 
 
static const char *format_option(FILE *out, DWORD value, int width, 
				 const char *option) 
{ 
   if (!out || !option) 
      return option ; 
   char terminator = *option++ ; 
   const char *end = strchr(option,terminator) ; 
   if (!end) 
      return option ; 
   int len = end-option ; 
   if (width == 0) 
      width = len ; 
   for (int i = len ; i < width ; i++) 
      fputc(' ',out) ; 
   if (value) 
      fwrite(option,sizeof(char),len,out) ; 
   else 
      { 
      while (len-- > 0) 
	 fputc('-',out) ; 
      } 
   return end ; 
} 
 
//---------------------------------------------------------------------- 
 
static const char *format_alternative(FILE *out, DWORD value, int width, 
				      const char *option) 
{ 
   if (!out || !option) 
      return option ; 
   char terminator = *option++ ; 
   const char *end = strchr(option,terminator) ; 
   if (!end) 
      return option ; 
   const char *alternates[5] = { 0, 0, 0, 0, 0 } ; 
   alternates[0] = option ; 
   int num_alts = 1 ; 
   for (const char *alt = option ; alt < end ; alt++) 
      if (*alt == ';') 
	 { 
	 alternates[num_alts++] = alt+1 ; 
	 if (num_alts >= lengthof(alternates)-1) 
	    break ; 
	 } 
   alternates[num_alts] = end+1 ; 
   if (value >= num_alts) 
      value = num_alts-1 ; 
   int len = (int)(alternates[(int)value+1] - alternates[(int)value]) - 1 ; 
   if (width == 0) 
      width = len ; 
   for (int i = len ; i < width ; i++) 
      fputc(' ',out) ; 
   fwrite(alternates[(int)value],sizeof(char),len,out) ; 
   return end ; 
} 
 
//---------------------------------------------------------------------- 
 
static int format(FILE *out, const char *cfg, const char *fmt, 
		  const char *enums) 
{ 
   if (!out || !cfg || !fmt) 
      return FALSE ; 
   for (const char *s = fmt ; *s ; fmt = s) 
      { 
      while (*s && *s != '%' && *s != '\\') 
	 s++ ; 
      if (s != fmt) 
	 fwrite(fmt,sizeof(char),s-fmt,out) ; 
      if (*s == '\\') 
	 { 
	 s++ ;				// consume the backslash 
	 switch (*s) 
	    { 
	    case '\\':			// literal backslash 
	       fputc('\\',out) ; 
	       break ; 
	    case 't':			// tab 
	       fputc('\t',out) ; 
	       break ; 
	    default:			// don't know, so just print the char 
	       fputc(*s,out) ; 
	       break ; 
	    } 
	 s++ ;				// consume the char after backslash 
	 } 
      else if (*s == '%') 
	 { 
	 DWORD cfgval = 0 ; 
	 fmt = s ; 
	 s++ ;				// skip the percent sign 
	 if (*s == '[') 
	    cfgval = extract_field(s,cfg) ; 
	 int width = 0 ; 
	 while (isdigit(*s)) 
	    { 
	    width = 10*width + (*s-'0') ; 
	    s++ ; 
	    } 
	 switch (*s)			// dispatch on format character 
	    { 
	    case 'b':			// binary 
	       format_number(out,cfgval,width,2) ; 
	       break ; 
	    case 'o':			// octal 
	       format_number(out,cfgval,width,8) ; 
	       break ; 
	    case 'd':			// decimal 
	       format_number(out,cfgval,width,10) ; 
	       break ; 
	    case 'x':			// hex 
	       format_number(out,cfgval,width,16) ; 
	       break ; 
	    case 'e': 
	       format_enabled(out,cfgval,width) ; 
	       break ; 
	    case 'E': 
	       format_enabled(out,cfgval==0,width) ; 
	       break ; 
	    case 'f':			// flag 
	       format_flag(out,cfgval,width) ; 
	       break ; 
	    case 'n': 
	       format_yesno(out,cfgval == 0, width) ; 
	       break ; 
	    case 'N': 
	       format_full_yesno(out,cfgval == 0, width) ; 
	       break ; 
	    case 'y': 
	       format_yesno(out,cfgval,width) ; 
	       break ; 
	    case 'Y': 
	       format_full_yesno(out,cfgval,width) ; 
	       break ; 
	    case '(':			// enumerated list of values 
	       s = format_enum(out,cfgval,width,s,enums) ; 
	       break ; 
	    case '{': 
	       format_charlist(out,cfgval,width,s) ; 
	       break ; 
	    case '/': 
	       s = format_option(out,cfgval,width,s) ; 
	       break ; 
	    case '|': 
	       s = format_alternative(out,cfgval,width,s) ; 
	       break ; 
	    case '%':			// literal percent sign 
	       fputc('%',out) ; 
	       break ; 
	    case '!':			// rest of line is a comment 
	       while (*s && *s != '\n') 
		  s++ ; 
	       if (!*s)			// back up if we hit the end of string 
		  s-- ; 
	       s-- ;			// pre-undo the s++ below 
	       break ; 
	    case '\n':			// paste together two lines 
	       // do nothing, already skipping the newline 
	       break ; 
	    default: 
	       // don't know how to handle!  so, just output the format spec 
	       fwrite(fmt,sizeof(char),s-fmt,out) ; 
	       break ; 
	    } 
	 s++ ; 
	 } 
      } 
   fflush(out) ; 
   return TRUE ; 
} 
 
 
//---------------------------------------------------------------------- 
 
static void determine_ROM_size(int bus, int device, int func, int reg) 
{ 
   WORD orig_lo, orig_hi ; 
   WORD new_lo, new_hi ; 
   read_DWORD_register(bus,device,func,reg,&orig_lo,&orig_hi) ; 
   // try setting all address bits 
   write_DWORD_register(bus,device,func,reg,0xFC00,0xFFFF) ; 
   // check which actually got set 
   int read_error = FALSE ; 
   if (!read_DWORD_register(bus,device,func,reg,&new_lo,&new_hi)) 
      read_error = TRUE ; 
   // restore original state 
   write_DWORD_register(bus,device,func,reg,orig_lo,orig_hi) ; 
   if (read_error) 
      printf("(error)") ; 
   else if (new_lo == 0x0000 && new_hi == 0x0000) 
      printf("(no ROM)") ; 
   else 
      { 
      new_lo &= 0xFC00 ;		// mask out low ten bits 
      int lowbit = 0 ; 
      if (new_lo) 
	 for (int i = 10 ; i < 16 ; i++) 
	    if ((new_lo & (1U << i)) != 0) 
	       { 
	       lowbit = i ; 
	       break ; 
	       } 
      if (!lowbit) 
	 for (int i = 0 ; i < 16 ; i++) 
	    if ((new_hi & (1U << i)) != 0) 
	       { 
	       lowbit = i + 16 ; 
	       break ; 
	       } 
      const char *ROMstate = (orig_lo & 1) ? "enabled" : "disabled" ; 
      if (lowbit < 20) 
	 printf("(%dK,%s)",1 << (lowbit - 10),ROMstate) ; 
      else 
	 printf("(%dM,%s)",1 << (lowbit - 20),ROMstate) ; 
      } 
} 
 
//---------------------------------------------------------------------- 
 
static void determine_region_size(int bus, int device, int func, int reg) 
{ 
   WORD orig_lo, orig_hi ; 
   WORD new_lo, new_hi ; 
   read_DWORD_register(bus,device,func,reg,&orig_lo,&orig_hi) ; 
   // try setting all address bits (preserving the I/O-memory bit) 
   write_DWORD_register(bus,device,func,reg,0xFFFC|(orig_lo&1),0xFFFF) ; 
   // check which actually got set 
   int read_error = FALSE ; 
   if (!read_DWORD_register(bus,device,func,reg,&new_lo,&new_hi)) 
      read_error = TRUE ; 
   // restore original state 
   write_DWORD_register(bus,device,func,reg,orig_lo,orig_hi) ; 
   if (read_error) 
      printf("(error)") ; 
   else if (new_lo == 0x0000 && new_hi == 0x0000) 
      printf("(no region)") ; 
   else 
      { 
      if (orig_lo & 1)			// is it an I/O region? 
	 new_lo &= 0xFFFC ;		// mask out low two bits 
      else 
	 new_lo &= 0xFFF0 ;		// mask out low four bits 
      int lowbit = 0 ; 
      if (new_lo) 
	 for (int i = 2 ; i < 16 ; i++) 
	    if ((new_lo & (1U << i)) != 0) 
	       { 
	       lowbit = i ; 
	       break ; 
	       } 
      if (!lowbit) 
	 for (int i = 0 ; i < 16 ; i++) 
	    if ((new_hi & (1U << i)) != 0) 
	       { 
	       lowbit = i + 16 ; 
	       break ; 
	       } 
      if (lowbit < 10) 
	 printf("len=%d",1 << lowbit) ; 
      else if (lowbit < 20) 
	 printf("len=%dK",1 << (lowbit - 10)) ; 
      else 
	 printf("len=%dM",1 << (lowbit - 20)) ; 
      } 
} 
 
//---------------------------------------------------------------------- 
 
static int dump_base_address(int bus, int device, int func, int number, 
			     DWORD base, DWORD nextbase) 
{ 
   if (base) 
      { 
      printf("\t(%d) %8.08lX = %s ",number,base,((base & 1) ? "I/O" : "mem")) ; 
      if (base & 1) 
	 { 
	 // I/O base address 
	 printf("base=%8.08lX ",(base & ~3)) ; 
	 determine_region_size(bus,device,func,4*number+0x10) ; 
	 putchar('\n') ; 
	 } 
      else 
	 { 
	 // memory base address 
	 int type = (int)((base & 6) >> 1) ; 
	 int used = FALSE ; 
	 switch (type) 
	    { 
	    case 0: 
	       printf("base=%8.08lX ",(base & 0xFFFFFFF0L)) ; 
	       break ; 
	    case 1: 
	       printf("base=%6.06lX ",(base & 0x00FFFFF0L)) ; 
	       break ; 
	    case 2: 
	       printf("base=%8.08lX%8.08lX ",nextbase,(base & ~0x0F)) ; 
	       used = TRUE ; 
	       break ; 
	    case 3: 
	       printf("!reserved! ") ; 
	       break ; 
	    } 
	 determine_region_size(bus,device,func,4*number+0x10) ; 
	 if ((base & 8) != 0) 
	    printf(" prefetchable") ; 
	 putchar('\n') ; 
	 return used ;			// indicate whether next reg. used up 
	 } 
      } 
   return FALSE ;			// base does not extend to next reg. 
} 
 
//---------------------------------------------------------------------- 
 
static void dump_base_addresses(int bus, int device, int func, 
				DWORD base0, DWORD base1, DWORD base2, 
				DWORD base3, DWORD base4, DWORD base5) 
{ 
   if (base0 == 0 && base1 == 0 && base2 == 0 && base3 == 0 && 
       base4 == 0 && base5 == 0) 
      printf("No base addresses\n") ; 
   else 
      { 
      printf("Base Addresses:\n") ; 
      int used ; 
      used = dump_base_address(bus,device,func,0,base0,base1) ; 
      if (!used) 
	 used = dump_base_address(bus,device,func,1,base1,base2) ; 
      else 
	 used = FALSE ; 
      if (!used) 
	 used = dump_base_address(bus,device,func,2,base2,base3) ; 
      else 
	 used = FALSE ; 
      if (!used) 
	 used = dump_base_address(bus,device,func,3,base3,base4) ; 
      else 
	 used = FALSE ; 
      if (!used) 
	 used = dump_base_address(bus,device,func,4,base4,base5) ; 
      else 
	 used = FALSE ; 
      if (!used) 
	 dump_base_address(bus,device,func,5,base5,0) ; 
      } 
} 
 
//---------------------------------------------------------------------- 
 
static void dump_PCI_PM_capabilities(const char *caplist) 
{ 
   unsigned int PMC = *(unsigned int*)(caplist+2) ; 
   unsigned int PMCSR = *(unsigned int*)(caplist+4) ; 
   int PMCSR_ext = caplist[6] ; 
   int data = caplist[7] ; 
   printf("\t PMC    = ") ; 
   write_bits(PMC,PMC_bits,lengthof(PMC_bits)) ; 
   printf("\n\t\tDynClk = %d, PCI_PM version = %d\n", 
		  (PMC & 0x00C0) >> 6, 
		  (PMC & 0x0007)) ; 
   printf("\t PMCSR  = %4.04X, data-select=%d ",PMCSR, (PMCSR & 0x1E00) >> 9) ; 
   if (PMCSR & 0x6000) 
      printf("scale=0.%s1\n","00"+(3-((PMCSR & 0x1E00)>>9))) ; 
   else 
      printf("unknown/unimplemented\n") ; 
   printf("\t\tstate=D%d %s %s %s\n", 
                  PMCSR & 0x0003, 
                  PMCSR & 0x0010 ? "DynReport" : "", 
                  PMCSR & 0x0100 ? "PME#-ena"  : "", 
                  PMCSR & 0x8000 ? "PME#-active" : "") ; 
   printf("\t PMCSRX = %s %s %s %s\n", 
		  PMCSR_ext & 0x80 ? "BusPowerCtrl" : "--", 
		  PMCSR_ext & 0x20 ? "state-B2" : "--", 
                  PMCSR_ext & 0x40 ? "state-B3" : "--", 
		  PMCSR_ext & 0x10 ? "DynamicClock" : "--") ; 
   printf("\t Data   = %2.02X\n",data) ; 
   return ; 
} 
 
//---------------------------------------------------------------------- 
 
static void dump_AGP_capabilities(const char *caplist) 
{ 
   static char *agprate[] = { "default speed", "1x", "2x", "illegal", 
			      "4x", "illegal", "illegal", "illegal" } ; 
   BYTE AGPver = caplist[2] ; 
   DWORD AGPstat = *((DWORD*)&caplist[4]) ; 
   DWORD AGPcmd = *((DWORD*)&caplist[8]) ; 
   printf("\t supported AGP version = %d.%d\n",AGPver/16,AGPver%16) ; 
   printf("\t max request queue = %d, AGP side band addressing %ssupported\n", 
	  (int)((AGPstat >> 24)&0xFF),((AGPstat&0x200)?"":"not ")) ; 
   printf("\t supported transfer type(s): %s%s%s\n", 
	  ((AGPstat&1)?"1x ":""),((AGPstat&2)?"2x ":""), 
	  ((AGPstat&4)?"4x ":"")) ; 
   printf("\t AGP is currently %sabled at %s (sideband addr %s)\n", 
	  (AGPcmd&0x100?"en":"dis"),agprate[AGPcmd&7], 
	  (AGPcmd&0x200?"on":"off")) ; 
   return ; 
} 
 
//---------------------------------------------------------------------- 
 
static void dump_capabilities_list(int start_offset, const char *cfgdata) 
{ 
   if (!start_offset) 
      { 
      printf("empty Capabilities list!\n") ; 
      return ; 
      } 
   printf("Capabilities List:\n",start_offset) ; 
   do { 
      unsigned ID = cfgdata[start_offset] ; 
      unsigned next = cfgdata[start_offset+1] ; 
      printf("\tID @%2.02X = %2.02X ",start_offset,ID) ; 
      switch (ID) 
	 { 
	 case 0x00: 
	    printf("disabled capability\n") ; 
	    break ; 
	 case 0x01: 
	    printf("PCI Power Management\n") ; 
	    dump_PCI_PM_capabilities(cfgdata+start_offset) ; 
	    break ; 
	 case 0x02: 
	    printf("AGP Capabilities\n") ; 
	    dump_AGP_capabilities(cfgdata+start_offset) ; 
	    break ; 
	 default: 
	    printf("(unknown)\n") ; 
	    break ; 
	 } 
      start_offset = next ; 
      } while (start_offset != 0) ; 
} 
 
//---------------------------------------------------------------------- 
 
static void dump_device_specific_data(unsigned vendor, unsigned device, 
				      const char *cfgdata) 
{ 
   if (!cfgdata) 
      return ; 
   char *format_string ; 
   char const *enum_list = 0 ; 
   if (!know_device(vendor,device,format_string,enum_list)) 
      return ; 
   format(stdout,cfgdata,format_string,enum_list) ; 
   free(format_string) ; 
   return ; 
} 
 
//---------------------------------------------------------------------- 
 
static void write_IO_base_limit(const char *leadin, BYTE low, WORD high) 
{ 
   printf(leadin) ; 
   int size = (low & 0x0F) ; 
   if (size == 0) 
      printf("%4.04X",(low & 0xF0) << 8) ; 
   else if (size == 1) 
      printf("%8.08lX",((DWORD)high)|((low & 0xF0) << 8)) ; 
   else 
      printf("") ; 
   return ; 
} 
 
//---------------------------------------------------------------------- 
 
static void write_mem_base_limit(const char *leadin, WORD low, DWORD high, 
				 DWORD limit_low, DWORD limit_high) 
{ 
   printf(leadin) ; 
   int size = (low & 0x0F) ; 
   if (size == 0) 
      printf(" %4.04X0000",low & 0xFFF0) ; 
   else if (size == 1) 
      printf(" %8.08lX%4.04X0000",high,(low & 0xFFF0)) ; 
   else 
      printf(" ") ; 
   size = (limit_low & 0x0F) ; 
   if (size == 0) 
      printf("/%4.04X0000",limit_low & 0xFFF0) ; 
   else if (size == 1) 
      printf("/%8.08lX%4.04X0000",limit_high,(limit_low & 0xFFF0)) ; 
   else 
      printf("/") ; 
   return ; 
} 
 
//---------------------------------------------------------------------- 
 
#define setp setprecision 
 
static int dump_PCI_config(int bus, int device, int func, int report_missing, 
			   int &is_multifunc) 
{ 
   int i ; 
   PCIcfg *cfg = read_PCI_config(bus,device,func) ; 
   if (!cfg || cfg->vendorID == 0xFFFF || cfg->deviceID == 0xFFFF) 
      { 
      if (report_missing) 
	 printf("No PCI device at bus %2.02X device %2.02X function %2.02X\n", 
		bus,device,func) ; 
      return FALSE ; 
      } 
   if (!first_device) 
      printf("-----------------------------------------------------------\n") ; 
   first_device = FALSE ; 
   printf("PCI bus %2.02X device %2.02X function %2.02X:  ",bus,device,func) ; 
   printf("Header Type '") ; 
   switch (cfg->header_type & 0x7F) 
      { 
      case 0x00: 
	 printf("non-bridge") ; 
	 break ; 
      case 0x01: 
	 printf("PCI-PCI bridge") ; 
	 break ; 
      case 0x02: 
	 printf("CardBus bridge") ; 
	 break ; 
      default: 
	 printf("other [%2.02X]",cfg->header_type & 0x7F) ; 
	 break ; 
      } 
   if ((cfg->header_type & 0x80) != 0)	// make multi-function flag sticky 
      is_multifunc = 1 ;		// since some devs only show on func 0 
   printf("' (%s-func)\n",is_multifunc ? "multi" : "single") ; 
   const char *class_name = "???" ; 
   if (cfg->classcode < lengthof(class_names)) 
      class_name = class_names[cfg->classcode] ; 
   const char *subclass_name = get_subclass_name(cfg->classcode,cfg->subclass); 
   if (terse) 
      { 
      const char *vendorname = get_vendor_name(cfg->vendorID) ; 
      char unkvendor[40] ; 
      if (strcmp(vendorname,"???") == 0) 
	 { 
	 sprintf(unkvendor,"(Vendor %4.04X)",cfg->vendorID) ; 
	 vendorname = unkvendor ; 
	 } 
      const char *devname = get_device_name(cfg->vendorID,cfg->deviceID) ; 
      char unkdevice[40] ; 
      if (strcmp(devname,"???") == 0) 
	 { 
	 sprintf(unkdevice,"(DeviceID %4.04X)",cfg->deviceID) ; 
	 devname = unkdevice ; 
	 } 
      printf("%-38.38s º Class %2.02X: %-20.20s\tI/F: %2.02X\n" 
	     "%-38.38s º SubCl %2.02X: %-20.20s\tRev: %2.02X\n", 
	     vendorname,cfg->classcode,class_name,cfg->progIF, 
	     devname,cfg->subclass,subclass_name,cfg->revisionID) ; 
      return is_multifunc ; 
      } 
   else 
      { 
      printf("Vendor:\t%4.04X\t%-50.50s\n",cfg->vendorID, 
	     get_vendor_name(cfg->vendorID)) ; 
      printf("Device:\t%4.04X\t%-50.50s\n", 
	     cfg->deviceID,get_device_name(cfg->vendorID,cfg->deviceID)) ; 
      printf("Class:\t  %2.02X\t%-20.20s\tRevision:\t%2.02X\n", 
	     cfg->classcode,class_name,cfg->revisionID) ; 
      printf("SubClass: %2.02X\t%-20.20s\tProgramI/F:\t%2.02X\n", 
	     cfg->subclass,subclass_name,cfg->progIF) ; 
      } 
   printf("CommandReg:	 %4.04X =",cfg->command_reg) ; 
   WRITE_CMD_BITS(cfg->command_reg) ; 
   printf("\n" 
	  "Status Reg:	 %4.04X =",cfg->status_reg) ; 
   WRITE_STAT_BITS(cfg->status_reg) ; 
   printf(" (%s)\n",select_timing[(cfg->status_reg & 0x0600) >> 9]) ; 
   printf("CacheLine:	   %2.02X\tLatency:\t%2.02X\tBIST:\t     %2.02X\n", 
	  cfg->cacheline_size,cfg->latency,cfg->BIST) ; 
   switch(cfg->header_type & 0x7F) 
      { 
      case 0x00:  // non-bridge 
	 printf("SubsysVendor:    %4.04X\tSubsysDevice: %4.04X\n", 
		cfg->nonbridge.subsystem_vendorID, 
		cfg->nonbridge.subsystem_deviceID) ; 
	 dump_base_addresses(bus,device,func, 
			     cfg->nonbridge.base_address0, 
			     cfg->nonbridge.base_address1, 
			     cfg->nonbridge.base_address2, 
			     cfg->nonbridge.base_address3, 
			     cfg->nonbridge.base_address4, 
			     cfg->nonbridge.base_address5) ; 
	 printf("CardBus:     %8.08lX\tExpansionROM: %8.08lX ", 
		cfg->nonbridge.CardBus_CIS,cfg->nonbridge.expansion_ROM) ; 
	 determine_ROM_size(bus,device,func,0x30) ; 
	 printf("\n") ; 
	 printf("INTline:\t   %2.02X\tINTpin:       %2.02X\n", 
		cfg->nonbridge.interrupt_line,cfg->nonbridge.interrupt_pin) ; 
	 printf("MinGrant:\t   %2.02X\tMaxLatency:   %2.02X\n", 
		cfg->nonbridge.min_grant,cfg->nonbridge.max_latency) ; 
	 printf("Device-Specific Data:\n 40:") ; 
	 for (i = 0 ; i < 48 ; i++) 
	    { 
	    printf(" %8.08lX ",cfg->nonbridge.device_specific[i]) ; 
	    if (i % 6 == 5 && i < 47) 
	       printf("\n %2.02X:",4*(i+17)) ; 
	    } 
	 putchar('\n') ; 
	 if (cfg->status_reg & CAPLIST_BIT) 
	    dump_capabilities_list(cfg->nonbridge.cap_ptr,(char*)cfg) ; 
	 break ; 
      case 0x01:  // bridge 
	 printf("PrimaryBus:\t   %2.02X\tSecondaryBus:	%2.02X\tSubordinBus: %2.02X\n", 
		cfg->bridge.primary_bus,cfg->bridge.secondary_bus, 
		cfg->bridge.subordinate_bus) ; 
	 printf("SecLatency:\t   %2.02X\tExpansion ROM: %8.08lX ", 
		cfg->bridge.secondary_latency,cfg->bridge.expansion_ROM) ; 
	 determine_ROM_size(bus,device,func,0x38) ; 
	 printf("\n") ; 
	 printf("SecStatus:\t %4.04X =",cfg->bridge.secondary_status) ; 
	 WRITE_STAT_BITS(cfg->bridge.secondary_status) ; 
	 printf(" (%s)\n",select_timing[(cfg->bridge.secondary_status & 0x0600) >> 9]) ; 
	 printf("BridgeCntrl:\t %4.04X =", 
		cfg->bridge.bridge_control) ; 
	 WRITE_BCTRL_BITS(cfg->bridge.bridge_control) ; 
	 printf("\n") ; 
	 write_mem_base_limit("MemBase/Limit: ",cfg->bridge.memory_base_low,0, 
			      cfg->bridge.memory_limit_low,0) ; 
	 printf("\t") ; 
         write_IO_base_limit("IObase/limit: ",cfg->bridge.IO_base_low, 
                             cfg->bridge.IO_base_high) ; 
         write_IO_base_limit("/",cfg->bridge.IO_limit_low, 
                             cfg->bridge.IO_limit_high) ; 
	 printf("\n") ; 
	 write_mem_base_limit("PrefBase/Limit:",cfg->bridge.prefetch_base_low, 
			      cfg->bridge.prefetch_base_high, 
			      cfg->bridge.prefetch_limit_low, 
			      cfg->bridge.prefetch_limit_high) ; 
	 printf("\tINTline:  %2.02X\tINTpin:  %2.02X\n", 
		cfg->bridge.interrupt_line,cfg->bridge.interrupt_pin) ; 
	 dump_base_addresses(bus,device,func, 
			     cfg->bridge.base_address0, 
			     cfg->bridge.base_address1,0,0,0,0) ; 
	 printf("Device-Specific Data:\n 40:") ; 
 	 for (i = 0 ; i < 48 ; i++) 
	    { 
	    printf(" %8.08lX ",cfg->bridge.device_specific[i]) ; 
	    if (i % 6 == 5 && i < 47) 
	       printf("\n %2.02X:",4*(i+17)) ; 
	    } 
	 putchar('\n') ; 
	 break	; 
      case 0x02: 
	 printf("SubsysVendor: %4.04X\tSubsysDevice: %4.04X\n", 
		cfg->cardbus.subsystem_vendorID, 
		cfg->cardbus.subsystem_deviceID) ; 
	 printf("PCI bus: %2.02X\tCardBus bus: %2.02X\tSubordBus: %2.02X\tLatency: %2.02X\n", 
		cfg->cardbus.PCI_bus, cfg->cardbus.CardBus_bus, 
		cfg->cardbus.subordinate_bus, cfg->cardbus.latency_timer) ; 
	 printf("Memory0: %8.08lX bytes at %8.08lX\n", 
		cfg->cardbus.memory_limit0, cfg->cardbus.memory_base0) ; 
	 printf("Memory1: %8.08lX bytes at %8.08lX\n", 
		cfg->cardbus.memory_limit1, cfg->cardbus.memory_base1) ; 
	 printf("I/O range 0: %4.04X ports at %4.04X\n", 
		cfg->cardbus.IOlimit_0low, cfg->cardbus.IObase_0low) ; 
	 printf("I/O range 1: %4.04X ports at %4.04X\n", 
		cfg->cardbus.IOlimit_1low, cfg->cardbus.IObase_1low) ; 
	 printf("INTline:  %2.02X\tINTpin:  %2.02X\tBridgeCntrl: %4.04X\n", 
		cfg->cardbus.interrupt_line,cfg->cardbus.interrupt_pin, 
		cfg->cardbus.bridge_control) ; 
	 printf("Legacy Mode base address: %8.08lX\n", 
		cfg->cardbus.legacy_baseaddr) ; 
	 printf("Device-Specific Data:\n 80:") ; 
	 for (i = 0 ; i < 32 ; i++) 
	    { 
	    printf(" %8.08lX ",cfg->cardbus.vendor_specific[i]) ; 
	    if (i % 6 == 5 && i < 47) 
	       printf("\n %2.02X:",4*(i+33)) ; 
	    } 
	 putchar('\n') ; 
	 if (cfg->status_reg & CAPLIST_BIT) 
	    dump_capabilities_list(cfg->cardbus.cap_ptr,(char*)cfg) ; 
	 break ; 
      default: 
	 printf("Unknown header format %2.02X!\n",cfg->header_type & 0x7F) ; 
	 break ; 
      } 
   if (verbose) 
      dump_device_specific_data(cfg->vendorID,cfg->deviceID,(char*)cfg) ; 
   if (!report_missing) 
      putchar('\n') ; 
   return is_multifunc ; 
} 
 
//---------------------------------------------------------------------- 
 
static int read_device_ID(FILE *fp, char *&ID_data, int maxsize, 
			  int pcicfg_format) 
{ 
   char line[MAX_LINE] ; 
   long startpos = ftell(fp) ; 
   if (!read_nonblank_line(line,sizeof(line),fp,pcicfg_format)) 
      return FALSE ; 
   char *data_end = ID_data + maxsize - (MAX_DEVICE_NAME + 5) ; 
   const char *l = line ; 
   int is_vendor_line ; 
   if (pcicfg_format) 
      { 
      l = skip_whitespace(l) ; 
      is_vendor_line = strncmp(l,"Vendor",6) == 0 ; 
      if (is_vendor_line) 
	 l = skip_whitespace(l+6) ;	// skip to vendor ID 
      } 
   else 
      { 
      is_vendor_line = isxdigit(*l) ; 
      } 
   if (is_vendor_line) 
      { 
      *((char**)ID_data) = 0 ;		// pointer to next vendor ID 
      ID_data += sizeof(char*) ; 
      WORD *length = (WORD*)ID_data ; 
      *((WORD*)ID_data) = 0 ;		// length of data for vendor 
      ID_data += sizeof(WORD) ; 
      WORD ID = hextoint(l) ;		// get the vendor's ID 
      *((WORD*)ID_data) = ID ; 
      ID_data += sizeof(WORD) ; 
      l = skip_whitespace(l) ;		// skip to vendor name 
      int count = 0 ; 
      // copy the vendor name 
      while (*l && *l != '\n' && count++ < MAX_VENDOR_NAME) 
	 *ID_data++ = *l++ ; 
      *ID_data++ = '\0' ;		// ensure termination 
      do { 
	 startpos = ftell(fp) ; 
	 if (!read_nonblank_line(line,sizeof(line),fp,pcicfg_format)) 
	    break ; 
	 l = line ; 
	 int is_device_line ; 
	 if (pcicfg_format) 
	    { 
	    l = skip_whitespace(l) ; 
	    is_vendor_line = strncmp(l,"Vendor",6) == 0 ; 
	    is_device_line = isxdigit(*l) ; 
	    } 
	 else 
	    { 
	    is_vendor_line = isxdigit(*l) ; 
	    is_device_line = FALSE ; 
	    if (!is_vendor_line) 
	       { 
	       l = skip_whitespace(l) ; 
	       is_device_line = isxdigit(*l) ; 
	       } 
	    } 
	 if (is_device_line) 
	    { 
	    ID = hextoint(l) ;		// convert device ID 
	    if (ID != 0xFFFF) 
	       { 
	       *((WORD*)ID_data) = ID ; 
	       ID_data += sizeof(WORD) ; 
	       l = skip_whitespace(l) ; // skip to device name 
	       // copy the device name 
	       count = 0 ; 
	       while (*l && *l != '\n' && count++ < MAX_DEVICE_NAME) 
		  *ID_data++ = *l++ ; 
	       *ID_data++ = '\0' ;	// ensure termination 
	       if (ID_data >= data_end) 
		  { 
		  fprintf(stderr,"Too much information for a single vendor!\n") ; 
		  return TRUE ; 
		  } 
	       } 
	    } 
	 } while (!is_vendor_line) ; 
      *length = (ID_data - (char*)length) - sizeof(*length) ; 
      // back up to start of Vendor line 
      (void)fseek(fp,startpos,SEEK_SET) ; 
      return TRUE ; 
      } 
   else // !is_vendor_line 
      { 
      fprintf(stderr,"non-empty line found, but it is not a vendor ID line!\n"); 
      return FALSE ; 
      } 
} 
 
//---------------------------------------------------------------------- 
 
static int check_PCICFG_DAT_signature(FILE *fp, int complain = FALSE) 
{ 
   char signature[SIGNATURE_LENGTH] ; 
   (void)fseek(fp,0L,SEEK_SET) ; 
   signature[0] = '\0' ; 
   if (fread(signature,sizeof(char),sizeof(signature),fp) < sizeof(signature)) 
      return FALSE ; 
   int present = strncmp(signature,SIGNATURE,SIGNATURE_LENGTH) == 0 ; 
   if (present) 
      { 
      // skip the rest of the first line 
      int c ; 
      while ((c = fgetc(fp)) != EOF && c != '\n') 
	 ; 
      } 
   else 
      { 
      (void)fseek(fp,0L,SEEK_SET) ; 
      if (complain) 
	 { 
	 fprintf(stderr,"Invalid PCICFG.DAT\n") ; 
	 if (verbose) 
	    fprintf(stderr,"File Signature: \"%6.6s\"\n",signature) ; 
	 } 
      } 
   return present ; 
} 
 
//---------------------------------------------------------------------- 
 
static int load_device_IDs() 
{ 
   FILE *fp = open_PCICFG_DAT("r") ; 
   if (fp) 
      { 
      int pcicfg_format = check_PCICFG_DAT_signature(fp,TRUE) ; 
      char *prev_ID_data = 0 ; 
      while (!feof(fp)) 
	 { 
	 char *vendor_ID_data = (char*)malloc(MAX_VENDOR_DATA) ; 
	 if (!vendor_ID_data) 
	    { 
	    fprintf(stderr,"Insufficient memory for PCICFG.DAT contents\n" 
		           "Some vendors/devices will not be shown by name\n") ; 
	    return FALSE ; 
	    } 
	 char *ID_data = vendor_ID_data ; 
	 if (!read_device_ID(fp,ID_data,MAX_VENDOR_DATA,pcicfg_format)) 
	    break ; 
	 ID_data = (char*)realloc(vendor_ID_data, ID_data - vendor_ID_data) ; 
	 if (ID_data) 
	    vendor_ID_data = ID_data ; 
	 if (prev_ID_data) 
	    *((char**)prev_ID_data) = vendor_ID_data ; 
	 else 
	    device_ID_data = vendor_ID_data ; 
	 prev_ID_data = vendor_ID_data ; 
	 } 
      return TRUE ; 
      } 
   return FALSE ;    
} 
 
//---------------------------------------------------------------------- 
 
static int write_PCICFG_DAT_header(FILE *outfp) 
{ 
   fputs("PCICFG ;<<-- signature - DO NOT CHANGE\n",outfp) ; 
   return TRUE ; 
} 
 
//---------------------------------------------------------------------- 
 
static int copy_initial_comments(FILE *outfp, FILE *datfp) 
{ 
   char line[MAX_LINE] ; 
   int is_comment ; 
   long startpos ; 
   if (verbose) 
      printf("copying initial comments:\n") ; 
   do { 
      line[0] = '\0' ; 
      startpos = ftell(datfp) ; 
      if (!fgets(line,sizeof(line),datfp)) 
	 return FALSE ; 
      is_comment = is_comment_line(line) ; 
      if (is_comment) 
	 { 
	 fputs(line,outfp) ; 
	 if (verbose) 
	    printf("\t%s",line) ; 
	 } 
      } while (is_comment) ; 
   fseek(datfp,startpos,SEEK_SET) ; 
   return TRUE ; 
} 
 
//---------------------------------------------------------------------- 
 
static char *skip_string(char *s, char *end) 
{ 
   while (s < end && *s) 
      s++ ; 
   if (s < end) 
      s++ ;				// skip terminating NUL 
   return s ; 
} 
 
//---------------------------------------------------------------------- 
 
static int write_vendor_data(FILE *outfp, WORD ID, char *data, char *end) 
{ 
   if (!outfp || !data || data >= end) 
      return FALSE ; 
   // output the vendor's name and ID 
   fprintf(outfp,"Vendor %4.04X %s\n",ID,data) ; 
   data = skip_string(data,end) ; 
   // output all of the devices listed under the vendor 
   while (data < end) 
      { 
      ID = *((WORD*)data) ; 
      data += sizeof(WORD) ; 
      fprintf(outfp,"  %4.04X %s\n",ID,data) ; 
      data = skip_string(data,end) ; 
      } 
   return TRUE ; 
} 
 
//---------------------------------------------------------------------- 
 
static int merge_vendor_data(FILE *outfp, char *data1, char *data2) 
{ 
   data1 += sizeof(char*) ;		// skip the 'next' field 
   WORD length1 = *((WORD*)data1) ;	// get length of data 
   data1 += sizeof(WORD) ; 
   char *end1 = data1 + length1 ; 
   WORD ID1 = *((WORD*)data1) ;		// get vendor ID 
   data1 += sizeof(WORD) ; 
   data2 += sizeof(char*) ;		// skip the 'next' field 
   WORD length2 = *((WORD*)data2) ;	// get length of data 
   data2 += sizeof(WORD) ; 
   char *end2 = data2 + length2 ; 
   WORD ID2 = *((WORD*)data2) ;		// get vendor ID 
   data2 += sizeof(WORD) ; 
   if (ID1 == ID2) 
      { 
      fprintf(outfp,"Vendor %4.04X %s\n",ID1,data1) ; 
      data1 = skip_string(data1,end1) ; 
      data2 = skip_string(data2,end2) ; 
      ID1 = *((WORD*)data1) ; 
      data1 += sizeof(WORD) ; 
      ID2 = *((WORD*)data2) ; 
      data2 += sizeof(WORD) ; 
      while (data1 < end1 && data2 < end2) 
	 { 
	 if (ID1 <= ID2) 
	    { 
	    fprintf(outfp,"  %4.04X %s\n",ID1,data1) ; 
	    if (ID1 == ID2) 
	       { 
	       data2 = skip_string(data2,end2) ; 
	       ID2 = *((WORD*)data2) ; 
	       data2 += sizeof(WORD) ; 
	       } 
	    data1 = skip_string(data1,end1) ; 
	    ID1 = *((WORD*)data1) ; 
	    data1 += sizeof(WORD) ; 
	    } 
	 else // if (ID1 > ID2) 
	    { 
	    fprintf(outfp,"  %4.04X %s\n",ID2,data2) ; 
	    data2 = skip_string(data2,end2) ; 
	    ID2 = *((WORD*)data2) ; 
	    data2 += sizeof(WORD) ; 
	    } 
	 } 
      while (data1 < end1) 
	 { 
	 // copy the remainder of the first file's device IDs 
	 fprintf(outfp,"  %4.04X %s\n",ID1,data1) ; 
	 data1 = skip_string(data1,end1) ; 
	 ID1 = *((WORD*)data1) ; 
	 data1 += sizeof(WORD) ; 
	 } 
      while (data2 < end2) 
	 { 
	 // copy the remainder of the second file's device IDs 
	 fprintf(outfp,"  %4.04X %s\n",ID2,data2) ; 
	 data2 = skip_string(data2,end2) ; 
	 ID2 = *((WORD*)data2) ; 
	 data2 += sizeof(WORD) ; 
	 } 
      return 0 ; 
      } 
   else if (ID1 < ID2) 
      { 
      write_vendor_data(outfp,ID1,data1,end1) ; 
      return -1 ; 
      } 
   else // ID1 > ID2 
      { 
      write_vendor_data(outfp,ID2,data2,end2) ; 
      return +1 ; 
      } 
} 
 
//---------------------------------------------------------------------- 
 
static int merge_info(FILE *outfp, FILE *datfp, FILE *newfp) 
{ 
   if (!outfp || !datfp || !newfp) 
      return FALSE ; 
   if (!check_PCICFG_DAT_signature(datfp,TRUE)) 
      { 
      fprintf(stderr,"invalid signature in PCICFG.DAT\n") ; 
      return FALSE ; 
      } 
   if (!write_PCICFG_DAT_header(outfp) || !copy_initial_comments(outfp,datfp)) 
      { 
      fprintf(stderr,"error writing new header\n") ; 
      return FALSE ; 
      } 
   int pcicfg_format = check_PCICFG_DAT_signature(newfp,FALSE) ; 
   if (verbose) 
      printf("new data file is in %s format\n", 
	     pcicfg_format ? "PCICFG" : "Merlin's") ; 
   if (!copy_initial_comments(outfp,newfp)) 
      { 
      fprintf(stderr,"error copying initial comments\n") ; 
      return FALSE ; 
      } 
   char *data1 = (char*)malloc(MAX_VENDOR_DATA) ; 
   char *data2 = (char*)malloc(MAX_VENDOR_DATA) ; 
   if (!data1 || !data2) 
      { 
      fprintf(stderr,"Insufficient memory to merge data!\n") ; 
      if (data1) 
	 free(data1) ; 
      return FALSE ; 
      } 
   char *dat1 = data1 ; 
   char *dat2 = data2 ; 
   // load up the first vendor from each file 
   if (!read_device_ID(datfp,dat1,MAX_VENDOR_DATA,TRUE) || 
       !read_device_ID(newfp,dat2,MAX_VENDOR_DATA,pcicfg_format)) 
      { 
      free(data1) ; 
      free(data2) ; 
      fprintf(stderr,"empty data file!\n") ; 
      return FALSE ; 
      } 
   int done1 = FALSE ; 
   int done2 = FALSE ; 
   do { 
      int merge = merge_vendor_data(outfp,data1,data2) ; 
      switch (merge) 
	 { 
	 case -1: 
	    dat1 = data1 ; 
	    if (!read_device_ID(datfp,dat1,MAX_VENDOR_DATA,TRUE)) 
	       done1 = TRUE ; 
	    break ; 
	 case 0: 
	    dat1 = data1 ; 
	    if (!read_device_ID(datfp,dat1,MAX_VENDOR_DATA,TRUE)) 
	       done1 = TRUE ; 
	    // fall through to +1 
	 case +1: 
	    dat2 = data2 ; 
	    if (!read_device_ID(newfp,dat2,MAX_VENDOR_DATA,pcicfg_format)) 
	       done2 = TRUE ; 
	    break ; 
	 default: 
	    fprintf(stderr,"Missed case in switch()!\n") ; 
	    return FALSE ; 
	 } 
      } while (!done1 && !done2) ; 
   if (!done1) 
      { 
      // copy any remaining items from first file (we can't possibly have any 
      // left over from the second file, since PCICFG.DAT goes up to FFFFh) 
      do { 
	 dat1 = data1 + sizeof(char*) ;	// reset, but skip the 'next' field 
	 WORD length1 = *((WORD*)dat1) ; 
	 dat1 += sizeof(WORD) ; 
	 char *end1 = data1 + length1 ; 
	 WORD ID1 = *((WORD*)dat1) ;	// get vendor ID 
	 dat1 += sizeof(WORD) ; 
	 write_vendor_data(outfp,ID1,dat1,end1) ; 
	 dat1 = data1 ; 
	 } while (read_device_ID(datfp,dat1,MAX_VENDOR_DATA,TRUE)) ; 
      } 
   free(data1) ; 
   free(data2) ; 
   return TRUE ; 
} 
 
//---------------------------------------------------------------------- 
 
static int merge_new_info(const char *filename) 
{ 
   if (!filename || !*filename) 
      return FALSE ; 
   FILE *fp = fopen(filename,"r") ; 
   if (fp) 
      { 
      static char tempfile[] = "pcicfg.$$$" ; 
      FILE *datfp = open_PCICFG_DAT("r") ; 
      FILE *merged = fopen(tempfile,"w") ; 
      if (!datfp) 
	 return FALSE ; 
      if (!merged) 
	 { 
	 fprintf(stderr,"Unable to open temporary file for merge\n") ; 
	 return FALSE ; 
	 } 
      int success = merge_info(merged,datfp,fp) ; 
      (void) fclose(datfp) ; 
      (void) fclose(fp) ; 
      (void) fclose(merged) ; 
      if (success) 
	 { 
	 // copy the temporary file over PCICFG.DAT 
	 merged = fopen(tempfile,"r") ; 
	 fp = open_PCICFG_DAT("w") ; 
	 char buffer[BUFSIZ] ; 
	 int count ; 
	 while ((count = fread(buffer,sizeof(char),sizeof(buffer),merged)) > 0) 
	    { 
	    if (fwrite(buffer,sizeof(char),count,fp) < count) 
	       { 
	       fprintf(stderr,"Error copying temporary file to PCICFG.DAT!!\n") ; 
	       break ; 
	       } 
	    } 
	 (void) fclose(fp) ; 
	 (void) fclose(merged) ; 
	 } 
      else 
	 fprintf(stderr,"Unable to merge new data!\n") ; 
      unlink(tempfile) ; 
      return success ; 
      } 
   return FALSE ; 
} 
 
//---------------------------------------------------------------------- 
 
static int merge_new_info(int argc, char **argv) 
{ 
    if (!backup_PCICFG_DAT()) 
       { 
       fprintf(stderr,"Unable to backup PCICFG.DAT\n") ; 
       return 1 ; 
       } 
    while (argc > 0 && *argv) 
       { 
       if (!merge_new_info(argv[0])) 
	  return 2 ; 
       argc-- ; 
       argv++ ; 
       } 
    return 0 ; 
} 
 
//---------------------------------------------------------------------- 
 
int main(int argc, char **argv) 
{ 
   fprintf(stderr,"PCICFG v" VERSION " (c) Copyright 1997,1998 Ralf Brown\n") ; 
   get_exe_directory(argv[0]) ; 
   while (argc > 1 && argv[1][0] == '-') 
      { 
      switch (argv[1][1]) 
	 { 
	 case 'b': 
	    bypass_BIOS = TRUE ; 
	    if (argv[1][2] == '1') 
	       cfg_mech = 1 ; 
	    else if (argv[1][2] == '2') 
	       cfg_mech = 2 ; 
	    else 
	       determine_cfg_mech() ; 
	    break ; 
	 case 'm': 
	    // merge new info into PCICFG.DAT 
	    return merge_new_info(argc-2,argv+2) ; 
	 case 't': 
	    terse = TRUE ; 
	    break ; 
	 case 'v': 
	    verbose = TRUE ; 
	    break ; 
	 default: 
	    fprintf(stderr,"unrecognized option '%s'\n",argv[1]) ; 
	    break ; 
	 } 
      argv++ ; 
      argc-- ; 
      } 
   int maxbus = check_PCI_BIOS() ; 
   if (maxbus < 0) 
      { 
      if (!bypass_BIOS) 
	 { 
	 fprintf(stderr,"\nNo PCI BIOS detected\n") ; 
	 return 2 ; 
	 } 
      else 
	 maxbus = 0xFF ;		// have to scan ALL possible buses 
      } 
   if (!load_device_IDs()) 
      { 
      fprintf(stderr, 
	      "Unable to load the list of vendor and device IDs (PCICFG.DAT).\n" 
	      "Devices will not be identified by name.\n") ; 
      } 
   if (argc == 2 && argv[1][0] == '*') 
      { 
      for (int bus = 0 ; bus <= maxbus ; bus++) 
	 { 
	 for (int device = 0 ; device < 32 ; device++) 
	    { 
	    int multifunc = 0 ; 
	    for (int func = 0 ; func < 8 ; func++) 
	       { 
	       if (!dump_PCI_config(bus,device,func,0,multifunc)) 
		  break ; 
	       } 
	    } 
	 } 
      return 0 ; 
      } 
   if (argc < 4) 
      { 
      fprintf(stderr, 
	      "\nUsage:\tPCICFG [flag(s)] bus device func\n" 
	      "\tPCICFG [flag(s)] *         (to scan all devices)\n" 
	      "\tPCICFG -m file [file ...]  (to merge new info into PCICFG.DAT)\n" 
	      "\n" 
	      "Dumps info about the specified PCI device, or all devices\n" 
	      "Flags:\n" 
	      "\t-bN\tbypass BIOS -- talk directly to PCI hardware ports using\n" 
	      "\t\t access mechanism N (1 or 2) [using wrong one can reboot/hang!]\n" 
	      "\t-t\tterse -- output only device type and ID\n" 
	      "\t-v\tverbose output for known devices\n" 
	      "\n" 
	      "Use -v for more verbose output on devices specifically recognized by\n" 
	      "PCICFG.  Output is generally quite lengthy even without -v, so you\n" 
	      "should redirect output into a file or pipe it to MORE or LIST\n") ; 
      return 1 ; 
      } 
   char *end = 0 ; 
   int bus = (int)strtol(argv[1],&end,0) ; 
   int device = (int)strtol(argv[2],&end,0) ; 
   int func = (int)strtol(argv[3],&end,0) ; 
   if (bus > maxbus) 
      { 
      fprintf(stderr,"\nRequested PCI bus does not exist\n") ; 
      return 3 ; 
      } 
   else 
      { 
      int multifunc = 0 ; 
      dump_PCI_config(bus,device,func,1,multifunc) ; 
      } 
   return 0 ; 
} 
 
// end of file pcicfg.cpp //