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

    if (sys_pci_services)
        res = PCIBIOSReadU32(bus, dev, fun, rg);
    else
    {
        addr =  bus << 16   |
                dev << 11   |
                fun << 8    |
                rg & 0xFC   |
                0x80000000;

        offset = rg - rg & 0xFC;
        OutU32(PCI_ADDR, addr);
        res = InU32(PCI_DATA) >> (offset * 8);
    }

    return res;
}

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

    if (sys_pci_services)
        res = PCIBIOSReadU8(bus, dev, fun, rg);
    else
    {
        res = PCIReadU32(bus, dev, fun, rg) & 0xFF;
    }

    return res;
}

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

    if (sys_pci_services)
        res = PCIBIOSReadU16(bus, dev, fun, rg);
    else
    {
        res = PCIReadU32(bus, dev, fun, rg) & 0xFFFF;
    }

    return res;
}

U0 PCIWriteU32(I64 bus, I64 dev, I64 fun, I64 rg, I64 val)
{//Write U32 in PCI configspace at bus, dev, fun, reg.
    I64 addr, offset;

    if (sys_pci_services)
        PCIBIOSWriteU32(bus, dev, fun, rg, val);
    else
    {
        addr =  bus << 16   |
                dev << 11   |
                fun << 8    |
                rg & 0xFC   |
                0x80000000;

        offset = rg - rg & 0xFC;
        OutU32(PCI_ADDR, addr);
        OutU32(PCI_DATA, val << (offset * 8));
    }
}


U0 PCIWriteU8(I64 bus, I64 dev, I64 fun, I64 rg, I64 val)
{//Write U8 in PCI configspace at bus, dev, fun, reg.
    if (sys_pci_services)
        PCIBIOSWriteU8(bus, dev, fun, rg, val);
    else
    {
        PCIWriteU32(bus, dev, fun, rg, val & 0xFF);
    }
}

U0 PCIWriteU16(I64 bus, I64 dev, I64 fun, I64 rg, I64 val)
{//Write U16 in PCI configspace at bus, dev, fun, reg.
    if (sys_pci_services)
        PCIBIOSWriteU16(bus, dev, fun, rg, val);
    else
    {
        PCIWriteU32(bus, dev, fun, rg, val & 0xFFFF);
    }
}

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 = -1, cur = 0, b, d, f;


    if (sys_pci_services)
    {
        "(System has PCIBIOS)\n";
        res = PCIBIOSClassFind(class_code, n);
    }
    else
    {
        "(System does not have PCIBIOS)\n";
        for (b = 0; b < sys_pci_buses; b++)
            for (d = 0; d < 32; d++)
                for (f = 0; f < 8; f++)
                {
                    if (class_code == PCIReadU32(b, d, f, PCIR_PROG_IF) & 0xFFFFFF)
                    {
                        if (n == cur++)
                        {
                            res = b << 16 | d << 8 | f;
                            goto pci_end;
                        }
                    }
                }
    }
pci_end:
    return res;
}

Bool PCIBt(U8 reg RBX *bit_field, I64 reg RDX bit)
{ // MOV-based Bt for use in PCI device memory-mapped IO areas.  See Bt().
    bit_field += bit / 8;
    bit &= 7;
    return (*bit_field & 1 << bit) >> bit;
}

Bool PCIBtr(U8 reg RDX *bit_field, I64 reg RBX bit)
{ // MOV-based Btr for use in PCI device memory-mapped IO areas.  See Btr().
    U64 reg R9 chunk_mod = bit & 31, chunk_bit = 1 << chunk_mod;
    bit_field(U32 *) += bit / 32;
    Bool reg R8 result = (*bit_field(U32 *) & chunk_bit) >> chunk_mod;

    *bit_field(U32 *) &= ~chunk_bit;

    return result;
}

Bool PCIBts(U8 reg RDX *bit_field, I64 reg RBX bit)
{ // MOV-based Bts for use in PCI device memory-mapped IO areas.  See Bts().
    U64 reg R9 chunk_mod = bit & 31, chunk_bit = 1 << chunk_mod;
    bit_field(U32 *) += bit / 32;
    Bool reg R8 result = (*bit_field(U32 *) & chunk_bit) >> chunk_mod;

    *bit_field(U32 *) |= chunk_bit;

    return result;
}