// ###########################################################################
// #
// # OSCASDMMCCard - Implementation of OSCA SD/MMC Card Emulation Class
// # Copyright (C) 2009-2011 Tony Friery [DialTone]
// #
// # Credit due to bitwacker55@yahoo.com for the CRC Generation Code whose
// # license reads "Any and all use of this code is granted to any and all
//	# users."
// #
// # ALL RIGHTS RESERVED
// #
// ###########################################################################

#pragma hdrstop

#include "OSCASDMMCCard.h"
#include "FormDiag.h"
#include "utils.h"

#define FULL_SD_DEBUG 1 		// Define this to debug read/writes
//#define EXTRA_SD_DEBUG 1		// Define this to debug crc check, port r/w

///////////////////////////////////////////////////////////////////////
//
//  FUNCTION:	TOSCASDCard::Encode
//
//  PURPOSE:	Produce a 7-bit CRC value, MSB first.
//
//  INPUTS:		byte					Last round or initial round shift
//											register value (lower 7 bits significant)
//					byte					8-bit Value from byte stream to be CRC'd
//					byte					Usually 8, but last time is 7 to shift in
//											the augment string of 7 zeroes
//
//  OUTPUT:		byte					7-Bit CRC Value
//
//	 NOTES:		CRC register should be seeded initially with the value 0
//					prior to mixing in the variable packet data
//
///////////////////////////////////////////////////////////////////////
byte __fastcall TOSCASDCard::Encode(byte Seed, byte Input, byte Depth)
{
	byte 		regval;      	// shift register byte.
	byte 		count;
	byte 		cc;          	// data to manipulate.

	#define 	POLYNOM (0x9)  // polynomical value to XOR when 1 pops out.

	regval 	= Seed;    		// get prior round's register value.
	cc 		= Input;       // get input byte to generate CRC, MSB first.

	// msb first of byte for Depth elements
	// Set count to 8 or 7, count appropriate # of bits and shift value
	// towards MSB in order to get next bit
	for (count = Depth; count--; cc <<= 1)
	{
		// Shift seven bit register left and put in MSB value of input to LSB.
		regval = (regval << 1) + ((cc & 0x80) ? 1 : 0);

		// Test D7 of shift register as MSB of byte, test if 1 and therefore XOR.
		if (regval & 0x80)
		{
			regval ^= POLYNOM;
		}
	} // end byte loop.

	return (regval & 0x7f);    // return lower 7 bits of CRC as value to use.
}

///////////////////////////////////////////////////////////////////////
//
//  FUNCTION:	TOSCASDCard::MakeCommandCRC
//
//  PURPOSE:	Calculate the correct CRC7 value for the specified command
//					and parameters supplied
//
//  INPUTS:		byte					Command byte value
//					unsigned int		Parameter(s) value
//
//	 OUTPUT:		byte					CRC value calculated from command
//											and parameters supplied
//
///////////////////////////////////////////////////////////////////////
byte __fastcall TOSCASDCard::MakeCommandCRC(byte Command, unsigned int params)
{
	byte CrcAccum = 0;

	// Encode Command value first
	byte nuval = this->Encode(CrcAccum, Command, 8);
	CrcAccum = nuval;

	// Loop through parameter bytes, encoding and aggregating
	for (int i = 24; i >= 0; i -= 8)
	{
		nuval = this->Encode(CrcAccum, ((params >> i) & 0xff), 8);
		CrcAccum = nuval;
	}

	// Mix in last 7 bits of 0's to augment, and then shift final
	// CRC remainder left and or in the fixed '1' LSB
	nuval = this->Encode(CrcAccum, 0, 7);
	CrcAccum = (nuval << 1) | 1;

	return CrcAccum;
}

