In der Serie “Wie Rechner rechnen” habe ich versucht, die physikalischen Grundlagen eines Computers zu erklären und darzulegen, was in so einem Ding überhaupt im Inneren vorgeht. Speziell im letzten Teil haben wir dabei betrachtet, wie ein Computer nun genau Befehle ausführt. Heute nun soll es darum gehen, wie man diese Befehle eigentlich auf vernünftige Weise in den Rechner hineinbekommt (die Wünsche einiger Leser nach mehr Details zu modernen Techniken bei der Befehlsausführung habe ich vermerkt und reiche sie noch nach – ich möchte nur vorher einige Dinge einführen, die dafür nötig sind).

Fassen wir noch einmal kurz zusammen: irgendwo im Computer, meist im Cache oder im Arbeitsspeicher, stehen Instruktionen – Bitketten, die eine auszuführende Aktion codieren. Eine Instruktion setzt sich aus dem Opcode, der eigentlichen Aktion, und den Operanden, also den zu benutzenden Daten, zusammen. Das fiktive Beispiel aus dem letzten Beitrag war die Instruktion “0110110101”, welche in die Bestandteile 0110, 11 und 0101 unterteilt werden kann, wobei “0110” der Opcode ist und die Anweisung “addiere einen festen Wert zu dem in einem Register gespeicherten Wert” codiert, “11” das zu benutzende Register (im Beispiel den Akkumulator) beschreibt und “0101” schließlich den festen Wert, hier eine 5, codiert. Zusammengenommen ergibt die Bitkette also die Instruktion “addiere den Wert 5 auf den im Akkumulator gespeicherten Wert” (ich weiße – nur zur Sicherheit – noch einmal darauf hin, dass das Beispiel rein fiktiv ist und keine Entsprechung in der Realität hat). Man sagt, die Instruktionen sind im Maschinencode geschrieben; mehrere Instruktionen zusammen bilden ein Programm.

i-351bfdbddaf73e4f21e34d8877562942-Lochstreifen.png

Nun könnte man einen Computer natürlich programmieren, indem man viele dieser Instruktionen von Hand schreibt, in der richtigen Reihenfolge aneinanderreiht und dann in den Speicher eines Computers lädt (wie heutzutage das Laden funktionieren würde, ist noch einmal eine Sache für sich und eine Variante des bekannten Henne-Ei-Problems). In den Anfangsjahren der Informatik wurde das tatsächlich auch noch so gemacht; die Bitketten konnten allerdings nicht immer einfach komfortabel am Rechner eingegeben werden, sondern mussten teilweise noch manuell auf ein Trägermedium transferiert werden – die allseits bekannten Lochstreifen und Lochkarten. Lochstreifen (siehe Abbildung; Quelle: Wikipedia) waren lange Streifen aus Papier oder Kunststoff, auf welche Informationen in Form von Bitketten gespeichert wurden, wobei ein Loch etwa der logischen “1” entsprach und kein Loch entsprechend der logischen “0”. War der Lochstreifen breit genug für 8 nebeneinanderstehende Löcher, so konnte eine Bitkette der Länge 8 in jeder Zeile gespeichert werden. Eine Lochkarte war im Grunde das gleiche, nur in der Länge begrenzt (übrigens gab es Lochkarten und -streifen nicht erst mit dem Aufkommen von Computern – bereits vorher wurden sie eingesetzt, zum Beispiel in den bekannten Jacquardwebstühlen). “Programmiert” wurde also, indem man kleine Löcher in Papier gebohrt hat.

Wer jetzt der Meinung ist, dass ein derartiges Vorgehen höchst umständlich anmutet, hat natürlich vollkommen recht; in den Anfangsjahren gab es dazu aber keine Alternative. Dennoch war man natürlich bemüht, ein besseres Verfahren zu finden, da das Stanzen von Löchern doch ein wenig umständlich und der Code auch ziemlich schlecht zu lesen war. Hinzu kam, dass jeder Prozessortyp seine ganz eigene Codierung der Instruktionen hatte – Programme waren also nicht wirklich gut auf andere Architekturen portierbar.

