communityWir suchen ständig neue Tutorials und Artikel! Habt ihr selbst schonmal einen Artikel verfasst und seid bereit dieses Wissen mit der Community zu teilen? Oder würdet ihr gerne einmal über ein Thema schreiben das euch besonders auf dem Herzen liegt? Dann habt ihr nun die Gelegenheit eure Arbeit zu veröffentlichen und den Ruhm dafür zu ernten. Schreibt uns einfach eine Nachricht mit dem Betreff „Community Articles“ und helft mit das Angebot an guten Artikeln zu vergrößern. Als Autor werdet ihr für den internen Bereich freigeschaltet und könnt dort eurer literarischen Ader freien Lauf lassen.

BIOS-Level Programmierung Drucken E-Mail
Benutzerbewertung: / 52
SchwachPerfekt 
Geschrieben von: StarShaper   
Donnerstag, den 01. Juni 2006 um 10:30 Uhr
Beitragsseiten
BIOS-Level Programmierung
Die Tastatur
Video Programmierung
Grafik-Modi
Mausprogrammierung
Alle Seiten
bios-chip

Inhalt

In diesem Tutorial werden wir die unterste Ebene der Systemprogrammierung kennenlernen, die BIOS-Level Programmierung. Selbstverständlich existiert auf dieser Ebene nur eine Sprache mit der es möglich ist alles aus der Hardware rauszukitzeln was in ihr steckt - die Assemblersprache! Sie erfahren in diesem Tutorial unter Anderem wie man direkt mit Hardware Interrupts arbeitet, die Maus direkt beeinflußt und Pixel über die Grafikkarte mithilfe der BIOS Services auf den Bildschirm zeichnet. Um zu verstehen wie die Operationen vom BIOS bewerkstelligt werden, ist zunächst eine kurze Einführung in das Basic Input/Output System notwendig.

Das BIOS

Das BIOS ist die Basis-Software des Computers und kann im entferntesten Sinn, analog zum menschlichen Gehirn, als das Stammhirn des Rechners angesehen werden. Das BIOS regelt in der Boot-Up Phase alle niederen Funktionen des PCs. Um Zugriff auf das BIOS zu erlangen ist es erforderlich den Prozessor in den so genannten Real Mode zu schalten. Der Real Mode ist ein Operationsmodus der 80286 und x86-kompatiblen CPUs. Im Real Mode steht ein 1 MB großer segmentierter Speicher zur Verfügung. In diesem Modus findet keine Abbildung von virtuellen in physikalischen Adressraum statt. Das Programm das die Speicheradressen verwendet interpretiert diese als physikalischen Speicher und greift auf diesen über einen 20-Bit breiten Adressraum zu der in einem Bereich zwischen 0 und FFFFF liegt. Darüberhinaus ist im Real Mode kein Speicherschutz und auch keine Multitasking-Unterstützung vorhanden. Die Software hat jedoch vollständigen Zugriff auf die BIOS Routinen und peripheren Geräte.

Der Real Mode wird von neuen Prozessoren im Sinne der Abwärtskompatibilität unterstützt. Das heißt jeder moderne x86-Prozessor startet im Real Mode und wird später manuell vom Betriebssystem in den Protected Mode geschalten. Unter allen modernen Windows Betriebssystemen erledigt das der NT Loader (Abkürzung: ntldr) in der Startphase. Erst danach kann auf die vollständigen 32-Bit des Speichers zugegriffen werden. Nachdem der NT Loader einige Page Tables generiert hat um einen 16 MB großen Speicher mit aktiven Paging betreiben zu können, aktiviert er das Paging und Windows kann normal geladen werden.

Bei den in diesem Tutorial vorgestellten Quellcodes handelt es sich um 16-Bit Applikationen. Da nur im Real Mode ein direkter Zugriff auf die Hardware möglich ist, neue Windows Betriebssysteme der NT-Serie den Prozessor jedoch beim Start in den Protected Mode schalten und das System vollständig abschirmen, ist es ggf. erforderlich die erstellten Programme beim Start separat im DOS-Modus auszuführen. Sie können diese aber auch unter Windows NT laufen lassen. Zu diesem Zweck stellen Windows Betriebssysteme eine virtuelle DOS Maschine, auch WOW genannt, zur Verfügung. Diese läuft unter dem Namen ntvdm.exe im Taskmanager. Dabei handelt es sich um ein Win16 Subsystem das es 16-Bit Anwendungen ermöglicht so ausgeführt zu werden als ob diese direkt in einer normalen DOS Umgebung laufen würden.

