Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Курсова робота 11 варіант.docx
Скачиваний:
0
Добавлен:
01.05.2025
Размер:
852 Кб
Скачать
☆

2.3 Арифметичні операції над двійково-десятковими упакованими числами

Упаковані BCD-числа можна тільки додавати і віднімати. Для виконання інших дій над ними їх потрібно додатково перетворювати або в неупакований формат, або в двійкове представлення.

Додавання упакованих BCD-чисел

Спробуємо скласти два двозначних упакованих BCD-числа.

Приклад 2.3.1.

Додавання упакованих BCD-чисел

 67 = 0110 0111

 +

 75 = 0111 0101

 =

 142 = 1101 1100 = 220

В двійковому вигляді результат дорівнює 1101 1100 (або 220 в десятковому поданні), що невірно. Це відбувається з тієї причини, що мікропроцесор не підозрює про існування BCD-чисел і складає їх за правилами складання двійкових чисел. Насправді, результат в двійково-десятковому вигляді повинен бути рівний 0001 0100 0010 (або 142 в десятковому поданні).

Як і для неупакованих BCD-чисел, для упакованих BCD-чисел існує потреба якось коригувати результати арифметичних операцій. Мікропроцесор надає для цього команду daa:

daa (Decimal Adjust for Addition) - корекція результату складання для представлення у десятковому вигляді. Команда daa перетворить вміст регістру al у дві упаковані десяткові цифри. Отримана в результаті складання одиниця (якщо результат складання більше 99) запам'ятовується в прапорі cf, тим самим враховується перенесення в старший розряд.

Віднімання упакованих BCD-чисел

Аналогічно додаванню, мікропроцесор розглядає упаковані BCD-числа як двійкові і, відповідно, виконує віднімання BCD-чисел як двійкових.

Приклад 2.3.2.

Віднімання упакованих BCD-чисел

Виконаємо віднімання 67-75. Так як мікропроцесор

виконує віднімання способом додавання, то і ми підемо

цим:

 67 = 0110 0111

 +

 -75 = 1011 0101

 =

  -8 = 0001 1100 = 28???

Результат дорівнює 28 в десятковій системі числення, що є абсурдом. У двійково-десятковому коді результат повинен бути рівний 0000 1000 (або 8 в десятковій системі числення).

При програмуванні віднімання упакованих BCD-чисел програміст, як і при відніманні неупакованих BCD-чисел, повинен сам здійснювати контроль за знаком. Це робиться за допомогою прапора cf, який фіксує позику зі старших розрядів. 

Саме віднімання BCD-чисел здійснюється простою командою віднімання sub чи sbb. Корекція результату здійснюється командою das:

das (Decimal Adjust for Substraction) - корекція результату віднімання для представлення у десятковому вигляді. Команда das перетворить вміст регістру al у дві упаковані десяткові цифри.

3 Опис роботи програми

Дана програма реалізує модель арифметико-логічного пристрою. Вона виконує арифметичні операції віднімання та ділення над числами.

Після запуску програми відкривається головне вікно, яке має дружній інтерфейс (меню). Меню складається з 4-х пунктів:

  1. Віднімання чисел

  2. Ділення чисел

  3. Виведення інформації про програму

  4. Вихід із програми

Якщо натиснути клавішу «1» відбудеться перехід до мітки, де виконуються операція віднімання. Якщо натиснути клавішу «2» відбудеться перехід до мітки, де виконуються операція ділення. Якщо натиснути клавішу «3» відбудеться перехід до мітки, де виводиться інформація про програму. При натисненні клавіші «Escape», відбудеться вихід із програми. Також реалізована можливість повернення із кожного підпункту до головного меню програми. Для цього треба натиснути будь-яку клавішу.

Для введення чисел створенні спеціальні процедури:

  • input_integral_part0 – для введення цілої частини числа для віднімання.

  • input_fractional_part0 – для введення дробової частини числа для віднімання.

  • input_integral_part – для введення цілої частини числа для ділення.

  • input_fractional_part – для введення дробової частини числа для ділення.

