Учебный курс. Часть 28. Основы создания макросов

Автор: xrnd | Рубрика: Учебный курс | 16-11-2010

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

Что же такое макросы? Макросы – это шаблоны для генерации кода. Один раз создав макрос, мы можем использовать его во многих местах в коде программы. Макросы делают процесс программирования на ассемблере более приятным и простым, а код программы получается понятнее. Макросы позволяют расширять синтаксис ассемблера и даже добавлять собственные “команды”, которых нет в процессоре.

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

Синтаксис создания макроса

Для создания макроса используется директива macro. Эта директива имеет следующий синтаксис:

macro <название_макроса> [<список_параметров>]
{
    <тело_макроса>
}

Читать полностью »

Учебный курс. Часть 27. Синтаксис объявления меток

Автор: xrnd | Рубрика: Учебный курс | 03-11-2010

Не сомневаюсь, что объявлять метки вы уже научились 🙂 Однако, синтаксис FASM не ограничивается объявлением простых меток. В этой части мы рассмотрим дополнительную директиву для создания меток, а также научимся использовать локальные и анонимные метки.

В синтаксисе FASM существует 3 основных способа объявления меток:

1. Имя метки, после которого ставится двоеточие. Это самый простой способ. Обычно так объявляются метки в коде. (Подробнее об этом способе читайте в части 13 учебного курса)

exit_app:
    mov ax,4C00h
    int 21h

2. Использование директив объявления данных. Имя переменной является по сути той же меткой. Отличие от первого способа в том, что дополнительно с именем метки связывается размер переменной. (Подробнее читайте в части 5 учебного курса)

x db 5
y dw 34,1200,?
z rd 1

Читать полностью »

Создание листинга в FASM

Автор: xrnd | Рубрика: Учебный курс | 28-10-2010

Файл листинга – это очень полезная вещь. Он позволяет увидеть работу компилятора FASM как на ладони: что генерирует каждая строка исходного кода, сколько байт занимают машинные команды, какие значения присваиваются переменным. Листинг может помочь при отладке сложных программ. Также он пригодится начинающим изучать ассемблер.

Сначала необходимо скомпилировать программу – генератор листинга, которая поставляется вместе с FASM. Сделать это очень просто. Нужно открыть файл C:\FASM\TOOLS\WIN32\LISTING.ASM (у меня FASM находится в папке C:\FASM, у вас может быть по-другому) и скомпилировать его (пункт меню Run->Compile). Полученный исполняемый файл (LISTING.EXE) можно для удобства поместить в папку C:\FASM.

Если запустить этот файл из командной строки, то он сообщит способ своего использования:

Читать полностью »

Instruction Set Reference

Автор: xrnd | Рубрика: Книги | 22-10-2010

Полное название: «Intel Architecture Software Developer’s Manual. Volume 2: Instruction Set Reference»

Язык: английский.

Эта книга – официальная документация компании Intel по системе команд архитектуры x86. В ней содержится подробное описание всех целочисленных команд 32-битного ассемблера, а также команд некоторых расширений процессора. Кроме того рассматривается формат машинных команд и их коды. Это лучший справочник, которой должен быть всегда под рукой. Рекомендую всем, кто программирует на ассемблере.

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

Скачать книгу

Рекурсивные процедуры

Автор: xrnd | Рубрика: Исходники | 21-10-2010

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

Что такое факториал? Это математическая функция, определённая для целых неотрицательных чисел. 🙂 Обозначается восклицательным знаком (n! – факториал числа n). Факториал натурального числа равен произведению всех натуральных чисел до него. Факториал нуля равен 1. Подробнее можете прочесть здесь.

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

Читать полностью »

Учебный курс. Часть 26. Локальные переменные

Автор: xrnd | Рубрика: Учебный курс | 17-10-2010

До этой части учебного курса все переменные в наших программах были только глобальными – они создавались и инициализировались при запуске программы и к ним можно было обратиться из любой её части.

Локальные переменные используются для хранения промежуточных результатов во время выполнения процедуры. В отличие от глобальных, эти переменные являются временными и создаются при запуске процедуры. Для локальных переменных существует понятие области видимости – так называется область программы, в которой доступна переменная. Обычно в ассемблере область видимости ограничена процедурой, создавшей локальную переменную. Хотя возможны и более сложные варианты 😉

Создание локальных переменных

Чтобы создать локальные переменные в процедуре, необходимо выделить для них память. Эта память выделяется в стеке. Сделать это очень просто – достаточно вычесть из регистра SP значение, равное суммарному размеру всех локальных переменных в процедуре. Так как ширина стека равна 16 бит, то это значение должно быть кратно 2 байтам. При выходе из процедуры нужно восстановить указатель стека. Обычно это выполняется командой mov sp,bp (В bp сохраняется значение sp при входе в процедуру, как в случае с параметрами, передаваемыми через стек). Код процедуры с локальными переменными будет выглядеть следующим образом:

