Nejsem nijak výrazným nostalgikem a retro-mániemi většinou netrpím, přesto jsem se dal na Facebooku zviklat Vítem Zvánovcem a vida, jak jiní propadají počítačové retrovlně, podlehl jsem vábení a rozhodl se přispět svými pár bity do díla, když pro nic jiného, tak – řekněme – ze studijních důvodů.
Rozhodnuto, budu tedy emulovat… Což o to, emulovat se dá leccos a lecjak. S rostoucím výkonem procesorů roste obliba emulátorů starších a museálních zařízení fungujících přímo v prohlížeči a psaných v JavaScriptu. Takto se podařilo naemulovat mj. Linux a emulátory spekter a prvních PC s MS-DOSem jsou běžnou věcí.
To pokládám za určitý paradox, protože účelem JavaScriptu rozhodně nemá být dodat uživateli prohlížeče software. K tomu měla být primárně určená Java a javové applety, jenže té jako kdyby dnes už nikdo nevěřil a – další paradox – Java se používá nejvíc tam, kde existuje fixní API a kde by bylo výhodnější psát v C/C++, protože procesorového výkonu rozhodně není nazbyt, tedy v Androidu. Trochu mi to připadá, jako kdybychom výpočty pro předpověď počasí zadávali superpočítači v makrech tabulkového procesoru a rodinný rozpočet počítali v assembleru…
Rozhodl jsem se proto, že budu kanonický a programovat budu v Javě.
Druhou otázkou bylo, který osmibitový počítač zvolit. Zalovil jsem v paměti. První počítač jsem si koupil v r. 1991 a bylo to už PC, tehdy s procesorem 386SX na 16 MHz a jedním megabytem paměti; vlastníkem osmibitového počítače jsem nikdy nebyl. Z osmibitů jsem intimně poznal několik strojů: o panictví jsem přišel cca v r. 1983 na Video Genie System (klonu TRS-80), později jsem používal československé výrobky SP-830 (?), PMD-85 a IQ-150/151, a jako poslední osmibit jsem měl jakýsi polský stroj s CP/M. Z něj jsem přešel na PC kompatibilní monstrum Tesla PP-06, což byl pozoruhodný výtvor československé improvisační školy: protože např. nebyly k mání procesory Intel 8088, pépéčko
používalo 8086 (takže bylo vlastně rychlejší než na stejné frekvenci taktované IBM PC). A, překvapivě, ač to bylo obrovské a vážilo to snad metrák, jako jediný počítač, se kterým jsem do té doby přišel do styku, měl jakž-takž vyřešené chlazení a nepřehříval se.
Retrovášeň jsem se rozhodl ukojit pomocí PMD-85, což byl počítač, který mne svou notorickou nespolehlivostí a frapantně chybným návrhem přivedl k hardwaru: tyto počítače bylo nutné nejen opravovat, ale uživatel je musel i nejrůznějšími způsoby modovat
, aby vykompensoval konstrukční vady.
Tedy dobrá, javový emulátor PMD-85, vzhůru do díla.
Začal jsem emulací procesoru. Péemdéčko
běží na procesoru 8080A, tzn. na tom nejjednodušším, co se dá emulovat: emulace byla hotová za večer, a druhý den mi virtuální procesor prošel testem 8080/8085 CPU Excerciser (Sunhillow), který kontroluje i všechny idiosynkratické hodnoty flagů.
Emulátor CPU jsem si napsal nejprve v C, pak jsem ho ze zvědavosti přepsal do Pythonu a nakonec do Javy, dosti nezpůsobně u toho kleje, maskuje a castuje, proklínaje společnost Sun za to, že neimplementovala unsigned typy (Microsoft tuto chybu, tuším, v C# nezopakoval).
Překvapilo mě, jak je Java proti C pomalá. Na svém nikoli špičkovém počítači (optimalisovaném na tichost provozu, nikoli na procesorový výkon) mi celý Sunhillow test v C trvá 18 sekund, kdežto v Javě to samé zabere 9 minut. Buď neumím v Javě dobře a efektivně programovat, anebo jsou propagační řeči P.R.-manů Oraclu o tom, jak se Java blíží rychlostí k C/C++, od reality stále na hony vzdáleny. V Pythonu, podle očekávání, trval test přes hodinu, ale jeho doménou podobné výpočty nejsou (ano, přiznávám, přesto mne to jako zamilovaného pythonistu poněkud ranilo, dva řády je prostě moc…).
Zbývá maličkost, implementovat hardware počítače, což by neměla být s ohledem na jeho jednoduchost práce na příliš dlouhou dobu. Výstup na display a emulace magnetofonu jsou jasné, pro vstup chci použít nezávisle myšovatelnou javovou klávesničku, jejíž grafický návrh má arci, jak patrno, k dokonalosti dosud daleko.
Jsem tedy zvědav, co nakonec vytvořím. Za dosud nejlepší pokládám emulátor slovenského RM-Teamu, uvidíme, jak se s tímto benchmarkem poperu; o výsledku budu samozřejmě na tomto místě informovat.
Důležité upozornění!
Policie České republiky a šéfcensor Ústavu pro studium totalitních režimů Jaroslav Čvančara varují: citovat jakékoli texty z tohoto blogu způsobuje vážné risiko trestního stíhání! Četba na vlastní nebezpečí!
Retro pro osm bitů
- Autor: Tomáš Pecina
- Kategorie: Počítače
- Počet zobrazení: 3003
Komentáře
case 0xe4: /* CPO */
if (PE) {
PC += 3;
cyc += 11;
} else {
tb = mem[++PC & 0xffff];
tw = tb + (mem[++PC & 0xffff] << 8);
PC++;
SP--;
if (wr_mask[SP])
mem[SP] = PCH;
SP--;
if (wr_mask[SP])
mem[SP] = PCL;
PC = tw;
cyc += 17;
}
break;
To samé v Javě:
case 0xc4: // CNZ
if (ZFSET()) {
PC = (PC + 3) & 0xffff;
cyc += 11;
} else {
PC = (PC + 1) & 0xffff;
tb = mem[PC] & 0xff;
PC = (PC + 1) & 0xffff;
tw = tb + ((mem[PC] & 0xff) << 8);
PC = (PC + 1) & 0xffff;
SP = (SP - 1) & 0xffff;
if (wr_mask[SP])
mem[SP] = (byte)(PC >> 8);
SP = (SP - 1) & 0xffff;
if (wr_mask[SP])
mem[SP] = (byte)(PC & 0xff);
PC = tw;
cyc += 17;
}
break;
Bytecode je irreleventni, na cem zalezi dostanete pres
java -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly jmeno_programu
Je to ale o dost min citelny nez gcc -S.
interface Executable {
public abstract int exec();
}
class Opcode {
String mnemo, par;
int length;
Executable exec;
Opcode(String mnemo, String par, int length, Executable exec) {
this.mnemo = mnemo;
this.par = par;
this.length = length;
this.exec = exec;
};
}
public final Opcode [] opcodes = new Opcode [] {
…
// RZ
new Opcode("RZ", "", 1, new Executable() {
public int exec() {
if (ZFSET()) {
int tb = mem[SP] & 0xff;
SP = (SP + 1) & 0xffff;
PC = tb + ((mem[SP] & 0xff) << 8);
SP = (SP + 1) & 0xffff;
return 11;
} else {
PC = (PC + 1) & 0xffff;
return 5;
}
}
}
),
…
cyc += opcodes[opc].exec.exec();
…
Nevěděl jsem, že se dá takhle instancovat interface, ale z nějakého důvodu to funguje. Java je prostě divná…
Ale kdepak divna. To co jste udelal je anonymni trida. Proste nepojmenovana implementace interfacu Executable. Ja nejsem zadny Java propagator a nehajim ten jazyk az do roztrhani tela, ale kdyz se o nem takto vyjadrujete, tak vezte, ze se divnym stavate vy a ne ten jazyk, protoze jen poukazujete na to, ze mu (zatim) nerozumite.
"Přispělo k němu i to, že jako mnoho jiných z mé generace nemám příliš
dobrý vztah k objektově orientovanému programování
(OOP), a pokládám ho za modní výstřelek konce 80. let,
který z nějakého důvodu opomněl odeznít... jsa přesvědčen,
že neobjektově by se to samé dalo napsat mnohonásobně jednodušeji a efektivněji."
Psal jsem něco v Pascalu (včetně objektového), C, C++, php, minimum v C#.
Když jsem nedávno psal něco po delší době pro AVR v C, tak to pro mě byl po letech psaní v Javě
velký nezvyk a objekty mi hodně chyběly.
Vybavuji si z FELu ukázky kódu v C++ a tomu odpovídající C kódy (některé překladače nejdřív vytvořily z C++ kód v C) a musím říct, že kód v C++ byl přehlednější a kratší.
Myslím, že Java umožňuje psát programy velmi efektivně (z hlediska tvorby kódu), přehledně a jednoduše; např. tvorba okna v C win api a v Javě děděním z JFrame je myslím nesrovnatelná).
Z ukázky vašeho kódu bych tipoval, že jde o přepis z C,
kde 4 parametr byl ukazatel na metodu, objektově by myslím měla třída Opcode
potomky pro jednotlivé typy instrukcí s metodou execute různou pro různé
instrukce, mimo jiné by odpadl konstruktor se 4 parametry (o počtu parametrů se pěkně píše
např. v knize Čistý kód/R.C. Martin - ideální je metoda bez parametrů, monadická příp. dyadická)
a navíc je možné, že by část kódu v metodě exec mohla přejít do předka.
A ještě k anonymní instanci rozhraní; to je věc, která je dost užitečná a často používaná.
Mimo jiné umožňuje elegantně obejít absenci ukazatele na metody v C, nebo delegáty v C# -
ukázka použití v Javě je např. v Návrhové vzory/Pecinovský u vzoru Command.
A velmi často se používá (jako tzv. callback), pokud se mění kód obklopený stejnými úseky kódu, klasicky
např. getDBconnection-neco XXX-catch blok-finally-close nebo obdobně při použití transakce:
TransactionCallbackWithoutResult action = new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
transactionalUpdate(record);
}
};
getRequiredPropagationTransactionTemplate().execut e(action);
toto je velmi často používaná konstrukce (zde Spring a často v Hibernate).
Nemohl jsem si odpustit alespoň velmi stručný komentář k OOP a Javě.
Momentálně mi asi nejvíc dvojkolejnost polí a primitivních/normálních typů, která má daleko k eleganci pythonských konstrukcí. Boxing a unboxing si mi zdá nedomyšlený. Příkladmo když parsuji vstupní data do VT220, vytvářím si pracovní pole neukončených ESC-, CSI- a DCS- sekvencí, na což se mi hodí ArrayList, takže se nestarám o přetečení indexu. Ale když potřebuji data z těchto listů vložit do switche jako char, musím použít konstrukci (např.) (char)(int)esc.get(0), protože Java neumí zkonvertovat Integer na char žádným jiným způsobem. Proboha, proč? Vždyť je to odporné a znepřehledňuje to kod!
Mít pro to samé dva různé typy, např. Long a long, to je idiosynkrasie, kterou nemá snad žádný jiný programovací jazyk, a její účelnost je rovna nule.
K Integer vs int; vy máte trochu specifickou úlohu, ale já myslím, že každý typ se používá na něco jiného. Můžete všude použít Integer, ale pak se můžete dostat rychlostí někam k Pythonu. Když jsem kdysi zkoušel smyčku s Integer a int, byla smyčka s Integer cca 10x pomalejší; tj. na výpočty s čísly, práci s maticemi, smyčky apod. int, na data spíše Integer.
Např. Matlab měl (asi má pořád) pod kapotou Javu a když se hodně zadařilo, vypadl Java stacktrace s hláškou že "int XXX".
Naopak někdy chcete vědět, jestli je hodnota vůbec nastavená (Integer hodnota je null);např. persistentní objekt Hibernate s klíčem Integer jako identitou prostě s typem int nefunguje.
Potřebujete tak často ty přetypování? A nejdou ty switche nahradit polymorfismem (jak je to preferováno v OOP[tj. switch jen ve factory metodach] - osobně si nepamatuju, kdy jsem naposled použil switch - ale jak jsme psal; řeším trochu jiné typy úloh a rychlost provádění programu nehraje roli, tj. abych řešil, jestli switch nebo polymorfismus).
Jakých číslic? Tomu nerozumím, téměř vše, co vidíte, jsou bitmapy.
mimochodem; použil jste AWT nebo Swing?
Swing.
Potřebujete tak často ty přetypování? A nejdou ty switche nahradit polymorfismem
Nepotřebuji, kdybych potřeboval, napsal bych si na to statickou konversní methodu nebo subclassoval ArrayList.
Obecně mám objekty za samoúčelné všude tam, kde emuluji historický algorithmus. Příkladmo VT220 v sobě měl jednu 8051 a zpracování escape sekvencí řešil jako stavový automat (bohužel, jeho firmware není veřejně dostupný, pokud by byl, celá emulace by byla hračka). Když nad tento algorithmus začnu roubovat něco "moderního", jen si přidělám práci a s velkou pravděpodobností vytvořím něco, co nebude s originálem 100% kompatibilní. Když emuluji historický firmware, musím při tom i "historicky" myslet.
RSS kanál komentářů k tomuto článku