Консультация № 184132
03.10.2011, 05:28
0.00 руб.
03.10.2011, 12:55
0 2 1
Здравствуйте! Прошу помощи в следующем вопросе:

Построить график по функции и показать результат.
Формула:



Использование только Tasm модель памяти tiny размер переменных dw.

Обсуждение

давно
Посетитель
7438
7205
03.10.2011, 16:18
общий
это ответ
Здравствуйте, Посетитель - 382088!

Обращаю Ваше внимание, что выражение sin(x^3)^(2x) я рассматриваю, как (sin(x^3)^2)^x,
т.е. сначала возвожу в квадрат, а потом в степень х

Из-за большого разброса значений по X и Y применил разное масштабирование по осям абсцисс и ординат


[code h=207];Программа, одновременно считающая и рисующая часть графика по формуле.
;Формула: y=sin(x^3)^(2x) * 10^(10/sqrt(2))-1 на отрезке a=-1.3,b=-1.45
;Размер переменных : dw; Модуль памяти : tiny ; Использование TASM.

POWER MACRO ;Макро для вычисления степени st^st(1)
fyl2x ;st=st(1)*log[2]st
fld st ;x*log[2]e->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 ;exp = st * (2 в степени st(1))
fstp st(1) ;чтобы убрать значение из st(1)
ENDM

.model tiny, C ; модель памяти и порядок вызова параметров
.386
.code ; сегмент кода
.startup ; точка входа

mov ax, 0013h ; vga 320x200x256
int 10h

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

KOEF_X equ 100 ;единица по X соответствует KOEF_X точкам на экране
KOEF_Y equ 10 ;единица по Y соответствует KOEF_Y точкам на экране
Y_OF_AXIS_X equ 180 ;ордината оси абсцисс
X_OF_AXIS_Y equ 319 ;абсцисса оси ординат

main:
finit ;проинициируем сопроцессор

;ось ординат (X_OF_AXIS_Y,0)-(X_OF_AXIS_Y,199) цвета 0ch
call line, X_OF_AXIS_Y, 0, X_OF_AXIS_Y, 199, 0ch
;нарисуем "рисочки" для y = 1, 2, 3,...
mov si, KOEF_Y ;Y рисочки
ris_y_loop:
call line, X_OF_AXIS_Y-1, si, X_OF_AXIS_Y, si, 0ch
add si, KOEF_Y ;на следующую
cmp si, 200
jl ris_y_loop

;ось абсцисс (0,Y_OF_AXIS_X)-(319,Y_OF_AXIS_X) цвета 0ch
call line, 0, Y_OF_AXIS_X, 319, Y_OF_AXIS_X, 0ch
;нарисуем "рисочки" для x = -1, -2, -3,...
mov si, X_OF_AXIS_Y-KOEF_X ;Х рисочки
ris_x_loop:
call line, si, Y_OF_AXIS_X, si, Y_OF_AXIS_X+2, 0ch
sub si, KOEF_X ;на следующую
jge ris_x_loop

;вывод графика -1.45<=x<=-1.3, y=fun(x) цвета 0bh
call Graph, offset b, offset a, offset fun, 0bh

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

;выход в DOS
Exit:
mov ax, 0003h ; назад в текстовый режим
int 10h

mov ax, 4c00h ; bye-bye
int 21h

;подпрограммы

;Функция вычисления y по x, x = ST, т.е. в стеке сопроцессора
;возвращает экранную координату по оси Y
;Х остается в стеке сопроцессора для последующего вычисления
fun proc
local y:word ;переменная для получения значения из сопроцессора
fld st ;для показателя степени

fld st(1) ;st=x,st(1)=x,st(2)=x
fmul st,st(1) ;st=x^2,st(1)=x,st(2)=x
fmul st,st(1) ;st=x^3,st(1)=x,st(2)=x
fsin ;st=sin(x^3),st(1)=x,st(2)=x

;возведем в степень sin(x^3)^(2x) = (sin(x^3)^2)^x
fld st ;st=sin(x^3),st(1)=sin(x^3),st(2)=x,st(3)=x
fmulp st(1) ;st=sin(x^3)^2,st(1)=x,st(2)=x
POWER ;st=(sin(x^3)^2)^x,st(1)=x

