File:  [RetroPC.NET] / xmil / io / fdc.c
Revision 1.10: download - view: text, annotated - select for diffs
Thu Aug 12 02:45:06 2004 JST (21 years, 2 months ago) by yui
Branches: MAIN
CVS tags: HEAD
fix...

#include	"compiler.h"
#include	"dosio.h"
#include	"z80core.h"
#include	"pccore.h"
#include	"iocore.h"
#include	"nevent.h"
#include	"fddfile.h"
#include	"fdd_2d.h"
#include	"fdd_d88.h"
#include	"fdd_mtr.h"


static const UINT8 fdctype[] = {1,1,1,1,1,1,1,1,2,2,2,2,3,4,3,3};


void nvitem_fdcbusy(UINT id) {

	fdc.s.busy = FALSE;
	if (fdc.s.bufdir) {
		TRACEOUT(("dma ready!"));
		dmac_sendready(TRUE);
	}
}

static void setbusy(UINT clock) {

	fdc.s.busy = TRUE;
	nevent_set(NEVENT_FDC, clock, nvitem_fdcbusy, NEVENT_ABSOLUTE);
}

static REG8 getstat(void) {

	FDDFILE	fdd;
	REG8	cmd;
	REG8	type;
	REG8	ret;

	fdd = fddfile + fdc.s.drv;
	cmd = (REG8)(fdc.s.cmd >> 4);
	type = fdc.s.type;
	if (fdd->type == DISKTYPE_NOTREADY) {
		ret = FDDSTAT_NOTREADY;
	}
	else if (type != 0) {
		ret = fdc.s.stat;
	}
	else {
		ret = 0;
	}
	if ((type == 1) && (fdc.s.c == 0)) {
		ret |= FDDSTAT_TRACK00;
	}
	if ((type == 0) || (type == 1) || (type == 4) ||
		(cmd == 0x0a) || (cmd == 0x0b) || (cmd == 0x0f)) {
		if (fdd->protect) {
			ret |= FDDSTAT_WRITEP;
		}
	}
	if ((type == 1) || (type == 4)) {
		fdc.s.hole++;
		if (fdc.s.hole < 8) {
			ret |= FDDSTAT_INDEX;
		}
	}
	else if (!(ret & 0xf0)) {
		if (FDDMTR_BUSY) {
			ret |= FDDSTAT_BUSY;
		}
		if (fdc.s.bufdir) {
			ret |= FDDSTAT_DRQ | FDDSTAT_BUSY;
		}
		else if (cmd == 0x0f) {
			ret |= FDDSTAT_LOSTDATA;
		}
	}
	return(ret);
}

static void seekcmd(void) {

	FDDFILE	fdd;
	UINT	track;

	fdc.s.crcnum = 0;
	fdc.s.creg = fdc.s.c;
	fdd = fddfile + fdc.s.drv;
	track = (fdc.s.c << 1) + fdc.s.h;
	fdc.s.stat = fdd->seek(fdd, fdc.s.media, track) | FDDSTAT_HEADENG;
	FDDMTR_MOVE;
}

static REG8 type2cmd(REG8 sc) {

	REG8	dir;
	UINT	track;
	UINT8	*p;
	FDDFILE	fdd;
	UINT	size;
	REG8	stat;

	track = (fdc.s.c << 1) + fdc.s.h;
	if (!(fdc.s.cmd & 0x20)) {
		p = fdc.s.buffer;
		dir = FDCDIR_IN;
	}
	else {
		p = NULL;
		dir = FDCDIR_OUT;
	}
	size = sizeof(fdc.s.buffer);
	fdd = fddfile + fdc.s.drv;
	TRACEOUT(("read %.2x %d %d", fdc.s.drv, track, sc));
	stat = fdd->read(fdd, fdc.s.media, track, sc, p, &size);
	if (stat & FDDSTAT_RECNFND) {
		size = 0;
		dir = FDCDIR_NONE;
	}
	else if (dir == FDCDIR_OUT) {
		if (size) {
			ZeroMemory(fdc.s.buffer, size);
		}
		stat = stat & (~FDDSTAT_RECTYPE);
	}
	fdc.s.bufmedia = fdc.s.media;
	fdc.s.bufunit = fdc.s.drv;
	fdc.s.buftrack = track;
	fdc.s.bufsc = sc;
	fdc.s.bufwrite = FALSE;
	fdc.s.bufdir = dir;
	fdc.s.bufmark = fdc.s.cmd & 1;
	fdc.s.bufpos = 0;
	fdc.s.bufsize = size;
	fdc.s.curtime = 0;
	return(stat);
}

