AssemblerJan 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
|