U0 ATARepEntry(I64 base0, I64 base1, I64 unit, U8 *message, CATARep **_head, I64 *num_hints)
{
    I64      type;
    base0 &= -8;
    base1 &= -4;
    CATARep *tmpha;

    if (type = IDEATAProbe(base0,base1,unit))
    {
        *num_hints += 1;
        "\n$PURPLE$ $BT+X,\"%d\",LM=\"%d\\n\"$$FG$$LM,4$", *num_hints, *num_hints;
        if (type == BDT_ATA)
            "$RED$Hard Drive   $LTBLUE$ATA   ";
        else
            "$RED$CD/DVD Drive $LTBLUE$ATAPI ";
        "%s$FG$\n", message;
        if (base0 == blkdev.ins_base0 && unit == blkdev.ins_unit)
            "$PURPLE$(Drive originally installed from.)$FG$\n";
        "Base0:0x%04X Base1:0x%04X Unit:%d$LM,0$\n", base0, base1, unit;
        if (_head)
        {
            tmpha = CAlloc(sizeof(CATARep));
            tmpha->next = *_head;
            *_head = tmpha;
            tmpha->num = *num_hints;
            tmpha->type = type;
            tmpha->base0 = base0;
            tmpha->base1 = base1;
            tmpha->unit = unit;
        }
    }
}

Bool ATARepExitAllApplications()
{
    "\nWe're going to probe hardware.\n"
                "$RED$Exit all other applications.$FG$\n"
                "Press '$PURPLE$p$FG$' to probe or '$PURPLE$s$FG$' to skip.\n";
    if (ToUpper(CharGet(, FALSE)) == 'S')
        return TRUE;
    else
        return FALSE;
}

public I64 ATARep(Bool prompt=TRUE, Bool just_ide=FALSE, CATARep **_head=NULL)
{//Report possible ATA devices by probing.  Hard disks and CD/DVDs.
    I64 d1, d2, i, j, k, count = 0, unlock_flags = 0, num_hints = 0;

#assert BLKDEVS_NUM <= 64
    if (_head)
        *_head = NULL;

    if (prompt && ATARepExitAllApplications)
        return 0;

    for (i = 0; i < BLKDEVS_NUM; i++)
        if (blkdev.blkdevs[i].bd_signature == BD_SIGNATURE_VAL)
            BEqual(&unlock_flags, i, BlkDevLock(&blkdev.blkdevs[i]));

    if (!just_ide)
        for (k = 0; k < 256; k++)
        {
            i = -1;
            while (TRUE)
            {
                j = PCIClassFind(0x010100 + k, ++i);
                if (j < 0)
                    break;

                "\nSubcode:0x%X Bus:0x%X Dev:0x%X Fun:0x%X\n", k, j.u8[2], j.u8[1], j.u8[0];
                count++;

                d1 = PCIReadU32(j.u8[2], j.u8[1], j.u8[0], 0x10);
                d2 = PCIReadU32(j.u8[2], j.u8[1], j.u8[0], 0x14);
                if (d1 & 1 && d2 & 1)
                {
                    ATARepEntry(d1, d2, 0, "Primary IDE", _head, &num_hints);
                    ATARepEntry(d1, d2, 1, "Primary IDE", _head, &num_hints);
                }
                else
                {
                    d1=0x1F0; d2=0x3F6;
                    ATARepEntry(d1, d2, 0, "Primary IDE", _head, &num_hints);
                    ATARepEntry(d1, d2, 1, "Primary IDE", _head, &num_hints);
                }
                d1 = PCIReadU32(j.u8[2], j.u8[1], j.u8[0], 0x18);
                d2 = PCIReadU32(j.u8[2], j.u8[1], j.u8[0], 0x1C);
                if (d1&1 && d2&1)
                {
                    ATARepEntry(d1, d2, 0, "Secondary IDE", _head, &num_hints);
                    ATARepEntry(d1, d2, 1, "Secondary IDE", _head, &num_hints);
                }
                else
                {
                    d1 = 0x170;
                    d2 = 0x376;
                    ATARepEntry(d1, d2, 0, "Secondary IDE", _head, &num_hints);
                    ATARepEntry(d1, d2, 1, "Secondary IDE", _head, &num_hints);
                }
            }
        }
    if (!count)
    {
        d1 = 0x1F0;
        d2 = 0x3F6;
        ATARepEntry(d1, d2, 0, "Primary IDE", _head, &num_hints);
        ATARepEntry(d1, d2, 1, "Primary IDE", _head, &num_hints);

        d1 = 0x170;
        d2 = 0x376;
        ATARepEntry(d1, d2, 0, "Secondary IDE", _head, &num_hints);
        ATARepEntry(d1, d2, 1, "Secondary IDE", _head, &num_hints);
    }
    '\n\n';
    for (i = 0; i < BLKDEVS_NUM; i++)
        if (Bt(&unlock_flags, i))
            BlkDevUnlock(&blkdev.blkdevs[i]);
    return num_hints;
}

