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.

Ein kleines Beispiel: die bereits oben erwähnte (fiktive) Instruktion “0110110101”, ausgesprochen “addiere den Wert 5 auf den im Akkumulator gespeicherten Wert”, würde sich mit Hilfe einer Assemblersprache auf die folgende Art schreiben lassen:

add al, 5

Das Mnemonic al bezeichnet hier übrigens das Akkumulator-Register. Die Zusammenfassung mehrerer Assemblerbefehle, die zusammenhängend ausgeführt werden sollen, um eine Aufgabe zu lösen, nennt man Assemblerprogramm. Assemblerprogramme werden in der Regel auch nicht mehr über externe Geräte eingegeben und dann in Lochkarten gestanzt, sondern direkt im Computer eingegeben. Die Übersetzung in echten Maschinencode (denn nur solchen kann der Prozessor verstehen) übernimmt dann ein sogenannter Assembler. Das ist ein relativ einfaches Programm, welches die geschriebenen Assembler-Befehle aus einer Datei lesen und in Maschinencode übersetzen kann und am Ende zum Beispiel eine ausführbare Datei (unter Windows sind das die Dinger mit “.exe” hinten dran) erzeugt.

An dieser Stelle möchte ich noch auf eine kleine Eigenheit in Bezug auf Assemblersprachen hinweisen, um spätere Verwirrung zu vermeiden: in der Umgangssprache (unter Informatikern) werden nicht nur die Übersetzungsprogramme “Assembler” genannt, sondern mitunter auch der geschriebene Code selber. Man sagt dann, dass ein Programm “in Assembler geschrieben” ist – gemeint ist natürlich, dass es in einer Assemblersprache geschrieben wurde, aber Informatiker sind zuweilen recht faul, was diese Unterscheidung angeht. Ich werde nach Möglichkeit versuchen, die beiden Begriffe immer zu unterscheiden; wenn es mir einmal nicht gelingt, sollte es aber deswegen jetzt auch keine Verwirrung mehr geben.

Nun ist allein schon die Möglichkeit, Mnemonics anstelle von komplizierten Instruktions-Codes benutzen zu können, eine große Erleichterung beim Programmieren. In einer Assemblersprache sind allerdings noch mehr Dinge möglich. Eine in den Assembler-Anfangszeiten besonders wichtige Eigenschaft war die Unterstützung bei der Behandlung von Variablen und Sprungbefehlen.

Genauso, wie ein Programm im Speicher liegt, da es nicht nur ausschließlich in den Registern des Prozessors gehalten werden kann (dafür gibt es nicht genügend), können die Daten eines Programms meist auch nicht nur in den Registern vorhanden sein. Bisher habe ich es noch nicht erwähnt, aber eigentlich ist klar, dass auch Daten im Speicher gehalten werden. Sie sind genauso wie Instruktionen über eine Adresse ansprechbar; so könnte etwa das Register al in der obigen add-Anweisung auch durch eine Adresse ersetzt werden, so dass die 5 nicht mehr in den Akkumulator, sondern in den Speicher geschrieben wird. Nun ist das Hantieren mit Speicheradressen natürlich höchst unkomfortabel; aus diesem Grund können in einem Assemblerprogramm sogenannte Variablen deklariert werden. Das sind im Grunde nichts anderes als eindeutige Bezeichner für bestimmte Adressen im Speicher; die zugrundeliegende Adresse dabei entweder von Hand angegeben oder – und hier liegt der große Vorteil – automatisch vom Assembler bestimmt werden. Die Deklaration und Benutzung von Variablen im Code ist relativ simpel; nachfolgend ein kleines Beispiel, in welchem eine Variable var deklariert und anschließend an die von ihr bezeichnete Adresse im Speicher der Wert 5 geschrieben wird (das db bedeutet nur, dass lediglich ein einziges Byte an Speicher reserviert werden soll):

var db
mov var, 5

