Консультация № 180887
22.11.2010, 10:40
55.50 руб.
23.11.2010, 02:56
0 15 1
Здравствуйте, уважаемые эксперты! Прошу Вас ответить на следующий вопрос:
Ввести в консольном режиме два вещественных знаковых числа в виде строк. Ввод осуществить с помощью функции прерывания 21h. Перевести введенные строки в числа с осуществление контроля корректности введенных значений. Числа представляют собой границы интервала, левая должна быть меньше правой.
В графическом режиме нарисовать график функции f(x)=sin x + sin 2x + sin 3x + sin 4x + sin 5x в пределах заданного ранее интервала. График необходимо отмасштабировать так, чтобы минимальное значение функции на заданном интервале помещалось на нижней строчке экрана, а максимальное на верхней.
Модель памяти SMALL
Среда выполнения: DOS 6.22 или выше
TASM 5.0

Приложение:
Модель памяти SMALL
Среда выполнения: DOS 6.22 или выше
TASM 5.0

Обсуждение

давно
Посетитель
7438
7205
22.11.2010, 13:08
общий
Думаю, будет достаточно только фиксированной точки, т.е. рассматриваем числа только вида 3.1234
Научную запись (с порядком), например, 1.34e-4 мы не рассматриваем. Что скажете?
Об авторе:
"Если вы заметили, что вы на стороне большинства, —
это верный признак того, что пора меняться." Марк Твен
Неизвестный
22.11.2010, 13:11
общий
[q=7438][/q]
Думаю, что да, будет достаточно фиксированной точки.
давно
Посетитель
7438
7205
22.11.2010, 14:04
общий
Еще вопросы:

1) Какой будем использовать графический режим? 320х200х256 устроит?
2) По X и Y делаем пропорциональную шкалу или нет?
Или по Х тоже растягиваем от края до края?
Об авторе:
"Если вы заметили, что вы на стороне большинства, —
это верный признак того, что пора меняться." Марк Твен
Неизвестный
22.11.2010, 15:16
общий
[q=7438][/q]
да, устроит 320x200x256
нет, не обязательно пропорциональную шкалу. И еще сами оси можно не рисовать.
Спасибо вам за помощь)

Добавлено:
Точнее наоборот. Пропорциональную шкалу, а растягивать от края до края необязательно.



Извиняюсь что ввожу в заблуждение. По х от введенного первого числа до второго пропорционально.
давно
Посетитель
7438
7205
22.11.2010, 16:20
общий
Под пропорциональностью я имел в виду одинаковый масштаб по х и по y
Тут еще одна непонятка:
Не очень хорошая функция для этой задачи
Дело в том, что функция во многих местах стремится к бесконечности...
Как в таких условиях искать минимум и максимум функции?
Вот Вам рисунок Вашей функции, сделанный в другой программе:

Об авторе:
"Если вы заметили, что вы на стороне большинства, —
это верный признак того, что пора меняться." Марк Твен
давно
Посетитель
7438
7205
22.11.2010, 16:35
общий
В точках x = (pi * k ) / 5 [$8776$] 0.628 * k, k = 0,1,2,... функция стремится к [$177$][$8734$]
Такие точки достаточно часты, чтобы можно было их легко обойти при задании точек... Что делаем?

Можно, например, ограничиться значением y = 100. Если будет больше, или бесконечность, то не выводим,
но считаем min = -100 и max = 100 (что необходимо для построения графика)
Об авторе:
"Если вы заметили, что вы на стороне большинства, —
это верный признак того, что пора меняться." Марк Твен
давно
Посетитель
7438
7205
22.11.2010, 16:52
общий
Или требовать задавать только "правильный" интервал, типа [0.1, 0.5]
Об авторе:
"Если вы заметили, что вы на стороне большинства, —
это верный признак того, что пора меняться." Марк Твен
Неизвестный
22.11.2010, 17:00
общий
Цитата: Лысков Игорь Витальевич
Или требовать задавать только "правильный" интервал, типа [0.1, 0.5]

нет, точно нет)