Процедура bcd2str призначена для переводу BCD-числа в ASCII-рядок.

Для введення символів з клавіатури використовується функція 0Ah переривання 21h. За допомогою цієї функцій, якщо після введення числа ще не натиснута клавіша «Enter», його можна редагувати, використовуючи на клавіатурі стрілки вправо/вліво та клавішу «Backspace».

При введенні числа відбувається перевірка на введений символ. Якщо введено щось інше окрім цифрових значень, то на екрані з’являється повідомлення про помилку.

Під цілу частину числа виділяється 6 байт, під дробову також 6 байт. З урахуванням крапки та знака загальна кількість символів, яка відводиться під число дорівнює 14 байт. Так як формат зберігання числа в пам’яті упакований, в 1 байті зберігається 2 числа. Таким чином для числа виділяється 12 символів до крапки і 12 символів після.

Якщо в цілу або дробову частину числа буде введено більше символів ніж дозволяється, то відбудеться переповнення розрядної сітки і на екрані з’явиться повідомлення про помилку:

«Error! Переповнення розрядної сітки. Повторіть введення».

При виборі одного з пунктів меню, віднімання або ділення, програма спочатку пропонує ввести цілу частину числа А, потім дробову частину числа А, потім цілу частину числа B і в кінці дробову частину числа B. Після цього виконуються відповідні арифметичні операції і результат виводиться на екран.

Пісня виконання арифметичних дій, програма очікує на натиснення будь-якої клавіші для повернення до головного меню.

4 БЛОК-СХЕМА ПРОГРАМИ

4.1 Блок-схема головної програми

4.2 Блок схема операції віднімання

4.3 Блок-схема операції ділення

5. ЛІСТИНГ ПРОГРАМИ

.model small

.code

.386

start: mov ax,@data

mov ds,ax

mov es,ax

main_loop: mov ax,3; очищаем экран

int 10h

mov ah,9

mov dx,offset string0

int 21h

mov ah,0

int 16h

cmp al,'1'

je a2

cmp al,'2'

je a1

cmp al,'3'

je a0

cmp ah,1

jne main_loop

exit: mov ah,4Ch

int 21h

a0: mov ax,3; очищаем экран

int 10h

mov ah,9

mov dx,offset string8

int 21h

mov ah,0

int 16h

jmp main_loop

;---деление------------------------------------------------------

a1: mov ax,3; очищаем экран

int 10h

mov ah,9

mov dx,offset string1

int 21h

mov string2+30,'A'

mov string3+33,'A'

mov di,offset A

call input_integral_part

call input_fractional_part

inc string2+30;меняю A на B в сообщении 'Vvedit cilu chastynu chysla A'

inc string3+33;меняю A на B в сообщении "Vvedit drobovu chastynu chysla A"

mov di,offset B

call input_integral_part

call input_fractional_part

; при делении используем FPU (блок для работы с вещественными числами)

xor bx,bx

finit; инициализация FPU

fild A;загрузили число А

fild B;загрузили число В

fdivp st(1),st; поделили А на В

fst S;результат деления в переменную

mov di,offset string5

fldz;загрузили в FPU ноль

db 0DFh,0F1h;fcomip st,st(1) ;сравниваю число с нулем

jnz a3

mov byte ptr [di],'0'; если целая часть равна нулю

inc edi

jmp b01

a3: fstcw control ;устанавливаю режим "округление к нулю"

or control,0C00h

fldcw control

fld st ;дублирую содержимое st(0) в st(1)

frndint ;округляю до целого содержимое st(0)

fsub st(1),st ;в st(1) остается дробная часть

fldz

db 0DFh,0F1h;fcomip st,st(1) ;сравниваю число с нулем

jnz a4

mov al,'0'; если дробная часть равна нулю

