О р г а н и з а ц и я

п р о г р а м м ы

Организация программы.Сегменты.

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

Сегмент программы описывается директивами SEGMENT и ENDS.
Имя_Сегмента segment readonly Выравнивание Тип Разрядность 'Класс сегмента' ... ... Имя_Сегмента ends

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

Все пять операндов директивы SEGMENT необязательны.

  • READONLY. Если этот операнд присутствует, MASM выдаст сообщение об ошибке на все команды, выполняющие запись в данный сегмент. Другие ассемблеры этот операнд игнорируют.

  • Выравнивание. Указывает ассемблеру и компоновщику, с какого адреса может начинаться сегмент. Значения этого операнда:
    • BYTE — с любого адреса;
    • WORD — с четного адреса;
    • DWORD — с адреса, кратного 4;
    • PARA — с адреса, кратного 16 (граница параграфа);
    • PAGE — с адреса, кратного 256.
    • MEMPAGE — с адреса, кратного 4 Кбайт.
    По умолчанию используется выравнивание по границе параграфа(PARA).

  • Тип. Выбирает один из возможных типов комбинирования сегментов:
    • PRIVATE (значение по умолчанию) — сегмент такого типа не объединяется с другими сегментами.
    • PUBLIC (иногда используется синоним MEMORY) означает, что все такие сегменты с одинаковым именем, но разными классами будут объединены в один;

    • STACK — то же самое, что и PUBLIC, но должен использоваться для сегментов стека, потому что при загрузке программы сегмент, полученный объединением всех сегментов типа STACK, будет использоваться как стек;

    • сегменты типа COMMON с одинаковым именем также объединяются в один, но не последовательно, а по одному и тому же адресу, следовательно, длина суммарного сегмента будет равна не сумме длин объединяемых сегментов, как в случае PUBLIC и STACK, а длине максимального. Таким способом иногда можно формировать оверлейные программы;

    • AT — выражение указывает, что сегмент должен располагаться по фиксированному абсолютному адресу в памяти. Результат выражения, использующегося в качестве операнда для AT, равен этому адресу, деленному на 16.
      Например: segment at 40h — сегмент, начинающийся по абсолютному адресу 0400h.
      Такие сегменты обычно содержат только метки, указывающие на области памяти, которые могут потребоваться программе;

  • Разрядность. Этот операнд может принимать значения USE16 и USE32. Размер сегмента, описанного как USE16, не может превышать 64 Кб, и все команды и адреса в этом сегменте считаются 16-битными. В этих сегментах все равно можно применять команды, использующие 32-битные регистры или ссылающиеся на данные в 32-битных сегментах, но они будут использовать префикс изменения разрядности операнда или адреса и окажутся длиннее и медленнее. Сегменты USE32 могут занимать до 4 Гб, и все команды и адреса в них по умолчанию 32-битные. Если разрядность сегмента не указана, по умолчанию используется USE16 при условии, что перед директивой .MODEL не применялась директива задания допустимого набора команд .386 или старше.

  • Класс сегмента — это любая метка, взятая в одинарные кавычки. Все сегменты с одинаковым классом, даже сегменты типа PRIVATE, будут расположены в исполняемом файле непосредственно друг за другом.

Для обращения к любому сегменту следует сначала загрузить его сегментный адрес (или селектор в защищенном режиме) в какой-нибудь сегментный регистр. Если в программе определено много сегментов, удобно объединить несколько сегментов в группу, адресуемую с помощью одного сегментного регистра:
имя_группы group имя_сегмента...

Операнды этой директивы — список имен сегментов (или выражений, использующих оператор SEG), которые объединяются в группу. Имя группы теперь можно применять вместо имен сегментов для получения сегментного адреса и для директивы ASSUME.
assume     регистр:связь...

Директива ASSUME указывает ассемблеру, с каким сегментом или группой сегментов связан тот или иной сегментный регистр. В качестве операнда «связь» могут использоваться имена сегментов, имена групп, выражения с оператором SEG или слово «NOTHING», означающее отмену действия предыдущей ASSUME для данного регистра. Эта директива не изменяет значений сегментных регистров, а только позволяет ассемблеру проверять допустимость ссылок и самостоятельно вставлять при необходимости префиксы переопределения сегментов, если они необходимы.

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

