Jméno tohoto softwaru je EuroAssembler.
Všimněte si chybějící mezery mezi Euro a Assembler.
Název se často zkracuje jako €ASM.
V 7-bitovém (ASCII) prostředí bývá rovněž uváděn jako EUROASM
a v některých interních identifikátorech je to pouze ea.
Znak Euro € je na klávesnici v MS-Windows dostupný jako Alt~0128 nebo jako HTML entita €.
Některé další vlastnosti se u jiných asemblerů vidí pouze zřídka:
euroasm.exe source1.asm, source2.asm, more*.asm
Návěští, symboly definované pomocí EQU, struktury, to vše může být odkazováno dříve, než bylo definováno (i když to nelze doporučit). |0000: | ; Odkaz na strukturovanou proměnnou Today, která bude definována později. |0000:C706[1000]E007 | MOV [Today.Year],2016 ; Vlož číslo do proměnné typu WORD. |0006:C606[1200]0C | MOV [Today.Month],12 ; Vlož číslo do proměnné typu BYTE. |000B:C606[1300]1F | MOV [Today.Day],31 ; Vlož číslo do proměnné typu BYTE. |0010: | |0010:00000000 |Today DS Datum ; Definice strukturovaného symbolu, jehož struktura bude deklarována později. |0014: | |[Datum] |Datum STRUC ; Deklarace struktury Datum. |0000:.... |.Year DW WORD |0002:.. |.Month DB BYTE |0003:.. |.Day DB BYTE |0004: | ENDSTRUC Datum
<HTML tagem>
se považují za komentáře.
To umožňuje dokumentovat zdrojový kód pomocí značkovacího jazyka.HEAD
a ENDHEAD
. Vložitelný oddíl programového modulu HEAD..ENDHEAD
nemusí být udržován v samostatných hlavičkových souborech (jako např. *.hsoubory v jazyku C).
PROC..ENDPROC
€ASM zavádí
semiinline procedury PROC1..ENDPROC1
,
které se expandují z makra pouze jednou, během jeho první expanze.IMPORT RegCloseKey, LIB="user32.dll"
.
Importní knihovny nejsou linkerem €ASM vyžadovány, i když je podporuje.PROGRAM..ENDPROGRAM
generuje svůj vlastní objektový nebo spustitelný soubor.
Pokud autor chce, může udržovat celý mnohamodulový projekt v jediném velkém souboru.euroasm source.asm
.HelloL32.xand
HelloL64.x. Oba spustitelné soubory budou vytvořeny z následujícího zdrojového souboru
hello.asmjediným povelem
euroasm hello.asm
.
Můžeme je pak spustit v Linuxu nebo v jeho Windows emulátoru WSL:EUROASM CPU=x64 HelloL32 PROGRAM Format=ELFX, Entry=Main:, Width=32 ; HelloL32.exe funguje v 32bitovém Linuxu. Main: MOV EAX,4 ; Volání jádra sys_write=4. MOV EBX,1 ; Souborový deskriptor standardního výstupu (konzoly). MOV ECX,Message ; Adresa zprávy. MOV EDX,SIZE# Message ; Velikost zprávy. INT 0x80 ; Zavolej jádro. MOV EAX,1 ; Operace sys_exit=1. XOR EBX,EBX ; Vrať errorlevel=0. INT 0x80 ; Zavolej jádro. Message: DB "Hello, world of %^Width bits in Linux!",10 ENDPROGRAM HelloL32 HelloL64 PROGRAM Format=ELFX, Entry=Main:, Width=64 ; HelloL64.exe funguje v 64bitovém Linuxu. Main: MOV RAX,1 ; Volání jádra sys_write=1. MOV RDI,1 ; Souborový deskriptor standardního výstupu (konzoly). LEA RSI,[Message] ; Adresa zprávy. MOV RDX,SIZE# Message ; Velikost zprávy. SYSCALL ; Zavolej jádro. MOV RAX,60 ; Oparace sys_exit=60. XOR EDI,EDI ; Vrať errorlevel=0. SYSCALL ; Zavolej jádro. Message: DB "Hello, world of %^Width bits in Linux!",10 ENDPROGRAM HelloL64
Také bychom mohl skrýt většinu strojových instrukcí předchozích programů pomocí makroinstrukcí z knihoven
linapi.htm
(32 bit) a linabi.htm
(64 bit),
a použitím literálů (=B "Hello...") k definici zobrazovaných řetězců:
EUROASM CPU=x64 HelloL32 PROGRAM Format=ELFX, Entry=Main:, Width=32 ; HelloL32.exe funguje v 32bitovém Linuxu. INCLUDE linapi.htm ; Definuj 32bitová makra StdOutput a TerminateProgram. Main: StdOutput =B "Hello, world of %^Width bits in Linux!", Eol=Yes TerminateProgram Errorlevel=0 ENDPROGRAM HelloL32 %DROPMACRO * ; Zapomeň makra definovaná v "linapi.htm". HelloL64 PROGRAM Format=ELFX, Entry=Main:, Width=64 ; HelloL64.exe funguje v 64bitovém Linuxu. INCLUDE linabi.htm ; Definuj 64bitová makra StdOutput a TerminateProgram. Main: StdOutput =B "Hello, world of %^Width bits in Linux!", Eol=Yes TerminateProgram Errorlevel=0 ENDPROGRAM HelloL64
A ještě příklad programů určených pro MS-Windows:
EUROASM CPU=x64, SIMD=Yes HelloW32 PROGRAM Format=PE, Entry=Main:, Width=32 ; HelloW32.exe funguje v 32bitových a 64bitových Windows. INCLUDE winapi.htm ; Definuj 32bitová makra WinAPI a TerminateProgram. Main: WinAPI MessageBox,0,="Hello, world of %^Width bits in Windows!",="Title",0, Lib=user32.dll TerminateProgram Errorlevel=0 ENDPROGRAM HelloW32 %DROPMACRO * ; Zapomeň makra definovaná v "winapi.htm". HelloW64 PROGRAM Format=PE, Entry=Main:, Width=64 ; Hello64.exe funguje v 64bitových Windows. INCLUDE winabi.htm ; Definuj 64bitová makra WinABI a TerminateProgram. Main: WinABI MessageBox,0,="Hello, world of %^Width bits in Windows!",="Title",0, Lib=user32.dll TerminateProgram Errorlevel=0 ENDPROGRAM HelloW64
Tento manuál reprezentuje dokumentaci, referenční příručku, příklady a implementační poznámky. K jejich rozlišení slouží rozličné styly.
Podkladová barva stránky rozlišuje
Přerušované podtržení odkazuje na odstavec v rámci téže webové strany.
Podtržení naviguje na jinou HTML stránku v rámci tohoto webu.
Podtržení s
ikonou Link odkazuje na stránku Links
soustřeďující externí odkazy.
Podtržení s ikonou Exit naviguje mimo web EuroAssembleru, možná je budete chtít otevřít v novém okně nebo tabu.
Obsah tohoto návodu je organizován do kapitol se stromovou strukturou.
Definice nových pojmů jsou psány tučnou modrou kurzívou.
Implementační detaily, diskuse a osobní poznámky se zobrazují menším fontem.
Názvy souborů
jsou zvýrazněny uvozovkami.
Znaky použité v textu mají bílé pozadí.
Krátké úseky zdrojového kódu
se zobrazují neproporciálním fontem černě na žluté v toku textu.
; Delší ukázky zdrojového kódu se zobrazují v rámečku. ; Mohou mít více řádků. ;Negativní příkladyjsou přeškrtnuty.
Asembler (ASM) je jediný programovací jazyk, který nám dává plnou kontrolu nad strojovým kódem.
Muset ručně zapisovat každou instrukci pro Central Processing Unit (CPU)
je ovšem značně namáhavé, proto byly vynalezeny podprogramy:
procedury, funkce, makroinstrukce.
Podprogram je něco jako černá skříňka s dokumentovaným účelem, vstupem a výstupem.
Hlavní rozdíl mezi naším vlastním podprogramem a funkcí vyššího jazyka (HLL)
je v tom, že pokud nepracuje dle očekávání, můžeme snadno vystopovat chybu
krokováním programu v debugeru, a je to pouze naše vlastní chyba.
Podprogram v asembleru může vykonávat stejnou funkci jako povely vyššího programovacího jazyka (HLL) nebo volání operačního systému (OS). Makrojazyk EuroAssembleru dovoluje připravit pokročilá makra šitá na míru řešenému problému a vyvíjet pak programy v ASM téměř stejně rychle jako HLL.
Výhoda znalosti asembleru se projeví, když nějaký špatně napsaný cizí program způsobí výjimku a skončí. Dr.Watson, disasembler, debuger ukazuje pouze kód ve strojových instrukcích, který bez znalosti asembleru budeme jen stěží umět interpretovat, zatímco zkušený ASM programátor se bude cítit jako ryba ve vodě.
Hlavní nevýhodou asemblerů jsou chybějící standardní knihovny, které sjednocují programování v HLL jako C nebo Java. Mnoho ASM programátorů buduje své vlastní knihovny, což vyžaduje poskytovat jejich kód současně s programem. Ovšem tvorba vlastní knihovny je zároveň nejlepší metoda, jak si zapamatovat názvy a parametry v ní obsažených funkcí.
Distribuce EuroAssembleru obsahuje několik makroknihoven pro začátek a pro inspiraci.
Fáze | Použitý nástroj |
---|---|
design-time | představivost |
write-time | textový editor |
assembly-time | asembler |
combine-time | linker |
link-time | linker |
load-time | loader operačního systému |
bind-time | loader operačního systému |
run-time | procesor |
Nespokojenos s existujícími nástroji je hlavní důvod, proč někteří programátoři vyvíjejí vlastní programovací jazyk.
Nezanedbatelná je skutečnost, že vytvoření asembleru je velmi zajímavá činnost. Nekompletní seznam nástrojů, se kterými jsem měl potěšení se setkat, je uveden na odkazu [Assemblers] a [UsefulTools].
První nástroj, se kterým jsem se setkal když jsem začal v raných osmdesátých letech flirtovat s asemblerem, byl FDOS od IBM pro mainframy S360 [HLASM]. Což byl na svou dobu velmi sofistikovaný produkt s pokročilými funkcemi, jako sekce, klíčové operandy (keywords), s literály, s makrojazykem, jenž dovolovat manipulovat nejen se strojovými instrukcemi, ale i s vlastními makroproměnnými a jejich názvy.
V asemblerech pro architekturu Intel jsem pak postrádal mnohé z těchto funkcí. [NASM] ver.0.99 byl poměrně dobrý, první bootstrapová verze €ASM byla napsána právě v něm, ale byl jsem rozčarován jeho neschopností automaticky vybírat SHORT nebo NEAR formu instrukce JMP a dalšími návrhovými vadami, jako třeba neexpandovat %proměnné v řetězcích.
Nechápal jsem, proč konstantní symboly definované pomocí EQU musely být deklarovány před prvním užitím. Proč nemohu deklarovat makro v makru. Jak řešit situace, kdy soubor A ikluduje soubory B a C, a soubor C rovněž inkluduje soubor B a duplikuje tak jeho definice.
Nemám rád jazyky zamořené mezerami. V jazyce HLASM mezera za seznamem operandů znamenala začátek komentáře, který končil až s koncem děrného štítku. €ASM není tak přísný v tomto horror vacui, ve skutečnosti mezery mohou být vloženy mezi jazykové elementy kvůli zlepšení čitelnosti. Nicméně netisknutelné mezery nejsou téměř nikdy vyžadovány syntaxí.
€ASM nepoužívá anglické slovní modifikátory, jako
SHORT, NEAR, DWORD PTR, NOSPLIT
identifikované pouze jejich hodnotou. Místo toho preferuje paradigma Name=Value spolu s klíčovými modifikátory instrukcí, jakoDATA=QWORD,IMM=BYTE,MASK=K5,ZEROING=ON
, jež odstraňují nejednoznačnosti a nahrazují nehezké dekorátory navrhované v dokumentaci od Intelu.
Právo užívat EuroAssembler je poskytnuto každému, kdo se řídí touto licencí.
Nejsou žádná omezení ohledně účelu aplikací vyrobených tímto nástrojem.
Mohou být svobodně používány v soukromém, výukovém i komerčním prostředí.
EuroAssembler je poskytnut bezplatně tak, jak je, bez jakékoli záruky od jeho autora.
Tento software smí být redistribuován v nezměněné zipované podobě, tak jak byl stažen z EuroAssembler.eu. Za právo používat tento software nesmí být vyžadován žádný poplatek.
Můžete rozšiřovateuroasm.zipna jiné weby, repozitáře, archivy FTP, kompaktní disky a obdobná média. Snažte se prosím vždy šířit nejnovější verzi EuroAssembleru.
Zdrojový kód EuroAssembleru napsal Pavel Šrubař, AKA vitsoft, a jako takový je chráněn autorským zákonem.
Makroknihovny a vzorové projekty jsou uvolněny jako public domain a mohou být libovolně upravovány.
Modifikaci dodaných knihoven nicméně nemohu doporučit, protože vaše vylepšení by mohly být přepsány po vydání novější verze €ASM. Vytvořte raději soubory s vlastními názvy.
Smíte modifikovat zdrojový kód €ASM za výhradním účelem opravy chyb nebo obohacením o nové funkce, avšak nesmíte distribuovat takto upravený software. Smí být používán pouze na tomtéž počítači, kde byl upraven, sestaven a znovu slinkován.
EuroAssembler není open source. Nechci větvit vývoj EuroAssembleru do navzájem nekompatibilních verzí, kde každá větev poskytuje odlišné vylepšení. Raději prosím zašlete své modifikace autorovi nebo je napište do diskusního fóra, aby mohly být zařazeny do dalších vydání EuroAssembleru.
Distribuovaný soubor euroasm.zip
obsahuje adresáře a soubory jak jsou vyjmenovány na stránce
Sitemap
Čas poslední modifikace všech souborů je nastaven na nominální hodnotu vydání.
Názvy souborů jsou malými písmeny (Linuxová konvence) a ve 8.3 formátu (DOSová konvence),
takže je rozbalí jakákoli starší DOSová utilita.
K instalaci na novější verzi MS-Windows budete muset spustit konzolu jako administrátor.
Zvolte a vytvořte domovský adresář,
například C:\euroasm
, přejděte do něj a rozzipujte stažený soubor euroasm.zip
.
Přesuňte nebo zkopírujte spustitelný soubor euroasm.exe
do některé složky obsažené v proměnné environmentu PATH
,
takže bude moci být spouštěn jako euroasm
odkudkoli.
Při prvním spuštění se pokusí vytvořit globální konfigurační soubor euroasm.ini
v tomtéž adresáři.
Nyní byste jej měli upravit plain-text editorem.
Možná budete chtít upravit relativní hodnoty IncludePath= and LinkPath= v sekci[EUROASM]
absolutní cestou specifikující domácí adresář EuroAssembleru.
V sekci[PROGRAM]
možná budete chtít upravit preferovaný formát generovaných souborů, např.Format=PE, Subsystem=CON
aWidth=32
. Také můžete nahraditIconFile="euroasm.ico"
za svou preferovanou ikonu a nahrát ji do složkyobjlib.
Pro (nedoporučenou) holou minimální instalaci můžete vymazat celý domácí adresář.
Soubor euroasm.exe
nepotřebuje žádné další podpůrné soubory, úpravy environmentu nebo registrů MS-Windows.
Pokud byste chtěli číst tuto dokumentaci v jiném jazyce,
přejmenujte defaultní anglickou verzi tohoto manuálu eadoc\index.htm
na eadoc\man_eng.htm
a pak přejmenujte dostupný překlad, např. eadoc\man_cze.htm
na eadoc\index.htm
.
Pro vývojářskou instalaci rozbalte v domovském adresáři vývojářské skripty
z podarchivu generate.zip
.
Budete muset mít na svém počítači instalován webserver a PHP (verze 5.3 nebo novější).
Většina souborů EuroAssembleru je v HTML formátu, takže asi budete chtít začlenit €ASM do vašeho lokálního webserveru.
V mé instalaci Apache jsem do jeho konfiguračního souboru
httpd.confneboapache2.confpřidal<VirtualHost *:80> DocumentRoot C:/euroasm/ ServerName euroasm.localhost </VirtualHost>Dále jsem přidal řádek
127.0.0.1 euroasm.localhost
do souboru%SystemRoot%\SYSTEM32\drivers\etc\hosts. Nyní mohu napsateuroasm.localhost
do adresního řádku internetového prohlížeče a číst soubory EuroAssembleru lokálně.
Počítače komunikují s uživateli rozličnými kanály: standardní vstup a výstup, parametry příkazové řádky, proměnné environmentu, hodnota errorlevel, diskové soubory, hardwarová zařízení.
Základní forma komunikace mezi programem a lidským uživatelem má podobu proudu znaků, defaultně směrovaného na konzolu terminálu, kde byl program spuštěn. Může být přesměrován do souboru nebo do ovladače zařízení pomocí operátorů příkazové řádky >, >>, <, |.
Standardní vstup není v €ASM používán.
Standardní výstup zobrazuje chyby, varování a informativní zprávy generované EuroAssemblerem.
Standardní chybový výstup není v €ASM používán.
Parametry příkazové řádky nejsou používány.
€ASM předpokládá, že vše na příkazové řádce jsou názvy souborů, které má přeložit.
Veškeré parametry řídící asembler a linker jsou definovány
v konfiguračních souborech euroasm.ini
nebo přímo ve zdrojovém kódu.
Ve skutečnosti sice je několik parametrů EUROASM rozeznáváno na příkazovém řádku, avšak jejich preferované umístění je konfigurační soubor anebo zdrojový kód. Parametry z příkazového řádku jsou využity pouze v testech, kde potlačují některé informaticní zprávy.
Proměnné environmentu nejsou v €ASM používány.
Mohou však být začleněny do zdrojového textu v asm-time pomocí pseudoinstrukce
%SETE. A je samozřejmě také možné číst tyto proměnné v run-time
pomocí příslušného systémového volání, jako GetEnvironmentVariable()
.
€ASM nepoužívá žádná další zařízení (I/O porty, tiskárny,zvukové a grafické adaptéry apod.) během asm-time.
Informace detekované EuroAssemblerem během jeho činnosti jsou publikovány ve formě krátkých textových oznámení směrovaných na standardní výstup a do listingového souboru.
Oznámení je identifikováno kombinací písmene následovaného čtyřmi dekadickými číslicemi. Jejich kompletní text je definován ve zdrojovém souboru €ASM msg.htm.
Písmenový prefix a první číslice (0..9) určují závažnost tohoto oznámení.
Nejvyšší závažnost zjištěná během činnosti €ASM určuje hodnotu
errorlevel, se kterou euroasm.exe
končí.
Druh zprávy | Prefix | Rozsah identifikátorů | Závažnost | Vyhledávací značka |
---|---|---|---|---|
Informativní | I | I0000..I0999 | 0 | |# |
Ladicí | D | D1000..D1999 | 1 | |# |
Varování | W | W2000..W3999 | 2..3 | |## |
Nepotlačitelná varování | W | W4000..W4999 | 4 | |## |
Uživatelem definovaná chyba | U | U5000..U5999 | 5 | |### |
Chyba | E | E6000..E8999 | 6..8 | |### |
Fatální chyba | F | F9000..F9999 | 9 | |### |
EuroAssembler je upovídaný, avšak může být zcela umlčen při spuštění s parametrem
NOWARN=0000..0999
, a pokud nezjistí žádnou chybu.
Varování zpravidla nebrání zkompilovanému programu ve spuštění, jsou to spíše upozornění, že programátor něco opomenul nebo učinil překlep.
Oznámení se závažností 5..8 indikují, že některé řádky nebyly přeloženy kvůli chybě. Cílový soubor může být formálně v pořádku, ale pravděpodobně nebude fungovat, jak by měl.
Fatální chyby upozorňují na chybu interakce s operačním systémem, vyčerpání zdrojů, chybu v práci se soubory nebo interní chybu EuroAssembleru. Cílový soubor při fatální chybě nejspíš nebyl vůbec vytvořen.
Oznámení a varování v rozsahu I0000..W3999 mohou být potlačena parametrem EUROASM NOWARN=, ovšem tato pštrosí politika není dobrý nápad, vždy je lepší odstranit příčinu varování.
Hodláte-li publikovat svůj kód, měl by jít zkompilovat s hodnotou errorlevel 0.
Typické oznámení sestává z identifikátoru, za kterým následuje upravený text zprávy. Při jeho zobrazení na standardním výstupu je doprovázen pozičním indikátorem ve formě názvu zdrojového souboru následovaného číslem fyzického řádku ve složených závorkách, například
E6601 Symbol "UnknownSym" mentioned at "t1646.htm"{71} was not found. "t1646.htm"{71} ▲▲▲▲▲ ▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲ Identifikátor poziční indikátor
Obvykle vidíme pouze jeden poziční indikátor, pouze v případě zjištění chyby ve fázi expanze makra je přidán další indikátor určující číslo řádku v makroknihovně. V případě expanze makra uvnitř jiného makra budou poziční indikátory dále zřetězeny.
Oznámení zapisovaná do listingového souboru mají lehce odlišný formát. Poziční indikátor je vynechán, neboť chybové zprávy jsou vkládány hned pod zdrojový řádek způsobující chybu.:
|002B: | MOV SI,UnknownSym: ; E6601 expected. |### E6601 Symbol "UnknownSym" mentioned at "t1646.htm"{71} was not found. ▲▲▲▲ markerTextu oznámení předchází vyhledávací značka, která pomáhá nalézt chybu v listingu.
Pomocí vyhledávací funkce Find/FindNext
(Ctrl-F)
editoru nebo prohlížeče použitého ke čtení listingového souboru můžeme přeskakovat od jedné chyby ke druhé.
K prohlížení listingu můžete rovněž využít specializovaný €ASM listing viewer distribuovaný jako vzorový projekt.
Ladicí oznámení D1???
generovaná pseudoinstrukcí
%DISPLAY se vypisují i v případech jejího umístění do false větve příkazu
%IF nebo do bloku vyřazeného z kompilace pomocí
%COMMENT..%ENDCOMMENT.
Listing se generuje pouze v posledním průchodu (final pass).
Informativní oznámení se do listingu vůbec nezapisují, s výjimkou zpráv I056?
.
EuroAssembler čte dva druhy vstupních souborů: konfigurační a zdrojový.
EuroAssembler zapisuje dva druhy výstupních souborů: cílový a listing.
Konfigurační soubor s pevným názvem euroasm.ini
specifikuje
výchozí volby pro assembler. €ASM je načítá ze dvou souborů s identickým názvem a strukturou:
Globální konfigurační soubor je umístěn ve stejném adresáři
jako spustitelný soubor euroasm.exe
a zpracovává se jednou, když byl €ASM spuštěn.
Pokud předtím neexistoval, €ASM se jej pokusí vytvořit s výchozími hodnotami parametrů (factory-default).
Lokální konfigurační soubor se hledá ve stejném adresáři jako zdrojový text.
Pokud byl na příkazovém řádku definován více než jeden zdrojový soubor,
lokální konfigurační soubor se načítá znovu vždy při zahájení zpracování zdrojového souboru.
Lokální euroasm.ini
není EuroAssemblerem vytvářen, musíte zkopírovat globální soubor
a případně vymazat opakující se volby kvůli lepší výkonnosti.
Příklad povelu, který kompiluje dva zdroje:C:\PgmFiles\euroasm.exe Source1.asm D:\Temp\Source2.asm
EuroAssembler se pokusí načíst počáteční konfiguraci ze tří souborů:C:\PgmFiles\euroasm.ini
,.\euroasm.ini
,D:\Temp\euroasm.ini
.
Počáteční obsah konfigurace zabudovaný do euroasm.exe
jako factory-defaults je definován v objlib/euroasm.ini.
Soubor obsahuje dvě sekce: [EUROASM]
a [PROGRAM]
.
Ta první určuje parametry pro samotný €ASM, jako např. volbu CPU, druh informací zapisovaných do listingu, která varování se mají potlačit atd. Parametry ze sekce [EUROASM] mohou být redefinovány později ve zdrojovém textu pseudoinstrukcí EUROASM, kde také naleznete podrobnější popis parametrů.
Sekce [PROGRAM] určuje defaultní parametry vytvářeného programu, například paměťový model, formát, název výstupního souboru apod. Tyto parametry mohou být změněny později ve zdrojovém kódu pseudoinstrukcí PROGRAM.
Pořadí parametrů v konfiguračním souboru není důležité.
Na velikosti písmen v názvech parametrů nezáleží.
Parametry s logickými hodnotami (boolean) akceptují následující možnosti:
ON, YES, TRUE, ENABLE, ENABLED
jako zapnout
a OFF, NO, FALSE, DISABLE, DISABLED
jako vypnout.
Akceptují se rovněž numerické hodnoty, které budou převedeny na logické.
Rozhodnete-li se zveřejnit svůj program v EuroAssembleru, nemusíte specifikovat parametry příkazového řádku nutného ke kompilaci a linkování, neboť tyto mohou být definovány v samotném zdrojovém kódu. Typický program v €ASM začíná konfigurační pseudoinstrukcí, jako např.EUROASM AUTOALIGN=YES,CPU=PENTIUM
, takže pak není obtížné uhodnout, ve kterém asembleru byl napsán.
Jako vývojář programů v EuroAssembleru byste neměli spoléhat, že uživatelé budou mít stejný obsaheuroasn.inijako vy. Specifikujte všechna důležitá nastavení na začátku publikovaného zdrojového kódu. Lokální konfigurační soubor se hodí během vývoje, kdy zdroje ve stejném adresáři nemusejí explicitně specifikovat všechny parametry EUROASM a PROGRAM.
Povely ke konfiguraci EuroAssembleru jsou definovány v konfiguračních souborech a ve zdrojových souborech pseudoinstrukcí EUROASM). Jejich pořadí zpracování je následující:
euroasm.exespustí, výchozí volby jsou již definovány pomocí factory defaults.
Soubor se zdrojovým kódem obsahuje instrukce k překladu, obvykle se jedná o soubor s prostým textem
nebo HTML text uzpůsobený pro €ASM. Název souboru bude uveden na příkazovém řádku spouštějícím
euroasm.exe
. Může být doplněn absolutní cestou k souboru, například
euroasm /user/home/euroasm/MyProject/MySource.asm
,
nebo relativní či vynechanou cestou.
Struktura a obsah zdrojového kódu, který je €ASM schopen asemblovat a linkovat, je popsán níže v tomto dokumentu.
Hlavním účelem programování je získat ze zdrojového soubor cílový soubor. Může jím být objektový soubor (modul) nebo knihovna linkovatelná k jiným souborům, nebo to může být binární soubor ke speciálním účelům, anebo spustitelný soubor.
Formát cílového souboru je určen parametrem FORMAT= pseudoinstrukce PROGRAM. Pro podrobnější informace o výstupních formátech viz kapitolu Programové formáty.
Název výstupního souboru je určen návěštím pseudoinstrukce PROGRAM,
k němuž je připojena přípona závislá na zvoleném formátu.
Název cílového souboru není nezbytně odvozen od názvu zdrojového souboru, jako u mnoha jiných asemblerů.
Pokud je například ve zdrojovém souboru povel Hello PROGRAM FORMAT=COM
,
jeho výstupní soubor bude vytvořen v aktuálním adresáři a dostane název Hello.com
bez ohledu na název zdrojového souboru.
Defaultní jméno může být změněno parametrem OUTFILE= pseudoinstrukce PROGRAM.
Pokud je OUTFILE= uvedeno s relativní (nebo s žádnou) cestou, použije se aktuální adresář.
Listingový soubor je prostý text, kam €ASM zapisuje svou činnost:
Název listingového souboru vychází z názvu a cesty zdrojového souboru, k němuž je připojena přípona .lst.
Tuto výchozí cestu a název listingu lze změnit parametrem LISTFILE=
pseudoinstrukce EUROASM.
Vytvořme zdrojový soubor Hello.asm
s následujícím obsahem:
EUROASM DUMP=ON,DUMPWIDTH=18,DUMPALL=YES Hello PROGRAM FORMAT=COM,LISTLITERALS=ON, \ LISTMAP=OFF,LISTGLOBALS=OFF MOV DX,=B"Hello, world!$" MOV AH,9 INT 21h RET ENDPROGRAM Hello
Předložíme-li tento soubor EuroAssembleru příkazem
euroasm Hello.asm
, vygeneruje se mimo jiné listing Hello.asm.lst
.
Šířka hexadecimálního výstupu (levý sloupec listingu) je určena volbou EUROASM DUMPWIDTH=. Další volby EUROASM, které ovlivňují levý sloupec, jsou DUMPALL= a DUMP=OFF, která umí kompletně potlačit levý sloupec.
|<-Sloupec dump->|<--Zdrojový sloupec----- <--DumpWidth=18--> | | EUROASM DUMP=ON,DUMPWIDTH=18,DUMPALL=YES | |Hello PROGRAM FORMAT=COM,LISTLITERALS=ON, \ | | LISTMAP=OFF,LISTGLOBALS=OFF |[COM] ::::Section changed. |0100:BA[0801] | MOV DX,=B"Hello, world!$" |0103:B409 | MOV AH,9 |0105:CD21 | INT 21h |0107:C3 | RET |[@LT1] ====ListLiterals in section [@LT1]. |0108:48656C6C6F =B"Hello, world!$" |010D:2C20776F72 ----Dumping all. (kvůli DUMPALL=YES) |0112:6C64212400 ----Dumping all. | | ENDPROGRAM Hello ▲ oddělovač sloupcůLevý sloupec listingu vždy začíná indikátorem strojové poznámky (znakem roura |) a je ukončen oddělovačem sloupců, který určuje původ řádku.
Znak | Funkce |
---|---|
| (roura) | Konec strojové poznámky. Používá se u běžných instrukcí, které mohou být znovu užity jako €ASM zdroj. |
! (vykřičník) | Kopie zdrojového řádku s expandovanými %proměnnými (pokud LISTVAR=ENABLED ). |
+ (plus) | Zdrojové řádky generované při expanzi z %FOR,%WHILE,%REPEAT (pokud LISTREPEAT=ENABLED ). |
+ (plus) | Zdrojové řádky generované při expanzi z %MACRO (pokud LISTMACRO=ENABLED ). |
: (dvojtečka) | Řádka přidaná do listingu k zobrazení změněné [sekce]. |
. (tečka) | Řádka přidaná do listingu k zobrazení autoalignmentu (pokud AUTOALIGN=ENABLED ). |
- (minus) | Řádka přidaná do listingu k zobrazení celého dumpu (pokud DUMPALL=ENABLED ). |
= (rovnítko) | Řádka přidaná do listingu k zobrazení literálů (pokud LISTLITERALS=ENABLED ). |
(mezera) | Řádka přidaná do listingu k zobrazení obálkových řádků PROGRAM..ENDPROGRAM. |
* (hvězdička) | Řádka přidaná do listingu k zobrazení povelu INCLUDE* s vyřešením zástupných znaků *? |
S výjimkou oddělovače roura | mají řádky listingu formu strojové poznámky a jsou tudíž ignorovány, pokud bude listing znovu předložen EuroAssembleru jako zdrojový soubor.
Hexadecimální výpis strojového kódu (dump) u emitujících povelů začíná hexadecimální adresou (ofsetem v aktuální sekci), která je ukončena dvojtečkou :. V 16bitové sekci je tato adresa široká 16 bitů (čtyři hexadecimální cifry), v 32bitové a 64bitové sekci je to 32 bitů. Pak následují emitované (generované) bajty. Čísla v levém sloupci jsou vždy v hexadecimálním tvaru, bez uvádění explicitního číselného modifikátoru.
Pokud je zvolený parametr DUMPWIDTH= příliš nízký k zobrazení všech dat,
jsou buď vpravo oříznuty a nahrazeny tildou ~ (pokud DUMPALL=OFF
),
anebo jsou do listingu přidány další řádky s oddělovačem - (pokud DUMPALL=ON
).
V hexadecimálním výpisu mohou být použity ještě další dekorátory:
Dekorátor | Popis |
---|---|
~ | indikace zkrácení výpisu, používaná pokud DUMPALL=OFF |
.. | bajt rezervovaných dat (namísto hexadecimální hodnoty jsou-li data inicializována) |
[] | indikátor absolutní relokace |
() | indikátor relativní relokace |
{} | indikátor paragrafové relokace |
<N | použita komprese disp8*N |
Závorky [], () a {} obklopující hexadecimálně vypsané slovo nebo dvojslovo indikují, že adresa vyžaduje relokaci v link-time. Hodnota zobrazená v listingu se bude lišit od ofsetu zobrazovaného ve slinkovaném kódu nebo v debugeru při běhu programu.
Znak < následovaný dekadickou číslicí (N) signalizuje, že předchozí 8bitový displacement má být posunout vlevo o N bitů k získání efektivní hodnoty. (takzvaná komprese disp8*N). Číslice 1..6 specifikující kompresní faktor N není do sestaveného kódu emitována.
Závorky [ ] a { } indikují relokabilní hodnoty. | | EUROASM DUMPWIDTH=30,CPU=X64,SIMD=AVX512,EVEX=ENABLED |[CODE] ▼ ▼▼ ▼ |[CODE] SEGMENT WIDTH=16 |0000:EA[0500]{0000} | JMPF Label ; Absolute far jump encodes immediate seg:offset. |0005:CB |Label: RETF |[CODE64] |[CODE64] SEGMENT WIDTH=64 |00000000:62F36D28234D02<504 | VSHUFF32X4 YMM1,YMM2,[RBP+40h],4 |00000008:C3 ▲▲ | RET <5 je neemitovaný dekorátor disp8*N. ▲▲Displacement +02h bude posunut vlevo 5krát, takže efektivní displacement je ve skutečnosti +40h.U neemitujících instrukcí je sloupec dumpu buď prázdný, nebo obsahuje přídavné informace:
|[DATA] |[DATA] ; Přepnutí segment|section opakuje název ve sloupci dumpu. |0000: |; Prázdný nebo komentářový řádek zobrazuje ve sloupci dumpu pouze adresu v současné sekci. |0000: |Label: ; Ditto. | |;; Řádkový komentář začínající dvojitým středníkem potlačí výpis adresy ve sloupci dumpu. |[DATA]:0000 |Target EQU Label: ; Adresní symbol je zobrazen jako[segment]:offset
.
|4358 |%Counter %SET CX ; Nastavení %variable vypisuje její obsah hexadecimálně.
|TRUE | %IF "%Counter" == "CX" ; Předasemblerovský konstrukt zobrazuje vyhodnocenou logickou podmínku.
|[]:0010 | Bits EQU 16 ; Skalár (numerický symbol) je zobrazen s prázdným segmentem.
|FALSE | %ELSE ; Logické podmínky se týkají pseudoinstrukcí %IF, %ELSE, %WHILE, %UNTIL.
| | Bits EQU 32 ; Dump je prázdný v neemitovaných větvích %IF.
| | %ENDIF
Listingový soubor ve své defaultní konfiguraci je víceméně přesnou kopií zdrojového souboru,
až na přidaný sloupec dumpu.
V některých případech může být potřeba zkontrolovat, zda určité konstrukty fungují podle očekávání.
To se řídí následujícími parametry pseudoinstrukce EUROASM:
LISTINCLUDE=ON zobrazuje obsah vloženého souboru, který by byl jinak skryt.
LISTVAR=ON vytváří kopii každého povelu, ve které nahradí preprocesorovou %proměnnou její expandovanou hodnotou.
LISTMACRO=ON vkládá do listingu povely expandované v makroinstrukci.
LISTREPEAT=ON vkládá všechny iterace opakujících se konstruktů
%FOR..%ENDFOR, %WHILE..%ENDWHILE, %REPEAT..%ENDREPEAT
.
Opakující se expanze jsou v listingu zobrazeny jako vykomentované pomocí separátoru +.
Ve výchozím stavu, kdy je LISTREPEAT=DISABLED
, je zobrazována pouze první expanze.
Užitečnou vlastností listingu v €ASM je snaha udržovat jej znovupoužitelný jako zdrojový kód.
Chtěl jsem udržet tuto funkčnost bez ohledu na stav parametrů LIST*. Ve výchozím stavu, kdy je
LISTINCLUDE=OFF
, je povel INCLUDE normálně zobrazen a obsah vloženého souboru je skryt. PovelemEUROASM LISTINCLUDE=ON
se to obrací: originální pseudoinstrukce INCLUDE je odkomentována separátorem dumpu *, avšak řádky vložené tímto souborem jsou vloženy do listingu a stávají se tak platnými zdrojovými příkazy. Viz test t2220.Jsou li volby
LISTVAR, LISTMACRO, LISTREPEAT
zapnuty (enabled), originální řádek s parametremEUROASM LISTVAR=ENABLED
je zachován a expandované řádky budou vloženy pod něj a odkomentovány separátorem ! nebo +. Viz test t2230.
Volba EUROASM LIST=DISABLE zcela vypne generování listingu až do opětovného povolení, případně do konce souboru. Takový listing samozřejmě nebude znovupoužitelný jako zdrojový soubor.
Diskové soubory mohou být specifikovány včetně absolutní cesty,
tj. cesty jež začíná v kořenu souborového systému, např.
C:\ProgFiles\euroasm.exe D:\Project\source.asm
.
Soubory mohou být rovněž určeny relativní cestou,
např. euroasm ..\prowin32\skeleton.asm
.
Relativní cesta se vždy vztahuje k současnému pracovnímu adresáři.
Soubory mohou být specifikovány bez cesty, tj, pokud jejich název nebosahuje dvojtečku ani lomítko :, \, /. Umístění takových souborů je pak rekapitulováno níže uvedenou tabulkou:
Soubor | Význam | Umístění | Viz také |
---|---|---|---|
Executable | euroasm.exe | Adresář s euroasm.exe | OS PATH |
Input | Globalní euroasm.ini | Adresář s euroasm.exe | OS PATH |
Output | Globalní euroasm.ini | Adresář s euroasm.exe | OS PATH |
Input | Lokální euroasm.ini | Adresář se zdrojem | |
Input | Zdrojový soubor | Aktuální adresář | |
Input | Vložený zdrojový soubor | Adresář vkládaných souborů | EUROASM INCLUDEPATH= |
Output | Cílový soubor | Aktuální adresář | PROGRAM OUTFILE= |
Output | Listingový soubor | Adresář se zdrojem | EUROASM LISTFILE= |
Input | Linkovaný modul | Adresář linkovaných modulů | EUROASM LINKPATH= |
Input | Linkovaný stub | Adresář linkovaných modulů | PROGRAM STUBFILE= |
Input | Linkovaný soubor ikony | Adresář linkovaných modulů | PROGRAM ICONFILE= |
Import | Dynamicky importovaná funkce | Závisí na OS | IMPORT LIB= |
Aktuálním adresářem se rozumí složka přiřazená shellu v okamžiku, kdy by euroasm.exe
spuštěn.
€ASM ho nikdy nemění.
Adresář s euroasm.exe
je složka, v níž se nachází euroasm.exe
,
obvykle jeden z adresářů uvedených v proměnné environmentu PATH.
Adresář se zdrojem je složka, v níž je umístěn soubor s překládaným zdrojovým kódem.
Adresář vkládaných souborů je jeden z adresářů uvedených v parametru
EUROASM INCLUDEPATH=
.
Adresář linkovaných modulů je jeden z adresářů uvedených v parametru
EUROASM LINKPATH=
.
Tato kapitola popisuje formát zdrojového souboru, kterému €ASM rozumí a je schopen jej překládat.
Pokud byl zdrojový text napsán editorem používajícím WIDE (16bitové) kódování znaků (UTF-16), musí být nejprve uložen jako prostý text v UTF-8 nebo v 8bitovém kódování ANSI nebo OEM, než bude předložen EuroAssembleru.
Program napsaný v €ASM může chtít zobrazovat texty v jiných jazycích než v angličtině.
Takový text obsahuje znaky s hodnotou codepointu nad 127
(codepoint je pořadové číslo znaku v tabulce [Unicode]).
Mnoho evropských jazyků je spokojeno s omezenou sadou 256 znaků.
Relace mezi jejich kódy a odpovídajícím fontem se nazývá kódová stránka (Codepage).
MS-Windows používá různé kódové strany v konzolových aplikacích (OEM) a v grafických aplikacích (ANSI) a občas mezi nimi provádí automatické konverze. Avšak €ASM kódovou stranu zdrojového textu nikdy nemění.
Programátor používající v aplikaci několik lidských jazyků by měl v MS-Windows používat 16bitové znaky WIDE namísto 8bitových ANSI znaků v textových řetězcích. Jako demo-ukázku viz cpmix32.
Široké kódování (UTF-16) je definováno pseudoinstrukcí DU
(Define data in Unichars)
namísto DB
(Define data in Bytes).
Příslušné volání WinAPI musí být ve variantě WIDE pro správnou vizuální reprezentaci za běhu,
např. TextOutW()
namísto ANSI varianty TextOutA()
.
I tak ale kódování znaků v definici DU
je stále 8bitové.
Měli byste sdělit EuroAssembleru, která kódová strana byla použita při psaní 8bitového zdrojového textu.
Tuto informaci poskytuje parametr EUROASM CODEPAGE=
.
Kódová strana se může v rámci zdrojového textu dynamicky měnit a dovoluje tak střídat
více jazyků v rámci téhož programu.
Texty mířící na konzolu
(používající ANSI verzi WinAPI funkcí, jako např. WriteConsoleA()
nebo makroinstrukci
StdOutput) by měly být psány v kódové stránce OEM.
Asi budete používat dosový editor plain-textu, jako např. EDIT.COM
ke psaní konzolových programů. Tyto editory používají konzolové fonty v OEM kódových stránkách,
takže texty jsou zobrazeny korektně jak v editoru během psaní programu, tak i za jeho běhu.
Texty s volbou PROGRAM SUBSYSTEM=GUI zamýšlené pro grafický mód (používající WinAPI funkce jako TextOutA()
)
by měly být psány v kódové straně ANSI, např. pomocí Notepad.exe
.
Výchozí volbou je EUROASM CODEPAGE=UTF-8
, kdy znaky mají proměnnou šířku od jednoho do čtyř bajtů.
Díky inteligentnímu kódování [UTF8] jsou všechny non-ASCII UTF-8 znaky
kódovány jako bajty s hodnotou 128..255, což €ASM považuje za písmena,
takže mohou být beze změny použity v indentifikátorech.
Na rozdíl od 8bitových kódování ANSI nebo OEM, které omezují repertoár na 256 znaků, CODEPAGE=UTF-8 dovoluje mísení libovolných znaků definovaných v tabulce [Unicode] včetně neevropských abeced. MS-Windows API nepodporuje přímo řetězce v UTF-8, tyto vyžadují překódování za běhu na UTF-16, které jsou používány variantou WIDE u WinAPI funkcí jako TextOutW(). Toto překódování zajistí WinAPI MultiByteToWideChar() anebo makro DecodeUTF8. Samozřejmě ke korektnímu zobrazování exotických znaků (asijské abecedy, emoji ap.) je potřeba mít instalovány patřičné fonty.
Příklad freeware textového editoru podporujícího kódování UTF-8 je [PSPad].
Některé editory vkládají na začátek souboru sekvenci Byte Order Mark, tj.0xEF, 0xBB, 0xBF
. EuroAssembler považuje tyto tři znaky za tříznakové nepoužité návěstí na začátku zdrojového souboru, což obvykle ničemu nevadí.
U všech identifikátorů vytvořených vámi (programátorem) na velikosti písmen záleží: návěští, konstanty, uživatelské %proměnné, struktury, sekce, názvy maker. Naopak u vestavěných názvů na velikosti písmen nezáleží. Necitlivost k velikosti se týká všech výčtových hodnot: názvů registrů, strojových instrukcí a prefixů, vestavěných datových typů, číselných modifikátorů, jmen a parametrů pseudoinstrukcí, souborových atributů, systémových %^proměnných.
Názvy nezávislé na velikosti znaků jsou v tomto manuálu uváděny VELKÝMI PÍSMENY, ale mohly být zrovna tak psány malými nebo smíšenými znaky.
Každý bajt (8 bitů) ve zdrojovém textu €ASM je považován za znak. Mnoho znaků má v syntaxi asembleru speciální funkci, až na případy, kdy jsou umístěny uvnitř dvojitých nebo jednoduchých uvozovek. Znak je považován za neuvozený (unquoted) pokud se mezi ním a mezi počátkem řádku vyskytlo nula nebo sudý počet uvozovek.
ASCII | znak | jméno | funkce v €ASM |
---|---|---|---|
0..9 | řídicí | netisknutelný znak neboli bílá mezera | |
10 | konec řádku | konec řádku | |
11..31 | řídicí | netisknutelný znak neboli bílá mezera | |
32 | mezera | netisknutelný znak neboli bílá mezera | |
33 | ! | vykřičník | logický operátor |
34 | " | dvojitá uvozovka | ohraničení řetězce |
35 | # | modifikátor | modifikátor |
36 | $ | dolar | písmeno |
37 | % | procento | prefix preprocesingu |
38 | & | ampersand | logický operátor |
39 | ' | apostrof | ohraničení řetězce |
40 | ( | levá závorka | prioritní závorka |
41 | ) | pravá závorka | prioritní závorka |
42 | * | hvězdička | aritmetický a speciální operátor |
43 | + | plus | aritmetický operátor |
44 | , | čárka | oddělovač operandů |
45 | - | minus | aritmetický operátor |
46 | . | tečka | oddělovač členů |
47 | / | lomítko | aritmetický operátor |
48..57 | 0..9 | číslice | číslice |
58 | : | dvojtečka | oddělovač |
59 | ; | středník | indikátor komentáře |
60 | < | méně než | logický operátor, oddělovač komentáře |
61 | = | rovnítko | logický operátor, klíčový oddělovač, indikátor literálu |
62 | > | více než | logický operátor |
63 | ? | otazník | písmeno |
64 | @ | komerční at | písmeno |
65..90 | A..Z | velká písmena | písmeno |
91 | [ | levá hranatá závorka | závorka paměťového obsahu, operátor substringu |
92 | \ | zpětné lomítko | aritmetický operátor, pokračování řádku |
93 | ] | pravá hranatá závorka | závorka paměťového obsahu, operátor substringu |
94 | ^ | caret | logický operátor |
95 | _ | podtržítko | písmeno, oddělovač číslic |
96 | ` | grave | písmeno |
97..122 | a..z | malá písmena | písmeno |
123 | { | levá složená závorka | operátor sublistu |
124 | | | roura | logický operátor, oddělovač strojové poznámky |
125 | } | pravá složená závorka | operátor sublistu |
126 | ~ | tilda | logický operátor, indikace zkratky |
127 | delete | netisknutelný znak neboli bílá mezera | |
128..255 | non ASCII znaky | písmeno | |
ASCII | znak | jméno | funkce v €ASM |
Zdrojový soubor považujeme za text sestávající z řádků zpracovávaných zleva doprava, shora dolů.
Zdrojový soubor sestává z fyzických řádků. Fyzický řádek je řada znaků zakončená EOL (ASCII 10). Znak EOL je rovněž součástí fyzického řádku. Může být vynechán pouze pokud se jedná o poslední fyzický řádek souboru.
Instrukce (statement) je příkaz pro €ASM provést určitou akci, obvykle emitovat nějaký kód do cílového souboru nebo změnit svůj vnitřní stav. Typická instrukce zabírá právě jeden fyzický řádek, avšak delší instrukce mohou přesahovat do dalších řádků, pokud je použito pokračování řádku.
Instrukce sestává z několika polí, ty jsou rozlišovány podle pozice v řádku, podle separátoru nebo podle jejich obsahu. Všechna pole instrukce jsou nepovinná, kterékoli může být vynecháno, jen pole operandů nelze použít, bylo-li vynecháno pole operace.
Pořadí | Název pole | Ukončení |
---|---|---|
1. | Strojová poznámka | | nebo EOL |
2. | Návěstí | : nebo bílá mezera |
3. | Prefix | : nebo bílá mezera |
4. | Operace | bílá mezera |
5. | Operand | , |
6. | Řádkový komentář | EOL |
Příklad instrukce:
| strojová poznámka |Návěstí|Prefix|Operace | Operand | Řádkový komentář |00001234:F08705[78560000] |Mutex: LOCK: XCHG EAX,[TheLock] ; Guard the thread.Strojová poznámka začíná znakem vertikální čára (roura) |
, pokud je to první
nebílý znak na řádku. Poznámka je ukončena druhým výskytem téhož znaku anebo koncem fyzického řádku.
Obsahem strojové poznámky je obvykle hexadeciální adresa následovaná strojovým kódem generovaným dotyčnou instrukcí. Jak její název napovídá, strojovou poznámku generuje počítač do listingového souboru; programátor ji nikdy nepotřebuje generovat ručně
Strojové poznámky jsou ve zdrojovém kódu ignorovány, takže platný listing může být beze změny znovupoužit jako zdrojový soubor.
Pole pro návěstí může hostit kterýkoli z těchto prvků:
My1stStructure
, My1stLabel:
, Outer
[.data]
%Count
V prvním případě může jméno symbolu nebo struktury začínat tečkou .
,
což je činí lokálním. Symbol v poli návěstí může být zakončen jednou nebo více dvojtečkami
:
bezprostředně za identifikátorem.
Mezera mezi návěstím a následným polem může být v tom případě vynechána.
Strojový prefix je povel pro CPU změnit svůj vnitřní stav za běhu programu (v run-time). Podobá se strojové instrukci a ovlivňuje následující instrukci za běhu. Každý prefix se překládá na jednobytový operační kód.
Jméno | Skupina | Operační kód |
---|---|---|
LOCK | 1 | 0xF0 |
REP | 1 | 0xF3 |
REPE | 1 | 0xF3 |
REPZ | 1 | 0xF3 |
REPNE | 1 | 0xF2 |
REPNZ | 1 | 0xF2 |
XACQUIRE | 1 | 0xF2 |
XRELEASE | 1 | 0xF3 |
SEGCS | 2 | 0x2E |
SEGSS | 2 | 0x36 |
SEGDS | 2 | 0x3E |
SEGES | 2 | 0x26 |
SEGFS | 2 | 0x64 |
SEGGS | 2 | 0x65 |
SELDOM | 2 | 0x2E |
OFTEN | 2 | 0x3E |
OTOGGLE | 3 | 0x66 |
ATOGGLE | 4 | 0x67 |
Poslední čtyři názvy jsou implementovány pouze v EuroAssembleru.
SELDOM
a OFTEN
lze použít před podmíněnými skoky jako nápovědu pro novější CPU, jak předvídat výsledek.
OTOGGLE
a ATOGGLE
přepínají mezi 16bitovou a 32bitovou šířkou operandů a adresní částí strojového kódu.
Tyto prefixy jsou normálně generovány asemblerem interně, bez explicitní žádosti.
V jedné instrukci mohou být definovány až čtyři prefixy, avšak z každé skupiny pouze jeden.
Názvy prefixů jsou rezervovány, nelze je použít jako návěstí. Název může být zakončen dvojtečkou :, podobně jako symbol.
64bitová architektura AMD a Intel zavedla speciální prefixy REX
,
XOP
, VEX
, MVEX
, EVEX
.
€ASM považuje tyto prefixy za součást zakódování operace a neposkytuje proto mnemoniku pro jejich přímou deklaraci.
[AMDSSE5] zavedl ještě jeden prefix
DREX
, avšak jelikož se instrukce s tímto prefixem nedostaly do výroby, €ASM je nepodporuje.
Prefixy pro změnu segmentu SEG*S mohou být alternativně předpisovány jako komponenty paměťových proměnných v rámci registrového výrazu. V tom případě je prefix emitován pouze pokud není redundantní (pokud specifikuje nedefaultní segment). Explicitně specifikované prefixy jsou emitovány vždy, v pořadí, v jakém se objevily v instrukci.
EuroAssembler vydává varování, pokud byl prefix použit v rozporu s doporučením pro CPU. Lze to přepsat uvedením prefixu na samostatném řádku.
|0000:F091 |LOCK: XCHG AX,CX ; Prefix Lock by neměl být použit s registrovými operandy. |## W2356 Prefix LOCK: is not expected in this instruction. |0002:F0 |LOCK: ; Varování W2356 lze zamezit uvedením prefixu na samostatném řádku, |0003:91 | XCHG AX,CX ; např. kvůli zjišťování chování procesoru v takové situaci. |0004: | |0004:6691 | XCHG EAX,ECX ; Prefix 0x66 je emitován interně (v 16bitovém segmentu). |0006:6691 |OTOGGLE: XCHG EAX,ECX ; Jeho explicitní specifikace se nijak neprojeví, |0008:6691 |OTOGGLE: XCHG AX,CX ; avšak zde mění šířku registrů z 16 na 32 bitů.Pole operace je nejdůležitější část instrukce, předepisuje EuroAssembleru, co má dělat: něco deklarovat, změnit svůj vnitřní stav, emitovat něco do cílového souboru. Často udává jméno celé instrukci: mluvíme např. o operaci EXTERN namísto o instrukci s pseudoinstrukcí EXTERN v poli operace.
€ASM rozlišuje tři rody operací:
Instrukce mohou mít pole operace prázdné:
[CODE] ; Přesměruj další emitování do sekce [CODE]. ; Prázdnou instrukci lze použít pro optické oddělení nebo pro poznámku. Label: ; Definice návěstí, které však neemituje žádný kód ani data. LOCK: ; Definice strojového prefixu pro následnou instrukci.
Většina instrukcí žádá €ASM o generování strojového kódu nebo dat do cílového souboru, říká se jim emitující instrukce:
Operandy specifikují data, s nimiž instrukce pracuje. Počet operandů v instrukci není omezen a závisí na poli operace. Operandem může být jméno registru, číslo, matematický výraz, identifikátor, řetězec nebo jejich kombinace.
Pole operace je odděleno od prvního operandu nejméně jednou bílou mezerou. Operandy jsou navzájem odděleny čárkou ,, která není v uvozovkách. €ASM rozlišuje dva druhy operandů: pořadové a klíčové.
Pořadové operandy (neboli stručně ordinály) jsou definovány jejich pořadím v instrukci.
První operand má pořadové číslo jedna; v makrech je identifikován jako %1
.
Například v instrukci MOV AL,BL
má registr AL pořadové číslo 1 a BL číslo 2.
Strojová instrukce MOV kopíruje obsah druhého operandu do prvního.
Čárka mezi operandy zvyšuje pořadové číslo následujícího operandu i v případě, že je prázdný
(obsahuje nanejvýš netisknutelné znaky).
Operand strojové instrukce může představovat registr, bezprostřední číselnou hodnotu,
adresu nebo paměťovou proměnnou v hranatých závorkách, např. MOV AL,[ES:SI+16]
.
Jiné asemblery akceptují odlišnou syntaxi v paměťové proměnné, která však není podporována v €ASM, např.MOV AL,
neboES:[SI+16]MOV AL,
.[ES:16]+SI
€ASM vyžaduje, aby byl celý paměťový operand umístěn do hranatých závorek [].
Vedle pořadových operandů zavádí €ASM ještě jeden typ parametrů: klíčové operandy (neboli stručně klíče). Na klíče odkazujeme jménem (klíčovým slovem) namísto jejich pořadí v seznamu operandů. Klíčový operand má formát jméno=hodnota, kde jméno je identifikátor bezprostředně následovaný rovnítkem.
Klíčové operandy mají řadu výhod: jsou samopopisné (při rozumně zvoleném klíčovém slovu), nezáleží na jejich pořadí v seznamu operandů (žádné počítání čárek), mohou mít přiřazenu výchozí hodnotu a mohou být zcela vynechány, pokud mají výchozí hodnotu.
Klíčové operandy se nejčastěji pouívají u makroinstrukcí, avšak €ASM je umí využít u některých pseudoinstrukcí a dokonce i u strojových instrukcí. Například v instrukci
INC [EDI],DATA=DWORD
klíčový operandDATA=
informuje, kterou formu z možných strojových instrukcí INC chceme použít (inkrementovat BYTE, WORD nebo DWORD).
Pozor na vložení mezery mezi klíč a rovnítko:
|0000: |; Definujme dvě paměťové proměnné (s nedoporučenými názvy). |0000:3412 |DATA: DW 1234h |0002:7856 |WORD: DW 5678h |0004: | |0004:50 | PUSH AX, DATA=WORD |0005: |; Přeloženo jakoPUSH AX
.
|0005: |; Operand DATA=WORD
je rozpoznán jako redundantní avšak platný modifikátor.
|0005: |
|0005:506A00 | PUSH AX, DATA = WORD
|0008: |; Operand DATA = WORD
není rozpoznán jako klíčový
|0008: |; kvůli mezeře následující klíč DATA.
|0008: |; €ASM vidí druhý operand jako numerické srovnání symbolů DATA a WORD,
|0008: |; které náhodou v program existují (jinak by se vydalo hlášení E6601).
|0008: |; Jejich ofsety (0000h a 0002h) se liší, výsledkem je logické FALSE
|0008: |; reprezentované hodnotou 0. Instrukce je vyhodnocena jako PUSH AX, 0
|0008: |; což je legální, neboť €ASM akceptuje sjednocování vícenásobných operandů
|0008: |; do jedné instrukce v případě PUSH, POP, INC, DEC.
|0008: |; Řádek je přeložen do dvou strojových instrukcí: PUSH AX
a PUSH 0
.
Na pořadí klíčových operandů nezáleží. Je dobrým zvykem nejprve uvádět pořadové operandy a teprve pak všechny klíčové, avšak klíče mohou být rovněž rozptýleny mezi ordinály.
Label1: Operation1 Ordinal1,Ordinal2,,Ordinal4,, Label2: Operation2 Ordinal1,Keyword1=Value1,Ordinal2,,Ordinal4
Operation1 v předchozím příkladu má tři operandy s pořadovými čísly 1,2 a 4. Třetí oprand je prázdný a poslední dvě čárky na konci se ignorují, jelikož nenásleduje žádný neprázdný operand.
U Operation2 jsou použity smísené operandy. Všimněte si, že Ordinal2 má pořadové číslo 2, přestože je to třetí operand na seznamu. Klíčové operandy se nepočítají do pořadových čísel ordinálů, avšak prázdné operandy ano.
Řádková poznámka začíná neuvozeným středníkem ; a končí až s koncem fyzického řádku. Řádkové poznámky asembler ignoruje, jsou určeny pro lidského čtenáře strojového kódu.
Instrukce pokračuje na dalším fyzickém řádku, pokud se na pozici začátku některého pole vyskytne pokračovací znak, kterým je neuvozené zpětné lomítko \.
aLabel: \ ; Tento středník je redundantní MOV EAX, \ První operand instrukce MOV je cíl EBX ; a druhý operand je zdroj.
Vše za pokračovacím znakem se považuje za komentář a nepřekládá, takže středník pak může být vynechán. V mnohořádkových instrukcích můžeme komentovat kterýkoli fyzický řádek.
Celé pole se musí vejít na jeden fyzický řádek.
Zpětné lomítko \ je v €ASM rovněž použito jako binární operátor modulo, avšak binární operátor se nemůže vyskytnout na začátku operace a být tudíž zaměněn za pokračovací znak.
; modulo modulo pokračovací znak ; | | | |0000:01000200 | DW 5 \ 4, 6 \ 4, \ |0004:03000000 | 7 \ 4, 8 \ 4
Instrukce jsou zpracovávány jedna po druhé shora dolů; některé mohou ovlivňovat své následníky, nicméně většina z nich jsou samostatné. Z toho pohledu jsou tři druhy instrukcí
Bloková instrukce se musí vyskytovat v páru se svou zakončující instrukcí. Interní stav €ASM se změnil pouze mezi blokovou a zakončující instrukcí. Tomuto rozsahu se říká block.
Blok začíná na poli operace instrukce začátku-bloku a končí na poli operace instrukce konce-bloku.
Některé blokové operace mohou být předčasně zrušeny operací přerušení-bloku (EXIT), například po zjištění chyby během expanze makra.
Návěstí | Operace | ||||
---|---|---|---|---|---|
Závaznost | Co představuje | Co deklaruje | Počátek-bloku | Přerušení-bloku | Konec-bloku |
povinné | název programu | program | PROGRAM | nepoužívá se | ENDPROGRAM |
povinné | název procedury | symbol | PROC | nepoužívá se | ENDPROC |
povinné | název procedury | symbol | PROC1 | nepoužívá se | ENDPROC1 |
povinné | název struktury | structuru | STRUC | nepoužívá se | ENDSTRUC |
volitelné | identifikátor bloku | nic | HEAD | nepoužívá se | ENDHEAD |
volitelné | identifikátor bloku | nic | %COMMENT | nepoužívá se | %ENDCOMMENT |
volitelné | identifikátor bloku | nic | %IF | %ELSE | %ENDIF |
volitelné | identifikátor bloku | nic | %WHILE | %EXITWHILE | %ENDWHILE |
volitelné | přehozené identifikátory | nic | %REPEAT | %EXITREPEAT | %ENDREPEAT |
povinné | formální %proměnná | %variable | %FOR | %EXITFOR | %ENDFOR |
povinné | název makra | makro | %MACRO | %EXITMACRO | %ENDMACRO |
Některé blokové instrukce mají více jmen (aliasů):
místo ENDPROC
lze použít ENDP
,
místo ENDPROC1
lze použít ENDP1
,
místo %ENDREPEAT
lze použít %UNTIL
.
Pole návěstí blokové instrukce definuje název programu, procedury, struktury nebo makra.
V případě bloku %FOR..%ENDFOR návěstí deklaruje formální %proměnnou, která mění svou hodnotu v každém cyklu.
V ostatních preprocesorových smyčkách (%REPEAT, %WHILE) je pole návěstí nepovinné. Může obsahovat identifikátor,
který opticky spojuje počáteční a koncovou blokovou instrukci (kvůli kontrole na vnořování),
avšak jinak nemá žádný význam - nedeklaruje symbol.
Identifikátor bloku může být použit jako první (a jediný) operand odpovídající instrukce pro přerušení-bloku a konce-bloku.
Asemblery nejsou jednotné v syntaxi bloků. MASM používá tentýž blokový identifikátor v poli návěstí u obou instrukcí počátek-procedury i konec-procedury:
MyProcedure PROC ; MASM syntaxe ; kód procedury MyProcedure ENDPTo je vhodné pro rychlé vyhledání procedury, kdy nás její název uhodí do oka při projíždění levého sloupce. Na druhé straně se ale totéž návěstí objevuje dvakrát, což je v rozporu s nutností unikátnosti názvů symbolů.
Možná to byl důvod, proč Borland ve svém TASM IDEAL módu zvolil odlišnou syntaxi:
PROC MyProcedure ; TASM syntaxe ; kód procedury ENDP MyProcedureToto řeší problém dvojího výskytu téhož návěstí, avšak název MyProcedure se nikdy neobjevuje v poli návěstí, třebaže se jedná o řádný symbol.
€ASM přináší kompromisní řešení: jméno bloku je definováno v poli návěstí instrukce začátku-bloku a zároveň se může objevit v poli operandů instrukce konec-bloku.
MyProcedure PROC ; €ASM syntaxe ; kód procedury ENDP MyProcedureOperand instrukce konec-bloku může být vynechán, avšak pokud je použit, musí být identický s návěstím instrukce začátek-bloku. To umožňuje kontrolu vnoření, neboť €ASM ohlásí chybu, pokud identifikátory nesouhlasí.
Bloky se mohou vnořovat, avšak pouze korektně.
Blok %MACRO..%ENDMACRO v následujícím příkladu obsahuje korektně vnořený block %IF..%ENDIF:
WriteCMOS %MACRO Address,Value %IF %1 <= 30h %ERROR "Checksum protected area!" %EXITMACRO WriteCMOS %ENDIF MOV AL,%1 OUT 70h,AL MOV AL,%2 OUT 71h,AL %ENDMACRO WriteCMOS
Chybné vnoření bloků bude tolerováno pouze v procedurách deklarovaných s parametrem NESTINGCHECK=OFF.
Identifikátor v poli operandů instrukcí konec-bloku a přerušení-bloku slouží obvykle
pouze ke kontrole správného vnoření. Jsou-li však dva bloky téhož typu vnořeny do sebe,
identifikátor přerušení-bloku může být použit k definování, který blok se má přerušit.
Jako příklad viz test t2642,
kde jeden Inner %FOR
blok je vnořen do vnějšího Outer %FOR
bloku,
a operand instrukce %EXITFOR určuje, který blok se má přerušit (vnější či vnitřní).
Přepínací instrukce mění interní stav €ASM pro všechny následné instrukce až do jeho změny následující přepínací instrukcí, anebo až do konce zdrojového kodu.
V €ASM jsou pouze dvě přepínací instrukce: EUROASM,
a SEGMENT.
Ten druhý má dvě podoby:
[jméno] SEGMENT
– definice nového segmentu), a
[jméno]
– definice nové sekce v aktuálním segmentu (pokud dosud nebyla definována)
a přepnutí výstupu do této sekce.
Příklady přepínacích instrukcí:
EUROASM AUTOSEGMENT=OFF, CPU=486 ; Změň stav €ASM pro všechny následující instrukce. [Subprocedures] SEGMENT PURPOSE=CODE, ALIGN=BYTE ; Deklaruj nový segment. [.data] ; Přesměruj výstup následujících instrukcí do dříve definovaného segmentu [.data]. [StringData] ; Definuj novou sekci současného segmentu (v segmentu [.data]).
Všechny ostatní instrukce nejsou logicky vázány s ostatními ve svislé struktuře programu, a jsou tedy samostatné.
Velikost EuroAssemblerových elementů není v principu omezena.
To se týká délky řetězců, fyzických řádků, identifikátrů, zápisu čísel,
matematických výrazů, hloubky vnoření, počtu operandů apod.
Interně jsou jejich velikosti udržovány jako 32bitové číslo, takže teoretický limit je
2 GB = 2_147_483_647 bajtů (znaků)
.
V praxi jsme ovšem oemzeni množstvím dostupné paměti a rozměry zásobníku, takže €ASM může zhavarovat s fatální chybou F9110 Cannot allocate virtual memory. nebo F9210 Memory reserved for machine stack is too small for this source file.
Komentáře neboli poznámky jsou části zdrojového kódu, které nejsou zpracovávány asemblerem; jejich účelem je vysvětlování kódu pro lidského čtenáře. V €ASM rozlišujeme čtyři typy komentářů:
Řádková poznámka začíná neuvozeným středníkem ;; vše následující až do konce řádku asembler ignoruje. Řádkové poznámky jsou doslovně kopírovány do listingového souboru.
Label: CALL SomeProc ; Toto je řádková poznámka.
Strojové poznámky jsou zapisovány EuroAssemblerem do listingového souboru a obsahují generovaný strojový kód v hexadecimální notaci.
Strojová poznámka začíná vertikální čarou |, která je prvním tisknutelným znakem na řádku a končí jeho druhým výskytem na tomtéř řádku, anebo koncem fyzického řádku (podle toho, co přijde dříve). Je-li ukončovací znak | vynechán, celý řádek je považován za komentář. To je využito ke vkládání chybových oznámení do listingu, hned pod chybnou instrukci.
|0030:E81234 |Label1: CALL SomeProc ; Toto je řádková poznámka. |0033: |Label2: COLL OtherProc ; Zde došlo k překlepu. |### E6860 Unrecognized operation "COLL", ignored.
Strojové poznámky jsou EuroAssemblerem ignorovány a nekopírují se do listingu. Namísto toho se generují znova, když byl listing předložen €ASM k nové kompilaci jako zdrojový kód.
U strojových poznámek se nepředpokládá, že by byly programátorem vkládány do programu.
Začíná-li fyzický řádek znakem méně než <, je celý považován za značkovací komentář a ignorován. To umožňuje směšovat zdrojový kód s tagy hypertextového značkovacího jazyka. Značkovací komentáře se nekopírují do listingového souboru.
Díky implementaci značkovacích komentářů může být zdrojový kód v €ASM ukládán nejen jako prostý text, ale též ve formě HTML nebo XML hypertextu.
<h2>Popis MéProcedury</h2> <img src="JejiVyobrazeni.png"/> MéProcedury PROC ; Viz obrázek nahoře pro její popis.
Všechny zdrojové kódy dodávané s €ASM jsou kompletně uloženy ve formátu HTML, což umožnilo dokumentovat kód hypertextovými odkazy, tabulkami, obrázky a lepší vizuální reprezentací, než by poskytly prosté řádkové komentáře.
Chcete-li udržovat své kódy v HTML, ubezpečte se, že běžné povely pro assembler nezačínají znakem < a přeskupte zdrojový text tak, aby každý značkovací komentář začínal některým tagem HTML. Taky lze začínat komentářové řádky prázdným HTML tagem <span/> nebo <!---->.
Blokový komentář lze využít k dočasnému zneplatnění větší části zdrojového kódu nebo k začlenění dokumentace.
Blokový komentář začíná pseudoinstrukcí %COMMENT
a končí odpovídající pseudoinstrukcí %ENDCOMMENT.
Může zahrnovat mnoho programových řádků, které pak nemusejí začínat středníkem.
Blokové komentáře jsou kopírovány do listingového souboru.
€ASM sice nepřekládá text uvnitř takto vykomentovaného bloku, avšak musí jej i tak číst, aby našel odpovídající instrukci %ENDCOMMENT, takže odkomentovaný text musí být platný zdrojový kód.
Text v bloku %COMMENT..%ENDCOMMENT musí být řádně vnořený, i když se ignoruje.
Pseudoinstrukci%COMMENT
bychom mohli nahradit pomocí%IF 0
, avšak je intuitivnější.
CALL SomeProc ; Toto je řádková poznámka.
%COMMENT ; Toto je blokový komentář.
COLL OtherProc ; Tady došlo k překlepu.
%COMMENT ; Toto je vložený blokový komentář.
%ENDCOMMENT ; Konec vloženého komentáře.
; Tato instrukce je rovněž ignorována.
%ENDCOMMENT
; Emitování pokračuje zde.
Identifikátor je text, který dává jméno prvkům programu: symbolu, registru, instrukci, struktuře apod.
Délka identifikátoru není omezena a všechny jeho znaky jsou platné.
Číselná notace je způsob, kterým zapisujeme numerické hodnoty. Interně jsou celočíselné hodnoty uchovávány v €ASM jako 64bitová čísla se znaménkem.
Číselný modifikátor je jeden ze znaků B D E G H K M P Q T přpojených za sekvenci číslic, nebo 0N 0O 0X 0Y (nula následovaná písmenem) na začátku číselného zápisu. Všechny modifikátory jsou nezávislé na velikosti písmen. Modifikátor musí být použit u všech notací s výjimkou dekadického formátu, který je výchozí.
Čísla s pohyblivou desetinnou tečkou (FP) mohou používat tečku . k oddělení celočíselné a desetinné části.
Dalším číselným modifikátorem je podtržítko _, které je asemblerem ignorováno a slouží jen jako oddělovač číslic pro lepší čitelnost delších čísel. V žádném zápisu čísel nejsou povoleny mezery.
Dekadické číslo je kombinací dekadických číslic 0..9 s volitelně připojovaným
dekadickým modifikátorem D. Ještě existuje pět dalších dekadických přípon:
K (Kilo), říká EuroAssembleru, že má číslo vynásobit 210=1024,
M (Mega), říká EuroAssembleru, že má číslo vynásobit 220=1_048_576,
G (Giga), říká EuroAssembleru, že má číslo vynásobit 230=1_073_741_824,
T (Tera), říká EuroAssembleru, že má číslo vynásobit 240=1_099_511_627_776,
P (Peta), říká EuroAssembleru, že má číslo vynásobit 250=1_125_899_906_842_624.
Dekadické číslo může být také prefixováno modifikátorem 0N.
Všech šest čísel v následujícím příkladu má tutéž hodnotu:
1048576, 1048576d, 0n1048576, 1_048_576, 1024K, 1M
.
Největší číslo, které se vejde do 32 bitů je 0xFFFF_FFFF=4_294_967_295.
Největší číslo, které se vejde do 63 bitů je 0x7FFF_FFFF_FFFF_FFFF=9_223_372_036_854_775_807.
Binární číslo je zapsáno jako sled číslic 0 a 1 s připojeným
binárním modifikátorem B anebo s prefixem 0Y. Příklady:
0y101, 101b, 00110010b, 1_1111_0100B
odpovídají dekadickým číslům
5, 5, 50, 500
.
Největší 32bitové binární číslo je 1111_1111__1111_1111__1111_1111__1111_1111b.
Každá oktalová číslice 0..7 reprezentuje tři bity z odpovídající binární notace. Číslo je zakončeno oktalovou příponou Q anebo mu předchází předpona 0O nebo 0o (číslice nula následovaná velkým či malým písmenem O).
Příklad: 177_377q = 0o177_377 = 0xFEFF
Největší 32bitové oktalové číslo je 37_777_777_777q.
Největší 64bitové oktalové číslo je 1_777_777_777_777_777_777_777q.
Každá hexadecimální číslice kóduje čtyři bity do jednoho znaku. což vyžaduje 24=16 možných hodnot. Proto je deset dekadických čísli rozšířeno písmeny A, B, C, D, E, F s hodnotami 10, 11, 12, 13, 14, 15. U hexadecimálních cifer A..F nezáleží na velikosti písmen. Pokud by měla být první číslice hexadecimálního čísla reprezentována písmenem A..F, je třeba před číslo přidat nevýznamnou vedoucí nulu. Hexadecimální číslo je zakončeno příponou H, anebo začíná předponou 0X.
Příklad: 5h, 0x32, 1F4H, 0x1388, 0C350H
reprezentuje postupně čísla
5, 50, 500, 5000, 50000
.
Všechna čísla jsou v €ASM interně uchovávána jako 64bitové integery se znaménkem. Přestože instrukceMOV EAX,0xFFFF_FFFF
aMOV EAX,-1
se překládají na identické kódy, interně jsou jejich operandy uchovávány jako0x0000_0000_FFFF_FFFF
a0xFFFF_FFFF_FFFF_FFFF
. Logický výraz0xFFFF_FFFF = -1
je nepravda. |00000000:B8FFFFFFFF | MOV EAX, 0xFFFF_FFFF |00000005:B8FFFFFFFF | MOV EAX, -1 |FALSE | %IF 0xFFFF_FFFF = -1
Celá čísla mohou být zapsána jako binární, oktalová, dekadická nebo hexadecimání.
Některé číselné modifikátory se překrývají s hexadecimálními číslicemi B, D, E.
€ASM se snaží rozebírat co nejdelší zápis, aby předešel nejednoznačnostem:
1BH
je pochopeno jako hexadecimální číslo 0x1B=27 a nikoliv jako binární číslo 1 následovaná písmenem H.
2DH
je pochopeno jako hexadecimální číslo 0x2D=45 a nikoliv jako dekadické číslo 2 následované písmenem H.
3E2H
je pochopeno jako hexadecimální číslo 0x3E2=994 a nikoliv jako 3 * 102 následované písmenem H.
Notace | Předpona | Báze | Přípona | Násobitel |
---|---|---|---|---|
Binární | 0Y | 2 | B | 1 |
Oktalová | 0O | 8 | Q | 1 |
Dekadická | 0N | 10 | D | 1 |
K | 210 | |||
M | 220 | |||
G | 230 | |||
T | 240 | |||
P | 250 | |||
Hexadecimální | 0X | 16 | H | 1 |
Binární, oktalová a hexadecimální čísla musejí být vždy uváděna s předponou nebo s příponou (nebo s oběma, i když to není obvyklé). V EuroAssembleru není direktiva RADIX.
Pro více příkladů syntaxe čísel viz testy €ASM numbers tests.
Čísla s pohyblivou desetinnou tečkou, neboli reálná čísla se načítají ze zápisu s desetinnou tečkou a exponentu deseti v následující syntaxi:
Pořadí | Název pole | Obsah |
---|---|---|
1 | znaménko čísla | +, - nebo žádné |
2 | celočíselná část | číslice 0..9, separátor _ |
3 | desetinná tečka | . |
4 | neceločíselná část | číslice 0..9, separátor _ |
5 | číselný modifikátor | E nebo e |
6 | znaménko exponentu | +, - nebo žádné |
7 | exponentová část | číslice0..9, separátor_ |
Kupříkladu reálné číslo 1234.56E3
má hodnotu 1234.56 * 103=1234560.
Chybějící znaménko se považuje za +.
Neceločíselnou část lze vynechat, pokud je nula: 123.00E2 = 123.E2
Desetinnou tečku lze vynechat, pokud je neceločíselná část vynechána (je nulová).
Modifikátor E stále určuje, že se jedná o formát plovoucí tečky. 123.00E2 = 123.E2 = 123E2 = 12300.
Exponentovou část lze vynechat, pokud je nulová. Modifikátor E se v tom případě může také vynechat.
Bez modifikátoru je to existence desetinné tečky, která určuje, zda je číslo zapsáno
jako celočíselné (integer) nebo s plovoucí tečkou (real).
Příklad: 12345.67E0 = 12345.67E = 12345.67
V zápisu čísla s plovoucí tečkou nejsou dovoleny žádné mezery.
Číslo se považuje za plovoucí pokud jeho zápis obsahuje buď desetinnou tečku ., nebo modifikátor E (velké nebo malé písmeno) anebo obojí.
Všechny interní kalkulace jsou v €ASM prováděny pouze s 64bitovými celými čísly. Pokud by bylo číslo s plovoucí tečkou (FP) použito v matematickém výrazu, bude nejprve převedeno na celočíselnou hodnotu. Pokud by se číslo nevešlo do 64 bitů, ohlásí se chyba E6130 (number overflow). Pokud došlo ke ztrátě přesnosti zanedbáním neceločíselné části čísla, ohlásí se varování W2210 (precision lost).
Formát FP čísla dle [IEEE754] se zachovává pouze pokud je vědecká notace použita ke statické deklaraci čísel v pseudoinstrukcích DD, DQ, DT.
FP čísla s poloviční přesností (float16) nejsou podporována EuroAssemblerem ani procesory, s výjimkou dvou SIMD instrukcí VCVTPS2PH a VCVTPH2PS, a s výjimkou několika konverzních operací kódovaných s prefixem MVEX.
Na rozdíl od celých čísel, znaménko v FP notaci je neoddělitelné od následnujícíh číslic. Pokud omylem vložíte mezeru mezi znaménko a číslo s plovoucí tečkou, namísto definice FP čísla to bude €ASM považovat za operaci (unární minus aplikované na číslo) a tudíž nejprve převede FP číslo na integer, než je vyhodnotí. |00000000:001DF1C7 | DD -123.45E3 ; Single-precision FP number -123.45*103. |00000004:C61DFEFF | DD - 123.45E3 ; Dword signed integer number -123450. |00000008:00000000A023FEC0 | DQ -123.45E3 ; Double-precision FP number -123.45*103. |00000010:C61DFEFFFFFFFFFF | DQ - 123.45E3 ; Qword signed integer number -123450. |00000018:0000000000001DF10FC0 | DT -123.45E3 ; Extended-precision FP number -123.45*103. |00000022: | DT - 123.45E3 ; Tbyte integer number není podporováno. |### E6725 Datatype TBYTE expects plain floating-point number.
Vedle standardních hodnot mohou čísla s plovoucí tečkou nabývat speciální FP konstanty:
Konstanta | Interpretace | single precision (DD) | double precision (DQ) | extended precision (DT) |
---|---|---|---|---|
#ZERO | zero | 00000000 | 00000000_00000000 |
0000_00000000_00000000 |
+#ZERO | positive zero | 00000000 | 00000000_00000000 |
0000_00000000_00000000 |
-#ZERO | negative zero | 80000000 | 80000000_00000000 |
8000_00000000_00000000 |
#INF | infinity | 7F800000 | 7FF00000_00000000 |
7FFF_80000000_00000000 |
+#INF | positive infinity | 7F800000 | 7FF00000_00000000 |
7FFF_80000000_00000000 |
-#INF | negative infinity | FF800000 | FFF00000_00000000 |
FFFF_80000000_00000000 |
#PINF | pseudo infinity | 7F800000 | 7FF00000_00000000 |
7FFF_00000000_00000000 |
+#PINF | positive pseudo infinity | 7F800000 | 7FF00000_00000000 |
7FFF_00000000_00000000 |
-#PINF | negative pseudo infinity | FF800000 | FFF00000_00000000 |
FFFF_00000000_00000000 |
#NAN | not a number | 7FC00000 | 7FF80000_00000000 |
7FFF_C0000000_00000000 |
+#NAN | positive not a number | 7FC00000 | 7FF80000_00000000 |
7FFF_C0000000_00000000 |
-#NAN | negative not a number | FFC00000 | FFF80000_00000000 |
FFFF_C0000000_00000000 |
#PNAN | pseudo not a number | 7F800001 | 7FF00000_00000001 |
7FFF_00000000_00000001 |
+#PNAN | positive pseudo not a number | 7F800001 | 7FF00000_00000001 |
7FFF_00000000_00000001 |
-#PNAN | negative pseudo not a number | FF800001 | FFF00000_00000001 |
FFFF_00000000_00000001 |
#QNAN | quiet not a number | 7FC00000 | 7FF80000_00000000 |
7FFF_C0000000_00000000 |
+#QNAN | positive quiet not a number | 7FC00000 | 7FF80000_00000000 |
7FFF_C0000000_00000000 |
-#QNAN | negative quiet not a number | FFC00000 | FFF80000_00000000 |
FFFF_C0000000_00000000 |
#SNAN | signaling not a number | 7F800001 | 7FF00000_00000001 |
7FFF_80000000_00000001 |
+#SNAN | positive signaling not a number | 7F800001 | 7FF00000_00000001 |
7FFF_80000000_00000001 |
-#SNAN | negative signaling not a number | FF800001 | FFF00000_00000001 |
FFFF_80000000_00000001 |
U jmen speciálních FP konstant nezáleží na velikosti písmen.
Pokud je užito znaménko + nebo -, je neoddělitelné. Příklady:
FourNans DY 4 * QWORD #NaN ; Definuj vektor čtyř hodnot not-a-number s dvojitou přesností.
MOV ESI,=8*Q#ZERO ; Definuj 8*8 nulových bajtů v literální sekci a nechej na ně ukazovat ESI
Číslo můžr být zapsáno také jako znaková konstanta, cože je řetězec nanejvýš osmi znaků. Jeho číselná hodnota se bere z pořadového čísla znaků v tabulce ASCII. Příklad znakových konstant a jejich hodnot:
'0' = 30h = 48 'abc' = 636261h = 6513249 "4%%" = 2534h = 9524
Asemblery nejsou jednotné v interpretaci znakových konstant. MASM a TASM používají zápisovou konvenci, kdy pořadí znaků ve zdrojovém kódu odpovídá tomu, jak píšeme dekadická čísla: nejméně významná číslice je zcela vpravo.
€ASM a s ním i většina novějších asemblerů používají paměťovou konvenci, kde pořadí znaků odpovídá uložení znaků v oprační paměti paměti procesorů s architekturou little endian:
| | ; MASM and TASM: |00000000:616263 | DB 'abc' ; Řetězec. |00000003:63626100 | DD 'abc' ; Znaková konstanta |00000007:B863626100 | MOV EAX,'abc' ; AL='c'. | | ; €ASM, FASM, GoASM, NASM, SpASM: |00000000:616263 | DB 'abc' ; Řetězec. |00000003:61626300 | DD 'abc' ; Znaková konstanta. |00000007:B861626300 | MOV EAX,'abc' ; AL='a'.
Některé parametry mohou nabývat pouze předdefinované hodnoty,
například volba EUROASM CPU=
může být 086, 186, 286, 386, 486, 586, 686, PENTIUM, P6, X64
.
Třebaže některé výčtové hodnoty mohou vypadat jako čísla, nejsou počitatelné.
Kterékoli číslo může být interpretováno jako booleovská (logická) hodnota. Booleovská hodnota může mít jeden ze dvou stavů: pravda nebo nepravda. Číslo 0 je považováno za booleovskou hodnotu nepravda, zatímco jakékoli nenulové číslo je vyhodnoceno jako pravda.
Všechny vestavěné booleovské volby v €ASM mají rozšířený repertoár možných hodnot. Tyto volby akceptují
To se týká
Tento rozšířený repertoár platí pouze pro operandy vestavěné do €ASM.
Nejedná se o kdekoli použitelné symboly,
jako
.
K získání podobné funkčnosti v makrech by programátor nejprve musel takové symboly nadefinovat, např.MOV EAX,TRUE
FALSE EQU 0 false EQU 0 TRUE EQU -1 true EQU !false MOV EAX,TRUE
Je-li booleovské rozšíření použito jako klíčový operand makra,
může být rovněž testován v těle makra pomocí
%IF, %WHILE, %UNTIL
, například
MacroWithBool %MACRO Bool=On %IF %Bool ; Instrukce pokud Bool je pravda. %ELSE ; Instrukce pokud Bool je nepravda. %ENDIF %ENDMACRO MacroWithBool
Nyní můžeme makro vyvolat pomocí MacroWithBool Bool=Enable
, MacroWithBool Bool=No
apod.
MacroWithBool %MACRO Bool=0 %IF ! %Bool ; Toto je logický výraz. ; Instrukce pokud Bool je nepravda. %ENDIF %ENDMACRO MacroWithBool
Předchozí příklad nebude fungovat s booleovským rozšířením, např. MacroWithBool Bool=False
si bude stěžovat, že E6601 Symbol "False" was not found.. Avšak po převrácení logiky by měl fungovat dobře:
MacroWithBool %MACRO Bool=0 %IF %Bool %ELSE ; Instrukce pokud Bool je nepravda. %ENDIF %ENDMACRO MacroWithBool
Řetězec je sled libovolných znaků obklopený uvozovkami. Lze používat jak uvozovky dvojité ", tak i jednoduché ' (rovněž nazývané apostrofy). Obklopující uvozovky se nezapočítávají do obsahu řetězce. Všechny znaky uvnitř řetězce ztrácejí svůj sémantický význam, jsou jen tři výjimky:
€ASM nepoužívá únikový znak (escape), procento a uvozovky escapují samy sebe. Chcete-li takový znak použít v řetězci, musí být zdvojen. Tato duplikace se týká pouze zápisu řetězce ve zdrojovém textu a neprodlužuje obsah řetězce v paměti počítače.
Řetězce uzavřené v 'apostrofech' a ve "dvojitých uvozovkách" jsou ekvivalentní
s jedinou výjimkou: má-li být obsahem řetězce název souboru,lze použít pouze dvojité uvozovky.
neboť apostrof je platným znakem použitelným v názvech souborů na většině souborových systémů.
Další příklady řetězců:
Processor, neboli Central Processing Unit (CPU), operuje s daty a komunikuje s okolím (registry, paměť, zařízení). Typická operace načte informaci z registru, paměti nebo portu (I/O zařízení), učiní s ní nějakou transformaci a zapíše ji zpět do okolí. Nejmenší adresovatelnou jednotkou paměti je jeden bajt (1 B) a jejich počet je omezen adresním prostorem.
Registr je identifikován svým názvem, zařízení je identifikováno číslem portu, bajt paměti je identifikován jeho adresou.
CPU mód | GPR | I/O port | Adresní prostor |
---|---|---|---|
16bit | 8* 2 B | 64 KB (216) | 1 MB (216+4) |
32bit | 8* 4 B | 64 KB (216) | 4 GB (232) |
64bit | 16* 8 B | 64 KB (216) | 16384 PB (264) |
Adresní prostor je limitován architekturou a počtem vodičů mezi procesorem a paměťovými čipy. Kombinace logických jedniček a nul, která by se dala naměřit na těchto drátech, se nazývá fyzická adresa (PhA).
Z pohledu aplikačního programátora procesor zapisuje a čte z virtuální adresy (VA). Neuvažujeme-li segmentaci paměti, můžeme virtuální adrese říkat lineární adresa (LA). Virtuální a fyzická adresa byly totožné pouze u první generaci procesorů bez vyrovnávací paměti a stránkování.
Objekty sestaveného obrazu programů v chráněném módu jsou adresovány pomocí ofsetu od začátku obrazu v paměti (od ImageBase). Tento ofset se nazývá relativní virtuální adresa (RVA).
Pozice objektů v souboru bývá identifikována pomocí adresy v souboru (FA), definované vzdáleností objektu od začátku souboru.
PhA, VA, LA, RVA, FA jsou celočíselná nezáporná prostá čísla, avšak adresování v asm-time je komplikovanější. Z historických důvodů je adresní prostor rozdělen na segmenty paměti a každý segment je identifikován obsahem segmentového registru. Adresa v době kompilace je vyjádřena jako počet bajtů (ofset) mezi pozicí a počátkem segmentu, a samotnou identifikací segmentu. Viz také kapitoly Adresní symboly a Adresní výrazy.
Procesor přistupuje k datům v paměti rychleji, pokud je jejich adresa zarovnána (aligned), tedy zaokrouhlena na násobek mocniny dvou. Většina strojových instrukcí IA-32 si sice poradí s nezarovnanými daty, avšak trvá to déle, pokud data nejsou ve stejné stránce vyrovnávací paměti.
Paměťové proměnné by měly být zarovnány na jejich přirozený alignment odpovídající jejich velikosti, viz sloupec Autoalign v tabulce datatypů. Dvojslova DWORD mají hodnotu autoalignmentu rovnou 4, což znamená, že poslední dva bity by při správném zarovnání měly být nulové. QWORD jsou zarovnány k 8, takže nulové jsou poslední tři bity (8=23).
Zarovnání lze dosáhnout buď explicitně pomocí pseudoinstrukce ALIGN, anebo klíčem ALIGN= ve strojové instrukci nebo v definici procedury PROC nebo PROC1.
Paměťové proměnné budou automaticky zarovnány, pokud volba EUROASM
AUTOALIGN=ON. Například instrukce
Dvojslovo: DD 1234
bude automaticky zarovnána na 4
(ofset Dvojslovo bude dělitelný čtyřmi beze zbytku).
Výplňový znak, vkládaný podle potřeby před zarovnávanou proměnnou,
je nula 0x00 v datovém segmentu nebo NOP 0x90, případně vícebajtový NOP
v kódovém segmentu.
Hodnota parametru ALIGN= může být numerický výraze vyhodnocený na 1, 2, 4, 8 nebo vyšší mocniny dvou. €ASM rovněž akceptuje nulu nebo prázdnou hodnotu, což pak je identické s ALIGN=1 (bez efektu). Vedle těchto číselných hodnot ALIGN= rovněž akceptuje výčtové hodnoty BYTE, WORD, DWORD, QWORD, OWORD, YWORD, ZWORD nebo jejich krátké varianty B, W, D, Q, O, Y, Z.
Zarovnání je omezeno alignmentem segmentu, v němž příkaz leží. Pokud je altuální segment zarovnán na DWORD, nelze žádat o zarovnání na QWORD nebo OWORD. Výchozí zarovnání segmentů je OWORD (10h) a zvyšuje se na SectionAlign (obvykle na 1000h), pokud je program ve formátu ELFX nebo PE/DLL.
Kromě instrukčního modifikátoru ALIGN=
může být zarovnání vyžádáno také
samostatnou pseudoinstrukcí ALIGN, která dovoluje i záměrné zrušení zarovnání.
Třebaže si registr pamatuje zapsanou informaci, není součástí adresovatelné paměti. Registr může být osloven pouze svým jménem, nemá adresu.
Rodina | REGTYPE# | Členové | Velikost |
---|---|---|---|
GPR 8bit | 'B' | AL, AH, BL, BH, CL, CH, DL, DH,
DIB, SIB, BPB, SPB, R8B, R9B, R10B, R11B, R12B, R13B, R14B, R15B DIL, SIL, BPL, SPL, R8L, R9L, R10L, R11L, R12L, R13L, R14L, R15L | 1 |
GPR 16bit | 'W' | AX, BX, CX, DX, BP, SP, SI, DI, R8W, R9W, R10W, R11W, R12W, R13W, R14W, R15W | 2 |
GPR 32bit | 'D' | EAX, EBX, ECX, EDX, EBP, ESP, ESI, EDI, R8D, R9D, R10D, R11D, R12D, R13D, R14D, R15D | 4 |
GPR 64bit | 'Q' | RAX, RBX, RCX, RDX, RBP, RSP, RSI, RDI, R8, R9, R10, R11, R12, R13, R14, R15 | 8 |
Segment | 'S' | CS, SS, DS, ES, FS, GS | 2 |
FPU | 'F' | ST0, ST1, ST2, ST3, ST4, ST5, ST6, ST7 | 10 |
MMX | 'M' | MM0, MM1, MM2, MM3, MM4, MM5, MM6, MM7 | 8 |
XMM | 'X' | XMM0, XMM1, XMM2, XMM3, XMM4, XMM5, XMM6, XMM7, XMM8, XMM9, XMM10, XMM11, XMM12, XMM13, XMM14, XMM15, XMM16, XMM17, XMM18, XMM19, XMM20, XMM21, XMM22, XMM23, XMM24, XMM25, XMM26, XMM27, XMM28, XMM29, XMM30, XMM31 | 16 |
AVX | 'Y' | YMM0, YMM1, YMM2, YMM3, YMM4, YMM5, YMM6, YMM7, YMM8, YMM9, YMM10, YMM11, YMM12, YMM13, YMM14, YMM15, YMM16, YMM17, YMM18, YMM19, YMM20, YMM21, YMM22, YMM23, YMM24, YMM25, YMM26, YMM27, YMM28, YMM29, YMM30, YMM31 | 32 |
AVX-512 | 'Z' | ZMM0, ZMM1, ZMM2, ZMM3, ZMM4, ZMM5, ZMM6, ZMM7, ZMM8, ZMM9, ZMM10, ZMM11, ZMM12, ZMM13, ZMM14, ZMM15, ZMM16, ZMM17, ZMM18, ZMM19, ZMM20, ZMM21, ZMM22, ZMM23, ZMM24, ZMM25, ZMM26, ZMM27, ZMM28, ZMM29, ZMM30, ZMM31 | 64 |
Mask | 'K' | K0. K1, K2. K3, K4, K5, K6, K7 | 8 |
Bound | 'N' | BND0, BND1, BND2, BND3 | 16 |
Control | 'C' | CR0, CR2, CR3, CR4, CR8 | 4 |
Debug | 'E' | DR0, DR1, DR2, DR3, DR6, DR7 | 4 |
Test | 'T' | TR3, TR4, TR5 | 4 |
U jmen registrů nezáleží na velikosti písmen. Některé General Purpose Registers (GPR) mají aliasy, například AL je jiný název pro nižší polovinu AX, což je jiný název pro nižší polovinu EAX, což je jiný název pro nižší polovinu RAX.
Podobné aliasy mají SIMD (AVX) registry: XMM0 je jiný název pro nižší polovinu YMM0, což je jiný název pro nižší polovinu ZMM0.
Názvy 8bitových registrů DIB, SIB, BPB, SPB, R8B..R15B jsou aliasy pro nejméně významný bajt registrů RDI, RSI, RBP, RSP, R8..R15. Mohou být nazývány také jako DIL, SIL, BPL, SPL, R8L..R15L v souladu s dokumentací v manuálech Intelu. €ASM podporuje obě přípoy ~L i ~B. Tyto registry jsou dostupné pouze v 64bitovém módu.
Některé asemblery a manuály používají zápis ST(0), ST(1)..ST(7) pro registry jednotky pohyblivé desetinné tečky, tato syntaxe však není EuroAssemblerem akceptována. Podobně nefunguje ani záměna registru ST0 za ST (vrchol stacku FPU).
Procesor x86 obsahuje ještě některé další registry, které obsahují flagy, tabulky deskriptorů, řídicí a stavové registry FPU, avšak ty zde nejsou uvedeny, neboť nejsou přímo dostupné pomocí svého názvu.
Výsledek některých operací procesoru lze považovat za predikát, kterému lze přidělit mnemotechnické jméno a použít jako součást názvu strojových instrukcí.
Některým kombinacím aritmetických flagů CPU ZF, CF, OF, SF, PF lze přiřadit speciální jména, tzv. podmínkové kódy. Jsou použity v mnemonice podmíněného větvení Jcc nebo v instrukcích podmíněných přesunů CMOVcc.
Invertovaný kód může být použit v makroinstrukcích k přeskočení kódu, pokud podmínka není splněna. Viz sloupec invertovaná mnemonika.
Num. hodnota | Mnemonika | Alias | Popis | Podmínka | Invertovaná mnemonika |
---|---|---|---|---|---|
0x4 | E | Z | Equal | ZF=1 | NE |
0x5 | NE | NZ | Not Equal | ZF=0 | E |
0x4 | Z | E | Zero | ZF=1 | NZ |
0x5 | NZ | NE | Not Zero | ZF=0 | Z |
0x2 | C | B | Carry | CF=1 | NC |
0x3 | NC | NB | Not Carry | CF=0 | C |
0x2 | B | C | Borrow | CF=1 | NB |
0x3 | NB | NC | Not Borrow | CF=0 | B |
0x0 | O | Overflow | OF=1 | NO | |
0x1 | NO | Not Overflow | OF=0 | O | |
0x8 | S | Sign | SF=1 | NS | |
0x9 | NS | Not Sign | SF=0 | S | |
0xA | P | PE | Parity | PF=1 | NP |
0xB | NP | PO | Not Parity | PF=0 | P |
0xA | PE | P | Parity Even | PF=1 | PO |
0xB | PO | NP | Parity Odd | PF=0 | PE |
0x7 | A | NBE | Above | CF=0 && ZF=0 | NA |
0x6 | NA | BE | Not Above | CF=1 || ZF=1 | A |
0x3 | AE | NB | Above or Equal | CF=0 | NAE |
0x2 | NAE | B | Not Above nor Equal | CF=1 | AE |
0x2 | B | NAE | Below | CF=1 | NB |
0x3 | NB | AE | Not Below | CF=0 | B |
0x6 | BE | NA | Below or Equal | CF=1 || ZF=1 | NBE |
0x7 | NBE | A | Not Below nor Equal | CF=0 && ZF=0 | BE |
0xF | G | NLE | Greater | SF=OF && ZF=0 | NG |
0xE | NG | LE | Not Greater | SF<>OF || ZF=1 | G |
0xD | GE | NL | Greater or Equal | SF=OF | NGE |
0xC | NGE | L | Not Greater nor Equal | SF<>OF | GE |
0xC | L | NGE | Less | SF<>OF | NL |
0xD | NL | GE | Not Less | SF=OF | L |
0xE | LE | NG | Less or Equal | SF<>OF || ZF=1 | NLE |
0xF | NLE | G | Not Less nor Equal | SF=OF && ZF=0 | LE |
CXZ | CX register je Zero | CX=0 | |||
ECXZ | ECX register je Zero | ECX=0 | |||
RCXZ | RCX register je Zero | RCX=0 |
Instrukce rozšíření Single Instruction Multiple Data (V)CMPccSS,(V)CMPccSD,(V)CMPccPS,(V)CMPccPD používají jiné než všeobecné podmínkové kódy cc.
Pro původní instrukce CMPccSS,CMPccSD,CMPccPS,CMPccPD jsou dokumentovány pouze ty uvedené ve sloupci Alias.
Num. hodnota | Mnemonika | Alias | Popis |
---|---|---|---|
0x00 | EQ_OQ | EQ | Equal, Ordered, Quiet |
0x01 | LT_OS | LT | Less Than, Ordered, Signaling |
0x02 | LE_OS | LE | Less than or Equal, Ordered, Signaling |
0x03 | UNORD_Q | UNORD | Unordered, Quiet |
0x04 | NEQ_UQ | NEQ | Not Equal, Unordered, Quiet |
0x05 | NLT_US | NLT | Not Less Than, Unordered, Signaling |
0x06 | NLE_US | NLE | Not Less than or Equal,Unordered, Signaling |
0x07 | ORD_Q | ORD | Ordered, Quiet |
0x08 | EQ_UQ | Equal, Unordered, Quiet | |
0x09 | NGE_US | NGE | Not Greater than or Equal, Unordered, Signaling |
0x0A | NGT_US | NGT | Not Greater Than, Unordered, Signaling |
0x0B | FALSE_OQ | FALSE | False, Ordered, Quiet |
0x0C | NEQ_OQ | Not Equal, Ordered, Quiet | |
0x0D | GE_OS | GE | Greater than or Equal, Ordered, Signaling |
0x0E | GT_OS | GT | Greater Than, Ordered, Signaling |
0x0F | TRUE_UQ | TRUE | True, Unordered, Quiet |
0x10 | EQ_OS | Equal, Ordered, Signaling | |
0x11 | LT_OQ | Less Than, Ordered, Quiet | |
0x12 | LE_OQ | Less than or Equal, Ordered, Quiet | |
0x13 | UNORD_S | Unordered, Signaling | |
0x14 | NEQ_US | Not Equal, Unordered, Signaling | |
0x15 | NLT_UQ | Not Less Than, Unordered, Quiet | |
0x16 | NLE_UQ | Not Less than or Equal, Unordered, Quiet | |
0x17 | ORD_S | Ordered, Signaling | |
0x18 | EQ_US | Equal, Unordered, Signaling | |
0x19 | NGE_UQ | Not Greater than or Equal, Unordered, Quiet | |
0x1A | NGT_UQ | Not Greater Than, Unordered, Quiet | |
0x1B | FALSE_OS | False, Ordered, Signaling | |
0x1C | NEQ_OS | Not Equal, Ordered, Signaling | |
0x1D | GE_OQ | Greater than or Equal, Ordered, Quiet | |
0x1E | GT_OQ | Greater Than, Ordered, Quiet | |
0x1F | TRUE_US | True, Unordered, Signaling |
K předepsání matematických operací jsou v €ASM použity kombinace speciálních znaků. Umístění binárního operátoru mezi dvě čísla instruuje €ASMm aby nahradil tyto tři prvky výsledkem operace. Některé operátory jsou unární, modifikují hodnotu operandu, před kterým stojí.
Matematické operqace implementované v €ASM jsou uvedeny v následující tabulce.
Operation | Priorita | Vlastnosti | Levý operand | Operátor | Pravý operand | Výsledek | II (6) |
---|---|---|---|---|---|---|---|
Membership | 16 | binární nekomut. (1) | identifier | . | identifier | identifikátor | |
Attribute | 15 | unární noncomm. (3) | attr# | element | číslo nebo adresa | ||
Case-insens. Equal | 14 | binární komutativní (2) | string | == | string | logický | CMPS |
Case-sens. Equal | 14 | binární komutativní | string | === | string | logický | CMPS |
Case-insens. Nonequal | 14 | binární komutativní (2) | string | !== | string | logický | CMPS |
Case-sens. Nonequal | 14 | binární komutativní | string | !=== | string | logický | CMPS |
Plus | 13 | unární (3) | + | number | číselný | NOP | |
Minus | 13 | unární (3) | - | number | číselný | NEG | |
Shift Logical Left | 12 | binární nekomutativní | number | << | number | číselný | SHL |
Shift Arithmetic Left | 12 | binární nekomutativní | number | #<< | number | číselný | SAL |
Shift Logical Right | 12 | binární nekomutativní | number | >> | number | číselný | SHR |
Shift Arithmetic Right | 12 | binární nekomutativní | number | #>> | number | číselný | SAR |
Signed Division | 11 | binární nekomutativní | number | #/ | number | číselný | IDIV |
Division | 11 | binární nekomutativní | number | / | number | číselný | DIV |
Signed Modulo | 11 | binární nekomutativní | number | #\ | number | číselný | IDIV |
Modulo | 11 | binární nekomutativní | number | \ | number | číselný | DIV |
Signed Multiplication | 11 | binární komutativní | number | #* | number | číselný | IMUL |
Multiplication | 11 | binární komutativní | number | * | number | číselný | MUL |
Scaling | 10 | binární komutativní (5) | number | * | register | adresní výraz | |
Addition | 9 | binární komutativní | number | + | number | číselný | ADD |
Subtraction | 9 | binární nekomutativní | number | - | number | číselný | SUB |
Indexing | 9 | binární komutativní (5) | number | + | register | adresní výraz | |
Bitwise NOT | 8 | unární (3) | ~ | number | číselný | NOT | |
Bitwise AND | 7 | binární komutativní | number | & | number | číselný | AND |
Bitwise OR | 6 | binární komutativní | number | | | number | číselný | OR |
Bitwise XOR | 6 | binární komutativní | number | ^ | number | číselný | XOR |
Above | 5 | binární nekomutativní | number | > | number | logický | JA |
Greater | 5 | binární nekomutativní | number | #> | number | logický | JG |
Below | 5 | binární nekomutativní | number | < | number | logický | JB |
Lower | 5 | binární nekomutativní | number | #< | number | logický | JL |
Above or Equal | 5 | binární nekomutativní | number | >= | number | logický | JAE |
Greater or Equal | 5 | binární nekomutativní | number | #>= | number | logický | JGE |
Below or Equal | 5 | binární nekomutativní | number | <= | number | logický | JBE |
Lower or Equal | 5 | binární nekomutativní | number | #<= | number | logický | JLE |
číselný Equal | 5 | binární komutativní | number | = | number | logický | JE |
číselný Nonequal | 5 | binární komutativní (4) | number | != or <> | number | logický | JNE |
Logical NOT | 4 | unární (3) | ! | number | logický | NOT | |
Logical AND | 3 | binární komutativní | number | && | number | logický | AND |
Logical OR | 2 | binární komutativní | number | || | number | logický | OR |
Logical XOR | 2 | binární komutativní | number | ^^ | number | logický | XOR |
Segment separation | 1 | binární nekomutativní | number | : | number | adresní výraz | |
Data duplication | 0 | binární nekomut. (1) (5) | number | * | datatype | datový výraz | |
Range | 0 | binární nekomut. (1) | number | .. | number | range | |
Substring | 0 | binární nekomut. (1) | text | [ ] | range | text | |
Sublist | 0 | binární nekomut. (1) | text | { } | range | text |
(1) Speciální operace Membership, Duplication, Range, Substring, Sublist se řeší už na úrovni rozkladu textu místo výpočtu pomocí evaluátoru výrazů, jsou zde uvedeny pouze kvůli kompletnosti.
(2) Porovnávání řetězcu nezávislé na velikosti ignoruje velikost písmen A..Z avšak nikoli velikost písmen s diakritikou větších než ASCII 127.
(3) Unární operátor je aplikován na následující operand. Binární operátor pracuje se dvěma operandy. Operátor atributu je aplikován na následující element nebo výraz v závorkách.
(4) Numerická operace Nonequal má dva shodné operátory != a <>. Můžete si vybrat.
(5) Operace Multiplication, Scaling a Duplication sdílejí tentýž operátor *. Podobně Addition a Indexing sdílejí operátor +. Druh operace je určen typy operandů.
(6) Sloupec II ilustruje, která strojová instrukce je interně použita k vyhodnocení během překladu.
Komutativnost určuje, zda oba operátory binární operace mohou být zaměněny bez vlivu na výsledek.
Slouoec Priorita určuje pořadí zpracování operátorů. Výrazy s operátory s vyšší prioritou se zpracovávají dříve, avšak lze to změnit použitím prioritních závorek ( ). Operace se shodnou prioritou se zpracovávají v pořadí zápisu (zleva doprava).
Operace kalkulující s čísly se znaménky mají operátor prefixován znakem #. Operace Addition a Subtraction nepotřebují speciální znaménkovou verzi, neboť počítají se signed i unsigned čísly stejně.
Číselné i logické operace vracejí 64bitové číslo.
V případě booleovských operací má výsledek jednu ze dvou možných hodnot:
0
(nepravda) nebo -1 = 0xFFFF_FFFF_FFFF_FFFF
(pravda).
Kupříkladu výraz
'+' & %1 #>= 0 | '-' & %1 #< 0
je vyhodnocen jako
('+' & (%1 #>= 0)) | ('-' & (%1 #< 0))
a výsledkem je znaménko mínus (45) pokud je %1
negativní,
nebo znaménko plus (43) v ostatních případech.
Mezery oddělující operandy a operátory slouží jen pro lepší čitelnost a nejsou vyžadovány syntaktickými pravidly.
Skupina operátorů Shift dostala vyšší prioritu než v jiných jazycích, neboť považuji posuvy za speciální druh násobení a dělení.
Výraz4+3<<2
vyhodnocuje NASM jako(4+3)<<2 = 28
, zatímco v €ASM je vyhodnocen jako4+(3<<2) = 16)
.
Výraz (expression) je kombinace operandů, operátorů a prioritních závorek () zachovávající pravidla z níže uvedené tabulky.
Co může následovat | levá závorka | unární operátor | operand | binární operátor | pravá závorka | konec výrazu |
---|---|---|---|---|---|---|
začátek výrazu | ano | ano | ano | ne | ne | ano (2) |
levou závorku | ano | ano | ano | ne | ano (2) | ne |
unární operátor | ano | ne | ano | ne | ne | ne |
operand | ne | ne | ne | ano | ano | ano |
binární operátor | ano | ano (1) | ano | ne | ne | ne |
pravou závorku | ne | ne | ne | ano | ano | ano |
(1) Unární operátor za binárním je povolen, např.
5*-3
se vyhodnotí jako 5*(-3)
.
(2) Prázdné výrazy, prázdný obsah závorek a nadbytečné závorky se tolerují.
Tabulka vypisuje, které kombinace jsou přípustné. Měla by se číst po řádkách, např. první řádek sděluje, že výraz může začínat levou závorkou, unárním operátorem nebo operandem.
Výraz je rozdělen na elementární unární a binární operace, které jsou vyhodnocovány podle priority. Operace se shodnou prioritou jsou počítány zleva doprava. Prioritu lze zvětšit pomocí závorek ( ).
Výsledek číselného nebo logického výrazu je 64bitová numerická hodnota (signed integer). Lze ji považovat za číslo nebo za logickou hodnotu. Nulový výsledek se považuje za logickou hodnotu nepravda a nenulový výsledek za hodnotu pravda. Čistě logické výrazy, jako logické NOT, AND, OR, XOR a všechny porovnávací operace vracejí 0 anebo 0xFFFF_FFFF_FFFF_FFFF = -1. To umožňuje použít výsledek logických operací s následnými bitovými operacemi se všemi bity.
Porovnávací operace vracejí logickou hodnotu. Operace necitlivé na velikost písmen nejprve převádějí oba řetězce na stejnou velikost písmen před samotným srovnáním, to se ale týká pouze ASCII písmen A..Z, nikoli národních znaků s diakritikou.
Řetězcová porovnávání mají tu nejvyšší prioritu, neboť v asm-time nelze provádět žádnou jinou operaci kromě testu na rovnost. €ASM nemůže říci, který řetězec je "větší". |00000000:FFFFFFFFFFFFFFFF | DQ "EAX" == "eax" ; Pravda, řetězce jsou si rovny (až na velikost písmen). |00000008:0000000000000000 | DQ "EAX" === "eax" ; Nepravda, řetězce se liší velikostí písmen. |00000010:FFFFFFFFFFFFFFFF | DQ "I'm OK." === 'I''m OK.' ; Pravda, jejich netto hodnoty se shodují. |00000018:0000000000000000 | DQ "Müller" == "MÜLLER" ; Nepravda kvůli odlišné velikosti přehlasovaného U. |00000020:0000000000000000 | DQ "012" == "12" ; Nepravda, řetězce se neshodují. |00000028:0000000000000000 | DQ "123" = 123 ; Nepravda, znaková konstanta "123"=3355185 se liší od čísla 123. |00000030: | DQ "123" == 123 ; Syntaktická chyba; pravý operand není řetězcem. |### E6321 String compare InsensEqual with non-string operand in expression ""123" == 123". |00000030:
Porovnávání nezávislé na velikosti písmen by se mělo používat na vestavěné elementy €ASM, jako jsou názvy registrů nebo datatypů, např.
%IF '%1' !== 'ECX' %ERROR Only register ECX is expected as the first macro operand. %ENDIF
V ostatních případech je vhodnější použít srovnání citlivé na velikost písmen, neboť €ASM pak nemusí nejprve převádět řetězce na shodnou velikost:
DoSomethingWithMemoryVar %MACRO %IF '%1[1]' !=== '[' ; Zjisti, zda první operand makra začíná lomenou závorkou. %ERROR The first operand should be a memory variable in [brackets]. %ENDIF %ENDMACRO DoSomethingWithMemoryVar
Test složené závorky z předchozího příkladu selhává, pokud je prvním operandem makra
řetězec nebo znaková konstanta v uvozovkách, např.
DoSomethingWithMemoryVar 'xyz'
. Operace porovnání řetězců vyvolá
E6101 Expression "''' !=== '" is followed by unexpected character "[". kvůli chybě syntaxe.
Jeden trik, jak se vyhnout chybě E6101, je porovnávat zdvojené hodnoty.
V tom případě se jednoduché i dvojité uvozovky navzájem escapují:
DoSomethingWithMemoryVar %MACRO %IF '%1[1]%1[1]' !=== '[[' ; Zjisti, zda první operand makra začíná lomenou závorkou. %ERROR The first operand should be a memory variable in [brackets]. %ENDIF
Číselné porovnání používá jeden znak rovnítka =, případně ještě doplněný znaky < nebo > a může srovnávat hodnoty dvou prostých čísel nebo dvou adres v rámci téhož segmentu.
Číselné porovnání určí, která strana operace je větší. Termín above/below se používá při srovnávání adres nebo čísel bez znamének. Termín greater/lower se použije při srovnávání znaménkových čísel. Operátory považující čísla za znaménková mají předřazen znak #. Virtuální adresy jsou vždy bez znaménka, takže se nemůžeme zeptat, zda jsou "greater" nebo "lower".
|00000000:FFFFFFFFFFFFFFFF | DQ 5 < 7 ; Pravda, 5 je pod 7. |00000008:FFFFFFFFFFFFFFFF | DQ 5 #< 7 ; Pravda, 5 je menší než 7. |00000010:0000000000000000 | DQ 5 #< -7 ; Nepravda, 5 není menší než -7. |00000018:FFFFFFFFFFFFFFFF | DQ 5 < -7 ; Pravda, 5=0x0000_0000_0000_0005 je pod -7=0xFFFF_FFFF_FFFF_FFF9. |00000020:FFFFFFFFFFFFFFFF | DQ 123 = 0123 ; Pravda, obě čísla jsou shodná. |00000028:0000000000000000 | DQ "123" == "0123" ; Nepravda, oba řetězce se liší. |00000030:0000000000000000 | DQ "123" = "0123" ; Nepravda, obě strany se považují za znakové konstanty s různými hodnotami. |00000038: | DQ "123" = "000000123" ; "000000123" není číslo, je příliš velké na znakovou konstantu. |### E6131 Character constant "123" = "000000123" is too big for 64 bits. |00000038: |Podporované aritmetické operace jsou Addition, Subtraction, Multiplication, Division a Modulo (zbytek po dělení).
Unární mínus lze aplikovat pouze na skalární operandy.
Unární plus nemění hodnotu operandu; v seznamu operátorů je zařazen kvůli kompletnosti.
Sousedící binární a unární numerický operátor je v €ASM akceptován, i když to vypadá divně.
Je to užitečné při operacích s nahrazenou hodnotou, jako 5 + %1
,
kde symbolický argument %1 je náhodou negativní, třeba -2
.
Výraz je vyhodnocován jako
5 + %1
.-> 5 + -2 -> 5 + (-2) -> 3
Nejvyšší přípustná hodnota celého čísla ve zdrojovém kódu €ASM je
0xFFFF_FFFF_FFFF_FFFF
jako bezznaménková, anebo -> 18_446_744_073_709_551_6150x7FFF_FFFF_FFFF_FFFF
jako znaménková.
Přetečení během překladu je ignorováno u operací Addition, Subtraction a Shift Logical.
Chyba se ohlásí, pokud dojde k přetečení během Multiplication
a Shift Arithmetic Left, nebo pokud dojde k dělení nulou (division-by-zero)
během Division nebo Modulo.
Toto maximum nelze překročit ani u mezivýsledků, jako
-> 9_223_372_036_854_775_808 0x7FFF_FFFF_FFFF_FFFF * 2 / 2
(€ASM hlásí chybu). Nicméně přeskupený kód
0x7FFF_FFFF_FFFF_FFFFF * (2 / 2)
se přeloží dobře.
Přetečení není hlášeno během následujících číselných operací:
|00000000:0E00000000000000 | DQ 2 + 3 * 4 ; Výsledek je 14. |00000008:0200000000000000 | DQ 0xFFFF_FFFF_FFFF_FFF9 + 0x0000_0000_0000_0009 ; Výsledek je 2. |00000010:0200000000000000 | DQ -7 + 9 ; Výsledek je 2 (0xFFFF_FFFF_FFFF_FFF9 + 0x0000_0000_0000_0009). |00000018:0200010000000000 | DQ 0xFFF9 + 0x0009 ; Výsledek je 65538 (0x0000_0000_0000_FFF9 + 0x0000_0000_0000_0009). |00000020: |€ASM kalkuluje s celočíselným ořezaným dělením a s [Modulo] operací během překladu stejně jako strojová instrukce IDIV.
Než se uplatní znaménkové dělení, dělenec i dělitel jsou nejprve interně převedeny
na pozitivní čísla. Teprve pak, po vydělení kladných čísel, je výsledek převeden na negativní,
pokud jeden z operandů (ale ne oba) byly negativní.
Zbytek po dělení se znaménky je převeden na negativní pouze pokud divident byl negativní.
Posuvy nejsou komutativní. Operand na levé straně je považován za 64bitový integer a jeho hodnota bude posunuta vlevo či vpravo o počet bitů určený operandem na pravé straně.
Posuvy mají vyšší prioritu oproti jiným aritmetickým operacím, neboť korespondují
s výpočtem mocnin dvou a ne s násobením či dělením.
Tak například 1 << 7
je ekvivalentní 1 * 27
.
NASM vyhodnocuje výraz4 + 3 << 2
jako(4 + 3) << 2
, zatímco €ASM jako->284 + (3 << 2)
.->16
Bity vstupující do nejméně významného bitu (LSb) během operací Shift Left jsou vždy 0. Bity vstupující do nejvýznamnějšího bitu (MSb) během operací Shift Right jsou buď 0 (v případě Shift Logical Right), nebo kopírují jeho předchozí hodnotu (v případě Shift Arithmetic Right), zachovávajíce tak znaménko operandu.
Bity opouštějící LSb během Shift Right jsou zahazovány. Bity opouštějící MSb během Shift Left jsou také zahazovány, avšak €ASM ohlásí chybu overflow E6311, pokud se změnilo znaménko (udržované v MSb) výsledné hodnoty. Citlivost na přetečení (overflow) v asm-time je tedy jediný rozdíl mezi operacemi Shift Arithmetic Left a Shift Logical Left.
Operand vpravo od operátoru Shift může být libovolné číslo, nicméně pokud je větší než 64,
výsledkem posuvu je 0 s jednou výjimkou:
negativní číslo posunuté aritmeticky vpravo o víc než 64 bitů dává výsledek
0xFFFF_FFFF_FFFF_FFFF
.-> -1
Posun o 0 bitů nedělá nic. Posuv o negativní číslo jen obrací směr posuvu zprava doleva a obráceně.
Operace rotace během asm-time nejsou podporovány.
|00000000:0000010000000000 | DQ 1 << 16 ; Výsledek je 65536. |00000008:F4FFFFFFFFFFFFFF | DQ -3 #<< 2 ; Výsledek je -12. |00000010:8078675645342312 | DQ 0x1122_3344_5566_7788 << 4 ; Výsledek je 0x1223_3445_5667_7880. |00000018:98A9BACBDCEDFE0F | DQ 0xFFEE_DDCC_BBAA_9988 >> 4 ; Výsledek je 0x0FFE_EDDC_CBBA_A998. |00000020:98A9BACBDCEDFEFF | DQ 0xFFEE_DDCC_BBAA_9988 #>> 4 ; Výsledek je 0xFFFE_EDDC_CBBA_A998. |00000028:0000000000000000 | DQ 0x8000_0000_0000_0000 << 1 ; Výsledek je 0x0000_0000_0000_0000. |00000030: | DQ 0x8000_0000_0000_0000 #<< 1 ; Přetečení, MSb by se změnilo. |### E6311 ShiftArithmeticLeft 64bit overflow in "0x8000_0000_0000_0000 #<< 1". |00000030: |Bitové operace NOT, AND, OR, XOR provádějí logické operace s celým operandem bit po bitu.
|0000:FA | DB ~ 5 ; ~ 0000_0101b je 1111_1010b což je -6. |0001:04 | DB 5 & 12 ; 0000_0101b & 0000_1100b je 0000_0100b což je 4. |0002:0D | DB 5 | 12 ; 0000_0101b | 0000_1100b je 0000_1101b což je 13. |0003:09 | DB 5 ^ 12 ; 0000_0101b ^ 0000_1100b je 0000_1001b což je 9.Logické operace NOT, AND, OR, XOR umějí operovat s čísly stejně jako s booleovskými hodnotami.
Každý operand, interně uložený jako nenulové 64bitové číslo, je převeden na booleovské
pravda (0xFFFF_FFFF_FFFF_FFFF
) před vlastní logickou operací.
Operand s hodnotou 0 je považován za nepravda.
Číselné výrazy operují s numerickými hodnotami, jako 1, 0x23, '4567'
nebo se symboly reprezentujícími takové skalární hodnoty, jako NumericSymbolTen EQU 10
.
Na druhé straně, většina symbolů ve skutečném programu reprezentuje hodnotu adresy ukazující na
určitou pozici v programovém kódu.
Zatímco prosté číslo (skalár) je interně uloženo v €ASM v osmi bajtech, adresa potřebuje přídavný prostor k udržování informace, do kterého segmentu patří.
Představte si, že řídíte automobil a minuli jste na dálnici kilometrovník označený 123. V tom vám telefonují vaši přátelé, že právě minuli kilometrovník 97. Jaká je vzdálenost mezi vašimi auty? Odpověď je prostá jako odečítání dvou čísel pouze pokud obě auta jedou po téže dálnici.
Operace prováděné s adresami jsou omezeny. Nemohou být násobeny, děleny, posouvány. Jsou povoleny pouze dva druhy operací s adresami:
Paměťové proměnné jsou adresovány ofsetem proti prvnímu bajtu segmentu, kterému se říká displacement. Mohou být také doplněny za běhu obsahem jednoho nebo dvou registrů. Takovému zápisu se říká registrový výraz.
Na rozdíl od instrukcí s číselnou hodnotou vestavěnou do instrukčního kódu, jako třeba
ADD EAX,1234
, instrukce ukládající nebo čtoucí data z paměti musí mít celý operand uzavřený v hranatých závorkách [ ].
Například ADD EAX,[1234]
, kde 1234 je ofset proměnné typu DWORD v datovém segmentu, odkud bude načtena.
MASM dovoluje vynechat hranaté závorky i v případě, že je operandem proměnná v paměti, např.ADD EAX,Something
. Ubohý čtenář programu psaného v MASM pak musí vyhledat definici proměnné, aby zjistil, zda byla definována v paměti (Something DD 1
) anebo jako konstanta (Something EQU 1
). Novější asemblery naštěstí tento nedostatek napravují.
Adresní výraz může být doprovázen názvy registrů, stává se z něj registrový výraz.
Kompletní adresní výraz je definován schématem
segment: base + scale * index + displacement
kde
segment je segmentový registr CS, DS, ES, SS, FS, GS
,
base je BX, BP
v 16bitovém adresním módu, nebo
EAX, EBX, ECX, EDX, EBP, ESP, ESI, EDI, R8D..R15D
v 32bitovém adresním módu, nebo
RAX, RBX, RCX, RDX, RBP, RSP, RSI, RDI, R8..R15
v 64bitovém adresním módu,
scale je numerický výraz vyhodnocený na prosté číslo 0, 1, 2, 4 or 8
,
index je SI, DI
v 16bitovém adresním módu, nebo
EAX, EBX, ECX, EDX, EBP, ESI, EDI, R8D..R15D
v 32bitovém adresním módu, nebo
RAX, RBX, RCX, RDX, RBP, RSI, RDI, R8..R15
v 64bitovém adresním módu,
displacement je adresa nebo číselný výraz s šířkou nepřesahující adresní mód (16, 32, 64).
Jiné asemblery povolují odlišnou syntaxi adresace paměti, např.,
MOV EAX,Displ[ESI],
MOV EAX,dword ptr [Displ+ESI],
MOV EAX,Displ+[4*ESI].
MOV EAX,Displ+4*[ESI]+[EBX]
EuroAssembler vyžaduje, aby byl celý operand uzavřen do hranatých závorek:MOV EAX,[Disp+4*ESI+EBX]
.
Pořadí komponent v registrovém výrazu je libovolné.
Kterákoli část může být vynechána.
Měřítko (scale) není povoleno v 16bitovém adresním módu a nelze je použít, pokud není specifikován indexregistr.
ESP a RSP nemohou být použity jako indexregistr (nelze je škálovat).
Adresní módy různých šířek nelze kombinovat, např.
.
[EBX+SI]
16bitový adresní mód není dostupný v 64bitovém CPU módu.
16bitový adresní mód v 16bitovém a 32bitovém segmentu | |
---|---|
baseregistr | BX SS:BP |
indexregistr | SI DI |
displacement | 16bitový integer se znaménkem, znaménkově roztažený na šířku segmentu |
32bitový adresní mód v 16bitovém a 32bitovém segmentu | |
baseregister | EAX EBX ECX EDX ESI EDI SS:EBP SS:ESP |
indexregister | EAX EBX ECX EDX ESI EDI EBP |
displacement | 32bitový integer se znaménkem, znaménkově roztažený nebo oříznutý na šířku segmentu |
32bitový adresní mód v 64bitovém segmentu | |
baseregister | EAX EBX ECX EDX ESI EDI SS:EBP SS:ESP R8D..R15D |
indexregister | EAX EBX ECX EDX ESI EDI EBP R8D..R15D |
displacement | 32bitový integer se znaménkem, znaménkově rozšířený na šířku segmentu |
64bitový adresní mód v 64bitovém segmentu | |
baseregister | RAX RBX RCX RDX RSI RDI SS:RBP SS:RSP R8..R15 |
indexregister | RAX RBX RCX RDX RSI RDI RBP R8..R15 |
displacement | 32bitový integer se znaménkem, znaménkově rozšířený na šířku segmentu |
MOFFS adresní mód v 16bitovém, 32bitovém a 64bitovém segmentu | |
baseregister | žádný |
indexregister | žádný |
displacement | integer bez znaménka v šířce segmentu (16|32|64 bitů) |
Není-li segmentový registr explicitně specifikován, použije se k adresaci defaultní segment.
Pokud byl BP, EBP, RBP, ESP nebo RSP použit jako a baseregistr, defaultní segment je SS
,
jinak DS
.
Nedefaultní segmentový registr může být být specifikován buď jako explicitní
instrukční prefix SEGCS SEGDS SEGES SEGSS SEGFS SEGGS
,
anebo jako segmentový registr, který se stává součástí registrového výrazu.
Segmentový registr může být ve výrazu oddělen buď dvojtečkou :
(segmentovým separátorem) nebo znaménkem plus + (indexovací operátor):
Existuje drobný rozdíl mezi implicitním a explicitním předepsáním segmentu: pokud požaduje stejný segmentový registr, který je zároveň defaultní, €ASM emituje jeho prefix pouze byl-li uveden explicitně v prefixovém poli instrukce:
|0000:8B04 | MOV AX,[SI] |0002:8B04 | MOV AX,[DS:SI] |0004:3E8B04 | SEGDS: MOV AX,[SI] |0007:3E8B04 | SEGDS: MOV AX,[DS:SI]Viz také testy t3021, t3022, t3023 pro další příklady.
V registrových výrazech, kdy není použito škálování a není tedy jasné, který ze dvou registrů
je použit v roli indexregistru, €ASM považuje dříve uvedený (levostranný) registr za bázový.
Takže ve výrazu [ESI+EBP]
je ESI bázový registr a implicitní segment je DS,
zatímco v [EBP+ESI]
je bázový registr EBP a implicitní segment je SS.
Naštěstí se rolemi implicitních segmentů nemusíme trápit ve 32bitovém a 64bitovém chráněném paměťovém modelu FLAT, kdy jsou oba registry SS a DS naplněny stejným segmentovým deskriptorem.
Třebaže operátory * nebo + v registrovém výrazu vypadají jako běžné násobení či sčítání, specifikují zcela jinou operaci Scaling a Indexing, pokud jsou aplikovány na registr. Samotné násobení a sčítání se provádí až za běhu procesoru.
Operace Indexing má nižší prioritu než odpovídající Multiplication. Tudíž výraz
[EBX + 5 + ESI * 2 * 2]
se vyhodnotí jako[EBX + 5 + ESI * (2 * 2)]
.->[EBX + 5 + ESI * 4]
Datový výraz specifikuje statická data definovaná pseudoinstrukcí
D nebo pomocí literálů.
Formát datových výrazů je
duplikátor * typ hodnota, kde
duplikátor je nezáporné celé číslo, typ je primitivní
datový typ ve plné (BYTE UNICHAR WORD DWORD QWORD TBYTE OWORD YWORD ZWORD INSTR
)
nebo zkrácené (B U W D Q T S O Y Z I
) notaci, anebo jméno struktury.
Nepovinná hodnota určuje obsah datového pole, který je opakován
duplikátor-krát.
Duplikace není komutativní operace; duplikátor musí být na levé straně
duplikačního operátoru *.
Výchozí duplikační hodnota je 1 (kdy data nejsou duplikována).
Vnořené duplikace nejsou v €ASM podporovány.
Priorita duplikace je velmi nízká, datový výraz
2 + 3 * B 4
se vyhodnotí jako pět bajtů, z nichž každý obsahuje hodnotu 4.
Příklad:
D 3 * BYTE ; Deklaruje tři bajty s neinicializovaným obsahem. D W 0x5 ; Deklaruje slovo s obsahem 5. D 2 * U "some text" ; Deklaruje řetězec v Unicode (UTF-16) obsahující "some textsome text". D 3 * MyStruc ; Deklaruje tři instance strukturované proměnné MyStruc.
Viz také pseudoinstrukce D a testy t2480, t2481, t2482 pro více příkladů.
Následující výrazy nejsou počítány matematickým evaluátorem výrazů; vyhodnocují se již při čtení a parsování zdrojového textu.
Tečka . spojující dva identifikátory z nich učiní
kvalifikované jméno (FQN),
které vypadá jako identifikátor jmenného prostoru následovaný lokáním jménem.
FQN je nelokální, nikdy nezačíná tečkou.
Je-li například lokální symbol .bar
deklarovaný v proceduře nebo struktuře
Foo
, €ASM jej považuje za symbol s FQN názvem Foo.bar
.
Jmenný prostor (namespace) může být rovněž lokální (začínat tečkou), takže se operátor členství může vnořovat.
Rozsah (range) je definováno jako dva číselné výrazy oddělené operátorem rozsahu, což je .. (dvě tečky) a reprezentuje souvislou řadu celých čísel mezi těmito hodnotami, včetně prvé i poslední.
Operátor rozsahu má vlastnost zvanou sklon (slope), která může být negativní, nulová nebo pozitivní. Sklon je definován jako znaménko rozdílu mezi pravou a levou hodnotou rozsahu. Příklady:
0 .. 15 ; Rozsah představuje šestnáct čísel od 0 do 15; sklon je pozitivní. -5 .. -4 ; Rozsah představuje hodnoty -5 a -4; sklon je pozitivní. 3 .. 4 - 1 ; Rozsah představuje jednu hodnotu 3; sklon je nulový. 2..-2 ; Rozsah představuje pět hodnot 2, 1, 0, -1, -2; sklon je negativní.
Substring je operace vracející část vstupního textu. Operátorem substring je rozsah uzavřený v hranatých závorkách []. Text je považován za sekvenci 8bitoých znaků (bajtů) a rozsah určuje, které z nich budou použity.
%Sample1 %SET ABCDEFGH ; Preprocesní %proměnná %Sample1 nyní obsahuje 8 znaků.
DB "%Sample1[3..5]" ; Toto se ve skutečnosti přeloží jako DB "CDE"
Operace Sublist se podobá operaci Substring s tím rozdílem, že se používají složené závorky {} a že považuje vstupní text za pole čárkou oddělených položek (pokud dochází k expanzi %proměnné), nebo za sekvenci fyzických řádků (v případě inkluze souboru).
INCLUDE "MySource.asm"{1..10} ; Vlož prvních deset řádků souboru "MySource.asm".
Společné vlastnosti suboperací Substring a Sublist:
Suboperátor musí být připojen bez mezer.
Suboperaci lze připojit ve čtyřech případech:
%MyVar[2..8]
INCLUDEBIN "tada.wav"[44..%&]
euroasm.exespuštěn z příkazového řádku, např.
euroasm "MySource1.asm"{0..24}
BootSec PROGRAM OutFile="boot.sec"[0x7C01..0x7E00]
.Při aplikaci na jméno souboru toto musí být uvedeno v dvojitých uvozovkách.
%&
.Pořadové číslo posledního znaku|položky|řádku je přiřazeno EuroAssemblerem do automatické preprocesní %proměnné nazvané %&. Tato %proměnná je platná pouze uvnitř suboperace (uvnitř hranatých nebo složených závorek).
Taky můžete použít pseudoinstrukci %SETS k získání počtu znaků přiřazených %proměnné, a pseudoinstrukci %SETL k získání počtu položek v ní obsažených (délka pole).
Můžete použít atributový operátor FILESIZE# k získání počtu znaků v souboru v asm-time.
V operaci Substring hodnota automatické %proměnné %&
určuje počet znaků přiřazených substringované %proměnné
nebo (v případě suboperace souboru) určuje velikost souboru v bajtech.
V operaci Sublist reprezentuje pořadové číslo poslední neprázdné položky
v %proměnné, nebo (v případě suboperace souboru) určuje počet fyzických řádků v souboru.
Suboperovaný název souboru musí být uzavřen v uvozovkách i když jeho jméno neobsahuje mezery. Otevírací závorka musí bezprostředně následovat vstupní text (jméno %proměnné anebo ukončující závorku souborového jména). Mezi %proměnnou a levou závorkou suboperace nemohou být žádné mezery.
Suboperace jsou velmi tolerantní k hodnotám rozsahu. Bez varování akceptuje neexistující znak či položku, např. je-li některý člen rozsahu nulový nebo záporný. Rozsah s negativním sklonem prostě nevrací nic. Rozsah s nulovým sklonem vrací jediný znak|položku|řádek, pokud je index mezi 1 a %&, jinak nevrací nic.
|4142434445464748 |%Sample %SET ABCDEFGH ; %Proměnná %Sample nyní obsahuje 8 znaků. |0000:4142434445 | DB "%Sample[-3..5]" ; DB "ABCDE" |0005:434445464748 | DB "%Sample[ 3..99]" ; DB "CDEFGH" |000B:43 | DB "%Sample[ 3..3]" ; DB "C" |000C: | DB "%Sample[5..3]" ; DB "" |000C:4142434445464748205B352E2E335D | DB "%Sample [5..3]" ; DB "ABCDEFGH [5..3]" ; Toto není suboperace.Suboperační rozsah sestává ze tří komponent:
Pokud bude některá komponenta chybět, nahradí se výchozí hodnotou.
Defaultní minimální index je 1. Defaultní maximální index je %&.
|4142434445464748 |%Sample %SET ABCDEFGH ; Preprocesní %proměnná %Sample nyní obsahuje 8 znaků.
|0000:4142434445 | DB "%Sample[..5]" ; -> DB "%Sample[1..5]" -> DB "ABCDE"
|0005:434445464748 | DB "%Sample[3..]" ; -> DB "%Sample[3..8]" -> DB "CDEFGH"
|000B:4142434445464748 | DB "%Sample[..]" ; -> DB "%Sample[1..8]" -> DB "ABCDEFGH"
|0013:4142434445464748 | DB "%Sample[]" ; -> DB "%Sample[1..8]" -> DB "ABCDEFGH"
Všechny následující zápisy jsou totožné co se týká expanze %variable:
%variable %variable[1..%&] %variable[..%&] %variable[1..] %variable[..] %variable[] %variable{1..%&} %variable{..%&} %variable{1..} %variable{..} %variable{}
Poslední řádek předchozího příkladu je užitečný, potřebujeme-li připojit nějaký text hned za %proměnnou,
např. 123
k jejímu obsahu.
Nemůžeme psát %proměnná123
, neboť připojení číslic by změnilo jméno původní %proměnné.
Řešením je použití prázdné suboperace, která nemění obsah %proměnné, ale odděluje její jméno
od připojeného textu: %proměnná[]123
nebo %proměnná{}123
.
Pokud rozsah uvnitř závorek obsahuje pouze jedno číslo bez operátoru rozsahu,
považuje se současně za minimální i maximální index a bude expandován právě jeden znak|položka|řádek:
%Sample1[3]
.-> %Sample[3..3] -> C
Suboperace se mohou řetězit. Řetěz se zpracovává zleva doprava. Příklad: |4142432C4445462C2C4748492C4A4B4C |%Sample %SET ABC,DEF,,GHI,JKL ; %& je nyní 16 v %Sample[%&] a 5 v %Sample{%&}. |0000:4A4B | DB "%Sample{4..5}[2..6]{2}" ; DB "JK"
První sublist předchozího příkladu bere položky č. 4 a 5,
což dává seznam dvou položek GHI,JKL
. Následující substring extrahuje znaky
od druhého do šestého z tohoto sublistu. což dává HI,JK
.
Poslední operace sublist expanduje druhou položku, což jsou znaky JK
.
Suboperace se mohou vnořovat. Vnitřní rozsah je vyhodnocuje před vnějším.:
|31323334353637383930 |%Sample %SET 1234567890
|0000:3233343536 | DB "%Sample[2..%Sample[6]]" ; -> DB "%Sample[2..6]" -> DB "23456"
Pro každou emitující instrukci generuje asembler data nebo strojový kód, který bude nakonec vložen do výstupního souboru. Naštěstí nemusíme psát celý program v přesném pořadí vyžadovaném výstupním formátem. Generovaná data a kód jsou rozdělovány do svých výstupních sekcí. Přepnutí do jiné sekce je prosté: stačí uvést v poli návěstí jméno sekce v hranatých závorkách [ ].
Předstvte si, že jako programátor diktujete program své sekretářce (EuroAssembleru). Nadiktovali jste několik strojových instrukcí, které sekretářka zapsala na list papíru nazvaný[TEXT]
. Pak se rozhodnete diktovat jiný druh dat. Sekretářka vezmi jiný list, označí jej[DATA]
a začne psát tam. Později, až budete diktovat další strojové instrukce, sekretářka vezme list označený[TEXT]
a pokračuje v psaní tam, kde minule skončila.
Můžete otevírat nové listy a podle libosti mezi nimi přeskakovat. Až diktát skončí, použité listy budou secvaknuty dohromaty (slinkovány).
V EuroAssembleru se používá termín sekce pro pojmenovaný úsek segmentu charakterizovaný počáteční a koncovou adresou. Každý segment má jednu nebo více sekcí. Ve výchozím stavu má segment jen jednu sekci se shodným jménem, která vznikla při definici segmentu.
Architektura Intelu rozděluje paměť na segmenty ovládané segmentovými registry. Segment je v €ASM definován pseudoinstrukcí SEGMENT.
Za úsvitu počítačového věku programátoři vyžadovali více paměti, než pouhých 256 bajtů nebo 64 kilobajtů adresovatelných 8bitovými a 16bitovými registry. V před32bitových časech mohli designéři u Intelu zvolit spojení dvou 16bitových registrů, jakoDX:AX
neboSI:BX
k získání nepředstavitelného adresového prostoru 4 GB, avšak neučinili tak. Místo toho zavedli 16bitové segmentové registry specializované podle účelu adresované paměti: registr CS pro strojový kód, DS pro data, SS pro zásobník, ES pro extra dočasné použití.
Segmentové registry jsou použity k adresování 16bajtových kusů paměti zvaných paragrafy (neboli octonary word, OWORD). Lineární adresa v reálném módu CPU je počítána jako součetPoužívání segmentových registrů k adresaci 16bajtových paragrafů poskytlo 1 MB paměti adresovatelné každým segmentovým registem, což se tenkrát jevilo jako dostatečné pro každého.
- 16bitového nebo 32bitového ofsetu, a
- paragrafové adresy získané z momentálně platného segmentového registru a vynásobené 16.
Obsah segmentového registru v reálném módu procesoru reprezentuje
paragrafovou adresu segmentu.
Obsah segmentového registru v chráněném (protected) módu reprezentuje
index do tabulky deskriptorů, která udržuje navíc přídavné informace o segmentu
vedle jeho adresy a velikostního limitu: šířku a přístupová práva.
Tyto přídavné informace jsou fixní v reálném módu:
- segmentová počáteční adresa je určena jeho obsahem vynásobeným 16
- velikostní limit je 64 KB v 16bitovém adresním módu
- přístupová práva jsou všechno je dovoleno
- šířka segmentu je 16 bitů, avšak je povoleno i používání 32bitového ofsetu na CPU 386 a novějších.
Segment v run-time je souvislý blok paměti adresovaný obsahem jednoho segmentového registru.
Segment v link-time je pojmenovaná část objektového souboru, která může být spojena se segmenty stejného jména z jiných linkovatelných souborů.
V terminologii [MS_PECOFF] je linkovatelný segment nazýván section. Myslím, že termín segment je vhodnější, neboť COFF "sekce" se rozlišují přístupovými právy definovanými různými segmentovými registry, tudíž i různými segmentovými deskriptory.
V našem přirovnání segment-dálnice jsou segmenty v protected módu jako souběžné pruhy dálnice, takže mohou sdílet tytéž kilometrovníky (ofsety), avšak každý pruh je určen pro odlišný typ vozidel.
Segment ve write-time je část zdrojového kódu začínající povelem přepnutí sekce, a který končí přepnutím na jinou sekci nebo koncem programu.
V €ASM není instrukce ENDS (end-of-segment). Nelze říci tato část zdrojového kódu nepatří žádnému segmentu. Už když začínáte psát první instrukci svého programu, tak patří výchozímu (obálkovému) programu, a každý program implicitně definuje počáteční segmenty. Nicméně pokud je definována struktura nebo numerická konstanta, není důležité, který segment je momentálně platný, neboť struktury a skalární symboly nepatří žádnému segmentu.
Segmenty a sekce nemusejí být souvislé. Ve skutečnosti je nesouvislost jejich hlavním raison d'être. Dovoluje udržovat data ve zdrojovém textu poblíž kódu, který s nimi manipuluje, což přispívá ke srozumitelnosti programu.
Nejsou-li programové segmenty příliš velké, mohou být spojeny do segmentových grup (GROUP). Celá grupa je pak adresována tímtéž segmentovým registrem. Grupa je definována pseudoinstrukcí GROUP.
Příklad definice grupy: [DGRP] GROUP [DATA],[STRINGS]
definuje grupu s názvem [DGRP]
do níž patří segmenty
[DATA]
a [STRINGS]
.
Mezi segmentem a jeho sekcemi je v EuroAssembleru podobný vztah jako mezi grupou a jejími segmenty.
Jakmile je segment definován (pseudoinstrukcí SEGMENT)), automaticky je v něm vytvořena sekce se stejným jménem. tzv. základní sekce. Případné další sekce mohou být vytvořeny později, povelem s názvem této další sekce v poli návěstí. V €ASM neexistuje direktiva SECTION.
Vlastnosti sekce (class=, purpose=, combine=, align=) jsou zděděny ze segmentu, do něhož patří.
Výjimkou je zarovnání (alignment) u speciálních sekcí pro literály [@LT64] .. [@LT1], [@RT0], [@RT1]..
;
jejich zarovnání je odvozeno od dat, která se do nich zapisují.
Každý segment má jednu nebo více sekcí. Každá sekce patří do právě jednoho segmentu. Asembler během překladu považuje všechny segmenty za začínající na virtuální adrese 0. Na konci každého průchodu jsou sekce slinkovány do svých segmentů, takže mohou začínat na vyšší adrese, kde skončila předešlá sekce. Nicméně při prvním průchodu ještě není známa velikost sekcí, takže i zde jsou sekce považovány za začínající na virtuální adrese 0. Jakmile skončí poslední průchod, všechny sekce budou slinkovány, tj. jejich emitovaný obsah a relokace spojeny do základní sekce totožné se segmentem. Jakmile se dostane ke slovu linker, není si existence sekcí už vůbec vědom.
Proč bychom měli dělit segmenty na sekce? Ve skutečnosti nemusíme, vystačili bychom s jednou implicitní sekcí na segment. Na druhé straně může být výhodnější ve velkých programech seskupovat podobná data dohromady; můžeme chtít zvláštní sekce pro DWORD proměnné, pro čísla s pohyblivou čárkou, pro textové řetězce. Může to mimo jiné ušetřit několik bajtů výplně pro zarovnávání, které by jinak bylo nutné při mísení proměnných s různou velikostí. Takto jsou organizovány také sekce pro literály.
Další příležitostí, kdy se hodí sekce, je rychlé čtení dat z read-only "databází" definovaných staticky v datovém segmentu.
Databázi si můžeme mentálně vizualizovat jako tabulku s mnoha řádky a se sloupci obsahujícími datové položky v konstantní velikosti. K rychlému výběru určitého řádku databáze je užitečné emitovat všechny položky z jednoho sloupce sekvenčně do jejich zvláštní sekce, jednu po druhé. Data každého sloupce budou mít svou vlastní sekci. Šířka kolon by měla být doplněna na 1, 2, 4 nebo 8 bajtů, takže položky pak mohou být hledány jedinou strojovou instrukcíREPNE SCAS
. Jakmile bude položka nalezena, rozdíl mezi obsahem registru rDI a začátku sekce identifikuje její databázový řádek. Další položky z téhož řádku se pak vypočtou z této identifikace.
Tato metoda byla použita u vzorového projektu EuroConvertor a také v samotném EuroAssembleru, kde přiřazuje adresy zpracovávatele instrukcí (handleru) každé ze dvou tisíc mnemonických zkratek, viz DistLookupIiSetup.
Každá grupa obsahuje jeden nebo více segmentů Každý segment patří do právě jedné grupy (i když do ní nebyl vložen explicitně programátorem, během linkování bude vytvořena grupa se shodným názvem kvůli adresaci). Kdykoli se sestavuje program ve spustitelném formátu, grupy budou fyzicky spojeny do paměťového obrazu – image. Zaváděč spustitelného programu v reálném módu si není vědom existence grup ani segmentů.
Když €ASM začíná překládat program, vytvoří nejprve implicitní skupiny a grupy. Jejich názvy závisí na zvoleném programovém formátu:
FORMAT= | Názvy segmentů |
---|---|
BIN | [BIN] |
BOOT | [BOOT] |
COM | [COM] |
OMF | MZ | [CODE],[RODATA],[DATA],[BSS],[STACK] |
COFF | PE | DLL | ELF | ELFX | ELFSO | [.text],[.rodata],[.data],[.bss] |
Nejste-li s implicitními názvy spokojeni, mohou být na začátku programu předefinovány anebo vytvořena sada nových segmentů s odlišnými názvy. Nepoužité segmenty (do nichž nebylo nic emitováno) nebudou zařazeny do výsledného souboru a mohou být ignorovány.
Jakmile kompilace skončí a všechny segmenty z linkovaných modulů byly zkombinovány do hlavního programu, €ASM se dívá na segmenty, které nejsou v žádné grupě a vytvoří pro ně implicitní grupu, přičemž bere do úvahy paměťový model:
Modely s jedním kódovým segmentem (TINY, SMALL, COMPACT) linkují všechen kód do jediné grupy, bez ohledu na počet kódových segmentů, které mohly být v programu definovány.
Multikódové modely (MEDIUM, LARGE) ponechávají každý kódový segment v jeho vlastní grupě,
takže mezisegmentové skoky, volání a návraty by měly mít JMP|CALL|RET DIST=FAR
.
Podobně jednodatové modely (TINY, SMALL, MEDIUM) předpokládají, že se inicializovaná i neinicializovaná data vejdou do jedné grupy nepřesahující 64 KB, takže linker €ASM přiřazuje všechny datové a bss segmenty této implicitní grupě a register DS není třeba měnit během přístupu k datům z různých segmentů.
Jména grup, segmentů a sekcí jsou vždy obklopena hranatými závorkami.
Začíná-li jméno tečkou, na rozdíl od symbolů před něj není interně připojován jmenný prostor (namespace). Jména grup, segmentů a sekcí jsou vždy nelokální.
Počet znaků v jejich názvu je v principu libovolný, avšak může být omezen výstupním formátem programu. V modulech OMF délka názvu segmentu nebo grupy nesmí přesáhnout 255 znaků. Ve spustitelném formátu PE je jméno hlavičky segmentu ořezáno na 8 znaků.
€ASM považuje všechna jména segmentů za citlivá na velikost písmen. Potřebujete-li linkovat váš segment s objektovým modulem generovaným jiným kompilátorem, jenž konvertuje segmentové názvy na velká písmena nebo je mění připojováním podtržítek, měli byste tomu přizpůsobit svou konvenci pojmenovávání.
Segmentové jméno by mělo být unikátní, nemůžete definovat dva segmenty s identickým názvem v jednom programu, s výjimkou implicitních segmentů, pokud dosud nebyly použity (nic do nich nebylo emitováno). Je však možné definovat segmenty se stejným názvem v jiných programech a spojovat je v link-time. Takové segmenty pak budou spojovány v souladu s jejich vlastností COMBINE=.
Názvy sekcí nemohou být duplikovány už z principu. Když se jméno sekce vyskytne v programu podruhé, pouze přepne emitování do této sekce namísto vytváření nové.
Jména literálních sekcí začínají znaky @LT nebo @RT, raději se vyhněte vytváření sekcí začínající touto kombinací.
Se segmenty s dolarem $ v názvu se zachází poněkud odlišně: pokud souhlasí znaky na levé straně od tohoto znaku $, všechny takové segmenty budou slinkovány jeden za druhým v abecedním pořadí.
Existují konvence, jak jsou "sekce" pojmenovávány v modulech COFF, kterým byste se měli přizpůsobit, chcete-li úspěšně linkovat programy v €ASM s moduly vytvořenými jinými kompilátory.
Pokud €ASM vytváří chráněné spustitelné 32bitové nebo 64bitové programy ELFX nebo PE, nemusíme se vůbec zabývat segmenty, grupami nebo zásobníkem. Všechny segmentové registry již OS Linux nebo Windows naplnil a zásobník byl nastaven automaticky.
Při spouštění programu s formátem COM operační systém DOS naplnil registry CS=DS=SS=ES paragrafovou adresou struktury PSP, nastavil IP=100h a SP na konec zásobníkového segmentu, obvykle 0FFFEh. Zde se opět nemusíme o segmentové registry starat.
Jakmile je 16bitový spustitelný program (FORMAT=MZ) připraven v paměti ke startu, jeho segmentové registry již byly nastaveny zaváděčem DOSu. CS:IP byl nastaven na vstupní bod programu (ENTRY=), SS:SP byl nastaven na vrchol zásobníku, avšak registry DS a ES byly nastaveny na PSP, což není náš datový segment. To bude třeba v kódu programu napravit.
V architektuře Intel není instrukce pro nastavení segmentového registru přímým číslem, takže se to obchází před obecný registr nebo zásobník:
; Naplnění segmentového reigstru paragrafovou adresou segmentu [DATA] ; použitím obecného registru (což je rychlejší): MOV AX, PARA# [DATA] MOV DS,AX ; nebo použitím strojového zásobníku (což je kratší): PUSH PARA# [DATA] POP DS
Je na programátorovi, aby naplnil segmentový registr adresou jiného segmentu, kdykoli mají být data z něho použita. €ASM nedělá žádné předpoklady ohledně obsahu segmentových registrů; neeistuje v něm direktiva ASSUME, USING ani WRT.
Pořadí segmentů ve výstupním programu je obecně založeno na těchto parametrech:
Pořadí sekcí
Na konci každého průchodu jsou sekce slinkovány do segmentů v tomto pořadí:
[@LT64], [@LT32],..[@LT1]
).[@RT0], [@RT1], [@RT2]..
).Pořadí segmentů
Segmenty se kombinují a spojují v link-time v tomto pořadí:
Segmenty v každé grupě jsou v pořadí, v jakém byly definovány ve zdrojovém kódu, nikoli jak byly deklarovány v instrukci GROUP.
Při linkování spustitelného programu je každý segment přiřazen některé grupě, přinejmenším své implicitní s identickým názvem.
Implicitní grupy segmentů jsou použity interně pro relokační účely. Programy v chráněném módu (MODEL=FLAT) se o segmentové registry příliš nestarají, takže v programech pro Linux a Windows se segmenty ani grupami nemusíme zabývat.
Název | Účel segmentu | Přístup | Velikost 32bit | 64bit |
Zarovnání v souboru 32bit | 64bit | Poznámka |
---|---|---|---|---|---|
Hlavička MZ DOS | RW | 128 | 128 | 0) 2) | ||
Program MZ stub | RW | 16 | 16 | 0) 2) | ||
Podpis PE | RW | 4 | 4 | 32 | 32 | 0) 2) | |
Hlavička souboru | R | 20 | 20 | 16 | 16 | 0) | |
"Volitelná" hlavička | R | 224 | 240 | 16 | 16 | 0) 2) | |
Hlavičky "sekcí" | R | NrOfSe*40 | 16 | 16 | 0) | |
.text | CODE | RX | FiAl|SeAl | ||
.rodata | RODATA | R | FiAl|SeAl | ||
.data | DATA | RW | FiAl|SeAl | ||
.bss | BSS | RW | FiAl|SeAl | ||
.idata | IMPORT+IAT | RWX | 16|16 | 0) 2) | |
.edata | EXPORT | RW | 16|16 | 0) 2) 5) | |
.reloc | BASERELOC | RW | 16|16 | 0) 2) | |
.rsrc | RESOURCE | RW | 16|16 | 0) | |
Tabulka symbolů | (SYMBOLS) | R | NrOfSym*18 | 16 | 16 | 0) 1) 3) |
Tabulka řetězců | (STRINGS) | 4 | 4 | 0) 1) 3) |
Názve | Účel segmentu | Přístup | Velikost 32bit | 64bit |
Zarovnání v souboru 32bit | 64bit | Poznámka |
---|---|---|---|---|---|
Hlavička souboru | R | 52 | 64 | 0) | ||
Hlavičky "programů" | R | NrOfPh*(32|56) | 16 | 8 | 0) 2) | |
Hlavičky "sekcí" | R | NrOfSe*(40|64) | 8 | 16) | 0) | |
.symtab | SYMBOLS | NrOfSym*(16|24) | 16 | 8 | ||
.hash | HASH | R | 4 | 4 | 4) | |
.strtab | STRINGS | 1 | 1 | |||
.shstrtab | STRINGS | 1 | 1 | |||
.interp | RODATA | R | 1 | 1 | 4) | |
.plt | PLT | RX | NrOfJmp*16 | 16 | 4) |
.text | CODE | RX | FiAl|SeAl | ||
.rodata | RODATA | R | FiAl|SeAl | ||
.data | DATA | RW | FiAl|SeAl | ||
.bss | BSS | RW | FiAl|SeAl | ||
.dynamic | DYNAMIC | RW | NrOfRec*(8|16) | 8 | 16 | 4) |
Poznámky:
0) Speciální struktura bez vlastní hlavičky.
1) Použito pouze v objektovém modulu.
2) Použito pouze ve spustitelném programu.
3) Použito ve spustitelném programu pokud EUROASM DEBUG=ENABLED.
4) Použito ve spustitelném programu pokud je linkován s DSO.
Přístupová práva:
R Alokuj pamět a povol čtení.
W Povol zápis.
X Povol provádění instrukcí.
FiAl|SeAl maximum z hodnot FileAlignment | SegmentAlignment.
Pseudoinstrukce %DISPLAY Sections
zapisuje do listingového souboru kompletní dosud deklarovanou mapu grup, segmentů a sekcí,
jeden objekt na jeden řádek, reprezentovaný ladicím oznámením
D1260 (grupa), D1270 (segment), D1280 (sekce).
Segment je odsazen dvěma mezerami, sekce čtyřmi mezerami.
Namísto%DISPLAY Sections
bychom mohli zrovna tak použít%DISPLAY Segment
nebo%DISPLAY Groups
, výsledek je identický. Kterýkoli povel vždy zobrazuje celou mapu grup, segmentů a sekcí.
Podobnou mapu tiskne €ASM do listingu včetně konečných virtuálních adres,
ledaže by to bylo zakázáno volbou PROGRAM LISTMAP=OFF
.
Termín distance specifikuje vzdálenost mezi dvěma adresami,
není to ale pouhý rozdíl dvou ofsetů. V €ASM tento termín reprezentuje
jednu ze tří výčtových hodnot: FAR, NEAR, SHORT
.
Distance mezi dvěma adresami je FAR, pokud obě náleží do různých segmentů, jinak je NEAR nebo SHORT. Rozdíl ofsetů je SHORT, pokud se vejde do 8bitového integeru se znaménkem, tedy -128..+127.
€ASM je 64bitový assembler, může rovněž kompilovat programy
pracující s 32 a 16 bitovými slovy.
Počet bitů, s nimiž CPU současně pracuje, se nazývá šířka
a je buď 16
, 32
nebo 64
.
Šířka je vlastnost segmentu. Některé 32bitové formáty dovolují používat segmenty různých šířek v jednom souboru. Šířka adresního a operačního módu může být měněna ad hoc instrukčními prefixy ATOGGLE, OTOGGLE.
Pseudoinstrukce PROGRAM má rovněž vlastnost WIDTH=. Ta stanovuje výchozí šířku všech segmentů programu, u nichž nebyla explicitně změněna. Šířka programu je rovněž použita k výběru formátu výstupního souboru, například zda má být PE generován jako 32bitový nebo 64bitový.
Velikost (size) je prosté nezáporné číslo určující počet bajtů v objektu (registru, paměťové proměnné, struktuře, segmentu, souboru apod.). Velikost řetězce je definována v bajtech, bez ohledu na to, zda je složen z ANSI nebo z WIDE znaků.
Velikost objektu může být zjištěna v asm-time pomocí atributového operátoru SIZE# nebo FILESIZE#.
Velikost preprocesní %proměnné může být načtena pseudoinstrukcí %SETS.
Velikost a délka prvků EuroAssembleru (identifikátorů, čísel, struktur, výrazů, počtu operandů atd.) není z principu omezena, avšak velikosti jsou interně ukládány do 32bitových znaménkových integerů, takže limit je 2_147_483_647 znaků. V praxi jsme ovšem omezeni velikostí dostupné paměti.
Tento termín se používá k počítání čárkou oddělených položek v seznamu (v poli),
např. počtu operandů v instrukci.
Kupříkladu v instrukci
VPERMI2B XMM1,XMM2,XMM3,MASK=K4,ZEROING=ON
je délka pole operandů 5.
Délka pole obsaženého v %proměnné může být získána pseudoinstrukcí %SETL.
Názvy symbolů a struktur vytvořených v programu musí být unikátní.
U větších projektů může být udržování unikátních jmen obtížné, zvláště když více lidí
pracuje souběžně na různých částech programu.
Proto programátoři mohou používat lokální identifikátory,
které musí být unikátní pouze v úseku zdrojového kódu
zvaného jmenný prostor neboli namespace.
Jmenný prostor je rozsah zdrojového souboru definovaný pomocí bloku jmenného prostoru.
V €ASM máme čtyři blokové psuedoinstrukce, které vytvářejí jmenný prostor: PROGRAM, PROC,
PROC1, STRUC
. Název souboru je zároveň názvem jmenného prostoru.
Identifikátor je lokální, když jeho jméno začíná tečkou ..
Na rozdíl od standardních symbolů znak následující po této tečce může být číslice
a není chyba, pokud následující znaky tvoří rezervovaný název.
Příklady platných lokálních identifikátorů:
.L1, .20, .AX
.
Názvy lokálních identifikátorů se v €ASM udržují interně spojeny s názvem jmenného prostoru, tvoří tak kvalifikované jméno (FQN). Na lokální symboly můžeme odkazovat tímto .lokálním jménem pouze uvnitř jejich jmenného prostoru; kvalifikovaným jménem můžeme odkazovat kdekoli v programu.
Jmenný prostor začíná na poli operace příkazu začátku bloku a končí na poli operace konce bloku. Díky tomu samotný jmenný prostor (návěstí bloku) může být rovněž lokální a jmenné prostory se mohou vnořovat.
MyProg PROGRAM ; PROGRAM zahajuje namespace MyProg. ;
;
Main PROC ; PROC zahajuje vnitřní namespace Main. ;
.10: RET ; Lokální návěstí; jeho FQN je Main.10. ;
ENDP Main ; Po ENDP jsme opět v namespace MyProg. ;
;
.Local PROC ; Jeho FQN je MyProg.Local. ;
.10: RET ; FQN tohoto návěstí je MyProg.Local.10. ;
ENDP .Local ; MyProg.Local namespace končí hned za ENDP. ;
;
ENDPROGRAM MyProg
Vedle bloku jmenného prostoru je ještě jedna příležitost, kde je jmenný prostor použit: pole operandů instrukce definující strukturovanou proměnnou dočasně přebírá jmenný prostor struktury, z níž tvoříme její instanci:
DateProg PROGRAM ; PROGRAM zahajuje namespace DateProg. ;
;
Datum STRUC ; Deklarace struktury Datum vytváří namespace Datum. ;
.day DB 0 ;
.month DB 0 ;
.year DW 0 ;
ENDSTRUC Datum ; Namespace Datum končí hned za polem ENDSTRUC ;
;
[.data] ; Jméno segmentu není lokální, namespace se zde ignoruje. ;
Birthday DS Datum, .day=1, .month=1, .year=1970 ;
;
; Předchozí instrukce definuje čtyřbajtovou paměťovou proměnnou ;
; zvanou Birthday v sekci [.data] a staticky nastavuje její členy. ;
; Během definování proměnné "Birthday" €ASM využívá vlastnosti ;
; deklarované jako Datum.day, Datum.month, Datum.year (B,B,W). ;
; Členové mohou být odkazováni jako Birthday.day, Birthday.month, Birthday.year.;
Každý symbol definovaný v programu může být odkazován svým názvem kdekoli uvnitř programu v asm-time. Náš program (objektový modul) může být také slinkován s dalšími moduly a knihovnami, které mohly rovněž využít stejná jména pro své symboly, ale to je v pořádku a žádný konflikt nenastává, neboť programy jsou kompilovány každý zvlášť. Toto je standardní chování, takové symboly mají standardní privátní viditelnost omezenou na vnitřek jejich bloku PROGRAM..ENDPROGRAM.
Začíná-li název symbolu tečkou ., viditelnost takového privátního lokálního jména je ještě užší, je limitována na nejmenší blok namespace, v němž byl symbol definován (PROC..ENDPROC, STRUC..ENDSTRUC).
Na druhé straně ale programy slinkované z několika modulů mohou potřebovat přistupovat k symbolům mimo jejich standardní privátní viditelnost, například volat vstupní bod nějaké funkce z linkované knihovny. Jména takových globálních symbolů by měla být unikátní mezi všemi linkovanými moduly.
private | Global | ||||
Standard | local | statické linkování | dynamické linkování | ||
Public | Extern | eXport | Import |
Vlastnost scope symbolu může být zjišťována v asm-time atributovým operátorem SCOPE#, který vrací ASCII hodnotu velkého písmene zkratky pro jeho viditelnost, např.
MySymbol EXTERN MOV AL,SCOPE# MySymbol ; Toto je ekvivalentní k MOV AL,'E'
Odpovídající zkratky viditelností jsou uvedeny ve výše uvedené tabulce.
Stejné zkratky 'G','S','P','E','X','I'
jsou také použity,
pokud jsou vlastnosti symbolů vypsány povelem %DISPLAY Symbols
a také v listingu při
LISTGLOBALS=ENABLED.
Viditelnost GLOBAL, PUBLIC, EXTERN, EXPORT a IMPORT může být explicitně deklarována pseudoinstrukcí s tímto jménem. Viditelnost GLOBAL dále může být deklarována implicitně, tím že se jméno symbolu zakončí pomocí dvou (nebo i více) dvojteček ::. Symbol deklarovaný jako GLOBAL bude viditelný jako PUBLIC (byl-li definován v programu) anebo je označen jako EXTERN (pokud není v programu definován).
Zjednodušenou deklarací pomocí dvou dvojteček může být označena pouze viditelnost při statickém linkování (PUBLIC, EXTERN).
Má-li být symbol exportován (píšeme-li DLL) nebo má-li být dynamicky importován z jiné DLL,
použít dvě dvojtečky nestačí a potřebujeme buď explicitní deklaraci pomocí EXPORT|IMPORT
,
anebo je vyžadováno LINK importní_knihovna
.
Word1: DW 1 ; Standardní privátní viditelnost. Word2:: DW 2 ; Public scope deklarovaná implicitně (dvěma dvojtečkami). Word3 PUBLIC ; Public scope deklarovaná explicitně. Word4 GLOBAL ; Public nebo extern scope (podle existence definice Word4 v tomto programu). Word5 GLOBAL ; Public nebo extern scope (podle existence definice Word5 v tomto programu). Word6 EXTERN ; Externní scope. Symbol Word6 nesmí být v tomto programu definován. Word4: ; Definice symbolu Word4. MOV EAX,Word5 ; Reference externího symbolu Word5. ; Scope symbolu Word1 is PRIVATE. ; Scope symbolů Word2, Word3, Word4 je PUBLIC. ; Scope symbolů Word5, Word6 je EXTERN.
Důležitou vlastností textů a čísel uložených v počítačové paměti je datový typ, což je pravidlo určující, jak se má daný objekt interpretovat. €ASM rozeznává následující typy dat:
Jméno typu | Zkratka | Velikost | Auto alignment | Šířka | Typické úložiště | Znakový řetězec | Celé číslo |
Číslo s plovoucí tečkou | Spojený vektor |
---|---|---|---|---|---|---|---|---|---|
BYTE | B | 1 | 1 | 8 | R8 | ANSI | 8bit | ||
UNICHAR | U | 2 | 2 | 16 | R16 | WIDE | |||
WORD | W | 2 | 2 | 16 | R16 | 16bit | |||
DWORD | D | 4 | 4 | 32 | R32,ST | 32bit | jednoduchá přesnost | ||
QWORD | Q | 8 | 8 | 64 | R64,ST | 64bit | dvojitá přesnost | ||
TBYTE | T | 10 | 8 | 80 | ST | rozšířená přesnost | |||
OWORD | O | 16 | 16 | 128 | XMM | 4×D | 2×Q | |||
YWORD | Y | 32 | 32 | 256 | YMM | 8×D | 4×Q | |||
ZWORD | Z | 64 | 64 | 512 | ZMM | 16×D | 8×Q |
Jméno typu | Zkratka | Velikost | Autoalign | Použití |
---|---|---|---|---|
Jméno_struktury | S | různá | Explicitní zarovnání STRUC, jinak šířka programu | structurované proměnné |
INSTR | I | různá | 1 | strojové instrukce |
Použití fundamentálních typů je často omezeno na jejich první písmeno. Datové typy v plné nebo zkratkové notaci se používají pro explicitní definici pseudoinstrukcí D, pro implicitní definici dat v literálech, jako specifikaci zarovnání ALIGN= a v instrukčních modifikátorech.
€ASM si je částečně vědom datových typů. Například při zpracování strojové instrukce
INC [MemoryVariable]
se podívá, jak byla MemoryVariable definována a podle toho vybírá příslušné zakódování (byte|word|dword).
Symbol v asembleru je alias k číslu nebo k adrese.
Číselný symbol odpovídá na otázku kolik? a adresní symbol odpovídá na dotaz kde? (na které pozici v programu).
Číselný symbol je definován pseudoinstrukcí EQU
nebo jejím aliasem =, např. Dozen EQU 12
nebo Gross = 144
.
Adresní symbol je definován, pokud se jeho jméno objeví v instrukčním poli pro návěstí.
Hodnota číselného symbolu je interně uchovávána v 8 bajtech (signed QWORD), avšak adresní symbol navíc vyžaduje přídavnou informaci o sekci, do níž náleží.
V €ASM nelze definovat numerický symbol jako návěstí jiné instrukce než EQU, nebo jako samostatné návěstí bez pole operace. Každá instrukce patří do nějaké sekce (buď explicitně definované nebo implicitně vytvořené při startu programu.
Jméno symbolu je identifikátor (písmeno nebo tečka volitelně následovaná dalšími písmeny, číslicemi a tečkami),
které není rezervovaným jménem symbolu (bez ohledu na velikost písmen).
Název symbolu může být vždy zakončen jednou nebo více dvojtečkami :,
což pomáhá jej identifikovat jako jméno symbolu.
Dvojtečka není součástí jména symbolu.
Symboly by měly mít sebevysvětlovací názvy.
Zakončení názvu každého symbolu dvojtečkou : je dobrý zvyk jak při definování, tak i při odkazování na symbol, třebaže mnoho jiných asemblerů toto nepodporuje. Je pak jednodušší copy&pastovat symbol, aniž bychom museli mazat dvojtečku na konci. Dvojtečka pomáhá asembleru i lidskému čtenáři rozeznat jméno jako symbol a chrání před chybou, pokud bychom zvolili jméno kolidující s některou z tisíců instrukčních mnemonik.
Názvy struktur, registrů (mimo segmentových) nebo názvy strojových instrukcí nikdy dvojtečkou nekončí.
Symboly a struktury mohou být referovány (použity v instrukci) ještě dříve, než jsou definovány. Přesto je ale dobrou praxí definovat číselné symboly a struktury na počátku programu, neboť dopředné reference vyžadují přídavné průchody a prodlužují čas kompilace.
Kategorie | Rezervované jméno |
---|---|
Aktuální ofset v asm-time | $ |
Názvy segmentových registrů | CS, DS, ES, FS, GS, SS |
Názvy prefixů | ATOGGLE, LOCK, OFTEN, OTOGGLE, REP, REPE, REPNE, REPNZ, REPZ, SEGCS, SEGDS, SEGES, SEGFS, SEGGS, SEGSS, SELDOM, XACQUIRE, XRELEASE |
Jméno symbolu může obsahovat tečku ., která obvykle spojuje jmenný prostor s lokálním názvem symbolu. Úvodní tečka . činí symbol lokální, jelikož jeho jméno je ve skutečnosti spojeno se současným jmenným prostorem.
Tvorba jmen symbolů kolidující se jmény registrů nebo instrukcí není doporučena. Pokud opravdu potřebujete použít některé z těchto nedoporučených jmen pro svůj symbol, musí je následovat dvojtečka, např.
Byte: DB 1 ; Definuj symbol se jménem "Byte". MOV AX,Byte: ; Naplň AX ofsetem tohoto symbolu.
V ostatních případech je zakončování jména dvojtečkou : dobrovolné, ale doporučené.
Kategorie | Nedoporučené jméno |
---|---|
Fundamentální datové typy | B, BYTE, D, DWORD, I, INSTR, O, OWORD, Q, QWORD, S, T, TBYTE, U, UNICHAR, W, WORD, Y, YWORD, Z, ZWORD |
Názvy registrů | AH, AL, AX, BH, BL, BND0, BND1, BND2, BND3, BP, BPB, BPL, BX, CH, CL, CR0, CR2, CR3, CR4, CR8, CX, DH, DI, DIB, DIL, DL, DR0, DR1, DR2, DR3, DR6, DR7, DX, EAX, EBP, EBX, ECX, EDI, EDX, ESI, ESP, K0, K1, K2, K3, K4, K5, K6, K7 MM0, MM1, MM2, MM3, MM4, MM5, MM6, MM7, R10, R10B, R10D, R10L, R10W, R11, R11B, R11D, R11L, R11W, R12, R12B, R12D, R12L, R12W, R13, R13B, R13D, R13L, R13W, R14, R14B, R14D, R14L, R14W, R15, R15B, R15D, R15L, R15W, R8, R8B, R8D, R8L, R8W, R9, R9B, R9D, R9L, R9W, RAX, RBP, RBX, RCX, RDI, RDX, RSI, RSP, SEGR6, SEGR7, SI, SIB, SIL, SP, SPB, SPL, ST0, ST1, ST2, ST3, ST4, ST5, ST6, ST7, TR3, TR4, TR5, XMM0, XMM1, XMM2, XMM3, XMM4, XMM5, XMM6, XMM7, XMM8, XMM9, XMM10, XMM11, XMM12, XMM13, XMM14, XMM15, XMM16, XMM17, XMM18, XMM19, XMM20, XMM21, XMM22, XMM23, XMM24, XMM25, XMM26, XMM27, XMM28, XMM30, XMM31 YMM0, YMM1, YMM2, YMM3, YMM4, YMM5, YMM6, YMM7, YMM8, YMM9, YMM10, YMM11, YMM12, YMM13, YMM14, YMM15, YMM16, YMM17, YMM18, YMM19, YMM20, YMM21, YMM22, YMM23, YMM24, YMM25, YMM26, YMM27, YMM28, YMM30, YMM31 ZMM0, ZMM1, ZMM2, ZMM3, ZMM4, ZMM5, ZMM6, ZMM7, ZMM8, ZMM9, ZMM10, ZMM11, ZMM12, ZMM13, ZMM14, ZMM15, ZMM16, ZMM17, ZMM18, ZMM19, ZMM20, ZMM21, ZMM22, ZMM23, ZMM24, ZMM25, ZMM26, ZMM27, ZMM28, ZMM30, ZMM31 |
Názvy pseudoinstrukcí | ALIGN, D, DB, DD, DI, DO, DQ, DS, DU, DW, DY, DZ, ENDHEAD, ENDP, ENDP1, ENDPROC, ENDPROC1, ENDPROGRAM, ENDSTRUC, EQU, EUROASM, EXTERN, GLOBAL, GROUP, HEAD, INCLUDE, INCLUDE1, INCLUDEBIN, INCLUDEHEAD, INCLUDEHEAD1, PROC, PROC1, PROGRAM, PUBLIC, SEGMENT, STRUC |
Mnemonické názvy strojových instrukcí | AAA, AAD, ... XTEST, viz IiHandlers v €ASM zdroji pro kompletní seznam. |
Číselný symbol je definován pseudoinstrukcí EQU nebo jejím aliasem =, který specifikuje prosté číslo, číselný výraz nebo jiný numerický symbol. Příklady:
BufferSize: EQU 16K WM_KEYDOWN = 0x0100 Total EQU 2*BufferSize MOV ECX,BufferSize
Použití numerického symbolu namísto čísla má své výhody:
- Je-li název symbolu vhodně vybrán, např.
BufferSize
, je samovysvětlující a nemusíme komentovat, proč jsme plnili ECX zrovna touto hodnotou16K
.- Pokud se později rozhodneme změnit
BufferSize
během vývoje programu, je snazší tak učinit pouze na jednom místě, kde je definována.
Adresní symbol je definován, když se vyskytne jako návěstí strojové instrukce nebo prefixu, jako návěstí prázdné instrukce nebo pseudoinstrukce D*, PROC, PROC1.
Příklady:[DATA] SomeValue: DD 4 [CODE] MOV EAX,[SomeValue:] StartOfLoop: CALL SomeProcedure: DEC EAX JNZ StartOfLoop:
Zatímco numerický symbol BufferSize
byl zcela definován svou hodnotou,
v případě adresního symbolu SomeValue
to není dostačující.
Instrukce MOV EAX,SomeValue
plní EAX ofsetem symbolu,
tj. vzdáleností jeho pozice od začátku jeho segmentu.
Adresní symbol je definován dvěma vlastnostmi: jeho segmentem a jeho ofsetem.
Proto se adresnímu symbolu občas říká vector nebo relativní symbol
a číselný symbol je nazýván skalár nebo absolutní symbol
nebo konstanta.
Symbol lze v EuroAssembleru vytvořit jednou z pěti metod:
- Symbol je definován, pokud se jeho jméno vyskytne v poli návěstí. Takový symbol reprezentuje adresu uvnitř segmentu a zároveň i data emitovaná touto instrukcí. Instrukce může být prázdná (pouze návěstí) nebo může deklarovat data, prefix nebo strojovou instrukci. Pseudoinstrukce PROC a PROC1 také definují symbol, avšak pseudoinstrukce PROGRAM, STRUC ani SEGMENT nikoli.
- Externí a importované symboly jsou vytvářeny pseudoinstrukcí EXTERN, IMPORT nebo GLOBAL, nebo pokud jsou odkazovány se dvěma dvojtečkami připojenými k jejich jménu. Externí symbol není v programu definován, nesmí se objevit v poli návěstí (s výjimkou pseudoinstrukce EXTERN, která určuje jeho viditelnost).
- Literální symbol je vytvořen, když se objeví v programu poprvé. Nemá explicitní jméno, ve skutečnosti je jeho název reprezentován jeho hodnotou. Například instrukční kod
LEA ESI,[=D 123]
vytváří literální symbol uložený v tabulce symbolů EuroAssembleru pod názvem=D 123
.- €ASM udržuje speciální dynamický symbol
$
pro každou sekci, který reprezentuje současnou pozici asembleru v sekci.- Symbol může být také definován pseudoinstrukcí EQU nebo jejím aliasem =. To je jediný způsob, jak definovat prostý numerický symbol.
Speciální dynamický symbol $
představuje adresu příští volné pozice
v emitovaném kódu na počátku překladu instrukce, v níž je zmíněn.
Hodnota tohoto symbolu není konstantní, ale €ASM ji mění jakmile byla instrukce přeložena na strojový kód.
Programátor může měnit současnou hodnotu $ pseudoinstrukcí EQU, to odpovídá pseudoinstrukci ORG známé z jiných asemblerů.
Viz také test t2551 nebo vzorový projekt boot16.
Několik vlastností symbolu zvaných atributy je k dispozici ke zpracování za běhu asembleru.
Jakmile je symbol definován, nabývá atributy automaticky.
Mohou být testovány přidáním operátoru atributu před jméno symbolu.
Operátor atributu je jeho definující identifikátor bezprostředně následovaný znakem #.
Objekt, jehož se operátor atributu týká, může být oddělen žádnou nebo několika mezerami a může být v závorkách.
Například SIZE#SymbolName
nebo SIZE# SymbolName
nebo SIZE#(SymbolName)
.
Zapamatujte si, že v názvu symbolu záleží na velikosti písmen, avšak v názvu atributu nikoli.
Atributy GROUP#, SEGMENT# a SECTION# vracejí adresu, pokud jsou aplikovány na adresní symbol a vracejí skalární nulu, jsou-li aplikovány na číselný symbol. Ostatní atributy vždy vracejí skalár (prosté číslo).
Atribut OFFSET# vrací ofset symbolu v jeho segmentu jako prosté číslo, tj. počet bajtů mezi ním a počátkem jeho segmentu. Je-li symbol číselný, atribut vrací jeho hodnotu.
Symbol
a OFFSET#Symbol
jsou identické pouze pokud je
Symbol skalár, jinak to první představuje jeho adresu a to druhé prosté číslo.
Výraz Symbol - SEGMENT#Symbol
je totožný s OFFSET#Symbol
jak pro numerický, tak i pro adresní symbol.
Atribut PARA# představuje paragrafovou adresu začátku grupy, do níž symbol patří. Je to hodnota, kterou je třeba načíst do segmentového registru použitého k adresování. Je-li PARA# aplikován na numerický symbol, vrací skalární nulu.
Atribut GROUP# reprezentuje adresu začátku grupy, do níž symbol patří, tj. adresu prvního bajtu nejnižšího segmentu grupy. Při aplikování na numerický symbol vrací skalární nulu.
Atribut SEGMENT# představuje adresu začátku segmentu, do nějž symbol patří. Při aplikování na numerický symbol vrací skalární nulu.
Atribut SECTION# reprezentuje adresu počátku sekce, do níž symbol patří. Při aplikování na numerický symbol vrací skalární nulu. Pokud symbol leží v defaultní sekci (se jménem stejným jako její segment), jak SECTION# tak i SEGMENT# vracejí totožnou adresu.
Atribut SCOPE# vrací číslo odpovídající ASCII hodnotě
velkého písmene odpovídajícího viditelnosti symbolu. To může být
'E'
pro externí symboly, 'P'
pro public (veřejné) symboly,
'X'
pro exportované symboly, 'I'
pro importované symboly,
'S'
pro standardní privátní symboly, nebo '?'
pokud symbol není deklarován.
SIZE# představuje počet bajtů emitovaných instrukcí definující symbol. Typicky je to velikost dat definovaných pseudoinstrukcí D nebo délka strojové instrukce v bajtech. Symboly definované pseudoinstrukcí EQU nebo definované v neemitující instrukci mají atribut SIZE# rovný nule.
Atribut TYPE# vrací číslo odpovídající ASCII hodnotě velkého písmena
korespondujícího s typem objektu. Může to být jeden z
fundamentálních datových typů 'B', 'U', 'W', 'D',
'Q', 'T', 'O', 'Y', 'Z'
, strukturovaný datový typ 'S'
nebo
strojová instrukce 'I'
, pokud byl symbol definován datovou pseudoinstrukcí D.
Numerické symboly vracejí hodnotu 'N'
.
Návěstí strojové instrukce nebo strojového prefixu vracejí atribut 'I'
.
Adresní symbol definovaný samotným návěstím, dále návěstí PROC a PROC1,
jakož i externí symbol vrací atribut typu 'A'
(jako Adresa).
Nedeklarovaný symbol vrací '?'
.
Dopředná reference symbolu o něm vytváří záznam v tabulce symbolů. Nicméně při prvním průchodu je jeho typový atribut'?'
(nedeklarovaný) až dokud nebude nalezena jeho definice. Na druhé straně aplikace atributu na nedefinovaný symbol jej nečiní odkazovaným, tedy nezapisuje ho do tabulky symbolů. Proto můžeme testovat přítomnost symbolu v programu pomocí%IF TYPE#Symbol = '?'
.
Vedle symbolů může být atribut TYPE# aplikován i na některé jiné objekty: registr, strukturu, řetězec, výraz v závorkách () nebo [].
TYPE# registru je 'R'
a jeho SIZE# se rovná šířce registru v bajtech
(1,2,4,8,10,16,32,64).
TYPE# struktury je 'S'
a SIZE# odpovídá její velikosti v bajtech.
Proč používat SIZE# nebo TYPE# na symbol definovaný námi (programátorem), u kterého tedy předem známe jeho velikost a typ? Pokud se později rozhodneme změnit text proměnné Message, nemusíme se obtěžovat přepočítáváním jeho délky.
Operátory atributů jsou často použity v makrech k určení, jakým druhem operandů bylo makro vyvoláno: je-li to registr, datový symbol, přímá hodnota apod. Potřebujeme-li v makru zjistit, zda poskytnutá hodnota
%1
je prosté číslo, můžeme to otestovat dotazem%IF TYPE# %1 = 'N'
.
Viz testy t16* pro více příkladů na atributy.
Podrobnější diferenciace typu datového symbolu, kterou atribut TYPE# poskytuje, někdy není potřebná.
Můžeme například chtít vedět, zda operand makra %1
potřebuje relokaci. K tomu dochází, pokud je operandem adresní symbol nebo paměťová proměnná
obsahující adresní symbol.
Dotazy na TYPE# DataSymbol
nebo TYPE# [DataSymbol+RSI]
mohou vracet 'A', 'B','W','D','Q','T' či jakýkoli typ dat, s nímž byl DataSymbol definován.
Jinak vrátí 'N', pokud bylo operandem číslo nevyžadující relokaci, jako třeba
TYPE# MAX_PATH_SIZE
nebo TYPE# [RBP-16]
.
Zde se bude hodit sjednocení všech druhů adresních a externích symbolů atributovým operátorem SEGMENT#,
který vrátí relokabilní adresu počátku segmentu, nezávisle na datatypu symbolu.
Atribut TYPE# aplikovaný na takový atribut SEGMENT# pak vždy vrátí hodnotu 'A'.
Na druhé straně SEGMENT# ScalarSymbol
a TYPE#(SEGMENT#ScalarSymbol)
vrací 'N'.
%IF TYPE# (SEGMENT# %1) = 'A' ; %1 je adresový výraz vyžadující relokaci. %ELSE ; %1 je nerelokabilní výraz. %ENDIF
Všimněte si, že zřetězené atributy vyžadují závorky. To je kvůli tomu, že operátory atributů mají shodnou prioritu, takže jsou vyhodnocovány zleva doprava, a bez závorek by se první operátor snažil aplikovat na jiný unární operátor.
Viz test t1695 pro více příkladů.
Při aplikaci atributu TYPE# na registr se vrací hodnota 'R'
, bez ohledu na rodinu registrů.
Někdy ale může být užitečné znát přesný druh registru.
Atribut REGTYPE# vrací číslo reprezentující ASCII hodnotu velkého písmene
odpovídajího rodině registrů. Všeobecné registry (GPR) vracejí 'B', 'W', 'D'
nebo 'Q'
,
registry SIMD vracejí 'X', 'Y', 'Z'
, segmentové registry vracejí 'S'
atd. Pro kompletní seznam viz tabulku Registers.
Je-li tento atribut aplikován na objekt, který není registr, vrací '?'
.
Viz také test t1662.
Na rozdíl od předchozích atributů, FILESIZE# a FILETIME# mohou být aplikovány pouze na soubory specifikované jejich jménem, které musí být obklopeno dvojitými uvozovkami ". Název souboru může být absolutní, relativní nebo bez cesty, vztahuje se k aktuálnímu adresáři v asm-time.
Oba atributy zjišťují vlastnosti souboru v čase překladu.
FILESIZE# "filename"
vrací počet bajtů v souboru,
nebo 0 pokud soubor neexistuje.
FILETIME# "filename"
vrací timestamp souboru,
tj. počet sekund od půlnoci UTC 1. ledna 1970 k jeho poslední modifikaci.
Vrací 0 pokud soubor nebyl naleze.
Viz také test t1690.
Literální symboly neboli literály se podobají standardním symbolům asembleru.
Rozdíl je v tom, že jim nemusíme explicitně vymýšlet unikátní jméno.
Literál je definován kdykoli je použit a jeho jméno je představováno rovnítkem =,
za kterým následuje datový výraz,
např. =D(5)
nebo =B"Some text."
.
Data mohou být duplikována, ale na rozdíl od pseudoinstrukce D
(která může mít více operandů) zde může být pouze jeden datový výraz.
Příklady instrukcí s literály:
DIV [=W(10)] ; Vyděl číslo v DX:AX anonymní paměťovou proměnnou typu WORD s hodnotou 10. MOV DX,=B"This is a literal message.$" ; Naplň DX ofsetem řetězce definovaného někde v rodata segmentu. LEA ESI,[=D 0] ; Naplň ESI adresou paměťové proměnné typu DWORD obsahující hodnotu 0. CALL =I"RET" ; Proveď PUSH EIP a pak naplň EIP ofsetem strojové instruce RET definované někde v kódovém segmentu.LEA EBX,[=D 0,1,2,3]; Chyba: vícenásobná definice dat.MOV DX,=B"This is a literal message.",13,10; Chyba: vícenásobná definice dat.
První příklad deklaruje proměnnou=W(10)
. Bez literálů bychom museli nadefinovat datovou proměnnouTen DW 10
někde v datovém segmentu a přidělit jí explicitní unikátní jméno.
Výhodou literálů je, že jim nemusíme vymýšlet jména a explicitně je deklarovat pseudoinstrukcí D. Datový obsah je viditelný přímo v instrukci, která literál používá.
Všechny literály jsou zarovnány podle svých typů,
např. =D 5
je zarovnána DWORD bez ohledu na aktuální stav volby
EUROASM AUTOALIGN=.
Řetězcové literály, jako =B"Some text"
nebo =U"Some text"
jsou vždy implicitně zakončeny BYTE nebo UNICHAR nulou.
€ASM dovoluje zjednodušenou deklaraci neduplikovaných literálních řetězců,
kdy typový identifikátor B
nebo U
je vynechán, např. ="Some text"
.
Konkrétní typ řetězce (B nebo U) je pak určen %proměnnou %^UNICODE
nastavovanou pomocí EUROASM UNICODE=ON|OFF
.
Definice dat pomocí literálů nedovoluje programátorovi určit přesnou lokaci, kam bude literál emitován. €ASM vytváří služební sekci pro každý typ data v závislosti na jejich přirozeném zarovnání. Sekce pro literály je vytvořena buď
- v posledním segmentu, který má PURPOSE=LITERAL a PURPOSE=RODATA|DATA,
- nebo pokud žádný segment s účelem LITERAL neexistuje, pak v posledním segmentu s účelem RODATA,
- nebo pokud segment s PURPOSE=RODATA neexistuje, pak v posledním segmentu s PURPOSE=DATA,
- nebo pokud žádný segment DATA|RODATA neexistuje, vytvoří se implicitní segment s názvem
@LT
a účelem RODATA+LITERAL.Jména literálních sekcí jsou
[@LT64], [@LT32], [@LT16], [@LT8], [@LT4], [@LT2], [@LT1]
.
Literály s datatypemINSTRUC
, jako třeba=8*I"MOVSD"
, jsou emitovány do služební sekce[@RT0]
, která je obdobně vytvořena v segmentu s účelemPURPOSE=CODE+LITERAL
, nebo v posledním kódovém segmentu, pokud dosud neexistoval.Opakovaně použité literály se shodnou deklarací jsou znovupoužity, představují tutéž paměťovou proměnnou. Literály s nikoli doslovnou shodou, jako např.
=W+4
,=W 4
a=W(2+2)
se ukládají zvlášť jako odlišné symboly, ale jejich hodnota je znovupoužita, takže zabírá méně místa v sekci literálů. Podobně literály=B"Some text"
,=B'Some text'
a=B 'Some text'
jsou tři odlišné symboly, ale za běhu programu všechny tři dohromady okupují pouhých 9+1 bajtů paměti v literální sekci [@LT1].
I když nelze bránit programátorovi v přepisování dat definovaných jako literál, měly by být vždy považovány za read-only, jinak by to mohlo poškodit jinou část programu využívající těchže literálních dat.
Vlastnost | Standardní symbol | Literální symbol |
---|---|---|
Deklarace | Definovaný explicitně pseudoinstrukcí D či jejími klony, např. Dozen: DD 12 |
Deklarovaný při jeho prvním využití v instrukci, např. MOV ECX,=D 12 |
Jméno | Programátor musí symbolu určit unikátní název. | Název literálu je vytvořen automaticky z jeho hodnoty. |
Umístění v cílovém kódu | Umístění dat symbolu je zcela v režii programátora. | Programátor nemůže přímo ovlivňovat jeho umístění. |
Alignment | Je-li zarovnání požadováno, musí být vyždáno explicitně pseudoinstrukcí ALIGN nebo modifikátorem ALIGN= nebo volbou EUROASM AUTOALIGN=ENABLED. | Literály jsou vždy přirozeně zarovnány, jako by byla platná volba EUROASM AUTOALIGN=ENABLED . |
Náplň zarovnání | Kvůli minimalizaci náplně pro zarovnávání by měl být programátor opatrný při míchání proměnných s rozličnými velikostmi. | Literály všech velikostí jsou spojeny dohromady v klesajícím pořadí jejich velikostí, což automaticky minimalizuje náplň mezi nimi. |
Vícenásobné operandy |
Pseudoinstrukce D a její klony podporují vícenásobné operandy, např. Hello DB "Hello, world",13,10,'$' . |
Vícenásobné operandy nejsou přípustné. |
Zakončování nulou | Pouze na explicitní žádost, např. Hello: DU "Hello, world",0 . |
Automaticky, např. MOV ESI,=U "Hello, world" . |
Duplikace | Podporována, např. FourDoublePrecOnes: DY 4 * Q 1.0 |
Podporována, např. VMOVUPD YMM7, [= 4 * Q 1.0] . |
Přepisování hodnoty | Přípustné podle potřeby. | Programátor by se ho měl vyvarovat. |
Struktura je deklarována zdrojovým kódem reprezentovaným blokem STRUC..ENDSTRUC. V bloku budou deklarovány názvy, datové typy, velikosti a ofsety členů struktury. V terminologii objektového programování je struktura třídou (class) a strukturovaná paměťová proměnná objektem. Příklad:
DATUM STRUC ; Deklarace struktury DATUM. .Year D W .Month D B .Day D B ENDSTRUC DATUM Today DS DATUM ; Definice strukturované paměťové proměnné Today.
Ve výše uvedeném příkladu struktura DATUM tvoří symboly DATUM.Year, DATUM.Month, DATUM.Day
s hodnotami 0, 2, 3
. Tyto symboly jsou absolutní (skaláry)
a dávají jména relativním ofsetům členů struktury.
Definice Today vytváří strukturovanou paměťovou proměnnou.
Současně jsou vytvořeny symboly Today.Year, Today.Month, Today.Day
.
Jejich adresy jsou definovány někde v sekci [DATA] nebo [BSS],
nejsou to skaláry, nýbrž relokabilní adresy.
Hodnoty členů jsou nedefinovány (pokud byla strukturovaná proměnná definovaná v segmentu [BSS]),
anebo obsahuje samé nuly (pokud byla definovaná v segmentu [DATA]).
Členové strukturované proměnné mohly být definovány staticky v asm-time
klíčovými operandy, například Today DS Datum, .Day=31
,
viz též pseudoinstrukci DS. Při definování paměťové proměnné je v poli operandů dočasně platný jmenný prostor instrukce, viz
zde.
Člen strukturované proměnné může být změněn přímo, pomocí
MOV [Today.Month],12
Rovněž bychom mohli použít registr ukazující na celou strukturovanou proměnnou a využít tento registr k adresaci individuálních členů s relativními ofsety specifikovanými v deklaraci struktury:
MOV EDI,Today MOV [EDI+DATUM.Month],12
Více o strukturách viz zde.
Program v €ASM může používat preprocesní proměnné neboli %proměnné k manipulaci se zdrojovým textem v asm-time. Spolu s makroinstrukcemi tvoří mocný aparát šetřící opakující se programátorskou práci. Preprocesní aparát neovlivňuje cílový kód přímo, tak jak to dělá prostý asembler. Namísto toho manipuluje se zdrojovým textem, který může být modifikován %proměnnými a opakován preprocesními %pseudoinstrukcemi.
Preprocesní proměnné považují svůje obsah za sekvenci znaků, bez zkoumání jejich syntaktického významu.
Jakmile byl nastaven, může být obsah %proměnné použit (expandován) kdykoli se %proměnná vyskytne ve zdrojovém textu (s výjimkou komentářů). K expanzi dochází dříve, než je zdrojový řádek rozebrán na pole instrukce. Ve výchozím stavu je expandován celý obsah %proměnné, avšak může být omezen operátory substring a sublist.
Viz také €ASM funkci Předzpracování.
Rodina %proměnných ► | Uživatelem definované | Formální | Automatické | Systémové | ||
---|---|---|---|---|---|---|
EUROASM | PROGRAM | €ASM | ||||
forma názvu | %identifikátor | %identifikátor | %speciální_znak | %^volba | %^volba | %^pevný |
citlivost na velikost písmen v názvu | Ano | Ano | Ano | Ne | Ne | Ne |
Znovupřiřaditelnost | explicitně pomocí %SET* |
nepřímo v expanzi smyčky %FOR nebo makra | nepřímo expanzí makra | nepřímo pomocí parametru EUROASM |
nepřímo pomocí parametru PROGRAM |
Ne |
Jméno uživatelem definované %proměnné je tvořeno znakem procenta % následovaného identifikátorem, který není rezervovaným názvem %proměnné, bez ohledu na velikost písmen. Identifikátor musí začínat písmenem a nemůže obsahovat tečku ani jiné speciální znaky.
Kategorie | Rezervované jméno |
---|---|
Pseudoinstrukce | %COMMENT, %DEBUG, %DISPLAY, %DROPMACRO, %ELSE, %ENDCOMMENT, %ENDFOR, %ENDIF, %ENDMACRO, %ENDREPEAT, %ENDWHILE, %ERROR, %EXITFOR, %EXITMACRO, %EXITREPEAT, %EXITWHILE, %FOR, %IF, %MACRO, %PROFILE, %REPEAT, %SET, %SET2 %SETA, %SETB, %SETC, %SETE, %SETL, %SETS, %SETX, %SHIFT, %UNTIL, %WHILE |
Uživatelsky definované %proměnné jsou přiřazeny nebo vytvářeny programátorem pomocí jedné z rodiny pseudoinstrukcí %SET*.
%Proměnné mohou být později přiřazovány odlišnou hodnotou, nemusejí být unikátní v rámci programu.
%Proměnné nemusejí být přiřazeny před prvním použitím (expanzí).
Nepřiřazené %proměnné expandují do ničeho (prázdný text).
Jednou přiřazenou %proměnnou nelze odřadit, v €ASM neexistuje direktiva
%UNSET, UNDEFINE nebo UNASSIGN.
Nicméně přiřazení prázdné hodnoty (např. %SomeVar %SET
) je ekvivalentní jejímu odřazení.
€ASM nehlásí žádnou chybu, setká-li se s uživatelsky definovanou %proměnnou, která je prázdná,
která nebyla přiřazena dříve nebo která dosud nebyla ve zdrojovém textu vůbec zmíněna.
Viz také test t7321.
Symboly | Uživatelem definované %proměnné |
---|---|
jsou vlastností PROGRAMu | jsou vlastností EuroAssembleru |
jejich název nikdy nezačíná % | jejich název vždy začíná % |
mohou mít v názvu tečku | nikdy nemají tečku v názvu |
jsou deklarovány v poli návěstí | jsou přiřazovány pomocí pseudoinstrukcí %SET* |
mají atributy, jako TYPE# a SIZE# | jsou prosté kusy textu bez atributů |
mohou být zmiňovány dopředu | nemohou být zmiňovány dopředu |
musí být definovány právě jednou v programu | mohou být mnohokrát redefinovány |
nemohou být odkazovány, nebyly-li deklarovány | mohou být zmiňovány bez deklarace |
nemohou být předmětem operací sublist nebo substring | mohou být sublistovány a substringovány |
Formální %proměnná expanduje na hodnotu řídicího parametru smyčky %FOR anebo na hodnotu operandu v invokaci %MACRO. Je reprezentována identifikátorem stojícím v poli návěstí příkazu %FOR, nebo stojícím jako operand v prototypu %MACRO.
Viditelnost formální %proměnné je omezena na expandovaný blok.
Count %FOR 1..8 DB %Count %ENDFOR Count
Předchozí příklad generuje osm instrukcí DB, které definují bajtové hodnoty 1 až 8.
Identifikátor Count
použitý v příkazech %FOR a %ENDFOR je řídicí %proměnná,
a je dostupná uvnitř %FOR..%ENDFOR bloku jako formální %proměnná %Count
.
Formální %proměnné se rovněž používají k přístupu k pojmenovaným operandům makra
během jejich expanze.
V dalším příkladu máme dvě formální %proměnné makroinstrukce
definované v definici %MACRO jako identifikátory Where
a Stuff
. V těle makra jsou jejich hodnoty dostupné
jako formální %proměnné %Where
a %Stuff
.
Fill %MACRO Where, Stuff=0 ; Definice makra Fill. MOV %Where,%Stuff %ENDMACRO Fill ; Volání (expanze) makra Fill: Fill [Counter], Stuff=255 ; Bude přeloženo jakoMOV [Counter],255
Fill EBX ; Bude přeloženo jakoMOV EBX,0
Všimněte si, že formální %proměnné se píší bez znaménka procent když jsou deklarovány, avšak % musí být předřazeno jejich jménu když jsou referovány v těle příkazů %FOR nebo %MACRO. Toto je důležité pro dědění argumentů ve vnořených a rekurzivně volaných makroinstrukcích, viz např. t7233.
Viditelnost formálních proměnných má uvnitř bloku vyšší prioritu než uživatelem definované %proměnné téhož jména, bez ohledu, zda byly přiřazeny vně nebo uvnitř bloku %FOR..%ENDFOR či %MACRO..%ENDMACRO. Přiřazení nové hodnoty %proměnné s formálním jménem uvnitř makrobloku sice přiřadí tuto novou hodnotu uživatelem definované proměnné, avšak uvnitř makrobloku formální %proměnná převáží, viz test t7347 nebo t7362. Po odchodu z makra bude přiřazená %proměnná opět viditelná.
Automatické preprocesní %proměnné jsou tvořeny a udržovány EuroAssemblerem v čase překladu, jejich názvy obsahují speciální znaky a na rozdíl od uživatelsky definovaných %proměnných jejich hodnota nemůže být měněna pomocí pseudoinstrukcí %SET.
Mají rovněž omezené Scope, jejich použití mimo svou viditelnost vede k chybě.
Suboperace velikost a suboperace délka (procento následované ampersandem) %& reprezentuje
počet znaků nebo počet položek v seznamu nebo počet fyzických řádků v suboperovaném objektu.
Jeho viditelnost – scope je omezena na suboperační závorky [ ]
nebo { }.
Automatická suboperační proměnná %& se tvoří, když expanze vkládaného souboru nebo jiné %proměnné používá suboperace.
Jakmile je k %proměnné nebo ke jménu souboru připojen substring operátor [ ],
automatická %proměnná %&
může být použita uvnitř hranatých závorek, např. [1..%&]
,
a představuje počet bajtů v expandované proměnné nebo ve vkládaném souboru.
Pokud jsme například přidělili %proměnné pět písmen pseudoinstrukcí %Proměnná %SET ABCDE
,
pak její velkost je 5 a povel DB "%Proměnná[4..%&]"
bude expandovat na DB "DE"
.
Je-li k názvu proměnné připojen operátor sublist (pomocí složených závorek { }),
obsah této %proměnné se považuje za pole čárkou oddělených položek
a %&
reprezentuje jejich počet, tj. pořadové číslo poslední neprázdné položky.
Například pokud bylo nastaveno %Reglist %SET ax,cx,dx,bx,bp
,
jeho délka je 5 operandů (položek) a instrukce MOV %Reglist{3},%aReglist{%&}
se přeloží jako MOV dx,bp
.
Pokud je tentýž operátor { } připojen k názvu vkládaného (INCLUDE) souboru,
obsah souboru je považován za seznam fyzických řádků
a %&
reprezentuje počet řádků v souboru.
Například INCLUDE "file.inc"{%&-10 .. %&}
vloží do zdrojového textu posledních deset řádků z "file.inc".
Použití %proměnné %&
mimo závorky by vedlo k chybě.
1
do %&
.Počitadlo expanzí (procento následované tečkou) %.
udržuje dekadické číslo inkrementované EuroAssemblerem při každé expanzi
preprocesního bloku a může být využito ke tvorbě unikátních návěstí v opakovaných blocích.
Jeho viditelnost – scope je omezena na tělo preprocesního bloku
%MACRO, %FOR, %WHILE, %REPEAT. Pokud je použito mimo tyto bloky, expanduje na 0, viz test
t7362.
Je-li v makru nebo bloku %FOR, %WHILE, %REPEAT použito standardní nebo lokální návěstí, a pokud je makro či blok expandováno více než jednou, symbol z návěstí by byl definován více než jednou, což asembler považuje za chybu. Unikátnosti takového symbolu lze dosáhnout začleněním počitadla expanzí do jeho názvu.
Viz příklad makra AbortIf níže.
Návěstí Skip
je následováno %., což dává symbol
Skip%.
expandující na Skip1
a v budoucnu expandující na Skip2
při dalším vyvolání makra AbortIf.
%.
pomáhá vytvářet unikátní jména symbolů.Všechny následující automatické %makroproměnné mají viditelnost – scope omezenu na tělo bloku %MACRO..%ENDMACRO. Odkazují na operandy použité, když bylo makro vyvoláno (expandováno).
Je-li při vyvolání makra použito návěstí (label), je ve výchozím stavu umístěno k první expandované instrukci. Toto chování lze přepsat použitím automatické %makroproměnné %: (procento následované dvojtečkou) deklarované někde uvnitř definice makra. Pouze jedno takové návěstí může být deklarováno v makru. Přemístění návěstí makra k tomuto návěstí může ušetřit několik taktů při skoku přes instrukce kódu, který by musel být přeskočen, viz tento příklad:
SaveCursor %MACRO Videopage=BH
%IF TYPE#CursorSave != 'W' ; Pokud paměťová proměnná CursorSave dosud nebyla definována.
JMP %: ; Přeskoč na +4 (pod DW 0) pokud bylo makro vloženo do toku instrukcí.
CursorSave DW 0 ; Prostor k uložení kurzoru.
%ENDIF
%: MOV AH,3 ; Vstupní bod makra, kam by se jinak skákalo.
MOV BH,%Videopage
INT 10h ; Načti tvar kurzoru pomocí BIOS API.
MOV [CursorSave],CX
%ENDMACRO SaveCursor
...
Save: SaveCursor Videopage=0 ; Použij makro v programu.
...
JMP Save: ; Skočí na instrukci MOV AH,3
.
%:
reprezentuje "vstupní bod" makra.Viz také test t7215.
Pořadový operand makra může být odkazován číslem.
Na rozdíl od dávkových souborů pro DOS a Windows jejich počet není omezen na 9,
ale může být libovolné pozitivní číslo, např. %11.
Samozřejmě, pokud jedenáctý operand není specifikován při volání makra, %11 expanduje do prázdna.
Viz také pseudoinstrukci %SHIFT.
Automatická %proměnná %0 expanduje jméno samotného makra.
Další metoda, jak odkazovat na operand makra (pořadový i klíčový) je předřazení znaku procenta formálnímu názvu operandu.
Pokud je pořadovému číslu operandu nebo názvu formálního operandu předřazen znak pro
logický operátor NOT (vykřičník !), expanduje na invertovaný podmínkový kód ordinálního operandu.
To vyžaduje, aby referovaný operand skutečně obsahoval zkratku podmínkového kódu
(nezávisle na velikosti písmen) jako např. E, NE, C
atd.
Obsah operandu bude nahrazen odpovídajícím invertovaným kódem.
€ASM ohlásí chybu, pokud by operand neobsahoval platný podmínkový kód.
NASM používá ke stejné funkcionalitě operátor unary-minus -. Věřím, že operátor logical-not ! je vhodnější k inverzi logické hodnoty.
Viz makroAbortIf výše jako příklad.
Seznam pořadových operandů %* (procento následované hvězdičkou) je naplněn všemi pořadovými operandy z volání makra. Operandy jsou odděleny čárkou. Klíčové operandy jsou z listu vyřazeny.
Operandy makra mohou být referovány více metodami. Následující příklad demonstruje tři z nich:
CopyStr %MACRO FirstOp, SecondOp, ThirdOp ; Prototyp makra. MOV ESI,%FirstOp ; Použij formální název operandu. MOV EDI,%2 ; Použij pořadové číslo operandu. MOV ECX,%*{3} ; Použij třetí položku seznamu operandů. REP MOVSB %ENDMACRO CopyStr ... CopyStr Source, Dest, SIZE# Dest ; Vyvolání makra.
Délka seznamu pořadových operandů (pořadové číslo posledního neprázdného operandu) je nastaveno do automatické %proměnné počtu ordinálů %# (procento následované znakem #) a představuje počet pořadových operandů při volání makra (ne počtu deklarovaného v prototypu makra).
Stejná hodnota by také mohla být získána pomocí %NrOfOrdinals %SETL %*
.
Seznam klíčových operandů %=*
se podobá automatické %proměnné %*,
avšak obsahuje pouze čárkou oddělené operandy klíč=hodnota
aktuálně použité v invokaci makra.
Obě %proměnné %* a %=* mohou být použity k výrobě klonů maker s odlišnými názvy:
copystr %MACRO CopyStr %*, %=* %ENDMACRO copystrToto vytváří kopii předtím definovaného makra CopyStr avšak s odlišným názvem copystr. Všechny operandy použité při invokaci makra copystr budou předány doslova původnímu makru CopyStr.
Délka seznamu klíčových operandů %=#
reprezentuje počet klíčových operandů aktuálně použitých v invokaci makra
(nikoli počtu deklarovanému v prototypu makra).
Viz též t7364.
Stejná hodnota by také mohla být získána pomocí %NrOfKeys %SETL %=*
.
EuroAssembler udržuje sbírku preprocesních %^proměnných s hodnotami konfiguračních parametrů. Jejich hodnoty mohou být zjišťovány v asm-time.
Název systémové proměnné sestává z %^
(procento a caret) následovaný jedním z výčtových identifikátorů.
Hodnota systémové %^proměnné nemůže být přiřazena pseudoinstrukcemi %SET*; je dynamicky udržována EuroAssemblerem a odpovídá současné platné hodnotě konfiguračního parametru.
%^DumpWidth %SETA 32 ; Použijte EUROASM DumpWidth=32.
Programátor je může ovlivňovat pouze nepřímo, pomocí volby specifikované v
konfiguračním souboru euroasm.ini
nebo pomocí pseudoinstrukcí EUROASM a PROGRAM.
Kategorie | Názvy %^proměnných (case insensitive) |
---|---|
EUROASM | %^AES, %^AMD, %^AutoAlign, %^AutoSegment, %^CET, %^CodePage, %^CPU, %^CYRIX, %^D3NOW, %^Debug, %^DisplayEnc, %^DisplayStm, %^Dump, %^DumpAll, %^DumpWidth, %^EVEX, %^FPU, %^ImportPath, %^IncludePath, %^Interpreter, %^Linkpath, %^List, %^ListFile, %^ListInclude, %^ListMacro, %^ListRepeat, %^ListVar, %^LWP, %^MaxInclusions, %^MaxLinks, %^MMX, %^MPX, %^MVEX, %^NoWarn, %^Profile, %^Prot, %^Prov, %^RunPath, %^RTF, %^RTM, %^SHA, %^SIMD, %^Spec, %^SVM, %^TBM, %^TimeStamp, %^TSX, %^Undoc, %^Unicode, %^VIA, %^VMX, %^Warn, %^XOP, |
PROGRAM | %^DllCharacteristics, %^Entry, %^FileAlign, %^Format, %^IconFile, %^ImageBase, %^ListGlobals, %^ListLiterals, %^ListMap, %^MajorImageVersion, %^MajorLinkerVersion, %^MajorOSVersion, %^MajorSubsystemVersion, %^MaxExpansions, %^MaxPasses, %^MinorImageVersion, %^MinorLinkerVersion, %^MinorOSVersion, %^MinorSubsystemVersion, %^Model, %^OutFile, %^SectionAlign, %^SizeOfHeapCommit %^SizeOfHeapReserve, %^SizeOfStackCommit, %^SizeOfStackReserve, %^StubFile, %^Subsystem, %^TimeStamp, %^Width, %^Win32VersionValue, |
€ASM | %^Date, %^EuroasmOs, %^Pass, %^Proc, %^Program, %^Section, %^Segment, %^SourceExt, %^SourceFile, %^SourceLine, %^SourceName, %^Time, %^Version, |
jsou přiřazeny hodnotami specifikovanými v sekci [EUROASM]
souboru euroasm.ini
nebo pseudoinstrukcí EUROASM.
Pro popis %^proměnných této kategorie viz příslušný klíč pseudoinstrukce EUROASM.
jsou přiřazeny hodnotami specifikovanými v sekci [PROGRAM]
souboru euroasm.ini
nebo pseudoinstrukcí PROGRAM.
Pro popis %^proměnných této kategorie viz příslušný klíč pseudoinstrukce PROGRAM.
Hodnota těchto %^proměnných je udržována samotným €ASM a programátor je nemůže přímo měnit. Jsou popsány zde:
euroasm source*.asm
budou sdílet stejnou hodnotu %^Date a %^Time nastavené z lokálního času počítače, když byl euroasm.exespuštěn.
Kombinace €ASM systémových %^proměnných je interně použita k identifikaci pozice instrukce v chybových oznámeních:
"%^SourceName%^SourceExt"{%^SourceLine}
, např.
"HelloWorld.asm"{3}
€ASM %^proměnná %^Section
může být použita k úschově a obnově aktuální sekce a segmentu v makrech.
Spolu s instrukcí EUROASM PUSH
zaručuje, že prostředí EuroAssembleru nebude modifikováno expanzí makra,
ani když je makro požadovalo změnit.
aMacro %MACRO ; Deklarace makra potřebujícího emitovat do své privátní sekce. EUROASM PUSH ; Ulož všechny volby EUROASM na jeho vlastní zásobník. %BackupSec %SET %^Section ; Ulož aktuální jméno sekce do uživatelsky definované proměnné. [.MacroPrivateSection] ; Přepni do makrem požadované sekce. ... ; Deklaruj tělo makra. [%BackupSec] ; Přepni zpět do origináoní sekce, ať už byla jakákoli. EUROASM POP ; Obnov volby EUROASM. %ENDMACRO aMacro
Jiný příklad použití €ASM %^proměnných:
%MonthList %SET Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec %Day %SETA %^DATE[7..8] ; Použití %SETA místo %SET plní %Day dekadickou hodnotou, aby se zbavila vedoucí nepodstatné nuly. InfoMsg DB "This program was assembled with €ASM %^EuroasmOs ver.%^Version",13,10 DB "on %MonthList{%^Date[5..6]} %Day-th, %^Date[1..4] at %^Time[1..2]:%^Time[3..4].",13,10,0 ; InfoMsg nyní obsahuje něco jako ; This program was assembled with €ASM Win ver.20081231 ; on Feb 8-th, 2009 at 22:05.
Výčtovým volbám, jako %^CPU, %^FORMAT, %^MODEL atd. bude přiřazen text s velkými písmeny. Mohou být testovány v asm-time pomocí operací pro porovnávání řetězců.
Číselným volbám jsou přiřazovány hodnoty v desítkovém zápisu. Pozitivní znaménko + se vynechává. Mohou být testovány pomocí operací porovnávání čísel.
Booleovské volby, např. AutoSegment=, Priv=
atd.,
jsou přiřazovány odpovídajícícm systémovým %^proměnným %^Autosegment, %^Priv
jako 0 (nepravda) nebo -1 (pravda), bez ohledu na to, zda byly specifikovány
pomocí výčtových hodnot ON/OFF, YES/NO, TRUE/FALSE nebo logickým výrazem.
Mohou být testovány pomocí logických výrazů anebo přímo jako operand %IF,
např. %IF %^UNDOC
.
Rozsahové volby EUROASM WARN= and NOWARN= jsou přiřazeny systémovým %^proměnným
%^Warn, %^NoWarn
jako řetězy 3999 číslic 0 (nepravda) nebo 1 (pravda).
První číslice odpovídá momentálnímu stavu oznámení č. I0001, druhá č. I0002, poslední č. W3999.
Příklad: %IF %^WARN[2820]
bude překládat následnou instrukci
pouze pokud je oznámení W2820 povoleno.
Systémové %^proměnné mohou být využity v makrech k varování, že prostředí EuroAssembleru není nastaveno tak, jak je potřeba. Příklady:
%IF "%^MODEL" !== "FLAT" %ERROR Macro "%0" is intended for flat memory model only. %ENDIF %IF %^SizeOfStackCommit < 16K %ERROR This recursive macro requires stack size at least 16 KB. %ENDIF %IF %^Width = 64 && ! %^AMD %ERROR This 64-bit program for MS-Windows should have AMD=Enabled. %ENDIF %IF %^NoWarn[2101] %ERROR You shouldn't suppress W2101. Move unused symbols to an included file instead. %ENDIF
V asembleru rozeznáváme tři rody instrukcí:
strojové instrukce vytvořené výrobcem CPU,
pseudoinstrukce vytvořené výrobcem asembleru,
makroinstrukce vytvořené programátorem.
Strojová instrukce je nejelementárnější příkaz procesoru, aby provedl nějakou kalkulaci nebo jinou manipulaci s daty za běhu programu.
EuroAssembler používá Intelskou syntaxi, kde první operand určuje cíl (což je často zároveň jeden ze zdrojových operandů), za nímž může následovat druhý operand – zdroj.
Tato syntaxe se používá v dokumentaci výrobců CPU a je použita i u většiny asemblerů, s výjimkou v Unixu používaného gas, který preferuje alternativní paradigma představovaného syntaxí AT&T s přehozeným pořadím operandů. Více rozdílů mezi syntaxí AT&T a Intel je popsáno v [ATTsyntax].
EuroAssembler implementuje mnemoniku strojových instrukcí jak byly definovány výrobci procesorů. Rovněž implementuje několik nedokumentovaných instrukcí a rozšíření, které budou popsány níže.
Některé strojové instrukce dovolují alternativní kódování stejné mnemoniky,
€ASM preferuje to kratší, nebyl-li instruován jinak.
€ASM respektuje menmoniku zvolenou programátorem, proto nikdy nekóduje
třeba LEA ESI,[MemoryVariable]
jako MOV ESI,MemoryVariable
,
i když je druhý zápis o jeden bajt kratší.
Jsou pouze dvě výjimky, kdy není mnemonika dodržena:
MOVZX r64,r/m32
implementované jako
MOV r32,r/m32
, kdy využívá vedlejší efekt architektury IA-64
spočívající v nulování vyšší poloviny 64bitových registrů, pokud se zapisuje do jejich dolní poloviny.
Viz test t3043.
Strojová instrukce může manipulovat s registry a paměťovými proměnnými různé šířky,
obvykle s operandy typu BYTE, WORD nebo DWORD. Avšak výrobce CPU definoval pro instrukce stejnou mnemoniku
bez ohledu na šířku dat.
Tak například SUB [MemoryVariable],4
říká CPU, aby odečetl číslo 4 od obsahu
MemoryVariable, které mohlo být definováno jako DB, DW, DD nebo DQ.
€ASM se podívá na typ MemoryVariable a vybere odpovídající zakódování odpovídající tomuto typu.
Ovšem ofset mohl být rovněž vyjádřen jako obsah registru nebo jako prosté číslo, např.
SUB [ESI],4
, a typ proměnné je pak neznámý.
Jednou z metod jak sdělit EuroAssembleru, jaká šířka dat je žádoucí, je použití
instrukční přípony, což je jedno z písmen B W D Q S N F
připojené k mnemonickému názvu instrukce.
€ASM dovoluje rozšiřovat mnemonickou příponou B, W, D, Q mnoho instrukcí se všeobecným účelem.
Instrukce pro předávání zpracování CALL, JMP, RET
mohou být modifikovány příponou
N nebo F oznamující asembleru, zda je vzdálenost cíle
NEAR nebo FAR, tj. zda cíl patří do téhož segmentu nebo zda se jeho deskriptor má také měnit.
Instrukce pro nepodmíněný skok JMP
může být rovněž doplněna příponou
S, je-li vzdálenost cíle zakódovatelná v 8 bitech (-128..+127).
Instrukce €ASM akceptující příponu | Přípona |
---|---|
ADC, ADD, AND, CMP, CMPS, CRC32, DEC, DIV, IDIV, IMUL, INC, LODS, MOV, MOVS, MUL, NEG, NOT, OR, RCL, RCR, ROL, ROR, SAL, SAL2, SAR, SBB, SCAS, SHL, SHR, STOS, SUB, TEST, TEST2, XOR | B, W, D, Q |
BT, BTC, BTS, BTR, ENTER, HINT_NOP, IRET, LEAVE, POP, POPF, PUSH, PUSHF | W, D, Q |
PUSHA, POPA | W, D |
INS, MOVSX, MOVZX, OUTS | B, W, D |
XLAT | B |
CALL, RET | N, F |
JMP | S, N, F |
Použití přípony je v mnoha případech zbytečné, neboť šířka paměťové proměnné může být odvozena z jeho atributu pro typ
anebo je šířka určena šířkou registru použitého jako jeden z operandů.
Pokud je šířka registru v konfliktu s příponou, ohlásí se chyba, např. v MOVW AL,[ESI]
.
Přípona je sporadicky použita i v jiných asemblerech, viz např. STOSB/W/D, OUTSB/W/D, RETN/F apod. €ASM pouze důsledně rozšiřuje toto vylepšení.
Mnemoniky mnoha instrukcí pro SIMD končí písmeny
~SS, ~SD, ~PS, ~PD
rovněž specifikujícími typy operandů (Scalar/Packed Single/Double-precision). €ASM však toto nepovažuje za přípony instrukcí.Existuje několik konfliktů mnemoniky s příponou s jinou instrukcí, jsou však řešitelné pomocí typu a počtu operandů:
|00000000: | ; Standardní MOV versus MMX Move Doubleword: |00000000:C7450800000000 | MOVD [EBP+8],0 ; Ulož číslo do paměťové proměnné typu DWORD (přípona ~D). |00000007:0F7E4508 | MOVD [EBP+8],MM0 ; Ulož DWORD z registru MMX do paměti. |0000000B: | |0000000B: | ; Posun SHL versus Double Precision Shift: |0000000B:C1650804 | SHLD [EBP+8],4 ; Posuň DWORD v paměti logicky vlevo o 4 bity (přípona ~D). |0000000F:0FA4450804 | SHLD [EBP+8],EAX,4 ; Vysuň 4 bity z registru EAX doleva do pamětové proměnné. |00000014: | |00000014: | ; Porovnej řetězce versus Porovnej skalární FP číslo s dvojitou přesností: |00000014:A7 | CMPSD ; Porovnej DWORDy na [DS:ESI] a [ES:EDI] (přípona ~D). |00000015:A7 | CMPSD [ESI],[EDI] ; Totéž, dokumentováno explicitními operandy. |00000016:F20FC2CA00 | CMPSD XMM1,XMM2,0 ; Porovnej dvě skalární FP čísla s dvojitou přesností na rovnost.
Strojové instrukce se shodnou mnemonikou a funkčností mohou být někdy zakódovány
do různých strojových kódů. Například okamžitá hodnota operandu může být zakódována v jednom bajtu,
pokud nepřekračuje rozsah -128..+127, anebo může být zakódována jako celé slovo či dvojslovo.
Podobné pravidlo platí pro hodnotu displacementu v adresních výrazech.
Škálovaný adresní výraz, jako [1*ESI+EBX]
může být kódován bez SIB
jako [ESI+EBX]
nebo s využitím SIB a s explicitním škálovacím faktorem 1.
€ASM preferuje kratší variantu, ale lze to změnit přídavným klíčovým operandem zvaným instrukční modifikátor.
Jiné asemblery dekorují operandy speciálními direktivami
byte, word, dword, qword, short, strict, near, far, ptr
k dosažení požadovaného kódu, např.add word ptr [StringOfBytes + 4], 0x20
nebojmp short SomeLabel
. Namísto těchto direktiv používá €ASM buď příponu mnemoniky, nebo instrukční modifikátor.Modifikátory pro AVX
MASK=, ZEROING=, SAE=, ROUND=, BCST=
jsou v €ASM využívány namísto nekonzistních a bídně dokumentovaných dekorátorů typu{k} {z} {ru-sae} {4to16} {uint16} {cdab}
navrhovaných v dokumentaci [IntelAVX512] a [IntelMVEX].
Typickou hodnotou modifikátoru je výčtová hodnota BYTE, WORD, DWORD
.
Většinu modifikátorů lze zkrátit na jejich první písmeno.
U názvů ani hodnot instrukčních modifikátorů nezáleží na velikosti písmen.
Některé modifikátory jsou booleovské, jejich hodnota může být
TRUE, YES, ON, ENABLE, ENABLED
pokud pravda,
a FALSE, NO, OFF, DISABLE, DISABLED
v ostatních případech.
Logickým modifikátorem může být také výraz vyhodnocený jako nula (nepravda) nebo ne-nula (pravda),
viz boolovská rozšíření.
Pokud €ASM nemůže vyhovět požadovanému modifikátoru, ohlásí varování a ignoruje ho.
Volbou EUROASM DISPLAYENC=ON
můžeme zapnout zobrazování aktuálně použitých modifikátorů.
V tom případě €ASM doprovodí každou strojovou instrukci diagnostickým oznámením
D1080 explicitně dokumentujícím modifikátory použité při kódování instrukce.:
Jako dědictví po starších procesorech některé strojové instrukce mají více než jedno kódování.
Například instrukce POP rAX
může být zakódována jako 0x58 nebo jako 0x8FC0,
přičemž oba způsoby jsou funkčně ekvivalentní.
Modifikátor CODE= vybírá kódování, které má €ASM použít.
Modifikátor může být SHORT nebo LONG
alias S nebo L. Jako výchozí se bere ten, který poskytuje kratší kód, obvykle CODE=SHORT
.
Mají-li dvě kódování stejnou velikost, CODE=SHORT vybírá variantu s numericky nižším operačním kódem.
|00000000:43 | INC EBX |00000001:43 | INC EBX,CODE=SHORT ; Zděděné kódování Intel 8080, není dostupné v 64bitovém módu. |00000002:FFC3 | INC EBX,CODE=LONG |00000004: | |00000004:50 | PUSH EAX |00000005:50 | PUSH EAX,CODE=SHORT ; Zděděné kódování Intel 8080, není dostupné v 64bitovém módu. |00000006:FFF0 | PUSH EAX,CODE=LONG |00000008: | |00000008:87CA | XCHG ECX,EDX |0000000A:87D1 | XCHG ECX,EDX,CODE=LONG ; Modifikátor zaměňuje operandy v komutativních operacích XCHG, TEST. |0000000C:87D1 | XCHG EDX,ECX |0000000E:87CA | XCHG EDX,ECX,CODE=LONG |00000010: | |00000010:C3 | RET |00000011:C3 | RET CODE=LONG |00000012:C20000 | RET CODE=SHORT ; Byl požadován numericky nižší operační kód 0xC2, který ale vyžaduje imm16. |00000015: | |00000015:83C07F | ADD EAX,127 |00000018:83C07F | ADD EAX,127,CODE=LONG |0000001B:057F000000 | ADD EAX,127,CODE=SHORT ; Byl požadován kratší operační kód 0x05, který ale neumí znaménkově roztáhnout imm8.V některých případech volba numericky nižšího operačního kódu pomocí modifikátoruCODE=SHORT
může vést k delšímu zakódování, viz výše v příkladuADD r32,imm8
.
Tento modifikátor ovlivňuje velikost operandu, tedy šířku dat, se kterými instrukce pracuje. Může to být jedna z výčtových hodnot BYTE, WORD, DWORD, QWORD, TBYTE, OWORD, YWORD, ZWORD neboli B, W, D, Q, T, O, Y, Z. Výchozí hodnota není specifikována.
Modifikátor DATA= má stejnou funkci jako přípona mnemoniky, jsou pouze dva rozdíly:
Existují další možnosti, jak řídit velikost operandu. Je-li jedním z operandů registr, jeho šířka převládne a nelze to přepsat příponou ani modifikátorem. Není-li šířka specifikována registrem, €ASM se dívá na atribut TYPE# paměťového operandu.
Priorita specifikací velikosti dat:
Viz následující příklady:
|00000000:00000000 |MemoryVariable DB 0,0,0,0 |00000004:0107 | ADD [EDI],EAX ; Šířka je určena registrem (32 bitů). |00000006:830701 | ADDD [EDI],1 ; Šířka je určena příponou (32 bitů). |00000009:66830701 | ADD [EDI],1,DATA=W ; Šířka je určena modifikátorem (16 bitů). |0000000D:800701 | ADDB [EDI],1,DATA=W ; Šířka je určena příponou (8 bitů). Varování: modifikátor byl ignorován. |## W2401 Modifier "DATA=WORD" could not be obeyed in this instruction. |00000010:660107 | ADDB [EDI],AX ; Šířka je určena registrem (16 bitů). Chyba: přípona byla ignorována. |### E6740 Impracticable operand-size requested with mnemonic suffix. |00000013:8387[00000000]01 | ADDD [EDI+MemoryVariable],1 ; Šířka je určena příponou (32 bitů). |0000001A:668387[00000000]01 | ADD [EDI+MemoryVariable],1,DATA=W ; Šířka je určena modifikátorem (16 bitů). |00000022:8087[00000000]01 | ADD [EDI+MemoryVariable],1 ; %Sířka je určena atributem TYPE# MemoryVariable = 'B' (8 bitů). |00000029:800701 | ADD [EDI],1 ; Chyba: Šířka nebyla určena. |### E6730 Operand size could not be determined, please use DATA= modifier.Některé instrukce kódují malou okamžitou hodnotu jako jeden bajt, třebaže operují s celým slovem. Hodnota bajtu je znaménkově rozšířena procesorem za běhu programu.
Modifikátor IMM=
může nabývat hodnnot BYTE, WORD, DWORD, QWORD alias B, W, D, Q
a specifikuje, jak by měla být hodnota operandu zakódována v instrukci.
Hodnota displacementu v adresním výrazu může být zakódována v jednom bajtu,
pokud se vejde do rozsahu -128..+127. Tato hodnota bude procesorem za běhu znaménkově roztažena.
Hodnoty mimo tento rozsah jsou zakódovány v plné velikosti, tj. jako 16tibitové nebo 32bitové slovo
v souladu s šířkou segmentu (která mohla být dočasně změněna prefixem ATOGGLE).
Toto je defaultní chování €ASM.
Modifikátor DISP=
může mít stejné hodnoty jako modifikátor IMM=
a určuje, zda má být displacement zakódován v plné velikosti nebo jako bajt.
Škálováním se rozumí vynásobení obsahu indexregistru v registrovém adresním výrazu hodnotou 0, 1, 2, 4 or 8 za běhu programu.
Modifikátor SCALE=
může být buď SMART nebo VERBATIM
(případně zkráceně S, V). Výchozí hodnota je SCALE=SMART
.
V módu verbatim se neprovádějí žádné optimalizace s indexovým a bázovým registrem;
škálování je zakódováno s bajtem SIB i v případech, kdy je škálovací faktor 1 nebo 0.
Kódování s SCALE=VERBATIM
používá pokud možno bajt SIB.
Ve smart módu (default) €ASM zkouší rearanžovat registry a nepoužívat SIB, není-li to nezbytně nutné.
Optimalizační "smart" pravidla:
(IR
je indexregister, BR
je baseregister, disp
je displacement):
[0*IR+BR+disp] > [BR+disp]
[1*IR+BR+disp] > [IR+BR+disp]
[2*IR+disp] > [BR+IR+disp]
Všimněte si, že optimalizace pomocí SCALE=SMART může změnit roli registrů (báze|index) a tudíž i defaultní segmentový registr (SS|DS) použitý k adresaci. V paměťovém modelu FLAT je to obvykle jedno, jinak použijte SCALE=VERBATIM.
Je-li zobrazování zakódování instrukcí povoleno pomocí EUROASM DisplayEnc=Yes
,
modifikátor SCALE=VERBATIM říká, že bajt SIB byl emitován, jinak SCALE=SMART signalizuje, že SIB chybí.
Tento modifikátor určuje distanci cíle v instrukcích pro předání zpracování. Hodnotou modifikátoru je jedna z možností FAR, NEAR, SHORT alias F, N, S.
DIST=FAR
se použije, pokud je cíl v odlišném segmentu
a musí se změnit obsah registrů jak rIP, tak i CS.
Ve výchozím stavu €ASM při přechodech uvnitř segmentu automaticky vybírá mezi vzdáleností SHORT a NEAR v závislosti na rozdílu ofsetů.
Modifikátor DIST= plní stejnou funkci jako instrukční přípona, jsou jen dva rozdíly:
JMP, CALL, RET
,
zatímco modifikátor DIST= může být aplikován i na další instrukce pro předání zpracování:
LOOPcc, Jcc, JrCXZ
.Modifikátor DIST=NEAR
nebo DIST=FAR
může být také aplikován
na pseudoinstrukce PROC, PROC1
.
Důsledkem nastavení procedury na DIST=FAR je to, že instrukce CALL a JMP na takovou proceduru
budou defaultně FAR a že instrukce RET uvnitř takové procedury také bude defaultně DIST=FAR
.
Tento modifikátor vybírá referenční rámec při adresaci paměti v 64bitovém módu.
Dovolené hodnoty jsou ABS, REL nebo zkráceně A, R.
Ofset zakódovaný v instrukci s absolutní adresaci je vztažen
k začátku segmentu, který je v asm-time vždy 0.
Při relativní adresaci je ofset vztažen k pozici příští instrukce,
tedy k obsahu registru RIP.
V historických módech (16 a 32bitovém) je referenční rámec napevno ADDR=REL
v instrukcích pro předání zpracování
(přímý JMP, CALL, LOOP, Jcc), a jako ADDR=ABS
ve všech ostatních instrukcích.
Adresování vztažené k RIP je o jeden bajt kratší a nepotřebuje relokaci.
Proto je adresování s ADDR=REL
preferováno jako výchozí v 64bitovém módu.
Explicitní výběr absolutní a RIP-relativní adresace má význam pouze
v 64bitovém módu pokud by absolutní adresa vyžadovala relokaci v link-time.
K tomu dochází, je-li paměťová proměnná specifikována jako adresní symbol
(nikoli prosté číslo), a pokud v adresaci není zapojen bázový ani indexový registr.
Všechny následující modifikátory se vztahují pouze k instrukcím používajícím kódování Advanced Vector eXtensions (AVX). Možné hodnoty prefixu jsou XOP, VEX, VEX2, VEX3, MVEX, EVEX (zkratky nejsou k dispozici).
Většina instrukcí AVX má mnemoniku prefixovánu písmenem V~. Některé instrukce jsou definovány pouze jedním druhem AVX prefixů, ty pak nepotřebují explicitní modifikátor. Pokud instrukce může být zakódována více různými prefixy, €ASM defaultně vybírá ten nejkratší.
Prefix VEX existuje ve dvou variantách: VEX2 a VEX3. Delší kódování (VEX3) je automaticky vybráno, pokud instrukce používá indexregistr nebo bázový registr R8..R15, případně pokud používá mapu operačních kódů 0F38 nebo 0F3A.
Prefix EVEX nebo MVEX bude použit namísto VEX pokud instrukce používá registr XMM16..XMM31, YMM16..YMM31, ZMM0..ZMM31, K0..K7, nebo modifikátor EH=, SAE=, ROUND=, MASK=, ZEROING=, OPER=.
Instrukce kódovatelné jak pomocí EVEX, tak i MVEX používají jako default PREFIX=EVEX
.
Software napsaný pro CPU Intel® Xeon Phi™ musí explicitně žádat PREFIX=MVEX
pro každou takovou
obojživelnou instrukci.
V tom případě je užitečné disablovat EVEX EUROASM EVEX=DISABLED
a být tudíž varován, pokud některá instrukce MVEX
bude nedopatřením zakódována jako EVEX.
Explicitní specifikace modifikátoru EH=
(který je použitelný pouze u MVEX)
rovněž vybírá prefix MVEX, psát PREFIX=MVEX
v takovém případě není nutné.
Prefix | Požadovaná volba EUROASM |
---|---|
XOP | SIMD=AVX, AMD=ENABLED, XOP=ENABLED |
VEX | SIMD=AVX |
MVEX | SIMD=AVX512, MVEX=ENABLED |
EVEX | SIMD=AVX512, EVEX=ENABLED |
Modifikátor MASK=
(podobně jako ZEROING=, EH=, SAE=, ROUND=, BCST=, OPER=
)
je aplikovatelný pouze s Enhanced Advanced Vector eXtensions (EVEX nebo MVEX).
MASK specifikuje, který maskovací registr je použit k řízení toho, které elementy (celá čísla nebo čísla s plovoucí tečkou)
mají být zapisována do cílového registru. Pouze ty elementy, jež mají nastaven odpovídající bit v maskovacím registru, budou zapsány.
Ostatní elementy jsou buď nulovány (pokud modifikátor ZEROING=ON
) nebo zůstávají nezměněny (ZEROING=OFF
).
Možné hodnoty parametru MASK= jsou K0, K1, K2, K2, K3, K4, K5, K6, K7 nebo výraz vyhodnocený na číslo 0..7.
Výchozí hodnota je MASK=0
. Registr K0 je speciální, všechny jeho bity se berou jako nastavené,
takže maskování se neuplatňuje.
Booleovský modifikátor ZEROING=
určuje, zda elementy maskované obsahem maskovacího registru
by měly být vynulovány nebo ponechány beze změny, čemuž se říká merging.
Nemá význam, pokud je MASK=K0
nebo pokud registr masky není vůbec specifikován.
Default je ZEROING=OFF
(merging). Modifikátor lze aplikovat pouze s kódováním prefixu EVEX.
Booleovský modifikátor EH=
(Eviction Hint) lze aplikovat pouze na instrukce
zakódované s prefixem MVEX.
EH=1
informuje CPU, že data jsou non-temporal a že patrně nebudou brzy znovu použita,
takže nemá význam je držet ve vyrovnávací paměti CPU. Týká se to pouze instrukcí typu registr-do paměti.
Hodnota EH je rovněž konzultována v instrukcích typu registr-do-registru, kde vybírá mezi operacemi swizzle a statickým zaokrouhlováním.
Pokud je booleovský modifikátor SAE=
(Suppress All Exceptions) zapnut,
instrukce nebude vyvolávat žádnou výjimku (přerušení) při operacích s plovoucí tečkou,
například když operovala s hodnotou not-a-number.
Instrukce s nastaveným SAE=ON
se chová, jako by všechny bity registru MXCSR byly nastaveny.
V kódování EVEX je ve výchozím stavu SAE povoleno kdykoli je použito statické zaokrouhlování. Toto chování nelze vypnout.
Modifikátor ROUND=
určuje statický zaokrouhlovací mód, lze jej aplikovat na instrukce
s prefixem EVEX nebo MVEX se zaokrouhlovací sémantikou, například pro konverzi
z dvojité na jednoduchou přesnost čísel s plovoucí tečkou.
Má čtyři možné hodnoty: NEAR, UP, DOWN, ZERO alias N, U, D, Z.
Statické zaokrouhlování je dostupné pouze v operacích typu registerZMM-do-registru ZMM, ne pokud je některý operand v paměti nebo pokud jsou použity registry XMM nebo YMM. Default je bez zaokrouhlování, v tom případě platí obecné zaokrouhlování řízené bity RM registru MXCSR.
Booleovský modifikátor BCST=
lze použít k povolení data broadcasting (vysílání) v operacích,
které čtou data z paměti. Pokud je BCST=ENABLED
, zdrojový paměťový operand čte z paměti pouze jeden element
a jeho obsah je pak zkopírován (vyslán) do všech pozic cílového registru.
Výchozí stav je BCST=OFF
. Broadcasting nelze použít u operací typu registr-do-registru.
Modifikátor OPER=
kóduje druh operace prováděné za běhu se zdrojovým operandem.
Možné operace jsou broadcasting, rounding, conversion, swizzling.
Možnou hodnotou modifikátoru je číselný výraz vyhodnocený na číslo 0..7.
Tato hodnota bude zakódována v bitech 6, 5, 4 32bitového prefixu EVEX nebo MVEX.
Bity jsou pojmenovány S2, S1, S0 ve specifikaci MVEX
[IntelMVEX], a L', L, b ve specifikaci EVEX
[IntelAVX512].
Tytéž bity jsou rovněž ovlivněny modifikátory BCST=, ROUND=, SAE=
a šířkou registrů SIMD,
avšak přímé uvedení modifikátoru OPER= má vyšší prioritu, pokud by došlo ke konfliktu.
Modifikátor OPER= je jediný způsob, jak požadovat speciální konverzní nebo swizzle (míchací) operace pro instrukce kódované pomocí MVEX a dostupné na Intel® Xeon Phi™ procesoru. Ne všechny operace z níže uvedené tabulky jsou dostupné u všech MVEX instrukcí, dokumentace na [IntelMVEX] by měla být prohlédnuta před použitím modifikátoru.
OPER= | register-do-registru, EH=0 | registr-do-registru, EH=1 | paměť-do-registru | registr-do-paměti |
---|---|---|---|---|
0 | no swizzle {dcba} | ROUND=NEAR,SAE=NO | no operation | no conversion |
1 | swap (inner) pairs {cdab} | ROUND=DOWN,SAE=NO | bcst 1 element {1to16} or {1to8} | not available |
2 | swap with two-away {badc} | ROUND=UP,SAE=NO | bcst 4 elements {4to16} or {4to8} | not available |
3 | cross-product swizzle {dacb} | ROUND=ZERO,SAE=NO | convert from {float16} | convert to {float16} |
4 | bcst a element across 4 {aaaa} | ROUND=NEAR,SAE=YES | convert from {uint8} | convert to {uint8} |
5 | bcst b element across 4 {bbbb} | ROUND=DOWN,SAE=YES | convert from {sint8} | convert to {sint8} |
6 | bcst c element across 4 {cccc} | ROUND=UP,SAE=YES | convert from {uint16} | convert to {uint16} |
7 | bcst d element across 4 {dddd} | ROUND=ZERO,SAE=YES | convert from {sint16} | convert to {sint16} |
OPER= | registr-do-registru | paměť-do-registru |
---|---|---|
0 | DATA=OWORD,SAE=NO | DATA=OWORD,BCST=OFF |
1 | DATA=ZWORD,SAE=YES,ROUND=NEAR | DATA=OWORD,BCST=ON |
2 | DATA=YWORD,SAE=NO | DATA=YWORD,BCST=OFF |
3 | DATA=ZWORD,SAE=YES,ROUND=DOWN | DATA=YWORD,BCST=ON |
4 | DATA=ZWORD,SAE=NO | DATA=ZWORD,BCST=OFF |
5 | DATA=ZWORD,SAE=YES,ROUND=UP | DATA=ZWORD,BCST=ON |
6 | reserved | reserved |
7 | DATA=ZWORD,SAE=YES,ROUND=ZERO | reserved |
Požadavek na zarovnání může být přidán ke kterékoli strojové instrukci a také k pseudoinstrukcím D, PROC, PROC1, STRUC. V kapitole alignment jsou uvedeny přípustné hodnoty. Tento instrukční modifikátor má stejný efekt, jako by před instrukcí byla explicitní pseudoinstrukce ALIGN.
Tento modifikátor může být připojen pouze k pseudoinstrukcím
PROC, ENDPROC, PROC1, ENDPROC1.
Jeho hodnota je logická, default je NESTINGCHECK=ON
. Vypnutí kontroly vnoření
vypíná chybové oznámení při nesouhlasu identifikátorů na začátku a konci bloku,
což je potřebné k zavedení vazeb mezi některými pseudoinstrukcemi.
Viz definici maker Procedure a
EndProcedure jako příklad.
Některé instrukce architektury IA-64 pracují s pevně stanovenými registry. €ASM akceptuje dobrovolnou explicitní specifikaci takových registrů; což poslouží jako dokumentace pro lidského čtenáře a může být také využito ke specifikaci šířky adresy nebo k určení nedefaultního segmentového registru.
Unární instrukce FPU (jednotky s pohyblivou tečkou) s implicitní destinací ST0 mohou tento registr explicitně uvádět na místě prvního operandu, nebo jej mohou vynechat. V mnoha dalších instrukcích FPU je defaultní cílový registr ST0 a defaultní zdrojový registr ST1, v tom případě jeden nebo oba operandy mohou být vynechány. Viz ovladače instrukcí FNOP, FCMOVB, FADD, FIADD, FADDP, FXCH, FCOM.
|00000000:000000000000F03F |Mem DQ 1.0 |00000008: | |00000008:DAC1 | FCMOVB ; ST0 = ST1 if Below. |0000000A:DAC1 | FCMOVB ST0,ST1 ; ST0 = ST1 if Below. |0000000C: | |0000000C:DAC7 | FCMOVB ST0,ST7 ; ST0 = ST7 if Below. |0000000E:DAC7 | FCMOVB ST7 ; ST0 = ST7 if Below. |00000010: | |00000010:D8C1 | FADD ; ST0 += ST1. |00000012:D8C1 | FADD ST0,ST1 ; ST0 += ST1. |00000014: | |00000014:DC05[00000000] | FADD ST0,[Mem] ; ST0 += [Mem]. |0000001A:DC05[00000000] | FADD [Mem] ; ST0 += [Mem]. |00000020: | |00000020:DCC7 | FADD ST7,ST0 ; ST7 += ST0. |00000022:DCC7 | FADD ST7 ; ST7 += ST0. |00000024: | |00000024:D9E9 | FLDL2T ; ST0 = log210. |00000026:D9E9 | FLDL2T ST0 ; ST0 = log210.Řetězcové instrukce implicitně adresují zdroj jako paměť [DS:rSI]
nebo port DX
, a cíl jako paměť [ES:rDI]
nebo port DX
.
Vedle jejich bezoperandové verze €ASM toleruje operandy explicitně určující tento zdroj a cíl
s možným přepsáním segmentu a změnou adresní šířky.
Výchozí překladová tabulka je implicitně adresována jako [DS:rBX]
.
€ASM akceptuje volitelný paměťový operand, který může specifikovat přepsání segmentu
a odlišnou šířku bázového registru.
Registr smyčky LOOP může být specifikován jako nepovinný druhý operand.
|00000000:D7 | XLAT |00000001:D7 | XLATB ; XLAT a XLATB jsou identické. |00000002:D7 | XLATB [DS:EBX] ; Segment DS je default, přepis není potřeba. |00000003:26D7 | XLATB [ES:EBX] ; Přepsání segmentu. |00000005:67D7 | XLATB [BX] ; Změna adresní šířky z 32 na 16 bitů. |00000007: | |00000007:E2F6 | LOOP $-8 |00000009:E2F6 | LOOP $-8,ECX ; Výchozí čítač ve 32bitovém módu je ECX. |0000000B:67E2F5 | LOOP $-8,CX ; Registr čítače (jeho adresní šířka) změněna na 16 bitů.Smyčky nejsou v €ASM omezeny na krátkou vzdálenost -128..127.
Pokud je cíl skoku instrukcí LOOP, LOOPcc, JCXZ, JECXZ, JRCXZ
FAR nebo NEAR,
€ASM místo něj přeloží tři instrukce:
LOOP $+2+2 ; Skoč na proxy-skok místo na originální cíl.
JMPS $+JMPSsize+JMPsize ; Přeskoč proxy-skok, jakmile smyčka skončila (rCX je nula).
JMP target ; NEAR nebo FAR nepodmíněný proxy-skok na původní cíl.
|[CODE1] |[CODE1] SEGMENT
|00000000:E366 | JECXZ CloseLabel:
|00000002:E364 | JECXZ CloseLabel:,DIST=SHORT
|00000004:E302EB05E95B000000 | JECXZ CloseLabel:,DIST=NEAR
|0000000D:E302EB07EA[68000000]{0000}| JECXZ CloseLabel:,DIST=FAR
|00000018: |
|00000018:E302EB05E947010000 | JECXZ DistantLabel:
|00000021:E302EB05E93E010000 | JECXZ DistantLabel:,DIST=SHORT
|## W2401 Modifier "DIST=SHORT" could not be obeyed in this instruction.
|0000002A:E302EB05E935010000 | JECXZ DistantLabel:,DIST=NEAR
|00000033:E302EB07EA[68010000]{0000}| JECXZ DistantLabel:,DIST=FAR
|0000003E: |
|0000003E:E302EB07EA[00000000]{0000}| JECXZ FarLabel:
|00000049:E302EB07EA(00000000){0000}| JECXZ FarLabel:,DIST=SHORT
|## W2401 Modifier "DIST=SHORT" could not be obeyed in this instruction.
|00000054:E302EB05E9(00000000) | JECXZ FarLabel:,DIST=NEAR
|0000005D:E302EB07EA[00000000]{0000}| JECXZ FarLabel:,DIST=FAR
|00000068: |CloseLabel:
|00000068:909090909090909090909090~~| DB 256 * B 0x90 ; Kód k oddálení DistantLabel.
|00000168: |DistantLabel:
|[CODE2] |[CODE2] SEGMENT
|00000000: |FarLabel:
Podmíněný skok na vzdálenost překračující bajtový limit -128..127 byl zaveden s generací procesoru 386. Pokud má náš program běžet také na starších CPU, NEAR a FAR podmíněný skok bude EuroAssemblerem přeložen jako dvě instrukce:
J!cc $+J!ccsize+JMPsize ; Přeskoč proxy-skok, pokud platí invertovaná podmínka
JMP target ; NEAR nebo FAR nepodmíněný skok na původní cíl.
Proxy-skok namísto standardního NEAR podmíněného skoku existujícího v CPU=386 bude přeložen, jsou-li splněny tři podmínky:
Ve většině asemblerů mohou mít instrukce PUSH, POP, INC, DEC právě jeden operand. €ASM neomezuje počet operandů, jsou prováděny jeden za druhým ve specifikovaném pořadí. Je-li použit instrukční modifikátor nebo přípona, bude aplikována na všechny operandy. |00000000:57FF370FA06A04 | PUSH EDI,[EDI],FS,4 |00000007:590FA18F0658 | POP ECX,FS,[ESI],EAX |0000000D:40FF07 | INC EAX,[EDI],DATA=DWORD |00000010:48664AFEC9 | DEC EAX,DX,CL
Instrukce AAD
a AAM používají jako výchozí radix číslo 10
pro adjustování AL před dělením nebo po násobení čísel uložených ve formátu BCD.
V €ASM tyto operace akceptují volitelný 8bitový operand, např. AAD 16
.
|00000000:D40A | AAM
|00000002:D40A | AAM 10
|00000004:D410 | AAM 16
|00000006:D50A | AAD
|00000008:D50A | AAD 10
|0000000A:D510 | AAD 16
Pokud oba operandy instrukce TEST určují tentýž registr, druhý operand může být vynechán.
Je-li počet bitů, o který se má rotovat nebo posouvat v instrukcích RCL, ROL, SAL, SHL, RCR, ROR, SAR, SHR roven jedné, druhý operand může být vynechán.
|00000000:85D2 | TEST EDX,EDX |00000002:85D2 | TEST EDX ; Druhý operand TEST je defaultně identický s prvním. |00000004: | |00000004:D1D0 | RCL EAX,1 |00000006:D1D0 | RCL EAX ; Vynechaný počet bitů je roven jedné. |00000008:D165F8 | SHL [EBP-8],1,DATA=DWORD |0000000B:D165F8 | SHL [EBP-8],DATA=DWORDInstrukce, která nedělá nic (no-operation) kromě zabírání určitého času a zvětšování IP registru
je ve všech x86 procesorech implementována jako jednobajtový NOP, konkrétně XCHG rAX,rAX
(operační kód 0x90).
Počínaje Pentium II (CPU=686
) Intel zavedl dedikované mnohobajtové NOP instrukce
s kódy 0x18..0x1F prefixované 0x0F.
Mnohobajtový NOP je k zarovnávání vhodnější než série jednobajtových NOPů,
neboť se z paměti načítá jako jeden celek najednou.
Na starších CPU tento NOP musí být emulován staršími instrukcemi, jako XCHG reg,reg
nebo LEA reg,[reg]
.
[Sandpile] a [NasmInsns]
definují jejich mnemoniky jako nedokumentované instrukce HINT_NOP0, HINT_NOP1, HINT_NOP2..63
.
s jedním operandem požadované délky.
Namísto zahlcení seznamu instrukcí 64 novými mnemonikami zavádí €ASM
pouze jednu mnemoniku HINT_NOP
(s možnými příponami HINT_NOPW, HINT_NOPD, HINT_NOPQ
)
s ordinálním číslem definovaným v prvním operandu a specifikací paměti odsunutou na místo druhého operandu.
Kromě toho €ASM implementuje ještě bezoperandové instrukce NOP1, NOP2, NOP3, NOP4, NOP5, NOP6, NOP7, NOP8, NOP9
zabírající specifikovaný počet bajtů a respektující aktuální úroveň a mód CPU.
Mnemonika | Operační kód (hexa) | Ekvivalentní instrukce v syntaxi €ASM |
---|---|---|
16bit mode, CPU=086 | ||
NOP1 | 90 | XCHG AX,AX |
NOP2 | 87C9 | XCHG CX,CX |
NOP3 | 9087C9 | XCHG AX,AX ; XCHG CX,CX |
NOP4 | 87C987D2 | XCHG CX,CX ; XCHG DX,DX |
NOP5 | 9087C987D2 | XCHG AX,AX ; XCHG CX,CX ; XCHG DX,DX |
NOP6 | 87C987D287DB | XCHG CX,CX ; XCHG DX,DX ; XCHG BX,BX |
NOP7 | 9087C987D287DB | XCHG AX,AX ; XCHG CX,CX ; XCHG DX,DX ; XCHG BX,BX |
NOP8 | 87C987D287DB87E4 | XCHG CX,CX ; XCHG DX,DX ; XCHG BX,BX ; XCHG SP,SP |
NOP9 | 9087C987D287DB87E4 | XCHG AX,AX ; XCHG CX,CX ; XCHG DX,DX ; XCHG BX,BX ; XCHG SP,SP |
16bit mode, CPU=686 | ||
NOP1 | 90 | NOP DATA=WORD |
NOP2 | 6690 | OTOGGLE NOP |
NOP3 | 666790 | OTOGGLE ATOGGLE NOP |
NOP4 | 670F1F00 | NOP [EAX],DATA=WORD |
NOP5 | 670F1F4000 | NOP [EAX],DATA=WORD,DISP=BYTE |
NOP6 | 670F1F442000 | NOP [EAX+0*EAX],DATA=WORD,SCALE=VERBATIM,DISP=BYTE |
NOP7 | 66670F1F442000 | NOP [EAX+0*EAX],DATA=DWORD,SCALE=VERBATIM,DISP=BYTE |
NOP8 | 670F1F8000000000 | NOP [EAX],DATA=WORD,DISP=DWORD |
NOP9 | 670F1F842000000000 | NOP [EAX+0*EAX],DATA=WORD,SCALE=VERBATIM,DISP=DWORD |
32bit mode, CPU=386 | ||
NOP1 | 90 | XCHG EAX,EAX,DATA=DWORD |
NOP2 | 6690 | XCHG AX,AX,DATA=WORD |
NOP3 | 8D4000 | LEA EAX,[EAX],DATA=DWORD |
NOP4 | 8D442000 | LEA EAX,[EAX+0*EAX],DATA=DWORD,SCALE=VERBATIM,DISP=BYTE |
NOP5 | 3E8D442000 | LEA EAX,[DS:EAX+0*EAX],DATA=DWORD,SCALE=VERBATIM,DISP=BYTE |
NOP6 | 8D8000000000 | LEA EAX,[EAX],DATA=DWORD,DISP=DWORD |
NOP7 | 8D842000000000 | LEA EAX,[EAX+0*EAX],DATA=DWORD,SCALE=VERBATIM,DISP=DWORD |
NOP8 | 3E8D842000000000 | LEA EAX,[DS:EAX+0*EAX],DATA=DWORD,SCALE=VERBATIM,DISP=DWORD |
NOP9 | 663E8D842000000000 | LEA AX,[DS:EAX+0*EAX],DATA=WORD,SCALE=VERBATIM,DISP=DWORD |
32bit mode, CPU=686 | ||
NOP1 | 90 | NOP DATA=DWORD |
NOP2 | 6690 | NOP DATA=WORD |
NOP3 | 0F1F00 | NOP [EAX],DATA=DWORD |
NOP4 | 0F1F4000 | NOP [EAX],DATA=DWORD,DISP=BYTE |
NOP5 | 0F1F442000 | NOP [EAX+0*EAX],DATA=DWORD,SCALE=VERBATIM,DISP=BYTE |
NOP6 | 660F1F442000 | NOP [EAX+0*EAX],DATA=WORD,SCALE=VERBATIM,DISP=BYTE |
NOP7 | 0F1F8000000000 | NOP [EAX],DATA=DWORD,DISP=DWORD |
NOP8 | 0F1F842000000000 | NOP [EAX+0*EAX],DATA=DWORD,SCALE=VERBATIM,DISP=DWORD |
NOP9 | 660F1F842000000000 | NOP [EAX+0*EAX],DATA=WORD,SCALE=VERBATIM,DISP=DWORD |
64bit mode, CPU=X64 | ||
NOP1 | 90 | NOP DATA=DWORD |
NOP2 | 6690 | NOP DATA=WORD |
NOP3 | 0F1F00 | NOP [RAX],DATA=DWORD |
NOP4 | 0F1F4000 | NOP [RAX],DATA=DWORD,DISP=BYTE |
NOP5 | 0F1F442000 | NOP [RAX+0*RAX],DATA=DWORD,SCALE=VERBATIM,DISP=BYTE |
NOP6 | 660F1F442000 | NOP [RAX+0*RAX],DATA=WORD,SCALE=VERBATIM,DISP=BYTE |
NOP7 | 0F1F8000000000 | NOP [RAX],DATA=DWORD,DISP=DWORD |
NOP8 | 0F1F842000000000 | NOP [RAX+0*RAX],DATA=DWORD,SCALE=VERBATIM,DISP=DWORD |
NOP9 | 660F1F842000000000 | NOP [RAX+0*RAX],DATA=WORD,SCALE=VERBATIM,DISP=DWORD |
Mnemonika | Operační kód (hexa) | Ekvivalentní instrukce v syntaxi €ASM |
Instrukce PINSRB, PINSRW, PINSRD (vlož Byte/Word/Dword do cílového registru XMM) akceptují jako zdrojový operand č.2 ne pouze obecný registr (GPR) odpovídající šířky, ale i širší registr. Bude z něj použit pouze nejnižší BYTE|WORD|DWORD.
|00000000:660F3A20C902 | PINSRB XMM1,CL,2 |00000006:660F3A20C902 | PINSRB XMM1,CX,2 |0000000C:660F3A20C902 | PINSRB XMM1,ECX,2 |00000012: | |00000012:660FC4C902 | PINSRW XMM1,CX,2 |00000017:660FC4C902 | PINSRW XMM1,ECX,2Instrukce proměnného mísení používá pevně implikovaný registr XMM0 jako maskovací registr. €ASM dovoluje explicitní specifikaci tohoto registru jako třetí operand.
|00000000:660F3815CA | BLENDVPD XMM1,XMM2 |00000005:660F3815CA | BLENDVPD XMM1,XMM2,XMM0 |0000000A: | |0000000A:660F3814CA | BLENDVPS XMM1,XMM2 |0000000F:660F3814CA | BLENDVPS XMM1,XMM2,XMM0 |00000014: | |00000014:660F3810CA | PBLENDVB XMM1,XMM2 |00000019:660F3810CA | PBLENDVB XMM1,XMM2,XMM0Maskovatelné kopírování do paměti používá jako pevný cíl destinaci [DS:rDI]. €ASM dovoluje explicitní specifikaci cílové paměti jako volitelný první operand.
|00000000:0FF7CA | MASKMOVQ MM1,MM2 |00000003:0FF7CA | MASKMOVQ [DS:EDI],MM1,MM2 ; Defaultní destinace je [DS:EDI]. |00000006:260FF7CA | MASKMOVQ [ES:EDI],MM1,MM2 ; Přepis segmentu. |0000000A: | |0000000A:660FF7CA | MASKMOVDQU XMM1,XMM2 |0000000E:660FF7CA | MASKMOVDQU [DS:EDI],XMM1,XMM2 ; Defaultní destinace je [DS:EDI]. |00000012:26660FF7CA | MASKMOVDQU [ES:EDI],XMM1,XMM2 ; Přepis segmentu.Segmentový deskriptor u systémových instrukcí VERR, VERW (operand 1) a LAR, LSL (operand 2) může být specifikován jako 16bitová paměťová proměnná anebo 16, 32 nebo 64bitový obecný registr GPR (bude použito jen dolních 16 bitů).
|00000000:0F00E6 | VERR SI |00000003:0F00E6 | VERR ESI |00000006: | |00000006:0F00EE | VERW SI |00000009:0F00EE | VERW ESI |0000000C: | |0000000C:660F02C6 | LAR AX,SI |00000010:660F02C6 | LAR AX,ESI |00000014:0F02C6 | LAR EAX,SI |00000017:0F02C6 | LAR EAX,ESI |0000001A: | |0000001A:660F03C6 | LSL AX,SI |0000001E:660F03C6 | LSL AX,ESI |00000022:0F03C6 | LSL EAX,SI |00000025:0F03C6 | LSL EAX,ESI€ASM implementuje několik instrukcí, které nejsou dokumentovány v oficiálních specifikacích výrobců CPU.
Nemusejí fungovat se všemi generacemi procesorů a vyžadují explicitní volbu EUROASM UNDOC=ENABLED
.
Pro informaci viz jejich obslužné procedury (handlery) BB0_RESET, CMPXCHG486, F4X4, FCOM2, FCOMP5, FFREEP, FMUL4X4, FNSETPM, FRSTPM, FSBP1, FSBP2, FSBP3, FSTDW, FSTP1, FSTP8, FSTP9, FSTSG, FXCH4, FXCH7, HCF, HINT_NOP, IBTS, ICEBP, INT1, JMPE, LOADALL, LOADALL286, PREFETCHWT1, PSRAQ, SAL2, SALC, SETALC, SMINTOLD, TEST2, UD0, UD1, UD2A, UMOV, XBTS, VLDQQU.
Pseudoinstrukce (také se jim říká direktivy) jsou příkazy asembleru formálně podobné běžným strojovým instrukcím — mnoho z nich může mít pole návěstí a operandy. Některé pseudoinstrukce (ALIGN a D) mohou dokonce emitovat data a kód.
Pseuodinstrukcí EUROASM programátor ovlivňuje různá nastavení EuroAssembleru samotného
- volby EUROASM. Příslušná volba je určena klíčovým operandem.
Stejné klíče jsou použity také v sekci [EUROASM]
konfiguračního souboru
euroasm.ini.
Volby specifikované touto pseudoinstrukcí přepisují defaultní volby z konfiguračního souboru. Jména voleb nezávisí na velikosti písmen.
Aktuální hodnota každé volby může být získána pomocí
EUROASM systémových %^proměnných se shodným jménem, například
InfoMsg DB "This program uses code page %^CODEPAGE.",13,10,0
Volbám očekávajícím booleovskou hodnotu může být předloženo výčtové slovo TRUE, YES, ON, ENABLE, ENABLED
nebo FALSE, NO, OFF, DISABLE, DISABLED
nebo mohou obsahovat
logický výraz.
Vedle klíčových operandů pseudoinstrukce EUROASM rozeznává ještě dva pořadové operandy s výčtovou hodnotou PUSH nebo POP. €ASM udržuje speciální zásobník voleb a tyto dvě direktivy dovolují uschovat a obnovit celý soubor voleb EuroAssembleru na tento zásobník. To se hodí u maker, které dočasně vyžadují nějakou neobvyklou volbu. Slepé nastavení takové volby by mělo vedlejší efekt na instrukce následující po vyvolání makra (EUROASM je přepínací instrukce). Proto je lepší uložit aktuální volby na jejich zásobník na počátku makra a obnovit je na jeho konci. Příklad:
SomeMacro %MACRO ; Definice makra. EUROASM PUSH, NOWARN=2102 ; Ulož všechny volby a pak potlač varování W2102. ; Tady budou instrukce, které mohou vyvolat W2102. ... EUROASM POP ; Obnov zásobník voleb, W2102 již není potlačena. %ENDMACRO SomeMacro
Booleovská volba s výchozí hodnotou AUTOALIGN=ON
. Paměťové proměnné
definované nebo rezervované pseudoinstrukcí D budou implicitně zarovnány
v souladu se svým typem (TYPE#).
K zarovnaným proměnným může CPU přistupovat rychleji, na druhé straně tato volba může nafouknout velikost programu, pokud jsou data různých typů často mísena. Je proto lepší manuálně seskupovat datové položky stejných velikostí.
EUROASM AUTOALIGN=
.Strukturované datové proměnné (definované pomocí DS structure_name
)
se nezarovnávají podle svého největšího členu, jsou zarovnány na šířku segmentu
(WORD, DWORD or QWORD) pokud je AUTOALIGN=ENABLED.
Programátor by měl definovat strukturu s ohledem na přirozené zarovnání jejich členů. To je zvláště důležité v 64bitovém módu, kde API vyžaduje zarovnaná data. Během konverze špatně navržených 32bitových struktur se musely uměle vkládat výplňové členy typu DWORD zarovnávající délky na QWORD a zaokrouhlující délku struktury na násobek osmi. Viz například WinAPI strukturu MSG.
Autoalignment se neaplikuje na strojové instrukce.
Chcete-li proceduru zarovnanou na počátek stránky vyrovnávací paměti (kvůli lepší výkonnosti),
měla by být zarovnána explicitně, např.Rapid PROC ALIGN=OWORD
.
Booleovská volba s výchozí hodnotou AUTOSEGMENT=ON
.
Sekce, do níž €ASM emituje, je automaticky změněna, aby byla v souladu s účelem
(PURPOSE=) segmentu.
Jedná-li se o strojovou instrukci nebo strojový prefix nebo PROC,
€ASM přepne do naposledy definované sekce s účelem PURPOSE=CODE.
Podobně, pokud pseudoinstrukce D a její klony včetně DI
definuje nebo rezervuje data, současná sekce je přepnuta na poslední sekci
typu DATA nebo BSS.
Pseudoinstrukce ALIGN, makra a všechny neemitující operace, jako např. EQU nebo samotné návěstí, nemění současnou sekci.
Spoléháte-li na autosegmentaci, vyvarujte se případu, kdy nová sekce začíná voláním makra, pseudoinstrukcí ALIGN nebo samotným návěstím. Takové povely nepřepnou současnou sekci. Možná budete chtít vložitNOP
neboPROC
k přepnutí do kódové sekce,DB 0
k přepnutí na data, aneboDB
k přepnutí na BSS sekci. Příklad:EUROASM AUTOSEGMENT=ON Hello PROGRAM FORMAT=PE, ENTRY=Main: INCLUDE winapi.htm; Základní makra. Title DB "World!",0 ; Korektně automaticky přepla do [.data].Main: StdOutput Title ; Makro nepřeplo do [.text], jak bylo asi očekáváno.TerminateProgram ENDPROGRAM Hello ; Hello.exe zde nefunguje, neboť ENTRY je v sekci [.data].Návěstí Main: chybně zůstalo v předchozí sekci [.data]. Náorava je prostá:
- vložit strojovou instrukci
Main: NOP
- nebo vytvořit proceduru
Main: PROC
- nebo přepnout ručně:
[.text]
nad řádkemMain: StdOutput Title
EUROASM AUTOSEGMENT=ON Hello PROGRAM FORMAT=PE, ENTRY=Main: INCLUDE winapi.htm; Základní makra. Title DB "World!",0 ; Korektně automaticky přepla do [.data]. Main: PROC ; Korektně automaticky přepla do [.text]. StdOutput Title TerminateProgram ENDPROC Main: ENDPROGRAM Hello ; Hello.exe works as expected.
AUTOSEGMENT= je slabá volba, je automaticky vypínána, když programátor změní aktuální sekci
uvedením [jména_sekce]
v poli návěstí.
Potřebujete-li udržet AUTOSEGMENT zapnutý po ruční změně sekce, musíte pak tuto volbu explicitně znovu zapnout pomocíEUROASM AUTOSEGMENT=ON
, anebo uložit její stav pomocíEUROASM PUSH
a pak jej obnovit pomocíEUROASM POP
.
€ASM umí používat řetězce v Unicode za běhu programu, ale definice dat ve zdrojovém kódu je i tak definována v bajtech. Volba CODEPAGE= říká EuroAssembleru. která kódová stránka by měla být aktuálně použita k převodu bajtů zdrojového kódu na Unicode v asm-time.
Kódová stránka může být určena přímou 16bitovou celočíselnou hodnotou podle
[CodePageMS],
např. CODEPAGE=1253
pro řeckou abecedu.
Hodnoty CODEPAGE= mohou být rovněž určeny jako výčtové slovo, např.
CODEPAGE=CP852, CODEPAGE=WINDOWS-1252, CODEPAGE=ISO-8859-2
atd.
Pro kompletní seznam viz DictCodePages
U jmen těchto specifikací nezáleží na velikosti písmen.
Přestože některé tyto konstanty mohou vypadat jako aritmetické rozdíly, jsou rozeznávány doslova a nejsou vyhodnocovány jako čísla.
Výchozí a zároveň doporučená hodnota je CODEPAGE=UTF-8
.
Viz též kódování znaků výše.
Jestliže je v pseudoinstrukci z rodiny INCLUDE* specifikován soubor bez cesty,
€ASM jej bude hledat v adresářích definovaných ve volbě EUROASM INCLUDEPATH=
.
Cesty zde mohou být odděleny středníkem ; nebo čárkou , a celý seznam cest
by měl být v uvozovkách.
Jako oddělovač složek lze použít zpětné \ i dopředné / lomítko.
Poslední lomítko může být vynecháno. Výchozí hodnotou je INCLUDEPATH="./,./maclib,../maclib,"
.
Tato syntaxe nepovoluje názvy složek začínající či končící mezerou, ovšem takových jmen byste se měli vyvarovat i z jiných důvodů.
Je-li linkovaný soubor specifikován bez cesty, €ASM jej bude hledat
v adresářích definovaných ve volbě EUROASM LINKPATH=
.
Cesty zde mohou být odděleny středníkem ; nebo čárkou , a celý seznam cest
by měl být v uvozovkách.
Jako oddělovač složek lze použít zpětné \ i dopředné / lomítko.
Poslední lomítko může být vynecháno. Výchozí hodnotou je LINKPATH="./,./objlib,../objlib,"
.
Je-li dynamicky sdílený objekt (DSO neboli objekt ELFSO) specifikován bez cesty,
Linuxový dynamický linker jej bude hledat v adresářích definovaných touto volbou
EUROASM RUNPATH=
.
Výchozí hodnotou je RUNPATH="./,./objlib,../objlib,"
.
Parametr MAXINCLUSIONS omezuje maximální povolený počet úspěšných provedení příkazů INCLUDE*. Brání to ve vyčerpání prostředků, pokud bychom omylem zadali rekurzivní smyčku INCLUDE.
Výchozí hodnota je EUROASM MAXINCLUSIONS=64
.
Tento parametr omezuje maximální povolený počet linkování souborů. Opět to chrání před rekurzivní smyčkou příkazů LINK.
Výchozí hodnotou je EUROASM MAXLINKS=64
.
Ne všechny strojové instrukce architektury IA-32 jsou dostupné na všech typech procesorů.
Tato volba EUROASM určuje minimální (nejstarší) model CPU, na kterém má program běžet.
Možnosti jsou
086 alias 8086,
186,
286,
386,
486,
586 alias PENTIUM,
686 alias P6,
X64.
Výchozí hodnota je EUROASM CPU=586
. 64bitové programy potřebují povolit EUROASM CPU=X64
.
EuroAssembler se chová, jako když vyšší verze CPU podporuje všechny instrukce předchozích verzí.
Následující skupina booleovských voleb říká EuroAssembleru, která rozšíření procesoru jsou vyžadována na cílovém počítači. Ve výchozím stavu jsou všechny volby vypnuty, musíte explicitně povolit každé rozšíření, pro něž budete programovat.
ABM=
: instrukce Advanced Bit Manipulation.
AES=
instrukce Advance Encryption Standard (AESNI).
AMD=
instrukce specifické pro výrobce CPU AMD.
CET=
instrukce Control-flow Enforcement Technology.
CYRIX=
instrukce specifické pro výrobce CPU CYRIX.
D3NOW=
instrukce 3DNow! od AMD.
EVEX=
instrukce AVX-512 kódované s prefixem EVEX.
FMA=
: instrukce Fused Multiply-Add.
FPU=
instrukce pro Floating-Point Unit (matematický koprocesor).
LWP=
instrukce pro LightWeight Profiling od AMD.
MMX=
: instrukce pro MultiMedia Extensions.
MPX=
: instrukce pro Memory Protection Extensions.
MVEX=
instrukce AVX-512 kódované s prefixem MVEX.
PRIV=
: instrukce v privilegovaném módu.
PROT=
: instrukce v chráněném módu.
SGX=
: instrukce pro Software Guard Extensions.
SHA=
instrukce pro Secure Hash Algorithm od Intelu.
SPEC=
ostatní speciální instrukce.
SVM=
: instrukce pro Shared Virtual Memory.
TSX=
: instrukce pro Transactional Synchronization Extensions od Intelu.
UNDOC=
nedokumentované instrukce.
VIA=
instrukce specifické pro výrobce CPU VIA Geode.
VMX=
instrukce pro Virtual Machine Extensions.
XOP=
instrukce AVX kódované s prefixem XOP od AMD.
Tato volba definuje, která generace instrukcí Single Instruction Multiple Data (SIMD)
je vyžadována k překladu následných instrukcí.
Možné výčtové hodnoty jsou
SSE1 alias SSE alias booleovské pravda,
SSE2,
SSE3,
SSSE3,
SSE4,
SSE4.1,
SSE4.2,
AVX,
AVX2,
AVX512.
Výchozí hodnotou je SIMD=DISABLED
(neočekávají se žádné instrukce SIMD).
Volby generace CPU, rozšíření CPU, generace SIMD neomezují €ASM v překladu instrukcí pro vyšší procesor, avšak pokud strojová instrukce vyžaduje generaci momentálně nepovolenou volbami EUROASM, bude vydáno varování. To by mělo upozornit, že program nepoběží na každém PC, případně že jste se dopustili překlepu v menmonice.
Tyto dvě booleovské volby jsou určeny k ladění samotného procesu překladu, viz také pseudoinstrukci %DISPLAY. Jsou-li volby povoleny, €ASM vkládá pod každou asemblovanou instrukci diagnostické oznámení zobrazující, jak byla instrukce rozdělena do polí a jaké instrukční modifikátory byly použity při jejím zakódování.Příklad:
EUROASM DISPLAYSTM=ON .L: MOV EAX,[ESI+16],ALIGN=DWORD EUROASM DISPLAYSTM=OFF, DISPLAYENC=ON LEA EDX,[ESI+16] ADD EAX,EDX
Listing předchozího příkladu:
| | EUROASM DISPLAYSTM=ON |00000000:8B4610 |.L: MOV EAX,[ESI+16],ALIGN=DWORD |# D1010 **** DISPLAYSTM ".L: MOV EAX,[~~ALIGN=DWORD " |# D1020 label=".L" |# D1040 machine operation="MOV" |# D1050 ordinal operand number=1,value="EAX" |# D1050 ordinal operand number=2,value="[ESI+16]" |# D1060 keyword operand,name="ALIGN",value="DWORD" | | EUROASM DISPLAYSTM=OFF, DISPLAYENC=ON |# D1010 **** DISPLAYSTM "EUROASM DISPL~~SPLAYENC=ON " |# D1040 pseudo operation="EUROASM" |# D1060 keyword operand,name="DISPLAYSTM",value="OFF" |# D1060 keyword operand,name="DISPLAYENC",value="ON" |00000003:8D5610 | LEA EDX,[ESI+16] |# D1080 Emitted size=3,DATA=DWORD,DISP=BYTE,SCALE=SMART,ADDR=ABS. |00000006:01D0 | ADD EAX,EDX |# D1080 Emitted size=2,CODE=SHORT,DATA=DWORD.Volby DUMP=, DUMPWIDTH= and DUMPALL= ovlivňují vzhled sloupce s emitovaným kódem v souboru listingu.
Booleovská volba DUMP=
kompletně vypíná tento sloupec;
listing pak kopíruje zdrojový text takřka doslovně. Výchozí hodnota je DUMP=ON
.
DUMPWIDTH= nastavuje šířku levého sloupce v listingu, neboli určuje počet znaků,
které se vejdou mezi počáteční | a koncovou svislou čáru | (včetně těchto dvou znaků).
Výchozí hodnota je DUMPWIDTH=27
, což vyhovuje pro instrukce dlouhé až 8 bajtů.
Povolená hodnota tohoto parametru je mezi 16 a 128 znaky.
Vypisovaná data sestávají z ofsetu (4 nebo 8 hexadecimálních číslic), separátoru : a dvou hexadecimálních číslic na každý bajt generovaného kódu.
Je-li generovaný kód delší, než se vejde do sloupce dumpu,
booleovská volba DUMPALL=
rozhoduje, zda zbytek kódu bude zahozen
(vypuštění je indikováno tildou ~ místo posledního znaku),
nebo zda do listingu budou vkládány přídavné řádky, dokud nebude všechen generovaný kód vypsán.
Výchozí volba je DUMPALL=OFF
.
Viz také popis souboru listingu.
Opatrně s povolováním DUMPALL=ON s dlouhými definicemi dat, jako
DB 2048 * B 0
, které mohou zahltit listing mnoha řádky bezcenného výpisu.
Volba určuje název souboru s listingem. Jako výchozí volba se používá
LISTFILE="%^SourceName%^SourceExt.lst"
, tedy kopie jména a přípony zdrojového souboru,
ke které se připojí .lst
.
Není-li určeno jinak, listing se vytváří ve stejném adresáři jako jeho zdrojový soubor.
Ke každému zdrojovému souboru se generuje jeho listing.
Rodina booleovských voleb EUROASM LIST* určuje, co se bude zapisovat do souboru listingu.
LIST=OFF zcela potlačí generování listingu, dokud není opět zapnut pomocí LIST=ON.
Výchozí volba je LIST=ON
.
Vypnutí byť jen malé části listingu způsobí, že již nebude znovupoužitelný jako zdrojový soubor,
Obsah souboru importovaného pomocí INCLUDE* je ve výchozím stavu vynechán z listingu (LISTINCLUDE=OFF
).
Pokud je tato volba zapnuta (LISTINCLUDE=ON
), z pseudoinstrukce INCLUDE se stane komentář
a pod ní budou uvedeny všechny instrukce ze souboru.
LISTMACRO= určuje, zda instrukce z expanze makra budou zobrazeny v listingu.
Výchozí stav je LISTMACRO=OFF
, kdy je zobrazena pouze invokace (volání) makra.
Volba LISTREPEAT= se podobá LISTMACRO= až na to, že řídí vypisování
obsahu bloků %FOR, %WHILE a %REPEAT.
Ve výchozím stavu (LISTREPEAT=OFF
) se vypisuje pouze první iterace bloku.
Pokud se v instrukci vyskytuje preprocesní %proměnná a pokud je volba EUROASM LISTVAR=ON
,
instrukce je v listingu duplikována pod původní instrukcí jako její komentář, avšak
%proměnné jsou přitom nahrazeny expandovaným textem. Výchozí volba je LISTVAR=OFF
.
Viz také popis listingového souboru výše.
Booleovská volba UNICODE= určuje šířku znaků.
Jsou-li neurčité řetězce definovány bez typového určení B nebo U pomocí D "neurčitý řetězec"
nebo ="literální neurčitý řetězec"
, tato volba určuje, zda se mají považovat
za řetězce bajtů (8bitových znaků) nebo unichar (16bitových znaků).
Hodnota UNICODE signalizovaná %^proměnnou %^UNICODE
je rovněž konzultována
v makrech a definicích struktur, které mají odlišné verze pro ANSI (8bitové) nebo WIDE (16bitové) kódování.
Dále je konzultována v makrech WinAPI (32 bitů)
a WinABI (64 bitů) k určení verze
Windows API funkce (ANSI nebo WIDE), jež má být volána.
Mnoho funkcí ve WinAPI očekává specifikaci délky řetězců ve znacích namísto v bajtech. Atributový operátor SIZE# vrací velikost řetězce vždy v bajtech. Tento rozpor se dá vyřešit pomocí systémové %^proměnné %^UNICODE:
aString D "String" ; Symbol aString definuje 6 bajtů, pokud UNICODE=OFF anebo 12 bajtů, pokud UNICODE=ON. %IF %^UNICODE ; Symbol je ve WIDE verzi. MOV ECX, SIZE# aString / 2 %ELSE ; Symbol je v ANSI verzi. MOV ECX,SIZE# aString %ENDIF ; ECX je nyní naplněn počtem znaků v aString.Vychytralejší řešení využívá faktu, že %^UNICODE (a všechny další booleovské %^proměnné) expanduje buď do 0 nebo do -1, a že posun vlevo o negativní hodnotu je kalkulován jako posun vpravo o negovanou hodnotu. Je-li %^UNICODE=-1, velikost v bajtech bude posunuta doprava o 1 bit, což odpovídá dělění dvěma:
aString D "String" ; Symbol aString definuje 6 bajtů, pokud UNICODE=OFF anebo 12 bajtů, pokud UNICODE=ON. MOV ECX, SIZE# aString << %^UNICODE ; ECX je nyní naplněn počtem znaků v aString.
Tato booleovská volba specifikuje, zda má být přeložena ladicí verze programu.
Když je EUROASM DEBUG=ENABLED
, linker zařazuje tabulku symbolů
nebo další ladicí informace do výstupního programu (formátu PE).
Makra mohou měnit své chování v závislosti na podmínce %IF %^DEBUG
.
Konečné vydání vašeho programu by mělo být přeloženo s touto vobou vypnutou.
Tato booleovská volba specifikuje, zda má být přeložena profilovatelná verze programu. Profilování zatím v této verzi €ASM není implementováno.
Konečné vydání vašeho programu by mělo být přeloženo s touto vobou vypnutou.
Volby WARN= a NOWARN= určují, která informační a varovná oznámení mají být vydávána během činnosti asembleru. Pomocí NOWARN= lze potlačit oznámení s identifikátorem pod 4000. Potlačená oznámení nemají vliv na konečnou hodnotu ERRORLEVEL. Uživatelem definovaná oznámení (U5000..U5999) a chyby s vyšší závažností potlačit nelze.
Hodnotou volby je buď číslo, nebo rozsah čísel (range), která by neměla přesáhnout 3999.
Operandy WARN= a NOWARN= se mohou v pseudoinstrukci EUROASM opakovat,
budou zpracovány zleva doprava. Například EUROASM NOWARN=0600..0999, WARN=705
potlačí informativní oznámení I0600 až I0999 s výjimkou oznámení I0705, které zůstává povoleno.
Výchozí hodnota je WARN=0..3999
(všechna oznámení povolena}.
Pseudoinstrukce PROGRAM a ENDPROGRAM definují blok zdrojového kódu, který generuje samostatný výstupní soubor.
V mnoha jiných asemblerech je to celý zdrojový soubor, který vytváří výstupní soubor, někdy zvaný
modul nebo jednotka kompilace. Například povel
nasm -f win32 HelloWorld.asm -o HelloWorld.obj
instruuje NetWide Assembler, aby z celého zdrojového souboru HelloWorld.asm
vygeneroval soubor ve formátu COFF HelloWorld.obj
.
V €ASM může být z jednoho zdrojového souboru generován více než jeden výstupní soubor
povelem euroasm HelloWorld.asm
, pokud zdrojový soubor obsahuje více než jeden blok PROGRAM..ENDPROGRAM.
Návěstí pseudoinstrukce PROGRAM reprezentuje název výstupního programu. I když nedefinuje symbol, jméno musí dodržovat pravidla pro názvy symbolů, tj. písmeno následované dalšími písmeny a číslicemi. Tentýž identifikátor může být použit jako první a jediný operand odpovídající pseudoinstrukce ENDPROGRAM.
Jeden zdrojový soubor může obsahovat více programových bloků a tyto se mohou vnořovat. Každý blok PROGRAM..ENDPROGRAM se překládá do jiného výstupního souboru.
Symboly definované v programu nejsou viditelné mimo svůj blok PROGRAM.ENDPROGRAM. Chce-li program volat návěstí z jiného programu, musí být označeno jako EXTERN a PUBLIC, i kdyby oba programy ležely v jednom zdrojovém souboru nebo kdyby byl jeden vnořen ve druhém.
Na druhé straně preprocesní %proměnné, definice maker a volby EUROASM jsou viditelné v celém zdrojovém souboru a mohou tak přenášet informace mezi programy v asm-time. Viz vzorový program LockTest jako příklad.
Pseudoinstrukce PROGRAM má spoustu klíčových parametrů specifikujících vlastnosti výstupního souboru.
Stejné klíče jsou použity v oddílu [PROGRAM]
konfiguračního souboru
euroasm.ini
Hodnoty všech voleb programu mohou být zjišťovány v asm-time pomocí
systémových %^proměnných.
Například v oznámení InfoMsg DB "This is a %^WIDTH-bit program.",13,10,0
bude systémová %^proměnná %^WIDTH
nahrazena aktuální šířkou programu (16, 32 nebo 64).
Na rozdíl od klíčových parametrů EUROASM, které ovlivňovaly pouze část souboru, volby u pseudoinstrukce PROGRAM ovlivňují celý program en bloc. Nemůžeme mít půlku programu s grafickým subsystémem a zbytek s konzolovým. Proto jsou volbyLISTMAP=, LISTGLOBALS=, LISTLITERALS=
vlastnostmi pseudoinstrukce PROGRAM, aleLISTINCLUDE=, LISTMACRO=, LISTREPEAT=, LISTVAR=
jsou vlastnostmi EUROASM.
Formát a souborová přípona výstupního souboru je určena tímto parametrem programu.
FORMAT= | Přípona výstupního souboru | Šířka programu |
Paměťový model | Popis formátu |
---|---|---|---|---|
BIN | .bin | 16bits | TINY | Binární soubor |
BOOT | .sec | 16bits | TINY | Bootovatelný soubor |
COM | .com | 16bits | TINY | Spustitelný soubor DOS/CPM |
ELF | .o | 32bits | FLAT | Linkovatelný objektový modul |
ELFX | .x | 32bits | FLAT | Spustitelný soubor pro Linux |
ELFSO | .so | 32bits | FLAT | Dynamicky sdílený soubor pro Linux |
OMF | .obj | 16bits | SMALL | Linkovatelný objektový modul |
LIBOMF | .lib | 16bits | SMALL | Objektová knihovna ve formátu OMF |
MZ | .exe | 16bits | SMALL | Spustitelný program pro DOS |
COFF | .obj | 32bits | FLAT | Linkovatelný objektový modul |
LIBCOF | .lib | 32bits | FLAT | Objektová knihovna ve formátu COFF |
PE | .exe | 32bits | FLAT | Spustitelný program pro MS-Windows |
DLL | .dll | 32bits | FLAT | Dynamická knihovna pro MS-Windows |
Výchozí formát (při vynechání PROGRAM FORMAT=
) je BIN
.
Viz také programové formáty níže pro více podrobností.
Tento parametr určuje šířku a mód programu:
EUROASM CPU=X64
by měla být také nastavena.)Programová šířka zároveň představuje výchozí šířku všech jeho segmentů. Její hodnotou je číselný výraz vyhodnocený jako 16, 32, 64, nebo 0. Prázdná nebo nulová hodnota (výchozí stav) určuje, že šířku programu má stanovit interně €ASM v souladu s výchozími šířkami v tabulce FORMAT=. Nicméně pokud je segment definován, můžeme mu předepsat odlišnou šířku. €ASM neprotestuje proti mísení 16bitových a 32bitových segmentů v jednom programu.
Paměťový model určuje velikosti a distance kódu a dat. Jeho hlavní funkcí je nastavit výchozí hodnotu vzdáleností (DIST) pro segmenty a procedury v programu.
Programová volba MODEL= se bere v úvahu v procedurách (PROC, PROC1)
a v instrukcích předávání zpracování (JMP, CALL, RET) bez explicitně stanovené distance.
V monokódových modelech (TINY,SMALL,COMPACT,FLAT) je výchozí vzdálenost DIST=NEAR.
V mnohokódových modelech (MEDIUM,LARGE,HUGE) je výchozí vzdálenost DIST=FAR.
V monodatových modelech (TINY,SMALL,MEDIUM,FLAT) jsou všechna data adresována relativně k začátku datové grupy.
V mnohodatových modelech (COMPACT,LARGE,HUGE) je na programátorovi, aby naplnil segmentový registr
paragrafovou adresou dat, než k nim bude přistupováno.
MODEL= | Výchozí vlastnosti segmentu | Vlastnosti při linkování | Obvyklé využití | |||||
---|---|---|---|---|---|---|---|---|
Distance CODE | Distance DATA | Šířka segmentu |
Mnoho- kódový | Mnoho- datový |
Překrývání segmentů | Mód CPU | Použito ve formátech | |
TINY | NEAR | NEAR | 16 | ne | ne | ano | real | COM |
SMALL | NEAR | NEAR | 16 | ne | ne | ne | real | MZ, OMF |
MEDIUM | FAR | NEAR | 16 | ano | ne | ne | real | MZ, OMF |
COMPACT | NEAR | FAR | 16 | ne | ano | ne | real | MZ, OMF |
LARGE | FAR | FAR | 16 | ano | ano | ne | real | MZ, OMF |
HUGE | FAR | FAR | 32 | ano | ano | ne | real | MZ, OMF |
FLAT | NEAR | NEAR | 32,64 | ne | ne | ano | protected | ELF, PE, DLL, COFF |
Prázdná hodnota (výchozí stav) určuje, že paměťový model má stanovit interně €ASM v souladu s hodnotou v tabulce FORMAT=.
Subsystém je číselný identifikátor v hlavičce souboru Portable Executable, který specifikuje, zda MS-Windows má vytvářet novou konzolu když PE program startuje. Hodnotou subsystému je číslo nebo jedno ze slov vyjmenovaných v této tabulce:
SUBSYSTEM= | Hodnota | Poznámka |
---|---|---|
0 | 0 | Neznámý subsystém. |
1 | NATIVE | Subsystém není použit (ovladač zařízení). |
2 | GUI | Grafický subsystém MS-Windows. |
3 | CON | Konzolový (znakový) subsystém MS-Windows. |
5 | OS2 | Znakový subsystém OS/2. |
7 | POSIX | Znakový subsystém Posix. |
8 | WXD | Nativní ovladač MS-Windows 95/98. |
9 | WCE | Grafický subsystém MS-Windows CE. |
Výchozí hodnota je SUBSYSTEM=CON
.
Změňte ji na SUBSYSTEM=GUI
, pokud váš spustitelný program
vytváří grafická okna, místo aby se spokojil se standardním textovým vstupem a výstupem.
Tento parametr určuje adresu, od které začíná běžet spustitelný program. Obvykle je to návěstí, jehož adresa bude vložena do CS:rIP když zaváděč předává zpracování programu při jeho spuštění.
Výchozí hodnota parametru je prázdná,
v tom případě ji €ASM nastaví na 0 pokud PROGRAM FORMAT=BIN
nebo na 256 pokud PROGRAM FORMAT=COM
nebo na 0x7C00 pokud PROGRAM FORMAT=BOOT
.
U ostatních spustitelných programů €ASM ohlásí chybu, když ENTRY= nebylo zadáno.
Tento parametr omezuje počet průchodů zdrojovým kódem od pseudoinstrukce PROGRAM až po ENDPROGRAM. O počtu průchodů rozhoduje €ASM, tento parametr pouze určuje horní limit.
EuroAssembler opakuje průchody, dokud se mění ofsety symbolů. Jakmile jsou všechny stabilní, provede poslední (finální) průchod.
Za určitých (vzácných) okolností to může vést k oscilacím délky emitovaného kódu kvůli optimalizacím délky SHORT a NEAR skoků. V tom případě by €ASM vyžadoval další a další průchody. Jakmile se číslo průchodu přiblíží k %^MAXPASSES-1, tento předposlední průchod se označí jako fixing pass. V tomto průchodu mohou ofsety pouze růst ke vyšším hodnotám; nadbytečné místo se zaplňuje bajty prázdné operace NOP. Viz test t7181 coby příklad oscilujícího programu s fixing průchodem.
Výchozí hodnotou je MAXPASSES=32. Asi ji budete potřebovat zvýšit jen v extrémních případech programů se spoustou maker a podmíněných konstruktů. Ve svých programech jsem dosáhl maxima 44 průchodů spotřebovaných během překladu modulu iiz.htm.
Parametr MAXEXPANSIONS= omezuje počet expanzí bloků typu %FOR, %WHILE, %REPEAT nebo %MACRO.
€ASM deklaruje číselnou vlastnost programu zvanou %.
a zvyšuje její hodnotu při expanzi bloku. Jakmile hodnota přesáhne volbu MAXEXPANSIONS,
€ASM vydá oznámení o chybě a další expanze zastaví.
Výchozí hodnota je MAXEXPANSIONS=65536.
Tento mechanismus chrání €ASM před vyčerpáním paměťových prostředků, pokud některá špatně napsaná preprocesní smyčka včas neskončí.
Totéž počitadlo expanzí se využívá k udržování hodnoty automatické %proměnné %..
Volba OUTFILE= specifikuje jméno spustitelného nebo linkovatelného výstupního souboru.
Jméne je vztaženo k aktuálnímu adresáři, není-li specifikováno jinak.
Výchozí hodnotou je OUTFILE="%^PROGRAM"
následovaný příponou určenou volbou FORMAT=.
Kupříkladu Hello PROGRAM FORMAT=MZ
generuje výstupní soubor "Hello.exe"
.
Na jméno výstupního souboru lze aplikovat operátor(y) suboperace,
například OutFile="MyData.bin"[1..256]
zkompiluje celý modul v paměti,
avšak pouze prvních 256 bajtů bude zapsáno do výstupního souboru MyData.bin
. Viz také vzorový program
binboot.htm jako praktický příklad využití suboperace substring.
Volba STUBFILE= má význam pouze u spustitelných souborů ve formátu PE nebo DLL. Stub je 16bitový program formátu MZ, který se dostane ke slovu, když je soubor určený pro Windows nedopatřením spuštěn v 16bitovém operačním systému (DOS). Obvykle je jeho úlohou pouze informovat uživatele, že this program requires MS-Windows.
Je-li volba STUBFILE prázdná (default), €ASM použije svůj vlastní vestavěný
soubor stub.
Jinak zde očekává předem zkompilovaný spustitelný MZ program.
Byl-li STUBFILE= specifikován bez cesty, €ASM jej hledá v cestách určených volbou
EUROASM LINKPATH=
.
Předepsaný 16bitový program stub může mít stejnou funkcionalitu jako hlavní aplikace pro 32bitové Windows. Takový spustitelný program pak pracuje stejně v DOSu i v MS-Windows. Jako ukázku viz vzorový projekt LockTest.
Volba ICONFILE= by měla definovat existující soubor obsahující pouze ikonu pro Windows,
která pak bude vestavěna do segmentu zdrojů [.rsrc] našeho programu ve formátu PE nebo DLL.
Tato ikona bude graficky reprezentovat výstupní program v prostředí Windows (Plocha, Průzkumník atd.).
Byl-li soubor ikony specifikován bez cesty, bude se hledat v některém adresáři určeném volbou EUROASM LINKPATH=
.
Výchozí hodnotou je EUROASM ICONFILE="euroasm.ico"
,
což představuje ikonu dodávanou
s EuroAssemblerem ve složce objlib
.
Volba ICONFILE= se uplatní pouze pokud žádný soubor resources není linkován do výstupního programu, jinak se ignoruje a bude místo něj použita první ikona ze souboru resources.
Je-li parametr ICONFILE=
prázdný, ikona není použita
a €ASM sekci resources vůbec nevytváří.
Tyto tři volby určují, která přídavná informace bude vypisována na konec listingového souboru. Viz test t8302 jako příklad formátu ListMap a ListGlobals.
Je-li LISTLITERALS=ON, obsah datové a kódové literální sekce @LT16, @LT8, @LT4, @LT2, @LT1, @RT0 bude vypsán do listingu. Viz test t1711 jako příklad formátu ListLiterals.
Tato volba specifikuje nominální čas poskytovaný systémovým %^proměnným EuroAssembleru %^DATE a %^TIME, a který bude také zabudován do některých struktur COFF formátu: PFCOFF_FILE_HEADER.TimeDateStamp, PFLIBCOF_IMPORT_OBJECT_HEADER.TimeDateStamp, PFRSRC_RESOURCE_DIRECTORY.TimeDateStamp.
Hodnota tohoto parametru představuje počet sekund, které uběhly od půlnoci
1. ledna 1970, UTC.
Pokud je nastavena na -1 nebo nechána prázdnou (výchozí stav),
bude nastavena podle systémového časovače při startu euroasm.exe
.
TIMESTAMP= lze využít ke zfalšování času, kdy byl cílový program vytvořen.
Ostatní parametry pseudoinstrukce PROGRAM jsou obvykle důležité pouze v souborech rodiny COFF (PE, DLL, COFF), kde tvoří PE header. Pro jejich podrobnější popis viz [MS PECOFF]. Neměňte jejich výchozí hodnoty, pokud nevíte, co děláte.
Pseudoinstrukce SEGMENT deklaruje paměťový segment a určuje jeho vlastnosti.
Každá deklarace segmentu zároveň deklaruje sekci stejného jména.
Další sekce tohoto segmentu mohou být deklarovány později,
instrukcí s názvem sekce v poli návěstí, např.
[Strings] ; Deklaruj sekci [Strings] v aktuálním segmentu.
.
Jméno segmentu je zadáno v poli návěstí a vypadá jako identifikátor v hranatých závorkách. Vlastnosti segmentu se zadávají pomocí klíčových operandů.
€ASM automaticky deklaruje několik výchozích segmentů když začíná překládat program. Ve většině případů to stačí a není třeba explicitně deklarovat další segmenty. Počet a účel výchozích segmentů záleží na programovém formátu. Pokud tyto výchozí segmenty nebyly v programu použity (nic do nich nebylo emitováno), budou zahozeny a vůbec se neobjeví v cílovém souboru. Dochází k tomu, pokud jsme nebyli spokojeni s výchozími segmenty a deklarovali své vlastní s odlišnými názvy a vlastnostmi.
Klíčový operand SEGMENT PURPOSE= určuje, pro jaký druh informací je segment určen. Parametr je důležitý v chráněném módu (formáty ELFX, PE, DLL), kdy přístupové bity deskriptoru řídí operávnění číst, zapisovat a provádět obsah segmentu.
PURPOSE= | Alias | Přístup | Výchozí jméno | Obsahuje |
---|---|---|---|---|
CODE | TEXT | read, execute | [.text]|[CODE] | Programový kód (instrukce) (1) |
RODATA | RDATA | read | [.rodata]|[RODATA] | Inicializovaná data pouze ke čtení (1) |
DATA | IDATA | read, write | [.data]|[DATA] | Inicializovaná data (1) |
BSS | UDATA | read, write | [.bss]|[BSS] | Neinicializovaná data (1) |
STACK | read, write | [STACK] | Strojový zásobník (1) | |
LITERALS | LITERAL | read | parazituje na jiných data/code segmentech | Literální sekce (2) |
DRECTVE | discarded | [.drectve] | Direktivy pro linker (3) | |
PHDR | Hlavičky programu (4) | |||
INTERP | Dynamický interpreter (4) | |||
SYMBOLS | [.symtab] | [.dynsym] | Symboly (4) | ||
HASH | [.hash] | Kontrolní součty jmen symbolů (4) | ||
STRINGS | [.strtab] | [.dynstr] | [.shstrtab] | Jména symbolů nebo sekcí (4) | ||
DYNAMIC | [.dynamic] | Dynamické záznamy (4) | ||
RELOC | [.rel(a)*] | Relokace (4) | ||
GOT | [.got] | GOT - tabulka ofsetů (4) | ||
PLT | [.plt] | PLT - tabulka procedur (4) | ||
EXPORT | [.edata] | Dynamické exporty (4) | ||
IMPORT | [.idata] | Dynamické importy (4) | ||
RESOURCE | [.rsrc] | Resources (4) | ||
EXCEPTION | [.pdata] | Run-time přerušení (5) | ||
SECURITY | Certifikáty (5) | |||
BASERELOC | discarded | [.reloc] | Load-time relokace (4) | |
DEBUG | [.debug] | Data pro debuger (5) | ||
COPYRIGHT | ARCHITECTURE | Architektura (5) | ||
GLOBALPTR | RVA globálního pointeru (5) | |||
TLS | [.tls] | Paměť vlákna (5) | ||
LOAD_CONFIG | Načtení konfigurace (5) | |||
BOUND_IMPORT | Bound import (5) | |||
IAT | [.idata] | Importní adresní tabulka (4) | ||
DELAY_IMPORT | Zpožděný import (5) | |||
CLR | [.cormeta] | Metadata CLR (5) | ||
RESERVED | Reservováno (5) |
Segmenty se speciálními jmény (4),(5) budou označeny na odpovídající pozici v tabulce DataDirectory ve "volitelné" hlavičce souboru PE nebo DLL.
Ačkoli operand PURPOSE= akceptuje pouze výčtové hodnoty vyjmenované ve výše uvedené tabulce,
mohou být kombinovány použitím operátoru Addition + nebo
Bitwise OR |, např.
[TINY] SEGMENT PURPOSE=CODE|DATA|BSS|STACK
nebo [.rodata] SEGMENT PURPOSE=DATA+LITERALS
.
Není-li prametr PURPOSE= uveden, €ASM jej odhadne podle jeho třídy (class) nebo [názvu] za použití těchto pravidel:
STACK
, bude použit PURPOSE=STACK
.BSS
nebo UDATA
, bude použit PURPOSE=BSS
.DATA
, bude použit PURPOSE=DATA
.PURPOSE=CODE
.PURPOSE=LITERALS
je použit spolu s CODE nebo DATA a pouze navrhuje, že by tento segment
měl hostovat literální sekce.
Neby-li žádný segment explicitně označen s PURPOSE=LITERAL, €ASM vybere poslední datový nebo kódový segment,
jakmile narazí na literální symbol.
Odhad účelu se nejprve dívá na vlastnost SEGMENT CLASS=
,
a pouze pokud je prázdná, dívá se na jméno segmentu.Tento mechanismus je použit
se segmenty definovanými ve formátu OMF k předání jejich účelu do linkovaného spustitelného souboru.
Šířkou segmentu může být numerický výraz vyhodnocený jako 16, 32 nebo 64. Jako výchozí hodnota (při vynechání) je šířka segmentu určena šířkou programu.
Tento parametr požaduje zarovnání segmentu v paměti za běhu.
Výchozí hodnotou je ALIGN=OWORD
(16 bajtů).
Speciální ELF a PE segmenty, jako [.symtab], [.strtab], [.reloc] atd. mohou mít odlišné zarovnání.
Tento parametr specifikuje, jak budou segmenty z jiných programových modulů kombinovány při linkování. Je to důležité u programů typu MZ (16bitový pro DOS) sestavovaného z více modulů. Možné hodnoty:
Hodnotou parametru CLASS= je nepovinný identifikátor. Může být použit linkerem k odhadnutí účelu segmentu (CODE|DATA|BSS) v objektových formátech, které nepřenášejí informaci o účelu segmentů (OMF).
Tato pseudoinstrukce určuje adresní rámec a vyjmenovává segmenty adresovatelné jedním segmentovým registrem.
Grupy segmentů se uplatní ve velkých 16bitových programech v reálném módu. Pouze 16bitový segment může být členem grupy.
Jméno grupy je definováno v poli návěstí pseudoinstrukce GROUP.
Jména jejich segmentů jsou v poli operandů. všechna jména jsou v hranatých závorkách [ ].
Jméno grupy se může shodovat se jménem prvního segmentu. Příklad:
[DGROUP] GROUP [DATA],[STRINGS]
.
Segmenty patřící do grupy mohou být definovány před nebo po instrukci GROUP.
Tato pseudoinstrukce nemá žádné klíčové operandy.
Vztah mezi grupou a jejími segmenty v době linkování se podobá vztahu mezi segmentem a jeho sekcemi v době překladu.
Pseudoinstrukce PROC a ENDPROC deklarují blok procedury a její jmenný prostor. Procedura často končí strojovou instrukcí RET, takže blok může být volán pomocí CALL. Po vykonání se vrací zpět za instrukci CALL.
Povinné návěstí pseudoinstrukce PROC
deklaruje symbol, který je zároveň názvem procedury.
Stejný identifikátor může být použit jako první a zároveň jediný operand odpovídající pseudoinstrukce ENDPROC
.
Místo ENDPROC lze také používat její variantu ENDP.
Pseudoinstrukce ENDPROC
může rovněž definovat své vlastní návěstí.
To však nereprezentuje adresu návratu ze subprogramu, nýbrž ukazuje na kód následující po bloku PROC..ENDPROC.
Návěstí ENDPROC je užitečné pouze, pokud je PROC..ENDP blok využit k definování jmenného prostoru
a ne jako volatelný blok podprogramu. Příklady:
SubPgm:PROC ; Definuj PROC jako volatelný podprogram. ; Instrukce procedury jsou zde. TEST JakásiPodmínka JC .Abort: ; Jdi k návratu a pak pod instrukciCALL SubPgm:
. TEST JináPodmínka JC .End: ; Jdi pod pseudoinstrukci.End: ENDP
.Patrně ne to, co programátor chtěl.. ; Další instrukce procedury. .Abort: RET ; Návrat pod instrukciCALL SubPgm:
. .End: ENDP SubPgm:
NameSp:PROC ; Definuj PROC jako průchozí blok.
; Instrukce procedury jsou zde.
TEST JakásiPodmínka
JC .End: ; Jdi pod pseudoinstrukci .End: ENDP NameSp:
.
; Další instrukce procedury. Chybí zde RET.
.End: ENDP NameSp: ; Pokračuj pod touto instrukcí.
Pseudoinstrukce PROC, ENDPROC, PROC1, ENDPROC1
neemitují žádný strojový kód.
Skok na ENDPROC
se liší od skoku na makroinstrukci
EndProcedure
definovanou v
makroknihovně volacích konvencí.
K čemu vlastně jsou procedury dobré? Mohli bychom se bez nich snadno obejít, avšak zabalení kusu kódu do PROC..ENDPROC má svoje výhody:
- Kód je lépe strukturován a snazší k porozumění.
- €ASM kontroluje správné spárování návěstí, což je zvláště důležité, pokud jsou procedury vnořeny.
- Je jasné, kde procedura končí. Nemusíme prohlíže kód a hledat, která z instrukcí RET je poslední, když potřebujeme zkopírovat proceduru do jiného programu.
- Každý blok PROC..ENDP definuje svůj vlastní jmenný prostor a zabraňuje tak konfliktům lokálních jmen v proceduře použitých.
Pseudoinstrukce PROC a PROC1 akceptují klíčové operandy DIST= and ALIGN=.
DIST= nastavuje vzdálenost procedury (NEAR nebo FAR).
Když je DIST=FAR
, všechna volání této procedury jsou FAR, a všechny instrukce RET uvnitř této procedury jsou jako výchozí rovněž FAR.
(lze to ovšem změnit přípomou instrukce, tedy CALLN/CALLF, RETN/RETF).
Výchozí hodnota tohoto parametru zavísí na paměťovém modelu.
Výchozí zarovnání procedury je ALIGN=BYTE
.
Pro lepší výkonnost může být užitečné doplnit často volané procedury parametrem
PROC ALIGN=OWORD
, pokud není nárůst délky programu problém.
Tato booleovská volba dovoluje vypnout kontrolu párování návěstí v bloku PROC..ENDPROC. Má to jen vyjímečné uplatnění v makrech simulujících vestavěné pseudoinstrukce, které potřebují ošálit kontext, jako je Procedure a EndProcedure.
Viz také instrukční modifikátor NESTINGCHECK=.
Pseudoinstrukce PROC neakceptuje pořadové parametry proceduře předávané. Místo toho mohou být operandy předávány v registrech nebo na zásobníku. Makroknihovny volacích konvencí dodávané s EuroAssemblereme ale definují makra Procedure a EndProcedure s podobnou funkcí jako PROC a ENDPROC, které naopak dovolují předávání libovolného počtu operandů jako parametru maker, když je Procedura volána.
Pseudoinstrukce PROC1 a ENDPROC1 se podobají na PROC a ENDPROC, avšak jsou tu dva rozdíly:
Procedura deklarovaná blokem PROC1..ENDPROC1 se může vyskytnout v programu více než jednou. Opakované deklarace PROC1..ENDPROC1 se stejným návěstím jsou ignorovány a emitovány pouze jednou.
Předurčuje to použití PROC1 k definici napůl-inline maker, které obsahují jak volání procedury, tak i proceduru samotnou. Když je procedura definována v makru pomocí PROC1..ENDPROC1, takové makro může být voláno mnohokrát, ale volaná procedura bude přeložena a emitována pouze jednou, během první expanze makra.
[@RT1]
a je automaticky zřízena v segmentu s účelem PURPOSE=CODE+LITERAL
nebo v naposledy definovaném kódovém segmentu.
V některých případech může €ASM použít také sekce [@RT2]
, [@RT3]
atd.
K tomu dochází, pokud kód uvnitř bloku PROC1..ENDPROC1 obsahuje další napůl-inline makra,
takže aktuální sekce již je [@RT1]
a €ASM musí zvolit odlišnou sekci.
Emitování procedur do odlišné sekce než hlavní program má také výhodu, že taková procedura nemusí být přeskakována pomocí JMP. Často to vede ke kratšímu programu, neboť skok přes napůl-inline makro nemusí přeskakovat celé tělo procedury, což by jinak mohlo překročit 128 bajtovou hranici a vyžadovat delší variantu skokové instrukce.
Blok deklarovaný pseudoinstrukcemi HEAD a ENDHEAD pouze vymezuje oddíl zdrojového kódu. Tento oddíl může být vložen do jiného zdrojového souboru pseudoinstrukcí INCLUDEHEAD nebo INCLUDEHEAD1. Blok obvykle obsahuje interface programovacího objektu (definice struktur, maker, konstant), které potřebujeme vložit do jiných, separátně překládaných modulů.
Návěstí pseudoinstrukce HEAD může být použito jako identifikátor bloku, avšak nevytváří symbol. V jednom zdrojovém souboru může být definováno více než jeden blok HEAD..ENDHEAD. Pokud jsou tyto bloky vnořeny jeden do druhého, bude vložen celý vnější (delší) blok.
Jazyky bez takového mechanismu musejí ukládat interface do separátních hlavičkových souborů. S pomocí HEAD.ENDHEAD je můžeme udržovat spolu s implementačním tělem v jednom kompaktním souboru.
Tato pseudoinstrukce začleňuje zdrojový soubor s názvem specifikovaným jako její operand do hlavního zdrojového textu. Příkaz INCLUDE je virtuálně nahrazen obsahem inkludovaného souboru.
Inkluze mohou být vnořeny, tj. vložený soubor může obsahovat další povely INCLUDE.
Dvojité uvozovky mohou být vynechány, pokud jméno souboru obsahuje pouze alfanumerické znaky bez mezer a punktuace.
Každá pseudoinstrukce INCLUDE může mít neomezený počet operandů, např.
INCLUDE "Win*.htm", ./MyConstants.asm, C:\MyLib\*.inc
.
Je-li soubor specifikován bez cesty, bude hledán ve složkách definovaných volbou EUROASM INCLUDEPATH=.
Obsahuje-li jméno souboru aspoň jedno lomítko nebo dvojtečku
/ \ : , znamená to, že bylo specifikováno s vlastní cestou a INCLUDEPATH=
se v takovém případě ignoruje.
Název souboru může obsahovat žolíkové znaky * ?, v tom případě €ASM začlení všechny soubory odpovídající masce. Pořadí inkluze záleží na operačním systému.
Chování INCLUDE je popsáno v této tabulce:
Cesta | Žolík | Příklad | Pokud je nalezen aspoň jeden soubor | Pokud nebyl nalezen žádný soubor |
---|---|---|---|---|
Ne | Ne | file.inc | Hotovo, nehledej dále v INCLUDEPATH. | Error E6914. |
Ano | Ne | ./file.inc | Hotovo. | Error E6914. |
Ne | Ano | file*.inc | Prohledávej další soubory v INCLUDEPATH. | Nic nebylo vloženo, nehlas žádnou chybu. |
Ano | Ano | ./file*.inc | Pokračuj v hledání v dané cestě. | Nic nebylo vloženo, nehlas žádnou chybu. |
Pokud byl bezprostředně za jméno souboru přidán operátor substring
nebo sublist, bude vložena pouze část takového souboru.
Příklad: INCLUDE "file.inc"{%&-20..%&}
začlení posledních dvacet řádků file.inc
(automatická %proměnná %&
reprezentuje počet řádků v souboru).
Jméno souboru musí být v uvozovkách, pokud je použita suboperace.
Pokud název souboru obsahuje žolíkové znaky * ?,
suboperace se bude vztahovat na všechny soubory vyhovující masce.
Pseudoinstrukce include jednou se chová stejně jako INCLUDE
,
ale nejprve se podívá, zda už nebyl vložen stejný soubor
(se stejnou velikostí a obsahem, bez ohledu na jméno).
V takovém případě jej přeskočí.
Používání INCLUDE1 namísto INCLUDE dovoluje vyřešit vzájemné závislosti zdrojových knihoven.
Pokud některá knihovna používá makra, struktury a konstanty definované v jiné knihovně,
můžeme volat INCLUDE1 jiná.knihovna
.
Pseudoinstrukce INCLUDEHEAD začleňuje ze jmenovaného souboru pouze obsah bloků HEAD..ENDHEAD. Viz test t2420. Neexistuje-li ani jeden blok HEAD..ENDHEAD, nebo pokud je nekompletní (chybí ENDHEAD), €ASM ohlásí chybu.
Pokud je použita suboperace, je nejprve aplikována na celý soubor a bloky HEAD..ENDHEAD jsou hledány až v takto ořezaném zdroji.
INCLUDEHEAD1 a INCLUDE1 se ignorují, pokud soubor nebo jen část z něho již byly vloženy do hlavního programu pomocí INCLUDE, INCLUDE1, INCLUDEHEAD nebo INCLUDEHEAD1.
Knihovna se považuje za již vloženou, pokud byla inkludována jako celek pomocí INCLUDE nebo INCLUDE1, pokud její interface byl inkludován pomocí INCLUDEHEAD nebo INCLUDEHEAD1, nebo pokud byla inkludována pouze její suboperovaná část.
Na rozdíl od INCLUDE a INCLUDEHEAD nechápe tato pseudoinstrukce obsah souboru jako zdrojový kód k přeložení, ale emituje jeho obsah tak jak je na pozici určenou aktuálním ukazatelem $ aktuální sekce.
Vložení binárních dat by nemělo být zaměňováno s linkováním; neaktualizuje relokabilní adresy ani nespojuje externí symboly. Například povel
INCLUDEBIN "C:\WINNT\Media\chimes.wav"[0x2C..]
přeskočí prvních 0x2C bajtů hlavičky WAV ve zvukovém souboru a zavede jen zbytek (syrové hudební vzorky) do překládaného cíle, jako by byly definovány pomocí DB.Viz též test t2470.
Pseudoinstrukce LINK určuje soubor(y), které by měly být linkovány do aktuálního programu.
Každý ordinální operand reprezentuje název souboru, může mít žolíkové znaky * ? a může být specifikován bez anebo s cestou. Relativní cesta odkazuje na aktuální adresář.
Neobsahuje-li jméno souboru cestu, bude hledáno ve všech adresářích definovaných volbou
EUROASM LINKPATH=
.
Na rozdíl od inkludovaných souborů zde suboperace nejsou podporovány.
Linkovatelné soubory mají speciální interní strukturu, která by patrně byla suboperací poškozena.
Pozice příkazu LINK v programu není důležitá, k samotnému linkování dojde až bude poslední průchod programu končit. Pořadí linkovaných souborů však respektuje pořadí, v němž se objevily v příkazech LINK. Příklady:
LINK Subproc.obj, "..\My modules\W*.obj"
Viz také statické linkování pro další informace
Pseudoinstrukce GLOBAL, PUBLIC, EXTERN, EXPORT, IMPORT
nastavují
viditelnost symbolu, což bude použito při linkování.
Symbol, jehož viditelnost se deklaruje, může být v poli návěstí nebo v poli operandů nebo v obojím. Více než jeden symbol může být deklarován jedním povelem. Dotyčné symboly mohou být odkazovány dopředu nebo zpětně.
Příklad explicitní deklarace čtyř symbolů: Sym1 PUBLIC Sym2, Sym3, Sym4
Deklarace symbolu jako PUBLIC pouze sděluje EuroAssembleru, že symbol, který byl či bude definován někde jinde v programu, by měl být odkazovatelný z jiných staticky linkovaných modulů. Deklarace PUBLIC zatím symbol nevytváří, ve skutečnosti musí být definován někde jinde v tomtéž programu.
Pseudoinstrukce EXTERN symbol
říká EuroAssembleru, že tento symbol není definován v programu,
takže odkazy na jeho adresu musí být v link-time záplatovány.
Bylo by chybou definovat symbol deklarovaný jako EXTERN
v tomtéž modulu. Místo toho bude symbol hledán v ostatních modulech
v čase linkování a pouze linker může ohlásit chybu, pokud jej nenajde.
Pseudoinstrukci GLOBAL lze použít ke zjednodušení nakládání s viditelnostmi PUBLIC a EXTERN. Pokud je symbol deklarován jako GLOBAL, chová se buď jako PUBLIC nebo jako EXTERN, v závislosti na tom, zda byl či nebyl definován ve stejném programu.
Programátor ovšem ví, jestli symbol patří do aktuálního programu nebo ne, tak proč je deklarace PUBLIC a EXTERN duplikována pomocí GLOBAL? Mějme program PgmA definující PUBLIC symbol SymA a odkazující na EXTERN symbol SymB. Podobně program PgmB definuje SymB a odkazuje na SymA:PgmA PROGRAM PUBLIC SymA EXTERN SymB CALL SymB: ; Reference na externí symbol. SymA: RET ; Definice public symbolu. ENDPROGRAM PgmA PgmB PROGRAM PUBLIC SymB EXTERN SymA CALL SymA: ; Reference na externí symbol. SymB: RET ; Definice public symbolu. ENDPROGRAM PgmBNahradíme-li deklarace PUBLIC a EXTERN pomocí GLOBAL, stejná deklarace bude moci být použita ve všechn staticky linkovaných programech a můžeme ji copy&pastovat nebo inkludovat, což je snazší k údržbě.:PgmA PROGRAM GLOBAL SymA, SymB CALL SymB: ; Reference na externi symbol. SymA: RET ; Definice public symbolu. ENDPROGRAM PgmA PgmB PROGRAM GLOBAL SymA, SymB CALL SymA: ; Reference na externí symbol. SymB: RET ; Definice public symbolu. ENDPROGRAM PgmBDalší raison d'être direktivy GLOBAL je zpětná kompatibilita s NASM, který nezná direktivu PUBLIC a používá místo ní GLOBAL.
Viditelnosti IMPORT a EXPORT se používají při dynamickém linkování,
kdy náš program volá importovanou funkci z DLL.
Tato pseudoinstrukce akceptuje klíčový parametr LIB=
,
jenž specifikuje soubor knihovny. Parametr LIB= lze vynechat, pokud jsou symboly importovány
z výchozí knihovny MS-Windows kernel32.dll
.
Knihovní soubor nemusí být v uvozovkách, pokud dodržuje DOSové konvence 8.3.
Knihovna je vždy specifikována bez cesty. Operační systém má svoje vlastní pravidla
([WinDllSearchOrder])
týkající se adresářů, v nichž se mají knihovny hledat v bind-time.
Viditelnost EXPORT se použije, když vytváříme dynamickou knihovnu; deklaruje symboly, které mají být importovány jinými programy. Podobně jako u PUBLIC, symbol označený pro EXPORT musí být dříve či později definován v programu.
Pseudoinstrukce EXPORT rozeznává dva klíčové parametry
FWD=
a LIB=
specifikující, že exportovaný symbol (jméno funkce)
je ve skutečnosti poskytováno jinou dynamickou knihovnou (definovanou pomocí LIB=)
pod odlišným názvem symbolu (definovaným pomocí FWD=). Příklad:
kernel32 PROGRAM FORMAT=DLL EXPORT EnterCriticalSection, LIB="NTDLL.dll", FWD=RtlEnterCriticalSection ; Další kernelové funkce. ENDPROGRAM kernel32
Knihovna "kernel32.dll" nabízí API funkci RtlEnterCriticalSection, která je ve skutečnosti poskytována knihovnou "NTDLL.dll". V jiné verzi MS-Windows může být poskytnuta odlišnou knihovnou "XPDLL.dll", ale programy importující funkci z proxy knihovny "kernel32.dll" nebudou potřebovat aktualizaci ani rekompilaci.
Tato pseudoinstrukce je určena k explicitnímu
zarovnání aktuálního sekčního ukazatele $. Kupříkladu
ALIGN OWORD
v kódové sekci bude emitovat několik (0 až 15)
bajtů instrukce NOP, takže další instrukce bude emitována na adresu zarovnanou k octwordu (16).
V datové sekci ALIGN používá bajt NUL (0x00)
namísto NOP (0x90) jako výplň.
Operand může být specifikován v krátké nebo dlouhé notaci:
B, U, W, D, Q, T, O, Y, Z, BYTE, UNICHAR, WORD, DWORD, QWORD, TBYTE, OWORD, YWORD, ZWORD
nebo jako aritmetický výraz vyhodnocovaný na mocninu dvou:
1, 2, 4, 8, 16, 32, 64, 128, 256, 512
.
ALIGN TBYTE
zarovnává na 8.
Pseudoinstrukce ALIGN nesmí mít návěstí, ale může mít dva operandy.
Ten druhý je určen k záměrnému "odrovnání"; nemusí být mocninou dvou,
ale musí být menší než první operand. Například
ALIGN OWORD, QWORD
zarovná $ na lichý násobek osmi.
ALIGN 8,2
zarovná aktuální ofset na druhý bajt osmibajtového slova.
Tomu vyhovují ofsety 2, 10, 18, 26 atd.
Struktura reprezentuje virtuální sekci datových deklarací, kterou si lze představit jako masku nebo mřížku položenou nad kusem paměti. Struktura je deklarována blokem STRUC..ENDSTRUC. Jediné instrukce použitelné v bloku jsou
Deklarace struktury neemituje žádná data do cílového souboru.
Data budou emitována nebo rezervována až když bude struktura skutečně použita v datové definici
(v pseudoinstrukci D nebo DS).
Říkáme tomu, že struktura bude instanciována.
Jsou-li v definici struktury definována inicializovaná data,
budou použita k inicializaci odpovídajících členů při její instancionizaci.
(pseudoinstrukcí D nebo DS), ledaže by byly explicitně redefinovány.
Definici dat ve struktuře musí mít lokální jména (začínající tečkou .).
To dovoluje použít
Každý člen dostává svůj ofset relativní k začátku struktury. Nezaáleží na tom, jaká programová sekce byla aktuální při deklaraci struktury. Každá deklarace dočasně vytváří svou vlastní pseudosekci s virtuální adresou 0.
Struktura musí dostat unikátní název definovaný v poli návěstí instrukce STRUC a volitelně také v poli operandu instrukce ENDSTRUC.
Na velikost struktury můžeme odkazovat pomocí atributu SIZE#Jméno_struktury
.
Pseudoinstrukce STRUC akceptuje klíčový operand ALIGN= specifikující zarovnání instancí struktury,
pokud je EUROASM AUTOALIGN=ON
.
Není-li zarovnání explicitně požadováno v deklaraci STRUC,
bude použito zarovnání závislé na šířce programu (PROGRAM WIDTH=
), tedy WORD, DWORD nebo QWORD.
Viz testy t2500, t2501, t2504 pro více příkladů deklarací struktur.
Inicializovaná i neinicializovaná data jsou definována a rezervována pseudoinstrukcí D.
Je-li v ní specifikována statická hodnota, data jsou definována.
Pokud je hodnota vynechána, data jsou rezervována.
Je-li nastavena volba EUROASM AUTOSEGMENT=ON
, data typu INSTR způsobí přepnutí do kódové sekce,
všechny ostatní definice dat přepínají do datové sekce a
rezervace dat do sekce bss (neinicializovaná data).
Viz test t2482 pro další příklady.
Každý operand pseudoinstrukce D
je datový výraz.
K mnemonice D
může být připojena přípona
B, U, W, D, Q, T, O, Y, Z, I, S
. Tato přípoda definuje výchozí datový typ
použitý, pokud nebyl výslovně specifikován v operandu.
Například DD 2,3,4
definuje tři proměnné DWORD se statickými hodnotami 2, 3 a 4.
Přípona zároveň určuje typ symbolu, který definuje data.
V definici Sym1 DQ B 1, W 2, D 4
přípona Q určuje, že typ symbolu Sym1 je QWORD, třebaže
tento symbol definuje pouze bajt, slovo a dvojslovo.
Výchozí typ určený příponou může být přepsán v operačním poli
explicitním datovým type v krátké nebo dlouhé notaci.
Operandy bez explicitní redefinice berou svůj typ z přípony instrukce D, např.
DB 27, "$", W 120
definuje dva bajty následované jedním slovem.
Datatypy v operandu mohou být také specifikovány dlouhým typovým názvem, např.
DB 27, "$", WORD 120
.
Viz test t2481 pro další příklady.
Například TranslateTable: D 256 * BYTE
rezervuje 256 neinicializovaných bajtů.
Není-li duplikace použita, výchozí duplikační hodnota (duplikátor) je 1.
Negativní duplikátor není povolen.
Duplikátor 0 nedefinuje ani nerezervuje žádná data, ale stále poskytuje datový typ svému symbolu
a pokud je AUTOALIGN=ON
, zarovnává aktuální ofset $.
Není-li použita žádná přípona, výchozí datový typ se bere z prvního neprázdného operandu
např. D D 2,3,4
definuje tři DWORDy s hodnotami 2,3 a 4.
Není-li výchozí typ určen, jako např. D 2
, €ASM oznámí chybu.
Jedinou výjimkou, kdy datový typ nemusí být specifikován,
je definice textového řetězce s vynecháním specifikátoru B nebo U,
např. D "Nějaký text."
. V tom případě bude datovým typem BYTE nebo UNICHAR
v závislosti na aktuální hodnotě volby EUROASM UNICODE=
.
L1: D B 5 ; Definuj jeden bajt s hodnotou 5. TYPE#L1='B', SIZE#L1=1. L2: D 2*WORD 3 ; Definuj dvě slova s hodnotou 3. TYPE#L2='W', SIZE#L2=4. L3: DW W ; Rezervuj jedno slovo. TYPE#L3='W', SIZE#L3=2. L4: DW 0*D ; Nezerezvuj nic, jen zarovnej na WORD. TYPE#L4='W', SIZE#L4=0. L5: DQ ; Nerezervuj nic, jen zarovnej na QWORD. TYPE#L5='Q', SIZE#L5=0. L6: D ; Nedělej nic. TYPE#L6='A', SIZE#L6=0.
Na rozdíl od jiných asemblerů vynechaný operand neemituje žádná data, €ASM vyžaduje, aby byl specifikován typ operandu a případně i jeho hodnota, bez ohledu zda je operace D s příponou nebo bez ní. KupříkladuDB
rezervuje jeden bajt dat v MASM, avšak nedělá nic v €ASM. Použijte místo tohoD B
neboDB B
.
EuroAssembler umí definovat strojový kód instrukce jako data pomocí pseudoinstrukce DI. Ta se podobá pseudoinstrukcím DB nebo DU, ale obsah řetězce není doslovně emitován, nýbrž nejprve přeložen asemblerem. Text v uvozovkách operandu DI by měla být platná strojová instrukce, může mít prefix a operandy, ale ne návěstí.
Například DI "SEGES:MOVSB"
definuje bajty 0x26,0xA4.
D 8*I"MOVSD"
definuje osm bajtů 0xA5.
Viz test t2515 pro další příklady instrukce DI.
Strukturovaná paměťová proměnná je definována pseudoinstrukcí
DS jméno_struktury
nebo jen D jméno_struktury
.
€ASM nedovoluje vícenásobné operandy při definici strukturovaného objektu,
jako
.
Duplikace je ale podporována, např. DS MyStruc1, Mystruc2 DS 4*MyStruc
.
Členy struktury mohou být přepsány staticky, pomocí klíčových operandů. Klíčem je lokání jméno člena bezprostředně následované rovnítkem = a novou hodnotou tohoto člena. Jmenný prostor pole operandů v instrukci DS je dočasně změněn na jmenný prostor deklarace struktury.
Instance struktury MyStruc deklarované výše v příkladu STRUC
mohla být definována jako MyObject DS MyStruc, .Member2=2, .Member4=4
.
To inicializuje obsah MyObject.Member2 na DWORD integer 2, a obsah
MyObject.Member4 na BYTE integer 4.
Obsah MyObject.Member3 je již staticky definován jako BYTE integer 255,
ostatné členy MyObject zůstávají neinicializovány.
Je-li aspoň jeden člen inicializován, objekt je při AUTOSEGMENT=ENABLED
emitován do datové sekce namísto bss, neinicializované členy jsou naplněny nulami.
Viz též test t2510.
Pseudoinstrukce EQU (nebo její synonymum =) definuje symbol uvedený v jejím poli návěstí. Tato pseudoinstrukce musí mít právě jeden operand specifikující adresu nebo číselnou hodnotu symbolu.
Instrukce Label:EQU $
nebo Label:= $
jsou ekvivalentní k Label:
, tj. specifikaci samotného návěstí,
což přiřazuje adresu návěstí k hodnotě Label.
EQU je jediný způsob, jak definovat prostý číselný symbol,
jako třeba FILE_ATTRIBUTE_ARCHIVE = 00000020h
.
Viz kteroukoli makroknihovnu v říši PROGRAM jako příklad definic numerických symbolů, např. winsfile.htm.
Tyto pseudoinstrukce definují blokovou poznámku, tedy rozsah zdrojového kódu ignorovaný EuroAssemblerem. V poli návěstí %COMMENT může být identifikátor dávající bloku jméno, avšak netvoří symbol. Stejný identifikátor může být použit jako operand instrukce %ENDCOMMENT. Pomáhá to ke kontrole správného spojení %COMMENT & %ENDCOMMENT, zejména pokud jsou poznámky vnořeny.
%DROPMACRO říká EuroAssembleru, aby zapomenul dříve deklarované makroinstrukce.
Jedna pseudoinstrukce %DROPMACRO může zapomenout jedno nebo více maker specifikovaných jako její operand(y),
např. %DROPMACRO Macro1, Macro2, Macro3
.
Taky může zapomenou všechna dosud deklarovaná makra pomocí %DROPMACRO *
.
Viz příklad %DROPMACRO example níže.
Instrukce mezi %IF a %ENDIF se překládají pouze tehdy, pokud podmínka v prvním (a jediném) operandu pseudoinstrukce je vyhodnocena jako pravda. %IF akceptuje booleovská rozšíření a rovněž akceptuje prázdný operand, který je vždy vyhodnocen jako nepravda.
Uvnitř bloku %IF..%ENDIF se může vyskytnout pseudoinstrukce %ELSE, který obrací logiku: instrukce mezi %IF a %ELSE se překládají, pokud je podmínka v %IF pravda a instrukce mezi %ELSE a %ENDIF se překládají, pokud je podmínka %IF nepravda..
Pseudoinstrukce %IF může mít identfikátor v poli pro návěstí, který netvoří symbol, ale identifikuje blok. Stejný identifikátor může být použit v poli operandu instrukcí %ELSE a %ENDIF.
Pseudoinstrukce %FOR a %ENDFOR vytvářejí blok, jenž je překládán opakovaně pro každý pořadový operand %FOR. V poli návěstí %FOR musí být identifikátor. Není to symbol, místo toho definuje formální preprocesní %proměnnou, která je dostupná v těle bloku %FOR..%ENDFOR (a nikde jinde). Jméno této %proměnné sestává ze znaménka procenta % následovaného identifikátorem.
Operandem může být libovolný element: registr, číslo, výraz, řetězec. Formální %proměnné bude postupně přiřazován každý z pořadových operandů a blok bude emitován s touto hodnotou. Následující příklad definuje smyčku %FOR se třemi operandy a emituje tři paměťové proměnné:
data %FOR "a", 3*B(5), "Long text" D %data %ENDFOR databude expandováno na |00000000:61 + D "a" |00000001:050505 + D 3*B(5) |00000004:4C6F6E672074657874 + D "Long text" |0000000D: |
Zopakování identifikátoru v poli operandu instrukcí %ENDFOR a %EXITFOR je dobrovolné a slouží ke kontrole správného párování blokových instrukcí.
Operandem %FOR rovněž může být číselný rozsah; blok pak bude emitován pro každou celočíselnou hodnotu z tohoto rozsahu. Sklon rozsahu může být negativní, krok řídicí formální %proměnné pak je -1 namísto +1.
i %FOR 0..5 ; Sklon je pozitivní, tedy krok je +1. DB "A"+%i ; Definuj bajty "A","B","C","D","E","F". %ENDFOR i j %FOR 'z'..'x' ; Sklon je negativní, tedy krok je -1. DB %j ; Definuj bajty 'z','y','x'. %ENDFOR j
Viz také test t2640.
%FOR akceptuje celočíselný klíčový parametr STEP=
explicitně určující krok, kterým je %proměnná inkrementována při použití rozsahu.
Výchozí hodnotou je nula (STEP=0
), která má speciální význam:
efektivní krok je pak buď +1 nebo -1
v závislosti na sklonu rozsahu.
Oba druhy operandů (výčtový a rozsah) se mohou kombinovat. Je-li STEP= explicitně stanoven a jeho znaménko se liší od sklonu v operandu rozsahu, smyčka není v tom případě překládána. Na druhé straně, pokud je STEP= vynechán nebo nastaven na 0, rozsahy obou sklonů mohou být kombinovány v jednom bloku %FOR a každý z operandů rozsahu obdrží svůj vhodný krok +1 nebo -1. Ukázka:
a %FOR 1..3, 6..4, 7 ; Blok je přeložen s %a = 1,2,3,6,5,4,7. %ENDFOR b %FOR 0..64, 256, 400..300, 512, STEP=16 ; Blok je přeložen s %b = 0,16,32,48,64,256,512. %ENDFOR
Má-li formální %proměnná bloku %identické jméno s jinou předtím uživatelsky definovanou %proměnnou, formální %proměnná převáží a ta uživatelsky definovaná není v bloku %FOR..%ENDFOR viditelná. Viz test t2641.
Pokud €ASM narazí na pseudoinstrukci %EXITFOR
,
přeruší překlad dalších instrukcí bloku %FOR..%ENDFOR
a pokračuje pod %ENDFOR bez ohledu na to, kolik nezpracovaných operandů ještě zbývá.
i %FOR 0..9 DB %i %IF %i>=3 %EXITFOR i %ENDIF DB "a" + %i %ENDFOR i ; Definuje bajty 0,"a",1,"b",2,"c",3
Ve vnořených blocích %FOR..%ENDFOR může být formální proměnná (první a jediný operand %EXITFOR) použita ke specifikaci, který z vnořených bloků má být opuštěn. Viz test t2642 jako příklad.
Blok instrukcí mezi %WHILE a %ENDWHILE se překládá asemblerem opakovaně dokud podmínka v prvním (a jediném) operandu %WHILE je pravda. Je-li podmínka nepravda už při vstupu do bloku, bude celý přeskočen.
V poli návěstí %WHILE a v poli operandu %ENDWHILE a %EXITWHILE může být stanoven identifikátor pro vizuální svázání, který však netvoří symbol.
Na rozdíl od %FOR, který dočasně deklaruje a udržuje svou vlastní řídicí %proměnnou, %WHILE to nedělá. Je na programátorovi, aby deklaroval nějakou uživatelskou %proměnnou před vstupem do bloku a pak ji uvnitř %WHILE..%ENDWHILE měnil a testoval. Příklad:
%i %SETA 3 ; Definuj uživatelskou %proměnnou %i, která bude řídit expanzi bloku. Id1 %WHILE %i ; Opakuj blok, dokud je %i nenulové. Id1 je identifikátor bloku. C%i: DB %i %i %SETA %i - 1 ; Změň řídicí proměnnou %i. %ENDWHILE Id1 ; Konec smyčky. Identifikátor souhlasí. ; Instrukce emitované blokem %WHILE..%ENDWHILE: C3: DB 3, C2: DB 2, C1: DB 1.
%EXITWHILE v bloku způsobí přeskočení zbytku instrukcí, €ASM bude pokračovat pod %ENDWHILE.
Viz testy t2700, t2701, t2702.
Podmíněný blok %REPEAT..%ENDREPAT se podobá na %WHILE..%ENDWHILE,
avšak logika je obrácená. %REPEAT nepřijímá návěstí ani operand.
Instrukce v bloku jsou přeloženy vždy aspoň jednou.
Test řídicí podmínky je v poli operandu %ENDREPEAT;
pokud se vyhodnotí jako nepravda, €ASM bude blok opakovaně překládat.
Namísto mnemoniky %ENDREPEAT
lze použít alias %UNTIL
.
Blok %REPEAT..%ENDREPEAT taky může používat identifikátor ke kontrole spárování. Na rozdíl od ostatních blokových instrukcí je ale pozice blokového identifikátoru odlišná: může být specifikován jako první operand %REPEAT a zopakován v poli návěstí %ENDREPEAT (neboli %UNTIL).
%i %SETA 3 ; Definuj %proměnnou %i, která bude řídit expanzi bloku. %REPEAT Id1 l Id1 je pouze identifikátor bloku, jinak nemá význam. C%i: DB %i %i %SETA %i - 1 ; Změň řídicí %proměnnou i. Id1 %UNTIL %i = 0 ; Konec smyčky. Identifikátor souhlasí. ; Instrukce emitované blokem %REPEAT..%UNTIL block: C3: DB 3, C2: DB 2, C1: DB 1.
%EXITREPEAT v bloku způsobí přeskočení zbývajících instrukcí; €ASM bude pokračovat pod %ENDREPEAT.
Viz testy t2750, t2751, t2752.
Pseudoinstrukce %SET spolu s ostatními členy této rodiny jsou určeny k přiřazení hodnoty do preprocesních %proměnných. Přiřazovaná %proměnná bude v poli návěstí této pseudoinstrukce.
%SET přiřazuje seznam operandů jako doslovný text, včetně oddělujících čárek. Pouze mezery mezi mnemonikou (%SET) a seznamem operandů a také mezery za posledním operandem jsou vynechány. Podobně jsou mezery vynechány při použití pokračování řádku.
%CardList %SET Hearts, Diamonds, Clubs, Spades ; Comment
%CardList nyní obsahuje řetězec Hearts, Diamonds, Clubs, Spades (31 znaků včetně mezer a čárek).
Viz test t2810.
%SETA akceptuje aritmetické výrazy. Budou vyhodnoceny a pak přiřazeny %proměnné jako dekadické číslo. Pokud operand %SETA není platným výrazem, ohlásí se chyba.
Je-li použit více než jeden operand, každá hodnota je vložena jako odpovídající položka do %proměnné, které je přiřazováno. Příklad:
%Value %SETA PoolEnd - PoolBegin %Sizes %SETA 2+3, 4, ,-5*2
Rozdíl mezi ofsety PoolEnd a PoolBegin
v předchozím příkladu je spočítán a přiřazen do %proměnné %Value jako dekadické číslo.
%Proměnná %Sizes nyní obsahuje text 5,4,,-10 (8 znaků).
Individuální položky ze %Sizes mohou být získány pomocí operace sublist,
jako třeba %Sizes{2}
.
Viz test t2821.
%SETA je vhodnější pro modifikaci kontrolní proměnné v preprocesních smyčkách, jako%i %SETA %i+1
. I když textové přiřazení pomocí%i %SET %i+1
by také fungovalo, s pseudoinstrukcí %SET není výraz počítán okamžitě a mohli bychom skončit s něčím jako+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1
po patnácté expanzi.
%SETB se podobá %SETA, ale akceptuje rozšířené booleovské výrazy, které po vyhodnocení přiřazuje v podobě binárních číslic 1 a 0.
Viz též test t2831.
Na rozdíl od %SETA, binární číslice nejsou odděleny čárkami, je-li více než jeden operand použit v pseudoinstrukci %SETB. Položky mohou být získány pomocí operace substring. Příklad:
%TooBig %SETB 5 > 4 ; %Proměnné %TooBig je přiřazena hodnota znaku 1 (pravda). %Flags %SETB %TooBig, 2,,3>2,off,4,, ; %Flags je přiřazena hodnota 110101. %IF %Flags[1] ; Pravda, vyhodnotí 1. člen %Flags, tj. %TooBig, tj. 1. Flags: DB %Flags[]b ; Přeloží se jako Flags: DB 110101b.
%SETC akceptuje matematický výraz ve svém operandu, který se musí vyhodnotit na prosté číslo ne vyšší než 255 a ne menší než -128. Výsledek pak bude přiřazen jako znak s touto vypočtenou hodnotou ASCII bajtu. Příklad:
%Quote %SETC """" ; Přiřadí znak "uvozovka" (ASCII 33). %Tab %SETC 9 ; Přiřadí znak "tabelátor" (ASCII 9). %NBSP %SETC -1 ; Přiřadí znak "nepřerušitelná mezera" (ASCII 255).
Podobně jako %SETB, mohou být definovány vícenásobné operandy a výsledné znaky nejsou oddělovány čárkami.
%Hexadigits %SETC 'A','B','C','D','E','F' ; %Hexadigits nyní obsahuje šest znaků ABCDEF.
Viz test also t2841.
%SETC dovoluje přiřazení speciálních znaků, což by jinak nemuselo být možné kvůli syntaktickým pravidlům €ASM.%Space %SETC 32
přiřazuje jednu mezeru. Téhož bychom mohli dosáhnout pomocí%QuotedSpace %SET " "
a suboperací vybrat pouze druhý ze tří přiřazených znaků:%Space %SET %QuotedSpace[2]
.
Tato pseudoinstrukce čte environmentovou proměnnou operačního systému v době překladu (asm-time) a přiřazuje ji preprocesní %proměnné. Jméno environmentové proměnné je specifikováno v poli operandu bez uvozovek, procent či dolarů, např.
%OS %SETE OS Msg: DB "This program was assembled at %OS system."
€ASM oznámí varování W2520 pokud je požadovaná proměnná prázdná nebo není definována.
%SETE dovoluje načítat více než jednu environmentovou proměnnou, jejich hodnoty budou přiřazeny bez uvovozek a odděleny čárkami. Příklad:
%CpuInfo %SETE PROCESSOR_ARCHITECTURE, PROCESSOR_IDENTIFIER, \ PROCESSOR_LEVEL, PROCESSOR_REVISION
Na mém starém počítači to přiřadí následující text do %CpuInfo:x86,x86 Family 15 Model 1 Stepping 2, GenuineIntel,15,0102
. Kvůli čárce vložené do hodnoty %PROCESSOR_IDENTIFIER% nemusí být snadné načítat individuální komponenty pomocí sublistu %CpuInfo{4}. Takže je lepší použít %SETE pouze pro jednu environmentovou proměnnou.
%SETS se dívá na %proměnnou ve svém poli prvního operandu a přiřazuje jeho velikost (size), tj. počet bajtů, které jeho hodnota okupuje.
%SomeVar %SET ABC, DEF %SomeSize %SETS %SomeVar ; %SomeSize je nyní 8 (3 písmena + čárka + mezera + 3 písmena). %SizeOfSomeSize %SETS %SomeSize ; %SizeOfSomeSize je nyní 1 (jedna číslice).
%SETS musí mít právě jeden operand, který vypadá jako preprocesní %proměnná (procento následované identifikátorem).
Viz test t2861.
%SETL se podobá na %SETS, avšak přiřazuje délku (length) obsahu %proměnné, tj počet čárkou oddělených položek.
%SomeVar %SET ABC, DEF %SomeLength %SETL %SomeVar ; %SomeLength je nyní 2 (2 čárkou oddělené položky). %LengthOfSomeLength %SETL %SomeLength ; %LengthOfSomeLength je nyní 1 (jedna položka).
%SETL musí mít právě jeden operand, který vypadá jako preprocesní %proměnná (procento následované identifikátorem).
Vzi test t2866.
Uvažme, jak se překládá povel %Var1 %SET %Var2
.
€ASM nejprve expanduje %Var2 a výsledek expanze přiřadí do %Var1.
První dvě slova nejsou expandována, neboť %Var1
je právě přiřazovaný cíl, a %SET
je rezervované jméno, které se neexpanduje nikdy.
%SET2 je podobné jako %SET, ale pole operandů je expandováno 2 krát před samotným přiřazením. Každá expanze "spolkne" jeden znak procenta.
%V1 %SET "A" %V2 %SET "B" %V3 %SET "C" i %FOR 1..3 %DataExp %SET2 %%V%i DB %DataExp %ENDFOR i ; Emituje DB "A", DB "B", DB "C".
Viz také test t2871.
Pseudoinstrukce %SET2 se uplatní pouze v některých speciálních makrech, jako EndProcedure, kde je použita k expandování %proměnné s dosud neznámým, dynamicky se měnícím jménem.
Když jsou překládány pseudoinstrukce z rodiny SET*,
€ASM neexpanduje slovo v poli návěstí v pseudoinstrukcích jako
%Label %SET* cokoli
.
Toto platí pro %SET, %SETA, %SETB, %SETC, %SETU, %SETE, %SETS, %SETL, %SET2
ale nikoli pro %SETX. V této pseudoinstrukci je pole návěstí rovněž expandováno.
Po expanzi návěstí se %SETX chová stejně jako obyčejné %SET,
což znamená, že vyžaduje platné jméno %proměnné v poli návěstí.
Kupříkladu %%Var1 %SETX ABC
je ekvivalentní s %Var1 %SET ABC
.
Použitím %SETX můžeme přiřazovat %proměnné, jejichž jména nejsou explicitně určena a dynamicky se mění. Příklad:
i %FOR 1..4 %%M%i %SETX %i ; Totéž jako %M1 %SET 1, %M2 %SET 2 atd. %ENDFOR ; Přiřadily se hodnoty 1,2,3,4 %proměnným %M1,%M2,%M3,%M4.
Viz také test t2881.
%SETX se uplatní ve speciálních makrech, jako Procedure, kde je použito k přiřazování adres ze zásobníkového rámce (např. EBP+12) %proměnným, jejichž název nebyl určen v době psaní makra.
Blok instrukcí ohraničený pseudoinstrukcemi %MACRO a %ENDMACRO se nazývá
deklarace makra. Identifikátor v poli návěstí %MACRO
je název makra.
Pseudoinstrukce %MACRO spolu se svými operandy tvoří prototyp makra,
který deklaruje jméno makra a dává jména jeho argumentům.
Jakmile bylo deklarováno, makro může být v programu expandováno mnohokrát.
Dokud €ASM čte instrukce deklarace makra ve zdrojovém textu, neemituje žádná data. Instrukce z těla makra bydou emitovány až když je makro expandováno, uvedením jména makroinstrukce v programu.
Pseudoinstrukce %EXITMACRO umožňuje přerušit emitování například když byla detekována nějaká chyba.
Jak %EXITMACRO, tak %ENDMACRO mohou mít uvedeno jméno makra v poli operandu ke zvýraznění párování bloku.
Příklad definice a expanze makra:
AlignEAX %MACRO ; Makro pro zaokrouhlení EAX na násobek 4. ADD EAX,3 AND EAX,-4 %ENDMACRO AlignEAX MOV EAX,13 AlignEAX ; Po expanzi makra bude EAX obsahovat 16.
Pro více informací viz kapitolu Makroinstrukce.
Pseudoinstrukce %SHIFT je užitečná pouze v makru. Snižuje pořadové číslo operandů makra o hodnotu definovanou svým operandem. %SHIFT nemůže mít návěstí a má povolen pouze jeden operand, který se vyhodnotí na prosté číslo. Při jeho vynechání se předpokládá hodnota 1.
%SHIFT 0
nedělá nic.
%SHFT s negativním číslem invertuje směr posunu operandů.
Účinek operace %SHIFT je omezen pouze je-li k operandům makra přistupováno podle jejich pořadových čísel, jako %1, %2 atd. Přístup k operandům pomocí formálních jmen zůstává operací %SHIFT nenarušen.
Operandy vysunuté vlevo z pozice %1 do pozice nula nebo negativní již nebudou nadále přístupné pomocí pořadových čísel, avšak nejsou navždy ztraceny, neboť mohou být posunuty zpět pomocí negativního argumentu pseudoinstrukce %SHIFT.
| |Sample %MACRO Oper1, Oper2, Oper3 | |L1: DB %1, %Oper1 | | %SHIFT 1 | |L2: DB %1, %Oper1 | | %SHIFT 2 | |L3: DB %1, %Oper1 | | %ENDMACRO Sample |0000: | |0000: |Sample 0x44, 0x55, 0x66, 0x77 | +Sample %MACRO Oper1, Oper2, Oper3 |0000:4444 +L1: DB %1, %Oper1 | + %SHIFT 1 |0002:5544 +L2: DB %1, %Oper1 | + %SHIFT 2 |0004:7744 +L3: DB %1, %Oper1 | + %ENDMACRO Sample |0006: |Viz také test t7221.
Pseudoinstrukce %ERROR vloží uživatelsky definované oznámení do listingu a na standardní výstup oznámení. Oznámení se podobá těm, která jsou generována samotným €ASM. %ERROR se často používá v makroinstrukcích a obvykle varuje programátora, že makro nebylo použito očekávaným způsobem.
Uživatelská oznámení mají kód závažnosti U a úroveň 5,
tedy někde mezi varováním a chybou.
Programátor může určit identifikátor zprávy nepovinným klíčovým operandem
ID=
, který může specifikovat prosté číslo mezi
5000 a 5999.
%ERROR rovněž akceptuje číslo identifikátoru v rozmězí 0..999
a v tom případě k němu interně přidává 5000. Výchozí hodnotou je 0,
takže uživatelsky definované oznámení má identifikaci U5000,
nebylo-li (použitím ID=
) určeno jinak.
Text oznámení nemusí být v uvozovkách. Pokud sestává z více než jednoho operandu, budou spojeny doslova, včetně uvozovek a čárek. Příklady:
%ERROR Id=5123, Došlo k chybě. Zkuste to znovu.
Viz test t2581 pro více příkladů.
Pseudoinstrukce %DISPLAY je určena k získávání informací o interních objektech EuroAssembleru.
Každý takový objekt je zobrazen formou ladicího oznámení se závažností 1.
Oznámení je zobrazeno na standardní konzole (v každém průchodu)
a v listingu (v posledním průchodu).
%DISPLAY je aktivní i v neemitujících pasážích, jako bloky zakomentované pomocí
%COMMENT nebo %IF. Je určen k vyšetřování postupů €ASM, pokud něco nefunguje dle očekávání.
Pseudoinstrukce %DISPLAY přijímá libovolný počet operandů – kategorií specifikujících
druh objektů, které chceme zkontrolovat. Kategorie mohou být uvedeny jako pořadové nebo klíčové operandy
s hodnotou určující filtr, který může omezit počet zobrazených řádků.
Názvy kategorií nezávisí na velikosti písmen, ale hodnota filtru ano.
Hodnota filtru specifikuje prvních několik znaků z názvu objektů, které chceme zobrazit.
Hodnota filtru může končit hvězdičkou *, ale ta není povinná a bez ní to funguje stejně.
Například instrukce %DISPLAY Macros=Alig
zobrazí všechna makra, jejich jména začínají
"Alig".
Operandy pseudoinstrukce %DISPLAY mají poněkud uvolněnou syntaxi.
Kategorie objektu (pořadový nebo klíčový operand) může být zkrácen.
Stačí uvést tolik znaků, kolik je potřeba k identifikaci kategorie.
Například %DISPLAY se
zobrazí mapu všech segmentů a jejich sekcí.
%DISPLAY File
zobrazuje seznam vstupních souborů (zdrojový a vložené knihovny).
%DISPLAY sym=Num*, sym=En
zobrazí symboly, jejichž jména začínají Num nebo En.
%DISPLAY UserVar
, %DISPLAY UserVar=*
a %DISPLAY user=
fungují stejně
(prázdný filtr propustí všechna jména %proměnných).
Nefiltrovatelné kategorie, jako segmenty, kontextový zásobník, automatické makro %proměnné,
vždy zobrazují kompletní seznam, případná hodnota filtru se ignoruje.
Při specifikaci filtru uživatelsky definovaných a systémových %proměnných
může být počáteční znak procenta %, případně %^ vynechán,
anebo musí být procento zdvojeno (jinak by byla proměnná expandována na svůj obsah).
%DISPLAY UserVar=Loc
, %DISPLAY us=Loc*
a %DISPLAY user=%%Loc
mají shodnou funkci: zobrazují aktuální obsah uživatelsky definovaných %proměnných,
jejichž jména začínají na %Loc.
Operand %DISPLAY | Oznámení | Filtr | Řazení | Zobrazované objekty |
---|---|---|---|---|
All | D1100..D1900 | ano | abecední | Všechny níže specifikované objekty (zkratka pro Fil,Ch,Se,St,Co,Sym,L,Rel,M,V). |
Files | D1150..D1190 | ignorován | přirozené | Zdrojové soubory vložené do programu. |
Chunks | D1200..D1240 | ignorován | přirozené | Kusy zdrojového kódu. |
Sections | D1250..D1290 | ignorován | přirozené | Mapa grup, segmentů a sekcí. |
Segments | D1250..D1290 | ignorován | přirozené | Mapa grup, segmentů a sekcí. |
Groups | D1250..D1290 | ignorován | přirozené | Mapa grup, segmentů a sekcí. |
Structures | D1300..D1340 | ano | abecední | Struktury deklarované v programu. |
Context | D1350..D1390 | ignorován | dle zásobníku | Kontextový zásobník blokových instrukcí. |
Symbols | D1400..D1450 | ano | abecední | Všechny explicitně definované symboly (zkratka pro Fix,Unf,Unr,Ref). |
UnfixedSymbols | D1410..D1450 | ano | abecední | Symboly, jejichž vlastnosti dosud nejsou stabilní. |
FixedSymbols | D1420..D1450 | ano | abecední | Symboly, jejichž vlastnosti již jsou fixovány. |
UnreferencedSymbols | D1430..D1450 | ano | abecední | Dosud nepoužité symboly. |
ReferencedSymbols | D1440..D1450 | ano | abecední | Aspoň jednou zmíněné symboly. |
LiteralSymbols | D1500..D1540 | ignorován | abecední | Všechny literály. |
Relocations | D1550..D1590 | ignorován | přirozené | Relokační záznamy. |
Macros | D1600..D1690 | ano | abecední | Makroinstrukce dosud definované. |
Variables | D1700..D1790 | ano | abecední | Všechny preprocesní %proměnné momentálně nastavené (zkratka pro Au,Fo,Us,Sys). |
AutomaticVariables | D1710..D1730 | ignorován | fixní | Automatické makro %proměnné. |
FormalVariables | D1740..D1750 | ano | abecední | Formální makro/for %proměnné. |
UserVariables | D1760..D1770 | ano | abecední | Uživatelem definované preprocesní %proměnné. |
SystemVariables | D1780..D1790 | ano | abecední | Systémové preprocesní %^proměnné. |
Zobrazené oznámení zpravidla obsahuje jméno objektu, jeho atributy a další vlastnosti.
Operandy Groups, Segments, Sections jsou stejné, kterýkoli z nich vždy zobrazuje kompletní strom.
Řádky D1260 s grupami zobrazuje názvy grup.
Řádky D1270 se segmenty jsou odsazeny dvěma mezerami a zobrazují účel, šířku, zarovnání, kombinování, třídu, zdroj.
Řádky D1280 se sekcemi jsou odsazeny o čtyři mezery a zobrazují adresu, velikost, zarovnání, referenci, zdroj.
Vlastnost src= specifikuje, zda soubor nebo kus zdroje je
euroasm.ini
Vlastnost Chunku type= určuje informaci obsaženou v tomto kusu zdrojového textu:
Booleovská vlastnost ref= specifikuje, zda symbol, struktura nebo sekce byly použity
(referovány aspoň jednou v programu). Členové skupiny jsou automaticky brány jako used,
když je struktura definována.
Podobnou vlastností je fix= specifikující, zda ofset tohot symbolu je již fixní, tj. stabilní mezi průchody.
Vlastnost kontextu emit= informuje, zda je blok v normálním (emitujícím) stavu,
nebo zda je jím pouze procházeno bez generování kódu či dat.
Kontextová vlastnost %.= zobrazuje momentální hodnotu expanzního počitadla v tomto bloku.
Vlastnost src= identifikuje pozici ve zdrojovém textu, kde byl zobrazovaný objekt definován, ve standardní podobě "FileName"{LineNumber}.
Automatické a formální %proměnné jsou definovány pouze při expanzi %macro nebo %for,
tj. pokud je příkaz %DISPLAY Auto,Formal
vložen do těla %MACRO..%ENDMACRO nebo %FOR..%ENDFOR.
a makro je pak voláno.
Viz testy t2901..t2917 pro příklady volání %DISPLAY.
Na rozdíl od jiných instrukcí je %DISPLAY živá i v neemitujících blocích. Opatrně se zařazováním nefiltrované instrukce %DISPLAY v opakujících se preprocesních smyčkách (%FOR, %WHILE, %REPEAT), jelikož to může podstatně zahltit výstup.
Hlavním účelem %DISPLAY je hledat chybu v asm-time, kdy €ASM nepracuje podle očekávání. Spolu s volbami EUROASM
DISPLAYSTM=, DISPLAYENC=
a s volbami PROGRAMLISTGLOBALS=, LISTLITERALS=, LISTMAP=
poskytuje %DISPLAY silnou zbraň.
Pro vyšetřování programu za běhu použijte debugger nebo makro Debug.
Tyto pseudoinstrukce jsou rezervovány pro další rozšiřování EuroAssembleru, nejsou zatím implementovány. Viz také booleovské volby EUROASM DEBUG= a PROFILE=.
Makro je definováno blokem instrukcí (tělem makra) mezi pseudoinstrukcemi %MACRO a %ENDMACRO. Samotná pseudoinstrukce %MACRO, neboli ( prototyp makra) musí mít návěstí, které bude později použito k vyvolání makra (neboli k jeho expanzi).
Instrukce, která má jméno dříve definovaného makra ve svém poli operace,
se nazývá makroinstrukce nebo zkrátka jen makro.
Bude nahrazeno instrukcemi z bloku
%MACRO..%ENDMACRO
.
Makrem může být pevný statický sled instrukcí, jako
CarriageReturn %MACRO MOV AH,2 ; 3 instrukce mezi %MACRO a %ENDMACRO jsou tělem makra. MOV DL,13 INT 21h %ENDMACRO CarriageReturn
Užitečnější jsou makra, která mohou modifikovat expandované instrukce
v závislosti na operandech, se kterými jsou volány.
Makro bývá voláno s operandy dostupnými v těle makra jako
formální %proměnné nebo jako pořadové %proměnné %1, %2, %3,...
.
Operandy v makrodefinici mohou dostat dočasné formální
symbolické jméno; uvnitř makra jsou pak dostupné pod tímto formálním jménem
prefixovaným znakem procenta %.
Nebo mohou být referovány svým pořadovým číslem, rovněž prefixovaným znakem procenta %.
Klíčové operandy jsou dostupné pouze svým formálním jménem (klíčem) prefixovaným znakem procenta %.
Příklady:
Copy %MACRO Source, Destination, Size=ECX ; Instrukci %MACRO se říká prototyp makra. MOV ESI, %Source ; nebo MOV ESI, %1 MOV EDI, %Destination ; nebo MOV EDI, %2 MOV ECX, %Size REP MOVSB %ENDMACRO Copy
Předchozí makro nepotřebně přesouvá počet kopírovaných bajtů (Size)
do registru ECX i když už tam jsou v době jeho volání.
V tom případě může být expandovaná instrukce MOV ECX,ECX
uspořena:
Copy %MACRO Source, Destination, Size=ECX MOV ESI, %Source MOV EDI, %Destination %IF "%Size" !== "ECX" MOV ECX, %Size %ENDIF REP MOVSB %ENDMACRO Copy
Nyní když je makro voláno s parametry Copy From, To, Size=ecx
nebo jako Copy From, To
, žádná nadbytečná instrukce MOV ECX,ECX
do něj nebude expandována.
Pokud by jméno formální makro %proměnné náhodou kolidovalo se jménem některé
dříve uživatelem definované %proměnné, viditelnost uživatelské %proměnné
je dočasně potlačena ve prospěch formální %proměnné. Viz test t7347.
Automatické %proměnné, jako
%*, %#, %:, %1, %2,,, nejsou viditelné vně těla makra.
Počet operandů specifikovaný při volání makra nemusí odpovídat
počtu operandů zadanému při jeho deklaraci.
Je-li makro voláno s méně pořadovými operandy než deklaruje jeho prototyp,
€ASM to nepovažuje za chybu a tiše expanduje chybějící operandy do ničeho (do prázdna).
Je-li makro vyvoláno s více operandy, než specifikuje jeho prototyp,
tyto nadbytečné operandy nejsou k dispozici pod formálními jmény,
ale stále mohou být referovány pomocí automatického pořadového čísla, jako %2, %3 atd.
Viz též pseudoinstrukci %SHIFT.
Je-li při invokaci makra vynechán klíčový operand, ponechává si výchozí hodnotu deklarovanou v definici makra. Přidání dobrovolného klíčového operandu dovoluje rozšířit funkčnost makroinstrukce bez narušení zpětné kompatibility. Uvažujme toto prosté makro:
Write %MACRO TextPtr,TextSize ; Zapiš text na standardní výstup. MOV DX,%TextPtr MOV CX,%TextSize MOV BX,1 ; Handle standardního výstupu. MOV AH,40h ; Zapiš řetězec DS:DX do souboru nebo zařízení. INT 21h ; Vyvolej službu DOS. %ENDMACRO Write
Později můžeme chtít použít toto makro pro zápis i do jiných zařízení, než standardní výstup.
Rozšiříme je tedy klíčovým operandem Handle=
s předdefinovanou výchozí hodnotou
standardního výstupu:
Write %MACRO TextPtr,TextSize,Handle=1 ; Zapiš text na standardní výstup nebo do zařízení. MOV DX,%TextPtr MOV CX,%TextSize MOV BX,%Handle ; Handle požadovaného zařízení. MOV AH,40h ; Zapiš řetězec DS:DX do souboru nebo zařízení. INT 21h ; Vyvolej službu DOS. %ENDMACRO Write
Nyní je možné zapisovat i do jiných zařízení, například do řádkové tiskárny pomocí
Write Message,80,Handle=4
.
Rozšířené makro Write je zpětně kompatibilní.
Ani když náš starý program obsahuje aktualizovanou makroknihovnu
s vylepšeným makrem Write, nemusíme jej rekompilovat.
Podobně jako preprocesní %proměnné, mohou být makra redefinována. Není to ale obvyklé a €ASM bude v tom případě varovat oznámením W2512. Jednou definované makro může být oddefinováno pseudoinstrukcí %DROPMACRO.
Příklad situace, kdy může být zapomenutí makra užitečné, je emulace strojové instrukce
makrem se stejným jménem.
Strojová instrukce BSWAP, která reverzuje pořadí bajtů v 32bitovém registru,
nebyla dostupná, dokud se neobjevil procesor Intel 80386.
Přehození pořadí bylo u starších CPU řešitelné makrem, pomocí tří instrukcí ROR nebo ROL.
Pokud zjistíme, že náš program poběží na Pentiu, můžeme zapomenout na makro
a €ASM bude překládat BSWAP
jako nativní strojovou instrukci.
Pokročilý makrojazyk EuroAssembleru dovoluje měnit programovací styl.
Můžeme tvořit makroinstrukce, které napodobují funkce vyššího programovacího jazyka
Viz makra Ii*
ve zdrojovém souboru €ASM ii.htm jako příklad pseudojazyka vyvinutého
pro inteligentní popis konverze instrukcí do strojového kódu.
Pokud něco nefunguje podle očekávání, je vždy možné se podívat na generovaný kód expandovaných makroinstrukcí a zůstat u prostého asembleru.
Cílem činnosti €ASM je výstupní soubor v některém z formátů
vybraném volbou PROGRAM FORMAT=
.
Jsou tři kategorie výstupních souborů EuroAssembleru:
.onebo
.obj.
Defaultní přípona objektové nebo importní knihovny je .lib
, v případě dynamické knihovny to je .so
nebo .dll
.
.x,
.exenebo
.com. Může rovněž vytvářet dynamicky zaváděnou knihovnu DLL, velmi podobnou formátu PE, ovšem ta může být vykonávána pouze nepřímo, invokací jejích exportovaných funkcí jiným programem nebo speciálním Windows zaváděčem, jako je
RUNDLL32.exe.
Volba PROGRAM FORMAT=BIN
byla vybrána jako výchozí, pokud není FORMAT= explictině uvedeno.
Další výchozí volby pro formát BIN jsou:
Name: PROGRAM FORMAT=BIN, OUTFILE=%^PROGRAM.bin, MODEL=TINY, WIDTH=16, \ ENTRY=0, IMAGEBASE=0, SECTIONALIGN=0, FILEALIGN=0.
€ASM vytváří výchozí segment [BIN] s univerzálním účelem:
[BIN] SEGMENT WIDTH=16,ALIGN=16, \ PURPOSE=CODE+DATA+BSS+STACK+LITERALS
Následující volby pseudoinstrukce PROGRAM jsou irelevantní: DLLCHARACTERISTICS, ENTRY, ICONFILE, MAJOROSVERSION, MAJORSUBSYSTEMVERSION, MAJORIMAGEVERSION, MAJORLINKERVERSION, MINOROSVERSION, MINORSUBSYSTEMVERSION, MINORIMAGEVERSION, MINORLINKERVERSION, WIN32VERSIONVALUE, SIZEOFHEAPCOMMIT, SIZEOFHEAPRESERVE, SIZEOFSTACKCOMMIT, SIZEOFSTACKRESERVE, STUBFILE, SUBSYSTEM.
Obsah souboru formátu BIN je jednoduchý: prosté spojení emitovaných obsahů jeho segmentů. Neinicializované segmenty (BSS) jsou vynechány.
Zarovnání segmentů v image je určeno nejvyšší hodnotou
z PROGRAM FILEALIGN=0
, PROGRAM SECTIONALIGN=0
a
SEGMENT ALIGN=16
.
Mezery mezi segmenty jsou zaplněny buď kódem NOP 0x90 (pokud oba sousední segmenty mají
SEGMENT PURPOSE=CODE
), jinak kódem 0x00.
Typickou aplikací binárního formátu jsou čistě datové soubory, konverzní tabulky, ovladače a jiné speciální záležitosti, viz BIN projects.
Volba PROGRAM FORMAT=BOOT
generuje binární formát přizpůsobený bootování (zavádění operačního systému).
Rozdíly oproti formátu BIN:
.sec.
Výchozí volby pro formát BOOT:
Name: PROGRAM FORMAT=BOOT, OUTFILE=%^PROGRAM.sec, MODEL=TINY, WIDTH=16, \ ENTRY=, IMAGEBASE=0, SECTIONALIGN=0, FILEALIGN=0.
Následující volby pseudoinstrukce PROGRAM jsou irelevantní: DLLCHARACTERISTICS, ENTRY, ICONFILE, MAJOROSVERSION, MAJORSUBSYSTEMVERSION, MAJORIMAGEVERSION, MAJORLINKERVERSION, MINOROSVERSION, MINORSUBSYSTEMVERSION, MINORIMAGEVERSION, MINORLINKERVERSION, WIN32VERSIONVALUE, SIZEOFHEAPCOMMIT, SIZEOFHEAPRESERVE, SIZEOFSTACKCOMMIT, SIZEOFSTACKRESERVE, STUBFILE, SUBSYSTEM.
Viz vzorové projekty boottest.htm a boot16.htm.
Soubory ve formátu COM jsou dědictvím po operačním systému CP/M, jsou přímo spustitelné v DOS a ve 32bitových Windows. V jiných systémech pouze v emulátoru DOSu.
Výchozí volby pro formát COM:
Name: PROGRAM FORMAT=COM,OUTFILE=%^PROGRAM.com,MODEL=TINY,WIDTH=16,IMAGEBASE=0, \ ENTRY=256,SECTIONALIGN=0,FILEALIGN=0.
Volby ENTRY=0x100
a IMAGEBASE=0
jsou v tomto formátu pevné
a nelze je měnit (mohou být vynechány z instrukce PROGRAM).
€ASM vytvoří implicitní segment [COM] s univerzálním účelem:
[COM] SEGMENT WIDTH=16,ALIGN=16,PURPOSE=CODE+DATA+BSS+STACK+LITERALS
Následující volby pseudoinstrukce PROGRAM jsou irelevantní: DLLCHARACTERISTICS, ICONFILE, MAJOROSVERSION, MAJORSUBSYSTEMVERSION, MAJORIMAGEVERSION, MAJORLINKERVERSION, MINOROSVERSION, MINORSUBSYSTEMVERSION, MINORIMAGEVERSION, MINORLINKERVERSION, WIN32VERSIONVALUE, SIZEOFHEAPCOMMIT, SIZEOFHEAPRESERVE, SIZEOFSTACKCOMMIT, SIZEOFSTACKRESERVE, STUBFILE, SUBSYSTEM.
Struktura souboru COM se podobá formátu BIN, žádné metainformace nejsou
v souboru uloženy s výjimkou jeho přípony .com
, která říká operačnímu systému,
aby jej považoval za spustitelný.
Zaváděč OS alokuje 64 KB paměti, naplní segmentové registry
CS,DS,ES,SS paragrafovou adresou tohoto bloku, inicializuje 256 bajtů dlouhou strukturu
[PSP] na ofsetu 0, načte obsah souboru
na ofset 256 (0x0100), nastaví ukazatel zásobníku na vrchol alokovaného bloku
(obvykle SP=0xFFFE) a nakonec nastaví IP=0x0100.
Velikost kódu, dat a zásobníku by dohromady neměla přesáhnout 64 KB. Program ve formátu COM může používat 32bitové registry, pokud je CPU=386 nebo vyšší. Rovněž může za běhu požadovat další paměťové bloky od operačního systému.
Typickou aplikací tohoto zastaralého formátu jsou krátké a rychlé utility a programy Terminate-and-Stay-Resident (TSR) poskytující služby v DOS. Viz ukázkové projekty pro DOS.
Následující příklad programu ve formátu COM je pouze 1 bajt dlouhý, ale i tak je to formálně platný počítačový program, i když nic nedělá:
EUROASM Shortest PROGRAM FORMAT=COM RET ENDPROGRAM Shortest
Program ve formátu COM také může linkovat jiné moduly a knihovny, viz kombinace linkeru.
Specifikací programového formátu MZ vytvoříme 16bitový, případně 32bitový spustitelný program pro reálný mód,
který může být přímo spuštěn v DOS a ve 32bitových Windows.
Jeho struktura je popsána v dokumentaci [MZ]
a [MZEXE].
Dosový spustitelný program začíná podpisem MZ 'M','Z'
.
Výchozí volby pro PROGRAM FORMAT=MZ
jsou:
PROGRAM FORMAT=MZ, ENTRY=, OUTFILE=%^PROGRAM.exe, MODEL=SMALL, WIDTH=16, IMAGEBASE=0, \ SECTIONALIGN=0, FILEALIGN=0, SIZEOFSTACKCOMMIT=8K, SIZEOFHEAPCOMMIT=1M
€ASM vytvoří výchozí implicitní segmenty [CODE], [RODATA], [DATA], [BSS], [STACK] ve formátech MZ, OMF, LIBOMF.
Parametr PROGRAM SizeOfStackCommit=
vytváří defaultní velikost segmentu [STACK],
takže nemusíme explicitně definovat strojový zásobník, pokud byla volba AUTOSEGMENT=
povolena.
Parametr PROGRAM SizeOfHeapCommit=
může být použit k omezení velikosti heap
prealokované zaváděčem (člen .e_maxalloc Dosové hlavičky).
Pokud je paměťový model HUGE nebo FLAT a šířka programu není explicitně určena, použije se
PROGRAM WIDTH=32
, jinak je to 16.
ImageBase=0
v tomto formátu a nemůže být měněna.
Explictní stanovení PROGRAM Entry=
je v MZ formátu povinné.
Následující volby pseudoinstrukce PROGRAM jsou irelevantní: DLLCHARACTERISTICS, ENTRY, ICONFILE, MAJOROSVERSION, MAJORSUBSYSTEMVERSION, MAJORIMAGEVERSION, MAJORLINKERVERSION, MINOROSVERSION, MINORSUBSYSTEMVERSION, MINORIMAGEVERSION, MINORLINKERVERSION, WIN32VERSIONVALUE, SIZEOFHEAPRESERVE, SIZEOFSTACKRESERVE, STUBFILE, SUBSYSTEM.
Jako příklad spustitelného programu MZ pro DOS viz test t8300.
Object Module Format popsaný v dokumentaci [OMF] je určen k linkování do 16bitových a 32bitových programů pro reálný mód. Importy v tomto formátu jsou linkovatelné také do programů v chráněném módu (MS-Windows).
Výchozí segmenty jsou stejné jako u formátu MZ.
Soubor je rozpoznán pro linkování, pokud sestává z platných záznamů OMF a první záznam je THEADR nebo LHEADR.
Výchozí volby pro tento formát jsou:
Name: PROGRAM FORMAT=OMF,OUTFILE=%^PROGRAM.obj,MODEL=SMALL,WIDTH=16
Následující volby pseudoinstrukce PROGRAM jsou irelevantní: DLLCHARACTERISTICS, ENTRY, FILEALIGN, ICONFILE, IMAGEBASE, MAJOROSVERSION, MAJORSUBSYSTEMVERSION, MAJORIMAGEVERSION, MAJORLINKERVERSION, MINOROSVERSION, MINORSUBSYSTEMVERSION, MINORIMAGEVERSION, MINORLINKERVERSION, WIN32VERSIONVALUE, SECTIONALIGN, SIZEOFHEAPCOMMIT, SIZEOFHEAPRESERVE, SIZEOFSTACKCOMMIT, SIZEOFSTACKRESERVE, STUBFILE, SUBSYSTEM.
Jako příklad modulu OMF viz test t8400.
Formát knihovny OMF je popsán v Apendix2 stejného dokumentu jako [OMF].
Hashovaný slovník vyžadovaný formátem na konci knihovny je EuroAssemblerem vytvářen,
avšak při linkování je ignorován. Je-li knihovna LIBOMF linkována EuroAssemblerem do jiného programu,
její PUBLIC symboly budou hledány sekvenčně.
Velikost stránky knihoven LIBOMF vytvářených EuroAssemblerem je napevno 16.
Výchozí segmenty jsou stejné jako ve formátu MZ.
Souborový formát LIBOMF je rozpoznáván pro LINK pokud začíná záznamem LIBHDR s délkou strany 16, 32, 64,..32K, a pokud je tento záznam následován platnými moduly OMF, které začínají záznamem THEADR nebo LHEADR a které končí záznamem MODEND nebo MODEND32. Slovník hashů na konci knihovny není používán.
Výchozí volby formátu PROGRAM FORMAT=LIBOMF
jsou:
Name: PROGRAM FORMAT=LIBOMF,OUTFILE=%^PROGRAM.lib
Ostatní vlastnosti jsou zděděny z linkovaných modulů.
Následující volby pseudoinstrukce PROGRAM jsou irelevantní: DLLCHARACTERISTICS, ENTRY, FILEALIGN, ICONFILE, IMAGEBASE, MAJOROSVERSION, MAJORSUBSYSTEMVERSION, MAJORIMAGEVERSION, MAJORLINKERVERSION, MODEL, MINOROSVERSION, MINORSUBSYSTEMVERSION, MINORIMAGEVERSION, MINORLINKERVERSION, WIN32VERSIONVALUE, SECTIONALIGN, SIZEOFHEAPCOMMIT, SIZEOFHEAPRESERVE, SIZEOFSTACKCOMMIT, SIZEOFSTACKRESERVE, STUBFILE, SUBSYSTEM, WIDTH.
Moduly, které mají být vloženy do knihovny, by měly být předem přeloženy do souborů ve formátu OMF.
Pokud program představující knihovnu také obsahuje nějaký kód,
bude přeložen a uložen jako první modul knihovny.
Moduly z jiných linkovaných knihoven, které neobsahují žádný globální symbol,
nebudou do cílové knihovny vůbec zařazeny.
Příklad statické knihovny linkované ze tří samostatných modulů:
MyLib: PROGRAM FORMAT=LIBOMF LINK "Module1.obj", "Module2.obj", "Module3.obj" ENDPROGRAM MyLib
Třebaže byl formát OMF vyvinut pro programy v reálném módu, může být doplněn OMF záznamy COMENT/IMPDEF a taková knihovna importních záznamů použita v programu pro Windows.
Některé knihovní programy, jako [ALIB],
vytvářejí delší variantu importních záznamů, která přidává záznamy LEDATA+FIXUPP
včetně relokabilního strojového kódu proxy-skoků na importovanou funkci.
€ASM nevytváří delší verzi importních záznamů,
ale jak dlouhá, tak krátká verze jsou akceptovány linkerem.
Příklad programu vytvářejícího čistě importní knihovnu v krátkém formátu OMF:
ImpLib PROGRAM FORMAT=LIBOMF IMPORT LIB="kernel32.dll",TerminateProcess,TerminateThread IMPORT LIB="user32.dll",CreateCursor,CreateIcon,CreateMenu ENDPROGRAM ImpLib
Jako příklad knihovny LIBOMF viz test t8600.
EuroAssembler implementuje objektový formát COFF v modifikaci od Microsoftu popsané v [MS_PECOFF]. Tento popis také platí pro €ASM formáty LIBCOF, PE, DLL (formáty založené na COFF).
€ASM zde vytváří výchozí segmenty
("sekce" v terminologii Microsoftu):
[.text], [.rodata], [.data], [.bss]
.
Strojový zásobník pro spustitelný soubor bude vytvořen zaváděčem při spouštění programu
a nemusíme se o něj starat.
Výchozí volby pro PROGRAM FORMAT=COFF
jsou:
PROGRAM FORMAT=COFF,OUTFILE=%^PROGRAM.obj,MODEL=FLAT,WIDTH=32
Následující volby pseudoinstrukce PROGRAM jsou irelevantní: DLLCHARACTERISTICS, ENTRY, FILEALIGN, ICONFILE, IMAGEBASE, MAJOROSVERSION, MAJORSUBSYSTEMVERSION, MAJORIMAGEVERSION, MAJORLINKERVERSION, MINOROSVERSION, MINORSUBSYSTEMVERSION, MINORIMAGEVERSION, MINORLINKERVERSION, WIN32VERSIONVALUE, SECTIONALIGN, SIZEOFHEAPCOMMIT, SIZEOFHEAPRESERVE, SIZEOFSTACKCOMMIT, SIZEOFSTACKRESERVE, STUBFILE, SUBSYSTEM.
Hodnota generovaná do členu PFCOFF_FILE_HEADER.Machine
je pro zděděný mód COFF vždy 0x014C
(Intel 386) bez ohledu na hodnotu EUROASM CPU=
.
Pro 64bitový mód PECOFF je vždy 0x8664
(architektura AMD64).
Architektura Itanium (0x0200) není podporována
Obsah členu PFCOFF_FILE_HEADER.TimeDateStamp
odpovídá aktuálnímu systémovému času, ledaže by byl zfalšován volbou EUROASM TIMESTAMP=
.
Linkovaný modul COFF je rozeznáván obsahem členu
PFCOFF_FILE_HEADER.Machine,
který může mít jednu z hodnot 0x0000, 0x014C, 0x014D, 0x014E, 0x0200, 0x8664
.
Jako příklad modulu COFF viz test t8850 (pro Windows) nebo t9000 (pro Linux).
Formát knihovny COFF je popsán v [COFFlib].
Výchozí volby pro PROGRAM FORMAT=LIBCOF
jsou:
PROGRAM FORMAT=LIBCOF,OUTFILE=%^PROGRAM.lib,MODEL=FLAT,WIDTH=32
Výchozí segmenty jsou stejné jako u formátu COFF.
Následující volby pseudoinstrukce PROGRAM jsou irelevantní: DLLCHARACTERISTICS, ENTRY, FILEALIGN, ICONFILE, IMAGEBASE, MAJOROSVERSION, MAJORSUBSYSTEMVERSION, MAJORIMAGEVERSION, MAJORLINKERVERSION, MINOROSVERSION, MINORSUBSYSTEMVERSION, MINORIMAGEVERSION, MINORLINKERVERSION, WIN32VERSIONVALUE, SECTIONALIGN, SIZEOFHEAPCOMMIT, SIZEOFHEAPRESERVE, SIZEOFSTACKCOMMIT, SIZEOFSTACKRESERVE, STUBFILE, SUBSYSTEM.
Knihovna COFF je identifikována podpisem !<arch>
následovaným bajtem 0x0A
.
Knihovnu LIBCOF můžeme vytvořit linkováním z jiných objektových souborů. Moduly by měly být nejprve přeloženy do souborů ve formátu COFF (ale lze i OMF nebo ELF). Pokud program představující knihovnu také obsahuje nějaký kód (mimo instrukce LINK), bude přeložen a uložen jako první člen knihovny. Moduly nedeklarující žádný globální symbol nebudou do knihovny vůbec zařazeny. Příklad knihovny LIBCOF obsahující tři moduly:
MyLib: PROGRAM FORMAT=LIBCOF LINK "Module1.obj", "Module2.obj", "Module3.obj" ENDPROGRAM MyLib
€ASM nevytváří delší verzi importních knihovních záznamů, ale jeho linker akceptuje dlouhou i krátkou verzi. Příklad programu vytvářejícího importní knihovnu v krátkém formátu:
ImpLib: PROGRAM FORMAT=LIBCOF IMPORT LIB="kernel32.dll",TerminateProcess,TerminateThread IMPORT LIB="user32.dll",CreateCursor,CreateIcon,CreateMenu ENDPROGRAM ImpLib:
Jako příklad knihovny LIBCOF viz test t9150.
ELF neboli Executable and Linkable Format je používán v Linuxu. Existují tři druhy souborů ELF:
Výchozí volby pro PROGRAM FORMAT=ELF
jsou
Name: PROGRAM FORMAT=ELF, OUTFILE=%^PROGRAM.o, MODEL=FLAT, WIDTH=32, \ FILEALIGN=16
ELF je objektový (linkovatelný) soubor s přípomou .o
.
Jeho defaultní segmenty jsou [.text], [.rodata], [.data], [.bss]
.
Segmentům se říká sekce v dokumentaci [ELF].
Vedle těchto "sekcí" vytváří €ASM také služební sekce [.symtab], [.strtab], [.shstrtab], [.rela.text], [.rela.data] aj.
.
Viz test t9750 jako příklad objektu ELF.
Následující volby pseudoinstrukce PROGRAM jsou irelevantní: DLLCHARACTERISTICS, ENTRY, ICONFILE, IMAGEBASE, MAJOROSVERSION, MAJORSUBSYSTEMVERSION, MAJORIMAGEVERSION, MAJORLINKERVERSION, MINOROSVERSION, MINORSUBSYSTEMVERSION, MINORIMAGEVERSION, MINORLINKERVERSION, WIN32VERSIONVALUE, SECTIONALIGN, SIZEOFHEAPCOMMIT, SIZEOFHEAPRESERVE, SIZEOFSTACKCOMMIT, SIZEOFSTACKRESERVE, STUBFILE, SUBSYSTEM.
Spustitelnému programu pro Linux přiděluje €ASM souborovou příponu .x
,
pokud není předepsáno jinak volbou PROGRAM OUTFILE=.
Formát ELFX vytváří segmentové skupiny [LOAD.HDR], [LOAD.CODE], [LOAD.RODATA], [LOAD.DATA]
,
viz například test t9850.
Těmto skupinám se v [ELF] dokumentaci říká program headers.
Name: PROGRAM FORMAT=ELFX, OUTFILE=%^PROGRAM.x, MODEL=FLAT, WIDTH=32, \ ENTRY=, IMAGEBASE=4M, FILEALIGN=16, SECTIONALIGN=4K
Výchozí přípona souboru je .x
. Parametr ENTRY= je povinný, určuje vstupní bod programu.
Následující volby pseudoinstrukce PROGRAM jsou irelevantní: DLLCHARACTERISTICS, ICONFILE, MAJOROSVERSION, MAJORSUBSYSTEMVERSION, MAJORIMAGEVERSION, MAJORLINKERVERSION, MINOROSVERSION, MINORSUBSYSTEMVERSION, MINORIMAGEVERSION, MINORLINKERVERSION, WIN32VERSIONVALUE, SIZEOFHEAPCOMMIT, SIZEOFHEAPRESERVE, SIZEOFSTACKCOMMIT, SIZEOFSTACKRESERVE, STUBFILE, SUBSYSTEM.
Volba PROGRAM FORMAT=ELFSO vytváří DSO - Dynamic Shared Object pro Linux.
EuroAssembler vytváří DSO s příponou souboru .so
, avšak neumí jej dynamicky linkovat,
nenahrazuje zde funkcionalitu komplexního Linuxového dynamického linkeru GNU ld
.
Soubor DSO může být linkován do programu ELFX pouze staticky.
Name: PROGRAM FORMAT=ELFSO, OUTFILE=%^PROGRAM.so, MODEL=FLAT, WIDTH=32, \ IMAGEBASE=4M, FILEALIGN=4K, SECTIONALIGN=4K
Následující volby pseudoinstrukce PROGRAM jsou irelevantní: DLLCHARACTERISTICS, ENTRY, ICONFILE, MAJOROSVERSION, MAJORSUBSYSTEMVERSION, MAJORIMAGEVERSION, MAJORLINKERVERSION, MINOROSVERSION, MINORSUBSYSTEMVERSION, MINORIMAGEVERSION, MINORLINKERVERSION, WIN32VERSIONVALUE, SECTIONALIGN, SIZEOFHEAPCOMMIT, SIZEOFHEAPRESERVE, SIZEOFSTACKCOMMIT, SIZEOFSTACKRESERVE, STUBFILE, SUBSYSTEM.
Portable Executable formát PE pro Windows je popsán v dokumentu [MS_PECOFF].
Výchozí volby pro PROGRAM FORMAT=PE
jsou
Name: PROGRAM FORMAT=PE,OUTFILE=%^PROGRAM.exe,MODEL=FLAT,WIDTH=32,IMAGEBASE=4M,FILEALIGN=512,SECTIONALIGN=4K, \ SUBSYSTEM=CON,ICONFILE="euroasm.ico",MAJORLINKERVERSION=1,MINORLINKERVERSION=0,ENTRY=, \ MAJOROSVERSION=4,MINOROSVERSION=0,MAJORIMAGEVERSION=1,MINORIMAGEVERSION=0, \ MAJORSUBSYSTEMVERSION=4,MINORSUBSYSTEMVERSION=0,WIN32VERSIONVALUE=0,DLLCHARACTERISTIC=0x000F, \ SIZEOFSTACKRESERVE=1M,SIZEOFSTACKCOMMIT=8K,SIZEOFHEAPRESERVE=4M,SIZOHEAPCOMMIT=1M
Výchozí segmenty jsou stejné jako ve formátu COFF.
Soubor PE začíná Dosovým programem (stub) ve formátu MZ, který se spouští pokud PE program není spuštěn ve Windows.
Na pozici členu PFMZ_DOS_HEADER.e_lfanew
očekává signaturu PE s bajty 'P','E',0,0
.
Starší souborový formát se signaturou NE (New Executable), používaný v 16bitových Windows a OS/2, není EuroAssemblerem podporován.
Po souborové hlavičce COFF následuje PFPE_OPTIONAL_HEADER. Téměř všechna její pole lze konfigurovat
pomocí voleb pseudoinstrukce PROGRAM.
Položka PROGRAM ENTRY=
je v PE formátu povinná.
Volba PROGRAM STUBFILE=
specifikuje jméno MZ programu použitého, když
byl náš program spuštěn v DOS.
Pokud je ponecháno prázdné, €ASM použije vlastní vestavěný stub, který oznámí
This program was launched in DOS but it requires Windows. a skončí.
Výchozí volba PROGRAM ICONFILE="euroasm.ico"
určuje jméno souboru s ikonou
,
která bude vestavěna do segmentu resources spustitelného souboru.
Vizuálně reprezentuje náš program na Ploše nebo v Průzkumníku Windows.
Tento parametr je ignorován, pokud byl explicitně přilinkován libovolný soubor resources; pak se použije první ikona z tohoto souboru. Je-li volba ICONFILE= prázdná, a pokud není přilinkován žádný soubor resources, příslušná sekce [.rsrc] bude z PE souboru zcela vynechána.
Po "volitelné" hlavičce následuje 16 záznamů speciálních adresářů, které identifikují sekce s účely jinými než běžné účely CODE, DATA, BSS. Viz posledních 16 řádků lines v tabulce účelu segmentů, počínaje EXPORT.
EuroAssembler nativně podporuje jen několik speciálních adresářů:
Ostatní speciální adresáře nejsou v této verzi €ASM podporovány. Nicméně jejich segmenty mohou být definovány explicitně a jejich obsah vytvořen ručně nebo nějakou utilitou třetí strany, a pak emitován do segmentu pomocí INCLUDEBIN nebo definicemi dat. Pokud parametr segmentu PURPOSE= přesně (až na velikost písmen) souhlasí s názvem v tabulce účelů, bude vytvořen odpovídající vstup ve "volitelné" hlavičce PE, který pokryje celý obsah segmentu. Příklad:
[.cormeta] SEGMENT PURPOSE=CLR D '<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">' D ' <application>' D ' <!-- A list of all Windows versions that this application is designed to work with.>' D ' </application>' D ' </compatibility>'
Pokud je povolena volba EUROASM DEBUG=ENABLED
,
tabulka symbolů bude připojena k image souboru PE.
Debugery by měly být schopny načítat symbolická jména z laděného spustitelného souboru, a spojit je s disasemblovanými zdrojovými řádky. Bohužel ale žádný z mně známých debugerů nebyl schopen využít tabulku symbolů z PE (místo toho bylo třeba nejprve přeložit do formátu COFF).
Souborový formát DLL je téměř identický s formátem PE, s několika drobnými rozdíly:
Člen souborové hlavičky
PFCOFF_FILE_HEADER.Characteristic je označen jako pfcoffFILE_DLL = 0x2000
,
výchozí přípona souboru je .dll
, ImageBase je 256 MB.
Name: PROGRAM FORMAT=DLL,OUTFILE=%^PROGRAM.dll,IMAGEBASE=256M
Volba ENTRY=
je u DLL nepovinná.
Výchozí segmenty jsou stejné jako ve formátu COFF.
Dynamicky linkovatelné symboly musí být deklarovány s viditelností EXPORT.
Pseudoinstruckce EXPORT podporuje
DLL forwarding
exportovaných funkcí do jiné funkce v jiné knihovně DLL, používajíc klíčové operandy EXPORT
se jmény FWD= and LIB=. Viz test t9475 jako příklad forwardingu.
Formát DLL může být používán pro knihovnu resource, která obsahuje pouze sekci [.rsrc], typicky se jedná o sbírku ikon. Toho je dosaženo linkováním souboru resources, který byl vytvořen pomocí resource kompileru třetí strany. Příklad resource-only DLL obsahující 3 ikony můžeme vidět v testech t9485 a t9536.
Microsoft resources je obecné jméno pro multimediální data, jako bitmapové obrázky,
ikony, tvary kurzorů, fonty apod.
Tyto zdroje (resources) používané v grafických programech jsou popsány ve skriptu resources
jako strom odkazující na jednotlivé prvky grafických souborů.
Typický skript je prostý textový soubor s příponou .rc
a měl by být zkonvertován pomocí resource compiler
na binární soubor resources s příponou .res
, kterou umí €ASM linkovat.
Jeho formát je popsán v dokumentaci [RSRC].
nefunguje,
EuroAssembler neumí kompilovat skripty resources. Použijte nástroj třetí strany, jako
[MS_RC], [GoRC],
nebo [ResourceHacker].MyCompiledResource PROGRAM FORMAT=RSRC
Je-li soubor resources linkován do PE nebo DLL image vytvořené EuroAssemblerem,
volba PROGRAM ICONFILE=
se ignoruje. Soubor je přeložen na interní strukturu
binárního stromu v sekci [.rsrc], na niž odkazuje speciální adresář RESOURCE.
Šířka výstupních souborů linkovaných EuroAssemblerem je určována volbou
PROGRAM WIDTH=
, jejíž výchozí hodnota pro formáty založené na COFF je 32 bitů.
K vytvoření 64bitových programů ELF, ELFX, ELFSO, PE, DLL, COFF nebo LIBCOF musí být
explicitně stanovena PROGRAM WIDTH=64. Rovněž by pak měla být povolena volba
EUROASM CPU=X64
.
Člen | PROGRAM WIDTH=16 | PROGRAM WIDTH=32 | PROGRAM WIDTH=64 |
---|---|---|---|
PFCOFF_FILE_HEADER.Machine | 0x014C (Intel 386) | 0x014C (Intel 386) | 0x8664 (AMD64) |
PFCOFF_FILE_HEADER.Characteristics:32BIT_MACHINE | 0 (nepravda) | 0x0100 (pravda) | 0 (nepravda) |
PFCOFF_FILE_HEADER.Characteristics:LARGE_ADDRESS_AWARE | 0 (nepravda) | 0 (nepravda) | 0x0020 (pravda) |
PFPE_OPTIONAL_HEADER32.Magic | 0x010B (PE32) | 0x010B (PE32) | 0x020B (PE32+) |
SIZE# PFPE_OPTIONAL_HEADER32 | 224 | 224 | 240 |
Tato kapitola popisuje možnosti EuroAssembleru
Mnoho asemblerů poskytuje nástroje pomáhající programátorovi s opakující se namáhavou prací, říká se jim makroasemblery. Preprocesní (makro) aparát je v EuroAssembleru rozpoznatelný podle znaménka procenta % přidaného před pseudoinstrukce řídicí opakované generování bloků kódu (%REPEAT, %WHILE, %FOR, %MACRO), podmíněný překlad (%IF, %COMMENT), laděni v asm-time (%DISPLAY) a přiřazování a expanzi preprocesních %proměnných (rodina pseudoinstrukcí %SET*).
Tato sbírka nástrojů manipuluje se zdrojovým textem dříve, než je předložen ke konečnému zpracování asemblerem (předložen prostému asembleru, který o preprocesním aparátu nic neví).
Některé kompilery provedou preprocesing ve speciálním nultém průchodu, který načítá vstupní zdrojový kód a jehož výstupem je kód prostého asembleru. Předzpracovaný mezikód pak může být manuálně prohlédnut.
EuroAssembler používá odlišný přístup: namísto předzpracování celého zdrojového souboru najednou jej předzpracovává instrukci po instrukci v každém průchodu. To dovoluje manipulovat i s údaji, jenž se dynamicky mění a které nejsou pevné dříve než €ASM dostal příležitost projít celým programem aspoň jednou; týká se to například vzdálenosti mezi návěstími, velikosti dosud nedefinovaných struktur apod.
Když €ASM čte řádek zdrojového textu, nejprve ho prohledá, zda obsahuje procento %. Pokud ano, dívá se na následující znak a připraví kopii zdrojového řádku pro prostý asembler, expandovanou dle následujících pravidel:Pro více detailů ohledně viditelnosti %proměnných viz zdrojový text VarExpand.
Znak následující % Příklad Co jej nahradí % %% Jednoduchý znak procenta %
& %& Velikost nebo délka suboperace . %. Počitadlo expanzí : %: Návěstí makra ! %!formal Invertovaná podmínka, např. NC
* %* Seznam pořadových operandů makra # %# Počet pořadových operandů makra =* %=* Seznam klíčových operandů makra =# %=# Počet klíčových operandů makra ^identifier %^Width Systémová %^proměnná (čísla 16, 32 nebo 64} decimal digit(s) %12 Pořadový operand makra, např. dvanáctý letter(s) %If Jméno pseudoinstrukce je ponecháno neexpandované ( %If
)%Size Pokud je to formální operand, bude expandován na svou hodnotu, %OtherId jinak je expandován jako uživatelem definovaná preprocesní %proměnná
Vztah mezi preprocesingem a prostým asemblerem se podobá vztahu mezi Javascriptem a prostým textem HTML v internetových browserech.
Správnou funkci €ASM preprocesingu můžeme zkontrolovat v listingu
povolením voleb EUROASM LISTVAR=ENABLE, LISTREPEAT=ENABLE, LISTMACRO=ENABLE
.
V této kapitole se podíváme, jako se dá v €ASM rozdělit funkčnost programu na menší subprogramy.
Dejme tomu, že budeme potřebovat funkci, která kalkuluje třetí mocninu kladného čísla. Výsledek by se měl vejít do 32 bitů, jinak program ohlásí přetečení a skončí.
Předpokládejme 32bitový mód a vstupní číslo načtené do registru EAX. Použijeme instrukci MUL (unsigned multiplication) dvakrát:
Přímé řešení vkládá kód přímo do hlavního toku programu.
; EAX obsahuje vstupní číslo N. MOV ECX,EAX ; Zkopíruj vstupní číslo N do registru ECX. MUL ECX ; Nechť EDX:EAX = N*N JC Abort: ; CF=OF=1 pokud je EDX nenulové (32bitové přetečení). MUL ECX ; Nechť EDX:EAX = N*N*N JC Abort: ; Abort při přetečení. ; EAX nyní obsahuje N3, pokračuj v hlavním toku programu.
Pokud je výše zmíněná kalkulace potřebná častěji než jednou, měli bychom uvážit
refactorizaci přímého kódu na subproceduru, která bude volána opakovaně.
Vložíme proceduru nazvanou Cube
do hlavního toku programu,
když bude její funkce poprvé potřeba. Vložení volatelné procedury vyžaduje její přeskočení.
; EAX obsahuje vstupní číslo N. CALL Cube: ; Vyvolej funkci, která kalkuluje N3. JC Abort: ; Abort při overflow. JMP Bypass: ; Přeskoč kód funkce. Cube PROC ; Procedura kalkuluje třetí mocninu N. ; Input: EAX=celé číslo N. ; Output: CF=OF=0, EAX=N3, ECX=N, EDX=0. ; Overflow:CF=OF=1, EAX,ECX,EDX nedefinováno. MOV ECX,EAX ; Zkopíruj vstupní číslo N do registru ECX. MUL ECX ; Nechť EDX:EAX = N*N JC .Abort ; CF=OF=1 pokud je EDX nenulové (32bitové přetečení). MUL ECX ; Nechť EDX:EAX = N*N*N .Abort:RET ; CF=OF=1 pokud je EDX nenulové (32bitové přetečení). ENDPROC Cube Bypass: ; EAX nyní obsahuje N3, pokračuj v hlavním toku programu.
Instrukci JMP Bypass:
bychom mohli uspořit, pokud by
procedura byla definována někde jinde, mimo programový tok.
Toho lze dosáhnout přemístěním procedury do jiné sekce, například do [Subproc]
.
; EAX obsahuje vstupní číslo N. CALL Cube: ; Vyvolej funkci, která kalkuluje N3. JC Abort: ; Abort při overflow. %CurrentSect %SET %^Section ; Zálohuj jméno současné sekce do %proměnné. [Subproc] ; Přepni emitování do jiné kódové sekce. Cube PROC ; Procedura kalkuluje třetí mocninu N. ; Input: EAX=celé číslo N. ; Output: CF=OF=0, EAX=N3, ECX=N, EDX=0. ; Overflow:CF=OF=1, EAX,ECX,EDX nedefinováno. MOV ECX,EAX ; Zkopíruj vstupní číslo N do registru ECX. MUL ECX ; Nechť EDX:EAX = N*N JC .Abort ; CF=OF=1 pokud je EDX nenulové (32bitové přetečení). MUL ECX ; Nechť EDX:EAX = N*N*N .Abort:RET ; CF=OF=1 pokud je EDX nenulové (32bitové přetečení). ENDPROC Cube [%CurrentSect] ; Návrat k původní sekci. ; EAX nyní obsahuje N3, pokračuj v hlavním toku programu.
Namísto ručního přepnutí do odlišné sekce bychom mohli využít
blok PROC1..ENDPROC1
, který přepíná do jiné sekce
[@RT1] a vrací se zpět zcela automaticky.
; EAX obsahuje vstupní číslo N. CALL Cube: ; Vyvolej funkci, která kalkuluje N3. JC Abort: ; Abort při overflow. Cube PROC1 ; Procedura kalkuluje třetí mocninu N. Je umístěna v sekci [@RT1]. ; Input: EAX=celé číslo N. ; Output: CF=OF=0, EAX=N3, ECX=N, EDX=0. ; Overflow:CF=OF=1, EAX,ECX,EDX nedefinováno. MOV ECX,EAX ; Zkopíruj vstupní číslo N do registru ECX. MUL ECX ; Nechť EDX:EAX = N*N JC .Abort ; CF=OF=1 pokud je EDX nenulové (32bitové přetečení). MUL ECX ; Nechť EDX:EAX = N*N*N .Abort:RET ; CF=OF=1 pokud je EDX nenulové (32bitové přetečení). ENDPROC1 Cube ; Konec procedury v sekci [@RT1]. Návrat do [.text]. ; EAX nyní obsahuje N3, pokračuj v hlavním toku programu.
Definice funkce Cube v místě, kde je použita, je vhodná pro porozumění.
Na druhé straně, pokud je takových funkcí více, zahlcuje to hlavní linku programu.
Organizace by byla jasnější, pokud by pomocné podprogramy byly odloženy do jiného souboru,
např. functions.inc
. Tento soubor pak bude
inkludována do hlavního zdrojového kódu v asm-time.
INCLUDE "functions.inc" ; Soubor obsahující proceduru Cube: PROC
.
; EAX obsahuje vstupní číslo N.
CALL Cube: ; Vyvolej funkci, která kalkuluje N3.
JC Abort: ; Abort při overflow.
; EAX nyní obsahuje N3, pokračuj v hlavním toku programu.
Funkce definované v inkludovaném souboru functions.inc
mohou být
zabaleny do bloku functions PROGRAM
..ENDPROGRAM
a přeloženy zvlášť do objektového formátu OMF, ELF nebo COFF functions.obj
,
případně do knihovny objektů. Jméno funkce (Cube
) musí být deklarováno jako
GLOBAL nebo PUBLIC v objektovém kódu, a jako GLOBAL nebo EXTERN v hlavním souboru.
Namísto explicitní deklarace pomocí GLOBAL lze globálnost funkcí označovat dvojitou dvojtečkou
(Cube::
). Přeložený objekt pak bude staticky linkován do hlavního programu v link-time.
LINK "functions.obj" ; Objektový soubor s procedurou Cube. ; EAX obsahuje vstupní číslo N. CALL Cube:: ; Vyvolej funkci, která kalkuluje N3. JC Abort: ; Abort při overflow. ; EAX nyní obsahuje N3, pokračuj v hlavním toku programu.
Funkce definované v inkludovaném souboru functions.inc
také mohly být zabaleny
do bloku functions PROGRAM
..ENDPROGRAM
a přeloženy zvlášť do dynamicky linkované knihovny functions.dll
.
Jméno funkce (Cube
) musí být deklarováno jako EXPORT v této knihovně,
a jako IMPORT v hlavním spustitelném souboru.
Přeložená funkce z programu DLL pak bude dynamicky svázána s hlavním programem v bind-time.
IMPORT Cube, LIB="functions.dll" ; EAX obsahuje vstupní číslo N. CALL Cube:: ; Vyvolej funkci, která kalkuluje N3. JC Abort: ; Abort při overflow. ; EAX nyní obsahuje N3, pokračuj v hlavním toku programu.
Alternativní přístup k opakování inline kódu je využití makra, které expanduje kdykoli je požadována jeho funkčnost.
Instrukce, které definují makro, nemusí být přeskakovány, neboť neemitují žádný kód, avšak makrodefinice se musí objevit dříve, než bude makro použito. Tato definice také mohla být odložena do inkludovaného souboru, podobně jako v metodě PROC v INCLUDE.
Cube %MACRO
MOV ECX,EAX ; Zkopíruj vstupní číslo N do registru ECX.
MUL ECX ; Nechť EDX:EAX = N*N
JC Abort%.: ; CF=OF=1 pokud je EDX nenulové (32bitové přetečení).
MUL ECX ; Nechť EDX:EAX = N*N*N
Abort%.: ; Jméno návěstí je modifikováno %proměnnou %.
, jež se inkrementuje při každé expanzi.
%ENDMACRO Cube
; EAX obsahuje vstupní číslo N.
Cube ; Expanze makra.
JC Abort: ; Abort při overflow.
; EAX nyní obsahuje N3, pokračuj v hlavním toku programu.
Inline makra jsou rychlá, ale každá invokace opakuje celý kód procedury.
Velikost programu bychom mohli omezit, pokud by makro volalo proceduru s funkčním kódem,
která mohla být rovněž odložena do inkludovaného functions.inc
.
Funkce makra pak bude omezena na předání eventuálních parametrů a skrytí volací konvence
(v našem prostém příkladu ale žádné parametry předávány nejsou).
INCLUDE "functions.inc" ; Soubor s definicí procedury Cube. Cube %MACRO ; Definuj makro Cube. CALL Cube: ; Volání procedury Cube: %ENDMACRO Cube ; EAX obsahuje vstupní číslo N. Cube ; Vyvolej makro volající inkludovanou proceduru. JC Abort ; Abort při overflow. ; EAX nyní obsahuje N3, pokračuj v hlavním toku programu.
Nevýhodou předchozí metody je, že musíme udržovat dva bloky kódu: definici makra a definici procedury. €ASM poskytuje blok procedury PROC1, který se překládán pouze jednou, i když je makro tento blok obsahující voláno opakovaně. Díky tomu je kód procedury emitován pouze jednou, když je makro voláno poprvé, a pokud by nebylo vůbec voláno, kód procedury se vůbec neemituje. Makroknihovna s takovými napůl-inline makry může být začleněna do zdrojového kódu a nezvyšuje přitom velikost výsledného kódu, pokud makro nebylo v programu použito (expandováno).
Tato metoda je preferována ve většině makroknihoven dodaných s EuroAssemblerem.
Cube %MACRO ; Definice napůl-inline maker Cube. CALL Cube: ; Volání procedury Cube: Cube: PROC1 ; PROC1 blok je emitován pouze jednou při první expanzi makra. MOV ECX,EAX ; Kopíruj vstupní číslo N do registru ECX. MUL ECX ; Nechť EDX:EAX = N*N JC .Abort: ; CF=OF=1 pokud je EDX nenulové (32bitové přetečení). MUL ECX ; Nechť EDX:EAX = N*N*N .Abort:RET ; CF=OF=1 pokud je EDX nenulové (32bitové přetečení). ENDPROC1 Cube: %ENDMACRO Cube ; EAX obsahuje vstupní číslo N. Cube ; Vyvolej makro, které volá PROC1. JC Abort ; Abort při overflow. ; EAX nyní obsahuje N3, pokračuj v hlavním toku programu.
Tato kapitola se blíže zabývá, jak jsou bloky programu zpracovány EuroAssemblerem.
Uvažme prostý textový soubor src.asm
předložený asembleru:
DB 'This source "src.asm" has' DB ' no PROGRAM statement.',13,10 DB 'EuroAssembler will use ' DB 'a fictive envelope instead.'
Jelikož v souboru není definován žádný blok PROGRAM..ENDPROGRAM
,
výstupní formát cílového souboru je určen pouze sekcí [PROGRAM] konfiguračního souboru euroasm.ini
,
nebo vestavěným továrním defaultem, kterým je PROGRAM FORMAT=BIN,MODEL=TINY,WIDTH=16
.
EuroAssembler formálně balí každý zdrojový soubor do dvou fiktivních
obálkových pseudoinstrukcí PROGRAM a ENDPROGRAM.
Přidaná obálková instrukce PROGRAM odvozuje své návěstí (název modulu)
ze jména zdrojového souboru po odříznutí přípony.
Ze zdrojového souboru src.asm
tedy vytvoří binární soubor src.bin
.
To je chování kompatibilní s mnoha dalšími asemblery.
Pokud by jméno zdrojového souboru začínalo číslicí, např.
123.asm, takový název programu není přijatelný pro €ASM, který proto před jméno modulu přidá znak grave ` a zdroj123.asmbude přeložen na`123.bin.Podobně, pokud by návěstí pseudoinstrukce PROGRAM obsahovalo otazník ? nebo jiný znak nepřijatelný pro souborový systém, takový znak pak bude v názvu souboru nahrazen podtržítkem _. Instrukce
IsNumlockOn? PROGRAM FORMAT=COM
vyrobí program se jménemIsNumlockOn_.com.€ASM používá ANSI verze Windows API pro zacházení se jmény souborů, takže je lepší se vyvarovat národních znaků mimo aktuální codepage ve jménech souborů.
Jakmile je zdrojový soubor načten v paměti, €ASM začíná číst zdroj počínaje fiktivní
obálkovou instrukcí PROGRAM
. Až bude načtena odpovídající pseudoinstrukce
ENDPROGRAM
, skončil jeden průchod asembleru.
€ASM se podívá na všechny symboly nadefinované v programu
a kontroluje, zda jejich ofsety jsou označeny jako fixed, tj. nezměnily se mezi průchody.
Jestliže aspoň jeden symbol dosud nemá svůj ofset zafixován,
je potřeba další průchod a €ASM se vrací k pseudoinstrukci PROGRAM
.
Až budou mít všechny symboly ofset stabilní, €ASM zahájí finální průchod,
v němž se generuje kód a data do cílového souboru a také se generuje listing.
K přeložení každého programu jsou zapotřebí nejméně dva průchody.
assembly progress ─> ┌─────────┬──────────────────────────────────┐ █ │envelope │src: PROGRAM │ █ ┌█ ├─────────┼──────────────────────────────────┤ █ │ █ │ {1}│ DB 'This source "src.asm" has' │ █ │ █ │"src.asm"│ DB ' no PROGRAM statement.',13,10│ █ │ █ │ {3}│ DB 'EuroAssembler will use ' │ █ │ █ │ {4}│ DB 'a fictive envelope instead.' │ █ │ █ ├─────────┼──────────────────────────────────┤ █ │ █ │envelope │ ENDPROGRAM src: │ █┘ █─┐ └─────────┴──────────────────────────────────┘ █ ││ │ │ │ I0010 EuroAssembler started.───────────────────────┤│ │ │ │ I0180 Assembling source file "src.asm".────────────┤│ │ │ │ I0270 Assembling source "src".─────────────────────┘│ │ │ │ I0310 Assembling source pass 1.─────────────────────┘ │ │ │ I0330 Assembling source pass 2 - final.──────────────────────┘ │ │ I0760 16bit TINY BIN file "src.bin" created from source, size=99.───┘ │ I0750 Source "src" (4 lines) assembled in 2 passes with errorlevel 0.─┤ I0860 Listing file "src.asm.lst" created, size=717.───────────────────┤ I0990 EuroAssembler terminated with errorlevel 0.─────────────────────┘
Dvě obálkové pseudoinstrukce jsou použity nezávisle na tom, zda byl explicitní blok PROGRAM..ENDPROGRAM ve zdrojovém textu definován či ne. Zdrojové řádky mezi začátkem souboru a explicitní pseudoinstrukcí PROGRAM, stejně jako řádky mezi explicitní pseudoinstrukcí ENDPROGRAM a koncem souboru be neměly emitovat žádný kód ani data. V tom případě je obálkový zdroj prázdný a nevytváří se z něj žádný cílový soubor.
Uvažme následující zdrojový soubor src.asm
.
Obsahuje explicitní blok Src:PROGRAM..ENDPROGRAM Src:
(řádky 5..8)
uvnitř neviditelných obálkových pseudoinstrukcí src: PROGRAM
a ENDPROGRAM src:
.
Když byl interní blok Src:PROGRAM..ENDPROGRAM Src:
spatřen během překladu,
celý tento blok je přeskočen až dokud nedojde k poslednímu průchodu vnějším blokem
src: PROGRAM..ENDPROGRAM src:
.
Pak €ASM odloží momentálně překládaný poslední průchod stranou,
a začne překládat vnitřní blok v potřebném počtu průchodů, dokud nevytvoří
a neuzavře cílový soubor. Pak se €ASM vrátí k dokončení posledního průchodu předtím přerušeného průchodu
vnějším (obálkovým) programem.
EUROASM ; Common options. ; Source file "src.asm" ; with PROGRAM defined explicitly. Src:PROGRAM FORMAT=BIN DB 'Data emitted ' DB 'by program Src.' ENDPROGRAM Src:
Všimněte si chyby: zalomení komentářového řádku {3} způsobilo vznik ne-komentářového řádku {4}.
Výraz explicitly.
bude považován za platné návěstí (definice adresního symbolu).
To způsobí, že obálkový program již není považován za prázdný a bude z něj vytvořen
cílový binární soubor src.bin
, i když s nulovou délkou (obsahuje pouze jeden symbol s nulovou délkou).
Vnitřní program z řádků {5..8} vytváří cílový soubor Src.bin
dlouhý 28 bajtů, avšak ten bude hned přepsán obálkovým cílovým souborem src.bin
s nulovou délkou, který má náhodou téměř identické jméno
(souborový systém v Dos a Windows je nezávislý na velikosti znaků).
┌─────────┬──────────────────────────────────┐ █ assembly progress ─────────> │envelope │src: PROGRAM │ █ ┌█ ┌█ ├─────────┼──────────────────────────────────┤ █ │ █ │ █ │ {1}│ EUROASM ; Common options. │ █ │ █ │ █ │ {2}│ ; Source file "src.asm" │ █ │ █ │ █ │ {3}│ ; with PROGRAM defined │ █ │ █ │ █ │ {4}│explicitly. │ █┐ │ █┐ │ █ │"src.asm"│Src:PROGRAM FORMAT=BIN │ │ │ │ │ █─█ ┌█ │ {6}│ DB 'Data emitted ' │ │ │ │ │ █ │ █ │ {7}│ DB 'by program Src.' │ │ │ │ │ █ │ █ │ {8}│ ENDPROGRAM Src: │ └█ │ └█ │ █┘ █┐ ├─────────┼──────────────────────────────────┤ █ │ █ │ └█ │envelope │ ENDPROGRAM src: │ █┘ █┘ █┐ └─────────┴──────────────────────────────────┘ █ ││ │ │ │ ││ │ │ ││ I0010 EuroAssembler started.────────────────────┤│ │ │ │ ││ │ │ ││ I0180 Assembling source file "src.asm".─────────┤│ │ │ │ ││ │ │ ││ I0270 Assembling source "src".──────────────────┘│ │ │ │ ││ │ │ ││ I0310 Assembling source pass 1.──────────────────┘ │ │ │ ││ │ │ ││ I0310 Assembling source pass 2.─────────────────────────────┘ │ │ ││ │ │ ││ I0330 Assembling source pass 3 - final.────────────────────────────────┘ │ ││ │ │ ││ W2101 Symbol "explicitly." was defined but never used. "src.asm"{4}─────────┘ ││ │ │ ││ I0470 Assembling program "Src". "src.asm"{5}──────────────────────────────────┘│ │ │ ││ I0510 Assembling program pass 1. "src.asm"{5}──────────────────────────────────┘ │ │ ││ I0530 Assembling program pass 2 - final. "src.asm"{5}───────────────────────────────┘ │ ││ I0660 16bit TINY BIN file "Src.bin" created, size=28. "src.asm"{8}─────────────────────┤ ││ I0650 Program "Src" assembled in 2 passes with errorlevel 0. "src.asm"{8}──────────────┘ ││ W3990 Overwriting previously generated output file "Src.bin".─────────────────────────────┤│ I0760 16bit TINY BIN file "src.bin" created from source, size=0.──────────────────────────┤│ I0750 Source "src" (8 lines) assembled in 3 passes with errorlevel 3.─────────────────────┤│ I0860 Listing file "src.asm.lst" created, size=1372.──────────────────────────────────────┘│ I0990 EuroAssembler terminated with errorlevel 3.──────────────────────────────────────────┘
EuroAssembler dovoluje definovat v jednom zdrojovém souboru více programových bloků a pak je překládat jedním příkazem. Pamatujte, že symboly použité v různých blocích PROGRAM..ENDPROGRAM mají privátní viditelnost, takže se navzájem nevidí, i když byly definovány ve stejném zdrojovém souboru. Chceme-li volat proceduru definovanou v Pgm1 z programu Pgm2, volaný symbol musí být globální a oba přeložené moduly musejí být slinkovány dohromady.
┌─────────┬──────────────────────────────────┐ █ assembly progress ─────────────────> │envelope │src: PROGRAM │ █ ┌█ ├─────────┼──────────────────────────────────┤ █ │ █ │ {1}│ EUROASM ; Common options. │ █ │ █ │ {2}│Pgm1:PROGRAM FORMAT=PE,ENTRY=Run1:│ █┐ │ █─█ ┌█ ┌█ │ {3}│ ; Pgm1 data. │ │ │ █ │ █ │ █ │ {4}│Run1: ; Pgm1 code. │ │ │ █ │ █ │ █ │"src.asm"│ ENDPROGRAM Pgm1: │ │ │ █┘ █┘ █┐ │ {6}│ ; Pgm2 description. │ │ │ █ │ {7}│Pgm2:PROGRAM FORMAT=PE,ENTRY=Run2:│ │ │ └█ ┌█ ┌█ │ {8}│ ; Pgm2 data. │ │ │ █ │ █ │ █ │ {9}│Run2: ; Pgm2 code. │ │ │ █ │ █ │ █ │ {10}│ ENDPROGRAM Pgm2: │ └█ │ █┘ █┘ █┐ ├─────────┼──────────────────────────────────┤ █ │ └█ │envelope │ ENDPROGRAM src: │ █┘ █┐ └─────────┴──────────────────────────────────┘ █ ││ │ │ │ │ │ │ │ │ │ ││ I0010 EuroAssembler started.───────────────────┤│ │ │ │ │ │ │ │ │ │ ││ I0180 Assembling source file "src.asm".────────┤│ │ │ │ │ │ │ │ │ │ ││ I0270 Assembling source "src".─────────────────┘│ │ │ │ │ │ │ │ │ │ ││ I0310 Assembling source pass 1.─────────────────┘ │ │ │ │ │ │ │ │ │ ││ I0330 Assembling source pass 2 - final.──────────────────┘ │ │ │ │ │ │ │ │ ││ I0470 Assembling program "Pgm1". "src.asm"{2}─────────────────┤ │ │ │ │ │ │ │ ││ I0510 Assembling program pass 1. "src.asm"{2}─────────────────┘ │ │ │ │ │ │ │ ││ I0510 Assembling program pass 2. "src.asm"{2}──────────────────────┘ │ │ │ │ │ │ ││ I0530 Assembling program pass 3 - final. "src.asm"{2}───────────────────┘ │ │ │ │ │ ││ I0660 32bit FLAT PE file "Pgm1.exe" created, size=14320. "src.asm"{5}──────┤ │ │ │ │ ││ I0650 Program "Pgm1" assembled in 3 passes with errorlevel 0. "src.asm"{5}─┘ │ │ │ │ ││ I0470 Assembling program "Pgm2". "src.asm"{7}────────────────────────────────┤ │ │ │ ││ I0510 Assembling program pass 1. "src.asm"{7}────────────────────────────────┘ │ │ │ ││ I0510 Assembling program pass 2. "src.asm"{7}─────────────────────────────────────┘ │ │ ││ I0530 Assembling program pass 3 - final. "src.asm"{7}──────────────────────────────────┘ │ ││ I0660 32bit FLAT PE file "Pgm2.exe" created, size=14320. "src.asm"{10}─────────────────────┤ ││ I0650 Program "Pgm2" assembled in 3 passes with errorlevel 0. "src.asm"{10}────────────────┘ ││ I0750 Source "src" (10 lines) assembled in 2 passes with errorlevel 0.───────────────────────┤│ I0860 Listing file "src.asm.lst" created, size=1736.─────────────────────────────────────────┘│ I0990 EuroAssembler terminated with errorlevel 0.─────────────────────────────────────────────┘
Proč bychom měli balit moduly spolu s jejich dokumentací do jednoho velkého zdrojového souboru, místo abychom je roztrousili do balíku menších souborů? Je to otázka individuálních preferencí.
Jedním z důvodů může být přenos informací mezi moduly preprocesními %proměnnými. Na rozdíl od běžných symbolů není viditelnost %proměnných omezována hranicemi bloků PROGRAM..ENDPROGRAM. Předpokládejme, že v programu Pgm2 potřebujeme znát velikost datového segmentu programu Pgm1. Jeho velikost můžeme načíst do %proměnné povelem
%Pgm1DataSize %SETA SIZE# [DATA]
umístěným v Pgm1 hned nad jehoENDPROGRAM Pgm1
. V posledním průchodu Pgm1 již je velikost segmentu spolehlivě určena a %proměnná%Pgm1DataSize
bude viditelná v celém zdroji pod svou definicí, takže s ní Pgm2 může počítat.Dalším příkladem užitečnosti seskupování programů je, pokud jsou si navzájem podobné nebo sdílejí společná data deklarovaná preprocesními %proměnnými. Následující příklad generuje ve smyčce tři podobné krátké programy
RstLPT1.com,RstLPT2.com,RstLPT3.com:Nr %FOR 1,2,3 ; Zopakuj blok %FOR..%ENDFOR třikrát. RstLPT%Nr PROGRAM FORMAT=COM ; Program k resetu portu tiskárny. MOV DX,%Nr ; Pořadové číslo LPT portu (1,2,3). MOV AH,1 ; Funkce BIOSu INITIALIZE LPT PORT. INT 17h ; Použij funkci BIOSu k resetu tiskárny. MOV DX,Message ; Vlož adresu řetězce do DS:DX. MOV AH,9 ; Funkce DOSu WRITE STRING TO STDOUT. INT 21h ; Použij funkci DOSu k ohlášení výsledku. RET ; Ukonči program Message:DB "LPT%Nr was reset.$" ENDPROGRAM RstLPT%Nr %ENDFOR Nr ; Generuj 3 klony programu.
Programové moduly mohou být vnořeny jeden do druhého. Například při překladu
obojživelného programu spustitelného v DOSu i ve Windows můžeme chtít zohlednit fakt,
že DOSovský spustitelný soubor MZ je vestavěn jako stub
do spustitelného PE programu pro Windows a poskytuje stejnou funkčnost.
Viz vzorový projekt LockTest
jako příklad duálního DOS&Windows programu.
Opět, pokud vnější program vidí vnitřní programový blok v neposledním průchodu, bude přeskočen. Až v posledním průchodu vnějšího bloku bude tento dočasně suspendován, vnitřní program kompletně přeložen a uložen, a pak se teprve vnější program vrátí k dokončení posledního průchodu.
┌─────────┬──────────────────────────────────┐ █ assembly progress ──────────────> │envelope │src: PROGRAM │ █ ┌█ ├─────────┼──────────────────────────────────┤ █ │ █ │ {1}│ EUROASM ; Common options. │ █ │ █ │ {2}│Pgm1: PROGRAM FORMAT=PE,ENTRY=Run:│ █┐ │ █─█ ┌█ ┌█ │ {3}│Run: ; Pgm1 data + code. │ │ │ █ │ █ │ █ │ {4}│ Pgm2: PROGRAM FORMAT=COFF │ │ │ █┐ │ █┐ │ █─█ ┌█ │"src.asm"│ ; Pgm2 data + code. │ │ │ │ │ │ │ █ │ █ │ {6}│ ENDPROGRAM Pgm2: │ │ │ └█ │ └█ │ █┘ █─█ │ {7}│ ; Pgm1 more code. │ │ │ █ │ █ │ █ │ {8}│ LINK "Pgm2.obj" │ │ │ █ │ █ │ █ │ {9}│ ENDPROGRAM Pgm1: │ └█ │ █┘ █┘ █─█ ├─────────┼──────────────────────────────────┤ █ │ █ │envelope │ ENDPROGRAM src: │ █┘ █─┐ └─────────┴──────────────────────────────────┘ █ ││ │ │ │ │ │ │ │ │ │ │ I0010 EuroAssembler started. ──────────────────┤│ │ │ │ │ │ │ │ │ │ │ I0180 Assembling source file "src.asm".────────┤│ │ │ │ │ │ │ │ │ │ │ I0270 Assembling source "src".─────────────────┘│ │ │ │ │ │ │ │ │ │ │ I0310 Assembling source pass 1.─────────────────┘ │ │ │ │ │ │ │ │ │ │ I0330 Assembling source pass 2 - final.──────────────────┘ │ │ │ │ │ │ │ │ │ I0470 Assembling program "Pgm1". "src.asm"{2}─────────────────┤ │ │ │ │ │ │ │ │ I0510 Assembling program pass 1. "src.asm"{2}─────────────────┘ │ │ │ │ │ │ │ │ I0510 Assembling program pass 2. "src.asm"{2}──────────────────────────┘ │ │ │ │ │ │ │ I0530 Assembling program pass 3 - final. "src.asm"{2}───────────────────────────┘ │ │ │ │ │ │ I0470 Assembling program "Pgm2". "src.asm"{4}───────────────────────────────────────┤ │ │ │ │ │ I0510 Assembling program pass 1. "src.asm"{4}───────────────────────────────────────┘ │ │ │ │ │ I0530 Assembling program pass 2 - final. "src.asm"{4}───────────────────────────────────┘ │ │ │ │ I0660 32bit FLAT COFF file "Pgm2.obj" created, size=78. "src.asm"{6}──────────────────────┤ │ │ │ I0650 Program "Pgm2" assembled in 2 passes with errorlevel 0. "src.asm"{6}────────────────┘ │ │ │ I0560 Linking COFF module ".\Pgm2.obj". "src.asm"{9}───────────────────────────────────────────┤ │ │ I0660 32bit FLAT PE file "Pgm1.exe" created, size=14320. "src.asm"{9}──────────────────────────┤ │ │ I0650 Program "Pgm1" assembled in 3 passes with errorlevel 0. "src.asm"{9}─────────────────────┘ │ │ I0750 Source "src" (9 lines) assembled in 2 passes with errorlevel 0.──────────────────────────────┤ │ I0860 Listing file "src.asm.lst" created, size=1237.───────────────────────────────────────────────┘ │ I0990 EuroAssembler terminated with errorlevel 0.────────────────────────────────────────────────────┘
Několik užitečných vlastností EuroAssembleru pomůže programátorovi s kontrolou, zda se jeho zdrojový kód přeložil tak, jak bylo zamýšleno.
Toto hledání chyb v asm-time pomáhá nalézt nedorozumění a chyby v EuroAssembleru samotném, nikoli v překládaném programu.
Nejvíce pomůže sloupec pro dump přeloženého kódu v listingu.
Opakované úseky považované za bezchybné jsou ze zobrazování vynechány, ale mohou být vyžádány
direktivami EUROASM LISTINCLUDE=ON, LISTVAR=ON, LISTMACRO=ON, LISTREPEAT=ON
.
Rozpoznávání polí v instrukcích lze objednat pomocí volby EUROASM DISPLAYSTM=ON
,
která přidává komentářové řádky identifikující každé pole.
Jelikož to významně nafukuje listing, je lepší omezit DISPLAYSTM pouze na podezřelé řádky
a pak volby zase vypnout anebo obnovit předchozí stav:
EUROASM PUSH, DISPLAYSTM=ON ; Nejprve ulož všechny volby EUROASM pomocí PUSH. MyMacro Operand1, Operand2 ; "MyMacro" zatím nebylo deklarováno jako makro, takže se považuje za návěstí (label). D1010 **** DISPLAYSTM "MyMacro Operand1, Operand2" D1020 label="MyMacro" D1040 unknown operation="Operand1" D1050 ordinal operand number=1,value="Operand2" EUROASM POP ; Obnov volby EUROASM. D1010 **** DISPLAYSTM "EUROASM POP" D1040 pseudo operation="EUROASM" D1050 ordinal operand number=1,value="POP" ; Pole instrukcí nejsou nadále zobrazovány.
Podrobnosti zakódování strojových instrukcí si lze vyžádat volbou
EUROASM DISPLAYENC=ON
, která pod strojové instrukce vkládá komentářový řádek
se seznamem použitých modifikátorů.
EUROASM PUSH, DISPLAYENC=ON ; Nejprve ulož všechny volby EUROASM pomocí PUSH. SHRD [RDI+64],RDX,2 D1080 Emitted size=6,DATA=QWORD,DISP=BYTE,SCALE=SMART,ADDR=ABS,IMM=BYTE. VMOVNTDQA XMM17,[RBP+40h] D1080 Emitted size=7,PREFIX=EVEX,DATA=OWORD,OPER=0,DISP=BYTE,SCALE=SMART,ADDR=ABS. EUROASM POP ; Obnov volby EUROASM. Detaily nadále nebudou zobrazovány.
Všechny konfigurační volby nastavitelné v pseudoinstrukcích EUROASM a PROGRAM lze získat a testovat pomocí systémových %^proměnných:
%IF %^NOWARN[2101] %ERROR Neměli byste potlačovat varování W2101. Raději přesuňte nepoužité symboly do inkludovaných souborů. %ENDIF
Nejmocnějším nástrojem pro asm-time ladění je pseudoinstrukce %DISPLAY, která zobrazuje interní €ASM objekty v čase překladu a pomáhá tak zjistit, proč €ASM nefunguje podle očekávání.
Viz testy t2901..t2917 jako příklady.
Linkování je v terminologii výpočetní techniky proces, kdy separátně přeložené moduly jsou spojovány, interakce mezi globálními symboly vyřešeny, kód a data zkombinovány a přeformátovány do cílového souboru. Viz dokumentaci [Linkers] pro podrobnější informace.
Na rozdíl od jných linkerů nevytváří EuroAssembler pouze spustitelné soubory, ale také relinkovatelné soubory ve formátech ELF, COFF a OMF, případně jejich knihovny LIBCOF a LIBOMF (viz Objektové konvertory a tabulky podporovaných kombinací).
Linkování je řízeno pseudoinstrukcí LINK následovanou jmény vstupních modulů. Přijatelné formáty vstupních souborů jsou dvojího druhu:
CPU mód | Šířka programu | Výstupní spustitelný |
Výstupní linkovatelný |
Vstupní linkovatelný | Vstupní importovatelný |
---|---|---|---|---|---|
Real | 16 | BIN, BOOT, COM, MZ | OMF, LIBOMF, COFF, LIBCOF | OMF, LIBOMF, COFF, LIBCOF | - |
Real | 32 | BIN, BOOT, COM, MZ | OMF, LIBOMF, COFF, LIBCOF | OMF, LIBOMF, COFF, LIBCOF | - |
Prot | 32 | ELFX, PE, DLL | ELF, COFF, LIBCOF, OMF, LIBOMF | ELF, COFF, LIBCOF, RSRC, OMF, LIBOMF | ELF, COFF, LIBCOF, DLL, OMF, LIBOMF |
Prot | 64 | ELFX, PE, DLL | ELF, COFF, LIBCOF | ELF, COFF, LIBCOF, RSRC | ELF, COFF, LIBCOF, DLL, OMF, LIBOMF |
Viz také tabulky přípustných kombinací.
Všimněte si, že se formát OMF nemůže linkovat do 64bitových programů.
Formát linkovaného souboru se rozpoznává podle svého obsahu, nikoli podle souborové přípony. Každý linkovaný modul se nejprve načte a převede do interního formátu (PGM) před vlastním linkováním.
Není důležité, kde v bloku PROGRAM..ENDPROGRAM jsou pseudoinstrukce LINK umístěny; ony pouze sbírají jména linkovaných modulů a jejich linkování je odloženo na konec programu.
Kód a data linkovaných objektových souborů ve formátech ELF, COFF nebo OMF budou zkombinovány a přidány ke kódu a datům hlavního programu (tj. do toho, ke kterému je linkováno). Hlavní program může být také prázdný. Linker zároveň vyřeší vzájemné odkazy mezi PUBLIC a EXTERN symboly všech linkovaných modulů.
Na rozdíl od jiných linkerů neočekává EuroAssembler jména linkovaných modulů jako argumenty příkazového řádku. I když chceme využít EuroAssembler jen jako pouhý linker, musíme vytvořit linkovací skript, což je v podstatě €ASM zdrojový program. Požadovaný výstupní formát a šířka bude specifikován jako argument pseudoinstrukce PROGRAM:
MyExeFile PROGRAM FORMAT=PE, WIDTH=32, ListMap=Yes, ListGlobals=Yes LINK MyCoff.obj, PascalOmf.obj, Win32.lib ENDPROGRAM MyExeFile
Uložení tohoto skriptu jako MyScript.asm
a vykonání
euroasm MyScript.asm
vyprodukuje program pro Windows MyExeFile.exe
a listing MyScript.asm.lst
s mapou linkovaných sekcí a globálních symbolů.
Vedle samostatných objektových modulů (ELF, OMF, COFF) mohou být kód a data linkovány také z objektových knihoven ve formátech LIBCOF a LIBOMF.
Je-li hlavní výstupní program spustitelný, €ASM linkuje pouze ty moduly z knihovny, které jsou aspoň jednou zmíněny jinými moduly (tzv. chytré linkování). Pomáhá to udržovat malou velikost linkovaného souboru vyloučením mrtvého kódu.
Pokud bychom přesto potřebovali zkombinovat neodkazované knihovní procedury do našeho spustitelného programu, museli bychom explicitně deklarovat jejich nereferencovaná jména v hlavním programu jako GLOBAL.
Chytré linkování se neuplatňuje při vytváření linkovatelného výstupního formátu, například pokud tvoříme knihovnu LIBCOF z jiných knihoven a samostatných objektových modulů. V tom případě všechny (referencované i nereferencované) moduly budou do výstupního souboru přilinkovány.
Dobrým důvodem, proč rozdělovat velký projekt na menší, separátně překládané moduly, je rychlejší sestavení.
Jakmile projekt roste a velikost jeho zdrojového kódu se zdvojnásobí, počet symbolů v něm se pravděpodobně také zdvojnásobí. Každý symbol musí být porovnán s polem jiných již definovaných symbolů kvůli vyloučení duplicit, počet porovnávání se tedy zečtyřnásobí. Počet kontrol, a tedy spotřebovaný čas roste téměř kvadraticky s velikostí zdroje.
Během vývoje jsme zpravidla soustředěni na jednu část (modul) projektu, takže ostatní nezměněné moduly se nemusejí rekompilovat během každého vývojového cyklu. Viz také Makefile manažer).
Rekapitulace: Chcete-li staticky linkovat vlastní funkci (proceduru),
deklarujte ji jako PUBLIC function
(nebo zakončete její jméno při definici dvěma dvojtečkami function:: PROC
)
a přeložte funkci do objektového nebo knihovního formátu.
Pak přeložte hlavní program, kde byla linkovaná funkce deklarována jako EXTERN function
(anebo její jméno zakončeno dvěma dvojtečkami)
a přidejte do programu pseudoinstrukci LINK module.obj
.
V hlavním programu pak lze CALL function::
jako by byla přeložena v jeho vlastním těle.
Totéž lze aplikovat na funkce z knihoven třetí strany.
Je ovšem třeba respektovat její zveřejněné jméno, volací konvenci, počet, pořadí a typy argumentů.
Tato verze EuroAssembleru nepodporuje dynamické linkování Linuxových dynamických knihoven (DSO).
Povel LINK DSO.so
se pokusí přilinkovat soubor pouze staticky.
Tato kapitola se zabývá pouze dynamickými knihovnami MS-Windows.
Kód a data dynamicky linkovaných funkcí není kopírován do cílového spustitelného souboru (image),
zůstávají v dynamické knihovněě (DLL), která musí být dostupná na počítači, kde program běží.
Jakmile náš program zavolá funkci z DLL, ve skutečnosti vykonává zástupný kód
(thunk) reprezentovaný voláním instrukce proxy-skoku (stub).
€ASM generuje stuby ve speciální importové sekci [.idata]
ve formě nepřímého absolutního blízkého skoku (JMPN).
Každý takový skok je 7 bajtů dlouhý (0xFF2425[00000000]
)
a používá ukazatel do Import Address Table (IAT) jako svůj nepřímý 32bitový cíl.
Virtuální adresa ukazatele [00000000]
je vyřešena linkerem,
ale vlastní 32 nebo 64bitová virtuální adresa knihovní funkce (ukazované vyřešeným dvojslovem)
bude upravena později, zaváděčem v čase bind-time, kdy naše aplikace startuje.
Zaváděč implementovaný v jádře Windows, potřebuje dvě informace ke spojení knihovní funkce a k úpravě její adresy v IAT:
1) Jméno dynamicky linkovaného symbolu (jméno funkce) nebo její pořadové číslo v tabulce exportovaných symbolů
Volání podle těchto čísel (ordinal numbers) není v €ASM podporováno.
2) Název knihovny exportující daný symbol (bez cesty).
Cesta k souboru knihovny bude stanovena zaváděčem. Pořadí, v jakém Windows prohledává cesty ke knihovně, je vysvětleno ve [WinDllSearchOrder].
Program, který chce volat symbol (importovanou funkci) z DLL, by měl deklarovat symbol pomocí IMPORT.
Může být deklarován také jako GLOBAL, buď explicitně nebo implicitně
( CALL ImportedSymbol::
), avšak €ASM považuje takový globální symbol za EXTERN (staticky linkovaný)
a nikoli IMPORT (dynamicky linkovaný) a postěžuje si, že nebyl nalezen odpovídající PUBLIC symbol.
Je několik metod, jako říci EuroAssembleru, že symbol má být linkován dynamicky:
Použijte explicitní importní deklaraci, například
IMPORT ImportedSymbol1, ImportedSymbol2, LIB="dynamic.dll"
.
Dynamická knihovna by měla být specifikována bez cesty a může být vynechána,
pokud je to základní knihovna jádra Windows kernel32.dll
.
Přítomnostdynamic.dllnení zjišťována během linkování, program se sestaví bez chyb dokonce i když takový soubor na našem počítači vůbec neexistuje. Avšak Windows si začne stěžovat, že spouštěný program nemůže nalézt knihovnudynamic.dllanebo že ImportedSymbol nebyl exportován knihovnoudynamic.dll.
Přilinkujte k programu importní knihovnu, pomocí LINK "winapi.lib"
.
Takový soubor může být ve formátu LIBOMF nebo LIBCOF
a obvykle obsahuje pouze deklarace jmen exportovaných symbolů a jejich knihovních souborů
(čistě importní knihovna).
€ASM ztotožní nedefinované globální symboly s názvy z importní knihovny a redeklaruje je jako IMPORT.
Některé starší knihovní programy, jako [ALIB], produkují importní knihovny ve dlouhém formátu, který obsahuje přídavný kód skoků na thunk. €ASM dlouhý formát akceptuje, avšak ignoruje tento nadbytečný obsah.
Některé kompilátory zatemňují (mangle) exportovaná jména dekorací pomocí podtržítek a dalším obsahem prozrazujícím volací konvenci a počet operandů. €ASM to nepodporuje, importované funkce musejí být volány identickým jménem, které je exportováno.
Vzorový projekt dll2lib.htm ukazuje, jak vytvořit exportní knihovnu z dynamických knihoven DLL systému MS-Windows.
%SystemRoot %SETE SystemRoot LINK "%SystemRoot\system32\USER32.DLL"
Linkování dynamické knihovny nekopíruje její kód a data do našeho programu,
pouze detekuje jména exportovaných funkcí.
Instrukce je exkvivalentní deklaraci IMPORT *, LIB="USER32.DLL"
nebo přilinkování importní knihovny LINK "USER32.lib"
.
LoadLibrary("library.dll"); GetProcAddress(SymbolName);
předtím, než bude použito SymbolName.Rekapitulace: Chcete-li dynamicky linkovat svou vlastní funkci
(proceduru) do jiného programu, deklarujte ji jako EXPORT function
a přeložte funkci
do formátu DLL (mylib PROGRAM FORMAT=DLL
). Nezapomeňte distrubuovat mylib.dll
spolu s vašimi programy.
Pak přeložte hlavní spustitelný program a v něm deklarujte linkovanou funkci pomocí
IMPORT function, LIB=mylib.dll
.
Hlavní program ji pak může volat pomocí CALL function
.
Častěji asi budete potřebovat volat funkce z dynamické knihovny třetí strany,
což je případ
MS-Windows API. Mohli byste explicitně vyjmenovat každou funkci WinAPI
pseudoinstrukcemi jako IMPORT function1,function2,LIB=user32.dll
,
ale pohodlnější řešení je použít importovou knihovnu, která deklaruje všechna jména funkcí
touto dynamickou knihovnou exportovaných. Pak nebude potřeba přidávat novou importní deklaraci pokaždé,
jakmile bude během vývoje vašeho programu přidána nová funkce z WinAPI.
Prostě volejte novou funkci s dvojitou dvojtečkou (CALL NováFunkce::
)
a pokud se jmého NováFunkce objeví v importní knihovně, funkce bude importována.
Můžete rovněž chtít používat makra
WinAPI (32bit) nebo
WinABI (64bit)
která se postarají o deklarace IMPORT a o automatický výběr mezi variantami ANSI a WIDE.
EuroAssembler umí vytvářet knihovny z předem přeložených objektových modulů (souborů ve formátu ELF, OMF nebo COFF). A pokud knihovní program také obsahuje nějaký kód, bude přilinkován do knihovny jako její první člen.
Library PROGRAM FORMAT=LIBOMF ; nebo FORMAT=LIBCOF ObjModule1:: PROC ; Jeden z objektových modulů může být také definován zde. ; Zdojový kód modulu ObjModule1. ENDP ObjModule1:: LINK "ObjModule2.obj", "ObjModule3.obj" ; Další moduly ELF, OMF nebo COFF. ENDPROGRAM Library
Pokud linkované moduly obsahují importní informace, budou také zkopírovány do výstupní knihovny. Čistě importní knihovny obsahují pouze importní deklarace. Tyto mohou být explicitně specifikovány jako IMPORT, nebo načteny z existující dynamické knihovny DLL, nebo linkovány z jiných importních knihoven. Následující příklad využívá postupně všechny tři metody:
ImpLibrary PROGRAM FORMAT=LIBOMF ; nebo FORMAT=LIBCOF IMPORT Symbol1, Symbol2, LIB="DynamicLibrary1.dll" ; Explicitní deklarace. LINK "C:\MyDLLs\DynamicLibrary2.dll" ; Automatická detekce exportů z DLL. LINK "OtherImportLibrary.lib" ; Reimport z jiné knihovny. ENDPROGRAM ImpLibrary
Příklady knihoven vytvořených ze tří separátně přeložených modulů jsou v €ASM testech:
t8552 (objektová knihovna LIBOMF pro 16bitový Dos),
t9113 (objektová knihovna LIBCOF pro 32bitová Windows),
t9164 (objektová knihovna LIBCOF pro 64bitová Windows),
t8675 (importní knihovna LIBOMF pro Windows),
t9225 (importní knihovna LIBCOF pro Windows),
Poptávka po objektovém konvertoru bude asi slabá, neboť EuroAssembler může přímo linkovat všechny hlavní objektové formáty OMF, ELF a COFF. Příklady:
OMFobject PROGRAM FORMAT=OMF ; Konvertuj objekt COFF na objekt OMF. LINK "COFFobject.obj" ENDPROGRAM OMFobject
COFFobject PROGRAM FORMAT=COFF; Konvertuj objekt OMF na objekt COFF. LINK "OMFobject.obj" ENDPROGRAM COFFobject
ELFobject PROGRAM FORMAT=ELF; Konvertuj objekt COFF na objekt ELF. LINK "COFFobject.obj" ENDPROGRAM ELFobject
COFFobject PROGRAM FORMAT=COFF; Konvertuj objekt ELF na objekt COFF. LINK "ELFobject.o" ENDPROGRAM COFFobject
OMFlibrary PROGRAM FORMAT=LIBOMF ; Konvertuj knihovnu LIBCOF na knihovnu LIBOMF. LINK "COFFlibrary.lib" ENDPROGRAM OMFlibrary
COFFlibrary PROGRAM FORMAT=LIBCOF ; Konvertuj knihovnu LIBOMF na knihovnu LIBCOF. LINK "OMFlibrary.lib" ENDPROGRAM COFFlibrary
Atributový operátor FILETIME# načítá čas poslední modifikace souboru, což se dá využít k detekci, zda cílový soubor potřebuje přeložit nebo ne. Stačí porovnat čas cílového souboru s časem všech zdrojových souborů, na kterých cíl záleží. Pokud cílový soubor dosud neexistoval, jeho FILETIME# vrací 0, což je totéž, jako by byl velmi starý, takže jeho vytvoření bude i tak vyžadováno.
; Rekompiluj "source.asm" pouze poku "target.exe" neexistuje nebo je starší než jeho zdrojové soubory. %IF FILETIME# "target.exe" > FILETIME# "source.asm" && FILETIME# "target.exe" > FILETIME# "included2source.inc" %ERROR "target.exe" je čerstvý, není třeba ho znovu překládat. %ELSE target PROGRAM FORMAT=PE INCLUDE "source.asm" ENDPROGRAM target %ENDIF
Jako příklad sofistikovanějšího makefile viz hlavní zdrojový soubor EuroAssembleru euroasm.htm.
Počítačové programy často píšeme v asembleru proto, aby byly rychlé a malé. To ale nejsou jediná kritéria, jak se dá program optimalizovat:
Viz také optimalizační tutoriály.
Podívejme se, jak může EuroAssembler pomoci s optimalizací.
Ve výchozím stavu vybírá €ASM nejkratší možné zakódování strojových instrukcí. Na druhé straně ale respektuje mnemoniku zvolenou programátorem, což nemusí být vždy ta nejkratší varianta. Za zapamatování stojí několik pravidel:
|0000:B80000 | MOV AX,0 |0003:29C0 | SUB AX,AX ; Použití SUB nebo XOR pro nulování registru je kratší. Vedlejší efekt: změní to flagy. |0005: | |0005:89D8 | MOV AX,BX |0007:93 | XCHG AX,BX ; XCHG je kratší než MOV. Vedlejší efekt: druhý registr je také změněn. |0008: | |0008: |Label: |0008:8D06[0800] | LEA AX,[Label] |000C:B8[0800] | MOV AX,Label ; Zavedení ofsetu do registru (MOV) je kratší nez zavedení jeho adresy (LEA). |000F: | |000F:5053 | PUSH AX,BX |0011:60 | PUSHAW ; PUSH/POP všech 8 registrů najednou je kratší než individuální PUSH/POP. |0012: | |0012:050100 | ADD AX,1 |0015:40 | INC AX ; Increment/decrement je kratší než add/subtract 1. |0016: | |0016: |LoopStart: |0016:49 | DEC CX |0017:75FD | JNZ LoopStart: |0019:E2FB | LOOP LoopStart: ; LOOP, JCXZ jsou kratší nez separátní test a skok.Programy aspirující do kategorie těch nejkratších by měly mít PROGRAM FORMAT=COM
a EUROASM AUTOALIGN=OFF
.
Mohou končit prostým RET
namísto volání DOS funkce TERMINATE PROCESS,
protože návratová adresa na zásobníku programů COM je inicializována na 0
a tam na začátku bloku PSP leží přerušení ukončující proces.
Hello PROGRAM FORMAT=COM MOV DX,=B "Hello world!$" MOV AH,9 INT 21h RET ENDPROGRAM Hello
Pro další inspiraci se podívejte na
[Golfing_tips],
Hugi Size Coding Competition Series,
Assembly nibbles competition,
Graphical Tetris in 1986 bytes by Sebastian Mihai,
BootChess play in 487 bytes by Oliver Poudade.
Programy pro Windows vytvořené v €ASM budou kratší,
pokud bude volba PROGRAM ICONFILE=
explicitně prázdná
a nebude přilinkován žádný soubor resources.
V tom případě nebude sekce [.rsrc] v PE souboru vůbec přítomna.
Můžete také experimentovat s použitím programových voleb jako
PROGRAM FILEALIGN=
.
Psaní rychlých programů je plně v rukou programátora, EuroAssembler zde příliš nepomůže,
neprovádí žádnou optimalizaci za vašimi zády, jako to dělají kompilátory vyšších jazyků.
Možná budete chtít nastavit EUROASM AUTOALIGN=ON
, aby se všechna data zarovnala kvůli rychlému přístupu.
Naprostá kontrola zakódování instrukce pomocí modifikátorů
dovoluje v €ASM vybrat variantu s přesnou velikostí kódu, což může být rychlejší
než verze s minimální velikostí doplněnou pomocí NOP. €ASM podporuje
optimalizované instrukce NOP pro snadné ruční zarovnávání.
Existuje více triků, jak urychlit program: rozbalení smyček, paralelizace, vyvarování se zbytečných přístupů do paměti a v neposlední řadě výběr nejrychlejšího algoritmu. Výkonnost taky závisí na modelu CPU a jeho generaci. Dobrým průvodcem je manuál [SoftwareOptimisation] od Agnera Foga.
Výkonnost programu se obvykle směňuje s jeho velikostí, například mnoho triků zmenšující velikost programu uvedených nahoře vede k jeho pomalejšímu vykonávání. K optimalizaci na rychlost se hodí hlavně kritické části programu, které budou vykonávány mnohokrát.
EuroAssembler není optimalizován na rychlost překladu, nicméně jeho trvání obvykle není problém. Nejvíce záleží na počtu průchodů, který je řízen EuroAssemblerem samotným a programátor jej nemůže přímo ovlivňovat. Nejméně dva průchody jsou požadovány pokaždé. Jejich počet se zvyšuje, pokud program obsahuje dopředné reference, smyčky v asm-time, makroinstrukce.
Když €ASM překládá dopředný skok, nejprve předpokládá krátkou vzdálenost (DIST=SHORT)
k dosud nedefinovanému cíli, a rezervuje prostor pro dvoubajtový operační kód.
Pokud víme při psaní programu, že cíl bude dále než 127 bajtů, mohli bychom explicitně určit
DIST=NEAR
, což by mohlo ušetřit jeden průchod. Ten by ale byl uspořen jen pokud by
byly specifikovány vzdálenosti všech podobných skoků, což obvykle nestojí za námahu.
Chcete-li vědět, proč €ASM potřebuje tolik průchodů, vložte direktivu
%DISPLAY UnfixedSymbols
před ENDPROGRAM
, abyste zjistili
ofsety kterých symbolů oscilují mezi průchody asembleru.
Čas sestavení velkých projektů může být významně snížen rozdělením kódu na menší, samostatně překládané moduly, které pak budou ve finále spojeny (slinkovány) dohromady. Viz například zdrojový kód euroasm.htm.
EuroAssembler zavádí nové pohodlné rysy ne příliš obvyklé mezi jinými asemblery:
INCLUDE "Win*.inc"
.INCLUDE1 file
místo INCLUDE file
a €ASM se o to postará.EQU
nebo STRUC
.FLD ST0,[=Q 1.234]
.euroasm.exepevně vestavěny a mohou tak být operativně přizpůsobeny modifikací maker API.
Dobře okomentovaný program je snadné číst a udržovat. EuroAssembler dovoluje formátování HTML poznámek, takže zdrojový kód v něm psaných programů může být přímo publikován na webových stránkách a každá jeho část může být hned dokumentována bohatě formátovanými poznámkami, tabulkami, obrázky, hypertextovými odkazy.
Velikost a jazyk identifikátorů není omezován, takže mohou být samopopisné.
Pokud není angličtina váš rodný jazyk, je dobrým nápadem preferovat symboly
s neanglickými názvy, jako Drucken
místo Print
,
файл
místo file
apod.
Pomůže to čtenáři vašeho programu rozeznat vestavěná rezervovaná slova od identifikátorů
vytvořených autorem programu.
Některé prvky jazyka EuroAssembler používají dekorátory pomáhajícími lidskému čtenáři rozlišit kategorii dekorovaných identifikátorů:
Pokud jste dočetli tento manuál až sem a chcete vyzkoušet EuroAssembler, stáhněte jeho poslední verzi, vytiskněte si papírový tahák a prohlédněte si vzorové projekty. Mnoho štěstí!