V podstatě jsem nedávno dělal něco, co by se luštitelům taky mohlo hodit, tak to tu popíšu aspoň letem světem a když tak se mi tu dá psát do Soukromých Zpráv, nebo na email gilhad zavinac seznam tecka cz kde se rad podelim o další zkušenosti a podrobnosti, nebo něco napíšu na fórum.
Šlo o vývoj programu pro ATmega2560 (z Arduino Mega (Pro)), který je dost podobný ATmega328P (z Arduina Uno), jen má víc RAM i FLASH i pinů a vychytávek na nich (88 GPI/O, 3 nezávislé HW Serial, spoustu časovačů atd). Taky jsem toho nechtěl málo, ovládat VGA monitor, PS/2 klávesnici, SD kartu, I2C, komunikovat a sdílet externí RAMchip s retro počítačem a 6xNeoPixel jen tak pro začátek. Programován trošku v C/C++, hodně v ASM a ve FORTHu (interaktivní zásobníkový jazyk s příkazovou řádkou a možností programování z klávesnice/Seriálu za běhu). Současný stav převážně funkční, částečně ve vývoji. Slov (příkazů) ve FORTHu zatím asi 415.
Kód: Vybrat vše
| size | hexa | build/FORTH_c_003.elf
--------+--------+--------+--------------------------
FLASH: | 61462 | 0xF016 | data: 628 + text: 60834
RAM: | 19892 | 0x4DB4 | data: 628 + bss: 19264
Kód programu tady
https://github.com/githubgilhad/memxFOR ... ORTH_c_003, zdrojáky desky tady
https://github.com/githubgilhad/MegaHom ... ad/MHF-002, zdrojáky debuggeru AVRemu tady
https://github.com/githubgilhad/AVRemu (můj fork s přidaným ATmega2560). (Forknuto z
https://github.com/MuellerA/AVRemu)
ukázka dvou počítačů(jeden připojený, druhý ne), jednoho monitoru a jedné PS/2 klávesnice
A protože se ne vždy vše poskládalo (asi 150 zdrojových souborů bez knihoven) a popřekládalo jak bych si představoval, tak jsem to často různě procházel a zkoumal v přeloženém stavu, pomocí
AVRemu který potřebuje jako vstup *.bin soubor (viz Diegův článek) a nejlíp i *.xref, kde jsou uloženy adresy a jejich jména (viz Xref file format), protoze pak to v disasemblovaném kódu ta jména bude používat.
Zádrhely a postup
- pro věci ve Flash se někde používají adresy bytové (adresují se jednotlivé byte), jinde wordové (adresují je jen celá - 2bytová - slova, protože to je nativní přístup atmegy k flash. 2*Aw=Ab) a různé tooly narůzných místech používají jedny, nebo druhé a člověk si musí u svý toolů ohmatat co a jak. Když mu to někde uzazuje polovinu, nebo dvojnásobek, tak tam je jiný režim, než čekal - nejlépe si nedřív přeložit nějaký Blink a na tom si to vyzkoušet.
- data sekce .data jsou uložena nejdřív ve flash (za tabulkou přerušení) a po startu se teprve přenesou do RAM. Dále v programu se čtou z RAM.
- data sekce .bss jsou nulová, nejsou uložena nikde, po startu se prostě celé .bss vynuluje. Dále v programu se čtou z RAM
- PROGMEM data jsou ve flash a čtou se z flash
- pro čtení dat z RAM se používají instrukce LDS reg,addr, pro čteni z flash instrukce LPM reg,addr a pro čtení neměnných hodnot (jako třeba adresy, argumenty zadané číslem nebo #define LED 13 a podobně) se použije LDI reg,value
- normálně zavolám něco jako
Kód: Vybrat vše
AVRemu -d -m atmega328p -x uno_flash.xref uno_flash.bin >uno_flash.dis
pro vytvoření disasemblovaného souboru, nebo Kód: Vybrat vše
AVRemu -e -m atmega328p -x uno_flash.xref uno_flash.bin
pro jeho krokování
- ze souboru uno_flash.dis můžeme vygrepovat instrukce BREQ, BRNE, ... RJMP, JMP a dát je do *.xref jako "j addr J_addr" např. JMP 1234 a příště už víme, že skáče na Label Jump nevyřešenou na adrese 0x1234
- obdobně CALL 4321 přvedeme na nevyřešené Call jako
- krokování začíná instrukcí na adrese 0x0000 - tam je obsluha pro RESET, vetšinou instrukce nebo tak něco
- následně se nastaví stavovy registr SREG (pomocí instrukcí OUT) a zásobník a kdesi cosi, vynuluje se .bss oblast a překopírují se .data a případně provedou další inicializace. Pak se zavolá a po návratu se to přes beznadějně zacyklí. Zde získáme adresy main a v něm setup a loop a taky .data a .bss (pokud je ještě nemáme ) a můžeme je zanést do uno_flash.xref, příště se nám již budou zobrazovat.
- Pokud víme, že to je UNO, můžeme pojmenovat i porty (a některé nám to pojmenuje samo). Taky si můžeme udělat sbírku vzorů knihoven a volání a pak neznámé funkce LC_0x??? pak porovnávat s těmito vzory, pokud začínají stejně, hned máme kandidáta na správné jméno a okolní funkce budeme hledat ve stejné knihovně
- zanedlouho si zapamatujeme/zapíšeme i obvyklé manévry okolo for-cyklů, if-ů, while-cyklů a přiřazování argumentů a můžeme začít psát provizorní program v C // s komentáři okolo které adresy až kam zpět přeložený úsek sahá
- Já to dělal kvůli rozsahu (a znalostem) ručně a pomocí skriptů, ale se spoustou toho může AI pomoct a vy nato kdekoli navázat a vnutit tam svou představu a třeba ho zase nechat navázat na svou práci - například místo vygenerovaného tam vnutit svoje
Kód: Vybrat vše
#define SEVER 0
#define VYCHOD 1
#define JIH 2
#define ZAPAD 3
for (int smer = SEVER; smer <= ZAPAD; smer++) {...};
a mít kód trochu přehlednější a čitelnější
Nebo si lépe nazvat uživatelské funkce, místo LC_0x4321 třeba použít regulujBazen
- taky se hodí si někde vedle udělat zachraneny_projekt/ a v něm si čas od času přeložit dosažené a vidět, zda přeložené odpovídá zhruba zachráněnému (tady už naopak *.xref můžeme mít plně kompletní (
Kód: Vybrat vše
Elf.rb <elf-file(in)> <bin-file(out)> <xref-file(out)>
např. Kód: Vybrat vše
Elf.rb zachraneny_projekt.elf zachraneny_projekt.bin zachraneny_projekt.xref
) a posuzování funkcí a proměnných nám půjde snadno)
Už je to nějaké dlouhé a je pozdě v noci, tak toho nechám (asi je tam většina důležitého) a pokud by chtěl někdo něco k této cestě, tak se ozvěte