CATARep *ATARepFind(CATARep *haystack_head, I64 needle_num)
{
    while (haystack_head)
    {
        if (haystack_head->num == needle_num)
            return haystack_head;
        haystack_head = haystack_head->next;
    }
    return NULL;
}

CATARep *ATAIDDrives(CATARep *head, CATARep **_ata_drive, CATARep **_atapi_drive)
{//This is for when trying to sort-out main hard drives and CD/DVD drives.
    CATARep *res = NULL, *tmpha = head, *ata_drive = NULL, *atapi_drive = NULL;
    CBlkDev *bd;
    Bool     was_silent = Silent, ins_found = FALSE;

    bd = Letter2BlkDev(':', FALSE);
    Silent(was_silent);
    while (tmpha)
    {
        if (!res && bd && bd->type == tmpha->type)
        {
            if (bd->type == BDT_ATAPI && bd->base0 == tmpha->base0 && bd->unit == tmpha->unit)
                res = atapi_drive = tmpha;
            else if (bd->type == BDT_ATA &&
                    bd->base0 == tmpha->base0 &&
                    bd->base1 == tmpha->base1 &&
                    bd->unit == tmpha->unit)
                res = ata_drive=tmpha;
        }
        if (!res || res->type != tmpha->type)
        {
            if (tmpha->type == BDT_ATA)
            {
                if (!ata_drive || tmpha->unit<ata_drive->unit ||
                        tmpha->unit == ata_drive->unit &&
                        tmpha->num < ata_drive->num)
                    ata_drive = tmpha;
            }
            else if (tmpha->type == BDT_ATAPI)
            {
                if (!atapi_drive || !ins_found &&
                        (tmpha->unit < atapi_drive->unit ||
                        tmpha->unit == atapi_drive->unit && tmpha->num < atapi_drive->num))
                    atapi_drive = tmpha;
            }
        }
        if (tmpha->type == BDT_ATAPI && bd && bd->type == BDT_ATA &&
            tmpha->base0 == blkdev.ins_base0 && tmpha->unit == blkdev.ins_unit)
        {
            if (!ins_found)
            {
                atapi_drive = tmpha;
                ins_found = TRUE;
            }
        }
        tmpha = tmpha->next;
    }
    if (_ata_drive)
        *_ata_drive = ata_drive;
    if (_atapi_drive)
        *_atapi_drive = atapi_drive;

    return res;
}

CBlkDev *ATAMount(U8 first_drive_let, I64 type, I64 base0, I64 base1, I64 unit)
{
    CBlkDev *res;

    if (0 <= first_drive_let - 'A' < DRIVES_NUM && (type == BDT_ATA || type == BDT_ATAPI) && 0 <= unit <= 1)
    {
        res = BlkDevNextFreeSlot(first_drive_let, type);
        res->unit = unit;
        res->base0 = base0;
        res->base1 = base1;
        if (BlkDevAdd(res,, FALSE, FALSE))
            return res;
    }

    return NULL;
}

I64 MountIDEAuto()
{//Try to mount hard drive and CD/DVD, automatically. (Kernel.Config option).
//It uses 'C' and 'T' as first drive letters or whatever you set
//in config when compiling Kernel.BIN.
    I64      res  = 0;
    CATARep *head = NULL, *ata_drive = NULL, *atapi_drive = NULL, *tmpha;

    ATARep(FALSE, TRUE, &head);
    ATAIDDrives(head, &ata_drive, &atapi_drive);
    if (ata_drive && ATAMount(blkdev.first_hd_drive_let, BDT_ATA, ata_drive->base0, ata_drive->base1, ata_drive->unit))
        res++;
    if (atapi_drive && ATAMount(blkdev.first_dvd_drive_let, BDT_ATAPI,
            atapi_drive->base0, atapi_drive->base1, atapi_drive->unit))
        res++;
    tmpha = head;
    while (tmpha)
    {
        if (tmpha != ata_drive && tmpha != atapi_drive)
        {
            if (tmpha->type == BDT_ATA && ATAMount(blkdev.first_hd_drive_let, BDT_ATA,
                    tmpha->base0, tmpha->base1, tmpha->unit))
                res++;
            else if (tmpha->type == BDT_ATAPI && ATAMount(blkdev.first_dvd_drive_let, BDT_ATAPI,
                        tmpha->base0, tmpha->base1, tmpha->unit))
                res++;
        }
        tmpha = tmpha->next;
    }
    LinkedListDel(head);
    blkdev.mount_ide_auto_count = res;

    return res;
}