Základní konstrukce v assembleru

Pokud programujete pokročilejší program, tak se bez assembleru neobejdete. Každý jazyk používá své metody, jak psát v jazyce symbolických adres. Je proto důležité si přečíst, jak se to či ono provádí. Turbo Pascal 7 umožňuje zapisovat assembler plným nebo Pascalovským způsobem.

Ten Pascalovský je jednodušší na pochopení. Ten standardní Vám ale umožní obsáhnout všechny instrukce (v TP7 pouze pro 286 a nižší). Protože se dost často setkávám s dotazy, že by ten či onen chtěl naprogramovat zvukovou kartu či IPX síť, avšak jeho TP5 neumí bloky assembleru nebo neví, jak má použít to či ono, rozhodl jsem se, že Vám ukážu některé základní konstrukce. Je samozřejmě nutné, abyste o assembleru už něco věděli.

Nejčastějším způsobem je přistupování do paměti:

Když chcete např. něco uložit do video paměti v grafickém módu, provedete to tímto způsobem:

Mem[$a000:0] := BYTE; (8 bitů)
MemW[$a000:0] := WORD; (16 bitů)
MemL[$a000:0] := DWORD; (32 bitů)
MemL[$a000:0] := (DWORD and $ffffff) or (MemL[$a000:0] and $ff000000)
 

 

 

(24 bitů)
 


V assembleru to zapíšete následovně:

 
asm  
 push    ds               ; vždy uschovávejte DS registr, pokud
                 ; s ním pracujete. TP7 v něm má totiž
                 ; segment Vašich VAR proměnných!
 mov      ax, $a000        ; do seg.registru nelze vkládat přímo
 mov      ds, ax            ; cíl je vždy nalevo
 xor      si, si            ; většinou rychlejší než MOV SI, 0
                 ; ale zase mění Flags
 mov      al, BYTE
 mov     [si], al  ; možno samozřejmě také rovnou MOV [SI], BYTE
 mov      ax, WORD
 mov     [si], ax  ; DS je standardní segmentový reg.pro SI
                 ; pro ES by se napsalo MOV ES:[SI], AX                 ; také je možné změnit seg.registr pomocí
                 ; prefixů, pro ES se používá 26h          
 db       66h;              ; TP7 nezná 32 bitové instrukce
                 ; prefix 66h mění WORD operand na DWORD
                 ; 67h ($67) mění ADRESU na DWORD!!!
 mov      ax, DWORD
 db      66h;
 mov      [si], ax
 mov     ax, [si]  ; toto naopak přečte WORD z [SI]

end;  

Přístup na porty je jednou z důležitých věcí, pokud např. měníte paletu na VGA kartě nebo programujete zvukovou kartu. Pozor! Porty nejsou COM a LPT, to jsou rozhraní. Takto se dá získat např. náhodné číslo z jednoho z časovačů PC:

Port[$43] := 0;
BYTE := Port[$40];

V assembleru platí, že pokud chcete přistupovat k portu, jehož číslo je menší než 256, můžete to provést přímo. K vyšším musíte použít registr DX:

 
asm  
 mov     al,0
 out     43h,al           
 mov     dx,40h           ; toto je zbytečné, protože $40 <= $ff
 in      al,dx             ; v AL je náhodné číslo  

end;  

 

Funkce v Pascalu vrací své hodnoty v registrech. Byte se vrací v AL (tedy stačí, když před ukončením funkce vložíte svůj výsledek do registru AL, nebo můžete také použít @RESULT - tento symbol ale můžete použít jen ve funkci a jen tehdy, není-li za její hlavičkou nápis ASSEMBLER;, který Vám normálně umožní ji napsat celou podle sebe a navíc s ušetřením jednoho BEGIN a END;), Word v AX a LongInt nebo typ Pointer ve dvojici DX:AX, kde DX je MSW nebo segment a AX je LSW nebo offset.
 


Důležitou kapitolou jsou také přerušení. Vždy je nutné naplnit nějaké ty registry a pak volat příslušné přerušení.

V TP7 se toto řeší příkazem INTR (navíc musíte použít jednotku DOS). Musíte si definovat proměnnou, která bude zastupovat registry:
 

VAR R : registers;
 

Pak je můžete naplnit:
 

R.AH := 0;
 

R.AL := $13;
 

A poté volat přerušení:
 

INTR($10,R);
 

Tímto jsme změnili aktivní video mód na grafiku v režimu 320x200 a 256 barvách (textový režim 80x25 v 16 barvách, který je standardní, má číslo režimu v AL rovno 3). Nastavit můžeme také registry AL a AH s příkazem WITH, který Vám ušetří neustálé psaní toto R:
 
        with R do
        begin
         AH := 0;
         AL := $13;
        end;  
Ale to se hodí jen pokud těch registrů máte více. Jednodušší a rychlejší je nastavit oba registry současně pomocí:
 

R.AX := $13;
 

Pro DOSové funkce se používá přerušení $21. Můžete jej volat stejně jako každé jiné, nebo použít:
 

MSDOS(R);
 

Kde předáváte jen registry. Ty můžete po vykonání přerušení také přečíst, jako každou jinou proměnnou.

