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.
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.
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.