Расширенный ассемблер: NASM

Следующая глава | Предыдущая глава | Содержание | Указатель

 

Глава 2: Запуск NASM

Перевод: AsmOS group, © 2001

2.1 Синтаксис командной строки NASM

Для ассемблирования файла вы должны ввести следующую команду:

nasm -f <format> <filename> [-o <output>]

Например,

nasm -f elf myfile.asm

будет ассемблировать myfile.asm в ELF-объектный файл myfile.o. А строка

nasm -f bin myfile.asm -o myfile.com

будет ассемблировать myfile.asm в обычный бинарный файл myfile.com.
Для получения файла-листинга, содержащего слева от оригинального исходного текста шестнадцатиричные коды, генерируемые NASM, используйте ключ -l, обозначающий имя файла-листинга, например:

nasm -f coff myfile.asm -l myfile.lst

Для получения справки по командной строке NASM, укажите следующий ключ:

nasm -h

При этом вы получите также список доступных форматов выходных файлов и что они означают. Если вы используете Linux, но не уверены, какая ваша система — a.out или ELF, введите

file nasm

в каталоге, где находятся бинарные файлы NASM. В ответ вы получите что-то вроде

nasm: ELF 32-bit LSB executable i386 (386 and up) Version 1

Это означает, что ваша система — ELF и вы должны при ассемблировании использовать ключ -f elf. Если же вы увидите

nasm: Linux/i386 demand-paged executable (QMAGIC)

или что-то наподобие этого, ваша система — a.out, и при ассемблировании нужно будет указать ключ -f aout. (Linux системы a.out считаются устаревшими и на сегодняшний день встречаются редко).
Подобно Unix компиляторам и ассемблерам, NASM "бесшумен": вы не будете видеть никаких сообщений вообще, если только это не сообщения об ошибках.

2.1.1 Ключ -o: Указание имени выходного файла

Обычно NASM выбирает имя выходного файла самостоятельно; так как это зависит от формата объектного файла. Если формат объектного файла — Microsoft (obj и win32), он удалит расширение .asm (или любое другое, какое вам нравится использовать — NASMу все равно) из имени исходного файла и заменит его на .obj. У объектных файлов Unix-формата (aout, coff, elf и as86) он будет заменять расширение на .o. Для формата rdf он будет использовать расширение .rdf, а в случае формата bin он просто удалит расширение, например из myfile.asm получится файл myfile.

Если выходной файл уже существует, NASM перезапишет его, если только его имя не совпадает с именем входного файла — в этом случае появится предупреждение и в качестве выходного файла будет использовано имя nasm.out.

В случаях, когда имя по умолчанию недопустимо, используйте ключ -o командной строки, позволяющий определить необходимое вам имя выходного файла. Имя выходного файла должно следовать за ключем -o, неважно с пробелом между ними или без. Например:

nasm -f bin program.asm -o program.com
nasm -f bin driver.asm -odriver.sys

2.1.2 Ключ -f: Указание формата выходного файла

Если ключ -f в командной строке отсутствует, NASM будет выбирать формат выходного файла самостоятельно. В распространяемой версии NASM формат по умолчанию всегда bin; если вы создаете собственную копию NASM, то при компиляции можете переопределить значение OF_DEFAULT на то, которое вам нужно по умолчанию.

Как и для ключа -o, разделительный пробел между -f и форматом выходного файла необязателен: -f elf и -felf для NASM идентичны.

Полный список доступных выходных форматов может быть получен при помощи команды nasm -h.

2.1.3 Ключ -l: Генерация файла-листинга

Если в командной строке вы укажете ключ -l и имя файла (как обычно, пробел необязателен), NASM будет генерировать из исходника файл-листинг, где адреса и генерируемый код будут расположены слева, а исходный код с развернутыми многострочными макросами (за исключением тех, которые специально требуют обратное: см. параграф 4.2.9) — справа. Например:

nasm -f elf myfile.asm -l myfile.lst

2.1.4 Ключ -E: Перенаправление ошибок в файл

Под MS-DOS перенаправление стандартного потока ошибок в файл может быть сопряжено с трудностями (хотя способы имеются). Так как NASM обычно направляет предупреждения и сообщения об ошибках в поток stderr, перехват этих сообщений ( например для последующего просмотра в редакторе) может вызвать сложности.

