Ein Computer rechnet mit Leitungen, auf denen Strom fließt oder nicht: mit 1 und 0. Du baust daraus Schritt für Schritt ein Rechenwerk, aus Gattern einen Addierer für zwei Bits, für drei und für vier, dazu ein Flipflop, das sich ein Bit merkt, und zum Schluss ein Rechenwerk mit denselben Befehlen und Flags wie die CPU Sandbox. Jede Schaltung probierst du auf der Seite aus und schreibst sie danach in Ruby oder Python, als Funktion, die nur Gatter benutzen darf.
In der CPU Sandbox addiert ADD A, B zwei Bytes in einem einzigen Befehl. Aber wie macht ein Chip das, der nur Leitungen kennt, auf denen eine Spannung anliegt oder nicht? Diese Seite baut die Antwort von unten auf: erst Gatter, die aus ein oder zwei Bits ein neues machen, dann Schaltungen, die zwei Zahlen addieren, eine, die sich ein Bit merkt, und am Ende ein kleines Rechenwerk, das addiert, subtrahiert und Bits verknüpft.
Einrichten musst du nichts. Die Schaltungen probierst du direkt auf der Seite aus, und deinen Code schreibst du in die Karten der Aufgaben; »Ausführen« prüft ihn sofort. Was Bits und das Zweiersystem sind, steht im Handbuch unter Bits und Bytes.
Im Chip liegt auf jeder Leitung eine Spannung an oder nicht. Die eine Möglichkeit heißt 1, die andere 0. Ein Gatter ist eine kleine Schaltung, heute aus wenigen Transistoren, mit einem oder zwei Eingängen und einem Ausgang: Was am Ausgang herauskommt, hängt nur davon ab, was gerade an den Eingängen anliegt. Welche Ausgabe zu welchen Eingaben gehört, sagt die Wahrheitstabelle des Gatters, eine Zeile für jede Kombination der Eingänge.
Vier Gatter reichen für fast alles auf dieser Seite:
Klick auf die Eingänge links, oder auf eine Zeile der Wahrheitstabelle. Leitungen mit einer 1 leuchten gelb, Leitungen mit einer 0 sind grau.
Die Gatter sind gezeichnet wie in der Norm DIN EN 60617, international IEC 60617: als Rechteck, in dem steht, was das Gatter tut. & steht für UND, ≥1 für ODER (»mindestens eine 1«), =1 für XOR (»genau eine 1«) und 1 für ein Gatter, das seinen Eingang nur weitergibt. Ein kleiner Kreis am Ausgang dreht das Ergebnis um: Die 1 mit Kreis ist das NICHT. Diese Zeichen findest du in den meisten deutschen Schulbüchern. In englischen Büchern und in vielen Simulatoren siehst du stattdessen die amerikanischen Formen nach ANSI/IEEE 91, bei denen jedes Gatter eine eigene Form hat, das UND zum Beispiel wie ein D. Gemeint sind dieselben Gatter.
Gatter lassen sich hintereinanderschalten: Der Ausgang des einen wird zum Eingang des nächsten. Zwei Schaltungen mit den Eingängen a und b:
a und b, dahinter ein NICHT: nicht (a oder b)a und eins für b, dahinter ein UND: (nicht a) und (nicht b)Rechne beide Schaltungen für alle vier Kombinationen von a und b im Kopf
oder auf Papier durch. Für wie viele der vier Kombinationen liefern sie
dasselbe?
Fang mit a = 0 und b = 0 an. Die erste Schaltung: 0 oder 0 ist 0, das NICHT macht daraus 1. Die zweite: nicht 0 ist 1, zweimal, und 1 und 1 ist 1.
Hier geht es weiter, sobald du deine Antwort oben abgegeben hast.
Beide Schaltungen liefern in jeder Zeile dasselbe: 1 nur dann, wenn a und b beide 0 sind. Das ist eine der beiden Regeln von De Morgan, nach dem Mathematiker Augustus De Morgan: nicht (a oder b) ist dasselbe wie (nicht a) und (nicht b), und ebenso ist nicht (a und b) dasselbe wie (nicht a) oder (nicht b). Mit ihnen wird aus einem ODER ein UND und umgekehrt, wenn man an den richtigen Stellen umdreht. Gleich brauchst du das.
Dass man mit 1 und 0 rechnen kann wie mit Zahlen, nach festen Regeln wie den beiden von eben, ist älter als jeder Computer.
George Boole (1815–1864), englischer Mathematiker, Professor in Cork in Irland. In seinem Buch The Laws of Thought von 1854 rechnete er mit Aussagen, die wahr oder falsch sind, wie mit Zahlen. Nach ihm heißen true und false in vielen Sprachen Boolean, in Python ist es der Typ bool.
Bild: unbekannter Künstler, um 1864, gemeinfrei, zugeschnitten (Wikimedia Commons)
Gut achtzig Jahre später zeigte Claude Shannon, damals Student am MIT, dass Booles Algebra genau beschreibt, was Schalter tun.
Claude Shannon (1916–2001). Wie viele Bits eine Nachricht mindestens braucht, rechnete er 1948 aus und gründete damit die Informationstheorie; das Wort Bit machte er bekannt. Er fuhr Einrad, jonglierte und baute die Maus Theseus, die selbst lernte, durch ein Labyrinth zu finden.
Schon als Student am MIT zeigte er 1937 in seiner Masterarbeit, dass sich Schaltungen aus Relais mit der Algebra von George Boole beschreiben und vereinfachen lassen, mit 1 und 0 für einen Kontakt, der geschlossen oder offen ist. Damit begann die Theorie der digitalen Schaltungen.
Einen Code mit so wenigen Bits wie möglich suchten er und Robert Fano fast gleichzeitig, jeder für sich. Der Shannon-Fano-Code kam nah heran, aber nicht immer ganz.
In derselben Arbeit von 1948 ließ er englische Texte Wort für Wort entstehen, jedes Wort nach dem vorigen aus einem Buch gezogen: ein winziges Sprachmodell, von Hand.
Foto: Tekniska museet, CC BY 2.0, zugeschnitten (Wikimedia Commons)
A Symbolic Analysis of Relay and Switching Circuits, Claude E. Shannon, 1937 als Masterarbeit am MIT geschrieben, 1938 gedruckt. Schaltungen aus Relais, wie sie damals in Telefonvermittlungen steckten, als Gleichungen mit 1 und 0, die man vereinfachen kann wie Terme; damit fängt die Theorie der Schaltungen an, die diese Seite benutzt: als PDF bei DSpace@MIT.
Zwei weitere Gatter haben einen Kreis am Ausgang: NAND ist ein UND, dessen Ergebnis umgedreht wird, und NOR ein umgedrehtes ODER.
NAND hat eine erstaunliche Eigenschaft: Aus NAND-Gattern allein lässt sich jedes andere Gatter bauen, und damit jede Schaltung überhaupt. Chips werden darum gern aus NAND gebaut: In der heute üblichen CMOS-Technik braucht ein NAND vier Transistoren, ein UND sechs, denn es ist ein NAND mit einem NICHT dahinter.
In den beiden Karten unten steht nand(a, b) schon fertig. Deine Funktionen dürfen ihre Eingänge nur an nand weitergeben, als ob sie Leitungen wären: nicht selbst vergleichen, nicht rechnen und keine Tabelle nachschlagen. Darauf achtet die Prüfung: Sie ruft deine Funktionen zuerst mit 0 und 1 auf und dann noch einmal mit Signalen, die nur nand lesen kann. Ein if a == 1 oder ein a & b in deinem Code fällt dabei auf, auch wenn die Ergebnisse stimmen. nand selbst ist mit == geschrieben, denn in Ruby und Python gibt es keine Transistoren.
Schreib not_(a) und and_(a, b) nur mit nand: Deine Funktionen geben ihre Eingänge an nand weiter und rechnen nicht selbst. Der Unterstrich ist nötig, weil not und and in Ruby schon Wörter der Sprache sind. Die Prüfung ruft not_(0), not_(1) und and_ für 00, 01, 10 und 11 auf, erst mit 0 und 1 und dann mit Signalen, die nur nand lesen kann.
Schau in die Wahrheitstabelle von NAND: Was kommt bei nand(0, 0) heraus und was bei nand(1, 1), wenn also beide Eingänge gleich sind?
not_(a) ist ein nand, an dessen beiden Eingängen a liegt. Und ein UND ist ein NAND, dessen Ergebnis umgedreht wird: Dafür hast du jetzt not_.
Schreib not_(a) und and_(a, b) nur mit nand: Deine Funktionen geben ihre Eingänge an nand weiter und rechnen nicht selbst. Der Unterstrich ist nötig, weil not und and in Python schon Wörter der Sprache sind. Die Prüfung ruft not_(0), not_(1) und and_ für 00, 01, 10 und 11 auf, erst mit 0 und 1 und dann mit Signalen, die nur nand lesen kann.
Schau in die Wahrheitstabelle von NAND: Was kommt bei nand(0, 0) heraus und was bei nand(1, 1), wenn also beide Eingänge gleich sind?
not_(a) ist ein nand, an dessen beiden Eingängen a liegt. Und ein UND ist ein NAND, dessen Ergebnis umgedreht wird: Dafür hast du jetzt not_.
Schreib or_(a, b) und xor_(a, b), wieder nur mit nand. Deine Funktionen not_ und and_ aus der Karte davor darfst du hierher kopieren und benutzen. Die Prüfung ruft or_ und xor_ für 00, 01, 10 und 11 auf, erst mit 0 und 1 und dann mit Signalen, die nur nand lesen kann.
Für das ODER: Erinnere dich an die Regel von De Morgan oben. Wann ist a oder b gleich 0, und wie sieht das als UND aus?
a oder b ist dasselbe wie nicht ((nicht a) und (nicht b)). Welches Gatter rechnet »nicht (… und …)« in einem Schritt?
Für das XOR: Es ist 1, wenn a oder b 1 ist, aber nicht beide. Mit deinen Funktionen: das ODER von a und b, UND das NAND von a und b.
Schreib or_(a, b) und xor_(a, b), wieder nur mit nand. Deine Funktionen not_ und and_ aus der Karte davor darfst du hierher kopieren und benutzen. Die Prüfung ruft or_ und xor_ für 00, 01, 10 und 11 auf, erst mit 0 und 1 und dann mit Signalen, die nur nand lesen kann.
Für das ODER: Erinnere dich an die Regel von De Morgan oben. Wann ist a oder b gleich 0, und wie sieht das als UND aus?
a oder b ist dasselbe wie nicht ((nicht a) und (nicht b)). Welches Gatter rechnet »nicht (… und …)« in einem Schritt?
Für das XOR: Es ist 1, wenn a oder b 1 ist, aber nicht beide. Mit deinen Funktionen: das ODER von a und b, UND das NAND von a und b.
Hier geht es weiter, sobald du deine Antwort oben abgegeben hast.
So sehen die vier Gatter aus NAND aus. Hinter dem ODER steckt eine Regel von De Morgan: nand(nicht a, nicht b) ist nicht ((nicht a) und (nicht b)), und das ist dasselbe wie a oder b. Für das XOR reichen vier NAND.
Im Zweiersystem addierst du schriftlich wie im Zehnersystem, nur dass jede Ziffer 0 oder 1 ist. Zwei Bits zusammen ergeben höchstens 1 + 1, und das ist im Zweiersystem 10: Die Ziffer, die stehen bleibt, ist 0, und eine 1 geht als Übertrag in die nächste Spalte.
| a | b | a + b | Übertrag c |
Summe s |
|---|---|---|---|---|
| 0 | 0 | 00 | 0 | 0 |
| 0 | 1 | 01 | 0 | 1 |
| 1 | 0 | 01 | 0 | 1 |
| 1 | 1 | 10 | 1 | 0 |
In der Spalte a + b steht das Ergebnis mit zwei Ziffern: Die linke Ziffer ist der Übertrag c, die rechte die Summe s.
Eine Schaltung, die das rechnet, hat zwei Eingänge und zwei Ausgänge: die Summe s und den Übertrag c, englisch Carry. Jeder der beiden Ausgänge ist ein einziges Gatter.
Welche Gatter liefern die Summe s und den Übertrag c? Vergleich die
beiden Spalten der Tabelle mit den Wahrheitstabellen der Gatter oben.
Hier geht es weiter, sobald du deine Antwort oben abgegeben hast.
Die Schaltung heißt Halbaddierer: Sie addiert zwei Bits, aber ein Übertrag aus der Spalte rechts davon hat in ihr keinen Platz. Für eine ganze Spalte fehlt also noch ein Eingang.
Schreib half_adder(a, b). Es gibt eine Liste [s, c] zurück, die Summe und den Übertrag: half_adder(1, 1) ergibt [0, 1]. Die Gatter not_, and_, or_ und xor_ stehen schon da, und deine Funktion gibt ihre Eingänge nur an sie weiter. Die Prüfung ruft half_adder für 00, 01, 10 und 11 auf, erst mit 0 und 1 und dann mit Signalen, die nur die Gatter lesen können.
Oben hast du herausgefunden, welches Gatter die Summe liefert und welches den Übertrag. Beide bekommen a und b.
Zurück kommt eine Liste mit zwei Einträgen: zuerst das Ergebnis des einen Gatters, dann das des anderen.
Schreib half_adder(a, b). Es gibt eine Liste [s, c] zurück, die Summe und den Übertrag: half_adder(1, 1) ergibt [0, 1]. Die Gatter not_, and_, or_ und xor_ stehen schon da, und deine Funktion gibt ihre Eingänge nur an sie weiter. Die Prüfung ruft half_adder für 00, 01, 10 und 11 auf, erst mit 0 und 1 und dann mit Signalen, die nur die Gatter lesen können.
Oben hast du herausgefunden, welches Gatter die Summe liefert und welches den Übertrag. Beide bekommen a und b.
Zurück kommt eine Liste mit zwei Einträgen: zuerst das Ergebnis des einen Gatters, dann das des anderen.
Beim schriftlichen Addieren kommt in jeder Spalte außer der rechten der Übertrag aus der Spalte davor dazu. So addierst du 0110 und 0111, von rechts nach links, die Überträge klein darüber:
Übertrag 1 1 0
0 1 1 0 6
+ 0 1 1 1 + 7
--------- ---
1 1 0 1 13
In der dritten Spalte von rechts stehen 1, 1 und der Übertrag 1: zusammen 3, im Zweiersystem 11, also Summe 1 und wieder ein Übertrag. Eine Schaltung für eine solche Spalte hat drei Eingänge, a, b und den Übertrag von rechts c_in, und zwei Ausgänge, die Summe s und den Übertrag nach links c_out. Sie heißt Volladdierer. Ihre Wahrheitstabelle hat acht Zeilen, eine für jede Kombination der drei Eingänge.
In wie vielen der acht Zeilen der Wahrheitstabelle eines Volladdierers ist
c_out gleich 1?
Schreib die acht Zeilen auf, von 000 bis 111, und rechne in jeder a + b + c_in im Zweiersystem aus. Der Übertrag ist die linke Ziffer.
Hier geht es weiter, sobald du deine Antwort oben abgegeben hast.
Ein Übertrag entsteht, sobald mindestens zwei der drei Bits 1 sind: bei 011, 101, 110 und 111. Die Summe ist 1, wenn eine ungerade Zahl der Bits 1 ist: bei 001, 010, 100 und 111.
Einen Volladdierer kannst du aus zwei Halbaddierern bauen. Der erste addiert a und b, der zweite addiert zu dessen Summe den Übertrag c_in. Bleibt die Frage, was aus den beiden Überträgen wird.
Schreib full_adder(a, b, c_in), das [s, c_out] zurückgibt: full_adder(1, 1, 1) ergibt [1, 1], denn 1 + 1 + 1 ist im Zweiersystem 11. Bau ihn aus zwei Halbaddierern; deinen half_adder darfst du dafür hierher kopieren. Die Prüfung ruft full_adder für alle acht Kombinationen auf, erst mit 0 und 1 und dann mit Signalen, die nur die Gatter lesen können.
Rechne full_adder(1, 0, 1) von Hand mit zwei Halbaddierern: Der erste addiert a und b. Der zweite addiert die Summe des ersten und c_in.
Die Summe des zweiten Halbaddierers ist schon s. Für c_out hast du zwei Überträge, aus jedem Halbaddierer einen: Wann muss c_out 1 sein?
Ein Gerüst:
def full_adder(a, b, c_in) s1, c1 = half_adder(a, b) s, c2 = half_adder(…) [s, …] # hier: c1 und c2 zusammen end
Schreib full_adder(a, b, c_in), das [s, c_out] zurückgibt: full_adder(1, 1, 1) ergibt [1, 1], denn 1 + 1 + 1 ist im Zweiersystem 11. Bau ihn aus zwei Halbaddierern; deinen half_adder darfst du dafür hierher kopieren. Die Prüfung ruft full_adder für alle acht Kombinationen auf, erst mit 0 und 1 und dann mit Signalen, die nur die Gatter lesen können.
Rechne full_adder(1, 0, 1) von Hand mit zwei Halbaddierern: Der erste addiert a und b. Der zweite addiert die Summe des ersten und c_in.
Die Summe des zweiten Halbaddierers ist schon s. Für c_out hast du zwei Überträge, aus jedem Halbaddierer einen: Wann muss c_out 1 sein?
Ein Gerüst:
def full_adder(a, b, c_in): s1, c1 = half_adder(a, b) s, c2 = half_adder(…) return [s, …] # hier: c1 und c2 zusammen
Hier geht es weiter, sobald du deine Antwort oben abgegeben hast.
Die beiden gestrichelten Rahmen sind die Halbaddierer. Ihre Überträge laufen durch ein ODER: Einer von beiden reicht für den Übertrag nach links, und beide zugleich kommen nie vor.
Für Zahlen mit vier Bits stellst du vier Volladdierer nebeneinander, so wie die vier Spalten der schriftlichen Addition: Jeder bekommt ein Bit von a und das Bit von b darunter, und sein Übertrag c_out wird der Übertrag c_in seines linken Nachbarn. Der rechte bekommt als Übertrag eine feste 0. Weil der Übertrag von rechts nach links durch alle Volladdierer läuft, heißt die Schaltung Ripple-Carry-Addierer, nach englisch ripple, eine kleine Welle.
Aus vier Bits kommen dabei vier Bits heraus und ein Übertrag aus der linken Spalte, der in kein Bit mehr passt.
Was zeigt der Addierer für 4 Bit bei 1111 + 0001?
Rechne schriftlich von rechts nach links und schreib die Überträge darüber.
Hier geht es weiter, sobald du deine Antwort oben abgegeben hast.
1111 ist 15, und 15 + 1 = 16 braucht ein fünftes Bit: In den vier Bits steht 0000, und der Übertrag aus der linken Spalte ist 1. Genauso in der CPU Sandbox, nur mit 8 Bit: 255 + 1 ergibt dort 0, und das Carry-Flag CF ist 1 (Handbuch, MiniCPU: Flags).
0111 und 0000 ein und schalte dann b0 auf 1. Schau auf die Leitungen zwischen den Volladdierern: Der Übertrag läuft nacheinander durch alle vier, denn jeder Volladdierer kann erst rechnen, wenn der rechts von ihm fertig ist. Bei 64 Bit wären es 64 nacheinander; schnelle Rechenwerke rechnen die Überträge darum mit zusätzlichen Gattern im Voraus aus (Carry-Lookahead).
In Ruby und Python sind die vier Bits einer Zahl eine Liste, das höchste Bit vorn, so wie du die Zahl schreibst: 0110 ist [0, 1, 1, 0]. Das Bit ganz rechts hat also den Index 3.
Schreib add4(a, b) für zwei Zahlen mit 4 Bit, als Listen mit dem höchsten Bit vorn. Zurück kommt [s, c], die vier Bits der Summe und der Übertrag aus der linken Spalte: add4([0, 1, 1, 0], [0, 1, 1, 1]) ergibt [[1, 1, 0, 1], 0], und add4([1, 1, 1, 1], [0, 0, 0, 1]) ergibt [[0, 0, 0, 0], 1]. Deine Funktionen half_adder und full_adder darfst du hierher kopieren. Die Prüfung ruft add4 mit zehn Paaren auf, erst mit 0 und 1 und dann mit Signalen, die nur die Gatter lesen können.
Fang rechts an, beim Index 3, mit dem Übertrag 0, wie beim schriftlichen Addieren. Der Übertrag, der aus einer Spalte kommt, geht in die Spalte links davon.
Eine Schleife über die Indizes 3, 2, 1 und 0: 3.downto(0) do |i|. In jeder Runde ein full_adder mit a[i], b[i] und dem Übertrag.
Ein Gerüst:
def add4(a, b) s = [0, 0, 0, 0] c = 0 3.downto(0) do |i| # hier: full_adder mit a[i], b[i] und c; das Bit nach s[i], der Übertrag nach c end [s, c] end
Schreib add4(a, b) für zwei Zahlen mit 4 Bit, als Listen mit dem höchsten Bit vorn. Zurück kommt [s, c], die vier Bits der Summe und der Übertrag aus der linken Spalte: add4([0, 1, 1, 0], [0, 1, 1, 1]) ergibt [[1, 1, 0, 1], 0], und add4([1, 1, 1, 1], [0, 0, 0, 1]) ergibt [[0, 0, 0, 0], 1]. Deine Funktionen half_adder und full_adder darfst du hierher kopieren. Die Prüfung ruft add4 mit zehn Paaren auf, erst mit 0 und 1 und dann mit Signalen, die nur die Gatter lesen können.
Fang rechts an, beim Index 3, mit dem Übertrag 0, wie beim schriftlichen Addieren. Der Übertrag, der aus einer Spalte kommt, geht in die Spalte links davon.
Eine Schleife über die Indizes 3, 2, 1 und 0: for i in range(3, -1, -1):. In jeder Runde ein full_adder mit a[i], b[i] und dem Übertrag.
Ein Gerüst:
def add4(a, b): s = [0, 0, 0, 0] c = 0 for i in range(3, -1, -1): # hier: full_adder mit a[i], b[i] und c; das Bit nach s[i], der Übertrag nach c pass return [s, c]
Bis hierher hing der Ausgang jeder Schaltung nur davon ab, was gerade an ihren Eingängen anliegt. Eine solche Schaltung heißt Schaltnetz. Ein Register wie A in der CPU Sandbox muss sich aber eine Zahl merken, auch wenn sich die Eingänge danach ändern. Dafür führt man den Ausgang eines Gatters zurück an einen Eingang davor: Die Schaltung bekommt einen Zustand und heißt dann Schaltwerk.
Das einfachste Schaltwerk ist das RS-Flipflop aus zwei NOR-Gattern. Sein Ausgang Q ist das gespeicherte Bit, der zweite Ausgang nicht Q (»Q quer«) das umgedrehte Bit. Jedes NOR bekommt einen Eingang von außen und den Ausgang des anderen NOR:
S heißt Set, »setzen«, und R Reset, »zurücksetzen«. Am Anfang sind S und R beide 0, Q ist 0 und nicht Q ist 1. Denk an die Wahrheitstabelle von NOR: Es liefert nur dann 1, wenn beide Eingänge 0 sind.
S wird 1 und danach wieder 0. R bleibt die ganze Zeit 0. Was ist Q danach?
Solange S 1 ist: Q quer = NOR(1, Q) ist 0, also Q = NOR(0, 0) = 1. Was ändert sich, wenn S zurück auf 0 geht?
Jetzt werden S und R beide 1, und danach im selben Augenblick beide wieder 0. Was ist Q danach?
Hier geht es weiter, sobald du deine Antwort oben abgegeben hast.
Ist S einmal 1 gewesen, hält sich das Flipflop selbst: Q ist 1, also ist \(\overline{\mathrm{Q}} = \mathrm{NOR}(\mathrm{S}, 1) = 0\), und damit bleibt \(\mathrm{Q} = \mathrm{NOR}(0, 0) = 1\), auch wenn S wieder 0 ist. Erst R = 1 setzt Q zurück auf 0, und auch das bleibt.
Sind S und R beide 1, sind Q und nicht Q beide 0. Gehen dann beide im selben Augenblick zurück auf 0, sehen beide NOR zwei Nullen und schalten auf 1, dann sehen beide eine 1 und schalten auf 0, immer weiter: In der Simulation oben schalten alle Gatter gleich schnell, und die Schaltung kommt nie zur Ruhe. In echter Hardware ist eines der beiden Gatter ein wenig schneller, aber welches, weiß man vorher nicht. Darum heißt S = R = 1 der verbotene Zustand des RS-Flipflops.
William Eccles (1875–1966) und F. W. Jordan, britische Physiker. 1918 meldeten sie eine Schaltung aus zwei Elektronenröhren zum Patent an, die sich gegenseitig in ihrem Zustand halten: das erste elektronische Latch, ein Speicher für ein Bit. Sie hieß zuerst Eccles–Jordan trigger circuit, heute heißt sie Flipflop. 1943 steckte sie in Colossus, dem britischen Rechner, der im Krieg verschlüsselte Nachrichten knackte.
Im RS-Flipflop aus zwei NOR-Gattern halten sich die beiden Gatter genauso gegenseitig fest wie die beiden Röhren.
Damit der verbotene Zustand nicht vorkommt, bekommt das Flipflop zwei UND-Gatter davor. Der Eingang D (Daten) sagt, welches Bit gespeichert werden soll, und der Takt sagt, wann: Ist der Takt 1, wird D gespeichert, und Q folgt D. Ist der Takt 0, sind S und R beide 0, und das Flipflop hält sein Bit, egal was an D anliegt. Weil das NICHT vor dem einen UND steht, sind S und R nie zugleich 1.
Genauer heißt diese Schaltung taktzustandsgesteuertes D-Flipflop oder D-Latch: Solange der Takt 1 ist, folgt Q dem Eingang D. Die Register eines Prozessors übernehmen D dagegen meist nur in dem Augenblick, in dem der Takt von 0 auf 1 springt; das erreicht man mit zwei solchen Latches hintereinander. Ein Register mit 8 Bit besteht aus acht Flipflops mit demselben Takt, eins für jedes Bit.
Schreib d_latch(d, clock, q): Es sagt, welches Bit das D-Flipflop nach einem Augenblick speichert. q ist das Bit, das es bisher gespeichert hat, clock der Takt. Ist clock 1, kommt d zurück; ist clock 0, kommt q zurück. Nur mit den Gattern, wie bisher: Ein if clock == 1 merkt die Prüfung. Sie ruft d_latch für alle acht Kombinationen auf.
Zwei Fälle liefern eine 1: Der Takt ist 1 und d ist 1, oder der Takt ist 0 und q ist 1. Den ersten Fall rechnet and_(clock, d).
Den zweiten Fall rechnet ein UND aus dem umgedrehten Takt und q. Beide Fälle zusammen verbindet ein ODER.
Schreib d_latch(d, clock, q): Es sagt, welches Bit das D-Flipflop nach einem Augenblick speichert. q ist das Bit, das es bisher gespeichert hat, clock der Takt. Ist clock 1, kommt d zurück; ist clock 0, kommt q zurück. Nur mit den Gattern, wie bisher: Ein if clock == 1 merkt die Prüfung. Sie ruft d_latch für alle acht Kombinationen auf.
Zwei Fälle liefern eine 1: Der Takt ist 1 und d ist 1, oder der Takt ist 0 und q ist 1. Den ersten Fall rechnet and_(clock, d).
Den zweiten Fall rechnet ein UND aus dem umgedrehten Takt und q. Beide Fälle zusammen verbindet ein ODER.
Der Teil des Prozessors, der rechnet, heißt Rechenwerk oder ALU, nach englisch Arithmetic Logic Unit. Es bekommt zwei Zahlen und einen Befehl und liefert ein Ergebnis und die Flags: kleine Merker, die sagen, wie die Rechnung ausgegangen ist. Die MiniCPU der CPU Sandbox kennt zum Rechnen unter anderem ADD, SUB, AND, OR und XOR und hat zwei Flags (Handbuch, MiniCPU: Flags):
ADD der Übertrag aus dem obersten Bit und nach SUB 1, wenn geborgt werden musste, weil die erste Zahl kleiner war. AND, OR und XOR setzen CF immer auf 0.Das Rechenwerk dieser Seite hat dieselben fünf Befehle und dieselben beiden Flags, nur mit 4 statt mit 8 Bit. Für AND, OR und XOR reichen vier Gatter nebeneinander, eins für jedes Bit. Für ADD gibt es den Addierer. Bleibt SUB, und auch dafür nimmt das Rechenwerk den Addierer.
Ein Rechenwerk hat nur einen Addierer. Wie rechnet es 0101 − 0011, also
5 − 3, mit 4 Bit?
Im Zweierkomplement mit 4 Bit ist −3 dasselbe wie 16 − 3 = 13, also 1101. Wie kommst du von 0011 auf 1101?
Hier geht es weiter, sobald du deine Antwort oben abgegeben hast.
Von einer Zahl mit 4 Bit b abziehen heißt dasselbe wie 16 − b addieren und den Übertrag aus der linken Spalte wegzulassen. Und 16 − b ist b mit allen Bits umgedreht, plus 1: Aus b = 0011 wird 1100 + 1 = 1101, das ist 13 = 16 − 3. Das ist das Zweierkomplement aus der Kata Negative Zahlen in einem Byte. Das Umdrehen erledigen vier XOR-Gatter, an deren zweitem Eingang beim Subtrahieren eine 1 liegt, und die 1 dazu kommt als Übertrag c_in in den rechten Volladdierer.
Und das Carry-Flag? Bei 5 − 3 rechnet der Addierer 0101 + 1100 + 1 = 1 0010: Es gibt einen Übertrag, und es musste nicht geborgt werden. Bei 3 − 5 rechnet er 0011 + 1010 + 1 = 0 1110: kein Übertrag, aber 3 ist kleiner als 5. Nach SUB ist CF also genau dann 1, wenn aus dem Addierer kein Übertrag kommt.
So sieht das Rechenwerk aus, mit Leitungen, die vier Bits auf einmal tragen (die dicken Linien, über ihnen ihr Wert): Links laufen a und b nach unten zum Addierer Σ und zu den drei Gattern für AND, OR und XOR. Rechts sagt der Befehl auf zwei Leitungen, was zu tun ist (gestrichelt): sub ist bei SUB 1 und dreht über das =1 die Bits von b um, kommt als Übertrag in den Addierer und dreht den Übertrag aus dem Addierer für CF um; sel wählt im Block MUX (Multiplexer), welches der vier Ergebnisse herauskommt. Rechnen tun alle vier Teile immer, der Multiplexer gibt nur eins davon weiter, und bei AND, OR und XOR als CF eine 0. ZF kommt aus einem NOR über die vier Bits des Ergebnisses.
Schreib alu(op, a, b) für zwei Zahlen mit 4 Bit wie bei add4. op ist einer der Texte 'add', 'sub', 'and', 'or' und 'xor'. Zurück kommt [r, zf, cf], das Ergebnis mit 4 Bit und die beiden Flags, so wie die CPU Sandbox sie setzt: alu('sub', [0, 0, 1, 1], [0, 1, 0, 1]) ergibt [[1, 1, 1, 0], 0, 1], denn für 3 − 5 muss geborgt werden. op ist kein Signal: Mit if op == 'sub' darfst du wählen, was gerechnet wird, wie der Multiplexer. Die Bits von a und b gehen nur durch Gatter. Deine Funktionen von vorher darfst du hierher kopieren. Die Prüfung ruft alle fünf Befehle mit 15 Paaren auf.
Fang mit 'and', 'or' und 'xor' an: ein Gatter für jeden Index, CF ist dabei 0. ZF ist 1, wenn kein Bit von r 1 ist: ein NICHT hinter drei ODER, die alle vier Bits verbinden.
Für 'add' und 'sub' brauchst du deinen Addierer mit einem Übertrag am Anfang: Kopier add4 und gib ihm einen dritten Parameter c statt der festen 0. Bei 'sub' drehst du jedes Bit von b mit xor_(bit, 1) um und fängst mit dem Übertrag 1 an.
CF ist bei 'add' der Übertrag aus dem Addierer und bei 'sub' sein Gegenteil. xor_(c, sub) mit sub = 1 bei 'sub' und 0 bei 'add' erledigt beides.
Schreib alu(op, a, b) für zwei Zahlen mit 4 Bit wie bei add4. op ist einer der Texte 'add', 'sub', 'and', 'or' und 'xor'. Zurück kommt [r, zf, cf], das Ergebnis mit 4 Bit und die beiden Flags, so wie die CPU Sandbox sie setzt: alu('sub', [0, 0, 1, 1], [0, 1, 0, 1]) ergibt [[1, 1, 1, 0], 0, 1], denn für 3 − 5 muss geborgt werden. op ist kein Signal: Mit if op == 'sub' darfst du wählen, was gerechnet wird, wie der Multiplexer. Die Bits von a und b gehen nur durch Gatter. Deine Funktionen von vorher darfst du hierher kopieren. Die Prüfung ruft alle fünf Befehle mit 15 Paaren auf.
Fang mit 'and', 'or' und 'xor' an: ein Gatter für jeden Index, CF ist dabei 0. ZF ist 1, wenn kein Bit von r 1 ist: ein NICHT hinter drei ODER, die alle vier Bits verbinden.
Für 'add' und 'sub' brauchst du deinen Addierer mit einem Übertrag am Anfang: Kopier add4 und gib ihm einen dritten Parameter c statt der festen 0. Bei 'sub' drehst du jedes Bit von b mit xor_(bit, 1) um und fängst mit dem Übertrag 1 an.
CF ist bei 'add' der Übertrag aus dem Addierer und bei 'sub' sein Gegenteil. xor_(c, sub) mit sub = 1 bei 'sub' und 0 bei 'add' erledigt beides.
Die ALU der MiniCPU kannst du dir genauso vorstellen, nur 8 Bit breit und mit weiteren Befehlen wie ADC, CMP und den Schiebebefehlen SHL und SHR. In der Sandbox selbst rechnet sie mit JavaScript, nicht mit Gattern, aber ihre Ergebnisse und Flags sind dieselben. Dass sie 8 Bit breit ist, merkst du in der Stufe MiniCPU 8X: Rechnungen mit den 16-Bit-Registern X und Y brauchen dort einen Takt mehr. CMP A, B rechnet A − B wie SUB und behält nur die Flags: Danach sagt CF = 1, dass A kleiner als B ist, genau wie bei deinem Rechenwerk.
SUB mit a = 0011 und b = 0101 ein, also 3 − 5, und schau auf r, ZF und CF. In der CPU Sandbox steht nach LDI A, 3, LDI B, 5 und SUB A, B die Zahl 254 in A, und CF ist 1 (Handbuch, MiniCPU: Flags). Wie hängen 254 und dein Ergebnis mit 4 Bit zusammen?

In der CPU Sandbox programmierst du einen ganzen Prozessor mit Registern, Speicher und Bildschirm, erst in Assembler und später in C. Das Rechenwerk dieser Seite steckt dort in jedem ADD und jedem CMP.
Code: The Hidden Language of Computer Hardware and Software, Charles Petzold, 1999. Von Morsezeichen und Taschenlampen über Relais zu Gattern, Addierern und Flipflops und weiter bis zu einem ganzen Computer, Schritt für Schritt wie auf dieser Seite, nur viel ausführlicher: die Seite des Autors zum Buch.
The Elements of Computing Systems, Noam Nisan und Shimon Schocken, MIT Press. Der Kurs »From Nand to Tetris« fängt genau hier an, bei einem einzigen NAND, und baut daraus in zwölf Projekten einen Computer, seinen Assembler, einen Compiler und ein kleines Betriebssystem. Der Kurs mit allen Projekten und Werkzeugen ist frei: nand2tetris.org.