BIOS-Level Programmierung - Video Programmierung |
Geschrieben von: StarShaper | |||||||||||||||||||||||||||||||||||||||
Donnerstag, den 01. Juni 2006 um 10:30 Uhr | |||||||||||||||||||||||||||||||||||||||
Seite 3 von 5
Video Programmierung mit INT 10hVielleicht kennen Sie noch die alten MS-DOS Spiele aus den frühen 90er Jahren? Die Grafikchips waren in ihrer Leistung und ihrem Funktionsumfang noch relativ bescheiden. Technologien heutiger High-End Grafikprozessoren die mit den neuesten Vertex- und Pixel-Shadern arbeiten, parallel mehrere Rendering-Pipelines zur Verfügung stellen und Transistoren jenseits der 130 Millionen Marke auf einem einzelnen Chip beherbergen standen noch nicht zur Verfügung. So ist es aus heutiger Sicht nicht weiter verwunderlich das die damaligen Spiele im Gegensatz zu aktuellen Grafik-Knüllern etwas antiquitiert wirken. Neue APIs wie Direct3D, DirectDraw und OpenGL stellen dem Programmierer von heute eine riesige Anzahl an Werkzeugen zur Grafikprogrammierung zur Auswahl. Hersteller moderner GPUs versuchen ständig die Spezifikationen neuer API-Versionen zu erfüllen und zu übertreffen. Doch es war ein langer und steiniger Weg bis die ersten brauchbaren APIs den Grafikprogrammieren auf dem Massenmarkt zur Verfügung standen. Als mit Windows 95 erstmals der Nachfolger von MS-DOS Ende des Jahres 1995 erschien, kam ein Problem auf das von Microsoft gelöst werden musste. Während DOS noch den direkten Zugriff auf die Videokarte, die Tastatur und Maus sowie das Soundsystem und andere Komponenten des Systems gewährte, baute Windows 95 auf dem Protected Mode auf, in dem kein direkter bzw. nur ein eingeschränkter Zugriff (signierte Treiber) auf das System gestattet wird. Microsoft benötigte eine Methode den Programmierern eine Schnittstelle zur Verfügung zu stellen ohne das Model des "Geschützten Modus" zu unterwandern. Das war die Geburtsstunde von DirectX. Im Laufe der Zeit entwickelte sich DirectX zu einer Sammlung von APIs für alle Bereiche der Spieleprogrammierung inklusive exzellenter Sound Komponenten (DirectX Sound) und vorgefertigten Installationsroutinen (DirectSetup). Microsoft verwendete aber in seinen ersten Betriebssystemen der NT-Serie die damals noch weitaus ausgreiftere OpenGL API die de facto als Industriestandard galt. Als Ende der 90er die Hardware Komponenten immer günstiger wurden und sich auch der Otto Normalverbraucher leistungsstarke Hardware kaufen konnte, wurde die Entwicklung von DirectX rasant beschleunigt und ist dafür verantwortlich das DirectX heute eine professionelle und leistungsstarke API-Sammlung zur Spieleprogrammierung ist. Mit dem Auftauchen von Windows Vista am Horizont wird diese Entwicklung fortgesetzt und die neue Windows Graphics Foundation, auch DirectX 10 genannt, bedeutet einen weiteren Quantensprung in der Programmierung. Alle neuen Schnittstellen haben eines gemeinsam: Sie stellen eine weitere Abstraktionsschicht dar und trennen den Programmierer von der Hardware und den zugrundeliegenden Technologien ab. Das ist der Preis für eine standardisierte und plattformunabhängige API. Ein Preis der von Vielen zugunsten einer höheren Produktivität gerne gezahlt wird. Wir hingegen bewegen uns knapp über der Hardwareebene und wollen in diesem Tutorial erfahren wie die Video Programmierung mithilfe der BIOS-Dienste funktioniert. Dazu sehen wir uns zunächst einmal die möglichen Level des Zugriffs an. Wenn ein Programm beispielsweise Zeichen im Textmodus auf dem Bildschirm darstellen möchte kann es effektiv zwischen drei verschiedenen Zugriffsebenen wählen.
Je nach nachdem was das Programm leisten soll, muss sich der Entwickler entscheiden welche der drei Zugriffsmethoden er auswählt. Die Methoden bauen in Schichten aufeinander auf. Während die MS-DOS Interrupts auf BIOS-Level Routinen zurückgreifen, verwenden die BIOS Routinen widerum den direkten Video Zugriff um die Ausgabe zu erzeugen. Text-ModusEs existieren zwei verschiedene Arten von Video-Modi, der Text- und der Grafik-Modus. Im Text-Modus wird der Bildschirm von oben bis unten in Reihen und Spalten aufgeteilt. Je nachdem welche Schriften/Fonts gerade ausgewählt sind wird die Zeilenhöhe bestimmt. Die Fonts befinden sich in einem Speicher und können in neuen BIOS Versionen auch geändert werden. Dadurch lassen sich individuelle Fonts für den Text-Modus gestalten. Der Video Speicher für den Text-Modus ist in multiple separate Bildschirmseiten (engl. Video Text Pages) aufgeteilt. Programme können eine Seite präsentieren und gleichzeitig Text zu anderen versteckten Seiten schreiben. Die Standard Video Seite ist die Seite 0. Jedem Zeichen wird ein Byte mit Attributwerten zugeteilt in dem die Hintergrundfarbe und die Farbe des Zeichens definiert ist. Die Zeichen sind exakt auf dem Bildschirm positioniert und können auch zum Blinken gebracht werden. Der Video Controller invertiert zu diesem Zweck die Vorder- und Hintergrundfarbe abwechselnd in einer festgelegten Taktrate. Das Blinking kann mithilfe einer BIOS Funktion an- und abgeschaltet werden. Alle Pixel auf jedem gängigen Röhren-Monitor und auch auf allen modernen LCD-Monitoren werden mithilfe eines additiven Farbmodells erzeugt. Innerhalb eines so genannten RGB-Farbraums entstehen auf diese Weise sichtbare Farben indem jeweils der Anteil einer Grundfarbe gelöscht bzw. aktiviert wird. Die Grundfarben sind Rot, Grün und Blau. Um also die Farbe eines Pixels festzulegen ist die Kontrolle der drei Grundfarben erforderlich. Dazu steht ein 4-Bit großer Werteraum zur Verfügung. Die ersten drei Bits entscheiden darüber welche Grundfarben im Pixel leuchten sollen und welche nicht. Das vierte Bit gibt die Intensität der Leuchtkraft des gesamten Pixels an. Beispielsweise wird die Farbe Schwarz intern als 0000 dargestellt, die Farbe Weiß hingegen mit 1111 und die Farbe Rot mit 0100. Ein helles strahlendes Rot erhält man indem man zusätzlich das vierte Bit auf 1 setzt. Das folgende Abbild zeigt den Aufbau eines Attribut Bytes bestehend aus 8 Bit. Das linke Nibble ist für die Hintergrundfarbe verantwortlich und das rechte Nibble für die Vordergrundfarbe. Falls das Blinking aktiviert ist wird das Intensitätsbit des Hintergrundes als Identifikationsbit verwendet. In diesem Fall ist es nicht möglich eine strahlende Farbe für den Hintergrund zu verwenden. Unter Windows ist das Blinking auf den Vollbildmodus beschränkt. Um in Assemblersprache ein entsprechendes Attribut Byte aus zwei Farben zu erzeugen ist ledeglich ein Shift sowie eine Oder Operation erforderlich. Der x86-Befehlssatz stellt hierfür den Befehl SHL (Shift left) zur Verfügung. Das nachfolgende Beispiel zeigt im Ansatz wie ein blauer Hintergrund mit gelben Zeichen erzeugt werden kann. blue = 1 yellow = 1110 mov bh, (blue SHL 4) OR yellow ; Erzeugt 00011110 Im nächsten Abschnitt werden wir uns die wesentlichen Funktionen ansehen die INT 10h dem Programmierer zur Verfügung stellt. Tabelle 3 beschreibt diese Funktionen kurz.
Die Funktion 0 von INT 10h setzt den Video-Modus. Bevor ein neuer Video-Modus gesetzt wird sollte der alte Modus in einer Variable zwischengespeichert werden. Auf diese Weise lässt sich kurz vor dem Programmende der alte Zustand wieder herstellen. Das nachfolgende Beispiel zeigt wie das prinzipiell aussehen könnte. mov ah, 0Fh ; Aufruf von 0Fh int 10h mov vmode, al ; Speichert die Nummer des Modus in der Variable vmode mov columns, ah ; Speichert die Anzahl der Spalten (Zeichen oder Pixel) mov page, bh ; Speichert die aktive Bildschirmseite (Video Page) mov ah, 0 ; Aufruf von 00h mov al, 3 ; Setzt den Video Modus 3 (farbiger Text) int 10h Die Funktionen 1-3 dienen hauptsächlich diversen Manipulationen des Cursors. Eine relativ einfache Möglichkeit den Cursor zu verstecken ist zum Beispiel einen ungültigen bzw. zu großen Wert einzugeben. Die beiden Prozeduren HideCursor und ShowCursor veranschaulichen dies. HideCursor PROC mov ah, 3 ; Empfängt die Cursor Größe int 10h or ch, 30h ; Setzt die obere Reihe auf einen ungültigen Wert mov ah, 1 ; Setzt die Cursor Größe int 10h ret HideCursor ENDP ShowCursor PROC mov ah, 1 ; Setzt die Cursor Größe mov cx, 0607h ; Standardgröße int 10h ret ShowCursor ENDP Die Funktion 6 scrollt ein rechteckiges Fenster entlang. Ein Fenster kann zuvor entsprechend definiert werden. Die Größe des Fensters auf dem Bildschirm ist gegeben durch zwei 2D-Koordinaten. Die Punkte bestimmen wo jeweils die gegenüberliegenden Eckpunkte liegen. Diese Eckpunkte spannen das Fenster auf. Die Register CH und CL definieren die Reihe und Spalte des linken oberen Eckpunktes. Der untere rechte Eckpunkt wird durch die Register DH und DL definiert. Wird entlang eines Fensters hinab oder hinauf gescrollt kann das Fenster auf diese Weise vollständig bereinigt werden. Das folgende Beispiel zeigt ein vollständiges Assembler Programm das ein Fenster mitsamt Text erzeugt. Sobald die Taste 'd' gedrückt wird, löscht die Funktion 06h den Text des erstellten Fensters. TITLE Write and delete Color Text Window (wrianddelctw.asm) COMMENT ! Dieses Programm demonstriert wie sich ein Fenster generieren lässt und sich Zeichen in diesem Fenster löschen lassen. Dazu wird der farbige Text-Modus verwendet. Last update: 25/05/06 ! .model small, stdcall .stack 200h .386 .data message BYTE "Welcome to another CodePlanet Tutorial!", 0 press BYTE "(Press 'd' to delete this message)", 0 .code main PROC mov ax, @data mov ds, ax mov ax, 0600h ; Scrollt ein Fenster entlang mov bh, 00011110b ; Setzt alle Attribute der Zellen auf blaue Hintergrundfarbe ; und gelbe Vordergrundfarbe mov cx, 050Ah ; Linker oberer Eckpunkt mov dx, 0A42h ; Rechter unterer Eckpunkt int 10h mov ah, 2 ; Positioniert den Cursor innerhalb der Fensters mov dx, 0714h ; Reihe 7, Spalte 20 mov bh, 0 ; Video Page 0 int 10h mov dx, OFFSET message ; Schreibt einen Text auf den Bildschirm call WriteString mov ah, 2 mov dx, 0814h mov bh, 0 int 10h mov dx, OFFSET press call WriteString mov ah, 10h int 16h cmp al, 64h ; Wurde die Taste 'd' gedrückt? jne Exit Del: mov ah, 6 ; Löscht die Zeichen in dem aufgespannten Fenster mov al, 0 mov ch, 5 mov cl, 0Ah mov dh, 0Ah mov dl, 42h mov bh, 00011110b int 10h mov ah, 10h ; Warte auf einen Tastendruck int 16h Exit: mov ah, 4Ch mov al, 0 int 21h main ENDP ;--------------------------------------------------------- ; Str_length ; Gibt die Länge eines einen Null-terminierten Strings zurück. ; Empfängt: pString - Zeiger auf den String ; Rückgabewert: AX = Stringlänge ;--------------------------------------------------------- Str_length PROC USES di, pString:PTR BYTE mov di, pString mov ax, 0 ; Zeichen Zähler L1: cmp byte ptr[di], 0 ; Ende des Strings? je L2 ; Ja, springe zu quit inc di ; Nein, gehe zum nächsten Zeichen inc ax ; Addiere 1 zum Zähler jmp L1 L2: ret Str_length ENDP ;-------------------------------------------------------- ; WriteString ; Schreibt einen Null-terminierten String auf die Ausgabe ; Empfängt: DS:DX zeigt auf den String ; Rückgabewert: - ;-------------------------------------------------------- WriteString PROC pusha INVOKE Str_length, dx ; AX = string length mov cx, ax ; CX = number of bytes mov ah, 40h ; write to file or device mov bx, 1 ; standard output handle int 21h ; call MS-DOS popa ret WriteString ENDP END main Vielleicht kennen Sie Programme die den Text der auf dem Bildschirm präsentiert wird, über das Soundsystem akustisch wiedergeben. Eine mögliche Variante einen Teil dieser Funktionalität zu realisieren ist die INT 10h Funktion 08h zu benutzen. Die Funktion liest das Zeichen an der aktuellen Cursor Position aus. Mithilfe der beiden nachfolgenden Funktionen kann ein Zeichen samt Attribut auch geschrieben werden. Die Funktion 09h unterscheidet sich von 0Ah nur in der Spezifikation des Attribut Bytes. Dieses ist bei 0Ah nicht anzugeben. Wird ein Zeichen geschrieben, so muss in den meisten Fällen anschließend manuell der Cursor um die Anzahl der geschriebenen Zeichen nach vorne verschoben werden. .data char BYTE 0 attribute BYTE 0 mov ah, 8 ; Aufruf von 08h. Die Funktion liest ein Zeichen ; und das zugehörige Attribut Byte aus. mov bh, 0 ; Video Page 0 int 10h mov char, al ; Speichert das Zeichen in der Variable char mov attribute, ah mov ah, 9 ; Die Funktion 08h schreibt ein Zeichen samt Attribut ; an die aktuelle Cursor Position. mov al, 'A' mov bh, 0 mov bl, 0F1h ; Attribut blau auf weiß mov cx, 1 ; Gibt an wie oft das Zeichen 'A' wiederholt wird int 10h Wie bereits erwähnt wurde ist es im Text-Modus möglich die Zeichen zum Blinken zu bringen wenn das Blinking aktiviert wurde. Mit der Unterfunktion 03h lässt sich das Blinking sowie die Intensität kontrollieren indem direkt das vierte Bit manipuliert wird. Der Quellcode textblinking.asm veranschaulicht die Anwendung der Funktion. TITLE Color Text Blinking (textblinking.asm) COMMENT ! Dieses Programm gibt einen blinkenden Text in verschiedenen Farben aus. Die Option Blinking steht unter Windows nur im Vollbildmodus zur Verfügung! Last update: 25/05/06 ! .model small, stdcall .stack 200h .386 NewLine MACRO ; Zeilenumbruch mov dl, 0dh mov ah, 02 int 21h mov dl, 0ah mov ah, 02 int 21h ENDM .data ATTRIB_HB = 10000000b string BYTE "I'm Blinking!" color BYTE 1 .code main PROC mov ax, @data mov ds, ax call clrScreen ; Leere den Bildschirm call EnableBlinking ; Aktiviere das Blinking mov cx, SIZEOF string mov si, OFFSET string L1: push cx ; Speichere den Schleifenzähler mov ah, 9 ; Schreibe das Zeichen/Attribut mov al, [si] ; Zeichen das angezeigt werden soll mov bh, 0 ; Video Page 0 mov bl, color ; Attribut or bl, ATTRIB_HB ; Setze das Blinking/Intensitäts Bit mov cx, 1 ; Zeige es einmal an int 10h mov cx, 1 ; Verschiebe den Cursor nach call AdvanceCursor ; Nächste Bildschirm Spalte inc color ; Nächste Farbe inc si ; Nächstes Zeichen pop cx ; Stelle den Schleifenzähler wieder her! loop L1 NewLine ; Makro mov ah, 4Ch mov al, 0 int 21h main ENDP ;-------------------------------------------------- ; EnableBlinking ; Aktiviert das Blinking indem das höchste Bit ; im Attribut Byte auf 1 gesetzt wird. ; Funktioniert unter Windows nur im Vollbildmodus. ;-------------------------------------------------- EnableBlinking PROC push ax push bx mov ax, 1003h mov bl, 1 ; Blink-Modus (Intensität = 0, Blinking = 1) int 10h pop bx pop ax ret EnableBlinking ENDP ;-------------------------------------------------- ; AdvanceCursor ; Verschiebt den Cursor n Spalten nach rechts. ; Empfängt: CX = Anzahl der Spalten ; Rückgabewert: - ;-------------------------------------------------- AdvanceCursor PROC pusha L1: push cx ; Schleifenzähler mit n initialisiert mov ah, 3 ; Empfange die aktuelle Cursor Position - 03h mov bh, 0 ; Video Page 0 (ins DH, DL) int 10h ; Der Aufruf verändert das CX Register! inc dl ; Inkrementiere die Spalten mov ah, 2 ; Setze die Cursor Position int 10h pop cx ; Stelle den Schleifenzähler wieder her! loop L1 ; Nächste Spalte popa ret AdvanceCursor ENDP ;------------------------------------------------------ ; clrScreen ; Bereinigt den Bildschirm (video page 0) und lokalisiert ; den Cursor auf Reihe 0 und Spalte 0. ;------------------------------------------------------- clrScreen PROC pusha mov ax, 0600h ; Scrolle das Fenster hoch mov cx, 0 ; Obere linke Ecke (0,0) mov dx, 184Fh ; Untere rechte Ecke (24,79) mov bh, 7 ; Normale Attribute int 10h ; Rufe BIOS mov ah, 2 ; Lokalisiere Cursor auf 0,0 mov bh, 0 ; Video Page 0 mov dx, 0 ; Reihe 0, Zeile 0 int 10h popa ret clrScreen ENDP END main Unter Windows NT muss das Programm im Vollbildmodus ausgeführt werden. Öffnen Sie dazu die Eigenschaften des Programms und wählen Sie die Registerkarte Bildschirm. Setzen Sie einen Haken im Kontrollkästchen Vollbild. Falls dies nicht zum Erfolg führt, können Sie die Kommandozeile separat öffnen und über Alt+Enter in den Vollbildmodus schalten. Anschließend sollten Sie das Programm ohne Probleme aufrufen können. |
|||||||||||||||||||||||||||||||||||||||
Zuletzt aktualisiert am Samstag, den 22. Mai 2010 um 17:08 Uhr |
AUSWAHLMENÜ | ||||||||
|