Любое численное выражение также может состоять из одного значения с плавающей точкой (flat assembler не может производить во время компиляции операции с плавающей точкой) в научной записи. Для распознания компилятором, эти значения должны содержать в конце букву «f», либо включать в себя по крайней мере один символ ".» или «E». Так, «1.0», «1E0» и «1f» определяют одно и то же значение с плавающей точкой, когда как просто «1» определяет целочисленное значение.
Операнд любого перехода или инструкция вызова может предваряться не только операторами размера, но также одним из операторов, определяющих тип перехода: «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»).
Если инструкция использует некоторую адресацию в памяти, по умолчанию будет генерироваться кратчайшая 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.1 Условное ассемблирование
С помощью директивы «if» можно ассемблировать или не ассемблировать блок инструкций в зависимости от выполнения условия. За ней должно следовать логическое выражение, определяющее условие. Инструкции на следующих строках ассемблируются, только если это условие выполняется, иначе они пропускаются. Опциональная директива «elseif» со следующим за ней логическим выражением, определяющим дополнительное условие, начинает следующий блок инструкций, который ассемблируется, если предыдущие условия не выполняются, а данное дополнительное условие выполняется. Опциональная директива «else» начинает блок инструкций, которые ассемблируются, если не выполняется ни одно из условий. «end if» заканчивает последний блок инструкций.
Вы должны помнить, что директива «if» обрабатывается на стадии ассемблирования и поэтому не влияет на директивы препроцессора, такие как определения символьных констант и макроинструкции — когда ассемблер распознает директиву «if», весь препроцессинг уже закончен.
Логическое выражение состоит из логических значений и логических операторов. Логические операторы выглядят так: «~» для логического отрицания, «&» для логического И, «|» для логического ИЛИ. Отрицание имеет высший приоритет. Логическое значение может быть числовым выражением, оно будет считаться ложным в случае равенства нулю, иначе оно будет истинным. Для создания логического значения можно сравнить два числовых выражения, используя один из следующих операторов: «=» (равно), «<» (меньше), «>» (больше), «<=» (меньше или равно), «>=» (больше или равно), «<>» (не равно).
«used» со следующим за ним символом имени, это логическое значение, которое проверяет, использовался ли где-нибудь данный символ (он возвращает правильный результат даже если символ используется только после этой проверки). За оператором «defined» может следовать любое выражение, обычно это только одно символьное имя; этот оператор проверяет, содержит ли данное выражение исключительно символы, определенные в коде, и доступные из текущей позиции.
Следующий простой пример использует константу «count» которая должна быть определена где-то в коде:
if count>0
mov cx,count
rep movsb
end if
Эти две инструкции будут ассемблированы только если константа «count» больше нуля. Следующий пример показывает более комплексную условную структуру:
if count & ~ count mod 4
mov cx,count/4
rep movsd
else if count>4
mov cx,count/4
rep movsd
mov cx,count mod 4
rep movsb
else
mov cx,count
rep movsb
end if
Первый блок инструкций ассеблируется, если константа «count» не равна нулю и кратна четырем, если это условие не выполняется, оценивается второе логическое условие, следующее за «else if», и если оно верно, ассемблируется второй блок инструкций, иначе ассемблируется последний блок, который следует за строкой, содержащей только «else».
Также есть операторы, которые позволяют сравнивать значения, которые представляют собой последовательности символов. «eq» проверяет такие значения на тождественность. Оператор «in» проверяет, принадлежит ли данное значение к списку значений, следующему за оператором. Список должен быть заключен между символами «<» и «>», а его члены должны быть разделены запятыми. Символы считаются одинаковыми, если они имеют одно и то же значение для ассемблера — например, «pword» и «fword» для ассемблера одинаковы поэтому не различаются вышеуказанными операторами. Так же «16 eq 10h» является истиной, однако «16 eq 10+4» нет.
Оператор «eqtype» имеют ли сравниваемые значения одинаковую структуру, и принадлежат ли структурные элементы одному типу. Различаемые типы включают в себя числовые выражения, строки, заключенные в кавычки, значения с плавающей точкой, адресные выражения (выражения в квадратных скобках или предваренные оператором «ptr»), мнемоники инструкций, регистры, операторы размера, операторы перехода и операторы типа кода. И каждый из специальных символов, действующих как разделители, такой как запятая или двоеточие, это отдельный тип сам по себе. Например, два значения, каждое из которых состоит из имени регистра и числового выражения, разделенных запятой, будут распознаны как один тип, независимо от вида регистра и сложности числового выражения; за исключением строк, заключенных в кавычки и значений с плавающей точкой, которые относятся к специальным видом числовых выражений и распознаются как разные типы. Поэтому условие «eax,16 eqtype fs,3+7» является истиной, но «eax,16 eqtype eax,1.6» — ложь.
2.2.2 Повторение блоков инструкций
«times» повторяет одну инструкцию указанное количество раз. За ней должно следовать числовое выражение, определяющее количество повторений, и инструкция, которую нужно повторять (опционально для того, чтобы отделить число и инструкцию, можно использовать двоеточие). Специальный символ «%», использующийся внутри инструкции, эквивалентен номеру текущего повтора. Например, «times 5 db %» определит пять байтов со значениями 1, 2, 3, 4, 5. Поддерживается также рекурсивное использование директивы «times», например, «times 3 times % db %» определит шесть байтов со значениями 1, 1, 2, 1, 2, 3.