///////////////////////////////////////////////////////////////////////
//
//  FUNCTION:	TOSCASDCard::GetCtrl2Port
//
//  PURPOSE:	Get the current state of the sys_sdcard_ctrl2 port
//
//  INPUTS:		void
//
//  OUTPUT:		byte					Value of the port where:
//											Bit 0 = Selected (/CS)
//											Bit 1 = Powered (/POWER)
//
//	 NOTES:		The logical polarity of returned value is already
//					compensated for the fact that it's active low by
//					this function
//
///////////////////////////////////////////////////////////////////////
byte __fastcall TOSCASDCard::GetCtrl2Port(void)
{
	byte retv = 0x00;

	retv |= (this->fSelected) ? 0x00 : 0x01;
	retv |= (this->fPowered) ? 0x00 : 0x02;

#ifdef EXTRA_SD_DEBUG
	if ((DiagForm) && (DiagForm->IsActive(DIAG_SDMMC)))
	{
		DiagForm->AddMessage(DIAG_SDMMC, UnicodeString::Format(
			L"Read ctrl2: 0x%2.2x (CS=%s PWR=%s)",
			OPENARRAY(TVarRec, (retv, (fSelected ? L"Y" : L"N"), (fPowered ? L"Y" : L"N")))
		));
	}
#endif

	return retv;
}

///////////////////////////////////////////////////////////////////////
//
//  FUNCTION:	TOSCASDCard::WriteCtrl2Port
//
//  PURPOSE:	Write new value to the sys_sdcard_ctrl2 port
//
//  INPUTS:		byte					Value to be written.
//											Bit 2 = Selected (/CS)
//											Bit 3 = Powered (/POWER)
//
//	 OUTPUT:		void
//
//	 NOTES:		The logical polarity of the data is active-low, so
//					this function will invert the logic to make future
//					handling simpler
//
///////////////////////////////////////////////////////////////////////
void __fastcall TOSCASDCard::WriteCtrl2Port(byte Value)
{
	// X | X | X | X | /POWER | /CS | X | X
	this->fSelected 	= (Value & 0x04) ? false : true;
	this->fPowered 	= (Value & 0x08) ? false : true;

#ifdef EXTRA_SD_DEBUG
	if ((DiagForm) && (DiagForm->IsActive(DIAG_SDMMC)))
	{
		DiagForm->AddMessage(DIAG_SDMMC, UnicodeString::Format(
			L"ctrl2 Write 0x%2.2x (CS=%s PWR=%s)",
			OPENARRAY(TVarRec, (Value, (fSelected ? L"Y" : L"N"), (fPowered ? L"Y" : L"N")))
		));
	}
#endif
}

///////////////////////////////////////////////////////////////////////
//
//  FUNCTION:	TOSCASDCard::WriteCtrl1Port
//
//  PURPOSE:	Write new value to the sys_sdcard_ctrl1 port
//
//  INPUTS:		byte					Value to be written.
//											Bit 6 = SPI Enabled from FPGA
//											Bit 7 = SPI Speed Toggle
//											(0 = 250 KHz, 1 = 8 MHz)
//
//	 OUTPUT:		void
//
///////////////////////////////////////////////////////////////////////
void __fastcall TOSCASDCard::WriteCtrl1Port(byte Value)
{
	this->fSPIEnabled = (Value & 0x40) ? true : false;
	this->fSPISpeed	= (Value & 0x80) ? true : false;

	// SPI_SPEED | FPGA_SD_ENABLE_OUT | ..unused..
#ifdef EXTRA_SD_DEBUG
	if ((DiagForm) && (DiagForm->IsActive(DIAG_SDMMC)))
	{
		DiagForm->AddMessage(DIAG_SDMMC, UnicodeString::Format(
			L"ctrl1 Write 0x%2.2x (SPD=%s SPIEN=%s)",
			OPENARRAY(TVarRec, (Value,
			(this->fSPISpeed ? L"8MHz" : L"250KHz"), (this->fSPIEnabled ? L"Y" : L"N")))
		));
	}
#endif
}

///////////////////////////////////////////////////////////////////////
//
//  FUNCTION:	TOSCASDCard::GetBusy
//
//  PURPOSE:	Determine whether the SD/MMC hardware is currently busy
//
//  INPUTS:		void
//
//	 OUTPUT:		bool					true if the hardware is busy, else false
//
///////////////////////////////////////////////////////////////////////
bool __fastcall TOSCASDCard::GetBusy(void)
{
	return false;
}

///////////////////////////////////////////////////////////////////////
//
//  FUNCTION:	TOSCASDCard::AddToQueue
//
//  PURPOSE:	Local method used to add bytes to the internal queue
//
//  INPUTS:		byte					Value to be queued
//
//	 OUTPUT:		void
//
///////////////////////////////////////////////////////////////////////
void __fastcall TOSCASDCard::AddToQueue(byte Value)
{
	this->fOutputBuffer[this->fBufferWritePointer++] = Value;

	if (this->fBufferWritePointer >= 1024)
	{
		this->fBufferWritePointer = 0;
	}
}

