- •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
- •Хід роботи:
- •Контрольні питання:
1.2. Особливості сегментування адрес у пк
Насамперед відзначимо, що в ПК замість терміна "базування адрес" використовується термін "сегментування адрес", а базові регістри називають сегментними регістрами. Крім того, розмір пам'яті в ПК дорівнює 1 Мб, тобто 220 байтів (до=20), тому абсолютні адреси тут 20-розрядні, а розміри сегментів не повинні перевищувати величину 64 Кб, тобто 216 байтів (m=1б), тому зсуву тут - це 16-розрядні адреси.
Перша особливість сегментування адрес у ПК полягає в тім, що як сегментні регістри можна використати не будь-які регістри, а лише регістри CS, DS, SS і ES. Звичайно, початкові адреси сегментів пам'яті можна зберігати й в інших регістрах (скажемо, в АХ), однак використати ці інші регістри для сегментування адрес не вдасться.
Оскільки в ПК усього чотири сегментних регістри, то одночасно можна працювати із чотирма сегментами пам'яті: установлюємо на початок кожного із цих сегментів свій регістр і далі сегментуємо по ньому всі адреси із цього сегмента. А що робити, якщо треба працювати з більшим числом сегментів пам'яті, скажемо, з п'ятьма? Коли буде потрібно доступ до осередків з п'ятого сегмента, вибираємо один із сегментних регістрів і десь рятуємо (у звичайному регістрі або комірці пам'яті) його вміст, а потім записуємо в цей регістр початок п'ятого сегмента й далі використовуємо цей регістр для доступу до осередків п'ятого сегмента. Пізніше, якщо треба, можна відновити колишнє значення цього регістра, щоб він знову вказував на один з перших 4 сегментів. Міняючи в такий спосіб значення сегментних регістрів, можна працювати з будь-яким числом сегментів пам'яті.
Відзначимо, що взаємне розташування сегментів пам'яті може бути кожним. Вони можуть не перетинатися (див. ліворуч), можуть перетинатися (див. у середині), а можуть і повністю збігатися (див. праворуч), тобто два сегментних регістри можуть указувати на початок того самого сегмента:
(Осередки з перетинання можуть сегментуватися відразу по декількох сегментних регістрах.) Які саме сегменти пам'яті використати - це особиста справа автора програми.
Розміри сегментів також визначаються автором програми, аби тільки ці розміри не перевершували 64 Кб. Установивши сегментний регістр на початок сегмента, ми потенційно можемо по ньому просегментувати 64 Кб, ну а скільки з них реально буде використано, тобто який розмір сегмента, уже визначає автор програми.
Тепер про другу особливість сегментування адрес у ПК. При записі команд на мові Асемблера посилання на сегментний регістр вказується за допомогою наступної конструкції:
<сегментний регістр>:<адресне вираження>,
яка називається адресною парою і яка "говорить", що адреса, що є значенням вираження, повинна бути просегментована по зазначеному регістру. Наприклад, у команді MOV AX,ES:X адреса змінної X буде сегментуватись по регістру ES.
Запис CS:, DS:, SS: або ES: прийнятий називати префіксом сегментного регістра або просто префіксом (приставкою). У мові А префікс завжди записується перед адресою, що повинна бути просегментована. Однак у машинній мові ситуація трохи інша: тут префікс ставиться перед всією командою. Наприклад, команда MOV AX,ES:X насправді записується у вигляді двох машинних команд:
ES:
MOV АХ,Х
ES: - це спеціальна команда (без операндів), що і називається префіксом. Усього таких команд-префіксів чотири, по одній на кожний сегментний регістр. Сама по собі така команда-префікс нічого не робить, але вона впливає на наступну за нею команду: префікс "говорить", що адресний операнд у наступній команді повинен бути просегментований по відповідному регістру (у нашому прикладі - по регістру ES). Якщо префікс поставлений перед командою, де немає адресного операнда, то він виконується як "порожня" команда.
Отже, у машинних командах сегментні регістри вказуються не усередині команд, а перед ними (у вигляді команд-префіксів). Навіщо це зроблено, буде пояснено трохи пізніше. А поки відзначимо, що в мові Асемблера, записувати префікс поза командою не можна; наприклад, запис ES: MOV АХ,Х уважається помилкової з погляду мови Асемблера. Префікс обов'язково повинен вказуватися усередині команди але перед адресою.
Третя особливість сегментування адрес у ПК пов'язана з розміром сегментних регістрів. Загальна схема базування адрес припускає, що розміри базових регістрів досить більші й у них може бути розміщена будь-яка абсолютна адреса, будь-яка база. Однак у ПК ця умова не виконується: у ПК абсолютні адреси - 20-розрядні, а всі регістри, у тому числі й сегментні, 16-розрядні. Природно, виникає питання: як в 16-розрядних сегментних регістрах вдається розмістити 20-розрядні базові адреси?
У ПК ця проблема вирішена в такий спосіб. Ми дотепер уважали, що як базу можна використати будь-яку адресу. У ПК же на початкові адреси сегментів накладається обмеження: як базу можна використати будь-яку адресу, але кратну 16. Особливість цих адрес у тім, що в них останні 4 біти нульові, або, що те ж саме, у шістнадцятирічному записі цих адрес остання цифра завжди нульова, тобто вони мають вигляд XXXX0h, де X - будь-яка цифра. А раз так, то цю нульову цифру можна явно не вказувати, а лише мати на увазі. Так і роблять: у сегментних регістрах зберігають тільки перші 16 бітів початкової адреси сегмента, тобто перші чотири шістнадцятирічні цифри. Наприклад, якщо початком сегмента є адреса 12340h, то в сегментному регістрі буде зберігатися величина 1234Ь. Початкова адреса сегмента без останнього шістнадцятирічного 0 називають номером сегмента й позначають як seg.
Центральний процесор, природно, враховує цю особливість сегментних регістрів і при сегментуванні адреси насамперед до вмісту сегментного регістра приписує праворуч цей незазначений 0 і тільки потім додає зсув, зазначений у команді. Умовно це можна зобразити так:
СР=ХХХХ ХХХХ0 - база КІП ... CP:YYYY ... ==> + YYYY - зсув
