AMIGA REVIEW obsah časopisu online!
  Domov     Software     Hry     Obaly     Download  

Assembler

Jan Hlavatý

Vítejte zpět v našem pidiseriálu o assembleru a o systému. V této části si uvedeme přehled některých nejpoužívanějších instrukcí MC68000, a jejich použití se pokusím názorně předvést na malých polopatických příkladech.

ADRESACE
Takže: Jak už víte, každá instrukce představuje určitou operaci, kterou má procesor provést. K tomu, aby procesor mohl provést některé operace, potřebuje vědět nejen CO má provést, ale i s ČÍM to má provést. Pokud si například přejete přičíst 14 k datovému registru D0, procesor obdrží informaci, že chcete 1) něco k něčemu přičíst, 2) že to má přičíst k registru D0, 3) že to kolik má přičíst je konstanta 14. Všechny tyto informace jsou zakódovány ve strojovém kódu instrukce, kterou assembler vygeneruje. O jakou operaci jde je v assemblerovském zápisu vyjádřeno pomocí opcode, což je jakýsi dobře zapamatovatelný slovní popis nebo zkratka, jednoznačně určující danou operaci. Čeho se operace týká určují operandy. Instrukce obvykle mívá žádný, jeden nebo dva operandy. Procesor zná mnoho různých způsobů jak získat data pro operaci. Říká se jim módy adresace. Udávají způsob, jakým zjistit potřebná data nebo adresu, na které jsou uložena. Tady je malý přehled:

Použité značky:
Dn ... některý datový registr
An ... některý adresový registr
Xn ... některý datový nebo adresový registr
d8 ... osmibitový offset se znaménkem
d16 .. šestnáctibitový offset se znaménkem
xxx .. nějaká přímo uvedená adresa nebo konstanta
PC ... registr PC - obsahuje adresu právě zpracovávané instrukce
bd ... 16 nebo 32 bitová adresa nebo offset
od ... 16 nebo 32 bitová adresa nebo offset
FAKTOR ... 1, 2 , 4 nebo 8 - udává kolikrát se násobí Xn

MÓDY ADRESACE 68000/68020
Existují dvě verze zápisu adresačních módů. První je jen pro 68000, druhý a novější se používá pro 68020+. Většina assemblerů schopných překládat instrukce 68020+ umí obě tyto varianty, nebo používá novější variantu i pro 68000. V přehledu uvedu oba způsoby ve tvaru 68000/68020. Obecně v novém formátu se jednotlivé komponenty, které (součtem) dávají dohromady adresu, zapisují dovnitř kulaté závorky a jsou odděleny čárkami. Většinu komponentů lze vynechat - pak na jejich místo bude dosazena 0. Kde je u verze 68000 uvedeno "neex.", instrukce je platná jen pro 68020+.
1. Dn / Dn ... data v datovém registru
2. An / An ... data v adresovém registru
3. (An) / (An) ... data která jsou v paměti na adrese která je uložena v adresovém registru
4. (An)+ / (An)+ ... data, která jsou v paměti na adrese, jež je uložena v adresovém registru; k obsahu adresového registru se PO provedení operace přičte velikost načítané hodnoty (1 pro BYTE, 2 pro WORD a 4 pro LONG)
5. -(An) / -(An) ... PŘED provedením operace se od obsahu adresového registru odečte velikost načítané hodnoty; poté se tato zmenšená hodnota vezme jako adresa odkud se načtou data pro operaci (či kam budou uložena)
6. d16(An) / (d16,An) ... adresa operandu vznikne sečtením adresy v adresovém registru a 16 bitového offsetu (-32768...+32767)
7. d16(pc) / (d16,PC) ... adresa dat vznikne sečtením 16-bitového offsetu a aktuální pozice PC (obvykle ukazuje za první WORD právě zpracovávané instrukce)
8. xxx.w / xxx.w ... data jsou na absolutní adrese která vznikne z xxx rozšířením se znaménkem na 32 bitů
9. xxx.l / xxx.l ... data se nalézají na absolutní adrese xxx
10. d8(An,Xn) / (dB,An,Xn) ... adresa operandu vznikne sečtením adresy uložené v adresovém registru, offsetu uloženého v některém registru a dalšího osmibitového offsetu; offset může být 32 bitový (Xn.l) nebo 16 bitový (Xn.w)
11. d8(PC,Xn) / (dB,PC,Xn) .. adresa dat vznikne sečtením 16-bitového offsetu a aktuální pozice PC s dalším offsetem (16 nebo 32 bitovým) uloženým v některém registru
12. #xxx / #xxx ... data jsou přímo uvedena v kódu instrukce jako konstanta; logicky nejde toto použít pro cílový operand - do konstanty nejde nic uložit
13. neex. / (d8 An,Xn*FAKTOR) ... adresa je součtem 2, 4 nebo 8-násobku Xn plus An plus 8 bitový offset
14. neex. / (bd,An,Xn*FAKTOR) ... adresa je součtem 2, 4 nebo 8-násobku Xn plus An plus 16 nebo 32-bitový offset.
15. neex. / ([bd,An],Xn*FAKTOR,od) ... ze součtu bd a An se vypočítá adresa, z té se načte LONG, k němu se přičte násobek Xn plus od a výsledkem je kýžená adresa
16. neex. / ([bd,An,Xn*FAKTOR],od) ... ze součtu bd, An a násobku Xn se vypočítá adresa, z té se načte LONG, k němu se přičte bd
17. neex. / (d8 PC,Xn*FAKTOR) ... k PC se přičte d8 a násobek Xn
18. neex. / (bd,PC,Xn*FAKTOR) ... k PC se přičte bd a násobek Xn
19. neex. / ([bd,PC],Xn*FAKTOR,od) ... součtem PC a bd se vypočítá adresa, z ní se načte LONG, k němu se přičte násobek Xn a od
20. neex. / ([bd,PC,Xn*FAKTOR],od) ... součtem PC, bd a násobku Xn se vypočítá adresa, z ní se načte LONG a k němu se přičte od