NTVDM nutzt dazu einen speziellen Modus der x86-Prozessoren namens Virtual-8086 Mode. Der Modus erlaubt es Anwendungen, die für den Real Mode konzipiert wurden, in einer kontrollierten Umgebung zu laufen. Ihr Programm wird also von der virtuellen Maschine emuliert während Windows NT allein darüber entscheidet wie und ob das Programm auf die Hardware zugreifen darf. Da die Programme unter einer Multitasking Umgebung laufen, können die 16-Bit Anwendungen Windows NT auch nicht zum Absturz bringen. Windows NT verhindert in jedem Fall das NTVDM Instruktionen ausführt die direkt den Speicher oder die Hardware manipulieren können!

Neben der Bedingung das unsere Programme im Real Mode laufen müssen, gibt es noch einige weitere Punkte zu berücksichtigen um mit Assemblersprache auf der BIOS Ebene programmieren zu können. So benötigen wir einen entsprechenden Assemblierer und 16-Bit Linker. In diesem Tutorial greifen wir auf den Microsoft Macro Assembler 6.x zurück. Microsoft bietet den MASM 6.x nicht mehr als Retail Produkt an. Bei Bedarf lässt sich dieser jedoch separat beim Support bestellen. Sie müssen sich darum jedoch nicht kümmern. Der MASM 6.x ist samt Update Patch und 16-Bit Linker im Anhang dieses Tutorials enthalten.

Windows Vista und Windows 7

Bitte beachten Sie das einige der hier vorgestellten 16-Bit DOS-basierten Programme seit Windows Vista nicht mehr lauffähig sind. Das betrifft nur die Programme, die im Fullscreen Mode operieren. Alle anderen Programme werden von der virtuellen DOS-Maschine unter Windows Vista und Windows 7 ordnungsgemäß ausgeführt. Nähere Details zu dem Problem entnehmen Sie der Beschreibung unter "Some 16-bit DOS-based Programs and the Command Prompt will not run in full-screen mode in Windows Vista and in Windows 7". Falls Sie Windows Vista oder Windows 7 nutzen, müssen sie diese Programme unter DOS ausführen oder die Videotreiber von Windows XP installieren.

Einführung in den Microsoft Macro Assembler

Der Microsoft Macro Assembler, auch MASM genannt, ist ein Assembler für die x86 Mikroprozessor Familie. Er wurde ursprünglich von Microsoft für die Entwicklung auf dem MS-DOS Betriebssystem entwickelt und war für einige Zeit der bekannteste Assembler für dieses Betriebssystem. Er unterstützte eine breite Anzahl an Makro Einrichtungen und strukturierten Programmier-Idiomen, inklusive High-Level Konstruktionen für Schleifen, Prozeduren Aufrufen und Alternationen. Aufgrunddessen kann der MASM auch als ein Beispiel für einen High-Level Assembler angesehen werden. Den späteren Versionen des MASM wurden kontinuierlich zusätzliche Komponenten hinzugefügt und entsprechend auf die neuen Windows Betriebssysteme angepasst. Der MASM ist eines von wenigen Microsoft Tools für die es keine getrennten 16-Bit und 32-Bit Versionen gab.

Anfang der 1990er erschienen erstmals alternative Assembler. Darunter der populäre NASM und der Borland Turbo Assembler. Trotz der aufkommenden Konkurrenz war der MASM auch bis spät in die 90er der gängigste Assembler auf dem Markt. Insbesondere die Tatsache das Microsoft den kommerziellen Verkauf einstellte und begann den MASM kostenlos als Teil des DDK zu publizieren verhalf dem Assembler zu seiner anhaltenden Popularität. Aber auch der neu erschienene MASM32, mit dem es nun auch möglich war richtige Windows Applikationen zu entwickeln, sowie die weit verbreiteten Tutorials trugen dazu bei.