///////////////////////////////////////////////////////////////////////
//
//  FUNCTION:	TOSCASDCard::GetByte
//
//  PURPOSE:	Retrieve the next byte from the internal queue, or
//					0x00 if none is present
//
//  INPUTS:		void
//
//	 OUTPUT:		byte					next byte from the queue (FIFO)
//
///////////////////////////////////////////////////////////////////////
byte __fastcall TOSCASDCard::GetByte(void)
{
	byte retv = 0x00;

	if (this->fBufferReadPointer != this->fBufferWritePointer)
	{
		retv = this->fOutputBuffer[this->fBufferReadPointer++];
		this->fIsReading = true;

		if (this->fBufferReadPointer >= 1024)
		{
			this->fBufferReadPointer = 0;
		}
	}

#ifdef FULL_SD_DEBUG
	if ((DiagForm) && (DiagForm->IsActive(DIAG_SDMMC)))
	{
		DiagForm->AddMessage(DIAG_SDMMC, UnicodeString::Format(
			L"Read spi_port: 0x%2.2x",
			OPENARRAY(TVarRec, (retv))
		));
	}
#endif

	return retv;
}

///////////////////////////////////////////////////////////////////////
//
//  FUNCTION:	TOSCASDCard::HandleCommand
//
//  PURPOSE:	Handle a received command
//
//  INPUTS:		byte					Checksum value supplied as part
//											of the incoming command
//
//	 OUTPUT:		void
//
///////////////////////////////////////////////////////////////////////
void __fastcall TOSCASDCard::HandleCommand(byte Checksum)
{

#ifdef EXTRA_SD_DEBUG
	// Calculate the _correct_ CRC7 for the relevant command
	byte testCrc = this->MakeCommandCRC(this->fCurrentCommand | 0x40, this->fCurrentParams);

	if ((DiagForm) && (DiagForm->IsActive(DIAG_SDMMC)))
	{
		DiagForm->AddMessage(DIAG_SDMMC, UnicodeString::Format(
			L"Cksum: 0x%2.2x Expected: 0x%2.2x",
			OPENARRAY(TVarRec, (Checksum, testCrc))
		));
	}
#endif

	// Check if this is an application-specific command being processed
	if (this->fWaitingAppCmd)
	{
		// It is, so act on it accordingly
		switch (this->fCurrentCommand)
		{
			case ACMD41_SEND_OP_COND:
				if ((DiagForm) && (DiagForm->IsActive(DIAG_SDMMC)))
				{
					DiagForm->AddMessage(DIAG_SDMMC, L"Processing ACMD41_SEND_OP_COND");
				}

				this->AddToQueue(0xff);		// Ncr (Command<->Response Delay)
				this->AddToQueue(0x00);		// Write Response - OK
				this->fCurrentOpMode = SD_CMDWAIT;
				break;
			default:
				if ((DiagForm) && (DiagForm->IsActive(DIAG_SDMMC)))
				{
					DiagForm->AddMessage(DIAG_SDMMC, UnicodeString::Format(
						L"Unsupported AppCmd: 0x%2.2x",
						OPENARRAY(TVarRec, (this->fCurrentCommand))
					));
				}

				this->fCurrentOpMode = SD_CMDWAIT;
				break;
		}

		this->fWaitingAppCmd = false;
	}
	else
	{
		// It's not an app-specific command, so handle a basic command
		switch(this->fCurrentCommand)
		{
			case CMD0_GO_IDLE_STATE:
				if ((DiagForm) && (DiagForm->IsActive(DIAG_SDMMC)))
				{
					DiagForm->AddMessage(DIAG_SDMMC, L"CMD0_GO_IDLE_STATE");
				}

				this->FlushQueue();
				this->AddToQueue(0xff);		// Ncr (Command<->Response Delay)
				this->AddToQueue(0x01);		// Write Response - IDLE State
				this->fCurrentOpMode = SD_CMDWAIT;
				break;
			case CMD9_SEND_CSD:
				{
				if ((DiagForm) && (DiagForm->IsActive(DIAG_SDMMC)))
					{
						DiagForm->AddMessage(DIAG_SDMMC, L"CMD9_SEND_CSD");
					}

					this->fCurrentOpMode = SD_CMDWAIT;
					word blockCount = 0;

					if (this->fImageStream != NULL)
					{
						blockCount = this->fImageStream->Size / (256 * 1024);	// Nr 256k (C_MULT) blks
					}

					byte csdValues[] = {
						0xff, 0x00, 0xfe,			// NCR, OK, START
						0x00, 0x0e, 0x00, 0x32, // Bytes 0, 1, 2, 3
						0x5d, 0x59,  				// Bytes 4, 5

						// CSIZE follows here
						(blockCount & 0xc00) >> 10,	// Bits 11..10
						(blockCount & 0x3fc) >> 2,		// Bits 9..2
						(blockCount & 0x03) << 6,		// Bits 1..0

						// 7f, 80, 0a
						0x03, 0x80, 0x00, 0x02,	// Bytes 9, 10, 11, 12

						0x40,	0x00, 0x01,			// Bytes 13, 14, 15
						0xff, 0xff					// Data Send Checksum
					};

					this->FlushQueue();
					for (int i = 0; i < 21; i++)
					{
						this->AddToQueue(csdValues[i]);
					}
				}
				break;
			case CMD10_SEND_CID:
				{
					if ((DiagForm) && (DiagForm->IsActive(DIAG_SDMMC)))
					{
						DiagForm->AddMessage(DIAG_SDMMC, L"CMD10_SEND_CID");
					}

					this->fCurrentOpMode = SD_CMDWAIT;
					byte cidValues[] = {
						0xff, 0x00, 0xfe,	// NCR, OK, START
						0x02,					// Sandisk Manufacturer ID
						'O', 'E',			// OEM/Application ID
						'M', 'U', 'L',		// ID String
						'8', 'R',
						VERBYTE,				// Revision (BCD)
						0x01, 0x23, 		// Serial No
						0x45, 0x67,
						// Date is now XXXXYYYY YYYYMMMM with Year 0 = 2000
						0xac, 0x00,			// Date (MY - offset from 1997)
						0x01,					// Card Checksum (LSB always 1)
						0xff, 0xff			// Data Send Checksum
					};

					this->FlushQueue();
					for (int i = 0; i < 21; i++)
					{
						this->AddToQueue(cidValues[i]);
					}
				}
				break;
			case CMD17_READ_SINGLE_BLOCK:
				if ((DiagForm) && (DiagForm->IsActive(DIAG_SDMMC)))
				{
					DiagForm->AddMessage(DIAG_SDMMC, UnicodeString::Format(
						L"CMD55_READ_SINGLE_BLOCK (0x%8.8x)",
						OPENARRAY(TVarRec, (this->fCurrentParams))
					));
				}

				if (this->fImageStream != NULL)
				{
					this->FlushQueue();
					this->AddToQueue(0x00);		// Write Response - OK
					this->AddToQueue(0xfe);		// Start Token

					this->fImageStream->Seek(this->fCurrentParams, soBeginning);

					byte Value = 0x00;
					for (int i = 0; i < 512; i++)
					{
						this->fImageStream->Read(&Value, 1);
						this->AddToQueue(Value);
					}

					this->AddToQueue(0xff);		// Dummy CRC16
					this->AddToQueue(0xff);		// Dummy CRC16
				}
				else
				{
					this->FlushQueue();
					this->AddToQueue(R1_ADDR_ERR | R1_PARAM_ERR);		// Write Response - ERROR
				}

				this->fCurrentOpMode = SD_CMDWAIT;
				break;
			case CMD24_WRITE_BLOCK:
				if ((DiagForm) && (DiagForm->IsActive(DIAG_SDMMC)))
				{
					DiagForm->AddMessage(DIAG_SDMMC, UnicodeString::Format(
						L"CMD24_WRITE_BLOCK (0x%8.8x)",
						OPENARRAY(TVarRec, (this->fCurrentParams))
					));
				}

				if (this->fImageStream != NULL)
				{
					this->fImageStream->Seek(this->fCurrentParams, soBeginning);
					this->AddToQueue(0xff);		// Ncr (Command<->Response Delay)
					this->AddToQueue(0x00);		// Write Response - OK
				}
				this->fCurrentOpMode = SD_WAITWRSTART;
				break;
			case CMD55_APP_CMD:
				if ((DiagForm) && (DiagForm->IsActive(DIAG_SDMMC)))
				{
					DiagForm->AddMessage(DIAG_SDMMC, L"CMD55_APP_CMD");
				}

				this->AddToQueue(0xff);		// Ncr (Command<->Response Delay)
				this->AddToQueue(0x00);		// Write Response - OK
				this->fWaitingAppCmd = true;
				this->fCurrentOpMode = SD_CMDWAIT;
				break;
			default:
				if ((DiagForm) && (DiagForm->IsActive(DIAG_SDMMC)))
				{
					DiagForm->AddMessage(DIAG_SDMMC, UnicodeString::Format(
						L"Unsupported Cmd: 0x%2.2x",
						OPENARRAY(TVarRec, (this->fCurrentCommand))
					));
				}

				this->fCurrentOpMode = SD_CMDWAIT;
				break;
		}
	}
}

