/*
 * MyTDIO.c
 *
 * Handles a trackdisk RAW read with WORDSYNC.
 * I can't get the ETD_RAWREAD to work, so coded one myself. DAV
 */

#include <exec/types.h>
#include <exec/nodes.h>
#include <exec/lists.h>
#include <exec/memory.h>
#include <exec/interrupts.h>
#include <exec/ports.h>
#include <exec/libraries.h>
#include <exec/io.h>
#include <exec/tasks.h>
#include <exec/execbase.h>
#include <exec/devices.h>
#include <devices/trackdisk.h>
#include <hardware/custom.h>
#include <hardware/cia.h>
#include <hardware/intbits.h>
#include <hardware/adkbits.h>
#include <hardware/dmabits.h>
#include <resources/disk.h>

#define __NO_PRAGMAS
#include <functions.h>

#define CIAF_DSKSELALL (CIAF_DSKSEL3 | CIAF_DSKSEL2\
			| CIAF_DSKSEL1 | CIAF_DSKSEL0)

#ifndef NUMHEADS
#define NUMHEADS 2
#endif

extern struct Custom custom;
extern struct CIA ciaa, ciab;

/* Above doesn't seem to work with Aztec, small data model, so ... */

#define CUSTOMADD	((struct Custom *)0xdff000)
#define CIAAADD		((struct CIA *)0xbfe001)
#define CIABADD		((struct CIA *)0xbfd000)

/*
 * Disable/Enable interrupts at the CPU
 */
static APTR sysStack;
static ULONG oldSR;

cpuDisable()
{
	oldSR = SetSR(0x700L,0x700L);
	sysStack = SuperState();
}
cpuEnable()
{
	if(sysStack) UserState(sysStack);	
	SetSR(oldSR,0x700L);
}


/* make a special read request up */

MyTDIO(struct IOExtTD *tdreq)
{
	struct Custom *pcustom = CUSTOMADD;	/*&custom;*/
	struct CIA *pciaa = CIAAADD;		/*&ciaa;*/
	struct CIA *pciab = CIABADD;		/*&ciab;*/

	UWORD command;
	ULONG actual,length,offset;
	APTR data;
	UBYTE flags;
	int drive,head;
	UWORD dsklenw;
	long timeout;

	struct IOStdReq *req = (struct IOStdReq *) tdreq;

	if(!(req->io_Command == ETD_RAWREAD || req->io_Command == TD_RAWREAD)) {
		req->io_Error = 100;
		return;
	}

	/* our request - local copy parameters */
	command = req->io_Command;
	flags = req->io_Flags;
	actual = req->io_Actual;	/* SPECIAL - expect drive unit # here */
	length = req->io_Length;
	data = req->io_Data;
	offset = req->io_Offset;	/* Track (not cyl) */

	drive = actual;
	head  = offset % (NUMSECS*TD_SECTOR);

	/* Seek to track */

	req->io_Offset = offset * (NUMSECS*TD_SECTOR);
	req->io_Command = TD_SEEK;
	DoIO((struct IORequest *)req);
	if(req->io_Error != 0) {
		req->io_Error = 101;
		return;
	}

	/* Do raw - read magic */

	cpuDisable();

	pcustom->dmacon = DMAF_SETCLR | DMAF_DISK | DMAF_MASTER;

	/* turn OFF bits - not GCR, and no pre-comp. for now */ 
	pcustom->adkcon = ADKF_PRECOMP0|ADKF_PRECOMP1|ADKF_MSBSYNC|ADKF_WORDSYNC;
	/* turn ON bits - mfm */
	pcustom->adkcon = ADKF_SETCLR|ADKF_WORDSYNC|ADKF_MFMPREC|ADKF_FAST;
	/* turn ON wordsync if required */
	if(flags & IOTDF_WORDSYNC)
		pcustom->adkcon = ADKF_SETCLR|ADKF_WORDSYNC;

	
	pcustom->dsksync = 0x4489;

	pcustom->dsklen = DSKDMAOFF;	/* 0x4000 */
	pcustom->dskpt = data;
	dsklenw = 0x8000 | ((length >> 1) & 0x3fff);
	pciab->ciaprb &= ~CIAF_DSKMOTOR;	/* ensure motor on */

	if(drive == 0) 
		pciab->ciaprb &= ~CIAF_DSKSEL0;
	else if(drive == 1)
		pciab->ciaprb &= ~CIAF_DSKSEL1;
	else if(drive == 2)
		pciab->ciaprb &= ~CIAF_DSKSEL2;
	else if(drive == 3)
		pciab->ciaprb &= ~CIAF_DSKSEL3;

	if(head == 0) 
		pciab->ciaprb &= ~CIAF_DSKSIDE;
	else
		pciab->ciaprb |=  CIAF_DSKSIDE;

	pcustom->dsklen = dsklenw;		/* first write */

	timeout = 0;
	while(pciaa->ciapra & CIAF_DSKRDY) {	/* wait for ready */
		if(++timeout > 1000000L) {
			pcustom->dsklen = DSKDMAOFF;
			pciab->ciaprb |= (CIAF_DSKSELALL);
			cpuEnable();
			req->io_Error = 102;
			return;	/* timed out waiting for ready */
		}
	}

	if(flags & IOTDF_INDEXSYNC) {
		timeout = pciab->ciaicr;
		timeout = 0;
		while(!(pciab->ciaicr & CIAICRF_FLG)) {	/* wait for index pls */
			if(++timeout > 1000000L) {
				pcustom->dsklen = DSKDMAOFF;
				pciab->ciaprb |= (CIAF_DSKSELALL);
				cpuEnable();
				req->io_Error = 103;
				return;	/* timed out waiting for index */
			}
		}
	}

	pcustom->intena = INTF_SETCLR | INTF_DSKBLK;
	pcustom->intreq = INTF_DSKBLK;		/* clear pending DMA-done */
	pcustom->dsklen = dsklenw;		/* second write - start DMA*/
	timeout = 0;
	while(!(pcustom->intreqr & INTF_DSKBLK)) { /* wait for DMA done */
		if(++timeout > 1000000L) {
			pcustom->dsklen = DSKDMAOFF;
			pciab->ciaprb |= (CIAF_DSKSELALL);
			cpuEnable();
			req->io_Error = 104;
			return;	/* timed out waiting for DMA */
		}
	}
	pciab->ciaprb |= (CIAF_DSKSELALL);
	pcustom->dsklen = DSKDMAOFF;
	pcustom->intreq = INTF_DSKBLK;		/* clear DMA-done */
	cpuEnable();
	req->io_Error = 0;

	return;
}

