#help_index "PCI;Processor;Devices;Info"

//The file was downloaded from
//http://www.pcidatabase.com/reports.php?type=tab-delimeted

#define PCI_DEV_FILE "::/Misc/PCIDevices.DD"

/****
//1) Download http://www.pcidatabase.com/reports.php?type=tab-delimeted
//2) Rename to ::/Misc/PCIDevices.DD
//3) ToDolDoc("::/Misc/PCIDevices.DD");
//4) Edit and remove file header and tail
//5) Text find-and-replace "=0A=" with "". (Doesn't seem necessary anmore.)
//6) Run PCIDevFileGen(). (Doesn't seem necessary anmore.)

public U0 PCIDevFileGen()
{
	Bool first=TRUE,del=FALSE,del2=FALSE,cont=FALSE;
	CDoc *doc=DocRead(PCI_DEV_FILE,
				DOCF_PLAIN_TEXT|DOCF_DBL_DOLLARS|DOCF_NO_CURSOR);
	CDocEntry *doc_e=doc->head.next,*doc_e2;
	while (doc_e!=doc) {
		doc_e2=doc_e->next;
		if (first) {
			if (doc_e->type_u8==DOCT_TEXT) {
				if (doc_e->tag[0]==';')
					del=TRUE;
			}
			first=FALSE;
		}
		if (doc_e->type_u8==DOCT_TEXT && doc_e->tag[StrLen(doc_e->tag)-1]=='=' &&
					doc_e2->type_u8==DOCT_NEW_LINE) {
			doc_e->tag[StrLen(doc_e->tag)-1]=CH_SPACE;
			cont=TRUE;
		}
		del2=del;
		if (doc_e->type_u8==DOCT_NEW_LINE) {
			first=TRUE;
			del2=FALSE;
			if (cont) {
				del=TRUE;
				cont=FALSE;
			}
		}
		if (del)
			DocEntryDel(doc,doc_e);
		del=del2;
		doc_e=doc_e2;
	}
	DocWrite(doc);
}
****/

//$LK,"::/Misc/PCIDevices.DD",A="PI:::/Misc/PCIDevices.DD"$
U0 PCILookUpSingle(CDoc *doc, I64 m, I64 d, U8 **_vendor, U8 **_dev)
{
	Bool		 first = TRUE;
	U8			 buf[8], *vendor = NULL, *dev = NULL;
	CDocEntry	*doc_e = doc->head.next;

	while (doc_e != doc)
	{
		if (first)
		{
			if (doc_e->type_u8 == DOCT_TEXT && doc_e->tag[0] != ';' && StrLen(doc_e->tag) >= 4)
			{
				buf[0](U16) = '0x';
				buf[2](U32) = doc_e->tag(U32 *)[0];
				buf[6] = '\0';
				if (Str2I64(buf) == m)
				{
					doc_e = doc_e->next->next->next;
					if (doc_e->type_u8 == DOCT_TEXT)
					{
						vendor = SysStrNew(doc_e->tag);
						first = FALSE;
						break;
					}
				}
			}
			first=FALSE;
		}
		if (doc_e->type_u8 == DOCT_NEW_LINE)
			first = TRUE;
		doc_e = doc_e->next;
	}

	if (vendor)
	{
		while (doc_e != doc)
		{
			if (first)
			{
				if (doc_e->type_u8 == DOCT_TAB)
				{
					doc_e = doc_e->next->next->next->next;
					if (doc_e->type_u8 == DOCT_TEXT && StrLen(doc_e->tag) >= 4)
					{
						buf[0](U16) = '0x';
						buf[2](U32) = doc_e->tag(U32 *)[0];
						buf[6] = '\0';
						if (Str2I64(buf) == d)
						{
							doc_e = doc_e->next->next->next;
							if (doc_e->type_u8 == DOCT_TEXT)
							{
								dev = SysStrNew(doc_e->tag);
								break;
							}
						}
					}
				}
				else
					break;
				first = FALSE;
			}
			if (doc_e->type_u8 == DOCT_NEW_LINE)
				first = TRUE;
			doc_e = doc_e->next;
		}
	}

	if (vendor)
		*_vendor = vendor;
	else
		*_vendor = SysStrNew("Unknown");

	if (dev)
		*_dev = dev;
	else
		*_dev = SysStrNew("Unknown");
}