В связи с этим имеется специальный ключ -E, после которого указывается имя файла (пробел необязателен). Данный ключ перенаправляет стандартный поток ошибок в указанный файл. Таким образом, вы можете запустить NASM, например следующим образом:

nasm -E myfile.err -f obj myfile.asm

2.1.5 Ключ -s: Перенаправление ошибок в stdout

Ключ -s перенаправляет стандартный поток ошибок stderr в выходной поток stdout (естественно, в MS-DOS). Например, для ассемблирования файла myfile.asm и передачи ошибок программе more, вы можете ввести следующее:

nasm -s -f obj myfile.asm | more

См. также ключ -E, параграф 2.1.4.

2.1.6 Ключ -i: Каталоги поиска включаемых файлов

Когда NASM встречает в исходнике директиву %include (см. параграф 4.5), он будет искать указанный в ней файл не только в текущем каталоге, но и во всех каталогах, указанных в командной строке при помощи ключа -i. Следовательно, вы можете включить файлы из, например, библиотеки макросов, введя следующую команду:

nasm -ic:\macrolib\ -f obj myfile.asm

(Как обычно, пробел между -i и строкой поиска допустим, но не обязателен)

С целью переносимости кода в NASMе не "зашито" соглашение об именовании файлов той или иной ОС, под которой он запущен; строка, которую вы укажете как аргумент ключа, будет обработана в точности так, как есть. Заключительный обратный слэш в приведенном выше примере под Unix необходим, т.к. в этой ОС заключительные слэши обычно требуются.

(Вы можете извлечь из этого обстоятельства выгоду, использовав данный ключ "не по назначению" — например ключ -ifoo будет заставлять директиву %include "bar.i" искать файл foobar.i...)

Если вы хотите описать стандартный путь поиска включаемых файлов, такой как /usr/include в системе Unix, вы должны поместить одну или более директив -i в переменную окружения NASM (см. параграф 2.1.13).

Для совместимости с make-файлами большинства С компиляторов, данный ключ может быть также задан как -I.

2.1.7 Ключ -p: Предварительно включаемые файлы

При помощи ключа -p NASM позволяет вам предварительно включить некоторые файлы в ваш исходник. Так, строка запуска

nasm myfile.asm -p myinc.inc

эквивалентна строке nasm myfile.asm и помещением в начало файла директивы %include "myinc.inc". В целях симметричности с ключами I, -D и -U данный ключ может быть также задан как -P.

2.1.8 Ключ -d: Предопределение макроса

Аналогично тому, как ключ -p дает альтернативу помещения в начало исходного файла директивы %include, ключ -d дает альтернативу директиве %define. Таким образом, команда

nasm myfile.asm -dFOO=100

альтернативна помещению в начало файла директивы

%define FOO 100

Вы можете также опустить значение константы: ключ -dFOO эквивалентен строке %define FOO. Данная возможность может быть полезна при ассемблировании для включения/выключения опций, проверяемых при помощи директивы %ifdef, например -dDEBUG. Для совместимости с make-файлами большинства С компиляторов, данный ключ может быть также задан как -D.

2.1.9 Ключ -u: Отмена определения макроса

Ключ -u отменяет определение ранее определенного макроса. Например, в результате выполнения следующей командной строки:

nasm myfile.asm -dFOO=100 -uFOO

FOO не будет предопределенным макросом для программы. Это полезно для временного отключения опций, заданных в make-файлах. Для совместимости с make-файлами большинства С компиляторов, данный ключ может быть также задан как -U.

2.1.10 Ключ -e: Только препроцессирование

NASM допускает выполнение только препроцессирования входного файла. Использование ключа -e (не требующего параметров) заставит NASM препроцессировать входной файл, развернуть все макро ссылки, удалить все комментарии и директивы препроцессора и вывести результирующий файл в стандартный выходной поток (или сохранить его как отдельный файл, если используется также опция ).

Данный ключ неприменим для программ, где препроцессор должен вычислить выражения, зависящие от значений адресов: такой код как

%assign tablesize ($-tablestart)

будет вызывать ошибку в режиме "только препроцессирование".

