1. Einführung

  2. Das Problem

  3. Verfügbare Hardware

  4. Steuerprogramm in NSD-Darstellung

  5. Das Steuerprogramm als Asemblerprogramm

  6. Syntax der Assembler-Sprache

  7. Ein C-Simulator für den Mikroprozessor

  8. Der C-Simulator in NSD-Darstellung

  9. Der C-Sourcecode des C-Simulators


C-KURS WS 01/02 - Übungen2 - Aufgaben


AUTHOR: Gerd Döben-Henisch
DATE OF FIRST GENERATION: Oct-10, 2001
DATE OF LAST CHANGE: Oct-10, 2001
EMAIL:
Gerd Döben-Henisch

Einführung

Die Übung zur zweiten Vorlesung ist als Mini-Projekt konzipiert, im Rahmen dessen alle Themen der Vorlesung einmal behandelt werden. Ausgehend von einer Problemstellung wird ein Lösungsszenario vorgegeben, das dann mit den in der zweiten Vorlesung genannten Mitteln bearbeitet werden soll.

START


Das Problem

Eine Maschine entwickelt während ihres Betriebes Wärme, die einen bestimmten Schwellwert nicht überschreiten soll. Es wird ein Kontroller gesucht, der innerhab eines bestimmten Zeitintervalls die maximale Temperatur während der Temperaturschwankungen speichert.

START






Verfügbare Hardware

Das zuvor genannte Problem soll mit einem Mikroprozessor (CPU) gelöst werden, desen Hardwarearchitektur nebenstehend abgebildet ist.

Über Adressleitungen kann der Mikroprozessor bestimmte Speicherzellen in einem RAM adressieren. Mittels Datenleitungen kann er Inhalte von speicherzellen lesen oder schreiben.

Gelesene Inhalte von Speicherzellen kann der Mikroprozessor in zwei internen Datenregistern DR1 und DR2 zwischenspeichern.

Die aktuell zu bearbeitende Adresse für das auszuführende Befehlswort wird im internen Adressregister AR gespeichert.

Bit 0 des Statusregisters SR repräsentiert das Ergebnis von Vergleichsoperationen. Wenn A < B dann ist SR-Bit0 = 0, sonst 1.

Bit 1 des Statusregisters SR repräsentiert eine Flag zum Beenden des Programms: Wenn SR-Bit1 = 0, dann weitermachen, andernfalls stoppen.


START


HW


Ein kleines Steuerprogramm in NSD-Darstellung

Dieses kleine Programm ist auf die wesentlichen Elemente reduziert: die Datenübernahme durch einen Sensor wird hier im Programm selbst 'simuliert', d.h. es wird einfach ein Wert gesetzt. Dann werden die beiden wichtigen Temperatuwerte gelesen und verglichen. Wichtig ist hier, dass der Vergleich selbst noch keine Verzweigung beinhaltet! Es wird nur das Bit-0 im Statusregister auf 1 gesetzt, wenn das letzte Maximum nicht kleiner ist als die aktuelle Temperatur. Nachdem das Bit-0 im Statusregister gesetzt worden ist (oder auch nicht), gibt es eine Verzweigungsabfrage: Ist Bit0 = 0 dann passiert garnichts (NOP := NO OPERATION). Ist Bit0 = 1 dann wird ein Unterprogramm aufgerufen, das das neue Maximum übernimt.

Interessantes Detail: Die NSD-Darstellung wurde eigentlich konzipiert, um Sprünge ('jumps', 'gotos') zu verhindern. Auf der Ebene von Assemblerprogrammen gibt es aber garkeine Konstrukte, mit denen man direkt Schleifen ('loops') realisieren könnte. Stattdessen gibt es Sprünge mit symbolischen Namen zu Adressen, die Unterprogramme enthalten. Man kann die symbolischen Namen daher auffassen als Namen von Unterprogrammen bzw. als Namen für Befehle.

START


NSD-ASM




Das Steuerprogramm als Assemblerprogramm

$AKTTEMP: 5

$OLDMAX: 3

MOVE $OLDMAX DR1

MOVE $AKTTEMP DR2

CMP DR1 DR2

IF SR0 = 0 $CHANGE

EXIT

$CHANGE: MOVE $AKTTEMP $OLDMAX

EXIT



START






Die Syntax zum Assemblerprogramm

  1. S -> [LAB] COM

    -> S [LAB] COM

  2. COM -> COM_E | COM_MV | COM_CM | COM_IF | COM_DT

  3. COM_E -> EXIT

  4. COM_MV -> MOVE XWORD

  5. XWORD -> DRY COMDTSIMPLE

    -> COMDTSIMPLE DRY

    -> COMDTSIMPLE COMDTSIMPLE

  6. COMDTSIMPLE -> $ALPHA

  7. ALPHA -> +[A, ...,Z]+[A, ...,Z]+[A, ...,Z]+[A, ...,Z]+[A, ...,Z]+[A, ...,Z]+[A, ...,Z]+[A, ...,Z]

  8. COM_CM -> CMP DRY DRZ

  9. COM_IF -> IF SRW COMDTSIMPLE

  10. COM_DT -> LAB DIGIT

  11. DIGIT -> *{0, ...,9}

  12. LAB -> $ALPHA:

  13. Y -> 1 | 2

  14. Z -> 1 | 2

  15. W -> 0 | 1



