TI2/Rechnerarchitektur-Tutorium, 21./22.04.2015

Organisatorisches

Kontakt

TI2 bestehen

  • 3 Säulen (Klausur, aktive/passive Teilnahme)
  • Klausur
    • Modulnote = Klausurnote
  • passive Teilnahme
    • n-2 Tutorien besuchen (KW 20 optional)
    • Anwesenheitspflicht
  • aktive Teilnahme
    • n-2 Übungszettel bestehen
    • und einmal vorrechnen

Übungszettel

Abgabezyklus
Abgabezyklus
  • Wöchentliche Ausgabe der Zettel
  • 2 Wochen Bearbeitungszeit
  • Abgabe: Freitags 10:00 Uhr
  • Bearbeitung und Abgabe in 2er-Gruppen
  • Namen auf allen Abgaben

Abgabe der Übungszettel

  • Papierabgabe
    • In mein Fach (1. OG)
    • Tackern!
    • Namen und Tutorium (Wochentag) auf der Abgabe
    • Bei Rechercheaufgaben: Quellen angeben
  • Elektronische Abgabe
    • Im KVV hochladen (nur einer aus der Gruppe)
    • Nur notwendige Dateien
    • Nicht gezippt o.ä. sondern direkt hochladen
    • Namen und Tutorium (Wochentag) als Kommentar im Kopf
    • Entwicklung unter 64bit-Linux
    • Programme müssen auf andorra kompilieren und laufen
  • Plagiate: Aberkennung der aktiven Teilnahme

Bewertung der Übungszettel

  • 3-Punkte-System
  • Jeweils 2 Aufgaben werden pro Zettel bewertet
  • Theorie-Aufgaben
    • 1 Punkt: Ansatz zu erkennen
    • 2 Punkte: Gut (nur kleinere Fehler)
    • 3 Punkte: Perfekt / Klausurreif
  • Programmier-Aufgaben
    • 1 Punkt: Ansatz zu erkennen
    • 2 Punkte: Programm kompiliert hat jedoch kleinere Fehler
    • 3 Punkte: Programm kompiliert und erfüllt Aufgabenstellung
  • Jede bewertete Aufgabe: mindestens 1 Punkt
  • Insgesamt: mindestens 3 Punkte

Klausur

  • Klausurtermin steht noch nicht fest
    • Nachklausur zusammen mit ProInformatik (vermutlich 2.10.2015)
  • neue Prüfungsordnung: 120 min Klausur
    • 60 min TI2 / Rechnerarchitektur
    • 60 min TI3 / Betriebs- und Kommunikationssysteme
  • alte Prüfungsordnung: 90 min Klausur
    • TI2-Teil aus der anderen Klausur + zusätzliche Aufgabe(n)
  • Nachklausur
    • Nur für alte Prüfungsordnung
    • Für neue Prüfungsordnung wieder nach dem WS2015/16
  • Anmeldung im Campus-Management absolut notwendig

Wunschkonzert

  • Montag, 16:15-17:45 Uhr
  • Raum: SR 049
  • Beginn: 27.04.2015
  • Konzept:
    • Ihr kommt mit Fragen zum Wunschkonzert
    • Ich beantworte eure Fragen
    • Vor allem auch für weitergehende oder Grundlagen-Fragen, die in den Tutorien nicht unbedingt besprochen werden können

Vorlesung

Allgemein

  • Vorlesung befasst sich mit dem theoretischen Teil der Rechnerarchitektur
  • Kein Assembler in der Vorlesung
  • Tutorium: Praktische Umsetzung durch Assembler-Programmierung

von-Neumann-Architektur

  • Programme = Daten
Aufbau der Von-Neumann Architektur, 2007, Lukas Grossar, Public Domain
Aufbau der Von-Neumann Architektur, 2007, Lukas Grossar, Public Domain

Register vs. Hauptspeicher

  • Hauptspeicher ist zu langsam für die CPU
  • CPU hat zusätzlichen schnellen Speicher direkt an der CPU: Register
    • rax, rbc, rcx, rdx, rsi, rdi, rbp, rsp, r8, r9, r10, r11, r12, r13, r14, r15
    • (Nur) auf den Registern können Operationen ausgeführt werden
  • Die ersten 8 Register haben spezielle Bedeutung
    • Vor allem bei Funktionsaufrufen (Calling Conventions)

Simpler Interpreter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Interpreter {
    static int PC;              // Program counter holds the address of the next instruction
    static int AC;              // Register for doing arithmetic, accumulator
    static int instruction;     // Current instruction
    static int instructionType; // Type of the current instruction, i.e. what to do
    static int dataLocation;    // Address of the data for the instruction
    static int data;            // Holds the operand
    static boolean runBit=true; // Bit used to halt the computer

    public static void interpreter (int memory[], int startingAddress) {
        PC = startingAddress;                                      // Initialize the program
        while( runBit ) {
            instruction = memory[PC];                              // Fetch next instruction
            PC = PC + 1;                                           // Increment PC
            instructionType = getInstructionType(instruction);     // Determine instruction
            dataLocation = findData(instruction, instructionType); // Locate data
            if( dataLocation >= 0 )                                // No operand if -1
                data = memory[dataLocation];                       // Fetch data
            execute(instructionType, data);                        // Execute instruction
        }
    }
}

