A tárgy első részében tanult ismeretek részbeni összefoglalására tervezzük meg egy egyszerű mikroprocesszoros rendszer hardverét, és írjuk meg működtető szoftverét!
A feladat tárgyköre és célja:Adott egy hipotetikus mikroprocesszor a következő illesztési felülettel:
Kezdetben a CPU a 0000h címről indul. Megszakításkor egy CALL 1000h utasítást hajt végre, regisztereket nem ment, a további megszakításokat letiltja.
Az első feladat (hardver tervezése) megoldásához szüksége lesz rá, ezért megadjuk még a processzor perifériára történő írásának az idődiagramját. Vegyük észre, hogy az IOWR jel megszűnésekor az adat még kellő ideig stabil!Adott egy beléptető periféria, ami tartalmaz:
A periféria be és kimenetei a következők:
A beléptető periféria C/D választó bemenetét az A0 címbitre kötve tehát az eszköz a rendszerben 2 db periféria címet foglal el: az alacsonyabb címen a vezérlő/státusz regiszter, a magasabb címen az adatregiszter található.
Minden kártyához (kártyaazonosítóhoz) tartozik egy 4 decimális jegyből álló kód: a zárat akkor kell nyitni, ha a kártya lehúzása után megnyomott első 4 billentyű éppen a hozzá tartozó kódot adja. (Időzítéssel nem foglalkozunk!) A feladat megoldásához felhasználjuk a beléptető periféria alábbi funkcióit:
A feladat megoldásához adott még: 8kB ROM, 8kB RAM, 3/8 dekóder, 3db 8 bites címkomparátor, 1 piros és 1 zöld LED nyitó irányban 2V feszültségeséssel és 5-15mA áramigénnyel, 2db 250 Ohm-os ellenállás, 2db D tároló. A D tárolók (a tanult módon) az órajel felfutó élere tárolják el a D bemenet logikai értékét.
Illesszünk a hipotetikus mikroprocesszorhoz 8kB EPROM-ot a 0000h címre, 8kB RAM-ot a 2000h címre, a beléptető perifériát a 2Eh báziscímre (így 2Eh/2Fh címeken érhető el), valamint egy zöld és egy piros LED-et az 1Ah és az 1Bh portcímekre (D tárolók és ellenállások segítségével) úgy, hogy az adatbusz D0 bitjével lehessen az állapotukat vezérelni!
Készítsünk memóriatérképet! A memóriatérkép mellett megadtuk az A15-13 címbitek értékét is; ebből (is) világosan látható, hogy egy 3/8-as dekóderrel az EPROM és a RAM illesztése könnyen megoldható: a 0000h-1FFFh címeken elérhető EPROM esetén A15-13=000, a 2000h-3FFFh címeken elérhető RAM esetén A15-13=001, tehát az 3/8-as dekóder O0, illetve O1 kimenetére kell őket kötnünk. Mindkét memória mérete 8kB, így megcímzésükhöz 13 bitre (A12-0) van szükség. Az adatbusz bekötésénél ügyeljünk arra, hogy az EPROM-ba nem tudunk beleírni!
A beléptető perifériát címkomparátorral a 2Eh báziscímre illesztjük: a címkomparátor P7-1 bemenetére a címbusz A7-1 bitjeit kötjük, de az A0 címbit helyett P0-ra fixen logikai 0-t (L) kötünk, Q7-0 bemenetére pedig az 1Eh értéket kapuzzuk. Így a két perifériacím (2Eh és 2Fh) bármelyikének megszólítása esetén a komparátor kimenete aktív lesz. A címkomparátor G engedélyező bemenetét fixen logikai 0-ra (L) kötjük. (Meghajthatnánk az IORD és IOWR jelekkel egy ÉS kapun keresztül, ami 0 aktív logikában VAGY kapuként funkcionál, de időzítési megfontolásból most nem ezt tesszük.) A címkomparátor 0 aktív P=Q kimenetével engedélyezzük a beléptető perifériát annak CS bemenetén. Az A0 címbitet rákötjük a beléptető periféria C/D bemenetére; az IORD, IOWR, és D7-0 jeleket pedig értelemszerűen a beléptető periféria RD, WR, és D7-0 lábaira kötjük. A periféria INTR kimenetét inverteren keresztül kötjük a CPU felfutó élre érzékeny INT lábára.
A LED-ek meghajtásához fontoljuk meg a következőket! TTL alkatrészekről lévén szó, a D tároló kimenetének áram terhelhetősége logikai 1 értéknél bizonyosan 1mA alatt van, logikai 0-nál viszont 20mA környékén, tehát közvetlenül a D tároló kimenetéről csak logikai 0 esetén tudjuk biztosítani a LED-ek meghajtásához a szükséges áramot. Ha az egy LED-en nyitó irányban eső feszültség 2V, és figyelembe vesszük, hogy a D tároló kimenetén nem pontosan 0V a feszültség, valamint az 5V névleges értékű logikai 1 sem pontosan 5V, akkor a 250 Ohm-os áramkorlátozó ellenálláson még kb. 2V feszültségeséssel számolhatunk, ami 8mA áramot biztosít; ez a LED-ek számára megfelelő. Tehát a LED-eket a D tárolók kimenetéről 250-os ellenálláson keresztül logikai 1 ("H") szintre kötjük. (Vigyázzunk, hogy a LED-ek rajzjelének állása a pozitív áramirányt kövesse a H értéktől a D tároló kimentet felé!) Ne felejtsük el, hogy ezzel a megoldással azt is eldöntöttük, hogy a LED-ek kigyújtásához 0-t kell a D tárolókba írnunk, kioltásához pedig 1-et!
A D tárolókat az 1Ah és az 1Bh portcímekre illesztjük címkomparátorokkal. (A címkomparátorok P7-0 bemenetére a címbusz A7-0 bitjeit kötjük, Q7-0 bemenetére pedig az 1Ah illetve 1Bh értékeket kapuzzuk.) A címkomparátorok engedélyezéséhez a G engedélyező bemenetükre a CPU IOWR kimenetét kötjük, mivel a tárolókat csak írnunk kell. A címkomparátorok 0 aktív P=Q kimenetét a D tároló felfutó élre érzékeny órajel bemenetére kötjük; így az IOWR megszűnésekor a felfutó él hatására a kiválasztott D tároló eltárolja a D bemenetére kötött adatbusz D0 bitjének ekkor még stabilan tartott értékét (lásd 1. ábra).
A beléptető periféria zárja kezdetben zárt állásban van (így indul, ez nem a mi dolgunk). Követelmény, hogy:
Vegyük észre, hogy mivel a zárásról semmiféle értesítést sem kapunk, az 1. követelményt csak úgy tudjuk kielégíteni, ha folytonosan kiolvassuk a zár állapotát és (szükség esetén) beállítjuk a LED-eket. Az már tervezői döntés, hogy ebben a ciklusban folytonosan állítjuk-e LED-ek állapotát vagy csak változás esetén, hasonlóan az is, hogy a ciklusban csak a nyitottról zártra állítással foglalkozunk és (kártyalehúzással és helyes kóddal kiváltott) nyitáskor végezzük el a LED-ek állítását vagy inkább minden LED állítást a ciklusban végzünk. Most válasszuk azt, hogy ebben a ciklusban végzünk el minden LED állítást és állapotváltozástól függetlenül mindig beállítjuk a LED-eket a zár beolvasott állásának megfelelően!
Éljünk azzal az előfeltevéssel is, hogy a kártyalehúzás és a számjegyleütés események közül egyszerre csak az egyik következik be! (A rendszer működése kellően gyors a felhasználóhoz képest.)
A kártyalehúzást és a számjegyek leütését lekérdezni is képesek vagyunk, és megszakítást is használhatunk. Tegyük most az utóbbit!
Mivel a megszakítások használatát választottuk, elesünk annak a lehetőségétől, hogy a programszámlálót használjuk a program állapotának a tárolására, ezért kézenfekvő, hogy használjunk egy állapotváltozót, nevezzük AV-nek. Hordozza AV azt az értéket, hogy az utolsó kártyalehúzást követően hány helyes számjegy érkezett (lehetséges értékek 0-tól 4-ig) és egy másik számértékkel kódoljuk azt, ha nem volt még kártyalehúzás vagy érvénytelen számjegy jött. Könnyen belátható, hogy csak egy ilyen "érvénytelen" állapotra van szükség, ugyanis csak egyfajta esetben lehet folytatás: ha kártyalehúzás jön. Az is nyilvánvaló, hogy az AV=4 esetet sem szükséges megkülönböztetnünk az érvénytelentől, mert ha egyszer már elvégeztük a nyitást, a továbbiakban ez az állapot nem különbözik az érvénytelentől (ismét kártyalehúzásra kell várnunk). Vizsgáljuk meg az alábbi állapotgráfot!
A fenti állapotgráfnak megfelelő véges automatát formálisan is lekódolhatnánk, de helyette inkább gondolkozzunk egy kicsit, mert az megtérül a programozáskor! ;-) Az inputnak két fajtája lehetséges: kártyalehúzás vagy számjegy érkezése.
A főprogram beállítja a verem helyét, az állapotváltozó értékét, engedélyezi a megszakításokat, majd végtelen ciklusban kiolvassa a zár állapotát és megjeleníti a LED-eken.
Az IT rutin menti a használt regisztereket, majd:
A hipotetikus processzornak a következő regiszterei vannak: A, F, B, C, D, E, H, L és SP regiszterek az alábbi értelmezéssel:
Az alábbi folyamatábra a főprogram működését írja le.
5. ábra: A főprogram működése
Amint a folyamatábrán is látható, a program végtelen ciklusban fut. Kártyalehúzás, valamint számjegy leütése esetén megszakítás keletkezik, ekkor a főprogram működését megszakítva a megszakítási rutin kerül meghívásra.
A használt mnemonikok a 8085 és a Z80 assembly nyelvére emlékeztetnek. Összesen 5 fajta címzési móddal találkozunk:
Néhány fontosabb mnemonik jelentése:
Lássuk a program kódját!
; hardver címek Z_LED EQU 1Ah ; zöld LED portcíme P_LED EQU 1Bh ; piros LED portcíme STATUSZ EQU 2Eh ; beléptető periféria státusz regiszterének portcíme ZAR_VEZ EQU 2Eh ; beléptető periféria zárvezérlő regiszterének portcíme ADAT EQU 2Fh ; beléptető periféria adatregiszterének portcíme ; ; egyéb konstansok HOSSZ EQU 04 ; a belépőkártyákhoz tartozó kód hossza KLE EQU 00000001b ; maszk a kártyalehúzás teszteléséhez SZJ EQU 00000010b ; maszk a számjegy beütésének teszteléséhez ZARALL EQU 00000100b ; maszk a zár állapotának kiolvasásához NYISD EQU 00000100b ; a nyitás parancs kódja ERVTLN EQU 255 ; az érvénytelen állapot kódja LAP_MER EQU 100H ; 256 byte, a belepőkártyák kódtáblájának laphatárra helyezéséhez ; ; változók címei AV EQU 2000h ; az állapotváltozót itt taroljuk (a RAM-ban) K_AZON EQU 2001h ; a kártyaazonosítót itt tároljuk ; A kódtáblát majd a főprogram után az EPROM-ban helyezzük el az értékek definiálásával! ; ; itt kezdődik a főprogram ORG 0000h ; a programkód elhelyezése a 0000h kezdőcímtől LDI SP,3FFFh ; veremmutató beállítása a RAM tetejére LDI A,ERVTLN ; az érvénytelen állapot kódja LD AV,A ; állapotváltozó beállítása EI ; megszakítások engedélyezése ; ; most jön a program főciklusa FOCIKL: IN STATUSZ ; a beléptető periféria állapotának kiolvasása ANI ZARALL ; zárállapot vizsgálata JNZ nyitva ; ha a D
2
bit 1-es volt: nyitva van ; ha nem ugrott el, akkor tudjuk, hogy zárva van! LDI A,0 ; kigyújtás OUT P_LED ; piros LED világítson LDI A,1 ; kioltás OUT Z_LED ; zöld LED ne világítson JMP FOCIKL ; végtelen ciklus NYITVA: LDI A,0 ; kigyújtás OUT Z_LED ; zöld LED világítson LDI A,1 ; kioltás OUT P_LED ; piros LED ne világítson JMP FOCIKL ; végtelen ciklus ; ; A kódtáblát laphatárra helyezzük az alábbi apró trükkel: ; (a fenti programkód rövidsége miatt nyilván "ORG 0100h"-val ekvivalens) ORG ($+LAP_MER-1) AND NOT (LAP_MER-1) KODTABLA: DB 1, 4, 5, 2, 3, 4, 7, 8, 8, 0, 2, 3, 6, 2, 4, 9 DB 4, 4, 3, 1, 7, 8, 0, 1, 3, 6, 4, 1, 4, 7, 6, 5 DB ... ; összesen 64 sor, soronként 16 értékkel, azaz 64x16=1024=4x256 db számjegy ; ; most jön a megszakítási rutin ORG 1000h ; az IT rutin elhelyezése az 1000h címtől ; minden olyan regiszter értékét elmentjük, amit használni fogunk PUSH AF ; az A regiszter és a flag-ek értékének mentése a verembe PUSH BC ; a BC regiszterpár értékének mentése a verembe PUSH HL ; a HL regiszterpár értékének mentése a verembe IN STATUSZ ; mi volt a megszakítás oka? ANI KLE ; kártyalehúzás jött? JNZ LEHUZ ; igen ; biztos, hogy számjegy volt, nem mert kártyalehúzás! IN ADAT ; számjegy kiolvasása (kötelező, mert különben az IT fennmarad) LD B,A ; a számjegy mentése LD A,AV ; állapotváltozó betöltése CPI ERVTLN ; JZ IT_VEGE ; ha érvénytelen, akkor nincs további teendőnk ; az AV értéke szükségképpen a [0,HOSSZ-1] intervallumba esik (lásd később: HOSSZ tesztelése) ; a várt számjegy a KODTABLA+LAP_MER*AV+K_AZON címen van, készítsük el a címet! LD HL,KODTABLA ; kódtábla kezdőcíme, a laphatárra helyezés miatt L érteke 0 ADD H ; A:=A+H, túlcsordulás tudjuk, hogy nem lesz! LD H,A ; A KODTABLA-ban az (AV). lapon keressük a ... LD L,K_AZON ; ... (K_AZON). értéket LD A,(HL) ; a várt számjegy kiolvasása indirekt címzéssel CMP B ; fent B-be mentettük a beérkezett számjegyet JNZ ROSSZJ ; hibás a beütött számjegy ; a beütött számjegy helyes volt, ezért AV 1-gyel nő: LD A,AV INC A LD AV,A CPI HOSSZ ; elértük-e már a kódsorozat hosszát? JNZ IT_VEGE ; még nem ; ha mar elértük: nyitunk + AV:=ERVTLN NYITAS: LDI A,NYISD ; nyitás parancs kódja OUT ZARVEZ ; parancs kiadása ROSSZJ: LDI A, ERVTLN ; az új állapot: érvénytelen LD AV,A ; állapot eltárolása JMP IT_VEGE ; ugrás az IT rutin végére ; kártyalehúzás feldolgozása LEHUZ: IN ADAT ; belépőkártya kódjának kiolvasása LD K_AZON,A ; kártyakód eltárolása LDI A,0 ; ez lesz az új állapot LD AV,A ; állapotváltozó beállítása ; Az IT rutin végén a regisztereket fordított sorrendben kell visszatölteni! IT_VEGE: POP HL POP BC POP AF EI ; Az IT engedélyezésének hatása egy utasítást késik! RET ; visszatérés az IT rutinból END ; ez a direktíva jelzi, hogy vége a fordításnak
A hallgatók önállóan gondolják végig, hogy miben különbözne a program, ha nem használnánk megszakítást, hanem helyette állapotolvasással kérdeznénk le, hogy történt-e kártyalehúzás, illetve érkezett-e számjegy! Módosítsák ennek megfelelően a programot önállóan!
FIGYELEM! Először mindenki készítse el a saját megoldását és csak utána olvassa el az alábbi minta megoldást!
Az IT rutin már tartalmazza mind a két esemény (kártyalehúzás, számjegy érkezése) feldogozását; készítsünk ezekből szubrutinokat! A főprogram végtelen ciklusában mentsük el a beolvasott állapotot a B regiszterbe (ez egy utasítás beszúrását jelenti), majd a jelzőbitek beállítása után a B regiszterből az állapotot visszatöltve vizsgáljuk meg, hogy fennáll-e a kártyalehúzás illetve a számjegy érkezése, amelyik igen, ahhoz hívjuk meg a megfelelő szubrutint.
Megjegyzések:
; hardver címek Z_LED EQU 1Ah ; zöld LED portcíme P_LED EQU 1Bh ; piros LED portcíme STATUSZ EQU 2Eh ; beléptető periféria státusz regiszterének portcíme ZAR_VEZ EQU 2Eh ; beléptető periféria zárvezérlő regiszterének portcíme ADAT EQU 2Fh ; beléptető periféria adatregiszterének portcíme ; ; egyéb konstansok HOSSZ EQU 04 ; a belépőkártyákhoz tartozó kód hossza KLE EQU 00000001b ; maszk a kártyalehúzás teszteléséhez SZJ EQU 00000010b ; maszk a számjegy beütésének teszteléséhez ZARALL EQU 00000100b ; maszk a zár állapotának kiolvasásához NYISD EQU 00000100b ; a nyitás parancs kódja ERVTLN EQU 255 ; az érvénytelen állapot kódja LAP_MER EQU 100H ; 256 byte, a belepőkártyák kódtáblájának laphatárra helyezéséhez ; ; változók címei AV EQU 2000h ; az állapotváltozót itt taroljuk (a RAM-ban) K_AZON EQU 2001h ; a kártyaazonosítót itt tároljuk ; A kódtáblát majd a programrészek után az EPROM-ban helyezzük el az értékek definiálásával! ; ; itt kezdődik a főprogram ORG 0000h ; a programkód elhelyezése a 0000h kezdőcímtől LDI SP,3FFFh ; veremmutató beállítása a RAM tetejére LDI A,ERVTLN ; az érvénytelen állapot kódja LD AV,A ; állapotváltozó beállítása ; a megszakítások engedélyezésére most nincs szükség ; ; most jön a program főciklusa FOCIKL: IN STATUSZ ; a beléptető periféria állapotának kiolvasása
MOV
B,A
; a periféria állapotának elmentése
ANI ZARALL ; zárállapot vizsgálata JNZ nyitva ; ha a D
2
bit 1-es volt: nyitva van ; ha nem ugrott el, akkor tudjuk, hogy zárva van! LDI A,0 ; kigyújtás OUT P_LED ; piros LED világítson LDI A,1 ; kioltás OUT Z_LED ; zöld LED ne világítson JMP TOV1 ; további vizsgálatokra van szükség... NYITVA: LDI A,0 ; kigyújtás OUT Z_LED ; zöld LED világítson LDI A,1 ; kioltás OUT P_LED ; piros LED ne világítson TOV1:
MOV
A,B
; periféria elmentett állapotának visszatöltése
ANI KLE ; kártyalehúzás jött? JZ TOV2 ; ha nem: további vizsgálat...
CALL
KLEHUZ
; igen: meghívjuk a szubrutint
JMP FOCIKL ; ekkor már számjegy nem jöhetett...
TOV2:
MOV
A,B
; periféria elmentett állapotának visszatöltése
ANI
SZJ
; számjegy jött?
JZ
FOCIKL
; ha nem: folytatjuk a főciklust
CALL SZAMJ
;
igen: meghívjuk a szubrutint
JMP
FOCIKL
; folytatjuk a főciklust
; ; most jönnek a szubrutinok ; ; számjegy érkezésének feldolgozása SZAMJ: IN ADAT ; számjegy kiolvasása LD B,A ; a számjegy mentése LD A,AV ; állapotváltozó betöltése CPI ERVTLN ; JZ SZ_VEG ; ha érvénytelen, akkor nincs további teendőnk ; az AV értéke szükségképpen a [0,HOSSZ-1] intervallumba esik (lásd később: HOSSZ tesztelése) ; a várt számjegy a KODTABLA+LAP_MER*AV+K_AZON címen van, készítsük el a címet! LD HL,KODTABLA ; kódtábla kezdőcíme, a laphatárra helyezés miatt L érteke 0 ADD H ; A:=A+H, túlcsordulás tudjuk, hogy nem lesz! LD H,A ; A KODTABLA-ban az (AV). lapon keressük a ... LD L,K_AZON ; ... (K_AZON). értéket LD A,(HL) ; a várt számjegy kiolvasása indirekt címzéssel CMP B ; fent B-be mentettük a beérkezett számjegyet JNZ ROSSZJ ; hibás a beütött számjegy ; a beütött számjegy helyes volt, ezért AV 1-gyel nő: LD A,AV INC A LD AV,A CPI HOSSZ ; elértük-e már a kódsorozat hosszát? JNZ SZ_VEGE ; még nem ; ha mar elértük: nyitunk + AV:=ERVTLN NYITAS: LDI A,NYISD ; nyitás parancs kódja OUT ZARVEZ ; parancs kiadása ROSSZJ: LDI A, ERVTLN ; az új állapot: érvénytelen LD AV,A ; állapot eltárolása SZ_VEG: RET ; visszatérés a szubrutinból
; kártyalehúzás feldolgozása KLEHUZ: IN ADAT ; belépőkártya kódjának kiolvasása LD K_AZON,A ; kártyakód eltárolása LDI A,0 ; ez lesz az új állapot LD AV,A ; állapotváltozó beállítása RET ; visszatérés a szubrutinból ; ; A kódtáblát laphatárra helyezzük az alábbi apró trükkel: ORG ($+LAP_MER-1) AND NOT (LAP_MER-1) KODTABLA: DB 1, 4, 5, 2, 3, 4, 7, 8, 8, 0, 2, 3, 6, 2, 4, 9 DB 4, 4, 3, 1, 7, 8, 0, 1, 3, 6, 4, 1, 4, 7, 6, 5 DB ... ; összesen 64 sor, soronként 16 értékkel, azaz 64x16=1024=4x256 db számjegy
;
END ; ez a direktíva jelzi, hogy vége a fordításnak