Какова структура ассемблерной программы

Программирование на уровне машинных команд — это тот минимальный уровень, на котором возможно составление программ. Система машинных команд должна быть достаточной для того, чтобы реализовать требуемые действия, выдавая указания аппаратуре вычислительной машины.

Каждая машинная команда состоит из двух частей:

  • операционной — определяющей, «что делать»;
  • операндной — определяющей объекты обработки, «с чем делать».

Машинная команда микропроцессора, записанная на языке ассемблера, представляет собой одну строку, имеющую следующий синтаксический вид:

метка команда/директива операнд(ы) ;комментарии

При этом обязательным полем в строке является команда или директива.

Метка, команда/директива и операнды (если имеются) разделяются по крайней мере одним символом пробела или табуляции.

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

По умолчанию язык ассемблера не различает заглавные и строчные буквы в написании команд или директив.

FASM. Установка FASM. Структура программы на ассемблере. Урок 1

Примеры строк кода:

Count db 1 ;Имя, директива, один операнд
mov eax,0 ;Команда, два операнда
cbw ; Команда

Метки

Метка в языке ассемблера может содержать следующие символы:

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

Первым символом в метке должна быть буква или спецсимвол (но не цифра). Максимальная длина метки – 31 символ. Все метки, которые записываются в строке, не содержащей директиву ассемблера, должны заканчиваться двоеточием : .

Команды

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

Директивы

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

Операнды

Операнд – объект, над которым выполняется машинная команда или оператор языка программирования.
Команда может иметь один или два операнда, или вообще не иметь операндов. Число операндов неявно задается кодом команды.
Примеры:

  • Нет операндов ret ;Вернуться
  • Один операнд inc ecx ;Увеличить ecx
  • Два операнда add eax,12 ;Прибавить 12 к eax

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

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

ЯЗЫК АССЕМБЛЕРА С НУЛЯ | #1 НАЧАЛО

  • идентификаторы;
  • цепочки символов, заключенных в одинарные или двойные кавычки;
  • целые числа в двоичной, восьмеричной, десятичной или шестнадцатеричной системе счисления.
Идентификаторы

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

Правила записи идентификаторов.

Комментарии

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

Структура программы на ассемблере

Программа, написанная на языке ассемблера, может состоять из нескольких частей, называемых модулями . В каждом модуле могут быть определены один или несколько сегментов данных, стека и кода. Любая законченная программа на ассемблере должна включать один главный, или основной, модуль, с которого начинается ее выполнение. Модуль может содержать сегменты кода, сегменты данных и стека, объявленные при помощи соответствующих директив. Перед объявлением сегментов нужно указать модель памяти при помощи директивы .MODEL.

Пример «ничего не делающей» программы на языке ассемблера:

.686P
.MODEL FLAT, STDCALL
.DATA
.CODE
START:
RET
END START

В данной программе представлена всего одна команда микропроцессора. Эта команда RET . Она обеспечивает правильное окончание работы программы. В общем случае эта команда используется для выхода из процедуры.
Остальная часть программы относится к работе транслятора.
.686P — разрешены команды защищенного режима Pentium 6 (Pentium II). Данная директива выбирает поддерживаемый набор команд ассемблера, указывая модель процессора. Буква P, указанная в конце директивы, сообщает транслятору о работе процессора в защищенном режиме.
.MODEL FLAT, stdcall — плоская модель памяти. Эта модель памяти используется в операционной системе Windows. stdcall — используемое соглашение о вызовах процедур.
.DATA — сегмент программы, содержащий данные.
.CODE — блок программы, содержащей код.
START — метка. В ассемблере метки играют большую роль, что не скажешь о современных языках высокого уровня.
END START — конец программы и сообщение транслятору, что начинать выполнение программы надо с метки START .
Каждый модуль должен содержать директиву END , отмечающую конец исходного кода программы. Все строки, которые следуют за директивой END , игнорируются. Если опустить директиву END , то генерируется ошибка.
Метка, указанная после директивы END , сообщает транслятору имя главного модуля, с которого начинается выполнение программы. Если программа содержит один модуль, метку после директивы END можно не указывать.

Комментариев к записи: 4

Источник: prog-cpp.ru

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?

Cancel Create

asm / lab01_helloworld.textile

  • Go to file T
  • Go to line L
  • Copy path
  • Copy permalink