Hier die Ableitung des obigen Assemblerprograms aus der Syntax (fett repräsentiert terminale Symbole):

  1. S

  2. S COM

  3. S LAB COM COM

  4. S COM LAB COM COM

  5. S COM COM LAB COM COM

  6. S COM COM COM LAB COM COM

  7. S COM COM COM COM LAB COM COM

  8. S COM COM COM COM COM LAB COM COM

  9. S COM COM COM COM COM COM LAB COM COM

  10. COM COM COM COM COM COM COM LAB COM COM

  11. COM_DT COM_DT COM_MV COM_MV COM_CM COM_IF COM_E LAB COM_MV COM_E

  12. LAB DIGIT LAB DIGIT MOVE XWORD MOVE XWORD CMP DRY DRZ IF SRW COMDTSIMPLE EXIT $ALPHA: MOVE XWORD EXIT

  13. $ALPHA: *{0, ...,9} $ALPHA: *{0, ...,9} MOVE COMDTSIMPLE DRY MOVE COMDTSIMPLE DRY CMP DR1 DR2 IF SR0 $ALPHA EXIT $CHANGE: MOVE COMDTSIMPLE COMDTSIMPLE EXIT

  14. $AKTTEMP: 5 $OLDMAX: 3 MOVE $ALPHA DRY MOVE $ALPHA DRY CMP DR1 DR2 IF SR0 $CHANGE EXIT $CHANGE: MOVE $ALPHA $ALPHA EXIT

  15. $AKTTEMP: 5 $OLDMAX: 3 MOVE $OLDMAX DR1 MOVE $AKTTEMP DR2 CMP DR1 DR2 IF SR0 $CHANGE EXIT $CHANGE: MOVE $AKTTEMP $OLDMAX EXIT





START






Ein C-Programm als Simulator für den Mikroprozessor

Es soll jetzt ein C-Programm entwickelt werden, das den oben vorgestellten Mikroprozessor mit dieser Assemblersprache simulieren kann.

Es wird angenommen, dass die Programme,die simuliert werden sollen, als Dateien mit der Endung .asm vorliegen.

Der Simulator wird zu einem ausführbaren Befehl mit Namen asmsim gemacht, dem bei Aufruf auf der Kommandozeile zwei Argumente übergeben werden können.

asmsim arg1 arg2

arg1 ist der Name der Programmdatei die simuliert werden soll, und arg2 ist ein DEBUG-Flag, das besagt, ob das Programm im DEBUG-Modus laufen soll oder nicht.

Die grundsätzliche Aufgabenstellung für den C-Simulator ist klar: Lese die Programmdatei ein und Simuliere die Ausführung. Zwei Fragen müssen beantwortet werden:

  1. Welche Datenstrukturen werden benötigt?

  2. Welche Operationen auf diesen Daten werden benötigt?

Vom Ansatz her wird vermutet, dass folgende Datenstrukturen auf jeden Fall benötigt werden:

  1. Eine Repräsentation des RAM als eine Folge adressierbarer Speicherzellen zum Lesen und schreiben von Zahlen und Buchstaben.

  2. Die Register DR1, DR2 sowie AR für Zahlen und Adressen.

  3. Die Flags SR0 (Ergebnis CMP-Operation), SR1(Ergebnis EXIT-operation) und SR3 (DEBUG-Flag).

  4. Aufgrund der Verwendung von symbolischen Adressen wird man auch eine Symboltafel benötigen, die Symbole mit Adressen verknüpft.

Ob diese Annahmen hinreichend sind und wie die Operationen im einzelnen aussehen, wird in den nächsten Schritten erarbeitet werden.

START






Der C-Simulator in NSD-Darstellung

Wir beginnen die NSD-Darstellung auf der obersten Ebene mit zwei Aktionsblöcken.

Als erstes soll geklärt werden, wie das Einlesen des Programms funktionieren kann.

Nimmt man an, dass es im C-Simulaor eine Variable RAM gibt, die eine Folge von adressierbaren Speicherzellen darstellt so, dass man jedem Zeichen des Programmtextes genau eine Speicherzelle zuordnen könnte, dann könnte man den Programmtext zunächst 1-zu-1 übertragen.

In der Sprache C gibt es das Konstrukt des Arrays und des Pointers, um Folgen von Speicherzellen zu adressieren. Arrays werden intern in C über Pointer realisiert und sind nicht ganz so flexibel wie Pointer, sollen hier aber als Datentyp für das RAM benutzt werden. Um zu sagen, welches Element in einem Array aktuell adressiert ist, benutzt man eine INDEX, der die Nummer des Elementes bezeichnet, auf das man gerade zugreifen will. Der niedrigste INDEX ist "0"! Ein Array mit 3 Elementen hat z.B. die Indizes aus dem intervall [0,2].