///////////////////////////////////////////////////////////////////////
//
//  FUNCTION:	TOSCASDCard::WriteByte
//
//  PURPOSE:	Function called when a byte is written to the hardware
//					by the FPGA.
//
//  INPUTS:		byte					Byte value to be written
//
//	 OUTPUT:		void
//
///////////////////////////////////////////////////////////////////////
void __fastcall TOSCASDCard::WriteByte(byte Value)
{
	switch(this->fCurrentOpMode)
	{
		case SD_CMDWAIT:
			if ((Value & 0xc0) != 0x40)
			{
#ifdef FULL_SD_DEBUG
				if ((DiagForm) && (DiagForm->IsActive(DIAG_SDMMC)))
				{
					DiagForm->AddMessage(DIAG_SDMMC, UnicodeString::Format(
						L"Ignoring bad command: 0x%2.2x",
						OPENARRAY(TVarRec, (Value))
					));
				}
#endif
			}
			else
			{
				this->fCurrentCommand = (Value & 0x3f);
				this->fCurrentParams = 0;
				this->fCurrentOpMode = SD_WAITA1;

#ifdef FULL_SD_DEBUG
				if ((DiagForm) && (DiagForm->IsActive(DIAG_SDMMC)))
				{
					DiagForm->AddMessage(DIAG_SDMMC, UnicodeString::Format(
						L"Received Command: 0x%2.2x",
						OPENARRAY(TVarRec, (this->fCurrentCommand))
					));
				}
#endif
			}
			break;
		case SD_WAITA1:
			this->fCurrentParams <<= 8;
			this->fCurrentParams |= Value;
			this->fCurrentOpMode = SD_WAITA2;
			break;
		case SD_WAITA2:
			this->fCurrentParams <<= 8;
			this->fCurrentParams |= Value;
			this->fCurrentOpMode = SD_WAITA3;
			break;
		case SD_WAITA3:
			this->fCurrentParams <<= 8;
			this->fCurrentParams |= Value;
			this->fCurrentOpMode = SD_WAITA4;
			break;
		case SD_WAITA4:
			this->fCurrentParams <<= 8;
			this->fCurrentParams |= Value;
			this->fCurrentOpMode = SD_WAITCHK;

#ifdef FULL_SD_DEBUG
			if ((DiagForm) && (DiagForm->IsActive(DIAG_SDMMC)))
			{
				DiagForm->AddMessage(DIAG_SDMMC, UnicodeString::Format(
					L"Received Params: 0x%8.8x",
					OPENARRAY(TVarRec, (this->fCurrentParams))
				));
			}
#endif
			break;
		case SD_WAITCHK:
			this->HandleCommand(Value);	// Pass supplied checksum
			break;
		case SD_WAITWRSTART:
			if (Value == 0xfe)
			{
				// Start Token
				if ((DiagForm) && (DiagForm->IsActive(DIAG_SDMMC)))
				{
					DiagForm->AddMessage(DIAG_SDMMC, L"Write Start Token Received");
				}
				this->fWriteOffset 		= 0;
				this->fCurrentOpMode 	= SD_EXECUTE;
			}
			else if (Value != 0xff)
			{
				// Anything else besides start token and "dead" clocks
				if ((DiagForm) && (DiagForm->IsActive(DIAG_SDMMC)))
				{
					DiagForm->AddMessage(DIAG_SDMMC, L"Non-$FF Token Received");
				}
				fCurrentOpMode = SD_CMDWAIT;
			}
			break;
		case SD_EXECUTE:
			switch (this->fCurrentCommand)
			{
				case CMD24_WRITE_BLOCK:
					if (this->fImageStream != NULL)
					{
						this->fIsWriting = true;
						this->fImageStream->Write(&Value, 1);
						this->fChangesPending = true;
						this->fWriteOffset++;

						if (this->fWriteOffset == 512)
						{
							this->AddToQueue(0x04);		// Write Response - OK
							this->AddToQueue(0xff);		// Ncr (Command<->Response Delay)
							this->fCurrentOpMode = SD_CMDWAIT;
						}
					}
					break;
				default:
					if ((DiagForm) && (DiagForm->IsActive(DIAG_SDMMC)))
					{
						DiagForm->AddMessage(DIAG_SDMMC, UnicodeString::Format(
							L"spi_port Write: 0x%2.2x",
							OPENARRAY(TVarRec, (Value))
						));
					}
					break;
			}
			break;
		default:
			if ((DiagForm) && (DiagForm->IsActive(DIAG_SDMMC)))
			{
				DiagForm->AddMessage(DIAG_SDMMC, UnicodeString::Format(
					L"spi_port Write: 0x%2.2x",
					OPENARRAY(TVarRec, (Value))
				));
			}
			break;
	}
}

