Добавил:
ivanov666
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:Архитектура вычислительных систем и Ассемблер с приложением методических указаний к лабораторным работам. Учебное пособие
.pdf
5.3. Команды FPU 211
p
min
2
· 0,012=2
p
−1
min
· 0,12, но представляется это число как
0000 4000...00
в чём можно убедиться при помощи отладчика. Если попытаться прочесть такую
запись как корректное число, то получим нулевой знаковый бит, нулевой смещённый порядок, что соответствует порядку
p
−1
есть
(−1)0· 2
один из видов
min
· 0,012=
специальных значений,
X
min
, что неверно. Денормализованные числа —
4
p
− 1
min
, и мантиссу
0,0100...2,то
которые нельзя раскодировать по общему
правилу.
Представление отрицательных вещественных чисел, в том числе из диапазона
(−X
, 0) , отличается от представления их модулей только знаковым битом.
min
Как было сказано в разделе 2.8.2, в некоторых источниках нормализованной
формой мантиссы считается число, включающее один разряд целой части и 63 бита
дробной [16] или целое беззнаковое 64-битное число с единицей в старшем разряде[87]. Обе этих трактовки приводят к тому же самому двоичному представлению,
что и описанная выше.
Виды значений
Регистры сопроцессора могут содержать следующие значения:
–
вещественные числа — порядок не равен0и не состоит из всех единиц, старший
бит мантиссы равен 1;
денормализованные вещественные числа — порядок и старший бит мантиссы
–
равны 0, но мантисса не равна нулю;
нули (
–
+0,0и−0,0
, в соответствии со знаковым битом) — порядок и мантисса
равны нулю;
бесконечности (
–
как
+infи-inf
+∞и−∞
, в соответствии со знаковым битом, обозначаются
) — порядок состоит из всех единиц, старший бит мантиссы —
единица, остальные равны нулю;
– нечисла двух типов:
сигнальные нечисла (при появлении такого значения в стеке генерируется
–
исключение недействительной операции);
тихие нечисла (не генерируют исключения, но результат вычислений
–
с операндом-нечислом — тоже нечисло):
вещественная неопределённость
–
nan
(знаковый бит не имеет значения) — порядок состоит из всех единиц, два старших бита мантиссы — единицы, остальные нули;
–
другие тихие нечисла — порядок состоит из всех единиц, два старших
бита мантиссы — единицы, остальные — не все нули;
– недопустимые значения.
Начиная с 80387 некоторые ранее недопустимые значения стали нечислами различного типа, и наоборот — многие недопустимые для современных сопроцессоров
значения были корректными нечислами в ранних дискретных моделях.
,

212 Глава 5. Синтаксис и команды GNU Assembler x86
Если один из операндов равен произвольному тихому нечислу, он интерпретируется как вещественная неопределённость. Если вещественная неопределённость
является результатом операции, она может быть равной только описанному значению nan.
5.3.2. Возможные форматы экспорта-импорта
У меня есть что-то, я могу поделиться с тобой.
И это алая дверь.
Б. Б. Гребенщиков. Алая дверь
Регистры сопроцессора могут содержать только вещественные числа расширенной точности или специальные значения формата расширенной точности. Тем
не менее, при выгрузке значений из стека возможно преобразовать их в различные
форматы трёх основных видов — с плавающей запятой, целые двоичные и целые
двоично-десятичные.
Соответственно, при явной загрузке значений из памяти в стек FPU или выполнении вычислений с операндом в памяти возможен экспорт значений из этих
форматов.
Форматы с плавающей запятой
FPU поддерживает импорт и экспорт в стандартные форматы с плавающей запятой одинарной и двойной точности, соответствующие стандарту IEEE 754-2008.
Также возможен импорт-экспорт в нестандартный 80-битный формат двойной
расширенной точности, совпадающий с внутренним представлением чисел FPU.
Конкретный выбор формата определяется суффиксом команды (раздел 5.1.5).
Суффиксsсоответствует одинарной точности (32 бита,
бита,
double),t
— нестандартному формату расширенной точности (80 бит, для
float),l
— двойной (64
GCC этот формат соответствует типу long double).
Если суффикс размера не указан, используется одинарная точность (float).
Целые форматы
Поддерживается импорт и экспорт в двоичные знаковые целые форматы
от двух до восьми байт. При экспорте значение округляется в соответствии с текущими настройками FPU. Отрицательные числа представлены в дополнительном
коде.
Выбор формата определяется суффиксом команды (раздел 5.1.5). Суффикс
s
соответствует короткому целому (16 бит,
и чаще всего
int
). Импорт и экспорт в шестидесятичетырёхбитное целое (то есть
short),l
— длинному (32 бита,
long
суффикс q) в FPU не поддерживается.

