Hintergründe zum Programm und Implementierungshinweise
Diese Seite soll dem, der sich für
ausgewählte Teile der Realisierung des Programms, der internen
Darstellung, wie auch für Möglichkeiten der Realisierung eines
Simulators mit gleichem Aufbau für andere
Prozessoren interessiert, Einblick in die Hintergründe geben.
Das Programm wurde mit MS-VisualC++ 5.0 erstellt.
Die Klassen für die dockenden Sichten sind freie Software der Firma
Micro Focus Inc. und sind auf der
Homepage von
Mark Conway
zu finden. MRCEXT wurde unter der GNU General Public License veröffentlicht.
Interne Darstellung von Typdefinitionen
Die interne Darstellung von Datentypen orientiert sich an der
durch das OMF51-EXT Format vorgegebenen Struktur,
ist aber prinzipiell für jeden Prozessor geeignet.
Zunächst sind 11 verschiedene Grundtypen, d.h. die in C
eingebauten Datentypen mit einer Nummer definiert.
0 untyped 1 Bit 2 signed char 3 unsigned char 4 signed short 5 unsigned short 6 signed long 7 unsigned long 8 float (4 Byte) 9 Double (==float) 10 Codelabel 11 void 12 float-long (8-Byte) (nicht implementiert bei Keil-C51!)
Letztendlich führt jeder noch so
komplexe Datentyp im letzten Zweig auf einen dieser Typen.
Komplexe Datentypen
werden nun dadurch beschrieben, daß eine verkettete Liste von
verschiedenen Typdescriptoren aufgebaut wird.
Dazu sind weitere Typen definiert, die allerdings nicht den Typ
selbst, sondern seinen Aufbau beschreiben.
Definiert sind:
0x20 Listen Descriptor, beschreibt eine Liste von Elementen. 0x21 Pointer Descriptor, beschreibt einen Keil-C51- Generic Pointer
und ist deshalb nur in Verbindung mit diesem Compiler relevant.0x22 Array Descriptor, beschreibt Dimensionen und Größe jeder Dimension eines Arrays. 0x23 Function Descriptor, beschreibt ParameterListe und Rückgabewert einer Funktion. 0x24 Typ Descriptor, beschreibt den Namen eines definierten Typs. 0x25 Struktur Descriptor, beschreibt Größe und Anzahl der Elemente einer Struktur. 0x26 BitArray Descriptor 0x27 Spaced Pointer Descriptor, beschreibt einen Zeiger und den Speicherbereich, auf den er zeigt. 0x28 Generic Pointerdescriptor, beide Pointerdescriptoren sind in diese Form überführbar.
Also ein Beispiel:
Es sei eine solche C-Struktur definiert:
typedef struct { unsigned short a; signed long l; } test_t;
und eine Variable x: test_t x;
Folgende (einfache) Liste von Typdescriptoren wird nun aufgebaut:
1. StrukturDescriptor (Size=6) 2. Listdescriptor (2 Elemente) |--- Typ unsigned short (name="a",offset=0) |--- Typ signed long (name="l",offset=2)
Dabei wird jeder Typdescriptor,
mit Ausnahme der Standardtypen durch die Instanz einer Klasse (CTypdesc)
realisiert, die sich gegenseitig über Pointer referenzieren.
Der Listdescriptor enthält seinerseits eine Liste von
Zeigern auf seine Elemente. Ist der Wert der Referenz (des
Zeigers) kleiner als 0x1E, ist es der endgültige Typ,
ansonsten ein Zeiger auf den Descriptor des Typs. Diese
Unterscheidung ist möglich, da der Befehl new keinen
Speicher in diesem Adreßbereich 0-0x1E allokiert.
Es ist leicht vorzustellen, daß sich so auch kompliziert
verschachtelte Strukturen beschreiben lassen. Ein Sonderfall
sind Pointer, die rekursiv auf den einen Referenzdatentyp zeigen.
In diesem Fall ist die verkettete Liste in sich
geschlossen. Die Referenz zu einem bestimmten Symbol wird dadurch
hergestellt, daß die Verwaltungsstruktur des
Labels (labeldef_t) den Zeiger auf das erste Element der Liste
(hier also auf den Strukturdescriptor) zugewiesen bekommt.
Wurde nicht mit OBJEXT übersetzt, ist der Typ eines Label 0, d.h.
ohne Typinformation. Ein Typ kann dann, wie unter
Datentypen ohne "OBJECTEXTEND"
beschrieben, zugewiesen werden.
Aufbau der verketteten Liste für verschiedene Datentypen
Der Descriptor eines Elements kann seinerseits wieder eine der
folgenden Kombinationen enthalten.
Struktur:
-> Strukturdescriptor ->
Listdescriptor -> Descriptor für Element 0
-> Descriptor für Element 1
.
-> Descriptor für Element n
Array:
-> Arraydescriptor -> Descriptor der Arrayelemente
Pointer:
-> Pointerdescriptor -> Descriptor des Typs, auf den der
Zeiger weist
Die Belegung der einzelnen Elemente der Klasse
"CTypdesc" findet sich im Header "typdesc.h".
Die Klasse CProc und ihre Funktionen
Die Klasse CProc stellt eine virtuelle Basisklasse dar, die ein
möglichst universelles Interface zur Modellierung
verschiedenster Prozessoren realisiert. D.h. diese Klasse
deklariert eine Reihe von Methoden, die innerhalb einer
konkreten Klasse zu realisieren sind, stellt also das Gerüst zur
Einbindung verschiedener Prozessoren in die
Simulatoroberfläche zur Verfügung.
Natürlich besitzen die unterschiedlichen CPUs unterschiedliche
Spezialitäten, die sich einer generischen
Verallgemeinerung entziehen (z.B. die Speicherbereiche des 8051).
Es ist also immer eine Anpassungsarbeit bei der Einbindung eines
anderen Prozessors zu leisten, die nicht
nur in der Implementierung der durch CProc
geforderten Funktionen besteht. Der Aufwand wird aber durch diese
Strukturierung erheblich verringert. Beispielsweise Derivate der
8051-Familie lassen sich so sehr einfach
implementieren, da nur die Realisierung der Klasse CProc51 zu
ändern ist. Das Hauptprogramm enthält immer
eine Instanz der konkreten Prozessorklasse. (Also JSTEPApp besitzt
die Membervariable CProc51 p51).
Alle anderen Module referenzieren diese aber über den Header
"proc.h", der die virtuelle Basisklasse deklariert,
d.h. alle anderen Module kennen nur den
"Universalprozessor". Nicht alle Funktionen können und
müssen immer
unterstützt werden (z.B. Memorymapping, Breakpoints auf
Datenzugriffe). In diesem Fall müssen die Funktionen
dennoch als Dummy vorhanden sein und einen entsprechenden
Returncode liefern.
(Funktionen die unbedingt notwendig für die Funktion sind, werden
im Folgenden mit [M] gekennzeichnet.)
!! In der Version 1 wird Memorymapping auch von der Oberfläche
nicht unterstützt.
Funktionen und ihre Bedeutung
void Init(
)
Initialisiert
den Prozessor, d.h. löscht den Speicher und setzt alle Register
auf den Resetwert.
void
ResetCPU()
Setzt die simulierte CPU in den Resetzustand. Dieser sollte dem
Zustand nach einem Hardwarereset entsprechen.
ULONG
GetProgramCounter( ) [M]
Die Funktion liefert den aktuellen Stand des Programcounters als
Long.
BOOL GetPointer( | ||
ULONG* pointeraddr, | ||
ULONG* pointerval, | ||
CTypdesc* pointtyp=NULL, | ||
USHORT pointmem=0, | ||
ULONG* pointtomem=0 ) | [M] |
Die Funktion liefert den Wert
eines Zeigers und den durch den Zeiger referenzierten
Speicherbereich zurück. Die Funktion
ist sehr 8051-spezifisch, da andere Prozessoren in der Regel nicht
verschiedene Zeiger auf verschiedene Speicherbereiche
besitzen. In einem solchen Fall sind die letzten drei Parameter
nicht zu unterstützen.
*pointeraddr | Die Adresse des Zeigers.
Da die tatsächliche Adresse sich u.U. erst aus dem Lowteil und P2 ergibt, ist die Referenz auf eine Variable zu liefern, die dann in der Prozedur modifiziert wird. |
|
*pointerval | Die Referenz auf eine
Variable, in die der Wert des Zeigers eingetragen wird. |
|
pointtyp | Typ des Zeigers pointtyp->typ gibt die Art des Zeigers an. | |
pointmem | Der Speicherbereich, in dem der Zeiger steht. | |
pointomem | In diesen Parameter wird
der Speicherbereich eingetragen, auf den der Zeiger weist. |
Kann ein Zeiger nicht ermittelt
werden (z.b. ungültiger Speicherspezifizierer bei
Generic-Pointer), liefert die Funktion FALSE,
ansonsten TRUE zurück.
ULONG
GetMemSize(ULONG mempec=0) [M]
Liefert die Größe in Byte -1 des spezifizierten Speicherbereichs
zurück. Prozessoren, die nur über einen Speicher verfügen,
unterstützen den Parameter nicht. Die Konstanten für den 8051
sind ebenfalls in "proc.h" definiert.
int
GetMemAlignment() [M]
Liefert den Typ der Ablage von Werten im Speicher zurück. Diese
Angabe ist insbesondere für die Darstellung in allen Watch-
und Speicherwindows erforderlich.
0 = H/L
1 = L/H
int
ParseObjFile(CObjInfo* poi ,LPCSTR filename) [M]
Die Funktion wird beim Öffnen eines Absolutfiles aufgerufen. Als
Parameter werden der Zeiger auf die Klasse "CObjInfo"
und der
komplette Name und Pfad des Absolutfiles mitgegeben. Diese
Funktion hat die Aufgabe, das Absolutfile einzulesen, den
simulierten
Programmspeicher zu füllen, die Modul- und Prozedurdefinitionen
anzulegen und letztendlich die Symbole, Labels und
HLL-Zeileninformationen entsprechend anzulegen. Kann das
Absolutfile nicht richtig eingelesen werden, liefert die Funktion
-1, sonst 0 zurück. Ehe ein File eingelesen werden kann, muß der
zu belegende Speicherbereich allokiert sein, bzw. per Default
vorhanden sein. Die aufgetretenen Fehler sollten mit einer
MessageBox angezeigt werden.
BOOL
LoadHexfile(CObjInfo* poi ,CString& hexfileName)
Die
Funktion lädt ein Intel-Hexfile. Als Parameter werden der Zeiger auf die Klasse
"CObjInfo" und der
komplette Name und Pfad des Absolutfiles mitgegeben. Da das Hexfile keine
Symbolinformationen enthält, wird faktisch
nur der Code-Speicher gefüllt. Kann das Hexfile nicht
richtig eingelesen werden, liefert die Funktion -1, sonst 0 zurück.
Ehe ein File eingelesen werden kann, muß der zu belegende
Speicherbereich allokiert sein, bzw. per Default vorhanden sein.
Die aufgetretenen Fehler sollten mit einer MessageBox angezeigt
werden.
UINT
Reassemble( ULONG code, CString& ms, CObjInfo* pobj=NULL, int
modID=-1) [M]
Die Funktion wird aufgerufen, um ab dem aktuellen Programcounter
eine Anweisung zu reassemblieren.
Der gelieferte Text steht in ms. Damit auch Symbole
richtig dargestellt werden können, wird der Zeiger auf die
Objektinformation
bereitgestellt und auch die Modulkennung des Moduls, in dem zu
reassemblieren ist.
int
ExecNextCmd( ) [M]
Die Funktion simuliert den nächsten Assemblerbefehl ab dem
momentanen Programcounterstand. Wird der Befehl ausgeführt,
liefert die Funktion 0 zurück. Läuft die Funktion auf einen
Programm-Breakpoint, muß der Returnwert -1 zurückgegeben werden.
Läuft die Funktion auf einen Daten-Breakpoint, muß die Funktion
-2 zurückgeben. Nebenbei sind noch folgende Aufgaben zu
realisieren:
- | Ein Zähler, der mit "GetCycleCnt" abzufragen ist, ist je nach erforderlichen CPU-Zyklen zu erhöhen. |
- | Bei allen
"Call"-Aufrufen ist die Funktion
"AddToCallStackWnd" der Klasse CMainFrame mit
der Adresse des Call-Befehls aufzurufen. |
- | Bei allen
"RET(i)"-Aufrufen ist die Funktion
"RemoveFromCallStackWnd" der Klasse CMainFrame
mit der Rückkehradresse aufzurufen. |
- | Wenn
Tracelog eingeschaltet ist, sind entsprechend dem Tracemodus
der komplette Befehl mit Adresse und Registerinformation oder aber die getraceten Variablen in ein File namens temptrace.log zu schreiben. |
void
SetProgramCounter(ULONG pc)
Setzt den Programcounter auf die übergebene Adresse.
ULONG
GetStartUpAddress( )
[M]
Liefert die Startadresse nach einem Reset.
CRegInfo*
GetRegInfo( ) [M]
Die Simulation des Prozessors soll eine Klasse CRegInfo enthalten,
die die Informationen über die Register eines
Prozessors (Name, Adresse) enthält. Über diese Klasse wird vom
Registerwindow auf die einzelnen Register
zugegriffen. Die Klasse CRegInfo ist bei der Initialisierung
aufzubauen, indem für jedes Register ein Eintrag mit
"CRegInfo::AddReg" anzulegen ist (->
"reginfo.h"). Der Parameter ploc beim Aufruf
der Funktion ist ein Zeiger
auf ein Register. (Beim 8051 liegen die Register mit im Speicher.
Für andere Prozessoren müssen die Register
getrennt simuliert werden.)
ULONG
GetRegOffset(int index=0) [M]
Liefert bei Registern, deren Adresse nicht konstant ist
(Registerbank), den aktuell eingestellten Offset zurück.
BOOL
SetBreakpoint(ULONG addr, USHORT fmt) [M]
Setzt an der spezifizierten Adresse einen Breakpoint. Die
Formatangabe gibt an, ob es sich um einen permanenten
oder temporären Breakpoint im Programmcode, oder um einen
Lese/Schreib-Breakpoint in einem bestimmten
Speicherbereich handelt. Ist an der Adresse kein Speicher
vorhanden oder kann die Funktion nicht ausgeführt werden
weil das Format nicht unterstützt wird, liefert die Funktion
FALSE, sonst TRUE.
BOOL
RemoveBreakpoint(ULONG addr, USHORT fmt=0) [M]
Löscht den Breakpoint an der spezifizierten Adresse. Ist an der
Adresse kein Breakpoint gesetzt, liefert die Funktion
FALSE, sonst TRUE.
BOOL
IsBreakpointAtAddr(ULONG addr, USHORT fmt=0) [M]
Testet, ob sich an der spezifizierten Adresse ein Breakpoint
befindet, der der Formatspezifikation entspricht. Wenn ja,
liefert die Funktion TRUE. Ist fmt=0, dann wird nach nur nach
einem beliebigen Programm-Breakpoint an der Adresse
gesucht.
void
RestoreOpcode(ULONG addr)
Stellt den originalen OP-Code wieder her, wenn der
Programm-Breakpoint durch Setzen eines illegalen OP-Codes erzeugt
wurde. Der Breakpoint wird aber nicht gelöscht. Werden
Programm-Breakpoints anders realisiert, ist die Funktion nur als
Dummy zu implementieren bzw. anzupassen.
void
RestoreBkpt(ULONG addr)
Stellt an einer Adresse wieder einen Breakpoint durch Einfügen
eines illegalen OP-Codes her. Der Breakpoint selbst muß
schon existieren. Werden Programm-Breakpoints anders realisiert,
ist die Funktion nur als Dummy zu implementieren bzw.
anzupassen.
USHORT
GetBkptFormat(ULONG addr, USHORT fmt=0)
Liefert das Format des Breakpoints auf der angegebenen Adresse,
wenn vorhanden. Formate können sein:
BKPT_CODE BKPT_XDATA 0x0200 //(nur 8051!!) BKPT_DATA 0x0300 //(nur 8051!!) BKPT_IDATA 0x0700 //(nur 8051!!) BKPT_TMP 0x0800 BKPT_DISABLED TRACEPOINT RUNTIMEMPT
BOOL
ClrTempBkpt(ULONG addr)
Löscht, wenn vorhanden, einen temporären Breakpoint und setzt den
originalen OP-Code wieder ein. Wurde der temporäre
Breakpoint gefunden, ist der Rückgabewert TRUE.
bkpt_t*
GetNextBreakpoint(POSITION& pos) [M]
Listet alle vorhandenen Breakpoints. Der erste Zeiger wird bei pos=0
und von da an bis pos wieder =0 geliefert.
ULONG
GetStepOverAddr(ULONG addr) [M]
Liefert die Adresse des auf den aktuellen ASM-Befehl folgenden
Befehls. Diese Funktion wird für die ASM-StepOver Funktion
benötigt. Bei allen nicht "call"-Befehlen ist diese
Adresse gleich der übergebenen Adresse.
BOOL
IsReturn(ULONG addr)
[M]
Testet, ob an der spezifizierten Adresse ein RET oder RETI steht.
Wenn ja, Returnwert=TRUE.
BOOL
GetMemFromAddr(ULONG addr,ULONG* valp,ULONG memspec=0) [M]
Liefert ein Byte von der spezifizierten Speicheradresse im
angegebenen Speicherbereich in *valp. Wenn erfolgreich, ist der
Returnwert=TRUE. Ist an der Adresse kein Speicher vorhanden, ist
der Returnwert=FALSE.
BOOL
SetMemAtAddr(ULONG addr,ULONG* val, ULONG memspec=0) [M]
Setzt ein Byte an der angegebenen Adresse und liefert in *val den
alten Wert zurück. Ist die Funktion erfolgreich, ist der
Returnwert=TRUE. Ist an der Adresse kein Speicher vorhanden, ist
der Returnwert=FALSE.
BOOL
IsMemAtAddr(ULONG addr, ULONG memspec=0)
Testet, ob an der angegebenen Adresse im angegebenen
Speicherbereich Speicher vorhanden ist. Wenn ja, ist der
Returnwert=TRUE.
int
CreateMemInRange(ULONG startaddr, ULONG endaddr, ULONG memspec)
Legt für den Bereich Speicher der gewünschten Größe an. Ist
das nicht möglich, kann der Speicher nicht allokiert werden,
oder der simulierte Prozessor kann den Bereich gar nicht
adressieren, liefert die Funktion 0 zurück. Wird die Funktion
nicht unterstützt, ist der Rückgabewert = -1. Im anderen Fall
einen Index für den allokierten Bereich.
BOOL
DeleteMem(int memspec=-1, int index=-1)
Löscht den unter index angelegten Speicherbereich. Ist ein Index = -1,
werden alle Bereiche unter dem Speicherspezifizierer
aus dem Mapping gelöscht. Ist der Speicherspezifizierer -1,
werden unter dem Index alle Speicherbereiche aus dem
Mapping entfernt. Wenn erfolgreich, ist der Returnwert=TRUE,
sonst FALSE. Wird die Funktion nicht unterstützt, ist der
Rückgabewert = FALSE.
CPtrList*
GetMemMapping(ULONG memspec=0)
Liefert einen Zeiger auf eine Pointerliste, die alle allokierten
Bereiche des angegebenen Speicherbereichs enthält, zurück. Jedes
Element der Liste ist ein Zeiger auf eine Struktur mit den
Elementen Startadresse, Endadresse, Name des Speicherbereichs
und Zeiger auf den Anfang im PC-Speicher. Wird die Funktion
nicht unterstützt, ist der Rückgabewert = NULL.
ULONG
GetDestinationAddress(ULONG addr,ULONG memspec=0)
Liefert
die tatsächliche, physische Adresse einer Speicherzelle.
Normalerweise ist die logische = physische Adresse.
Ausnahme beim 8051 ist zum Beispiel der PDATA-Bereich, wo der
höherwertige Adreßteil durch das P2-Register bestimmt
wird.
ULONG
GetCyclCnt(BOOL delete=FALSE)
Liefert den aktuellen Stand des CPU-Zykluszählers und setzt
diesen zurück.
BOOL
EnableTraceLog(LPCSTR logfile=NULL,int tracetyp=0)
Wenn Enable=TRUE wird der Trace eingeschaltet. Das bedeutet, alle
Daten werden hexkodiert in ein File "logfile.log"geschrieben.
Ist Logfile=NULL wird der Trace abgeschaltet. Wird die Funktion
nicht unterstützt, ist der Rückgabewert = FALSE.
void
SetMeasurePoint(CMeasurePoint* pmpt)
Setzt einen Meßpunkt.
(-> Beschreibung Analysator-Fenster)
Der Parameter ist ein Zeiger auf die Klasse CMeasurePoint. Bei
Erreichen der Adresse, auf die der Meßpunkt gesetzt ist, sind die
entsprechenden Membervariablen innerhalb der Funktion
"ExecCmd" zu setzen. CMeasurePoint ist in
"objinfo.h" definiert.
void
SetStackWnd(void* ps)
Setzt
den Zeiger auf das Stackfenster. Damit können durch die
Simulation Stackoperationen in das Fenster eingetragen werden.
Der
Zeilenparser
Der Zeilenparser stellt dem Benutzer eine einzige Funktion
(Evaluate) zur Berechnung beliebiger C-Ausdrücke zur Verfügung.
Folgende Operatoren sind zulässig:
+,-,*,/,%,&,^,|,||,&&,[],(),~,->,.,>>,<<,>,>,==,sizeof
Zahlen können ganzzahlig dezimal, hexadezimal oder als Fließkommazahl eingegeben werden.
Das Argument
von sizeof muß zur Vereinfachung immer geklammert sein. Typecasts werden NICHT unterstützt! |
Als Übergabeparameter ist ein
Zeiger auf eine Struktur vom Typ eval_t mitzugeben. (->
parser.h)
Der Parameter "pexpression" ist ein nicht konstanter
Zeiger auf den nullterminierten String des Ausdrucks,
der zu berechnen ist. Die Auflösung des Ausdrucks erfolgt
folgendermaßen:
Jedem Operator ist eine Priorität zugeordnet.
Prinzipiell wird davon ausgegangen, daß sich der Ausdruck
immer in Unterausdrücke der Form:
Operand1-Operator1-Operand2-Operator2
zerlegen läßt.
In dieser Weise werden auch tief
verschachtelte Ausdrücke berechnet. Ein besonderes Problem
stellen dabei die
monadischen Operatoren dar. Sie haben immer eine höhere Priorität
als die diadischen Operatoren (ausgenommen Klammern).
Sie werden bei der Zerlegung eines
Ausdrucks dem Operanden zugeordnet und gleich
bei der Berechnung des Wertes des Operanden berücksichtigt.
Die Operatoren . und ->
zerlegen dagegen einen Operanden. Das ist deshalb erforderlich, da
z.B. Ausdrücke der Form:
Name[ i ].element berechnet werden müssen. Das geschieht dann in
folgender Weise:
Zur genaueren Angabe des
Gültigkeitsbereiches von Variablen können diese in der Form:
Modulname:Prozedurname:Variablename
angegeben werden.
Der Typ des Ergebnisses ist immer der des Operanden1, ausgenommen
boolsche Ausdrücke. Kann ein Ausdruck
evaluiert werden, liefert die Funktion den Rückgabewert=0.
Kann der Ausdruck nicht berechnet werden (z.B. Variable nicht
gefunden oder Typinformation paßt nicht),
liefert die Funktion -1 zurück. Sind die Klammerebenen nicht paarig,
ist der Rückgabewert = -2.
Einlesen eines Absolutfiles und
interne Darstellung von Symbolen
Das Absolutfile liefert sog. Debug-Records. Diese beschreiben
Symbole, die entweder Labels, Variablen oder
HLL-Zeilen identifizieren. Die symbolischen HLL-Adressen werden
vom Compiler selbst erzeugt. Sie werden
benötigt, um den C-Code dem Assemblercode zuzuordnen. Um den
Aufwand bei der Suche nach Variablen
und Labels zu verringern, werden die Zeileninformationen in
getrennten Listen verwaltet.
Für jedes Label, Variable wird eine Struktur labeldef_t
angelegt und in einer Zeigerliste verwaltet. Die Struktur
(definiert in objinfo.h) enthält folgende Angaben über das
Symbol:
Der Datentyp ist entweder ein Standardtyp wie
oben beschrieben,
oder aber ein Zeiger auf einen Typdescriptor.
Zum schnellen Auffinden der Labeldescriptoren sind
diese in einer Hash-Tabelle abgelegt, deren Key der
Labelname ist. Die Zeileninformationen werden ebenso
in einer Hash-Tabelle verwaltet, deren Key die Adresse
der HLL-Zeile ist.
Eine solche Tabelle wird für jedes Modul angelegt.
Die Daten der Klasse CObjinfo sind prinzipiell so strukturiert:
CObjinfo
|
|- Liste der geladenen Module
| |
| |- Modul 0
| | |
| | |- Modulname
| | |- Module-ID
| | |- Pfad zum Sourcefile
| | |- niedrigste Adresse
| | |- höchste Adresse
| | |- Liste der zum Modul gehörigen Prozeduren
| | | |
| | | |- Prozedurdefinition 0
| | | | |
| | | | |- Prozedurname
| | | | |- Startadresse
| | | | |- Endadresse
| | | :
| | | |- Prozedurdefinition n
| | |
| | |
| | |- Hashtable HLL-Zeilen (Key=Adresse)
| :
| |- Modul n
|
|
|- Hashtabelle Labels (Key=Name)
|
|- Labelliste1
| |- Labelinstanz 0
| | |
| | |- labelname
| | |- Adresse
| | |- Speicherbereich
| | |- Gültigkeitsbereich
| | |- Modul
| | |- Prozedur
| :
| |- Labelinstanz n
:
|- Labelliste n
Die Klasse CObjinfo stellt einige
Funktionen zum Anlegen der notwendigen Verwaltungsinformationen für
Labels, HLL-Zeilen, Module und Prozeduren bereit.
int
AddModuleInfo( LPCSTR modname=NULL, LPCSTR modpath=NULL)
Diese Funktion legt die Struktur für ein neues Modul an und
liefert eine ID für das angelegte Modul zurück.
(Den Zeiger auf das Modul erhält man über
objinfo.modules.GetAt(ID) ). Eine Prozedur wird in das Modul
mit der Funktion CModDef.AddProc eingetragen.
Modulname ist der aus dem Absolutfile gelesene Modulbezeichner.
Der Pfad zum Quellfile muß nicht angegeben werden, wenn das Quellfile
im gleichen Pfad wie das Absolutfile steht.
int
AddDbgInfo (ULONG addr,int type,char* label,int
modid=-1,CProcDef* pproc=NULL,CTypdesc* ptd=NULL)
Die Funktion fügt der Labeltabelle ein neues Symbol hinzu.
addr Die Adresse des Symbols
wird gebildet durch die Addition von Speicherbereich (8051-spezifisch) und Gültigkeitsbereich des Symbols.
Der Gültigkeitsbereich kann folgende Werte annehmen:
Local 0 Public 1 Segment 2 Der Speicherbereich kann folgende Werte annehmen:
typ
0x0100 CODE (default) 0x0200 XADTA 0x0400 DATA 0x0800 IDATA 0x1000 BIT 0x2000 NUMBER 0x4000 Segmentlabel 0x8000 PDATA label Der Name des Labels. modID Die ID des Moduls, in dem das Symbol definiert ist. pproc Zeiger auf die Prozedurverwaltungsstruktur, in der das
Symbol definiert ist.ptd Entweder ein Standardtyp oder ein Zeiger auf einen Typdescriptor.
Enthält das Absolutfile keine Typinformationen, kann hier ein Typ
eingetragen werden. Das Symbol wird dann im Watchfenster mit
diesem Typ angezeigt. (Empfohlen: der int-Typ des Prozessors.)
int
AddDbgInfo(ULONG addr, int lineno, int modid)
Die Funktion trägt in die HLL-Zeilentabelle des Moduls die
Zeilennummer ein. Damit kann unter Angabe einer Adresse
erkannt werden, ob zur Adresse eine entsprechende HLL-Zeile gehört.
UCHAR*
GetOMFRec(CFile* fp)
Die Funktion liefert einen Record eines unter fp geöffneten
Absolutfiles zurück. Funktioniert natürlich nur bei Files,
in denen die Records in der Form
Recordtyp(16)-Recordlänge(16)-Daten(x)-Checksumme(8) abgelegt sind.
Liefert die Funktion 0 zurück, ist das Ende des Files erreicht.