Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
VSiS.docx
Скачиваний:
128
Добавлен:
31.03.2015
Размер:
404.2 Кб
Скачать

Чтобы изменить параметры теневых копий общих папок

  1. Откройте узел «Управление компьютером (локальным)».

  2. В дереве консоли щелкните правой кнопкой мыши узел Общие папки, выделите пункт Все задачи и выберите команду Настройка теневых копий. Расположение

  • Управление компьютером/Служебные программы/Общие папки

  • Выберите том, в который требуется внести изменения, и нажмите кнопку Параметры.

  • В диалоговом окне можно изменить параметры, описанные в таблице.

     

    Примечания

    • Для выполнения данной процедуры необходимо входить в группу "Администраторы" на локальном компьютере или получить соответствующие полномочия путем делегирования. Если компьютер присоединен к домену, эту процедуру могут выполнять члены группы "Администраторы домена". 

    • Чтобы открыть управление компьютером, нажмите кнопку Пуск, выберите Панель управления, дважды щелкните Администрирование, а затем Управление компьютером

    Сведения о функциональных различиях

    • Сервер может вести себя по-разному в зависимости от версии и выпуска установленной операционной системы, прав и разрешений вашей учетной записи и настроек меню. Для получения дополнительных сведений см. раздел Просмотр справки в Интернете. 

    18. Принципы представления данных в компьютере

    Данные в компьютере представляются в виде кода, который состоит из единиц и нулей в разной последовательности. Код - набор условных обозначений для представления информации. Кодирование - процесс представления информации в виде кода.

    Схема приема информации и ее передачи:

      Кодирование текстовой информации.

    Кодовая таблица - это внутреннее представление символов в компьютере. Во всем мире в качестве стандарта принята таблица ASCII (American Standard Code for Information Interchange - Американский стандартный код для обмена информацией). Для хранения двоичного кода одного символа выделен 1 байт = 8 бит. Учитывая, что каждый бит принимает значение 0 или 1, количество их возможных сочетаний в байте равно 28 = 256. Значит, с помощью 1 байта можно получить 256 разных двоичных кодовых комбинаций и отобразить с их помощью 256 различных символов. Эти комбинации и составляют таблицу ASCII.   Для сокращения записей и удобства  пользования кодами символов используют шестнадцатеричную систему счисления, состоящую из 16 символов - 10 цифр и 6 латинских букв: A,B,C,D,E,F. Так, буква S будет представлена в шестнадцатеричной системе по сравнению с двоичной более компактным кодом 53.  Стандарт ASCII определяет первые 128 символов от 0 до 127: цифры, буквы латинского алфавита, управляющие символы. Первые 32 символа являются управляющими и предназначены в основном для передачи команд управления. Вторая половина таблицы от 128 до 255 – национальный алфавит.

    Кодирование чисел. Двоичная система счисления обладает такими же свойствами, что и десятичная, только для представления чисел используется не 10 цифр, а всего две. Соответственно и разряд числа называют не десятичным, а двоичным.   Для кодирования числа, участвующего в вычислениях, используется специальная система правил перевода из десятичной системы исчисления в двоичную. В результате число будет записано двоичным кодом, т.е. представлено различным сочетанием всего двух цифр - 0 и 1. 

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

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

    Системы счисления

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

    Примером позиционной формы записи чисел является та, которой мы пользуемся (так называемая арабская форма чисел). Так, в числах 123 и 321 значения цифры 3, например, определяются ее положением в числе: в первом случае она обозначает три единицы (т.е. просто три), а во втором – три сотни (т.е. триста).

    Тогда полное число получается по формуле:

    где l – количество разрядов числа, уменьшенное на 1,

    i – порядок разряда,

    m – основание системы счисления,

    ai – множитель, принимающий любые целочисленные значения от 0 до m-1, и соответствующий цифре i-го порядка числа.

    Например, для десятичного (m = 10) числа 345 его полное значение рассчитывается по формуле:

    3*102 + 4*101 + 5*100 = 345.

    Римские числа являются примером полупозиционной системы образования числа: так, в числах IX и XI знак I обозначает в обоих случаях единицу (признак непозиционной системы), но, будучи расположенным слева от знака X (обозначающего десять), вычитается из десяти, а при расположении справа – прибавляется к десяти. В первом случае полное значение числа равно 9, во втором – 11.

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

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

    Шестнадцатеричная система счисления  используется для кодирования дискретного сигнала, потребителем которого является хорошо подготовленный пользователь – специалист в области информатики. В такой форме представляется содержимое любого файла, затребованное через интегрированные оболочки операционной системы, например, средствами Norton Commander в случае MS DOS. Используемые знаки для представления числа – десятичные цифры от 0 до 9 и буквы латинского алфавита – A, B, C, D, E, F.

    Десятичная система счисления используется для кодирования дискретного сигнала, потребителем которого является так называемый конечный пользователь – неспециалист в области информатики (очевидно, что и любой человек может выступать в роли такого потребителя). Используемые знаки для представления числа – цифры от 0 до 9.

    Соответствие между первыми несколькими натуральными числами всех трех систем счисления представлено в таблице перевода:

    Десятичная

    система

    Двоичная система

    Шестнадцатеричная система

    0

    0

    0

    1

    1

    1

    2

    10

    2

    3

    11

    3

    4

    100

    4

    5

    101

    5

    6

    110

    6

    7

    111

    7

    8

    1000

    8

    9

    1001

    9

    10

    1010

    A

    11

    1011

    B

    12

    1100

    C

    13

    1101

    D

    14

    1110

    E

    15

    1111

    F

    16

    10000

    10

    Для различения систем счисления, в которых представлены числа, в обозначение двоичных и шестнадцатеричных чисел вводят дополнительные реквизиты:

    • для двоичных чисел – нижний индекс справа от числа в виде цифры 2 или букв В либо b (binary – двоичный), либо знак B или b справа от числа. Например, 1010002 = 101000b = 101000= 101000B = 101000b;

    • для шестнадцатеричных чисел - нижний индекс справа от числа в виде числа 16 или букв H либо h (hexadecimal – шестнадцатеричный), либо знак  H или h справа от числа. Например, 3AB16 = 3ABH = 3ABh = 3ABH = 3ABh.

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

    19. Центральный процессор – это мозг компьютера. Его задача – выполнять программы, находящиеся в основной памяти. Он вызывает команды из памяти, определяет их тип, а затем выполняет их одну за другой. Компоненты соединены шиной, представляющей собой набор параллельно связанных проводов, по которым передаются адреса, данные и сигналы управления. Шины могут быть внешними (связывающими процессор с памятью и устройствами ввода-вывода) и внутренними.

    Процессор состоит из нескольких частей. Блок управления отвечает за вызов команд из памяти и определение их типа. Арифметико-логическое устройство выполняет арифметические операции и логические операции.

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

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

    Рассмотрим регистры процессора Intel 486. Этот процессор имеет 32 программно-доступных регистра. Данные регистры можно разделить на две большие группы –16 пользовательских регистров и 16 системных регистров. Как следует из названия, пользовательскими регистры называются потому, что программист может использовать их при написании своих программ. К этим регистрам относятся:

    Регистры общего назначения. Все регистры этой группы позволяют обращаться к своим “младшим” частям (см. рис 3.)

    Рассматривая этот рисунок, заметьте, что использовать для самостоятельной адресации можно только младшие 16 и 8-битные части этих регистров. Старшие 16 бит этих регистров как самостоятельные объекты недоступны. Это сделано, для совместимости с младшими 16-разрядными моделями микропроцессоров фирмы Intel.

    Регистры АХ, ВХ, СХ и DХ конструктивно устроены так, что возможен независимый доступ к их старшей и младшей половинам; можно сказать, что каждый из этих регистров состоит из двух байтовых регистров, обозначаемых AH, AL, BH и т.д.

    Т.о., с каждым из этих регистров можно работать как с единым целым, а можно работать и с его «половинками». Например, можно записать слово в АХ, а затем считать только часть слова из регистра АН или заменить только часть слова в регистре AL и т.д. Такое устройство регистров позволяет использовать их для работы как с числами, так и с символами.

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

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

    eax/ax/ah/al (Accumulator register) — аккумулятор. Применяется для хранения промежуточных данных. В некоторых командах использование этого регистра обязательно;

    ebx/bx/bh/bl (Base register) — базовый регистр. Применяется для хранения базового адреса некоторого объекта в памяти;

    ecx/cx/ch/cl (Count register) — регистр-счетчик. Применяется в командах, производящих некоторые повторяющиеся действия. Его использование зачастую неявно и скрыто в алгоритме работы соответствующей команды.

    edx/dx/dh/dl (Data register) — регистр данных. Так же, как и регистр eax/ax/ah/al, он хранит промежуточные данные. В некоторых командах его использование обязательно; для некоторых команд это происходит неявно.

    Следующие два регистра используются для поддержки так называемых цепочечных операций, то есть операций, производящих последовательную обработку цепочек элементов, каждый из которых может иметь длину 32, 16 или 8 бит:

    esi/si (Source Index register) — индекс источника. Этот регистр в цепочечных операциях содержит текущий адрес элемента в цепочке-источнике;

    edi/di (Destination Index register) — индекс приемника (получателя). Этот регистр в цепочечных операциях содержит текущий адрес в цепочке-приемнике.

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

    esp/sp (Stack Pointer register) — регистр указателя стека. Содержит указатель вершины стека в текущем сегменте стека.

    ebp/bp (Base Pointer register) — регистр указателя базы кадра стека. Предназначен для организации произвольного доступа к данным внутри стека.

    Сегментные регистры. В программной модели микропроцессора имеется шесть сегментных регистров: cs, ss, ds, es, gs, fs. Их существование обусловлено спецификой организации и использования оперативной памяти микропроцессорами Intel. Она заключается в том, что микропроцессор аппаратно поддерживает структурную организацию программы в виде трех частей, называемых сегментами. Соответственно, такая организация памяти называется сегментной.

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

    Сегмент кода. Содержит команды программы. Для доступа к этому сегменту служит регистр cs (code segment register) — сегментный регистр кода. Он содержит адрес сегмента с машинными командами, к которому имеет доступ микропроцессор (то есть эти команды загружаются в конвейер микропроцессора).

    Сегмент данных. Содержит обрабатываемые программой данные. Для доступа к этому сегменту служит регистр ds (data segment register) — сегментный регистр данных, который хранит адрес сегмента данных текущей программы.

    Сегмент стека. Этот сегмент представляет собой область памяти, называемую стеком. Работу со стеком микропроцессор организует по следующему принципу: последний записанный в эту область элемент выбирается первым. Для доступа к этому сегменту служит регистр ss (stack segment register) — сегментный регистр стека, содержащий адрес сегмента стека.

    Дополнительный сегмент данных. Неявно алгоритмы выполнения большинства машинных команд предполагают, что обрабатываемые ими данные расположены в сегменте данных, адрес которого находится в сегментном регистре ds. Если программе недостаточно одного сегмента данных, то она имеет возможность использовать еще три дополнительных сегмента данных. Но в отличие от основного сегмента данных, адрес которого содержится в сегментном регистре ds, при использовании дополнительных сегментов данных их адреса требуется указывать явно с помощью специальных префиксов переопределения сегментов в команде. Адреса дополнительных сегментов данных должны содержаться в регистрах es, gs, fs (extension data segment registers).

    Регистры состояния и управления. В микропроцессор включены несколько регистров (см. рис 3.), которые постоянно содержат информацию о состоянии как самого микропроцессора, так и программы, команды которой в данный момент загружены на конвейер. К этим регистрам относятся:

    регистр флагов eflags/flags;

    регистр указателя команды eip/ip.

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

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

    Системные регистры можно разделить на три группы:

    Регистры управления. В группу регистров управления входят 4 регистра: cr0, cr1, cr2, cr3. Эти регистры предназначены для общего управления системой. Хотя микропроцессор имеет четыре регистра управления, доступными являются только три из них — исключается cr1, функции которого пока не определены (он зарезервирован для будущего использования).

    Регистры системных адресов. Эти регистры еще называют регистрами управления памятью. Они предназначены для защиты программ и данных в мультизадачном режиме работы микропроцессора.

    При работе в защищенном режиме микропроцессора адресное пространство делится на:

    глобальное — общее для всех задач;

    локальное — отдельное для каждой задачи.

    Регистры отладки. Это группа регистров предназначенных для аппаратной отладки. Средства аппаратной отладки впервые появились в микропроцессоре i486. Аппаратно микропроцессор содержит восемь регистров отладки, но реально из них используются только 6.

    Большинство из системных регистров программно доступны.

    Центральный процессор выполняет каждую команду за несколько шагов:

    вызывает следующую команду из памяти и переносит ее в регистр команд;

    меняет положение счетчика команд, который теперь должен указывать на следующую команду;

    определяет тип вызванной команды;

    если команда использует слово из памяти, определяет где находится это слово;

    переносит слово, если это необходимо, в регистр центрального процессора;

    выполняет команду;

    переходит к шагу 1, чтобы начать выполнение следующей команды.

    Такая последовательность шагов является основой работы всех компьютеров.

    Сама возможность написать программу, имитирующую работу ЦП, показывает, что программа не обязательно должна выполняться реальным процессором, относящимся к аппаратному обеспечению. Напротив, вызывать из памяти, определять тип команд и выполнять эти команды может другая программа. Такая программа называется интерпретатором.

    Написание программ-интерпретаторов, которые имитируют работу процессора, широко используется при разработке компьютерных систем.

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

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

    Практически каждое предложение содержит описание объекта, над которым или при помощи которого выполняется некоторое действие. Эти объекты называются операндами.  Их можно определить так:  операнды - это объекты (некоторые значения, регистры или ячейки памяти), на которые действуют инструкции или директивы, либо это объекты, которые определяют или уточняют действие инструкций или директив.  Операнды могут комбинироваться с арифметическими, логическими, побитовыми и атрибутивными операторами для расчета некоторого значения или определения ячейки памяти, на которую будет воздействовать данная команда или директива.  Возможно провести следующую классификацию операндов:  постоянные, или непосредственные, операнды  адресные операнды  перемещаемые операнды  счетчик адреса  регистровый операнд  базовый и индексный операнды  структурные операнды  Записи  Рассмотрим подробнее характеристику операндов из приведенной классификации:  -)Постоянные или непосредственные операнды - число, строка, имя или выражение, имеющие некоторое фиксированное значение. Имя не должно быть перемещаемым, то есть зависеть от адреса загрузки программы в память. К примеру, оно может быть определено операторами equ или =.  -)Адресные операнды - задают физическое расположение операнда в памяти с помощью указания двух составляющих адреса: сегмента и смещения. -)Перемещаемые операнды - любые символьные имена, представляющие некоторые адреса памяти. Эти адреса могут обозначать местоположение в памяти некоторых инструкции (если операнд - метка) или данных (если операнд - имя области памяти в сегменте данных).  Перемещаемые операнды отличаются от адресных тем, что они не привязаны к конкретному адресу физической памяти. Сегментная составляющая адреса перемещаемого операнда неизвестна и будет определена после загрузки программы в память для выполнения.  -)Счетчик адреса - специфический вид операнда. Он обозначается знаком $.  Специфика этого операнда в том, что когда транслятор ассемблера встречает в исходной программе этот символ, то он подставляет вместо него текущее значение счетчика адреса. Значение счетчика адреса, или, как его иногда называют, счетчика размещения, представляет собой смещение текущей машинной команды относительно начала сегмента кода.  В формате листинга счетчику адреса соответствует вторая или третья колонка (в зависимости от того, присутствует или нет в листинге колонка с уровнем вложенности). Если взять в качестве пример любой листинг, то видно, что при обработке транслятором очередной команды ассемблера счетчик адреса увеличивается на длину сформированной машинной команды. Важно правильно понимать этот момент.  К примеру, обработка директив ассемблера не влечет за собой изменения счетчика. Директивы, в отличие от команд ассемблера, - это лишь указания транслятору на выполнение определенных действий по формированию машинного представления программы, и для них транслятором не генерируется никаких конструкций в памяти. -)Регистровый операнд - это просто имя регистра. В программе на ассемблере можно использовать имена всех регистров общего назначения и большинства системных регистров.  -)Базовый и индексный операнды. Этот тип операндов используется для реализации косвенной базовой, косвенной индексной адресации или их комбинаций и расширений.   -)Структурные операнды используются для доступа к конкретному элементу сложного типа данных, называемого структурой.   -)Записи (аналогично структурному типу) используются для доступа к битовому полю некоторой записи. Операнды являются элементарными компонентами, из которых формируется часть машинной команды, обозначающая объекты, над которыми выполняется операция.  В более общем случае операнды могут входить как составные части в более сложные образования, называемые выражениями. 

    20. Директивы Ассемблер имеет ряд операторов, которые позволяютуправлять процессом ассемблирования и формирования листингаЭти операторы называются псевдокомандами илидирективамиОни действуют только в процессе ассемблирования программы и не генерируют машинных кодов. Директивы формирования листинга не очень интересны, так как вывести на печать текст программы не составляет труда для любого современного редактора. Директивы ассемблирования буду изучать и по ходу дела добавлять их в блог. К сожалению, стройного и полного описания директив ассемблирования и примеров, разъясняющих их назначение и разницу между их параметрами, нет ни в одной из имеющихся у меня книг. Более-менее они описаны опять же у старичка Абеля (но книжка все таки старовата) и не плохое есть описание есть у Юрова, но что тот, что другой не приводят ни каких примеров, которые бы пояснили разницу между параметрами директивы SEGMENT и т.д. Так что придется все исследовать самостоятельно. Директива SEGMENT Любые ассемблерные программы содержат, по крайней мере, один сегмент - сегмент кода. В некоторых программах используется сегмент для стековой памяти и сегмент данных для определения данных. Еще раз вспомним, что физически сегмент представляет собой область памяти, занятую командами и (или) данными, адреса которых вычисляются относительно значения в соответствующем сегментном регистре. Стандартные Директивы Сегментации Синтаксическое описание сегмента на ассемблере представляет собой конструкцию, изображенную на рисунке ниже: Важно отметить, что функциональное назначение сегмента несколько шире, чем простое разбиение программы на блоки кода, данных и стека. Сегментация является частью более общего механизма, связанного с концепцией модульного программирования. Она предполагает унификацию оформления объектных модулей, создаваемых компилятором, в том числе с разных языков программирования. Это позволяет объединять программы, написанные на разных языках. Именно для реализации различных вариантов такого объединения и предназначены операнды в директиве SEGMENT. Рассмотрим их подробнее.

    • Атрибут выравнивания сегмента (тип выравнивания) сообщает компоновщику о том, что нужно обеспечить размещение начала сегмента на заданной границе. Это важно, поскольку при правильном выравнивании доступ к данным в процессорах i80х86 выполняется быстрее. Допустимые значения этого атрибута следующие:

      • BYTE — выравнивание не выполняется. Сегмент может начинаться с любого адреса памяти;

      • WORD — сегмент начинается по адресу, кратному двум, то есть последний (младший) значащий бит физического адреса равен 0 (выравнивание на границу слова);

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

      • PARA — сегмент начинается по адресу, кратному 16, то есть последняя шестнадцатеричная цифра адреса должна быть 0h (выравнивание на границу параграфа);

      • PAGE — сегмент начинается по адресу, кратному 256, то есть две последние шестнадцатеричные цифры должны быть 00h (выравнивание на границу 256-байтной страницы);

      • MEMPAGE — сегмент начинается по адресу, кратному 4 Кбайт, то есть три последние шестнадцатеричные цифры должны быть 000h (адрес следующей 4-Кбайтной страницы памяти).

    По умолчанию тип выравнивания имеет значение PARA.

    • Атрибут комбинирования сегментов (комбинаторный тип) сообщает компоновщику, как нужно комбинировать сегменты различных модулей, имеющие одно и то же имя. Значениями атрибута комбинирования сегмента могут быть:

      • PRIVATE — сегмент не будет объединяться с другими сегментами с тем же именем вне данного модуля;

      • PUBLIC — заставляет компоновщик соединить все сегменты с одинаковыми именами. Новый объединенный сегмент будет целым и непрерывным. Все адреса (смещения) объектов, а это могут быть, в зависимости от типа сегмента, команды и данные, будут вычисляться относительно начала этого нового сегмента;

      • COMMON — располагает все сегменты с одним и тем же именем по одному адресу. Все сегменты с данным именем будут перекрываться и совместно использовать память. Размер полученного в результате сегмента будет равен размеру самого большого сегмента;

      • AT xxxx — располагает сегмент по абсолютному адресу параграфа (параграф — объем памяти, кратный 16; поэтому последняя шестнадцатеричная цифра адреса параграфа равна 0). Абсолютный адрес параграфа задается выражением xxx. Компоновщик располагает сегмент по заданному адресу памяти (это можно использовать, например, для доступа к видеопамяти или области ПЗУ), учитывая атрибут комбинирования. Физически это означает, что сегмент при загрузке в память будет расположен, начиная с этого абсолютного адреса параграфа, но для доступа к нему в соответствующий сегментный регистр должно быть загружено заданное в атрибуте значение. Все метки и адреса в определенном таким образом сегменте отсчитываются относительно заданного абсолютного адреса;

      • STACK — определение сегмента стека. Заставляет компоновщик соединить все одноименные сегменты и вычислять адреса в этих сегментах относительно регистра ss. Комбинированный тип STACK (стек) аналогичен комбинированному типу PUBLIC, за исключением того, что регистр ss является стандартным сегментным регистром для сегментов стека. Регистр sp устанавливается на конец объединенного сегмента стека. Если не указано ни одного сегмента стека, компоновщик выдаст предупреждение, что стековый сегмент не найден. Если сегмент стека создан, а комбинированный тип STACK не используется, программист должен явно загрузить в регистр ss адрес сегмента (подобно тому, как это делается для регистра ds), а также установить регистр SP в правильное значение.

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

    • Атрибут класса сегмента (тип класса) — это заключенная в кавычки строка, помогающая компоновщику определить соответствующий порядок следования сегментов при собирании программы из сегментов нескольких модулей. Компоновщик объединяет вместе в памяти все сегменты с одним и тем же именем класса (имя класса, в общем случае, может быть любым, но лучше, если оно будет отражать функциональное назначение сегмента). Типичным примером использования имени класса является объединение в группу всех сегментов кода программы (обычно для этого используется класс 'code'). С помощью механизма типизации класса можно группировать также сегменты инициализированных и не инициализированных данных.

    • Атрибут размера сегмента. Для процессоров i80386 и выше сегменты могут быть 16 или 32-разрядными. Это влияет, прежде всего, на размер сегмента и порядок формирования физического адреса внутри него. Атрибут может принимать следующие значения:

      • USE16 — это означает, что сегмент допускает 16-разрядную адресацию. При формировании физического адреса может использоваться только 16-разрядное смещение. Соответственно, такой сегмент может содержать до 64 Кбайт кода или данных;

      • USE32 — сегмент будет 32-разрядным. При формирования физического адреса может использоваться 32-разрядное смещение. Поэтому такой сегмент может содержать до 4 Гбайт кода или данных.

    Директива ENDS обозначает конец сегмента. Обе директивы SEGMENT и ENDS должны иметь одинаковые имена. Упрощенные Директивы Сегментации Стандартные директивы сегментации изначально использовались для оформления программы в трансляторах MASM и TASM. Поэтому их называют стандартными директивами сегментации. Для простых программ, содержащих по одному сегменту для кода, данных и стека, хотелось бы упростить ее описание. Для этого в трансляторы MASM и TASM ввели возможность использования упрощенных директив сегментации. Но здесь возникла проблема, связанная с тем, что необходимо было как-то компенсировать невозможность напрямую управлять размещением и комбинированием сегментов. Для этого совместно с упрощенными директивами сегментации стали использовать директиву указания модели памяти MODEL, которая частично стала управлять размещением сегментов и выполнять функции директивы ASSUME (поэтому при использовании упрощенных директив сегментации директиву ASSUME можно не использовать).Директива MODEL связывает сегменты, которые в случае использования упрощенных директив сегментации имеют предопределенные имена, с сегментными регистрами (хотя явно инициализировать ds все равно придется). Пример программы с использованием упрощенных директив сегментации:

    ;---------------------------------------- masm ;режим работы TASM: ideal или masm model small ;модель памяти .data ;сегмент данных message db 'Введите две шестнадцатеричные цифры,$' .stack ;сегмент стека db 256 dup ('?') ;сегмент стека .code ;сегмент кода main proc ;начало процедуры main mov ax,@data ;заносим адрес сегмента ;данных в регистр ax mov ds,ax ;ax в ds ;далее текст программы (см. сегмента кода в листинге 3.1 книги) mov ax,4c00h ;пересылка 4c00h в регистр ax int 21h ;вызов прерывания с номером 21h main endp ;конец процедуры main end main ;конец программы с точкой входа main

    Синтаксис директивы MODEL: Модификатор модели памяти

    Значение модификатора

    Назначение

    use16

    Сегменты выбранной модели используются как 16-битные (если соответствующей директивой указан процессор i80386 или i80486)

    use32

    Сегменты выбранной модели используются как 32-битные (если соответствующей директивой указан процессор i80386 или i80486)

    dos

    Программа будет работать в MS-DOS

    Модель памяти является обязательным параметром директивыMODEL. Этот параметр определяет модель сегментации памяти для программного модуля. Модели памяти

    Модель

    Тип кода

    Тип данных

    Назначение модели

    TINY

    near

    near

    Код, данные и стек объединены в одну группу с именем DGROUP и размером до 64Кб. Используется для создания программ формата .com. Некоторые языки эту модель не поддерживают. СS=DS=SS=DGROUP

    SMALL

    near

    near

    Код занимает один сегмент, данные и стек объединены в одну группу с именем DGROUP (хотя для описания могут использоваться разные сегменты). Эту модель обычно используют для большинства программ на ассемблере. CS=_text DS=SS=DGROUP

    MEDIUM

    far

    near

    Код занимает несколько сегментов, по одному на каждый объединяемый программный модуль. Все ссылки на передачу управления — типа far (вызов подпрограмм). Данные и стек объединены в одной группе DGROUP; все ссылки на них — типа near (для доступа к данным испльзуется только смещение). CS=<модуль>_text DS=SS=DGROUP

    COMPACT

    near

    far

    Код находится в одном сегменте, данные и стек в группе DGROUP и могут занимать несколько сегментов, так что для обращения к данным требуется указывать сегмент и смещение ( ссылка на данные — типа far). CS=_text DS=SS=DGROUP

    LARGE

    far

    far

    Код может занимать несколько сегментов, по одному на каждый объединяемый программный модуль. Стек и данные находятся в группе DGROUP. Для ссылки на данные используются дальние указатели -far. CS=<модуль>_text DS=SS=DGROUP

    HUGE

    far

    far

    Тоже что и модель LARGE, что касается TurboAssebmler.

    FLAT

    far

    far

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

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

    Формат директивы (режим MASM)

    Формат директивы (режим IDEAL)

    Назначение

    .CODE [имя]

    CODESEG[имя]

    Начало или продолжение сегмента кода

    .DATA

    DATASEG

    Начало или продолжение сегмента инициализированных данных. Также используется для определения данных типа near

    .CONST

    CONST

    Начало или продолжение сегмента постоянных данных (констант) модуля

    .DATA?

    UDATASEG

    Начало или продолжение сегмента неинициализированных данных. Также используется для определения данных типа near

    .STACK [размер]

    STACK [размер]

    Начало или продолжение сегмента стека модуля. Параметр [размер] задает размер стека

    .FARDATA [имя]

    FARDATA [имя]

    Начало или продолжение сегмента инициализированных данных типа far

    .FARDATA? [имя]

    UFARDATA [имя]

    Начало или продолжение сегмента неинициализированных данных типа far

    Наличие в некоторых директивах параметра [имя] говорит о том, что возможно определение нескольких сегментов этого типа. С другой стороны, наличие нескольких видов сегментов данных обусловлено требованием обеспечить совместимость с некоторыми компиляторами языков высокого уровня, которые создают разные сегменты данных для инициализированных и неинициализированных данных, а также констант. При использовании директивы MODEL транслятор делает доступными несколько идентификаторов, к которым можно обращаться во время работы программы, с тем, чтобы получить информацию о тех или иных характеристиках данной модели памяти. Идентификаторы, создаваемые директивой MODEL

    Имя идентификатора

    Значение переменной

    @code

    Физический адрес сегмента кода

    @data

    Физический адрес сегмента данных типа near

    @fardata

    Физический адрес сегмента данных типа far

    @fardata?

    Физический адрес сегмента неинициализированных данных типа far

    @curseg

    Физический адрес сегмента неинициализированных данных типа far

    @stack

    Физический адрес сегмента стека

    Если вы посмотрите на текст примера, то увидите пример использования одного из этих идентификаторов. Это @data – с его помощью мы получили значение физического адреса сегмента данных нашей программы. Необязательные параметры язык и модификатор языка определяют некоторые особенности вызова процедур. Необходимость в использовании этих параметров появляется при написании и связывании программ на различных языках программирования. Язык — необязательный операнд, принимающий значения C, PASCAL, BASIC, FORTRAN, SYSCALL и STDCALL. Если он указан, подразумевается, что процедуры рассчитаны на вызов из программ на соответствующем языке высокого уровня, следовательно, если указан язык C, все имена ассемблерных процедур, объявленных как PUBLIC, будут изменены так, чтобы начинаться с символа подчеркивания, как это принято в C. Модификатор — необязательный операнд, принимающий значенияNEARSTACK (по умолчанию) или FARSTACK. Во втором случае сегмент стека не будет объединяться в одну группу с сегментами данных. После того как модель памяти установлена, вступают в силу упрощенные директивы определения сегментов, объединяющие действия директив SEGMENT и ASSUME. Кроме того, сегменты, объявленные упрощенными директивами, не требуется закрывать директивой ENDS — они закрываются автоматически, как только ассемблер обнаруживает новую директиву определения сегмента или конец программы. Директива .CODE описывает основной сегмент кода .code имя_сегмента эквивалентно: _TEXT segment word public ’CODE’ для моделей TINY, SMALL и COMPACT name_TEXT segment word public ’CODE’ для моделей MEDIUM, HUGE и LARGE (name — имя модуля, в котором описан данный сегмент). В этих моделях директива .CODE также допускает необязательный операнд — имя определяемого сегмента, но все сегменты кода, описанные так в одном и том же модуле, объединяются в один сегмент с именем NAME_TEXT. Директива .STACK описывает сегмент стека .stack размер эквивалентно: STACK segment para public ’stack’ Необязательный параметр указывает размер стека. По умолчанию он равен 1 Кб. Директива .DATA описывает обычный сегмент данных .data эквивалентно: _DATA segment word public ’DATA’ Директива .DATA? описывает сегмент неинициализированных данных .data? Эквивалентно: _BSS segment word public ’BSS’ Этот сегмент обычно не включается в программу, а располагается за концом памяти, так что все описанные в нем переменные на момент загрузки программы имеют неопределенные значения. Директива .CONST описывает сегмент неизменяемых данных .const Эквивалентно: CONST segment word public ’CONST’ В некоторых операционных системах этот сегмент будет загружен так, что попытка записи в него может привести к ошибке. Директива .FARDATA описывает сегмент дальних данных .fardata имя_сегмента эквивалентно: имя_сегмента segment para private ’FAR_DATA’ Доступ к данным, описанным в этом сегменте, потребует загрузки сегментного регистра. Если не указан операнд, в качестве имени сегмента используется FAR_DATA. Директива .FARDATA? описывает сегмент дальних неинициализированных данных .fardata? имя_сегмента эквивалентно: имя_сегмента segment para private ’FAR_BSS’ Как и в случае с FARDATA, доступ к данным из этого сегмента потребует загрузки сегментного регистра. Если имя сегмента не указано, используется FAR_BSS. Во всех моделях памяти сегменты, представленные директивами .DATA, .DATA?, .CONST, .FARDATA и .FARDATA?, а также сегмент, описанный директивой .STACK, если не был указан модификатор FARSTACK, и сегмент .CODE в модели TINY автоматически объединяются в группу с именем FLAT — для модели памяти FLATили DGROUP — для всех остальных моделей. При этом сегментный регистр DS (и SS, если не было FARSTACK, и CS в модели TINY) настраивается на всю эту группу, как если бы была выполнена команда ASSUME. Порядок загрузки сегментов При использовании стандартных директив сегментациисегменты загружаются в память в том порядке, в котором они описываются в тексте программы. При использовании упрощенных директив сегментации (по умолчанию) устанавливается порядок загрузки сегментов, существующий в MS DOS и часто требуемый для взаимодействия программ на ассемблере с программами на языках высокого уровня. Порядок загрузки сегментов: 1. Все сегменты класса 'CODE'. 2. Все сегменты, не принадлежащие группе DGROUP и классу 'CODE'. 3. Группа сегментов DGROUP: 3.1. Все сегменты класса 'BEGDATA'. 3.2. Все сегменты, кроме классов 'BEGDATA', 'BSS' и 'STACK'. 3.3. Все сегменты класса 'BSS'. 3.4. Все сегменты класса 'STACK'. Знание порядка загрузки сегментов необходимо, например, для вычисления длины программы или адреса ее конца. Для этого надо знать, какой сегмент будет загружен последним, и смещение последнего байта в нем. P.S. В книге Зубкова, порядок загрузки сегментов описан несколько иначе, НО практика показала, что его описание не правильное. При использовании упрощенных директив сегментации, директива .seq, которая, по идее должна устанавливать порядок загрузки сегментов, как они описаны в прорамме, этого не делает! А делает это дериктива.alpha, и то, только при использовании TASM, только в модели small, и при этом она добавляет заголовок в exe файл длиной 600h байт! MASMже, при использовании упрощенных директив сегментации всегда размещает сегменты в соответствии с порядком MS DOS, забивая на директивы .seq и .alpha. И еще с выравниванием сегментов, при использовании упрощенных директив сегментации, происходит какая-то свистопляска :). Все это выявлено опытным путем. Все в следующих сериях супер сериала "Hello World". И так, жем вас у экранов мониторов :)

    21. Способы адресации 

    Способом, или режимом адресации называют процедуру нахождения операнда для выполняемой команды. Если команда использует два операнда, то для каждого из них должен быть задан способ адресации, причем режимы адресации первого и второго операнда могут как совпадать, так и различаться. Операнды команды могут находиться в разных местах: непосредственно в составе кода команды, в каком-либо регистре, в ячейке памяти; в последнем случае существует несколько возможностей указания его адреса. Строго говоря, способы адресации являются элементом архитектуры процессора, отражая заложенные в нем возможности поиска операндов. С другой стороны, различные способы адресации определенным образом обозначаются в языке ассемблера и в этом смысле являются разделом языка. Следует отметить неоднозначность термина "операнд" применительно к программам, написанным на языке ассемблера. Для машинной команды операндами являются те данные (в сущности, двоичные числа), с которыми она имеет дело. Эти данные могут, как уже отмечалось, находиться в регистрах или в памяти. Если же рассматривать команду языка ассемблера, то для нее операндами (или, лучше сказать, параметрами) являются те обозначения, которые позволяют сначала транслятору, а потом процессору определить местонахождение операндов машинной команды. Так, для команды ассемблера

    mov mem, AX

    в качестве операндов используется обозначение ячейки памяти mem, a также обозначение регистра АХ. В то же время, для соответствующей машинной команды операндами являются содержимое ячейки памяти и содержимое регистра. Было бы правильнее говорить об операндах машинных команд и о параметрах, или аргументах команд языка ассемблера.

    По отношению к командам ассемблера было бы правильнее использовать термин "параметры", оставив за термином "операнд" обозначение тех физических объектов, с которыми имеет дело процессор при выполнении машинной команды, однако обычно эти тонкости не принимают в расчет, и говоря об операндах команд языка, понимают в действительности операнды машинных команд. В архитектуре современных 32-разрядных процессоров Intel предусмотрены довольно изощренные способы адресации; в МП 86 способов адресации меньше. В настоящем разделе будут описаны режимы адресации, используемые в МП 86. В книгах, посвященных языку ассемблера, можно встретить разные подходы к описанию способов адресации: не только названия этих режимов, но даже и их количество могут различаться. Разумеется, способов адресации существует в точности столько, сколько их реализовано в процессоре; однако, режимы адресации можно объединять в группы по разным признакам, отчего и создается некоторая путаница, в том числе и в количестве имеющихся режимов. Мы будем придерживаться распространенной, но не единственно возможной терминологии.

    Регистровая адресация. Операнд (байт или слово) находится в регистре. Этот способ адресации применим ко всем программно-адресуемым регистрам процессора.

    inc СН ;Плюс 1 к содержимому СН

    push DS ;DS сохраняется в стеке

    xchg ВХ,ВР ;ВХ и ВР обмениваются содержимым

    mov ES, АХ ;Содержимое АХ пересылается в ES

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

    mov АН, 40h ;Число 40h загружается в АН

    mov AL,'*' ;Код ASCII символа "*' загружается в AL

    int 21h ;Команда прерывания с аргументом 21h

    limit = 528 ;Число 528 получает обозначение limit

    mov CX,limit ;Число, обозначенное limit, загружается в СХ

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

    ; Сегмент данных

    mes db "Урок 1' ;Строка символов

    ;Сегмент команд

    mov DX,offset mes ;Адрес строки засылается в DX

    В приведенном примере относительный адрес строки mes, т.е. расстояние в байтах первого байта этой строки от начала сегмента, в котором она находится, заносится в регистр DX. Прямая адресация памяти. Адресуется память; адрес ячейки памяти (слова или байта) указывается в команде (обычно в символической форме) и поступает в код команды:

    ;Сегмент данных

    meml dw 0 ;Слово памяти содержит 0

    mem2 db 230 ;Байт памяти содержит 230

    ;Сегмент команд inc meml ;Содержимое слова meml увеличивается на 1

    mov DX, meml ; Содержимое слова с именем menu загружается в DX

    mov AL,mem2 ; Содержимое байта с именем mem2 загружается в АL

    Сравнивая этот пример с предыдущим, мы видим, что указание в команде имени ячейки памяти обозначает, что операндом является содержимое этой ячейки; указание имени ячейки с описателем offset - что операндом является адрес ячейки. Прямая адресация памяти на первой взгляд кажется простой и наглядной. Если мы хотим обратиться, например, к ячейке meml, мы просто указываем ее имя в программе. В действительности, однако, дело обстоит сложнее. Вспомним, что адрес любой ячейки состоит из двух компонентов: сегментного адреса и смещения. Обозначения meml и mem2 в предыдущем примере, очевидно, являются смещениями. Сегментные же адреса хранятся в сегментных регистрах. Однако сегментных регистров четыре: DS, ES, CS и SS. Каким образом процессор узнает, из какого регистра взять сегментный адрес, и как сообщить ему об этом в программе? Процессор различает группу кодов, носящих название префиксов. Имеется несколько групп префиксов: повторения, размера адреса, размера операнда, замены сегмента. Здесь нас будут интересовать префиксы замены сегмента. Команды процессора, обращающиеся к памяти, могут в качестве первого байта своего кода содержать префикс замены сегмента, с помощью которого процессор определяет, из какого сегментного регистра взять сегментный адрес. Для сегментного регистра ES код префикса составляет 26h, для SS - 361i, для CS - 2Eh. Если префикс отсутствует, сегментный адрес берется из регистра DS (хотя для него тоже предусмотрен свой префикс). Если в начале программы с помощью директивы assume указано соответствие сегменту данных сегментного регистра DS

    assume DS:data

    то команды обращения к памяти транслируются без какого-либо префикса, а процессор при выполнении этих команд берет сегментный адрес из регистра DS. Если в директиве assume указано соответствие сегмента данных регистру ES

    assume ES:data

    (в этом случае сегмент данных должен располагаться перед сегментом команд), то команды обращения к полям этого сегмента транслируются с добавлением префикса замены для сегмента ES. При этом предложения программы выглядят обычным образом; в них по-прежнему просто указываются имена полей данных, к которым производится обращение. Однако в ряде случаев префикс замены сегмента должен указываться в программе в явной форме. Такая ситуация возникает, например, если данные расположены в сегменте команд, что типично для резидентных обработчиков прерываний. Для обращения к таким данным можно, конечно, использовать регистр DS, если предварительно настроить его на сегмент команд, но проще выполнить адресацию через регистр CS, который и так уже настроен должным образом. Если в сегменте команд содержится поле данных с именем mem, то команда чтения из этого поля будет выглядеть следующим образом:

    mov AX,CS:mem

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

    mov AX,0B800h ;Сегментный адрес видеобуфера

    mov ES,AX ;Отправим его в ES

    mov byte ptr ES:0, ' ! ' ;Отправим символ на 1-е знакоместо экрана

    mov byte ptr ES:2, ' ! ' ;Отправим символ на 2-е знакоместо экрана

    Настроив регистр ES на сегментный адрес видеобуфера BS00h, мы пересылаем код знака "!" сначала по относительному адресу 0 (в самое начало видеобуфера, в байт со смещением 0), а затем на следующее знакоместо, имеющее смещение 2 (в нечетных байтах видеобуфера хранятся атрибуты символов, т.е. цвет символов и фона под ними). В обеих командах необходимо с помощью обозначения ES: указать сегментный регистр, который используется для адресации памяти. Встретившись с этим обозначением, транслятор включит в код команды префикс замены сегмента, в данном случае код 26h. В приведенном примере мы снова столкнулись с использованием атрибутивного оператора byte ptr, который позволяет в явной форме задать размер операнда. Однако если раньше этот оператор использовался, чтобы извлечь байт из данного, объявленного, как слово, то здесь его назначение иное. Транслятор, обрабатывая команду

    mov byte ptr ES:0, ' ! '

    не имеет возможности определить размер операнда-приемника. Разумеется, видеобуфер, как и любая память, состоит из байтов, однако надо ли рассматривать эту память, как последовательность байтов или слов? Команда без явного задания размера операнда

    mov ES:0, ' ! '

    вызовет ошибку трансляции, так как ассемблер не сможет определить, надо ли транслировать это предложение, как команду пересылки в видеобуфер байта 21h, или как команду пересылки слова 0021h. Между прочим, на первый взгляд может показаться, что в обсуждаемой команде достаточно ясно указан размер правого операнда, так как символ (в данном случае "!") всегда занимает один байт. Однако транслятор, встретив обозначение "!", сразу же преобразует его в код ASCII этого символа, т.е. в число 21h, и уже не знает, откуда это число произошло и какой размер оно имеет. Стоит еще отметить, что указание в команде описателя word ptr

    mov word ptr ES:0,'!'

    не вызовет ошибки трансляции, но приведет к неприятным результатам. В этом случае в видеобуфер будет записано слово 002lh, которое заполнит байт 0 видеобуфера кодом 21h, а байт 1 кодом 00h. Однако атрибут 00h обозначает черный цвет на черном фоне, и символ на экране виден не будет (хотя и будет записан в видеобуфер). При желании можно избавиться от необходимости вводить описатель размера операнда. Для этого надо пересылать не непосредственное данное, а содержимое регистра:

    mov AL,'!' mov ES:0,AL

    Здесь операндом-источником служит регистр AL, размер которого (1 байт) известен, и размер операнда-приемника определять не надо. Разумеется, команда

    mov ES:0,AX

    заполнит в видеобуфере не байт, а слово. Для адресации к видеобуферу в вышеприведенном примере использовался сегментный регистр дополнительных данных ES. Это вполне естественно, так как обычно регистр DS служит для обращения к полям данных программы, а регистр ES как раз и предназначен для адресации всего остального. Однако при необходимости можно было воспользоваться для записи в видеобуфер регистром DS:

    mov AX,0B800h ;Сегментный адрес

    mov DS,AX ; видеобуфера в DS

    mov byte ptr DS:0, ' ! ' ;Символ в видеобуфер

    Любопытно, что хотя обозначение DS: здесь необходимо, транслятор не включит в код команды префикс замены сегмента, так как команда без префикса выполняет адресацию по умолчанию через DS. Если, однако, по умолчанию выполняется адресация через DS, то нельзя ли опустить в последней команде обозначение сегментного регистра? Нельзя, так как обозначение DS: число указывает, что число является не непосредственным операндом, а адресом операнда. Команда (неправильная)

    mov 6,10

    должна была бы переслать число 10 в число 6, что, разумеется, лишено смысла и выполнено быть не может. Команда же

    mov DS:6,10

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

    Регистровая косвенная (базовая и индексная). Адресуется память (байт или слово). Относительный адрес ячейки памяти находится в регистре, обозначение которого заключается в прямые скобки. В МП 86 косвенная адресация допустима только через регистры ВХ, ВР, SI и DI. При использовании регистров ВХ или ВР адресацию называют базовой, при использовании регистров SI или DI - индексной. Преобразуем приведенный выше пример, чтобы продемонстрировать использование косвенной адресации через регистр.

    mov AX,0B800h ;Сегментный адрес

    mov ES,AX ; видеобуфера в ES

    mov BX,2000 ;Смещение к середине экрана

    mov byte ptr ES:[ВХ], ' ! ' ;Символ на экран

    Настроив ES, мы засылаем в регистр ВХ требуемое смещение (для разнообразия к середине видеобуфера, который имеет объем точно 4000 байт), и в последней команде засылаем код в видеобуфер с помощью косвенной базовой адресации через пару регистров ES:BX с указанием замены сегмента (ES:). Если косвенная адресация осуществляется через один из регистров ВХ, SI или DI, то подразумевается сегмент, адресуемый через DS, поэтому при адресации через этот регистр обозначение DS: можно опустить:

    mov AX,0B800h ;Сегментный адрес

    mov DS,AX ;видеобуфера в DS

    mov BX,2000 ;Смещение к середине экрана

    mov byte ptr [ВХ], ' ! ' ;Символ на экран

    Кстати, этот фрагмент немного эффективнее предыдущего в смысле расходования памяти. Из-за отсутствия в коде последней команды префикса замены сегмента он занимает на 1 байт меньше места. Регистры ВХ, SI и DI в данном применении совершенно равнозначны, и с одинаковым успехом можно воспользоваться любым из них:

    mov D1,2000 ;Смещение к середине экрана

    mov byte ptr [DI] , ' ! ' ;Символ на экран

    Не так обстоит дело с регистром ВР. Этот регистр специально предназначен для работы со стеком, и при адресации через этот регистр в режимах косвенной адресации подразумевается сегмент стека; другими словами, в качестве сегментного регистра по умолчанию используется регистр SS. Обычно косвенная адресация к стеку используется в тех случаях, когда необходимо обратиться к данным, содержащимся в стеке, без изъятия их оттуда (например, если к эти данные приходится считывать неоднократно). Пример такого рода операций будет приведен при обсуждении следующего режима адресации. Сравнивая приведенные выше фрагменты программ, можно заметить, что использование базовой адресации, на первый взгляд, снижает эффективность программы, так как требует дополнительной операции - загрузки в базовый регистр требуемого адреса. Действительно, базовая адресация в нашем примере не оправдана - в случае прямого обращения к памяти вместо двух команд

    mov BX,2000 ;Смещение к середине экрана

    mov byte ptr ES: [BX] , ' ! ' ;Символ на экран

    можно использовать одну

    mov byte ptr ES:2000,'!' ;Выведем символ в середину экрана

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

    Регистровая косвенная адресация со смещением (базовая и индексная). Адресуется память (байт или слово). Относительный адрес операнда определяется, как сумма содержимого регистра BX, BP, SI или DI и указанной в команде константы, иногда называемой смещением. Смещение может быть числом или адресом. Так же, как и в случае базовой адресации, при использовании регистров BX, SI и DI подразумевается сегмент, адресуемый через DS, а при использовании ВР подразумевается сегмент стека и, соответственно, регистр SS. Рассмотрим применение косвенной адресации со смещением на примере прямого вывода в видеобуфер.

    mov AX,0B800h ;Сегментный адрес

    mov ES,AX ;видеобуфера в ES

    mov DI, 80*2*24 ;Смещение к нижней строке экрана

    mov byte ptr ES: [DI] ,'О' ;Символ на экран

    mov byte ptr ES:2[DI],'К' ;Запишем символ в следующую позицию

    mov byte ptr ES:4[DI],' ! ' ;Запишем символ в следующую позицию

    В этом примере в качестве базового выбран регистр DI; в него заносится базовый относительный адрес памяти, в данном случае смещение в видеобуфере к началу последней строки экрана. Модификация этого адреса с целью получить смещение по строке экрана осуществляется с помощью констант 2 и 4, которые при вычислении процессором исполнительного адреса прибавляются к содержимому базового регистра DI. Иногда можно встретиться с альтернативными обозначениями того же способа адресации, которые допускает ассемблер. Вместо, например, 4[ВХ] можно с таким же успехом написать [ВХ+4], 4+[ВХ] или [ВХ]+4. Такая неоднозначность языка ничего, кроме путаницы, не приносит, однако ее надо иметь в виду, так как с этими обозначениями можно столкнуться, например, рассматривая текст деассемблированной программы. Рассмотрим теперь пример использования базовой адресации со смещением при обращении к стеку:

    ;Основная программа

    push DS ;В стек загружаются значения

    push ES ;трех регистров,

    push SI ;передаваемых подпрограмме

    call mysub ;Вызов подпрограммы mysub,

    ;использующей эти параметры

    ;Подпрограмма mysub

    mov BP,SP ;Поместим в ВР текущий адрес вершины стека

    mov АХ,2[ВР], ;Читаем в АХ последний параметр (SI)

    mov ВХ,4[ВР] ;Читаем в ВХ предыдущий параметр (ES)

    mov CX,6[BP] ;Читаем в СХ первый параметр (DS)

    Здесь продемонстрирован классический прием чтения содержимого стека без извлечения из него этого содержимого. После того, как основная, программа сохранила в стеке три параметра, которые потребуются подпрограмме, командой call вызывается подпрограмма mysub. Эта команда сохраняет в стеке адрес возврата (адрес следующего за call предложения основной программы) и осуществляет переход на подпрограмму. Состояние стека при входе в подпрограмму приведено на рис. 2.15.

    Рис.2.15. Состояние стека после загрузки в него трех параметров и перехода на подпрограмму

    Если бы подпрограмма просто сняла со стека находящиеся там параметры, она первым делом изъяла бы из стека адрес возврата, и лишила бы себя возможности вернуться в основную программу (подробнее вопросы вызова подпрограммы и возврата из нее будут обсуждаться в последующих разделах). Поэтому в данном случае вместо команд pop удобнее воспользоваться командами mov. Подпрограмма копирует в ВР содержимое трех параметров и перехода на мое SP и использует затем этот адрес в качестве базового, модифицируя его с помощью базовой адресации со смещением. Кстати, мы опять сталкиваемся здесь с той весьма обычной ситуацией, когда программист не имеет возможности обращаться по наглядным символическим адресам, которых в стеке, естественно, нет, а вынужден определять "вручную" смещения к интересующим его элементам стека. При этом необходимо учесть и алгоритм выполнения команды call, которая, сохраняя в стеке адрес возврата в основную программу, смещает указатель стека еще на одно слово. В нашем фрагментарном примере мы не рассматриваем вопрос возврата в основную программу. Вдумчивый читатель мог также усомниться в правильности или, лучше сказать, в разумности текста подпрограммы. Ведь перенося параметры из стека в регистры общего назначения, подпрограмма затирает их исходное содержимое. Если же они не содержали ничего нужного, то ими можно было воспользоваться для передачи параметров в подпрограмму, а не связываться с мало наглядными операциями со стеком. Действительно, ради краткости мы опустили операции, практически необходимые в любой подпрограмме - сохранение в стеке (опять в стеке!) тех регистров, которые будут использоваться в подпрограмме. Кстати, это относится и к регистру ВР. В реальной подпрограмме эти действия следовало выполнить, что привело бы к изменению смещений при регистре ВХ, которые приняли бы значения (с учетом сохранения 4 регистров) 10, 12 и 14. Во всех приведенных выше примерах регистр использовался для хранения базового адреса, а смещение, если оно требовалось, указывалось в виде константы. Возможна и обратная ситуация, когда в качестве смещения выступает адрес массива, а в регистре находится индекс адресуемого элемента в этом массиве. Рассмотрим относительно реальный пример такого рода. Пусть нам надо заполнить массив из 10000 слов натуральным рядом чисел. Зарезервируем в сегменте данных место под этот массив, а в сегменте команд организуем цикл занесения в последовательные слова массива ряда нарастающих чисел. Нам придется воспользоваться несколькими новым командами (inc, add и loop), которые в дальнейшем будут рассмотрены более подробно.

    ;Сегмент данных

    array dw 10000

    ;Сегмент команд

    mov SI, 0 ;Начальное значение индекса элемента в массиве

    mov АХ, 0 ;Первое число-заполнитель

    mov СХ,10000;Число шагов в цикле (всегда в СХ)

    fill: mov array[SI],AX ;Занесение числа в элемент массива

    inc AX ;Инкремент числа-заполнителя

    add SI,2 ;Смещение в массиве к следующему слову

    loop fill ;Возврат на метку fill (СХ раз)

    Цикл начинается с команды, помеченной меткой fill (правила образования имен меток такие же, как и для имен полей данных). В этой команде содержимое АХ, поначалу равное 0, переносится в ячейку памяти, адрес которой вычисляется, как сумма адреса массива array и содержимого индексного регистра SI, в котором в первом шаге никла тоже 0. В результате в первое слово массива заносится 0. Далее содержимое регистра АХ увеличивается на 1, содержимое регистра SI - на 2 (из-за того, что массив состоит из слов), и командой loop осуществляется переход на метку fill, после чего тело цикла повторяется при новых значениях регистров АХ и SI. Число шагов в цикле, отсчитываемое командой loop, определяется исходным содержимым регистра СХ. Базово-индексная адресация. Адресуется память (байт или слово). Относительный адрес операнда определяется, как сумма содержимого следующих пар регистров:

    [ВХ] [SI] (подразумевается DS:[BX][SI])

    [ВХ][DI] (подразумевается DS:[BX][DI])

    [ВР] [SI] (подразумевается SS:[BP][SI])

    [ВР] [DI] (подразумевается SS:[BP][DI])

    Это чрезвычайно распространенный способ адресации, особенно, при работе с массивами. В нем используются два регистра, при этом одним из них должен быть базовый (ВХ или ВР), а другим - индексный (SI или DI). Как правило, в одном из регистров находится адрес массива, а в другом - индекс в нем, при этом совершенно безразлично, в каком что. Трансформируем предыдущий пример, введя в него более эффективную базово-индексную адресацию.

    ;Сегмент данных

    array dw 10000

    ;Сегмент команд

    mov BX,offset array ;Базовый адрес массива в

    ;базовом регистре

    mov SI, 0 ;Начальное значение индекса

    ;элемента в массиве

    mov АХ, 0 ;Первое число-заполнитель

    mov CX,10000 ;Число шагов в цикле

    fill: mov [BX][SI],AX ;Отправим число в массив

    inc AX ;Инкремент числа-заполнителя

    add SI, 2 ;Смещение в массиве к следующему слову

    loop fill ;На метку fill (CX раз)

    Повышение эффективности достигается за счет того, что команда занесения числа в элемент массива оказывается короче (так как в нее не входит адрес массива) и выполняется быстрее, так как этот адрес не надо каждый раз считывать из памяти. Базово-индексная адресация со смещением. Адресуется память (байт или слово). Относительный адрес операнда определяется как сумма содержимого двух регистров и смещения. Это способ адресации является развитием предыдущего. В нем используются те же пары регистров, но полученный с их помощью результирующий адрес можно еще сместить на значение указанной в команде константы. Как и в случае базово-индексной адресации, константа может представлять собой индекс (и тогда в одном из регистров должен содержаться базовый адрес памяти), но может быть и базовым адресом. В последнем случае регистры могут использоваться для хранения составляющих индекса. Приведем формальный пример рассматриваемого режима адресации. Пусть в сегменте данных определен массив из 24 байтов, в котором записаны коды латинских и русских символов верхнего ряда клавиатуры:

    sims db "QWERTYUIOP{}'

    db "ЙЦУКЕНПШЦЗХЪ'

    Последовательность команд

    mov BX,12 ;Число байтов в строке

    mov SI, 6

    mov DL,syms[BX][SI]

    загрузит в регистр DL элемент с индексом 6 из второго ряда, т.е. код ASCII буквы Г. Тот же результат можно получить, загрузив в один из регистров не индекс, а адрес массива:

    mov BX, off set sym

    mov SI,6

    mov DL, 12 [BX] [SI]

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

    22. Процесс подготовки и отладки программы на языке ассемблера включает этапы подготовки исходного текста, трансляции, компоновки и отладки.

    Подготовка исходного текста программы выполняется с помощью любого текстового редактора. Файл с исходным текстом должен иметь расширение .asm. При выборе редактора для подготовки исходного текста программы следует иметь в виду, что многие текстовые процессоры (например, Microsoft Word) добавляют в выходной текст служебную информацию о формате (размер страниц, тип шрифта и др.). Поэтому следует воспользоваться редактором, выводящим в выходной файл «чистый текст», без каких-либо управляющих символов. К таким редакторам относятся, например, широко распространенные у нас Лексикон, Norton Editor, редактор edit.com, входящий в состав операционной системы MS-DOS и др. Поскольку при интенсивном программировании часто приходится переносить фрагменты текста из одной программы в другую, желательно, чтобы редактор имел средства деления экрана на независимые окна. Таким свойством обладают программы Лексикон (10 окон) и Norton Editor (2 окна, что в большинстве случаев вполне достаточно) но не обладает редактор edit.com. При работе в операционной среде какой-либо системы программирования, например, Borland C, можно воспользоваться редактором, встроенным в эту среду.

    Трансляция исходного текста программы состоит в преобразовании строк исходного языка в коды машинных команд и выполняется с помощью транслятора с языка ассемблера (т.е. с помощью программы ассемблера). Можно воспользоваться макроассемблером корпорации IBM, пакетами TASM корпорации Borland или MASM корпорации Microsoft. Трансляторы различных разработчиков имеют некоторые различия, в основном, в части описания макросредств. Однако, входной язык (т.е. мнемоника машинных команд и других операторов и правила написания предложений ассемблера) для всех ассемблеров одинаков. В результате трансляции образуется объектный файл с расширением .obj.

    Компоновка объектного файла выполняется с помощью программы компоновщика (редактора связей). Эта программа получила такое название потому, что ее основное назначение — подсоединение к файлу с основной программой файлов с подпрограммами и настройка связей между ними. Однако компоновать необходимо даже простейшие программы, не содержащие подпрограмм. Дело в том, что у компоновщика есть и вторая функция — изменение формата объектного файла и преобразование его в выполнимый файл, который может быть загружен в оперативную память и выполнен. Файл с программой компоновщика обычно имеет имя link.exe, хотя это может быть и не так. Например, компоновщик корпорации Borland назван tlink.exe. Компоновщик желательно брать из одного пакета с ассемблером. В результате компоновки образуется загрузочный, или выполнимый файл с расширением .exe.

    Известно, что программы, выполняемые под управлением MS-DOS, могут принадлежать к одному из двух типов, которым соответствуют расширения имен выполнимых файлов .com и .exe. Основное различие этих программ заключается в том, что программы типа .com состоят из единственного сегмента, в котором размещаются программные коды, данные и стек, а в программах типа .exe для собственно программы, данных и стека предусматриваются отдельные сегменты. В результате размер программы типа .com не может превысить 64 Кбайт, а размер программы типа.exe практически не ограничен, так как в нее может входить любое число сегментов команд и данных. При разработке программных продуктов чаще используется формат .exe, как обладающий большей универсальностью. Однако некоторые специфические программы (обработчики аппаратных прерываний, резидентные программы) обычно пишутся в формате .com.

    Если исходная программа написана в формате .com, то после трансляции и компоновки обычным образом ее надо преобразовать в файл типа .com, для чего используется внешняя команда DOS exe2bin. Позже этот вопрос будет рассмотрен подробнее.

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

    При использовании пакета корпорации Borland следует взять «турбо-дебаггер» td.exe, при трансляции и компоновке программы с помощью пакета корпорации Microsoft — отладчик Codeview (файл cv.exe). Можно воспользоваться и отладчиком debug.exe, входящим в состав операционной системы MS-DOS, хотя с ним не очень удобно работать, так как эта программа не обеспечивает привычный для сегодняшнего пользователя полноэкранный интерфейс с системой меню. В настоящей книге будет предполагаться, что читатель выполняет предложенные примеры с помощью пакета корпорации Microsoft (транслятор masm.exe, компоновщик link.exe, отладчик cv.exe). Кстати, никакие другие программы, входящие в этот пакет, нам не понадобятся.

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

    masm /z /zi /n p,p,p;

    (Еще раз напоминаем, что как в тексте программы на языке ассемблера, так и при вводе с клавиатуры командных строк можно с равным успехом использовать и прописные, и строчные буквы.)

    Ключ /Z разрешает вывод на экран строк исходного текста программы, в которых ассемблер обнаружил ошибки (без этого ключа поиск ошибок пришлось бы проводить по листингу трансляции).

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

    Ключ /N подавляет вывод в листинг перечня символических обозначений в программе, от чего несколько уменьшается информативность листинга, но существенно сокращается его размер.

    Стоящие далее параметры определяют имена файлов: исходного (p.asm), объектного (p.obj) и листинга (p.lst). Точка с запятой подавляет формирование файла p.crf с перекрестными ссылками, который нам не нужен.

    Строка вызова компоновщика может иметь следующий вид:

    link /co p,p;

    Ключ /CO передает в загрузочный файл символьную информацию, позволяющую отладчику CV выводить на экран полный текст исходной программы, включая метки, комментарии и проч. Стоящие далее параметры обозначают имена модулей: объектного (p.obj) и загрузочного (p.exe). Символ точки с запятой подавляет формирование файла с листингом компоновки (p.map) и использование библиотечного файла с объектными модулями подпрограмм.

    Как уже отмечалось, компоновщик создает загрузочный, готовый к выполнению, модуль в формате .exe. Запуск подготовленной программы p.exe осуществляется командой

    p.exe

    или просто

    p

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

    cv p

    где p — имя файла с отлаживаемой программой. По умолчанию отладчик загружает файл с расширением .exe. В процессе работы отладчик использует также файл с исходным модулем p.asm, поэтому перед отладкой не следует переименовывать ни исходный, ни выполнимый файлы. Правила работы с отладчиком CodeView Microsoft и его основные команды будут описаны в одной из следующих статей.

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

    @ echo off

    masm /z /zi /n p,p,p;

    if errorlevel 1 goto err

    link /co p,p;

    goto end

    :err

    echo Ошибка трансляции !

    goto fin

    :end

    echo Конец сеанса

    :fin

    echo .

    Если трансляция прошла успешно, на экран выводится сообщение «Конец сеанса» и создаются файлы p.obj, p.exe и p.lst; при наличии ошибок в программе на экран будут выведены строки листинга с ошибками, а за ними сообщение «Ошибка трансляции!». Поскольку в приведенном командном файле имя программы указано в явной форме, текущий вариант программы всегда должен иметь одно и то же имя (в приведенном примере — p.asm). Конечно, можно составить командный файл, воспринимающий текущее имя программы в качестве параметра при его запуске, однако практика показывает, что такая методика замедляет работу и иногда приводит к драматическим ошибкам. Удобнее отлаживать программу всегда под одним и тем же именем, а после отладки записывать в специально созданный каталог архива под уникальным именем (например, под именем, соответствующим номеру примера в книге: 01-01.asm).

    Создайте файл с программой из примера 1.1. Подготовьте программу к выполнению. Не смущайтесь, увидев на экране сообщение

    LINK : warning L4021: no stack segment

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

    Изучите листинг трансляции (файл p.lst). На рис. 2.1 приведен несколько сокращенный текст листинга трансляции примера 1.1, из которого были удалены комментарии. Обратите внимание на следующие моменты.

    Команды программы имеют различную длину и располагаются в памяти вплотную друг к другу. Так, первая команда mov AX,text, начинающаяся с байта 0000 сегмента, занимает 3 байта. Соответственно, вторая команда начинается с байта 0003. Вторая команда имеет длину 2 байта, поэтому третья команда начинается с байта 0005 и т.д.

    Предложения программы с операторами segment, assume, end не транслируются в какие-либо машинные коды и не находят отражения в памяти. Они нужны лишь для передачи транслятору служебной информации.

    Транслятор не мог полностью определить код команды mov AX,text. В этой команде в регистр AX засылается адрес сегмента text. Однако этот адрес станет известен лишь в процессе загрузки выполнимого файла программы в память. поэтому в листинге на месте этого адреса стоит прочерк.

    Данные, введенные нами в программу, также оттранслировались: вместо символов текста в загрузочный файл попадут коды ASCII этих символов. Подробнее о кодах ASCII будет рассказано в статье 7.

    Из листинга трансляции легко определить размер отдельных составляющих программы и всей программы в целом. В нашем случае длина сегмента команд (в который вошли и данные) равна всего 2Ah = 42 байт (символ «h» обозначает, что число записано в шестнадцатиричной системе счисления).

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

  • Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]