image: cpu-sandbox.webp kind: projekt topic: sandboxes reference: [gitlab] template: https://github.com/specht/cpu-sandbox.git class_repo: git@git.nhcham.org:specht/cpu-sandbox-programs-2026.git template_name: der CPU Sandbox

Vom Byte zum 3D-Labyrinth

In der CPU Sandbox programmierst du einen kleinen 8-Bit-Computer, erst in Assembler, später in C. Register, Flags, Speicher, Bildschirm und Geräte sind dabei jederzeit sichtbar, bis hinunter zum einzelnen Takt auf dem Bus. Und am Ende läuft auf derselben Maschine ein 3D-Labyrinth.

1. Worum es geht

Zur CPU Sandbox gehören 26 Beispielprogramme, von der Addition zweier Zahlen bis zu Spielen und Grafikeffekten. Alles, was auf dem Bildschirm erscheint, berechnet die simulierte Maschine selbst, Pixel für Pixel, ohne versteckte Grafikbefehle.

CPU Sandbox mit laufendem 3D-Raycaster, dem C-Quelltext mit Maschinenbytes, Registern, Geräten und RAM.

Im Bild läuft 25-raycaster.c, ein Labyrinth im Stil von Wolfenstein 3D, ganz ohne Gleitkommazahlen. In den Beispielen steckt noch viel mehr; jedes Bild hier ist der Bildschirm eines Beispielprogramms:

Du arbeitest mit zwei Git-Repositories. Im Workspace ist ~ dein Ordner /workspace.

Ordner Inhalt Wer ändert es?
~/cpu-sandbox/ die App mit den Beispielen nur deine Lehrkraft
~/cpu-sandbox/programs/ das Klassen-Repository mit euren Programmen ihr, jede und jeder im eigenen Ordner

Die Karten auf dieser Seite schauen in deinem Workspace nach und sagen dir, was sie finden. Sie ändern nichts.

2. Einrichten

Einmalig: Zugang zu GitLab

Mit einem SSH-Schlüssel meldet sich dein Workspace beim GitLab-Server an, ohne dass du jedes Mal ein Passwort eingibst. Melde dich zuerst auf git.nhcham.org an. Prüfe dann im Terminal, ob du schon einen Schlüssel hast:

ls ~/.ssh/id_ed25519.pub

Erscheint No such file or directory, erzeugst du einen neuen. Bestätige den vorgeschlagenen Speicherort mit Enter; die Passphrase darfst du im Workspace leer lassen und noch zweimal Enter drücken:

ssh-keygen -t ed25519 -C "Vorname Nachname"

Zeige nun den öffentlichen Schlüssel an und kopiere die ganze Zeile, die mit ssh-ed25519 beginnt:

cat ~/.ssh/id_ed25519.pub

In GitLab gehst du oben rechts auf dein Profilbild und dann auf Edit profile → Access → SSH keys → Add new key. Füge die Zeile bei Key ein, gib als Titel zum Beispiel Workspace an und speichere.

Die Datei ~/.ssh/id_ed25519 ohne .pub ist dein privater Schlüssel. Den kopierst du nie und lädst ihn nirgendwo hoch.

Teste die Verbindung. Beim ersten Mal fragt SSH nach, ob du dem Server vertraust; antworte mit yes. Danach begrüßt dich GitLab mit deinem Benutzernamen:

ssh -T git@git.nhcham.org

Meldet SSH Permission denied (publickey), fehlt der Schlüssel in GitLab, oder du hast eine andere Zeile kopiert als die aus id_ed25519.pub.

Aufgabe 1Dein Schlüssel

Dein öffentlicher SSH-Schlüssel liegt in ~/.ssh/id_ed25519.pub.

Tipp

~ ist im Workspace /workspace. ls ~/.ssh zeigt, was dort liegt.

Jeder Commit trägt einen Namen und eine E-Mail-Adresse. Trage deinen echten Namen und die E-Mail-Adresse deines GitLab-Kontos ein:

git config --global user.name "Vorname Nachname"
git config --global user.email "deine.mail@example.org"

Das ist in jedem Workspace nur einmal nötig. Ohne diese Angaben weigert sich Git zu committen.

Aufgabe 2Git kennt dich

Git kennt deinen Namen und die E-Mail-Adresse deines GitLab-Kontos.

Einmalig: Klonen

