Консультация № 183219
17.05.2011, 20:35
47.74 руб.
17.05.2011, 23:41
0 7 1
Здравствуйте, уважаемые эксперты! Прошу вас ответить на следующий вопрос:
Помогите решить 2 задачи на ассемблере, ввиде 1 программы для "14" варианта(номера):

Вот условие:


Требуется:
1)каждую функцию реализовать в ввиде отдельной подпрограммы
2)Использовать наиболее подходящий способ передачи аргумента
3)Сформировать макрокоманды, если вычисления встречаются более 1 раза.
4)Организовать ввод данных с клавиатуры и вывод результата на экран.

Расставте комментарии к коду, чтобы было легче ориентироваться и прежде всего понять код.

Спасибо.

Обсуждение

Неизвестный
17.05.2011, 20:36
общий
Извиняюсь вот изображение!

давно
Старший Модератор
31795
6196
17.05.2011, 20:53
общий
Цитата: памятка
Не секрет, что разные ОС и разные ассемблеры требуют разного оформления программ.
Поэтому, при задании вопроса обязательно указывайте:
Какой процессор используется. Если х86-совместимый, то можно не указывать. За исключением случая,
когда необходимы специальные возможности (например, MMX, 3D-NOW!, SSE, SSE2, ...);
Какая платформа (для х86-совместимых: ДОС/Windows/*nix);
Какой ассемблер предпочителен (TASM, MASM, NASM, FASM, WASM, ...);
Для программ операционной системы ДОС предпочтительная модель памяти (TINY, SMALL, LARGE, ...),
а также тип процессора (от этого зависит использование команд, 32-битных регистров и т.п.).
В случае работы в графическом видеорежиме, не забудьте указать его номер или параметры (напр., 640x480 x 16 цветов);
Размер переменных (байт, слово, двойное слово), с которыми должна работать программа,
а также являются они числами со знаком или без знака.


Судя по формулам тут нужен FPU? правильно?
Об авторе:
Мне безразлично, что Вы думаете о обо мне, но я рад за Вас - Вы начали думать.

Неизвестный
17.05.2011, 22:19
общий
Извините забыл.

Кратко о системе:
Win xp sp2
процесор Intel(R) Core 2 Solo U3500

Предпочтительный ассемблер TASM

Размеры переменных : на усмотрение, какие требуются, такими и оперировать; если в выражении возможны отрицательные числа, то конечно учитывать знак.
давно
Посетитель
7438
7205
18.05.2011, 00:14
общий
Это пример на понимание команд сопроцессора.
Будьте готовы к обилию оных
Да и ввод/вывод чисел с плавающей точкой простым не назовешь...
Об авторе:
"Если вы заметили, что вы на стороне большинства, —
это верный признак того, что пора меняться." Марк Твен
Неизвестный
18.05.2011, 10:36
общий
Адресаты:
Буду готов!
давно
Посетитель
7438
7205
18.05.2011, 10:48
общий
Если никто не напишет раньше, то сделаю, только чуть позже. Пока, увы, занят...
Об авторе:
"Если вы заметили, что вы на стороне большинства, —
это верный признак того, что пора меняться." Марк Твен
давно
Посетитель
7438
7205
20.05.2011, 13:04
общий
это ответ
Здравствуйте, Юдин Евгений Сергеевич!
Больше всего проблем не с самим вычислением, а с вводом/выводом
Я адаптировал функции из пакета MASM32.
Удачи Вам в разборе кода!

[code h=200]
.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

;ln(st)
LN MACRO
fld1 ;1
fxch ;st(1)<->st
fyl2x ;st=1*log[2]st=log[2]x
fldl2e ;st=log[2]e
fdivp ;st=st(1)/st=log[2]x/log[2]e=lnx
ENDM

;ln^3(1+cos|z-1|)
CALC_ln3 MACRO
fld z ;z->st
fld1 ;1
fsubp ;st-1->st
fabs ;abs(st)
fcos ;st=cos(|st|)
fld1 ;1
fadd ;st=st+1
LN ;ln(st)
fld st ;st(1)=st
fld st ;st(2)=st(1)=st
fmulp ;st^2
fmulp ;находим ln(1+cos|z-1|)^3
ENDM

cseg segment para use16 public 'code'
assume cs:cseg, ss:dseg, ds:dseg, es:dseg
start:
mov ax, dseg
mov ds, ax ;настроим сегментные регистры
mov es, ax ;на сегмент dseg
mov ss, ax
mov sp, 0fffeh ;стек в вершине
;введем данные
lea dx, sEnterX ;строка приглашения
call GetFloat ;вводим вещественное, результат в st
fstp x ;сохраним в x
lea dx, sEnterY
call GetFloat
fstp y ;y
lea dx, sEnterZ
call GetFloat
fstp z ;z

call calc_a ;получаем в st расчет a
fst a ;сохраним в a
push offset sNum ;адрес буфера для числовой строки
call FloatToStr ;преобразовываем
lea si, sA ;строка пояснения 'a='
lea bx, sNum ;числовая строка
call prStr ;выводим

call calc_b ;получаем в st расчет b
fst b
push offset sNum
call FloatToStr
lea si, sB
lea bx, sNum
call prStr ;аналогично

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

GetFloat proc ;ввод вещественного числа
mov ah, 9
int 21h ;выводим приглашение из dx
lea dx, sBuf
mov ah, 0ah
int 21h ;вводим строку
lea ax, string ;адрес строки
push ax ;параметр в стек
call StrToFloat ;преобразовываем в вещественное
ret
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
CALC_ln3 ;ln^3(1+cos|z-1|)
fld y ;st = y
fmul c071 ;st = 0.71y
faddp ;st = ln^3(1+cos|z-1|)+0.71y

fld1 ;1
fld x ;x
fld st ;x
fld st ;x
fmulp ;x*x
fmulp ;x*x*x
fdivp st(1), st ;x^-3
fmul c2 ;2x^-3

fld z ;z
fmul y ;z*y
fmul c0005 ;0.005zy
faddp ;2x^-3 + 0.005zy
fdiv ;(ln^3(1+cos|z-1|)+0.71y)/
ret ; (2x^-3 + 0.005zy)
calc_a endp

;расчет b
;результат в st
calc_b proc
CALC_ln3 ;ln^3(1+cos|z-1|)
fadd c5 ;ln^3(1+cos|z-1|) + 5
ret
calc_b endp

;П/п конвертации float <-> ASCII
;Можно особо не вникать :). Переделан код из пакета MASM32

; вспомогательная п/п конвертации float в ASCII.
; результат всегда - 18 байт с лидирующими нулями
;
; На входе: ST(0) = число для конветации, 0 <= ST(0) < 1E19.
; szTemp = 18-байтовый буфер.
;
; На выходе: szTemp = сконвертированный результат.

FloatToBCD PROC
push bp
mov bp, sp
sub sp, 10 ;выделим буфер в стеке
push si di

; инструкция fbstp конвертирует число из st в упакованное BCD число
; из 10 байт, 2 цифры на байт
; знак в старшем полубайте мы игнорируем

fbstp [bp-10] ;[bp-10] адрес временного буфера

; распаковываем BCD в ASCII.

lea si, [bp-2] ;BCD, начинаем с правого края, знаковый игнорируем

lea di, szTemp ;сюда распакуем
mov cx, 9 ;9 байт

convertBCDloop:
mov al, [si] ; xxxx xxxx AAAA BBBB
dec si ;сдвигаем влево
rol ax, 12 ; BBBB xxxx xxxx AAAA
rol ah, 4 ; xxxx BBBB xxxx AAAA
and ax, 0f0fh ; 0000 BBBB 0000 AAAA
add ax, 3030h ; 3B3A
mov [di], ax ;пишем
add di, 2
loop convertBCDloop

pop di si
mov sp, bp
pop bp
ret
FloatToBCD ENDP

sBuffer equ word ptr [bp+4] ;параметр - адрес буфера
iExp equ word ptr [bp-2] ;експонента
stat equ word ptr [bp-4] ;сохрпненные режимы сопроцессора
mystat equ word ptr [bp-6] ;устанавливаемые режимы сопроцессора

;преобразовываем вещественное из st в строку, адрес которой в стеке
FloatToStr PROC
push bp
mov bp, sp
sub sp, 6 ;под 3 переменные
push si di

;инициализация сопроцессора
fclex
fstcw stat
mov mystat, 027fh
fldcw mystat

mov di, sBuffer ;буфер

;прверим на 0
ftst ;st = 0?
fstsw ax ;флаги в ax
sahf ;из ah в PSW
jnz floatNotZero ;можно анализировать

mov ax, '0' ;для 0, запишем '0',0
stosw
jmp FTSRet

floatNotZero: ;что-то есть
jg floatPositive ;число положительное?

fabs ;взять модуль числа
mov byte ptr [di], '-' ; нарисуем знак миеус
inc di ;продвигаем указатель

floatPositive: ;положительное число
fld st(0) ;st(1) = st(0)

;найдем ближайшую снизу степень 10
;мы не можем добиться большой точности из-за округления
;
;log2(Y) x log10(2) = log10(fpin)

fxtract ; ST => мантисса, експонента
fstp st(0) ; мантиссу убираем
fldlg2 ; push log10(2)
fmulp st(1), st ; ST = log10(fpin), fpin
fistp iExp ; ST = fpin

; 8-байтовые вещественные числа формата double могут обеспечить только
; 16 точных знаков после запятой (вообще говоря, 15.9)
; Поэтому для чисел только с точкой, без порядка, ограничим диапозон 1E17

cmp iExp, 16 ;проверяем порядок
jae eForm ;на формирование числа с порядком

;проверим, целое ли число?
fld st(0) ; ST(1) = ST
frndint ; округляем
fcomp st(1) ; сравниваем
fstsw ax
sahf
jnz eForm ;не целое - формируем число с порядком

; Число целое - просто конвертируем
call FloatToBCD

; Найдем начало числа в 18-байтном буфере
mov si, 17
mov cx, iExp
sub si, cx
inc cx
lea si, szTemp[si]

; Уберем возможный лидирующий 0
cmp byte ptr [si], '0'
jne CopyNum
inc si
dec cx

; Копируем числовую строку в результирующую строку
CopyNum:
rep movsb
jmp FTSRet

; Используем формат [-]d.ddddddE+ddd.
; Ограничимся 7 цифрами
eForm:
mov ax, 6
sub ax, iExp ;выравниваем експоненту
call PowerOf10 ;умножаем на 10^ax

; Имеем или >= 7 цифр, или <
; Определимся
fcom ten7 ;сравним с 1.0e6
fstsw ax
sahf
jnc convertBCD ;>= 7 - на конвертацию
fmul ten ;нет - добавим еще один разряд
dec iExp

; Конвертируем в BCD
convertBCD:
call FloatToBCD

lea si, szTemp+11 ; адрес первых 7 байт мантиссы

; Если экспонента между -1 и 6, то можно обойтись без научной нотации
mov cx, iExp
cmp cx, -1
jl scientific
cmp cx, 6
jg scientific

; Будем копировать cx+1 цифр, точку и оставшиеся 6-cx цифр
; Если экспонента 0, добавим лидирующий 0
cmp cx, -1
jne formNum
mov byte ptr [di], '0'
inc di
formNum:
inc cx
rep movsb
mov byte ptr [di], '.'
inc di
mov cx, 6
sub cx, iExp
rep movsb

; уберем незначащие завершающие нули
TrimOffZeros:
cmp byte ptr [di-1], '0'
jne CmpPoint
dec di
jmp TrimOffZeros

; Если убрали до точки, то уберем и ее тоже
CmpPoint:
cmp byte ptr [di-1], '.'
jne ToExit
dec di

; Готово!
ToExit:
jmp FTSRet

; копируем мантиссу, сначала одну цифру, потом точку, потом оставшиеся 6 цифр
scientific:
movsb ; первая цифра
mov byte ptr [di], '.' ; точка
inc di
movsw ; 6 цифр после точки
movsw
movsw

; уберем незначащие завершающие нули
TrimOffZeros2:
cmp byte ptr [di][-1], '0'
jne exponent
dec di
jmp TrimOffZeros2

; формируем порядок
exponent:
mov byte ptr [di], 'e' ; экспонента
mov ax, iExp
test ax, ax ; знак
jl negativeExp
mov byte ptr [di][1], '+'
jmp formExp
negativeExp:
mov byte ptr [di][1], '-'
neg ax

formExp: ; преобразуем в три цифры
mov cx, 10

xor dx, dx
div cx
add dl, '0'
mov [di][4], dl ; единицы

xor dx, dx
div cx
add dl, '0'
mov [di][3], dl ; десятки

xor dx, dx
div cx
add dl, '0'
mov [di][2], dl ; сотни

add di, 5 ; продвинем указатель

; завершаем строку и выходим
FTSRet:
mov byte ptr [di], 0
fldcw stat ; восстанавливаем состояние
fwait ; сопроцессора
pop di si
mov sp, bp
pop bp
ret 2
FloatToStr ENDP

; Умножение числа на 10^ax (для внутреннего употребления)
;
; На входе: AX = степень 10, -4932..4932.
; ST(0) = число
; На выходе: ST(0) = число x 10^ax

PowerOf10 PROC
push si di
mov si, 10 ;длина одного элемента в таблице
mov cx, ax ;сохраним знак числа
mov bx, ax ;сохраним число
test ax, ax ;проверим на знак
jge PONext
neg bx ;bx = |ax|
PONext:
fld1 ;1
;идем по разрядам
mov al, bl
and ax, 0fh ;единицы
jz tensDigit ;0 - ничего не делаем
mul si ;*длину элемента тавлицы
mov di, ax ;di - адрес множителя в таблице
fld ten_1[di-10] ;-10 из-за того, что таблица начинается с 1
fmulp st(1), st ;умножаем
tensDigit:
mov al, bl
shr al, 4
and ax, 0fh ;десятки (16-ричные)
jz hundredDigit
mul si
mov di, ax
fld ten_16[di-10]
fmulp st(1), st
hundredDigit:
mov al, bh
and ax, 1fh ;сотни (16-ричные)
jz setSign
mul si
mov di, ax
fld ten_256[di-10]
fmulp st(1), st
setSign:
test cx, cx ;учтем знак порядка
jl negativeSign
fmulp st(1), st ;x^a
jmp PORet
negativeSign:
fdivp st(1), st ;1/x^|ax|
PORet:
pop di si
ret
PowerOf10 ENDP

;
; Конвертация строки в вещественное число
; Вход: адврес строки - параметром в стеке
; Выход: число в ST
szIn equ word ptr [bp+4] ;параметр - адрес строки
sign equ byte ptr [bp-1] ;знак мантиссы
expsign equ byte ptr [bp-2] ;знак порядка
decimal equ word ptr [bp-4] ;позиция точки
stat equ word ptr [bp-6] ;старое состояние сопроцессора
temp equ word ptr [bp-8] ;переменная

StrToFloat PROC
push bp
mov bp, sp
sub sp, 8 ;выделим место в стеке под переменные
push si di

xor ax, ax ;пока считаем, что все положительное
mov sign, al
mov expsign, al
mov decimal, -1 ;без точки

fstcw stat ;настроим режим сопроцессора
mov temp, 027fh
fldcw temp

; Определимся со знаком
mov si, szIn ;адрес строки
mov al, [si]

cmp al, '+' ;указан +?
jne STFCmpMenus
inc si ;сместим si
mov al, [si] ;читаем следующий
jmp STFCmpZero
STFCmpMenus:
cmp al, '-' ;-?
jne STFCmpZero
inc si ;сместим si
mov sign, 1 ;пометим, что отрицательное
mov al, [si] ;читаем следующий

STFCmpZero:
cmp al, 0 ;нулевая строка?
je STFExit

; Инициализация сопроцессора
fclex
xor bx, bx ;число цифр до точки
fldz
xor cx, cx ;экспонента


;основной цикл анализа

; si = адрес строки
; al = очередной символ
; bx = число цифр до точки
; cx = экспонента
; ST(0) = аккумулятор

cvtloop:
cmp al, 'E' ;экспонента?
je doExponent
cmp al, 'e'
je doExponent

cmp al, '.' ;точка?
jne cvtDigit
mov decimal, bx ;запомним позицию точки
jmp cvtNext ;и на продолжение

cvtDigit: ;цифры
sub al, '0' ;конвертируем ASCII в число
jb STFFinish ;не цифра - прекращаем
cmp al, 9
ja STFFinish ;не цифра - прекращаем
mov temp, ax ;очередной разряд
fmul ten ;d *= 10
fiadd temp ;d += очередной разряд
inc bx ;инкремент числа цифр

cvtNext:
inc si ;на следующий символ
mov al, [si] ;читаем его
test al, al ;проверяем на 0
jnz cvtloop
jmp STFFinish ;строка кончилась

; Получили мантиссу в ST
; Займемся порядком

; si = адрес строки
; al = очередной символ
; bx = счетчик цифр
; cx = экспонента
; ST(0) = мантисса

doExponent:
inc si
mov al, [si] ;очередной символ
test al, al ;конец?
jz STFFinish

; Проверим порядок на знак
cmp al, '+'
jne expCmpMenus
inc si
mov al, [si]
jmp expCmpZero
expCmpMenus:
cmp al, '-'
jne expCmpZero
inc si
mov expsign, 1 ;порядок отрицательный
mov al, [si]
expCmpZero:
test al, al ;конец?
jz STFFinish

expLoop: ;конвертируем порядок в число
sub al, '0'
jb STFFinish ;нецифра - завершаемся
cmp al, 9
ja STFFinish
xchg ax, cx
mov dx, 10
imul dx
add cx, ax ;накапливаем число в cx

inc si
mov al, [si] ;очередной символ
test al, al ;конец?
jnz expLoop

; Выравним мантиссу с учетом порядка

; ST(0) = мантисса
; cx = невыровненный порядок
; bx = общее число цифр

STFFinish:
cmp expsign, 0
je STFPoint
neg cx ;отрицательный порядок
STFPoint:
mov ax, decimal ;позиция точки
cmp ax, -1
je STFMult ;а не было точки
sub bx, ax ;bx = число цифр справа от точки
sub cx, bx ;выравниваем порядок

; Умножаем на 10^порядок
; ST(0) = мантисса
; cx = порядок

STFMult:
mov ax, cx
call PowerOf10

; Учтем знак числа
cmp sign, 0
je STFExit
fchs

STFExit:
fldcw stat
mov ax, si ;вернем, на всякий случай, в ax
; адрес во входном буфере
pop di si
mov sp, bp
pop bp
ret 2
StrToFloat ENDP

cseg ends

dseg segment use16 para public 'data'
;переменные
x dd ?
y dd ?
z dd ?
;
a dd ?
b dd ?
;константы для решения задачи
c071 dd 0.71
c2 dd 2.
c0005 dd 0.005
c5 dd 5.

;таблицы для быстрого умножения на 10^порядок
ten_1 dt 1.0e1 ;для единиц
dt 1.0e2
dt 1.0e3
dt 1.0e4
dt 1.0e5
dt 1.0e6
dt 1.0e7
dt 1.0e8
dt 1.0e9
dt 1.0e10
dt 1.0e11
dt 1.0e12
dt 1.0e13
dt 1.0e14
dt 1.0e15
ten_16 dt 1.0e16 ;для десятков
dt 1.0e32
dt 1.0e48
dt 1.0e64
dt 1.0e80
dt 1.0e96
dt 1.0e112
dt 1.0e128
dt 1.0e144
dt 1.0e160
dt 1.0e176
dt 1.0e192
dt 1.0e208
dt 1.0e224
dt 1.0e240
ten_256 dt 1.0e256 ;для сотен
dt 1.0e512
dt 1.0e768
dt 1.0e1024
dt 1.0e1280
dt 1.0e1536
dt 1.0e1792
dt 1.0e2048
dt 1.0e2304
dt 1.0e2560
dt 1.0e2816
dt 1.0e3072
dt 1.0e3328
dt 1.0e3584
dt 1.0e4096
dt 1.0e4352
dt 1.0e4608
dt 1.0e4864
;константы, используемые при преобразованиях
ten dq 10.0
ten7 dq 1.0e6
ten17 dq 1.0e17
;буфер для внутреннего преобразования
szTemp db 18 dup (0)
;буфер для получения ответа
sNum db 20 dup (0)
;строки приглашения и результата
sEnterX db 'Enter x: $'
sEnterY db 0dh,0ah,'Enter y: $'
sEnterZ db 0dh,0ah,'Enter z: $'
sA db 0dh,0ah,'a = ',0
sB db 0dh,0ah,'b = ',0
;структура для запроса строки по функции 0ah
sBuf db 32
cnt db ?
string db 32 dup (?)
dseg ends

end start
[/code]
5
Проделана гигантская работа - большое вам спасибо!
Об авторе:
"Если вы заметили, что вы на стороне большинства, —
это верный признак того, что пора меняться." Марк Твен
Форма ответа