/*
 * c't-Bot
 * 
 * This program 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 2 of the License, or (at your
 * option) any later version. 
 * This program 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 this program; if not, write to the Free 
 * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 * MA 02111-1307, USA.
 * 
 */

/*! 
 * @file 	mmc-vm.c
 * @brief 	Virtual Memory Management mit MMC / SD-Card
 * @author 	Timo Sandmann (mail@timosandmann.de)
 * @date 	30.11.2006
 * @see		Documentation/mmc-vm.html
 */


//TODO:	* Statistikausgabe fuer MCU ergaenzen
//		* Code optimieren, Groesse und Speed

#include "ct-Bot.h"  

#ifdef MMC_VM_AVAILABLE

#include "mmc-vm.h"
#include "mmc.h"
#include "mmc-low.h"
#include "mmc-emu.h"
#include "display.h"
#include "timer.h"
#include "mini-fat.h"
#include "log.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#ifdef MCU
	#define MMC_START_ADDRESS 0x2000000	// [512;2^32-1] in Byte - Sinnvoll ist z.B. Haelfte der MMC / SD-Card Groesse, der Speicherplatz davor kann dann fuer ein Dateisystem verwendet werden
	#define MAX_SPACE_IN_SRAM 6			// [1;127] - Pro Page werden 512 Byte im SRAM belegt, sobald diese verwendet wird
	#define swap_out	mmc_write_sector
	#define swap_in		mmc_read_sector
	#define swap_space	mmc_get_size()
#else
	#define MMC_START_ADDRESS 0x1000000	// [512;2^32-1]
	#define MAX_SPACE_IN_SRAM 2			// [1;127] - Pro Page werden 512 Byte im RAM belegt, sobald diese verwendet wird
	#define swap_out	mmc_emu_write_sector
	#define swap_in		mmc_emu_read_sector
	#define swap_space	mmc_emu_get_size()
#endif	

#if MMC_ASYNC_WRITE == 1
	#define MAX_PAGES_IN_SRAM MAX_SPACE_IN_SRAM-1
	static uint8* swap_buffer;	/*!< Puffer fuer asynchrones write-back */
#else
	#define MAX_PAGES_IN_SRAM MAX_SPACE_IN_SRAM
#endif

typedef struct{			/*!< Struktur eines Cacheeintrags */
	uint32	addr;		/*!< Tag = MMC-Blockadresse der ins RAM geladenen Seite */ 
	uint8*	p_data;		/*!< Daten = Zeiger auf 512 Byte grosse Seite im RAM */ 
	#if MAX_PAGES_IN_SRAM > 2
		uint8	succ;	/*!< Zeiger auf Nachfolger (LRU) */
		uint8	prec;	/*!< Zeiger auf Vorgaenger (LRU) */
	#endif
	uint8	dirty;		/*!< Dirty-Bit (0: Daten wurden bereits auf die MMC / SD-Card zurueckgeschrieben) */
} vm_cache_t;

#ifdef VM_STATS_AVAILABLE
	typedef struct{
		uint32 page_access;		/*!< Anzahl der Seitenzugriffe seit Systemstart */
		uint32 swap_ins;		/*!< Anzahl der Seiteneinlagerungen seit Systemstart */
		uint32 swap_outs;		/*!< Anzahl der Seitenauslagerungen seit Systemstart */
		uint32 vm_used_bytes;	/*!< Anzahl der vom VM belegten Bytes auf der MMC / SD-Card */
		uint32 time;			/*!< Timestamp bei erster Speicheranforderung */
	} vm_stats_t;
	
	vm_stats_t stats_data = {0};	
#endif

static uint32 mmc_start_address = MMC_START_ADDRESS;	/*!< physische Adresse der MMC / SD-Card, wo unser VM beginnt */
static uint32 next_mmc_address = MMC_START_ADDRESS;		/*!< naechste freie virtuelle Adresse */
static uint8 pages_in_sram = MAX_PAGES_IN_SRAM;			/*!< Groesse des Caches im RAM */
static int8 allocated_pages = 0;						/*!< Anzahl bereits belegter Cachebloecke */
static uint8 oldest_cacheblock = 0;						/*!< Zeiger auf den am laengsten nicht genutzten Eintrag (LRU) */
static uint8 recent_cacheblock = 0;						/*!< Zeiger auf den letzten genutzten Eintrag (LRU) */

