1/* ----------------------------------------------------------------------- *
2 *
3 *   Copyright 2009 Shao Miller - All Rights Reserved
4 *
5 *   This program is free software; you can redistribute it and/or modify
6 *   it under the terms of the GNU General Public License as published by
7 *   the Free Software Foundation, Inc., 53 Temple Place Ste 330,
8 *   Boston MA 02111-1307, USA; either version 2 of the License, or
9 *   (at your option) any later version; incorporated herein by reference.
10 *
11 * ----------------------------------------------------------------------- */
12
13/*
14 * dskprobe.c
15 *
16 * Routines for probing BIOS disk drives
17 */
18
19/* Change to 1 for debugging */
20#define DBG_DSKPROBE 0
21
22#include <stdint.h>
23#include "memdisk.h"
24#include "bda.h"
25#include "conio.h"
26
27/* Function type for printf() */
28typedef int (f_printf) (const char *, ...);
29
30/* Dummy printf() that does nothing */
31static f_printf no_printf;
32static f_printf *dskprobe_printfs[] = { no_printf, printf };
33
34#define dskprobe_printf (dskprobe_printfs[DBG_DSKPROBE])
35
36static void dskprobe_pause(com32sys_t *);
37
38/* Probe routine function type */
39typedef int (f_probe) (uint8_t, com32sys_t *);
40static f_probe probe_int13h_08h, probe_int13h_15h, probe_int13h_41h;
41
42/* We will probe a BIOS drive number using INT 0x13, AH == func */
43static void probe_any(uint8_t func, uint8_t drive, com32sys_t * regs)
44{
45    regs->eax.b[1] = func;	/* AH == sub-function for probe */
46    regs->edx.b[0] = drive;	/* DL == drive number to probe */
47    intcall(0x13, regs, regs);
48    return;
49}
50
51/**
52 * Determine if the return from probe_int13h_01h indicates a failure; a
53 * return of zero indicates no known failure.
54 */
55static int probe_int13h_01h_fail(int istatus)
56{
57    int status = 0;
58
59    if (istatus >= 256)
60	status = istatus;
61    else
62	switch (istatus) {
63	case 1: status = istatus;
64	}
65    return status;
66}
67
68/**
69 * INT 0x13, AH == 0x01: Get status of last command.
70 */
71static int probe_int13h_01h(uint8_t drive)
72{
73    int status;
74    com32sys_t regs;
75
76    memset(&regs, 0, sizeof regs);
77    probe_any(0x01, drive, &regs);
78    status = (regs.eflags.l & 1) * 256 + regs.eax.b[1];
79    dskprobe_printf("  AH01: CF%d AH%02x", regs.eflags.l & 1, regs.eax.b[1]);
80    return status;
81}
82
83/**
84 * INT 0x13, AH == 0x08: Get drive parameters.
85 */
86static int probe_int13h_08h(uint8_t drive, com32sys_t * regs)
87{
88    int present;
89    int status;
90
91    memset(regs, 0, sizeof *regs);
92    probe_any(0x08, drive, regs);
93    dskprobe_printf("  AH08: CF%d AH%02x AL%02x BL%02x DL%02x    ",
94		    regs->eflags.l & 1, regs->eax.b[1], regs->eax.b[0],
95		    regs->ebx.b[0], regs->edx.b[0]);
96    present = !(regs->eflags.l & 1) && !regs->eax.b[1];
97    status = probe_int13h_01h(drive);
98    present = present && !(probe_int13h_01h_fail(status));
99    dskprobe_printf("  P%d\n",  present);
100    return present;
101}
102
103/**
104 * INT 0x13, AH == 0x15: Get disk type.
105 */
106static int probe_int13h_15h(uint8_t drive, com32sys_t * regs)
107{
108    int present;
109    int status;
110
111    memset(regs, 0, sizeof *regs);
112    probe_any(0x15, drive, regs);
113    dskprobe_printf("  AH15: CF%d AH%02x AL%02x CX%04x DX%04x",
114		    regs->eflags.l & 1, regs->eax.b[1], regs->eax.b[0],
115		    regs->ecx.w[0], regs->edx.w[0]);
116    present = !(regs->eflags.l & 1) && regs->eax.b[1];
117    status = probe_int13h_01h(drive);
118    present = present && !(probe_int13h_01h_fail(status));
119    dskprobe_printf("  P%d\n",  present);
120    return present;
121}
122
123/**
124 * INT 0x13, AH == 0x41: INT 0x13 extensions installation check.
125 */
126static int probe_int13h_41h(uint8_t drive, com32sys_t * regs)
127{
128    int present;
129    int status;
130
131    memset(regs, 0, sizeof *regs);
132    regs->ebx.w[0] = 0x55AA;	/* BX == 0x55AA */
133    probe_any(0x41, drive, regs);
134    dskprobe_printf("  AH41: CF%d AH%02x BX%04x CX%04x DH%02x",
135		    regs->eflags.l & 1, regs->eax.b[1], regs->ebx.w[0],
136		    regs->ecx.w[0], regs->edx.b[1]);
137    present = !(regs->eflags.l & 1) && (regs->ebx.w[0] == 0xAA55);
138    status = probe_int13h_01h(drive);
139    present = present && !(probe_int13h_01h_fail(status));
140    dskprobe_printf("  P%d\n",  present);
141    return present;
142}
143
144/*
145 * We will probe the BIOS Data Area and count the drives found there.
146 * This heuristic then assumes that all drives of 'drive's type are
147 * found in a contiguous range, and returns 1 if the probed drive
148 * is less than or equal to the BDA count.
149 * This particular function's code is derived from code in setup.c by
150 * H. Peter Anvin.  Please respect that file's copyright for this function
151 */
152int probe_bda_drive(uint8_t drive)
153{
154    int bios_drives;
155    int err;
156
157    if (drive & 0x80) {
158	bios_drives = rdz_8(BIOS_HD_COUNT);	/* HDD count */
159    } else {
160	uint8_t equip = rdz_8(BIOS_EQUIP);
161	if (equip & 1)
162	    bios_drives = (equip >> 6) + 1;	/* Floppy count */
163	else
164	    bios_drives = 0;
165    }
166    err = (drive - (drive & 0x80)) >= bios_drives ? 0 : 1;
167    dskprobe_printf("BDA drive %02x? %d, total count: %d\n", drive, err,
168		    bios_drives);
169    return err;
170}
171
172/*
173 * We will probe a drive with a few different methods, returning
174 * the count of succesful probes
175 */
176int multi_probe_drive(uint8_t drive)
177{
178    int c = 0;
179    com32sys_t regs;
180
181    dskprobe_printf("INT 13 DL%02x:\n", drive);
182    /* Only probe the BDA for floppies */
183    if (drive & 0x80) {
184
185	c += probe_int13h_08h(drive, &regs);
186	c += probe_int13h_15h(drive, &regs);
187	c += probe_int13h_41h(drive, &regs);
188    }
189    c += probe_bda_drive(drive);
190    dskprobe_pause(&regs);
191    return c;
192}
193
194/*
195 * We will probe a contiguous range of BIOS drive, starting with drive
196 * number 'start'.  We probe with a few different methods, and return
197 * the first drive which doesn't respond to any of the probes.
198 */
199uint8_t probe_drive_range(uint8_t start)
200{
201    uint8_t drive = start;
202    while (multi_probe_drive(drive)) {
203	drive++;
204	/* Check for passing the floppy/HDD boundary */
205	if ((drive & 0x7F) == 0)
206	    break;
207    }
208    return drive;
209}
210
211/* Dummy printf() that does nothing */
212static int no_printf(const char *ignored, ...)
213{
214    (void)ignored;
215    return 0;
216}
217
218/* Pause if we are in debug-mode */
219static void dskprobe_pause(com32sys_t * regs)
220{
221    if (!DBG_DSKPROBE)
222	return;
223    dskprobe_printf("Press a key to continue...\n");
224    memset(regs, 0, sizeof *regs);
225    regs->eax.w[0] = 0;
226    intcall(0x16, regs, NULL);
227    return;
228}
229