This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Cannot retrieve contributors at this time
216 lines (142 sloc) 25.8 KB

  • Open with Desktop
  • View raw
  • Copy raw contents Copy raw contents Copy raw contents

Copy raw contents

Лабораторная работа №1. Первая программа на языке ассемблера

Краткие теоретические сведения

Структура ассемблерной программы

Каждый язык программирования имеет свои особенности и часто — отличия, характерные для конкретной программно-аппаратной платформы. В языке ассемблера, благодаря его низкоуровневости, особенности и специфика платформы ощущаются очень сильно. Рассмотрим пример простой программы на этом языке для 32-битной архитектуры x86 и ОС Linux. Традиционно первая программа выводит приветственное сообщение на экран.

SECTION .data hello: DB ‘Hello world!’,10 ; ‘Hello world!’ плюс символ ; возврата каретки helloLen: EQU $-hello ; Длина строки ‘Hello world!’ SECTION .text ; Начало секции кода GLOBAL _start ; Метка _start должна быть глобальной, ; чтобы линкер смог её найти и сделать ; точкой входа в программу. _start: mov eax,4 ; Системный вызов для записи (sys_write) mov ebx,1 ; Описатель файла $1$ — стандартный вывод mov ecx,hello ; Адрес строки hello в ecx mov edx,helloLen ; helloLen — константа, а не переменная, ; потому нет необходимости использовать ; mov edx,[helloLen] для получения ; действительного значения int 80h ; Вызов ядра mov eax,1 ; Системный вызов для выхода (sys_exit) mov ebx,0 ; Выход с кодом возврата $0$ (без ошибок) int 80h ; Вызов ядра

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

Синтаксис ассемблера NASM , которым мы будем пользоваться далее, является регистрочувствительным. Т.е. есть разница между большими и малыми буквами.

Команда может быть директивой — указанием транслятору, которое выполняется в процессе превращения программы в машинный код. Многие директивы начинаются с точки. Для удобства чтения программы они обычно пишутся БОЛЬШИМИ БУКВАМИ. Кроме директив еще бывают инструкции — команды процессору. Именно они и будут составлять машинный код программы.

Нужно отметить, что понятие «машинного кода» очень условно. Часто оно обозначает просто содержимое выполняемого файла, хранящего кроме собственно машинных команд еще и данные (в нашем случае — текст выводимого сообщения «Hello world»).

Особенности создания ассемблерной программы

На платформе Linux язык ассемблера является самым низкоуровневым языком программирования. Т.е. он больше любых других приближен к архитектуре ЭВМ и ее аппаратным возможностям, позволяет получить к ним более полный доступ, нежели в языках выского уровня, наподобие C/C++, Perl, Python и пр. Заметим, что получить полный доступ к ресурсам компьютера в современных архитектурах нельзя, самым низким уровнем работы прикладной программы является обращение напрямую к ядру ОС. Именно на этом уровне и работают программы, написанные на ассемблере в Linux. Но, в отличие от языков высокого уровня (ЯВУ), ассемблерная программа содержит только тот код, который ввел программист, и конечно же вся ответственность за логичность кода полностью лежит на плечах программиста.

Читайте также:
Что значит групповая программа

Простой пример. Обычно подпрограммы заканчиваются командой возврата. Если в ЯВУ ее не задать явно, транслятор все равно добавит ее в конец подпрограммы. Ассемблерная подпрограмма без команды возврата не вернется в точку вызова, а будет выполнять код, следующий за подпрограммой, как будто он является ее продолжением.

Еще пример. Можно попробовать «выполнить» данные вместо кода. Часто это лишено смысла. Но если программист это сделает, транслятор не выдаст никаких сообщений об ошибке. Язык ассемблера позволяет делать все! Вопрос состоит лишь в том, какие усилия придется приложить, чтобы реализовать идею на этом языке.

Тут меньше ограничений, чем в ЯВУ, но, в то же время, и удобства и простоты создания программ тоже меньше.

