Отправляет email-рассылки с помощью сервиса Sendsay
  Все выпуски  

RFpro.ru: Ассемблер? Это просто! Учимся программировать


Хостинг портала RFpro.ru:
Московский хостер
Профессиональный ХОСТИНГ на базе Linux x64 и Windows x64

РАССЫЛКИ ПОРТАЛА RFPRO.RU

Чемпионы рейтинга экспертов в этой рассылке

Boriss
Статус: Академик
Рейтинг: 2249
∙ повысить рейтинг »
_Ayl_
Статус: Практикант
Рейтинг: 1831
∙ повысить рейтинг »
vladisslav
Статус: 6-й класс
Рейтинг: 1235
∙ повысить рейтинг »

/ КОМПЬЮТЕРЫ И ПО / Программирование / Assembler (Ассемблер)

Номер выпуска:1336
Дата выхода:13.05.2010, 11:30
Администратор рассылки:Лысков Игорь Витальевич, Модератор
Подписчиков / экспертов:275 / 60
Вопросов / ответов:2 / 2
IRC-канал по теме:#assembler

Вопрос № 178069: Здравствуйте, уважаемые эксперты. Требуется написать программу (с комментариями кода), которая чертит график распределения Максвелла с изменяющимися параметрами скорости в интервале от 0 до 4500 м/c, температура и масса молекулы для данного интерв...


Вопрос № 178233: Доброго времени суток, уважаемые эксперты! Позвольте представить на ваше рассмотрение очередной проект(предварительный бюджет 700-800 р) В общих чертах задача поставлена так: Спроектировать и запрограммировать на ассемблере регулят...

Вопрос № 178069:

Здравствуйте, уважаемые эксперты.
Требуется написать программу (с комментариями кода), которая чертит график распределения Максвелла с изменяющимися параметрами скорости в интервале от 0 до 4500 м/c, температура и масса молекулы для данного интервала принимается постоянной. Однако график не должен быть статичным, и температура и масса могла меняться пользователем.
Платформа Dos, ассемблер TASM.
Приблизительный результат можно увидеть http://chemistry.ru/course/content/models/Maxwell.html

Отправлен: 27.04.2010, 18:15
Вопрос задал: Филимонов Алексей Викторович, Посетитель
Всего ответов: 1
Страница вопроса »


Отвечает Лысков Игорь Витальевич, Модератор :
Здравствуйте, Филимонов Алексей Викторович.
Ну почему же, хоть какое-то решение... Вполне нормальное решение
Программа далеко неочевидная, пришлось повозиться...
Используемые клавиши:
Esc - выход
Enter - отработка новых значений. Обратите внимание, масса должна быть задана корректно (в программе осуществляется контроль)
Стрелки вниз/вверх - делают текущими для коррекции поля параметров
BackSpace - вытирает последний символ
Код:
;Функция плотности вероятности распределения Максвелла 
;молекул идеального газа по скоростям
;f(v)=(4/sqrt(Pi)) * (m/(2kT))^(3/2) * v^2 * exp(-(mv^2)/(2kT))
;v - скорость частицы газа, m - ее масса, T - температура газа,
;k=1.38*10^(- 23) Дж/К - постоянная Больцмана
.model tiny, C
.386

VStep equ 14 ;коэффициент для преобразования
;абсциссы в скорость
.data
m dd 3.32e-27 ;масса молекулы (водорода, для примера)
k dd 1.38e-23 ;постоянная Больцмана
a dd 0 ;m/(2kT)
b dd 0 ;4/sqrt(Pi)
T dw 250 ;темперарура в Кельвинах
fNum dw 8 ;флаг выбора, что вводится 1 - темпер, 0 - масса
iLen dw 0 ;длина введенной массы

;константы
c2 dd 2.
c4 dd 4.
c3_10_5 dd 3.e5 ;для преобразования в экранные координаты
c10 dd 10.
c100 dd 100.

s10 db '*10',0 ;надпись возле оси ординат
s4 db '-4',0 ;показатель степени там же
sTeq db 'T=',0 ;надпись для ввода температуры
sMeq db 'm=',0 ;надпись для ввода массы
sfBuf db 16 dup (?) ;буфер для ввода массы (вещественного числа)

.code
.startup

;посчитаем b=4/sqrt(Pi)
call Calc_b

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

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

main_loop:
;посчитаем a=m/(2kT)
call Calc_a

call line, 0, 190, 319, 190, 8 ;ось абсцисс
call gor, 1000, 4000, 8 ;рисочки с надписями на оси абсцисс
call line, 0, 0, 0, 190, 8 ;ось ординат
call ver, 1, 6, 8 ;рисочки с надписями на оси ординат
call PrintText, 10, 6, offset s10, 8 ;надпись '*10^-4'
call PrintText, 34, 2, offset s4, 8 ;показатель чуть выше

call Graph, 0, 319, offset CalcFun, 0bh ;график

