- •Курсова робота з дисципліни: «Системне програмування» на тему: «Модель арифметико-логічного пристрою»
- •Завдання
- •1 Формати числових даних
- •2 Виконання арифметичних операцій
- •2.1 Арифметичні операції над двійковими числами
- •Imul операнд_1 [, операнд_2, операнд_3]
- •Idiv дільник
- •2.2 Арифметичні операції над двійково-десятковими неупакованими числами
- •3 Опис роботи програми
- •2.3 Арифметичні операції над двійково-десятковими упакованими числами
- •6 Результати роботи програми
- •Висновки
- •Список використаної літератури
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» відбудеться перехід до мітки, де виводиться інформація про програму. При натисненні клавіші «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