Эти особенности приводят к тому, что ассемблерные программы часто «подвешивают» компьютер, особенно у начинающих программистов. Выделим разновидности «зависания» по способу борьбы с ним.

  • Простое — для выхода из него достаточно нажать Ctrl+C (сначала нажимается клавиша Ctrl, и дальше нужно, не отпуская ее, нажать вторую клавишу — C; затем клавиши отпускаются в любом порядке). Программа при этом аварийно завершается выходом в ОС.
  • Мягкое — кажется, что машина никак не реагирует на клавиатуру и безнадежно зависла. В любом случае, ядро системы при этом продолжает работать и позволяет использовать базовые функции для сохранения целостности данных. Этими функциями можно управлять при помощи т. н. Magic Keys (см. описание SysRq Keys).
  • Жесткое — если зависло ядро ОС. Это может случиться в случае использования тестового ядра, находящегося в разработке, или при неправильной ручной сборке ядра, или при попытке использовать недокументированные особенности аппаратного обеспечения. В этом случае поможет аппаратный сброс при помощи кнопки «Reset», расположенной на передней панели системного блока.

Важно помнить, что в 90% случаев зависание является простым. Чаще всего не хватает аппаратных возможностей компьютера для быстрой обработки данных и необходимо просто подождать или нажать Ctrl+C.

Процесс обработки программы на языке ассемблера

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

  1. Набор программы в текстовом редакторе и сохранение ее в отдельном файле. Каждый файл имеет имя и тип, называемый иногда расширением. Тип в основном используется для определения назначения файла. Например, программа на C имеет тип c , на Pascal — pas , на языке ассемблера — asm .
  2. Обработка текста программы транслятором. На этом этапе текст превращается в машинный код, называемый объектным. Кроме того есть возможность получить листинг программы, содержащий кроме текста программы различную дополнительную информацию и таблицы, созданные транслятором. Тип объектного файла — o , файла листинга — lst . Этот этап называется трансляцией.
  3. Обработка полученного объектного кода компоновщиком. Тут программа «привязывается» к конкретным условиям выполнения на ЭВМ. Полученный машинный код называется выполняемым. Кроме того, обычно получается карта загрузки программы в ОЗУ. Выполняемый файл обычно не имеет расширения в отличие от программ ОС семейства DOS и Windows, карта загрузки — map . Этот этап называется компоновкой или линковкой.
  4. Запуск программы. Если программа работает не совсем корректно, перед этим может присутствовать этап отладки программы при помощи специальной программы — отладчика. При нахождении ошибки приходится проводить коррекцию программы, возвращаясь к шагу 1.

Таким образом, процесс создания ассемблерной программы можно изобразить в виде следующей схемы. Конечной целью, напомним, является работоспособный выполняемый файл hello (см. рис. [pic:l1]).

Основные возможности текстового редактора mcedit

mcedit — это текстовый редактор, встроенный в двухпанельный файловый менеджер Midnight Commander. Сама по себе среда Midnight Commander (или просто mc ) очень схожа с другими «командерами». Например, чтобы создать в текущем каталоге файл lab1.asm и начать его редактирование, можно набрать:

Общий вид командной строки для запуска:

mcedit [-bcCdfhstVx?] [+число] file

+число переход к указанной числом строке (не ставьте пробел между знаком + и числом)
-b черно-белая цветовая гамма
-c цветовой режим ANSI для терминалов без поддержки цвета
-d отключить поддержку мыши
-V вывести версию программы

mcedit — это полноценный полноэкранный редактор, позволяющий редактировать файлы размером до 64 Мб, с возможностью редактирования бинарных файлов. Основными возможностями являются: копирование блока, перемещение, удаление, вырезка, вставка; отмена; выпадающие меню; вставка файлов; макро-команды; поиск регулярных выражений и их замена; подсветка синтаксиса; перенос по словам; изменяемая длина табуляции; использование перенаправления потоков для применения, например, проверки орфографии при помощи ispell.

Редактор крайне прост в использовании и может быть использован без предварительного изучения. Выпадающее меню вызывается клавишей F9. Список наиболее часто используемых горячих клавиш приведен ниже (Ctrl и Shift обозначают соответствующие клавиши клавиатуры, Meta — условное обозначение для набора мета-клавиш, на современном компьютере это обычно Alt или Esc):