vm_cache_t page_cache[MAX_PAGES_IN_SRAM];				/*!< der eigentliche Cache, vollassoziativ, LRU-Policy */

/*! 
 * Gibt die Blockadresse einer Seite zurueck
 * @param addr	Eine virtuelle Adresse
 * @return		Adresse
 * @author 		Timo Sandmann (mail@timosandmann.de)
 * @date 		30.11.2006
 */
inline uint32 mmc_get_mmcblock_of_page(uint32 addr){
	#ifdef MCU
		/* Eine effizientere Variante von addr >> 9 */
		asm volatile(
			"mov %A0, %B0	\n\t"
			"mov %B0, %C0	\n\t"
			"mov %C0, %D0	\n\t"
			"lsr %C0		\n\t"
			"ror %B0		\n\t"
			"ror %A0		\n\t"		
			"clr %D0			"		
			:	"=r"	(addr)
			:	"0"		(addr)
		);	
		return addr;
	#else
		return addr >> 9;
	#endif	// MCU		
}

#ifdef VM_STATS_AVAILABLE
	/*! 
	 * Gibt die Anzahl der Pagefaults seit Systemstart bzw. Ueberlauf zurueck
	 * @return		#Pagefaults
	 * @author 		Timo Sandmann (mail@timosandmann.de)
	 * @date 		30.11.2006
	 */
	inline uint32 mmc_get_pagefaults(void){
		return stats_data.swap_ins;
	}

	/*! 
	 * Erstellt eine kleine Statistik ueber den VM
	 * @return		Zeiger auf Statistikdaten
	 * @date 		01.01.2007
	 */	
	vm_extern_stats_t* mmc_get_vm_stats(void){
		static vm_extern_stats_t extern_stats = {0};
		memcpy(&extern_stats, &stats_data, sizeof(vm_stats_t));	// .time wird spaeter ueberschrieben
		uint16 delta_t = TICKS_TO_MS(TIMER_GET_TICKCOUNT_32 - stats_data.time)/1000;
		if (delta_t == 0) delta_t = 1;
		extern_stats.page_access_s = extern_stats.page_access / delta_t;
		extern_stats.swap_ins_s = extern_stats.swap_ins / delta_t;
		extern_stats.swap_outs_s = extern_stats.swap_outs / delta_t;		
		extern_stats.device_size = swap_space;
		extern_stats.vm_size = swap_space - mmc_start_address;
		extern_stats.cache_size = pages_in_sram;
		extern_stats.cache_load = allocated_pages;
		extern_stats.delta_t = delta_t;
		return &extern_stats;	
	}

	/*! 
	 * Gibt eine kleine Statistik ueber den VM aus (derzeit nur am PC)
	 * @date 		01.01.2007
	 */		
	void mmc_print_statistic(void){
		#ifdef PC
			vm_extern_stats_t* vm_stats = mmc_get_vm_stats();
			printf("\n\r*** VM-Statistik *** \n\r");
			printf("Groesse des Volumes: \t\t%lu MByte \n\r", vm_stats->device_size>>20);
			printf("Groesse des VM: \t\t%lu MByte \n\r", vm_stats->vm_size>>20);
			printf("Belegter virt. Speicher: \t%lu KByte \n\r", vm_stats->vm_used_bytes>>10);									
			printf("Groesse des Caches: \t\t%u Byte \n\r", (uint16)vm_stats->cache_size<<9);
			printf("Auslastung des Caches: \t\t%u %% \n\r", ((uint16)vm_stats->cache_load<<9)/((uint16)vm_stats->cache_size<<9)*100);
			printf("Seitenzugriffe: \t\t%lu \n\r", vm_stats->page_access);
			printf("Seiteneinlagerungen: \t\t%lu \n\r", vm_stats->swap_ins);
			printf("Seitenauslagerungen: \t\t%lu \n\r", vm_stats->swap_outs);
			printf("Seitenzugriffe / s: \t\t%u \n\r", vm_stats->page_access_s);
			printf("Seiteneinlagerungen / s: \t%u \n\r", vm_stats->swap_ins_s);
			printf("Seitenauslagerungen / s: \t%u \n\r", vm_stats->swap_outs_s);
			printf("Cache-Hit-Rate: \t\t%f %% \n\r", (100.0-((float)vm_stats->swap_ins/(float)vm_stats->page_access)*100.0));
			printf("Messdauer: \t\t\t%u s \n\r", vm_stats->delta_t);
		#else
			/* Ausgabe fuer MCU derzeit nur ueber Logging */
			#ifdef LOG_AVAILABLE 
				vm_extern_stats_t* vm_stats = mmc_get_vm_stats();
				/* Texte wie oben */
				LOG_INFO(("%lu", vm_stats->device_size>>20));
				LOG_INFO(("%lu", vm_stats->vm_size>>20));
				LOG_INFO(("%lu", vm_stats->vm_used_bytes>>10));									
				LOG_INFO(("%u", (uint16)vm_stats->cache_size<<9));
				LOG_INFO(("%u", ((uint16)vm_stats->cache_load<<9)/((uint16)vm_stats->cache_size<<9)*100));
				LOG_INFO(("%lu", vm_stats->page_access));
				LOG_INFO(("%lu", vm_stats->swap_ins));
				LOG_INFO(("%lu", vm_stats->swap_outs));
				LOG_INFO(("%u", vm_stats->page_access_s));
				LOG_INFO(("%u", vm_stats->swap_ins_s));
				LOG_INFO(("%u", vm_stats->swap_outs_s));
				LOG_INFO(("%u", (uint8)(100.0-((float)vm_stats->swap_ins/(float)vm_stats->page_access)*100.0)));
				LOG_INFO(("%u", vm_stats->delta_t));				
			#endif
			// TODO: Display-Ausgabe?
		#endif	
	}
