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 - Video Programmierung Drucken E-Mail
Benutzerbewertung: / 47
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

Video Programmierung mit INT 10h

Vielleicht 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.

  • MS-DOS-Level Zugriff: Jeder Computer der MS-DOS ausführt oder emuliert kann INT 21h nutzen um Ausgaben durchzuführen. I/Os können schnell zu anderen Geräten umgeleitet werden. Die Ausgabe ist ziemlich langsam und die Textfarbe kann nicht beeinflußt werden.
  • Direct Video Zugriff: Zeichen werden direkt in den Video RAM kopiert, so dass die Ausführung nahezu sofort geschieht. Ausgaben können nicht umgeleitet werden. Auf allen Windows Systemen der NT-Serie kann dieser Zugriff nur im Vollbild Modus verwendet werden.
  • BIOS-Level Zugriff: Zeichen werden ausgegeben indem die Funktion INT 10h verwendet wird. Ausgaben können nicht umgeleitet werden und bei großen Auflösungen kann es zu geringfügigen Verzögerungen kommen.

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-Modus

Es 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.

rgb-mode

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.

Table 3: INT 10h Functions
Funktionsnummer Beschreibung
0 Durch Aufruf dieser Funktion wird ein Videomodus ausgewäht und initialisiert.
1 Setzt die Cursor Linie und kontrolliert die Gestalt und die Größe des Cursors.
2 Der Cursor, der die Bildschirmposition für die Zeichenausgabe über eine der BIOS- oder DOS-Funktionen zur Zeichenausgabe bestimmt, wird durch Aufruf dieser Funktion versetzt.
3 Die Position des Textcursors in einer bestimmten Bildschirmseite und die Start- und Endzeile des blinkenden Bildschirmcursors werden ausgelesen.
6 Scrollt ein Fenster auf der aktuellen Bildschirmseite (Video Page) aufwärts und ersetzt alle passierten Linien durch Leerzeichen.
7 Scrollt ein Fenster auf der aktuellen Bildschirmseite abwärts und ersetzt alle passierten Linien durch Leerzeichen.
8 Liest das Zeichen und sein Attribut Byte an der aktuellen Cursor Position aus.
9 Ein Zeichen mit einer bestimmten Farbe (Attribut) wird an die aktuelle Cursorposition (in einer vorgegebenen Bildschirmseite) geschrieben.
0A Ein Zeichen wird an die aktuelle Cursorposition (in einer vorgegebenen Bildschirmseite) geschrieben.
0C Ein Grafikpixel wird im Grafik-Modus auf den Bildschirm geschrieben.
0D Liest die Farbe eines einzelnen Grafikpixels an einer bestimmten Position.
0E Ein Zeichen wird an die aktuelle Cursorposition in der aktuellen Bildschirmseite geschrieben, wobei die Farbe des alten Zeichens an dieser Bildschirmposition beibehalten wird.
0F Gibt Information über den Video-Modus.
10 Schaltet das Blinking und die Intensität.
1E Schreibt einen String auf den Bildschirm im Teletype-Modus.

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.

screen-window

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Ü