Grafik und Text ausgeben – gar nicht so einfach (2)

Loading

Weiter geht es heute mit dem zweiten Teil über Grafik- und Textausgabe vor allem früher, aber auch heute, der direkt an den Teil von gestern anschließt. Es ist übrigens für den Artikel egal ob Text ausgegeben wird (zahlreiche Rechner für den Einsatz im Büro verfügten nur über eine Textdarstellung) oder Grafik, ja die Grafikausgabe ist sogar einfacher. Denn auch beim Text müssen die Pixel aus denen ein Zeichen besteht (5 x 7 beim Apple, 8×8 bei vielen Rechnern, 8×16 oder 9×14 bei IBM Grafikkarten) ja übertragen werden. Sie stehen aber nicht als Bitmuster im RAM sondern in einem eigenen Font-ROM; und müssen erst dort ausgelesen werden.

Speichertiming

Soweit ist alles gut. Das Problem, das sich aber ergibt, ist, dass die Ausgabe dauernd erfolgen muss. Das heißt auf den Grafikspeicher wird dauernd zugegriffen. Nehmen wir mal einen 16 KByte großen Speicher, den erreichten die letzten Heimcomputer problemlos, CGA oder MDA Grafikkarten des IBM PC hatten einen ebenso großen Bildschirmspeicher, 50-mal pro Sekunde an einen Monitor übertragen mit einem Drittel Overhead für die Ränder, ergeben eine Datenrate von 1.067 Kbyte/s. Ein Z80 System hatte damals typisch 4 MHz Takt und benötigte dafür mindestens DRAMs mit 250 ns Zugriffszeit und etwa 370 bis 400 ns Zykluszeit. Die Zugriffszeit ist die Zeit, nach der ein Prozessor die Daten abrufen kann, das muss bei den meisten Prozessoren nach einem Takt erfolgen, also hier der Kehrwert von 4 MHz = 250 ns. Die Zykluszeit ist bei DRAM länger, weil danach die Information wieder restauriert werden muss und Leitungen zum Bus wieder geschlossen werden. Sie ist aber relevant, denn erst danach kann wieder ein Zugriff erfolgen. Bei 1.067 MByte pro Sekunde findet alle 937 ns ein Zugriff statt, das ist während 40 Prozent der verfügbaren Zeit der Fall.

Das ist ein Problem, den damals wurden DRAM-Bausteine als speicher verwendet, die nur eine Datenleitung haben, an der Typenbezeichnung an einer „41“ erkennbar, also 4116 für ein 16 Kbit DRAM, 4164 für ein 64 Kbit RAM und 41256 für ein 256 Kbit RAM. Ein 8-Bit-Prozessor mit 8 Datenleitungen brauchte so immer (mindestens) acht Bausteine. Mehr konnten es durchaus sein, so hatten Rechner mit 32 KByte RAM meist zwei Bänke mit je 8 x 4116 Bausteinen und 128 KByte Rechner dann zwei Bänke mit 4164 Bausteinen. Die Wahl des Intel 8088 für den IBM PC erfolgte denn auch (neben anderen Gründen) weil man so weniger Leitungen und Speicherbausteine brauchte.

Das Problem bei einer solchen Organisation ist: egal an welcher Adresse der Grafikspeicher liegt, es werden immer bei jedem Zugriff alle acht Bausteine angesprochen, da in jedem RAM nur ein Bit eines Bytes gespeichert ist, z.B. Bit 0 im ersten Baustein und Bit 7 im letzten Baustein.CPU und Grafikausgabe, egal über welche Methode die Ausgabe erfolgt, müssen sich absprechen, damit es nicht zu einem Konflikt bei dem Zugriff kommt.

Zur Lösung dieser Herausforderung gibt es verschiedene Methoden. Man kann dies durch einen separaten Grafikspeicher lösen. Dieser wird dann nur in den Adressbereich eingeblendet, wenn ein Zugriff durch die CPU nötig ist und dann kann sie sich mit der Ausgabe synchronisieren, das geschieht meist durch ein Anlegen eines „Wait“ Signals an die CPU, wenn gerade ein Zugriff erfolgt. Das ist eine elegante Methode. Einen separaten Grafikspeicher hatte z.B. der Osborne 1. Alle Einbaukarten in den IBM PC arbeiten auch nach diesem Prinzip.

Bei einer Reihe von Computern wie dem Apple II, Amstrad CPC oder den MSX nutzte man eine Eigenheit des Maschinenzyklus von CPUs aus. Sie hatten im ersten Zyklus beim Holen eines Befehls eine feste Reihenfolge:

  • Fetch: Befehl aus dem Speicher holen
  • Decode: Befehl untersuchen und feststellen was zu tun ist
  • Execute: Den Befehl ausführen