static REG8 type2flash(void) {

	FDDFILE	fdd;

	fdc.s.bufwrite = FALSE;
	fdd = fddfile + fdc.s.bufunit;
	if (fdd->protect) {
		return(FDDSTAT_WRITEFAULT);
	}
	return(fdd->write(fdd, fdc.s.bufmedia, fdc.s.buftrack,
									fdc.s.bufsc, fdc.s.buffer, fdc.s.bufpos));
}

static REG8 crccmd(void) {

	UINT	track;
	FDDFILE	fdd;
	REG8	stat;

	track = (fdc.s.c << 1) + fdc.s.h;
	fdd = fddfile + fdc.s.drv;
	stat = fdd->crc(fdd, fdc.s.media, track, fdc.s.crcnum, fdc.s.buffer);
	if (stat & FDDSTAT_RECNFND) {
		fdc.s.crcnum = 0;
		stat = fdd->crc(fdd, fdc.s.media, track, 0, fdc.s.buffer);
	}
	if (!(stat & FDDSTAT_RECNFND)) {
		fdc.s.bufdir = FDCDIR_IN;
		fdc.s.bufsize = 6;
		fdc.s.rreg = fdc.s.buffer[0];
	}
	else {
		fdc.s.bufdir = FDCDIR_NONE;
		fdc.s.bufsize = 0;
	}
	fdc.s.bufwrite = FALSE;
	fdc.s.curtime = 0;
	return(stat);
}

static void bufposinc(void) {

	BRESULT	r;
	REG8	stat;

	if (fdc.s.busy) {
		return;
	}
	fdc.s.bufpos++;
	fdc.s.curtime = 0;
	if (fdc.s.bufpos >= fdc.s.bufsize) {
		r = FALSE;
		if (fdc.s.type == 2) {
			stat = 0;
			if (fdc.s.cmd & 0x10) {
				r = TRUE;
			}
			if ((fdc.s.cmd & 0x20) && (fdc.s.bufwrite)) {
				stat = type2flash();
				if (stat & (FDDSTAT_RECNFND | FDDSTAT_WRITEFAULT)) {
					r = FALSE;
				}
				fdc.s.stat = stat;
			}
			if (r) {
				fdc.s.rreg = fdc.s.r + 1;
				stat = type2cmd(fdc.s.rreg);
				if (!(stat & FDDSTAT_RECNFND)) {
					fdc.s.r = fdc.s.r + 1;
					fdc.s.stat = stat;
				}
				else {
					r = FALSE;
				}
			}
		}
		if (!r) {
			fdc.s.bufdir = FDCDIR_NONE;
			dmac_sendready(FALSE);
		}
	}
}