5.3. Команды FPU 213
Если суффикс размера не указан, число импортируется или экспортируется
как короткое (16-битное) знаковое целое, в большинстве реализаций языка C++
соответствующее типу short.
Двоично-десятичный формат
FPU поддерживает экспорт и импорт только в один вид двоично-десятичных чисел — это 80-битный упакованный целый BCD-формат в виде значения со
знаком.
Всего такое число занимает десять байт. Старший из них — знаковый. Его
старший бит хранит знак числа — ноль соответствует положительному числу,
единица — отрицательному. Младшие семь бит знакового байта не имеют значения.
Остальные девять байтов содержат модуль числа в виде восемнадцати упакованных
десятичных цифр.
Таким образом, BCD-формат FPU, как и форматы с плавающей запятой, включает два нуля: +0 и −0.
5.3.3. Общие команды
— Статус-кво! — раздался каменный голос судьи.
— Восстановим статус-кво!
А. В. Жвалевский, И. Е. Мытько.
Девять подвигов Сена Аесли. Подвиги 5-9
Исторически набор команд FPU включает команды для начальной настройки
сопроцессора, а также для синхронизации с центральным процессором. В настоящее время синхронизация не требуется.
Сброс FPU
Так как ранние модели сопроцессора FPU были отдельными устройствами,
перед началом работы было необходимо определить, есть ли сопроцессор в системе, и, в случае его наличия, инициализировать сопроцессор. Для инициализации
предназначена команда finit — сброс сопроцессора.
Команда
tw
, а начиная с 80387 —
чением
finit
0x037F
восстанавливает значения по умолчанию в регистрахcw,sw,
fipиfdp
. Управляющее слово инициализируется зна-
(округление к ближайшему, 64-битная мантисса, все исключения
замаскированы — то есть можно спокойно делить на0, брать корень из отрицательных чисел и т. п., но результат будет не числом). Слово состояния обнуляется
(
top =0
изменяются, но все они помечаются пустыми в слове теговtw. Регистры
, никакие флаги исключений не установлены). Регистры данных никак не
fipиfdp
обнуляются.

214 Глава 5. Синтаксис и команды GNU Assembler x86
Современные операционные системы сбрасывают и настраивают сопроцессор
во время загрузки. Выполнять сброс вручную не стоит, так как это может повлиять
на выполнение дальнейших расчётов на ЯВУ.
Ожидание синхронизации
Оригинальный арифметический сопроцессор, выполненный в виде отдельной
микросхемы, мог работать параллельно с центральным процессором. Для их синхронизации использовалась команда
wait/fwait
. Этим мнемоникам соответствует один и тот же машинный код. Эта команда приостанавливает работу либо FPU,
либо центрального процессора — в зависимости от того, какой из них «вырвался
вперёд» — и ждёт отстающего. Кроме того, многие команды управления сопроцессором реализованы в двух вариантах — с ожиданием и без. Мнемоника команды
без ожидания отличается префиксомnпосле префикса FPUf, например,
и
fstsw
. При этом, согласно документации Intel, машинный код команды без префиксаnсостоит из кода команды
В частности, команда
fstsw
wait/fwait
и кода команды с префиксомn.
полностью эквивалентна последовательности
fnstsw
fwait
+ fnstsw.
В современных процессорах параллельная работа команд FPU и основного набора невозможна, так что команда
из двух команд — с префиксом
wait/fwait
n
и без — в настоящее время необходимо выбирать
эквивалентна
nop
. Соответственно,
вариант с префиксом.
5.3.4. Загрузка, выгрузка и пересылка данных
И падут предо мною преграды стекла,
Я смогу без препятствий входить в зеркала!
С. А. Калугин. Скульптор лепит автопортрет
Невозможно напрямую загрузить в стек сопроцессора значение регистра основного процессора или, наоборот, выгрузить значение из стека FPU в регистр CPU.
Также невозможно загрузить в стек произвольную константу. Допускается только
загрузка данных из памяти в вершину стека FPU (имена соответствующих команд
включают суффиксld, от load) и выгрузка вершины стека в память (суффиксst,
от store).
Это необходимо учитывать при написании ассемблерных вставок — все входные и выходные параметры, использующиеся как аргументы инструкций сопроцессора, должны располагаться в памяти. В частности, листинг 5.10 показывает
вычисление значения выражения x + a и запись результата в y.
Для этого в стек FPU загружаетсяx(значениеxоказывается на вершине стека
и получает обозначение
st(0)
), затем к нему прибавляетсяa, затем полученное
значение выгружается в y.