#endif

/*! 
 * Gibt die letzte Adresse einer Seite zurueck
 * @param addr	Eine virtuelle Adresse
 * @return		Adresse 
 * @author 		Timo Sandmann (mail@timosandmann.de)
 * @date 		30.11.2006
 */
inline uint32 mmc_get_end_of_page(uint32 addr){
	return addr | 0x1ff;	// die unteren 9 Bit sind gesetzt, da Blockgroesse = 512 Byte
}

/*! 
 * Gibt den Index einer Seite im Cache zurueck
 * @param addr	Eine virtuelle Adresse
 * @return		Index des Cacheblocks, -1 falls Cache-Miss
 * @author 		Timo Sandmann (mail@timosandmann.de)
 * @date 		30.11.2006
 */
int8 mmc_get_cacheblock_of_page(uint32 addr){
	uint32 page_addr = mmc_get_mmcblock_of_page(addr);
	int8 i;
	for (i=0; i<allocated_pages; i++){	// O(n)
		if (page_cache[i].addr == page_addr) return i;	// Seite gefunden :)
	}
	return -1;	// Seite nicht im Cache
}

/*! 
 * Laedt eine Seite in den Cache, falls sie noch nicht geladen ist
 * @param addr	Eine virtuelle Adresse
 * @return		0: ok, 1: ungueltige Adresse, 2: Fehler bei swap_out, 3: Fehler bei swap_in
 * @author 		Timo Sandmann (mail@timosandmann.de)
 * @date 		30.11.2006
 */
