1) Базовая арифметика FPU. Команды сравнения FPU. Трансцендентные операции FPU. Константы FPU. Команды управления FPU. 2) Программа типа СОМ 3) Программа типа ЕХЕ 4) Передача параметров процедуре через стек. Два основных подхода. Выполнил: Дуйсембеков Досбол
* FPU. FPU (Floating Point Unit) используется для ускорения и упрощения вычислений с плавающей точкой. Сопроцессор (другое название FPU) ориентирован на математические вычисления - в нем отсутствуют операции с битами, зато расширен набор математических ф-ций: тригонометрические, логарифм и т.д. До некоторых моделей FPU исполнялся виде отдельной микросхемы, устанавливаемой опционально. В некоторых 486 и во всех Pentium (и выше), FPU интегрирован в процессор, поэтому можно считать, что он присутствует в каждом более-менее современном компьютере. Начиная с Pentium Pro FPU имеет дополнительные команды, упрощающие сравнение чисел. В любом случае, если FPU недоступен, или не поддерживает какие-либо ф-ции, то возможна их программная эмуляция. Несмотря на объемность данной главы, использовать FPU очень просто, что видно из примера. Но нужно понимать особенности работы сопроцессора, чтобы избежать теоретических ошибок. Программирование совсем старых (до 387) FPU несколько отличалось от последующих версий и рассматриваться не будет т.к это уже не актуально
* Использование FPU Для работы с FPU существует отдельная группа команд, название которых начинается с символа "f". Сопроцессор работает параллельно CPU, и, поэтому, раньше (до 287) необходимо было перед многими командами FPU вставлять команду fwait, чтобы приостановить выполнение до окончания вычислений. В последующих сопроцессорах эта команда встроена в инструкции FPU, поэтому ее использовать не нужно. Но остались также не ожидающие команды, имеющие приставку "n". Пример fsave/fnsave - одно и тоже действие, но первая команда вызовет исключение, если оно произошло до этого, но еще не вызывалось. На самом деле эти две команды - одни и тоже, но перед fsave компилятор добавляет команду fwait. Операндом команд является, как правило, один из регистров st(), или ячейка памяти. Другой операнд обычно подразумевается st(0). Комнады с операндом "память" могут иметь приставку "i", что означает операнд - обычное число, без приставки - в FP формате. Большинство команд не работает с регистрами общего назначения. Для работы с числом из регистра общего назначения можно, например, использовать стек: Пример: сложение eax с ecx, используя FPU Это именно пример использования FPU, для сложения двух регистров есть команда add. * push eax * fild dword ptr [esp] * push ecx * fiadd dword ptr [esp] * pop eax ;очистим стек * fistp dword ptr [esp] * pop eax ;eax= результат Несмотря на то, что многие команды работают только с st(0), можно легко использовать число из другого FP регистра, поменяв значения регистров командой fxch. Некоторые команды имеют суффикс "r", что означает назначение операции - второй операнд. Также, если в команде присутствует суффикс "p", то происходит выталкивание числа из стека сопроцессора. Процедуры, работающие с FPU обычно придерживаются следующего соглашения: На входе и выходе стек сопроцессора пуст. Иногда для возврата FP числа используется st(0). Можно использовать все регистры FPU, за исключением CW. Команд FPU не много, и их легко отличить по первой букве "f", поэтому не будет дано их описание. Для справки о командах, и получения другой разнообразной информации о процессоре, желательно загрузить документацию с сайта Intel. Последний раз она была по адресу: Программирование процессоров Intel описано в документах из раздела "Manuals" ~10 мб, они очень полезны для программиста на Assembler'е. Также описание команд можно найти в пакете MASM32, и в комплекте с компилятором
Команда:КомандаFADD приемник,источник приемник источник Назначение:Назначение СложениеСложение вещественных чисел Команда:FADDP приемник,источник Назначение:Сложение с выталкиванием из стека стек Команда:FIADD источник Назначение:Сложение целых чисел Процессор:Процессор 8087 Команда:FSUB приемник,источник Назначение:Вычитание вещественных чисел Команда:FSUBP приемник,источник Назначение:Вычитание с выталкиванием из стека Команда:FISUB источник Назначение:Вычитание целых чисел Процессор:8087 Базовая арифметика FPU Команда выполняет сложение источника и приемника и помещает результат в приемник. Команда FADDP после этого выталкивает ST(0) из стека (помечает ST(0) как пустой и увеличивает ТОР на один). Команды сложения могут принимать следующие формы:приемникарезультаттогостекаодин Командымогут FADD источник, когда источником является 32- или 64-битная переменная, а приемником — ST(0);когда является&mdash FADD ST(0),ST(n), FADD ST(n),ST(0), FADDP ST(n),ST(0), когда источник и приемник заданы явно в виде регистров FPU; регистр FADD без операндов эквивалентно FADD ST(0),ST(1); FADDP без операндов эквивалентно FADDP ST(1),ST(0); FIADD источник, когда источником является 16- или 32-битная переменная, содержащая целое число, а приемником ST(0).число
* Выполняет вычитание источника из приемника и сохраняет результат в приемнике. Команда FSUBP после этого выталкивает ST(0) из стека (помечает ST(0) как пустой и увеличивает ТОР на один). Команды вычитания могут принимать следующие формы:вычитание этого * FSUB источник, когда источником является 32- или 64-битная переменная, содержащая вещественное число, а приемником ST(0); * FSUB ST(0),ST(n), FSUB ST(n),ST(0), FSUBP ST(n),ST(0), когда источник и приемник заданы явно в виде регистров FPU;регистров * FSUB без операндов эквивалентно FSUB ST(0),ST(1); FSUBP без операндов эквивалентно FSUBP ST(1),ST(0); * FISUB источник, когда источником является 16- или 32-битная переменная, содержащая целое число, а приемником ST(0). * Если один из операндов бесконечность, результат бесконечность соответствующего знака. Если оба операнда бесконечности одного знака, результат не определен (происходит исключение «недопустимая операция»).исключение
Команда:FMUL приемник,источник Назначение:Умножение Умножение вещественных чисел Команда:FMULP приемник,источник Назначение:Умножение с выталкиванием из стека Команда:FIMUL источник Назначение:Умножение целых чисел Процессор:8087 Эти команды эквивалентны FSUB/FSUBP/FISUB во всем, кроме того, что они выполняют вычитание приемника из источника, а не источника из приемника.команд Выполняет умножение источника и приемника и помещает результат в приемник. Команда FMULP после этого выталкивает ST(0) из стека (помечает ST(0) как пустой и увеличивает ТОР на один). Так же как и остальные команды базовой арифметики, команды умножения могут принимать следующие формы:команды FMUL источник, когда источником является 32- или 64-битная переменная, а приемником ST(0); FMUL ST(0),ST(n), FMUL ST(n),ST(0), FMULP ST(n),ST(0), когда источник и приемник заданы явно в виде регистров FPU; FMUL без операндов эквивалентно FMUL ST(0),ST(1); FMULP без операндов эквивалентно FMULP ST(1),ST(0); FIMUL источник, когда источником является 16- или 32-битная переменная, содержащая целое число, а приемником ST(0).
Команда:FDIVR приемник,источник Назначение:Обратное деление вещественных чисел Команда:FDIVRP приемник,источник Назначение:Обратное деление с выталкиванием Команда:FIDIVR источник Назначение:Обратное деление целых чисел Процессор:8087 Выполняет деление приемника на источник и сохраняет результат в приемнике. Команда FDIVP после этого выталкивает ST(0) из стека (помечает ST(0) как пустой и увеличивает ТОР на один). Команды могут принимать следующие формы: FDIV источник, когда источником является 32- или 64-битная переменная, содержащая вещественное число, а приемником ST(0); FDIV ST(0),ST(n), FDIV ST(n),ST(0), FDIVP ST(n),ST(0), когда источник и приемник заданы явно в виде регистров FPU; FDIV без операндов эквивалентно FDIV ST(0),ST(1); FDIVP без операндов эквивалентно FDIVP ST(1),ST(0); FIDIV источник, когда источником является 16- или 32-битная переменная, содержащая целое число, а приемником ST(0). При делении бесконечности на ноль (так же как и на любое число) результат бесконечность, при делении нуля на бесконечность (так же как и на любое число) результат ноль. При делении на ноль нормального числа происходит исключение деления на ноль, а если флаг ZM = 1, в качестве результата записывается бесконечность соответствующего знака.качестве Эти команды эквивалентны FDIV/FDIVP/FIDIV во всем, кроме того, что они выполняют деление источника на приемник, а не приемника на источник.
* Эти команды выполняют деление ST(0) на ST(1) и помещают остаток от деления в ST(0). Деление осуществляется при помощи последовательных вычитаний ST(1) из ST(0), но за один раз выполняется не более 64 таких вычитаний. Если ST(0) не стал меньше ST(1) за это время, говорят, что в ST(0) находится частичный остаток от деления. Если был получен точный остаток, флаг С2 сбрасывается в 0, если частичный устанавливается в 1, так что можно повторять эту команду до обнуления С2. Если вычисление привело к точному остатку, три младших бита частного (то есть числа потребовавшихся вычитаний) сохраняются в С0, С3, С1 (биты 2, 1, 0 соответственно). Например, используя FPREM1, можно уменьшить аргумент тангенса, вычислив его остаток от деления на /4, тогда потребуются младшие три бита частного, чтобы определить, не поменялся ли при этой операции знак тангенса. болеевремяустанавливаетсяможнобитычтобыопределитьменяэтой операции
Команда:FABS Назначение:Найти абсолютное значение Процессор:8087 Команда:FCHS Назначение:Изменить знак Процессор:8087 Команда:FRNDINT Назначение:Округлить до целого Процессор:8087 Команда:FSCALE Назначение:Масштабировать по степеням двойки Процессор:8087 Команда:FXTRACT Назначение:Извлечь экспоненту и мантиссу Процессор:8087 Команда:FSQRT Назначение:Извлечь квадратный корень Процессор:8087 Различие между FPREM и FPREM1 заключается в разном определении значения частного. Сначала эти команды выполняют вещественное деление ST(0) на ST(1), округляют результат (FPREM1 к ближайшему целому, FPREM к нулю), а затем, если частное меньше 64, вычисляяют точный остаток, а если больше частичный.значенияначала Если ST(0) был отрицательным числом переводит его в положительное. Изменяет знак ST(0), превращая положительное число в отрицательное, и наоборот. Округляет значение ST(0) до целого числа в соответствии с режимом округления, заданным битами RC.режим Умножает ST(0) на два в степени ST(1) и записывает результат в ST(0). Значение ST(1) предварительно округляется в сторону нуля до целого числа. Эта команда выполняет действие, обратное FXTRACT. Разделяет число в ST(0) на мантиссу и экспоненту, сохраняет экспоненту в ST(0) и помещает мантиссу в стек, так что после этого ТОР уменьшается на 1, мантисса оказывается в ST(0), а экспонента в ST(1). Вычисляет Вычисляет квадратный корень из ST(0) и сохраняет результат в ST(0).
УсловиеC3C0 st(0)>источник 00 st(0)
* Структура программы типа.СОМ очень проста, а поэтому программист, которому нужно скомпилировать подобного сорта быструю утилиту, может сосредоточиться на логике программы и в минимальной степени вникать проблемы управления транслятором. Однако программы типа.СОМ имеют определенные недостатки, и поэтому наиболее серьезные программы на языке ассемблера в системе DOS написаны так, что их можно преобразовать в файлы типа.ЕХЕ. В то время как программы типа.СОМ имеют существенное ограничение на общий размер (не более 64 Кбайт в сумме на собственно программу, данные и стек), программы типа.ЕХЕ могут быть практически неограниченного размера (в пределах доступной памяти компьютера). Хотя обычный программный загрузчик в системе DOS не использует этого достоинства файлов типа.ЕХЕ, но способность загружать отдельные части больших программ в разные участки памяти, а также благоприятная возможность выделить "чистый код" программы, который может разделяться несколькими задачами, -все это существенно в многозадачных средах типа Мicrosoft Windows. В операционной системе DOS загрузчик всегда вводит программу типа.ЕХЕ в память непосредственно над префиксом сегмента программы, хотя порядок сегментов программы, данных и стека может меняться. Файл типа.ЕХЕ содержит заголовок, или блок управляющей информации характерного формата. Размер заголовка определяется числом команд программы, настраиваемых во время загрузки, но всегда кратен 512 байт. До того как DOS передает управление программе, вычисляются начальные значения регистра сегмента программы CS и указателя команд IP, при этом используются информация о точке входа из заголовка файла типа.ЕХЕ, а также адрес загрузки программы. Эта информация извлекается из оператора ЕND исходного текста одного из модулей программы. Регистры сегмента данных DS и дополнительного сегмента ES указывают на префикс, таким образом, программа может обращаться к указателю блока окружения, хвосту команды и другой полезной информации, находящейся в РSР. Начальное содержимое регистра сегмента стека SS и указателя стека SP устанавливается из заголовка. Эта информация берется из объявления сегмента с атрибутом SТАСК в исходном тексте программы. Пространство памяти, выделяемое под стек, может инициализироваться тем или иным значением в зависимости от определения сегмента стека. Когда программа типа.ЕХЕ завершает свою работу, то она должна вернуть управление DOS с помощью прерывания Int 21Н с функцией 4СН. Программа типа.ЕХЕ, поступающая на вход компоновщика, может состоять из большого числа отдельных объектных модулей. Допускается, чтобы каждый модуль использовал уникальное имя сегмента программы, а процедуры имени атрибут либо NEAR, либо FAR в зависимости от условий определения имени и размеров выполнимой программы. Программист должен заботиться о том, чтобы компонуемые вместе модули содержали только один сегмент с атрибутом SТАСК и только одну точку входа, определяемую директивой ассемблера ЕND. Результатом работы компоновщика является файл с расширением.ЕХЕ, который можно немедленно запустить на выполнение.
* Передача параметров процедуре через стек. Два основных подхода. * Здесь нам хотелось бы рассмотреть подробнее вопрос о передаче параметров через стек. Мы уже останавливались на нем (шаг 36 в разделе "Assembler: 16-битное программирование"). Это не единственный способ передачи параметров, но именно через стек передаются параметры API-функциям, поэтому на это необходимо обратить внимание. Состояние стека до и после вызова процедуры приводится на рисунке 1. шаг 36 * Рисунок 1 демонстрирует стандартный вход в процедуру, практикующийся в таких языках высокого уровня, как Паскаль и Си. * Рис.1. Схема передачи параметров в процедуру (стек растет сверху вниз)
* При входе в процедуру выполняется стандартная последовательность команд: * PUSH EBP * MOV EBP, ESP * SUB ESP, N; N - количество байт для локальных переменных. * Адрес первого параметра определяется как [ЕВР+08Н], что мы уже неоднократно использовали. Адрес первой локальной переменной, если она зарезервирована, определяется как [ЕВР - 4] (имеется в виду переменная типа DWORD). В конце процедуры идут команды: * MOV ESP, ЕВР * POP EBP * RET M * Здесь M - объем, взятый у стека для передачи параметров. * Такого же результата можно добиться, используя команду * ENTER N, 0 * (заменяет последовательность команд: * PUSH EBP * MOV EBP, ESP * SUB ESP * ) в начале процедуры и * LEAVE * (заменяет последовательность команд: * MOV ESP, ЕВР * РОР ЕВР * ) * в конце процедуры. Эти команды появились еще у 286-го процессора и дали возможность несколько оптимизировать транслируемый код программы, особенно в тех случаях, когда речь идет о больших по объему модулях, создаваемых на языке высокого уровня.