2.1.11 Ключ -a: Отключение препроцессора

Если NASM используется в качестве выходной части компилятора, то для увеличения скорости компиляции желательно полностью подавить препроцессирование в случае, если компилятор уже делает его. Ключ -a, не требующий никаких параметров, заставляет NASM заменить свой препроцессор ничего не делающей "заглушкой".

2.1.12 Ключ -w: Разрешение/Запрещение предупреждений при ассемблировании

В процессе ассемблирования NASM может замечать множество вещей, заслуживающих внимания пользователя, но не представляющих собой таких серьезных ошибок, чтобы NASM не смог сгенерировать выходной файл. Эти предупреждения выдаются так же, как и ошибки, но перед собственно сообщением добавляется слово "warning". Предупреждения не являются причиной прерывания процесса генерации выходного файла.

Некоторые события менее серьезны, чем другие: они только иногда заслуживают внимания пользователя. В связи с вышесказанным, NASM поддерживает ключ командной строки -w, включающий или выключающий определенные классы предупреждений. Эти классы предупреждений описываются по имени, например orphan-labels; вы можете разрешить предупреждения данного класса при помощи ключа -w+orphan-labels и запретить их ключом -w-orphan-labels.

Ниже перечислены классы подавляемых предупреждений:

2.1.13 Переменная окружения NASM

Если вы определите переменную окружения NASM, программа будет интерпретировать ее как список дополнительных ключей командной строки, обрабатывающихся раньше "настоящих" параметров командной строки. Вы можете использовать эту возможность, например для описания стандартных каталогов поиска включаемых файлов, поместив в пременную NASM ключ -i.

Значение пременной разделяется пробелами, поэтому значение -s -ic:\nasmlib будет обработано как два отдельных ключа. В то же время значение -dNAME="my name" не будет воспринято так, как вы хотите (внутри есть пробел) и командный процессор NASMа соответственно не поймет двух бессмысленных параметров -dNAME="my и name".

Для разрешения этого введено следующее правило: если вы начинаете переменную NASM некоторым символом, не являющимся знаком "минус", NASM будет воспринимать этот символ как разделитель опций. Таким образом, значение
!-s!-ic:\nasmlib переменной NASM эквивалентно -s -ic:\nasmlib, но зато теперь !-dNAME="my name" будет работать правильно.

2.2 Пользователям MASM: Отличия

Если вы использовали для написания программ MASM, или TASM в режиме совместимости с MASM, или a86, прочитайте данный раздел, в котором приводятся основные отличия синтаксиса MASM и NASM. Если же вы вообще не использовали раньше MASM, просто пропустите этот раздел и читайте дальше.

2.2.1 NASM чувствителен к регистру символов

Самым "простым" отличием является регистро-чувствительность NASM — он различает обращения к таким меткам, как foo, Foo или FOO. Если вы ассемблируете .obj-файлы для DOS или OS/2, то для перевода всех экспортируемых в другие модули символов в верхний регистр можете активизировать директиву UPPERCASE (описана в параграфе 6.2). Однако в пределах одного модуля NASM различает метки, отличающиеся друг от друга только регистром.

2.2.2 NASM требует квадратные скобки для ссылок на память

NASM был разработан, кроме всего прочего, для упрощения запоминания синтаксиса. Одна из целей проекта NASM состоит в том, чтобы везде, где это возможно, было взаимно однозначное соответствие между отдельной строкой кода NASM и генерируемой из нее инструкцией. В MASM вы этого сделать не можете: если определите, например,

foo equ 1 bar dw 2

затем напишете две строки кода

mov ax,foo mov ax,bar

то будут сгенерированы две совершенно различных инструкции, несмотря на то, что синтаксис на вид совершенно идентичен.

NASM уходит от этой нежелательной ситуации путем упрощения синтаксиса для ссылок на память. Правило элементарно — любой доступ к содержимому памяти требует постановки квадратных скобок вокруг адреса, а при любом доступе к адресу переменной квадратные скобки не ставятся. Таким образом, инструкция вида mov ax,foo будет всегда ссылаться на константу времени компиляции, неважно EQU ли это или адрес переменной, в то же время для получения доступа к содержимому переменной bar вы должны использовать код mov ax,[bar].