;выводим рамочку с массой
mov ax, fNum ;инверсный флаг активности
xor ax, 8
or ax, 1
call PrFloat, 319-10*8-2, 16, offset sMeq, offset m, ax, 1

;выводим рамочку с температурой
Pr_temp:
mov ax, fNum ;флаг активного окна (более яркий)
or ax, 1 ;синий цвет, активный - голубой
call PrInt, 319-10*8-2, 2, offset sTeq, T, ax, 1

wait_key: ;цикл ожидания нажатия на клавишу
mov ah, 0
int 16h

cmp ah, 1
je exit ;Escape - выход
cmp al, 0dh
je key_Enter ;Enter - отработать новые значения
cmp ah, 50h
je key_Arrow ;стрелки - меняем активное окошко
cmp ah, 48h
je key_Arrow
cmp al, 8
je key_BS ;BackSpace - вытираем послдний символ
cmp al, '0' ;проверим на цифру
jb cmp_menus ;меньше - проверить на минус для массы
cmp al, '9'
jbe cmp_digit ;цифра '0'-'9' - на проверку, что вводим
cmp_menus:
cmp al, '.'
je cmp_massa_key ;точка - на проверку для массы
cmp al, '-'
je cmp_massa_key ;минус - на проверку для массы
or al, 20h ;сделаем маленькой буквой
cmp al, 'e' ;e - на проверку для массы
je cmp_massa_key
jmp wait_key ;все остальное игнорируем
cmp_digit: ;цифры
test fNum, 8 ;что вводим?
jnz temperature_code ;=8 - температура, как целое
cmp_massa_key: ;вводим массу
call floatCode ;отработаем
jc wait_key ;ошибка - игнорируем
call PrintText, 319-8*8-2, 16, offset sfBuf, 9 ; выведем строку jmp wait_key ;и на ожидание клавиши
temperature_code: ;вводим температуру
push ax ;умножаем старшие разряды на 10
mov ax, 10 ;и добавляем младший
mul T
pop dx
and dx, 0fh
add ax, dx
cmp ax, 1000 ;можно ввести только 3 цифры
jae wait_key
mov T, ax ;все ок, сохраняем
jmp Pr_temp ;отрисовать

key_Arrow: ;стрелки
xor fNum, 8 ;меняем активное окно
mov ax, fNum ;флаг активного окна (более яркий)
xor ax, 8
or ax, 1 ;синий цвет, активный - голубой
;выведем строку с массой
call PrintText, 319-8*8-2, 16, offset sfBuf, ax
jmp Pr_temp ;на вывод температуры

key_Enter: ;по Enter-у отрабатываем
call atof, offset sfBuf, offset m ;преобразовываем во float
test ax, ax ;проверим, ок?
jnz wait_key ;ошибка - ничего не деаем
call Graph, 0, 319, offset CalcFun,0 ;вытерем старый
jmp main_loop ;и рисуем заново

key_BS: ;BackSpace
test fN um, 8 ;что вводим?
jnz temperature_BS
call floatBS ;для массы
jc wait_key ;если ошибка
call Clear, 319-8*8-2, 16, 8 ;вытираем на экране
call PrintText, 319-8*8-2, 16, offset sfBuf, 9 ;и выводим строку из буфера
jmp wait_key ;на ожидание клавиши
temperature_BS: ;для температуры
cmp T, 0 ;в левой позиции
je wait_key ; ничего не делаем
mov ax, T ;делим на 10, удаляем младший разряд
xor dx, dx
mov cx, 10
div cx
mov T, ax ;сохраняем
jmp Pr_temp ;отрисуем

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

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

;преобразование строки во float
;параметры:
;psFloat - адрес строки
;pdFloat - адрес float
atof proc psFloat:word, pdFloat:word
uses si
local i:word
cmp iLen, 8
jne af_ret_c ;разрешаем только полностью введенное число
mov si, psFloat
;считаем три символа (точку игно рируем),
; как 3-х значное число
lodsb ;первый символ - число сотен
and al, 0fh
mov ah, 100
mul ah
mov i, ax ;накапливаем
lodsb
cmp al, '.' ;проверим на точку
jne af_ret_c
lodsb ;число десятков
and al, 0fh
mov ah, 10
mul ah
add i, ax ;накапливаем
lodsb ;число единиц
and ax, 0fh
add i, ax ;3-х значное число
fild i ;загружаем
fdiv c100 ;делим на 100
lodsb ;проверяем на 'e'
or al, 20h
cmp al, 'e'
jne af_ret_c
lodsb ;проверяем на '-'
cmp al, '-'
jne af_ret_c
lodsw ;порядок
xchg al, ah
and ax, 0f0fh
aad ;преобразуем в двоичное число
mov cx, ax ;столько раз надо разделить на 10
shr cx, 1 ;или на 100
af_div_loop:
fdiv c100 ;делим cx раз на 100
loop af_div_loop
shr ax, 1 ;если нечетное число. то
jnc af_save_m
fdiv c10 ;делим еще и на 10
af_save_m:
mov si, pdFloat ;адрес float
fstp dword ptr [si] ;сохраняем
xor ax, ax ;признак, что все ок
ret
af_ret_c:
mov ax, 1 ;признак ошибки
ret
atof endp