F3 Начать выделение текста. Повторное нажатие F3 закончит выделение
Shift+F3 Начать выделение блока текста. Повторное нажатие F3 закончит выделение
F5 Скопировать выделенный текст
F6 Переместить выделенный текст
F8 Удалить выделенный текст
Meta+l Переход к строке по её номеру
Meta+q Вставка литерала (непечатного символа). См. ниже
Meta+t Сортировка строк выделенного текста
Meta+u Выполнить внешнюю команду и вставить в позицию под курсором её вывод
Ctrl+f Занести выделенный фрагмент во внутренний буфер обмена mc (записать во внешний файл)
Ctrl+k Удалить часть строки до конца строки
Ctrl+n Создать новый файл
Ctrl+s Включить или выключить подсветку синтаксиса
Ctrl+t Выбрать кодировку текста
Ctrl+u Отменить действия
Ctrl+x Перейти в конец следующего слова
Ctrl+y Удалить строку
Ctrl+z Перейти на начало предыдущего слова
Shift+F5 Вставка текста из внутреннего буфера обмена mc (прочитать внешний файл)
Meta+Enter Диалог перехода к определению функции
Meta+- Возврат после перехода к определению функции
Meta++ Переход вперед к определению функции
Meta+n Включение/отключение отображения номеров строк
tab Отодвигает вправо выделенный текст, если выключена опция «Постоянные блоки»
Meta-tab Отодвигает влево выделенный текст, если выключена опция «Постоянные блоки»
Shift+Стрелки Выделение текста
Meta+Стрелки Выделение вертикального блока
Meta+Shift+- Переключение режима отображения табуляций и пробелов
Meta+Shift++ Переключение режима «Автовыравнивание возвратом каретки»

Также работают и привычные по Norton и Volkov Commander’ам клавиши:

Ctrl-Ins копировать
Shift-Ins вставить
Shift-Del вырезать
Ctrl-Del удалить выделенный текст

Выделение мышью также работает на некоторых терминалах.

Клавиши автозавершения (обычно Alt-Tab или Escape Tab) завершают слово, на котором находится курсор, используя ранее применявшиеся в файле слова.

Для задания макроса нажмите Ctrl-R и нажимайте клавиши, которые нужны для воспроизведения в будущем. Повторное нажатие Ctrl-R завершит запись макроса. Затем нажмите на клавишу, на которую хотите повесить этот макрос. Макрос сохранится, когда нажмете Ctrl-A и затем назначенную макросу клавишу. Макрос выполнится по нажатию Meta, Ctrl, или Esc назначенной клавиши, если клавиша не используется другими функциями.

Дополнительную информацию, как обычно в Linux, можно получить при помощи команды man mc .

Правила оформления ассемблерных программ

При наборе программ на языке ассемблера придерживайтесь следующих правил:

  • директивы набирайте большими буквами, инструкции – малыми;
  • пишите текст широко;
  • не выходите за край экрана – его неудобно будет редактировать и печатать;
  • для отступов пользуйтесь табуляцией (клавиша TAB );
  • блоки комментариев задавайте с одинаковым отступом.

Оптимальной считается такая строка:

Количество табуляций перед комментарием определяется длиной аргументов команды и может быть от 1 до 3.

По мере знакомства с синтаксисом языка будут приводиться дополнительные правила.

NASM превращает текст программы в объектный код. Имя программы задается в командной строке. В простейшем случае это выглядит так: nasm hello.asm (расширение указывать обязательно). Текст программы из файла hello.asm преобразуется в объектный код, который запишется в файл hello.o . Т. о. имена всех файлов получаются из имени входного файла и расширения по умолчанию.

NASM всегда создает выходные файлы в текущем каталоге.

NASM не запускают без параметров, т. к. он — всего лишь транслятор, а не интегрированная среда разработки.

Рекомендуется в рабочем каталоге все файлы хранить в определенной иерархии. Например, для первой работы создайте каталог ~/labs/asm/01 .

Для запуска транслятора достаточно набрать nasm hello.asm . При этом вы не увидите никаких сообщений — они появляются только в случае ошибок или предупреждений. При наличии ошибок объектный файл не создается.

Читайте также:
Программы аналитики Вайлдберриз отзывы

Например, для компиляции приведенного выше текста программы «Hello World» необходимо писать:

nasm -f elf hello.asm

Ключ -f указывает транслятору создавать бинарные файлы в формате ELF (если используется 64-битная версия Linux, следует вместо elf указывать elf64 для генерации 64-битного кода).

Более подробно синтаксис командной строки рассмотрен в следующих работах.