fld st(1) ;st=x,st(1)=(sin(x^3)^2)^x,st(2)=x
fild c2 ;st=2,st(1)=x,st(2)=(sin(x^3)^2)^x,st(3)=x
fsqrt ;st=sqrt(2),st(1)=x,st(2)=(sin(x^3)^2)^x,st(3)=x
fdivp st(1) ;st=x/sqrt(2),st(1)=(sin(x^3)^2)^x,st(2)=x

fild c10 ;st=10,st(1)=x/sqrt(2),st(2)=(sin(x^3)^2)^x,st(3)=x

POWER ;st=10^(x/sqrt(2)),st(1)=(sin(x^3)^2)^x,st(2)=x

fmulp ;st=10^(x/sqrt(2)) * (sin(x^3)^2)^x,st(1)=x

fld1 ;st=1,st(1)=10^(x/sqrt(2))*(sin(x^3)^2)^x,st(2)=x
fsubp st(1),st ;y=st=10^(x/sqrt(2))*(sin(x^3)^2)^x-1,st(1)=x

fimul coef_y ;масштабирование, st=coef_y*y,st(1)=x
fistp y ;сохраним в y, y=int(coef_y*y),st=x
mov ax, Y_OF_AXIS_X ;ордината оси абсцисс
sub ax, y ;ax = 0 - y
ret
fun endp

;рисует график, последовательно соединяя точки отрезками
;параметры:
;xmin - адрес вещественной переменной левого значения интервала
;xmax - адрес вещественной переменной правого значения интервала
;pFun - адрес функции, вычисляющей экранную координату по вертикали
;col - цвет линий
Graph proc xmin:word, xmax:word, pFun:word, col:word
local step:dword ;шаг по Х
local cnt:word ;переменная для получения из сопроцессора числа точек по Х
local xekr:word ;экранная координата по Х

;посчитаем количество точек по Х
mov si, xmax ;адрес Xmax
fld dword ptr [si] ;st=Xmax=b
mov si, xmin ;адрес Xmin
fsub dword ptr [si] ;st=b-a
fld st ;st=b-a,st(1)=b-a
fimul coef_x ;длина в точках
fist cnt ;сохраним число точек = числу отрезков
fdivp st(1), st ;шаг по Х = (b-a) / число точек
fstp step ;сохраним шаг по Х

fld dword ptr [si] ;st=a, начальное значение Х
fimul coef_x ;в точках
fistp xekr ;сохраним в xekr
add xekr, X_OF_AXIS_Y ;привяжем экранную координату Х к оси Y
mov cx, cnt ;число отрезков
fld dword ptr [si] ;st=a, начальное значение Х
Graph_loop: ;цикл рисования отрезков
call word ptr [pFun] ;считаем y первой точки
mov bx, ax ;сохраним в bx

fadd step ;st=st+step
call word ptr [pFun] ;ax = y второй точки

cmp bx, 199
jle Gr_ax_max
mov bx, 199
Gr_ax_max:
cmp ax, 199
jle Gr_bx_min
mov ax, 199
Gr_bx_min:
test bx, bx ;проверим, чтобы не выходить за 0 (за пределы экрана)
jge Gr_ax
xor bx, bx ;отрицательные y меняем на 0
Gr_ax:
test ax, ax ;аналогично
jge Gr_draw
xor ax, ax
Gr_draw:
mov dx, ax ;когда оба 0 или 199, то игнорируем!
or dx, bx
jz Graph_next
cmp dx, 199
je Graph_next

;рисуем линию (xekr, cx)-(xekr+1,ax) цветом col
mov dx, xekr
inc dx
call line, xekr, bx, dx, ax, col

Graph_next:
inc xekr ;готовим xekr для следующего шага
loop Graph_loop

fstp st ;уберем из стека сопроцессора последний Х
ret
Graph 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 to_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
to_Line_ret:
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

.data
coef_x dw KOEF_X ;коэффициент масштабирования по X
coef_y dw KOEF_Y ;коэффициент масштабирования по Y
a dd -1.3
b dd -1.45
c2 dw 2
c10 dw 10

END
[/code]
Об авторе:
"Если вы заметили, что вы на стороне большинства, —
это верный признак того, что пора меняться." Марк Твен
давно
Посетитель
7438
7205
03.10.2011, 16:20
общий
Бесплатный вопросик... Ну-ну...
Об авторе:
"Если вы заметили, что вы на стороне большинства, —
это верный признак того, что пора меняться." Марк Твен
Форма ответа