* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 32bitove instrukce ve vkladanem Assembleru v TP 7 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Dvaatricetibitove instrukce znaji vsechny procesory od 386 vys. TP 7 je dokonce bezne pouziva pro operace s longinty, jenomze jeho vkladany Assembler zna pouze starsi sadu pro 286. Pokud chceme assemblit 32bitove, nezbyva nez si pomoci strojovym kodem a prekladac trochu osidit. Nastesti se architektura procesoru 80x86 vyvijela postupne a vyvojarum se kvuli rozsireni na 32 bitu nejspis nechtelo vymyslet uplne novou sadu opkodu, tak to vyresili jednoduse: 32bitove instrukce vypadaji uplne stejne jako 16bitove, jenom maji pred sebou jeste kouzelny prefix $66. V Assembleru tenhle prefix muzeme zapsat primo do kodu jako jednobytovou konstantu: db $66. Samozrejme je treba pocitat s tim, ze program obsahujici takovehle instrukce nebude fungovat na 286kach nebo jeste starsich rachotinach. Tolik teorie, vsechno ostatni si vysvetlime na praktickych prikladech. Presuny dat ============= 32bitova instrukce zapis v TP -------------------------------------------------- mov EAX,neco db $66; mov AX,neco mov nekam,EBX db $66; mov nekam,BX mov ECX,EDX db $66; mov CX,DX mov ECX,[SI+5] db $66; mov CX,[SI+5] mov [ES:DI+6],EDX db $66; mov [ES:DI+6],DX mov EDX,[SI+BX+40] db $66; mov DX,[SI+BX+40] push EAX db $66; push AX pop EBX db $66; pop BX mov EAX,$12345678 db $66; mov AX,$5678; dw $1234 Pripsani prefixu 66h ma stejny ucinek jako prepsani registru napr. AX na EAX (tedy kdyby prekladac tahle jmena znal, jako ze nezna): instrukce Mov misto dvou bytu zkopiruje ctyri. Pozor: neni mozne napsat treba "db $66; mov AL,neco" - to je osmibitova instrukce a prefix $66 s ni nic nezmuze. Protoze TP pracuje v 16bitovem real modu, jsou vsechny adresy 16bitove, takze se pri zapisu "db $66; mov DX,[SI+BX+40]" BX na EBX nezmeni. 32bitove prime hodnoty se musi rozdelit na dva wordy, prvni napred (na PC mame system "little endian", cili "nizsi word napred, vyssi nakonec") - - viz posledni radek tabulky. Velice zakerne jsou konstanty definovane stylem const neco=123. Prekladac je interne povazuje za prime hodnoty (narozdil od const neco:typ=123, coz jsou bezne promenne) a typ urci podle velikosti hodnoty: cokoli mensiho nez $FFFF je word a basta. Proto je nutne rucne doplnit horni word: const konstanta=500; asm db $66; mov CX,konstanta; dw 0 end Kdyz to neudelate, pripletou se do hodnoty dva byty z nasledujici instrukce a skonci to bud padem programu na neplatnou instrukci nebo potizemi kvuli spatnemu cislu. Dokud kopirujeme jenom pres registry a ofsety, je to jednoduche, proste pripiseme prefix a je vystarano. Potize nastanou, kdyz chceme kopirovat z/do 32bitovych promennych: var l:longint; asm db $66; mov AX,l <----- Tohle fungovat nebude, protoze prekladac si mysli, end ze ta instrukce je 16bitova, a nedovoli kombinovat 16b registr s 32b promennou (stejne jako by neslo kombinovat treba 8b registr se 16b promennou). Mame celkem asi ctyri moznosti, jak to obejit: 1. Adresu promenne si nacist pres Lea, Les nebo Lds a pak neni co resit. Ale znamena to zbytecne instrukce navic a plytvani volnymi registry. 2. Pretypovat to: var l:longint; asm db $66; mov AX,word ptr l end Nejjednodussi, ale obcas mi to prekladac odmital. 3. Pouzit absolutni promennou, ktera predefinuje tu puvodni: var l:longint; w:word absolute l; asm db $66; mov AX,w end Promenna w neexistuje samostatne, je to jenom alternativni definice toho sameho mista v pameti jako promenna l (tj. zacina na stejne adrese). Diky little endianu by pro hodnoty mensi nez $FFFF fungovala i v normalnim pascalskem kodu, v Assembleru je to taky v pohode. 4. Pouzit variantni zaznam: var l:record case boolean of false:(cely:longint); true:(dolniword,horniword:word); end; asm db $66; mov AX,dolniword end Pro prakticke pouziti zbytecne slozite, davam to sem jen pro uplnost. Retezcove instrukce ===================== 32bitova instrukce zapis v TP -------------------------------------------------- lodsd db $66; lodsw rep stosd db $66; rep stosw repne movsd db $66; repne movsw repe scasd db $66; repe scasw Rep a spol. jsou taky prefixy, ale kombinace s 66h jim nastesti nevadi. Registry SI a DI se meni o 4. V pripade Lodsd a Stosd jdou data pres registr EAX. Pocet opakovani pro Rep* v real modu je vzdy CX, nikdy ECX. Aritmeticke instrukce ======================= 32bitova instrukce zapis v TP -------------------------------------------------- shl EAX,16 db $66; shl AX,16 add EAX,[SI+5] db $66; add AX,[SI+5] sub EBX,NejakyLongint db $66; sub BX,word ptr NejakyLongint xor ECX,ECX db $66; xor CX,CX cmp EDX,$12345678 db $66; cmp DX,$5678; dw $1234 mul ECX db $66; mul CX Podobne je to s instrukcemi And, Or, Test, Shr, Not, Neg, Adc, Sbb a dalsimi: proste se prida prefix k 16bitove instrukci a je to. Opet pozor na 32bitove prime hodnoty, horni word se musi zadat za instrukci jako samostatna konstanta (dw). Promenne je opet potreba nejak prevleknout za wordy. Instrukce Mul a Imul v 32bitovem formatu funguji tak, ze EAX vynasobi 32bitovym operandem, dolni dword vysledku ulozi do EAX a horni do EDX. Preruseni =========== 32bitove instrukce se daji pouzivat i v procedurach pro obsluhu preruseni, ma to ovsem jeden hacek. Direktiva interrupt zajisti automaticke zazalohovani a obnoveni pouze 16bitovych registru. Proto je potreba vsechny 32bitove registry, ktere uvnitr obsluzne procedury menime, ulozit a obnovit rucne pomoci db $66; push ... a db $66; pop ... . * * * A to je vse, pratele. Nic vic se o tehle problematice snad ani napsat neda, preji vesele kodovani. Jestli potrebujete vedet vic, podivejte se do AThelpu, tam najdete vsechno. (c) Mircosoft 2.2.2012 http://mircosoft.mzf.cz