Как видно из схемы на рис. [pic:l1], чтобы получить исполняемую программу, объектный файл необходимо передать на обработку компоновщику (или, как его еще называют, линковщику):

ld -o hello hello.o

Ключ -o с последующим значением задает в данном случае имя создаваемого исполняемого файла.

Формат командной строки LD подробно рассмотрен в следующих работах, также его можно увидеть, набрав ld —help . Для получения более подробной информации см. man ld . Запустить на выполнение созданный исполняемый файл можно, набрав в командной строке:

Примечание: в данном случае исполняемый файл hello выполняется из текущего каталога (что обеспечивают символы «./» перед его именем).

Порядок выполнения работы

  1. Создайте в своем домашнем каталоге новый подкаталог с именем asm_01 . Создайте в нем с помощью редактора mcedit текстовый файл lab1.asm , и введите в него текст программы из п. [sec:11], пользуясь правилами оформления ассемблерных программ.
  2. Оттранслируйте полученный текст программы в объектный файл.
  3. Выполните линковку объектного файла и запустите получившийся исполняемый файл.
  4. Измените в тексте программы выводимую на экран строку с Hello world! на свою фамилию. Повторите пункты 2 и 3.
  1. Как обрабатываются блоки текста в редакторе mcedit ?
  2. Как восстановить в mcedit удаленные строки?
  3. Какие основные отличия ассемблерных программ от ЯВУ?
  4. В чем отличие инструкции от директивы?
  5. Каковы правила оформления программ на языке ассемблера?
  6. Каковы этапы получения выполняемого файла?
  7. Каково назначение этапа трансляции?
  8. Каково назначение этапа компоновки?
  9. Какие файлы могут создаваться при трансляции программы, какие из них создаются по-умолчанию?
  10. Каковы форматы файлов для nasm и ld ?

Источник: github.com

Структура программы на языке ассемблера

Глава из книги “Ассемблер для процессоров Intel Pentium”

Опубликовано: 08.04.2006
Версия текста: 1.0

Материал этой главы посвящен вопросам организации и компоновки программного кода на языке ассемблера. Затронуты вопросы взаимодействия различных частей ассемблерной программы, организации сегментов программного кода, данных и стека в контексте различных моделей памяти. Напомню, что мы рассматриваем эти аспекты применительно к макроассемблеру MASM фирмы Microsoft, хотя многие положения действительны и для других компиляторов. Начнем с анализа сегментов. Мы уже сталкивались с этими вопросами в главе 3, сейчас же рассмотрим их более детально.

4.1. Организация сегментов

Для хорошего понимания, как работает программа на ассемблере, нужно очень четко представлять себе организацию сегментов. Применительно к процессорам Intel Pentium термин “сегмент” имеет два значения:

  • Область физической памяти заранее определенного размера. Для 16-разрядных процессоров размер сегмента физической памяти не может превышать 64 Кбайт, в то время как для 32-разрядных может достигать 4 Гбайт.
  • Область памяти переменного размера, в которой могут находиться программный код, данные или стек.

Физический сегмент может располагаться только по адресу, кратному 16, или, как иногда говорят, по границе параграфа. Логические сегменты тесно связаны с физическими. Каждый логический сегмент ассемблерной программы определяет именованную область памяти, которая адресуется селектором сегмента, содержащимся в сегментном регистре. Сегментированная архитектура создает определенные трудности в процессе разработки программ. Для небольших программ, меньших 64 Кбайт, программный код и данные могут размещаться в отдельных сегментах, поэтому никаких особых проблем не возникает.

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

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

Логические сегменты могут содержать три основных компонента программы: программный код, данные и стек. Макроассемблер MASM обеспечивает правильное отображение этих компонентов на физические сегменты памяти, при этом сегментные регистры CS, DS и SS содержат адреса физических сегментов памяти.

4.2. Директивы управления сегментами и моделями памяти макроассемблера MASM

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

  • .DATA (.data) — определяет начало инициализированного сегмента данных с именем _DATA и при наличии предыдущего сегмента завершает его. Этой директиве должна предшествовать директива .MODEL. Сегмент, определенный с атрибутом .DATA, должен содержать только инициализированные данные, то есть имеющие начальные значения, например:

