Ćwiczenie nr 2 Cel ćwiczenia: zapoznanie się z nowymi metodami adresowania portów, urządzeń do nich podpiętych (adresowanie pośrednie, bezpośrednie, rejestrowe) oraz poznanie struktury wewnętrznej pamięci RAM układu mikrokontrolera 8051, zapoznanie się z operacjami arytmetycznymi na liczbach jednobajtowych, poznanie różnych formatów danych, użycie wyświetlacza LCD i klawiatury matrycowej przy korzystaniu z podprogramów systemu DSM- 51. zapoznanie się z obszarem pamięci nazywanym stosem oraz ze sposobami wykorzystywania stosu. Pamięć wewnętrzna RAM. W mikrokontroler 8051 jest wbudowana wewnętrzna pamięć RAM, do jej adresowania używany jest adres ośmiobitowy, co pozwala na rozróżnienie 256 komórek pamięci (0..255),każda komórka to 1 bajt pamięci. W rzeczywistości w mikrokontrolerze 8051 umieszczono 128 bajtów pamięci RAM (adresy: 00..7F). Natomiast w obszarze adresów 80..FF umieszczono rejestry specjalne (SFR), które sterują różnymi funkcjami mikrokontrolera. 128 komórek RAM pod adresami 00..7FH. W przeciwieństwie do obszaru rejestrów specjalnych obszar jest ciągły, tzn. pod wszystkimi adresami od 00 do 7FH istnieją kolejne komórki pamięci. Pamięć ta może być wykorzystana dowolnie, ale w pamięci tej zostały wyróżnione pewne obszary, które mogą (ale nie muszą) być wykorzystane w sposób szczególny. Początkowe 32 bajty pamięci stanowią 4 banki rejestrów. Każdy bank zawiera 8 rejestrów: R0..R7. Do tego obszaru można odwołać się podając adresy, ale prościej jest użyć nazwy rejestru R0,..,R7 (np: MOV A,R0). PRZYKŁAD_1 REJESTRY R0..R7 B0R7 EQU 7 ;rejestr R7 z banku 0 B1R7 EQU 8+7 ;rejestr R7 z banku 1 B2R7 EQU 10H+7 ;rejestr R7 z banku 2 B3R7 EQU 18H+7 ;rejestr R7 z banku 3 MOV B0R7,#0 ;wpisz numer banku MOV B1R7,#1 ;do rejestru R7 MOV B2R7,#2 MOV B3R7,#3 ;wyczyść wyświetlacz LCD ;bank 0 MOV A,R7 ;A <- R7=0 ;akumulator na LCD SETB RS0 ;bank 1 MOV A,R7 ;A <- R7=1 SETB RS1 ;bank 3 MOV A,R7 ;A <- R7=3 CLR RS0 ;bank 2 MOV A,R7 ;A <- R7=2 Program 1 opisuje sposób używania rejestrów R0..R7 z banków 0..3. Wybór banków występuje zgodnie z tabelą: RS1 RS0 BANK ADRESY 0 0 0 00..07 0 1 1 O8..0F 1 0 2 10..17 1 1 3 18..1F 1
W tym przykładzie przy zapisie danych posłużono się adresowaniem bezpośrednim. Natomiast przy odczycie wykorzystano adresowanie rejestrowe. Na początku rejestrom 7 z każdego z czterech baków nadana jest etykieta(np:b0r7). Następnie do 7 rejestru baku 0, 1, 2, 3 jest wpisywany ich numer (czyli 0,1,2,3). W dalszej części pobierane są do akumulatora wartości liczbowe zapisane w poszczególnych bankach i wyświetlane na wyświetlaczu LCD. Dwa z pośród wyżej wymienionych rejestrów (R0 i R1) z dowolnego banku maja jeszcze jedną ważną cechę. Za ich pomocą można adresować inne komórki pamięci RAM przez tzw. adresowanie pośrednie. Czyli kod rozkazu zawiera określenie rejestru w którym znajduje się adres komórki pamięci zawierającej argument. PRZYKŁAD_2 ADRESOWANIE POŚREDNIE ;wyczyść wyświetlacz LCD MOV 43H,#55H ;do komórki pamięci 43H wpisz liczbę 55H ;(43H) <- 55H MOV A,43H ;do akumulatora przepisz zawartość komórki 43H ;A <- (43H)=55H ;akumulator na LCD MOV R0,#40H ;do R0 wpisz liczbę 40H która będzie adresem MOV R2,#10 ;do R2 wpisz liczbę 10 -licznik pętli LOOP: ;zeruj 10 komórek pamięci ;od adresu 40H czyli obszar 40H..49H MOV @R0,#0 ;wpisz liczbę 0 pod adres umieszczony w R0 INC R0 ;zwiększ adres DJNZ R2,LOOP ;powtórz zapis n-razy zgodnie z licznikiem MOV A,43H ;A <- (43H)=0 ;akumulator na LCD W przykładzie tym wykorzystywane są nowe polecenia: INC (DEC) zwiększ (zmniejsz) o jeden. DJNZ złożenie dwóch poleceń: DEC zmniejsz o 1 i polecenie skoku w przypadku gdy zmniejszana wartość nie osiągnęła stanu =0 (np: DJNZ R2,LOOP zmniejsz o 1 wartość rejestru R2 i jeśli nie jest tam zero skocz do pętli LOOP). Program 3.4 korzysta z adresowania pośredniego. Początkowo do komórki pamięci o adresie 43H jest wpisana liczba 53H, która jest przepisana do akumulatora i wyświetlona na wyświetlacz LCD. Następnie do rejestru R0 jest wpisana liczba 40H, która potem będzie traktowana jako adres (poprzez odpowiednie odwołanie się to tego rejestru), a do rejestru R2 jest wpisana liczba 10H liczba pomocnicza licznik pętli. W pętli LOOP komendą MOV @R0,#0 do rejestru o adresie umieszczonym w rejestrze R0 (wartość tego rejestru jest zwiększana o 1, w wyniku czego do obszaru od 40H do 49H zostaną wpisane 0 ) będzie wpisywana liczba zero i tak aż 10 razy dopóki rejestr R2 nie osiągnie wartości 0. ZADANIE_1 Wykonaj iloczyn logiczny R1&R3 z banku pamięci nr 2. Wynik zapisz w RAM(11h). ZADANIE_2 Wpisz do komórek pamięci RAM od adresu RAM(32H) do RAM (42H) kolejne liczby całkowite od liczby 1. ZADANIE_3 Przepisz liczby zapisane w pamięci RAM od adresu RAM(40H) do RAM (50H) do pamięci RAM od adresu RAM(60H) Operacje arytmetyczne. Mikrokontroler 8051 jest mikrokontrolerem 8-bitowym. Oznacza to, że możemy wyróżnić w nim 256 różnych stanów, które mogą reprezentować liczby od 0 do 255. Mikrokontroler posiada wbudowaną jednostkę arytmetyczno logiczną (ALU), która potrafi wykonywać operacje na liczbach jednobajtowych. Wszystkie dane do działań oraz wyniki muszą się zawierać w zakresie 0..9, co praktycznie dyskwalifikuje go do szerszych zastosowań. W systemie DSM-51 występuje podprogram WRITE_HEX, który wypisuje na wyświetlaczu LCD bajt z akumulatora w postaci hexadecymalnej, tzn. wypisywana jest liczba z zakresu 0..9, A, B, C, D, E, F. Należy zwrócić uwagę, że zapis szesnastkowy liczb 0..9 2
jest równy zapisowi dziesiętnemu, a więc przy liczbach mniejszych od 10 nie trzeba przeliczać wyniku z zapisu hexadecymalnego na dziesiętny. PRZYKŁAD_1 DODAWANIE LJMP ORG START 100H ;wyczyść wyświetlacz LCD MOV A,#2 ;wpisz do akumulatora liczbę 2 ADD A,#2 ;dodaj do akumulatora liczbę 2 ;wynik w akumulatorze ;akumulator na LCD W przykładzie_1 zostało wykorzystane nowe polecenie ADD, które po wcześniejszym załadowaniu do akumulatora liczby dodaje ją z liczbą umieszczoną przy poleceniu ADD. Wynik dodawania jest Z A W S Z E umieszczany w akumulatorze i wyświetlany na wyświetlacz LCD za pomocą podprogramu WRITE_HEX ( jest to 4). Wartość wskazana przy poleceniu ADD może być liczbą (np: #2), zawartością rejestru (ADD A,R0 lub inny), zawartością komórki adresowanej bezpośrednio lub zawartością komórki adresowanej pośrednio (@Ri). PRZYKŁAD_2 KALKULATOR LCALL WAIT_KEY ;pobierz pierwszy czynnik z klawiatury matrycowej MOV R0,A ;zapamiętaj w R0 ;wyświetl na LCD MOV A,#'+' ;znak sumy wyświetl LCALL WRITE_DATA ;na LCD jako znak LCALL WAIT_KEY ;pobierz drugi czynnik MOV R1,A ;zapamiętaj w R1 ;wyświetl na LCD MOV A,#'=' ;znak równości MOV A,R0 ;pierwszy czynnik do A ADD A,R1 ;dodaj drugi czynnik wynik w akumulatorze ;wyświetl sumę Przykład_2 korzystając z klawiatury możemy sami określić jakie liczby chcemy dodawać. Dzięki podprogramowi WAIT_KAY wczytywane są w akumulator liczby, które odpowiadają kodom naciśnietych klawiszy, podprogramem WRITE_HEX są wypisywane na wyświetlacz liczby albo znaki odpowiadające naciśniętemu klawiszowi. Klawisze [0]..[9] ponumerowane są odpowiednio 0..9, a pozostałe mają kolejne wartości 10..15, czyli 0A..0F. Kolejność klawiszy jest następująca: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,,,,, Esc, Enter Program działa następująco: po naciśnięciu klawisza pobierany jest jego kod i przepisywany jest z akumulatora do rejestru R0 i jednocześnie liczba odpowiadająca kodowi wyświetlana jest na LCD, następnie dzieje się to samo z drugim czynnikiem, z tą różnicą, że jego kod jest umieszczony w rejestrze R1, pomiędzy cyframi na wyświetlaczu pojawiają się znaki ( +, = ), są one umieszczane na LCD za pomocą podprogramu WRITE_DATA, który zmienia podany kod liczbowy na znak. Przy obliczaniu sumy ładowany jest do akumulatora najpierw rejestr R0, a następnie komendą ADD do akumulatora dodawana jest zawartość rejestru R1. Wynik z akumulatora na LCD. Dopóki używane są małe liczby, wynik odpowiada zapisowi dziesiętnemu, jeżeli suma liczb jest większa od 9 to na wyświetlaczu wynik jest ;podany w postaci hexadecymalnej. Aby wynik był zrozumiały używa się kodu BCD, który rozkłada liczbę np. 389 na trzy czynniki i zapisuje je w osobnych bajtach: 3 (00000011), 8(00001000), 9(00001001), jak widać do zapamiętania liczby 0..9 wystarczy tylko 4 bity, reszta jest nie używana 3
Przeznaczenie całego bajtu, czyli 8 bitów, na zapamiętanie 1 cyfry jest nie potrzebą stratą miejsca. Dlatego najczęściej stosowany zapis to tzw. upakowane BCD, czyli wykorzystanie jednego bajtu do zapisania w nim dwóch liczb. Przy formacie BCD wyróżnia się tylko liczby 0..99. Zakłada się, że pozostałe stany w tych bajtach nigdy nie wystąpią. Przypadkowe ich wystąpienie (wynikające z błędu programu) spowoduje dalsze błędy przy ich interpretacji. Jeżeli wyniki obliczeń mają być przedstawione w kodzie BCD, są dwie możliwości postępowania: cały czas w trakcie obliczeń posługiwać się kodem BCD w trakcie obliczeń posługiwać się liczbami binarnym, na koniec zamienić liczby binarne na BCD Pierwszy sposób nadaje się tylko do małych liczb 0..9. W drugim sposobie w celu zamiany liczby binarnej na BCD najprościej użyć do tego celu dzielenia. W mikrokontrolerze 8051 dzielenie wykonywane jest zawsze na tych samych rejestrach. Przed wykonaniem dzielenia dzielna powinna być umieszczona w akumulatorze, a dzielnik w rejestrze B (rejestr o adresie F0 w obszarze rejestrów specjalnych). Po wykonaniu dzielenia w rejestrze A otrzymywany jest wynik, a w rejestrze B reszta z dzielenia. Zakładając, że liczba jest mniejsza od 100, można zamienić ja na BCD dzieląc ja przez 10. Wynikiem tej operacji będzie liczba dziesiątek (w akumulatorze), natomiast resztą (w rejestrze B) liczba jednostek. PRZYKŁAD_3 ZAMIANA BIN - BCD MOV A,#63 ;wpisz dzielną do A MOV B,#10 ;wpisz dzielnik do B ;dzielenie A/B ;wynik dzielenia w A - cyfra dziesiątek z 63 ;reszta z dzielenia w B czyli cyfra jednostek ;zamień półbajty -liczba dziesiątek do górnej połówki A ;dodaj liczbę jednostek -liczba jednostek do dolnej połówki A ;wypisz liczbę BCD Przykład_3 w bardzo sprytny sposób realizuje zamianę liczby binarnej na BCD. W wyniku dzielenia liczby z akumulatora przez 10 otrzymano rozdzielenie liczby dziesiątek (A) i jednostek (B). Należy te liczby spakować razem do jednego bajtu. Rozkaz SWAP zamienia młodsze 4 bity akumulatora ze starszymi 4 bitami. W ten sposób liczba dziesiątek zostaje umieszczona w 4 bardziej znaczących miejscach. Dodanie do akumulatora reszty (z rejestru B) spowoduje powstanie w akumulatorze z powrotem liczby 63 w formacie upakowane BCD. Zamiana ma następujący przebieg: Kolejne linie kodu MOV A,#63 MOV B,#10 ADDA,B Rejestr A (akumulator) xxxx xxxx 0011 1111 = 63 0011 1111 = 63 0000 0110 = 6 0110 0000 = 60 BCD 0110 0011 = 63 BCD Rejestr B xxxx xxxx xxxx xxxx 0000 1010 = 10 0000 0011 = 3 0000 0011 = 3 0000 0011 = 3 Należy pamiętać, iż liczby w poszczególnych 4 bitowych komórkach, nie mogą być większe od 9, gdyż przy wyświetlaniu na LCD nie pojawią się tam liczby dziesiętne, a hexadecymalne. PRZYKŁAD_4 DODAWANIE MOV A,#250 ;wpisz do A liczbę 250 4
ADD A,#10 ;dodaj do A liczbę 10 w A 260-256=4 Przykład_4 pokazuje co się stanie gdy suma dodawanych liczb przekroczy 255 (jeden bajt to 255). Na wyświetlaczu zostanie wypisana wartość 4, podczas, gdy wynik dodawania wynosi 260. Prawidłowy wynik to 4 + przeniesienie, które w mikrokontrolerze jest przechowywane w rejestrze stanu PSW, w bicie przeniesienia C (flaga CY - PSW.7) W tym przypadku przeniesienie C jest ustawione na 1, w innych przypadkach równa się 0. W programie bit ten może być wykorzystywany na dwa sposoby. Jeżeli założono, że liczby, na których działamy są zapisywane w 1 bajcie to ustawienie tej flagi sygnalizuje błąd. Jeżeli prowadzone jest dodawanie liczb które spowodują ustawienie tej flagi to bit ten powinien być uwzględniony w dodawaniu. Służy do tego komenda ADDC A,.., która działa tak jak ADD. Dodaje liczby wielobajtowe. PRZYKŁAD_5 ODEJMOWANIE MOV A,#6 ;wpisz do A liczbę 6 CLR C ;zeruj bit przeniesienia SUBB A,#1 ;odejmij z pożyczką A <- A-1-C = 6-1-0 = 5 MOV A,#6 ;wpisz do A liczbę 6 SETB C ;ustaw bit przeniesienia SUBB A,#1 ;odejmij z pożyczką A <- A-1-C = 6-1-1 = 4 Program_5 wprowadza nowy rozkaz : SUBB, który od zawartości akumulatora odejmuje drugi argument oraz zawartość flagi C. Flaga C przy odejmowaniu pełni rolę pożyczki. Ponieważ nie ma rozkazu odejmowania nie uwzględniającego flagi C, zawsze przed rozpoczęciem odejmowania należy wyzerować flagę C. Program działa następująco: czyszczenie wyświetlacza LCD, zapis 6 do akumulatora, wyzerowanie flagi C, odjęcie od akumulatora 1, wynik na LCD = 5 prawidłowe działanie. Druga część podobna, ale flaga C =1 i w tym przypadku od akumulatora odjęte będzie 1 (SUBB A,#1) i jeszcze 1 z flagi C wynik = 4. Problem pojawia się gdy wynikiem odejmowania jest LICZBA UJEMNA. Do reprezentowania liczb ujemnych powstał kod U2, w formacie tym mogą być zapisane liczby z zakresu 128.. + 127. Na siedmiu bitach są liczby a jeden bit to znak. PRZYKŁAD_6 LICZBY UJEMNE U2 CLR A ;zeruj A CLR C ;zeruj C SUBB A,#1 ;A <- 0-1 = -1 MOV R0,A ;zapamiętaj w R0 MOV A,R0 ;A <- R0=-1 ADD A,#1 ;A <- A+1 = -1+1 = 0 Program_6 udowadnia istnienie liczb ujemnych w mikrokontrolerze 8051. Kolejne liczby ujemne są reprezentowane na jednym bajcie następująco: -1 = FF -2 = FE -3 =FD. Na końcu tego programu użyto rozkazu SJMP, zamiast LJMP, jak widać rozkaz ten zajmuje tylko 2 bajty, podczas gdy LJMP zajmował 3. 5
PRZYKŁAD_7 MNOŻENIE MOV A,#0F1H ;mnożna MOV B,#2 ;mnożnik MUL AB ;mnożenia A*B ;starsza część wyniku w B młodsza część wyniku w A XCH A,B ;wypisz starszą część MOV A,B ;pobierz młodszą część ;wypisz młodszą część Program_7 wprowadza nowe rozkazy MUL mnożenie i XCH (dokąd, skąd) wymiana. O ile pierwsza komenda jest zrozumiała, o tyle rozkaz XCH nie, jego działanie polega na zamianie dwóch argumentów między sobą, przy czym jednym z nich jest zawsze akumulator. Użycie tej instrukcji w przykładzie pozwala na wyświetlenie w pierwszej kolejności starszej części wyniku, bez wykorzystywania dodatkowych rejestrów. PRZYKŁAD_8 KALKULATOR BCD LCALL WAIT_KEY ;pobierz pierwszy czynnik MOV B,#10 ;zamień liczbę na BCD ;dzieląc przez 10 MOV R0,A ;zapamiętaj w R0 (BCD) ;wypisz na LCD MOV A,#'+' ;znak sumy LCALL WAIT_KEY ;pobierz drugi czynnik MOV B,#10 ;zamień liczbę na BCD ;dzieląc przez 10 MOV R0,A ;zapamiętaj w R1 (BCD) ;wypisz na LCD MOV A,#'=' ;znak równości MOV A,R0 ;pierwszy czynnik do A ADD A,R1 ;dodaj drugi czynnik DA A ;poprawka dodawania liczb BCD ;wypisz wynik na LCD Program_8 jest kalkulatorem liczb BCD. Użyto tu rozkazu poprawki dziesiętnej DA którego działanie polega na zamianie liczby na liczbę dziesiętną, ale należy pamiętać, iż rozkaz tyczy się dodawania i to liczb do 10. PRZYKŁAD_9 KALKULATOR MNOŻENIE BCD<100 6
LCALL WAIT_KEY ;pobierz pierwszy czynnik MOV R0,A ;zapamiętaj w R0 MOV B,#10 ;zamień liczbę na BCD ;dzieląc przez 10 ;wypisz na LCD MOV A,#'*' ;znak iloczynu LCALL WAIT_KEY ;pobierz drugi czynnik MOV R1,A ;zapamiętaj w R1 MOV B,#10 ;zamień na BCD ;wypisz na LCD MOV A,#'=' ;znak równości MOV A,R0 ;pierwszy czynnik do A MOV B,R1 ;drugi czynnik do B MUL AB ;pomnóż A*B - wynik w A (liczba <256) MOV B,#10 ;zamień na BCD ;przy założeniu < 100 ;wypisz wynik na LCD Program_9 jest kalkulatorem mnożący liczby BCD, którego działanie opiera się o przykłady z poprzednich programów. Wnioski: Mikrokontroler 8051 pozwala na bezpośrednią realizację działań na liczbach jednobajtowych: 1. Przy liczbach 0..255: dodawanie ADD, ADDC odejmowanie SUBB mnożenie MUL dzielenie DIV 2. Przy liczbach -128..+127: dodawanie ADD, ADDC odejmowanie SUBB 3. Przy liczbach 0..99 w kodzie BCD: dodawanie ADD, ADDC + rozkaz korekcji dziesiętnej DAA. Stos 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_1 PAMIĘĆ PODRĘCZNA LJMP START 7
MOV A,#'D' ;wpisz do A kod litery D PUSH ACC ;przechowaj akumulator na stosie czyli litera D MOV A,#'=' ;wpisz znak równości LCALL WRITE_DATA POP ACC ;pobierz wartość ze stosu do akumulatora ;wyświetl jako liczbę - kod litery D = 44H W przykładzie_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_2 PODPROGRAMY MOV A,#137 ;do A liczba 137 ACALL WRITE_BCD_HEX ;wywołaj podprogram ;************************************** ;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 ;wyświetl setki MOV A,B ;dziesiątki i jedn. do A ;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 ;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 ;dzieląc przez 100 PUSH ACC ;przechowaj setki MOV A,B ;wydziel dziesiątki MOV B,#10 ;dzieląc przez 10 ;przesuń dziesiątki ORL A,B ;dodaj jednostki POP B ;odtwórz setki do B RET ;koniec podprogramu Przykład_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ć. 8
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_3 KALKULATOR MNOŻENIE BCD 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 ;************************************** ;podprogram wypisuje liczbę z akumulatora ;na wyświetlacz LCD w postaci BCD WRITE_BCD: MOV B,#100 ;wydziel setki JZ ONLY_LOW ;wypisz setki jeśli ;różne od 0 ONLY_LOW: MOV A,B ;wydziel dziesiątki MOV B,#10 ORL A,B ;połącz z jednostkami ;pisz dziesiątki i jedn. RET ;koniec podprogramu Przykład 3 jest kalkulatorem BCD, który wykorzystuje podprogramy WRITE_BCD i, ONLY_LOW wywoływane rozkazem ACALL. 9