может тогда другую функцию?
это сложно переделывается?
Если нет, давайте лучше вот эту, она без разрывов: f(x)=sin x + sin 2x + sin 3x + sin 4x + sin 5x
давно
Посетитель
7438
7205
22.11.2010, 17:16
общий
Ну и чудненько, так и делаем...
А переделывается легко: меняем только функцию...
Так как насчет пропорциональности?
Может тоже растянем по Х, от края до края? Будет по Х один масштаб, по Y другой...
Об авторе:
"Если вы заметили, что вы на стороне большинства, —
это верный признак того, что пора меняться." Марк Твен
Неизвестный
22.11.2010, 17:20
общий
[q=7438][/q]
давайте растянем)
я вам надоела уже наверно)
давно
Посетитель
7438
7205
22.11.2010, 17:29
общий
Все, вопросов больше не имею.
Сделаю чуть позже, может ночью, может завтра... (осталось немного, свести все до кучи, но я сейчас занят)
Об авторе:
"Если вы заметили, что вы на стороне большинства, —
это верный признак того, что пора меняться." Марк Твен
давно
Посетитель
7438
7205
23.11.2010, 02:54
общий
это ответ
Здравствуйте, katbka!
Вот такая получилась программа.
Думаю, разберетесь сами, программа достаточно прокомментирована.
Если что не будет ясно, обращайтесь в мини-форум.
Оси я все-таки нарисовал. Без них как-то неуютно
Код:

.model small, C ; модель памяти и порядок вызова параметров
.386
.data
sLeft db 'Enter left boundary: $' ;запрос левой границы
sRight db 0ah,'Enter right boundary: $' ;запрос правой границы

.data?
fLeft dq ? ;левая введенная граница (double)
fRight dq ? ;правая введенная граница (double)
fxDelta dq ? ;приращение по Х (double)
fyMax dq ? ;максимальное значение Y (double)
fyMin dq ? ;минимальное значение Y (double)
fyDelta dq ? ;приращение по Y (double)

sNum db ? ;буфер ля ввода числовой строки
sLen db ?
sString db 80 dup (?)

color_axis equ 0ddh ;цвет осей
color_graph equ 022h ;цвет графика

.code ;сегмент кода
.startup ;точка входа

lea dx, sLeft
mov ah, 9
int 21h ;приглашение для ввода левой границы

mov sNum, 80 ;максимальный размер
lea dx, sNum
mov ah, 0ah
int 21h ;вводим строку

lea si, sString ;введенная строка
lea di, fLeft ;сюда запишем
call StrToFloat ;преобразуем в double

Repeat: ;вводим правцую границу
lea dx, sRight
mov ah, 9
int 21h ;приглашение для ввода праой границы

lea dx, sNum
mov ah, 0ah
int 21h ;вводим строку

lea si, sString
lea di, fRight ;сюда запишем правую границу
call StrToFloat ;преобразуем в double

fld fLeft ;сравним границы
fld fRight ;левая должна быть меньше правой
fcompp ;сравниваем вещественные числа
fstsw ax
sahf
jbe Repeat ;иначе на повтор ввода правой границы

call CalcConsts ;посчитаем некоторые переменные

;рисуем график
mov ax, 0013h ;переходим в граф режим vga 320x200x256
int 10h

mov ax, 0a000h
mov es, ax ; es - сегмент видео

call PrintAxis ;рисуем оси
call PrintGraph ;рисуем график

finish:
mov ah, 0 ;ждем нажатия на клавишу
int 16h

mov ax, 0003h ;возвращаемся в текстовый режим
int 10h

mov ax, 4c00h ;выход
int 21h

PrintAxis proc ;рисуем оси
local x:word, y:word ;переменные
;рисуем ось абсцисс
fld fyMax ;макс Y = расстоянию от верха
fdiv fyDelta ;получаем число точек по вертикали
fistp y ;сохраняем номер строки
cmp y, 0 ;если за экраном, то не рисуем
jl PrintAxisY
cmp y, 199
jg PrintAxisY
call line, 0, y, 319, y, color_axis
PrintAxisY:
;рисуем ось ординат
fld fLeft
fchs ;(-1) * левый край = расстояние от левого края
fdiv fxDelta ;получаем число точек по горизонтали
fistp x ;сохраняем номер колонки
dec x ;x=x-1
cmp x, 0 ;если за экраном, то не рисуем
jl PrintAxisRet
cmp x, 319
jg PrintAxisRet
call line, x, 0, x, 199, color_axis
PrintAxisRet:
ret
PrintAxis endp