U0 PCILookUpDevs()
{
	CPCIDev	*tmppci;
	I64		 w1, w2, b, d, f, timeout = 32 * 8 * 2;
	CDoc	*doc;

	if (dev.pci_head.next != &dev.pci_head)
		return;
	doc = DocRead(PCI_DEV_FILE, DOCF_PLAIN_TEXT | DOCF_NO_CURSOR);
	for (b = 0; b < sys_pci_buses; b++)
		for (d = 0; d < 32; d++)
			for (f = 0; f < 8; f++)
			{
				w1 = PCIReadU16(b, d, f, PCIR_VENDOR_ID);
				Yield; // keep from hanging graphics on bare-metal
				if (w1 != 0xFFFF)
				{
					tmppci = SysCAlloc(sizeof(CPCIDev));
					tmppci->bus = b;
					tmppci->dev = d;
					tmppci->fun = f;
					tmppci->vendor_id = w1;
					tmppci->device_id = w2 = PCIReadU16(b, d, f, PCIR_DEVICE_ID);
					tmppci->sub_code =		PCIReadU8(b, d, f, PCIR_SUB_CODE);
					tmppci->class_code =	PCIReadU8(b, d, f, PCIR_CLASS_CODE);
					tmppci->prog_if = 		PCIReadU8(b, d, f, PCIR_PROG_IF);
					tmppci->revision_id = PCIReadU8(b, d, f, PCIR_REVISION_ID);
					tmppci->bist =				PCIReadU8(b, d, f, PCIR_BIST);
					tmppci->header_type = PCIReadU8(b, d, f, PCIR_HEADER_TYPE);
					tmppci->latency_timer=PCIReadU8(b, d, f, PCIR_LATENCY_TIMER);
					tmppci->capabilities= PCIReadU8(b, d, f, PCIR_CAPABILITIES);
					tmppci->cache_line_size=PCIReadU8(b, d, f, PCIR_CACHE_LINE_SIZE);
					tmppci->subsys_id = 	PCIReadU16(b, d, f, PCIR_SUBSYS_ID);
					tmppci->subsys_vendor_id=PCIReadU16(b, d, f, PCIR_SUBSYS_VENDOR_ID);
					tmppci->erom =				PCIReadU32(b, d, f, PCIR_EXPANSION_ROM);
					tmppci->base[0] = 		PCIReadU32(b, d, f, PCIR_BASE0);
					tmppci->base[1] = 		PCIReadU32(b, d, f, PCIR_BASE1);
					tmppci->base[2] = 		PCIReadU32(b, d, f, PCIR_BASE2);
					tmppci->base[3] = 		PCIReadU32(b, d, f, PCIR_BASE3);
					tmppci->base[4] = 		PCIReadU32(b, d, f, PCIR_BASE4);
					tmppci->base[5] = 		PCIReadU32(b, d, f, PCIR_BASE5);
					PCILookUpSingle(doc, w1, w2, &tmppci->vendor_str, &tmppci->dev_id_str);
					QueueInsert(tmppci, dev.pci_head.last);
					timeout = 32 * 8 * 2;
				}
				else if (sys_pci_buses == 256 && --timeout <= 0)
					goto lud_done;
			}
lud_done:
	DocDel(doc);
}

public CPCIDev *PCIDevFind(U16 class_code=NULL, U16 sub_code=NULL,
												U16 vendor_id=NULL, U16 device_id=NULL,
												U8 _bus=0xFF, U8 _dev=0xFF, U8 _fun=0xFF)
{//return first device with matching class & subcode, vendor & device id, or a specific device.
	PCILookUpDevs;
	CPCIDev *p = dev.pci_head.next;

	while (p != &dev.pci_head)
	{
		if (p->vendor_id == vendor_id && p->device_id == device_id ||
				p->class_code == class_code && p->sub_code == sub_code ||
				p->bus == _bus && p->dev == _dev && p->fun == _fun)

			return p;
 
		p = p->next;
	}
	return NULL;
}

public U0 PCIRep()
{//Report description of PCI devices.
	CPCIDev *tmppci;

	"PCI Buses:%d\n", sys_pci_buses;
	if (!FileFind(PCI_DEV_FILE))
	{
		"You don't have the PCI device file.\n";
		return;
	}
	PCILookUpDevs;
	tmppci = dev.pci_head.next;
	while (tmppci != &dev.pci_head)
	{
		"%02X:%02X:%01X %02X-%02X-%02X ",
					tmppci->bus, tmppci->dev, tmppci->fun,
					tmppci->class_code, tmppci->sub_code, tmppci->prog_if;
		if (!IsRaw)
			"$$GREEN$$";
		"%s ", tmppci->vendor_str;
		if (!IsRaw)
			"$$CYAN$$";
		"%s", tmppci->dev_id_str;
		if (!IsRaw)
			"$$FG$$";
		'\n';
					, 
		tmppci=tmppci->next;
	}
}

