/*
 * rawrite98.c Write a binary image to diskette
 * Version 1.3
 * Copyright (c) KATO Takenori, 1995, 1996, 2000. All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer as
 *    the first lines of this file unmodified.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This source code is specific to BORLAND C++ 5.0 small model.
 */

/*
 * History:
 * Version 1.3:
 * 	Fixed the bug in DMA boundary check.
 * 	BC++ 3 -> BC++ 5
 * Version 1.2:
 * 	Supported 1.44MB diskette
 * Verions 1.1:
 * 	Fixed bugs.
 */


#include <alloc.h>
#include <ctype.h>
#include <dir.h>
#include <dos.h>
#if __BORLANDC__ < 0x400
#include <pc98.h>
#else
#define	_BIOS_NECPC
#include <bios.h>
#endif
#include <fcntl.h>
#include <stdio.h>
#include <string.h>

/* error code */
#define ECM			0x0010
#define EDB			0x0020
#define EEN			0x0030
#define EEC			0x0040
#define EOR			0x0050
#define ENR			0x0060
#define ENW			0x0070
#define EDE			0x00A0
#define END			0x00C0
#define EMA			0x00E0
#define ENoFile		0x1000
#define EUnsupport	0x2000
#define ENo1MIF		0x3000
#define ENoMem		0x4000
#define ENoFDD		0x5000


#define MEDIA_1200		0
#define MEDIA_1200_A	1
#define MEDIA_1440		2
#define MAXMEDIATYPE	3

struct mediainfo_t {
	unsigned char	da;
	int		nsec;
	char*		size;
};

struct mediainfo_t minfo[] = {
	/* Order might be important. See testmediatype() routine */
	{0x90, 15, "1.2MB"},		/* 1.2MB on 1MB I/F */
	{0x10, 15, "1.2MB"},		/* 1.2MB on 720KB I/F */
	{0x30, 18, "1.4MB"},		/* 1.4MB on 1MB I/F */
};


void errormsg(int errorcode, char* fname)
{
	switch(errorcode) {
	case ECM:
		fprintf(stderr, "Control Mark\n"); break;
	case EDB:
		fprintf(stderr, "DMA Boundary\n"); break;
	case EEN:
		fprintf(stderr, "End of Cylinder\n"); break;
	case EEC:
		fprintf(stderr, "Equipment Check\n"); break;
	case EOR:
		fprintf(stderr, "Over Run\n"); break;
	case ENR:
		fprintf(stderr, "Not Ready\n"); break;
	case ENW:
		fprintf(stderr, "Not Writable\n"); break;
	case EDE:
		fprintf(stderr, "Data Error\n"); break;
	case END:
		fprintf(stderr, "No Data\n"); break;
	case EMA:
		fprintf(stderr, "Missing Address mark\n"); break;
	case ENoFile:
		fprintf(stderr, "File not found: %s\n", fname); break;
	case ENo1MIF:
		fprintf(stderr, "Device not configured\n"); break;
	case EUnsupport:
		fprintf(stderr, "Unsupported media type\n"); break;
	case ENoMem:
		fprintf(stderr, "Not enough memory\n"); break;
	case ENoFDD:
		fprintf(stderr, "Not FDD\n"); break;
	default:
		fprintf(stderr, "Unknown Error 0x%0x\n", errorcode); break;
	}
}

/* test media type */
int testmediatype(unsigned char duda, int verbose)
{
#if __BORLANDC__ < 0x400
	struct	diskinfo dp;
#else
	union	REGS regs;
#endif
	int	retcode;
	int	seclen;
	int	i;

	for (i = 0; i < MAXMEDIATYPE; i++) {
#if __BORLANDC__ < 0x400
		dp.cmd = 0x5a;			/* read ID */
		dp.devtype = (duda & 0x0f) | minfo[i].da;
		dp.cylnum = 0;
		dp.headnum = 0;
		retcode = pc98disk(&dp);
		seclen = dp.seclen;
#else
		regs.h.ah = 0x7a;
		regs.h.al = (duda & 0x0f) | minfo[i].da;
		regs.h.cl = 0;
		regs.h.dh = 0;
		int86(0x1b, &regs, &regs);
		if (regs.x.cflag)
			retcode = 0xff00 | regs.h.ah;
		else
			retcode = 0;
		seclen = regs.h.ch;
#endif
		if ((retcode & 0xff00) == 0) {
 			if (verbose)
	            printf("\nFound %s diskette.\n", minfo[i].size);
			if ((seclen & 0xff) == 2) {
				/* No error and 512 bytes/sector */
				return i;
			} else {
            			return EUnsupport;
			}
		}
	}

	/* XXX */
	if ((retcode & 0xff00) != 0)
		return retcode;	/* Disk BIOS Error */

	if ((seclen & 0xff) != 2)
		return EUnsupport;	/* Not 512 bytes/sector */

	/* NOT REACHED */
	return -1;
}

