Einführung
Tabellen (Tables) stellen eine effektive Möglichkeit dar, Werte im Speicher abzulegen. Es existieren 2 Arten um Tabellen in Assembler zu erstellen:
Tabellen im Data-Segment
Tabellen, die im Data-Segment angelegt werden, werden bei jedem Programmstart erneut im „flüchtigen Speicher“ (Ram) abgelegt. Dabei sind die Werte, die in den Tabellen gespeichert werden von Anfang an variabel, da die Tabellen erst nach dem Programmstart mit Werten befüllt werden können und so jederzeit verändert werden können.
Tabellen im Code-Segment
Tabellen, die im Code-Segment gespeichert werden, sind fest im Code-Speicher abgelegt und nicht „flüchtig“. Um auf die Daten zuzugreifen, benötigt man einen Datenpointer (dptr). Außerdem enthalten die Tabellen ab Programmstart immer die selben Daten und sind nicht veränderbar! Man ist also während der Programmlaufzeit nur in der Lage Werte auszulesen.
Wann verwende ich welche Tabellen-Art?
Im Normalfall hängt dies von der Aufgabenstellung ab. Es muss natürlich immer genügend Speicherplatz für die Tabellen zur Verfügung stehen. In der Regel wird eine Tabelle, deren Werte sich während der Laufzeit nicht ändern sollen im Code-Segment abgelegt und umgekehrt.
Anwendung
Tabellen im Data-Segment
Um eine Tabelle im Data-Segment anzulegen, muss man sich zuerst einen freien Adressbereich suchen. Anschließend benötigt man einen Tabellennamen (Label) und die Anzahl der benötigten Datensegmente.
In diesem Beispiel beginne ich mit der Adresse 0x30, dem Label „dTabel“ und 3 benötigten Segmenten:
dseg at 0x30 dTabel: ds 3
Danach kann man im Code-Segment mithilfe eines Registers als Referenz Daten in der Tabelle speichern:
cseg at 0x0000 mov r0,#dTable mov @r0,#1 inc r0 mov @r0,#2 inc r0 mov @r0,#3
Die Tabelle ist nun befüllt mit den Werten 1, 2 und 3. Genauer gesagt befindet sich an der Adresse…
- 0x30 der Wert 1
- 0x31 der Wert 2
- 0x32 der Wert 3
Zu Beginn weist man r0 die Tabelle „dTable“ zu, was bedeutet, dass r0 nun auf die Adresse 0x30 verweist, welche man im Data-Segment beschrieben hat. Im nächsten Schritt beschreibt man dann auch schon die Adresse 0x30 mit dem ersten Wert. Über den Befehl inc inkrementiert man seine Referenz, sodass sie wiederum im nächsten Schritt auf die Adresse 0x31 verweist.
Zu guter Letzt lassen sich natürlich auch Daten aus der Tabelle abfragen. Dazu speichert man zuerst die Adresse im Akkumulator ab, von der man den Wert abfragen möchte. Anschließend kommt r0 wieder als Referenz zum Einsatz um mittels @r0 auf den Wert zuzugreifen.
mov a,#dTable add a,#0x02 mov r0,a mov a,@r0
Tabellen im Code-Segment
Tabellen im Code-Segment lassen sich relativ schnell anlegen und mit Werten befüllen. Dazu sucht man sich zuerst wieder einen freien Adressbereich und einen Tabellennamen (Label). Diesmal im Code-Segment natürlich. Anschließend lassen sich auch schon Werte zuweisen.
Diesmal beginne ich bei der Adresse 0x0300 und mit dem Tabellennamen „cTable„:
cseg at 0x0300 cTable: db 1 db 2 db 3
Mittels db spreche ich eine Adresse an und weise ihr den darauf folgenden Wert zu.
Als nächstes ist man schon in der Lage, die Daten während des Programmablaufs abzufragen:
cseg at 0x0000 mov dptr,#cTable mov a,#0x02 movc a,@a+dptr
Diesmal verweist der Datenpointer (dptr) auf die Adresse der Tabelle, da sie sich im Code-Segment befindet. In den Akkumulator lädt man den Index, den man später abfragen möchte. In diesem Fall wird dem Akkumulator der Wert 2 zugewiesen. Der letzte Befehl lautet movc! Hier muss eindeutig zwischen movc und mov unterschieden werden. Denn nur mit ersterem ist es möglich, im Code-Segment hinterlegte Tabellen-Werte abzurufen.
Zusammenfassung
Tabellen im Data-Segment
- werden im flüchtigen Speicher abgelegt
- können jederzeit verändert werden
Tabellen im Code-Segment
- werden im Code-Segment abgelegt
- können nicht verändert werden
- Datenpointer benötigt
Quelltext-Beispiel
Das folgende (kommentierte) Quelltext-Beispiel legt die Werte ‚H‘, 3 und 0x04 in einer Tabelle im Data-Segment ab der Adresse 0x40 ab:
$NOMOD51 //Vordefinitionen vergessen #include <at89c5131.h> //Neue Definitionen einbinden dseg at 0x40 //Nachfolgende Befehle betreffen das Data-Segment ab Adresse 0x40 dTabel: //Tabellenname ds 3 //3 Data-Segmente reserviert cseg at 0x0000 mov r0,#dTable //r0 als Referenz für dTable mov @r0,#'H' //'H' wird in die Tabelle geschrieben (Adresse 0x40) inc r0 //Referenz wird erhöht mov @r0,#3 //3 wird in die Tabelle geschrieben (Adresse 0x41) inc r0 //Referenz wird erhöht mov @r0,#0x04 //0x04 wird in die Tabelle geschrieben (Adresse 0x42) end
Im nächsten (kommentierten) Quelltext-Beispiel sind die Werte ‚H‘, ‚E‘, ‚L‘, ‚L‘, ‚O‘ in einer Tabelle im Code-Segment ab der Adresse 0x0350 abgelegt. Der Wert ‚O‘ wird im Folgenden abgefragt und im Akkumulator gespeichert:
$NOMOD51 //Vordefinitionen vergessen #include <at89c5131.h> //Neue Definitionen einbinden cseg at 0x0350 //Nachfolgende Befehle betreffen das Code-Segment ab Adresse 0x0350 cTable: //Tabellenname db 'H' //'H' wird in der Tabelle gespeichert db 'E' //'E' wird in der Tabelle gespeichert db 'L' //'L' wird in der Tabelle gespeichert db 'L' //'L' wird in der Tabelle gespeichert db 'O' //'O' wird in der Tabelle gespeichert cseg at 0x0000 mov dptr,#cTable //Datenpointer als Referenz fuer cTable mov a,#0x04 //Wert 4 im Akkumulator speichern movc a,@a+dptr //den Inhalt aus Akkumulator + dptr (0x04 + 0x0350 = 0x0354) in den Akkumulator laden end