
- •2. Команди додавання і віднімання.
- •3. Команди множення й ділення.
- •Індивідуальні завдання:
- •Контрольний приклад
- •Індивідуальні завдання:
- •Контрольний приклад
- •Індивідуальні завдання:
- •Контрольний приклад
- •Команда lea. При використанні регістрів-модифікаторів часто доводиться записувати в них ті чи інші адреси. Нехай, наприклад, нам необхідно занести в регістр вх адресі змінної X:
- •Індивідуальні завдання.
- •Контрольний приклад
- •1. Сегментування адрес у пк
- •1.1. Загальна схема базування адрес
- •1.2. Особливості сегментування адрес у пк
- •Zzzzz - абсолютна адреса
- •1.3. Сегментні регістри за замовчуванням
- •2. Програмні сегменти
- •2.1. Значення імені сегмента
- •2.2. Адресні змінні
- •Bl dw а2 еквівалентно Bl dw offset а2 в2 dd а2 еквівалентно в2 dd вод а2 : offset a2
- •Індивідуальні завдання:
- •Контрольний приклад
- •1. Схема базування адреси в пк?
- •Invoke виpаз [, аpгументи]
- •Invoke ExitProcess, 0
- •Invoke ExitProcess, 0
- •Invoke MessageBox, null, addr MsgBoxText, addr MsgBoxCaption, mb_ok
- •Invoke ExitProcess, null
- •Invoke MessageBox,null, addr MsgBoxText,addr MsgBoxCaption,mb_ok
- •Хід роботи:
- •Написати програму, що створює полнофункціональну Windows пpогpаму, яка виводить повідомлення "Win32 assembly is great!".
- •Контрольні питання:
- •Лабораторна робота n 2
- •Теоретичні відомості.
- •Invoke LoadIcon,null,idi_application
- •Invoke LoadCursor,null,idc_arrow
- •Invoke RegisterClassEx, addr wc ; Регістpація нашого класу вікна
- •Inst:hinstance,hPrevInst:hinstance,CmdLine:lpstr,CmdShow:dword
- •Invoke LoadIcon,null,idi_application
- •Invoke LoadCursor,null,idc_arrow
- •Invoke RegisterClassEx, addr wc
- •Invoke ShowWindow, hwnd,CmdShow
- •Invoke UpdateWindow, hwnd
- •Invoke GetMessage, addr msg,null,0,0
- •Invoke TranslateMessage, addr msg
- •Invoke DispatchMessage, addr msg
- •Invoke PostQuitMessage,null
- •Invoke DefWindowProc,hWnd,uMsg,wParam,lParam
- •Хід роботи:
- •Контрольні питання:
- •Лабораторна робота n 3
- •Теоретичні відомості.
- •Invoke BeginPaint,hWnd, addr ps
- •Invoke GetClientRect,hWnd, addr rect
- •Invoke EndPaint,hWnd, addr ps
- •Лабораторна робота n 4
- •Теоретичні відомості.
- •Invoke InvalidateRect, hWnd,null,true
- •Хід роботи:
- •Контрольні питання:
- •Характеристика повідомлень, що посилають від клавіатури.
- •Лабораторна робота n 5
- •Теоретичні відомості.
- •Invoke lstrlen,addr AppName
- •Invoke TextOut,hdc,hitpoint.X,hitpoint.Y,addr AppName,eax
- •Хід роботи:
- •Контрольні питання:
- •Лабораторна робота n 6
- •Теоретичні відомості.
- •Invoke LoadMenu, hInst, offset MenuName
- •Invoke LoadMenu, hInst, offset MenuName
- •Хід роботи:
- •Контрольні питання:
Zzzzz - абсолютна адреса
Оскільки приписування праворуч нуля до шістнадцятирічного числа еквівалентно множенню цього числа на 16, то формулу обчислення абсолютної адреси по адресній парі CP:ofs можна виразити так:
Аабс = 16*[CP] + ofs
Наприклад, якщо ES=1234h, тоді адресна пара ES:53h задає абсолютну адресу 16*1234h+53h = 12340h+53h = 12387h.
Тепер уточнимо одну річ. Як ми знаємо, адреси, що вказують у командах, можна модифікувати по регістрах ВХ, ВР, SI і DI. Як сполучається ця модифікація із сегментуванням? Правила тут наступні. Спочатку виконується модифікація адреси по регістрах-модифікаторах, у результаті чого виходить адреса, яку ми називаємо виконавчою. При цьому, нагадаємо, обчислення ведеться по модулю 216, тобто виконавча адреса - це завжди 16-розрядна адреса. Потім ця адреса розглядається як зсув і саме він сегментується, тобто саме до нього додається вміст сегментного регістра, помножене на 16. Причому дане підсумовування ведеться по модулі 220, щоб не вийшла адреса, більше максимально припустимого. Таким чином, більш точна формула обчислення абсолютної адреси така:
Аабс = (Авик + 16*[СР]) mod 220
Відзначимо, що сегментування виконавчої адреси відбувається, тільки якщо команда здійснює доступ до пам'яті. Якщо ж у команді не передбачене звертання до пам'яті, то сегментний регістр не враховується. Скажемо, команда LEA r16,А не звертається до пам'яті, тому адреса А не буде сегментуватися, тобто по цій команді виконується операція r16:=Авик, а не операція r16:=Аабс.
І, нарешті, розглянемо ще одну, найцікавішу особливість сегментування адрес у ПК.
1.3. Сегментні регістри за замовчуванням
Якщо проаналізувати реальні машинні програми, то можна помітити, що в них взагалі ж вказуються адреси всього із трьох областей пам'яті - сегмента команд, сегмента даних і сегмента стека. Наприклад, у командах переходу завжди вказуються адреси інших команд, тобто це посилання на сегмент команд - ту область пам'яті, де розташовуються команди програми. У командах, що працюють зі стеком, природно, вказуються адреси із сегмента стека - області пам'яті, займаної стеком. В інші ж командах (пересиланнях, арифметичних і т.п.) вказуються, як правило, адреси із сегмента даних - тієї області пам'яті, де розташовані змінні, масиви й інші дані програми.
З урахуванням цієї особливості реальних програм у ПК прийнятий ряд угод, які дозволяють у багатьох командах не вказувати явно сегментні регістри, а мати на увазі їх за замовчуванням.
По-перше, домовляються, щоб початкові адреси цих трьох сегментів пам'яті завжди перебували в певних сегментних регістрах, а саме: регістр CS повинен указувати на початок сегмента команд, регістр DS - на початок сегмента даних, а регістр SS - на початок сегмента стека:
Що ж стосується регістра ES, то щодо нього немає ніяких домовленостей, він вільний і може вказувати на будь-який сегмент пам'яті.
По-друге, домовляються про наступне. Перед адресами переходу регістр CS явно не вказується, він мається на увазі по умовчанню; наприклад, у команді переходу досить указати тільки мітку (JMP L), префікс же CS: буде доданий автоматично (JMP CS:L). При посиланнях на сегмент даних можна явно не вказувати регістр DS, він мається на увазі за замовчуванням; наприклад, якщо X - ім'я змінної, то замість команди MOV DS:X,0 можна писати просто MOV Х,0. І, нарешті, домовляються, що при посиланнях на стек можна явно не вказувати регістр SS, він мається на увазі за замовчуванням.
Більш точно правила про вибір сегментних регістрів за замовчуванням формулюються в такий спосіб:
• Адреси переходу завжди сегментуються по регістрі CS.
• У так званих рядкових командах діють особливі угоди.
• У всіх інших командах.
Якщо адреса в команді не модифікується або якщо він модифікується, але серед модифікаторів немає регістра ВР, то ця адреса вважається посиланням у сегмент даних і тому за замовчуванням сегментуються по регістрі DS; якщо адреса модифікується по ВР, то він уважається посиланням у стек і тому за замовчуванням сегментується по регістру SS. (Тут слід уточнити одну річ, пов'язану з непрямими переходами: Розглянемо такий фрагмент:
X DW l
JMP X ; JMP DS:X = goto CS:L L: ...
У команді переходу X - це ще не адреса переходу, а адреса того осередку, де перебуває адреса переходу. Тому в даній команді мається на увазі, що X є адресою із сегмента даних і тому за замовчуванням сегментується по регістрі DS. Але от коли із цього осередку обрана адреса переходу (L), то вже він сегментується по регістру CS.)
Отже, якщо в команді не зазначений сегментний регістр, то він вибирається згідно із цими правилами. Але якщо сегментний регістр зазначений явно, тоді ці правила не діють, а використовується зазначений регістр.
Що дають розглянуті угоди про сегментні регістри? А те, що якщо їх дотримуватися, тоді в більшості команд не треба явно вказувати сегментні регістри, оскільки потрібні в командах регістри саме збігаються з регістрами, якими мається на увазі за замовчуванням. Саме із цієї причини всі програми, які ми дотепер писали, були правильними, хоча ми в них ніякі сегментні регістри й не вказували.
Вигода від цих угод також і в тім, що вони дозволяють скоротити розміри машинних програм, оскільки перед переважною більшістю машинних команд можна не ставити команди-префікси, команди пророблять правильно й без них. Саме заради цього й пішли на "винос" префіксів за межі машинних команд: якби префікс вказувався в самій команді, то угоди нічого не дали б - місце для префікса однаково залишалося б у команді, і це місце однаково треба було б чимось заповнювати. Префікси ж як окремі команди можна опускати, не вказувати.
Отже, якщо дотримуватися угод про сегментні регістри, то текст програми на мові Асемблера буде коротше й розмір машинної програми буде менше.
А що буде, якщо цих угод не дотримуватися? Тоді потрібно буде в командах виписувати повні адресні пари. Наприклад, якщо ми встановили на початок області даних не регістр DS, а, скажемо, регістр SS, тоді для засилання 0 в осередок X із цієї області потрібно писати команду MOV SS:X,0; писати ж команду MOV Х,0 уже не можна, тому що вона сприйметься як MOV DS:X,0 і тому зашле 0 невідомо куди. Ясно, що краще дотримуватися зазначених угод, якщо ми не хочемо увесь час виписувати префікси.
Відзначимо, що одне із цих угод не можна порушувати в жодному разі: регістр CS завжди зобов'язаний указувати на початок сегмента команд. Це пояснюється тим, що в ПК адресу чергової команди, яку треба виконати, завжди вказується парою регістрів CS і IP, де покажчик команд IP містить зсув цієї команди, відлічене від початку сегмента команд. Тому, якщо регістр CS буде вказувати не на початок сегмента команд, то поводження машини буде непередбаченим. По цій же причині перед адресами переходу не можна вказувати префікс, відмінний від CS: (префікс же CS: можна не вказувати, оскільки він і так: мається на увазі за замовчуванням).
Тепер розглянемо таке питання: якщо дотримуватися угод про сегментні регістри, то чи існують випадки, коли доводиться явно вказувати сегментні регістри? Так, такі випадки є, і їх взагалі ж два,
Перший - це коли ми в цілому дотримуємося даних угод, але в якімсь окремому випадку хочемо їх порушити. Нехай, приміром, ми хочемо записати в регістр АХ уміст осередку L, розташованої в сегменті команд. Використати тут команду MOV AX,L не можна, оскільки вона сприйметься як MOV AX,DS:L і тому запише в АХ уміст осередку, що має зсув L, але перебуває не в сегменті команд, а в сегменті даних. Як видно, нас тут не влаштовує угода про те, що в команді MOV за замовчуванням береться регістр DS, тому ми зобов'язані явно вказати той сегментний регістр, що нам потрібний, тобто ми зобов'язані записати команду MOV AX,CS:L.
Другий випадок - це коли нам треба працювати із сегментом пам'яті, відмінним від сегментів команд, даних і стека. У цьому випадку на початок цього сегмента встановлюють регістр ES і при посиланнях на осередки цього сегмента використають даний регістр, наприклад: INC ES:Y. Опускати тут префікс ES: не можна, тому що він не мається на увазі за замовчуванням. Відзначимо попутно, що регістр ES саме й призначений для роботи з додатковими сегментами пам'яті.
Отже, є випадки, коли доводиться явно вказувати сегментні регістри але, на щастя, ці випадки зустрічаються рідко, і в більшості команд вказуються не повні адресні пари, а тільки зсуву.