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

Assembler a systém

Jan 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

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 )