Лекция по программированию

Автор работы: Пользователь скрыл имя, 11 Января 2011 в 19:34, лекция

Краткое описание

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

Содержимое работы - 1 файл

LECTS.DOC

— 893.00 Кб (Скачать файл)

Программирование  сопроцессора

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

  • библиотека эмулятора (потеряла актуальность для процессоров семейства Pentium, но отчасти стала вновь актуальной после появления MMX);
  • библиотека, использующая арифметический сопроцессор;
  • библиотека альтернативной математики.

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

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

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

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

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

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

     Механизм  вызова программ эмуляции основан на использовании прерываний с номерами 34h - 3Eh. Перед тем как оставить программу резидентной, функция _dos_keep восстанавливает содержимое указанных векторов прерываний, делая невозможным доступ резидентной программе к модулям эмулятора. Да и самих этих модулей уже нет в памяти - на их место может быть загружена новая программа.

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

     Ситуация  с драйверами аналогична - драйверы, как правило, составляются на языке  ассемблера, поэтому средства эмуляции библиотек Си недоступны.

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

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

Пример

     Вот пример простой программы, которая  выполняет вычисления по следующей  несложной формуле:

z = x + y;

В этой программе  значения x и y задаются в виде констант.

 

; =====================================================

; Простейшая программа  для работы с арифметическим

; сопроцессором

; Синтаксис MASM

;

; =====================================================

...

.DATA

; Здесь находятся  константы с одинарной

; точностью x и  y

x   real4 1.0

y  real4 2.0

; Резервируем четыре байта для результата

.data?

z  real4  ?

.CODE

...

 

; инициализируем  сопроцессор

finit

; Записываем в  стек численных регистров

; значение x

fld    x

 

; Складываем содержимое  верхушки стека

; с константой y

fadd   y

; Записываем результат  в ячейку z

fstp   z

; синхронизируем  вычисления

fwait

...

 

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

     Вот как выглядит предыдущий пример в  контексте языка Си (в зависимости  от среды программирования может возникнуть необходимость в модификации примера):

 

...

float x=1.0;

float y  = 2.0;

float z;

 
 

asm {

      finit

      fld x

      fadd y

           fstp z

           fwait

}

printf ("x= %f \r\n", x);

printf ("y= %f \r\n", y);

printf ("z= %f \r\n", z);

...

 

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

Обработка особых случаев

     В арифметическом сопроцессоре имеются  два механизма обработки ошибок, возникающих при выполнении различных команд.

     Первый  механизм основан на генерации так  называемого прерывания особого  случая (INT 10h). Это прерывание вырабатывается в том случае, когда происходит какая-нибудь ошибка (например, деление  на нуль) при условии, что соответствующие биты масок особых случаев в регистре управления не установлены.

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

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

  1. сбросить флажки особых случаев в регистре состояния;
  2. выполнить одну или несколько команд сопроцессора;
  3. проверить состояние флажков особых случаев в регистре состояния, в частности, бит суммарной ошибки ES;
  4. если какой-либо флажок установлен, вызвать программу обработки ошибочной ситуации;
  5. в программе обработки ошибочной ситуации можно сбросить флажки особых случаев, записав соответствующее значение в регистр состояния

Особые  значения результатов работы сопроцессора

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

Неточный  результат

     В результате выполнения некоторых операций может возникнуть такая ситуация, когда невозможно точно представить результат. Например, при делении числа 1.0 на 3.0 результатом является бесконечная периодическая двоичная дробь 0.010101.. (или десятичная бесконечная дробь 0.333...). Такое число не может быть представлено точно ни в одном формате вещественных чисел.

Обычно неточный результат является результатом  округления и может не рассматриваться  как ошибка.

Переполнение

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

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

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

Антипереполнение

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

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

Деление на нуль

     Этот  особый случай возникает при попытке  выполнить деление конечного ненулевого числа на нуль.

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

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

Недействительная  операция

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

Информация о работе Лекция по программированию