Консультация # 186409: Уважаемые эксперты! Пожалуйста, ответьте на вопрос: требуется написать программу для ТАSM для вычисления данных выражений. Каждую функцию реализовать в виде отдельной подпрограммы, используя наиболее подходящий спос...
Уважаемые эксперты! Пожалуйста, ответьте на вопрос: требуется написать программу для ТАSM для вычисления данных выражений.
Каждую функцию реализовать в виде отдельной подпрограммы, используя наиболее подходящий способ передачи аргументов. Вычисления, которые встречаются несколько раз, оформить в виде макрокоманд. Организовать ввод значений исходных переменных с клавиатуры
и вывод результатов на экран. Обработать возможные ошибки при вводе чисел с клавиатуры. Все числа считать вещественными, которые могут представляться в форме с фиксированной и плавающей точкой.
Когда то я уже задавал подобрый вопрос и он есть у меня в истории. Требуется написать так программу, чтобы она была весьма простой и максимально откоментированной. В прошлой программе громоздким считать нужно таблицы для вывода степеней(окончание программы).Буду очень рад если сможете написать все это в максимально возможные сроки. Спасибо!
Здравствуйте, Юдин Евгений Сергеевич! Работу с вещественными числами, в любом случае, простой не назовешь... Раз Вам не понравилось, как сделано в masm-е, я сделал по-другому Надеюсь, что такое решение Вас устроит... Имейте в виду, что при вводе переменных необходимо следить, чтобы выражение под корнем было
>= 0
Код :
.model small, stdcall
.386 ;необходимо для команды fcos
;макро для вычисления y^x
;в st должно находиться xlog[2]y
POWER MACRO
fld st ;x*log[2]y->st(1)
frndint ;округляем st до целого
fsub st(1),st ;st(1)-=st
fxch ;st(1)<->st
f2xm1 ;st=(2 в степени st) - 1
fld1 ;1->st
fadd ;st+=1
fscale ;y^x = st * (2 в степени st(1))
fstp st(1) ;чтобы убрать значение из st(1)
ENDM
;e^st
;st = z
EXP MACRO
fldl2e ;log(осн 2)e->st
fmulp ;st(1)*log(осн 2)e->st
POWER ;st=e^(st)
ENDM
;log[3](st)
LOG3 MACRO
fld1 ;1
fxch ;st(1)<->st
fyl2x ;st=1*log[2]st=log[2]y
fld1 ;1
fld c3 ;3
fyl2x ;st=1*log[2]3=log[2]3
fdiv ;st=st(1)/st=log[2]x/log[2]3=log[3]y
ENDM
;((log[3](y))^2-z)^(1/5)
CALC_YZ MACRO yoff,zoff
lea bx,y ;address y
fld dword ptr [bx] ;y->st
LOG3 ;st=log[3]y
fld st ;st=st(1)
fmulp ;st=log[3]y^2
lea bx,z ;address z
fsub dword ptr [bx] ;st=log[3]y^2-z
;возведем в степень 1/5
fld1 ;1
fdiv c5 ;1/5
fxch ;st(1)<->st
fyl2x ;st=st(1)*log[2]st
POWER ;находим (log[3](y)^2-z)^(1/5)
ENDM
.code
start:
mov ax, @data
mov ds, ax ;настроим сегментные регистры
mov es, ax ;на сегмент @data
mov ss, ax
mov sp, 0fffeh ;стек в вершине
;введем данные
x_loop:
lea si, sEnterX ;строка приглашения
call GetFloat ;вводим вещественное, результат в st
jc x_loop ;С=1 - ошибка формата! На повтор ввода!
fstp x ;сохраним в x
y_loop:
lea si, sEnterY
call GetFloat
jc y_loop
fstp y ;y
z_loop:
lea si, sEnterZ
call GetFloat
jc z_loop
fstp z ;z
call calc_a ;получаем в st расчет a
fst a ;сохраним в a и оставим в st(0)
push offset sNum ;адрес буфера для числовой строки
call float2str ;преобразовываем вещественное число из st(0)
lea si, sA ;строка пояснения 'a='
lea bx, sNum ;числовая строка
call prStr ;выводим две строки [si]+[bx]
call calc_b ;получаем в st расчет b
fst b ;сохраним в b и оставим в st(0)
push offset sNum ;адрес буфера для числовой строки
call float2str ;преобразовываем вещественное число из st(0)
lea si, sB ;строка пояснения 'b='
lea bx, sNum ;числовая строка
call prStr ;выводим две строки [si]+[bx]
lea si, sAny ;Press any key
call prsz ;выводим строку из si
mov ah, 0 ;ждем any key
int 16h
mov ax,4c00h
int 21h ;выход в ДОС
GetFloat proc ;ввод вещественного числа
call prsz ;выводим приглашение из si
lea dx, sBuf
mov ah, 0ah
int 21h ;вводим строку
lea ax, string ;адрес строки
push ax ;параметр в стек
call str2float ;преобразовываем в вещественное
ret ;число в st(0)
GetFloat endp
prsz proc ;вывод стоки ASCIIZ [si]
lodsb
cmp al, 0
je prret
int 29h
jmp prsz
prret:
ret
prsz endp
prStr proc near ;вывод двух строк ASCIIZ
call prsz ;первая [si]
mov si, bx
call prsz ;вторая [bx]
ret
prStr endp
;расчет a
;результат в st
calc_a proc
fld x ;st = x
fmul c3 ;st = 3*x
fcos ;st = cos(3x)
fld st ;st(1) = st
fmulp ;st = cos(3x)^2
fmul c008 ;st = 0.08*cos(3x)^2
fld z ;st = z
EXP ;st = e^z
fsubp st(1),st ;st = 0.08*cos(3x)^2 - e^z
CALC_YZ ;st = (log[3](y)^2 - z)^(1/5)
fld z ;st = z
fmul C7 ;st = 7z
faddp ;st = (log[3](y)^2 - z)^(1/5) + 7z
fdivp ;st = (0.08*cos(3x)^2 - e^z)/
ret ; ((log[3](y)^2 - z)^(1/5) + 7z)
calc_a endp
;расчет b
;результат в st
calc_b proc
CALC_YZ ;st = (log[3](y)^2 - z)^(1/5)
fld y ;st = y
fmul C5 ;st = 5y
fsubp ;st = (log[3](y)^2 - z)^(1/5) - 5y
fadd C00006 ;st = (log[3](y)^2 - z)^(1/5) - 5y + 0.00006
ret
calc_b endp
;-----------------------------------------
;П/п конвертации float <-> ASCII
;-----------------------------------------
;преобразование вещественного числа-строки во float
;если строка некорректна, то возвращается C=1
str2float proc uses si di, numstr:word
local numfloat:dword, c10num:word, nextnum:word, order:word
;будем накапивать число в регистре сопроцессора, начинаем с нуля
fldz
;будем умножать/делить на 10
mov c10num, 10
;порядок = 0
mov order, 0
;dl=0/1 - положительное/отрицательное число, dh=1 - знак задан
xor dx, dx
;cl=0/1 - целая/дробная часть, ch=1 - точка задана
xor cx, cx
;bl=0/1 - положительная/отрицательная степень после Е, bh=1 - степень задана
xor bx, bx
;адрес строки
mov si, numstr
sym_loop:
xor ax, ax
lodsb
cmp al, 0dh
je set_order ;строка кончилась
cmp al, 0
je set_order ;строка кончилась
cmp al, '+'
je plus_sign ;+
cmp al, '-'
je minus_sign ;-
cmp al, '.'
je point_sign ;.
cmp al, 'e'
je order_sign ;e
cmp al, 'E'
je order_sign ;E
cmp al, '0'
jb num_err ;ошибка - не цифра
cmp al, '9'
ja num_err
sub al, '0' ;'0'-'9' -> 0-9
mov nextnum, ax ;сохраним слово в памяти
mov dh, 1 ;после цифры задавать знак числа нельзя!
cmp bh, 0 ;вводим порядок?
jne num_order
;нет, вводим мантиссу
jcxz part_integer ;точки еще не было - целая часть числа
part_broken: ;дробная часть
fld numfloat ;вес цифры: 0.1, 0.001, 0.0001,...
fimul nextnum ;умножаем на цифру
faddp ;и добавляем до числа
fld numfloat ;формируем вес следующей цифры
fidiv c10num
fstp numfloat
jmp sym_loop
num_order: ;вводим порядок
mov ax, 10 ;вводим до конца строки двоичное число
mul order
add ax, nextnum
mov order, ax
jmp sym_loop
num_err: ;ошибка (любая)
fstp numfloat ;уберем из сопроцессора число
stc ;признак ошибки
jmp str2float_ret ;на общий выход (чтобы подправить стек)
part_integer: ;целая часть
fimul c10num ;умножаем на 10
fiadd nextnum ;и добавляем цифру
jmp sym_loop
plus_sign: ;+
cmp bh, 0 ;вводим порядок?
jne plus_order
;нет, вводим мантиссу
cmp dh, 0 ;проверим на повтор
jne num_err ;знак задан! Ошибка!
mov edx, 0100h ;помечаем, что знак задан и остался +
jmp sym_loop
plus_order: ;знак в порядке
cmp bh, 2 ;задан?
je num_err ;повтор! Ошибка!
mov bx, 0200h ;помечаем, что знак задан и остался +
jmp sym_loop
minus_sign: ;-
cmp bh, 0 ;вводим порядок?
jne minus_order
cmp dh, 0 ;проверим на повтор
jne num_err
mov edx, 0101h ;помечаем, что знак задан и стал -
jmp sym_loop
minus_order:
cmp bh, 2 ;задан?
je num_err
mov bx, 0201h ;помечаем, что знак задан и он -
jmp sym_loop
point_sign: ;.
cmp bh, 0
jne num_err ;точка в порядке запрещена
cmp ch, 0 ;проверим на повтор
jne num_err
mov ecx, 0101h ;помечаем, что точка задана
fld1
fidiv c10num ;1/10
fstp numfloat ;зададим вес первой дробной цифры (0.1)
jmp sym_loop
order_sign: ;символ порядка (e или E)
ftst ;проверим на 0
fstsw ax ;флаги в ax
sahf ;флаги в регистре флагов
jz num_err ;при задании порядка должна быть ненулевая мантисса
cmp bh, 0
jne num_err ;и только раз задано e(E)
mov bh, 1 ;помечаем, что задан порядок
jmp sym_loop
set_order: ;строка обработана, учтем порядок
cmp bh, 0 ;порядок есть?
je set_sign ;нет - на установку знака
mov cx, order ;порядок
jcxz set_sign ;порядок = 0 - ничего менять не надо
cmp bl, 0 ;+ или -
je positive_loop
negative_loop: ;-
fidiv c10num ;делим cx раз на 10
loop negative_loop
jmp set_sign
positive_loop: ;+
fimul c10num ;умножаем cx раз на 10
loop positive_loop
set_sign: ;учтем знак
cmp dl, 0 ;+ или -
je str2float_ok ;+ выходим
fchs ;меняем на -
str2float_ok:
clc ;все ок
str2float_ret:
ret
endp
;преобразование float из sp в строку по адресу pStr
float2str proc uses di, pStr:word
local dig:word, c10:word
mov c10, 10 ;будем делить на 10
mov di, pStr ;здесь будем формировать строку
ftst ;Проверяем число
fstsw ax ;флаги в ax
sahf ;флаги в регистре флагов
jnz float2str_notZero ;не 0
mov ax, '0' ;если 0, то рисуем 0 и выводим
stosw
jmp float2str_Ret ;на выход
float2str_notZero: ;не 0
jnc f2s_1 ;проверим знак
mov al, '-' ;для отрицательного выведем знак минус
stosb
fchs ;и меняем знак числа.
; Пояснение далее пойдёт на примере. ; ST(0) ST(1) ST(2) ST(3) ...
; Отделим целую часть от дробной. ; 73.25 ... что-то не наше
f2s_1:
fld1 ; 1 73.25 ...
fld st(1) ; 73.25 1 73.25 ...
; Остаток от деления на единицу даст дробную часть.
fprem ; 0.25 1 73.25 ...
; Если вычесть её из исходного числа, получится целая часть.
fsub st(2), st ; 0.25 1 73 ...
fxch st(2) ; 73 1 0.25 ...
; Сначала поработаем с целой частью. Считать количество цифр будем в CX.
xor cx, cx
; Поделим целую часть на десять,
f2s_2: fidiv c10 ; 7.3 1 0.25 ...
fxch st(1) ; 1 7.3 0.25 ...
fld st(1) ; 7.3 1 7.3 0.25 ...
; отделим дробную часть - очередную справа цифру целой части исходного числа,-
fprem ; 0.3 1 7.3 0.25 ...
; от частного оставим только целую часть
fsub st(2), st ; 0.3 1 7 0.25 ...
; и сохраним цифру
fimul c10 ; 3 1 7 0.25 ...
fistp dig ; 1 7 0.25 ...
; считаем цифры
inc cx
; сохраняем в стеке.
push dig
fxch st(1) ; 7 1 0.25 ...
; Так будем повторять, пока от целой части не останется ноль.
ftst
fstsw ax
sahf
jnz f2s_2
; Теперь выведем цифры целой части.
f2s_3: pop ax
; Вытаскиваем очередную цифру, переводим её в символ и выводим.
add al, 30h
stosb
; И так, пока не выведем все цифры.
loop f2s_3 ; 0 1 0.25 ...
; Итак, теперь возьмёмся за дробную часть, для начала проверив её существование.
fstp st(0) ; 1 0.25 ...
fxch st(1) ; 0.25 1 ...
ftst
fstsw ax
sahf
jz f2s_5 ;дробной части нет - выходим
; Если она всё-таки ненулевая, выведем точку и 6 знаков после точки
mov al, '.' ;рисуем точку
stosb
mov cx, 6 ;6 знаков после запятой
; Помножим дробную часть на десять
f2s_4: fimul c10 ; 2.5 1 ...
fxch st(1) ; 1 2.5 ...
fld st(1) ; 2.5 1 2.5 ...
; отделим целую часть - очередную слева цифру дробной части исходного числа,-
fprem ; 0.5 1 2.5 ...
; оставим от произведения лишь дробную часть
fsub st(2), st ; 0.5 1 2 ...
fxch st(2) ; 2 1 0.5 ...
; сохраним полученную цифру во временной ячейке
fistp dig ; 1 0.5 ...
; и сразу выведем.
mov ax, dig
add al, 30h
stosb
; Теперь, если остаток дробной части ненулевой
fxch st(1) ; 0.5 1 ...
ftst
fstsw ax
sahf
; и мы вывели менее cx цифр, продолжим
loopnz f2s_4 ; 0 1 ...
; Итак, число выведено. Осталось убрать мусор из стека.
f2s_5: fstp st(0) ; 1 ...
fstp st(0) ; ...
mov byte ptr [di], 0 ;закрываем строку
float2str_ret:
ret
endp
.data
x dd 0
y dd 0
z dd 0
a dd ?
b dd ?
;константы
c008 dd 0.08
c3 dd 3.
c0005 dd 0.005
c7 dd 7.
c5 dd 5.
c00006 dd 0.00006
sAny db 0dh,0ah,'Press any key',0
sEnterX db 0dh,0ah,'Enter x: ',0
sEnterY db 0dh,0ah,'Enter y: ',0
sEnterZ db 0dh,0ah,'Enter z: ',0
sA db 0dh,0ah,'a = ',0
sB db 0dh,0ah,'b = ',0
sBuf db 32
cnt db ?
string db 32 dup (?)
sNum db 32 dup (?)
end start
Консультировал: Лысков Игорь Витальевич (Старший модератор)
Дата отправки: 25.06.2012, 02:39
5
нет комментария ----- Дата оценки: 25.06.2012, 08:24
Команда портала RFPRO.RU благодарит Вас за то, что Вы пользуетесь нашими услугами. Вы только что прочли очередной выпуск рассылки. Мы старались.
Пожалуйста, оцените его. Если совет помог Вам, если Вам понравился ответ, Вы можете поблагодарить автора -
для этого в каждом ответе есть специальные ссылки. Вы можете оставить отзыв о работе портале. Нам очень важно знать Ваше мнение.
Вы можете поближе познакомиться с жизнью портала, посетив наш форум, почитав журнал,
который издают наши эксперты. Если у Вас есть желание помочь людям, поделиться своими знаниями, Вы можете зарегистрироваться экспертом.
Заходите - у нас интересно!