MCS-51
Dyrektywty asemblerowe

Definiowanie segmentów

Segment jest blokiem pamięci programu lub danych, który tworzy asembler z kodu lub danych znajdujących się w pliku źródłowym. Użycie poszczególnych segmentów w pliku źródłowym jest zależne od aplikacji. Mikrokontrolery rodziny MCS-51 mają kilka różnych, specyficznych dla siebie, przestrzeni pamięci, jak:

  • pamięć kodu programu,
  • wewnętrzna pamięć danych adresowana bitowo, bezpośrednio lub pośrednio,
  • zewnętrzna pamięć danych.

Segmenty są więc używane do umiejscowienia kodu programu, stałych i zmiennych w wymienionych przestrzeniach.

Uwaga Należy pamiętać, że większość dyrektyw nie może być umieszczana w dowolnym miejscu kodu źródłowego, gdyż wiąże się z określonym segmentem pamięci. Na przykład rezerwacja bloku pamięci danych za pomocą dyrektywy DS nie może być wykonana w segmencie kodu. Podobnie instrukcje asemblerowe nie mogą być umieszczane np. w segmencie danych.

Licznik położenia

Podczas swojej pracy program asemblujący (asembler) przyporządkowuje każdemu segmentowi tzw. licznik położenia (ang. location counter), który jest wskaźnikiem adresu w przestrzeni aktywnego segmentu. Adres ten jest adresem względnym dla segmentu relokowalnego lub adresem bezwzględnym (rzeczywistym) dla segmentu absolutnego. Kiedy dany segment jest po raz pierwszy aktywowany w wyniku napotkania przez asembler odpowiedniej dyrektywy w pliku źródłowym, jego licznik położenia jest ustawiany na 0. Wartość tego licznika jest zmieniana po przeanalizowaniu każdej instrukcji o wartość związaną z jej implementacją w danym segmencie. Dyrektywy inicjalizacji i rezerwacji pamięci (np. DS, DB lub DBIT) zmieniają wartość licznika położenia po przydzieleniu pamięci przez tą dyrektywę. Dyrektywa ORG ustala nową wartość dla licznika położenia. Jeśli nastąpi zmiana aktywnego segment na inny, a potem powrót do niego, to licznik położenia przyjmuje taką wartość, jaką miał ostatnio przy korzystaniu z tego segmentu. Zawsze, kiedy asembler napotka etykietę, przypisuje jej bieżącą wartość licznika położenia i typ bieżącego segmentu.

Znak $ odpowiada wartość licznika położenia w aktywnym segmencie. Używając symbolu $ należy pamiętać, że jego wartość zmienia się z każdą instrukcją, lecz tylko po tym, jak instrukcja ta zostanie całkowicie przeanalizowana przez asembler. Jeśli użyje się znaku $ w miejscu operandu instrukcji lub dyrektywy, to reprezentuje on wtedy adres pierwszego bajta tej instrukcji.

Segmenty relokowalne

Segmenty relokowalne mają swoją nazwę, klasę jak również inne atrybuty. Segmenty relokowalne o tej samej nazwie lecz umieszczone w różnych modułach obiektowych są uważane jako części tego samego segmentu i są nazywane segmentami częściowymi. Ich łącznie przeprowadzane jest podczas linkowania przez program linkera.

Segmenty relokowalne są tworzone przy użyciu dyrektywy SEGMENT, w której trzeba określić nazwę segmentu oraz jego klasę. Na przykład

MOJPROG   SEGMENT   CODE

definiuje segment o nazwie MOJPROG w klasie pamięci CODE. Oznacza to, ze wszystko, co pojawi się w tym segmencie będzie umieszczone w pamięci programu.

Po definicji nazwy segmentu relokowalnego należy go wybrać wykorzystując dyrektywę RSEG. Użycie dyrektywu RSEG powoduje, że wybrany segment staje się dla programu asemblującego segmentem aktywnym, nazywanym inaczej segmentem bieżącym, do momentu, aż nie zostanie wybrany przez omawianą dyrektywę lub przez dyrektywę określającą segment absolutny, inny segment. Na przykład:

          RSEG   MOJPROG