Die Lösung des Problems wurde in den 1950er Jahren entwickelt: die Assemblersprachen1. Eine Assemblersprache ist in ihrer einfachsten Form nichts weiter als eine Möglichkeit, Maschinencode (also Instruktionen) in einer besser lesbaren Form zu notieren. Obgleich es unterschiedliche Arten gibt, Assemblercode niederzuschreiben, ähneln sich Assemblersprachen in vielen Punkten. So werden die Opcodes, die ja nichts anderes als binäre Zahlen sind, durch besser verständliche (und vor allem besser merkbare) Wörter ersetzt. Aus mir nicht näher bekannten Gründen (ich tippe auf Speicherplatzersparnis) wurden dabei nicht irgendwelche Wörter gewählt, sondern möglichst kurze, eindeutige und natürlich englische. Die Anweisung “addiere” wird folgerichtig üblicherweise mit add abgekürzt, Subtrahieren mit sub und die Anweisung zum Verschieben von Speicherinhalten mit mov. In Anlehnung an die Funktion der Wörter, nämlich das Merken der komplizierten Opcodes zu vereinfachen, werden sie übrigens auch Mnemonics genannt. Hinter dem Wort für die Aktion folgen nun die dazugehörigen Operanden; auch diese können über Mnemonics dargestellt werden, etwa im Fall von Registern; Speicheradressen und Zahlen lassen sich dagegen ganz normal im bekannten Dezimalsystem angeben.

1 / 2 / 3 / 4 / Auf einer Seite lesen