uint8 mmc_load_page(uint32 addr){
	if (addr >= next_mmc_address) return 1;	// ungueltige virtuelle Adresse :(
	#ifdef VM_STATS_AVAILABLE
		stats_data.page_access++;
	#endif
	int8 cacheblock = mmc_get_cacheblock_of_page(addr); 
	if (cacheblock >= 0){	// Cache-Hit, Seite ist bereits geladen :)
		/* LRU */	
		#if MAX_PAGES_IN_SRAM > 2
			if (recent_cacheblock == cacheblock) page_cache[cacheblock].succ = cacheblock;	// Nachfolger des neuesten Eintrags ist die Identitaet
			if (oldest_cacheblock == cacheblock){
				oldest_cacheblock = page_cache[cacheblock].succ;	// Nachfolger ist neuer aeltester Eintrag
				page_cache[page_cache[cacheblock].succ].prec = oldest_cacheblock;	// Vorgaenger der Nachfolgers ist seine Identitaet				
			}  
			else{
				page_cache[page_cache[cacheblock].prec].succ = page_cache[cacheblock].succ;	// Nachfolger des Vorgaengers ist eigener Nachfolger
				page_cache[page_cache[cacheblock].succ].prec = page_cache[cacheblock].prec;	// Vorganeger des Nachfolgers ist eigener Vorgaenger
			}
			page_cache[cacheblock].prec = recent_cacheblock;	// alter neuester Eintrag ist neuer Vorgaenger
		#else
			oldest_cacheblock = (pages_in_sram - 1) - cacheblock;	// aeltester Eintrag ist nun der andere Cacheblock (wenn verfuegbar)
		#endif
		recent_cacheblock = cacheblock;						// neuester Eintrag ist nun die Identitaet
		return 0;
	}
	/* Cache-Miss => neue Seite einlagern, LRU Policy */
	int8 next_cacheblock = oldest_cacheblock;
	if (allocated_pages < pages_in_sram){
		/* Es ist noch Platz im Cache */
		next_cacheblock = allocated_pages;
		page_cache[next_cacheblock].p_data = malloc(512);	// Speicher anfordern
		if (page_cache[next_cacheblock].p_data == NULL){	// Da will uns jemand keinen Speicher mehr geben :(
			if (pages_in_sram == 1) return 1;	// Hier ging was schief, das wir so nicht loesen koennen
			/* Nicht mehr genug Speicher im RAM frei => neuer Versuch mit verkleinertem Cache */
			pages_in_sram--;
			return mmc_load_page(addr);
		}
		allocated_pages++;	// Cache-Fuellstand aktualisieren		
	}
	/* Pager muss nun aktiv werden */
	#ifdef VM_STATS_AVAILABLE
		stats_data.swap_ins++;
	#endif
	#if MMC_ASYNC_WRITE == 1	// im asnychronen Fall holen wir erst die neue Seite, dann kann sich das Zurueckschreiben ruhig Zeit lassen
		uint8* p_tmp = page_cache[next_cacheblock].p_data;
		if (swap_in(mmc_get_mmcblock_of_page(addr), swap_buffer) != 0) return 3;
	#endif
	if (page_cache[next_cacheblock].dirty == 1){	// Seite zurueckschreiben, falls Daten veraendert wurden
		#ifdef VM_STATS_AVAILABLE
			stats_data.swap_outs++;
		#endif
		if (swap_out(page_cache[next_cacheblock].addr, page_cache[next_cacheblock].p_data, MMC_ASYNC_WRITE) != 0) return 2;
	}
	#if MMC_ASYNC_WRITE == 1
		page_cache[next_cacheblock].p_data = swap_buffer;
		swap_buffer = p_tmp;	
	#else
		if (swap_in(mmc_get_mmcblock_of_page(addr), page_cache[next_cacheblock].p_data) != 0) return 3;
	#endif
	#if MAX_PAGES_IN_SRAM > 2
		oldest_cacheblock = page_cache[oldest_cacheblock].succ;	// Nachfolger des aeltesten Eintrags ist neuer aeltester Eintrag
	#else
		oldest_cacheblock = (pages_in_sram - 1) - next_cacheblock;	// neuer aeltester Eintrag ist nun der andere Cacheblock (wenn verfuegbar)
	#endif
	page_cache[next_cacheblock].addr = mmc_get_mmcblock_of_page(addr);	// Cache-Tag aktualisieren
	/* LRU */
	#if MAX_PAGES_IN_SRAM > 2
		page_cache[next_cacheblock].prec = recent_cacheblock;	// Vorgaenger dieses Cacheblocks ist der bisher neueste Eintrag
		page_cache[recent_cacheblock].succ = next_cacheblock;	// Nachfolger des bisher neuesten Eintrags ist dieser Cacheblock  
	#endif
	recent_cacheblock = next_cacheblock;					// Dieser Cacheblock ist der neueste Eintrag
	return 0;
}