NĚKTERÉ NEJPOUŽÍVANĚJŠÍ INSTRUKCE
Abychom mohli okamžitě zahájit experimentování, popíšu nyní některé z nejpoužívanějších instrukcí. Pak budu moci ukázat nějaké příklady. V popisu budu používat syntaxi 68020, protože je použitelná i pro 68000.

MOVE, MOVEA, LEA, MOVEQ, MOVEM
MOVE je asi nejpoužívanější instrukce vůbec. Používá se k přesunu dat odněkud někam. Při tomto kopírování je kopírovaná hodnota otestována a nastavuje odpovídající flagy. POZOR: Pokud pomocí MOVE chcete něco dát do adresového registru, assembler to přeloží na MOVEA, což je JINÁ INSTRUKCE, která sice dělá logicky totéž co MOVE (kopíruje nějakou hodnotu do adresového registru), ale nenastavuje flagy.

Pár příkládků:
move.l #123456,d0 ;d0.1 nastaví na hodnotu 123456 (konstantu)
move.b mamlas,boban ;načte byte z proměnné "mamlas" a uloží ho do proměnné "boban".
move.l 4.w,a6 ;načte z absolutní adresy 4, která je zde poznačena pouhými 16-ti bity, LONG a uloží ho do registru A6. NENASTAVÍ FLAGY!!! (MOVEA)

MOVEQ je zkrácenou verzí MOVE.l #xxxx,Dn, kde xxxx je v rozsahu -128..+127. Používá se co nejvíce, protože kód instrukce MOVEQ #12,d0 je dlouhý 1 WORD, zatímco MOVE.l # 12,d0 tři WORDy! Když píšete nějaký program, snažte se vždy používat nejkratší a nejrychlejší varianty instrukcí, i když ty delší dělají naprosto totéž.
LEA je instrukce sloužící k vložení adresy něčeho do adresového registru. Pokud chce třeba nějaká rutina v A0 adresu nějakého jména, může to vypadat:

LEA jmeno,a0