5.3. Команды FPU 215
Листинг 5.10. Вычисление y = x + a вставкой в код C++
1 const volatile doublea=12;
2 double x = 1, y;
3 asm(
4 "fldl␣%[X]\n" // st(0) = %[X]
5 "faddl␣%[A]\n" // st(0) = %[X] + %[A]
6 "fstpl␣%[Y]\n" // %[Y] = %[X] + %[A], стек пуст
7 :[Y]"=m"(y)
8 :[X]"m"(x), [A]"m"(a)
9 :"cc"
10 );
Параметры (как входные
ти. К константе
volatile
, чтобы компилятор не оптимизировал её и разместил в памяти, как
a
[X]и[A]
, так и выходной
(значению входного параметра
[Y]
) расположены в памя-
[A]
) применён модификатор
и необходимо.
В списке перезаписываемых регистров GCC не позволяет описывать элементы
стека сопроцессора. Это не приводит к ошибкам, так как временные переменные
не помещаются в стек сопроцессора.
Если вычисления должны быть не вставкой в код C++, а частью программы на
ассемблере, все числа, включая константы (кроме, может быть, тех, для загрузки
которых есть специальные команды), необходимо разместить в памяти.
Листинг 5.11. Вычисление y = x + a
1 .data
2 a: .double 12
3 x: .double 1
4 y: .double
5 .text
6 fldl x
7 faddl a
8 fstpl y
При выходе из вставки или функции стек сопроцессора должен быть таким же,
как на входе — обычно пустым, если только через него не возвращается значение
(тогда в стеке не должно быть ничего, кроме возвращаемого значения).
Размер числа, загружаемого из памяти или выгружаемого в память, определяется суффиксом команды (таблица 5.1). При отсутствии суффикса целый операнд
загружается из 16 бит или соответственно усекается при экспорте до 16 бит
short
), а вещественный загружается или записывается с одинарной точностью
(
float
, 32 бита). Таким образом, в отличие от команд основного набора, для ко-
(
манд FPU, работающих с данными в памяти, при неуказании суффикса размера
подразумевается суффикс s.

216 Глава 5. Синтаксис и команды GNU Assembler x86
Загрузка данных в стек FPU
Для загрузки данных в стек сопроцессора предназначен набор инструкций
(load, таблица 5.14).
Команды загрузки данных в стек FPU
Таблица 5.14
Команда Действие
fld src Помещает вещественное число src на вершину стека FPU
Если источник src в памяти, его размер определяется суффиксом
команды (по умолчанию — 32 бита, float)
Загрузка целых чисел
fild smem Помещает целое знаковое число smem на вершину стека FPU
Размер источника определяется суффиксом команды
(по умолчанию — 16 бит, short)
fbld smem Помещает целое двоично-десятичное число smem на вершину стека
FPU
Размер источника — 80 бит
Загрузка констант
fldz Загрузка 0
fld1 Загрузка 1
fldpi Загрузка π
fldl2t Загрузка log210
fldl2e Загрузка log2e
fldlg2 Загрузка log102
fldln2 Загрузка ln 2
f*ld
После загрузки значение преобразуется в число с двойной расширенной точностью (80 бит). Ячейка, куда было помещено значение, получает обозначение
st(0)
номера ранее занятых ячеек увеличиваются на единицу (ранее обозначавшееся как
st(0) значение становится st(1) и так далее).
В стек можно поместить значение одного из элементов стека сопроцессора,
значение из памяти или одну из предопределённого набора констант. Невозможно
напрямую загрузить в стек сопроцессора значение регистра основного процессора (если подобная необходимость возникает, это делается в два приёма через
промежуточную переменную в памяти).
,

5.3. Команды FPU 217
Выгрузка данных из стека FPU
Для выгрузки данных из стека сопроцессора предназначен набор инструкций
f*st[p] (таблица 5.15).
Выгружаемое значение — вершина стека FPU
st(0)
. Суффиксp(pop), который
может присутствовать в имени команды после суффиксаst(store), соответствует
выталкиванию
получает обозначение
так что старое значение
полагается после полного имени команды, включающего суффиксы
st(0)
из стека, так что значение, ранее обозначавшееся как
st(0)
. Отсутствие суффиксаpсоответствует копированию,
st(0)
остаётся на вершине стека. Суффикс размера рас-
st
st(1)
и (при
необходимости) p.
Команды выгрузки данных из стека FPU
Таблица 5.15
Команда Действие
fst dest Копирует st(0) в dest
fstp dest Выталкивает st(0) в dest
Приёмник
FPU
Если dest в памяти, его размер определяется суффиксом команды
(по умолчанию — 32 бита, f loat)
fist dmem Копирует st(0) в dmem как целое
fistp dmem Выталкивает st(0) в dmem как целое
Размер приёмника определяется суффиксом команды
(по умолчанию — 16 бит, short)
fbst dmem Копирует st(0) в dmem как двоично-десятичное целое
fbstp dmem Выталкивает st(0) в dmem как двоично-десятичное целое
Размер приёмника — 80 бит
dest
может быть переменной в памяти или пустым регистром
Выгрузка с округлением
,
Ниже показан пример использования команд загрузки и выгрузки (листинг 5.12).
Листинг 5.12. Последовательная загрузка и выгрузка данных
1 double x = 5.7, y;
2 float f;
3 long double L;
4 int i = 10;
5
6
asm( // st: 0 1 2 3
7 "fldl␣%[x]\n" // в стеке: x
8 "fldz\n" // в стеке: 0, x

218 Глава 5. Синтаксис и команды GNU Assembler x86
9 "fld1\n" // в стеке: 1, 0, x
10 "fildl␣%[i]\n" // в стеке: i, 1, 0, x
11 "fstps␣%[f]\n" // в стеке: 1, 0, x f = i
12 "fstpt␣%[L]\n" // в стеке: 0, x L = 1
13 "fstpl␣%[y]\n" // в стеке: x y = 0
14 "fistpl␣%[i]\n" // стек пуст i = x
15
16
17 :[x]"m"(x)
18 :"cc"
19 )
20
21
22
23
24 PRINT(y)
25 PRINT(i)
26 PRINT(f)
27 PRINT(L)
:[y]"=m"(y), [i]"+m"(i), [f]"=m"(f), [L]"=m"(L)
#define PRINT(val) cout << #val << "␣=␣" << val << "␣␣";
PRINT(x)
Вначале в стек сопроцессора последовательно загружаются четыре значения: вещественная переменная двойной точности
x =5,7
, константы — ноль и единица,
а также целое 32-битное число i =10.
После загрузки всех четырёх значений в стеке сопроцессора находятся следую-
щие значения:
st(0) = 10 = i
st(1) = 1
st(2) = 0
st(3) = 5,7=x
Все они внутри стека хранятся в 80-битном вещественном формате.
Затем верхнее значение
, выталкивается из стека и записывается по адресу параметра
10
st(0)
, равное последнему загруженному значению
[f]
, преобразо-
i =
ванное в вещественное число одинарной точности. Новое значение вершины стека
st(0)
после выталкивания — единица. Соответственно, изменятся и обозначения
более глубоких элементов стека: st(1) = 0 и st(2) = x.
Затем новое значение вершины стека, равное единице, выталкивается в пара-
метр
[L]
как 80-битное число (суффиксt—
соответствует типу
[y]
как число двойной точности. Последний оператор выталкивает значение
в
в параметр
5,7
округляется в соответствии с текущими настройками округления, в данном
[i]
long double
как длинное целое (
). Ноль выталкивается из стека и записывается
int
ten bytes
, что для компиляторов GCC
), после чего стек остаётся пустым. Значение
случае — к ближайшему, то есть получим [i]=6.
x

5.3. Команды FPU 219
Соответственно, результат отладочной печати в конце листинга выглядит сле-
дующим образом: x=5.7 y=0 i=6 f=10 L=1.
Используя команды выгрузки или загрузки из памяти, необходимо внимательно
следить за суффиксами команд. В вышеописанном примере команда
fstp %[y]
не вызвала бы ошибки ни во время компиляции, ни во время выполнения, но
переменнаяyполучила бы весьма странное значение. По умолчанию (без указания
суффикса)
fstp
записывает снятое с вершины стека значение как вещественное
число одинарной точности, то есть из 64 бит переменнойyбудут перезаписаны
только первые 32, причём в формате, не соответствующем типу double.
Пересылка данных внутри стека FPU
Для пересылки данных внутри стека сопроцессора можно использовать коман-
ды
fld st(i)
помещения значения
для загрузки копии значения
st(0)
в ранее пустую ячейку
st(i)
в вершину стека и
st(i)
fst st(i)
для
. Кроме того, существует
две специализированные команды (таблица 5.16).
Команды пересылки данных FPU
Таблица 5.16
Команда Действие
fcmovCC %st(i), %st(0) Присваивание st(0) = st(i), если верно CC (e, ne,
b/nae, be/na, ae/nb, a/nbe, u и nu)
fxch Меняет местами st(0) и st(1)
fxch %st(i) Меняет местами st(0) и st(i)
Команда условного копирования
flags
гистра
условия
, а не регистра состоянияsw. При этом для неё доступны не все
CC
, перечисленные в таблице 5.11, и даже не все синонимы доступных
fcmovCC
использует как условие флаги ре-
условий. Используемые условия перечислены в таблице 5.17.
Условия
быть прямо или косвенно (путём сохранения слова состояния
части в
Команда обмена регистров
fcmovCC
flags
) установлены командами сравнения FPU, то есть флаги
включают те биты регистра флагов
fxch
на самом деле не копирует данные, а переиме-
flags
, которые могут
sw
и загрузки его
CF,ZF,PF
новывает регистры, так что её выполнение практически не занимает времени. Так
как большинство команд работает с вершиной стека, переименование регистров
с помощью fxch часто бывает удобным.
.

220 Глава 5. Синтаксис и команды GNU Assembler x86
Условия fcmovCC и их связь с флагами состояния flags
Таблица 5.17
CC
e ZF =1 st(0) = src if equal
ne ZF =0 st(0) = src if not equal
b/nae CF =1 st(0) <src if below / if not above or equal
be/na
nb/ae CF =0 st(0) src if not below / if above or equal
nbe/a
u PF =1 src и st(0) несравнимы if unordered
nu PF =0 src и st(0) сравнимы if not unordered
Условие
(флаги)
CF =1
ZF =1
CF =0
ZF =0
Условие (f*cmp src)
st(0) src if below or equal / if not above
st(0) >src if not below or equal / if above
Загрузка и выгрузка управляющих регистров
Содержимое управляющих регистров также может быть сохранено в памяти
(таблица 5.18).
Команды загрузки и выгрузки управляющих регистров FPU
Таблица 5.18
Команда Действие
fnstcw dmem16 Выгрузка управляющего слова cw в память
fldcw smem16 Загрузка управляющего слова cw из памяти
fnstsw dest16 Выгрузка (разрушающая) слова состояния sw в память или
регистр ax
Управляющее словоcwможно как выгрузить в память по заданному адресу,
так и загрузить из неё.
Слово состояния сопроцессораswможно только сохранить в память, а также
в регистр ax (и только в этот регистр).
После выгрузки слова состоянияswкомандой
fnstsw
теряется значение спе-
циальных флагов C0−C3.
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
