Druhy instrukcí MB5016
Minule jsme skončili definicí obecného formátu instrukcí v binárním strojovém kódu. Nyní si stručně popíšeme jednotlivé skupiny instrukcí. Detaily lze najít v souboru README.md v gitovém repozitáři.
Instrukce pro přenos dat mezi registry jsou MV pro zkopírování hodnoty zdrojového do cílového registru a EXCH pro výměnu hodnot dvou registrů. Do této skupiny patří také instrukce CSRR a CSRW pro čtení a zápis hodnot řídicích registrů.
Pro načtení dvou bajtů z paměti do cílového registru slouží instrukce LD. Adresa se bere ze zdrojového registru. Naopak instrukce STO uloží hodnotu zdrojového registru na adresu uloženou v cílovém registru. Podobně fungují LDB a STOB, které ale přenáší pouze jeden bajt mezi nižší polovinou registru a pamětí. Ještě existují speciální instrukce LDIS a DDSTO, které si vysvětlíme později.
Aritmetické a logické operace kromě výpočtu výsledku také nastavují bity příznakového registru. Základní aritmetické funkce jsou inkrement a dekrement o 1 nebo 2, sčítání, odčítání, bitové posuny a převrácení pořadí bitů: INC1, INC2, DEC1, DEC2, ADD, SUB, SHL, SHR, SHRA, REV. Logické operace zahrnují: NOT, AND, OR, XOR. Do této skupiny instrukcí patří i porovnání CMPS, CMPU, které fungují podobně jako odčítání, ale výsledek nikam neukládají a pouze nastavují příznaky.
Obecně velký problém pro hardwarovou implementaci představují násobení a dělení, protože čistě kombinační obvody pro tyto operace vychází hodně velké. Naštěstí FPGA Cyclone IV obsahuje specializované hardwarové násobičky, proto bylo možné snadno přidat instrukce násobení ( MULSS, MULSU, MULUS,
MULUU). Kombinační děličku sice umí syntetizér automaticky poskládat z logických elementů, ale zabrala by skoro polovinu celého FPGA. Prostorově méně náročnější by vyšlo dělení počítané postupně v několika strojových taktech. Třetí, nejpomalejší, ale nakonec zvolená možnost, je řešit dělení programově.
Podmíněné instrukce se chovají různě podle hodnoty vybraného bitu příznakového registru. Každá instrukce existuje v 16 variantách, zahrnujících vždy číslo jednoho z osmi příznakových bitů a testovanou hodnotu 0 nebo 1. Podmíněné alternativy mají instrukce MV, EXCH, LD. Specifická je podmíněná verze LDIS, která buď načte hodnotu z paměti, nebo inkrementuje hodnotu zdrojového registru.
Když se zamyslíme nad fungováním instrukce LD, narazíme na problém. Abychom mohli pomocí LD načíst hodnotu z paměti, musíme nejdřív nějak dostat do registru adresu této hodnoty. K tomu nám chybí instrukce, obvykle existující v jiných instrukčních sadách, zapisující do registru konstantní hodnotu definovanou přímo v programu.
Tento nedostatek bychom mohli obejít pomocí poněkud krkolomné konstrukce, pro jejíž fungování je důležité, že k inkrementování registru pc dochází okamžitě po načtení instrukce z paměti. Tedy během provádění instrukce je v registru adresa následující instrukce.
mv r0, pc # uložení adresy následující instrukce do registru inc2 pc, pc # přeskočí následující dva bajty $data_w 0x1234 # hodnota uložená v paměti inc2 r0, r0 # posun na adresu dat ld r0, r0 # načte hodnotu z paměti do registru r0
Čtyři instrukce a zabraných 10 B v paměti pro zápis každé 16bitové konstanty není právě ideální, zvlášť když těchto operací bývá v kódu poměrně hodně. Řešení nabízí instrukce LDIS (LoaD and Increment Source), která načte hodnotu z adresy ve zdrojovém registru do cílového registru a pak přičte 2 k hodnotě zdrojového registru. Předchozí sekvenci instrukcí můžeme zkrátit na:
ldis r0, pc $data_w 0x1234
Instrukce LDIS, pokud se pro adresu použije registr sp provádí operaci POP (přečtení hodnoty ze zásobníku). Proto máme i duální instrukci DDSTO (Decrement Destination and STOre) fungující jako PUSH (uložení na zásobník).
Zatím jsme se nedostali k instrukcím skoků a volání podprogramů. Díky tomu, že registr pc je zařazen mezi obecnými registry, je možné pro tyto účely použít už představené instrukce. Skok na adresu uloženou v registru rX lze zapsat jako LD pc, rX, buď nepodmíněnou nebo podmíněnou variantu. Nepodmíněný skok na adresu zapsanou přímo v kódu programu se provede instrukcí LD pc, pc.
Pokud chceme tento skok podmíněný, musíme v případě neprovedení skoku přeskočit v kódu cílovou adresu, což zařídí podmíněná varianta instrukce LDIS pc, pc. Volání podprogramu vyžaduje nejprve uložení cílové adresy do registru ca. Následně se pomocí EXCH ca, pc nastaví adresa podprogramu do pc a návratová adresa (stará hodnota pc) se uschová do ca. Návrat z podprogramu provede instrukce MV pc,
ca.
Přerušení a výjimky
Procesor MB5016 podporuje i obsluhu přerušení. Při příchodu přerušení se podle zdroje přerušení nastaví příslušný bit v horním bajtu příznakového registru. Pokud je povoleno přerušení bitem ie v registru f, místo následující instrukce se atomicky zakáže přerušení a zavolá se obslužná rutina přerušení, což se provede výměnou hodnot registrů ia a pc. Když je v bitu ie nula, odloží se volání handleru do okamžiku nastavení bitu ie na jedničku.
V počítači MB50 existují dva zdroje přerušení. Jeden jsou systémové hodiny, generující přerušení každých 10 ms. Druhý zdroj přerušení je klávesnice, která vyvolá přerušení pro každý přijatý scan code.
Pro návrat z přerušení musíme zkopírovat uloženou návratovou adresu z registru ia do registru pc. Zároveň je potřeba připravit se na následující volání obsluhy přerušení. Příprava zahrnuje nastavení adresy handleru do registru ia a povolení přerušení nastavením bitu ie v příznakovém registru. Pro nastavení ia je nutné mít adresu handleru někde k dispozici.
K tomuto účelu slouží řídicí registr csr1. Nové volání může nastat okamžitě, jestliže přišlo přerušení během současného běhu handleru, který se vykonává se zakázaným přerušením. Všechny tři akce, tedy kopírování ia do pc, kopírování csr1 do ia a povolení přerušení je proto potřeba provést atomicky. Za tím účelem existuje speciální instrukce RETI.
Stejně jako přerušení fungují i výjimky. Rozdíl je v tom, že přerušení je vyvoláno externě, ale výjimka je následkem interní chyby při provádění instrukce, např. načtení instrukce s neznámým opcode. Z implementačních důvodů jsou pro výjimky rezervované dva bity příznakového registru. Při vyvolání výjimky je nastaven jeden bit, když se zavolá obsluha, nastaví se druhý bit a první se vynuluje. Protože výjimka musí být ošetřena okamžitě, výskyt výjimky, když je zakázané přerušení, způsobí zastavení procesoru.
Obsah následujícího dílu
V příštím díle se budeme věnovat mikroarchitektuře, tedy návrhu a implementaci vnitřní struktury procesoru MB5016.