Heute ist der MASM32 immer noch der populärste Assembler, trotz anhaltender Konkurrenz von neuen Produkten wie dem aktuellen NASM, FASM, GoAsm, oder HLA. Es existieren viele laufende Software Projekte die den MASM unterstützen, darunter IDE's (z.B. RadASM), Debugger (z.B. OllyDbg), und Disassembler (z.B. IDAPro). Das MASM32 Projekt, zu finden auf movsd.com, hat zahlreiche Dokumentationen, Tutorials, Bibliotheken und Quellcodes für die MASM32 Entwickler zusammengestellt. Der Microsoft Macro Assembler wird durch eine große Community unterstützt, darunter zahlreiche Webseiten und Support Foren wie dem MASM Forum. Trotz seines Alters bleibt der MASM nach wie vor einer der am besten unterstützten Assembler in der Geschichte der Computerprogrammierung.

Obwohl der MASM kein kommerzielles Produkt mehr ist, setzt Microsoft den Support weiterhin fort. Ein Grund dafür besteht darin das im Hause Microsoft und weltweit immer noch eine große Menge an Assembler Quellcode existiert. Für die Microsoft MASM 6.x Produktlinie wurden seit der Standard Edition 6.11 weitere Updates publiziert. Microsoft hat das Angebot des MASM 6.11 eingestellt und bietet diesen nurmehr auf direkte Anfrage beim Support an. Die neuen Versionen des MASM werden seither direkt mit den Visual C++ Versionen mitgeliefert. Beispielsweise enthält die Visual C++ 2002 Distribution den MASM 7.0, Visual C++ 2003 die Version 7.1 und die aktuelle Version Visual C++ 2005 die Version 8.0 des MASM. Diese unterstützt neben der 32-Bit Entwicklung auch die 64-Bit Architektur.

Die Versionsunterschiede der letzten Microsoft Macro Assembler bestehen hauptsächlich in Bugfixes, dem Hinzufügen von neuen Instruktionen für neue Prozessoren und der Verbesserung der 64-Bit Unterstützung. Neue radikale Änderungen sind für die Zukunft nicht mehr zu erwarten.

Microsoft Macro Assembler installieren

Falls Sie im Besitz einer neuen Visual C++ Edition sind, oder sogar eine Visual Studio Entwicklungsumgebung Ihr Eigen nennen, können Sie den mitgelieferten MASM verwenden. Sie finden diesen im Visual C++ Verzeichnis unter VC/bin und dem Namen ml.exe. Um die Quellcodes direkt mithilfe Ihrer Visual Studio IDE assemblieren zu können, müssen Sie bedenken das der ML 8.x standardmäßig mit der Option /coff assembliert und nicht mit /omf. Das bedeutet es werden Dateien im Common Object File Format (COFF) generiert. Dies ist für die Entwicklung von 32-Bit Assembler Applikationen die Standardeinstellung. Bei 16-Bit Anwendungen muss allerdings das Object Module File Format (OMF) verwendet werden! Um Dateien im alten OMF generieren zu können benötigen Sie zudem einen 16-Bit Linker. Dieser ist Bestandteil des im Anhang enthaltenen MASM 6.x.

Ich möchte anmerken das ich in diesem Tutorial ausschließlich in der Kommandozeile, ohne den Balast einer IDE arbeiten werde und auf den MASM in der Version 6.14 zurückgreife. Wir arbeiten schließlich nur eine Ebene über der Hardware und sollten uns dementsprechend auch vom überflüssigen Schnickschnack die eine IDE mit sich bringt verabschieden. Nachdem Sie das Paket im Anhang heruntergeladen haben, können Sie mit der Installation des MASM 6.11 beginnen. Starten Sie dazu die Setup.exe und installieren Sie den Microsoft Macro Assembler.

masm6

Installieren Sie den Assembler in dem angegebenen Pfad unter C:\MASM611\. Nachdem Sie den Assembler erfolgreich installiert haben, können Sie diesen auf die aktuelle Version updaten. Dazu befindet sich die Datei ml614.exe - ML614.exe 6.14 patch available - im Paket. Verschieben sie das Programm in das Verzeichnis des MASM 6.11 und patchen Sie den MASM auf eine aktuelle Version. Anschließend benötigen Sie noch den 16-Bit Linker. Führen Sie dazu das Programm lnk563.exe aus. Es wurden drei Dateien entpackt. Kopieren Sie diese ebenfalls in das MASM Verzeichnis. Damit haben Sie die Installation erfolgreich abgeschlossen.

Arbeiten mit dem MASM