PrintGraph proc ;рисуем график
local x1:word, y1:word, x2:word, y2:word
mov x1, 0 ;начинаем с левого края
mov x2, 0
fld fLeft ;начинаем с x = fLeft
fld st ;сохраним для дальнейших вычислений
call fun ;y = f(x1)
fsubr fyMax ;растояние от верха
fdiv fyDelta ;в точках
fistp y1 ;y1 - строка первой точки
mov cx, 319 ;до конца строки
PrintPointsLoop: ;цикл рисования отрезков
inc x2 ;следующая точка на экране
fadd fxDelta ;добавляем дельту по X
fld st ;сохраним для дальнейших вычислений
call fun ;y = f(x2)
fsubr fyMax ;растояние от верха
fdiv fyDelta ;в точках
fistp y2 ;y2 - строка второй точки
;соединяем две точки отрезком
call line, x1, y1, x2, y2, color_graph
inc x1 ;сдвигаем первую точку
mov ax, y2
mov y1, ax
loop PrintPointsLoop ;по всей строке
fstp st ;уберем из стека сопроцессора сохраненную величину
ret
PrintGraph endp

CalcConsts proc ;посчитаем необходимые переменные
local iWork:word

;посчитаем приращение по X
fld fRight
fsub fLeft
mov cx, 319 ;число сравнений
mov iWork, cx
fidiv iWork
fstp fxDelta ;fxDelta = (fRight - fLeft) / 319

;найдем минимальное и максимальное Y
fld fLeft
fld st ;сохраним для дальнейших вычислений
call fun
fst fyMax ;пусть fyMax = f(x=fLeft)
fstp fyMin ;fyMin = f(x=fLeft)
SearchMinMaxLoop:
fadd fxDelta ;сдвигаем на приращение
fst ;сохраним
call fun
fcom fyMax ;сравним fyMax и f(x)
fstsw ax
sahf
jbe CmpMin ;если новый Y <= fyMax, то обходим
fst fyMax ;иначе сохраним, как fyMax
CmpMin:
fcom fyMin ;сравним с минимальным
fstsw ax
sahf
jae CmpNext
fst fyMin
CmpNext:
fstp st ;выкинем из стека значение функции
loop SearchMinMaxLoop;по всем
fstp st ;выкинем из стека и сохранненый X

;посчитаем приращение по Y
fld fyMax
fsub fyMin
mov iWork, 199
fidiv iWork
fstp fyDelta ;fyDelta = (fyMax - fyMin) / 199
ret
CalcConsts endp

StrToFloat proc ;преобразование строки в double
local Sign:word, Digit:word, C10:dword, Base:dword

mov Sign, 1 ;положительное число
mov C10, 10 ;будем умножать/делить на 10
mov Base, 10 ;делим цифру для дробной части
fldz ;начинаем с 0
xor cx, cx ;считаем точки
SearchStart: ;ищем начало числа
lodsb ;очередной символ
call CmpSep ;проверим на сепараторы
jc StrToFloatRet ;конец строки - на выход
jz SearchStart ;сепаратор - читаем следующий символ

cmp al, '-' ;проверим на минус
je NegativeNum ;отрицательное число
cmp al, '+' ;можно записать + для положительного
je NextDigit ;на чтение следующего символа
jmp CmpDigit ;на проверку символа
NegativeNum:
mov Sign, -1 ;пометим знак

NextDigit: ;читаем следующий символ
lodsb
call CmpSep ;проверяем на сепаратор
jbe StrToFloatRet ;конец строки или сепараторы - на конец