Модели памяти и упрощенные директивы определения сегментов.

  • .MODEL:
    Задает модель памяти.
    .model модель,язык,модификатор

    Модель — одно из следующих слов:
    • TINY — код, данные и стек размещаются в одном и том же сегменте размером до 64 Кб. Эта модель памяти чаще всего используется при написании на ассемблере небольших программ;

    • SMALL — код размещается в одном сегменте, а данные и стек — в другом (для их описания могут применяться разные сегменты, но объединенные в одну группу). Эту модель памяти также удобно использовать для создания программ на ассемблере;

    • COMPACT — код размещается в одном сегменте, а для хранения данных могут использоваться несколько сегментов, так что для обращения к данным требуется указывать сегмент и смещение (данные дальнего типа);

    • MEDIUM — код размещается в нескольких сегментах, а все данные — в одном, поэтому для доступа к данным используется только смещение, а вызовы подпрограмм применяют команды дальнего вызова процедуры;

    • LARGE и HUGE — и код, и данные могут занимать несколько сегментов;

    • FLAT — то же, что и TINY, но используются 32-битные сегменты, так что максимальный размер сегмента, содержащего и данные, и код, и стек, — 4 Мб.

    Язык — необязательный операнд, принимающий значения C, PASCAL, BASIC, FORTRAN, SYSCALL и STDCALL. Если он указан, подразумевается, что процедуры рассчитаны на вызов из программ на соответствующем языке высокого уровня, следовательно, если указан язык C, все имена ассемблерных процедур, объявленных как PUBLIC, будут изменены так, чтобы начинаться с символа подчеркивания, как это принято в C.

    Модификатор — необязательный операнд, принимающий значения NEARSTACK (по умолчанию) или FARSTACK. Во втором случае сегмент стека не будет объединяться в одну группу с сегментами данных.

    После того как модель памяти установлена, вступают в силу упрощенные директивы определения сегментов, объединяющие действия директив SEGMENT и ASSUME.
    Кроме того, сегменты, объявленные упрощенными директивами, не требуется закрывать директивой ENDS — они закрываются автоматически, как только ассемблер обнаруживает новую директиву определения сегмента или конец программы.

  • .CODE
    Oписывает основной сегмент кода.
    .code имя_сегмента
    эквивалентно
    _TEXT     segment word public ’CODE’
    для моделей TINY, SMALL и COMPACT и
    name_TEXT     segment word public ’CODE’
    для моделей MEDIUM, HUGE и LARGE (name — имя модуля, в котором описан данный сегмент).

  • .STACK
    Oписывает сегмент стека. .stack размер
    эквивалентна директиве
    STACK     segment para public ’stack’.

  • .data
    Описывает обычный сегмент данных и соответствует директиве
    _DATA     segment word public ’DATA’

    .data?
    Описывает сегмент неинициализированных данных:
    _BSS     segment word public ’BSS’

  • .const
    Описывает сегмент неизменяемых данных:
    CONST     segment word public ’CONST’
    В некоторых операционных системах этот сегмент будет загружен так, что попытка записи в него может привести к ошибке.

  • .fardata
    .fardata имя_сегмента
    Сегмент дальних данных:
    имя_сегмента segment para private ’FAR_DATA’
    Доступ к данным, описанным в этом сегменте, потребует загрузки сегментного регистра. Если не указан операнд, в качестве имени сегмента используется FAR_DATA.

    .fardata? имя_сегмента
    Сегмент дальних неинициализированных данных:
    имя_сегмента segment para private ’FAR_BSS’
    Как и в случае с FARDATA, доступ к данным из этого сегмента потребует загрузки сегментного регистра. Если имя сегмента не указано, используется FAR_BSS.

Директивы задания набора допустимых команд.

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

  • .8086 — используется по умолчанию. Разрешены только команды 8086;
  • .186 — разрешены команды 80186;
  • .286 и 286c — разрешены непривилегированные команды 80286;
  • .286p — разрешены все команды 80286;
  • .386 и .386c — разрешены непривилегированные команды 80386;
  • .386p — разрешены все команды 80386;
  • .486 и .486c — разрешены непривилегированные команды 80486;
  • .486p — разрешены все команды 80486;
  • .586 и .586c — разрешены непривилегированные команды P5 (Pentium);
  • .586p — разрешены все команды P5 (Pentium);
  • .686 — разрешены непривилегированные команды P6 (Pentium Pro, Pentium II);
  • .686p — разрешены все команды P6 (Pentium Pro, Pentium II);
  • .8087 — разрешены команды NPX 8087;
  • .287 — разрешены команды NPX 80287;
  • .387 — разрешены команды NPX 80387;
  • .487 — разрешены команды FPU 80486;
  • .587 — разрешены команды FPU 80586;
  • .MMX — разрешены команды IA MMX;
  • .K3D — разрешены команды AMD 3D.

Не все ассемблеры поддерживают каждую директиву, например MASM и WASM не поддерживают .487 и .587, так как их действие не отличается от .387. Естественно, ассемблеры, вышедшие до появления последних процессоров и расширений, не в состоянии выполнять соответствующие им команды.

Если присутствует директива .386 или выше, ассемблер WASM всегда определяет все сегменты как 32-битные.


Программирование на Машинно-Ориентированных Языках.
Преподаватель: Коробов С.А.