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é nebezpečí trestního stíhání! Četba na vlastní nebezpečí!

14. 9. 2015

Thinking outside the box

Předpokládám, že úlohu, jež dala původ nadepsanému rčení, znáte: úkolem je spojit devět bodů uspořádaných do matice 3 x 3 souvislou čarou složenou z nejvýše čtyř úseček. Je řešitelná pouze tak, že úsečky leží mimo pravoúhelník vymezený danými body, ale najít řešení je pro většinu lidí obtížné, neboť intuitivně předpokládají, že úsečky musejí ležet pouze uvnitř pravoúhelníku – ačkoli zadání o tom ničeho nepravilo.

Stereotypy a automatismy v myšlení jsou zrádné, o čemž jsem se přesvědčil naposledy během uplynulého week-endu. Pojal jsem totiž úmysl, že pro svůj (na třetím blogu traktovaný) emulátor doplním emulaci mikroprocesoru Zilog Z80. To je, myslím, jediná elektronická součástka, kterou jsem v životě opravdu miloval, a i když jsem měl původně v úmyslu omezit se na tři emulované historické stroje – PMI-80, PMD 85 a IQ 151 – mít Z80 by mi umožnilo pojmout do emulátoru i počítač Ondra, který byl hardwarově velmi zdařilým dílem a jeho většímu rozšíření zabránila pouze pro praktické používání nevhodná, příliš ořezaná klávesnice.

Domníval jsem se, že maje naemulovaný Intel8080A, nezabere mi doplnit instrukce Z80 než jeden, nejvýš dva dny. Jenže celý jeden den a spoustu nervů mi zabralo hledání chyby, kterou jsem nemusel udělat, kdybych nebyl myslel – v krabici.

Posuďte sami. Pro emulátory používám instruction exerciser (více o něm zde), který jsem si rovněž přepsal do Javy, abych mohl rychleji najít chyby implementace jednotlivých instrukcí. Pro 8080 žádný problém, ale u Z80 jsem narazil na chybu, kterou jsem nemohl a nemohl najít. Až poté, co jsem si zprovoznil emulátor CP/M, v něm druhdy slavný debugger DDT, a propadaje střídavě amoku a stavům chorobné skleslosti, jsem na to, co je problémem, nakonec přišel. A protože je to problém věru bizarní, dělím se o něj se svými milými čtenáři i zde.

Když si prohlédneme zdrojový soubor Franka D. Cringla z r. 1994, vidíme, že jádrem exerciseru jsou test cases, jejichž dlouhá řada jest popsána v makrech na ř. 194–728. Povšimněme si parametrů těchto maker: vyjma triviálních hodnot 0 a –1 jsou všechny konstanty (literály) zapsány hexadecimálně.

Až na dvě výjimky, na ř. 277 a 285, kde se stkví neobvyklý zápis 010. Když jsem ho přepisoval do Javy, automaticky jsem napsal 0x08, domnívaje se, že Frank Cringle udělal chybu a mínil zapsat 0x10 a protože si neuvědomil, že assembler takto zapsanou konstantu vyhodnotí oktalově, vyšlo mu něco, co nechtěl, ale co je s ohledem na neměnnou hodnotu CRC nutno respektovat. Pro tuto hypothesu hovořilo i to, že je v závorce za voláním makra uvedeno (1024 cycles), což odpovídá počtu jedniček v řádku – tedy deseti.

Dlouhé hodiny jsem se pak vrtal ve svém kodu a pátral, co mám špatně, protože CRC ne a ne vyjít, jak mělo. Řešení bylo arci zcela triviální, ale tak outside the box, že mě stálo dobrých deset hodin času: Protože Cringle programoval v r. 1994 a assembloval v CP/M, předvěšená nula pro assembler neznamenala, že má konstantu interpretovat jako oktalovou: tehdejší softwarové nástroje to ještě nedělaly. Assembler proto vyhodnotil jeho zápis 010 jako 10 (dekadických); vedlejším důsledkem bylo, že ve skutečnosti se neprovádí 1024, nýbrž 2048 cyklů.

Jak se to celé stalo, bych mohli zjistit asi jen od Cringla, kdybychom mu napsali a pokud si to ještě pamatuje. Moje (upgradovaná) hypothesa zní, že původně měl 0x10, ale protože si uvědomil (nebo laděním zjistil), že by instrukce CPI/CPIR vyšla (o jeden byte) mimo rozsah testovacího pole, opravil hodnotu na deset, ale zapomněl smazat nulu a nenaznal, že se mu tím změnil i počet cyklů.

Já vím, chybami se člověk učí, jen by jich, pokud bych směl prosit, mohlo být o něco méně.