CmpDigit: ;проверяем символ
cmp al, '.' ;десятичная точка
je Point
cmp al, ',' ;или запятая
je Point
cmp al, '0' ;проверим на цифры
jb StrToFloatRet ;на выход
cmp al, '9'
ja StrToFloatRet
and ax, 0fh ;превратим в число 0-0fh
mov Digit, ax ;сохраним для сопроцессора
jcxz PartInteger ;если целая часть
cmp cx, 9 ;для дробной части разрешаем 8 знаков после запятой
je StrToFloatRet
fild Digit ;введенную цифру
fidiv Base ;делим на 10^n, где n - позиция цифры
faddp ;и складываем с нашим числом
mov eax, Base ;n = n + 1
imul c10
mov Base, eax
inc cx ;считаем цифры после запятой
jmp NextDigit
PartInteger: ;целая часть
fimul c10 ;просто умножаем на 10
fiadd Digit ;и добавляем новую цифру
jmp NextDigit
Point: ;встретилась точка
test cx, cx
jnz StrToFloatRet ;уже была - на выход
inc cx ;помечаем, что пошла дробная часть
jmp NextDigit
StrToFloatRet: ;выход
fimul Sign ;учтем знак
fstp qword ptr [di] ;и сохраним в переменной
ret
StrToFloat endp

;FC = 1 - конец строки
;FZ = 1 - разделители
CmpSep proc ;проверка на разделитель
cmp al, 0dh
je CS_eol ;конец строки
cmp al, ' '
je CS_ret ;пробел
cmp al, 9
CS_ret: ;и табуляция
clc
ret
CS_eol:
stc
ret
CmpSep endp

;рисуем линию (x1,y1)-(x2,y2) цветом color
Line proc uses di bx cx, x1:word, y1:word, x2:word, y2:word, color:byte
local i:word, \ ;для работы со сопроцессором
delta_x:word, \ ;длина проекции на ось абсцисс
delta_y:word, \ ;длина проекции на ось ординат
incx:word, \ ;приращение по X
incy:word ;приращение по Y

;определим длину проекции на ось абсцисс и шаг по оси X
mov ax, x2
sub ax, x1 ;ax=x2-x1;

;определим шаг по X (+1 если вперед, -1 если назад, 0 если не меняется)
mov incx, 0 ;пусть incx=0
test ax, ax ;ax=delta_x
jz set_delta_x ;не меняется
jg set_x_1 ;вперед?
dec incx ;назад, значит incx=-1
neg ax ;найдем ax=abs(delta_x)
jmp set_delta_x ;на сохранение
set_x_1:
inc incx ;вперед, значит incx=1;
set_delta_x:
mov delta_x, ax ;delta_x = abs(x2-x1)

;определим длину проекции на ось ординат и шаг по оси Y
mov ax, y2
sub ax, y1 ;ax=y2-y1;

;определим шаг по Y (+1 если вперед, -1 если назад, 0 если не меняется)
mov incy, 0 ;пусть incy=0
test ax, ax ;ax=delta_y
jz set_delta_y ;не меняется
jg set_y_1 ;вперед?
dec incy ;назад, значит incy=-1
neg ax ;найдем ax==abs(delta_y)
jmp set_delta_y ;на сохранение
set_y_1:
inc incy ;вперед, значит incy=1;
set_delta_y:
mov delta_y, ax ;delta_y=abs(y2-y1)

;определим большее из проекций как основное напрвление
cmp ax, delta_x ;ax=delta_y
jge from_y ;y будет основным
cmp delta_x, 0 ;проверим, чтобы не было delta_x=0 (для точки),
jz Line_ret ; иначе будет деление на 0
;delta_x>delta_y && delta_x!=0
;основное направление - по оси X
fild delta_y
fidiv delta_x ;st=k=(float)(delta_y/delta_x)

;for (int i=0;i<delta_x;i++)
xor cx, cx ;cx=i
jmp cmp_i_x ;на проверку i<delta_x
x_loop: ;тело цикла
mov i, cx ;запишем переменную цикла в память (для сопроцессора)
fld st ;st=st(1)=k
fimul i ;st=k*i
fimul incy ;st=incy*k*i
call floor ;округлим до целого в большую сторону
fistp i ;сохраним в переменной
mov ax, i ;относительный номер строки на экране
add ax, y1 ;добавим до ординаты начальной точки
mov dx, 320 ;получим индекс начала строки экрана в сегменте экрана
imul dx ; для этого умножим на длину в байтах одной стоки
mov bx, ax ;сохраним bx=y=(y1+floor(incy*k*i))*320
;посчитаем X
mov ax, incx ;X меняется ровно на шаг приращения,
imul cx ; умноженному на индекс точки
add ax, x1 ;добавим абциссу начальной точки ax=x=x1+incx*i