stosb

jmp b31

a4: call bcd2str; процедура, которая переведет BCD-число в строку

fldz

b31: db 0DFh,0F1h;fcomip st,st(1)

jz b01

mov al,','

stosb

inc bx

fmul y1 ;умножаю дробную часть на 1.0e17

call bcd2str

b01: mov byte ptr [di],'$'

mov ah,9

mov dx,offset string6

int 21h

mov ah,0

int 16h

jmp main_loop

;--------вычитание--------------------------------------

a2: mov ax,3; очищаем экран

int 10h

mov ah,9

mov dx,offset string7

int 21h

mov x,offset A+11

mov string2+30,'A'

mov string3+33,'A'

call input_integral_part0

inc string2+30;меняю A на B в сообщении 'Vvedit cilu chastynu chysla A'

mov di,offset A+5

call input_fractional_part0

inc string3+33;меняю A на B в сообщении "Vvedit drobovu chastynu chysla A"

mov x,offset B+11

call input_integral_part0

mov di,offset B+5

call input_fractional_part0

mov cx,12; число состоит из 12 символов

mov di,offset string5; здесь будет результат

mov eax,dword ptr A+4

cmp eax,dword ptr B+4;сравниваем старшие части А и В

jnz @29

mov eax,dword ptr A;если старшие части А и В равны

cmp eax,dword ptr B;сравниваем младшие

@29: jb @25; если В больше чем А отнимем А от В

and bx,0;начинаем обработку с нулевого байта заодно сделаем CF=0

@27: mov al,byte ptr A[bx];в AL очередной байт

sbb al,byte ptr B[bx];вычитание с учетом займа

das;коррекция результата вычитания

mov byte ptr S[bx],al;очередной байт результата

inc bx;переходим к следующему займу

loop @27

jmp @26

@25: mov byte ptr [di],'-';если В больше чем А поставим перед результатом минус

inc di

and bx,0;начинаем обработку с нулевого байта заодно сделаем CF=0

@28: mov al,byte ptr B[bx];в AL очередной байт

sbb al,byte ptr A[bx];вычитание с учетом займа

das;коррекция результата вычитания

mov byte ptr S[bx],al;очередной байт результата

inc bx;переходим к следующему займу

loop @28

@26: mov bx,offset S+11;распаковываем результат

mov cx,6

@30: mov al,[bx]

dec bx ;уменьшение содержимого регистра на 1

@24: mov ah,0

ror ax,4; циклический сдвиг вправо младшая цифра попала в АН, старшая в AL

shr ah,4; досдвигаем младшую цифру

or ax,'00';превращаем в АСКИ-символы

stosw;отправляем в строку

@31: loop @30

mov byte ptr [di],',';выводим дробную часть

inc di

mov cx,6

@22: mov al,[bx]

dec bx

mov ah,0

ror ax,4

shr ah,4

or ax,'00'

stosw

loop @22

mov ah,9

mov dx,offset string6;результат на экран

int 21h

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

int 16h

jmp main_loop

;-процедуры----------------------------------------------------

;-ввод целой части числа для вычитания-------------------------

input_integral_part0 proc

@0: mov ah,9; приглашение на экран

mov dx,offset string2;"введите целую часть числа A"

int 21h

i1: mov ah,0Ah;функция 0Ah прерывания 21h для ввода строки с клавиатуры

mov dx,offset buffer; сюда пойдет ввод -- > buffer db 7,?,7 dup(?)

;7 - максимальное число символов, которое можно ввести плюс символ Enter

;? - здесь будет реальное число символов от 0 до 6 не считая Enter

;7 dup(?) место под максимальное число символов плюс символ Enter

int 21h

mov ch,0

mov cl,buffer+1; здесь реальное число символов от 0 до 6

mov si,offset buffer+2; здесь начало символьной строки

cmp cl,6

ja @3er

mov di,12; считаем, что упакованных символов 12 так как отвели под них 6 байт

