Eine redundante Geschichte der Programmierung

Der ewige Kampf gegen Redundanzen

Christoph Knabe

Seit den Anfängen der Programmierung behinderten Redundanzen im Quellcode die Wartung und Wiederverwendung. Redundanz bedeutet, dass derselbe Sachverhalt an mehreren Stellen im Quellcode ausgedrückt ist. Die Notwendigkeit, das zu vermeiden [1], führte in den letzten 50 Jahren zu zahlreichen Programmierkonstrukten, deren Zusammenhang einem im Alltag oft nicht bewusst ist. Sie werden anhand 14 verbreiteter Programmiersprachen besprochen. Wenn man die Zusammenhänge verstanden hat, ist man auch gut für die Zukunft gerüstet.

Ich hatte als Schüler 1971 die „Chance“, eine 15 Jahre alte Technik zu lernen, nämlich die Programmierung einer ausrangierten Zuse-22-Rechenanlage. Was man in der schnelllebigen Informatik normalerweise als Zeitverschwendung ansehen würde, ermöglicht mir jetzt aber einen Überblick über die Programmiertechniken der letzten 50 Jahre. Die Zuse 22 war als einer der ersten in Serie gefertigten Computer weltweit (Verkauf ab 1958) äußerst ressourcensparsam aufgebaut. Sie wurde in einer maschinennahen Sprache, dem sogenannten Freiburger Code programmiert. Das Beispielprogramm aus Tabelle 1 summiert die von n bis 1 fallenden natürlichen Zahlen auf und gibt das Ergebnis aus (getestet mit dem Z22-Simulator von Wolfgang Pavel [2]). Nur der Inhalt der Tabellenspalte Befehl wurde auf Lochstreifen gestanzt und als Programm eingelesen. Die Spalte Adresse gibt an, in welches Wort der Befehl dabei geladen wird. Die Spalte Erklärung entspricht einem heutigen Kommentar. Wir finden hier symbolische, kombinierbare Operationscodes (B = bringen, A = addieren, S = subtrahieren, T = transportieren, U = umspeichern, C = Const-Wert, PP = wenn-Positiv, E = Execute from (springen zu), D = drucken, Z = Stopp). Die Adressierung erfolgt jedoch rein numerisch mit absoluten Speicheradressen. Die beiden Variablen i und summe werden unter den Adressen 2048 und 2049 abgelegt. Der Algorithmus wird damit ab der Adresse 2050 gespeichert, wohin auch mittels PPE2050 zurückgesprungen wird, wenn der Inhalt von i noch positiv ist. Redundant sind hier die Adressangaben: 2048 für das i kommt vier Mal, 2049 für die summe drei Mal, 2050 für den Programm- und Schleifenbeginn zwei Mal explizit und einmal implizit vor. Das Programm ist so weder im Arbeitsspeicher verschiebbar noch einfach erweiterbar – eine sehr wartungsfeindliche Situation also.

Artikelserie

Tabelle 1: Summiere Zahlen von n bis 1 im Freiburger Code (Zuse 22)

Adresse Befehl Erklärung
T2048T Transportiere Folgendes nach Zellen 2048 ff.
2048 10′ i: Startbelegung für i ist n, hier die Ganzzahl 10
2049 0′ Summe: Startbelegung mit Ganzzahl 0
2050 B2049 Bringe die Summe in den Akku(mulator)
2051 A2048 Addiere i auf den Akku
2052 U2049 Umspeichern des Akkus nach Summe
2053 B2048 Bringe i in den Akku
2054 SC1 Subtrahiere den Const-Wert 1 vom Akku
2055 U2048 Umspeichern des Akkus nach i
2056 PPE2050 Wenn Akku Positiv Execute from (springe zu) 2050
2057 B2049 Bringe Summe in den Akku
2058 D Drucke Akku
2059 Z0 Stopp
E2050E Execute now from 2050
Relative und symbolische Adressierung

Einen Fortschritt stellte die später entwickelte „relative Adressierung“ dar. Sie ermöglichte es, ein Unterprogramm zu schreiben, als begänne es an der Adresse 0. Durch einen bestimmten Vorbefehl beim Einlesen des Unterprogramms konnte das Leseprogramm dazu veranlasst werden, die tatsächliche Anfangsadresse im Register 26 zu speichern. Durch Anhängen von A26 an eine Relativadresse wurde das Leseprogramm dazu gebracht, die Relativadresse um den Inhalt von Register 26 erhöht im Arbeitsspeicher abzulegen. Der bedingte Sprungbefehl zum Programmanfang würde beim Programm aus Tabelle 1 mit relativer Adressierung also PPE2A26 statt PPE2050 lauten. Damit wäre die Verschiebbarkeit des Programms erreicht. Auch die relative Adressierung war noch sehr sparsam. Sie benötigte keine weiteren Ressourcen als das Register 26 zur Einlesezeit. Bei heutigen Assemblern hat die relative Adressierung ihre Entsprechung im Basisregister.

Die relative Adressierung erleichtert zwar das Verschieben eines Unterprogramms, ist aber innerhalb des Unterprogramms genauso starr wie die absolute Adressierung. Wenn man in obiges Beispiel vor die eigentliche Berechnung eine Anfangsaktion einbauen möchte, muss sich auch das relative Sprungziel 2A26 verschieben. Bei der Zuse 23 (Verkauf ab 1961) wurde daher die „symbolische Adressierung“ eingeführt, siehe Programmieranleitung für die Z23 ab Seite 65 ff. [3]. Sie löste in Klammern stehende, bis zu fünf Zeichen lange Namen in Adressen auf. Das Programm aus Tabelle 1 würde mit den symbolischen Adressen (I), (SUMME) und (ANFAN) zunächst wie in Listing 1 aussehen.

Listing 1
T2048T
(I) 10'
(SUMME) 0'
(ANFAN) B(SUMME)
A(I)
U(SUMME)
B(I)
SC1
U(I)
PPE(ANFAN)
B(SUMME)
D
Z0
E(ANFAN)E

Hier können jetzt an beliebiger Stelle weitere Befehle eingebaut werden, ohne das Programm zu zerstören. Dieser Fortschritt war so groß, dass der Assembler für die Siemens 2002 nach dieser Technik Prosa (Programmieren mit Symbolischen Adressen) benannt wurde. An Ressourcen wurden dafür zur Programmlesezeit ein Adressierprogramm und eine Symboltabelle benötigt.
Wir lernen hier auch schon das häufigste Verfahren zur Beseitigung von Redundanzen kennen: Man gibt dem redundanten Programmteil einen Namen und verwendet den Namen anstelle der bisherigen, redundanten Programmteile.

Geschrieben von
Christoph Knabe
Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.