summaryrefslogtreecommitdiffstats
path: root/source/ct-Bot/mmc-vm.c
diff options
context:
space:
mode:
Diffstat (limited to 'source/ct-Bot/mmc-vm.c')
-rw-r--r--source/ct-Bot/mmc-vm.c534
1 files changed, 534 insertions, 0 deletions
diff --git a/source/ct-Bot/mmc-vm.c b/source/ct-Bot/mmc-vm.c
new file mode 100644
index 0000000..c8782c8
--- /dev/null
+++ b/source/ct-Bot/mmc-vm.c
@@ -0,0 +1,534 @@
+/*
+ * 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