Міністерство освіти і науки, молоді та спорту України
Національний університет “Львівська політехніка”
Кафедра ЕОМ
Звіт
з лабораторної роботи № 7
на тему:
Багатомодульне програмування.
з дисципліни:
" Засоби системного програмування"
Варіант № 11
Мета: Оволодіти навиками створення багатомодульних програм. Засвоїти правила взаємодії різних модулів.
Теоретичні відомості
Багатомодульне програмування
При роботі на мові Асемблер під багатомодульним програмуванням розуміють процес написання кількох процедур, підпрограм, у різних вихідних файлах з подальшим їх об’єднанням на етапі лінкування. Таке доцільно робити, коли:
є необхідність компонування декількох програм написаних на різних мовах програмування (наприклад, для об’єднання потужності мов високого рівня та ефективності асемблера);
програма, написана у вигляді одного модуля, може виявитись завеликою для асемблювання;
окремі модулі можуть бути написані різними проектувальниками з метою подальшої інтеграції;
з причини великого розміру виконавчого модуля може виникнути необхідність перекриття окремих частин програми в процесі виконання.
Кожна програма асемблюється окремо і генерує власний унікальний об’єктний (OBJ) модуль. Програма компонувальник (LINK) як правило виконання починається з основної програми, яка викликає одну або декілька підпрограм. Підпрограми, в свою чергу, можуть задіювати інші підпрограми.
Виклик іншої програми зумовлює необхідність міжсегментного (довгого) виклику (CALL). Дана операція спочатку записує до стеку вміст регістру CS і заносить до цього регістру адресу іншого сегменту, потім записує до стеку значення регістру IP і заносить до цього регістру нову відносну адресу.
Таким чином, в стеку запам’ятовуються і адреса кодового сегменту, і зміщення для наступного повернення з підпрограми.
Наприклад, міжсегментний виклик (CALL) може складатись з такого об’єктного коду:
9A 0002 AF04
Машинний код для міжсегментного виклику CALL – 9A. При цьому команда CALL записує значення 0002 у вигляді 0200 до регістру IP, а значення 04AF – до регістру CS. Комбінація цих адрес вказує на першу виконувану команду у програмі, що викликається:
Кодовий сегмент
04AF0
Зміщення в IP
0200
Дійсна адреса
04CF0
При виході з процедури, що викликалась, міжсегментна команда REТ відновлює обидві адреси в регістрах CS і IP і, таким чином, передає управління на наступну після CALL команду.
Розглянемо, наприклад, основну програму MAINPROG та підпрограму, що викликається за допомогою міжсегментного виклику CALL - SUBPROG.
EXTRN SUBPROG : FAR
MAINPROG: .
CALL SUBPROG
....
PUBLIC SUBPROG
SUBPROG: .
....
RET
Команда CALL в основній програмі повинна “знати”, що програма, яка викликається, знаходиться поза межами даного сегменту. Директива EXTRN вказує асемблеру, що посилання на SUBPROG має атрибут FAR, тобто є визначеним в іншому асемблерному модулі. Оскільки сам асемблер не має можливості точно визначити такі посилання, він генерує пустий об’єктний код для наступного його заповнення при компонуванні:
9A 0000 ---- E
Підпрограма SUBPROG містить директиву PUBLIC, що вказує асемблеру і компонувальнику, що інший модуль повинен “знати” адресу SUBPROG. В результаті, після успішного асемблювання програм MAINPROG і SUBPROG в окремих об’єктних модулях, вони можуть бути скомпільовані наступним чином:
D:\HOME\TASM > tasm MAINPROG /l
D:\HOME\TASM > tasm SUBPROG /l
D:\HOME\TASM > tlink MAINPROG + SUBPROG
Компонувальник встановлює відповідності між адресами EXTRN в одному об’єктному модулі з адресами PUBLIC в іншому і заносить необхідні відносні адреси. Потім він об’єднує два об’єктних модуля в один виконавчий.
В разі неможливості розв’язати посилання компонувальник видає повідомлення про помилку.
Директива EXTRN має такий формат:
EXTRN ім’я : тип [, … ]
Можна призначити більше одного імені (до кінця рядка) або закодувати додаткові директиви EXTRN. В іншому асемблерному модулі відповідне ім’я повинно бути визначене і ідентифіковане як PUBLIC. Існують такі типи елементів: ABS, BYTE, WORD, DWORD, FAR, NEAR. Ім’я може бути визначене через EQU і повинно задовольняти реальному визначенню імені.
Директива PUBLIC вказує асемблеру і компонувальнику, що адреса вказаного ідентифікатора є доступною з інших програм. Директива має такий формат:
PUBLIC ідентифікатор [, … ]
Можна призначити більше одного ідентифікатора (до кінця рядка) або закодувати додаткові директиви PUBLIC. Ідентифікатори можуть бути мітками (включаючи PROC-мітки), змінними або числами. Неправильними ідентифікаторами є імена регістрів та EQU-ідентифікатори, що призначають більш як двобайтні значення.
Існує декілька способів компонування програм. Кожен з них характеризується одною або декількома такими рисами:
директиви EXTRN і PUBLIC в якості міток;
спільний кодовий сегмент - шляхом використання директиви PUBLIC в заголовку кодового сегменту кожної з програм, що компонуються;
спільні дані.
Передача параметрів через стек
Даний спосіб взаємодії програм характеризується тим, що програма, що викликає іншу, передає їй параметри шляхом запису даних до стеку. Кожна команда PUSH при цьому повинна записувати до стеку дані розміром в одне слово з пам’яті або з регістру.
Програма, приведена далі, перш ніж викликати підпрограму SUBMUL заносить у стек значення з полів PRICE і QTY. Після команди CALL стек виглядає в так:
... │ 1600 │ D213 │ 4001 │ 0025 │ 0000 │ C213 │
6 5 4 3 2 1
Ініціалізуюча команда PUSH DS заносить адресу сегменту даних у стек. Ця адреса може відрізнятися в різних версіях DOS.
Команда PUSH AX заносить у стек нуль.
Команда PUSH PRICE заносить у стек слово даних (2500).
Команда PUSH QTY заносить у стек друге слово даних (0140).
Команда CALL заносить у стек вміст регістра CS (D213)
Оскільки що команда CALL представляє тут міжсегментний виклик, то в стек заноситься також вміст регістра IP(1600).
Програма, що викликається використовує регістр BP для доступу до параметрів у стеку, але вона, також, запам'ятовує вміст регістра BP, записуючи його в стек. У даному випадку, припустимо, що регістр BP містить нуль, тоді цей нуль (два байти) буде записано у вершині стеку (ліворуч). Потім програма поміщає в регістр BP вміст із регістра SP, оскільки у якості індексного регістра може використовуватися регістр BP, але не SP. Команда завантажує в регістр BP значення 0072. Спочатку регістр SP містив розмір порожнього стека, тобто комірки 80. Запис кожного слова в стек зменшує вміст SP на 2:
│ BP │ IP │ CS │ QTY │ PRICE│ AX │ DS │
│ 0000 │ 1600 │ D213 │ 4001 │ 0025 │ 0000 │C213 │
│ │ │ │ │ │ │
SP: 72 74 76 78 7A 7C 7E
Оскільки BP тепер також містить 0072, то параметр ціни (PRICE) буде за адресою BP+8, а параметр кількості (QTY) - за адресою BP+6. Програма пересилає ці величини зі стеку в регістри AX і BX відповідно, і виконує множення.
Перед поверненням у вихідну програму в регістрі BP відновлюється початкове значення, а вміст у регістрі SP збільшується на 2, з 72 до 74.
Остання команда RET являє собою міжсегментне повернення (“довгий” перехід) у вихідну програму. По цій команді виконуються наступні дії:
З вершини стека відновлюється значення регістра IP (1600).
Вміст регістра SP збільшується на 2, від 74 до 76.
З нової вершини стеку відновлюється значення регістра CS (D213).
Вміст регістра SP збільшується на 2 від 76 до 78.
У такий спосіб здійснюється коректне повернення у викликаючу програму. Залишилося одне невелике пояснення. Команда RET закодована як RET 4. Параметр 4 являє собою кількість байт у стеку, використаних при передачі параметрів (два слова в даному випадку). Команда RET додасть цей параметр до вмісту регістра SP, одержавши значення 7C. Таким чином, зі стеку вилучаються непотрібні більше параметри. Будьте особливо уважні при відновленні регістра SP - помилки можуть привести до непередбачуваного результату.
TITLE CALLMULL ;Головна програма
EXTRN SUBMUL:FAR
STACKSG SEGMENT PARA STACK 'Stack'
DW 64 DUP(?)
STACKSG ENDS
DATASG SEGMENT PARA 'Data'
QTY DW 0140H
PRICE DW 2500H
DATASG ENDS
CODESG SEGMENT PARA PUBLIC 'Code'
BEGIN PROC FAR
ASSUME CS:CODESG,DS:DATASG,SS:STACKSG
PUSH DS
SUB AX,AX
PUSH AX
MOV AX,DATASG
MOV DS,AX
PUSH PRICE
PUSH QTY
CALL SUBMUL ;Викликати підпрограму
RET
BEGIN ENDP
CODESG ENDS
END BEGIN
ТITLE SUBMUL ;Підпрограма множення
CODESG SEGMENT PARA PUBLIC 'CODE'
SUBMUL PROC FAR
ASSUME CS:CODESG
PUBLIC SUBMUL
PUSH BP
MOV BP,SP
MOV AX,[BP+8] ;Вартість
MOV BX,[BP+6] ;Кількість
MUL BX ;Добуток в DX:AX
POP BP
RET
SUBMUL ENDP
CODESG ENDS
END SUBMUL
ЗАВДАННЯ:
Створити *.exe програму, яка реалізовує обчислення, заданого варіантом виразу.
Програма повинна складатися з чотирьох модулів:головний модуль – містить спільний сегмент стеку, необхідні дані та виклик основних процедур;модуль вводу - забезпечує ввід даних з клавіатури в десятковій формі;модуль безпосередніх обчислень – здійснює всі необхідні арифметичні дії. модуль виводу – забезпечує вивід на екран результату в десятковій формі.
Всі модулі повинні бути в різних файлах і об’єднані на етапі лінкування. Передача параметрів може здійснюватися довільним чином.
Переконатися у правильності роботи кожного модуля зокрема та програми загалом.
Скласти звіт про виконану роботу з приведенням тексту програми та коментарів до неї.
Дати відповідь на контрольні запитання.
Мій варіант :
11
X=(A4-B3-K)*D1+E4/F2
311
Код програми :
Файл main.asm
;********************************************************************************
;головний модуль ;тут будуть викликатися усі інші функції
;********************************************************************************
DOSSEG
;оголошення процедур які міститимуться в окремих модулях
EXTRN Input :FAR, Calculation :FAR, Output :FAR
;зовнішня змінна-прапорець : використовуватиметься для коректного завершення програми
;при виникненні помилки вона збереже наявність помилки
PUBLIC erflag
;******************************************************************************************
;***********************************ОПИС МАКРОКОМАНД***************************************
;******************************************************************************************
;макрокоманда для ініціалізації стеку, та сегментних регістрів
INITIAL MACRO csname, dsname, ssname;макрокоманда із формальними параметрами
ASSUME cs:csname, ds:dsname, ss:ssname, es:dsname
push ds
xor ax, ax
push ax
mov ax, dsname
mov ds, ax
mov es, ax
ENDM
;********************************************************************************
;опис сегменту стеку
;********************************************************************************
;стек міститиме адреси поверненя з підпрограм до головної програми
;також через нього передаватимуться параметри
;він використовуватиметься усіма модулями
STACKSG SEGMENT PARA STACK 'Stack'
DW 127 DUP(0)
STACKSG ENDS
;********************************************************************************
;опис сегменту даних
;********************************************************************************
;при компонуванні утвориться загальний (спільний) сегмент даних
DATASG SEGMENT PARA PUBLIC 'Data'
erflag DB 0
;текстові повідомлення
hello_message db '********************************************************************************', 10, 13
db '***************************BEGIN OF THE PROGRAM*********************************', 10, 13
db '********************************************************************************', 10, 13
db 'hrupa : ki-23', 10, 13
db 'student : Dasho', 10, 13
db 'lab-7_var-11', 10, 13, 10
db '--------------------------------------------------------------------------------', 10, 13
db 'X=(A4-B3-K)*D1+E4/F2', 10, 13
db '--------------------------------------------------------------------------------', 10, 13, '$'
papa_message db 10, 10, 13
db '********************************************************************************', 10, 13
db '*************************THE END OF THE PROGRAM*********************************', 10, 13
db '********************************************************************************', 10, 13, '$'
DATASG ENDS
;********************************************************************************
;опис сегменту даних
;********************************************************************************
;при компонуванні утвориться загальний (спільний) сегмент коду
CODESG SEGMENT PARA PUBLIC 'Code'
main PROC FAR ;мітка, що визначає початок головної процедури
;ініціалізаця стеку та регістра ds
INITIAL codesg, datasg, stacksg ;виклик макрокоманди із заданням параметрів
call clnscr ;очистити екран та встановити курсор на початок екрану
call hello ;вивести на екран текст привітання
;виклик процедури для введення даних
CALL Input
;в разі виявлення помилки
cmp erflag,0 ;якщо відмінне від 0 то
jne exit ;аваріне завершення програми
;виклик процедури для обчислення
CALL Calculation
;в разі виявлення помилки
cmp erflag,0 ;якщо відмінне від 0 то
jne exit ;аваріне завершення програми
;виклик процедури для виведення результату на екран
CALL Output
;в разі виявлення помилки
cmp erflag,0 ;якщо відмінне від 0 то
jne exit ;аваріне завершення програми
;заверешння програми
exit: ;мітка, потрібна для завершення програми у разі виникнення помилки
call papa ;вивести на екран повідомлення про завершення програми
ret
main ENDP
;-------------------------------------------------------------------
;вивести на екран привітання----------------------------------------
hello PROC
mov ah, 09
lea dx, hello_message
int 21h
ret
hello ENDP
;-------------------------------------------------------------------
;вивести на екран прощання------------------------------------------
papa PROC
mov ah, 09
lea dx, papa_message
int 21h
ret
papa ENDP
;-------------------------------------------------------------------
;очистити екран та встановити курсор на початок екрану--------------
clnscr PROC ;процедура ближньої адресації для очистки екрану
mov ax, 0600h ;ah 06 - прокрутка ; al 00 - весь екран
mov bh, 07 ;нормальний атрибут (чорно/білий)
mov cx, 0000 ;верхня ліва позиція
mov dx, 184fh ;нижня права позиція
int 10h ;передача управління в BIOS
;встановлення курсору на початку екрану
mov ah, 02 ;запит на встановлення курсору на екрані, не залежить чи є на ньому вже якісь символи, вони ігноруються
mov bh, 00 ;номер екрану
mov dx, 0200h ;встановлення курсору : 2 рядок, 10 стовбець
int 10h ;переривання програми для передачі керування в BIOS
ret ;завершення процедури
clnscr ENDP ;завершення опису коду процедуриs
CODESG ENDS ;завершення опису сегмента коду
END main ;вказівка на головну процедуру
Файл input.asm
DOSSEG
;глобальна змінна
EXTRN erFlag:BYTE
;змінні, які доступні в цьому файлі
PUBLIC A, B, D, E, F
;************************************************************
;опис спільного сегменту даних у модулі input
DATASG SEGMENT PARA PUBLIC 'Data'
;список параметрів, потрібних для введення числа із клавіатури
variable label byte ;імя списку параметрів
max_len db 10 ;максимальна кількість цифр числа (символів ASCII)
real_len db ? ;реальне число введених символів
tmp_var db 10 dup(?) ;тимчасова змінна для зберігання числа в ASCII форматі
;тимчасова змінна для зберігання вже переведеного до 16 системи введеного числа
tmp db 4 dup(?) ;масимальна розмірність змінної - 4 байти
;---------------------------------------------------------------------------------------
;змінні, потрібні для обчислень : ------------------------------------------------------
A dw ?, ?
B dw ?, ?
D db ?
E dw ?, ?
F dw ?
;---------------------------------------------------------------------------------------
;текстові повідомлення
mes_vvid_A db 10, 13, 'Vvid A : ' , '$'
mes_vvid_B db 10, 13, 'Vvid B : ' , '$'
mes_vvid_C db 10, 13, 'Vvid C : ' , '$'
mes_vvid_D db 10, 13, 'Vvid D : ' , '$'
mes_vvid_E db 10, 13, 'Vvid E : ' , '$'
mes_vvid_F db 10, 13, 'Vvid F : ' , '$'
message_erorr_data db 10, 13, '!!!_vy vvely nekorektni dani _!!!', 10, 13, '$'
DATASG ENDS
;************************************************************
;опис спільного сегменту коду у модулі input
CODESG SEGMENT PARA PUBLIC 'CODE'
ASSUME DS:DATASG, CS:CODESG
;-------------------------------------------------------------------
;процедура для вводу змінних----------------------------------------
input proc FAR
public Input
;збереження вмістимого регістрів
push ax
push dx
;******************
;введення A (4)
mov ah, 09 ;9 функція для виводу
lea dx, mes_vvid_A ;адреса виводимого тексту
int 21h ;переривання для виводу на екран
;-----------------
mov max_len, 10 ;задання максимальної кількості вводимих цифер
call input_var ;виклик для введення числа
call ASCII_to_16 ;переведення до машинного вигляду числа
mov ax, word ptr [tmp]
mov A, ax
mov ax, word ptr [tmp+2]
mov A+2, ax
;******************
;введення B (4)
mov ah, 09
lea dx, mes_vvid_B
int 21h
;-----------------
mov max_len, 10
call input_var
call ASCII_to_16
mov ax, word ptr [tmp]
mov B, ax
mov ax, word ptr [tmp+2]
mov B+2, ax
;******************
;введення D (1)
mov ah, 09
lea dx, mes_vvid_D
int 21h
;-----------------
mov max_len, 2
call input_var
call ASCII_to_16
mov al, byte ptr [tmp]
mov D, al
;********************
;введення E (4)
mov ah, 09
lea dx, mes_vvid_E
int 21h
;-----------------
mov max_len, 10
call input_var
call ASCII_to_16
mov ax, word ptr [tmp]
mov E, ax
mov ax, word ptr [tmp+2]
mov E+2, ax
;*********************
;введення F (2)
mov ah, 09
lea dx, mes_vvid_F
int 21h
;-----------------
mov max_len, 5
call input_var
call ASCII_to_16
mov ax, word ptr [tmp]
mov F, ax
;відновлення вмістимого регістрів
pop dx
pop ax
ret
input ENDP
;-------------------------------------------------------------------
;ввід числа із клавіатури-------------------------------------------
;в разі некоректних вхідних даних спроба вводу повториться----------
input_var PROC near ;процедура ближньої адресації для введення тексту(числа) із клавіатури
;збереження вмістимого регістрів
push ax
push dx
push bx
push cx
vvid:
mov ah, 0ah ;функція для вводу тексту(числа) з клавіатури
lea dx, variable ;сюди буде записуватися текст(число в ASCII форматі), введений із клавіатури
int 21h ;виклик 21 переривання DOS
;перевірка на коректність вхідних даних
xor cx, cx
mov cl, real_len
mov bx, cx
dec bx
perevirka:
mov al, tmp_var[bx]
cmp al, 39h ;якщо символ поза межами
ja erorr ;ASCII-кодів цифер
cmp al, 30h ;то здійснюється повтор
jb erorr ;вхідного числа
jmp dali
erorr:
mov ah, 09 ;вивід на екран
lea dx, message_erorr_data ;повідомлення для введення некоректних даних
int 21h
jmp vvid
dali:
dec bx ;перехід на старший розряд
loop perevirka ;продовження циклу перевірки на коректність вхідних даних
;відновлення вмістимого регістрів
pop cx
pop bx
pop dx
pop ax
ret ;завершення процедури
input_var ENDP ;завершення опису коду процедури
;-------------------------------------------------------------------
;переведення символів з ASCII форматом до єдиного числа у 16 системі
;процедура завжди даватиме 4-байтне число---------------------------
;але якщо буде число 2-бйтне, то старші байти будуть рівні 0--------
ASCII_to_16 PROC near ;процедура для переведення до 16 системи числення
;збереження вмістимого регістрів
push ax
push dx
push bx
push cx
;------------------------------
xor cx, cx ;обнулення cx
mov cl, real_len ;кількість 10-ових цифр
mov bx, cx ;потрібно для адресації
dec bx ;байтів введеного числа у зворотньому порядку
mov dl, 1h ;множник основи
mov dh, 10d ;число на яке буде збільшуватися множник основи
mov tmp, 0 ;тут запишеться число
mov tmp+1, 0
mov tmp+2, 0
mov tmp+3, 0
loop10: ;цикл для переведення поточного розряду до 16 системи
xor ax, ax ;обнулення регістра ax
clc ;очищення прапорця CF
mov al, tmp_var[bx] ;запис поточного байта
and al, 0Fh ;переведення символів ASCII до цифер із діапазону від 0 до 9
mul dl ;множення на основу
add tmp, al ;нарахування суми результуючого числа
adc tmp+1, ah ;нарахування суми із врахуванням переносу до старшого байта
adc tmp+2, 0 ;врахування переносу до старшого байта
adc tmp+3, 0 ;врахування переносу до старшого байта
mov al, dl ;збільшення множника ->
mul dh ;основи дл наступного ->
mov dl, al ;старшого розряду
dec bx ;перехід на молодший байт введеного числа для доступу до старшого розряду
loop loop10 ;продовжити поки не цикл не пройдеться по всіх розрядах введеного числа
;відновлення вмістимого регістрів
pop cx
pop bx
pop dx
pop ax
ret ;завершення процедури
ASCII_to_16 ENDP ;завершення опису коду процедури
CODESG ENDS
END
Файл calc.asm
DOSSEG
;глобальна змінна
EXTRN A:dword, B:dword, D:word, E:dword, F:word, erflag:byte
PUBLIC X
;************************************************************
;опис спільного сегменту даних у модулі calc
DATASG SEGMENT PARA PUBLIC 'Data'
k_low equ 0311h
k_high equ 0000h
;тимчасові змінні, потрібні для зберігання поточних значень обрахунків------------------
tmp1 dw ?, ?
tmp2 dw ?, ?
tmp3 dw ?, ?
tmp4 dw ?, ?
;---------------------------------------------------------------------------------------
;змінна для кінцевого результату обчислень----------------------------------------------
X dw ?
error_data_div db 10, 13
db '!!!_INCORECTLY_DATA_!!!', 10, 13
db 'DIVIDE BY ZERO_!!!', 10, 13, '$'
error_data_sub db 10, 13
db '!!!_INCORECTLY_DATA_!!!', 10, 13
db 'result must be unsigned_!!!', 10, 13, '$'
error_divisor db 10, 13
db 'DIVISOR IS TO SMALL', 10, 13
db 'DIVIDE OVERFLOW_!!!', 10, 13, '$'
DATASG ENDS
;************************************************************
;опис спільного сегменту коду у модулі calc
CODESG SEGMENT PARA PUBLIC 'CODE'
ASSUME DS:DATASG, CS:CODESG
MOV AX,DATASG
MOV DS,AX
;-------------------------------------------------------------------
;процедура, яка проводитиме оючислення------------------------------
calculation PROC FAR
public calculation
;збереження вмістимого регістрів
push ax
push dx
push bx
push cx
;*******************************************************
;ОБЧИСЛЕННЯ ВИРАЗУ
;X=(A4-B3-K)*D1+E4/F2
;-------------------------------------------------------
;A4-B3(4)->tmp1(4)
mov ax, word ptr A
mov dx, word ptr A+2
mov bx, word ptr B
mov cx, word ptr B+2
cmp ax, bx
ja @@step1
mov ah, 09h
lea dx, error_data_sub
int 21h
mov erflag, 1
jmp @@exit
@@step1:
sub ax, bx
sbb dx, cx
mov tmp1, ax
mov tmp1+2, dx
;--------------------------------------------------------
;tmp1(4)-K->tmp2(4)
mov ax, tmp1
mov dx, tmp1+2
cmp ax, k_low
ja @@step2
mov ah, 09h
lea dx, error_data_sub
int 21h
mov erflag, 1
jmp @@exit
@@step2:
sub ax, k_low
sbb dx, 0
mov tmp2, ax
mov tmp2+2, dx
;-----------------------------------------------------------
;tmp2*D1->tmp3(4)
mov ax, tmp2
mov dx, tmp2+2
mov bl, byte ptr D
xor bh, bh
mul bx
mov tmp3, ax
mov tmp3+2, dx
;-----------------------------------------------------------
;E4/F2->tmp4(2)
mov ax, word ptr E
mov dx, word ptr E+2
mov bx, F
cmp bx, 0
jne @@step4_p
mov ah, 09h
lea dx, error_data_div
int 21h
mov erflag, 1
jmp @@exit
@@step4_p:
cmp dx, bx
jb @@step4_div
mov ah, 09h
lea dx, error_divisor
int 21h
mov erflag, 1
jmp @@exit
@@step4_div:
div bx
mov tmp4, ax
;------------------------------------------------------------
;tmp3(4)+tmp4(2)->X(4)
clc
mov ax, tmp3
mov dx, tmp3+2
mov bx, tmp4
add ax, bx
adc dx, 0
mov X, ax
mov X+2, dx
@@exit: ;вихід із процедури
;відновлення вмістимого регістрів
pop cx
pop bx
pop dx
pop ax
ret
calculation ENDP
CODESG ENDS
END
Файл output.asm
DOSSEG
;глобальна змінна
EXTRN X:WORD
;************************************************************
;опис спільного сегменту даних у модулі calc
DATASG SEGMENT PARA PUBLIC 'Data'
message_output db 10, 13
db '--------------------------------------------------------------------------------', 10, 13
db '!!!_obchyslennia vykonano_!!!', 10, 13
db '--------------------------------------------------------------------------------', 10, 13
db 'Result is : X = ', '$'
mes_out_x db 10 dup (' '), '$' ;змінна для зберігання результату обчислення в ASCII форматі
DATASG ENDS
;************************************************************
;опис спільного сегменту коду у модулі calc
CODESG SEGMENT PARA PUBLIC 'CODE'
ASSUME DS:DATASG, CS:CODESG
MOV AX,DATASG
MOV DS,AX
;-------------------------------------------------------------------
;вивести на екран результат обчислень-------------------------------
output PROC FAR
public output
;збереження вмістимого регістрів
push ax
push dx
push bx
push cx
;виведення на екран
mov ah, 09
lea dx, message_output
int 21h
call r16_to_ASCII
mov ah, 09
lea dx, mes_out_x
int 21h
;відновлення вмістимого регістрів
pop cx
pop bx
pop dx
pop ax
ret
output ENDP
;-------------------------------------------------------------------
;здійснити перетворення з 16 формату до ASCII-----------------------
r16_to_ASCII PROC near
mov ax, word ptr X
mov bx, 10d
xor si, si
loop30:
cmp ax, 10d
jb dali1
xor dx, dx
div bx
mov mes_out_x[si], dl
add mes_out_x[si], 30h
inc si
jmp loop30
dali1:
mov mes_out_x[si], al
add mes_out_x[si], 30h
;дзеркально обертаємо результат обчислень для виведення на екран
mov ax, si ;кількість значущих байт для виведення
xor di, di
mov dl, 2
div dl ;проходити потрібно лише до середини
mov cl, al
xor ch, ch
loop40:
mov al, mes_out_x[di] ;беремо з початку повідомлення байт
mov bl, mes_out_x[si] ;беремо з кінця повідомлення байт
mov mes_out_x[di], bl ;заносимо з кінця в початок
mov mes_out_x[si], al ;заносимо в кінець з початку
dec si ;для руху з кінця до середини
inc di ;для руху з початку до середини
cmp cx, 0
jb loop40
ret
r16_to_ASCII ENDP
CODESG ENDS
END
Скрін програми :
/
/
Висновок :
На даній лабораторній роботі я оволодів навиками створення багатомодульних програм та засвоїв правила взаємодії різних модулів.