Jeder dieser Schritte konnte einen oder mehrere Takte umfassen. Bei einem Z80-Prozessor brauchte er immer 4 Takte für diesen Teil eines Befehls. Dabei griff er nur in den ersten zwei Takten auf den Speicher zu. In der zweiten Hälfte des Zyklus tat er das nie. Also kam man auf die Idee, die Speicherzugriffe für die Bildübertragung in die zweite Hälfte (Takt 3+4) zu legen. Solang also die Zugriffe niemals mehr als 50 Prozent der Gesamtzeit ausmachen ist das eine gute und vor allem billige Lösung. Der Nachteil: jeder Mikroprozessor hat auch komplexere Befehle, die weitere Speicherzugriffe erfordern. Im obigen Pseudo-Code-Beispiel z.B. alle Ladeoperationen in ein Register. Diese gehen Befehle mit weiteren Bytes gehen schneller weil das Dekodieren nur einmal zu Befehlsanfang nötig ist. Für einen Speichertransfer braucht der Z80 nur 3 Takte. Damit nun aber diese Operationen in die Zeit rutschen, indem die Pixel ausgelesen werden, wurden in diesen Fällen die Taktzyklen auf ein Vielfaches einer festen Taktzahl – beim Z80 waren es 4 – angehoben. Das verlangsamte den Prozessor, bei den CPC ist die Verzögerung bekannt, die Maßnahme kostete da rund 25 Prozent der Geschwindigkeit. Der Vorteil ist das man nur eine Leitung vom Wait-Pin der Z80 zum Gate Array ziehen muss und dieses bei jedem Transfer die Leitung auf logisch 1 setzt.

Alternativ investiert man mehr Geld in die Speicherchips. Es gibt prinzipiell mehrere Lösungen:

  • Statische Bausteine
  • Bausteine mit mehr Datenleitungen
  • Dual-Ported RAM

Ein statisches RAM speichert die Information nicht in einem Kondensator, sondern einem Flip-Flop. Solche RAMs haben keine Zykluszeit, sondern nur eine Zugriffszeit. Damit wäre ein Zugriff in der zweiten Takthälfte möglich weil dann zwei Takte antat ein Takt zur Verfügung stehen. Solche Bausteine sind aber viel teurer als dynamische RAM, weil sie typischerweise damals nur ein Viertel der Datenmenge eines vergleichbar teuren DRAM fassten.

Ein RAM mit anderer interner Organisation – es gab damals auch Bausteine mit 4 oder 8 Datenleitungen – erlaubt es die Zugriffe von CPU und Bildschirmlogik zu verteilen. Bei Vier Leitungen pro Chip ist ein 64 Kbit Chip dann 16 K x 4 organisiert. Zwei Chips bilden einen 16 KByte Adressraum und speichern mit 8 Datenleitungen ein Byte. Legt man in diesen 16K-Bereich den Bildschirmspeicher so kann die CPU ihre Programme in den anderen sechs Chips des 64 KByte Speichers problemlos ausführen, ohne einen Konflikt zu haben. Absprechen muss sie sich nur, wenn sie auch auf den Bildschirmspeicher schreiben will. Es gibt aber einen guten Grund, warum diese Lösung recht unpopulär war. Denn wer sich an die Zeit erinnert: man arbeitete meist interaktiv. Bei einem Spiel wird sowieso dauernd das Bild erneuert, vor allem war das Scrollen bei dem der ganze Bildschirminhalt umgewälzt wird sehr langsam. So gesehen brachte diese Lösung recht wenig, weil eben recht viel in den Grafikspeicher geschrieben wurde.

Eigens für das Problem erfunden wurden Dual Ported RAM. Sie hatten zwei Ausgänge die separat von CPU und Videoprozessor genutzt wurden und sich somit nicht ins Gehege kamen. Damit dies ging, war der Port für den Videoprozessor ein besonderer: bei einem Zugriff wurden gleich 256 zusammenhängende Bit in einen Zwischenspeicher geladen und dann jeweils ausgegeben. Die Logik laß den Arbeitsspeicher ja sowieso sequentiell aus.

Bei Videoprozessoren oder Grafikkarten die separat eingebaut wurden (IBM PC) war es meist auch so, das die Karten mit ihren Kontrollern oder Videoprozessoren ihren dann separaten Speicher selbst kontrollierten. Die CPU konnte dann mal nicht einfach so auf diesen Speicher zugreifen. Sie musste das mit dem Videoprozessor absprechen. Beim IBM PC wurde bei MDA und CGA Karte dies so gelöst, das das BIOS bei Zugriffen wartete, bis der Controller signalisierte, das er im Bereich war, den man nicht sah (horizontale und vertikale Austastlücke). Während der Zeit – etwa ein Drittel der Gesamtzeit, dürfte dann geschrieben werden. Viele Programme umgingen das BIOS und schrieben direkt in den für die Ausgabe zuständigen Speicher, der bei einer festen Adresse im Adressraum lag. Dann gab es Bildstörungen, „Schneegestöber“ genannt. Wie viel Performance das kostet, sieht man an einem wirklich schlechten technischen Konzept, dem des Ti 99/4a. Bei ihm wurde der gesamte 16 KByte Speicher vom Grafikprozessor verwaltet und die CPU musste immer warten, was diesen Rechner wirklich langsam machte.

