Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Архитектура вычислительных систем и Ассемблер с приложением методических указаний к лабораторным работам. Учебное пособие

.pdf
Скачиваний:
0
Добавлен:
07.09.2026
Размер:
2 Мб
Скачать
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
теряется значение спе-
циальных флагов C0C3.
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]