Mám dotaz k volání funkce s parametry. Když parametry pomocí PUSH uložím, pak zavolám funkci pomocí CALL (uloží na zásobník návratovou adresu), pak první POP ve funkci vrací tuto návratovou adresu, nebo ne?
Poddotaz k NOP (jen pro zajímavost). Assembler znám jen ze Z80 a tam bylo NOP jako nulový bajt, přišlo mi to šikovné, protože "prázdná" pamět nic nedělá a myslel jsem, že je to pravidlem u všech procesorů, pletl jsem se, nebo je to jen u tohoto ukázkového, protože to tak prostě vyšlo?
Díky za odpovědi.
Máte pravdu, návratovou adresu není možné zahodit a vzhledem k tomu, že tento mikroprocesor neumožňuje relativní adresování vůči SP ani SP aritmetiku (nejde například udělat ADD SP,#2), tak je nutné návratovou adresu ručně uložit a potom opět obnovit. Na toto jsem v komentářích zapoměl.
NOP - to je taky zajímavá otázka. Například 6502 mělo pro opkód 0x00 instrukci BRK, tedy BREAK a to mi připadne trošku vhodnější. Když například na konci svého assemblerovského programu zapomenu RET nebo něco na způsob EXIT 0 (podle pravidel daného OS, například v DOSu int 20h), tak by se v případě NOPů prostě pokračovalo dále až např. (v případě Z80 a jeho typických použití) někam ke grafické paměti. Kdežto BRK natvrdo program zastaví.
Ale mnoho mikroprocesorů, například i x86 to prostě nijak neřeší a na 0x00 je tuším také ADD, stejně jako v případě tohoto ukázkového mikroprocesoru, ale to je spíš náhoda, protože zrovna instrukční sadu x86 nemám rád.
; sekvence instrukcí volající funkci
PUSH A ; uložení registru A do zásobníku
PUSH B ; uložení registru B do zásobníku
LD A,param1 ; načtení prvního parametru funkce
LD B,param2 ; načtení druhého parametru funkce
CALL funkce ; zavolání funkce (a uložení návratové adresy na zásobník)
POP B ; obnovení registru B
POP A ; obnovení registru A
...
...
Skoda, ze v priklade je zminen jenom 1 typ preruseni (tady nemaskovatelne), pritom treba 6502 pouziva maskovatelne i nemaskovatelne. Bylo by zajimave vysvetlit rozdily.
Jinak ohledne: """Přerušovací rutina, která modifikuje obsah obou pracovních registrů tedy musí začínat a končit takto:"""
Ta rutina nemusi nutne pouzivat zasobnik, i kdyz je to bezna praxe. Dulezite je navratit obsah menenych registru zpet, no a protoze leckdy jde o to, aby preruseni bylo vykonano co nejrychleji, tak lze pouzit i jine techniky.
Napr. docela
; vstup do irq
IRQ STA tempadd ; ulozeni A nekam docasne
... vlastni prace v preruseni
LDA tempadd ; nacteni A z docasneho ulozeni
RTS ; navrat z preruseni
tempadd .byte 00 ; temporary prostor
Nebo jeste drsneji
; vstup do irq
IRQ STA zpet+1 ; ulozeni A primo do mista zpetneho nacteni
... vlastni prace v preruseni
zpet LDA #00 ; nacteni A, kde bude 00 zmenena podle vstupu do IRQ
RTS ; navrat z preruseni
Tyto a obdobne techniky dost zasadne zkracuji pocet cyklu potrebnych na preruseni, protoze prace se zasobnikem je vetsinou casove nejnakladnejsi.
Pravda, pokud budeme předpokládat, že přerušení je jednoúrovňové (a nebude se tedy před ukončením přerušovací rutiny volat přerušení další), tak je použití zásobníku overkill. Taky na některých mikroprocesorech lze použít stínové registry (například druhou sadu registrů) - viz Z80, 8048, 8051 atd. Pro co největší urychlení přerušovací rutiny jsou všechny postupy dobré.
>Mikroprocesor při přijetí operačního kódu instrukce NOP prostě žádnou výraznou
>operaci neprovede a pokračuje v provádění následující instrukce.
>K jakému účelu je možné tuto instrukci použít? První způsob použití spočívá ve
>vytvoření „rezervované“ paměti, popř. k přepisu nějaké instrukce bez nutnosti >přemístění zbytku programu v operační paměti.
Toto je ale obrovske riziko, lebo malware moze hladat sekvencie NOP a do nich vkladat svoj kod...
>Druhý nejčastější způsob použití spočívá v tvorbě zpožďovacích smyček, protože je >prakticky na všech mikroprocesorech známá doba trvání provedení této instrukce >(například 2 takty).
Toto chcel a vyzuzival MS a koli tomu nebootli Win95 na K7-ke a muselo sa to patchovat, kedze K7 mala 3 paralelne FPU/ALU tak sa s tymto pouzitim NOP-u nedalo pocitat. MS to pouzil na cakanie na odozvu zaraidenia namiesto pouzita IRQ a tak sa system sekol,
>Třetí oblastí použití je „zarovnání“ instrukcí na některých architekturách tak,
>aby instrukce začínaly například na násobku 16 bitů či 32 bitů.
Toto sa mi paci a vidim to ako zaujimve pre VLIW
a este ma napadlo setrenie tym,ze X milionov tranzitorov nezmeni svoj stav a to pomoze spotrebe CMOS-ky...
Pravda, malware si taková místečka může najít a využít. Na starších OS se také dalo vkládat "přídavný" program do data segmentu, ostatně podívejte se například na command.com v XP, kolik je v něm na konci místa :-) Je pravda, že někde v kódu se musel udělat odskok na "přídavný" program, ale to není až tak velký problém - prostě se nějaká instrukce nahradí JMP či CALL a "přídavný" program na konci onu instrukci provede. Když se dá pozor na všechny ostatní registry a flagy, tak to funguje celkem dobře. Dnes již nevím, jestli data segmenty mají nahozen executable flag, ale díky častým buffer overflow chybám možná jo :-)
Zpožďovací smyčky - jasně, tato je technika použitelná především tam, kde mám HW plně pod kontrolou, například různé jednočipy s pevně udanými délkami doby běhu instrukcí atd. Na "velké" mikroprocesory s šíleně dlouhými pipelinami, více jádry, překladu do RISCových instrukcí atd., NOPy ve funkci zpožďovačů moc fungovat nebudou.
není to nějaké divné vzhledem k popsanému zásobníku LIFO?
|zásobník>
|A
|BA
|CBA
pop A vezme ze zásobníku návratovou adresu C
|BA
|A na RET zůstane proměnná A a ne návratová adresa...
další věc co by stála za zmínku, jak se pracuje s textem. Případně jakou konkrétní roli zde hraje procesor a jak to celé funguje například spolu s unicode. A co další objemnější bloky dat, jako obrázky atd..
už se nemůžu dočkat dalšího dílu, téma vypadá velmi slibně.
Ma to byt skutecne jinak, nejprve je zapotrebi ve funkci zachovat hodnotu navratove adresy a tu posleze obnovit (viz zacatek diskuse). V praxi se to dnes resi jeste jinak - pouzije se takzvany zasobnikovy ramec (stack frame), ale k tomu je zapotrebi umet adresovat relativne k hodnote nejakeho registru, vetsinou nazyvaneho BP (base pointer). K tomu se jeste dostaneme.