asm {
USE32

SYS_PCIBIOS_SERVICE_DIR::	DU32	0;
SYS_PCI_SERVICES::			DU32 	0;

SYS_FIND_PCIBIOS_SERVICE_DIR::
				MOV 		ESI, 0xE0000
				MOV 		ECX, (0x100000 - 0xE0000) / 4
@@05:			CMP 		U32 [ESI], '_32_'
				JNE 		@@20
				PUSH		ECX
				XOR 		ECX, ECX
				MOV 		CL,  U8 9[ESI]
				SHL 		ECX, 4
@@10:			MOV 		EDI, ESI
				XOR 		EAX, EAX
				XOR 		EDX, EDX
@@15:			MOV 		DL,  U8 [EDI]
				ADD 		EAX, EDX
				INC 		EDI
				DEC 		ECX
				JNZ 		@@15
				POP 		ECX
				TEST		AL,  AL
				JNZ 		@@20
				MOV 		U32 [SYS_PCIBIOS_SERVICE_DIR],  ESI
				MOV 		ESI, U32 4[ESI]
				MOV 		U32 [SYS_PCIBIOS_SERVICE_CALL], ESI
				RET

@@20:			ADD 		ESI, 4
				LOOP		@@05
				MOV 		U32 [SYS_PCIBIOS_SERVICE_DIR], 0
				RET

SYS_FIND_PCI_SERVICES::
				MOV 		ESI, U32 [SYS_PCIBIOS_SERVICE_DIR]
				TEST		ESI, ESI
				JNZ 		@@05
				MOV 		U32 [SYS_PCI_SERVICES], 0
				RET
@@05:			MOV 		EAX, '$$PCI'
				XOR 		EBX, EBX
				DU8 		0x9A; 					 //CALL CGDT.cs32:PCIBIOS_SERVICE
SYS_PCIBIOS_SERVICE_CALL::	DU32	0;
				DU16		CGDT.cs32;
				TEST		AL, AL
				JNZ 		@@05
				LEA 		ESI, U32 [EBX + EDX]
				MOV 		U32 [SYS_PCI_SERVICES], ESI
				RET

@@05:			MOV 		U32 [SYS_PCI_SERVICES], 0
				RET

USE64
C32_EAX:: 			DU32		0;
C32_EBX:: 			DU32		0;
C32_ECX:: 			DU32		0;
C32_EDX:: 			DU32		0;
C32_ESI:: 			DU32		0;
C32_EDI:: 			DU32		0;
C32_EFLAGS::		DU32		0;

C32_RSP:: 			DU64		0;

_FAR_CALL32::
//This calls a 32-bit mode routine.
//(We must switch from 64-bit mode to do it.)
//
//NON REENTRANT
//
				PUSH		RBP
				MOV 		RBP, RSP
				MOV 		RAX, U64 SF_ARG1[RBP]
				TEST		RAX, RAX
				JNZ 		@@05
				POP 		RBP
				RET1		8 			//return FALSE
@@05:			MOV 		U32 [C32_ADD], EAX
				PUSH_REGS
				PUSHFD
				XOR 		RAX, RAX
				PUSH		U64 FS:CTask.addr[RAX]
				PUSH		U64 GS:CCPU.addr[RAX]
				MOV 		U64 [C32_RSP], RSP
				PUSH		U32 CGDT.ds 				//STACKSEG
				PUSH		U32 BOOT_RAM_LIMIT			//STACK
				PUSH		U32 0 						//FLAGS--interrupts off
				PUSH		U32 CGDT.cs32
				LEA 		RAX, [@@15]
				PUSH		RAX
				IRET
USE32
@@15:
				WBINVD
//disable paging
				MOV_EAX_CR0
				BTR 		EAX, CR0f_PG
				MOV_CR0_EAX

				MOV 		ECX, IA32_EFER
				XOR 		EDX, EDX
				XOR 		EAX, EAX
				WRMSR

				MOV 		AX, CGDT.ds
				MOV 		FS, AX
				MOV 		GS, AX
//SS already set

				MOV 		EAX, U32 [C32_EAX]
				MOV 		EBX, U32 [C32_EBX]
				MOV 		ECX, U32 [C32_ECX]
				MOV 		EDX, U32 [C32_EDX]
				MOV 		ESI, U32 [C32_ESI]
				MOV 		EDI, U32 [C32_EDI]
				MOV 		U32 [C32_EFLAGS], 0

				DU8 		0x9A; 					 //CALL CGDT.cs32:[C32_ADD]
C32_ADD::	DU32	0;
				DU16		CGDT.cs32;

				PUSHFD
				POP 		U32 [C32_EFLAGS]

				MOV 		U32 [C32_EAX], EAX
				MOV 		U32 [C32_EBX], EBX
				MOV 		U32 [C32_ECX], ECX
				MOV 		U32 [C32_EDX], EDX
				MOV 		U32 [C32_ESI], ESI
				MOV 		U32 [C32_EDI], EDI

				PUSH		U32 0 	//Return from next call will be 64-bit
				CALL		SYS_ENTER_LONG_MODE

USE64			MOV 		RSP, U64 [C32_RSP]
				POP 		RAX
				CALL		SET_GS_BASE
				POP 		RAX
				CALL		SET_FS_BASE

				POPFD
				POP_REGS
				XOR 		RAX, RAX
				MOV 		AL, TRUE
				POP 		RBP
				RET1		8
}