void IOOUTCALL fdc_o(UINT port, REG8 value) {

	REG8	cmd;

	if ((port & (~7)) != 0x0ff8) {
		return;
	}
//	TRACEOUT(("fdc %.4x,%.2x [%.4x]", port, value, Z80_PC));
	switch(port & 7) {
		case 0:									// コマンド
			fdc.s.cmd = value;
			cmd = (REG8)(value >> 4);
			fdc.s.type = fdctype[cmd];
			TRACEOUT(("fdc cmd: %.2x", value));
			if (fdc.s.bufwrite) {
				fdc.s.stat = type2flash();
			}
			fdc.s.bufdir = FDCDIR_NONE;
			// マリオは コマンド発行後にbusyを見張る
			// 逆にソーサリアンとかは busyだとエラーになる…
			// 条件は何?
			setbusy(20);
			switch(cmd) {
				case 0x00:								// リストア
				//	if (value & 8) {					// LAYDOCK
				//		setbusy(0);
				//	}
					fdc.s.motor = 0x80;					// モーターOn?
					fdc.s.c = 0;
					fdc.s.step = 1;
					fdc.s.r = 0;						// デゼニワールド
					seekcmd();
					fdc.s.rreg = 0;
					break;

				case 0x01:								// シーク
					fdc.s.motor = 0x80;					// モーターOn
					fdc.s.step = (SINT8)((fdc.s.c<=fdc.s.data)?1:-1);
					fdc.s.c = fdc.s.data;
					seekcmd();
					break;

				case 0x02:								// ステップ
				case 0x03:
					if (fdc.s.motor) {
						fdc.s.c += fdc.s.step;
						if (cmd & 1) {
							seekcmd();
						}
					}
					break;

				case 0x04:								// ステップイン
				case 0x05:
					if (fdc.s.motor) {
						fdc.s.step = 1;
						fdc.s.c++;
						if (cmd & 1) {
							seekcmd();
						}
					}
					break;

				case 0x06:								// ステップアウト
				case 0x07:
					if (fdc.s.motor) {
						fdc.s.step = -1;
						fdc.s.c--;
						if (cmd & 1) {
							seekcmd();
						}
					}
					break;

				case 0x08:								// リードデータ
				case 0x09:
				case 0x0a:								// ライトデータ
				case 0x0b:
					setbusy(500);
					fdc.s.stat = type2cmd(fdc.s.r);
					break;

				case 0xc:								// リードアドレス
					setbusy(200);
					fdc.s.stat = crccmd();
					break;

				case 0x0d:								// フォースインタラプト
					setbusy(0);							// 必要ない?
//					fdc.s.skip = 0;						// 000330
					dmac_sendready(FALSE);
					break;

				case 0x0e:								// リードトラック
					setbusy(200);
					ZeroMemory(fdc.s.buffer, 0x1a00);
					fdc.s.bufpos = 0;
					fdc.s.bufsize = 0x1a00;
					fdc.s.bufdir = FDCDIR_IN;
					break;

				case 0x0f:								// ライトトラック
					break;
			}
			break;

		case 1:									// トラック
			fdc.s.c = value;
			break;

		case 2:									// セクタ
			FDDMTR_WAITSEC(value);
			fdc.s.r = value;
			fdc.s.rreg = value;
			break;

		case 3:									// データ
			fdc.s.data = value;
			if ((fdc.s.motor) && (fdc.s.bufdir == FDCDIR_OUT)) {
				fdc.s.buffer[fdc.s.bufpos] = value;
				fdc.s.bufwrite = TRUE;
				bufposinc();
			}
			break;

		case 4:									// ドライブ・サイド
			fdc.s.ctbl[fdc.s.drv] = fdc.s.c;
			fdc.s.c = fdc.s.ctbl[value & 0x03];
			fdc.s.motor = (UINT8)(value & 0x80);
			fdc.s.drv = (UINT8)(value & 0x03);
			fdc.s.h = (UINT8)((value >> 4) & 1);
			fdc.s.cmd = 0;							// T&E SORCERIAN
			fdc.s.type = 0;

			FDDMTR_DRVSET;
			if (!fdc.s.motor) {
				fdc.s.r = 0;						// SACOM TELENET
				fdc.s.rreg = 0;
			}
			break;
	}
}

REG8 IOINPCALL fdc_i(UINT port) {

// static	BYTE	timeoutwait;
// static	BYTE	last_r;
// static	short	last_off;
	REG8	ret;

	if ((port & (~7)) != 0x0ff8) {
		return(0xff);
	}
#if 0
	if ((port &= 0xf) != 8) {
		last_r = -1;
		last_off = -1;
		timeoutwait = 4;
	}
#endif
	switch(port & 7) {
		case 0:									// ステータス
			ret = fdc.s.busy;
			if (ret) {
				return(ret);
			}
#if 1
			if (fdc.s.bufdir) {					// YsII
				fdc.s.curtime++;
				if (fdc.s.curtime >= 8) {
					fdc.s.stat |= 0x04;
					bufposinc();
				}
			}
#endif
			ret = getstat();
			if (!(ret & 0x02)) {
				dmac_sendready(FALSE);
			}
			return(ret);

		case 1:									// トラック
			return(fdc.s.creg);

		case 2:									// セクタ
			return(fdc.s.rreg);

		case 3:									// データ
			if (fdc.s.motor) {
				if (fdc.s.bufdir == FDCDIR_IN) {
					fdc.s.data = fdc.s.buffer[fdc.s.bufpos];
TRACEOUT(("read %.2x %.2x [%.4x]", fdc.s.data, fdc.s.bufpos, Z80_PC));
					bufposinc();
				}
			}
			return(fdc.s.data);

		case 4:									// FM
		case 5:									// MFM
			return(0x00);

		case 6:									// 1.6M
			fdc.s.media = DISKTYPE_2HD;
			break;

		case 7:									// 500K/1M
			fdc.s.media = DISKTYPE_2D;
			break;
	}
	return(0xff);
}


// ----

void fdc_reset(void) {

	FDDMTR_INIT;
	ZeroMemory(&fdc, sizeof(fdc));
	fdc.s.step = 1;
}


RetroPC.NET-CVS <cvs@retropc.net>