Вначале напишем ассемблерный код, решающий поставленную задачу, а затем разберём, что делает каждая его строчка.
Code
var
S: Single;
I: Integer;
begin
S := 8.7654321;
I := 10;
asm
FLD [S] // 1
FIMUL [I] // 2
SUB ESP, 4 // 3
FNSTCW [ESP] // 4
FNSTCW [ESP + 2] // 5
FWAIT // 6
OR [ESP], $0F00 // 7
FLDCW [ESP] // 8
FRNDINT // 9
FLDCW [ESP + 2] // 10
ADD ESP, 4 // 11
FSTP [S] // 12
end;
...
end
1. Команда FLD записывает вещественное число (в нашем случае -- 32-разрядное, хотя можно записывать и значения в других форматах) на вершину стека сопроцессора (его стек -- это особая структура из восьми регистров).
2. Команда FIMUL (Integer Multiplication) выполняет умножение вещественного числа, находящегося на вершине стека (мы только что записали туда переменную S),
на целое число, находящееся по адресу, указанному в качестве операнда (у нас указан адрес переменной I). При этом умножаемое выталкивается из стека, а результат умножения записывается на его (стека) вершину.
Теперь самое интересное: нужно отбросить дробную часть у полученного результата. Эта операция является разновидностью округления, а для округления предназначена команда FRNDINT. Сопроцессор поддерживает 4 режима округления -- округление до ближайшего целого, до ближайшего меньшего/большего целого и, наконец, округление путём отбрасывания дробной части. Перевести сопроцессор в нужный режим округления можно, записав нужное значение в поле RC (Rounding Control) 16-разрядного регистра управления сопроцессором (CWR). Поле представлено 10-м и 11-м битом этого регистра. Для того, чтобы округлять путём отбрасывания, оно должно содержать двоичное значение 11, т.е. оба его бита должны быть "включены".
Алгоритм наших дальнейших действий таков: мы должны сохранить текущее содержимое регистра CWR, затем записать в его поле RC значение 11, вызвать команду округления и, наконец, восстановить прежнее содержимое регистра.
3. Выделяем в стеке (только уже в обычном стеке, а не в стеке сопроцессора) 4 байта -- этого хватит для двух копий содержимого регистра CWR.
4. Команда FNSTCW сохраняет управляющее слово (содержимое регистра CWR) по указанному адресу -- на вершине стека.
5. То же самое значение записываем в стек ниже. Эту копию мы изменять не будем -- она понадобится, чтобы восстановить значение регистра CWR после того, как мы закончим округление.
7. С помощью маски $0F00 изменяем значение первой копии управляющего слова. В слове $0F00 "установлены" биты с номерами 8, 9, 10, 11. Зачем нужны 10-й и 11-й -- уже понятно, а 8-й и 9-й биты -- это поле управления точностью, и 11 -- это его значение по умолчанию. Команда OR использует значение по адресу ESP -- туда мы записывали слово командой FNSTCW ещё в строке под номером 4. Но не нужно забывать, что команда OR исполняется основным процессором, в то время как FNSTCW -- сопроцессором. Процессор и сопроцессор работают параллельно, и нет гарантии, что к моменту исполнения 7-й строки сопроцессор уже справится со строкой № 4. Чтобы такая гарантия появилась, надо перед командой OR вставить команду FWAIT -- она останавливает основной процессор до тех пор, пока сопроцессор не выполнит последнюю поступившую в него команду.
8. С помощью команды FLDCW загружаем только что подготовленное нами управляющее слово в регистр CWR. Теперь сопроцессор будет округлять так, как нам нужно.
9. FRNDINT -- непосредственно округление. Команда округляет число, находящееся на вершине стека сопроцессора, в соответствии с установленным режимом округления, и записывает результат на место исходного числа.
10. Команда FLDCW восстанавливает предусмотрительно сохранённое нами значение управляющего слова сопроцессора.
11. Выделенные в стеке 4 байта нам больше не нужны -- восстанавливаем прежний адрес вершины стека.
12. Командой FSTP сохраняем наш результат, находящийся на вершине стека сопроцессора, то бишь в регистре ST(0), в переменную S.
От меня:
Убери переменную I и эту строчку "FIMUL [I] // 2" и будет тебе то что ты хотел, сам проверял