Metropoli BBS
VIEWER: pcicfg.cpp MODE: TEXT (LATIN1)
/************************************************************************/
/*									*/
/*  Version 1.00							*/
/*	 by Ralf Brown							*/
/*									*/
/*  File pcicfg.cpp	       PCI configuration data dumper		*/
/*  LastEdit: 12/21/97							*/
/*									*/
/*  (c) Copyright 1995,1996,1997 Ralf Brown				*/
/*									*/
/*  This code may be freely redistributed in its entirety.  Excerpts    */
/*  may be incorporated into other programs provided that credit is     */
/*  given.								*/
/*									*/
/************************************************************************/

#include <ctype.h>
#include <dos.h>
#include <io.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define VERSION "1.00"

#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 */

//----------------------------------------------------------------------

#define CAPLIST_BIT 0x0010	// does the device have a capabilities list?

#define MAX_LINE     512	// maximum length of a line in the .DAT files

#define SIGNATURE "PCICFG"	// PCICFG.DAT signature at start of file
#define SIGNATURE_LENGTH (sizeof(SIGNATURE)-1)

//----------------------------------------------------------------------

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 ;
   } ;

//----------------------------------------------------------------------

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 select_timing[] =
   {
    "fast",
    "med",
    "slow",
    "???"
   } ;

static const char *const PMC_bits[] =
   {
     0, 				// bits 0-2: version (001)
     0,
     0,
     0, 				// bits 3-4: reserved (00)
     0,
     "DevSpec Init",                    // bit 5
     0, 				// bits 6-7: DynClk
     0,
     "FullClk",
     "D1-supp",
     "D2-supp",
     0, 				// bit 11: reserved (0)
     "PME#-D0",                         // bit 12
     "PME#-D1",                         // bit 13
     "PME#-D2",                         // bit 14
     "PME#-D3",                         // bit 15
   } ;

/************************************************************************/
/*    global variables							*/
/************************************************************************/

static int verbose = FALSE ;
static int terse = FALSE ;
static int first_device = TRUE ;

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
	  .....
*/

/************************************************************************/
/************************************************************************/

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 void skip_whitespace(char *&string)
{
   if (string)
      while (*string == ' ' || *string == '\t')
	 string++ ;
}

//----------------------------------------------------------------------

static int check_PCI_BIOS()
{
   union REGS regs ;
   regs.x.ax = 0xB101 ;
   int86(0x1A,&regs,&regs) ;
   if (regs.h.ah == 0x00)
      return regs.h.cl ;		// last PCI bus in system
   else
      return -1 ;			// no PCI BIOS detected
}

//----------------------------------------------------------------------

static PCIcfg *read_PCI_config(int bus, int device, int func)
{
   static PCIcfg cfg ;
   union REGS regs ;

   regs.x.ax = 0xB109 ;
   regs.h.bh = bus ;
   regs.h.bl = (device<<3) | (func & 0x07) ;
   for (int i = 0 ; i < sizeof(cfg) ; i += sizeof(short))
      {
      regs.x.di = i ;
      union REGS outregs ;
      int86(0x1A,&regs,&outregs) ;
      if (outregs.x.cflag != 0)
	 return 0 ;
      WORD word = outregs.x.cx ;
      *((WORD*)(((BYTE*)&cfg)+i)) = word ;
      }
   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)++ ;
      WORD ID = *((WORD*)data)++ ;
      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)++ ;
      WORD ID = *((WORD*)data)++ ;
      if (ID == vendorID)
	 {
	 // OK, we've found the vendor, now scan for the device
	 // 1. skip the vendor name
	 while (*data)
	    data++ ;
	 if (data < next)		// skip the NUL
	    data++ ;
	 // 2. check each device ID in turn
	 while (data < next)
	    {
	    ID = *((WORD*)data)++ ;
	    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 += 5 ;
	 skip_whitespace(newline) ;
	 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)
	    {
	    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+1) ;
	    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))

