TI2-Tutorium, 17.04.2013

Orga-Krams

Ich

TI2 bestehen

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

Übungszettel

  • Gibt es im Sakai
  • Wöchentliche Ausgabe der Übungszettel
  • 14 Tage Zeit zum Bearbeiten
  • 1 bewertete Aufgabe
    • Bewertung: bestanden / nicht bestanden – keine Punkte
  • Abgabe
    • nur in 2er-Gruppen (bei ungerader Anzahl ist eine 3er-Gruppe möglich)
    • Ausgedruckt (!) bis Freitags 10 Uhr in mein Fach (1. Stock, linke Reihe)
    • Programmieraufgaben zusätzlich per E-Mail mit Betreff: [TI2] Übung 4, Peter Lustig, Armin Maiwald
    • Abgabe per GIT-Repository o.ä. statt E-Mail nach Absprache möglich
  • Abgabe des ersten Übungsblatts am 26.04.2013

Tutorien

  • keine Wechsel der Tutorien
  • keine Tutoriumsübergreifenden Übungsgruppen

Vorlesung

Allgemein

  • Vorlesung befasst sich mit dem theoretischen Architektur eines Computers
  • Kein Assembler in der Vorlesung
  • Vorlesung: 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
e8c8feffffbf00000000e8aefeffff

Maschinenbefehle

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

1
2
3
4
5
6
0xba 0x0e 0x00 0x00 0x00 | mov edx, 0xe
0xbe 0xf4 0x05 0x40 0x00 | mov esi, 0x4005f4
0xbf 0x01 0x00 0x00 0x00 | mov edi, 0x1
0xe8 0xc8 0xfe 0xff 0xff | call dword 0x420
0xbf 0x00 0x00 0x00 0x00 | mov edi, 0x0
0xe8 0xae 0xfe 0xff 0xff | call dword 0x410

Allgemeines

Installation testen

  • Zum Testen, ob nasm funktioniert könnt ihr diese Zip-Datei1 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>

int foo(int, int, int);

int main()
{
    int i = foo(42,1337,2047);
    printf("i hat den Wert: %d",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: 4195588

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

  1. Original von Dimitri Schachmann – leicht modifiziert von mir.