Рассмотрим вопросы организации и компоновки программного кода на языке ассемблера, вопросы взаимодействия различных частей ассемблерной программы, организации сегментов программного кода, данных и стека в контексте различных моделей памяти.
6.1. Организация сегментов
Для хорошего понимания, как работает программа на ассемблере, нужно очень четко представлять себе организацию сегментов. Применительно к процессорам Intel Pentium термин «сегмент» имеет два значения:
– область физической памяти заранее определенного размера. Для 16-разрядных процессоров размер сегмента физической памяти не может превышать 64 Кбайт, в то время как для 32-разрядных может достигать 4 Гбайт;
– область памяти переменного размера, в которой могут находиться программный код, данные или стек.
Физический сегмент может располагаться только по адресу, кратному 16, или, как иногда говорят, по границе параграфа. Логические сегменты тесно связаны с физическими. Каждый логический сегмент ассемблерной программы определяет именованную область памяти, которая адресуется селектором сегмента, содержащимся в сегментном регистре. Сегментированная архитектура создает определенные трудности в процессе разработки программ. Для небольших программ, меньших 64 Кбайт, программный код и данные могут размещаться в отдельных сегментах, поэтому никаких особых проблем не возникает.
01 Введение в модель памяти: стек
Для больших программ, занимающих несколько сегментов кода или данных, необходимо правильно адресовать данные, находящиеся в разных сегментах данных. Кроме того, если программный код находится в нескольких сегментах, то усложняются реализация переходов и ветвлений в программе, а также вызовы процедур. Во всех этих случаях требуется задавать адреса в виде сегмент:смещение.
При использовании 32-разрядного защищенного режима эти проблемы исчезают. Например, в плоской модели памяти (о ней мы поговорим чуть позже) для адресации программного кода и данных достаточно 32-разрядного эффективного адреса внутри непрерывной области памяти.
Логические сегменты могут содержать три основных компонента программы: программный код, данные и стек. Макроассемблер MASM обеспечивает правильное отображение этих компонентов на физические сегменты памяти, при этом сегментные регистры CS, DS и SS содержат адреса физических сегментов памяти.
6.2. Директивы управления сегментами и моделями памяти
макроассемблера MASM
В макроассемблер MASM включены директивы, упрощающие определение сегментов программы и, кроме того, предполагающие те же соглашения, которые используются в языках высокого уровня Microsoft. Упрощенные директивы определения сегментов генерируют необходимый код, указывая при этом атрибуты сегментов и порядок их расположения в памяти. Везде мы будем использовать именно упрощенные директивы определения сегментов, наиболее важные из которых перечислены ниже:
• .DATA (.data) – определяет начало сегмента инициализированных данных с именем DATA и при наличии предыдущего сегмента завершает его. Этой директиве должна предшествовать директива .MODEL. Сегмент, определенный с атрибутом .DATA, должен содержать только инициализированные данные, то есть имеющие начальные значения, например:
024. Модель памяти C++ — Андрей Янковский
string1 db «Text string»
• .DATA? (.data?) – определяет сегмент данных, в котором располагаются неинициализированные данные. При наличии предыдущего сегмента новый сегмент завершает его. Неинициализированные данные могут объявляться в сегменте .DATA? при помощи оператора ?. Преимуществом директивы .DATA?
является то, что при ее использовании уменьшается размер исполняемого файла и, кроме того, обеспечивается лучшая совместимость с другими языками. Этой директиве должна предшествовать директива .MODEL. Вот пример использования директивы .DATA?:
• .CONST (.const) – определяет начало сегмента данных, в котором определены константы. При наличии предыдущего сегмента новый сегмент завершает его. В целях совместимости с другими языками данные должны быть в формате, совместимом с принятыми в языках высокого уровня соглашениями. Сегмент, определенный директивой .CONST, имеет атрибут «только для чтения». Этой директиве должна предшествовать директива .MODEL.
• .STACK (.stack) [размер] – определяет начало сегмента стека с указанным размером памяти, который должен быть выделен под область стека. Если параметр не указан, размер стека предполагается равным 1 Кбайт. При наличии предыдущего сегмента новый сегмент завершает его. Этой директиве должна предшествовать директива .MODEL.
• .CODE (.code) [имя] – определяет сегмент программного кода и заканчивает предыдущий сегмент, если таковой имеется. Необязательный параметр имя замещает имя TEXT, заданное по умолчанию. Если имя не определено, ассемблер создает сегмент с именем TEXT для моделей памяти tiny, small, compact и flat или сегмент с именем имя_модуля_ТЕХТ для моделей памяти medium, large и huge. Этой директиве должна предшествовать директива .MODEL, указывающая модель памяти, используемую программой.
• .MODEL (.model) модель_памяти [,соглашение_о_вызовах] [,тип_ОС] [.параметр_стека] – определяет модель памяти, используемую программой. Директива должна находиться перед любой из директив объявления сегментов. Она связывает определенным образом различные сегменты программы, определяемые ее параметрами tiny, small, compact, medium, 1arge, huge или flat. Параметр модель_памяти является обязательным.
Если разрабатывается процедура для включения в программу, написанную на языке высокого уровня, то должна быть указана та модель памяти, которая используется компилятором языка высокого уровня. Кроме того, модель памяти должна соответствовать режиму работы (типу) процессора. Это имеет значение для плоской модели памяти, которую можно применять только в режимах .386, .486, .586, .686.
Модель памяти определяет, какой тип адресации данных и команд поддерживает программа (near или far). Это имеет смысл для команд перехода, вызовов и возврата из процедур. В табл. 6.1 демонстрируются эти особенности.
В зависимости от «расстояния» переходы бывают трех типов: короткие (short), ближние (near) и дальние (far). Тип перехода задается необязательным параметром инструкции jmp. Если тип не задан, по умолчанию используется тип near.
Максимальная «длина» короткого перехода (то есть максимальное расстояние между текущим и целевым адресом) ограничена. Второй байт инструкции (операнд) содержит только одно 8-разрядное значение, поэтому целевой адрес может быть в пределах от -128 до 127 байтов. При переходе выполняется знаковое расширение 8-разрядного значения и его добавление к текущему значению Е(IР).
«Длина» ближнего перехода (near) зависит только от режима процессора. В реальном режиме меняется только значение IP, поэтому мы можем «путешествовать» только в пределах одного сегмента (то есть в пределах 64 Кб); в защищенном режиме используется EIP, поэтому целевой адрес может быть где угодно в пределах 4 Гб адресного пространства.
Переход типа far модифицирует кроме IP еще и сегментный регистр CS, который используется при вычислении фактического адреса памяти. Поэтому команда перехода должна содержать новое значение CS.
Источник: studfile.net
Модели памяти. Общая структура программы
Для небольших программ с одним сегментом кода, данных и стека типичной является следующая структура:
STACKSG SEGMENT STACK; сегмент стека
DATASG SEGMENT; сегмент данных
CODESG SEGMENT; сегмент команд
ASSUME CS: CODESG, DS: DATASG, SS: STACKSG
S: MOV AX, DATASG
MOV DS, AX; загрузить DS
MOV AX, 4C00h; завершить программу с кодом возврата 0
INT 21h; выполнить прерывание 21h
END S; конец программы, точка входа
Сегмент стека описан с параметром STACK, поэтому в самой программе не нужно загружать сегментный регистр SS. Сегмент стека следует описывать всегда, даже когда не предполагается его использовать, т.к. стек использует операционная система для обработки прерываний. Рекомендуемый размер сегмента стека с запасом составляет 128 байтов. Если программа сама использует стек, то под него отводится столько места, сколько требуется, плюс 128 байтов.
Затем следует сегмент данных, в котором необходимо описать все используемые переменные с помощью директив описания данных. В сегменте кода первой директивой должна идти ASSUME, для задания соответствия между сегментами и соответствующими сегментными регистрами. После директивы ASSUME выполняется инициализация сегментного регистра данных. Затем могут идти все остальные команды программы.
Обязательным последним действием программы в сегменте кода должен быть вызов специальной функции под номером 4C. Функция определена за прерыванием 21h и предназначена для выполнения корректного выхода в операционную систему MS-DOS после завершения работы программы. Для выполнения функции в регистр AH должен быть помещен код 4Ch, а в регистр AL – код выхода. Как правило, успешное завершение программы соответствует коду выхода 0.
В конце программы следует указать директиву END – признак конца программы. Все строки после этой директивы игнорируются. В директиве указывается метка команды, с которой должно начаться выполнение программы (точка входа, в нашем примере эта метка S).
Все предложения должны входить в состав программного сегмента. Размещать их вне сегментов недопустимо. Вне сегментов разрешено указывать только директивы информационного характера (например, EQU). В сегменте данных разрешается размещать команды, а в сегменте команд описания переменных.
Для программ, содержащих по одному сегменту кода, данных и стека, описание директив сегментации можно упростить. Для этого в транслятор TASM введена возможность использования упрощенных директив сегментации (табл. 6).
Табл. 6. Упрощенные директивы определения сегмента.
Режим MASM | Режим IDEAL | Назначение |
.CODE [имя] | CODESEG [имя] | Начало или продолжение сегмента кода. |
.DATA | DATASEG | Начало или продолжение сегмента инициализированных данных. Также используется для определения данных типа NEAR. |
.CONST | CONST | Начало или продолжение сегмента констант модуля |
.DATA? | UDATASEG | Начало или продолжение сегмента неинициализированных данных. Также используется для определения данных типа NEAR. |
.STACK [размер] | STACK [размер] | Начало или продолжение сегмента стека модуля. Параметр [размер] задает размер стека. |
.FARDATA [имя] | FARDATA [имя] | Начало или продолжение сегмента инициализированных данных типа FAR. |
.FARDATA? [имя] | UFARDATA [имя] | Начало или продолжение сегмента неинициализированных данных типа FAR. |
Совместно с упрощенными директивами сегментации используется директива модели памяти MODEL, которая позволяет управлять размещением сегментов и выполняет функции директивы ASSUME. Директива связывает сегменты с сегментными регистрами (но явно инициализировать DS все равно необходимо). Формат директивы следующий:
Параметр «модель памяти» является обязательным. Он определяет модель сегментации памяти: набор и размеры сегментов данных и кода, способ связывания сегментов и сегментных регистров. В табл. 7 приведены возможные значения параметра.
Табл. 7. Модели памяти.
Модель | Тип кода | Тип данных | Описание модели |
TINY | NEAR | NEAR | Код и данные объединены в одну группу с именем DGROUP. |
SMALL | NEAR | NEAR | Код занимает один сегмент, данные объединены в одну группу с именем DGROUP. Модель обычно используют для большинства программ на ассемблере. |
MEDIUM | FAR | NEAR | Код занимает несколько сегментов, по одному на каждый объединяемый модуль. Все ссылки на передачу управления типа FAR. Данные объединены в одной группе; все ссылки на них типа NEAR. |
COMPACT | NEAR | FAR | Код в одном сегменте; ссылка на данные типа FAR |
LARGE | FAR | FAR | Код и данные расположены в нескольких сегментах. |
Параметр «модификатор директивы» уточняет особенности выбранной модели (табл. 8). Для микропроцессоров 386 и выше сегменты могут быть 16 или 32-разрядными. При use16 используется 16-разрядная адресация, а при use32 – 32-разрядная. В первом случае размер сегмента не должен превышать 64 Кбайт, во втором – 4 Гбайт.
Табл. 8. Модификаторы модели памяти.
Модификатор | Назначение |
USE16 | Сегменты выбранной модели 16-разрядные. |
USE32 | Сегменты выбранной модели 32-разрядные. |
DOS | Программа будет работать в MS-DOS. |
Параметры «язык» и «модификатор языка» необязательные и задают особенности вызова процедур при связывании программ на различных языках программирования. При использовании директивы MODEL транслятор делает доступными несколько идентификаторов, к которым можно обращаться, чтобы получить информацию о параметрах модели (табл. 9).
Табл. 9. Идентификаторы, создаваемые директивой MODEL.
Приведем пример использования упрощенных директив сегментации.
MASM; режим работы MASM
MODEL SMALL; модель памяти SMALL
.DATA; начало сегмента данных
.STACK; начало сегмента стека
DB 128 DUP(?); размер стека 128 байт
.CODE; начало сегмента кода
MOV DS, AX; адрес сегмента данных в регистр AX
. ; остальные команды программы
MOV AX, 4C00h; завершить программу с кодом возврата 0
INT 21h; выполнить прерывание 21h
END S; конец программы, точка входа
Следует заметить, что стандартные и упрощенные директивы сегментации не исключают друг друга. Стандартные директивы необходимы для получения полного контроля над размещением сегментов в памяти и их комбинированием с сегментами других модулей (см. раздел 8.2). Упрощенные директивы удобно использовать для простых программ.
Контрольные вопросы
1. В чем заключается идея сегментирования адресов?
2. Что представляет и как описывается программный сегмент?
3. В чем состоит назначение сегментных регистров?
4. Объясните назначение и применение директив SEGMENT и ASSUME.
5. Опишите структуру программы на языке ассемблера.
6. Для чего используется модели памяти?
Источник: studopedia.su
Изучение основных требований к программам на языке ассемблера, моделей памяти и структуры программ, этапов ассемблирования, компоновки и выполнения программ
Указание: сначала прочесть весь текст, иметь представление о содержимом, а затем выполнить задание. На все это — две пары занятий.
РАЗРАБОТА ПРОГРАММ НА ЯЗЫКЕ АССЕМБЛЕРА ДЛЯ СЕМЕЙСТВА
КОМПЬЮТЕРОВ СОВМЕСТНЫХ С IBM PC.
ЦЕЛЬ: показать основные требования к программам на языке ассемблера, модели памяти и структуры программ, этапы ассемблирования, компоновки и выполнения программы.
Язык ассемблера является довольно необычным и сложным, это объясняется сегментной организацией памяти и одновременной адресацией четырех сегментов. В языке имеется более
100 базовых символических команд, в соответствии с которыми ассемблер генерирует более 3800 машинных команд. Кроме того, в распоряжении программиста имеется около 20 директив, предназначенных для определения памяти, инициализации переменных и т.д. Учитывая, что в любой программе имеется множество данных и адресов, то разработка ассемблерных программ превращается в кропотливый труд и требующий много усилий.
Тем не менее затраченное время и усилия на изучение языка ассемблер вознаградится. Ассемблерные программы обычно эффективнее (занимает меньший объем памяти и выполняются быстрее)
эквивалентных программ, транслируемых с тех или иных языков высокого уровня.
2. Организация программного обеспечения.
Программное обеспечение для микрокомпьютеров создаются на языках высокого уровня, таких как Бейсик, Фортран, Паскаль,
Си и др. Однако в тех случая, когда требуется понимание основных принципов работы компьютера, полная реализация вcех его возможностей, использование внешних устройств с наибольшей эффективностью — необходимо программирование на уровне машинных языков (кодов). Но на машинном языке программы не кодируют.
Для этого используется близкий к машинному языку — язык ассемблера, в котором программист пользуется символическими мнемокодами вместо машинных команд и описательными именами для полей данных и адресов памяти.
При программировании на языке ассемблера первое,что должно быть известно об аппаратном обеспечении микрокомпьютера, это какие регистры он имеет и как они используются при выполнении машинных команд, а также какие существуют способы адресации при выборке команд и данных из основной памяти и т.д.
Указанные вопросы рассматриваются на процессоре 8086/88 как типичного и описывается механизм сегментации памяти, являющейся реализацией одной из современных идей организации памяти.
В процессоре 8086/88 основная (оперативная) память разделена на участки по 1 байту (8 бит) каждый (рис.1,a)), которые различаются номерами 0, 1, 2, 3 и т.д.
1 байт номер байта ┌─────────────┐сегмент
Источник: vunivere.ru