sub di,cx; от 12 минусуем реальное число введенных символов

shr di,1;делим пополам

neg di;умножаем на -1

add di,x;offset A+11 это будет смещение в переменной А, запись идет наоборот

test cx,1; количество символов было четное или нет?

jz @1

mov ah,0

inc cx; если не четное добавим один символ и сделаем АН=0

jmp @2

@1: lodsb; получили очередной символ

sub al,'0'

cmp al,9

ja @3; если не цифра, то сообщим об ошибке

mov ah,al; поместим этот символ из AL в AH

@2: lodsb; получили очередной символ

sub al,'0'

cmp al,9

ja @3; если не цифра, то сообщим об ошибке

shl al,4; сдвигаем содержимое AL на 4 бита влево

shr ax,4;и сдвигаем содержимое AХ на 4 бита вправо -- теперь в AL оба упакованных символа

mov [di],al; переводим строку цифр в BCD-число и отправляем в переменную

dec di

dec cx; уменьшаем СХ на 2 так как LOOP тоже отнимет от CX единицу

loop @1

retn; если все нормально выходим из процедуры

@3: mov ah,9

mov dx,offset string4;при вводе числа допущена ошибка!

int 21h

jmp @0

@3er: mov ah,9

mov dx,offset string9;сообщение о переполнение разрядной сетки

int 21h

jmp @0

input_integral_part0 endp

;---ввод дробной части числа для вычитания---------------------------

input_fractional_part0 proc

@4: mov ah,9

mov dx,offset string3;введите дробную часть числа A

int 21h

mov ah,0Ah

mov dx,offset buffer

int 21h

mov ch,0

mov cl,buffer+1

mov si,offset buffer+2

cmp cl,6

ja @6er

test cx,1

jz @5

mov bx,offset buffer+2

add bx,cx

mov byte ptr [bx],'0'

inc cx

@5: lodsb

sub al,'0'

cmp al,9

ja @6; если не цифра, то сообщим об ошибке

mov ah,al

lodsb

sub al,'0'

cmp al,9

ja @6; если не цифра, то сообщим об ошибке

shl al,4

shr ax,4

mov [di],al; переводим строку цифр в BCD-число

dec di

dec cx

loop @5

retn

@6: mov ah,9

mov dx,offset string4;при вводе числа допущена ошибка!

int 21h

jmp @4

@6er: mov ah,9

mov dx,offset string9;сообщение о переполнение разрядной сетки

int 21h

jmp @4

input_fractional_part0 endp

;--ввод целой части для деления-----------------------

input_integral_part proc

d0: mov ah,9

mov dx,offset string2;введите целую часть числа A

int 21h

mov ah,0Ah

mov dx,offset buffer

int 21h

mov ch,0

mov cl,buffer+1

mov si,offset buffer+2

cmp cl,6

ja d2er

xor eax,eax; обнулили регистр ЕАХ

cdq; обнулили регистр EDX

d1: lodsb;получили очередной символ

sub al,'0'

cmp al,9

ja d2; проверили цифра или нет

imul edx,10; умножили содержимое EDX на десять

add edx,eax; сложили содержимое EDX и ЕАХ

loop d1; и так столько раз сколько было символов

mov eax,index[6*4]

mul edx; дополнительно умножили EDX на 1000000

mov [di],eax

mov [di+4],edx; поместили содержимое ЕАХ в младшую часть переменной, а EDX в старшую часть

ret

d2: mov ah,9

mov dx,offset string4;при вводе числа допущена ошибка!

int 21h

jmp d0

d2er: mov ah,9

mov dx,offset string9;сообщение о переполнение разрядной сетки

int 21h

jmp d0

input_integral_part endp

;--ввод дробной части для деления-----------------------

input_fractional_part proc

d4: mov ah,9

mov dx,offset string3;введите дробную часть числа A

int 21h

mov ah,0Ah