Mnohem rychlejší je ale použití bloku assembleru, kde máte jistotu, kolik instrukcí se použije a nemusíte používat další jednotku a definovat proměnné (zápis $10 je rovnocenný zápisu 10h, mimo ASM lze ale použít jen $, v ASM lze použít i B pro binární zápis čísla):

 
asm  
 mov     ax,$13
 int     $10  
end;  

Pokud přecházíte na Free Pascal, což je stejný jazyk jako TP7 (ale již 32 bitový, rychlejší, běžící ve chráněném režimu, kde máte sice k dispozci "neomezeně" paměti, ale zase Vám z toho plynou další omezení), můžete používat samozřejmě assembler dál, ale bude nutné jej přizpůsobit Flat modelu. Také je nutné si uvědomit, že ve chráněném režimu nemůžete jen tak přistupovat na porty a také členění paměti je jiné.

Free Pascal má oproti Turbo Pascalu 7 tyto změny:

- je 32 bitový, tedy rychlejší na moderních CPU
- může využívat veškerou paměť počítače včetně virtuální (swap)
- obsahuje velké množství jednotek
- podporuje instrukce 386, 486, Pentium, MMX, SSE, 3DNow, Pentium II, III a 4, 64 bitové CPU, SSE2 a FPU.
- je na 98% kompatibilní se syntaxí Turbo Pascalu 7
- podporuje konstrukce C, Delphi, a další
- lze vytvářet aplikace pro DOS, Windows, Linux, a další
- obsahuje rozsáhlou dokumentaci (ale jen v angličtině)

FP má jistě i další výhody, ale ty Vás budou zajímat teprve tehdy, až se k nim dopracujete. Pokud přecházíte z TP7, je nutné si uvědomit hlavně tyto věci:

- příkaz PORT již nelze použít bez jednotky PORTS. Lepší je ale používat příkazy OUTPORTx a INPORTx, kde za X dosadíte šířku portu (B,W či L). Tyto příkazy lze navíc používat pouze pod DOSem, nebo-li s využitím jednotky GO32.

- přerušení INT 21h je funkční pouze v DOSové verzi Vašich programů (můžete spustit DOSovou verzi pod Windows, ale pokud kompilujete přímo pro Windows nebo Linux, pod tímto přerušením rozhodně nenajdete služby DOSu).

- příkaz MEM lze používat. Ve chráněném režimu existují dva typy pamětí. DOSová a ta ostatní. Příkazem MEM se dostanete do prvního 1 MB paměti, což je stejné chování, jako v TP7. K ostatní paměti se přistupuje přes proměnné, které si definujete přes VAR nebo alokujete na haldě přes NEW či GETMEM.

- assembler v FP je výhradně 32 bitový, tedy je nutné s tím počítat. Nastavovat registry přes AL nebo AX sice stále jde, ale je to pomalejší než nastavení celého registru EAX. Také si např. umědomte, že instrukce LOOP testuje registr ECX, tedy pokud ho nejprve nastavíte pomocí CX, asi Vám to nebude fungovat správně!

- musíte se naučit rozlišovat přerušení, která jsou určena pro reálný režim a která pro chráněný.

- Flat model nepoužívá segmentové registry (výjimka je přístup do DOSové paměti, kterou budete potřebovat stále pro přístup do video paměti, pokud nepoužíváte LFB, pro využití DMA, čtení BIOSu, atd.). Namísto toho každý registr ESI, atd. už obsáhne až 4 GB paměti. To je maximální velikost jednoho prostoru, který se alokuje přes selektor. FP standardně alokuje prostor pro haldu, kde má umístěné všechny proměnné a ukáže na něj registry (selektory) DS a ES, které se používá i pro přesun (instrukce MOVSx), takže s nimi nemusíte nijak manipulovat (leda byste ručně volali nějaké funkce DOSu, které je mění - lepší je ale používat vestavěné funkce FP). FP má DOSovou paměť také namapovanou přes selektor FS, tedy na FS:[$a0000] najdete i videopaměť.

Free Pascal také umožňuje používat vícero syntaxe assembleru. TP7 používá standardní syntaxi Intelu, která spočívá hlavně v těchto věcech:

- segmentový registr je vždy před závorkou ES:[DI]
- cílový operand je vždy nalevo
- jméno instrukce neoznačuje šířku, a je tedy občas nutné ji zadat pomocí ukazatele, kdy se napíše: INC BYTE PTR [$10]
- bázové adresování se zapisuje: FS:[Base+Index*Scale+Offs]
- vzdálené opuštění funkce se zapíše jako RET FAR

Příklad:

 
asm  
 mov     dx,$80
 in     al,dx
 mov     eax,3
 int     10h  
end;  

FP umí ale i AT&T, která má tyto pravidla:

- instrukce má na svém konci ještě udánu šířku operand, tj. L pro DOWRD, W pro WORD nebo B pro BYTE
- před registry se musí uvádět znak %
- bázové adresování se zapisuje jako: FS:Offs(Base,Index,Scale)
- nalevo je zdrojový operand, cílový je napravo
- vzdálené opuštění funkce se zapíše jako LRET
- před konstantou je nutné psát znak $, který tedy neznamená hexa!

například:

 
asm  
 movw   $128,%dx
 inb    %dx,%al
 movl   $3,%eax
 int     $16  
end;  

Ale o tom si můžete přečíst v dokumentaci Free Pascalu.