MOVEM je instrukce sloužící k hromadnému ukládání a načítání registrů do/z paměti. V podstatě jediným rozumným použitím je schovávání obsahů registrů na zásobník na začátku rutiny a jejich obnovení na konci rutiny. To umožní psát rutiny, které nezničí data v registrech, i když tyto registry používají. Seznam registrů je instrukcí dodáván jako jejich výpis. Jednotlivé registry jsou odděleny "/", lze specifikovat i skupinu po sobě následujících registrů bez vypisování všech:

MOVEM.l d0/d2/a3-a6,-(sp)
;uloží na zásobník registry d0, d2, a3, a4, a5, a6

MOVEM.l (sp)+,d0/d2/a3-a6
;obnoví původní obsah d0, d2, a3, a4, a5, a6

Některé assemblery dovolují nadefinovat si pro tyto seznamy registrů symbol, a tím zamezit poměrně častým chybám - když se spletete a obnovujete jiný seznam registrů než jste uložili. Definice vypadá takto:

SEZNAM_REGISTRU EQUR d2/d3/d5/a6 ;seznam jakou MOVEM

Takto nadefinovaný symbol můžete použít místo seznamu registrů u MOVEM:

RUTINKA:
MOVEM.l SEZNAM_REGISTRU,-(sp) ;schovat ... rutina - mění registre
MOVEM.l (sp)+,SEZNAM_REGISTRU ;obnovit
RTS

JSR, BSR, RTS
Tyto tři instrukce se používají pro vytváření podprogramů, neboli rutin. Jsou to úseky vašeho programu, které provádí určitou činnost, jež je v průběhu potřeba víckrát. Je to, jako kdyby jste si vystřihli kousek vašeho programu (ten který se opakuje) a dali ho o kousek vedle. Na všech místech, kam by jste ten kousek potom museli pokaždé opsat umístíte pouze odkaz, že si má program "odskočit" na vaši rutinku, kterou máte vede, a pak se zase vrátit zpět a pokračovat dál. Abyste mohli něco takového provádět, potřebujete 1) instrukci, která sdělí procesoru, že si má odskočit a kam, 2) instrukci, která řekne procesoru, aby se vrátil zpátky tam, kde byl předtím. K odskočení do podprogramu máte k dispozici dvě instrukce: JSR a BSR. Obě dělají totéž, rozdíl je jen ve způsobu jakým procesoru sdělují KAM si má odskočit. BSR udává adresu rutiny relativně vůči PC. To má výhodu v tom, že výsledný kód instrukce je kratší a instrukce je nezávislá na umístění programu v paměti. Nevýhodou je omezená vzdálenost adresy skoku od aktuální pozice PC v závislosti na šířce offsetu (+-128 pro BYTE,+-32728 pro WORD na 68000. Na 68020+ lze použít jako offset i LONG, tudíž omezení mizí) a nemožnost skoků mezi různými sekcemi programu (protože mezi nimi není pevně daná vzdálenost). Naproti tomu JSR nabízí několik možností jak specifikovat adresu rutiny. Nebudu vypisovat všechny způsoby, jen ty ne zajímavější. Pokud chcete zjistit zda nějaká instrukce může používat nějaké adresační módy, není nic jednoduššího - napište ji a zkuste jestli ji assembler "sežere". Když ne, je to jasný!

JSR xxx.l ... nejobvyklejší způsob
JSR (d 16,An) ... k An je přičten offset a výsledek je adresou rutiny - tento způsob je používán pro volání rutin v sdílených knihovnách Amigy
JSR (An) ... adresa rutiny je v adresovém registru
JSR (d16,PC) ... v podstatě totéž co BSR.W

Samotný princip funkce je takovýto: V instrukci je nějakým způsobem uvedena adresa rutiny. Procesor si schová aktuální pozici PC na zásobník a skočí na adresu rutiny. Pro návrat z rutiny slouží instrukce RTS, která ze zásobníku vybere původní pozici PC a skočí zpět na ni. V podstatě každý váš program je při spuštění podprogramem procesu, který ho spustil. Zejména při spouštění z CLI je nejprve nahrán do paměti, do A0 je dán pointer na parametry z příkazové řádky, do D0 je dán počet znaků parametrů a váš program je zavolán jako podprogram. Před ukončením vašeho programu nastavíte do D0 návratový kód (obvykle 0 pokud je všechno v pořádku, 5 jako varování, 10 při chybě a 20 při totálním selhání) a ukončíte svůj program pomocí RTS.