.data val1 DW 11 string1 DB «Text string» byte1 DB ?

  • .DATA? (.data?) — определяет сегмент данных, в котором располагаются неинициализированные данные. При наличии предыдущего сегмента новый сегмент завершает его. Неинициализированные данные могут объявляться в сегменте .DATA? при помощи оператора ?. Преимуществом директивы .DATA? является то, что при ее использовании уменьшается размер исполняемого файла и, кроме того, обеспечивается лучшая совместимость с другими языками. Этой директиве должна предшествовать директива .MODEL. Вот пример использования директивы .DATA?:

.data? DB 5 DUP (?)

  • .CONST (.const) — определяет начало сегмента данных, в котором определены константы. При наличии предыдущего сегмента новый сегмент завершает его. В целях совместимости с другими языками данные должны быть в формате, совместимом с принятыми в языках высокого уровня соглашениями. Сегмент, определенный директивой .CONST, имеет атрибут “только для чтения”. Этой директиве должна предшествовать директива .MODEL.
  • .STACK (.stack) [размер] — определяет начало сегмента стека с указанным размером памяти, который должен быть выделен под область стека. Если параметр не указан, размер стека предполагается равным 1 Кбайт. При наличии предыдущего сегмента новый сегмент завершает его. Этой директиве должна предшествовать директива .MODEL.
  • .CODE (.code) [имя] — определяет сегмент программного кода и заканчивает предыдущий сегмент, если таковой имеется. Необязательный параметр имя замещает имя _TEXT, заданное по умолчанию. Если имя не определено, ассемблер создает сегмент с именем _TEXT для моделей памяти tiny, small, compact и flat или сегмент с именем имя_модуля_TEXT для моделей памяти medium, large и huge. Этой директиве должна предшествовать директива .MODEL, указывающая модель памяти, используемую программой.
  • .MODEL (.model) модель_памяти [,соглашение_о_вызовах] [,тип_ОС] [,параметр_стека] — определяет модель памяти, используемую программой. Директива должна находиться перед любой из директив объявления сегментов. Она связывает определенным образом различные сегменты программы, определяемые ее параметрами tiny, small, compact, medium, large, huge или flat. Параметр модель_памяти является обязательным.

Если разрабатывается процедура для включения в программу, написанную на языке высокого уровня, то должна быть указана та модель памяти, которая используется компилятором языка высокого уровня. Кроме того, модель памяти должна соответствовать режиму работы (типу) процессора. Это имеет значение для плоской модели памяти, которую можно применять только в режимах .386, .486, .586, .686. Модель памяти определяет, какой тип адресации данных и команд поддерживает программа (near или far). Это имеет смысл для команд перехода, вызовов и возврата из процедур. В табл. 4.1 демонстрируются эти особенности.

Модель памяти Адресация кода Адресация данных Операционная система Чередование кода и данных
TINY NEAR NEAR MS-DOS Допустимо
SMALL NEAR NEAR MS-DOS, Windows Нет
MEDIUM FAR NEAR MS-DOS, Windows Нет
COMPACT NEAR FAR MS-DOS, Windows Нет
LARGE FAR FAR MS-DOS, Windows Нет
HUGE FAR FAR MS-DOS, Windows Нет
FLAT NEAR NEAR Windows NT, Windows 2000, Windows XP, Windows 2003 Допустимо

Таблица 4.1. Параметры моделей памяти

Все семь моделей памяти поддерживаются всеми компиляторами MASM, начиная с версии 6.1.

Модель small поддерживает один сегмент кода и один сегмент данных. Данные и код при использовании этой модели адресуются как near (ближние). Модель large поддерживает несколько сегментов кода и несколько сегментов данных. По умолчанию все ссылки на код и данные считаются дальними (far).

Модель medium поддерживает несколько сегментов программного кода и один сегмент данных, при этом все ссылки в сегментах программного кода по умолчанию считаются дальними (far), а ссылки в сегменте данных — ближними (near). Модель compact поддерживает несколько сегментов данных, в которых используется дальняя адресация данных (far), и один сегмент кода с ближней адресацией (near). Модель huge практически эквивалентна модели памяти large.

Читайте также:
Как удалить программу в убунту через терминал

Должен заметить, что разработчик программ может явно определить тип адресации данных и команд в различных моделях памяти. Например, ссылки на команды внутри одного сегмента кода в модели large можно сделать ближними (near). Проанализируем, в каких случаях лучше всего подходят те или иные модели памяти.