mov dx,offset buffer

int 21h

mov ch,0

mov cl,buffer+1

mov si,offset buffer+2

cmp cl,6

ja d6er

mov ebx,6

sub bx,cx

xor eax,eax

cdq

d5: lodsb

sub al,'0'

cmp al,9

ja d6

imul edx,10

add edx,eax

loop d5

mov eax,index[ebx*4]; дробная часть будет умножена в отличие от целой не на миллион,

;а в зависимости от количества символов на 1,10,100,1000,10000,100000, то есть 6 символов на 1

;1 символ на 100000

mul edx

add [di],eax; и складываем с находящейся здесь целой частью

adc [di+4],edx

ret

d6: mov ah,9

mov dx,offset string4;при вводе числа допущена ошибка!

int 21h

jmp d4

d6er: mov ah,9

mov dx,offset string9;сообщение о переполнение разрядной сетки

int 21h

jmp d4

input_fractional_part endp

;-перевод BCD-числа в ASCII-строку

bcd2str proc

push ecx

fbstp temp

mov ecx,9 ;в десятом байте информация о знаке числа

test ebx,ebx

jnz b1

b0: cmp byte ptr [ecx-7+temp],0

jnz b1

loop b0 ;пропускаем незначащие (нулевые) разряды слева

b1: mov al,byte ptr [ecx-1+temp];загружаем первую значащую пару разрядов

cmp al,9 ;если в старшей тетраде 0 - пропустить старшую тетраду

ja b2

add al,30h ;младшую тетраду переводим в ASCII

stosb

dec ecx

jz b3

b2: movzx ax,byte ptr [ecx-1+temp];распаковываем остальные разряды числа

ror ax,4 ;выделяем старшую и младшую тетрады

shr ah,4

add ax,'00' ;переводим в ASCII-код

stosw

loop b2

b3: pop ecx

retn

bcd2str endp

.data

string0 db 'Model aryfmetyko-logichnogo prystroyu'

db 0Dh,0Ah,'Natysnit 1, shob vidnyaty'

db 0Dh,0Ah,'Natysnit 2, shob podilyty'

db 0Dh,0Ah,'Natysnit 3, shob otrymaty informaciyu pro programu'

db 0Dh,0Ah,'Natysnit Esc, shob vyyty z programy$'

string1 db 'Dilennya chysel z fiksovanoyu krapkoyu$'

string7 db 'Vidnimannya chysel z fiksovanoyu krapkoyu$'

string2 db 0Dh,0Ah,'Vvedit cilu chastynu chysla A',0Dh,0Ah,'$'

string3 db 0Dh,0Ah,'Vvedit drobovu chastynu chysla A',0Dh,0Ah,'$'

string4 db 0Dh,0Ah,'Pry vvedeni chysla dopushena pomylka!',0Dh,0Ah,'$'

string9 db 0Dh,0Ah,'Error! Perepovnennya rozryadnoi sitky. Povtorit vvedennya',0Dh,0Ah,'$'

buffer db 9,?,9 dup(?)

string6 db 0Dh,0Ah,'Rezultat '

string5 db 50 dup('$')

string8 db 'Programa modelyuvannya operacii vidnimannya ta dilennya'

db 0Dh,0Ah,'chysel z fiksovanoyu krapkoyu z zadanoyu kilkistyu rozryadiv.'

db 0Dh,0Ah,'Kilkist baytiv v cilyh ta drobovyh chastynah chysla: 6'

db 0Dh,0Ah,'Format chysla: upakovanyy'

db 0Dh,0Ah,'Vykonav: student grupy KM-105 Lukashenko Andriy$'

A dq ?

B dq ?

S dq ?

index dd 1,10,100,1000,10000,100000,1000000

y1 dq 1.0e17 ;множитель

save db 108 dup(?)

control dw ? ;переменная под содержимое регистра CWR

temp dt ? ;переменная под BCD

x dw ?

end start