Für die Bearbeitung komplexerer Aufgabenstellungen, die über das reine Verrechnen von Zahlen hinausgehen, ist die Kontrolle des Programmflusses unerlässlich. In unseren bisherigen Betrachtungen sah es immer so aus, als würde streng eine Instruktion nach der anderen geladen; bei näherer Betrachtung erweist sich das allerdings als sehr nachteilig, da so nur wirklich lineare Probleme zu lösen sind. In der Regel sind allerdings auch Verzweigungen im Programmfluss und Wiederholungen von Programmcode nötig, außerdem auch der Aufruf sogenannter Unterprogramme (ein Unterprogramm ist im Grunde nichts anderes als ein Codeabschnitt, der eine meist kleinere Aufgabe löst). Und hier kommen die Sprungbefehle ins Spiel; sie ermöglichen, dass der sonst streng lineare Programmablauf unterbrochen werden kann, indem die nächste zu ladende Instruktion angegeben wird. Springt man dabei zurück zu einer bereits ausgeführten Instruktion, hat man eine Wiederholung; überspringt man einen Teil der Instruktionen, hat man eine Verzweigung im Programmcode; und springt man schließlich an eine ganz andere Stelle im Code, so entspricht dies dem Aufruf eines Unterprogramms. Wir erinnern uns: die Adresse der nächsten zu ladenden Instruktion steht im Instruktionsregister; modifiziert man nun also nun diesen Wert, kann ein Sprung in der Instruktionsreihenfolge durchgeführt werden. Der mnemonische Code für die einfachste derartige Anweisung, den unbedingten Sprung, ist jmp (als Abkürzung für “jump”, also Sprung), dass als Operanden das Sprungziel, also die Adresse der nächsten Instruktion erwartet. Zu Zeiten der Erstellung von Maschinencode per Hand war es dabei noch nötig, die exakte Adresse (die Adresse der konkreten Speicherzelle, welche die gewünschte Instruktion enthält) zu kennen, was insbesondere bei Vorwärtssprüngen kompliziert war. Außerdem hatte jede Änderung am Maschinencode zur Folge, dass unter Umständen die Sprungziele angepasst werden mussten (da sich durch das Einfügen von Befehlen ja die Adressen aller nachfolgenden Instruktionen ändern). Im Assemblercode ist es nun möglich, dieses Problem zu umgehen, und zwar dank der Verwendung von Sprungmarken; dies sind einfache Bezeichner, die an beliebigen Stellen im Code zwischen zwei Instruktionen eingefügt werden können und bei der Übersetzung in Maschinencode automatisch in konkrete Adressen umgewandelt werden. Hierzu ein kleines (nicht sinnvolles!) Beispiel (var ist unsere Variable von vorhin):

mov al, var
loop:
add al, 1
jmp loop

Der Code macht nichts anderes, als zu Anfang in das Register al den Wert zu schreiben, der im Speicher an der durch var beschriebenen Stelle steht und dann in einer (unendlich lang laufenden) Schleife immer wieder den Wert 1 darauf zu addieren.

Neben den unbedingten Sprüngen gibt es – wenig überraschend – auch die bedingten Sprünge, die nur dann ausgeführt werden, wenn eine bestimmte Bedingung erfüllt ist. Auch hierzu ein diesmal etwas sinnvolleres Beispiel (die Erklärung folgt darunter, die Zeilen sind nummeriert):

(1)       mov al, 0
(2)       mov bl, var1
loop:
(3)       sub bl, var2
(4)       cmp bl, 0
(5)       jl end
(6)       inc al
(7)       jmp loop
end:
(8)       add bl, var2