Модель tiny работает только в 16-разрядных приложениях MS-DOS. В этой модели все данные и код располагаются в одном физическом сегменте. Размер программного файла в этом случае не превышает 64 Кбайт. С другой стороны, модель flat предполагает несегментированную конфигурацию программы и используется только в 32-разрядных операционных системах.

Эта модель подобна модели tiny в том смысле, что данные и код размещены в одном сегменте, только 32-разрядном. Хочу напомнить, что многие примеры из этой книги разработаны именно для модели flat.

Для разработки программы для модели flat перед директивой .model flat следует разместить одну из директив: .386, .486, .586 или .686. Желательно указывать тот тип процессора, который используется в машине, хотя на машинах с Intel Pentium можно указывать директивы .386 и .486. Операционная система автоматически инициализирует сегментные регистры при загрузке программы, поэтому модифицировать их нужно, только если необходимо смешивать в одной программе 16- и 32-разрядный код. Адресация данных и кода является ближней (near), при этом все адреса и указатели являются 32-разрядными.

Параметр соглашение_о_вызовах используется для определения способа передачи параметров при вызове процедуры из других языков, в том числе и языков высокого уровня (C++, Pascal). Параметр может принимать следующие значения: C, BASIC, FORTRAN, PASCAL, SYSCALL, STDCALL. При разработке модулей на ассемблере, которые будут применяться в программах, написанных на языках высокого уровня, обращайте внимание на то, какие соглашения о вызовах поддерживает тот или иной язык. Более подробно соглашения о вызовах мы будем рассматривать при анализе интерфейса программ на ассемблере с программами на языках высокого уровня.

Параметр тип_ОС равен OS_DOS, и на данный момент это единственное поддерживаемое значение этого параметра.

Наконец, последний параметр параметр_стека устанавливается равным NEARSTACK (регистр SS равен DS, области данных и стека размещаются в одном и том же физическом сегменте) или FARSTACK (регистр SS не равен DS, области данных и стека размещаются в разных физических сегментах). По умолчанию принимается значение NEARSTACK. Рассмотрим примеры использования директивы .MODEL:

.model flat, c

Здесь параметр flat указывает компилятору на то, что будет использоваться 32-разрядная линейная адресация. Второй параметр c указывает, что при вызове ассемблерной процедуры из другой программы (возможно, написанной на другом языке) будет задействован способ передачи параметров, принятый в языке C. Следующий пример:

.model large, c, farstack

Здесь используются модель памяти large, соглашение о передаче параметров языка C и отдельный сегмент стека (регистр SS не равен DS).

.model medium, pascal

В этом примере используются модель medium, соглашение о передаче параметров для Pascal и область стека, размещенная в одном физическом сегменте с данными.

4.3. Структура программ на ассемблере MASM

Программа, написанная на ассемблере MASM, может состоять из нескольких частей, называемых модулями, в каждом из которых могут быть определены один или несколько сегментов данных, стека и кода. Любая законченная программа на ассемблере должна включать один главный, или основной (main), модуль, с которого начинается ее выполнение.

Основной модуль может содержать программные сегменты, сегменты данных и стека, объявленные при помощи упрощенных директив. Кроме того, перед объявлением сегментов нужно указать модель памяти при помощи директивы .MODEL. Поскольку подавляющее большинство современных приложений являются 32-разрядными, то основное внимание в этом разделе мы уделим именно таким программам, хотя не обойдем вниманием и 16-разрядные программы, которые все еще используются. Начнем с 16-разрядных программ.

В следующем примере показана 16-разрядная программа на ассемблере, в которой используются упрощенные директивы ассемблера MASM:

.model small, c ; эта директива указывается до объявления ; сегментов .stack 100h ; размер стека 256 байт .data ; начало сегмента данных . . . ; данные . . . .code ; здесь начинается сегмент программ main: . . . ; команды ассемблера . . . end main end

Здесь оператор end main указывает на точку входа main в главную процедуру. Оператор end закрывает последний сегмент и обозначает конец исходного текста программы. В 16-разрядных приложениях MS-DOS можно инициализировать сегментные регистры так, чтобы они указывали на требуемый логический сегмент данных. Листинг 4.1 демонстрирует это.