int writedata(char *buffer, FILE* fp, unsigned char duda, int type,
			int verbose)
{
#if __BORLANDC__ < 0x400
	struct	diskinfo dp;
#else
	unsigned char	disk_result[32];
	struct	diskinfo_t dinfo;
#endif
	int	cylinder, head;
	int	flag, retcode;

	flag = 0;
	head = cylinder = 0;
	while (cylinder < 80) {
		if (fread(buffer, 512, minfo[type].nsec, fp) <
		    minfo[type].nsec)
			flag = 1;

        if (verbose)
	        printf("C:%02d H:%02d\n", cylinder, head);
#if __BORLANDC__ < 0x400
		dp.cmd = 0xd5;
		dp.devtype = duda;
		dp.datalen = 512 * minfo[type].nsec;
		dp.seclen = 2;
		dp.cylnum = cylinder;
		dp.headnum = head;
		dp.secnum = 1;
		dp.databuf = buffer;

		retcode = pc98disk(&dp);
#else
		dinfo.command = _CMD_SEEK | _CMD_RETRY | _CMD_MF | _CMD_MT;
		dinfo.drive = duda;
		dinfo.head = head;
		dinfo.cylinder = cylinder;
		dinfo.data_len = 512 * minfo[type].nsec;
		dinfo.sector_len = 2;
		dinfo.sector = 1;
		dinfo.nsectors = minfo[type].nsec;
		dinfo.buffer = MK_FP(_DS, buffer);
		dinfo.result = MK_FP(_DS, &disk_result);
		retcode = _bios_disk(_DISK_WRITE, &dinfo);
#endif
		if (retcode & 0xff00) {
			errormsg(retcode & 0x00f0, "");
			return 1;
		}

		if (flag == 1)
			break;
		head++;
		head &= 1;
		if (head == 0)
			cylinder++;
	}
	return 0;
}


int main(int argc, char* argv[])
{
	char	fname[MAXPATH];
	char	*buffer, *abuffer;
	int	drive, type, verbose;
	unsigned char	duda;
	unsigned char far*	dosparameter;
	FILE*	fp;

	printf("RaWrite(98) 1.3 - Write disk file to raw diskette\n");
	printf("(C)Copyright KATO Takenori, 1995, 1996, 2000.\n");
	printf("All rights reserved.\n\n");

	if (strcmp(argv[argc - 1], "-v") == 0) {
		argc--;
		verbose = 1;
	} else {
		verbose = 0;
	}

	if (argc < 2) {
		printf("Enter source file name: ");
		scanf("%s", fname);
	} else
		strncpy(fname, argv[1], MAXPATH);

	_fmode = O_BINARY;
	if ((fp = fopen(fname, "r")) == NULL) {
		errormsg(ENoFile, fname);
		return 1;
	}

	if (argc < 3) {
		printf("Enter destination drive: ");
		scanf("%s", fname);
	} else
		strncpy(fname, argv[2], 1);

	drive = fname[0];
	drive = toupper(drive) - 'A';

	/* Get DA/UA */
	dosparameter = (unsigned char far*)MK_FP(0x60, 0x6c + drive);
	duda = *dosparameter;

	/* Is it FDD? */
	switch (duda & 0xf0) {
	case 0x90: case 0xf0: case 0x30:
		/* Yes, nothing to do */
		break;
	default:
		fclose(fp);
		errormsg(ENoFDD, "");
		return 1;
	}

	/* We need to check unit number */
	if ((duda & 0x0f) > 3) {
		fclose(fp);
		errormsg(ENoFDD, "");
		return 1;
	}

	printf("Please insert a formatted diskette into drive %c and "
		   "press RETURN key : ", drive + 'A');
	(void)flushall();
	(void)fgetc(stdin);

	/* Check Media type */
	type = testmediatype(duda, verbose);
	if (type == EUnsupport) {
		/* This program support 1200KB and 1440KB diskette */
		fclose(fp);
		errormsg(EUnsupport, "");
		return 1;
	}
	if (type & 0xff00) {
		/* Disk BIOS error */
		fclose(fp);
		errormsg(type & 0xf0, "");
		return 1;
	}
	/* OK, adjust DU/DA */
	duda &= 0x0f;
	duda |= minfo[type].da;

	if ((abuffer = buffer = malloc(18 * 512 * 2)) == NULL) {
		fclose(fp);
		errormsg(ENoMem, "");
		return 1;
	}

	/* Check DMA boundary */
	if ((_DS + ((unsigned int)buffer >> 4) & 0x0fff) > 0xdc0)
		buffer += 18 * 512;

	if (writedata(buffer, fp, duda, type, verbose)) {
		fclose(fp);
		free(abuffer);
		printf("Terminated by error.\n");
		return 1;
	}
	fclose(fp);
	free(abuffer);
	printf("Done.\n");
	return 0;
}