Опытный
 
Профиль
Группа: Участник
Сообщений: 312
Регистрация: 8.1.2007
Где: Ленобласть
Репутация: 12 Всего: 23
|
Код | .486C cseg segment use16 assume cs: cseg, ds: nothing, ss: sseg ; Процедура вывода числа на вершине стэка FPU. Число после вывода выталкивается outp: push ax push cx push dx push bp mov bp, sp push 000Ah ; Основание нашей системы счисления push 0000h ; Ячейка для передачи цифр из FPU в CPU setc cl ; CL = 1 для вывода ' +' или ' -' перед числом, CL = 0 для вывода ' = ' или ' = -' ftst fstsw ax sahf setc ch ; CH = 1 if ST(0) < 0 mov ah, 02h ; Выводим пробел mov dl, 20h int 21h test cl, cl jnz short @13 mov dl, 3Dh ; '=' int 21h mov dl, 20h int 21h @13: test ch, ch jz short @14 mov dl, 2Dh ; '-' int 21h jmp short @15 @14: test cl, cl jz short @15 mov dl, 2Bh ; '+' int 21h @15: fabs ; Со знаком разобрались, выводим модуль числа fld1 ; Сначала отделим целую и дробную части fld st(1) fprem fsub st(2), st fxch st(2) ; ST(0) = целая часть, ST(1) = 1, ST(2) = дробная часть fild word ptr [bp - 2] xor cx, cx ; ST(0) = 10, ST(1) = целая часть, ST(2) = 1, ST(3) = дробная часть fld st(1) @16: fprem ; Получаем последнюю цифру ST(0) fist word ptr [bp - 4] ; Сохраняем её в стэке, так как цифры получаются справа налево, push word ptr [bp - 4] ; а выводить их надо слева направо inc cx ; Увеличиваем счётчик цифр целой части fsubp st(2), st ; Сдвигаем ST(0) на одну цифру вправо fdiv st(1), st fld st(1) ftst ; Если ST(0) = 0, то целую часть разобрали fstsw ax sahf jnz short @16 ; ST(0) = 0, ST(1) = 10, ST(2) = 0, ST(3) = 1, ST(4) = дробная часть fstp st(0) fstp st(1) ; ST(0) = 10, ST(1) = 1, ST(2) = дробная часть mov ah, 02h @17: pop dx ; Выводим цифры целой части на экран add dl, 30h int 21h loop @17 fxch st(2) ; ST(0) = дробная часть, ST(1) = 1, ST(2) = 10 ftst fstsw ax sahf jz short @18 ; Если дробная часть ноль, выводить её не будем mov ah, 02h ; Иначе начинаем с точки mov dl, 2Eh int 21h mov cx, 0006h ; Выведем не более шести цифр @19: fmul st, st(2) ; Сдвигаем дробную часть на одну цифру влево, получаем a.bcd... Выделим a fxch st(1) ; ST(0) = 1, ST(1) = a.bcd..., ST(2) = 10 fld st(1) ; ST(0) = a.bcd, ST(1) = 1, ST(2) = a.bcd..., ST(3) = 10 fprem ; ST(0) = 0.bcd, ST(1) = 1, ST(2) = a.bcd..., ST(3) = 10 fsubr st, st(2) ; ST(0) = a, ST(1) = 1, ST(2) = a.bcd..., ST(3) = 10 fsub st(2), st ; ST(0) = a, ST(1) = 1, ST(2) = 0.bcd..., ST(3) = 10 fistp word ptr [bp - 4] mov dl, [bp - 4] ; Выводим очередную цифру дробной части add dl, 30h mov ah, 02h int 21h fxch st(1) ftst fstsw ax sahf loopnz @19 ; Если дробная часть ноль, или уже выведено шесть цифр, закругляемся @18: fstp st(0) ; Очистка стэка сопроцессора fstp st(0) fstp st(0) leave pop dx pop cx pop ax ret ; Процедура ввода числа. Введённое число сохраняется на вершине стэка inp: push ax push dx push bp mov bp, sp push 000Ah ; Инициализируем кадр стэка push 0000h mov ah, 02h ; Выводим ' = ' mov dl, 20h int 21h mov dl, 3Dh int 21h mov dl, 20h int 21h fld1 ; Знак числа @10: mov ah, 01h ; Вводим символ int 21h cmp al, 2Dh ; '-' jne short @2 fchs ; Если введён минус, меняем +1 на -1 jmp short @10 ; и вводим следующий символ @2: fldz ; Будущее число @7: cmp al, 0Dh ; <Enter> - конец ввода je short @3 cmp al, 2Eh ; '.' - переход к вводу дробной части je short @4 sub al, 30h jb short @5 ; < '0' или > '9' - ошибка cmp al, 09h jbe short @6 @5: mov ax, 4C00h ; При ошибке молча завершаемся int 21h @6: fimul word ptr [bp - 2] mov [bp - 4], al ; Иначе ST(0) = 10 * ST(0) + введённая цифра fiadd word ptr [bp - 4] mov ah, 01h int 21h jmp short @7 @4: fld1 ; Ввод дробной части. 1 суть 10 в степени 0 @9: mov ah, 01h int 21h cmp al, 0Dh ; <Enter> - конец ввода je short @8 sub al, 30h jb short @5 ; < '0' || > '9' - ошибка cmp al, 39h ja short @5 fidiv word ptr [bp - 2] fld st(0) ; Формируем 10 в следующей степени и дублируем её mov [bp - 4], al ; Умножаем на введённую цифру, тем самым получая её на нужном месте fimul word ptr [bp - 4] faddp st(2), st ; И добавляем к числу jmp short @9 @8: fstp st(0) ; Убираем 10 в степени @3: fmulp st(1), st ; Вспоминаем про знак @1: mov ah, 02h ; Допереводим строку mov dl, 0Ah int 21h leave pop dx pop ax ret ; Точка входа @: mov ah, 02h ; Вводим коэффициенты квадратного уравнения mov dl, 61h ; 'a' finit int 21h call inp inc dl ; 'b' int 21h call inp inc dl ; 'c' int 21h call inp fxch st(2) ftst ; Проверяем a == 0 fstsw ax sahf jnz short @20 fstp st(0) ; Проверяем a == 0 && b == 0 ftst fstsw ax sahf jnz short @21 fstp st(0) ; Проверяем a == 0 && b == 0 && c == 0 ftst fstsw ax sahf mov ah, 02h mov dl, 21h ; Если все коэффициенты ноль, бесконечно много решений jz short @22 mov dl, 3Fh ; Иначе нет решений @22: int 21h fstp st(0) ; Очистка стэка сопроцессора jmp @5 @21: fdivp st(1), st ; Решаем линейное уравнение ST(0) x + ST(1) = 0, получаем x = - ST(1) / ST(0) fchs mov ah, 02h mov dl, 78h ; 'x' int 21h clc ; Выводим ' = ' и решение call outp jmp @5 @20: fdiv st(2), st ; Решаем квадратное уравнение axx + bx + c = 0, a = ST(0), b = ST(1), c = ST(2) fdivp st(1), st ; Приводим его к виду xx + b/a x + c/a = 0 fld1 fadd st(0), st fdivp st(1), st fchs ; Вычислили -b/2a fst st(2) ; ST(0) = ST(2) = -b/2a, ST(1) = c/a fmul st(0), st ; ST(0) = bb/4aa, ST(1) = ac/aa, ST(2) = -b/2a fsubrp st(1), st ; ST(0) = (bb-4ac)/4aa, ST(1) = -b/2a ftst ; Проверка на знак дискриминанта, совпадающий со знаком ST(0) fstsw ax sahf mov ah, 02h mov dl, 78h ; 'x' ja short @11 jb short @12 fstp st(0) ; Дискриминант равен 0, оба корня суть -b/2a int 21h clc call outp ; Ну, выводим jmp @5 @11: fsqrt ; Дискриминант > 0. ST(0) = sqrt(bb-4ac)/2a, ST(1) = -b/2a (в случае a > 0) fsub st(1), st ; ST(0) = sqrt(bb-4ac)/2a, ST(1) = -b/2a - sqrt(bb-4ac)/2a fadd st(0), st ; ST(0) = 2sqrt(bb-4ac)/2a, ST(1) = -b/2a - sqrt(bb-4ac)/2a fadd st, st(1) ; ST(0) = -b/2a + sqrt(bb-4ac)/2a, ST(1) = -b/2a - sqrt(bb-4ac)/2a (Для a < 0 корни получаются в обратном порядке) int 21h clc call outp mov dl, 0Dh int 21h mov dl, 0Ah int 21h mov dl, 78h int 21h clc call outp jmp @5 @12: fchs ; Дискриминант < 0. Корни суть (-b + i sqrt(4ac-bb))/2a и (-b - i sqrt(4ac-bb))/2a fsqrt ; ST(0) = sqrt(4ac-bb)/2|a|, ST(1) = -b/2a fxch st(1) fld st(1) fchs fld st(1) int 21h clc ; Выводим корни - сначала действительную часть, потом мнимую, call outp stc call outp mov ah, 02h mov dl, 69h ; потом 'i' int 21h mov dl, 0Dh int 21h mov dl, 0Ah int 21h mov dl, 78h int 21h clc call outp stc call outp mov dl, 69h int 21h jmp @5 cseg ends sseg segment use16 stack db 0400h dup (?) sseg ends end @ |
--------------------
Что непонятно - спрашиваем 
|