Kommentare (20)

  1. #2 Marcus Frenkel
    August 31, 2011

    Interessant. Ich habe noch nie von der Maschine gehört – wieder etwas gelernt. Die “Initial Orders”-Sprache erscheint mir zwar wirklich wie ein reiner Aufsatz auf Maschinencode, aber gut, Assembler ist es trotzdem. Danke.

  2. #3 Nele
    August 31, 2011

    Sehr cooler Artikel. Das erinnert mich an die 80er, als ich noch einen 8-Bit-Computer in Z80-Code programmiert habe. Damals habe ich per Hand und Rodnay Zaks Z80-Bibel assembliert und das ganze dann mit einem Monitor byteweise in den Speicher geschrieben. Mit Monitor meine ich jetzt nicht das Datensichtgerät sondern die Software zur Kontrolle des Computers auf Prozessorebene.

    Das war die beste Möglichkeit überhaupt, ein einfaches Computersystem von Grund auf kennen zu lernen. Mit den heutigen Computerarchitekturen geht sowas wahrscheinlich überhaupt nicht mehr. :)

  3. #4 Marcus Frenkel
    August 31, 2011

    Ach, das geht schon auch mit heutigen Architekturen; sie haben ja immer noch die Befehle von damals (plus eine Menge mehr). Zum Kennenlernen kann da auf Assemblerebene mit einfachsten Sachen gearbeitet werden; wer richtig in die Tiefe will, kann sogar ein einfaches Betriebssystem programmieren, das ist auch nicht so sehr kompliziert (durch die vielen SSE-Befehle sogar eher einfacher).

    Und dank Virtualisierung muss man dabei nicht einmal seinen eigenen Rechner lahmlegen. 😉

  4. #5 cimddwc
    August 31, 2011

    Mit entsprechenden Hochsprachen-Makros angereichert, kann man auch heutzutage noch ganz gut größere Programme in Assembler schreiben, finde ich. :) Nur Berechnungen werden gern etwas umständlich.

    Richtig lustig wird’s dann mit Befehlen wie cvttpd2pi, maskmovdqu, punpcklbw, rsqrtss und was die Hersteller sich sonst so alles im Rahmen von SSEx & Co. ausgedacht haben… zum Glück brauch ich die eher selten bis gar nicht. :)

  5. #6 Informatiker (grad) ohne Durchblick
    August 31, 2011

    Erst mal vielen Dank für die ganzen hoch interessanten Blogbeiträge. Hab ich zwar fast alles schon mal gehört, aber eine Auffrischung tut immer gut. Vor allem wenn sie so anschaulich und verständlich geschrieben ist.

    Allerdings tu ich mich grad mit der Tabelle etwas schwer, genauer mit den Einträgen für al. Denn in Zeile 6 (der Tabelle) wechselt der Wert von 1 auf 0 und in Zeile 12 von 2 auf -1. Wieso?
    Ist das ein Tippfehler, oder stehe ich grad völlig auf dem Schlauch.

  6. #7 Marcus Frenkel
    August 31, 2011

    @Informatiker (grad) ohne Durchblick
    Das waren Tippfehler. Oder besser: Copy&Paste-Fehler; jeder Informatiker weiß, dass man das nicht macht. Jeder Informatiker macht es trotzdem. Meine Güte.

    Danke für den Hinweis, die sind mir beim Probelesen durch die Lappen gegangen. 😉

  7. #8 rolak
    August 31, 2011

    Na endlich. Meine Lieblingssprachen :-)

    aber Informatiker sind zuweilen recht faul, was diese Unterscheidung angeht

    Auch wenn (z.B. als von mir ausgehende Verallgemeinerung) die Einsortierung richtig sein könnte – es wären zwei (10) Dinge anzumerken:

    • Es gibt so etwas wie produktive Faulheit, wie z.B. die Verbeserung von Vorhandenem, weil nicht so lange gewartet werden möchte etc pp
    • Informatiker wissen berufsbedingt um die Verschiedenheit von Wort und Bedeutung, haben kein Problem damit, verschiedene ähnliche Bedeutungen mit einem einzigen Wort zu beschreiben, so lange es der Kontext erlaubt auf das Gemeinte zu schließen. Bei Teekesselchen im allgemeinen ‘beschwert’ sich ja auch keiner – obgleich die Bedeutungen dort oft wesentlich weiter auseinanderklaffen.

    die Bitketten konnten allerdings nicht einfach komfortabel am Rechner eingegeben werden, sondern mussten noch manuell auf ein Trägermedium transferiert werden

    Nun ja, ‘komfortabel’ kann es wirklich nicht genannt werden, aber ‘am Rechner’ war sehr wohl nicht nur möglich, sondern teilweise sogar nötig. Selbst zu meinen Lebzeiten liefen noch Rechner (wars ne PDP11 oder doch eine 9er?) mit einer Hex-Tastatur (und das war schon golden, eine Reihe Schalter war durchaus üblich) plus ein Taster, mittels der byte für byte, binary of cause, der loader eingepiddelt werden konnte, der dann den ersten Lochstreifen einlas.
    Oder bei meinem 8085 die paar byte handgelötetes DiodenROM, dessen code über die cpu-eingebaute serielle Schnittstelle und die gebastelte PLL vom Flohmarkt-Kassettenrekorder den Monitor las, letzterer, wie oben schon erwähnt, so eine Art OS für Arme. Dieses Rom war die Verbesserung (aus Faulheit!) der kleinen Zähler/dipswitch/latch-Kombination, mit der bis dahin die loader-bits ins RAM getrieben wurden.
    Also bitte, ein festes BIOS ist doch schon absoluter Luxus :p

  8. #9 Marcus Frenkel
    August 31, 2011

    Nun ja, ‘komfortabel’ kann es wirklich nicht genannt werden, aber ‘am Rechner’ war sehr wohl nicht nur möglich, sondern teilweise sogar nötig.

    Akzeptiert und korrigiert. 😉
    Das war vor meiner Zeit. Ich hatte zwar im Kopf, dass Maschinencode auch direkt eingegeben wurde (unter anderem auch über die erwähnten Schalter), aber hatte bei Maschinencode in erster Linie die Lochkarten vor Augen. 😉

    Und: niemand hat gesagt, dass Faulheit nicht schlecht wäre. Sie verwirrt nur manchmal die Außenstehenden – und manchmal sogar Informatiker selber. Man denke nur an die Verwirrung über den Zusammenhang zwischen “Parsern” und “Compilern”…

  9. #10 rolak
    August 31, 2011

    niemand hat gesagt, dass Faulheit nicht schlecht wäre

    Ich. Gerade eben oben :-)

    Übrigens ist in der rss-mail hinter dem smilie hinter dem ‘korrigiert’ ein Zeilenvorschub, der hier im HTML nicht auftaucht. Wunder über Wunder…
    Das mit der Faulheit erkläre ich typischerweise etwas überhöht, um die merkwürdigen Gesichter des Gegenübers zu bewundern. Auf ‘Du bist faul’ rechnet wohl keiner mit einem ‘Na zum Glück!’. Doch aufgepasst, es gibt auch die dunkle Seite der Faulheit…

    Das mit P&C ist doch simpel: nur einer trägt seinen Namen zurecht; der eine gibt einem Zeichenstrom Struktur, der andere nimmt sie einer source; das Ergebnis des einen wird typischerweise von einem Programm ausgewertet, das des anderen von einer cpu 😉 Trotzdem, ich kann nicht verhehlen daß diese ein-Wort-für-alle-Nummer eine der vielen Varianten ist, mit denen nicht nur ich meine Gegenüber ‘teste’, schaue ob genügend frei assoziierend mitgedacht wird, Reaktionen in Wort und Mimik genieße. Spielfreude.

    Zu der Rechnerfamilie von eben fiel mir noch einer dieser uralten koans ein, den ich vorsichtshalber mal kopiere:

    In the days when Sussman was a novice, Minsky once came to him as he sat hacking at the PDP-6.
    “What are you doing?”, asked Minsky.
    “I am training a randomly wired neural net to play Tic-Tac-Toe” Sussman replied.
    “Why is the net wired randomly?”, asked Minsky.
    “I do not want it to have any preconceptions of how to play”, Sussman said.
    Minsky then shut his eyes.
    “Why do you close your eyes?”, Sussman asked his teacher.
    “So that the room will be empty.”
    At that moment, Sussman was enlightened.

  10. #11 JPeelen
    August 31, 2011

    Die Programmiersprachen der frühen programmierbaren Taschenrechner (in meinem Fall ein TI-59) waren den Assemblersprachen sehr ähnlich. Schleifen und logische Verzweigungen wurden in derselben Weise mit Labels und GOTO definiert. Man konnte sogar den vorhandenen Speicher variabel in einen Teil für Programmcode und einen zur Datenspeicherung aufteilen. Aufrufbare Unterprogramme ließen sich definieren.
    Der Knaller war die Möglichkeit, diese Programme auf kleinen (Format wie ein Kaugummistreifen) Magnetkarten zu speichern, die ein Motor durch den Rechner zog.

  11. #12 Marcus Frenkel
    August 31, 2011

    @JPeelen
    Die Sprachen der aktuellen Taschenrechner (also “aktuell” heißt hier, aus meiner Schulzeit; ist also auch schon einige Jahre her) sahen da immer noch nicht besser aus. 😉

    @rolak
    Ein schöner Zeitvertreib. 😉

  12. #13 rolak
    August 31, 2011

    Die TI59 waren schon heiße Teile, lehrten mich einiges zum Thema Preisverfall: Nach dem Ferienjob einen zu 700DM erstanden, nach Weihnachten gabs den für unter 400… Aber allein das Pfeifen, wenn eine der Karten durchrutschte 😉

    Und ja, das war vor über 30 Jahren alles schon so wie heute auch noch: Die Dinger wurden gehackt durch Modifikation des Taktgenerators (‘overclocking’ gabs damals noch nicht) und wilde Programmcodekombinationen, gecrackt so daß die internen und die wechselbaren ROMs ausgelesen und auf Karte(n) übertragen werden konnten zum Verteilen (am besten im Diktat, sonst schriftlich) und reverse engineering (immer schriftlich), (usergestütztes) Paging in den swapfile aka Kartenstapel gab es auch schon.

    Und dann brachten diese $@$&#?!cnpxneqf den HP41 raus…

  13. #14 Dr. Webbaer
    September 3, 2011

    Wo wird noch mit Assembler entwickelt?

    MFG
    Dr. Webbaer (der selbst immerhin noch ein Einsatzgebiet kennt)

  14. #15 Marcus Frenkel
    September 3, 2011

    Im Spielebereich, im Betriebssystembereich, überall dort, wo gewisse Mikrooptimierungen vorgenommen werden müssen, im Compilerbau, im Grafikbereich, bei der Treiberprogrammierung…

  15. #16 Dr. Webbaer
    September 3, 2011

    Aja, danke. Assembler lebt also.
    MFG Wb

  16. #17 rolak
    September 4, 2011

    “If a program can’t rewrite its own code”,
    he asked, “what good is it?”
    (src)

    Das ginge zwar in (extrem wenigen) Hochsprachen auch, doch wenn es {zusätzlich} wesentlich ist, das generierte Kompilat auch in größeren Sequenzen auf Bitebene zu kontrollieren, geht nichts am jeweiligen Assembler vorbei.
    Selbst wenn dies wg exzessiver Verwendung camouflierender HLL-Makros kaum mehr auffallen sollte.

  17. #18 Mj
    September 11, 2011

    wer richtig in die Tiefe will, kann sogar ein einfaches Betriebssystem programmieren, das ist auch nicht so sehr kompliziert (durch die vielen SSE-Befehle sogar eher einfacher).
    Und dank Virtualisierung muss man dabei nicht einmal seinen eigenen Rechner lahmlegen. 😉

    Wie würde man das machen? Also mit welchen Tools optimalerweise?

  18. #19 rolak
    September 11, 2011

    Wie würde man das machen?

    Was, das Lahmlegen? 😀

    Also mit welchen Tools optimalerweise?

    Möglichst mit solchen, die Deinem Stil entgegenkommen. Generell Assembler und Debugger – ich nutze A386/D386, freie Software gibt es zu Hauf, wie z.B. nasm/gdb. Die erste schöne Übung ist ein (im Laufe der Zeit immer komfortabler werdender) ohne Betriebssystem auskommender Schmal-Debugger, der die Aktivitäten des startenden Erzeugnisses protokolliert…
    Wie bei der BS-Entwicklung allerdings SSE hilfreich sein soll, erschließt sich mir noch nicht^^

    Oh ja, einen der vielen Virtualisierer bräuchtest Du natürlich auch noch, es sei denn, Dir wäre ein separater PC als Spielwiese lieber.
    Und nicht vergessen: Es gibt viel zum Abgucken und Lernen (bsp)!

  19. #20 Geldhai
    September 16, 2011

    Lahmlegen verstehe ich jetzt auch nicht