|
|
C-KURS WS 01/02 - Übungen2 - AufgabenAUTHOR: Gerd Döben-Henisch DATE OF FIRST GENERATION: Oct-10, 2001 DATE OF LAST CHANGE: Oct-10, 2001 EMAIL: Gerd Döben-Henisch |
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. |
|
|
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. |
|
|
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.
|
|
|
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 |
|
|
Die Syntax zum Assemblerprogramm
Hier die Ableitung des obigen Assemblerprograms aus der Syntax (fett repräsentiert terminale Symbole):
|
|
|
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:
Vom Ansatz her wird vermutet, dass folgende Datenstrukturen auf jeden Fall benötigt werden:
Ob
diese Annahmen hinreichend sind und wie die Operationen im
einzelnen aussehen, wird in den nächsten Schritten erarbeitet
werden. |
|
|
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.
|
|
|
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;
}