add ax, bx ;сложим с индексом начала строки
mov di, ax ;будем адресовать через di

mov al, color ;цвет точки
mov es:[di], al ;рисуем!

inc cx ;на следующую точку
cmp_i_x:
cmp cx, delta_x ;дошли до конца?
jl x_loop
jmp Line_ret ;на выход

from_y: ;вдоль оси Y
fild delta_x
fidiv delta_y ;st=k=(float)(delta_x/delta_y)

;for (int i=0;i<delta_y;i++)
xor cx, cx ;cx=i
jmp cmp_i_y ;на проверку i<delta_y
y_loop: ;тело цикла
mov ax, incy ;Y меняется ровно на шаг приращения,
imul cx ; умноженному на индекс точки
add ax, y1 ;добавим абциссу начальной точки ax=y=y1+incy*i
mov dx, 320 ;получим индекс начала строки экрана в сегменте экрана
imul dx ; для этого умножим на длину в байтах одной стоки
mov bx, ax ;сохраним bx=y=(y1+incy*i)*320
;посчитаем X
mov i, cx ;запишем переменную цикла в память (для сопроцессора)
fld st ;st=st(1)=k
fimul i ;st=k*i
fimul incx ;st=incx*k*i
call floor ;округлим до целого в большую сторону
fistp i ;сохраним в переменной
mov ax, i ;относительный номер строки на экране
add ax, x1 ;ax=x=x1+floor(incx*k*i)

add ax, bx ;сложим с индексом начала строки
mov di, ax ;будем адресовать через di

mov al, color ;цвет точки
mov es:[di], al ;рисуем!

inc cx ;на следующую точку
cmp_i_y:
cmp cx, delta_y ;дошли до конца?
jl y_loop
Line_ret:
fistp i ;удалим из сопроцессора k
ret
Line endp

;округление до целого в большую сторону
;округление по умолчанию, до ближайщего, не устраивает
floor proc
local CtrlWordOld:word, CtrlWordNew:word
fstcw CtrlWordOld ;сохраним управляющее слово
fclex ;сбросим исключения
mov CtrlWordNew,0763h ;установим необходимое значение управляющего слова
fldcw CtrlWordNew ;загружаем управляющее слово
frndint ;округляем st до целого
fclex ;сбросим исключения
fldcw CtrlWordOld ;восстановим старое управляющее слово
ret
floor endp

;f(x)=sin(x)+sin(2x)+sin(3x)+sin(4x)+sin(5x)
fun proc
local num:word
fld st
fsin
fxch
fld st
mov num, 2
fimul num
fsin
fxch
fld st
mov num, 3
fimul num
fsin
fxch
fld st
mov num, 4
fimul num
fsin
fxch
mov num, 5
fimul num
fsin
faddp
faddp
faddp
faddp
ret
fun endp

end
Об авторе:
"Если вы заметили, что вы на стороне большинства, —
это верный признак того, что пора меняться." Марк Твен
Неизвестный
23.11.2010, 13:46
общий
При линковке(надеюсь это так называется tlink имя.obj) пишет:
Warning: No stack
Хотя потом запускается.
Можете объяснить что это?
давно
Посетитель
7438
7205
23.11.2010, 15:09
общий
Конечно, могу Не переживайте, все нормально.
Это сообщение говорит о том, что в программе не задан стек.
В таком случае, стек назначается по-умолчанию.
Об авторе:
"Если вы заметили, что вы на стороне большинства, —
это верный признак того, что пора меняться." Марк Твен
Неизвестный
23.11.2010, 15:28
общий
[q=7438][/q]
Большое Вам спасибо)
Форма ответа