Nehmen wir an, dass in den Variablen var1 und var2 zwei Werte gespeichert sind, wobei wir die Gleichung var1 / var2 berechnen wollen, und zwar ganzzahlig und mit Rest. In den ersten beiden Zeilen (1) und (2) des Codes wird das Register al mit dem Wert 0 und das Register bl mit dem Wert von var1 gefüllt. Anschließend folgt eine Sprungmarkierung. In der darauffolgenden Zeile (3) wird vom Wert in bl der Wert von var2 abgezogen. Anschließend erfolgt in (4) ein Vergleich (cmp von englisch “compare”) von bl und dem festen Wert 0. Dabei werden intern im Prozessor einige sogenannte Flags (separate Register mit einer Größe von nur einem Bit) gesetzt, je nachdem, ob der erste Wert größer, kleiner oder gleich dem zweiten Wert ist. Die Magie passiert in der nächsten Zeile (5): jl end ist ein bedingter Sprung (jl von englisch “jump less”), der in diesem Fall genau dann ausgeführt wird, wenn der vorhergehende Vergleich ergeben hat, dass der erste Wert kleiner als der zweite ist (für die Kenner: der Sprung wird durchgeführt, wenn das Zeroflag auf 0 und das Signflag ungleich dem Overflowflag ist). Ist dies nicht der Fall, wird in Zeile (6) der Wert im Register al um eins erhöht (inc von englisch “increment) und anschließend in (7) zurück zum Anfang der Schleife gesprungen. Wird der bedingte Sprung dagegen durchgeführt, wird noch die letzte Anweisung (8) hinter der Schleife ausgeführt, in welcher auf den Wert in Register bl noch einmal der Wert von var2 draufaddiert wird, um den korrekten Rest zu berechnen.

Eigentlich alles ganz klar, oder? Nein? Gut, hier eine kleine Wertetabelle für eine Beispielrechnung; nehmen wir an var1 hat den Wert 8 und var2 den Wert 3, wir wollen also 8/3 berechnen. Damit ergibt sich die folgende Tabelle; jede Zeile markiert einen Ausführungszyklus:

Codezeile al bl Bemerkung
2 0 8 Initialisierung
3 0 5 Berechnung von: bl – var2
4 0 5 Vergleich: 5 < 0?
5 0 5 5 ist nicht kleiner als 0, also passiert nichts
6 1 5 Inkrement: al
7 1 5 Sprung zurück zu “loop”
3 1 2 Berechnung von: bl – var2
4 1 2 Vergleich: 2 < 0?
5 1 2 2 ist nicht kleiner als 0, also passiert nichts
6 2 2 Inkrement: al
7 2 2 Sprung zurück zu “loop”
2 2 2 Berechnung von: bl – var2
4 2 -1 Vergleich: -1 < 0?
5 2 -1 -1 ist kleiner als 0, also Sprung zur Markierung “end”
8 2 2 Addition: bl + var2

 

Und in der Tat: 8 geteilt durch 3 ergibt bei ganzzahliger Division 2, Rest 2. Mal ehrlich: so schwer war es am Ende nun doch nicht, oder? Mit ein wenig Konzentration und gutem Willen lässt sich Assemblercode doch relativ gut verstehen. Zumindest besser als reiner Maschinencode. Aber ich gebe gerne zu, dass auch Assemblercode nicht optimal ist; zumindest wird er den Anforderungen der modernen Programmierwelt definitiv nicht gerecht. Er bietet jedoch die Möglichkeit, auf einer sehr niedrigen Ebene Programme schreiben (und vor allem auch später wieder lesen) zu können und wird auch heutzutage noch ab und zu benutzt. Die hauptsächliche Programmierung von Computern wird aber mit sogenannten Hochsprachen durchgeführt – aber die schauen wir uns ein andermal an.


1 Wer die erste Assemblersprache entwickelt hat (und wie sie hieß) weiß ich leider nicht – wer Informationen dazu hat, möge sich bitte in den Kommentaren melden!em beschriebenen Stelle steht und dann in einer (unendlich lang laufenden) Schleife immer wieder den Wert 1 darauf zu addieren.

Neben den unbedingten Sprüngen gibt es – wenig überraschend – auch die

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