Продолжая использовать наш сайт, вы даете согласие на обработку файлов cookie, которые обеспечивают правильную работу сайта. Благодаря им мы улучшаем сайт!
Принять и закрыть

Читать, слущать книги онлайн бесплатно!

Электронная Литература.

Бесплатная онлайн библиотека.

Читать: Flat Assembler 1.64. Мануал программера - Tomasz Grysztar на бесплатной онлайн библиотеке Э-Лит


Помоги проекту - поделись книгой:

FLAT ASSEMBLER 1.64

Мануал программера

Глава 1. Введение

Эта глава содержит всю важнейшую информацию, которая понадобится вам, чтобы начать использовать flat assembler. Если у вас уже есть опыт программирования на ассемблере, вам достаточно прочитать лишь первую главу перед использованием этого компилятора.

1.1 Обзор компилятора

Flat assembler — это быстрый компилятор ассемблера для процессоров с архитектурой х86, который делает множественные проходы для оптимизации размера сгенерированного машинного кода. Он способен скомпилировать сам себя и существуют версии для разных операционных систем. Все версии созданы для использования с помощью системной командной строки и в обращении с ними нет разницы.

1.1.1 Системные требования

Для работы всех версий требуется 32-битный процессор с архитектурой х86 (как минимум 80386), хотя также он должен обрабатывать программы для 16-битных процессоров с архитектурой х86. DOS-версия требует ОС, совместимую с MS DOS 2.0, Windows-версия требует консоль Win32, совместимую с версией 3.1.

1.1.2 Работа с компилятором из командной строки

Для запуска FASMа из командной строки вам понадобится ввести два параметра: первый — это путь к файлу с кодом, второй — путь к файлу-адресату информации. Если второй параметр не задан, название файла вывода будет создано автоматически. После показа короткой информации о названии программы и её версии, компилятор считает информацию из файла с кодом и скомпилирует её. После успешной компиляции FASM запишет сгенерированный код в файл-адресат и выведет краткую информацию о завершенном процессе; в противном случае он выведет информацию о первой ошибке.

Исходник должен быть текстовым файлом и может быть создан в любом текстовом редакторе. Обрыв строки допускается и в стандарте DOS, и в стандарте Unix, табуляции обрабатываются как пробелы.

В командную строку вы также можете включить опцию «-m», за которой должно следовать число, указывающее, сколько килобайт памяти может быть максимально задействовано flat assembler'ом. В случае DOS-версии эта опция ограничивает лишь использование расширенной памяти. Опция «-p» со следующим за ним числом может быть использована для того, чтобы ограничить количество проходов, которое будет делать ассемблер. Если код не может быть создан заданным числом проходов, ассемблирование прекратится с сообщением об ошибке. Максимальное значение этой опции равно 65536, а значение по умолчанию равно 100.

Не существует опций, оказывающих воздействие на выходные данные компилятора, вся необходимая flat assembler'у информация должна содержаться в исходном коде. Например, для установки формата файла-адресата используется директива «format» в начале кода.

1.1.3 Сообщения компилятора

Как было сказано выше, после успешной компиляции FASM выводит на экран сводку о проделанной работе. Она включает информацию о том, сколько было сделано проходов, сколько времени это заняло, и сколько байт записано в файл-адресат.

Вот пример такой сводки:

flat assembler version 1.64

38 passes, 5.3 seconds, 77824 bytes.

В случае ошибки во время компиляции, программа выведет на экран сообщение об ошибке. Например, когда компилятор не может найти файл ввода, он покажет следующее сообщение:

flat assembler version 1.64

error: source file not found.

Если ошибка связана с определенной частью кода, будет выведена строка, которая её вызвала. Также, чтобы помочь вам найти эту ошибку, будет показано место этой строки в коде, например:

flat assembler version 1.64

example.asm [3]:

mob ax,1

error: illegal instruction.

Это значит, что в третьей строке файла «example.asm» компилятор встретил неопознанную инструкцию. Если строка, вызвавшая ошибку, содержит макрос, также будет выведена строка в формулировке макроса, которая сгенерировала ошибочную инструкцию:

flat assembler version 1.64

example.asm [6]:

stoschar 7

example.asm [3] stoschar [1]:

mob al,char

error: illegal instruction.