wybiera segment MOJPROG zdefiniowany powyżej.

SEGMENT

Implementacja: Keil

Dyrektywa SEGMENT jest używana do deklaracji segmentów relokowalnych (adres absolutny ustalany jest dopiero na poziomie linkowania). Dyrektywa ta ma następujący format:

segment SEGMENT klasa
  • segment jest nazwą symboliczną przypisaną do segmentu. Nazwa ta jest następnie wykorzystywana w dyrektywie RSEG. Nazwa symboliczna segmentu może być również wykorzystywana w wyrażeniach do reprezentowania adresu bazowego połączonych segmentów, który zostanie obliczony przez linker.
  • klasa jest klasą pamięci, w jakiej ma być ulokowany segment (patrz tabela 2.1).

Nazwa symboliczna każdego segmentu w module musi być unikalna. W procesie linkowania łączone są segmenty tego samego typu. Nazwa segmentu, jeśli jest związana z tą samą klasą segmentu, może powtarzać się w innych modułach łączonych przez linker w jeden wspólny segment. Poniższa tabela przedstawia zdefiniowane klasy pamięci.

Tabela 4.1. Klasy segmentów

Klasa Opis
BIT Wewnętrzna pamięć danych adresowana bitowo (20H .. 2FH).
CODE Pamięć programu (0000H .. FFFFH).
DATA Wewnętrzna pamięć danych adresowana bezpośrednio (00H .. 7FH i rejestry SFR).
IDATA Wewnętrzna pamięć danych adresowana pośrednio (00H .. FFH).
XDATA Zewnętrzna pamięć danych (0000H .. FFFFH).

Przykłady:

MOJPROG   SEGMENT   CODE     ;definicja segmentu o nazwie MOJPROG 
                             ;w pamięci programu 
  DANE    SEGMENT   XDATA    ;definicja segmentu o nazwie DANE
                             ;w zewnętrznej pamięci danych

RSEG

Implementacja: Keil

Dyrektywa RSEG rozpoczyna segment relokowalny, który został wcześniej zadeklarowany przy wykorzystaniu dyrektywy SEGMENT. Dyrektywa RSEG ma następujący format:

RSEG segment
  • segment jest nazwą segmentu, która została wcześniej zdefiniowana przy użyciu dyrektywy SEGMENT. Raz rozpoczęty segment pozostaje ważny, dopóki nie zostanie rozpoczęty nowy segment.

Linker łączy segmenty w takiej kolejności, w jakiej występują dyrektywy SEGMENT.

Przykłady:

MOJPROG   SEGMENT   CODE
            RSEG MOJPROG
  
            MOV A,#0
            MOV P0,A
            ...

Segmenty bezwzględne (absolutne)

Segmenty absolutne rezydują w określonym typie pamięci pod stałym adresem ustalanym już na poziomie pisania kodu programu. Dzięki nim możliwe jest więc umieszczanie kodu i danych lub rezerwację przestrzeni pamięci od określonego adresu. Segmenty absolutne mogą być używane również wtedy, gdy istnieje konieczność odnoszenia się do pewnego stałego miejsca w pamięci.

CSEG, BSEG, DSEG, ISEG, XSEG

Implementacja: ASEM-51, Keil

Dyrektywy: CSEG, BSEG, DSEG, ISEG oraz XSEG pozwalają na określenie adresu bezwzględnego w odpowiednim segmencie programu od którego będą umieszczane instrukcje występujące po tej dyrektywie. Adres segmentu jest więc ustalany już w chwili pisania programu. Omawiane dyrektywy mają następujący format:

  CSEG AT adres     ;określa adres bezwzględny w segmencie klasy CODE
  BSEG AT adres     ;określa adres bezwzględny w segmencie klasy BIT
  DSEG AT adres     ;określa adres bezwzględny w segmencie klasy DATA
  ISEG AT adres     ;określa adres bezwzględny w segmencie klasy IDATA
  XSEG AT adres     ;określa adres bezwzględny w segmencie klasy XDATA
  • adres jest bezwzględnym adresem bazowym, od którego będzie rozpoczynał się segment. adres nie może zawierać odniesień w przód i musi być wyrażeniem, które po obliczeniu daje konkretną wartość.