/*! 
 * Fordert virtuellen Speicher an
 * @param size		Groesse des gewuenschten Speicherblocks
 * @param aligned	0: egal, 1: 512 Byte ausgerichtet
 * @return			Virtuelle Anfangsadresse des angeforderten Speicherblocks, 0 falls Fehler 
 * @author 			Timo Sandmann (mail@timosandmann.de)
 * @date 			30.11.2006
 */
uint32 mmcalloc(uint32 size, uint8 aligned){
	if (next_mmc_address == mmc_start_address){
		// TODO: Init-stuff here (z.B. FAT einlesen)	
		/* Inits */
		if (mmc_start_address > swap_space){
			mmc_start_address = swap_space-512;
			next_mmc_address = mmc_start_address;
		}
		#if MMC_ASYNC_WRITE == 1
			swap_buffer = malloc(512);
		#endif 
		#ifdef VM_STATS_AVAILABLE
			stats_data.time = TIMER_GET_TICKCOUNT_32;
		#endif
	}
	uint32 start_addr;
	if (aligned == 0 || mmc_get_end_of_page(next_mmc_address) == mmc_get_end_of_page(next_mmc_address+size-1)){
		/* Daten einfach an der naechsten freien virtuellen Adresse speichern */
		start_addr = next_mmc_address;
	} else {
		/* Rest der letzten Seite ueberspringen und Daten in neuem Block speichern */
		start_addr = mmc_get_end_of_page(next_mmc_address) + 1;
	}	
	if (start_addr+size > swap_space) return 0;	// wir haben nicht mehr virtuellen Speicher als Platz auf dem Swap-Device
	/* interne Daten aktualisieren */
	next_mmc_address = start_addr + size;
	#ifdef VM_STATS_AVAILABLE
		stats_data.vm_used_bytes = next_mmc_address-mmc_start_address;
	#endif
	return start_addr;
}

/*! 
 * Gibt einen Zeiger auf einen Speicherblock im RAM zurueck
 * @param addr	Eine virtuelle Adresse
 * @return		Zeiger auf uint8, NULL falls Fehler
 * @author 		Timo Sandmann (mail@timosandmann.de)
 * @date 		30.11.2006
 */
uint8* mmc_get_data(uint32 addr){
	/* Seite der gewuenschten Adresse laden */
	if (mmc_load_page(addr) != 0) return NULL;
	int8 cacheblock = mmc_get_cacheblock_of_page(addr);
	page_cache[cacheblock].dirty = 1;	// Daten sind veraendert
	/* Zeiger auf Adresse in gecacheter Seite laden / berechnen und zurueckgeben */
	return page_cache[cacheblock].p_data + (addr - (mmc_get_mmcblock_of_page(addr)<<9));	// TODO: 2. Summanden schlauer berechnen
}

/*! 
 * Erzwingt das Zurueckschreiben einer eingelagerten Seite auf die MMC / SD-Card   
 * @param addr	Eine virtuelle Adresse
 * @return		0: ok, 1: Seite zurzeit nicht eingelagert, 2: Fehler beim Zurueckschreiben
 * @author 		Timo Sandmann (mail@timosandmann.de)
 * @date 		15.12.2006
 */
