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 PCIBIOSReadU8(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 PCIBIOSReadU16(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 PCIBIOSReadU32(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 PCIBIOSWriteU8(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 PCIBIOSWriteU16(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 PCIBIOSWriteU32(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 PCIBIOSClassFind(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;
}