/* extipl.c --
 *	install and testing extended IPL
 *					Auther: takamiti@tsden.org
 *
 * CAUTION:
 *   TAKE CARE!
 *   I(WE) HAVE NOT SHARE IN THE TROUBLES, RUNNING BY YOUR OWN RISKS.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <dos.h>
#include <io.h>
#include <sys/stat.h>

#define VERSION		"Version 3.00 1997/10/25"

#define SECTOR_SIZE      512
#define	BOOT_FD         0x00
#define BOOT_HD         0x80
#define WRITE_SECTOR  0x0301
#define READ_SECTOR   0x0201
#define PARTITIONS	 4

#define OK		 0
#define ERR		-1

#define codecpy	memcpy

typedef unsigned char byte;
typedef unsigned long ulong;
struct hd_addr {
	byte head;
	byte sector;
	byte cyl;
};
typedef struct hd_addr hd_addr;

struct partition {
	byte	bootind;
	hd_addr	start;
	byte	systemind;
	hd_addr end;
	ulong	start_sector;
	ulong	nr_sector;
};
typedef struct partition partition;

extern int dmaoverrun(char *, int);
extern void extendedIPL();
extern void FDtestIPL();
/* extern void codecpy(); */

int fdtest(char *);
int install(char *);
int restore(char *);
int saveipl(char *);
int clrboot(char *);
int help(char *);
int dskrw(int, int);
int rdipl(int, char *), wripl(int, char *);
int save(char *, char *), load(char *, char *);
void tblsort(char *);

#if DEBUG
int debugout(char *);
#endif

struct cmdtab {
	char *command;
	int (*func)(char *);
	char *help;
} cmdtab[] =
    {
	{"install", install, "install 'extended_IPL' on fixed disk #0" },
	{"fdtest",  fdtest,  "testing 'extended_IPL' on diskette" },
	{"save",	saveipl, "save original-IPL to file" },
	{"restore", restore, "restore original-IPL"},
	{"clrboot", clrboot, "clear active flag"},
	{"help",	help,	 "show this"},
#if DEBUG
	{"debug",	debugout,"for debug mode"},
#endif
	{"", (int *)NULL, ""}
    };

char *diskbuf, *sectbuf;
char buf1[SECTOR_SIZE], buf2[SECTOR_SIZE];

void main(ac, av)
int ac;
char *av[];
{
    int i, r;

    if (ac < 2) help(av[1]);

    diskbuf = buf1;
    sectbuf = buf2;
    if (dmaoverrun(diskbuf, SECTOR_SIZE)) {
	diskbuf = buf2;
	sectbuf = buf1;
    }
    for(i = 0; *cmdtab[i].command; i++) {
	if (strcmp(av[1], cmdtab[i].command) == 0) {
	    r = (*(cmdtab[i].func))(av[2]);
	    fprintf(stderr, "%s.\n", (r == OK) ? "Ok": "Aborted");
	    exit(r);
	}
    }
    fprintf(stderr, "Unknown command \"%s\"\n", av[1]);
    exit(ERR);
}

int help(arg)
char *arg;
{
    int i;
    fprintf(stderr, "\n*** Extended IPL installer %s ***", VERSION);
    fprintf(stderr, "\nUsage: extIPL command [arg]\nVaild commands are:\n");
    for(i=0; *cmdtab[i].command; i++)
	fprintf(stderr,"\t%s\t- %s\n", cmdtab[i].command, cmdtab[i].help);
    exit(OK);
}

int save(file, buf)
char *file, *buf;
{
    int fd, i=0;

    if (*file == '\0') {
	do {
	    sprintf(file, "fdiskIPL.%03d", i++);
	    if (i > 999) return(ERR);
	} while (access(file, 0) == 0);
    }
    fd = open(file, O_CREAT | O_RDWR | O_TRUNC | O_BINARY, S_IREAD);
    if (fd < 0 || write(fd, buf, SECTOR_SIZE) != SECTOR_SIZE) {
	perror(file);
	return(ERR);
    }
    close(fd);
    return(OK);
}

int load(file, buf)
char *file, *buf;
{
    int	fd, r;

    fd = open(file, O_RDONLY | O_BINARY);
    if (fd < 0 || (r = read(fd, buf, SECTOR_SIZE)) < 0) {
	perror(file);
	return(ERR);
    }
    close(fd);
    return(r);
}