Это значит, что макрос в шестой строке файла «example.asm» создал неопознанную инструкцию в первой строке своей формулировки.

1.1.4 Форматы вывода

По умолчанию, если в исходнике нет директивы «format», flat assembler направляет сгенерированный код на вывод, создавая таким образом простой двоичный файл. По умолчанию он создает 16-битный код, но вы всегда можете переключить его в 32-битный или 16-битный режим, используя директивы «use32» или «use16». Выбор некоторых форматов файла-адресата автоматически переключает компилятор в 32-битный режим. Подробнее читайте о форматах, которые можете выбрать, в 2.4.

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

1.2 Синтаксис ассемблера

Информация, изложенная ниже, предназначена главным образом программистам, которые прежде использовали другие компиляторы ассемблера. Если вы новичок, читайте учебники по программированию на ассемблере.

Flat assembler по умолчанию использует Интеловский синтаксис ассемблерных инструкций, однако вы можете переделать их, используя возможности препроцессора (макросы и символьные константы). Он также имеет собственный набор директив — инструкций для компилятора.

Все символы, определенные внутри кода, чувствительны к регистру.

1.2.1 Синтаксис инструкций

Инструкции в ассемблере разделяются разрывами строк, и одна инструкция должна располагаться на одной строке. Если строка содержит точку с запятой, не считая точек с запятой, заключенных в кавычки, остаток её считается комментарием и компилятор её проигнорирует. Если строка содержит символ «\» (в конце концов точка с запятой и комментарий могут за ней следовать), то следующая срока прикрепляется к этой точке. После символа «\» строка не должна содержать ничего, кроме комментариев, следующих за точкой с запятой.

Каждая строка в коде — это последовательность отдельных предметов, каждый из которых может принадлежать к одному из трек типов. Первый тип — это знаки символов, которыми являются специальные знаки, которые трактуются отдельно, даже если не отделены пробелами о других символов. Любой из «+-*/=<>()[]{}:,|&~#`» — это знак символа. Последовательность других знаков, отделенная от остальных либо пробелами, либо знаками символов, это символ. Если первый знак такого символа двойная либо одинарная кавычка, он объединяет любую последовательность символов, даже специальных, следующих за ним, в строку. Она должна заканчиваться таким же знаком, каким начиналась (двойной либо одинарной кавычкой). Однако если встречаются две кавычки подряд (без знаков между ними), они также включаются в строку и она продолжается. Символы, отличные от знаков символов и строк, заключенных в кавычки, могут использоваться как имена, поэтому они также называются символами имен.

Таблица 1.1 Размеры операторов

Каждая инструкция состоит из мнемоника и различного числа операндов, разделенных запятыми. Операндом может быть регистр, непосредственное значение или адрес в памяти, он также может предварен оператором размера, чтобы определить или изменить его размер (таблица 1.1). Названия возможных регистров вы можете найти в таблице 1.2, их размеры не могут быть изменены. Непосредственные значения могут быть определены любым числовым значением.

Таблица 1.2 Регистры

Если операнд — это данные в памяти, адрес этих данных (также любого числового выражения, но оно может содержать регистры) должен быть заключен в квадратные скобки или предварен оператором «ptr». Например, инструкция «mov eax,3» поместит число 3 в регистр EAX, а инструкция «mov eax,[7]» поместит 32-битное значение из адреса 7 в EAX, и инструкция «mov byte [7],3» поместит число 3 в байт по адресу 7, это можно записать еще так: «mov byte ptr 7,3». Для того, чтобы установить, какой сегментный регистр будет использоваться для адресации, нужно поставить его название с двоеточием перед адресом внутри квадратных скобок или после оператора «ptr».

1.2.2 Описание данных

Чтобы описать данные или зарезервировать для них место, используйте одну из директив, перечисленных в таблице 1.3. За директивой описания данных должно следовать одно или несколько числовых значений, разделенных запятыми. Эти выражения определяют значения для простейших элементов данных, размер которых зависит от того, какая директива используется. Например «db 1,2,3» описывает три байта со значениями 1, 2 и 3 соответственно.

Таблица 1.3 Директивы данных

Директивы «du» и «db» также поддерживают сроки любой длины, заключенные в кавычки, которые будут конвертированы в последовательность байтов, если использована директива «db», или в последовательность слов с нулевым верхним байтом, если использована директива «du». Например, «db 'abc'» определяет три байта со значениями 61, 62 и 63.