#help_index "Info;Memory/Info"
public U0 MemBIOSRep()
{//Report the memory ranges reported by the BIOS at boot.
	U16 		*m01 = MEM_E801;
	CMemE820	*m20 = MEM_E820;
	CMemRange	*tmpmr;

	if (!IsRaw)
		"$$PURPLE$$Standard Addresses$$FG$$\n";
	else
		"Standard Addresses\n";

	"FEE00000-FEE00FFF See $LK,"APIC",A="MN:LAPIC_BASE"$\n\n";

	if (!IsRaw)
		"$$PURPLE$$VBE Linear Framebuffer$$FG$$\n";
	else
		"VBE Linear Framebuffer\n";

	"%08X-%08X  See $LK,"VBE Mode",A="FF:::/Kernel/KernelA.HH,framebuffer"$\n", text.fb_alias, text.fb_alias(U8 *) + text.buffer_size - 1;

	if (!IsRaw)
		"\n$$PURPLE$$32 Bit Device Mem$$FG$$\n";
	else
		"\n32 Bit Device Mem\n";

	while (LBts(&sys_semas[SEMA_DEV_MEM], 0))
		Yield;
	tmpmr = dev.mem32_head.next;
	while (tmpmr != &dev.mem32_head)
	{
		"%z: %08X-%08X\n", tmpmr->type, "Unused\0RAM\0Device", tmpmr->base, tmpmr->base + tmpmr->size - 1;
		tmpmr = tmpmr->next;
	}
	LBtr(&sys_semas[SEMA_DEV_MEM], 0);

	if (!IsRaw)
		"\n$$PURPLE$$BIOS Memory Report 15:E801$$FG$$\n";
	else
		"\nBIOS Memory Report 15:E801\n";

	"0000000000000000-%016X\n", 0x100000 + m01[0] * 1024 - 1;
	"0000000001000000-%016X\n", SYS_16MEG_AREA_LIMIT + m01[1] * 64 * 1024  - 1;

	if (m20->type)
	{
		if (!IsRaw)
			"\n$$PURPLE$$BIOS Memory Report 15:E820$$FG$$\n";
		else
			"\nBIOS Memory Report 15:E820\n";
		while (m20->type)
		{
			if (!IsRaw)
				"$$RED$$";
			switch(m20->type)
			{
				case MEM_E820t_USABLE:
					if (!IsRaw)
						"$$GREEN$$";
					"Usable:      ";
					break;

				case MEM_E820t_ACPI:
				case MEM_E820t_ACPI_NVS:
					"ACPI:        ";
					 break;

				case MEM_E820t_BAD_MEM:
					"Bad memory:  ";
					 break;

				case MEM_E820t_PERM_MEM:
					"Perm memory: ";

				case MEM_E820t_RESERVED:
				default:
					"Reserved:    ";
					break;
			 }
			"%016X-%016X", m20->base, m20->base + m20->len - 1;
			if (!IsRaw)
				"$$FG$$";
			'\n';
			m20++;
		}
	}
	if (!IsRaw)
		"\n$$PURPLE$$BIOS Total Memory Report$$FG$$: ";
	else
		"\nBIOS Total Memory Report: ";

	if (MemBIOSTotal < 1024 * 1024 * 1024)
		"%03d MiB\n", MemBIOSTotal / 1024 / 1024; 		
	else
		"%04d GiB\n", CeilU64(MemBIOSTotal / 1024 / 1024, 1024) / 1024;
}

public U0 MemPageRep()
{//Page Table Report.
	"MAPPED\t  :%010X with ", mem_mapped_space;
	if (!IsRaw)
		"$$RED$$";
	if (Bt(&mem_page_size, 30))
		"1GIG";
	else
		"2MEG";
	if (!IsRaw)
		"$$FG$$";

	" pages\n";

	"PML2\t  :%010X 2MEG  :%08X\n",
				*MEM_PML2(U64 *), *MEM_2MEG_NUM(U64 *);
	"PML3\t  :%010X 1GIG  :%08X\n",
				*MEM_PML3(U64 *), *MEM_1GIG_NUM(U64 *);
	"PML4\t  :%010X 512GIG:%08X\n",
				*MEM_PML4(U64 *), *MEM_512GIG_NUM(U64 *);
	"FIXED_AREA:%010X\n", SYS_FIXED_AREA;
	"HEAP_BASE :%010X\nHEAP_LIMIT:%010X\n", mem_heap_base, mem_heap_limit;
}