Здесь на экран дисплея выводится строка s1. При помощи следующих команд в сегментный регистр DS помещается адрес сегмента данных, указанного директивой .data:

Затем строка s1, адресуемая через регистры DS:DX, выводится на экран с использованием прерывания 9h функции 21h MS-DOS. Попробуйте закомментировать проанализированные две строки кода и посмотреть на результат работы программы.

Для 32-разрядных приложений шаблон исходного текста выглядит иначе:

.model flat .stack .data ; aaiiua .code main: . . . ; команды ассемблера . . . end main end

Основное отличие от предыдущего примера — другая модель памяти (flat), предполагающая 32-разрядную линейную адресацию с атрибутом near.

Как видно из примера, “классический” шаблон 32-разрядного приложения содержит область данных (определяемую директивой .data), область стека (директива .stack) и область программного кода (директива .code). Может случиться так, что 32-разрядному приложению на ассемблере потребуется несколько отдельных сегментов данных и/или кода. В этом случае разработчик может создать их с помощью директивы SEGMENT. Директива SEGMENT определяет логический сегмент и может быть описана следующим образом:

имя SEGMENT список атрибутов . . . имя ENDS

Замечу, что директива SEGMENT может применяться с любой моделью памяти, не только flat. При использовании директивы SEGMENT потребуется указать компилятору на то, что все сегментные регистры устанавливаются в соответствии с моделью памяти flat. Это можно сделать при помощи директивы ASSUME:

ASSUME CS:FLAT, DS:FLAT, SS:FLAT, ES:FLAT, FS:ERROR, GS:ERROR

Регистры FS и GS программами не используются, поэтому для них указывается атрибут ERROR.

Сейчас мы рассмотрим программный код 32-разрядной процедуры на ассемблере (она называется _seg_ex), в которой используются два логических сегмента данных. Процедура выполняет копирование строки src, находящейся в сегменте данных data1, в область памяти dst в сегменте данных data2 и содержит один логический сегмент программного кода (code segment).

Успокою читателей, незнакомых с принципами работы процедур (они рассмотрены далее в книге): в данном случае нас будет интересовать код внутри процедуры _seg_ex (команды, находящиеся между директивами _seg_ex proc и _seg_ex endp). Исходный текст программного кода процедуры _seg_ex представлен в листинге 4.2.

.586 .model flat option casemap:none data1 segment src DB «Test STRING To Copy» len EQU $-src data1 ends data2 segment public dst DB len+1 DUP(‘+’) data2 ends code segment _seg_ex proc assume CS:FLAT,DS:FLAT, SS:FLAT, ES:FLAT, FS:ERROR, GS:ERROR mov ESI, offset data1 mov EDI, offset data2 cld mov CX, len rep movsb mov EAX, offset data2 ret _seg_ex endp code ends end

При использовании модели flat доступ к данным осуществляется по 32-разрядному смещению, поэтому смысл показанных ниже команд, загружающих адреса логических сегментов (а заодно и адреса строк src и dst) в регистры ESI и EDI, думаю, понятен:

mov ESI, offset data1 mov EDI, offset data2

Группа следующих команд выполняет копирование строки src в dst, при этом регистр CX содержит количество копируемых байтов:

cld mov CX, len rep movsb

В регистре EAX возвращается адрес строки-приемника dst. Обращаю внимание читателей на то, что никакой инициализации сегментных регистров не требуется, поскольку это делается при помощи директивы .model flat. Еще один важный момент: программа, использующая модель flat, выполняется в одном физическом сегменте, хотя логических сегментов может быть несколько, как в нашем случае.

Работоспособность процедуры легко проверить, вызвав ее из программы на Visual C++ .NET (нужно только включить объектный файл процедуры в проект приложения). Исходный текст приложения приведен в листинге 4.3.

#include extern «C» char* seg_ex(void); int main(void) < printf(«EXTERNAL MODULE EXAMPLE: %sn», seg_ex()); return 0; >

Здесь процедура seg_ex является внешней, поэтому объявлена как extern.

Результатом выполнения программы будет строка на экране дисплея

EXTERNAL MODULE EXAMPLE: Test STRING To Copy+

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

Источник: www.rsdn.org

Рейтинг
( Пока оценок нет )
Загрузка ...
EFT-Soft.ru