Вычисление выражения (сумма ряда) - Assembler
Формулировка задачи:
Составить и отладить программу на ассемблере для вычисления выражения с помощью сопроцессора:
X = Сумме ряда(i от 1 до 3)(bi*sin(di)+ci*cos(ei)),
где bi и ci - 16-ти разрядные числа со знаком, хранящиеся в ячейках памяти с базовыми адресами: MEM - для bi(i=1,3) и ALF - для ci(i=1,3)(3 знака)
di и ei - однобайтные числа вводимые с клавиатуры.
X - вывести на дисплей по формату X = XXX.XXX
Решение задачи: «Вычисление выражения (сумма ряда)»
textual
Листинг программы
LOCALS .model small .stack 100h .data Prompt1 db 'A[', '$' Prompt2 db ']=', '$' CrLf db 0Dh, 0Ah, '$' kbdBuffer db 6, 0, 6 dup(0) MEM dw 312, 368, 587 ALF dw 567, 197, 421 D db 3 dup(?) E db 3 dup(?) tempF dw ? .code .486 ;Ввод массива байт (byte) ;cx - количество вводимых элементов ;ds:dx - адрес массива InputArray proc push ax push bx push cx push dx push si push di jcxz @@Exit ;если массив пустой - завершить mov si, 1 ;индекс элемента массива mov di, dx ;адрес текущего элемента массива @@ForI: ;вывод приглашения ввода элемента mov ah, 09h lea dx, Prompt1 int 21h mov ax, si call Show_AX mov ah, 09h lea dx, Prompt2 int 21h ;ввод числа mov ah, 0Ah ;ввод строки mov dx, offset kbdBuffer int 21h mov ah,09h ;перевод строки (на новую строку) lea dx, CrLf int 21h push si lea si, kbdBuffer+1 ; преобразование строки в число ;lea di, Numer call Str2Num pop si ; проверка на ошибку jnc @@NoError ; если есть ошибка ввода - повторить ввод jmp @@ForI ; если нет ошибки ввода - сохранить число @@NoError: ;сохранение введённого числоа в массиве ;mov [di], ax ;переход к следующему элементу inc si add di, 1 loop @@ForI @@Exit: pop di pop si pop dx pop cx pop bx pop ax ret InputArray endp ; преобразования строки в знаковое число (8 бит) ; на входе: ; ds:[si] - строка с числом ; ds:[di] - адрес числа ; на выходе ; ds:[di] - число ; CY - флаг переноса (при ошибке - установлен, иначе - сброшен) Str2Num proc push ax push bx push cx push dx push ds push es push si push ds pop es mov cl, ds:[si] xor ch, ch inc si cmp [si], byte ptr '-' jne @@IsPositive inc si dec cx @@IsPositive: jcxz @@Error mov bx, 10 xor ax, ax @@Loop: mul bl ; умножаем al на 10 ( ax=al*bl ) mov [di], al ; игнорируем старший байт cmp ah, 0 ; проверяем, результат на переполнение jnz @@Error mov al, [si] ; Преобразуем следующий символ в число cmp al, '0' jb @@Error cmp al, '9' ja @@Error sub al, '0' xor ah, ah add al, [di] jc @@Error ; Если сумма больше 255 inc si loop @@Loop pop si push si or al, al js @@Error cmp [si+1], byte ptr '-' jne @@Positive neg al or al, al jns @@Error @@Positive: mov [di], al clc pop si pop es pop ds pop dx pop cx pop bx pop ax ret @@Error: xor ax, ax mov [di], al stc pop si pop es pop ds pop dx pop cx pop bx pop ax ret Str2Num endp ; выводит знаковое 16-разрядное число из регистра AX на экран ; входные данные: ; ax - число для отображения Show_AX proc push ax push bx push cx push dx push di mov cx, 10 xor di, di ; di - кол. цифр в числе ; если число в ax отрицательное, то ;1) напечатать '-' ;2) сделать ax положительным or ax, ax jns @@Conv push ax mov dx, '-' mov ah, 2 ; ah - функция вывода символа на экран int 21h pop ax neg ax @@Conv: xor dx, dx div cx ; dl = num mod 10 add dl, '0' ; перевод в символьный формат inc di push dx ; складываем в стек or ax, ax jnz @@Conv ; выводим из стека на экран @@Show: pop dx ; dl = очередной выводимый символ mov ah, 2 ; ah - функция вывода символа на экран int 21h dec di ; повторяем пока di<>0 jnz @@Show pop di pop dx pop cx pop bx pop ax ret Show_AX endp ; Вывод вещественного числа ShowFloat proc near ; аргумент - количество цифр дробной части length_frac equ [bp+4] ; локальные переменные ten equ word ptr [bp-2] temp equ word ptr [bp-4] enter 4, 0 ; пролог - выделим в кадре стека 4 байта под локальные переменные mov ten, 10 ftst ; определяем знак числа fstsw ax sahf jnc @positiv mov al, '-' ; если число отрицательное - выводим минус int 29h fchs ; и получаем модуль числа @positiv: fld1 ; загружаем единицу fld st(1) ; копируем число на вершину стека fprem ; выделим дробную часть fsub st(2), st ; отнимем ее от числа - получим целую часть fxch st(2) ; меняем местами целую и дробную части xor cx, cx ; обнуляем счетчик ; далее идет стандартный алгоритм вывода целого числа на экран @1: fidiv ten ; делим целую часть на десять fxch st(1) ; обменяем местами st и st(1) для команды fprem fld st(1) ; копируем результат на вершину стека fprem ; выделим дробную часть (цифру справа от целой части) fsub st(2), st ; получим целую часть fimul ten ; *10 fistp temp ; получаем очередную цифру push temp ; заталкиваем ее глубже в стек inc cx ; и увеличим счетчик fxch st(1) ; подготовим стек к следующему шагу цикла (полученное частное на вершину, в st(1) - 1) ftst ; проверим не получили ли в частном 0? fstsw ax sahf jnz @1 ; нет - продолжим цикл ;если цифр меньше 3, то добавим несколько 0 jmp @@2 @@1: mov ax, 0 push ax inc cx @@2: cmp cx, 3 jb @@1 @2: ; извлекаем очередную цифру, переводим её в символ и выводим. pop ax add al, '0' int 29h loop @2 ; далее то же самое, только для дробной части. Алгоритм похож на вывод целого числа, только вместо деления умножение и проход по числу слева fstp st ; сначала проверим, есть ли дробная часть fxch st(1) ftst fstsw ax sahf jz @quit ; дробная часть отсутствует mov al, '.' int 29h ; если присутствует - выведем точку mov cx, length_frac ; помещаем в счетчик длину дробной части @3: fimul ten ; умножим на 10 fxch st(1) ; подготовка для fprem - меняем st и st(1) местами и fld st(1) ; копируем число на вершину fprem ; отделим дробную часть от целой fsub st(2), st ; и оставляем дробную fxch st(2) fistp temp ; выталкиваем полученное число из стека в temp mov ax, temp ; по дробной части идем слева, значит число выводим сразу, без предварительного сохранения в стек or al, 30h ; перевод в ascii int 29h ; на экран fxch st(1) ; подготовим стек к следующему шагу цикла (полученное частное на вершину, в st(1) - 1) ftst fstsw ax sahf ; проверим на 0 остаток дробной части loopne @3 @quit: fstp ; готово. Чистим стек сопроцессора fstp st leave ; эпилог ret 2 ShowFloat endp ;основная программа main proc mov ax, @data mov ds, ax ;ввод массива D mov al, 'D' mov [Prompt1],al lea dx, [D] mov cx, 3 call InputArray ;ввод массива E mov al, 'E' mov [Prompt1],al lea dx, [E] mov cx, 3 call InputArray ;вычисление суммы в цикле mov cx, 3 mov di, 0 ;смещение в массивах байт mov si, 0 ;смещение в массивах слов finit fldz @@ForI: lea bx, [D] mov al, byte ptr[di+bx] cbw mov [tempF], ax fild [tempF] fsin lea bx, [MEM] fild word ptr[si+bx] fmulp st(1), st(0) lea bx, [E] mov al, byte ptr[di+bx] cbw mov [tempF], ax fild [tempF] fcos lea bx, [ALF] fild word ptr[si+bx] fmulp st(1), st(0) faddp st(1), st(0) faddp st(1), st(0) add si, 2 add di, 1 loop @@ForI mov ax, 3 ;длина дробной части push ax call ShowFloat mov ax, 4C00h int 21h main endp end main
Объяснение кода листинга программы
Этот код на языке Assembler вычисляет сумму двух массивов, используя формулы Фурье для суммы ряда. Вначале кода вводятся два массива D и E с помощью функции InputArray. Затем происходит инициализация сумм и переменных для хранения дробной части числа. Далее начинается цикл, который проходит по всем элементам массивов D и E. В каждой итерации цикла выполняется следующее:
- Значение каждого элемента массива D преобразуется в число с плавающей точкой и добавляется к сумме.
- Значение каждого элемента массива E преобразуется в число с плавающей точкой и умножается на сумму, после чего результат сохраняется в другой переменной.
- Затем выполняется деление результата на 10 и выводится на экран. После завершения цикла выводится длина дробной части, после чего вызывается функция ShowFloat для вывода числа с плавающей точкой. Наконец, выводится контрольная сумма, которая должна быть равна 4C00h. Если это не так, то в коде ошибки.
ИИ поможет Вам:
- решить любую задачу по программированию
- объяснить код
- расставить комментарии в коде
- и т.д