///////////////////////////////////////////////////////////////////////
//
//  FUNCTION:	TOSCASDCard::Tick
//
//  PURPOSE:	Method called periodically to enable synching the
//					device clock against the CPU clock
//
//  INPUTS:		unsigned int		Number of cycles elapsed since last
//											time this method was called
//
//  OUTPUT:		void
//
///////////////////////////////////////////////////////////////////////
void __fastcall TOSCASDCard::Tick(unsigned int Cycles)
{
}

///////////////////////////////////////////////////////////////////////
//
//  FUNCTION:	TOSCASDCard::FlushQueue
//
//  PURPOSE:	Flush the serial buffer/queue
//
//  INPUTS:		void
//
//	 OUTPUT:		void
//
///////////////////////////////////////////////////////////////////////
void __fastcall TOSCASDCard::FlushQueue(void)
{
	this->fBufferWritePointer	= 0;
	this->fBufferReadPointer	= 0;

	// Clear buffer
	memset(&this->fOutputBuffer[0], 0, 1024);
}

///////////////////////////////////////////////////////////////////////
//
//  FUNCTION:	TOSCASDCard::AttachImage
//
//  PURPOSE:	Attach a file image to the SD Card hardware.
//
//  INPUTS:		UnicodeString		Filename of the image to be attached
//
//  OUTPUT:		void
//
///////////////////////////////////////////////////////////////////////
void __fastcall TOSCASDCard::AttachImage(UnicodeString imageName)
{
	// Close image stream if required
	if (this->fImageStream != NULL)
	{
		delete this->fImageStream;
		this->fImageStream = NULL;
	}

	this->fImageStream = new TMemoryStream();

	// Check that the memory stream was created
	if (this->fImageStream != NULL)
	{
		// Attempt to load the filename specified
		try
		{
			this->fImageStream->LoadFromFile(imageName);
		}
		catch (...)
		{
			// Error occurred ... cleanup
			delete this->fImageStream;
			this->fImageStream = NULL;
		}
	}

	// Mark the hardware as "clean" in terms of writes pending
	this->fChangesPending = false;
}