uint8 mmc_page_write_back(uint32 addr){
	int8 cacheblock = mmc_get_cacheblock_of_page(addr);
	if (cacheblock < 0) return 1;	// Seite nicht eingelagert
	if (swap_out(page_cache[cacheblock].addr, page_cache[cacheblock].p_data, MMC_ASYNC_WRITE) != 0) return 2;	// Seite (evtl. asynchron) zurueckschreiben
	page_cache[cacheblock].dirty = 0;	// Dirty-Bit zuruecksetzen
	return 0;
}

/*! 
 * Schreibt alle eingelagerten Seiten auf die MMC / SD-Card zurueck  
 * @return		0: alles ok, sonst: Summe der Fehler beim Zurueckschreiben
 * @author 		Timo Sandmann (mail@timosandmann.de)
 * @date 		21.12.2006
 */
uint8 mmc_flush_cache(void){
	uint8 i;
	uint8 result=0;
	for (i=0; i<allocated_pages; i++){
		if (page_cache[i].dirty == 1){
			if (page_cache[i].addr < mmc_get_mmcblock_of_page(swap_space))
				result += swap_out(page_cache[i].addr, page_cache[i].p_data, 0);	// synchrones Zurueckschreiben
			page_cache[i].dirty = 0;
		}
	}
	return result;	
}

/*! 
 * Oeffnet eine Datei im FAT16-Dateisystem auf der MMC / SD-Card und gibt eine virtuelle Adresse zurueck,
 * mit der man per mmc_get_data() einen Pointer auf die gewuenschten Daten bekommt. Das Ein- / Auslagern
 * macht das VM-System automatisch. Der Dateiname muss derzeit am Amfang in der Datei stehen.
 * Achtung: Irgendwann muss man die Daten per mmc_flush_cache() oder mmc_page_write_back() zurueckschreiben! 
 * @param filename	Dateiname als 0-terminierter String   
 * @return			Virtuelle Anfangsadresse der angeforderten Datei, 0 falls Fehler 
 * @author 			Timo Sandmann (mail@timosandmann.de)
 * @date 			21.12.2006
 */
uint32 mmc_fopen(const char *filename){
	uint32 block;
	/* Pufferspeicher organisieren */
	uint32 v_addr = mmcalloc(512, 0);
	uint8* p_data = mmc_get_data(v_addr);	// hier speichern wir im Folgenden den ersten Block der gesuchten Datei, der ist dann gleich im Cache ;)
	/* Die Dateiadressen liegen ausserhalb des Bereichs fuer den VM, also interne Datenanpassungen hier rueckgaengig machen */
	next_mmc_address -= 512;
	#ifdef VM_STATS_AVAILABLE
		stats_data.vm_used_bytes -= 512;
	#endif
	uint8 i;
	#ifdef MCU	// Debug-Info ausgeben
		#ifdef DISPLAY_AVAILABLE
			display_cursor(2,1);
			display_printf("Find %s: 0x",filename);
			uint16 k=0, j=0;
		#endif
	#else
		printf("Find %s...",filename);
		uint16 k=0, j=0;	
	#endif
	/* MMC-Block suchen zwischen Kartenanfang und VM-Startadresse (<= Kartengroesse) */
	for (block=0; block<mmc_get_mmcblock_of_page(mmc_start_address); block++){
		#ifdef MCU	// Debug-Info ausgeben
			#ifdef DISPLAY_AVAILABLE
				display_cursor(2,13);
				display_printf("%02x%04x", j, k);
				if (k==65535) j++;
				k++;
			#endif		
		#else
//			printf(".");	
//			fflush(stdout);
		#endif
		if (swap_in(block, p_data) != 0) break;	// Abbrechen, falls Fehler
		/* Blockanfang mit Dateinamen vergleichen */
		for (i=0; i<MMC_FILENAME_MAX; i++){
			if (filename[i] == '\0'){
				if (swap_in(++block, p_data) != 0) break;	// Ersten Sektor der Datei ueberspringen, dort stehen interne Daten
				page_cache[mmc_get_cacheblock_of_page(v_addr)].addr = block;	// Cache-Tag auf gefundene Datei umbiegen
				#ifdef MCU
					#ifdef DISPLAY_AVAILABLE
			  			k = block & 0xFFFF;
			  			j = (block >> 16) & 0xFFFF;
			  			display_cursor(2,1);
			  			display_printf("Found %s: 0x%02x%04x",filename,j,k);
					#endif
				#else
		  			k = block & 0xFFFF;
		  			j = (block >> 16) & 0xFFFF;
		  			printf("\n\rFound %s: 0x%02x%04x \n\r",filename,j,k);			
				#endif				
				return block<<9;	// gesuchte Datei beginnt hier :)
			}
			if (filename[i] != p_data[i]) break;	// gesuchte Datei beginnt nicht in diesem Block
		}
	}
	/* Suche erfolglos, aber der Cache soll konsistent bleiben */
	// TODO: ordentlich aufraeumen im Fehlerfall!
	page_cache[mmc_get_cacheblock_of_page(v_addr)].addr = 0x800000;	// Diesen Sektor gibt es auf keiner Karte <= 4 GB 
	page_cache[mmc_get_cacheblock_of_page(v_addr)].dirty = 0;	// HackHack, aber so wird der ungueltige Inhalt beim Pagefault niemals versucht auf die Karte zu schreiben
	#ifdef MCU
		#ifdef DISPLAY_AVAILABLE
			display_cursor(2,1);
			display_printf("%s not found ",filename);
		#endif	
	#else	
		printf("\n\r%s not found \n\r",filename);
	#endif		
	return 0;	// Datei nicht gefunden :(	
}