RAM[3] würde also das Element mit dem INDEX 3 repräsentieren, das das vierte Element ab 0 wäre.

Ein Problem besteht darin, dass C keine dynamischen Arrays kennt (es gibt Möglichkeiten, mit dynamischen Konstrukten zu arbeiten, diese sollen hier aber noch nicht behandelt werden). Dies bedeutet, dass man im Vorhinein die maximale Grösse des Programmes kennen muss bzw. die Programme dürfen bei der Erstellung eine bestimmte Grösse nicht überschreiten. Will man hier auf Nummer sicher gehen, dann muss man beim Einlesen der Datei die maximale Länge über eine Konstante PRG_MAXLENGTH kontrollieren.

EOF ist ein negativer Wert, der erzeugt werden soll, wenn die Funktion, die eine Datei liest, das Ende der Datei erreicht hat. Man kann diesen Wert also benutzen, um das Erreichen des Dateiendes zu erkennen.


Wenn die Datei eingelesen ist, dann kann der Simulator seine Arbeit beginnen.

Das Adressregister AR muss zu Beginn auf den ersten Befehl des Programms eingestellt sein. Dann kann der Simulator anhand des Inhalts des Adressregisters AR die erste Speicherzelle finden, ab der der nächste Befehl abgespeichert ist. Dies bedeutet, dass im Rahmen der Initialisierung der Register das erste Vorkommnis eines Befehls gesucht werden muss.

An dieser Stelle muss man sich auch entscheiden, ob man einmal alle Symbole durchscannt und eine feste Symboltabelle anlegt oder ob man von Fall zu Fall die Adresse ermittelt. Eine feste Symboltabelle ist auf jeden Fall effizienter.




START


SIMULATOR-TOPLEVEL

SIMULATOR-EINLESEN

SIMULATOR

Der Source-Code zum C-Simulator

(Noch unvollständig)

(Die Formatierung unter HTML ist leider nicht sehr gut)

/****************************

*

* csim.c

*

* main-Datei für den C-SIMULATOR

*

* It is assumed that

* arg1 = filename {___.asm}

* arg2 = DEBUG-Flag {0,1}

*

* BEGINN: 10-SEPT-2001

* LAST CHANGES: 12-SEPT-2001

*

* AUTOR: Gerd Doeben-Henisch

*

*********************************/



#include <stdio.h>

#include <errno.h>

#define PRG_MAX_LENGTH 1000



extern FILE *open_file(const char *);

extern int close_file(FILE *);



int main(int argc, char *argv[])

{

/********************/

/* VARIABLEN */

/********************/

int INDEX, PRG_ACT_LENGTH;

char c;

char (RAM)[PRG_MAX_LENGTH];

char *filename;

FILE *fh;

/***********************/

/* PRGOGRAMM EINLESEN */

filename = argv[1];

printf("FILENAME = %s\n",filename);

printf("\n----------------------------------\n");

fh = open_file(filename);

if (fh == NULL){

fprintf(stderr," %s "," ERROR BEIM EINLESEN DER DATEI ");

close_file(fh);

}

else{

INDEX=0;

c = fgetc(fh);

while (c != EOF){

RAM[INDEX]=c;

printf("%c",RAM[INDEX]);

INDEX++;

if (INDEX > PRG_MAX_LENGTH) { printf("ERROR: PROGRAMM ZU LANGE\n"); break; }

c = fgetc(fh);

} /*while*/

printf("\n----------------------------------\n");

printf("\nKONTROLLAUSDRUCK RAM :\n");

PRG_ACT_LENGTH = INDEX;

INDEX=0;

while (INDEX < PRG_ACT_LENGTH ){

printf("%c",RAM[INDEX]);

INDEX++;

}

printf("\n----------------------------------\n");

}/* else */

/***************/

/* SIMULATION */

/***************/

return 0;

}


/****************************

*

* files.c

*

* Funktionen zum Öfffnen und Schliessen von Dateien

*

* BEGINN: 10-SEPT-2001

* LAST CHANGES: Oct-10, 2001

*

*********************************/



#include <stdio.h>

#include <errno.h>

/******************************+

*

* open_file.c

*

* ***************************/

FILE *open_file(const char *filename)

/* Open filename for input; return NULL if problem */

{

FILE *f;

errno = 0;

if (filename == NULL) filename = "\0";

f = fopen(filename, "r");

if (f == NULL )

fprintf(stderr, "open_file(\"%s\") failed: %s\n", filename, strerror(errno));

return f;

}

/****************************************

*

* close_file.c

*

******************************************/

int close_file(FILE *f)

{

int s = 0;

if (f == NULL) return 0;

errno =0;

s = fclose(f);

if (s == EOF) perror("Close failed");

return s;

}


START