///////////////////////////////////////////////////////////////////////
//
//  FUNCTION:	TOSCASDCard::WriteToImage
//
//  PURPOSE:	Write the current image contents to a disk image file
//
//  INPUTS:		UnicodeString		Filename of the image to be written
//
//  OUTPUT:		void
//
///////////////////////////////////////////////////////////////////////
void __fastcall TOSCASDCard::WriteToImage(UnicodeString imageName)
{
	// Close image stream if required
	if (this->fImageStream != NULL)
	{
		// Attempt to write to the filename specified
		try
		{
			this->fImageStream->SaveToFile(imageName);
		}
		catch (...)
		{
			// Error occurred ... cleanup
		}
	}

	// Mark the hardware as "clean" in terms of writes pending
	this->fChangesPending = false;
}

///////////////////////////////////////////////////////////////////////
//
//  FUNCTION:	TOSCASDCard::GetChangesPending
//
//  PURPOSE:	Helper function used to determine whether the SD/MMC
//					image contains pending (i.e. unwritten) sectors and
//					therefore needs to be written to disk
//
//  INPUTS:		void
//
//  OUTPUT:		bool					true if there are pending writes to make
//
///////////////////////////////////////////////////////////////////////
bool __fastcall TOSCASDCard::GetChangesPending(void)
{
	return fChangesPending;
}