Директива «dp» или её синоним «df» допускают, чтобы значения состояли из двух числовых выражений, разделенных двоеточием, где первое значение — это верхнее слово, а второе — это нижнее двойное слово значения дальнего указателя. Также «dd» допускает такие указатели, состоящие из двух слов, разделенных двоеточием, и «dt» допускает слово и четверное слово, разделенные двоеточием, четверное слово запоминается первым. Директива «dt» с одним параметром допускает только значения с плавающей точкой и создает данные в FPU-формате двойной расширенной точности.

Все вышеперечисленные директивы поддерживают использование специального оператора «dup» для создания копий данных значений. Количество дубликатов должно стоять перед этим оператором, а их значение должно стоять после — это может быть даже цепь значений, разделенных запятыми, но эта цепь должна быть заключена в скобки, например «db 5 dup (1,2)» определяет пять копий данной последовательности из двух байтов.

«file» — это специальная директива и её синтаксис может быть различным. Эта директива включает цепь байтов из файла. В качестве параметра за ней должно идти в кавычках имя файла, далее, опционально, двоеточие и числовое выражение, указывающее начало цепочки байтов, далее, также опционально, запятая и числовое выражение, определяющее количество байтов в этой цепочке (если этот параметр не определен, то будут включены все данные до конца файла). Например, «file 'data.bin'» включит весь файл как двоичные данные, а «file 'data.bin':10h,4» включит только четыре байта, начиная со смещения 10h.

За директивой резервирования данных должно следовать одно числовое выражение, значение которого определяет количество резервируемых ячеек установленного размера. Все директивы описания данных также поддерживают значение «?», которое значит, что этой ячейке не должно быть присвоено какое-то значение. Эффект от этой директивы такой же, как от директивы резервирования данных. Неинициализированные данные не могут быть включены в файл вывода, и, таким образом, их значения всегда будут считаться неизвестными.

1.2.3 Константы и метки

В числовых выражениях вместо чисел вы также можете использовать константы и метки. Чтобы назначить их, используйте специальные директивы. Каждая метка может быть определена только однажды и она будет доступна из любой части кода (даже перед местом, где она была определена). Константа может быть переопределена много раз, но в этом случае она будет доступна только после присвоения значения и всегда будет равна значению из последнего определения перед местом, в котором она использована. Если константа определена лишь однажды, она, так же как и метка, доступна из любой части кода.

Определение константы состоит из имени константы, знака «=» и числового выражения, которое после вычисления становится значением константы. Это значение всегда вычисляется в то же время, что и определение константы. Например, с помощью директивы «count = 17» вы можете определить константу «count» и после использовать её в инструкциях ассемблера, таких как «mov cx,count» — которая превратится в «mov cx,17» во время процесса компиляции.

Существуют разные способы определения меток. Простейший из них — двоеточие после названия метки. За этой директивой на той же строке даже может следовать другая инструкция. Она определяет метку, значение которой равно смещению точки, в которой она определена. Этот метод обычно используется, чтобы пометить места в коде. Другой способ — это следование за именем метки (без двоеточия) какой-нибудь директивы описания данных. Метке присваивается значение адреса начала определенных в директиве данных и запоминается компилятором как метка для данных с размером ячейки, заданной директивой из таблицы 1.3.

Метка может быть обработана как константа со значением, равным смещению помеченного кода или данных. Например, если вы определяете данные, используя помеченную директиву «char db 224», для того, чтобы поместить адрес начала этих данных в регистр BX, вам нужно использовать инструкцию «mov bx,char», а для того, чтобы поместить в регистр DL значение байта, на который ссылается «char», нужно использовать «mov dl,[char]» (или «mov dl,ptr char»). Если вы попытаетесь ассемблировать «mov ax,[char]», FASM выдаст ошибку, так как он сравнивает размеры операндов, которые должны быть равны. Вы можете принудительно проассемблировать эту инструкцию, изменяя размер операнда: «mov ax, word [char]», но помните, что эта инструкция прочитает два байта, начинающихся с адреса «char», тогда как он был определен как один байт.

