НАЦІОНАЛЬНИЙ УНІВЕРСИТЕТ
«ЛЬВІВСЬКА ПОЛІТЕХНІКА»
Кафедра ЕОМ
/
Звіт
лабораторної роботи №7
«Багатомодульне програмування»
з дисципліни:
«Засоби системного програмування»
Варіант №20
Львів-2013
Мета: Оволодіти навиками створення багатомодульних програм. Засвоїти правила взаємодії різних модулів.
Багатомодульне програмування
При роботі на мові Асемблер під багатомодульним програмуванням розуміють процес написання кількох процедур, підпрограм, у різних вихідних файлах з подальшим їх об’єднанням на етапі лінкування. Таке доцільно робити, коли:
є необхідність компонування декількох програм написаних на різних мовах програмування (наприклад, для об’єднання потужності мов високого рівня та ефективності асемблера);
програма, написана у вигляді одного модуля, може виявитись завеликою для асемблювання;
окремі модулі можуть бути написані різними проектувальниками з метою подальшої інтеграції;
з причини великого розміру виконавчого модуля може виникнути необхідність перекриття окремих частин програми в процесі виконання.
Кожна програма асемблюється окремо і генерує власний унікальний об’єктний (OBJ) модуль. Програма компонувальник (LINK) як правило виконання починається з основної програми, яка викликає одну або декілька підпрограм. Підпрограми, в свою чергу, можуть задіювати інші підпрограми.
Виклик іншої програми зумовлює необхідність міжсегментного (довгого) виклику (CALL). Дана операція спочатку записує до стеку вміст регістру CS і заносить до цього регістру адресу іншого сегменту, потім записує до стеку значення регістру IP і заносить до цього регістру нову відносну адресу.
Таким чином, в стеку запам’ятовуються і адреса кодового сегменту, і зміщення для наступного повернення з підпрограми.
Команда CALL в основній програмі повинна “знати”, що програма, яка викликається, знаходиться поза межами даного сегменту. Директива EXTRN вказує асемблеру, що посилання на SUBPROG має атрибут FAR, тобто є визначеним в іншому асемблерному модулі.
Директива EXTRN має такий формат:
EXTRN ім’я : тип [, … ]
Можна призначити більше одного імені (до кінця рядка) або закодувати додаткові директиви EXTRN. В іншому асемблерному модулі відповідне ім’я повинно бути визначене і ідентифіковане як PUBLIC. Існують такі типи елементів: ABS, BYTE, WORD, DWORD, FAR, NEAR. Ім’я може бути визначене через EQU і повинно задовольняти реальному визначенню імені.
Директива PUBLIC вказує асемблеру і компонувальнику, що адреса вказаного ідентифікатора є доступною з інших програм. Директива має такий формат:
PUBLIC ідентифікатор [, … ]
Можна призначити більше одного ідентифікатора (до кінця рядка) або закодувати додаткові директиви PUBLIC. Ідентифікатори можуть бути мітками (включаючи PROC-мітки), змінними або числами. Неправильними ідентифікаторами є імена регістрів та EQU-ідентифікатори, що призначають більш як двобайтні значення.
Існує декілька способів компонування програм. Кожен з них характеризується одною або декількома такими рисами:
директиви EXTRN і PUBLIC в якості міток;
спільний кодовий сегмент - шляхом використання директиви PUBLIC в заголовку кодового сегменту кожної з програм, що компонуються;
спільні дані.
Використання директив EXTRN і PUBLIC в якості міток
Програма, приведена нижче, складається з основної програми MAINPROG та підпрограми SUBMUL. Директива EXTRN в основній програмі визначає SUBMUL як точку входу в підпрограму. Підпрограма містить директиву PUBLIC, яка вказує компонувальнику на те, що точкою входу для виконання є мітка SUBMUL. Підпрограма перемножує вміст регістру AX (значення з поля даних PRICE) на вміст регістру BX (значення з поля даних QTY), при цьому результат операції буде розміщено в регістровій парі DX:AX (в даному випадку – 002E 4000). В підпрограмі відсутній сегмент даних, оскільки вона не визначає ніяких даних.
*Примітка: Для успішного компонування компонувальник потребує знайти хоча б один стековий сегмент. В підпрограмі не визначено стекового сегменту, оскільки вона використовує ті ж самі стекові адреси, що й основна програма. Таким чином, стек, що визначається в основній програмі, є доступним і в підпрограмі.
Використання спільного кодового сегменту
В основній програмі та в усіх підпрограмах, що компонуються разом, необхідно використати атрибут PUBLIC в директиві SEGMENT:
CODESG SEGMENT PARA PUBLIC 'CODE'
В даному випадку з таблиці ідентифікаторів слідує, що узагальнений тип кодового сегменту – PUBLIC. Необхідно зауважити, що карта компонування вказує на наявність в програмі лише одного кодового сегменту - внаслідок того, що заголовки кодових сегментів основної програми та підпрограми співпадають, компонувальник об’єднує їх в один фізичний кодовий сегмент.
Використання спільних даних
Наявність спільних даних уможливлює обробку в одному асемблерному модулі даних, що є визначеними в іншому асемблерному модулі.
Для того, щоби змінні PRICE та QTY визначались в основній програмі, але завантаження значень з цих областей пам’яті в регістри BX та AX виконувалась в підпрограмі, необхідно виконати такі зміни в програмах з попереднього пункту:
в основній програмі ідентифікатори PRICE та QTY визначені як PUBLIC. Сегмент даних так само визначений як PUBLIC;
в підпрограмі ідентифікатори PRICE та QTY визначені як EXTRN та WORD. Таке визначення вказує асемблеру на довжину цих полів в 2 байт.
*Примітка:
Основна програма та підпрограма можуть визначати будь-які інші елементи даних, але спільними елементами будуть лише ті, що мають атрибути PUBLIC та EXTRN.
При компонуванні двох і більше асемблерних модулів необхідно визначити стек достатньо великого розміру – на практиці для великих програм визначення розміру в 64 слова є достатнім.
Передача параметрів через стек
Даний спосіб взаємодії програм характеризується тим, що програма, що викликає іншу, передає їй параметри шляхом запису даних до стеку. Кожна команда 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 - помилки можуть привести до непередбачуваного результату.
ЗАВДАННЯ:
Створити *.exe програму, яка реалізовує обчислення, заданого варіантом виразу.
Програма повинна складатися з чотирьох модулів:головний модуль – містить спільний сегмент стеку, необхідні дані та виклик основних процедур;модуль вводу - забезпечує ввід даних з клавіатури в десятковій формі;модуль безпосередніх обчислень – здійснює всі необхідні арифметичні дії. модуль виводу – забезпечує вивід на екран результату в десятковій формі.
Всі модулі повинні бути в різних файлах і об’єднані на етапі лінкування. Передача параметрів може здійснюватися довільним чином.
Переконатися у правильності роботи кожного модуля зокрема та програми загалом.
Скласти звіт про виконану роботу з приведенням тексту програми та коментарів до неї.
Дати відповідь на контрольні запитання.
ВАРІАНТИ ЗАВДАННЯ – ДИВ. ЛАБОРАТОРНУ РОБОТУ №4.
'X=K+B(2)/C(1)-D(2)*F(2)-E(1) K=6DD02316h'
Текст програми
Main.asm
EXTRN input :FAR, calculation :FAR, output :FAR
PUBLIC erFlag
STACKSG SEGMENT PARA STACK 'Stack'
DW 127 DUP(0)
STACKSG ENDS
DATASG SEGMENT PARA PUBLIC 'Data'
erFlag DB 0
DATASG ENDS
CODESG SEGMENT PARA PUBLIC 'Code'
main:
ASSUME CS:CODESG,DS:DATASG,SS:STACKSG
MOV AX,DATASG
MOV DS,AX
CALL input
cmp erFlag,0
jne A30
CALL calculation
cmp erFlag,0
jne A30
CALL output
cmp erFlag,0
jne A30
A30:
mov ah,4Ch
int 21h
CODESG ENDS
END main
Input.asm
EXTRN erFlag:BYTE
PUBLIC B,C,D,E, F
MY_MUL MACRO X,Y,Z
mov z,0
mov z+2,0
MOV AX,X
MUL Y
MOV Z,AX
MOV Z+2,DX
MOV AX,X+2
MUL Y
ADD Z+2,AX
mov ax,Z
mov dx,Z+2
ENDM
DATASG SEGMENT PARA PUBLIC 'Data'
B dw 0000h
C db 00h
D dw 0000h
F dw 0000h
E db 00h
X dw 00h,00h
X_Str db 10 dup (0)
TempStr db 10 dup (0)
TempBin dw 0,0
MaxLen dw 0
X_div2 dw 0,0
Y_div2 dw 0
MESSG_X DB 13,10,'X=K+B(2)/C(1)-D(2)*F(2)-E(1) K=6DD02316h','$'
MESSG_B DB 13,10,'B= ','$'
MESSG_C DB 13,10,'C= ','$'
MESSG_D DB 13,10,'D= ','$'
MESSG_E DB 13,10,'E= ','$'
MESSG_F DB 13,10,'F= ','$'
MESSG_X1 DB 13,10,'X= ','$'
erStr1 db 13,10,'Data not input_variable',13,10,'$'
erStr2 db 13,10,'Incorrectly data ',13,10,'$'
erStr2_1 db 13,10,' D =0 --> divide by zero ',13,10,'$'
erStr3 db 13,10,'Data is too long ',13,10,'$'
Mult10 dw 1,0
my_z dw 0,0
DATASG ENDS
CODESG SEGMENT PARA PUBLIC 'CODE'
ASSUME DS:DATASG, CS:CODESG
input proc FAR
public Input
lea dx, MESSG_X
mov ah, 09
int 21h
LEA DX,MESSG_B
MOV AH,09
INT 21H
mov di,offset B
mov MaxLen,5
mov cx,MaxLen
call input_variable
LEA DX,MESSG_C
MOV AH,09
int 21h
mov di,offset C
mov MaxLen,3
mov cx,MaxLen
call input_variable
cmp C,0
jne cont
mov ah,09
mov dx, offset erStr2_1
int 21h
mov ah,4Ch
int 21h
cont: ;X=K+B(2)/C(1)-D(2)*F(2)-E(1) K=6DD02316
LEA DX,MESSG_D
MOV AH,09
INT 21H
mov di,offset D
mov MaxLen,3
mov cx,MaxLen
call input_variable
LEA DX,MESSG_F
MOV AH,09
INT 21H
mov di,offset F
mov MaxLen,3
mov cx,MaxLen
call input_variable
LEA DX,MESSG_E
MOV AH,09
INT 21H
mov di,offset E
mov MaxLen,3
mov cx, MaxLen
call input_variable
ret
input ENDP
input_variable PROC
mov si,0
In_00: mov ah,01
int 21h
cmp al,0Dh
je In_1
In_0: mov dl,al
call CHECK_BYTE
mov TempStr[si],dl
inc si
loop In_00
In_1: push si
dec si
cmp cx,MaxLen
jne In_2
call Err1
In_2: mov bh,0
mov bl,TempStr[si]
MY_MUL Mult10,bx,my_z
add TempBin,ax
adc TempBin+2,dx
mov bh,0
mov bl,10
MY_MUL Mult10,bx,my_z
mov Mult10,ax
mov Mult10+2,dx
dec si
cmp si,0
jge In_2
mov ax, TempBin
mov dx,TempBin+2
pop si
cmp si,MaxLen
jl In_3
cmp MaxLen,10
jl In_2_1
js In_Err
cmp dx,0FFFFh
ja In_Err
jmp In_3
In_2_1: cmp MaxLen,5
jl In_2_2
cmp dx,00
ja In_Err
cmp ah,0ffh
ja In_Err
jmp In_3
In_2_2: cmp ax,00FFh
jbe In_3
In_Err: LEA DX,erSTR3
MOV AH,09
INT 21H
mov ah,4Ch
int 21h
In_3: mov [di],ax
mov [di+2],dx
mov TempBin,0
mov TempBin+2,0
mov Mult10,1
mov Mult10+2,0
RET
input_variable ENDP
Err1 PROC
LEA DX, erSTR1
MOV AH,09
INT 21H
mov ah,4Ch
int 21h
RET
Err1 ENDP
CHECK_BYTE PROC
sub dl,30h
cmp dl,00
jl ErS
cmp dl,0Ah
jl GO
ErS: LEA DX,erSTR2
MOV AH,09
INT 21H
mov ah,4Ch
int 21h
GO: RET
CHECK_BYTE ENDP
CODESG ENDS
END
Calc.asm
EXTRN B:WORD,C:BYTE,D:WORD, F:WORD, E:BYTE
PUBLIC X;,MESSG_Sign
DATASG SEGMENT PARA PUBLIC 'Data'
K_L EQU 2316h
K_H EQU 6DD0h
Temp1 dw 00h,00h
Temp2 dw 00h
Temp3 dw 00h
Temp4 dw 00h,00h
X dw 00h,00h
DATASG ENDS
CODESG SEGMENT PARA PUBLIC 'CODE'
ASSUME DS:DATASG, CS:CODESG
MOV AX,DATASG
MOV DS,AX
calculation PROC FAR
xor ax, ax ;X=K+B(2)/C(1)-D(2)*F(2)-E(1) K=6DD02316
xor bx, bx
xor cx, cx
xor dx, dx
; B/C
mov ax, B
mov bh, 0
mov bl, C
div bx
mov Temp1, ax
mov ax, 0
; K+B/C
mov dx, K_H
mov ax, K_L
add ax, Temp1
adc dx, 0
mov Temp2, ax
mov Temp2+2, dx
mov dx, 0
; D*F
mov ax, D
mul F
mov Temp3,ax
mov ax,0
; (K+B(2)/C(1))-(D(2)*F(2))
mov ax, Temp2
mov dx, Temp2+2
sub ax, Temp3
sbb dx, 0
mov Temp4, ax
mov ax, 0
; X=(K+B(2)/C(1)-D(2)*F(2))-(E(1))
mov ax, Temp4
mov dx, Temp4+2
mov bh, 00
mov bl, E
sbb dx, 0
mov X, ax
mov X+2, dx
ret
calculation ENDP
CODESG ENDS
END
Output.asm
EXTRN X:WORD;, MESSG_Sign :BYTE
DATASG SEGMENT PARA PUBLIC 'Data'
X_Str db 10 dup (0)
TempStr db 10 dup (0)
TempBin dw 0,0
MaxLen dw 0
X_div2 dw 0,0
Y_div2 dw 0
MESSG_X1 DB 13,10,'X= ','$'
erStr1 db 13,10,'Data not input_variable',13,10,'$'
erStr2 db 13,10,'Incorrectly data ',13,10,'$'
erStr2_1 db 13,10,' D =0 --> divide by zero ',13,10,'$'
erStr3 db 13,10,'Data is too long ',13,10,'$'
Mult10 dw 1,0
my_z dw 0,0
DATASG ENDS
CODESG SEGMENT PARA PUBLIC 'CODE'
ASSUME DS:DATASG, CS:CODESG
MOV AX,DATASG
MOV DS,AX
output PROC
mov di,0
mov Y_div2,10
mov cx,X
mov bx,X+2
O_1: mov X_div2,cx
mov X_div2+2,bx
call my_div2
add dl,30h
mov X_Str[di],dl
inc di
cmp bx,0
ja O_1
cmp cx,10
jae O_1
add cl,30h
mov X_Str[di],cl
mov dx,offset MESSG_X1
mov ah,09
int 21h
O_2:
mov dl,X_Str[di]
mov ah,02h
int 21h
dec di
jge O_2
ret
output ENDP
MY_DIV2 proc
sub cx,cx
sub bx,bx
mov dx,X_div2+2
mov ax,X_div2
M2_D1:
cmp dx,Y_div2
jb M2_D3
sub ax,Y_div2
sbb dx,00
add cx,01
adc bx,0
jmp M2_D1
M2_D3:
div Y_div2
add cx,ax
adc bx,00
ret
MY_DIV2 ENDP
CODESG ENDS
END
Висновки: На цій лабораторній роботі я оволодів навиками створення багатомодульних програм. Засвоїв правила взаємодії різних модулів.