Obsah
Překladač se skládá z preprocesoru, parseru a vlastního jádra, které přeloží jednu konkrétní instrukci pro daný procesor. Tato jádra jsou napsána jako zásuvné moduly.
Objekt:
var M6809 = { "parseOpcode": function (s,vars) { ... }, "endian": true };
Objekt má tedy jednu jedinou metodu parseOpcode a má volitelný atribut endian – pokud je true, jedná se o Big Endian (Motorola), jinak je procesor Little Endian (Intel, 6502).
Metoda parseOpcode dostává dva parametry: první je s, což je rozparsovaná instrukce, viz dále, a druhý je vars, což jsou hodnoty proměnných, které jsou v dané chvíli známé (tj. v prvním průběhu ty, které jsou už definované, ve druhém všechny, včetně dopočítaných).
Metoda parseOpcode vezme údaje z objektu s, identifikuje instrukci, doplní její operační kód a informace o celkové délce instrukce, a vrátí rozšířený objekt s.
V okamžiku volání metody má objekt s tento formát:
{ numline: 42, opcode: "PSHU", params: Array[1], addr: "0x100", lens: Array[0], bytes: 0 }
- opcode je název instrukce (uppercase)
- numline je číslo aktuálního řádku
- addr je adresa, na které je instrukce
- params je pole s parametry, které jsou zadány za instrukcí. Např. u instrukce „MOV A,B“ bude mít params dvě položky, „A“ a „B“
- bytes, lens jsou položky, které musí metoda doplnit. bytes je celková délka instrukce a lens je pole, v němž jsou jednotlivé operační kódy a kódy parametrů.
Pokud není instrukce rozpoznána, musí metoda vrátit hodnotu null. Překladač pak_ _vyvolá chybový stav „nerozpoznaná instrukce“.
Prosté instrukce
Jednoduché instrukce, jako „NOP“, které zabírají jediný bajt a nemají parametry, přeloží metoda tak, že do s.bytes uloží 1 a do s.lens[0] zapíše operační kód instrukce.
Instrukce s parametrem
Důležité je myslet na to, že hodnotu parametru dopočítá překladač ve druhém průchodu, takže se o ni nestará překládací modul. Ten ale musí doplnit volání parsovací funkce. Příklad – instrukce MVI A, 123
V poli „params“ budou hodnoty „A“ a „123“. Operační kód se spočítá z názvu „MVI“ a prvního parametru (params[0]). Do atributu s.bytes zadáme 2 a do pole lens[0] se zapíše operační kód (0x3F). Do pole lens[1] je potřeba zapsat funkci, která druhému průchodu řekne, že má spočítat aktuální hodnotu druhého parametru (params[1]). Takto:
lens[1] = function(vars){return Parser.evaluate(params[1],vars);}
Tedy jako hodnotu předáváme funkci s jedním parametrem (vars, známé pole proměnných) a funkce vrací výsledek výrazu Parser.evaluate(params[1],vars) – což je interní funkce překladače, která parsuje výrazy.
V případě dvoubajtového parametru (třeba LXI) se do lens[0] zapíše operační kód, do lens[1] výše uvedená funkce a do lens[2] hodnota null. Překladač pak doplní vypočítanou hodnotu na místa 1 a 2.
Vícebajtové operační kódy
Není problém, stačí jen správně nastavit bytes a kódy do pole lens.
Relativní skoky, krátké/dlouhé instrukce atd.
Lze využít volání Parser.evaluate(_výraz, vars), který vrátí vypočítanou hodnotu výrazu. Pro spočítání relativního skoku lze použít pseudoproměnnou _vars._PC, která obsahuje aktuální adresu překládané instrukce.
Problém může nastat u procesorů jako je 6502, kde lze zapsat instrukci LDA #10 a překladač se musí rozhodnout, jestli využije krátkou verzi se zero page, nebo dlouhou s absolutní adresou. V takovém případě je možné právě využít volání Parser.evaluate() a nechat si spočítat aktuální hodnotu výrazu. Pokud je známá, tj. nejsou v ní použité zatím nedefinované proměnné, dostanete výsledek, který můžete použít při rozhodování. Pokud je ve výrazu nějaká dopředná reference, nastane problém a Parser.evaluate vyhodí výjimku. Je potřeba si ji odchytit.
Výjimky
Pokud metoda narazí např. na podivnou kombinaci parametrů, musí vyhodit výjimku. Příklad:
throw "Bad addressing mode at line "+s.numline;