Последний и самый гибкий способ задания меток — это использование директивы «label». За этой директивой должно следовать имя метки, далее, опционально, размер оператора (может предваряться двоеточием), и далее, также опционально, оператор «at» и числовое выражение, определяющее адрес, на который данная метка должна ссылаться. Например, «label wchar word at char» определяет новую метку для 16-битных данных по адресу «char». Теперь инструкция «mov ax,[wchar]» после компиляции будет выглядеть так же, как «mov ax,word [char]». Если адрес не указан, директива «label» будет ссылаться на текущий адрес. Таким образом, «mov [wchar],57568» скопирует два байта, тогда как «mov [char],224» скопирует один байт на тот же адрес.

Метка, имя которой начинается с точки, обрабатывается как локальная, и её имя прикрепляется к имени последней глобальной метки (с названием, начинающемся с чего угодно, кроме точки) для создания полного имени этой метки. Так, вы можете использовать короткое имя (начинающееся с точки) где угодно перед следующей глобальной меткой, а в других местах вам придется пользоваться полным именем. Метки, начинающиеся с двух точек — исключения. Они имеют свойства глобальных, но не создают новый префикс для локальных меток.

«@@» обозначает анонимную метку, вы можете определить её множество раз. Символ «@b» (или эквивалент «@r») ссылается на ближайшую предшествующую анонимную метку, а символ «@f» ссылается на ближайшую после неё анонимною метку. Эти специальные символы нечувствительны к регистру.

1.2.4 Числовые выражения

В предыдущих примерах все числовые выражения были обычными числами, константами или метками. Но они могут быть более сложными, использовать арифметические или логические операторы для вычисления во время компиляции. Все эти операторы с их значениями приоритета перечислены в таблице 1.4.

Таблица 1.4 Арифметические и логические операторы в порядке приоритета

Операции с высшим приоритетом выполняются первыми, однако вы, конечно, можете изменить такой образ действий, заключив некоторые части выражения в скобочки. «+», «-", «*» и «/» — это стандартные арифметические операции, «mod» вычисляет остаток от деления нацело. «and», «or», «xor», «shl», «shr» и «not» совершают те же логические операции, что и инструкции ассемблера с такими же названиями. «rva» характерна только для формата вывода PE и производит превращение адреса в RVA.

Числа в выражениях по умолчанию обрабатываются как десятичные, двоичные числа должны иметь «b» в конце, восьмеричные числа должны заканчиваться на букву «o», шестнадцатеричные цифры должны начинаться символами «0x» (как в языке C), или символом «$» (как в языке Pascal) или должны заканчиваться буквой «h». Также заключенная в кавычки строка при включении в выражение будет конвертирована в число — первый символ станет минимальным значащим байтом числа. Числовые выражения, используемые как значения адреса, могут также содержать любой из общих регистров, используемых для адресации, они могут быть сложены или умножены на подходящие значения так, как это позволено в инструкциях архитектуры x86.

Также есть несколько специальных символов, которые могут быть использованы в числовом выражении. Первое — это «$», которое всегда равно значению текущего смещения, тогда как «$$» равно базовому адресу текущего диапазона адресов. Следующий символ — «%» — это номер текущего повтора в частях кода, которые повторяются, благодаря использованию некоторых специальных директив (смотрите 2.2). Также существует символ «%t», который всегда равен текущей отметке времени.

Любое численное выражение также может состоять из одного значения с плавающей точкой (flat assembler не может производить во время компиляции операции с плавающей точкой) в научной записи. Для распознания компилятором, эти значения должны содержать в конце букву «f», либо включать в себя по крайней мере один символ ".» или «E». Так, «1.0», «1E0» и «1f» определяют одно и то же значение с плавающей точкой, когда как просто «1» определяет целочисленное значение.

1.2.5 Переходы и вызовы

Операнд любого перехода или инструкция вызова может предваряться не только операторами размера, но также одним из операторов, определяющих тип перехода: «near» или «far». Например, если ассемблер в 16-битном режиме, инструкция «jmp dword [0]» станет далеким переходом, а если ассемблер в 32-битном режиме, она станет близким переходом. Чтобы заставить эту инструкцию обрабатываться по-разному, используйте формы «jmp near dword [0]» или «jmp far dword [0]».