Klone die App in dein Home-Verzeichnis:

cd ~
git clone https://github.com/specht/cpu-sandbox.git

Wechsle in ihren Ordner und klone dort das Klassen-Repository:

cd cpu-sandbox
git clone git@git.nhcham.org:specht/cpu-sandbox-programs-2026.git programs

Das Klassen-Repository muss in der App liegen und genau programs heißen. Gibt es programs/ schon, klone es nicht noch einmal und lösche nichts, sondern frag nach.

Aufgabe 3Die Sandbox ist da

Die App liegt in ~/cpu-sandbox, das Klassen-Repository darin in programs.

Aufgabe 4Ein eigenes Repository

programs ist ein eigenes Git-Repository und weiß, wohin es pushen kann.

3. Arbeiten

Die App starten

Starte die App in einem Terminal und lass es offen. Für Git öffnest du ein zweites Terminal.

cd ~/cpu-sandbox
./run

Ein neues Programm legst du in der App mit + neben der Programmauswahl an. Gib als Student folder deinen Ordnernamen an, zum Beispiel anna, dann einen Dateinamen, die Sprache und die CPU. Das Programm entsteht in programs/DEIN_NAME/.

Drei Ausbaustufen, eine Befehlssprache

Die MiniCPU gibt es in drei Stufen. Jede behält alles von der vorherigen und fügt genau eine Idee hinzu, sodass du nichts umlernen musst:

Modell Neue Idee Speicher und Register
MiniCPU 8 Daten bewegen, rechnen, vergleichen, springen, Schleifen 256 Byte; A–D, PC, Flags ZF und CF
MiniCPU 8S Stack: Unterprogramme mit CALL/RET, Rekursion zusätzlich Stackpointer SP
MiniCPU 8X Zeiger, große Programme, Grafik 64 KiB, 16-Bit-Adressen, Register X und Y

Die Schreibweise ist einheitlich: Das Ziel steht immer vorne, und eckige Klammern bedeuten immer »der Speicher an«. LDI A, 0x70 lädt die Zahl 0x70, LDA A, [0x70] das Byte, das an Adresse 0x70 steht. Wer sich vertut, bekommt eine Fehlermeldung, die genau diesen Unterschied erklärt. So sieht eine erste Schleife aus:

        LDI A, 5
        LDI B, 0
loop:   ADD B, A
        DEC A
        JNZ loop
        HLT
Zum Weiterdenken: Das Programm addiert 5 + 4 + 3 + 2 + 1. Was passiert, wenn A mit 0 statt mit 5 startet, und warum steht danach ausgerechnet 128 in B? Wer das beantworten kann, hat Überlauf, Flags und 8-Bit-Arithmetik verstanden.

In einen Befehl hineinsehen

Step führt einen Befehl aus, Step back nimmt ihn zurück. Neben jeder Quelltextzeile stehen Adresse und Maschinenbytes; geänderte Speicherzellen leuchten auf. Im Modus Cycles geht es noch eine Ebene tiefer: Jeder Klick ist ein Takt. Du siehst, wie der Opcode geholt und dekodiert wird, wie Operanden gelesen werden, was auf Adress- und Datenbus liegt und wann gesprungen wird.

MiniCPU im Taktmodus: Holphase von LDI B, 0 mit Adress- und Datenbus, daneben Quelltext, Register und RAM.

Takte vorhersagen. Warum braucht LDA A, [0x80] einen Takt mehr als LDI A, 0x80? Was schreibt CALL in den Speicher, und wohin? Warum kostet ein JZ, das springt, einen Takt mehr als eines, das nicht springt? Stell erst Vermutungen auf und prüf sie dann im Taktmodus.

Ein paar Dinge, die du in der Sandbox fast von selbst entdeckst:

  • Es gibt nur einen Speicher. Bildschirm und Stack sind einfach Bereiche darin, und ein Schreibzugriff an die falsche Adresse kann das Programm selbst verändern. Das Beispiel 05-self-modifying macht das sogar absichtlich.
  • Ein Label ist die Adresse eines Bytes, keine Zeilennummer.
  • 8-Bit-Arithmetik läuft über: 255 + 1 ergibt 0, und das Carry-Flag ist gesetzt.
  • Zeit kosten Takte, nicht Zeilen.

Von Assembler zu C

