S. Jemioło B. Jakubiec
G. Kulig Ł. Król
Klasa VgT
Ćwiczenie nr 5
TEMAT: STOS.
Cel ćwiczenia:
Celem ćwiczenia jest zapoznanie się z obszarem pamięci nazywanym stosem oraz ze sposobami wykorzystywania stosu.
Wiadomości wstępne:
Stos w mikrokontrolerze 8051 jest to specjalnie wydzierżawiona część pamięci RAM, która działa na zasadzie: ostatnio zapisane, pierwsze wzięte. Do zapisu danych na stos służy rozkaz: PUSH, do zdejmowania ze stosu danych służy rozkaz: POP. Przy używaniu tych komend programista nie musi znać adresów komórek stosowych, wystarczy, że zna je mikrokontroler, który do tego wykorzystuje rejestr zwany wskaźnikiem stosu (SP- stack pointer ), który jest umieszczony w obszarze rejestrów specjalnych (SFR). Wskaźnik stosu wskazuje zawsze adres wierzchołka stosu. Po sygnale RESET wskaźnik stosu jest ustawiony na adres 7, a więc stos umiejscowiony jest od adresu ósmego w górę (jest to miejsce, gdzie znajduj się pierwsze i kolejne banki rejestrów). Programista może sam zadeklarować od jakiego miejsca ma zaczynać się stos np: (MOV SP,#70H).
Rozkaz PUSH jest realizowany w dwóch etapach. Najpierw zwiększany jest wskaźnik stosu SP ← SP + 1, a następnie wartość stojąca przy rozkazie PUSH trafi do komórki pamięci adresowanej bezpośrednio przez wskaźnik stosu.
Rozkaz POP działa w odwrotnej kolejności, tzn. najpierw pobierana jest wartość ze stosu, a następnie wskaźnik stosu zmniejszany jest o jeden.
PRZYKŁAD 5.1
PAMIĘĆ PODRĘCZNA
LJMP START
ORG 100H
START:
LCALL LCD_CLR
MOV A,#'D' ;wpisz do A kod litery D
PUSH ACC ;przechowaj akumulator na stosie
LCALL WRITE_DATA ;wyświetl jako znak czyli litera D
MOV A,#'=' ;wpisz znak równości
LCALL WRITE_DATA
POP ACC ;pobierz wartość ze stosu do akumulatora
LCALL WRITE_HEX ;wyświetl jako liczbę - kod litery D = 44H
SJMP $
W przykładzie 5.1 wykorzystano stos do przechowywania akumulatora. Powyższy przykład wypisuje na wyświetlacz LCD wartość z akumulatora, najpierw w postaci znaku, a następnie jako liczbę w zapisie hexadecymalnym.
PRZYKŁAD 5.2
PODPROGRAMY
LJMP START
ORG 100H
START:
LCALL LCD_CLR
MOV A,#137 ;do A liczba 137
ACALL WRITE_BCD_HEX ;wywołaj podprogram
SJMP $
;**************************************
;podprogram wypisuje liczbę z akumulatora najpierw dziesiętnie a następnie ;szesnastkowo podprogram nie zmienia zawartości rejestrów
WRITE_BCD_HEX:
PUSH PSW ;przechowaj rejestry
PUSH B ;na stosie
PUSH ACC
ACALL BIN_BCD ;wywołaj podprogram zamiany liczby binarnej na liczbę BCD
XCH A,B ;zamień A<->B setki do A dziesiątki i jedn. do B
LCALL WRITE_HEX ;wyświetl setki
MOV A,B ;dziesiątki i jedn. do A
LCALL WRITE_HEX ;wyświetl
MOV A,#'='
LCALL WRITE_DATA
POP ACC ;odtwórz liczbę binarną
PUSH ACC ;-skopiuj ze stosu do A
;czyli pobierz do A i połóż A z powrotem
LCALL WRITE_HEX ;wyświetl
MOV A,#'H'
LCALL WRITE_DATA ;dopisz H do liczby
POP ACC ;odtwórz rejestry
POP B
POP PSW
RET ;powrót z podprogramu
;**************************************
;podprogram zamienia liczbę binarną z A
;na liczbę w kodzie upakowane BCD B - setki A - dziesiątki i jednostki
BIN_BCD:
MOV B,#100 ;wydziel setki
DIV AB ;dzieląc przez 100
PUSH ACC ;przechowaj setki
MOV A,B ;wydziel dziesiątki
MOV B,#10 ;dzieląc przez 10
DIV AB
SWAP A ;przesuń dziesiątki
ORL A,B ;dodaj jednostki
POP B ;odtwórz setki do B
RET ;koniec podprogramu
Przykład 5.2 - wykorzystują podprogramy i stos, który służy tutaj między innymi do umożliwienia powrotu z tych podprogramów. Aby umożliwić powrót z podprogramu, należy zapamiętać adres, pod który należy wrócić.
Podprogram BIN_BCD zamienia liczbę binarną, umieszczoną w akumulatorze, na trzycyfrową liczbę w kodzie BCD upakowane). Liczba setek jest umieszczona w rejestrze B, natomiast liczba dziesiątek i jednostek - w akumulatorze.
Użycie zamiast stosu rejestru lub komórki pamięci zdecydowanie utrudniłoby wykorzystanie tego podprogramu. Przy każdym wywołaniu trzeba by zagwarantować, że w danym miejscu pamięci nie ma aktualnie zapamiętanej informacji. Aby uniknąć przypadkowych błędów, należałoby zarezerwować jedną komórkę pamięci specjalnie dla tego podprogramu. Jest to niepotrzebna strata pamięci.
Należy zwrócić uwagę, że w podprogramie użyto rozkazów:
PUSH ACC
POP B
A więc wartość z akumulatora docelowo znalazła się w rejestrze B. Nie zakłóciło to w niczym operacji na stosie - jeden bajt został na stos położony i jeden zdjęty.
Wewnątrz podprogramu jest jeszcze użyta sekwencja:
POP ACC
PUSH ACC
Pobiera ona wartość ze stosu do akumulatora, a następnie kładzie ją z powrotem na stos. Skutek tego jest taki, że wartość z wierzchołka stosu zostaje skopiowana do akumulatora.
PRZYKŁAD 5.3
KALKULATOR MNOŻENIE BCD
LJMP START
ORG 100H
START:
LCALL LCD_CLR
LCALL WAIT_KEY ;pobierz pierwszy czynnik
MOV R0,A ;zapamiętaj w R0
ACALL WRITE_BCD ;wypisz w postaci BCD
MOV A,#'*' ;znak iloczynu
LCALL WRITE_DATA
LCALL WAIT_KEY ;pobierz drugi czynnik
MOV R1,A ;zapamiętaj w R1
ACALL WRITE_BCD ;wypisz w postaci BCD
MOV A,#'=' ;znak równości
LCALL WRITE_DATA
MOV A,R0 ;pomnóż oba czynniki
MOV B,R1
MUL AB
ACALL WRITE_BCD ;wypisz wynik w postaci BCD
SJMP $
;**************************************
;podprogram wypisuje liczbę z akumulatora
;na wyświetlacz LCD w postaci BCD
WRITE_BCD:
MOV B,#100 ;wydziel setki
DIV AB
JZ ONLY_LOW ;wypisz setki jeśli
LCALL WRITE_HEX ;różne od 0
ONLY_LOW:
MOV A,B ;wydziel dziesiątki
MOV B,#10
DIV AB
SWAP A
ORL A,B ;połącz z jednostkami
LCALL WRITE_HEX ;pisz dziesiątki i jedn.
RET ;koniec podprogramu
Przykład 5.3 jest kalkulatorem BCD, który wykorzystuje podprogramy WRITE_BCD i, ONLY_LOW wywoływane rozkazem ACALL.
Wnioski:
W mikrokontrolerze 8051 podczas korzystania ze stosu należy pamiętać, że ostatnio położony na stos bajt zdejmowany, jest jako pierwszy.
Obecność stosu pozwala na pamiętanie adresów ostatnich linii kodu programu podczas wykonywania skoków do podprogramów.