1) Если вершина стека st0 - свободна, могу ли я считать что остальные регистры тоже свободны? Или по хорошему надо делать проверку каждого из них? Задача: не затереть имеющиеся значения при вызове другой функции моей же программы. Сохранять всю среду сопроцессора - очень долго. 2) Как узнать за один прием - сколько сводобных регистров у фпу в данный момент? Если я использую fstsw, то биты 11-13 определяют число текущей вершины стека, однако оно становится нулем и в случае когда все регистры свободны и когда - все заняты. Или я что-то не так понимаю?
xRom2 Выяснение числа свободных регистров и их сохранение скорее всего будет дольше полного сохранения состояния сопроцессора. Некоторые компиляторы регистры сопроцессора очищают перед вызовом других функций, а числа с плавающей точкой передают через обычный стек(иногда функции возвращают результат в st0, но остальные регистры сопроцессора при этом свободны). Вообще ситуация такова, что в вызывающей функции проще очистить регистры, чем в вызываемой функции разбираться сколько и чего нужно сохранять.
Ничего не поделаешь - по негласному правилу при вызове функций стек FPU должен быть "девственно чист", иначе хлопот не оберешься. Ну узнаешь, что 2-3, а для нормальной работы функции нужно 4-5, и что будешь делать - писать 5 разных вариантов реализации под разное кол-во свободных регистров ?! Именно поэтому и договариваются, что в общем случае при вызове произвольных\"посторонних" функций стек FPU должен быть чист. И соотв-но отступать от этого правила можно только в случае когда точно знаешь, что вызываемая функция или вообще не юзвет FPU или юзает не более какого-то числа FPU-регистров
leo Сопроцессор ничего про хлопоты не знает. Он только может сепшин доставить на процессор через fwait.
Нет А зачем ? Что значит занят. Если в стеке FPU какие либо значения то это либо "мусор" который можно затереть,либо нужные данные. Если данные нужны то просто сохраняем состояние FPU а затем после наших манипуляций восстанавливаем. При таком подходе нам пофиг сколько регистров "занято" сколько "свободно" . SaveFPU db 110 dup (0) ; Буфер для сохранения состояния FPU. finit ; Инициализация мат.сопроцессора. fsave [SaveFPU] ; Сохраняем состояние FPU. frstor [SaveFPU] ; Восстанавливаем состояние FPU.
klzlk И в чем смысл сего "глубокомысленного" заявления? Во-первых, "сепшн доставляется" не только через fwait, а на любой FPU-команде, следующей за той, которая вызвала ошибку. Поэтому fwait обычно вставляют только в конце fpu-кода, да и то не всегда, а только в сл. когда он заканчивается операцией, которая может вызвать исключение. Во-вторых, тут речь идет не о самом сопроцессоре, а о том как его правильно использовать, чтобы с одной стороны не нарваться на сепшены, а с другой стороны не тормозить исполнние бесконечными fsave\frstor.
Эт правильно, но в данном случае вторая функция - обработчик аппаратного прерывания и может быть вызвана в произвольный момент времени. Я конечно могу обойтись в ней исключительно целочисленной арифметикой, но в плане объема кода - это будет тихий ужас. Да и скорость тоже наверно пострадает. Там-то дело по идее плевое: на вход подаются куча данных с локатора в вещественном виде (qword), мне надо перевести их в целые и выдать основной программе. Ну и сделать небольшие проверки на границы, что тоже удобнее делать через fpu. Кстати как я понимаю команду Bound тоже нельзя использовать под виндой в юзермоде? Ну я то знаю сколько регистров надо в каждом случае, я бы тогда делал fstp в отдельный буфер, выполнял все что мне нужно и загружал бы обратно. Ну типа push тока для фпу Я уже подумал было заменить все увеличивающи или уменьшающие стек сопра команды на макросы и воткнуть в каждый макрос отдельную переменную, которая либо уменьшается командами типа fstp/faddp или увеличивается командами fld/fscale. Идея была хороша, но проблема условных переходов ее похоронила: ассемблер-то тупо подсчитает число команд увеличивающих и умеьшающих стек, но то что часть из них не будет выполнена из-за условных переходов он подсчитать не может. Вобщем сохраняю пока всю среду сопра для надежности.
leo 1. При переполнении стека исключение на процессор не будет доставлено, если в управляющем регистре соответствующие исключения замаскированы. Посему переполнение безопасно. Другое это потеря данных на стеке. 2. Для этого используется сторонний буфер, стек потока(ядро например сохраняет контекст NPX на дне ядерного стека) или есчо какая память, для хранения переменных. Можно и надстройку написать, сохраняющую среду при переполнении стека.
xRom2 Для преобразования из чисел с плавающей точкой в целые сопроцессор не сильно нужен: нужно проверить знак, затем проверить, что значение не уползло за верхнюю или нижнюю границу чисел представимых ввиде целого, иначе просто вытащить мантисту и сдвинуть её - всего 6 ветвей. Для 4х байтовых чисел потребуется порядка 30 команд и 2-3 целочисленных регистра.
xRom2 Вот именно, что "по идее плевое", поэтому и не понятно, почему этим должен заниматься именно обработчик прерывания, а не основная программа (тем более, что в ней fpu-вычисления все равно использ-ся). Сравнение неотрицательных вещ.чисел на больше\меньше\равно с константами проще и быстрее делаются в целочисленном виде, нежели на fpu. С отрицательными чуть сложнее. А для преобразования (валидного\проверенного) double в целое стоит обратить внимание на "замечательную" SSE2-команду cvtsd2si r32,m64, которая не портит никакие xmm регистры
Основная программа - разработка головного офиса конторы, которой по сути плевать на все, ибо оборонка. Формально работает, а исправления могут длиться годами, вот и выкручиваемся как можем. Исходников-то нету, все засекречено, а локаторы-то уже давно новые, интферфейсы другие, данные выдают по другому. а сама программа работает в досе, и максимум что я смог - это трассировать и узнать где и как она забирает данные, и установить обработчик прерывания перед ее запуском, чтобы их туда подсовывать. Спасибо, я совсем упустил из виду возможности расширений. Ща проштудирую, спасибо. А пока сделал просто и надежно: выделил буфер и написал аналог пуша: при вызове он сохраняет в буфер, устаналивает флаг записи и число сохраненных регистров. При попе - все наоборот. Вобщем работает, хотя и не так быстро, но быстрее чем всю среду сохранять.
xRom2 Нету простого и надёжного способа сохранять регистры сопроцессора частично. Последовательность fstp/fld испортит состояние сопроцессора если верхний регистр пуст.
Я проверяю каждый регистр перед сохранением при помощи fxam, а потом просто два бита проверить и все. Единственная возможная проблема будет если пустой регистр окажется между двумя содержащими значения, но пока вроде такого не произошло.