_extern C32_EAX		U32 c32_eax;
_extern C32_EBX		U32 c32_ebx;
_extern C32_ECX		U32 c32_ecx;
_extern C32_EDX		U32 c32_edx;
_extern C32_ESI		U32 c32_esi;
_extern C32_EDI		U32 c32_edi;
_extern C32_EFLAGS	U32 c32_eflags;

_extern SYS_PCI_SERVICES U32 sys_pci_services;

_extern _FAR_CALL32 Bool FarCall32(U0 (*fp_addr)());//Not reentrant.For PCIBIOS.

U8 PCIReadU8(I64 bus, I64 dev, I64 fun, I64 rg)
{//Read U8 in PCI configspace at bus, dev, fun, reg.
	I64 res;

	PUSHFD
	CLI
	while (LBts(&sys_semas[SEMA_FAR_CALL32], 0))
		Yield;
	c32_eax = 0xB108;
	c32_ebx = bus << 8 + dev << 3 + fun;
	c32_edi = rg;
	if (FarCall32(sys_pci_services))
		res = c32_ecx.u8[0];
	else
		res = 0xFF;
	LBtr(&sys_semas[SEMA_FAR_CALL32], 0);
	POPFD

	return res;
}

U16 PCIReadU16(I64 bus, I64 dev, I64 fun, I64 rg)
{//Read U16 in PCI configspace at bus, dev, fun, reg.
	I64 res;

	PUSHFD
	CLI
	while (LBts(&sys_semas[SEMA_FAR_CALL32], 0))
		Yield;
	c32_eax = 0xB109;
	c32_ebx = bus << 8 + dev << 3 + fun;
	c32_edi = rg;
	if (FarCall32(sys_pci_services))
		res = c32_ecx.u16[0];
	else
		res = 0xFFFF;
	LBtr(&sys_semas[SEMA_FAR_CALL32], 0);
	POPFD

	return res;
}

U32 PCIReadU32(I64 bus, I64 dev, I64 fun, I64 rg)
{//Read U32 in PCI configspace at bus, dev, fun, reg.
	I64 res;

	PUSHFD
	CLI
	while (LBts(&sys_semas[SEMA_FAR_CALL32], 0))
		Yield;
	c32_eax = 0xB10A;
	c32_ebx = bus << 8 + dev << 3 + fun;
	c32_edi = rg;
	if (FarCall32(sys_pci_services))
		res = c32_ecx;
	else
		res = 0xFFFFFFFF;
	LBtr(&sys_semas[SEMA_FAR_CALL32], 0);
	POPFD

	return res;
}

U0 PCIWriteU8(I64 bus, I64 dev, I64 fun, I64 rg, I64 val)
{//Write U8 in PCI configspace at bus, dev, fun, reg.
	PUSHFD
	CLI
	while (LBts(&sys_semas[SEMA_FAR_CALL32], 0))
		Yield;
	c32_eax = 0xB10B;
	c32_ebx = bus << 8 + dev << 3 + fun;
	c32_edi = rg;
	c32_ecx = val;
	FarCall32(sys_pci_services);
	LBtr(&sys_semas[SEMA_FAR_CALL32], 0);
	POPFD
}

U0 PCIWriteU16(I64 bus, I64 dev, I64 fun, I64 rg, I64 val)
{//Write U16 in PCI configspace at bus, dev, fun, reg.
	PUSHFD
	CLI
	while (LBts(&sys_semas[SEMA_FAR_CALL32], 0))
		Yield;
	c32_eax = 0xB10C;
	c32_ebx = bus << 8 + dev << 3 + fun;
	c32_edi = rg;
	c32_ecx = val;
	FarCall32(sys_pci_services);
	LBtr(&sys_semas[SEMA_FAR_CALL32], 0);
	POPFD
}

U0 PCIWriteU32(I64 bus, I64 dev, I64 fun, I64 rg, I64 val)
{//Write U32 in PCI configspace at bus, dev, fun, reg.
	PUSHFD
	CLI
	while (LBts(&sys_semas[SEMA_FAR_CALL32], 0))
		Yield;
	c32_eax = 0xB10D;
	c32_ebx = bus << 8 + dev << 3 + fun;
	c32_edi = rg;
	c32_ecx = val;
	FarCall32(sys_pci_services);
	LBtr(&sys_semas[SEMA_FAR_CALL32], 0);
	POPFD
}

I64 PCIClassFind(I64 class_code, I64 n)
{/*Find bus, dev, fun of Nth class_code dev.

class_code is low three bytes
n is index starting at zero
Return: -1 not found
else bus, dev, fun.
*/
	I64 res;

	PUSHFD
	CLI
	while (LBts(&sys_semas[SEMA_FAR_CALL32], 0))
		Yield;
	c32_eax = 0xB103;
	c32_esi = n;
	c32_ecx = class_code;
	if (FarCall32(sys_pci_services) && !c32_eax.u8[1])
		res = c32_ebx.u8[1] << 16 + (c32_ebx & 0xF8) << 5 + c32_ebx & 7;
	else
		res = -1;
	LBtr(&sys_semas[SEMA_FAR_CALL32], 0);
	POPFD

	return res;
}

