Assembler a systémJan Hlavatý
Tak nám to pěkně uteklo, a máme tady zase naše malé dobrodružství v oblasti
programování v assembleru.
Minule jsme skončili malým prográmkem. Doufám že se vám podařilo ho úspěšně
přepsat, přeložit a spustit, takže už asi víte, co to dělá. No a teď si povíme,
JAK TO, že to dělá...
Takže: Nejdřív se pozastavíme nad formou programu, bez přihlížení k jeho
konkrétní funkci. Z minula víte, že zdrojový text je v podstatě textový soubor,
rozdělený na řádky. Assembler (tj. program) při překladu tento soubor postupně
prochází po řádcích a podle toho co tam najde buďto vygeneruje příslušnou
instrukci strojového kódu nebo udělá něco jiného (třeba NIC). Podle toho co
podle nich překladač dělá, rozdělujeme příkazy na jednotlivých řádcích na
INSTRUKCE a PSEUDOINSTRUKCE.
Instrukce je v podstatě popis instrukce strojového kódu, kterou chceme na tomto
místě vygenerovat. Popis instrukcí (jejich formát) definoval přímo výrobce
procesoru a je závazný pro všechny různé assemblery. Pseudoinstrukce naproti
tomu jsou speciální příkazy, určené pro ten který překladač. Vzhledem tomu, že
pseudoinstrukce nebyly nikdy standardizovány, vznikl v této oblasti poměrně
velký chaos. My budeme pokud možno používat ty nejrozšířenější, i když se
pokusím uvést i ty méně používané, abyste si je uměli přeložit do těch
používaných. No problemo! Takže doktore, připravte špendlíky, skalpel a
mikroskop, jde se pitvat náš prográmek!
Tááák, copak to tu máme? Na začátku jsou nějaké komentáře začínají středníkem,
hmmm.. jo... ha! Copak je "_LVOOpenLibrary=-552"? Tedy: Jde o definici
KONSTANTY. Obecně je to: <identifikátor> = <konstanta>, nebo <identifikátor> EQU
<konstanta>. "=" vypadá řekl bych jasně i a oboje dělá to samé - vytvoří
konstantu, jejíž jméno je bezprostředně na začátku řádku (stejně jako návěstí) a
přiřadí jí nějakou hodnotu. V dalším textu programu pak můžete místo přímého
číselného vyjádření psát jenom jméno té konstanty. Jakou to má výhodu?
Tak zaprvé: PŘEHLEDNOST, a to v případě že jméno konstanty je dobrým opisem
jejího významu (např. DELKA = 10).
Zadruhé: Snadná modifikovatelnost programu. Pokud chcete změnit nějakou hodnotu,
je daleko lepší změnit ji v definici konstanty na začátku programu, než hledat v
celém programu nějaké číslo a všude ho přepisovat (a to číslo co najdete přitom
vůbec nemusí být ta konstanta, jenom má stejnou hodnotu, ale to vy nevíte).
Takže výsledkem je: používat konstanty v maximální míře se vyplácí! Všechny
definice konstant by měly být na začátku programu a řádně okomentovány (alespoň
tam). Za zmínku stojí že konstantu lze nadefinovat pod stejným jménem jen
JEDNOU. V opačném případě nahlásí překladač chybu! Co by to bylo za konstantu,
kdyby se pořád měnila? Vraťme se k pitvě: na začátku tedy definujeme čtyři
konstanty: _LVOOpenLibrary, _LVOCloseLibrary, _LVOOutput, _LVOWrite. V dalším
textu programu jsou pak tyto konstanty používány místo těch nic neříkajících
záporných čísel. Copak tam máme dál? Dalším objektem našeho zájmu bude "section
prg,code". Toto je pseudoinstrukce, podobně jako přiřazení konstanty. Její
význam je trochu složitější, takže pozor: Když vytvoříte program, vytvoříte v
podstatě několik věcí:
1. Samotný kód programu, tedy strojový kód instrukcí procesoru
2. Různé texty a jiné konstanty, které se při běhu programu nemění
3. Proměnné, tedy rezervované místo v paměti, kam se při běhu programu budou
ukládat různé hodnoty. Tyto proměnné by se daly rozdělit na inicializované,
které mají na začátku běhu programu nějakou hodnotu, a neinicializované, které
obsahují nějakou nedefinovanou či náhodnou hodnotu. Tyto různé věci je vhodné
rozdělit do jakýchsi bloků (SEKCÍ) podle jejich typu. Kód programu, konstanty a
texty se obvykle dávají do takzvané KÓDOVÉ SEKCE. Znakem kódové sekce je, že se
data v ní obsažená NIKDY NEMĚNÍ. To je velice důležité pro kompatibilitu s
vyššími procesory a pro některé speciální vlastnosti programu v prostředí
multitaskingu, takže pokud chcete psát programy, které mění svůj vlastní kód,
tak na to okamžitě zapomeňte!!! V opačném případě váš program spadne na prvním
počítači s procesorem 68020 a výše... Zpět k sekcím: Existují tedy tři druhy
sekcí: CODE, DATA a BSS. V code sekci tedy bývá kód programu, konstanty a texty.
V DATA sekci pak bývají inicializované proměnné, tj. s předem nastavenými
hodnotami, které se však při běhu programu budou měnit. V BSS sekci jsou pak
neinicializované proměnné, které DOS před spuštěním vynuluje. V programu může
být i víc sekcí. Každá sekce se při spuštění programu nahrává jako samostatný
blok do paměti. Když tedy máte v programu více menších sekcí, je větší
pravděpodobnost že se vejde do paměti než jedna velká sekce (kvůli segmentaci
paměti). V našem případě tedy zakládáme kódovou sekci se jménem "prg". Obecně se
to dělá: SECTION <jméno sekce>,<typ_sekce>. Tady je ještě jedna věc, na kterou
musím upozornit, a to typ paměti, ze které se bude alokovat příslušný blok pro
sekci. Jak určitě víte, na Amize existují dva druhy paměti: CHIP a FAST. Pokud
potřebujete sekci uložit do nějakého specifického typu paměti (např. data
obrázku do chip-ram), musíte to vyjádřit při definici sekce. Obyčejně se to dělá
příponou "_C" (chip) nebo "_F" (fast) za typem sekce, tedy např. "section
gfx,data_c" vytváří datovou sekci "gfx" v chip-ram. Některé assemblery (A68k) to
dělají jinak - typ paměti vyžadují jako další parametr: "section gfx,data,chip".
Tento způsob však není rozšířený a uvádím ho jen abych vám umožnil "překlad". Co
se týče určování typu paměti - nikdy nepoužívejte přímé určení FAST ram. Na
většině Amig totiž tato paměť není a váš program na nich vůbec nepůjde nahrát a
spustit. Nejlepší je nepoužít určení paměti u kódových a datových sekcí, které
nemusí být v CHIP-ram vůbec. To pak znamená: pokud je FAST, tak FAST, jinak
CHIP. Jediný důvod pro určení do CHIP-ram je např. definice bitplánů obrázku,
dat sprajtů, samplů, copperlistů a podobně. Dále musím upozornit, že direktivu
"SECTION" lze použít se stejnými parametry VÍCKRÁT. Nová sekce se založí jen při
prvním použití. Dále už má jen funkci "následující data patří do sekce..." Tak
to by bylo. Dále už pak následují skutečné instrukce procesoru. Návěstí START
zde symbolicky označuje první instrukci našeho programu. Ve skutečnosti není
nutné, protože při spuštění programu procesor prostě skočí na začátek první
sekce. Jako první by se tedy měla definovat kódová sekce. Naše kódová sekce se
jmenuje "prg". První instrukcí programu je "MOVE.L 4.w,A6". MOVE je instrukce
pro kopírování hodnoty ze zdrojového operandu do cílového operandu (CO,KAM).
Zdrojovým operandem, je tady "4.w", což je ABSOLUTNÍ ADRESA 4, u které je
poznamenáno příponou ".w", že je to word (adresa 4 je dost malé číslo, aby se
vešlo do wordu. Tím jsme uspořili 2 byte, protože místo LONGu jsme použili WORD.
Procesor předpokládá, že zbytek do LONGu je nulový. Ve skutečnosti by tam ta
přípona ani být nemusela, většina assemblerů je schopna optimalizace, tedy
ušetří ty 2 byte automaticky). Pokud je v operandu takto přímo uvedena adresa
(natvrdo numericky nebo jako návěstí), pak se vždy jedná o to, co je na této
adrese uloženo, tj. v našem případě je to LONG, uložený na adrese 4. Tato adresa
má v systému Amigy zvláštní význam a je to vlastně jediná adresa (kromě
hardwarových registrů), odkud můžete na Amize něco legálně přečíst mimo váš
program. Je to vlastně jakýsi hřebík, na kterém visí celý operační systém - jen
odtud se můžete dostat ke zbytku systému. Na rozdíl od třeba C64, na Amize se
nesmíte odkazovat přímo na systémovou paměť ROM, a to z jednoduchého důvodu - u
různých verzí OS se ROM liší.
Kompatibilita jednotlivých Amig je řešena geniálním způsobem. Celý systém Amigy
je řešen stavebnicovým způsobem. Rutiny systému jsou rozděleny na jednotlivé
knihovny a jiné softwarové objekty, jejichž funkce se volají způsobem, který
není závislý na jejich verzi. Co se týče kompatibility DO BUDOUCNA, je systém
3.0 mojí A1200 to nejvymakanější, co jsem kdy viděl (a že jsem koukal). Škoda že
u verze 1.3 ještě autoři nevěděli přesně co chtějí, a teď si trhali vlasy když
měli implementovat třeba přesměrování grafiky. Já osobně bych nejraději zanechal
OS 1.3 temné minulosti a programoval jen pro 2.04 a výše, alespoň pokud jde o
systémové programování... Rozhodně stojí za to si sehnat novější verzi OS i do
starších Amig (A500).
Takže zpět: Pro nás nejzajímavějšími objekty systému jsou KNIHOVNY. Název je sám
o sobě dost výstižný. taková knihovna je v podstatě balík rutin, týkající se
určité oblasti služeb systému. No a obsah adresy 4 je právě adresa
nejdůležitější knihovny systému "exec.library". Pomocí této knihovny získáte
přístup ke všem ostatním objektům systému. Jakmile máte tzv. BÁZI execu, ten
můžete požádat o další knihovny a jiné věci. Mimochodem - BÁZE je obvyklé
označení nějaké pevné adresy, od které jsou pak pomocí OFFSETŮ odvozovány adresy
různých věcí. Obecně OFFSET = ADRESA NĚČEHO = BÁZE. Výhodou tohoto (relativního)
přístupu je, že když máme OFFSETY, stačí nám zjistit BÁZI ke které se vztahují a
máme adresy toho co chceme, a to nezávisle na konkrétním umístění v paměti. Aby
to bylo jasnější, vemte to třeba takto: Máte datovou STRUKTURU, tedy skupinku
proměnných pevně daného typu. 1 LONG a 1 WORD. Celá ta struktura má tedy
dohromady 6 byte. Znáte adresu celé té struktury, která je stejná jako adresa
prvního prvku té struktury (LONG). Normálně pracujete jen s adresou celé
struktury (například vám ji předá jiná část vašeho programu). No a vy teď
potřebujete přečíst z té struktury obsah toho WORDu. Jeho adresu neznáte, ale
znáte adresu celé té struktury a víte že váš WORD začíná hned za prvním LONGem,
který je dlouhý 4 byte. OFFSET vašeho WORDu je tedy vůči začátku celé struktury
4. Abyste získali adresu vašeho WORDu, prostě přičtete k BÁZI (adrese celé
struktury) OFFSET vašeho WORDu (jak daleko od začátku struktury je) a výsledkem
je adresa vašeho WORDu. No a můžete číst. Jasný? S knihovnou je to podobně. Od
systému získáte BÁZI vaší knihovny, a protože znáte OFFSETY funkcí které chcete
volat, jste v pohodě. Offsety všech funkcí a jiných věcí jsou obsaženy v
definičních souborech, které dodává výrobce. Protože však za ně žádá nemalý
peníz (i když si myslí, že chcete programovat komerčně a stráááášně na tom
vyděláte a on ne), lze najít všude možně různé alternativní soubory, kde jsou
sice všechny definice, ale bez jakýchkoliv komentářů, na které však nikdo
copyright nemá. Bylo by dobré si je sehnat. V tomto prográmku jsem prostě opsal
ty potřebné na začátek programu. Jsou to ty konstanty začínající na "LVO". To
LVO znamená Library Vector Offset. Pokud si všímáte, tyto offsety jsou záporné.
Knihovna totiž vypadá asi takto:
<vektory_funkcí><datové_struktury>
^báze
Vektory funkcí je hromada instrukcí pro skok na všechny rutiny knihovny. Jsou
organizovány tak, že každá další funkce je o 6 byte dál do minusu od báze
knihovny. Protože tyto skokové instrukce jsou v RAM je možné vylepšovat i funkce
knihoven v ROM. Datové struktury obsahují základní informace pro systém, aby s
knihovnou mohl pracovat. V našem případě jsme vyzvedli z adresy 4 bázi
"exec.library" a jsme odhodláni ji použít. Prográmek by měl vypsat text do okna
CLI, odkud byl volán. K tomu nám pomůže knihovna systému "dos.library", která má
na starosti všemožné vstupně-výstupní operace se soubory a podobně. K získání
báze nějaké knihovny (případně k jejímu načtení do paměti, když tam není) slouží
funkce exec.library OpenLibrary()". Všimněte si, že knihovny se na Amize
otevírají a zavírají - to umožní systému udržovat v paměti jen ty knihovny,
které zrovna někdo používá. Nesmíme zapomenout knihovnu zavřít, když ji už
nepotřebujeme! Jinak by zůstala pořád v paměti... takže pozor: kolikrát knihovnu
otevřete, tolikrát ji musíte taky zavřít, jinak hrozí CHAOS!! Mno. Funkce
OpenLibrary() potřebuje dva parametry, aby věděla jakou knihovnu má vlastně
otevřít. Zaprvé je to JMÉNO KNIHOVNY, kterou má otevřít. Jak se píšou jména?
Jako řada znaků ASCII v paměti ukončená nulovým bajtem. tato konvence je známa
zejména z jazyka C (však také byl systém z většiny v C napsán). Chceme-li tedy
předat jméno "dos.library", nadefinujeme tento text v paměti, ukončíme ho nulou
a adresu jeho začátku předáme funkci OpenLibrary v registru A1. To byl první
parametr. Jak se definuje text se podívejte na konec programu, kde je návěstí
"dosname". Za ním je pseudoinstrukce "dc". Tato pseudoinstrukce slouží k přímému
nadefinování obsahu paměti. Přípona ".b" znamená, že půjde o sekvenci BAJTů, což
je přesně to co potřebujeme pro text v ASCII (1 znak = 1 byte). Jako parametr
pak může být hromada hodnot oddělených čárkami. Pro snadnější definici textů je
také možné napsat tam text v uvozovkách ("text") nebo v apostrofech (text To
pak definuje víc bajtů za sebou místo zdlouhavého psaní (jako dc.b a,
h,o,j). Tímto máme v paměti nadefinováno jméno "dos.library" ukončené
nulou (vidíte ji? tam na konci? NEZAPOMENOUT NA NI!). Začátek jména jsme si
označili návěstím "dosname". Samozřejmě by tam mohlo byt "jiné jméno, třeba
rohlík", ale bylo by značně zavádějící. Jak ale dostat adresu začátku jména do
adresového registru A1? K vložení adresy něčeho do některého adresového registru
slouží instrukce LEA (Load Effective Address). Operandy této instrukce je opět
CO a KAM. Prvním parametrem je tedy adresa něčeho, druhým pak adresový registr,
do kterého se tato adresa zkopíruje. Způsoby, jakými se ta adresa dá získat,
jsou dva: První by vypadal takto: "LEA dosname,A1". V tomto případě je ABSOLUTNÍ
adresa přímo uvedena v kódu instrukce. Nevýhodou tohoto způsobu je, že tato
adresa je ABSOLUTNÍ, tj. museli byste přesně vědět, na kterou adresu se váš
program při spuštění nahraje. To však samozřejmě nevíte! Tento nedostatek je
odstraněn použitím tzv. RELOKAČNÍCH TABULEK, které jsou součástí spustitelného
souboru. Když se takový soubor nahraje, systém si tyto tabulky projde a opraví
kód vašeho programu, aby odpovídal adrese, na kterou byl nahrán. V některých
případech není vyhnutí, a to když pracujete s daty která jsou umístěna v jiné
sekci nebo když jsou data ve stejné sekci ale jsou příliš daleko od této
instrukce aby se dal použít následující způsob adresace, a to PC-relativní: "LEA
dosname(PC),A1". V tomhle případě si to assembler vyloží asi takhle: "vypočítej
16-bitový offset dosname vůči této instrukci a ulož ho do kódu instrukce". Při
běhu programu pak procesor naopak vyzvedne offset (WORD), připočte k němu adresu
této instrukce a výsledek uloží do A1. Všimněte si, že tento kód není závislý na
umístění v paměti, protože používá offset místo absolutní adresy. Samozřejmě na
68000 může tento offset být jen -32768..32767, takže objekt, jehož offset
počítáme nemůže být příliš daleko od této instrukce (i když 32k je dost na
assembler) a navíc musí být ve stejné sekci jako tato instrukce, protože
nemůžeme na jisto vědět jak daleko od této sekce budou jiné sekce, anžto se
alokují z paměti zvlášť. Takže: výsledný efekt obou variant je stejný, ale
pc-relativní verze je o 2 byte kratší a nepotřebuje relokaci. Tak a máme adresu
jména v A1. Druhým a posledním parametrem. OpenLibrary() je minimální číslo
verze knihovny, kterou chcete otevřít. To je dobré hlavně na to, abyste se
nepokusili volat funkce, které v této verzi knihovny vůbec nejsou (=CRASH!).
Je-li verze dané knihovny menší než tento parametr, systém se chová, jako by ji
vůbec nenašel a vrací chybu. Minimální verze knihovny se předává v registru D0.
Pokud je to 0, na verzi nezáleží. K vložení 0 do registru DO jsem použil
instrukci "MOVEQ". Tato instrukce provede rychlé nastavení datového registru na
nějakou hodnotu v rozsahu 128..127. Výhodou této instrukce je, že hodnota je
přímo obsažena v kódu instrukce, takže celá instrukce má jenom 1 WORD a je
velice rychlá. MOVEQ funguje pouze na datové registry. Funkce OpenLibrary()
vrací zpět v registru DO BÁZI požadované knihovny, nebo NULL (nulu) když
knihovna neexistuje nebo je příliš stará. Na tomto místě bych měl vysvětlit
obecnou konvenci volání funkcí knihoven. Základem je BÁZE knihovny, která musí
být VŽDY v registru A6. Parametry pro funkce se většinou předávají v registrech,
konvence se různí podle knihoven. Funkce zachovávají obsahy registrů kromě D0,
D1, A0,A1. Pokud funkce něco vrací, činí to vždy v registru D0. Funkce se volají
jako podprogramy pomocí offsetu funkce a báze v A6: "JSR <offset>(A6)". To by
byla 4. instrukce. Nyní se exec pokusil otevřít "dos.library" a pokud uspěl,
máme bázi v D0. V opačném případě máme v D0 nulu. Jak to otestujeme? Neměli
bychom spoléhat na nastavení flagu Z od systému, protože to není nikde zaručeno.
Tak si hodnotu v D0 budeme muset otestovat, je-li rovna 0. K tomu máme speciální
instrukci "TST". Tato instrukce otestuje svůj operand a nastaví flagy procesoru
podle hodnoty operandu: Je-li roven nule, nastaví příznak Z, je-li záporný (z
pohledu čísel se znaménkem), nastaví flag N. Nás zajímá první případ, tj.
rovnost nule. V závislosti na výsledku teď provedeme větvení programu. K tomu
použijeme tzv. PODMÍNĚNÝ SKOK. Je to instrukce, která otestuje nějaký flag (nebo
i víc) a v závislosti na výsledku pak provede PC-relativní skok (tj. s offsetem)
nebo ne. Offset tohoto skoku může být buď BYTE nebo WORD, podle toho, jak je cíl
daleko. Assembler většinou sám vybere vhodnou formu ale můžete ji přímo určit
příponou (.w pro WORD a .b nebo .s pro BYTE). Obecně pokud se neuvede přípona,
je implicitní hodnota WORD, takže pozor! To platí pro všechny instrukce s
příponami! Takže: instrukce "BEQ" provede skok, je-li flag Z nastaven. To se
stává zejména když jsme něco porovnávali instrukcí CMP a oba operandy si byly
rovny - od toho název (Branch if EQual). Tímto podmíněným skokem je zajištěno,
že se náš program nezhroutí i kdyby (nedejbože) nešla otevřít "dos.library".,
Návěstí ".fuj1" je tzv. LOKÁLNÍ protože má před sebou tečku. Lokální návěstí
platí jen v úseku mezi dvěma normálními, tj. GLOBÁLNÍMI návěstími. Lokální
návěstí se hojně používají zejména uvnitř podprogramů pro nevýznamná návěstí.
Skok na ".fuj1" má za následek přeskočení zbytku programu, který by jinak
způsobil zhroucení systému. Poté, co si jsme jisti, že v D0 je opravdu báze
"dos.library", dáme si ji do registru A6, kam patří. To provádí instrukce
"MOVE.L D0,A6". Vzhledem k tomu, že nebudeme používat žádnou jinou knihovnu,
nemusíme si bázi ukládat do žádné proměnné, ale necháme ji v A6. No a můžeme
volat funkce DOSu. DOS pohlíží na okno CLI ze kterého byl program spuštěn jako
na textový soubor. Když něco zapíšete do tohoto souboru, vypíše se to do okna
CLI. Tento výstupní soubor je připraven před spuštěním programu a jeho
FileHandle (jde o pointer, který jednoznačně určuje otevřený soubor pod DOSem je
možno zjistit použitím funkce Output() DOSu. Tato funkce nemá žádné parametry a
vrací FileHandle výstupního souboru nebo NULL pokud takový soubor neexistuje.
Zavoláme tedy Output, abychom mohli do výstupního souboru zapisovat. Výsledek
obdržíme v D0. Do souboru se zapisuje funkcí Write(). Ta potřebuje FileHandle
souboru do kterého se má zapisovat v D1, adresu dat která má zapisovat v D2 a
jejich délku v D3. Vrací počet bezchybně zapsaných byte v D0. Pokud to
nesouhlasí, můžete funkcí IoErr() zjistit příčinu - vrací chybový kód v D0 který
odpovídá těm z manuálu DOSu. Tady musím upozornit na chyták - přesunem D0 do D1
jsem zároveň otestoval, je-li D0 nula. Obecně totiž takovýto přenos DO DATOVÉHO
REGISTRU nastavuje příznaky procesoru. Pozor! Při přenosu do ADRESOVÉHO registru
tomu tak není! Pokud Output() vrátil NULL, nemá cenu něco zapisovat, protože
není kam. Zapisování je tedy v tom případě přeskočeno. Dále, protože v D1 už mám
FileHandle, nastavuji D2 a D3 na patřičné hodnoty - adresu a délku dat. Tady si
všimněte znaku "#" před symboly "text" a "textlen". Tento znak znamená, že se
jedná přímo o HODNOTY těchto symbolů, NE O OBSAH paměti kam ukazují. Podobně u
"MOVEQ #0,D0" jde o NULU samotnou ne o obsah adresy 0; Okej? Následuje volání
funkce Write(), na jejíž návratovou hodnotu zvesela zapomenu, anžto mě nezajímá.
To způsobí vypsání textu (to volání, hehe). Všimněte si metody, jakou jsem
spočítal znaky zcela automaticky (návěstí textlen). Symbol "*" představuje vždy
aktuální adresu instrukce na začátku tohoto řádku. Tady jsem do konstanty
"textlen" uložil rozdíl mezi adresami konce a začátku textu, což není nic jiného
než jeho délka. Když tedy text přepíšete na něco jiného (třeba "Joe of Agony je
debil"), délka textu se automaticky spočítá sama. No a dál už jen musíme uzavřít
knihovnu "dos.library", aby systém věděl, že už ji nepotřebujeme. To se provádí
pomocí funkce "CloseLibrary()" z "exec.library". Ta jako parametr vyžaduje bázi
knihovny kterou jsme dostali od OpenLibrary() v registru A1. Nakonec ještě
vrátíme systému kód 0 v registru D0, což pro něj znamená "dobrý". Hodně programů
na to zapomíná, takže vrací systému kódy jako 2342769201, takže chudák neví
která bije. No a nakonec provedeme návrat zpět do CLI instrukcí RTS, což je
návrat z podprogramu. Za tím už jsou jen definice textů které jsme si už
popsali. TAK CO? Je to až tak stráááááášně těžký? Není, co? Takže příště dodám
kompletní popis instrukcí a adresace a můžeme do toho natvrdo! ASSEMBLY - FEEL
THE POWER!!!!!!
;Hello.asm - malý testík
;exec.library:
_LVOOpenLibrary = -552
_LVOCloseLibrary = -414
;dos.library:
_LVOOutput = -60
_LVOWrite = -48
section prg,code
START move.l 4.w,a6 ; báze exec do a6
lea dosname(pc),a1 ; jméno dos.library
moveq #0,d0 ; jakákoli verze
jsr _LVOOpenLibrary(a6) ; otevřít
tst.l d0 ; mám dos.library?
beq .fuj1 ; ne->padám
move.l d0,a6 ; jo->do a6 s ní
jsr LVOOutput(a6) ; zjistit výstup
move.l d0,d1 ; mám ho v D1?
beq .fuj2 ; ne->padám
move.l #text,d2 ; adresu textu do d2
move.l #textlen,d3 ; délku textu do d3
jsr _LVOWrite(a6) ; Vypsat
.fuj2 move.l a6,a1 ; dosbase do a1
move.l 4.w,a6 ; execbase do a6
jsr LVOCloseLibrary(a6) ; zavřít dos
.fuj1 moveq #0,d0 ; normální kód
rts ; skončit
dosname dc.b "dos.library",0 ; jméno dosu
text dc.b "Nazdar mamlasos!",10 ; text,<LF>
textlen equ *-text ; výpočet délky textu 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
|