//----------------------------------------------------------------------

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)
	    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 ;
	    }
	 }
      value |= prev_value ;
      if (*s == '+')
	 {
	 *s++ ;
	 int negative = 0 ;
	 if (*s == '-')
	    negative = 1 ;
	 int 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_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++ ;
      while (value > 0)
	 {
	 const char *next = strchr(e,'\n') ;
	 if (!next)
	    return end ;
	 e = next+1 ;
	 value-- ;
	 }
      // OK, we have the desired string, so output it
      while (*e && *e != '\n')
	 {
	 fputc(*e++,out) ;
	 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 'y':
	       format_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 int read_DWORD_register(int bus, int device, int func, int reg,
			       WORD *lo, WORD *hi)
{
   union REGS regs, outregs ;
   regs.x.ax = 0xB109 ;
   regs.h.bh = bus ;
   regs.h.bl = (device<<3) | (func & 0x07) ;
   regs.x.di = reg ;
   int86(0x1A,&regs,&outregs) ;
   if (outregs.x.cflag != 0)
      return FALSE ;
   *lo = outregs.x.cx ;
   regs.x.di += 2 ;
   int86(0x1A,&regs,&outregs) ;
   if (outregs.x.cflag != 0)
      return FALSE ;
   *hi = outregs.x.cx ;
   return TRUE ;
}

//----------------------------------------------------------------------

static void write_DWORD_register(int bus,int device,int func,
				 int reg, WORD lo, WORD hi)
{
   union REGS regs, outregs ;
   
   regs.x.ax = 0xB10C ;			// write configuration word
   regs.x.di = 0x30 ;
   regs.h.bh = bus ;
   regs.h.bl = (device<<3) | (func & 0x07) ;
   regs.x.di = reg ;
   regs.x.cx = lo ;
   int86(0x1A,&regs,&outregs) ;
   regs.x.di += 2 ;
   regs.x.cx = hi ;
   int86(0x1A,&regs,&outregs) ;
}

//----------------------------------------------------------------------

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
   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 &= 0xFFF8 ;		// mask out low three 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) ;
}

//----------------------------------------------------------------------

static void dump_capabilities_list(int start_offset, const char *cfgdata)
{
   if (!start_offset)
      return ;
   printf("Capabilities List:\n") ;
   do {
      int next = cfgdata[start_offset] ;
      int ID = cfgdata[start_offset+1] ;
      printf("\tID = %2.02X ",ID) ;
      switch (ID)
	 {
	 case 0x01:
	    printf("PCI Power Management\n") ;
	    dump_PCI_PM_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 ;
}

//----------------------------------------------------------------------

#define setp setprecision

static int dump_PCI_config(int bus, int device, int func, int report_missing)
{
   int i ;
   PCIcfg *cfg = read_PCI_config(bus,device,func) ;
   if (!cfg || cfg->vendorID == 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") ;
      }
   printf("' (%s-func)\n", (cfg->header_type & 0x80) ? "multi" : "single") ;
   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)) ;
   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);
   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) ;
   if (terse)
      return (cfg->header_type & 0x80) != 0 ;
   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:	 %2.02X\tSecondaryBus:	 %2.02X\tSubordinBus:	%2.02X\n",
		cfg->bridge.primary_bus,cfg->bridge.secondary_bus,
		cfg->bridge.subordinate_bus) ;
	 dump_base_addresses(bus,device,func,
			     cfg->bridge.base_address0,
			     cfg->bridge.base_address1,0,0,0,0) ;
	 printf("IObase low:	 %2.02X\tIOlimit low:	 %2.02X\n",
		cfg->bridge.IO_base_low,cfg->bridge.IO_limit_low) ;
	 printf("...more...\n") ;
	 //!!!
	 printf("INTline:  %2.02X\tINTpin:  %2.02X\tBridgeCntrl: %4.04X\n",
		cfg->bridge.interrupt_line,cfg->bridge.interrupt_pin,
		cfg->bridge.bridge_control) ;
	 printf("Expansion ROM: %8.08lX ",cfg->bridge.expansion_ROM) ;
	 determine_ROM_size(bus,device,func,0x38) ;
	 printf("\n") ;
	 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 (int 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!\n") ;
      }
   if (verbose)
      dump_device_specific_data(cfg->vendorID,cfg->deviceID,(char*)cfg) ;
   if (!report_missing)
      putchar('\n') ;
   return (cfg->header_type & 0x80) != 0 ;
}

//----------------------------------------------------------------------

static int ignore_line(const char *line)
{
   while (*line && isspace(*line))
      line++ ;
   if (*line == '\0' || *line == '\n' || *line == ';')
      return TRUE ;
   else
      return FALSE ;
}

//----------------------------------------------------------------------