Из этого также следует, что NASM не нуждается в ключевом слове MASMа OFFSET, т.к. MASMовский код mov ax,offset bar означает то же самое, что и mov ax,bar для NASM. Если вы накопили достаточно много кода, написанного под MASM и хотите использовать его в NASMе, вы всегда можете вставить строчку вида %idefine offset, указывающую препроцессору, что ключевое слово OFFSET является ничего не делающей инструкцией.

Данное несоответствие еще в большей степени присутствует в a86, где объявление метки с завершающим двоеточием описывает собственно метку, а без двоеточия — переменную. Так, в a86 инструкция mov ax,var будет вести себя по разному, в зависимости от объявления метки var: как var: dw 0 (это метка) или как var dw 0 (а это уже переменная размером в слово). NASM по сравнению с этим очень прост: все является метками.

NASM, в целях упрощения, не поддерживает гибридный синтаксис MASMа и его клонов, такой, как например mov ax,table[bx], где ссылка на память обозначена частью внутри квадратных скобок, а частью — вне их. Правильный синтаксис в NASM для указанной выше инструкции будет mov ax,[table+bx]. Соответственно, инструкция вида mov ax,es:[di] не поддерживается, правильная инструкция — mov ax,[es:di].

2.2.3 NASM не хранит типы переменных

NASM не запоминает определямые вами типы переменных. Поскольку MASM эти вещи запоминает, то при встрече var dw 0 он запомнит, что вы определили var как пременную размером в слово и затем будет способен разрешить неопределенность при появлении инструкции mov var,2. NASM же преднамеренно не будет помнить ничто относительно символа var за исключением того, где он начинается, поэтому вы должны явно указывать mov word [var],2.

В соответствии с этим, NASM не поддерживает инструкции LODS, MOVS, STOS, SCAS, CMPS, INS или OUTS, поддерживаются только их формы вида LODSB, MOVSW и SCASD, где явно задается размер компонентов обрабатываемой строки.

2.2.4 NASM не поддерживает ASSUME

Как часть общей идеологии упрощения, NASM не поддерживает директиву ASSUME. NASM не будет следить за тем, какие значения вы помещаете в сегментные регистры и поэтому никогда автоматически не будет генерировать префикс замены сегмента.

2.2.5 NASM не поддерживает модели памяти

NASM не имеет никаких директив для поддержки различных 16-битных моделей памяти. Программист должен самостоятельно следить, какие функции предполагается вызывать "дальним вызовом", а какие — ближним, и соответственно помещать правильную форму инструкции RET (RETN или RETF; NASM допускает применение RET в качестве альтернативной формы RETN); кроме того, программист ответственен за кодирование инструкций CALL FAR, где это необходимо при вызове внешних функций, и должен также следить, какие описания внешних переменных являются дальними, а какие — ближними.

2.2.6 Различия в обработке чисел с плавающей точкой

NASM, в отличие от MASM, использует другие имена для ссылок на регистры сопроцессора: MASM ссылается на эти регистры как ST(0), ST(1) и т.д., в NASMе для этой цели используются имена st0, st1 и т.д.

Начиная с версии 0.96, NASM обрабатывает инструкции форм "nowait" так же, как и MASM-совместимые ассемблеры. Особая обработка, использованная в версиях 0.95 и младше была основана на неправильном понимании авторами.

2.2.7 Прочие различия

По историческим причинам NASM использует ключевое слово TWORD там, где MASM и совместимые с ним ассемблеры используют TBYTE.

NASM объявляет резервируемое пространство (неиницализированные данные) не так, как MASM: там, где MASM-программист может написать stack db 64 dup (?), NASM требует следующее — stack resb 64, что интерпретируется как "резервирование 64 байт". Так как NASM обрабатывает ? как обычный символ, вы можете написать что-то вроде ? equ 0, а затем использовать dw ?, что будет возможно полезно для каких-то целей. DUP однако остается неподдерживаемым синтаксисом.

И, наконец, в дополнение ко всему вышесказанному, макросы и директивы NASM работают совершенно отлично от MASM. Подробности приведены в главе 4 и главе 5.

Следующая глава | Предыдущая глава | Содержание | Указатель