UNIVERZITA TOMÁŠE BATI VE ZLÍNĚ FAKULTA APLIKOVANÉ INFORMATIKY
PROGRAMOVÁNÍ MIKROPOČÍTAČŮ CVIČENÍ 10 Využití zásobníku pro předání parametrů podprogramům a lokální proměnné Jan Dolinay Petr Dostálek Zlín 2013
Tento studijní materiál vznikl za finanční podpory Evropského sociálního fondu (ESF) a rozpočtu České republiky v rámci řešení projektu: CZ.1.07/2.2.00/15.0463, MODERNIZACE VÝUKOVÝCH
MATERIÁLŮ A DIDAKTICKÝCH METOD
Cvičení 10 – Využití zásobníku pro předání parametrů podprogramům a lokální proměnné
Cvičení 10 – Využití zásobníku pro předání parametrů podprogramům a lokální proměnné
STRUČNÝ OBSAH CVIČENÍ:
Ukázkový program s podprogramem na vyplnění pole Vysvětlení ukázkového programu Úkoly k procvičení
VSTUPNÍ ZNALOSTI: Toto cvičení předpokládá znalosti o využití zásobníku získané na přednáškách a znalosti programování mikropočítače HCS08 z předchozích cvičení.
CÍL:
Na tomto cvičení si ukážeme, jak lze využít zásobník pro předávání parametrů do podprogramů a pro vytvoření lokálních proměnných v podprogramech. Tyto znalosti nám umožní efektivnější tvorbu programů a také lepší porozumění kódu generovaného překladačem jazyka C.
Cvičení se vztahuje k těmto otázkám
Funkce a ovládání zásobníkové paměti, instrukce pro práci se zásobníkem u mikropočítače HC08
MODERNIZACE VÝUKOVÝCH MATERIÁLŮ A DIDAKTICKÝCH METOD CZ.1.07/2.2.00/15.0463,
2
Cvičení 10 – Využití zásobníku pro předání parametrů podprogramům a lokální proměnné
Řešené příklady Příklad 1 – Podprogram pro vyplnění pole konstantou
Úkol: Napište podprogram, který vyplní předané pole libovolným číslem v rozsahu 0 až 255. Vstupními parametry podprogramu budou v registrech H:X adresa začátku pole, v registru A počet prvků pole a na zásobníku číslo, kterým se má pole vyplnit jako jednobajtový parametr. Napište také program, který tento podprogram využije a naplní pole o 5-ti prvcích konstantou 3.
Řešení Podprogram bude pracovat s polem pomocí indexového adresování. Tento princip byl vysvětlen v kapitole 2, na příkladu nulování pole. Byla zde zmíněna i varianta předání adresy do podprogramu v registru H:X. Náš podprogram bude pro počitadlo průchodů cyklem (počtu již zpracovaných prvků) a pro uložení předaného počtu prvků pole používat lokální proměnné na zásobníku. Bude také přistupovat k parametru předanému na zásobníku, což je konstanta, kterou se má pole vyplnit. Hlavní program pouze připraví parametry na zásobník a do registrů a zavolá podprogram. Zdrojový kód podprogramu a hlavního programu je na následujících výpisech.
Vysvětlení programu Podívejme se nejprve na hlavní program. Instrukcemi LDHX a TXS se nastaví ukazatel zásobníku na adresu $98. Vlastně tím umísťujeme zásobník na námi požqadované místo. Toto obvykle provádí CodeWarrior při vytváření nového projektu a nastavuje zásobník na pozici definovanou nastavením projektu. Většinou konkrétní adresa umístění zásobníku pro nás není podstatná a můžeme jej ponechat na výchozí hodnotě určené vývojovým prostředím. V tomto případě jsme ale zásobník přemístili na adresu $98 a to z čistě studijních důvodů – takto bude obsah zásobníku při krokování programu snadno vidět spolu s proměnnými v okně Memory simulátoru, aniž bychom v tomto okně museli „skrolovat“. Dále už pokračuje kód programu, který řeší zadání. Hlavní program má za úkol připravit vstupní parametry pro podrpogram Podp a pak jej zavolat. Nejprve na zásobník vkládáme MODERNIZACE VÝUKOVÝCH MATERIÁLŮ A DIDAKTICKÝCH METOD CZ.1.07/2.2.00/15.0463,
3
Cvičení 10 – Využití zásobníku pro předání parametrů podprogramům a lokální proměnné
parametr určující, jakým číslem má podprogram vylnit pole. Zde to má být číslo 3, proto se na zásobník ukládá číslo 3. Provede se to přes registr A, tj. nejprve nahrajeme číslo 3 jako přímý operand do registru A a potom registr A vložíme na zásobník instrukcí PSHA. Dalším parametrem podprogramu je adresa pole. Na výpisu to není uvedeno, ale jistě dokážete sami definovat pole s názvem Pole, obsahující pět jednobajtových prvků. Adresu tohoto pole pak nahrajeme do registru H:X (LDHX #Pole). Do registru A poté nahrajeme počet prvků pole, což je 5 (LDA #5). Tím máme všechny vstupní parametry pro podprogram připraveny a můžeme jej zavolat instrukcí JSR Podp. Po volání podprogramu následuje instrukce PULA, která ze zásobníku odstraňuje parametr, který jsme tam před chvíli vložili (číslo 3). Toto je nutné aby se zásobník vrátil do původního stavu, tak jak byl předtím, než jsme začali připravovat parametry pro volání podprogramu Podp.* mainLoop: ; Insert your code here NOP LDHX #$98 TXS
; Aby byl videt zasobnik v simulatoru
LDA PSHA LDHX LDA JSR PULA
; param. Na zasobniku = vyplnim trojkami
#3
#Pole ; adresa pole #5 ; pocet prvku pole Podp ; volam podprogram s parametry 5 a 3: Podp(3,5); ; odstraneni parametru ze zasobniku
Obr. 1 – Kód hlavní programu dle zadání
Nyní, když už máme napsán kód, který podprogram Podp využívá, podívejme se dovnitř samotného podprogramu, viz následující výpis. Podprogram využívá 2 lokální proměnné, jednu pro počitadlo cyklu a druhou pro počet prvků pole. Vzpomeňte si, že podprogram dostává počet prvků pole v registru A. Tento údaj potřebuje po celou dobu opakování cyklu, aby s ním mohl porovnávat počet již zpracovaných prvků pole a věděl, kdy cyklus ukončit. Nemůže jej ovšem ponechat v registru A, protože ten bude využíván uvnitř cyklu. Elegantním řešením je tedy uložení počtu prvků pole do lokální proměnné. Vytvoření lokálních proměnných (jejich alokace) je vlastně operace rezervování (zabrání) místa na zásobníku. To lze provést např. instrukcí PSHA (uložení A na zásobník). Tím vložíme na zásobník nějakou hodnotu, která nemusí být podstatná pokud nechceme lokální proměnno zároveň inicializovat. Hlavně ale způsobíme posun ukazatele zásobníku, tj. na zásobníku vznikne prostor 1 bajtu, alokuje se lokální proměnná. MODERNIZACE VÝUKOVÝCH MATERIÁLŮ A DIDAKTICKÝCH METOD CZ.1.07/2.2.00/15.0463,
4
Cvičení 10 – Využití zásobníku pro předání parametrů podprogramům a lokální proměnné
Náš podprogram provádí alokaci právě tímto způsobem, instrukcí PSHA. Navíc chytře využíváme faktu, že registr A obsahuje při vstupu do podprogramu počet prvků pole (vstupní argument z volajícího programu). A počet prvků pole chceme uložit do jedné z lokálních proměnných podprogramu. První instrukcí PSHA se tak nejen rezervuje místo pro lokální proměnnou uchovávající v podprogramu počet prvků pole, ale zároveň se tento počet do ní i uloží. Druhá lokální proměnná je počitadlo průchodů cyklem. Počitadlo musí mít na začátku nulovou hodnotu, proto před alokací a inicializací této proměnné nulujeme registr A instrukcí CLRA a potom ukládáme tuto nulu na zásobník. ; Podprogram pro naplneni pole konstantou ; H:X - adresa pole; A - pocet prvku pole; Zasobnik: konstanta kterou vyplnit Podp: ; alokace lokalnich promennych PSHA ; lokalni promenna 1 = pocet prvku pole CLRA ; nuluj budouci pocitadlo PSHA ; lokalni promenna 2 = pocitadlo cyklu opak: LDA 5, SP ; parametr podprogramu = konstanta kterou vyplnit pole STA ,X ; se ulozi do aktualniho prvku pole AIX #1 INC 1,SP ; inkrementuj pocitadlo LDA 1,SP ; pocitadlo do A CMP 2,SP ; porovnej pocitadlo s poctem prvku pole BLO opak PULA ; uvolneni zasobniku PULA RTS Obr. 2 – Kód podprogramu pro vyplnění pole konstantou dle zadání
Kód za návěštím opak se opakuje tolikrát, kolik je prvků pole. To je zajištěno pomocí podmíněného skoku BLO (skoč jestliže je menší), který se provede po porovnání počitadla a celkového počtu prvků pole. Jak počitadlo tak i počet prvků jsou lokální proměnné a jak je vidět na výpisu, nejprve nahrajeme počitadlo do registru A (LDA 1,SP) a potom porovnáme registr A s počtem prvků (CMP 2,SP). Jestliže je počitadlo menší než počet prvků, provede se skok na návěští opak a zpracuje se pak další prvek pole. Jak se nastavuje zadané číslo do jednotlivých prvků pole uvnitř cyklu? Do registru A se nahraje konstanta, kterou se má pole vyplnit (LDA 5,SP) a následující instrukcí se z registru A uloží do aktuálního prvku pole (STA ,X) – viz indexové adresování při práci s polem. Jak jsme věděli, že u instrukce LDA 5,SP má být před SP právě číslo 5? Do každého prvku pole máme uložit konstantu předanou do našeho podprogramu jako parametr z volajícího programu. Konstanta se předává na zásobníku (viz zadání úkolu). Vzhledem k tomu, že na zásobník je nejprve uložena právě tato konstanta (parametr našeho MODERNIZACE VÝUKOVÝCH MATERIÁLŮ A DIDAKTICKÝCH METOD CZ.1.07/2.2.00/15.0463,
5
Cvičení 10 – Využití zásobníku pro předání parametrů podprogramům a lokální proměnné
podprogramu), pak návratová adresa (uložená automaticky při volání podprogramu) a pak ještě 2 lokální proměnné, nachází se konstanta na offsetu 5 od ukazatele zásobníku SP. Pro lepší představu o umístění lokálních proměnných a vstupního parametru na zásobníku se podívejte na následující obrázek.
Obr. 3 ‐ Stav zásobníku v podprogramu Podp po alokaci lokálních proměnných
Pomocí takového obrázku snadno zjistíme, jak přistupovat ke kterékoliv proměnné nebo argumentu na zásobníku (jaké číslo použít před SP v instrukcích jako LDA ??,SP nebo STA ??,SP apod..). Jestliže např. potřebujeme nahrát do registru A počitadlo cyklů (lokální proměnnou), podle obrázku vidíme, že tato proměnná je na adrese SP + 1 a tedy v instrukci LDA bude: LDA 1,SP. Při psaní vlastních programů si můžete podobný obrázek nakreslit. I u poměrně jednoduchého kódu se vám čas strávený jeho kreslením bohatě vrátí při psaní programu i hledání chyb.
Na konci podprogramu se pomocí instrukcí PULA odstraní lokální proměnné ze zásobníku. Instrukcí RTS se pak vracíme do volajícího programu. Vraťte se i vy k výpisu kódu hlavního programu výše a všimněte si, že za instrukcí JSR Podp, která volá náš podprogram je také instrukce PULA. Tato uvolňuje ze zásobníku argument, který hlavní program předával podprogramu – konstantu, kterou má podprogram vyplnit pole. Poznámka: Principiálně může vstupní parametry ze zásobníku odstranit buď volající (tj. hlavní program), tak jak to děláme v našem příkladě, nebo může odstranění parametrů provést samotný podprogram. Tato „dohoda“ o tom, kdo odstraní vstupní parametry ze zásobníku, spolu s MODERNIZACE VÝUKOVÝCH MATERIÁLŮ A DIDAKTICKÝCH METOD CZ.1.07/2.2.00/15.0463,
6
Cvičení 10 – Využití zásobníku pro předání parametrů podprogramům a lokální proměnné
dalšími „dohodami“ se označuje jako volací konvence (rozumějte dohoda pro volání podprogramů).
Testování programu Tento program je zbytečné nahrávat do mikropočítače, pro jeho otestování nám bohatě postačí simulátor. Pokud jste při vytváření projektu v CodeWarrior zvolili typ připojení „Monitor“, změňte je nyní na „Simulation“ výběrem v rozbalovacím seznamu v levé části okna CodeWarrior. Po překladu a odstranění případných chyb spusťte program v simulátoru obvyklým způsobem. Sledujte v okně Memory obsah paměti vyhrazené pro zásobník. Adresu aktuálního vrcholu zásobníku vždy najdete v registru SP v okně Registers. Po spuštění programu by to mělo být $98. Pak po vložení parametru (číslo 3) by se měl obsah SP snížit na $97 a na adrese $98 v okně Memory byste měli vidět vloženo číslo 3. Použijte příkaz Step Into pro krokování dovnitř podprogramu. Po provedení instrukce JSR uvidíte, že SP bude obsahovat hodnotu $95 (vložili se 2 bajty návratové adresy). Tyto dvě hodnoty vložené v zásobníku nejsou pro nás teď podstatné, bude se jednat o 16-ti bitovou adresu instrukce následující za instrukcí JSR Podp. Po alokaci lokálních proměnných by měla být v SP hodnota $93 a měli byste vidět, že bajt paměti nejblíže SP (adresa $94) bude na začátku podprogramu vynulován (jedná se o počitadlo cyklu). Bajt na adrese $95 bude obsahovat číslo 5, což je počet prvků pole (předaný do podprogramu v registru A, ale podprogram si jej uložil do lokální proměnné na zásobníku). Krokujte cyklus plnění pole a sledujte, jak se v počitadle cyklů na zásobníku inkrementuje počet průchodů cyklem.
Příklady k procvičení 1. Vytvořte podprogram, který spočítá lichá čísla v předaném poli. Adresa pole se předá na zásobníku, počet prvků pole se předá v registru A. Podprogram vrátí v registru A zjištěný počet lichých čísel. Napište program, který pomocí zadaného podprogramu určí počet lichých čísel v poli data s pěti prvky. Pole můžete inicializovat na libovolné hodnoty.
MODERNIZACE VÝUKOVÝCH MATERIÁLŮ A DIDAKTICKÝCH METOD CZ.1.07/2.2.00/15.0463,
7
Cvičení 10 – Využití zásobníku pro předání parametrů podprogramům a lokální proměnné
2. Vytvořte knihovnu podprogramů pro základní matematické operace: sčítání, odčítání, násobení a dělení. Napište pak program, který bude knihovnu využívat pro výpočet výrazu: vysledek = ((c1 + c2) * c3) / (c3-c1). Podprogramy budou mít následující názvy: soucet, rozdil, nasobeni, deleni, modulo. Vstupní hodnoty budou mít vždy velikost 8 bitů a budou se předávat na zásobníku ve vámi zvoleném pořadí. Výsledek (návratová hodnota) se bude u všech podprogramů předávat v registru A. Knihovna nesmí používat žádné globální proměnné. V případě potřeby použijte lokální proměnné na zásobníku. Podprogramy nesmí změnit hodnoty registrů s výjimkou A, který je použit pro vrácení výsledku. Knihovnu umístěte do samostatného souboru s názvem math.asm a vytvořte k ní hlavičkový soubor math.inc.
Doplňující zdroje [1]
Freescale: Firemní dokumentace pro mikropočítače HCS08, dostupné online: http://www.freescale.com/webapp/sps/site/prod_summary.jsp?code=S08GB&nodeId= 01624684491437EDD5
MODERNIZACE VÝUKOVÝCH MATERIÁLŮ A DIDAKTICKÝCH METOD CZ.1.07/2.2.00/15.0463,
8