/* ----------------------------------------------------------------------- *
 *
 *   Copyright 2009 Shao Miller - All Rights Reserved
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, Inc., 53 Temple Place Ste 330,
 *   Boston MA 02111-1307, USA; either version 2 of the License, or
 *   (at your option) any later version; incorporated herein by reference.
 *
 * ----------------------------------------------------------------------- */

/*
 * dskprobe.c
 *
 * Routines for probing BIOS disk drives
 */

/*
 * Uncomment for debugging
 *
 * #define DBG_DSKPROBE 1
 */

#include <stdint.h>
#include "memdisk.h"
#include "bda.h"
#include "conio.h"

/*
 * We will probe a BIOS drive numer using INT 13h, AH=probe
 * and will pass along that call's success or failure
 */
int probe_int13_ah(uint8_t drive, uint8_t probe)
{
    int err;
    com32sys_t regs;

    memset(&regs, 0, sizeof regs);

    regs.eax.b[1] = probe;	/* AH = probe                 */
    regs.edx.b[0] = drive;	/* DL = drive number to probe */
    intcall(0x13, &regs, &regs);

    err = !(regs.eflags.l & 1);
#ifdef DBG_DSKPROBE
    printf("probe_int13_ah(0x%02x, 0x%02x) == %d\n", drive, probe, err);
#endif
    return err;
}

/*
 * We will probe the BIOS Data Area and count the drives found there.
 * This heuristic then assumes that all drives of 'drive's type are
 * found in a contiguous range, and returns 1 if the probed drive
 * is less than or equal to the BDA count.
 * This particular function's code is derived from code in setup.c by
 * H. Peter Anvin.  Please respect that file's copyright for this function
 */
int probe_bda_drive(uint8_t drive)
{
    int bios_drives;
    int err;

    if (drive & 0x80) {
	bios_drives = rdz_8(BIOS_HD_COUNT);	/* HDD count */
    } else {
	uint8_t equip = rdz_8(BIOS_EQUIP);
	if (equip & 1)
	    bios_drives = (equip >> 6) + 1;	/* Floppy count */
	else
	    bios_drives = 0;
    }
    err = (drive - (drive & 0x80)) >= bios_drives ? 0 : 1;
#ifdef DBG_DSKPROBE
    printf("probe_bda_drive(0x%02x) == %d, count: %d\n",
	   drive, err, bios_drives);
#endif
    return err;
}

/*
 * We will probe a drive with a few different methods, returning
 * the count of succesful probes
 */
int probe_drive(uint8_t drive)
{
    int c = 0;
    /* Only probe the BDA for floppies */
    if (drive & 0x80) {
	c += probe_int13_ah(drive, 0x08);
	c += probe_int13_ah(drive, 0x15);
	c += probe_int13_ah(drive, 0x41);
    }
    c += probe_bda_drive(drive);
    return c;
}

/*
 * We will probe a contiguous range of BIOS drive, starting with drive
 * number 'start'.  We probe with a few different methods, and return
 * the first drive which doesn't respond to any of the probes.
 */
uint8_t probe_drive_range(uint8_t start)
{
    uint8_t drive = start;
    while (probe_drive(drive)) {
	drive++;
	/* Check for passing the floppy/HDD boundary */
	if ((drive & 0x7F) == 0)
	    break;
    }
    return drive;
}