Если операнд близкого перехода это немедленное значение, ассемблер, если возможно, сгенерирует кратчайший вариант этой инструкции перехода (но не будет создавать 32-битную инструкцию в 16-битном режиме или 16-битную инструкцию в 32-битном режиме, если оператор размера точно её не определит). Заданием оператора размера вы можете заставить ассемблер всегда генерировать длинный вариант (например, «jmp word 0» в 16-битном режиме или «jmp dword 0» в 32-битном режиме) или всегда создавать короткий вариант и завершаться с ошибкой, когда это невозможно (например «jmp byte 0»).

1.2.6 Установки размера

Если инструкция использует некоторую адресацию в памяти, по умолчанию будет генерироваться кратчайшая 8-битная форма, если значение адреса попадает в нужный диапазон, но он может быть изменен с помощью операторов «word» и «dword» перед адресом в квадратных скобках (или после оператора «ptr»). Такое размещение оператора размера также может быть использовано для установки размера адреса, отличного от размера, установленного в данном режиме по умолчанию.

Инструкции «adc», «add», «and», «cmp», «or», «sbb», «sub» и «xor» с первым 16-ти или 32-битным операндом по умолчанию генерируются в укороченной 8-битной форме, если второй операнд — это непосредственное значение, применимое для предписанных 8-битных значений. Она также может быть изменена операторами «word» и «dword» перед такими значениями. Сходные правила применимы к инструкции «imul» с непосредственным значениям в качестве последнего операнда.

Непосредственное значение как операнд для инструкции «push» без оператора размера, по умолчанию обрабатывается как слово, если ассемблер 16-битном режиме, и как двойное слово, если FASM в 32-битном режиме. Короткая 8-битная форма используется по возможности, операторы размера «word» и «dword» могут заставить инструкцию «push» быть сгенерированной в более длинной форме. Мнемоники «pushw» и «pushd» указывают ассемблеру сгенерировать 16-битный или 32-битный код без принуждения его использовать длинную форму инструкции.

Глава 2. Описание инструкций

Эта глава содержит детальную информацию об инструкциях и директивах, поддерживаемых FASMом. Директивы определения констант и меток уже описаны в 1.2.3, все остальные директивы будут описаны ниже в этой главе.

2.2 Директивы управления

Этот параграф описывает директивы, которые управляют процессом ассемблирования. Эти директивы выполняются во время ассемблирования и могут делать так, чтобы некоторые блоки инструкций ассемблировались по-разному или не ассемблировались вовсе.

2.2.1 Условное ассемблирование

С помощью директивы «if» можно ассемблировать или не ассемблировать блок инструкций в зависимости от выполнения условия. За ней должно следовать логическое выражение, определяющее условие. Инструкции на следующих строках ассемблируются, только если это условие выполняется, иначе они пропускаются. Опциональная директива «elseif» со следующим за ней логическим выражением, определяющим дополнительное условие, начинает следующий блок инструкций, который ассемблируется, если предыдущие условия не выполняются, а данное дополнительное условие выполняется. Опциональная директива «else» начинает блок инструкций, которые ассемблируются, если не выполняется ни одно из условий. «end if» заканчивает последний блок инструкций.

Вы должны помнить, что директива «if» обрабатывается на стадии ассемблирования и поэтому не влияет на директивы препроцессора, такие как определения символьных констант и макроинструкции — когда ассемблер распознает директиву «if», весь препроцессинг уже закончен.

Логическое выражение состоит из логических значений и логических операторов. Логические операторы выглядят так: «~» для логического отрицания, «&» для логического И, «|» для логического ИЛИ. Отрицание имеет высший приоритет. Логическое значение может быть числовым выражением, оно будет считаться ложным в случае равенства нулю, иначе оно будет истинным. Для создания логического значения можно сравнить два числовых выражения, используя один из следующих операторов: «=» (равно), «<» (меньше), «>» (больше), «<=» (меньше или равно), «>=» (больше или равно), «<>» (не равно).

«used» со следующим за ним символом имени, это логическое значение, которое проверяет, использовался ли где-нибудь данный символ (он возвращает правильный результат даже если символ используется только после этой проверки). За оператором «defined» может следовать любое выражение, обычно это только одно символьное имя; этот оператор проверяет, содержит ли данное выражение исключительно символы, определенные в коде, и доступные из текущей позиции.

Следующий простой пример использует константу «count» которая должна быть определена где-то в коде:

if count>0



Поделиться книгой:

На главную
Назад