Dabei soll gezeigt werden, dass Bits eine grundlegende mathematische Sache sind und keine elektrische, und dass sie auch ohne Strom leben können.
Es wird angenommen, dass der Zuhörer die Hardware Seite von Rechnern bisher nicht gross kennt, ausser Platinen oder gar nur Geräte zusammenzustecken und drauf Software zu installieren, also keine Kenntnis vorhanden ist, was drinnen in den Teilen auf den Platinen passiert.
Es wird lediglich vorausgesetzt, dass der Zuhörer die Grundlagen von programmieren kennt (und sei dies nur Scripte) und Konzepte wie Variablen, Ausdrücke, Zuweisungen, Schleifen und Unterprogramme kennt.
Dieser Vortrag ist eine stark abgewandelte (einiges gekürzt und mit anderem erweitert) und überarbeitete Form eines früheren Vortrages zum Thema Offene Hardware herstellen.
Dieser Vortrag wurde später, für eine Wiederholung am 2007.01.12 in der LUGS leicht überarbeitet (Tippfehler, Formulierungen verbessert und expandiert, z.T. verständlichere Erklärungen, Formatierung, mehr Links addiert).
Man hat bereits im 17. Jahrhundert Rechenmaschinen gebaut. Blaise Pascal (1623-62), Sohn eines Steuerbeamten, hat damals einen mechanischen Addierer für seinen Vater gebaut. Ältere Zuhörer werden sich noch an die mechanischen Registrierkassen errinnern, die früher in den Läden benutzt wurden, die "300 70 5 + <ratter> <ratter>" Dinger. Dort war die selbige Mechanik drin, nur mit Elektromotor statt Kurbel.
Achtung: Der Abakus ist viel älter (Jahrtausende), ist aber kein Rechner, sondern nur ein Speicher. Gerechnet wird im Kopf, der Abacus ist nur ein externer eine Zahl grosser (Zwischenergebnis) Speicher der das Gedächtnis entlastet, und so schneller Rechnen erlaubt. Das selbige Verfahren wie beim schriftlichen Rechnen, aber ohne Papierverbrauch, weil löschbar.
Problem all dieser Rechner war, dass sie kompliziert waren, weil sie dezimal rechneten. Alleine eine 1-stellige Addition von 2 Ziffern hat 10 mögliche Ziffern bei jedem Operanden, und damit 10*10=100 mögliche Kombinationen. Wenn man für Ergebnisse grösser 9 auch noch einen Übertrag will, gibt das eine zweite Einrichtung mit 100 Kombinationen. Will man mehrziffrige Zahlen Addieren hat man wegen dem Übertrag der 0 oder 1 sein kann, ab der zweiten Ziffer sogar 10*10*2=200 Möglichkeiten, mal 2 Einrichtungen. Multiplikation ergibt sogar 10*10*10=1000 Kombinationen, mal 2 Einrichtungen, weil man 10 mögliche Überträge hat.
Auch der Versuch von Charles Babbage (1791-1871) Mitte 1800ern einen Computer zu bauen, um Tabellen von Navigationsdaten automatisch und damit fehlerfrei zu rechnen, und ohne Person dazwischen fehlerfrei auszudrucken, ist letztlich daran gescheitert, dass dies mechanissh zu kompliziert war. Trotz Finanzierung durch die britische Marine, die gestrandete und gesunkene Schiffe wegen Rechen- und Druckfehler in Tabellenbüchern einsparen wollte.
Der Computer wurde realistisch möglich, als man auf binäre Zahlen umstellte, also Zahlen mit nur 2 Ziffern. Das vereinfachte die Rechner gewaltig (nur noch 2 Einrichtungen mit je 2*2*2=8 Kombinationen, sowohl für Addition wie auch für Multiplikation). Die einzelnen Ziffern von binären Zahlen werden Bits (Binariy digITS) genannt.
Beispiel binäres Rechnen mit vorzeichenlosen 8bit Zahlen (Bereich von 0..2^n-1, für n=8: 0..2^8-1=0..255) 7654 3210 Nummer n des Bits, n=7..0 1 Wert des Bits, für n=7..0:, 2^n=128..1 2631 8426 8421 ---- ---- 0100 1100 Operand1 | = 64 +8+4 = 76 + 0110 0111 Operand2 | = 64+32 +4+2+1 = 103 ( . . . Übertrag) | ---- ---- | --- 1011 0011 Ergebnis | = 128 +32+16 +2+1 = 179
Weil Binärzahlen sehr schnell sehr lang werden, fasst man oft 4 Bits zusammen, zu Hexadezimalen Zahlen, nach folgender Methode:
0000 -> 0 0001 -> 1 0010 -> 2 0011 -> 3 0100 -> 4 0101 -> 5 0110 -> 6 0111 -> 7 1000 -> 8 1001 -> 9 1010 -> A 1011 -> B 1100 -> C 1101 -> D 1110 -> E 1111 -> F 0000 0000 -> 00 0000 1111 -> 0F 0001 0000 -> 10 0001 1111 -> 1F 0010 0000 -> 20 ... ... 1111 1111 -> FF
Früher wurden auch Oktale Zahlen benutzt, bei denen man 3 Bits zusammenfasste, mit den Ziffern 0..7. Das braucht keine Ziffern A..F, aber reduziert die Stellen nur um 3 statt um 4. Oktal designte Rechner hatten oft durch 3 teilbare Wortbreiten. Demensprechend kamen hexadezimal designte Rechner auf, nachdem auf 2^n Bits Wortbreite standardisiert wurde, die nicht durch 3 teilbar sind.
Vorzeichen und damit negative Zahlen könnte man damit machen, indem man eine Zahl und ein separates Vorzeichen Bit verwendet. Aber dies ist zum Rechnen aufwendig (abhängig von Vorzeichen Addition durch Subtraktion ersetzen).
Zum Glüeck gibt es eine einfachere Lösung: Zählt man 0,1,2,...255,0,... überläuft unsere 8bit Zahl von 255 auf 0, weil der Computer nur eine endliche Menge Zahlen fassen kann. Wenn man nun von 0 einen Schritt zurückgeht, was -1 geben sollte, landet man auf 255. Man definiert nun einfach, dass 255 = -1 ist, 254 = -2, und so weiter. Damit kollidieren die 0..n positiven und -1..-n negativen Zahlen irgendwo in der Mitte. Man spaltet dabei der ganzen Zahlenbereich von 0..(2^n)-1 (hier bei 8bit 0..255) in 2 Hälften, von denen eine zum negativen Bereich -2^(n-1)..(2^(n-1))-1 (hier -128..127) uminterpretiert wird, und der verbleibende Rest von n-1 Bits (hier 7bit) als positiven Bereich 0..(2^(n-1))-1 (hier 0..127) erhalten bleibt, womit auch noch das linke Bit zum Vorzeichen wird, mit 0=positiv und 1=negativ:
Binär Dezimal ohne / mit Vorzeichen .... .... 0000 0001 1 +1 0000 0000 0 0 A 255 -> 0 1111 1111 255 -1 | = Überlauf ohne Vorzeichen 1111 1110 254 -2 nicht 256, da nur 8bit Bereich .... .... 1000 0001 129 -127 1000 0000 128 -128 A +127 -> -128 0111 1111 127 +127 | = Überlauf mit Vorzeichen 0111 1110 126 +126 nicht 128, da nur 7bit Bereich .... .... 0000 0010 2 +2 0000 0001 1 +1 0000 0000 0 +0 | 0 -> 255 1111 1111 255 -1 V = Unterlauf ohn Vorzeichen .... .... nicht -1, da voll 8bit Bereich ^----------- Vorzeichen falls eines definiert wird, 0 = +, 1 = -
Dieses Verfahren wird 2er Komplement genannt. Weil die Null damit eine positive Zahl an Platz verbraucht, hat es immer eine negative Zahl mehr, hier die -128.
Früher wurden zum Teil noch Rechner benutzt, wo es eine -0 gab (alles 1er), und damit einen symmetrischen Zahlenbereich, das nannte man 1er Komplement. Diese Rechner waren mathematisch schöner, aber komplexer zum herstellen und sind somit verschwunden.
Als Tabelle der Grundoperationen sieht zusammengefasst so aus:
a b & | ^ --- ----- 0 0 0 0 0 0 1 0 1 1 1 0 0 1 1 1 1 1 1 0
Schliesslich gibt es noch die NICHT (englisch NOT) Zeichen: ~ Funktion, mit nur einem Operand. Dessen Ergebnis ist NICHT (= Gegenteil vom) Operand, also: ~0=1 und ~1=0. Man nennt diese Operation Inversion. Sie ist vergleichbar mit einer Negation (Minus). 2 mal Inversion (NOT NOT) heben sich auf, wie 2 mal Negation (Minus Minus gibt Plus).
Die Kombination (a & b) | (c & d) ist übrigens so häufig, dass man diese auch ohne Klammern standardmässig so berechnet, wie man auch bei einem (a * b) + (c * d) man zuerst beide Multiplikationen ausrechnet. Ebenso kommt Inversion vor allem anderen, wie Negation. Ebenso sind UND und ODER kommutativ wie Multiplikation und Addition.
Diese Boolsche Logik ist übrigens ein klassischer Fall von etwas das lange vor seiner Nützlichkeit entdeckt wurde, George Boole (1815-64) war Mathematiker im 19. Jahrhundert. Das war, wie man unschwer sieht, im 19. Jahrhundert nur eine mathematische Kuriosität, komplett nutzlos. Heute kann man damit Rechner Logik beschrieben, genauso wie man mit Formeln Programme schreiben kann.
Wenn man die eine einziffrige Addition sum=a+b anschaut, so sieht man, dass die hintere Stelle davon ein EXODER ist. Und der Übertrag ist ein UND. Dieses EXODER/UND Paar nennt man einen Halb-Addierer, weil er nur a+b, aber nicht +Übertrag rechnen kann:
sum sum a b dez bin üb summe 0 + 0 = 0 = 00 = 0 0 0 + 1 = 1 = 01 = 0 1 1 + 0 = 1 = 01 = 0 1 1 + 1 = 2 = 10 = 1 0 a UND b _/ \_ a EXODER b
Aufwendiger wird es wenn man dann die zweite oder folgenden Ziffern rechnen will. Da muss man noch den Übertrag (der bisher immer 0 war) berücksichtigen, für einen Voll-Addierer sum=a+b+üb. Der Aufwand steigt auf 2 2-Eingang EXODER, 3 2-Eingang UND, sowie 1 3-Eingang ODER:
sum sum a b üb dez bin üb summe 0 + 0 + 0 = 0 = 00 = 0 0 0 + 1 + 0 = 1 = 01 = 0 1 1 + 0 + 0 = 1 = 01 = 0 1 1 + 1 + 0 = 2 = 10 = 1 0 0 + 0 + 1 = 1 = 01 = 0 1 0 + 1 + 1 = 2 = 10 = 1 0 1 + 0 + 1 = 2 = 10 = 1 0 1 + 1 + 1 = 3 = 11 = 1 1 üb = (a UND b) ODER (a UND üb) _/ \_ sum = (a EXODER b) EXODER üb ODER (b UND üb)
Die hintere Stelle ist schlicht a und b addieren (was nie mehr als 2 geben kann), und dann noch den Übertrag hinzuzählen (das geht nie über 3). Für den Übertrag braucht man Tests auf "über 1", was der Fall ist wenn irgend eine der 3 Kombinationen 2 gibt (die UND laufen als "beide 1 Tester", das ODER läuft als "über 1 Sammler"), für alle 3 Eingaben sind dann schlicht alle 3 UND aktiv, das ODER kümmert das nicht.
Dies alles muss man nun für die Anzahl Bits wiederholen. Das kostet ordentlich grossen Aufwand (Bits mal 6 Elemente). Und das ist immer noch ein ineffizienter Addierer. Es muss für jedes Bit zuerst das Übertrag-aus des Vorgängers bis zu Bit seinem Übertrag-ein kommen, das nennt man kaskadierten Übertrag (ripple carry). Und das ist langsam, weil man pro Bit die UND+ODER Durchlaufzeit abwarten muss. Man kann dies beschleunigen (mit einem look-head carry), aber das kostet weiteren Aufwand.
Und dann sollte ja noch die Subtraktion rein. Die kann man zum Glück recht einfach rechnen, indem man diff=a-b als diff=a+(-b) rechnet. Also den bestehenden Addierer um einen schaltbaren Negierer beim zweiten Operand erweitert. Der Aufwand dafür ist hauptsächlich ein steuerbarer Invertierer pro Bit (ein drittes EXODER). Dazu noch bei der ersten Stelle den Übertrag auf 1 zwingen. Oder aber den Ersatz des ersten EXODER durch eine kompliziertere Schaltung (ALU, siehe weiter unten), die aber massiv weiteren Aufwand verlangt.
Multiplikation oder gar Division brauchen noch viel mehr Aufwand. Für 32bit*32bit muss man z.B. 32 separate Additionen zu je 32bit ausführen. Entweder hintereinander in der selbigen Hardware (kostet 32 Taktzyklen), oder für jede Addition eine Wiederholung der obigen Hardware (kostet 32 mal Hardware).
Man sieht auch langsam, wohin all die Millionen Transistoren der modernen Chips hingehen. Bzw warum man früher massive Gestelle voller vieler Schaltungen benötigte (und warum Computer früher so teuer waren). Wie man sieht, wurde es aus gutem Grund erst so um 1990 (486er, 1.08 Millionen Transistoren) im PC zum Standard, die ganze Multiplikation alles aufs mal zu rechnen. Bei 8bit Prozessoren ist bis heute nur möglicherweise Multiplikation (z.B. 6809 oder AVR) und sehr selten Division (z.B. 8051) im Chip drin. Selbst manche 32bit RISC Prozessoren haben keine Division drin (z.B. ARM). Frühe Sparcs hatten beides nicht.
Da UND ein "Durchlass" und ODER ein "Sammler" sein kann, kann man diese dazu verwenden. Dazu bekommt jeder wählbare Eingang ein UND, der neben den Daten einen "Steuereingang" hat. Die Ausgänge aller UND werden von einem breiten ODER gesammelt.
Dies ist ein häufiger Sonderfall von (a & b) | (c & d), wo a und c die Eingänge e1 und e2 sind, und b und d die Steuereingänge s1 und s2 sind.
Sehr oft will man, dass ein Ausgang gleich einen oder den anderen Eingang ist. Um zu wählen welchen, will man nur ein Auswahl-Signal. Wenn dieses 0 ist soll der erste Eingang, bei 1 der zweite Eingang herauskommen:
e0 e1 wahl aus 0 0 0 = 0 (= e0) 0 1 0 = 0 (= e0) 1 0 0 = 1 (= e0) 1 1 0 = 1 (= e0) 0 0 1 = 0 (= e1) 0 1 1 = 1 (= e1) 1 0 1 = 0 (= e1) 1 1 1 = 1 (= e1) \_ (e0 UND (NICHT wahl)) ODER (e1 UND wahl)
Für e1 ist das UND ein normales "lass e1 durch wenn wahl=1". Für e0 will man nun durchlassen wenn wahl=0, also kehrt man wahl um, mit NICHT, um seinem UND auch ein 1 zu bieten. Man nennt so eine Kombination einen Multiplexer, hier genauer ein 2:1 Multiplexer.
Man kann auch 4 Eingänge e0..e3 mit 4 UND und entweder 4 Steuereingänge oder 2 Auswahleingänge (w0..w1) mit 4 Inverter Kombinationen ausführen. Das ist dann ein 4:1 Multiplexer. Mit 8 Steuereingängen oder 3 Auswahl w0..w2 kann man auch einen 8:1 mit e0..e7 haben, usw. Mit n Auswahl kann man immer (2^n):1 Signale multiplexen, weil man aus n Auswahlsignalen 2^n Steuereingänge durch NICHT und UND generieren kann. Die n Auswahl Signale kann man als n-bit breite Zahl auffassen, mit Wert 0..(2^n)-1, die man Adresse nennt.
Die oben aufgeführte Funktion für sum=a+b+üb (hinterste Spalte) von Digital Rechnen mit Boole weiter oben hat als Ergebnis 01101001, bzw die für den Übertrag hat 00010111. Wir können nun diese beiden Funktionen statt als 2 EXODER, bzw 3 UND plus 1 ODER auch einfach als 2 8:1 Multiplexer ausführen, mit a,b,üb als Auswahl Signalen und die beiden obigen Tabellen als deren 8 Eingänge.
Für die zusätzliche Subtraktion können wir einfach die Auswahl Add/Sub als vierten Auswahl Eingang zweier nun 16:1 grossen Multiplexer, mit je einer 16 grossen Tabelle verwenden.
Dieses Verfahren ist einfach zu entwerfen (keine Zerlegung der Logik in Grundfunktionen), und kann beliebig komplexe Funktionen, auch mit einigen oder vielen Eingängen durchführen. Es hat auch eine konstante recht schnelle Geschwindigkeit (nur die Durchlaufzeit von 1 mal NICHT+UND+ODER, identisch einem einzelnen Multiplexer oder EXODER). Es wird daher sehr gerne in der Ablaufsteuerung von Prozessoren eingesetzt, wo komplexe Funktionen dominieren. Z.B. wurde der Befehlsdekodierer des 1952/53er Röhrenrechners Whirlwind durch 120 Tabellen zu je 40 (5 Eingänge zu 2^5=32 Operationen + 8 Zusatzsignale) Bits implementiert.
Multiplexer Tabellen haben auch einen gravierenden Nachteil, sie sind teurer an Bauelementverbrauch als simple Logik. Man kann es aber etwas billiger haben, wenn man bei jedem Tabellenelement das 0 ist das zugehörige UND aus dem Multiplexer weglässt (und das ODER schmaler macht), und bei mehreren Tabellen (z.B. sum und üb) von den selbigen Eingängen (a und b und üb) die Inverter der Auswahl->Steuereingang Logik teilt.
Neben dieser Methode mit konstanten Tabellen, kann man aber auch mit variablen Tabellen arbeiten. Dabei werden die Tabellenwerte selber wiederum generiert. Ein einfacher 4:1 Multiplexer, mit einer 4 Bits grossen Tabelle als ihre 4 Eingangsbits e0..e3 vorgegeben, und die 2 zu kombinierende Datenbits a und b als Auswahlleitungen w0..w1, kann (wenn man an die 4 Eingansbits geeignete Tabellen anlegt) jede logische Kombination erzeugen:
e0..3 e0..3 jeweils a=1,b=1 a=0,b=1 a=1,b=0, a=0,b=0 0000 -> 0 1111 -> NICHT-0 = 1 1010 -> a 0101 -> NICHT-a 1100 -> b 0011 -> NICHT-b 1000 -> UND 0111 -> NICHT-UND 1110 -> ODER 0001 -> NICHT-ODER 0110 -> EXODER 1001 -> NICHT-EXODER
Sowas ergibt dann eine programmierbare Logikeinheit mit 2^4=16 Funktionen. Die DEC PDP-10 Rechner (mitte 1960er) haben z.B. beliebige Logikfunktionen in einem Program ermöglicht, indem die Hardware einfach pro Datenbit (36 davon) je einen solchen 4:1 Multiplexer hatte, und die gewunschte Funktion einfach als 4bit Tabelle in das Befehlswort codiert wurde. Der Befehl "Setze alle Bits der Variable b" wurde ausgeführt als "nehme Variable a und b" und gehe durch die Tabelle die immer 1 macht (1111) und speichere das Resultat in b".
Wenn man bereits eine solche Logikeinheit hat, und anderseits schnelle Übertragrechnung will (pro Bit nur das UND+ODER abwarten) von expliziter Logik, kann man auch beide Ansätze kombinieren. Der erste EXODER der Addition (und das zusätzliche der Subtraktion, und die ADD/Sub Auswahl) rechnet man per Logikeinheit (EXODER oder NICHT-EXODER). Die Übertrag Generierung und das zweite EXODER dagegen geschieht traditionell. Wenn man auch noch die Übertrag Schaltung abschaltbar macht (ein UND als Schalter in der Leitung von Übertrag zum EXODER) bekommt man dann eine 2*16=32 Funktionen Arithmetisch-Logische Einheit (ALU), z.B. den 74181 4bit ALU TTL Chip.
Aber auch die alles-als-Tabelle Version kann man hier machen. Die 2*4bit Daten-Eingabe, 1bit Übertrag-Eingabe und 5bit Funktionswahl (=14 Eingänge) sowie 4bit Daten-Ausgabe, 1bit Übertrag-Ausgabe (= 5 Ausgänge) lassen sich als einen Satz von 5 2^14(=16384bit) grosse Tabellen beschreiben, was in einen 27128 16kx8bit EPROM Speicherchip hinein passt. Wenn man die Funktionssammlung reduziert (man braucht zumeist nicht alle 32), kann man auch mit 1/2 (2764 8kx8bit) oder 1/4 (2732 4kx8) der Tabellengrösse auskommen.
Sobald wir Bits speichern können, können wir unsere Logik über mehrere Schritte laufen lassen. Dabei entsteht eine Abfolge (= Sequenz) von Speicherzuständen. Daher nennt man dies sequenzielle Logik. Logik ohne Speicher nennt man kombinatorische Logik, weil dort nur eine Kombination der aktuellen Eingänge möglich ist.
Wie speichert man nun Bits? Der einfachste Fall von Sequenziell ist, dass ein NICHT sich selber ansteuert. Folge ist: 0->1, das 1 dann nach vorne, worauf dann 1->0 passiert, wieder nach vorne, 0->1 und 1->0 und so weiter, unendlich. Damit versucht das NICHT dauernd sich selber umzuschalten, und ist unstabil. Das nennt man einen Oszillator. Der schwingt sehr schnell, so schnell die Schaltung schalten kann. Für einen langsameren Oszillator, der der restlichen Schaltung Zeit lässt, bremst man den Oszillator aus, mit einem Quarz in der Rückleitung (das Signal legt dann eine Strecke mit Schallgeschwindigkeit im Festkörper statt Lichtgeschwindigkeit im Leiter zurück).
Ein Kreis von 2 NICHT hat genau die gegenteilige Wirkung: 0->1->0, bzw 1->0->1, und ist damit bockstabil, ausser er wird von aussen gestört und somit umgeschaltet. Das nennt man daher ein Flipflop (FF). Damit können wir einen beliebigen Zustand, 0 oder 1, anlegen und er wird festgehalten, bis wir ihn überschreiben.
Zum so ein FF umschalten, muss man ihm von aussen "stören", wozu man das Bit "verteilt" zuführen muss, je nach 0 oder 1 muss dafür das eine oder das andere NICHT "angeschubst" werden, daher wird das auch ein Reset/Set FF (RS-FF) genannt. Technisch muss man 2 UND+NICHT oder 2 ODER+NICHT nehmen, damit man mit den UND oder ODER ihren zweiten Eingängen 2 "Störleitungen" hat.
Ein RS-FF zu benutzen ist mühsam. Zum dies einfacher machen gibt es verschiedenste "Verteiler" Schaltungen, die ich hier zum Zeit sparen weglasse. Das Resultat davon ist jedenfalls das heute üblich benutzte Data FF (D-FF), das 2 Eingänge hat: D für Daten, und Clk (= Clock, = Takt) zum es "anschubsen", als "speicher jetzt das passende" Signal. Dieses Signal ist ähnlich wie das Auswahl Signal beim Multiplexer, kein Datensignal sondern Steuerung.
Wenn man nun eine Zahl, die ja aus mehreren Bits besteht, speichern will, dann braucht man pro Bit ein D-FF. So eine Sammlung von D-FFs mit einem gemeinsamen Clk Signal nennt man ein Register. In so ein Register passt eine einzelne Variable. Und damit haben wir ein Konzept das jeder Programmierer kennt. Rechnen tut man ja indem man varible3 = variable1 OP variable2; macht, oder Varianten davon (Konstanten sind wie Variablen, aber aus dem Program zu lesen, statt berechnet). Im Rechner ist das nun 2 Register (für variable1 und 2) auswählen und zur ALU schicken, ALU Funktion auswählen, und beim Ergebnis Register (für variable3) einen Clock Puls anlegen.
Weil Speicher mit einem x:1 Multiplexer ausgewählt wird, der mit n Bits an Auswahl Signalen angesteuert wird (also ein (2^n):1 Multiplexer), haben Speicher auch immer ein 2^n Bits Grösse, und das ergibt die bekannte Grössenserie 2^10=1024=1k, 2^20=1048576=1M, 2^30=1073741824=1G. Aus diesem Grund werden Speichergrössen in 2^n Serien hergestellt, während alle anderen Grössen (Taktfrequenzen, Bitraten) in den üblichen 10^n Serien laufen. Daher weiss man auch immer genau ob eine "k" oder "M" Zahl mit 1000 oder 1024 verstanden werden muss, je nachdem ob es ein Takt oder eine Adresse ist.
Jede Variable entspricht einer Speicherstelle. Variablennamen sind nichts anders als symbolische Namen für die Adressnummer der Speicherstelle dieser Variable, also benamste Konstanten. Array Variablen sind eine Serie von Speicherstellen, deren Name die Adresse der ersten Speicherstelle (= Basisadresse) entspricht.
Da Adressen durch Bits representiert sind, kann man Adressen auch im Speicher als Daten abspeichern. Es ist dann möglich eine Adresse aus einer Variablen zu holen, in ein Register zu laden, und mit dessen Inhalt den Speicher erneut zu adressieren, und mit den Daten die dann ausgewählt sind zu arbeiten. Sowas nennt man indirekte Adressierung. Die dafür benutzten Variablen sind die allseits bekannten und oft nicht wirklich verstandenen Pointer.
Selbstverständlich kann man die Adresse bevor man sie benutzt auch rechnerisch umformen, z.B. für einen Arrayzugriff array[n] zur Basisadresse n dazu addieren. Das nennt man indizierte Adressierung. Ebenso kann man einen Pointer auf die Array Basisadresse setzen, und nach jedem Benutzen um 1 Element erhöhen, z.B. mit pointer++, das nennt man dass post-incrementierte Adressierung. Moderne Rechner machen diese Addition während sie auf Antwort vom Speicher warten, mit der ohnehin noch unbenutzten Addiereinheit.
Die Fähigkeit beliebige Speicherstellen in beliebiger Reihenfolge zu benutzen ergab auch den Namen Random Access Memory (RAM), im Gegensatz zu Disk basiertem Speicher.
Wie wir schon wissen rechnet der Addierer oder die ALU als kombinatorische Logik dauernd mit, egal was vorne ansteht, egal ob das Ergebnis benutzt wird. Man muss nur das Clk Signal des richtigen wartenden Registers anstossen. Anderseits wissen wir dass man Addition/Subtraktion/etc muss auswählen können. Ebenso muss man x verschiedene Multiplexer steuern für Eingabedaten auswählen. Und schliesslich Register und Speicher mit den richtigen Demultiplexer und Clk Signalen versorgen.
Für jede einzelne Rechenoperation braucht es dazu eine Sequenz von solchen Steuersignalen. Verschiedene solcher Sequenzen sind in der Ablauflogik im Befehlsdecoder das Prozessors eingebaut. Ein Program solle nun dann eine Sequenz nach der anderen von diesen auswählen. Dazu besteht es aus einer Liste von Nummern, die je eine Sequenz auswählen, den Befehlen oder Instruktionen.
Ein Ausschnitt C Code als Beispiel:
... int b=10, c=5; /* nur statische Variablen weil einfacher */ int proc(int a) { /* nur ein Parameter damit einfacher */ b = a+b*c-30; return (b*4-1); } ... int d; ... d=proc(5); ...
Nun übersetzen (compilieren) wir das zu Assembler, in diesem Beispiel für einen Z80 Prozessor, das Resultat ist für jeden Prozessor anders. Man sieht dass es mehrere Befehle für einen Ausdruck braucht:
... b: DW 10 ; 2 Bytes Platz reservieren, mit 10 füllen c: DW 5 ; int wird hier 16bit definiert, Z80 üblich proc: PUSH HL ; Parameter a (in HL Register) wegspeichern ; für später LD HL,(b) ; Arbeits Register HL = b LD DE,(c) ; Hilfs Register DE = c CALL mult16 ; hoffentlich vorhandene 16bit*16bit Routine POP DE ; a wieder zurückholen, nach DE ADD HL,DE ; HL = a+b*c LD DE,30 ; 30 holen für Subtraktion XOR A ; C Flag löschen damit SBC richtig geht SBC HL,DE ; HL = a+b*c-30 ; man könnte auch LD DE,-30 und ADD HL,DE ; nehmen aber ich wollte den Mangel an ; SUB HL,DE zeigen und den SBC Flick ; so etwas passiert oft in Assembler LD (b),HL ; Speicherplatz b = HL ADD HL,HL ; HL = HL*2 ADD HL,HL ; HL = HL*2, ergibt *4 DEC HL ; HL -= 1 RET ; HL enthält den Rückgabewert ; übliche Konvention dazu ... d: DW ; ohne Vorgabe wird mit 0 gefüllt ... LD HL,5 ; erster Parameter in HL übergeben ; oft werden einfach alle Parameter per Stack CALL proc ; proc aufrufen LD (d),HL ; und Ergebnis wegspeichern nach d ...
Übersetzen (Assemblieren) wir das weiter zu Binärcode (das was im binären Programfile drin ist). Man sieht das es teils mehrere Bytes (bei Z80 1..5 möglich) Code pro Befehl braucht:
Adr Bytes (alles in Hexadezimal, 0 = binär 0000, F = 1111) .... ; ... 1234: 0A 00 ; b DW 10 ; 000A = 10, merken: b = 1234 1236: 05 00 ; c DW 5 ; 0005 = 5, merken: c = 1236 1238: E5 ; procPUSH HL ; merken: proc = 1238 1239: 2A 34 12 ; LD HL,(b) ; 2A = LD HL,() 34 12 = 1234 von oben 123C: ED 58 36 12 ; LD DE,(c) ; ED = erweiterter Befehl, ED Tabelle ; solche Unregelmässigkeiten ; sind typisch für ; abwärtskompatible CPUs 1240: CD xx xx ; CALL mult16 ; CD = call, xx xx = wo mult16 ist 1243: D1 ; POP DE 1244: 19 ; ADD HL,DE 1245: 11 1E 00 ; LD DE,30 ; 001E = 30 1248: AF ; XOR A 1249: ED 52 ; SBC HL,DE ; wieder nur dank ED Tabelle 124B: 22 34 12 ; LD (b),HL 124E: 29 ; ADD HL,HL 124F: 29 ; ADD HL,HL 1251: 2B ; DEC HL 1252: C9 ; RET .... ; ... 2345: 00 00 ; d DW ; automatisch auf 0, merke d = 2345 .... ; ... 3456: 21 05 00 ; LD HL,5 ; *** Program startet hier *** 3459: CD 38 12 ; CALL proc ; 1238 von oben, dem PUSH HL 345C: 22 45 23 ; LD (d),HL .... ; ...
Um dies auszuführen wird nun so vorgegangen: Mit einem speziellen Adressregister namens Programzähler (PC), das momentan z.B. die Adresse 3456 drin hat, wird auf den Speicher zugegriffen. Es kommt der Inhalt 21 (der Befehl LD HL,Konstante) zurück. Dem PC wird 1 addiert, damit er für das nächste Byte vom Program bereit it, normales post-increment. Wobei anzumerken ist, dass post-increment eigentlich für dies hier erfunden wurde, und erst später für Arrays genutzt wurde.
Der Prozessor führt dann die für LD HL,Konstante einverdrahtete Sequenz aus, also mit dem PC nochmals 2 mal Zugriff (und danach jeweils PC + 1) und die Daten (05 00) in Register L und H (in der Reihenfolge) zu verfrachten. Dann ist der Befehl fertig.
Also nächsten Befehl holen (und PC + 1), was CD (CALL) beinhaltet. Das wird ausgeführt mit PC nochmals 2 mal Zugriff (und PC + 1), dann resultierenden PC (345C) wegspeichern damit der RET später funktioniert. Dann das geholte 1238 in den PC laden, der nächste Befehl ist das E5 (PUSH HL) in 1238. CALL macht also nichts anders als PUSH PC und LD PC,Konstante, und RET ist dann nichts anderes als POP PC.
So geht das weiter, Befehl für Befehl, solange Strom da ist, anfangend nach dem Einschalten oder Reset (welche beide PC=0000 machen). Immer Befehl holen und auswerten und ausführen, ohne Unterbruch. Daher braucht man auch ein ROM/PROM/EPROM/Flash Speicher mit einem Bootloader oder BIOS drin, damit der Prozessor sofort etwas zum machen vorgesetzt bekommt, sonst ist sofortiger Absturz vorprogrammiert (bzw vorverdrahtet).
Wer mal ein beliebiges C Program in den Assembler seines PCs (vermutlich ein 80386 Nachfolger) sehen will, sollte die -S Option vom gcc anschauen. Das artet sehr schnell in sehr viele Befehle aus, und daher auch so grosse Programfiles.
Bits kann man nicht nur wie heute üblich mit Elektronik machen, auch mit Relais (Elektromechanik) oder gar purer Mechanik oder Hydraulik oder gar Seilzügen geht dies. Man braucht bloss etwas womit man eine grosse Anzahl Elemente herstellen kann, die genug klein, billig und zuverlässig sein müssen. Die einfachste und sichtbarste bekannte Technologie, und damit auch primitivste mögliche Implementation ist in Mechanik. Diese folgt daher hier als erstes.
Für jede Implementation müssen wir als erstes eine Methode festlegen, wie wir die beiden Bit Zustände 0 und 1 representieren wollen. Erst danach können wir daran gehen, Strukturen zu entwerfen, die gesteuert durch Eingangsbits neue Ausgangsbits erzeugen. Als mechanisches Besipiel werd ich hier Schieber nehmen, also längliche steife Teile die ihrer Läengsache entlang verscheibbar sind. Dabei soll eine Endposition 0 und die andere 1 markieren.
Die Implementation von ODER erweist sich als trivial. Man braucht nur einen Ausgangsschieber, mit einer Stossfläche, die von den Stossflächen alles Eingangsschieber gestossen werden kann. Im gewissermassen ist der Ausgangsschieber ein "Sammler" der Eingagsschieber, und ODER ist ja die "sammeln" operation. Solange nur ein Schieber e0..e2 nach rechts (=1) geschoben ist, ist auch aus nach rechts verschoben:
0<->1 User stösst e0 Schieber nach rechts .. e0 |`--------------..--. aus Schieber von e0 ODER e1 ODER e2 |.--------------'| | .---------. .. `' | | 0<->1 .| .-----. |---. e1 |`--------------. | `----------' | | /| | |/\ | Anzeigefenster |.--------------' | .----------. | | | | |\/ | zeigt 1 an :: | ||\\\\\\| `| `-----' |---' e2 |`--------------. | | Feder `---------' |.--------------' `--' die aus Schieber nach links drückt `' ^ Druckknöpfe für User Eingabe
Dagegen ist die Implementation von UND einiges komplizierter. Schliesslich soll hier der aus Schieber nur dann bewegt werden wenn alle e* Schieber gedrückt werden. Dies kann man bewerkstelligen, indem man einen der e* Scheiber nimmt, und ihm am Ende einen Winkelhebel anbaut, der von einem anderen e* in/aus dem Weg geschoben werden kann. Nur wenn er in dem Weg ist, und dann noch der Schieber geschoben wird, hat das Auswirkung:
===== e0 0 A Hier auf/ab gezeichnet, kann aber Winkelhebel | | | auch seitliche Ausweichung sein \ | |_.-. 1 V .. \ _| |_.-' aus wenn e0 unten/1 UND e1 rechts/1 e1 |`--------.-'_:-' .------------- |.--------`-' `------------- -> Anzeige `'
Man sieht hier beim UND bereits eine ganze Menge Probleme die für mechanische Logik typisch sind:
Ein EXODER oder ein NICHT bringen noch grössere Probleme mit sich, sind aber machbar.
Wie man sieht ist Mechanik zwar möglich und recht simpel, aber weder schnell noch günstig. Nicht überraschend wurde auch nur ein ernsthafter Computer in Mechanik implementiert, der Z1 von Karl Zuse (ca 1935), aus Blech und Stangen mit Laubsäge in der elterlichen Wohnung gebaut. Von dem steht übrigens ein Nachbau (von ihm selbst gemacht) im Deutschen Museum in München.
Taste drücken | Hebel rotiert _.-' .-------------. V | _.-' .-. S | | _.-o | Strom o' | | c | .----. `----o' o-- - - ----->--------. \ | | h .----------. | | .---|---- \ | | r | | + - | | Distanz Elektro`[/////]- \ | | e V | | | | magnet | | | i | Batterie | `--------------- - - --------------' \ / b `----------' | V e Sender | Empfänger r
Schnell wurde dabei gemerkt, dass der vom Sender angeschickte Strom mit der distanz immer schwächer wurde. Die Lösung bestand dann darin, bevor es zu schwach wurde, einen Elektromagneten hinzustellen, der einen weiteren Schalter (mit eigener Batterie) betätigte, und somit ein wieder starkes Signal weiter schicken konnte. Der erste (nur digital nutzbare) Vertärker war erfunden. In Anlehnung an die Relaisstationen der Pferdepost (Reiter wechseln alle paar Stunden an einem solchen das Pferd gegen ein frisches) wurden diese elektromagnetisch betätigten Schalter ebenfalls Relais genannt (das Signal wechselt Stromkreis zu einem frischen).
Da Relais erlauben somit, mit Strom weiteren Strom zu schalten, also kann man mit ihnen ebenfalls digitale Logik implementieren. Dabei definiert man die Bits als kein Strom = 0 und Strom = 1. Der Magnet geht wie oben von Signal zu Minus ist also bei 1 Aktiv, und drückt damit den Schalter, der von Plus zu Signal verdrahtet ist.
Die ODER Funktion lässt sich nun sehr einfach implementieren: Man verdrahtet einfach die Schalter zweiter Relais neben einander. Egal ob der eine ODER der andere geschaltet wird, wird Strom abgegeben. Im Gegensatz zu der Mechanik oben ist nun aber auch ein UND simpel: Man verdrahtet einfach so, dass die Leitung von Plus durch den ersten UND dann durch den zweiten Schalter geht. Auch ein EXODER ist einfach: 2 Schalter in beiden Positionen Anschlüsse haben und über Kreuz zusammengehängt sind. Auch ein NICHT ist nur ein Schalter der nur ungedrückt zu ist:
| | | V V V A _.-o ODER _.-o _.-o UND | NICHT ----o' o---- ----o' o------o' o---- ----o----oo---- | | | | | V | V A | _.-o | _.-oo------o | EXODER `--o' o--' ----o' o------oo----o----
Richtungen spielen auch keine Rolle, da Strom einfach den Drähten folgt. Energie liefern die Batterien, und nicht die Signale. Und es ist schneller weil die Schalter die einzigen bewegten Teile sind, nicht ganze Schieber. Und bei Reed Relais sind die bewegten Schalterteile auf eine unter 1 Gramm schwere federnde Metallzunge reduziert, was Frequenzen bis etwa 1kHz zulässt.
Diese Vorgänge sind zwar interessant, aber kompliziert und interessieren uns für die Verständnis von Computer Hardware nichts! Wir nehmen hier daher einfach an, das wir ein Netzteil haben, das Spannung liefert, solange wir ihm nicht zuviel Strom abverlangen (dann tut ein besseres Netzteil abschalten und ein schlechteres geht in Rauch auf).
Strom = Spannung / Widerstand (Ohmsches Gesetz)
Aus dem Grund, und weil Leistung (bzw Hitzeentwicklung) = Spannung * Strom ist, nimmt man heute die immer kleineren Spannungen, 5V, 3.3V, 2.8V, 2.5V, 1.8V, ... . Je weniger Spannung und Strom umso kühler, aber zuwenig und der Rechner wird unzuverlässig (leicht störbar). Also geht es in immer kleineren Schritten, und man ärgert sich über Spannungsregler einstellen auf den Motherboards.
Strom = Spannung / (Widerstand W1 + Widerstand W2 + Widerstand W3)
Strom Spannung ---->-----. | voll 100% .-. | | W1 | | 50% `-' | wenig 50% (100%-50%) .-. | | W2 | | 25% `-' | weniger 25% (50%-25%) .-. | | W3 | | 25% `-' | nichts 0% (25%-25%) ----------'
Dabei stellt man dann fest, dass an jedem Widerstand genau ein Teil der Gesammtspannung "anfällt" der seinem Anteil am Gesammtwiderstand entspricht.
Da Transistoren bei kleiner Spannung am Eingang "zu" sind, also grosser Widerstand, und grosse Spannung am Ausgang, und umgekehrt grosse Spannung am Eingang kleinen Widerstand macht, und somit der Spannungsteiler kleine Spannung abgibt, haben Transistor-Spannungsteiler automatisch eine Signal unkehrende Funktion, sind also automatisch eine NICHT Funktion.
Uns interessieren hier nur 2 Transistor Sorten: Die spannungsgesteuerten MOSFET (Metall Oxid Silizium Feld Effekt Transistor) Transistoren (die sind in den meisten modernen hochintegrierten Chips drin) ergeben dann NMOS Logik:
Strom Spannung ------------>----. | voll voll .-. | | W1 | | `-' T2 gross T2 klein ----------. |------- fast voll fast nichts | |--' `--|| T2 (n-MOSFET, ergibt NMOS Logik) |->. | | nichts nichts -----------------'
Und die stromgesteuerten Bipolar Transistoren (die sind in den frühen niedrig integrierten Chips drin und noch früher in Einzeltransistor Logikmodulen verwendet), ergeben dann RTL Logik:
Strom Spannung ------------>----. | voll voll .-. | | W1 | | ____ `-' T2 gross T2 klein --|____|--. |------- fast voll fast nichts W3 | |.-' Strom `---| T2 (npn-Bipolar, ergibt mit W3 die RTL Logik) Begrenzer |`>. | | nichts nichts -----------------'
Mit 2 MOSFET Transistoren (einem p-MOSFET oben und einem n-MOSFET unten) nennt man dies CMOS Technik (C wie complementary) und das ist seit 1985 in fast allen modernen Chips drin:
Strom Spannung ------------>----. | voll voll |-<' .--|| T1 (p-MOSFET) | |--. T1 kl /T2 gr T1 gr/T2 kl ----------| |------- fast voll fast nichts | |--' `--|| T2 (n-MOSFET, ergeben zusammen CMOS) |->. | | nichts nichts -----------------'
Mit 2 npn-Bipolar Transistoren (kein pnp-Bipolar oben weil zu teuer zum integrieren, daher ist auch ein 3. Ansteuertransistor nötig) und ergibt sich die TTL Technik die von 1970 bis 1985 dominierte.
Strom Spannung ------------>----. | voll voll |.-' .---| T1 (npn-Bipolar, kein pnp, braucht daher T3!) |.-' |`>. T1 kl /T2 gr T1 gr/T2 kl ------| T3 |------- fast voll fast nichts |`>. |.-' `---| T2 (npn-Bipolar, ergibt mit oben TTL) |`>. | | nichts nichts -----------------' (vereinfacht gezeichnet, Widerstände fehlen)
Nimmt man nun bei MOSFETs und NMOS mehrere untere Transistoren so müssen alle mit 2.4..5V gefüttert werden, damit sie alle klein werden und der Ausgang auf 0..0.5V geht. Damit ergibt sich automatisch ein UND gefolgt von NICHT, also ein NICHT-UND, (englisch NOT-AND, kurz NAND):
Strom Spannung ------------>-----. voll=5V voll=5V | .-. | | W1 | | `-' T2a UND T2b kl T2a ODER T2b gr |------- fast nichts=0.5V fast voll=3.5V |---' -------------|| T2a (n-MOSFET, ergibt NMOS Logik) |->-. | |---' -------------|| T2b (n-MOSFET, ergibt NMOS Logik) |->-. | | ------------------' nichts=0V nichts=0V
Bei Bipolar Transistoren geht diese "Staplerei" nicht, weil der Strom durch T2a durch T2b gestört werden würde. Da muss man mehrere untere Transistoren neben einander stellen. Was dann dazu führt dass T2a ODER T3b klein ein 0 produziert, man bekommt ein NICHT-ODER (englisch NOT-OR, kurz NOR), das für RTL Logik typisch ist (und nervend, siehe später). Man kann RTL Logik retten indem man 0=2.4..5V und 1=0..0.8V umdefinitert, das nennt man inverse Logik, aber es ist verwirrend damit zu arbeiten. Daher ist RTL Logik schon früh selten geworden.
Alternativ kann man aber mit bipolar Transistoren auch immer nur einen T2 nehmen, aber dann in der Eingangsleitung mehrere Dioden Da und Db setzen (D3 ist nur ein weiteres Hilfsbauteil wie W3). Damit wird der Transistor gross bleiben, solange irgendein Eingang wenig liefert. Dieser Trick ist uralt, und wurde bereits in Röhrenrechnern benutzt (Dioden kosten etwa 1% von einer Röhre und ihrer Helferteile). Dies liefert auch das erwünschenswertere NAND Verhalten, und verdrängte RTL Logik. Man nennt sowas dann DTL Logik:
Strom Spannung ------------>-------. | | voll=5V voll=5V .-. .-. | | W3 | | W1 | | | | Da `-' D3 `-' T2 gross T2 klein --|<--+-->|--. |------ fast voll fast nichts | | |.-' --|<--' `---| T2 (npn-Bipolar, ergibt mit Da u Db DTL Logik) Db |`>. | | nichts=0V nichts=0V --------------------'
Diese Logiken kann man auch alle komplementär machen. Bei CMOS mit je einem Transistorpaar pro Eingang. Dabei werden die unteren "gefaltetet" hintereinander verdrahtet (von T2a zu T2b), und die oberen T1a und T1b "neben einender" verdrahtet. Jeder Eingang a und b steuert sein T1 und T2 Paar an.
Bei TTL wird das normale TTL T1 und T2 Paar (mit T3 Helfer) genommen und mit DTL-artigen Dioden und W3 versehen. RTL kann man nicht komplementär machen, und das ist neben der NOR Logik ein weiterer Grund für dessen aussterben.
Man kann dies auch bei beiden für beliebige Eingangszahlen "verbreitern", bei CMOS einfach mehr Paare, bei TTL einfach mehr linke Dioden.
Bisher können wir nur NICHT-UND (oder NICHT-ODER) herstellen. Wie steht es mit dem Resten der logischen Funktionen?
Die Rechner wurden kleiner, schneller und billiger (und stromsparender und zuverlässiger), dadurch das man in der zweiten Hälfte der 1960er die integrierte Schaltungen einführte, mehrere Transistoren auf einen Stück Silizium produziert und verschaltet. Zuerst mit SSI (small scale intergration, bis 30 Transistoren) nur ein grosses Modul pro kleinen Chip (z.B. ein TTL 7400 ist 4 Stück 2-Eingang NICHT-UND). Dann mit MSI (medium scale integration, bis 300 Transistoren) immer mehr (z.B. ein 7474 ist 2 volle D-FF, 7483 ist ein 4Bit Voll-Addierer), und dann mit LSI (large scale integration, bis 3000 Transistoren) noch mehr (z.B. ein 74181 ist eine ganze 4bit ALU, 74374 ist ein 8bit Register) und schliesslich ganz viel (z.B: ein 74481 ist ein 4bit Ausschnitt eines ganzen Rechner Datenpfades).
Solche 74(x)xx Chips sind universell verwendbar, und wurden daher bis ca 1990 auch in professionellen Schaltungen verbaut. Heute sind sie wegen langsam und viel Platzverbrauch und teuer (die Kosten vieler kleiner Chips addieren sich) fast nur noch in Hobby und unkritischen professionellen Schaltungen drin.
Je grösser man Chips macht, umso mehr trift nun aber das Problem auf, dass sie zu spezialisiert werden, nur für eine Funktion brauchbar, also nicht für jedermann nutzbar. Kombiniert man die daraus resultierenden geringeren Stückzahlen mit den immer grösseren Entwicklungskosten grosser Chips und sie werden zu teuer. Das ist so ab 1000-10'000 Transistoren der Fall.
Grosse Firmen wie IBM oder Telephonzentralen Hersteller können sich die Kosten erlauben, einen eigenen 1000-1'000'000 Transitor Chip nur für sich entwickeln zu lassen und die Kosten über wenige 1000 Kopien bezahlen zu lassen (daher sind Grossrechner auch heute noch so teuer). Sowas nennt man ein Application Specific Integrated Circuit (ASIC). Der Rest der Welt hat keine Chance da mitzumachen. Das war der Stand um 1970.
Eine Spezialform von ASIC, die Gate Arrays (GAs) erschien um 1980. Sie nutzen die CMOS Chip Struktur aus und baut die ganzen Transistor Zeilen und Stromversorgung nur einmal, um dann nur die Verdrahtung schaltungsspezifisch zu machen. Das verringert die Entwicklungskosten um den Faktor 10, aber unterhalb von 10'000 Stück wird es auch so zu teuer. Ein einzelnes solches GA im ZX81 ersetzte die gleichwertigen 18 74(x)xx Chips des ZX80. Ebenso haben ein paar ASICs ("Chipset") in meinem 386er die fast gleichwertigen vielen (etwa 100) 74(x)xx Chips in meinem 286er ersetzt.
Der Mikroprozessor um 1970 löste dann dieses Mengenproblem auch für viele kleine Aufgaben. Dies ist nichts anders als ein kleiner kompletter Prozessor auf einem Chip. Alle Daten Ein- und Ausgaben sind/waren normale 74(x)xx Chips, oder ebenfalls universelle Prozessor IO Erweiterungschips (PIO, UART, FDC, ...). Die ganze anwendungsspezifische Logik steckt im Program des Mikroprozessors, gespeichert in einem ebenfalls universellen Speicherchip. Alle Chips sind daher in grossen Stückzahlen herstellbar und somit billig trotz komplex.
Der Speicher kann, da universell und für jedes Program geeignet, beliebig gross hergestellt werden. Und somit sind komplexe Programme in einem einzelnen Speicherchip möglich, und sind so kleiner und billiger als viele einzelne Chips. Wie wir heute wissen, entstanden aus Mikroprozessoren nach einer Weile (etwa 5 Jahre) die PCs, vollständige richtige Computer.
Der erste Mikroprozessor, der Intel 4004 ( eingescanntes Datenblatt DPF) (daraus extrahierte Daten) bestand aus 2300 Transistoren, rechnete mit 4 Bit, hatte 17 4bit (1 Haupt und 16 Hilfs) Register, und konnte 4096*8bit ROM (kein PROM oder EPROM!) Program und 256*4bit separates SRAM (Daten) adressieren. Er war für einen Tischrechner (Vorläufer eines Taschenrechners) gedacht, und wurde ab Nov 1971 allgemein verkauft.
Der 1 Jahr später erschienene 8008 ( eingescanntes Datenblatt DPF) (daraus extrahierte Daten) mit 3500 Transistoren rechnete bereits mit 8 Bit, hatte nur 1+6 Register, konnte aber schon 16k*8bit gemeinsamen Program+Daten Speicher adressieren, und war für die Interna eines Terminals gedacht. Aus diesem wurden die ersten primitiven PCs (Scelbi 8-H, Titus Mark 8, nochmals Titus Mark 8) gebaut. Sein inkompatibel erweiterter Nachfolger 8080 (1974) war dann die Basis der ersten Phase der PC Revolution (die ganzen S100 und CP/M Rechner). Der oben benutzte Z80 ist wiederum ein kompatibel erweiterter 8080 Clone.
Als logische Konsequenz der Mikroprozessoren gab es mit grössern Chips danach die Mikrocontroller, anfangend mit dem 8048 (neu gezeichnetes Datenblatt PDF) (daraus extrahierte Daten). Diese haben auf einem einzelnen Chip einen Prozessor (im 8048 in etwa ein auf 8bit Daten verbreiteter 4004) und etwas Speicher (etwas ROM/PROM/EPROM (im 8048 1-4kBytes) und wenig SRAM (im 8048 64-256Bytes) und etwas IO (im 8048 3 8bit Ports), also ein ganz simpler kompletter Computer. Man muss nur Stromversorgung und etwas analog Krempel anhängen, und ein Program dafür schreiben.
Diese Microcontroller sind was man üblicherweise heute in Geräten findet, z.B. in Audio oder Video Geräten (und in deren Fernbedienungen), oder Telephonen (und in deren SIM Karten), oder in Haushaltsgeräten (Küche, Waschmaschine), oder in Heizungen, oder in Maschinen oder Autos oder Liften, oder in Ampelsteuerungen, oder in Verkaufsautomaten, aber auch in den meisten PC Peripheriegeräten (Tastatur, Maus, HD, Drucker, Scanner, Modem, WLAN Accesspoint), dort z.T. grössere Versionen mit separatem Speicher. Hierbei spricht man dann von Embedded Systemen. Der heutige Normalbürger besitzt und benutzt, ohne es zu wissen, etwa 30 solcher Computerchen.
Die erste Form die sich gross durchsetze und Jahrzehnte lang den Standard blieb waren die Kernspeicher. Hier wird pro Bit genau ein kleines billiges Bauteil, ein ringförmiges Stück Ferrit (Eisenoxid), benutzt. Da dieses gar keine elektrischen Anschlüsse hat werden die Bits als Magnetfeld eingespeichert statt als elektrische Spannung. Dazu wird ein Gitter horizontaler und vertikaler dünner Drähte benutzt, bei denen an jedem Kreuzungspunkt ein Ferritkern sitzt.
Zum die Drähte möglichst nahe zu packen, und somit den Speicher klein zu machen, werden sie abwechslungsweise von ihren beiden Enden mit Strom versorgt. Dies führt dazu, dass der Strom abwechslungsweise in beiden Richtungen läft, wesshalb die Ringe abwechslungsweise umgekehrt angeordnet werden:
| | | | Draht vertikal | | .-. .-. | | | .' .' `. `. | Draht horizontal --------.' .'--------`. `.-------- ------.' .'------------`. `.------ --> Stromrichtung .' .' | | `. `. `-' | | | | `-'Kern | | | | 4 Kernspeicher Bits .-. | | | | .-. `. `. | | .' .' in beiden Richtungen ------`. `.------------.' .'------ horizontal und vertikal --------`. `.--------.' .'-------- <-- viele Male wiederholt | `. `. .' .' | | | `-' `-' | | z.B. 4kBit = 64x64 Kerne | | | | 4kByte = Stapel 8* 64x64 | A V |
Zum ein Bit einschreiben werden beide durch dessen Kern laufende Drähte mit Strom versorgt, alle anderen Kerne die nur auf einem Draht sind bekommen zu wenig Magnetfeld ab und bleiben unbeeinflusst.
Zum ein Bit auslesen wird einfach das Bit gelöscht. Ein dritter Draht der 45 grad quer durch alle Bits geht registriert ob das Bit dabei gekehrt wurde (der Kern gibt beim "Wenden" einen kurzen Magnetpuls ab, und erzeugt im dritten Draht so einen Stromimpuls), und folgich vorher gesetzt war. Hinter dem Speicher muss ein Detektor (ein Satz reiner RS-FFs) stehen, dessen Bits vorgängig alle gelöscht wurden, und nun dort wo ein Stromimpuls kommt gesetzt werden. Dann wird der Detektor wie ein normales Register ausgelesen.
Da das Bit beim Lesen gelöscht wird, muss es nach dem Lesen erneut geschrieben werden, bevor der Detektor für das nächste Lesen gelöscht wird, sonst geht das Bit verloren. Da man oft eine Variable ausliest, verändert und wieder speichert (z.B. einen Zähler inkrementieren) konnten manche alte Prozessoren das erste Rückschreiben in solchen Fällen auslassen (read-modify-write Zyklus).
Heute gibt es 2 Sorten Speicher Chips:
bit=0 für diese Spalte bit=1 | | -)|(----------------------------)|(---- 5V | | | | | `-| T1 T3 |-' | ein SRAM Speicherbit | ||-. .-|| | | .-| | | |-. | in beiden Richtungen | T5 | | | | T6 | horizontal und vertikal |--. .--+----|-..-|----+--. .--| viele Male wiederholt | | | | | /\ | | | | | | ----- `-| |/ \| |-' ----- | z.B. 16kBit 128x128 | --- ||-' `-|| --- | | | .-| T2 T4 |-. | | | | | | | | -)|(----------------------------)|(---- Auswahl der Zeilen | | | | | ----- ----- | | 0V 0V | ZZZZZZZZZZZZZZZZAAZZZZZZZZZZZZZZZZ ZZZZZZZZZZZZZZZZAAZZZZZZZZZZZZZZZZ ZZZZZZZZZZZZZZZZAAZZZZZZZZZZZZZZZZ Gesammtstruktur vom SRAM Chip ZZZZZZZZZZZZZZZZAAZZZZZZZZZZZZZZZZ ZZZZZZZZZZZZZZZZAAZZZZZZZZZZZZZZZZ Z = Zellen wie oben ZZZZZZZZZZZZZZZZAAZZZZZZZZZZZZZZZZ A = Auswahl der Zeilen ZZZZZZZZZZZZZZZZAAZZZZZZZZZZZZZZZZ B = Bits auslesen oder ZZZZZZZZZZZZZZZZAAZZZZZZZZZZZZZZZZ einschreiben BBBBBBBBBBBBBBBBSSBBBBBBBBBBBBBBBB S = Steuerung des Chips ZZZZZZZZZZZZZZZZAAZZZZZZZZZZZZZZZZ ZZZZZZZZZZZZZZZZAAZZZZZZZZZZZZZZZZ z.B. 6264, 64kBit = 65536 Bit ZZZZZZZZZZZZZZZZAAZZZZZZZZZZZZZZZZ organisiert als 8kx8bit ZZZZZZZZZZZZZZZZAAZZZZZZZZZZZZZZZZ 256 Zeilen a je 32*8Bit ZZZZZZZZZZZZZZZZAAZZZZZZZZZZZZZZZZ ZZZZZZZZZZZZZZZZAAZZZZZZZZZZZZZZZZ ZZZZZZZZZZZZZZZZAAZZZZZZZZZZZZZZZZ ZZZZZZZZZZZZZZZZAAZZZZZZZZZZZZZZZZWelche der Zeilen "aktiv" ist, wird von einer Hälfte der Adresse bestimmt. Die Spalte die "angeschubst" wird, hängt von der anderen Adresshälfte ab. Welche Leitung davon ob eine 1 oder 0 gespeichert werden muss. Zum Lesen wird einfach eines der beiden vertikalen Leitungen benutzt, das Bit bleibt unverändert. SRAM wird heute v.a. im Prozessor drin für die Caches benutzt, ebenso in externen Cache Chips, aber auch als Hauptspeicher in Mikrocontrollern, weil im Stromverbrauch batteriefreundlich
Bit für diese Spalte | | | | | ein DRAM Speicherbit |--. .----| |---| 0V | | | | | | wie SRAM wiederholt | ----- C ebenso A, B und S Struktur | --- T | | | | -)|(------------------ Auswahl der Zeilen |Alle Cs einer Zeile werden zusammen geladen und entladen. Um eines zu schreiben muss die ganze Zeile geholt werden, das eine Bit ersetzt, und die Zeile zurückgeschrieben werden. Lesen leert die Kondensatoren, also löscht es alle Bits der Zeile, also müssen alle neu geschrieben werden. Noch schlimmer als bei Kernspeicher. Das ist der Hauptgrund warum DRAM langsamer als SRAM ist. Selbst ohne Lesen geht der Inhalt nach einer Weile verloren, muss also regelmässig durch Lesen und Neuschreiben aufgefrischt werden. DRAM wird in den SD-RAM Chips auf den DIMMs benutzt, weil es am meisten Kapazität fürs Geld und Platz bietet. Das dauernde neu Laden kostet auch Strom, was es für batteriebetriebene Geräte ineffizient macht.
Ein Cache (aus SRAMs) kann die Langsamkeit vom DRAMs (und den Leitungen) verstecken, indem er (hoffentlich) die häufig benutzten Daten beinhaltet. Ausserdem tut der Cache gleich einen Block von 4 oder 8 Speicherstellen (eine Cache Zeile) holen, damit man nur jedes 4te oder 8te Mal warten muss auf die DRAMs ihre Zeile auslesen und neu schreiben Aktion
Auch diese Chips wurden bereits ab einem Tausend Transistoren (also vor den SRAMs die mehrere Tausend brauchen) interessant, zumals sogar bloss eine Diode (halber Bipolar Transitor) pro Bit benötigt wird. Nicht nur für Mikroprozessoren, sondern auch für "normale" Computer. Also wurden auch hier beliebige Grössen verkaufbar. Man muss nur das Program in den Speicher einbauen (ROMs), einbrennen (PROMs), oder nur draufschreiben (EPROMs und Flash).
Ein Problem der ersten ROMs war, dass ihr Inhalt bei der Herstellung eingebaut werden muss, bevor das Gehäuse hergestellt wird. Die Struktur ist ähnlich wie bei DRAM, aber mit einem Metalltröpfchen (oder eben nicht) statt dem Kondensator. Also musste man sein Program dem ROM Hersteller schicken, damit er die Tröpfchen richtig setzt/weglässt. Das war zeitaufwendig (monatelang warten), vor allem bei Bugfixes. Und teuer war der Prozess auch, wenn auch deutlich weniger langwierig und teuer als ein Gate Array oder gar ein ASIC fertigen lassen. Das kann man heute mit dem Pressen von CDs vergleichen. Das war eines der Hauptprobleme des nur-ROM Program des 4004 Prozessors, das der RAM-Program taugliche 8008 nicht mehr hatte.
Dieses Problem wurde gelöst durch die Erfindung von PROM Chips (P = programmierbar), die erst beim Chip-Käufer programmiert werden. Die haben statt Metalltröpfchen kleine Nickelbückchen, welches an unerwünschten Orten vom Benutzer "weggebrannt" werden können (einfach Adresse und Daten anlegen und einen Puls hohe Spannung 12.5..25V geben). Die PROMs stehen zu ROMs im selbigen Verhältnis, wie CD-Rs zu gepressten CDs.
Später entstanden auch noch die mit UV-Licht löschbaren EPROMs (E = erasable) was übrigens eines der ersten Chips der Firma Intel waren. Die heutigen ohne UV elektrisch löschbaren Flash Chips sind deren Nachfolger, mit Intel als grösster Hersteller, quasi die CD-RWs der Chips.
SRAM und DRAM ist im Vergleich zu all denen die HDs der Chips, beliebig oft jederzeit schnell beschreibbar, die allerdings ohne Strom alles vergessend, dafür aber ohne Beschränkung auf ganze Blöcke lesen/schreiben, eben RAM.
Natürlich sind auch ROMs keine Erfindung des Chip Zeitalters, es gab sie schon früher. Dabei wurden mehrere Techniken verwendet. Für kleine ROMs (maximal ein paar 100 Worte) nahm man grosse Felder von Dioden, pro Bit eine (und damit einiges Platz verbrauchend). Eigentlich die selbige Schaltung wie bei Chip ROMs, nur dass man statt fehlender Metalltröpfchen einfach die Diode wegliess (und so Kosten etwa halbierte, bei 0.25$/Diode sehr relevant). Auch das in Logiktabellen und Arithmetisch-Logische Einheit erwähnte Whirlwind 40x120bit Befehlsdekodierer ROM war so ein Dioden ROM. Diese kleinen ROMs waren fast nur für Bootloader nutzbar (z.B. PDP-8/E Autostart Option mit 32*12bit ROM), zum beim Rechnerstart etwas Program von der Disk zu holen, das dann das restliche System nachlädt. Bereits ein BIOS war zu viel.
Wollte man mehr Platz, kam eine ROM Variante des Kernspeichers zum Einsatz. Bei diesem wurde eines der beiden Drähte durch oder neben den Kern geführt, je nachdem ob das Bit 0 oder 1 sein soll. Da der Kern nur noch dazu dient einen Impuls zu erzeugen oder nicht, und nicht als eigentlichen Speicher, können mehrere Bits sich einen grossen billigen Kern teilen. Limite ist wieviele Drähte durch den Kern durchpassen. Für Serienfabrikation wurden die auf Verdrahtungsfehler (= Bitfehler in einem einzelnen Exemplar) anfälligen Drähte durch einen Stapel dünner Platinen (oder gar Folien) ersetzt bei denen die Leiterbahnen durch oder neben einen 2-teiligen Kern gehen, z.B. das TROS Design im Befehlsdecoder eines IBM 360/40. Es gab sogar eine reine Drahtschleifen Variante ohne die Kerne, die im HP 9100 Tischrechner verwendet wurde.
ROM/PROM/EPROM/Flash Chips machen aber eigentlich nur eines: für jede Adresse (= Eingangs Kombination von a Bits) am Ausgang d Datenbits von sich geben. Sie sind dazu so verdrahtet:
ROM/PROM/EPROM/Flash Ausschnitt 3 Adressleitungen, 2^3=8 Adressen, 4bit breit I = Inverter, ergeben ~A0, ~A1, ~A2, ... o = verbunden, - = keine Verbindung U = UND um Adresse zu dekodieren, pro Adresse eine Zeile ... A2 A1 A0 |__ |__ |__ | | | | | | | I | I | I Produktterm: | | | | | | | | | | ..-----o-----o-----o--U - 0 ---..--x--x--x--x- alle ~An UND = 0 | | | | | | | | | | ..-----o-----o--o-----U - 1 ---..--x--x--x--x- nur A0 rest ~ UND = 1 | | | | | | | | | | ..-----o--o--------o--U - 2 ---..--x--x--x--x- nur A1 rest ~ UND = 2 | | | | | | | | | | ..-----o--o-----o-----U - 3 ---..--x--x--x--x- A1 UND A0 rest ~ = 3 | | | | | | | | | | ..--o--------o-----o--U - 4 ---..--x--x--x--x- nur A2 rest ~ UND = 4 | | | | | | | | | | ..--o--------o--o-----U - 5 ---..--x--x--x--x- | | | | | | | | | | ..--o-----o--------o--U - 6 ---..--x--x--x--x- | | | | | | | | | | ..--o-----o-----o-----U - 7 ---..--x--x--x--x- x = Datenbits und Diode | | | | | | | | | | = o oder - : : : : : : : : : : | | | | O O O O ODER der Bits | | | | .. D3 D2 D1 D0 Daten Ausgang
Die "I" sind Inverter, die "o" sind Verbindungen (- = keine Verbindung) die beim Herstellen eingebaut werden, die zusammen mit den "U" UND Elementen sorgen dafür, dass bei jeder möglichen Adresse eine Zeile (Produktterm genannt) aktiv wird. Die x sind die Daten. Bei ROMs sind das die Metalltröpfchen, bei PROMs die Metallbrückchen, bei EPROMs und Flash spezielle Transistoren die "hangen bleiben" können. Beides wirken mit dem "O" ODER Elementen unten zusammen um die Daten zu sammeln. Das ROM/PROM/EPROM/Flash ist also eine NICHT+UND+ODER Logik (implementiert als NICHT+NICHT-UND+NICHT-UND). Eigentlich nichts anders als ein grosser Multiplexer bei dem die UND Teile vor dem ODER teilweise angeschlossen sind.
Man kann daher ROMs auch als kombinatorische Logik Chips missbrauchen. Dazu lädt man in den Speicherchip die gewünschte Logik als Tabelle. So wurde z.B. ein PROM im Disk Controler vom Apple II benutzt. Die MIT Lisp Maschine verwendete z.B. 16 Stück 1024x4bit PROMs um einen 32bit breiten 32:1 Multiplexer zu bauen, mit dem Bitfelder innerhalb eines Wortes verschoben werden.
Als kleines Beispiel würde unser 2:1 Multiplexer von Schalten mit Boole weiter oben eine Tabelle von (2^3)x1=8x1=8it benötigen:
Adresse Daten Wir verdrahten: ...76543210 76543210 A0 = e0, A1 = e1, A2 = wahl, D0 = aus ...000: ...0 ab A3... = andere Eingäange ...001: ...1 für andere Funktionen ...010: ...0 ab D1... = andere Ausgänge ...011: ...1 für andere Funktionen ...100: ...0 ...101: ...0 Eingänge können von mehreren ...110: ...1 Funktionen geteilt werden, spart Platz ...111: ...1 \wiederholen für alle möglichen Positionen
Als grösseres Beispiel würde ein 4bit Addierer, 9 Eingänge (4 a Bits, 4 b Bits, 1 Übertrag) und 5 Ausgänge (4 Ergebnis Bits, 1 Übertrag) haben. Also braucht das ein (2^9)x5=512x5=2560bit an PROM Platz. Und folglich hier eine 512-zeilige Tabelle, wesshalb ich das hier nicht aufzeichne. Das würde man minimal in einem 512x8=4kBit Chip implementieren. Wenn man aber z.B. einen 64kBit=8192x8bit Chip hat, könnte man noch eine weitere 13-9=4 Eingang und 8-5=3 Ausgang Funktion mit einbauen. Oder man nimmt einen weiteren Eingang und verwendet diesen um zwischen Addition und Subtraktion (oder anderen Funktionen, bis zur vollen 16-Funktion und Arithmetik ALU, addiert dafür 5 Adressleitungen) Tabellen zu wählen.
Es wurden auf diese Weise schon ganze Eigenbau Rechner (z.B. die MyCPU) mit nur TTL Register Chips und EPROMs gebaut.
PROMs brauchen so viel Platz, weil ihre Multiplexer voll dekodiert sind. Das heisst für jede Kombination der Eingänge hat es eine Zeile im Chip drin. Das gibt für n Adressleitungen, 2^n Adressen und somit 2^n Zeilen. Eine kombinatorische Explosion. Das ist so für Speicher sinnvoll, weil man mit möglichst wenigen Auswahlsignalen möglichst viele Speicherstellen haben will, aber für Logik massive Platzverschwendung. Das exponentiale Wachstum an Zeilen killt diese Lösung oberhalb von etwa 10..12 Eingängen (der 74181 hat deren 14).
Die Antwort darauf war ein Chip mit ebenfalls brennbarer Definition der Zeilen, also auch ein "x" überall links im Chip. Das gibt ein PLA, und wurde ab etwa 1975 hergestellt. So eins hat nur etwa 4*Ausgänge an Zeilen statt 2^Eingänge an Zeilen. So eines wurde z.B. im C64 als Adress-Decoder verwendet um die Speicher Chips (Haupt DRAM, Farbtabelle SRAM, 3 ROMs, 2 Modul ROMS, sowie IO Chips) auszuwählen. Mit 16 Eingängen und 8 Ausgängen wäre dazu ein 64kx8=512kbit PROM nötig gewesen. So ist es aber nur ein kleines 2*16x4*8+8x4*8=1280bit PLA. Das zu einer Zeit als es bei ROMs noch mit 64kbit Schluss war. Und viel schneller sind diese kleinen Chips auch noch, weil geringere Signallaufdistanzen.
Aber die PAL Entwickler gingen noch weiter und lieferten auch PALs mit Flipflops bereits drin, damit man nicht mit Ausgang-pin + externes FF + Eingangs-pin Pins (und Chips) verschwenden muss. Das waren die "R" Serie Register PALs. Z.B. ist ein PAL16R6 ein 20pin (2 Strom, 18 Daten) Chip, mit 8 Eingangspins, 16 Spalten (8 von Pins, 8 von Ausgängen), 64 Zeilen, 8 8-Eingang ODER, 6 FFs (andere 2 Ausgänge ohne FF), 8 Ausgangspins. Die restlichen 2 Pins sind einer Clk für die FFs und einer um die FF Ausgänge abzuschalten. Alle PAL16Rx haben 2*16*8*8=2048bit.
Aber die "R" PALs waren recht inflexibel. Sie haben genau 2/4/6/8 FFs, fest verdrahtet, und können nur alle Eingänge aufs mal schalten, und haben kein Reset für die Flipflops. Also wurde anfangs 1980er dann der flexible 22V10 erfunden. Dies ist ein 24pin (2 Strom, 22 Daten), mit 12 Eingangspins, 12+10 Spalten, abgestuft 2*8+2*10..+2*16=120Zeilen pro Ausgang, 10 ODER, 10 wählbar aktiven FFs, sowie auch wählbar Ein-/Ausgänge, sogar in Betrieb schaltbar, und Reset. Der 22V10 braucht dafür ganze 5280 Bits. Den konfigurierbaren Ausgangs Mechanismus nennt man Makrozelle.
In den späteren 1980ern dann die kleineren 16V8 und 20V8 produziert. Ziel war, so klein (und billig) wie 16R8 bzw 20R8 zu sein, alle H, L und R Typen emulieren zu können (nur noch 2 statt über 30 Typen), und doch so viel wie möglich der 22V10 Features zu bieten. Diese waren ein Riesenerfolg. Seit etwa 1990 werden daher nur noch "V" PALs eingesetzt.
Die heutigen PALCE und GAL varianten der PAL Chips sind ausserdem noch löschbar, wie Flash Chips, und in CMOS Technik schnell und stromsparend und kühl. Sie sind auch durch Fehlprogrammierung nicht mehr zerstörbar. Und sie sind heute immer noch von mehreren Firmen als geringe Variantenzahl Standardteile erhältlich, und daher billig. Also ideal für Hobby-Elektroniker neben Mikroprozessoren, RAM und Flash Chips. ROMs als Logik Ersatz haben damit ausgedient.
Aber für komplexe Schaltungen, mit viel Innenleben in einem Chip, muss man alle die internen (nicht nach aussen verdrahteten) Makrozellen auch als "Ausgänge" (und folglich auch Eingänge) des Arrays zählen. Wenn man eine quadratisch anwachsende Anzahl MZs haben will, wie bei ASICs, dann muss man die Anzahl Bits auf O(1) statt O(2) reduzieren können.
Als Ausweg hat man die CPLDs (mehrere PALs auf einem Chip) und FPGAs (viele kleine ROMs auf einem Chip) erfunden. Diese offerieren Platz für viel (CPLDs) oder gar sehr viel (FPGAs) Logik auf einem Chip, ohne dass man in teure Gate Array oder ASIC investieren müsste.
Diese haben aber ein extrem massives Problem: Um die Programmierbits zu machen wird wie bei ROM/PROM/EPROM/Flash für Prozessoren oder PLAs/PALs ein Compiler benötigt. Die CPLD und FPGA Hersteller liefern dazu zwar Compiler/Tools. Aber diese sind aber alle Closed Source, zumeist nur für Windows NT und Solaris/Sparc, selten mal noch AIX oder HP-UX oder Linux/Intel, und sind Kommerzsoftware vom schlechtesten: bloatig, langsam, buggy, mit Dongles, und mit einer "Der User muss nichts wissen" (und darf nichts wissen!) Einstellung geschrieben. Und ausser den kostenlosen Abgaben für ausgewählte kleine Chips (für Schulungszwecke) sind sie schweineteuer, so $3000-30'000.
Das schlimmste ist aber, dass mangels Dokumentation der Bit Anordnungen (und Weigerung der Hersteller diese zu veröffentlichen) bessere alternative Compiler unmöglich sind. Dies verunmöglicht es auch, der "eine brauchbare Programmiersprache kann ihren eigenen Compiler kompilieren" Regel mit einer "ein brauchbarer Eigenbaurechner kann seine eigenen programmierbaren Chips kompilieren" Regel zu folgen. Daher kann man als Hobbyist diese Chips getrost vergessen, so schön (oder gar ideal) sie auch wären.
Die PALs sind dagegen, bis zum 22V10 hinauf (und noch ein paar wenige Einzelhersteller erweiterte Versionen), vollständig dokumentiert. Also kann man für die auch einschränkungslose gut funktionierende Open Source Compiler bekommen. Ich fand bisher zwei, die aber nur die beiden 16V8 und 20V8 Teile können: galprog - GAL Programmer Software for Linux und Galassembler für 16V8 und 20V8 Gals. Diese beiden PALs reichen aber eh für fast alles.
Diese Seite ist von Neil Franklin, letzte Änderung für VCFe 2006.04.27, letzte Änderung für LUGS Wiederholung 2007.01.12