JMP, BRA
Tyto instrukce obstarávají nepodmíněné skoky v programu. BRA je PC-relativní podobně jako BSR. JMP má více možností

JMP xxxx ... skočí na adresu xxxx

V podstatě NIKDY by jste neměli skákat na nějakou adresu natvrdo - třeba JMP $123456 - něco takového se na Amize legálně nemůže vyskytovat. Skoky v rámci programu musí být vždycky pomocí návěstí...

JMP (d16,Ax)
JMP (dB,An,Xi) - zajímavé pro výrobu tabulek s adresami rutin

PODMÍNĚNÉ SKOKY Bcc, CMP, TST, Scc
Podmíněné skoky umožňují větvení programu v závislosti na zrovna se vyskytnuvších okolnostech. V assembleru jsou podmíněné skoky řešeny jako skokové instrukce, které se provedou jen při určité kombinaci FLAGů procesoru. Tyto flagy jsou nastaveny obvykle nějakou operací před samotným podmíněným skokem. K nastavení těchto flagů máme k dispozici kromě toho, že většina instrukcí nastavuje flagy, také některé speciální instrukce k tomu určené. Instrukce TST porovná operand s nulou a nastaví flagy podle výsledků. Instrukce CMP porovná destination operand se source operandem a taktéž nastaví flagy podle výsledků. Různé kombinace flagů mají svoje označeni (condition codes - cc):

cc název kombinace flagů
HI High C + Z = 0
LS Low or Same C + Z = 1
CC (HS) Carry Clear (High or Same) C = 0  
CS (LO) Carry Set (Low) C = 1
NE Not Equal Z = 0
EQ Equal Z = 1
VC Overflow Clear V = 0
VS Overflow Set V = 1
PL Plus N = 0
MI Minus N = 1
GE Greater or Equal N (+) V = 0
LT Less Than N (+) V = 1
GT Greather Than Z + (N (+) V) = 0
LE Less or Equal Z + (N (+) V) = 7
T *) True
F *) False

