/*
 * rawrite98.c Write a binary image to diskette
 * Version 1.2
 * Copyright (c) KATO Takenori, 1995, 1996. 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++ 3.1 small model.
 */

/*
 * History:
 * Version 1.2:
 *     support 1.44MB diskette
 * Verions 1.1:
 *     bug fix
 */


#include <alloc.h>
#include <ctype.h>
#include <dir.h>
#include <pc98.h>
#include <stdio.h>
#include <fcntl.h>
#include <dos.h>

/* error code */
#define ECM			0x10
#define EDB			0x20
#define EEN			0x30
#define EEC			0x40
#define EOR			0x50
#define ENR			0x60
#define ENW			0x70
#define EDE			0xA0
#define END			0xC0
#define EMA			0xE0
#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;
};

struct mediainfo_t minfo[] = {
	/* Order might be important. See testmediatype() routine */
	{0x90, 15},			/* 1.2MB on 1MB I/F */
	{0x10, 15},			/* 1.2MB on 720KB I/F */
	{0x30, 18},			/* 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)
{
	struct diskinfo dp;
	int retcode;
	int i;

	for (i = 0; i < MAXMEDIATYPE; i++) {
		dp.cmd = 0x5a;			/* read ID */
		dp.devtype = (duda & 0x0f) | minfo[i].da;
		dp.cylnum = 0;
		dp.headnum = 0;
		retcode = pc98disk(&dp);

		if (((retcode & 0xff00) == 0) && ((dp.seclen & 0xff) == 2)) {
			/* No error and 512 bytes/sector */
			return i;
		}
	}

	if ((retcode & 0xff00) != 0)
		return (retcode & 0x00ff);	/* Disk BIOS Error */
	if ((dp.seclen & 0xff) != 2)
		return -1;			/* Not 512 bytes/sector */

	/* NOT REACHED */
	return -1;
}


int writedata(char *buffer, FILE* fp, unsigned char duda, int type)
{
	struct diskinfo dp;
	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;

		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);
		if (retcode & 0xff00) {
			errormsg(retcode & 0x00f0, "");
			return 1;
		}

		if (flag == 1)
			break;
		head++;
		head &= 1;
		if (head == 0)
			cylinder++;
	}
	return 0;
}


int main(void)
{
	char fname[MAXPATH];
	char *buffer, *abuffer;
	int count, drive, type;
	unsigned char duda;
	unsigned char far* dosparameter;
	FILE* fp;

	printf("RaWrite(98) 1.2 - Write disk file to raw diskette\n");
	printf("(C)Copyright KATO Takenori, 1995, 1996.\n");
	printf("All rights reserved.\n\n");

	printf("Enter source file name: ");
	scanf("%s", fname);
	_fmode = O_BINARY;
	if ((fp = fopen(fname, "r")) == NULL) {
		errormsg(ENoFile, fname);
		return 1;
	}

	printf("Enter destination drive: ");
	scanf("%s", fname);
	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;
	}

    /* Check DMA boundary */
    if ((_DS + ((unsigned int)buffer >> 4) & 0x0fff) > 0xdc0)
		buffer += 18 * 512;

	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);
    if (type == EUnsupport) {
		/* This program support 1200KB and 1440KB diskette */
		fclose(fp);
		errormsg(EUnsupport, "");
		return 1;
    }
    if (type == -1) {
		/* 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;
    }

	if (writedata(buffer, fp, duda, type)) {
		fclose(fp);
		free(abuffer);
		printf("Terminated by error.\n");
		return 1;
	}
    fclose(fp);
    free(abuffer);
    printf("Done.\n");
    return 0;
}

