//	Copyright (C) 2013 Michael McMaster <michael@codesrc.com>
//
//	This file is part of SCSI2SD.
//
//	SCSI2SD is free software: you can redistribute it and/or modify
//	it under the terms of the GNU General Public License as published by
//	the Free Software Foundation, either version 3 of the License, or
//	(at your option) any later version.
//
//	SCSI2SD is distributed in the hope that it will be useful,
//	but WITHOUT ANY WARRANTY; without even the implied warranty of
//	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//	GNU General Public License for more details.
//
//	You should have received a copy of the GNU General Public License
//	along with SCSI2SD.  If not, see <http://www.gnu.org/licenses/>.

#include "device.h"
#include "scsi.h"
#include "scsiPhy.h"
#include "bits.h"

CY_ISR_PROTO(scsiResetISR);
CY_ISR(scsiResetISR)
{
	scsiDev.resetFlag = 1;
	SCSI_RST_ClearInterrupt();
}

CY_ISR_PROTO(scsiAttentionISR);
CY_ISR(scsiAttentionISR)
{
	scsiDev.atnFlag = 1;
	SCSI_ATN_ClearInterrupt();
}

uint8 scsiReadDBxPins()
{
	return
		(SCSI_ReadPin(SCSI_In_DBx_DB7) << 7) |
		(SCSI_ReadPin(SCSI_In_DBx_DB6) << 6) |
		(SCSI_ReadPin(SCSI_In_DBx_DB5) << 5) |
		(SCSI_ReadPin(SCSI_In_DBx_DB4) << 4) |
		(SCSI_ReadPin(SCSI_In_DBx_DB3) << 3) |
		(SCSI_ReadPin(SCSI_In_DBx_DB2) << 2) |
		(SCSI_ReadPin(SCSI_In_DBx_DB1) << 1) |
		SCSI_ReadPin(SCSI_In_DBx_DB0);		
}

uint8 scsiReadByte(void)
{
	while (!(CY_GET_REG8(scsiTarget_StatusReg__STATUS_REG) & 1)) {}
	CY_SET_REG8(scsiTarget_datapath__F0_REG, 0);
	while (!(CY_GET_REG8(scsiTarget_StatusReg__STATUS_REG) & 2)) {}
	uint8 value = CY_GET_REG8(scsiTarget_datapath__F1_REG);
	return value;
}

void scsiRead(uint8* data, uint32 count)
{
	int prep = 0;
	int i = 0;

	while (i < count)
	{
		if (prep < count && (CY_GET_REG8(scsiTarget_StatusReg__STATUS_REG) & 1))
		{
			CY_SET_REG8(scsiTarget_datapath__F0_REG, 0);
			++prep;
		}
		if ((CY_GET_REG8(scsiTarget_StatusReg__STATUS_REG) & 2))
		{
			data[i] =  CY_GET_REG8(scsiTarget_datapath__F1_REG);
			++i;
		}
	}
}

void scsiWriteByte(uint8 value)
{
	while (!(CY_GET_REG8(scsiTarget_StatusReg__STATUS_REG) & 1)) {}
	CY_SET_REG8(scsiTarget_datapath__F0_REG, value);

	// TODO maybe move this TX EMPTY check to scsiEnterPhase ?
	//while (!(CY_GET_REG8(scsiTarget_StatusReg__STATUS_REG) & 4)) {}
	while (!(CY_GET_REG8(scsiTarget_StatusReg__STATUS_REG) & 2)) {}
	value = CY_GET_REG8(scsiTarget_datapath__F1_REG);	
}

void scsiWrite(uint8* data, uint32 count)
{
	int prep = 0;
	int i = 0;

	while (i < count)
	{
		if (prep < count && (CY_GET_REG8(scsiTarget_StatusReg__STATUS_REG) & 1))
		{
			CY_SET_REG8(scsiTarget_datapath__F0_REG, data[prep]);
			++prep;
		}
		if ((CY_GET_REG8(scsiTarget_StatusReg__STATUS_REG) & 2))
		{
			CY_GET_REG8(scsiTarget_datapath__F1_REG);
			++i;
		}
	}
}

static void busSettleDelay(void)
{
	// Data Release time (switching IO) = 400ns
	// + Bus Settle time (switching phase) = 400ns.
	CyDelayUs(1); // Close enough.
}

void scsiEnterPhase(int phase)
{
	if (phase > 0)
	{
		if (phase & __scsiphase_msg)
		{
			SCSI_SetPin(SCSI_Out_MSG);
		}
		else
		{
			SCSI_ClearPin(SCSI_Out_MSG);
		}

		if (phase & __scsiphase_cd)
		{
			SCSI_SetPin(SCSI_Out_CD);
		}
		else
		{
			SCSI_ClearPin(SCSI_Out_CD);
		}

		SCSI_CTL_IO_Write(phase & __scsiphase_io ? 1 : 0);
	}
	else
	{
		SCSI_ClearPin(SCSI_Out_MSG);
		SCSI_ClearPin(SCSI_Out_CD);
		SCSI_CTL_IO_Write(0);
	}
	busSettleDelay();
}

void scsiPhyInit()
{
	SCSI_RST_ISR_StartEx(scsiResetISR);
	SCSI_ATN_ISR_StartEx(scsiAttentionISR);

	// Interrupts may have already been directed to the (empty)
	// standard ISR generated by PSoC Creator.
	SCSI_RST_ClearInterrupt();
	SCSI_ATN_ClearInterrupt();
}
