Практикум_СП
.pdfПочаток
введення
паролю
Не |
правильно |
перевірка |
паролю |
Правильно |
виконання |
програми |
Кінець
Рисунок 6.2 Алгоритм функціонування програми з паролем.
Текст програми для виведення рядка Hitman, захищеної паролем:
model small stack 256 dataseg
password db 'H2' |
сегмент даних для |
|
pass_len = $-password |
||
string db 80 dup(?) |
програми пароля |
|
promt db 13,10,'vvedite parol:$' |
|
|
ok db 13,10,'parol prinyat$' |
|
|
H2 db 13,10,'Hitman','$' ;сегмент |
даних для програми ;виведення |
|
рядка |
|
|
codeseg |
mov ax,@data |
|
start: |
|
|
mov ds,ax |
|
|
begin: |
mov ah,09h |
|
mov dx,offset promt |
|
|
int 21h |
|
|
mov bx,0 |
|
|
pass: |
mov ah,08h |
|
int 21h cmp al,13 je compare
mov [string+bx],al
61
mov ah,02 |
сегмент коду для |
mov dl,'*' |
|
int 21h |
програми пароля |
inc bx |
|
jmp pass |
|
compare: push ds |
|
pop es |
|
mov si,offset string |
|
mov di,offset password |
|
cld |
|
mov cx,pass_len |
|
repe cmpsb |
|
jne error |
|
mov ah,09h |
|
mov dx,offset ok |
|
int 21h |
|
mov ah,9h |
;сегмент коду для програми виведення рядка |
mov dx,offset H2 |
|
int 21h |
|
exit:mov ax,4C00h int 21h
error: jmp begin ;перехід на перевірку пароля при введенні ;неправильного паролю
end start
Рисунок 6.3 Компіляція і запуск програми.
62
Рисунок 6.4 Дія програми при введенні невірного паролю.
Рисунок 6.5 Дія програми при введенні вірного паролю.
63
Практична робота № 7
Тема: Організація підпрограм. Способи передачі параметрів. Макроси.
Мета роботи: Вивчення способів організації процедур і передачі параметрів в програмах на мові асемблера х86.
Теоретичні відомості
Процедури (підпрограми) широко використовуються практично у всіх мовах програмування, оскільки забезпечують можливість модульного програмування складних алгоритмів, тобто окремої розробки, тестування, документування і зберігання в бібліотеках модулів, які можуть використовуватися іншими програмними одиницями, де потрібне виконання закладеного в модулях алгоритму. У програмі потрібно зберігати тільки одну копію процедури, яка може викликатися з будь-яких місць програми з подальшим поверненням на наступну команду після команди виклику процедури. Процедура оформляється за допомогою спеціальних директив початка і кінця, від яких залежить і вид використовуваної усередині тіла процедури команди повернення:
name |
PROC type |
|
|
- |
;Оператори процедури |
|
- |
|
RET |
- |
;Оператор повернення в модуль, що викликав |
|
||
name ENDP |
|
|
Команди виклику процедури |
||
CALL |
name |
|
забезпечують автоматичне збереження в стеку адреси наступної команди, щоб потім командою повернення з підпрограми можна було передати сюди управління і продовжити далі виконання програми, що викликала процедуру. При роботі з процедурами необхідно забезпечити баланс стека, тобто на момент виходу у вершині стека повинна знаходитися адреса повернення, інакше можлива передача управління на випадкові елементи пам'яті. Залежно від типу type процедури в стек заноситься або тільки зсув (type = NEAR, тобто внутрішньосегментний виклик) або повна логічна адреса Seg: Offset (type = FAR, тобто міжсегментний виклик). Від цього описувача залежить і код команди повернення RET. Якщо процедура визначена з атрибутом NEAR (він приймається за умовчанням), то команда RET заносить одне слово з верхівки стека в IP і тим самим забезпечує повернення в межах того сегменту, в якому знаходиться і сама процедура, виклики її з інших сегментів здійснювати неможливо. Якщо ж процедура визначена як дальня (має атрибут FAR), то команда повернення RET вибирає із стека два слова: перше (Offset) поміщається на IP, а друге (Seg) поміщається на CS, що дає можливість
64
викликати цю процедуру з будь-якого сегменту (у тому числі і з того, де вона визначена). Режими адресації команди CALL такі ж, як і для команди безумовного переходу JMP, тобто виклик може бути прямим, непрямим, внутрішньосегментним або міжсегментним, не допускається тільки короткий виклик:
CALL |
[NEAR PTR] |
name ;внутрішньосегментний |
прямий |
|
виклик по зсуву щодо імені name |
|
|
|
|
CALL |
[NEAR PTR] |
opr ;внутрішньосегментний |
непрямий |
|
виклик opr – регістр або слово в пам'яті, де міститься адреса процедури |
||||
CALL |
[FAR PTR]name ;міжсегментний прямий виклик процедури |
|||
за адресою (Seg:Offset) імені name |
|
|
|
|
CALL |
[FAR PTR]opr |
;міжсегментний |
непрямий |
виклик |
процедури за адресою (Seg:Offset), записаною в пам'яті |
на яку указує операнд |
|||
opr |
|
|
|
|
Операнд opr в командах непрямого переходу являє собою ім'я регістра, в якому міститься адреса переходу (тільки для внутрішніх переходів), або ім'я змінної, де записана адреса переходу (одне слово для внутрішньосегментного переходу або два слова для міжсегментного переходу), чи ж адресний вираз, яким визначається адреса пам'яті, де зберігається адреса переходу. Слід особливо підкреслити, що у разі внутрішньосегментного прямого виклику в команді CALL зберігається не адреса переходу (Offset), а зсув (Displacement) в байтах від сдедующей після CALL, команди до точки входу в процедуру. Під час виконання команди CALL воно складається з поточним вмістом IP, внаслідок чого його вміст стає рівним цільовій адресі (Offset) входу в процедуру. Це дозволяє переміщати кодовий сегмент в пам'яті без корекції інформації про переходи. У разі прямих міжсегментних викликів в команді CALL зберігається повна логічна адреса точки входу в процедуру (Seg: Offset) і, отже, він повинен коректуватися при переміщеннях сегменту, в якому знаходиться точка переходу.
;Приклади викликів процедур
;Модулі mod1 і mod2 асемблюються окремо
NAME mod1 Dseg SEGMENT
Addr_PN |
DW |
Go_Proc |
; адреса процедури Offset |
||
Addr_PF |
DD |
Start_Pr |
; адреса процедури Seg:0ffset |
||
Tabl_PN |
DW |
Fadd,Fsub,Fmul,Fdiv ;табл. адр. процедур Offset |
|||
Tabl_PF |
DD |
Dadd,Dsub,Dmul,Ddiv |
;те ж, але у вигляді Seg: Offset |
||
SwitchDW |
? |
|
; перемикач: 0, 1, 2 або 3 |
||
Dseg |
ENDS |
|
|
|
|
Cseg1 SEGMENT 'CODE' |
|
|
|||
|
EXTRN Begin: FAR |
|
|
||
Start |
PROC FAR |
|
|
|
|
|
PUSH DS |
|
; підготуватися |
65
|
SUB |
AX,AX |
|
; до повернення |
|
PUSH AX |
|
; у DOS |
|
; Приклади внутрішньосегментних викликів процедур |
||||
Go_Proc |
PROC |
|
|
|
|
MOV |
SI,Switch |
|
|
|
SHL |
SI, 1 |
|
; підготувати покажчик в таблиці |
|
CALL Tabl_PN[SI] |
|
; непрямий виклик в сегменті |
|
|
... |
|
|
|
Go_Proc |
ENDP |
|
|
|
|
... |
|
|
|
Start_Pr |
PROC FAR |
|
|
|
|
|
- |
|
|
|
|
- |
|
|
|
RET |
- |
|
|
|
|
|
|
|
Start_Pr |
ENDP |
|
|
|
|
... |
|
|
|
|
CALL Tabl_PN + 4 |
|
;непрямий виклик Fmul |
|
|
... |
|
|
|
|
MOV |
BX,Tabl_PN[SI] |
;ВХ = адреса з таблиці |
|
|
CALL ВХ |
|
;непрямий виклик (адреса в BX) |
|
|
... |
|
|
|
|
LEA |
ВХ,Tabl_PN |
|
;ВХ = база таблиці |
|
CALL NEAR PTR [BX] |
;непрямий виклик Fadd |
||
|
... |
|
|
|
|
CALL NEAR PTR [BX][SI] ;непрямий виклик процедури, її адреса визначається |
|||
;перемикачем SI в таблиці, початкова адреса якої ;міститься у ВХ |
||||
|
CALL Go_Proc |
|
;прямий виклик в сегменті типу NEAR |
|
; Приклади міжсегментних викликів процедур |
||||
|
... |
|
|
|
|
CALL Begin |
;прямий міжсегментний по зовнішньому імені |
||
|
... |
|
|
|
|
CALL Dsub |
|
;прямий міжсегментний по мітці типу FAR |
|
|
... |
|
|
|
|
CALL Tabl_PF + 8 |
;непрямий міжсегментний на Dmul |
||
|
... |
|
|
|
|
MOV |
SI,Switch |
|
;перемикач адр. дальнього переходу |
|
SHL |
SI,1 |
|
|
|
SHL |
SI,1 |
|
;4*SI = 0, 4, 8 або 12 |
|
CALL Tabl_PF[SI] |
;непрямий міжсегментний по таблиці |
||
|
... |
|
|
|
|
LEA |
BX,Tabl_PF |
;ВХ = база таблиці |
|
|
CALL FAR PTR [BX] |
;непрямий міжсегментний на Da |
||
|
... |
|
|
|
|
CALL FAR PTR [BX][SI] |
;непрямий між сегментний по |
||
;адресі з таблиці Tabl_JF, який визначається перемикачем в SI |
||||
|
... |
|
|
|
|
RET |
|
|
; повернення в DOS |
Csegl |
ENDS |
|
|
|
Cseg2 |
SEGMENT 'CODE' |
|
|
66
FADD PROC DSUB PROC FAR
--
--
-RET
RET DSUB ENDP
Fadd ENDP ;
;Dmul PROC FAR
Fsub PROC -
--
-RET
-Dmul ENDP RET ;
Fsub |
ENDP Ddiv |
PROC FAR |
|
; |
|
|
- |
Fmul |
PROC - |
|
|
|
- |
RET |
|
|
- |
Ddiv |
ENDP |
-...
|
RET CALL |
Addr_PF |
;непряма. |
Fmul |
ENDP ... |
|
;межсегм. за адресою |
; |
Cseg2 ENDS |
|
|
Fdiv |
PROC END |
Start |
|
-Name mod2
-Cseg3 DEGMENT 'CODE'
-PUBLIC Begin
RET -
Fdiv ENDP -
;Begin PROC FAR
Dadd PROC FAR -
--
-Begin ENDP
--
RET -
Dadd ENDP Cseg3 ENDS
;END
Модуль і процедура, що викликається, використовують одні і ті ж регістри. Перед викликом процедури необхідно зберегти потрібні регістри чи, що доцільніше, будувати процедури так, щоб при вході в процедуру зберігалися в стеку ті регістри, які в ній використовуватимуться, а потім перед поверненням процедура відновлювала ці регістри із стека. Важливим при використанні процедур є спосіб передачі параметрів, тобто забезпечення взаємного або роздільного використання даних в викликаємих модулях і процедурах, що викликаються. У цьому плані можна розділити процедури, які обробляють один і той же, набір даних (статичні параметри), і процедури, які при кожному виклику можуть обробляти різні набори даних (динамічні параметри). Якщо процедура обробляє статичні параметри і розташована в тому ж модулі, що і викликаючий її модуль, то вона може безпосередньо звертатися до цих параметрів. У випадку, якщо процедура розробляється окремо від викликаємого її модуля, статичні параметри можна передати їй через директиву EXTRN,
67
визначивши їх в викликаємому модулі для зовнішнього використання директивою PUBLIC.
Прогресивнішим є спосіб взаємозв'язку з даним через динамічні параметри, і реалізується він звичайно шляхом передачі адрес параметрів. Це можна зробити через регістри, через таблицю адресу йти через стек. Вочевидь, що не у всіх випадках доцільно передавати адреси, окремі параметри можуть передаватися підпрограмі своїм значенням. Слід зазначити, що при передачі параметрів через стек зручно організовувати вікно в стеку, покажчиком в якому встановлюється регістр ВР, структуру вікна доцільно визначати директивою опису структури STRUC. При передачі параметрів через стек часто виникає необхідність перед поверненням управління в зухвалий модуль звільнити стек від непотрібних надалі параметрів. В цьому випадку корисною є команда повернення з операндом (RET expr), значення якого визначає, скільки байтів повинно бути пропущено в стеку після вибірки адреси повернення. Приведені нижче фрагменти програмних модулів ілюструють основні прийоми передачі параметрів процедурі.
; Передача параметрів через зовнішні посилання
|
NAMEmod1 |
|
|
|
CSeg_P |
SEGMENT 'CODE' |
|
||
|
EXTRN |
S_Arr: WORD,Average:WORD,Counter:WORD |
||
|
PUBLIC |
Avrg |
|
|
|
ASSUME |
CS:CSeg_P |
|
|
Avrg |
PROG FAR |
|
|
|
|
PUSH AX |
|
; зберегти |
|
|
PUSH DX |
|
; робочі |
|
|
PUSH CX |
|
; регістри |
|
|
PUSH SI |
|
|
|
|
MOV |
CX,Counter |
; узяти лічильник |
|
|
SUB |
AX,AX |
; обнулити |
|
|
MOV |
DX,AX |
; суму |
|
|
MOV |
SI,AX |
|
; і індекс |
Next |
ADD |
AX,S.Arr[SI] |
; накопичувати |
|
|
ADC |
DX,0 |
|
; суму в DX,АХ |
|
ADD |
SI,2 |
|
|
|
LOOP Next |
|
|
|
|
DIV |
Counter |
; обчислити |
|
|
MOV |
Average,AX |
; і записати результат |
|
|
POP |
SI |
|
; відновити |
|
POP |
CX |
|
; вміст |
|
POP |
DX |
|
; регістрів |
|
POP |
AX |
|
; із стека |
CSeg_P |
ENDS |
|
|
|
|
END |
|
|
|
; Цей модуль розробляється окремо від модуля mod1 |
||||
|
NAMEmod2 |
|
|
|
SSeg |
SEGMENT |
STACK |
|
|
|
DW |
100 DUP (?) |
|
68
St_TopLABEL |
WORD |
|
|||
SSeg |
ENDS |
|
|
|
|
DSeg |
SEGMENT |
'DATA' |
|
||
|
PUBLIС |
S_Arr,Average,Counter |
|||
D_Arr DW |
100 DUP (?) ; початковий масив цілих чисел |
||||
Average |
DW |
? |
|
; середнє арифметичне |
|
Counter |
DW |
? |
|
; лічильник кількості елементів |
|
DSeg |
ENDS |
|
|
|
|
CSeg_M |
SEGMENT |
'CODE' |
|
||
|
EXTRN |
Avrg:FAR |
|
||
|
ASSUME |
CS:CSeg_M,DS:DSeg,SS:SSeg |
|||
Main |
PROC FAR |
|
|
; стартова точка програми |
|
|
MOV |
AX,SSeg |
|
; підготувати |
|
|
MOV |
SS,AX |
|
; сегментний регістр |
|
|
MOV |
SP,OFFSET SS:St_Top |
; і покажчик стека |
||
|
PUSH |
DS |
|
|
; підготувати |
|
SUB |
AX,AX |
|
; покажчик на PSP |
|
|
PUSH |
AX |
|
|
; для повернення в DOS |
|
MOV |
AX,DSeg |
|
; підготувати |
|
|
MOV |
DS,AX |
|
; сегментний регістр даних |
|
|
... |
|
|
|
|
|
MOV |
S_Arr[SI],AX |
; тут формуються елементи |
||
|
INC |
CX |
|
|
; і лічильник масиву |
|
ADD |
SI,2 |
|
|
|
|
MOV |
Counter,CX |
; записати кількість елементів |
||
|
CALL |
Avrg |
|
|
; і викликати процедуру обробки |
|
MOV |
DX,Average |
; узяти результат |
||
|
... |
|
|
|
|
|
RET |
|
|
|
; повернення в DOS |
Main |
ENDP |
|
|
|
|
CSeg_M |
ENDS |
|
|
|
|
|
END |
Main |
|
|
|
Якщо використовувати можливість утворення загальних областей пам'яті, то викликаючий модуль і модуль, що містить процедуру, яка викликається, повинні включати наступний сегмент даних:
DSeg SEGMENT |
COMMON |
; початковий масив цілих чисел |
|||
S_Arr |
DW |
100 DUP (?) |
|||
Average |
DW |
? |
; |
середнє арифметичне |
елементів |
Counter |
DW |
? |
; |
лічильник кількості |
|
DSeg ENDS |
|
|
|
|
|
В цьому випадку директиви PUBLIC і EXTRN для параметрів не потрібні, оскільки зв'язок за даними здійснюється через загальну для зухвалої програми і процедури область пам'яті. Необхідно тільки в модулі процедури в директиві ASSUME додати вказівку про сегментний регістр даних DS:DSeg. Слід зазначити, що імена параметрів в сегментах даних викликаючого модуля і модуля процедури можуть бути різними, важливо тільки, щоб була збережена їх структура.
69
; Приклад передачі параметрів через стек |
||
NAMEmod1 |
|
|
SSeg |
SEGMENT |
STACK |
|
DW 100 DUP (?) |
|
St_TopLABEL |
WORD |
|
SSeg |
ENDS |
|
DSef |
SEGMENT 'DATA' |
S_Arr DW 100 DUP (?) |
; початковий масив цілих чисел |
|||
Average |
DW |
? |
; середнє арифметичне |
|
Counter |
DW |
? |
; лічильник кількості елементів |
|
DSeg |
ENDS |
|
|
|
CSeg_M |
SEGMENT 'CODE' |
|
||
|
EXTRN |
Avrg:FAR |
|
|
|
ASSUME |
CS:CSeg_M,DS:DSeg,SS:SSeg |
||
Main |
PROC FAR |
|
; точка запуску програми |
|
|
MOV |
AX,SSeg |
; підготувати |
|
|
MOV |
SS,AX |
|
; сегментний регістр |
|
MOV |
SP,OFFSET SS:St_Top |
; і покажчик стека |
|
|
PUSH |
DS |
|
; підготувати |
|
SUD |
AX,AX |
; покажчик на PSP |
|
|
PUSH |
AX |
|
; для повернення в DOS |
|
MOV |
AX,DSeg |
; підготувати |
|
|
MOV |
DS,AX |
; сегментний регістр даних |
|
|
... |
|
|
|
|
MOV |
S_Arr[SI],AX |
; тут формуються елементи |
|
|
INC |
CX |
|
; і лічильник масиву |
|
ADD |
SI,2 |
|
|
|
... |
|
|
|
|
MOV |
AX,OFFSET Average |
; занести в стек |
|
|
PUSH |
AX |
; адреса результату |
|
|
MOV |
AX,OFFSET S_Arr |
|
|
|
PUSH |
AX |
|
; адреса масиву |
|
PUSH |
CX |
|
; і кількість елементів |
|
CALL |
Avrg |
|
; викликати процедуру обробки |
|
POP |
DX |
|
; узяти результат |
|
... |
|
|
|
|
RET |
|
|
; повернення в DOS |
Main |
ENDP |
|
|
|
CSeg_M |
ENDS |
|
|
|
|
END |
Main |
|
|
; mod2 розробляється окремо |
|
|||
|
NAMEmod2 |
|
|
|
CSeg_P |
SEGMENT 'CODE' |
|
||
|
PUBLIC |
Avrg |
|
|
|
ASSUME |
CS CSeg_P |
|
|
Avrg |
PROC FAR |
|
|
|
|
PUSH BP |
|
; зберегти BP |
|
|
MOV |
BP,SP |
|
; BP - покажчик вікна в стеку |
|
PUSH |
AX |
|
; зберегти |
|
PUSH |
DX |
|
; робочі |
70