Ich habe nicht umsonst 8 Bit Rechner als Referenz bei diesem Artikel genommen. Bei ihnen waren die Anforderungen am höchsten und eigene Grafikkarten oder Spezialbausteine für die Grafik eher selten. Der Grund für die hohen Anforderungen sind, dass sie nur 8 Bit auf einmal übertrugen und ihre einfachen Befehle nur wenige Takte umfassten. Sie griffen dadurch sehr häufig auf den Speicher zu. 16 Bit Mikroprozessoren holten jeweils 16 Bit auf einmal und holen so gleich zwei Befehle, die nur ein Byte lang waren auf einmal. So hatte ein 5 MHz 8086 die gleichen Anforderungen an Speichergeschwindigkeit wie ein 4 MHz Z80.

Heute

Heute hat sich viel geändert. Vor allem spielt die Bildaufbaurate praktisch keine Rolle mehr bei der Gesamtdatenrate vom und zum Speicher. Mein derzeitiger Büro-Rechner hat nicht mal eine eigene Grafikkarte, sondern eine integrierte Grafik, die nutzt einen Teil des Hauptspeichers als Grafikspeicher. Das ist variabel, abhängig, ob ich einen Text schreibe oder ein Spiel spiele. Beim Text belegt die Grafik 256 KByte von 32 GB. Bei einem Notebook mit separater (Spiele)-Grafikeinheit, aber immer noch Hauptspeichernutzung, sind es schon 4,2 GB die sich die Grafikunit (GPU) abzweigt. Davon belegen die Pixel die man sieht, den kleinsten Teil: selbst bei drei Monitoren mit 4K-Auflösung (3840 x 2160 Pixel) sind es bei 32 Bit pro Bildpunkt gerade mal 96 MByte.

Den meisten Speicher benötigen heute Grafikkarten für die Texturen, Objekte die schnell eingeblendet werden müssen und auch das Programm. Moderne Grafikkarten berechnen z.B. für jeden Lichtstahl, ob man ihn sieht oder nicht. Dafür benötigen sie auch einen Großteil der Datenübertragungsrate. In ihnen arbeiten Tausende von Recheneinheiten. Jede von ihnen führt in einem Takt mindestens eine, meist mehrere Operationen aus und muss daher dauernd mit neuen Daten „gefüttert“ werden. Das macht den Großteil der Datenübertragungsrate aus. Bei meinem derzeitigen Rechner steckt DDR4-3200 RAM. Das RAM hat eine Frequenz von 3.200 MHz und transferiert pro Takt jeweils 64 Bit, also insgesamt 25.600 Mbyte/s. Für 60 Hz Bildwiederholfrequenz, 16 Millionen Bildpunkte pro Monitor (8K Auflösung) und drei Monitore sind das nur 2,88 GByte pro Sekunde, also ein Bruchteil der Datenrate, die ein Speicherkanal schafft und heute haben die Rechner meist zwei bestückte Kanäle, in meinem Rechner der nachträglich aufgerüstet wurde, sind es sogar vier Module, auf die sich die Transfers verteilen.

Sie brauchen die hohe Datenrate um die ganzen Bilddaten, die verarbeitet werden müssen, bevor sie ausgegeben werden zu transferieren. Mit den Datenraten, die ein Mainboard bietet kommen separate Grafikkatzen schon lange nicht mehr aus. Je nach Preisklasse haben sie 256 bis 1024 Datenleistungen (anstatt 64 beim PC) und setzen auch spezielles Graphic-RAM GDDR RAM ein, das nochmals höher getaktet und schneller als dass RAM auf den DIMMS für Prozessoren ist. Meist ist um die Geschwindigkeit zu erhöhen es auch fest verlötet direkt bei der GPU, um die Wege und Widerstände zu verkürzen.