/*!
 * Liest die Groesse einer Datei im FAT16-Dateisystem auf der MMC / SD-Card aus, die zu zuvor mit 
 * mmc_fopen() geoeffnet wurde.
 * @param file_start	(virtuelle Anfangsadresse der Datei)
 * @return				Groesse der Datei in Byte
 * @date				12.01.2007
 */
uint32 mmc_get_filesize(uint32 file_start){
	file_len_t length;
	uint8* p_addr = mmc_get_data(file_start-512);
	/* Dateilaenge aus Block 0, Byte 256 bis 259 der Datei lesen */
	uint8 i;
	for (i=0; i<4; i++)
		length.u8[i] = p_addr[259-i];
	return length.u32;
}

/*! 
 * Leert eine Datei im FAT16-Dateisystem auf der MMC / SD-Card, die zuvor mit mmc_fopen() geoeffnet wurde.
 * @param file_start	(virtuelle) Anfangsadresse der Datei 
 * @return				0: ok, 1: ungueltige Datei oder Laenge, 2: Fehler beim Schreiben
 * @date 				02.01.2007
 */
uint8 mmc_clear_file(uint32 file_start){
	#ifdef PC
		printf("Start of file: %lu \n\r", file_start);
	#endif
	uint32 length = mmc_get_filesize(file_start);
	if (file_start == 0 || file_start + length >= mmc_start_address) return 1;	// Datei existiert nicht oder Laenge ist ungueltig
	/* Ersten Block der Datei laden (dessen Speicherbereich benutzen wir einfach als 0-Puffer) */
	uint8* p_addr = mmc_get_data(file_start);
	memset(p_addr, 0, 512);	// leeren Puffer erzeugen
	/* Alle Bloecke der Datei mit dem 0-Puffer ueberschreiben */
	int8 cache_block;
	uint32 addr;
	for (addr=file_start; addr<file_start+length; addr+=512){
		if (swap_out(mmc_get_mmcblock_of_page(addr), p_addr, 0) != 0) return 2;
		/* Falls ein Block der Datei im Cache ist, auch diesen leeren */
		cache_block = mmc_get_cacheblock_of_page(addr);
		if (cache_block >= 0){
			memset(page_cache[cache_block].p_data, 0, 512);
			page_cache[cache_block].dirty = 0;
		}
	}
	#ifdef PC
		printf("End of file: %lu \n\r", addr);
	#endif	
	return 0;
}

#endif	// MMC_VM_AVAILABLE