;Процедура с локальными переменными
myproc:
    push bp                 ;Сохранение BP
    mov bp,sp               ;Копирование указателя стека в BP
    sub sp,locals_size      ;Выделение памяти для локальных переменных
    ...
    mov sp,bp               ;Восстановление указателя стека
    pop bp                  ;Восстановление BP
    ret                     ;Возврат из процедуры

Читать полностью »

Процедуры с переменным количеством параметров

Автор: xrnd | Рубрика: Исходники | 29-09-2010

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

Вообще, на мой взгляд, процедуры с переменным количеством параметров являются не самым удачным и потенциально небезопасным приёмом программирования. Даже в C/C++ лучше их избегать. В большинстве случаев можно обойтись процедурой с фиксированным количеством параметров. Например, передавать процедуре 2 параметра: адрес массива переменной длины и количество элементов в этом массиве.

Для создания процедуры с переменным количеством параметров необходимо, чтобы стек очищался вызывающим кодом. Поэтому процедура должна заканчиваться командой RET без операндов. Параметры должны помещаться в стек в обратном порядке. Кстати, именно такие соглашения вызова используются компиляторами языка C.

Читать полностью »

Учебный курс. Часть 25. Передача параметров через стек

Автор: xrnd | Рубрика: Учебный курс | 26-09-2010

В предыдущих частях учебного курса все параметры передавались процедурам через регистры. В этой статье мы рассмотрим другой способ – передачу параметров через стек. Часто этот способ оказывается удобнее. Через регистры можно передать максимум 6-7 параметров, а через стек – сколько угодно. Кроме того можно написать процедуру с переменным количеством параметров. (Подробнее о таких процедурах вы можете прочитать здесь.)

Если через регистры передаётся больше 2-3 параметров, то приходится сохранять регистры внутри процедуры и опять же использовать стек. С другой стороны, обращение к параметрам в стеке происходит медленнее. Если вы оптимизируете программу по скорости выполнения, то имеет смысл передавать параметры через регистры.

Помещение параметров в стек

Перед вызовом процедуры параметры необходимо поместить в стек с помощью команды PUSH. Здесь существует два варианта: параметры могут помещаться в стек в прямом или в обратном порядке. Обычно используется обратный порядок и его я буду использовать в примерах. Параметры помещаются в стек, начиная с последнего, так что перед вызовом процедуры на вершине стека оказывается первый параметр:

; Данные
arg0     dw 0
arg1     dw 12
argN     dw 345
;---------------------------------------------------------------------
; Код
    push [argN]
    push ...
    push [arg1]
    push [arg0]
    call myproc

Читать полностью »

Учебный курс. Часть 24. Команды управления флагами

Автор: xrnd | Рубрика: Учебный курс | 10-09-2010

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

Как вы, наверно, помните флаги изменяются в результате выполнения арифметических и логических команд, а также команд сдвига. Регистр флагов можно сохранить в стек с помощью команды PUSHF и восстановить из стека с помощью команды POPF. Кроме того, в процессоре существуют специальные команды, которые позволяют явно установить или сбросить флаги CF, DF и IF. Это очень простые команды: у них нет операндов и результатом является только изменение значения соответствующего флага.

Флаг переноса CF

Команда CLC сбрасывает флаг CF.

Команда STC устанавливает флаг CF в единицу.

Команда CMC инвертирует значение флага CF.

Читать полностью »

Учебный курс. Часть 23. Ввод чисел с консоли

Автор: xrnd | Рубрика: Исходники, Учебный курс | 07-08-2010

В прошлой части мы научились преобразовывать числа в строку и выводить на консоль. А в этой займёмся обратной задачей – вводом чисел с консоли и преобразованием строки в число. Поскольку ввод в двоичном и восьмеричном виде используется редко, я рассмотрю только примеры ввода чисел в десятичном виде (со знаком и без знака) и в шестнадцатеричном.

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

Ввод строки с консоли

Для ввода строки можно использовать функцию MS-DOS 0Ah. Функция позволяет ввести строку длиной от 1 до 254 символов. При вызове в DX передаётся адрес буфера, первый байт которого должен содержать максимально допустимую длину строки. Длина считается вместе с символом конца строки CR (0dh). В результате работы функции во второй байт буфера записывается фактическая длина введённой строки (не считая символа CR). Начиная с третьего байта в буфер записываются символы строки. Подробнее о работе функции можно узнать в раритетном справочнике по DOS 🙂

Читать полностью »