Die heutigen Grafikkarten sind eine Weiterentwicklung der Grafikkarten aus den frühen Neunzigern. Bis dahin bot jede neue Generation von Grafikkarten bei der x86 Linie einfach mehr Speicher, mehr Farbe, eine höhere Auflösung. Aber DOS als Betriebssystem bot keine API, kein Interface für Grafik. Das musste jedes Programm noch selbst machen, indem es rudimentäre Routinen für das Setzen eines Punktes aufrief. Windows hatte erstmals eine API und es gab immer wieder auftauchende grafische Elemente wie den Rahmen um ein Fenster, die obere Menüleiste. Ein Fenster war erst mal eine leere Fläche. Es kamen mit dem Siegeszug von Windows 3.0 erstmals Windows-Beschleuniger“ Grafikkarten auf den Markt. Diese installierten einen Treiber in Windows. Wenn nun ein Fensterrahmen gezeichnet werden sollte, dann wurde nicht jeder Bildpunkt berechnet und gesetzt, das konnte eine Hardwareroutine in der Karte eigenständig tun. Weitere Routinen gab es für Linien, Textausgabe, ausgefüllte Flächen. Das beschleunigte den Bildaufbau enorm, denn in allen Fällen die bisher betrachtet wurden, war es immer noch die CPU die berechnete welche Speicherbaustelle verändert werden musste damit nur ein Pixel gesetzt werden konnte. Das erklärt auch warum damals Computer so langsam scrollten. Man konnte damals wirklich mitlesen, was geschrieben wurde. Beim Scrollen musste die CPU praktisch bei jeder ausgegebenen Zeile den ganzen Bildschirmspeicher umwälzen und das dauerte.

Ab Windows 95 gab es mit DirextX dann auch eine API für Spiele, bei der dann die Elemente vorhanden waren, die Spiele im Vollformat brauchten. Sie brauchte einige Jahre um sich durchzusetzen, zahlreiche Hersteller setzten auf eigene APIs, die als Treiber eingebunden wurden. Diese Karten waren leistungsfähiger, hatten aber den Nachteil das ein Spiel diese Treiber auch nutzen musste, weshalb es oft diese Grafikkarten im Bundle mit Spielen gab, die speziell angepasst wurden.

Heute gibt es das auch noch, aber es ist seltener geworden. Als neues Unterscheidungsmerkmal ist heute das Raytracing hinzugekommen das NVIDIA und ATI unterschiedlich durchführen. Daneben gibt es eigene Bibliotheken für die Nutzung der Rechenleistung einer Grafikkarte in PC-Anwendungen (CUDA), die meist nur mit bestimmten Anwendungen laufen. Photoshop als Bildbearbeitung ist hier ein populäres Beispiel. Seit ein, zwei Jahren wird auch die Rechenkapazität einer Grafikkarte für KI-Anwendungen genutzt. Damit kann man z.B. bei Videokonferenzen den Hintergrund dynamisch erkennen und auswechseln. Das enorme Steigen des Börsenwerts von NVIDIA hat seine Ursache in dem KI-Boom, weil dafür spezielle und sehr teure Grafikkarten dieser Firma eingesetzt werden, entsprechend sank er als DeepSeek angekündigt wurde, dass viel weniger Ressourcen braucht, auch kurzfristig um 17 Prozent. Trotzdem ist das Unternehmen nach einer Börsenwertsteigerung um 2000 Prozent pro Jahr das zweit wertvollste Unternehmen weltweit und 3,19 Billionen Dollar wert – und das bei gerade mal 30.000 Mitarbeitern.

2 thoughts on “Grafik und Text ausgeben – gar nicht so einfach (2)

  1. Ich weiß es leider nicht mehr, aber welchen Baustein hatte denn der PET? Soweit ich mich erinnern kann, wurde da für jedes Zeichen am Bildschirm ein Byte in einem reservierten Speicherbereich geschrieben, das war denn letztlich eine (8 bit) Adresse für einen eigenen „Character-ROM“ in dem die Bitmuster des jeweiligen Zeichens abgespeichert waren. Das oberste bit war dann wimre das „invers“ Flag.

    Möglicherweise verwechsel ich das aber auch mit anderen 8-bittern (AIM, Eurocom), ist alles schon lange her.

    1. Das Mainboard zeigt einen MOS 6545-1, der ist bauidentisch zum weit verbreiteten Motorola 6845 der auch im Artikel erwähnt wird.
      https://en.wikipedia.org/wiki/Motorola_6845
      https://thumbs.worthpoint.com/zoom/images2/1/0816/17/commodore-pet-8032-motherboard_1_76b57afea3bf8c31dd001c36616177a0.jpg
      Textausgabe ist komplexer als Grafik: während man da einfach den Speicher linear ausgeben muss, muss man bei einem Buchstaben den ASCII Code als Index nehmen um das Bitmuster im character-ROM zu suchen. Damit nicht genug: das Bitmuster dasd ausgelesen werden muss, verschiebt sich um 1 Byte pro Zeile. Der 6845 hatte dafür eigene Register in denen man die Zeilenadresse entnehmen konnte.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Diese Seite verwendet Akismet, um Spam zu reduzieren. Erfahre, wie deine Kommentardaten verarbeitet werden..