Emulujeme osmibit JavaScriptem

Napsat emulátor osmibitového procesoru v JavaScriptu není nijak obtížné. Vždyť co to je osmibitový procesor, že? 🙂

Ve svém IDE jsem chtěl uživatelům nabídnout možnost simulovat jednotlivé procesory a odkrokovat si přeložený kód. Což znamená, že jsem si musel napsat emulátor jednotlivých procesorů. Není to složitá práce…

Osmibitové procesory jsou velmi dobře dokumentované, často až na úroveň jednotlivých hradel. Do takové hloubky jít nemusíte, stačí, když emulujete chování jednotlivých instrukcí. Princip je jednoduchý. Připravíte si proměnné pro jednotlivé registry, připravíte si pole s 65536 položkami (jako že adresní prostor) a inicializujete si to všechno. Jeden „krok“ emulovaného procesoru pak znamená vykonání jedné instrukce.

V každém kroku si přečtete operační kód instrukce (na ni odkazuje registr PC), dekódujete ji a provedete, co je potřeba. Dekódování u osmibitu typu 8080 klidně udělejte pomocí jednoho SWITCH se spoustou CASE. U 6502 by stálo za úvahu rozložit kód na instrukci a operand (ale já jsem zbaběle udělal zase CASE).

Předem jsou nadefinované funkce pro základní operace – sčítání, odčítání, logické operace – a pro manipulaci s pamětí. Čtení a zápis z/do paměti není řešený přímým přístupem k poli, ale pomocí funkcí. Pak je možné snáz obsloužit některé stavy – například zakázat zápis do oblasti, kde je ROM, nebo vyřešit emulaci periferií, připojených do adresního prostoru. V emulaci, která je v IDE, je to jen prostý přístup k poli, ale třeba už v emulátoru PMI-80 je potřeba některé stavy ošetřit.

Všimněte si operace s vlastností cycles. Je to interní počítadlo hodinových taktů a je důležité k synchronizaci (v IDE není potřeba, ale v emulátorech reálných strojů už jo).

Představte si stroj s procesorem 8080, který běží na 2MHz (třeba takové PMD-85). To znamená, že za sekundu proběhnou dva miliony hodinových taktů. Nikoli instrukcí, těch bude míň. No a pro správné časování je zapotřebí, aby těch našich „virtuálních cyklů“ proběhlo za sekundu úplně stejně, tedy dva miliony. V ideálním světě bychom volali každou dvoumiliontinu sekundy jedno T, ale to je nesmysl. Ve skutečnosti není potřeba, aby všechny instrukce probíhaly lineárně – nic nebrání tomu, aby emulace probíhala metodou „ryc – a pak nic“. Tedy třeba udělat dva miliony T co nejrychleji, a pak zase počkat na další sekundu, pak zase dva miliony T, zase počkat do celé sekundy… V praxi by to ale hodně „cukalo“, tak se volí rozumný kompromis. Třeba 50 Hz. To znamená, že každých dvacet milisekund provedeme 40.000 T.

Já jsem zvolil nikoli 50 Hz, ale 60 Hz. Důvodem je kompatibilita s metodou requestAnimFrame. Na jedno volání pak připadá 2M/60 = 33.333T. V emulátoru PMI-80, kde procesor běží na frekvenci 1.111.111 Hz (10MHz / 9) tedy každou šedesátinu sekundy provádím 18.518 T.

Ten test s příznakem nope tam je proto, kdyby se náhodou nestihlo provést, co se provést má.

Na začátku jsem psal, že jeden krok emulace provede jednu instrukci, tady ale operuju s počten hodinových cyklů. Řešení: každý krok emulace vrátí počet cyklů (T), který instrukce zkonzumovala. V cyklu pak odečítám, a když se dostanu na nulu, je jasné, že jsem vyčerpal limit (případné rozdíly v řádech jednotek T jsou zanedbatelné).

 

Líbil se vám článek? Podpořte autora na Patreonu

Got Something To Say:

Vaše emailová adresa nebude zveřejněna.

banner

Copyright © 2017. Powered by WordPress & Romangie Theme.

banner