Доброго
здоров'ячка! Сегодня я
расcкажу вам как решать квадратные уравнения с помощью
сопроцесcора. Но,
что это такое? Я не буду вдаваться в хардверные дебри, скажу только,
что наряду с обычным процессором существует, так называемый,
математический сопроцессор. Его функции ясны: чтобы не забивать
основной процессор всякими math кодами (некоторые он может вычислить)
есть математическая машина.
Математикам: я здесь покажу как программируется сопроцессор на основе
решения квадратных уравнений.
Итак приступим!
Алгоритм решения:
- Мы имеем формулу общего квадратного уравнения (вспоминайте школу,
класс этак восьмой :-). Она выглядит так: ax^2+bx+c=0
- Требуется найти ее дискриминант: D=b^2-4ac
- Если D<0 то корней не
существует, иначе п.4
- Если D=0 то есть один
корень: X=-b/2a, иначе п.5
- Если D>0 то
существуют два корня: X1=(-b-sqrt(D))/2a,
X2=(-b+sqrt(D))/2a
С чего же начать? Ответ прост: с очистки
математической машины. Почти
все программы оставляют свои коды в сопроцессоре (для краткости будем
называть его СП). Делается это одной коммандой:
finit
Далее, найдем дискриминант:
fld
b
; Введем в стек СП число b
fmul
b
; Умножим b на самого себя
(b^2)
fld a
; Введем в стек СП число a
fld temp
; Введем в стек СП число temp (temp=4)
fmul
; Умножим a на temp
fld c
; Введем в стек СП число c
fmul
; Умножим a*temp на c (4ac)
fsubp st(1),st(0)
; Вычтем 4ac из b^2
fst D
; "Вытолкнем" дискриминант в переменную D
Ну вот, D нашли. Теперь нужно его сравнить с нулем.
Вот тут то и
начинаются проблемы: у СП есть момент сравнивания и все, а мне нужно
перейти на определенные "полки" программы. Зато у основного процессора
(далее ОП) такая возможность есть, а значит, мне нужно перенести
определенные флаги СП в ОП. Делается это так:
ftst
; Сравниваем D с нулем (на
этом дело СП заканчивается)
fstsw ax
; "копируем" регистр состояния SWR
в регистр AX
sahf
; "копируем" флаги C0, C2, C3
(нет, я не ошибся :-) в младший байт
регистра EFLAGS/FLAGS
Теперь мы можем "попрыгать" по программе, но сначала таблица
соответсвий:
Условие
|
Команда
"прыжка"
|
<
|
jc
|
=
|
jz
|
Условие "больше" нам не потребуется и вы поймете
почему. Я приведу и
детально прокомментирую остальную часть программы, потому-что ее,
фактически,
нельзя разрывать:
jc
j1
; если D < 0 то goto j1
jz j2
; если D = 0 то goto j2
; а если D > 0 то [далее]
finit
; опять очистили СП
fld temp2
; Введем в стек СП число temp2 (temp2=2)
fmul a
; Умножили temp2 на a
fld D
; Введем в стек СП дискриминант
fsqrt
; Посчитали корень даскриминанта
fld b
; Введем в стек СП число b
fchs
; Изменили знак у b
fsave temp2[4]
; Сохранили полное (регитры и стек) состояние СП (и стерли оное)
frstor temp2[4]
; Загрузили полное состояние
fsubr
; вычли корень дискриминанта из -b
fdivr
; первый корень
frstor temp2[4]
; Загрузили еще раз полное состояние
fadd
; сложили корень дискриминанта и -b
fdivr
; второй корень
jmp ex
; "прыгнули" на "выход"
; D < 0
; !no roots!
j1:
mov
ah, 09h
mov
dx, offset
mes1
int 21h
; вывели сообщение, что нет корней (под Win32)
jmp ex
; "прыгнули" на "выход"
; D = 0
; X = -b/(2*a)
j2:
finit
; опять очистили СП
fld temp2
; Введем в стек СП число temp2 (temp2=2)
fmul a
; Умножили temp2 на a
fld b
; Введем в стек СП число b
fdivr
; Разделили b на 2a
fchs
; Сменили знак
; Вот и единственный корень
Вот и все! Ошибок я не нашел (может Вы найдете).
Можете вводить целые и дробные параметры - все равно получите
правильный результат. Если у вас есть
TASM
(и за мелкими изменениями
MASM) то можете
скачать
данную программу.
При написании я частично пользовался книгой
Юрова ASSEMBLER 2-е
издание, за что ему большое спасибо!
Ну вот и все, что я хотел вам сказать. Бувайте!
Приложение 1. Полная программа на ASM
;
<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>
; Anton (Liloi) Moskalenko
; e-mail: liloi@mail.ru
;
<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>
; Create time & date: 15:55 14.08.2006
; Note: kv. urav
; Compiled with TASM 4.1
.286
.model small
.data
a dd 2.0
b dd 4.0
c dd 2.0
D dd ?
mes1 db 'No
roots! $'
temp dd 4.0
temp2 dd 2.0
.stack 256h
.code
;Main PROC
main proc
push @data
pop ds
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
finit
; discriminante
; C++:
; D = sqr(b)-4*a*c
fld b
fmul b
fld a
fld temp
fmul
fld c
fmul
fsubp st(1),st(0)
fst D
; <=>
ftst
fstsw ax
sahf
jc j1
jz j2
; D > 0
; X1 = (-b-sqrt(D))/(2*a)
; X2 = (-b+sqrt(D))/(2*a)
finit
fld temp2
fmul a
fld D
fsqrt
fld b
fchs
fsave temp2[4]
frstor temp2[4]
fsubr
fdivr ; first root
frstor temp2[4]
fadd
fdivr ; second root
jmp ex
; D < 0
; !no roots!
j1: mov ah, 09h
mov dx, offset
mes1
int 21h
jmp ex
; D = 0
; X = -b/(2*a)
j2: finit
fld temp2
fmul a
fld b
fdivr
fchs ; root
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
ex: mov ax, 04C00h
int 21h
main endp
;end of main
end main
|
Приложение 2. Аналогичная программа на С++
#include
<iostream.h>
#include <math.h>
int main(int argc, char *argv[])
{
const float a = 3, b = 2, c = -1;
float D = 0, x, x1, x2;
D = pow(b,2)-4*a*c;
if(D<0)
{
cout << "No roots!"
<< endl;
}else
if(D==0)
{
x = ((-1)*b)/(2*a);
}else
{
x1 = ((-1)*b-sqrt(D))/(2*a);
x2 = ((-1)*b+sqrt(D))/(2*a);
}
return 0;
}
|