Ab MiniCPU 8S kannst du Programme auch in MiniC schreiben, einer kleinen Variante von C, die schon beim Tippen übersetzt wird. Versteckt wird dabei nichts: Neben jeder C-Zeile stehen ihre Maschinenbytes, und der Reiter Assembly zeigt das erzeugte Programm. Du siehst, dass lokale Variablen auf dem Stack liegen, wie ein Funktionsaufruf sein Argument übergibt und wie das Ergebnis zurückkommt.

MiniCPU 8X berechnet einen rotierenden, schattierten Würfel aus einem C-Programm.

Der Würfel aus 18-cube.c nutzt eine Sinustabelle in Festkommaarithmetik, Perspektive, das Ausblenden verdeckter Flächen, Schattierung und Seitenwechsel (page flipping) gegen Flackern, und die Füllroutine ist von Hand in Assembler geschrieben.

Aufgaben, die sich selbst prüfen

Sechzehn Aufgaben prüfen sich selbst: vom Addieren zweier Bytes über Fibonacci, Multiplikation und Sortieren bis zu einem Schachbrettmuster, dazu Aufgaben speziell für C wie Fakultät, größter gemeinsamer Teiler, Primzahlsieb und Linienzeichnen. + legt ein Programm zu einer Aufgabe an, Check führt alle Testfälle aus und erklärt Fehler wie »0x82 should be 7, but it is 8«, und Try it lädt einen Testfall in die Maschine, damit du ihn Schritt für Schritt verfolgst.

Class progress sammelt die funktionierenden Lösungen der Klasse und sortiert sie nach Takten; das kleinste Programm in Bytes wird zusätzlich markiert. Wer schafft es mit weniger Takten? Wer mit weniger Bytes? Und geht beides gleichzeitig?

Besonders lehrreich ist es, dieselbe Aufgabe zweimal zu lösen, einmal in Assembler und einmal in C. Mit geradlinigen Lösungen ergibt sich etwa:

Aufgabe Assembler: Bytes · Takte C: Bytes · Takte
add 12 · 75 25 · 180
maximum 22 · 820 88 · 3.420
sort 42 · 4.260 176 · 19.271
multiply 33 · 5.991 92 · 1.935
Zum Weiterdenken: C braucht meist das Zwei- bis Vierfache an Bytes und Takten, dafür ist das Programm viel schneller geschrieben. Bei multiply ist es umgekehrt: Das C-Programm braucht nur ein Drittel der Takte. Woran liegt das? (Tipp: Wie multipliziert der Compiler, und wie die naheliegende Assembler-Lösung?) Ein besserer Algorithmus schlägt handgeschriebene Befehle.

Mehr zur App steht in ihrer Anleitung, alle Aufgaben in der Aufgabenliste.

4. Veröffentlichen

Deine Lehrkraft sieht deine Programme erst, wenn sie committet und gepusht sind. Das machst du im Ordner programs, nicht im Ordner der App:

cd ~/cpu-sandbox/programs
git pull
git add DEIN_NAME/
git commit -m "Aufgabe add von DEIN_NAME"
git push
Aufgabe 5Was gehört in deinen Commit?

Im Klassen-Repository arbeiten alle. Mit welchem Befehl nimmst du nur deine eigenen Dateien in den nächsten Commit auf?

Was gehört in deinen Commit?
Aufgabe 6Committet und gepusht

Deine Arbeit ist committet und gepusht. Erst dann sieht deine Lehrkraft sie in »Class progress« und die anderen nach git pull in ihrer App. Prüfe das am Ende jeder Stunde.

In einem Klassen-Repository veröffentlichen viele. Hat jemand kurz vor dir gepusht, lehnt Git deinen Push mit rejected ab, weil dir dessen Commits fehlen. Hol sie zuerst und push dann noch einmal:

git pull
git push

Öffnet git pull einen Editor für eine Commit-Nachricht, speichere sie unverändert und schließe den Editor. Meldet Git einen Konflikt, hat jemand dieselbe Datei geändert wie du. Dann gilt: nichts löschen, nichts überschreiben, nach Hilfe fragen. Solange du nur Dateien in deinem eigenen Ordner änderst, passiert das nicht.

5. Beim nächsten Mal

cd ~/cpu-sandbox && git pull
cd programs && git pull
cd .. && ./run

Und am Ende der Stunde: Aufgabe 5 noch einmal prüfen.