Wie bereits erwähnt, existiert seit geraumer Zeit auch ein 64-Bit Microsoft Macro Assembler. Der Appendix 64 (ml64.exe) weist darauf hin. Die Kommandozeilen Referenz für den ML und den ML64 ist auf den offiziellen MSDN Seiten unter Microsoft Macro Assembler Reference zu finden. Die Parameter sind wie folgt spezifiziert:

ML [[options]] filename [[ [[options]]  filename]]
ML64 [[options]] filename [[ [[options]]  filename]]
...
[[/link linkoptions]]

Die Assemblierung gestaltet sich denkbar einfach. Im Normalfall reicht es aus den Dateinamen mit dem enthaltenen Quellcode zu übergeben. Der Assembler sucht stets im aktuellen Verzeichnis nach verwiesenen Dateien. Statische Bibliotheken werden anhand der Erweiterung *.lib erkannt und entsprechend mit der generierten Objektdatei zu einem ausführbaren Programm gelinkt. Eine Zusammenfassung der Kommandozeilen Syntax können Sie sich auch über den Befehl /? anzeigen lassen.

mlconsole

Das BIOS

Das Basic Input Output System, kurz BIOS genannt, ist bei PCs der x86 Prozessor Architektur die Basis-Software, die der Computer direkt nach dem Einschalten lädt und ausführt. Sie wird dazu in einem nichtflüchtigen Speicher abgelegt und steht dem Steuerwerk der CPU so direkt zur Verfügung. Die primäre Funktion des BIOS besteht darin die Maschine vorzubereiten, so dass andere Softwareprogramme, wie beispielsweise die Firmware des CD/DVD-Laufwerks oder der Festplatten geladen und ausgeführt werden kann. Dieser Prozess wird als Boot-Up bezeichnet. Das BIOS steuert die grundlegenden Mechanismen beim Start jedes Rechners. Nach der Boot-Up Phase wird die Kontrolle an das Betriebssystem übergeben.

Das BIOS befindet sich auf einem PROM, EPROM oder auch Flash Speicher genannt. Nach dem Start entpackt es sich in den RAM und wird anschließend vom Prozessor ausgeführt. In modernen Rechnern wird ein zusätzliches Setup Programm aus dem flüchtigen CMOS geladen. Dieses enthält benutzerspezifische Einstellungen die im BIOS Konfigurationsmenü getätigt wurden. Dazu gehört neben der Reihenfolge in der die Laufwerke geladen werden, auch die Zeit und das Datum.

Als die ersten IBM-PCs erschienen, wollten viele Programmierer wissen wie man direkt mit der Computer Hardware arbeitet. Relativ schnell erschienen die ersten Publikationen die sich mit den Eigenschaften und geheimen Internas des IBM-PCs befassten. Darunter das populäre Inside the IBM-PC von Peter Norton. Wenig später entschied sich die Firma IBM den Assembler Quellcode des IBM PC/XT BIOS frei zu veröffentlichen. Spieleentwickler optimierten ihren Code mithilfe des Wissens das sie über das BIOS gewonnen hatten. Bekannte Spiele die davon profitierten waren die ersten Ego-Shooter Quake und Doom. In diesem Tutorial werden nun auch Sie die unterste Ebene der Systemprogrammierung kennenlernen. Sie werden unterhalb einer verwalteten Schicht, wie dem .NET Framework arbeiten, unterhalb des Betriebssystems und sogar noch unterhalb von MS-DOS selbst. Sie werden erfahren was passiert wenn eine Taste auf der Tatstatur gedrückt wird, wie der Tastatur Puffer ausgelesen wird, wie Farben dargestellt werden, wie Grafiken auf den Bildschirm gezeichnet werden und wie die Maus Bewegungen registriert. Bevor wir uns jedoch der praktischen Seite der Programmierung widmen werfen wir zunächst einen Blick auf den BIOS Datenbereich.

Der BIOS Datenbereich

Aufgrund der Tatsache das das BIOS sich auf nicht flüchtigen Speicherchips befindet, ist es nicht weiter verwunderlich das Informationen in einem Datenbereich (engl. Data Area) gespeichert werden müssen. Der BIOS ROM Datenbereich beinhaltet im Gegensatz zu den fest kodierten Instruktionen, allerdings nur einen kleinen Teil der Systemdaten, die vom BIOS Code benötigt werden. Außerhalb des eigenen Datenbereichs existiert ein weiterer Speicherbereich der vom BIOS verwaltet wird. Dieser kleine Speicherbereich hält Informationen über die Konfiguration des Computers bereit. Er speichert mitunter die Einstellungen die während des BIOS Setup getätigt werden.