(Beispiel aus der Vorlesungsfolie.)

Assembler

Simples C-Programm

1
2
3
4
5
6
#include <unistd.h>

int main() {
    write(1, "Hello, World!\n", 14);
    _exit(0);
}

Kompiliert mit gcc -o hello hello.c sieht der Teil der main-Methode so aus:

ba0e000000bef4054000bf01000000
e8c2feffffbf00000000e8a8feffff

Maschinenbefehle

Man kann das Programm auch wieder disassemblieren (mit objdump -M intel-mnemonic -d hello). Der Teil der main-Methode sieht dann folgendermaßen aus:

1
2
3
4
5
6
ba 0e 00 00 00           mov    edx,0xe
be f4 05 40 00           mov    esi,0x4005f4
bf 01 00 00 00           mov    edi,0x1
e8 c2 fe ff ff           call   400420 <write@plt>
bf 00 00 00 00           mov    edi,0x0
e8 a8 fe ff ff           call   400410 <_exit@plt>

Allgemeines

Installation testen

  • Zum Testen, ob nasm funktioniert könnt ihr diese Zip-Datei runterladen:

    jonas@arch ~ti2t % wget https://page.mi.fu-berlin.de/jonascleve/data/download/nasm_test.zip
    jonas@arch ~ti2t % unzip nasm_test.zip
    Archive:  nasm_test.zip
       creating: nasm_test/
      inflating: nasm_test/hello.asm
      inflating: nasm_test/bar.c
      inflating: nasm_test/foo.asm
      inflating: nasm_test/Makefile
    jonas@arch ~ti2t % cd nasm_test
    jonas@arch ~ti2t/nasm_test % make all
    nasm -f elf64 foo.asm
    gcc -o foo foo.o
    nasm -f elf64 hello.asm
    gcc -o hello hello.o bar.c
    jonas@arch ~ti2t/nasm_test % ./foo
    Hello, world!
    jonas@arch ~ti2t/nasm_test % ./hello
    Test Here!
    Hello from Assembly!

Vorbesprechung Übungszettel

Erstes Assembler-Programm

Idee

Wir wollen eine kleines Programm schreiben, dass drei Ganzzahlen entgegennimmt und von der Summe der ersten beiden die letzte Zahl abzieht. Also f(a, b, c)=a + b − c.

Wir schreiben ein C-Programm, dass unsere später in Assembler geschrieben Funktion aufruft und das Ergebnis ausgibt.

C-Programm

bar.c:

1
2
3
4
5
6
7
8
9
10
#include <stdio.h>

extern int foo(int, int, int);

int main()
{
    int i = foo(42,1337,2047);
    printf("i hat den Wert: %d\n",i);
    return 0;
}

Bei Ausführung sollte i hat den Wert: -668 herauskommen.

Assembler-Funktion

Eine erste Assembler-Funktion, die nicht macht, was wir wollen, aber wir können es zumindestens ausführen.

foo.asm:

1
2
3
4
section .text
global foo
foo:
    ret
  • Zeile 1: Jetzt kommt Quelltext
  • Zeile 2-3: Jetzt kommt die Funktion foo
  • Zeile 4: Beende die Funktion

Kompilieren und Ausführen

jonas@arch ~ti2t % nasm -f elf64 foo.asm
jonas@arch ~ti2t % gcc -o run foo.o bar.c
jonas@arch ~ti2t % ./run
i hat den Wert: 4195601

Woher kommt der Wert für i? Lösung: Calling Conventions

“The first six integer […] arguments are passed in registers RDI, RSI, RDX, RCX, R8, and R9 […] and the return value is stored in RAX.”

Etwas vernünftiges zurückgeben

Wir wollen erstmal einen festen Wert zurückgeben:

1
2
3
4
5
section .text
global foo
foo:
    mov rax, 1234
    ret
jonas@arch ~ti2t % nasm -f elf64 foo.asm
jonas@arch ~ti2t % gcc -o run foo.o bar.c
jonas@arch ~ti2t % ./run
i hat den Wert: 1234

Etwas vernünftiges zurückgeben

Mit dem Wissen, was in welchem Register steht können wir nun auch die komplette Funktion implementieren:

1
2
3
4
5
6
7
section .text
global foo
foo:
    add rdi, rsi
    sub rdi, rdx
    mov rax, rdi
    ret
jonas@arch ~ti2t % nasm -f elf64 foo.asm
jonas@arch ~ti2t % gcc -o run foo.o bar.c
jonas@arch ~ti2t % ./run
i hat den Wert: -668