strlen proc pString:word ;длина строки
uses di, bx
mov di, pString
xor bx, bx
sl_loop:
cmp byte ptr [di+bx], 0 ;ищем 0
je sl_ret
inc bx ;считаем
jmp sl_loop
sl_ret:
mov ax, bx ;результат
ret
strlen endp

;BackSpace для массы
floatBS proc
mov di, iLen
test di, di
jz im_ret_c ;нельзя для первой позиции
dec di
mov iLen, di ;уменьшаем длину
mov sfBuf[di], 0 ;и закрываем 0
clc ;все ок
ret
im_ret_c:
stc ;ошибка
ret
floatBS endp

;вводим символ для массы
floatCode proc
mov di, iLen ;индекс конца строки
cmp di, 8
je fc_ret_c ;все 8 символов введены - ошибка
cmp di, 6
jae fc_067 ;позиция цифр порядка - на ввод цифры cmp di, 5
je fc_5 ;позиция минуса порядка
cmp di, 4
je fc_4 ;позиция буквы 'e'
cmp di, 3
je fc_3 ; позиция второго символа после точки
cmp di, 2
je fc_2 ;позиция первого символа после точки
cmp di, 1
je fc_1 ;позиция точки
fc_067: ;проверка на цифру
cmp al, '0'
jb fc_ret_c ;не цифра - ошибка
cmp al, '9'
ja fc_ret_c
fc_save:
mov sfBuf[di], al ;сохраняем
inc di
mov byte ptr sfBuf[di], 0 ;закрываем 0
mov iLen, di ;новая длина
clc ;все ок
ret
fc_5:
cmp al, '-'
jne fc_ret_c ;ждем только '-'
jmp fc_save
fc_4:
or al, 20h ;сделаем маленькой
cmp al, 'e'
jne fc_ret_c ;ждем только 'e'
jmp fc_save
fc_3:
or al, 20h
cmp al, 'e'
jne fc_067 ;если не 'e', то на вставку цифры
mov al, '0' ;дополним одним 0
fc_save_0:
call fc_save
mov al, 'e'
jmp fc_save ;и вставим 'e'
fc_2:
or al, 20h
cmp al, 'e'
jne fc_067 ;если н е 'e', то на вставку цифры
fc_save_00:
mov al, '0'
call fc_save ;дополним двумя 0
jmp fc_save_0
fc_1:
cmp al, '.'
je fc_save ;если точка, то вставляем
or al, 20h
cmp al, 'e'
jne fc_ret_c ;если не 'e', то ошибка
mov al, '.'
call fc_save ;дополним точкой и двумя 0
jmp fc_save_00
fc_ret_c:
stc ;ошибка
ret
floatCode endp

;вывод рамки с максимум 3-х значным целым числом
;Параметры:
;IXStart - X координата начала вывода текста
;IYStart - Y координата начала вывода текста
;pPrompt - адрес строки в начале рамки
;INum - число
;icolor1 - цвет числа
;icolor2 - цвет рамки и текста
PrInt proc IXStart:word, IYStart:word, pPrompt:word, INum:word, \
icolor1:word, icolor2:word
local sBuffer:byte:8, \
left:word, right:word, \
top:word, bottom:word

