namespace Template
{
	class CacheLine
	{
		public CacheLine() { data = new uint[16]; }
		public uint[] data; // 64 bytes
		public bool valid, dirty;
	}
	class Cache
	{
		uint[] image = new uint[800 * 800 * 4]; // 800 * 800 * 16 bytes

		// artificial delay
		void Delay(){for(int i=0;++i<1000;)image[i]=image[i+1000];}

		// reading 32-bit values
		public uint Read32bit( uint address )
		{
			// TODO: prevent reads from RAM using a cache
			// ...

			// cache read miss: read data from RAM
			CacheLine line = new CacheLine();
			LoadLineFromMem( address, ref line );
			return line.data[(address & 63) >> 2];

			// TODO: store the data in the cache
			// ...
		}

		// writing 32-bit values
		public void Write32bit( uint address, uint value )
		{
			// TODO: prevent writes to RAM using a cache
			// ...

			// cache write miss: write data to RAM
			CacheLine line = new CacheLine();
			LoadLineFromMem( address, ref line );
			line.data[(address & 63) >> 2] = value;
			WriteLineToMem( address, ref line );

			// TODO: write to cache instead of RAM; evict to RAM if necessary
			// ...
		}

		// ============================================================================
		// CACHE SIMULATOR LOW LEVEL MEMORY ACCESS WITH LATENCY SIMULATION
		// ============================================================================

		// load a cache line from memory; simulate RAM latency
		void LoadLineFromMem( uint address, ref CacheLine line )
		{
			uint lineAddress = address & 0xFFFFFFC0; // set last six bits to 0
			for( int i = 0; i < 16; i++ ) line.data[i] = image[(lineAddress >> 2) + i]; // fetch 64 bytes into line
			Delay();
		}

		// write a cache line to memory; simulate RAM latency
		void WriteLineToMem( uint address, ref CacheLine line )
		{
			uint lineAddress = address & 0xFFFFFFC0; // set last six bit to 0
			for( int i = 0; i < 16; i++ ) image[(lineAddress >> 2) + i] = line.data[i]; // write 64 bytes into line
			Delay();
		}
	}
}