Omawiane dyrektywy określają segmenty absolutne odpowiednio w przestrzeni: danych adresowanej bitowo (BSEG), kodu (CSEG), wewnętrznej przestrzeni danych adresowanej bezpośrednio (DSEG), wewnętrznej przestrzeni danych adresowanej pośrednio (ISEG), zewnętrznej przestrzeni danych (XSEG), definiując jednocześnie klasę danego segmentu. Użycie jednej z wymienionych dyrektyw powoduje, że program asemblujący kończy wcześniejszy segment w danej klasie pamięci i tworzy nowy segment absolutny w wyspecyfikowanej klasie, rozpoczynając go od adresu podanego w instrukcji.

Uwaga W asemblerze firmy Keil możliwa jest również zmiana adresu segmentu absolutnego bez zmiany klasy segmentu. W takim wypadku używana jest tylko druga część omawianych dyrektyw, tj.:

  AT adres

Jeśli w instrukcji wykorzystującej którąś z powyższych dyrektyw nie zostanie określony adres, tzn. opuszczona część AT adres, to jest kontynuowany ostatni bezwzględny segment danego typu. Jeśli wcześniej nie został określony żaden segment bezwzględny tego typu, a w instrukcji nie określono adresu, to nowy segment jest tworzony od adresu 0.

Przykłady:

  BSEG AT 20h             ;absolutny segment danych adresowanych bitowo..
                          ;..począwszy od adresu 20h
   DEC_FLAGA:  DBIT 1     ;bit o adresie absolutnym 20h
   INC_FLAGA:  DBIT 1     ;bit o adresie absolutnym 21h
  
  ISEG AT 80h             ;absolutny segment danych adresowanych pośrednio..
                          ;..począwszy od adresu 80h
   TEMP_IA:  DS 2         ;dana 2-bajtowa (pierwszy bajt pod adresem 80h)
   TEMP_IB:  DS 4         ;dana 4-bajtowa (pierwszy bajt pod adresem 82h)
  
  BSEG                    ;dalsza część absolutnego segmentu danych..
                          ;..adresowanych bitowo
   ID_FLAGA: DBIT 1       ;bit o adresie absolutnym 22h
  
  DSEG AT 40h             ;absolutny segment danych adresowanch..
                          ;..bezpośrednio począwszy od adresu 40h
   TMP_A: DS 2            ;dana 2-bajtowa (pierwszy bajt pod adresem 40h)
   TMP_B:	DS 4            ;dana 4-bajtowa (pierwszy bajt pod adresem 42h)
  
  XSEG AT 1000h           ;absolutny segment w zewnetrznej pamięci danych..
                          ;..począwszy od adresu 1000h
   NAZWA_1:  DS 25        ;pierwszy z 25-ciu bajtów od adresem 1000h
   NAZWA_2:  DS 25        ;pierwszy z 25-ciu bajtów od adresem 1019h
   WERSJA:   DS 25        ;pierwszy z 25-ciu bajtów od adresem 1032h
  
  CSEG                    ;absolutny segment kodu od adresu 00h
             SJMP START   ;pierwszy bajt instrukcji umieszczony pod adr. 00h
  CSEG AT 0Bh             ;absolutny segment kodu od adresu 0Bh
             LJMP TIMER0  ;pierwszy bajt instrukcji umieszczony pod adr. 0Bh
  CSEG AT 23h             ;absolutny segment kodu od adresu 23h
             LJMP PORT_RS ;pierwszy bajt instrukcji umieszczony pod adr. 23h
  START:     MOV  SP,#60h
             MOV  R3,#0
             ...
  TIMER0:    INC  R3
             ...
             RETI
  PORT_RS:   JNB  RI,KONIEC_RS
             ...
  KONIEC_RS: RETI

Segment domyślny

Na początku procesu asemblacji pliku źródłowego, program asemblujący domyślnie przyjmuje, że wybrany został segment kodu. Dodatkowo licznik położenia przypisany do tego segmentu inicjowany jest adresem 0000H. Umożliwia to stworzenie prostego programu bez konieczności określania segmentów relokowalnych i/lub absolutnych.