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