*) nejde u Bcc + je OR (logické neboj (+) je XOR (logické exkluzivní nebo)

Tyto kombinace mají různý význam v závislosti na tom co způsobilo nastavení flagů.
Instrukce podmíněného skoku jsou relativní vůči PC podobně jako BRA. Jejich jména začínají na "B" a další dva znaky udávají condition code (cc), při kterém dojde ke skoku.

Např.:
BEQ <návěstí> ... skok je-li cc "EQ" pravdivý, tj. je-li flag "Z" nastaven

Instrukce Scc nastaví BYTE operandu na 0, pokud odpovídající cc není pravdivý, nebo na $ff (-1), pokud pravdivý je.

 

Př.:
SNE (a0) ... nastaví byte na adrese kam ukazuje A0 na 0 pokud flag Z = 1 nebo na $FF pokud flag Z = 0 (viz výpis "CCTest.s").

Následuje několik příkladů na výše uvedené instrukce:
;========================
;příklad 1 "Hopsanda.s"
;nahrajte si tento program do nějakého debuggeru a odkrokujte si ho
;========================
;podobná struktura programu je známá
;z BASICU - GOTO sem, GOTO tam... hrůza! Ne
;abyste něco ;takového psali do svých programů
;tohle má být jen ukázka skoků!!!!
SECTION sekce 1,CODE
START BRA.w Jupiii ;relativní skok uvnitř sekce
Hopsaaa JMP Huuu ;skok do jiné sekce nejde pc-relativně
Jupiii BRA.s Hopsaaa
Base DC.b "Vuci tomuto textu se bude skakat"
CNOP 0,4 ;alignment na hranici LONGu
OffsetOdBase = *-Base ;vypočteme offset vůči "Base". * označuje aktuální hodnotu PC na tomto místě
MOVEQ #0,d0 ;konec programu
RTS
SECTION sekce2,CODE
Huuu LEA Base,a0 ;do a0 dáme bázi
JMP OffsetOdBase(a0) ;nebo jmp (OffsetOdBase,a0) na 68020
END

 

;=========================
;Tohle si taky odkrokujte v debuggeru
;=========================

SECTION prg,CODE
START LEA retezec1(pc),a0
BSR NajdiA ;tím že neuvedu u bsr ".s" nebo ".w" nechám prostor assembleru aby to udělal sám (nebo mi vynadal)
RTS ;teď ve v D0 výsledek
;tady je testovací text ve kterém se hledá první "A" všimněte si že řetězce (stringy) se ukončují nulou
retezec1 DC.b "To jsem zvedav jestli to tady"
DC.b " to A vubec najde!",0
CNOP 0,4 ;nezapomeňte udržovat začátky rutin aligned
;Tato rutinka chce jako parametr adresu stringu v A0 a vrátí v D0 adresu prvního znaku "A" (velké A) v tomto stringu nebo nulu pokud žádný nenajde.
NajdiA MOVE.l a0,-(sp) ;schovat původní A0
.smycka MOVE.b (a0)+,d0 ;načtu si znak, Z bude nastaven pokud načtu 0, která označuje konec stringu
BEQ .nenasel ;byla to nula - nenašel jsem A
CMP.b #A,d0 ;bylo to "A"?
BNE .smycka ;nebylo-zkus další znak
SUBQ.l #1 ,a0 ;a0 teď ukazuje hned ZA A - spravit
MOVE.l a0,d0 ;do D0 adresu "A"
BRA.s .konec ;pokračuj ke konci
.nenasel MOVEQ #0,d0 ;nenašel - vracím nulu
.konec MOVE.l (sp)+,a0
RTS

Vytlačiť článok



Pozn.: články boli naskenované ako text a preto obsahujú aj zopár chýb. Taktiež neručíme za zdrojové kódy (Asm, C, Arexx, AmigaGuide, Html) a odkazy na web. Dúfame, že napriek tomu vám táto databáza dobre poslúži.

Žiadna časť nesmie byť reprodukovaná alebo inak šírená bez písomného povolenia vydavatela © ATLANTIDA Publishing



none

AMIGA REVIEW

57 ( 11-12 / 2000 )
56 ( 9-10 / 2000 )
55 ( 7-8 / 2000 )
54 ( 5-6 / 2000 )
53 ( 3-4 / 2000 )
52 ( 1-2 / 2000 )
 
51 ( 12 / 1999 )
50 ( 11 / 1999 )
49 ( 10 / 1999 )
48 ( 9 / 1999 )
46-47 ( 7-8 / 1999 )
45 ( 6 / 1999 )
44 ( 5 / 1999 )
43 ( 4 / 1999 )
42 ( 3 / 1999 )
41 ( 2 / 1999 )
40 ( 1 / 1999 )
 
39 ( 12 / 1998 )
38 ( 11 / 1998 )
37 ( 10 / 1998 )
36 ( 9 / 1998 )
35 ( x / 1998 )
34 ( x / 1998 )
33 ( 1-2 / 1998 )
 
32 ( 11-12 / 1997 )
31 ( 9-10 / 1997 )
30 ( 7-8 / 1997 )
29 ( 6 / 1997 )
28 ( 5 / 1997 )
27 ( 4 / 1997 )
26 ( 3 / 1997 )
25 ( 2 / 1997 )
24 ( 1 / 1997 )
 
23 ( 12 / 1996 )
22 ( 11 / 1996 )
21 ( 10 / 1996 )
20 ( 9 / 1996 )
18-19 ( 7-8 / 1996 )
17 ( 6 / 1996 )
16 ( 5 / 1996 )
15 ( 4 / 1996 )
14 ( 3 / 1996 )
13 ( 2 / 1996 )
12 ( 1 / 1996 )
 
11 ( 12 / 1995 )
10 ( 11 / 1995 )
9 ( 10 / 1995 )
8 ( 9 / 1995 )
7 ( 7 / 1995 )
6 ( 5 / 1995 )

ATLANTIDA NEWS

5 ( 3 / 1995 )
4 ( 1 / 1995 )
 
3 ( 11 / 1994 )
2 ( 9 / 1994 )
1 ( 7 / 1994 )
0 ( 5 / 1994 )