Tabellen (Assembler)

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