;нарисуем рамку, для этого посчитаем границы рамки
mov ax, I XStart
sub ax, 2 ;2 точки влево от начала вывода текста
mov left, ax
add ax, 8*5+4 ;5 символов по 8 точек + 2 влево + 2 вправо
mov right, ax
mov cx, IYStart
sub cx, 2 ;2 точки вверх
mov top, cx
add cx, 8+3 ;8 точек на символ + 2 вверх + 1 вниз
mov bottom, cx
call line, left, top, right, top, iColor2 ;сверху
call line, right, top, right, bottom, iColor2 ;справа
call line, left, top, left, bottom, iColor2 ;слева
inc right ;иначе почему-то
;не рисуется одна точка :(
call line, left, bottom, right, bottom, iColor2 ;снизу

call PrintText, IXStart, IYStart, pPrompt, iColor2 ;выводим подсказку
lea ax, sBuffer
call itoa, INum, ax ;преобразуем число в текст
lea ax, sBuffer
mov cx, IXStart
add cx, 8*2 ;обойдем подсказку
call Clear, cx, IYStart, 3 ;очистим 3 знакоместа
call PrintText, cx, IYStart, ax, iColor1 ;выведем 3 символа числа
ret
PrInt end p

;вывод рамки с 8-ми символьным вещественным числом
;Параметры:
;IXStart - X координата начала вывода текста
;IYStart - Y координата начала вывода текста
;pPrompt - адрес строки в начале рамки
;PFNum - адрес вещественного числа
;icolor1 - цвет числа
;icolor2 - цвет рамки и текста
PrFloat proc IXStart:word, IYStart:word, pPrompt:word, pFNum:word, \
icolor1:word, icolor2:word
local PFValue:dword, PFExp:word, \
PFw:word, \
PFleft:word, PFright:word, \
PFtop:word, PFbottom:word

;разберем число на мантиссу и десятичный порядок
lea ax, PFValue ;адрес мантиссы
lea cx, PFExp ;адрес порядка
call XTRACTR, pFNum, offset c10, ax, cx

;нарисуем рамку, для этого посчитаем границы рамки
mov ax, IXStart
sub ax, 2
mov PFleft, ax
add ax, 8*10+4 ;10 символов
mov PFright, ax
mov cx, IYStart
sub cx, 2
mov PFtop, cx
add cx, 8+3
mov PFbottom, cx
call line, PFleft, P Ftop, PFright, PFtop, iColor2
call line, PFright, PFtop, PFright, PFbottom, iColor2
call line, PFleft, PFtop, PFleft, PFbottom, iColor2
inc PFright
call line, PFleft, PFbottom, PFright, PFbottom, iColor2

;подсказка
call PrintText, IXStart, IYStart, pPrompt, iColor2
;выведем мантиссу, для этого умножим на 100 и выведем, как целое, и вставим точку
fld PFValue ;загружаем мантиссу
fmul c100 ;*100
fistp PFw ;в целое
lea ax, sfBuf ;буфер
call itoa, PFw, ax ;в строку
;вставим точку после первого знака
mov ax, word ptr sfBuf+1
mov word ptr sfBuf+2, ax
mov byte ptr sfBuf+1, '.'
;добавляем пордок
mov byte ptr sfBuf+4, 'e'
lea di, sfBuf+5 ;адрес порядка
mov ax, PFExp ;порядок
test ax, ax ;проверим на знак
jns PrExp
mov byte ptr [di], '-';минус
inc di
neg ax ;модуль
PrExp:
call itoa, ax, di ;в строку
mov cx, IXStart ;позиция вывода
add cx, 8*2 ;после подсказки
call Clear, cx, IYStart, 8 ;очищаем 8 символов числа
call PrintText, cx, IYStart, offset sfBuf, iColor1 ;вывод им
call strlen, offset sfBuf ;считаем длину строки
mov iLen, ax ;сохраняем
ret
PrFloat endp

;выводим текст
;параметры:
;xxStart - X координата начала вывода текста
;yyStart - Y координата начала вывода текста
;pStr - адрес строки
;TextColor - цвет
PrintText proc xxStart:word, yyStart:word, pStr:word, TextColor:word
uses bx, di, si
local GenTable:dword
;прочитаем в es:bp длинный адрес таблицы генератора символов
push es bp
mov ax, 1130h
mov bh, 3
int 10h
mov ax, bp
pop bp
mov word ptr GenTable, ax ;сохраним
mov word ptr GenTable+2, es
pop es
mov ax, 320 ;посчитаем адрес начала вывода
mul yyStart ;строка
add ax, xxStart ;колонка
mov di, ax ;смещение в видеобуфере
mov si, pStr ;адрес строки
PT_loop:
push di
lodsb ;очередной символ
cmp al, 0 ;конец строки
je PT_ret
push ds si ;нарисуем символ по таблице
lds bx, GenTable ;адрес таб лицы
mov ah, 0 ;байт в слово
shl ax, 3 ;на символ 8 байт
mov si, ax ;смещение в таблице генератора
mov ax, TextColor ;цвет
mov cx, 8 ;8 строк
PT_line_loop:
push cx
mov ah, [bx+si] ;маска 8 точек в строке
inc si
mov cx, 8 ;8 точек в строке
PT_column_loop:
shl ah, 1 ;смотрим очередной бит
jnc PT_column_next
mov es:[di], al ;1 - выводим точку
PT_column_next:
inc di ;на следующую точку в строке
loop PT_column_loop
add di, 320-8 ;на начало в следующей строке
pop cx ;число строк
loop PT_line_loop ;по строкам

pop si ds
pop di
add di,8 ;начало для следующего символа
jmp PT_loop ;на следующий символ

PT_ret:
pop di
ret
PrintText endp

;очистка X_count символов 8х8, начиная с (X_start, Y_Start)
Clear proc X_start:word, Y_Start:word, X_count:word
uses di, cx, ax
;находим стартовый адрес
mov ax, 320
mul Y_Start ;строка
add ax, X_start ;колонка
mov di, ax ;адрес

mov bx, X_Count ;число байт в строке равно
shl bx, 3 ; число символов * 8 байт на символ
mov dx, 320 ;приращение
sub dx, bx ; для следующей строки
mov al, 0 ;вытираем черным цветом
mov cx, 8 ;число строк
Cl_loop:
push cx
mov cx, bx ;в строке
rep stosb
add di, dx ;на следующую строку
pop cx
loop Cl_loop
ret
Clear endp

;преобразование числа NNum в строку [pSStr]
itoa proc NNum:word, pSStr:word
uses bx, di
xor cx, cx ;счетчик цифр
mov di, pSStr ;куда писать
mov bx, 10 ;делим на 10
mov ax, NNum ;число
i_div:
xor dx, dx
div bx ;в dx очередная младшая цифра
push dx ;сохраним в стеке
inc cx ;считаем
test ax, ax ;еще есть?
jnz i_div
i_save:
pop ax ;извлекаем, начиная со старшей
or al, '0' ;делаем символ
mov [di], al ;сохраняем
inc di ;для следующего
loop i_save
mov byte p tr ss:[di], 0 ;закрываем 0
ret
itoa endp

;вывод черточек с надписями на оси абсцисс, начиная с wStart до wEnd цветом clr
gor proc wStart:word, wEnd:word, clr:word
local sBuf:byte:8

mov bx, VStep ;коэффициент масштабирования
gor_loop:
mov ax, wStart
xor dx, dx
div bx
mov di, ax ;переведем в экранные координаты

;рисуем черточку (di,188)-(di,190)
call line, di, 188, di, 190, clr

;преобразуем число в строку
lea ax, sBuf
call itoa, wStart, ax
sub di, 15 ;левее черточки на 15
lea ax, sBuf
;выводим число, как текст ниже оси абсцисс
call PrintText, di, 192, ax, clr

;на следующую черточку
mov ax, wStart
add ax, 1000
mov wStart, ax
cmp ax, wEnd ;дошли до конца?
jbe gor_loop

ret
gor endp

;вывод черточек с надписями на оси ординат, начиная с wStart до wEnd цветом clr
ver proc wStart:word, wEnd:word, clr:word
local Num:word
mov ax, 30 ;вычисляем строку (от черты до черты 30 линий)
dec wEnd
mul wEnd
mov bx, ax ;номер первой линии (без смещения на 10)
mov wEnd, 0 ;будем писать снизу вверх до 0
ver_loop:
mov di, bx
add di, 10 ;номер строки + смещение 10
;черточка
call line, 0, di, 2, di, clr
mov ax, wStart
inc wStart ;для следующей надписи
add al, '0'
mov Num, ax ;надпись '1'-'6'
lea ax, Num
lea di, [di-4] ;выше на половину символа
;надпись
call PrintText, 3, di, ax, clr
sub bx, 30 ;следующая строка
cmp bx, wEnd ;дошли до верха?
jge ver_loop

ret
ver endp

;Функция вычисления плотности вероятности распределения Максвелла
;Параметр:
;x - значение абсциссы (0-319), преобразуем в скорость умножением на 14 (0-4566)
;f(v) = b * a^(3/2) * v^2 * exp(-a*v^2)
CalcFun proc x:word
local v:word, \ ;скорость = x * 14
y:word ;переменная для получения значения из сопроцессора

fld a ;a
mov ax, VStep ;14
mul x ;14*x
mov v, ax ;v=14*x
fimul v ;a*v
fimul v ;a*v^2
fld st ;st(1)=st=a*v^2
fld a ;a
fsqrt ;sqrt(a)
fmulp ;st=a^(3/2)*v^2
fmul b ;st=b*a^(3/2)*v^2
fxch ;st(1)=b*a^(3/2)*v^2; st=a*v^2
FCHS ;st=-a*v^2=z

;далее, найдем exp(-a*v^2)
fldl2e ;log(осн 2)e->st
fmulp ;st(1)*log(осн 2)e->st
fld st ;z*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)