Während Ihr Computer unter der Kontrolle des BIOS steht, finden die wichtigsten Speicheroperationen des BIOS in speziellen Bereichen statt die vom BIOS auf dem PC angelegt werden. Sobald das BIOS den Computer initialisiert sammelt es Informationen über die Ausrüstung und Eigenschaften des Computers und platziert die Ergebnisse an bestimmten Orten in Ihrem Systemspeicher. Zu den gesammelten Daten gehören Informationen über die Anzahl und Art der installierten Ports, der Typ des Display-Adapters usw. Neben den genannten Daten werden auch Flags abgespeichert, Basisadressen der I/O Adapter, Tastaturzeichen und diverse Modi. Alle diese Daten werden an ganz bestimmten Stellen im Arbeitsspeicher hinterlegt, so dass Programme gezielt darauf zugreifen können um so an Informationen über die auf dem System verfügbaren Features zu gelangen. Beispielsweise sucht das BIOS nach seriellen Ports an spezifizierten Adressen. Alle diese Daten bilden einen vom BIOS im Arbeitsspeicher angelegten Informationsblock, genannt der BIOS Datenbereich.

Der Datenbereich ist fixiert und liegt über der Interrupt Vektor Tabelle. Er umfasst 256 Bytes, angefangen bei der absoluten Startadresse 00400h und endet mit der Adresse 004FFh (0040:0000 - 0040:00FF). Die nachfolgende Tabelle 1 gibt einen Überblick über die Art der gespeicherten Daten. Die Zellenbelegung kann je nach Hersteller geringfügig abweichen.