int dskrw(drive, mode)
int drive, mode;
{
    union REGS r;
    struct SREGS seg;
    int i, err;

    for(i = 0; i < 8; i++) {
	r.x.ax = mode;
	r.x.bx = (int)diskbuf;
	r.x.cx = 1;
	r.x.dx = drive;
	seg.es = FP_SEG(diskbuf);
	int86x(0x13, &r, &r, &seg);
	if ((r.x.flags & 1) == 0)  return(OK);

	err = r.h.ah & 0xff;
	r.h.ah = 0;
	r.x.dx = drive;
	int86(0x13, &r, &r);	/* reset */
    }
    fprintf(stderr, "DISK BIOS Error(0x%02X)\n", err);
    return(ERR);
}

int rdipl(drive, buf)
int drive;
char *buf;
{
    if (dskrw(drive, READ_SECTOR) != OK) return(ERR);
    memcpy(buf, diskbuf, SECTOR_SIZE);
    return(OK);
}

int wripl(drive, buf)
int drive;
char *buf;
{
    if (dskrw(drive, READ_SECTOR) != OK) return(ERR);
    memcpy(diskbuf, buf, 446);
    return(dskrw(drive, WRITE_SECTOR));
}

int saveipl(arg)
char *arg;
{
    if (rdipl(BOOT_HD, sectbuf) != OK) return(ERR);
    return( save(((arg == NULL) ? "master.ipl": arg), sectbuf) );
}

int fdtest(arg)
char *arg;
{
    if (arg == NULL) {
	codecpy(sectbuf, FDtestIPL, SECTOR_SIZE);
    } else {
	if (load(arg, sectbuf) < OK) return(ERR);
    }
    *(unsigned int *)(sectbuf+510) = 0xaa55;
    fprintf(stderr, "Please insert blank diskette in drive \"A:\"\n");
    fprintf(stderr, "Then hit <ENTER> key. ");
    while (getchar() != '\n') ;
    return(wripl(BOOT_FD, sectbuf));
}

int install(arg)
char *arg;
{
    char name[80];

    if (rdipl(BOOT_HD, sectbuf)) return(ERR);
    fprintf(stderr, "*** Before exchange the master boot program,\n");
    fprintf(stderr, "*** You had better keep the original IPL code.\n");
    fprintf(stderr, "Enter file name to save: ");
    gets(name);
    if (save(name, sectbuf) != OK) return(ERR);
    fprintf(stderr, "Current IPL saved to '%s'.\n", name);
    fprintf(stderr, "Hit <ENTER> to install Extended-IPL ");
    while (getchar() != '\n') ;
    if (arg == NULL) {
	codecpy(sectbuf, extendedIPL, 446);
    } else {
	if (load(arg, sectbuf) != OK) return(ERR);
    }
    return(wripl(BOOT_HD, sectbuf));
}

int restore(arg)
char *arg;
{
    if (arg == NULL || load(arg, sectbuf) != SECTOR_SIZE) return(ERR);
    if (*(unsigned int *)(sectbuf+510) != 0xaa55) {
	fprintf(stderr, "IPL magic != 0xAA55\n");
	return(ERR);
    }
    fprintf(stderr, "Original IPL(%s) loaded. Hit <ENTER> to restore ", arg);
    while (getchar() != '\n') ;
    return(wripl(BOOT_HD, sectbuf));
}

int clrboot(arg)
char *arg;
{
    char *p;
    int i;

    if (dskrw(BOOT_HD, READ_SECTOR) != OK) return(ERR);
    for(i = 0, p = diskbuf + 0x1be; i < 4; i++, p += 16) {
	*p &= 0x7f;
    }
    return(dskrw(BOOT_HD, WRITE_SECTOR));
}

#if DEBUG
int debugout(arg)
char *arg;
{
    FILE *fp;
    char tmpbuf[SECTOR_SIZE];

    if ((fp = fopen("boot-sec", "wb")) == NULL) {
	return(ERR);
    }
    rdipl(BOOT_HD, sectbuf);
    if (strcmp(arg, ".0") == 0) {
	codecpy(sectbuf, extendedIPL, 446);
    } else if (strcmp(arg, ".1") == 0) {
	codecpy(sectbuf, FDtestIPL, 446);
    } else {
	load(arg, tmpbuf);
	memcpy(sectbuf, tmpbuf, 446);
    }
    fwrite(sectbuf, SECTOR_SIZE, 1, fp);
    fclose(fp);
    return(OK);
}
#endif