fmulp ;st=b*a^(3/2)*v^2*exp(-a*v^2)

;промасштабируем для вывода на экран
;у нас получились значения 0 - 6*10^(-4)
;умножим на 3*10^5 и получим величины 0-180
;отнимем от 190 и получим экранные 10-190, "перевернутые" (0 сверху)
fmul c3_10_5 ;умножаем на коэффициент 3*10^5
fistp y ;сохраним в y
mov ax, 190 ;ax = 190
sub ax, y ;ax = 190 - y
ret
CalcFun endp

;рис ует график, последовательно соединяя точки отрезками
;параметры:
;xmin - левая экранная координата графика
;xmax - правая экранная координата графика
;pFun - адрес функции, вычисляющей экранную координату по вертикали
;col - цвет линий
Graph proc xmin:word, xmax:word, pFun:word, col:word
Graph_loop: ;цикл рисования отрезков
mov ax, pFun ;адрес функции
call ax, xmin ;считаем y первой точки
mov cx, ax ;сохраним в cx

mov ax, xmin ;найдем x соседней справа точки
inc ax
mov si, ax ;сохраним в sі
cmp ax, xmax ;проверим, дошли и до правого края
ja Graph_ret

mov ax, pFun ;адрес функции
call ax, si ;ax = y второй точки