Table 1: BIOS Data Area
Offset - Segment 0040h Datentyp Beschreibung
0000 WORD Port-Adresse COM1
0002 WORD Port-Adresse COM2
0004 WORD Port-Adresse COM3
0006 WORD Port-Adresse COM4
0008 WORD Port-Adresse LPT1
000A WORD Port-Adresse LPT2
000C WORD Port-Adresse LPT3
000E WORD Port-Adresse LPT4
0010 WORD Liste der installierten Hardware
0012 BYTE Diagnose Flags der Initialisierung
0013 WORD Speichergröße in Kbytes
0015 WORD Speicher im I/O Kanal
0017 BYTE Tastatur Status Flags
0018 BYTE Zusätzliche Tastatur Status Flags
0019 BYTE Interner Tastatur Speicher (ASCII-Code bei Eingabe über Alt+Ziffern)
001A WORD Zeiger auf das Offset des nächsten Zeichens im Tastaturpuffer
001C WORD Zeiger auf das Offset des letzten Zeichens im Tastaturpuffer
001E 32 BYTE Tastaturpuffer, pro Zeichen zwei Bytes
003E BYTE Floppy Rekalibrierung
003F BYTE Floppy Motor Status
0040 BYTE Floppy Motor-Aus Zähler
0041 BYTE Floppy Status der letzten Operation
0042 7 BYTE Floppy Controller Status Bytes
0049 BYTE Aktueller Video-Modus
004A WORD Anzahl der Zeichen pro Zeile (Spalten)
004C WORD Größe der aktuellen Bildschirmseite in Bytes
004E WORD Adresse des Bildschirmspeichers relativ zum Video-RAM
0050 16 BYTE Cursorposition (8x 2 Byte für 8 Seiten)
0060 WORD End- und Startzeile des Cursors im Zeichenraster
(0-7 Farbe bzw. 0-14 Monochrom)
0062 BYTE Aktuelle Bildschirmseite
0063 WORD Port-Adresse des Video-Controllers
0065 BYTE Videomodus-Register der Grafikkarte
0066 BYTE Palettenregister (CGA)
0067 DWORD Reset-Restart-Adresse
006C WORD Zähler der Systemuhr; dieses Doppelwort wird bei jedem Timer-Aufruf
(ca. 18mal pro Sekunde) erhöht; alle 24 Stunden wird es auf Null gesetzt
0070 BYTE Zeitgeber-Überlauf; wird alle 24 Stunden erhöht
0071 BYTE CTRL-Break-Status
0072 WORD Reset-Status:
Der Wert gibt an, wie sich das BIOS nach einem Reset verhält.
0000h: Kaltstart
1234h: Warmstart
4321h: Warmstart, Speicherinhalt bleibt erhalten
5678h: System anhalten
9abch: Diagnoseprogramm
abcdh: Warteschleife aufrufen
0074 BYTE Status der letzten Festplatten-Operation (nur AT)
0075 BYTE Anzahl der Festplatten (nur AT)
0076 BYTE Festplatten-Kontrollbyte (nur AT)
0077 BYTE Port-Adresse Festplatte (nur AT)
0078 BYTE LPT1 Timeout Wert
0079 BYTE LPT2 Timeout Wert
007A BYTE LPT3 Timeout Wert
007B BYTE LPT4 Timeout Wert / nicht verwendet
007C BYTE COM1 Timeout Wert
007D BYTE COM2 Timeout Wert
007E BYTE COM3 Timeout Wert
007F BYTE COM4 Timeout Wert
0080 WORD Zeiger auf Anfang Tastaturpuffer (nur AT)
0082 WORD Zeiger auf Ende Tastaturpuffer (nur AT)
0084 BYTE Zahl der Videozeilen - 1 (nur EGA/VGA)
0085 WORD Punktzeilen pro Zeichen (nur EGA/VGA)
0087 4 BYTE EGA-/VGA-Status-Bereich (herstellerabhängig)
008B BYTE Floppy Daten und Schritt-Rate (nur PS/2)
008C BYTE Festplatten Controller-Status (nur PS/2)
008D BYTE Festplatten Fehler-Status (nur PS/2)
008E BYTE Festplatten Interrupt-Control (nur PS/2)
008F BYTE Diskettencontroller Information
0090 BYTE Medium in Laufwerk 0 (nur PS/2)
0091 BYTE Medium in Laufwerk 1 (nur PS/2)
0092 BYTE Medienstatus Diskettenlaufwerk 0 bei Start der Operation
0093 BYTE Medienstatus Diskettenlaufwerk 1 bei Start der Operation
0094 BYTE Augenblickliche Spur in Laufwerk 0 (nur PS/2)
0095 BYTE Augenblickliche Spur in Laufwerk 1 (nur PS/2)
0096 BYTE Tastenfeldstatus 3 (nur AT)
0097 BYTE Status Tastenfeld-LED (nur AT)
0098 DWORD Zeiger auf Wait-Flag (nur AT)
009C DWORD Zeit-Zähler (nur AT)
00A0 BYTE Wait-Status
00A1 95 BYTE Reserviert (herstellerabhängig belegt)
0100 BYTE HardCopy-Status

Zusätzlich ergänzt der erweiterte BIOS Datenbereich den für den Basis BIOS Datenbereich zur Verfügung stehenden Speicherbereich. Dieser zusätzliche Speicher belegt ein weiteres Kilobyte des Arbeitsspeichers. Programme lokalisieren die Adresse des Speichers mit dem Aufruf einer Basis BIOS Funktion, genannt Interrupt 15h. Obwohl die Ingenieure zahlreiche Ideen hatten, wie sie diesen zusätzlichen Speicherbereich funktionell verwenden wollten, existiert derzeit nur eine aktive Funktion in diesem erweiterten Speicher und belegt lediglich weitere 30 Byte. Das Betriebssystem Windows verwaltet seine eigenen Datentabellen und ist nicht auf den normalen oder den erweiterten BIOS Datenbereich angewiesen.

Wie schon ewähnt wurde, kontrolliert das BIOS zusätzlich eine andere Art des Speichers, den CMOS in dem die Setup Einstellungen gespeichert sind. Die BIOS Setup-Routinen sind hauptsächlich mit der Verwaltung dieses flüchtigen Speichers beschäftigt. Der CMOS wird durch einen Akku mit Strom versorgt, so dass die Daten auch bei einem längerfristigen Stromausfall erhalten bleiben. Wird die Batterie entfernt findet ein BIOS Reset statt und alle benutzerspezifischen Daten gehen verloren.



Zuletzt aktualisiert am Samstag, den 22. Mai 2010 um 17:08 Uhr
 
AUSWAHLMENÜ