///////////////////////////////////////////////////////////////////////
//
//  FUNCTION:	TOSCASDCard::SDReading
//
//  PURPOSE:	Helper function used to determine whether the SD/MMC
//					hardware is in the process of performing a Read operation
//
//  INPUTS:		void
//
//  OUTPUT:		bool					true if the device is reading, else false
//
///////////////////////////////////////////////////////////////////////
bool __fastcall TOSCASDCard::IsReading(void)
{
	if (this->fIsReading)
	{
		this->fIsReading = false;
		return true;
	}

	return false;
}

///////////////////////////////////////////////////////////////////////
//
//  FUNCTION:	TOSCASDCard::IsWriting
//
//  PURPOSE:	Helper function used to determine whether the SD/MMC
//					hardware is in the process of performing a Write operation
//
//  INPUTS:		void
//
//  OUTPUT:		bool					true if the device is writing, else false
//
///////////////////////////////////////////////////////////////////////
bool __fastcall TOSCASDCard::IsWriting(void)
{
	if (this->fIsWriting)
	{
		this->fIsWriting = false;
		return true;
	}

	return false;
}

///////////////////////////////////////////////////////////////////////
//
//  FUNCTION:	TOSCASDCard::IsAttached
//
//  PURPOSE:	Helper function used to determine whether the SD/MMC
//					driver currently has a viable disk imaged attached to it.
//					Viable means that an image of non-zero-length was
//					previously attached using the AttachImage method
//
//  INPUTS:		void
//
//  OUTPUT:		bool					true if a viable image is attached
//
///////////////////////////////////////////////////////////////////////
bool __fastcall TOSCASDCard::IsAttached(void)
{
	if (this->fImageStream == NULL)
	{
		// No memory stream, therefore nothing attached
		return false;
	}
	else if (this->fImageStream->Size > 0)
	{
		// Something is attached, and there's data present
		return true;
	}

	return false;
}

///////////////////////////////////////////////////////////////////////
//
//  FUNCTION:	TOSCASDCard::Reset
//
//  PURPOSE:	Perform a power-on-reset of the SD Card Device
//
//  INPUTS:		void
//
//	 OUTPUT:		void
//
///////////////////////////////////////////////////////////////////////
void __fastcall TOSCASDCard::Reset(void)
{
	// Initialise all variables at reset
	this->fPowered				= false;
	this->fSelected			= false;
	this->fSPIEnabled			= false;
	this->fSPISpeed			= false;		// default to 250 KHz
	this->fCurrentOpMode		= SD_CMDWAIT;
	this->fWaitingAppCmd		= false;
	this->fIsReading			= false;
	this->fIsWriting			= false;
	this->fChangesPending	= false;

	this->FlushQueue();
}

///////////////////////////////////////////////////////////////////////
//
//  FUNCTION:	TOSCASDCard::TOSCASDCard [CONSTRUCTOR]
//
//  PURPOSE:	Constructor for the SD Card Handler Class
//
//  INPUTS:		none
//
//	 OUTPUT:		none
//
///////////////////////////////////////////////////////////////////////
__fastcall TOSCASDCard::TOSCASDCard()
{
	// Perform initial Power-On-Reset
	this->fImageStream = NULL;
	this->Reset();
}

///////////////////////////////////////////////////////////////////////
//
//  FUNCTION:	TOSCASDCard::~TOSCASDCard [DESTRUCTOR]
//
//  PURPOSE:	Destructor for the SD Card Handler Class
//
//  INPUTS:		none
//
//	 OUTPUT:		none
//
///////////////////////////////////////////////////////////////////////
__fastcall TOSCASDCard::~TOSCASDCard()
{
	// Close image stream if required
	if (this->fImageStream != NULL)
	{
		delete this->fImageStream;
		this->fImageStream = NULL;
	}
}
//---------------------------------------------------------------------------

#pragma package(smart_init)