test cx, cx ;проверим, чтобы не выходить за 0 (за пределы экрана)
jge Gr_ax
xor cx, cx ;отрицательные y меняем на 0
Gr_ax:
test ax, ax ;аналогично
jge Gr_draw
xor ax, ax
Gr_draw:
mov dx, ax ;когда оба 0, то игнорируем!
or dx, cx
jz Graph_next
;рисуем линию (xmin, cx)-(si,ax) цветом col
call line, xmin, cx, si, ax, col
Graph_next:
mov xmin, si ;готовим xmin для следующ его шага
jmp Graph_loop
Graph_ret:
ret
Graph endp

;рисуем линию (x1,y1)-(x2,y2) цветом color
Line proc uses di bx, 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
mo v 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

Calc_a proc ;посчитаем a=m/(2kT)
fld m
fdiv c2
fdiv k
fidiv T
fstp a
ret
Calc_a endp

Calc_b proc ;посчитаем b=4/sqrt(Pi)
fld c4
fldpi
fsqrt
fdivp
fstp b
ret
Calc_b endp

;выделяем из вещественного числа [pFloat]
;мантиссу в [pValue] и порядок в [pExp] по осн ованию [pRadix]
XTRACTR proc pFloat:word, pRadix:word, pValue:word, pExp:word
local cw:word, cwdown:word

mov di, pFloat ;адрес float
FLD dword ptr [di] ;грузим в сопроцессор val
FTST ;проверим на знак
FSTSW AX ;выгрузим слово состояния в ax
sahf ;ah в регистр флагов
setb bl ;FC = 1 -> bl=1
jz @end ;число = 0 -> на конец
FABS ;|val|
FLD1 ;1 v
mov di, pRadix ;адрес основания (10)
FLD dword ptr [di] ;r 1 v
FYL2X ;l2r v
FLD ST ;l2r l2r v
FLD1 ;1 l2r l2r v
FLD ST(3) ;v 1 l2r l2r v
FYL2X ;l2v l2r l2r v
FDIVR ;lrv l2r v
;меняем способ округления, нам надо округление вниз
FNSTCW cw ;прочитаем cw
mov AX, cw ;установим 01 для округления вниз
bts AX, 10 ;10 бит = 1
btr AX, 11 ;11 бит = 0
mov cwdown, AX ;переменная для нового cw
FLDCW cwdown ;загрузим
FRNDINT ;округлили, получили порядок (e l2r v)
mov di, pExp ;адрес слова, куда пишем порядок
FIST word ptr [di] ;[lrv) l2r v
FLDCW cw ;восстановим cw

FCHS ;-[lrv) l2r v
FMUL ;-[lrv)*l2r v
FLD ST ;-[lrv)*l2r -[lrv)*l2r v

FRNDINT ;[-[lrv)*l2r] -[lrv)*l2r v
FSUB ST(1), ST ;[-[lrv)*l2r] {-[lrv)*l2r} v
FXCH ;{-[lrv)*l2r} [-[lrv)*l2r] v
F2XM1 ;(2^{-[lrv)*l2r} - 1) [-[lrv)*l2r] v
FLD1 ;1 (2^{-[lrv)*l2r} - 1) [-[lrv)*l2r] v
FADD ;2^{-[lrv)*l2r} [-[lrv)*l2r] v
FSCALE ;r^(-e) [-[lrv)*l2r] v
FMULP ST(2), ST ;[-[lrv)*l2r] v*r^(-e)
FSTP ST ;v*r^(-e)
test bl, 1 ;проверим знак числа
jz @end
FCHS ;для отрицательного ставим знак -
@end:
mov di, pValue ;адрес, куда сохраняем
FSTP dword ptr [di] ;сохраняем мантиссу
ret
XTRACTR endp

end

-----
Удачи!

Ответ отправил: Лысков Игорь Витальевич, Модератор
Ответ отправлен: 11.05.2010, 10:09
Номер ответа: 261330
Украина, Кировоград
Тел.: +380957525051
ICQ # 234137952
Mail.ru-агент: igorlyskov@mail.ru
Абонент Skype: igorlyskov