static WORD hextoint(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 int read_nonblank_line(char *buf, int size, FILE *fp)
{
   do {
      buf[0] = '\0' ;
      if (!fgets(buf,size,fp) || feof(fp))
	 return FALSE ;
      } while (ignore_line(buf)) ;
   return TRUE ;
}

//----------------------------------------------------------------------

static int read_device_ID(FILE *fp, char *&ID_data, int first)
{
   static char line[250] ;
   if (first)
      {
      if (!read_nonblank_line(line,sizeof(line),fp))
	 return FALSE ;
      }
   char *l = line ;
   while (*l && isspace(*l))
      l++ ;
   if (strncmp(l,"Vendor",6) == 0)
      {
      *((char**)ID_data)++ = 0 ;  // pointer to next vendor ID
      l += 6 ;
      while (*l && isspace(*l))		// skip to vendor ID
	 l++ ;
      WORD ID = hextoint(l) ;		// get the vendor's ID
      *((WORD*)ID_data)++ = ID ;
      if (ID == 0xFFFF)			// check for marker
	 return FALSE ;			// end of list
      while (*l && isspace(*l))		// skip to vendor name
	 l++ ;
      while (*l && *l != '\n')		// copy the vendor name
	 *ID_data++ = *l++ ;
      *ID_data++ = '\0' ;		// ensure termination
      do {
	 if (!read_nonblank_line(line,sizeof(line),fp))
	    return FALSE ;
	 l = line ;
	 while (*l && isspace(*l))
	    l++ ;
	 if (isxdigit(*l))
	    {
	    ID = hextoint(l) ;		// convert device ID
	    if (ID != 0xFFFF)
	       {
	       *((WORD*)ID_data)++ = ID ;
	       while (*l && isspace(*l)) // skip to device name
		  l++ ;
	       // copy the device name
	       while (*l && *l != '\n')	// copy the vendor name
		  *ID_data++ = *l++ ;
	       *ID_data++ = '\0' ;	// ensure termination
	       }
	    }
	 } while (strncmp(l,"Vendor",6) != 0) ;
      return TRUE ;
      }
   else
      return FALSE ;
}

//----------------------------------------------------------------------

static int load_device_IDs()
{
   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") ;
   free(datafile) ;
   if (fp)
      {
      char signature[SIGNATURE_LENGTH] ;
      fread(signature,sizeof(char),sizeof(signature),fp) ;
      if (strncmp(signature,SIGNATURE,SIGNATURE_LENGTH) != 0)
	 {
	 fclose(fp) ;
	 fputs("Invalid PCICFG.DAT\n",stderr) ;
	 return FALSE ;
	 }
      fseek(fp,0L,SEEK_END) ;
      long filesize = ftell(fp) ;
      if (filesize > UINT_MAX)
	 {
	 fclose(fp) ;
	 fputs("PCICFG.DAT too large ( >64K )\n",stderr) ;
	 return FALSE ;
	 }
      fseek(fp,(long)SIGNATURE_LENGTH,SEEK_SET) ;
      device_ID_data = (char*)malloc((int)filesize-SIGNATURE_LENGTH) ;
      if (device_ID_data)
	 {
	 char *ID_data = device_ID_data ;
	 char *prev_ID_data = 0 ;
	 int first = TRUE ;
	 while (!feof(fp))
	    {
	    if (prev_ID_data)
	       *((char**)prev_ID_data) = ID_data ;
	    prev_ID_data = ID_data ;
	    if (!read_device_ID(fp,ID_data,first))
	       break ;
	    first = FALSE ;
	    }
	 ID_data = (char*)realloc(device_ID_data,
				  ID_data-device_ID_data) ;
	 if (ID_data)
	    device_ID_data = ID_data ;
	 return TRUE ;
	 }
      else
	 fprintf(stderr,"Insufficient memory for PCICFG.DAT contents\n") ;
      }
   return FALSE ;   
}

//----------------------------------------------------------------------

int main(int argc, char **argv)
{
   fprintf(stderr,"PCICFG v" VERSION " (c) Copyright 1997 Ralf Brown\n") ;
   get_exe_directory(argv[0]) ;
   int maxbus = check_PCI_BIOS() ;
   if (maxbus < 0)
      {
      fprintf(stderr,"\nNo PCI BIOS detected\n") ;
      return 2 ;
      }
   while (argc > 1 && argv[1][0] == '-')
      {
      switch (argv[1][1])
	 {
	 case 't':
	    terse = TRUE ;
	    break ;
	 case 'v':
	    verbose = TRUE ;
	    break ;
	 default:
	    fprintf(stderr,"unrecognized option '%s'\n",argv[1]) ;
	    break ;
	 }
      argv++ ;
      argc-- ;
      }
   if (!load_device_IDs())
      {
      fprintf(stderr,"Unable to load the list of vendor and device IDs (PCICFG.DAT).\n");
      fprintf(stderr,"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++)
	    for (int func = 0 ; func < 8 ; func++)
	       {
	       if (!dump_PCI_config(bus,device,func,0))
		  break ;
	       }
      return 0 ;
      }
   if (argc < 4)
      {
      fprintf(stderr,"\nUsage:\tPCICFG [flag(s)] bus device func\n"
		     "\tPCICFG [flag(s)] *   (to scan all devices)\n"
	             "\n"
	             "Dumps info about the specified PCI device, or all devices\n"
	             "Options:\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\n"
	             "recognized by PCICFG.  Output is generally quite lengthy\n"
	             "even without -v, so you should redirect output into a\n"
	             "file or pipe it to MORE or LIST\n") ;
      return 1 ;
      }
   int bus = atoi(argv[1]) ;
   int device = atoi(argv[2]) ;
   int func = atoi(argv[3]) ;
   if (bus > maxbus)
      {
      fprintf(stderr,"\nRequested PCI bus does not exist\n") ;
      return 3 ;
      }
   else
      dump_PCI_config(bus,device,func,1) ;
   return 0 ;
}

// end of file pcicfg.cpp //
[ RETURN TO DIRECTORY ]