Mindennapi processzorunk rovat:
Motorola 68HC000FC12 (mellette a méretbeli összehasonlítás kedvéért a tegnapi 40-tűs DIP CPU)
Motorola 68000 II. rész
Felépítése
Címbusz
A 68000-es CPU-ban 24-bites címbuszt valósítottak meg, ami legfeljebb 16 MB fizikai memória egyidejű megcímzését tette lehetővé. A címtárolás és kiszámítás 32 biten történt, de a legmagasabb helyiértékű bájtot a lábak hiánya miatt figyelmen kívül hagyták. Ez a megoldás lehetővé tette a teljesen 32-bites címtérre tervezett szoftverek futtatását. Belső felépítésében a CPU így teljesen 32-bites volt, amivel jócskán megelőzte a teljesen 32-bites Intel 80386-ost. A Motorola szándéka a belső 32-bites címtérrel az előremenőleges kompatibilitás volt, így biztosítva azt, hogy a 68000-esre írt szoftverek a 68000-es utasításkészlet későbbi, 32-bites megvalósításaiból is profitálni tudjanak. Éles ellentétben áll ez a gondolkodásmód például az Intel 80286-os processzorában látott szegmentált megoldás által felvetett problémákkal, amelyeket végül is teljes egészében szoftveresen kell elhárítani. Nyilvánvalóvá válik belőle, hogy a 68000-es tervezői eleve 32-bitesnek szánták a sorozatot.
Persze, ez a nagyszerű tervezői előrelátás sem akadályozhatta meg a programozókat abban, hogy előrefelé inkompatibilis programokat írjanak. A "24-bites" programok, amelyek eldobták a legfelső címbájtot, vagy azt más, a címzéstől eltérő célokra használták, összeomolhattak a 32-bites 68K utódokon.
A belső regiszterek
A processzornak nyolc 32-bites általános célú adatregisztere volt (D0-D7), és nyolc címregisztere (A0-A7). Az utolsó címregiszter egyben a standard veremmutató szerepét is eljátszotta, így egyaránt lehetett A7-nek vagy SP-nek nevezni. Akárhogyis, ez tisztességes számú regisztert tartalmazó készlet volt. Ahhoz elég kicsi volt, hogy a 68000-es gyorsan reagáljon a megszakításokra (hiszen csak 15 vagy 16 regisztert kellett menteni), de mégis elég nagy volt ahhoz, hogy a legtöbb számítást gyorsan, memóriaelérés nélkül elvégezhesse a CPU.
A kétféle regisztertípus ugyan enyhén bosszantó volt, de a gyakorlatban nem volt nehéz a használatuk. Állítólag lehetővé tette a CPU tervezői számára a nagyobb fokú párhuzamosság elérését, egy kiegészítő végrehajtó egység implementálásával a címregiszterek számára.
Az egész számok tárolása a 68000 családban big-endian.
A státuszregiszter
A 68000-es összehasonlító, aritmetikai és logikai utasításai az eredménytől függően állították be a státuszregiszter bitjeit a későbbi feltételes ugróutasítások számára. A jelzőbitek a következők voltak: "zero" (Z), "carry" ©, "overflow" (V), "extend" (X) és "negative" (N). Az "extend" (X) bit külön jelentéssel bírt, mert el volt különítve az átviteli bittől (carry). Az aritmetikai, logikai és bitforgató utasításokból származó extra bit így megkülönböztethető volt az egyéb műveletekből származó carry bittől.
Az utasításkészlet
A tervezők megpróbálták a CPU assembly nyelvét ortogonálissá tenni. Ezért az utasításokat szétbontották műveletre és címzésmódra, és majdnem minden xímzésmód elérhető volt majdnem minden utasítás számára. Sok programozó utálta a „közel”ortogonális kialakítást, míg mások nagyon hálásak voltak az erőfeszítésért.
Bitszinten az assemblert író személy tisztán láthatta, hogy ezek az „utasítások” a számos műveleti kód bármelyikévé lefordulhattak. Ez egész jó kompromisszum volt, mert ugyanazt a kényelmet biztosította, mint egy valódi ortogonális gép, de megadta a CPU tervezőinek is azt a szabadságot, hogy kitölthették a műveleti kódok táblázatát.
Mindössze 56 utasítás mellett a legkisebb utasítás mérete is nagy volt a 16-bites korszakban. Ráadásul számos utasítás és címzésmód extra szavakat tett a címek mögé, vagy extra címzésmód-biteket stb.
Sok tervező úgy gondolta, hogy az MC68000 architektúrának kompakt kódja volt az áráért, különösen akkor, ha azt compiler állította elő. Ez a hit a még tömörebb kódban számos tervezőt állított az architektúra mellé, és hozzájárult annak hosszú életéhez.
Ez a hit (vagy tulajdonság, a tervezőtől függően) folytatódott a későbbi, továbbfejlesztett CPU-k utasításkészletével kapcsolatban is egészen az ARM architektúráig, amelynek Thumb utasításkészlete hasonlóan tömör kódot adott.
Privilégiumszintek
A CPU, és később az egész család, pontosan két privilégiumszintet valósított meg. A felhasználói módban a CPU minden funkciója hozzáférhető volt, kivéve a megszakítási rendszert. A supervisor szint hozzáférést biztosított mindenhez. Egy megszakítás mindig supervisor szinten futott. A státuszregiszterben tárolt supervisor bit látható volt a felhasználói programok számára.
Ennek a rendszernek a valós előnye az volt, hogy a supervisor szint saját, külön veremmutatóval bírt. Ez lehetővé tette, hogy egy multitaszk rendszer programszálanként nagyon kis vermeket használhasson, mivel a tervezőknek nem kellett előre nagyméretű memóriát lefoglalni a maximális megszakításmélységet is kiszolgáló vermek számára.
Megszakítások
A CPU hét megszakítási szintet ismert. A szintek 1-től 7-ig szigorú prioritási sorrendet élveztek. Ennek megfelelően a magasabb számú megszakítás mindig megszakíthatta az alacsonyabb számút. A státuszregiszterben egy privilegizált utasítás lehetővé tette az éppen aktuális megszakítás minimális szintjének beállítását, így blokkolva az alacsonyabb szintű megszakításokat. A 7. szint nem maszkolható volt, vagyis az NMI. Az 1. szintet bármelyik nagyobb szint megszakíthatta. A 0. szint volt a nem megszakítási szint, vagyis a normál programfutás szintje. A szintet a státuszregiszter tárolta, és a felhasználói programok számára látható volt.
A hardveres megszakítások a CPU e célra szolgáló három inputjának valamelyikére futottak be, ahol a legmagasabb, még függőben levő szintű bekódolódott. Gyakran külön megszakításvezérlőt alkalmaztak a megszaktások kódolásához, bár azokban a rendszerekben, amelyekben nem volt szükség háromnál több hardveres megszakításra, lehetséges volt a jelek közvetlenül a CPU-ra történő bekötése is, némileg komplexebb szoftver árán. A megszakításvezérlő lehetett akár egy egyszerű 74LS148 prioritás kódoló, vagy része lehetett egy VLSI chipnek, mint pl. az MC68901 multifunkció periférivezérlőnek, amelynek volt még UART, ja, számláló/időzítője és páehuzamos I/I portja.
A "kizárási táblázat" (az interrupt vektorok címei) fixen a memória első kilobájtjában kaptak helyet, a 0. és 1023. bájtok között. Összesen 256 db 32-bites vektor állt így rendelkezésre. Az első vektor a verem kezdőcíme volt, a második pedig a programterületé. A 3. és 15. közötti vektorok hibajelzésekre szolgáltak: buszhiba, címhiba, illegális utasítás, nullával való osztás, CHK & CHK2 vektor, privilégium megsértése, és néhány foglalt vektor, amelyek később a line 1010 emulátorrá és a line 1111 emulátorrá váltak, és a hardveres töréspont vektora. A valódi megszakítások a 24. vektorral kezdődtek: az álmegszakítás (nem volt hardveres nyugtázás), és az 1-től a 7. szintig terjedő autovektorok, aztán a 15 db TRAP vektor, majd még néhány foglalt vektor, végül a felhasználó által definiálható vektorok.
Mivel legalább a programkezdetre mutató vektornak érvényes értéket kellett tartalmaznia közvetlenül a bekapcsolás/reset után, a rendszerekbe közönségesen beépítettek némi ROM-ot is, ami a 0. bájton kezdődött, és a vektorokat és a boot kódját tartalmazta. Ugyanakkor egy általános célú rendszernél kívánatos lehet, hogy az operációs rendszer tetszése szerint megváltoztathassa a megszakítási vektorok tartalmát. Ezt gyakran úgy oldották meg, hogy a ROM-ban tárolt értékek a RAM-ban definiált újabb ugrótáblázatra mutattak, más esetekben a ROM-ot bankolással már futásidőben átkapcsolták RAM-má.
A 68000 nem teljesítette teljes egészében a már említett Popek és Goldberg-féle virtualizációs követelményeket, mert volt egyetlenegy nem privilegizált utasítása, a „MOVE from SR”, amivel felhasználói jogosultságszinten is lehetett olvasni a privilegizált tárhelyek egy kis részét.
A 68000 továbbá nem rendelkezett a virtuális memória egyszerű támogatásának képességével, ami pedig szükséges a hibás/tiltott memóriaelérések elfogásához és a belőlük való visszatéréshez. Bár a 68000-esnek ott van a buszhiba kizárása, ami használható ilyen célra, de ez nem ment meg annyi információt a processzor aktuális állapotából, amivel lehetővé tenné a programfutás folytatását azután, hogy az operációs rendszer lekezelte a hibát. Sok társaság épített olyan 68000-alapú Unix rendszereket, amelyek virtuális memóriával is dolgoztak: ezekben két, eltérő fázissal órajelezett 68000-es CPU-t használtak. Ha a „vezető” processzor hibás memóriaelérést észlelt, egy extra hardver megszakíthatta a „fő” processzort, hogy megóvja a hibára történő ráfutástól. Ez az interrupt rutin megvalósíthatta a virtuális memória funkcióit, és újraindíthatta a „vezető” 68000-est a megfelelő állapotban, miután a „fő” processzor visszatért a megszakító rutinból.
Ezeket a problémákat a 68K architektúra következő fő revíziójában, az MC68010-esben javították. A buszhiba és címhiba kizárások nagy mennyiségű processzorinformációt tároltak el a supervisor veremben, hogy segítsék a helyreállást, és a „MOVE from SR” utasítás is jogosultsághoz kötött lett. Helyébe a nem privilegizált "MOVE from CCR" utasítás lépett, hogy a felhasználói módú programok futása ne sérüljön; ha szükséges volt, az operációs rendszer elfoghatta, és emulálhatta a felhasználói módú „MOVE from SR” utasítást.
Az utasításkészlet részletei
A standard címzésmódok az alábbiak:
Regiszter direkt
adatregiszter, pl. "D0"
címregiszter, pl. "A6"
Regiszter indirekt
Egyszerű cím, pl. (A0)
Cím utólagos növeléssel, pl. (A0)+
Cím előzetes csökkentéssel, pl. -(A0)
Cím 16-bites előjeles eltolással, pl. 16(A0)
A tényleges csökkentési vagy növelési méret az operandustól függött: egy bájt olvasása eggyel, szóé kettővel, long-é pedig néggyel módosította a címregisztert.
Regiszter indirekt index-szel
8-bites előjeles eltolás, pl. 8(A0, D0) vagy 8(A0, A1)
PC (programszámláló) relatív, elmozdítással
16-bites előjeles eltolás, pl. 16(PC). Ez nagyon jól használható mód volt.
8-bites előjeles eltolás index-szel, pl. 8(PC, D2)
Abszolút memóriahely
Akár szám, pl. "$4000", akár egy szimbolikus név, amelyet az assembler fordított le
A legtöbb 68000 assembler a "$" szimbólumot használta a hexadecimális számok jelölésére a "0x" helyett.
Azonnali mód
Magában az utasításban tárolva, pl. "#400".
Plusz: hozzáférés a státuszregiszterhez, és a későbbi modellekben más, különleges regiszterekhez is.
A legtöbb utasításnak voltak pont-betű utótagjai, amelyekkel az utasítások 8-bites bájtokon (".b"), 16-bites szavakon (".w"), vagy 32-bites long értékeken (".l") dolgoztak.
A legtöbb utasítás diadikus, vagyis az utasításnak van forrása és célja, és a cél megváltozik. Az említésre méltó utasítások az alábbiak voltak:
Aritmetikai: ADD, SUB, MULU (előjel nélküli szorzás), MULS (előjeles szorzás), DIVU, DIVS, NEG (additív negálás) és CMP (kivonással elvégzett összehasonlító utasítás, ami azonban csak a státuszregiszter tartalmát módosította, a kivonás eredményét nem tárolta)
BCD aritmetika: ABCD és SBCD
Logikai: EOR (kizáró vagy), AND, NOT (logikai nem)
Eltolás: (logikai, pl. a jobbra tolás 0-t tett a legmagasabb bitbe) LSL, LSR, (aritmetikai eltolások, pl. a legmagasabb bit előjelként történő kiterjesztése) ASR, ASL, (forgató utasítások az eXtend bittel vagy anélkül:) ROXL, ROXR, ROL, ROR
Bitmanipuláció a memóriában: BSET (1-re), BCLR (0-ra) és BTST (beállította a Zero bitet)
Többfeladatos környezet felügyelete: TAS, test-and-set, ami egy lyan buszműveletet hasjtott végre, aminek során a szemaforok számára engedélyezte számos processzor szinkronizálását közös memória megosztása céljából
Folyamatellenőrzés: JMP (ugrás), JSR (szubrutinhívás), BSR (relatív szubrutinhívás), RTS (visszatérés szubrutinból), RTE (visszatérés kizárásból, pl megszakításból), TRAP (szoftveres kizárás, hasonló a szoftveres megszakításhoz), CHK (feltételes szoftveres kizárás)
Elágazás: Bcc (elágazás, ahol „cc” a lehetséges 16 feltétel valamelyikét jelenti).
Csökkentés-és-elágazás: DBcc (ahol "cc" jelentése ugyanaz, mint az elágazásnál), ami csökkentette az egyik adatregisztert, és ugrott, feltéve hogy a feltétel
még igaz volt, de a regiszter még nem csökkent le a -1 értékre. A -1 végértékként történő használata 0 helyett megkönnyítette az olyan ciklusok programozását, amelyeknek nem volt teendőjük akkor, ha a ciklusváltozó kezdőértéke 0 volt, anélkül, hogy ezt a ciklus elején külön ellenőrizni kellett volna.
... a trip back in time is all I need ...