Вам помог ответ? Пожалуйста, поблагодарите эксперта за это!
Как сказать этому эксперту "спасибо"?
  • Отправить SMS #thank 261330 на номер 1151 (Россия) | Еще номера »
  • Отправить WebMoney:

  • Вопрос № 178233:

    Доброго времени суток, уважаемые эксперты!
    Позвольте представить на ваше рассмотрение очередной проект(предварительный бюджет 700-800 р)
    В общих чертах задача поставлена так:
    Спроектировать и запрограммировать на ассемблере регулятор температуры для печи с четырьмя конфорками.
    Прошу откликнуться тех, кто готов взяться за этот проект.
    Заранее спасибо.

    Отправлен: 06.05.2010, 13:20
    Вопрос задал: Botsman, Профессионал
    Всего ответов: 1
    Страница вопроса »


    Отвечает Лысков Игорь Витальевич, Модератор :
    Здравствуйте, Botsman.
    Вот программа, реализующая упрощенную модель управления печью
    Код:
    ;Программа регулятора температуры для печи с четырьмя конфорками

    ;Считаем, что для управления режимами плиты используется биты порта p1 (активный 0)
    ;11111110b - 0/1 - включить/выключить индикатор нагрева конфорки 1
    ;11111101b - 0/1 - включить/выключить индикатор нагрева конфорки 2
    ;11111011b - 0/1 - включить/выключить индикатор нагрева конфорки 3
    ;11110111b - 0/1 - включить/выключить индикатор нагрева конфорки 4
    ;11101111b - 0/1 - включить/выключить нагрев нагрев конфорки 1
    ;11011111b - 0/1 - включить/выключить нагрев нагрев конфорки 2
    ;10111111b - 0/1 - включить/выключить нагрев нагрев конфорки 3
    ;01111111b - 0/1 - включить/выкл ючить нагрев нагрев конфорки 4
    ;режимы порта будем хранить в регистре r7
    ;посылая r7 в порт P1, отрабатываем режимы для всех конфорок сразу

    stack equ 60h ;стек
    value equ 30h ;начальный адрес для переменных 30H-33h с ожидаемой
    ; температурой для каждой из конфорок.
    ;Предполагаем, что температура в пределах 0-255
    work equ 40h ;переменная, необходимая для сравнения температур

    key equ 0 ;начальный адрес в памяти (0-3) для считывания состояния
    ;каждой из четырех ручек,
    ;(это могут быть физически, например, четыре регистра)
    ;возможны четыре положения каждой из ручек, которые
    ;определяются битами D0-D3:
    ;11111110b - положение 0 - выключить
    ;11111101b - положение 1 - нагреть условно до 80 градусов
    ;11111011b - положение 2 - нагреть условно до 100 градусов
    ;11110111b - положение 3 - нагреть условно до 120 градусов
    temp equ 20h ;начальный адрес в памяти (20h-23h) для считывания
    ; температуры с температурных датчиков.

    temp_0 equ 0 ;условные температуры для 4-х положений
    temp_1 equ 80
    temp_2 equ 100
    temp_3 equ 120
    ;начало программы с адреса 0
    mov sp,#stack ;инициируем вершину стека, изначально sp=#7
    call init ;все выключаем
    main:
    mov p1,r7 ;отработать режимы

    ;читаем положение ручек управления
    mov dptr,#key ;начальный адрес для считывания положения ручек
    mov r0,#value ;начальный адрес массива переменных, куда запишем ожидаемые
    ; значения температуры
    mov r3,#0feh ;маска для бита в байте режимов (r7)
    mov r2,#4 ;счетчик цикла (четыре конфорки)
    KeyLoop:
    call GetKey ;отработаем для одной конфорки
    djnz r4,KeyLoop ;r4=r4-1; циклим, пока r4!=0

    ;проверяем температуры конфорок
    mov dptr,#temp ;начальный адрес для считывания значения термодатчиков
    mov r0,#value ;начальный адрес массива переменных, где ожидаемые
    ; значения температуры
    mov r3,#0feh ;ма ска для бита в байте режимов (r7)
    mov r2,#4 ;счетчик цикла (четыре конфорки)
    TempLoop:
    call GetTemp ;отработаем для одной конфорки
    djnz r4,TempLoop ;r4=r4-1; циклим, пока r4!=0

    jmp main ;на повтор

    ;- подпрограммы ------------------------------------------
    init: ;начальная инициализация
    mov r7,#0ffh ;режимы - все выключить

    mov r0,#value ;обнулим, на всякий случай, массив температур
    mov r4,#4
    init_loop:
    mov @r0,#temp_0 ;пишем по адресу в r0 значение temp_0
    inc r0 ;на следующий адрес
    djnz r4,init_loop ;по всем
    ret

    ;считываем значение положения одной ручки
    GetKey:
    movx a,@dptr ;читаем память с состоянием ручки
    jb acc.0,GK_1 ;если бит D0=1, то проверяем дальше
    ;положение 0 - выключить
    mov @r0,#temp_0 ;запишем значение температуры 0 (на всякий случай)
    ;по большому счету, это значение не используется,
    ;положение выключить отрабатывается по-особому,
    ;но и не помешает...

    ;для положения 0 гасим индикатор и выключаем обогреватель...
    mov a,r3 ;a = маске для включения индикатора (нап ример, 0feh)
    swap a ;маска бита в старшей тетраде для обогревателя (0efh)
    anl a,r3 ;маска включения для индикатора и обогревателя (0eeh)
    cpl a ;маска для выключения (11h)!
    orl a,r7 ;выключаем режимы
    mov r7,a ;сохраняем
    jmp GK_ret ;на выход
    GK_1:
    jb acc.1,GK_2 ;проверяем бит D1 положения 1, если D1=1, то проверяем дальше
    mov @r0,#temp_1 ;значение ожидаемой температуры положения 1
    jmp GK_IndOn ;и на общую часть отработки
    GK_2:
    jb acc.2,GK_3 ;проверяем бит D2 положения 2, если D2=1, то проверяем дальше
    mov @r0,#temp_2 ;значение ожидаемой температуры положения 2
    jmp GK_IndOn
    GK_3:
    jb acc.3,GK_ret ;проверяем бит D3 положения 3, если D3=1, то на выход
    ; (если ничего не встретилось, значит ручка в
    ; промежуточном состоянии, например, крутится)
    mov @r0,#temp_3 ;значение ожидаемой температуры положения 3
    ;общая часть отработки
    GK_IndOn: ;включаем индикацию нагрева
    mov a,r7 ;старое с остояние
    anl a,r3 ;накладываем маску по "и"
    mov r7,a ;сохраняем

    GK_ret: ;коррекция маски и адресов для следующей конфорки
    mov a,r3 ;сдвинем маску
    setb c ;чтобы "вдвинуть" единицу
    rlc a ;сдвигаем влево на 1 бит, С=1 попадает в младший
    mov r3,a ;сохраняем

    inc dptr ;адрес состояния ручек
    inc r0 ;адрес для хранения значения ожидаемой температуры
    ret

    ;проверяем показания термодатчиков и управляем обогревателями
    GetTemp: ;проверим, надо ли управлять обогревателем
    mov a,r3 ;маска интересуемой конфорки
    orl a,r7 ;"или" с состоянием вкл/выкл индикатора
    xrl a,r3 ;xor даст 0, только если оба бита нуля (остальные = 1)
    ;т.е. нужная конфорка и она должна греть!
    jz GT_Temp ; на управление

    GT_off: ;выключаем обогреватель
    mov a,r3 ;маска конфорки
    swap a ;на место управления обогревателем (меняем местами тетрады)
    cpl a ;0 -> 1
    orl a,r7 ;выключаем
    mov r7,a ;сохраняем
    jmp GK_ret
    GT_Temp: ;управляем
    movx a,@dptr ;читаем показание термодатчика в aккумулятор
    mov work,@r0 ;читаем ожидаемое значение температуры
    cjne a,work,GT_cmp ;сравниваем, != идем на метку
    jmp GK_ret ;равно - ничего не делаем
    GT_cmp: ;проверим, меньше или больше
    jnc GT_off ;если показания термодатчика больше, то выключаем
    GT_on: ;если меньше, то включаем
    mov a,r3 ;маска конфорки
    swap a ;на место управления обогревателем (меняем местами тетрады)
    anl a,r7 ;включаем
    mov r7,a ;сохраняем

    jmp GK_ret ;на коррекцию маски и адресов для следующей конфорки

    end

    -----
    Удачи!

    Ответ отправил: Лысков Игорь Витальевич, Модератор
    Ответ отправлен: 12.05.2010, 13:25
    Номер ответа: 261350
    Украина, Кировоград
    Тел.: +380957525051
    ICQ # 234137952
    Mail.ru-агент: igorlyskov@mail.ru
    Абонент Skype: igorlyskov

    Оценка ответа: 5
    Комментарий к оценке:
    Спасибо! Подробности - письмом ;)

    Вам помог ответ? Пожалуйста, поблагодарите эксперта за это!
    Как сказать этому эксперту "спасибо"?
  • Отправить SMS #thank 261350 на номер 1151 (Россия) | Еще номера »
  • Отправить WebMoney:

  • Оценить выпуск »
    Нам очень важно Ваше мнение об этом выпуске рассылки!

    Задать вопрос экспертам этой рассылки »

    Скажите "спасибо" эксперту, который помог Вам!

    Отправьте СМС-сообщение с тестом #thank НОМЕР_ОТВЕТА
    на короткий номер 1151 (Россия)

    Номер ответа и конкретный текст СМС указан внизу каждого ответа.

    Полный список номеров »

    * Стоимость одного СМС-сообщения от 7.15 руб. и зависит от оператора сотовой связи. (полный список тарифов)
    ** При ошибочном вводе номера ответа или текста #thank услуга считается оказанной, денежные средства не возвращаются.
    *** Сумма выплаты эксперту-автору ответа расчитывается из суммы перечислений на портал от биллинговой компании.


    © 2001-2010, Портал RFpro.ru, Россия
    Авторское право: ООО "Мастер-Эксперт Про"
    Автор: Калашников О.А. | Программирование: Гладенюк А.Г.
    Хостинг: Компания "Московский хостер"
    Версия системы: 2010.6.14 от 03.03.2010

    В избранное