Contribution to International Economy

  • Зв`язок ассемблера з мовою програмування високого рiвня
РЕФЕРАТ

Атестаційна робота на тему «Зв`язок ассемблера з мовою програмування високого рiвня» присвячена питанням розробці програми, яка дозволяє показати технологiчнi можливостi використання ассемблера у программах, написаних на мовi високого рiвня С++.
Пояснювальна записка складається з ____ аркушів, з___ рисунків, ____ додатків.

Змiст
Вступ 3
1 Уточнення постановки завдання 4
2 Процесорні інструкції 5
3 Перелік та призначення режимів та структура діалогу 17
4 Характеристику та структури даних з якими працює програма 19
5 Опис програми 20
Заключення 25
Список використаних джерел 27
Додаток А. Текст програми 28
Додаток Б. програма і методика випробувань 37
Додаток В. Інструкція користувача 41

ВСТУП
Еволюція засобів проектування програм протягом останніх десятиліть здатна здивувати будь-яку Людину що займається розробкою програмного забезпечення. Особливо це стосується написання програм для операційних систем сімейства Windows. Сучасні інструментальні засоби дозволяють розробити додаток за допомогою декількох клацань миші і часто економлять тижні і місяці рутинної роботи програміста. Практично будь-яке середовище програмування містить майстра розробки додатків, які можуть створити додаток, що має ті або інші відмінні особливості. 
Проте, більшість серйозних застосувань пишуться із значною часткою ручної праці, і викликано це тим, що жоден інструментальний засіб програмування на мові високого рівня не може дати максимального виграшу в продуктивності. Це об'єктивний чинник, і обумовлений він самою структурою і семантикою мов високого рівня. 
Можливим вирішенням проблеми оптимізації додатків є застосування мови асемблера. Існує цілий ряд програм, що не потребують скільки-небудь серйозної оптимізації. Але як тільки мова заходить про додатки, що працюють в системах реального часу, драйверах пристроїв, мультимедіа-додатки, програмах для обробки звуку і зображень і взагалі про будь-які програми, критичні до часу виконання, застосування асемблера стає неминучим, тому що ніякі інші методи оптимізації працювати не будуть. 
Об’єктом дослідження даної роботи є асемблерні команди, з допомогою яких відбувається взаємодія з мовою високого рівня .
Предмет дослідження - методи взаємодії асемблерних команд з мовою високого рівня.. 
Основні задачі даної атестаційної роботи:
Аналіз способів взаємодії асемблерних команд з мовою високого рівня.
Вирішення питань програмування звязку асемблерних команд з програмою на мові високого рівня.

1 Уточнення постановки завдання

Темою даної атестаційної роботи є розробка програмного забезпечення для вивчення взаємодії асемблерних команд з мовою високого рівня.
В ході проведення роботи необхідно провести:
  аналіз способів взаємодії асемблерних команд з мовою високого рівня;
  вирішити питання програмування звязку асемблерних команд з програмою на мові високого рівня.
Для вирішення цих питань потрібно:
  провести аналіз способів взаємодії асемблерних команд з мовою високого рівня;
  розробити алгоритм програми;
  програмно реалізувати взаємодію асемблерних команд з мовою високого рівня.
Початкові дані, які використовуються в роботі:
  довідкова інформація про асемблерну команду CPUID; 
  довідкова інформація про засоби мови програмування С++ та середовища програмування Borland C++.
2 Процесорні інструкції

Нижче представлені набори інструкцій, використовувані мовою "Асемблер" для роботи з процесором на апаратному рівні. У сучасних процесорах, як і на зорі розвитку процесорів, інструкції використовуються для прискорення роботи і оптимізації виконуваних алгоритмів.

Розглянемо наступні процесорні інструкції:
 - 3dnow!
 - Amd64 (x86-64)
 - Cpuid
 - Em64t (x86-64)
 - Extended 3dnow!
 - Ia-32 (x86-32)
 - MIMD
 - MISD
 - MMX
 - SIMD
 - SISD
 - SSE
 - Sse2
 - Sse3
 - Sse4
 - Sse4.1
 - Sse4.2
 - Sse4a
 - Ssse3



3dnow!
3dnow! — додаткове розширення MMX для процесорів AMD, починаючи з AMD K6 3d. Причиною створення 3dnow! послужило прагнення завоювати перевагу над процесорами виробництва компанії Intel в області обробки мультимедійних даних. Хоча це розширення є розробкою AMD, його також інтегрували в свої процесори IBM, Cyrix та інші.
Технологія 3dnow! ввела 21 нову команду процесора і можливість оперувати 32-бітовими дійсними типами в стандартних MMX-регистрах. Також були додані спеціальні інструкції, що оптимізують перемикання в режим Mmx/3dnow! (femms, яка замінювала стандартну інструкцію emms) і роботу з кешем процесора. Таким чином технологія 3dnow! розширювала можливості технології MMX, не вимагаючи введення нових режимів роботи процесора і нових регістрів.
У 3dnow! компанія AMD додала новий тип даних — 32-бітові дійсні числа, інструкції для роботи з цим типом, а також деякі інструкції для маніпулювання стандартними типами MMX.
Перед початком роботи з розширенням 3dnow! слід переконатися, що ЦП їх підтримує. Для цього використовується інструкція CPUID. Загальний порядок дій при перевірці виглядає таким чином:
1. Переконатися, що процесор підтримує інструкцію CPUID. Якщо немає, то ЦП не підтримує і 3dnow!.
2. Виконати інструкцію cpuid із значенням EAX = 0 — це необхідно для наступного кроку.
3. Виконати CPUID із значенням EAX = 80000000h. Якщо після виконання інструкції в EAX буде значення менше 1, то 3dnow! не підтримується.
4. Виконати CPUID з EAX = 80000001h. Якщо після виконання біт 31 регістра EDX буде встановлений в 1, то 3dnow! підтримується. Інакше — ні.

Amd64 (x86-64)
Amd64 (також x86-64/intel64/em64t/x64) — 64-бітова архітектура мікропроцесора і відповідний набір інструкцій, розроблені компанією AMD. Це розширення архітектури x86 з повною зворотною сумісністю. Набор інструкцій x86-64 в даний час підтримується процесорами AMD Athlon 64, Athlon 64 FX, Athlon 64 X2, Turion 64, Opteron, останніми моделями Sempron. Цікаво, що цей набір інструкцій був підтриманий основним конкурентом AMD — компанією Intel під назвою Intel 64 (раніше відомі як Em64t і Ia-32e) в пізніх моделях процесорів Pentium 4, а також в Pentium D, Pentium Extreme Edition, Celeron D, Core 2 Duo і Xeon. Корпорації Microsoft і Sun Microsystems використовують для позначення цього набору інструкцій термін x64.
Розроблений компанією AMD набір інструкцій x86-64 (пізніше перейменований в Amd64) — розширення архітектури Intel Ia-32 (x86-32). Основною відмітною особливістю Amd64 є підтримка 16-ти 64-бітових регістрів загального призначення (проти 8-і 32-бітових в x86-32), 64-бітових арифметичних і логічних операцій над цілими числами і 64-бітових віртуальних адрес. Для адресації нових регістрів для команд введені так звані «префікси розширення регістра», для яких був вибраний діапзон код 40h-4fh, що використовуються для команд INC <регістр> і DEC <регістр> в 32- і 16-бітових режимах. Команди INC і DEC в 64-бітовому режимі повинні кодуватися в більш загальній, двобайтовій формі.
Архітектура x86_64 має:
- 16 цілочисельних 64-бітових регістра загального призначення (RAX, RBX, RCX, RDX, RBP, RSI, RDI, RSP, R8 — R15)
- 8 80-бітових регістрів з плаваючою крапкою (St0 — St7)
- 8 64-бітових регістрів Multimedia Extensions (Mm0 — Mm7, мають загальний простір з регістрами St0 — St7)
- 16 128-бітових регістрів SSE (Xmm0 — Xmm15)
- 64-бітовий покажчик RIP
- 64-бітовий регістр прапорів RFLAGS.
Розробляючи архітектуру x86-64, інженери корпорації AMD вирішили назавжди покінчити з головним "рудиментом" архітектуру x86 — сегментною моделлю пам'яті, яка передавалася по спадку ще з часів 8086/80286. Проте, як потім виявилось, вони дуже погарячкували. Архітектура стала абсолютно невіртуалізуємою. При розробці нової версії свого продукту для віртуалізації програмісти компанії Vmware зіткнулися з непередбаченими труднощами при реалізації 64-бітової віртуальної машини. Оскільки, для відділення кода монітора від кода «гостя» програмою використовувався механізм сегментації, це завдання стало практично нерозв'язним. Усвідомивши свою помилку, AMD повернула обмежений варіант сегментної організації пам'яті починаючи з ревізії D архітектури Amd64, що дозволило запускати 64-бітові ОС у віртуальних машинах. Intel, проте, цьому прикладу не послідувала, і тому ні на одному її процесорі, що не підтримує засобу апаратної віртуалізації, запустити 64-бітову віртуальну машину не можна. З метою перевірки того, чи можливий на даному процесорі запуск 64-бітових гостьових ОС чи ні, Vmware надає разом зі своїми продуктами спеціальну утиліту. Також слід зазначити, що команди LAHF і SAHF, що спочатку потрапили «під ніж», які також активно використовуються ПО віртуалізації, потім також були повернені в систему команд. З розповсюдженням засобів апаратної віртуалізації (Intel VT, AMD-V) потреба в сегментації знов поступово відпаде, проте Vmware як і раніше активно її використовує, і підтримка AMD-V навіть на сьогоднішній день в її продуктах немає.

Cpuid
CPUID (CPU Identification) — асемблерна інструкція процесорів x86, використовується для отримання інформації про процесор. Використовуючи її, програма може визначити тип ЦП і його можливості (наприклад, можна визначити, які розширення підтримуються процесором).
Інструкція CPUID вперше з'явилася в процесорах i486. Потім вона почала підтримуватися всіма процесорами починаючи з Intel 80486DX/SX/DX2 SL, AMD 80486DX4, Cyrix M1, UMC U5s.
Оскільки інструкція CPUID була відсутня в перших процесорах архітектури x86, перед її використанням слідує упевниться що ЦП її підтримує. Для цього проводиться спроба змінити біт 21 (ID) регістра EFLAGS. Якщо біт успішно поміняється, то інструкція CPUID процесором підтримується.

Значення EAX при виклику CPUID, вказані в дужках, визначає функцію виклику:
* CPUID(0) — в регістрі EAX повертається максимально допустиме значення параметра виклику; у регістрах EBX, EDX і ECX процесор повертає символьний рядок, специфічний для виробника. Символи рядка розміщуються в регістрах у вказаному порядку, починаючи з молодших байт. Процесори Intel повертають рядок "Genuineintel":
  * Ebx=756e6547h — "Genu", символ "G" в регістрі BL
  * Edx=49656e69h — "inei", символ "i" в регістрі DL
  * Ecx=6c65746eh — "ntel", символ "n" в регістрі CL. 
Процесори AMD повертають рядок "AUTHENTICAMD" (Ebx=68747541h, Ecx=444d4163h, Edx=69746e65h).
* CPUID(1) — в молодшому слові регістра EAX процесор повертає код ідентифікації він же сигнатура процесора і старший елемент 96-бітового серійного номера. Це ж значення міститься в регістрі DX після апаратного скидання:
  * EAX[3:0] — степпінг;
  * EAX[7:4] — модель;
  * EAX[11:8] — сімейство;
  * EAX[13:12] — тип;
  * EAX[31:14] — зарезервовано (0);
  * Регістри Ebx=0, Ecx=0 (резерв).
  * Регістр EDX містить список наявних розширень базової архітектури — відображає регістр властивостей (Feature Flags register). Призначення біт регістра приведене в таблиці. 2.
  * CPUID(2) — в регістрах EAX, EBX, ECX, EDX повертаються параметри конфігурації процесора. Молодші 8 битий EAX повідомляють, скільки разів потрібно підряд викликати інструкцію (з Eax=2) для отримання повної інформації про процесор. Решту байтів регістра EAX і інших регістрів містять дескриптори окремих вузлів, які розшифровуються по спеціальних таблицях. Ознакою використання кожного з регистров EAX, EBX, ECX, EDX є нуль в його біті 31. 
Виклик CPUID(2) з'явився з процесорами 6-го покоління. Поки що по ньому повідомляються тільки дескриптори елементів кешування. Наприклад, для Pentium Pro по CPUID(2) повертається Eax=03020101h, Ebx=0, Ecx=0, Edx=06040a42h. Це означає, що виклик потрібно робити одноразово (Al=1); TLB інструкцій для сторінок 4к має 32 входження (01h), для сторінок 4м — 2 входження; TLB даних для сторінок 4к на 64 входження (03h), для сторінок 4м — на 8 входжень (04h); первинний кеш інструкцій — 8к (06h), даних — 8к (0ah); вторинний кеш 256к (42h).
* CPUID(3) — отримання молодших 64 біт серійного номера процесора (Intel Processor Serial Number), доступно починаючи з Pentium III (сімейство 6, модель 7 і старше).
  * EDX — середні 32 біта ідентифікатора;
  * ECX — молодші 32 біта ідентифікатора.
Для того, щоб отримати інформацію про додаткові функції, наявні в процесорах, в регістрі EAX перед викликом CPUID має бути встановлений біт 31. Наприклад, щоб визначити максимально допустиме значення параметра для додаткових функцій, необхідно виконати CPUID із значенням EAX = 80000000h.
EAX = 0: Отримати ідентифікатор виробника процесора
При EAX = 0 ЦП повертає ідентифікатор виробника процесора (англ. Vendor ID) у вигляді 12 символів ASCII, що містяться в регістрах EBX, ECX, EDX (саме у такому порядку). У регістрі EAX же повертається максимально допустиме значення EAX при виклику CPUID.

Ось деякі ідентифікатори виробників процесорів:







Таблиця 2.1 Ідентифікатори виробників процесорів
ASCii-рядок HEX-значення EBX:EDX:ECX Назва виробника
"GenuineIntel" 756E6547:49656E69:6C65746E Intel
"AuthenticAMD" 68747541:69746E65:444D4163 AMD
"CyrixInstead" 69727943:736E4978:64616574 Cyrix
"CentaurHauls" 746E6543:48727561:736C7561 Centaur
"SiS SiS SiS " 20536953:20536953:20536953 SiS
"NexGenDriven" 4778654E:72446E65:6E657669 NexGen
"GenuineTMx86" 756E6547:54656E69:3638784D Transmeta
"RiseRiseRise" 65736952:65736952:65736952 Rise
"UMC UMC UMC " 20434D55:20434D55:20434D55 UMC
"Geode by NSC" 646F6547:79622065:43534E20 National Semiconductor

Em64t (x86-64)
Em64t (Extended Memory 64 Technology) - реалізація 64-бітових розширень Amd64 процесорної архітектури Ia-32 (архітектура x86-совместімих процесорів) фірми Intel. Основне поліпшення, забезпечуване Em64t — 64-бітова адресація оперативної пам'яті, що дозволяє зняти властиве 32-бітовим процесорам обмеження в 4 гігабайти пам'яті, що адресується безпосередньо.
Em64t забезпечує підтримку:
- 64-бітового лінійного дійсного адресного простору;
- 64-бітових покажчиків;
- 64-бітові регістри загального призначення;
- 64-бітових цілих чисел;
- підтримку до 1 терабайта адресного простору платформи.

MMX
MMX (Multimedia Extensions — мультимедійне розширення) — комерційна назва додаткового набору інструкцій, що виконують характерні для процесів кодування/декодування потокових аудіо/відео даних дії за одну машинну інструкцію. Вперше з'явився в процесорах Pentium MMX. Розроблений в лабораторії Intel в Хайфі, Ізраїль, в першій половині 1990-х.

SIMD
SIMD (англ. Single Instruction, Multiple Data) — принцип комп'ютерних обчислень, що дозволяє забезпечити паралелізм на рівні даних.

SISD
SISD (англ. Single Instruction, Single Data) — архітектура комп'ютера, в якій один процесор виконує один потік команд, оперуючи одним потоком даних. Відноситься до Фон-неймановськой архітектурі.

SSE
SSE (англ. Streaming SIMD Extensions, потокове SIMD-расширение процесора) — це SIMD (англ. Single Instruction, Multiple Data, Одна інструкція — безліч даних) набір інструкцій, розроблений Intel, і вперше представлений в процесорах серії Pentium III як відповідь на аналогічний набір інструкцій 3dnow! від AMD, який був представлений роком раніше. Спочатку назвою цих інструкцій було KNI що розшифровувалося як Katmai New Instructions (Katmai — назва першої версії ядра процесора Pentium III).
Технологія SSE дозволяла подолати 2 основних проблеми MMX — при використанні MMX неможливо було одночасно використовувати інструкції співпроцесора, оскільки його регістри використовувалися для MMX і робіт з дійсними числами.
SSE включає в архітектуру процесора вісім 128-бітових регістрів (xmm0 до xmm7), кожен з яких трактується як 4 послідовних значення з плаваючою крапкою одинарної точності. SSE включає набір інструкцій, який проводить операції з скалярними і упакованими типами даних.
Перевага в продуктивності досягається у тому випадку, коли необхідно провести одну і ту ж послідовність дій над разнимі даними.
Реалізація блоків SIMD здійснюється розпаралелюванням обчислювального процесу між даними. Тобто коли через один блок проходить по черзі безліч потоків даних.

Sse2
Sse2 (англ. Streaming SIMD Extensions 2, потокове SIMD-расширение процесора) — це SIMD (англ. Single Instruction, Multiple Data, Одна інструкція — безліч даних) набір інструкцій, розроблений Intel, і вперше представлений в процесорах серії Pentium 4.
Sse2 використовує вісім 128-бітових регістрів (xmm0 до xmm7), включених в архітектуру x86 з введенням розширення SSE, кожен з яких трактується як 2 послідовних значення з плаваючою крапкою подвійної точності. Sse2 включає набір інструкцій, який проводить операції з скалярними і упакованими типами даних. Також Sse2 містить інструкції для потокової обробки цілочисельних даних в тих же 128-бітових xmm регістрах, що робить це розширення переважнішим для цілочисельних обчислень, ніж використання набору інструкцій MMX, що з'явився набагато раніше.

Перевага в продуктивності досягається у тому випадку, коли необхідно провести одну і ту ж послідовність дій над великим набором однотипних даних.

Sse3
Sse3 (PNI — Prescott New Instruction) — третя версія SIMD-расширения Intel, нащадок SSE, Sse2 і x87. Вперше представлено 2 лютого 2004 року в ядрі Prescott процесора Pentium 4. У 2005 AMD запропонувала свою реалізацію Sse3 для процесорів Athlon 64 (ядра Venice і San Diego).

Набор Sse3 містить 13 інструкцій:
- FISTTP (x87)
- MOVSLDUP (SSE)
- MOVSHDUP (SSE)
- MOVDDUP (Sse2)
- LDDQU (Sse/sse2)
- ADDSUBPD (SSE)
- ADDSUBPD (Sse2)
- HADDPS (SSE)
- HSUBPS (SSE)
- HADDPD (Sse2)
- HSUBPD (Sse2)
- MONITOR (немає аналога в Sse3 для AMD)
- MWAIT (немає аналога в Sse3 для AMD).

Sse4
Sse4 це набір команд Intel Core мікроархітектура, вперше реалізований в процесорах серії Penryn (не слід плутати з Sse4a від AMD). Він був анонсований 27 Вересня 2006, проте детальний опис став доступний тільки навесні 2007.
Sse4 складається з 54 інструкцій, 47 з них відносять до Sse4.1 (вони є тільки в процесорах Penryn). Очікується, що повний набір команд (Sse4.1 і Sse4.2, тобто 47 + що залишилися 7 команд) буде доступний в процесорах Nehalem. Жодна з Sse4 інструкцій не працює з 64-х бітовими mmx регістрами (тільки з 128-ми бітовими xmm0-15).
Компілятор мови Сі від Intel починаючи з версії 10 генерує інструкції Sse4 при завданні опції -qxs.
У Sse4 додані інструкції, прискорюючі компенсацію руху у відеокодеках, швидке читання з WC пам'яті, безліч інструкцій для спрощення векторизації програм компіляторами. Вперше в Sse4 регістр xmm0 почав використовуватися як неявний аргумент для деяких інструкцій.

Sse4.1
Sse4.1 це розширений набір команд Sse4 від Intel.
Нижче приведені інструкції, що входять 
в Sse4.1:
- Прискорення відео (3 інструкції)
- Векторні примітиви (5 інструкцій)
- Вставки/витягання (4 інструкції)
- Скалярне множення векторів (2 інструкції)
- Змішування (4 інструкції)
- Перевірки битий
- Округлення (2 інструкції)
- Читання WC пам'яті

Sse4.2
Sse4.2 це розширений набір команд Sse4 від Intel.
Sse4.2 складається з 7 інструкцій. Очікується, що повний набір команд буде доступний в процесорах Nehalem.
Інструкції Sse4.2:
- Обробка рядків (4 інструкції)
- Підрахунок Crc32
- Підрахунок популяції одиничних біт
- Векторні примітиви

Sse4a
Sse4a це розширений набір команд Sse4 від AMD, розроблений швидше не як доповнення, а як альтернатива інструкціям Sse4 від Intel.

Ssse3
Ssse3 (Supplemental Streaming SIMD Extension 3) — це позначення дане Intel 4-у розширенню системи команд. Попереднє мало позначення Sse3 і Intel додав ще один символ 's' замість того, щоб збільшити номер розширення, можливо тому, що вони порахували Ssse3 простим доповненням до Sse3. Часто, до того як почало використовуватися офіційне позначення Ssse3, ці нові команди називалися Sse4. Також їх називали кодовими іменами Tejas New Instructions (TNI) і Merom New Instructions (MNI) по назві процесорів, де вперше Intel мала намір підтримати ці нові команди. З'явившись в Intel Core Microarchitecture, Ssse3 доступно в серіях процесорів Xeon 5100 (Server і Workstation версії), а також в процесорах Intel Core 2 (Notebook і Desktop версії).
Новими в Ssse3, в порівнянні з Sse3, є 16 унікальних команд, що працюють з упакованими цілими. Кожна з них може працювати як з 64-х бітовими (MMX), так і з 128-ми бітовими (XMM) регістрами, тому Intel в своїх матеріалах посилається на 32 нових команди.
Ось деякі з них:
- Робота із знаком (2 інструкції)
- Зрушення
- Перемішування байт
- Множення (2 інструкції)
- Горизонтальні складання/віднімання цілі (4 інструкції)
3 Перелік та призначення режимів та структура діалогу
На малюнку 2.1 зображений загальний алгоритм роботи програми з деталями структури діалогу роботи програми
 

Рис. 2.1 - Загальний алгоритм роботи програми

Згідно з вище приведеним алгоритмом користовачу пропонується ввести значення змінної n, для того щоб получити дані процесора.
Обмеження n лежать в діапазоні 0..2.
В залежності від введеного значення n - користовач побачить на екрані визначений результат. 
Для 0 - получаємо максимально допустиме значення параметра інструкції та тип процесору.
Для 1 - получаємо тип, вид процесору та інші характеристики.
Для 2 - получаємо додаткові дані характеристики процесора.
4 Характеристику та структури даних з якими працює програма

В програмі використовуються 4х байтні змінні для зберігання значення регістрів процесора EAX, EBX, ECX, EDX
unsigned long eax, ebx, ecx, edx
а також 2х байтні змінні для зберігання значення регістрів AX, BX, СX, DX
unsigned int ax, bx, cx, dx

Для зберігання даних бітової маски із 32 біт використовується об`єднання bit:
union bit { unsigned long ss;
  struct { unsigned a0:1; unsigned a1:1;
  unsigned a2:1; unsigned a3:1;
  unsigned a4:1; unsigned a5:1;
  unsigned a6:1; unsigned a7:1;
  unsigned a8:1; unsigned a9:1;
  unsigned a10:1;unsigned a11:1;
  unsigned a12:1;unsigned a13:1;
  unsigned a14:1;unsigned a15:1;
  unsigned a16:1;unsigned a17:1;
  unsigned a18:1;unsigned a19:1;
  unsigned a20:1;unsigned a21:1;
  unsigned a22:1;unsigned a23:1;
  unsigned a24:1;unsigned a25:1;
  unsigned a26:1;unsigned a27:1;
  unsigned a28:1;unsigned a29:1;
  unsigned a30:1;unsigned a31:1;
  } dword;
  }

5 Опис програми

Призначення програми: вивід у вікні консольної програми інформації про поточний процесор у регістрах EAX, EBX, EDX и ECX.
Спочатку у програмі з допомогою функції "soder_reg" получения отримуємо дані із регістрів EAX, EBX, ECX, EDX. Функція "soder_reg" містить у собі асемблерна вставку:

asm{ .586
  mov EAX, m
  cpuid
  mov [a],EAX
  mov [b],EBX
  mov [c],ECX
  mov [d],EDX
  }

в якій використовується інструкція cpuid.
Інструкція CPUID по вмісту регістра EAX визначає якого роду інформацію про процесор необхідно повернути. Перший раз її слід викликати із значенням EAX = 0. При цьому буде повернено максимально допустиме значення параметра інструкції, підтримуване даним процесором.
Для того, щоб отримати інформацію про додаткові функції, наявні в процесорах, в регістрі EAX перед викликом CPUID має бути встановлений біт 31. Наприклад, щоб визначити максимально допустиме значення параметра для додаткових функцій, необхідно виконати CPUID із значенням EAX = 80000000h.
При EAX = 0 ЦП повертає ідентифікатор виробника процесора (англ. Vendor ID) у вигляді 12 символів ASCII, що містяться в регістрах EBX, EDX, ECX (саме у такому порядку). У регістрі EAX же повертається максимально допустиме значення EAX при виклику CPUID.

Потім у програмі ми виводимо у шістнадцатерічній формі значення регістрів EAX, EBX, ECX, EDX

  cout<<"EAX="<< a<<endl;
  cout<<"EBX="<< b<<endl;
  cout<<"ECX="<< c<<endl;
  cout<<"EDX="<< d<<endl;

З допомогою функції binar_32 виводимо перші 32 біти (4 байти) змісту регістрів EAX, EBX, ECX, EDX

void binar_32 (unsigned long ch)
{ bit cod;
  cod.ss=ch; //заносим значение
  cout<< "No bits: 313029282726252423222120" ;
  cout<<"191817161514131211109 8 7 6 5 4 3 2 1 0"<<endl;
  cout<< "Val. of bits";
  cout<<'\t'<<cod.dword.a31<<' '<<cod.dword.a30;
  cout<<' '<<cod.dword.a29<<' '<<cod.dword.a28;
  cout<<' '<<cod.dword.a27<<' '<<cod.dword.a26;
  cout<<' '<<cod.dword.a25<<' '<<cod.dword.a24;
  cout<<' '<<cod.dword.a23<<' '<<cod.dword.a22;
  cout<<' '<<cod.dword.a21<<' '<<cod.dword.a20;
  cout<<' '<<cod.dword.a19<<' '<<cod.dword.a18;
  cout<<' '<<cod.dword.a17<<' '<<cod.dword.a16;
  cout<<' '<<cod.dword.a15<<' '<<cod.dword.a14;
  cout<<' '<<cod.dword.a13<<' '<<cod.dword.a12;
  cout<<' '<<cod.dword.a11<<' '<<cod.dword.a10;
  cout<<' '<<cod.dword.a9<<' '<<cod.dword.a8;
  cout<<' '<<cod.dword.a7<<' '<<cod.dword.a6;
  cout<<' '<<cod.dword.a5<<' '<<cod.dword.a4;
  cout<<' '<<cod.dword.a3<<' '<<cod.dword.a2;
  cout<<' '<<cod.dword.a1<<' '<<cod.dword.a0<<endl;
}

В кінці програми з допомою функцій dan_cpu_0, dan_cpu_1, dan_cpu_2 визначаємо тип процесора
void dan_cpu_0(int a, int b, int c,int d) //ЕАХ=0
{
  cout<<"max EAX= "<<a<<endl;
  cout<<"ide CPU= ";
  switch (toascii(b)){
  case 0x47: cout<<'G'; break; //Intel
  case 0x41: cout<<'A'; break; //AMD
  }
  switch (toascii(c)){
  case 0x6E: cout<<'l'; break;
  case 0x44: cout<<'D'; break;
  }
  switch (toascii(d)){
  case 0x69: cout<<'I'<<" CPU Intel"; break;
  case 0x65: cout<<'e'<<" CPU AMD"; break;
  }
  cout<<endl;
}

void dan_cpu_1(unsigned long a, unsigned long d)//ЕАХ=1
{ bit cod, cod1;
  long r=0, s=0, t=0, u=0;
  cod.ss=a; //заносим значение eax
  cout.setf(ios::dec);
  r=cod.dword.a0 + cod.dword.a1 + cod.dword.a2 + cod.dword.a3;
  cout<<"Modificasion CPU (stepping ID) = "<<r<<endl;
  s=cod.dword.a4 + cod.dword.a5 + cod.dword.a6 + cod.dword.a7;
  t=cod.dword.a8 + cod.dword.a9 + cod.dword.a10 + cod.dword.a11;
  cout<<"Model CPU and family CPU = ";
  if ((t==2)&&(s==1)) cout<<" Pentium III 566"<<endl;
  if ((t==4)&&((s==0)||(s==1))) cout<<" I486DX"<<endl;
  if ((t==4)&&(s==2)) cout<<" I486SX"<<endl;
  if ((t==5)&&(s==2)) cout<<" Pentium 75-200"<<endl;
  if ((t==5)&&(s==4)) cout<<" Pentium MMX 166-200"<<endl;
  if ((t==6)&&(s==1)) cout<<" Pentium Pro"<<endl;
  if ((t==6)&&(s==3)) cout<<" Pentium II model 3, Pentium II OverDrive"<<endl;
  if ((t==6)&&(s==5)) cout<<" Pentium II model 5, Pentium II Xeon"<<endl;
  if ((t==6)&&(s==6)) cout<<" Celeron model 6"<<endl;
  if ((t==6)&&(s==7)) cout<<" Pentium III, Pentium III Xeon"<<endl;
  if ((t==6)&&(s==3)) cout<<" Pentium II model 3"<<endl;
  cout<<"Type CPU = ";
  u=cod.dword.a12 + cod.dword.a13;
  switch (u){
  case 0: cout<<u<<" Type OEM"<<endl; break;
  case 1: cout<<u<<" Type Overdrive"<<endl; break;
  case 2: cout<<u<<" Type Dual"<<endl; break;
  }
 ....
  }
//*****************************************
void dan_cpu_2(unsigned long a, unsigned long b,unsigned long c,unsigned long d)//ЕАХ=2
{ bit cod, cod1, cod2, cod3;
  long r=0;
  cod.ss=a; //заносим значение eax
  cout.setf(ios::dec);
  r=cod.dword.a0 + cod.dword.a1 + cod.dword.a2 + cod.dword.a3+
  +cod.dword.a4 + cod.dword.a5 + cod.dword.a6 + cod.dword.a7;
  cout<<"Amoun CPUID= "<<r<<endl;
  cout<<"EBX="<< b<<endl;
  cout<<"ECX="<< c<<endl;
  cout<<"EDX="<< d<<endl;
  cod1.ss=b;//заносим значение ebx
  cod2.ss=c;//заносим значение ecx
  cod3.ss=d;//заносим значение edx
  ...
}



ЗАКЛЮЧЕННЯ

По суті, асемблер є мовою, на якій "говорить" процесор, і зникнути він може тільки разом із зникненням процесорів. З цієї ж причини асемблер має і завжди матиме одну фундаментальну перевагу перед мовами високого рівня: він найшвидший. Більшість додатків, що працюють в режимі реального часу, або написані на асемблері, або використовують в критичних ділянках коди асемблерні модулі. 
Мова асемблера, як засіб поліпшення продуктивності додатків, написаних на мовах високого рівня, використовується дуже широко. Розумне поєднання в одному додатку модулів, написаних на мові високого рівня і на асемблері, дозволяє досягти як високої швидкодії роботи програми, так і зменшення розміру виконуваної коди.
На практиці застосовуються два варіанти сумісного використання асемблера і мов високого рівня. У першому випадку використовується окремий файл об'єктного модуля, в якому розташовується одна або декілька процедур обробки даних. Виклик цих процедур здійснюється програмою, написаною з використанням високорівневого середовища розробки.
Другий варіант сумісного використання асемблера і мов високого рівня заснований на застосуванні вбудованого асемблера. Розробка процедур на вбудованому асемблері зручна, насамперед, завдяки швидкості відладки. Оскільки процедура розробляється в тілі основної програми, то не вимагається спеціальних засобів для компоновки такої процедури із основної програми. Не потрібно також піклуватися про порядок передачі параметрів в процедуру, що викликається, і про відновлення стека. До недоліків цього методу оптимізації можна віднести певні обмеження, які накладає середовище програмування на роботу асемблерних модулів, а також те, що процедури, розроблені на вбудованому асемблері, не можна перетворити в зовнішні окремо використовувані модулі.
В ході виконання роботи були виришені наступні завдання:
  проведений аналіз способів взаємодії асемблерних команд з мовою високого рівня;
  розроблений алгоритм програми;
  програмно реалізувана взаємодія асемблерних команд з мовою високого рівня.
Список використаних джерел

Зубков С.В. Assemler для DOS, Windows и Unix . – М.: ДМК Пресс; СПб.: ПИТЕР, 2004.
Подбельский В.В. Язык С++: Учебное пособие. - М.: "Финансы и статистика", 2004. 
Магда Ю. С. Использование ассемблера для оптимизации программ на C++. — СПб.: БХВ-Петербург, 2004.
Википедия.


Додаток А. Текст програми

/*Программа выводит в окне консольного приложения информацию
о текущем процессоре в регистрах EAX, EBX, EDX и ECX*/
#include <iostream.h>
#include <stdio.h>
#include <conio.h>
#include <dos.h>
#include <stdlib.h>
#include <ctype.h>
long a, b,c,d, n;
unsigned long eax, ebx, ecx, edx;//для 4 байтов
unsigned int ax, bx, cx, dx; //для 0 байтов
union bit { unsigned long ss;
  struct { unsigned a0:1; unsigned a1:1;
  unsigned a2:1; unsigned a3:1;
  unsigned a4:1; unsigned a5:1;
  unsigned a6:1; unsigned a7:1;
  unsigned a8:1; unsigned a9:1;
  unsigned a10:1;unsigned a11:1;
  unsigned a12:1;unsigned a13:1;
  unsigned a14:1;unsigned a15:1;
  unsigned a16:1;unsigned a17:1;
  unsigned a18:1;unsigned a19:1;
  unsigned a20:1;unsigned a21:1;
  unsigned a22:1;unsigned a23:1;
  unsigned a24:1;unsigned a25:1;
  unsigned a26:1;unsigned a27:1;
  unsigned a28:1;unsigned a29:1;
  unsigned a30:1;unsigned a31:1;
  } dword;
  }; //представление 32-разрядного битового поля с данными регистров
void binar_32 (unsigned long);//содержимое байта в двоичном виде
void binar (unsigned char); //содержимое 0 байтов регистров
void soder_reg (long);//содержимое регистров EAX, EBX, ECX, EDX
void dan_cpu_0(int,int,int,int);// данные о процессоре, если ЕАХ=0
void dan_cpu_1(unsigned long, unsigned long);// данные о процессоре, если ЕАХ=1
void dan_cpu_2(unsigned long, unsigned long,
  unsigned long,unsigned long); // данные о процессоре, если ЕАХ=0
//---------------------------------------------
main ()
{ cout<<"Vvedite EAX = 0,1,2"<<endl;
  cin>>n;
if (n>2)
  cout<<"Error!"<<endl;
else
{ soder_reg (n);// просмотр регистров
  cout<<"Registers:"<<endl;
  cout.setf(ios::hex);
  cout<<"EAX="<< a<<endl;
  cout<<"EBX="<< b<<endl;
  cout<<"ECX="<< c<<endl;
  cout<<"EDX="<< d<<endl;
  cout<<"-------------"<<endl;
  cout<<"4 byte EAX="<< endl;
  binar_32(a);
  cout<<"-------------"<<endl;
  cout<<"4 byte EBX="<< endl;
  binar_32(b);
  cout<<"-------------"<<endl;
  cout<<"4 byte ECX="<< endl;
  binar_32(c);
  cout<<"-------------"<<endl;
  cout<<"4 byte EDX="<< endl;
  binar_32(d);
  cout<<"-------------"<<endl;
 getch();
  cout.setf(ios::hex);
  ax=a<<24;
  ax>>=24;
  cout<<"AX= "<<a<<endl;
  bx=b<<24;
  bx>>=24;
  cout<<"BX= "<<bx<<endl;
  cx=c<<24;
  cx>>=24;
  cout<<"CX= "<<cx<<endl;
  dx=d<<24;
  dx>>=24;
  cout<<"DX= "<<dx<<endl;
  cout<<"0 byte EAX=";
  binar(ax);
  cout<<"-------------"<<endl;
  cout<<"0 byte EBX=";
  binar(bx);
  cout<<"-------------"<<endl;
  cout<<"0 byte ECX=";
  binar(cx);
  cout<<"-------------"<<endl;
  cout<<"0 byte EDX=";
  binar(dx);
  cout<<"-------------"<<endl;
  switch(n){
  case 0: dan_cpu_0(ax,bx,cx,dx); break;
  case 1: dan_cpu_1(a,d); break;
  case 2: dan_cpu_2(a,b,c,d);break;
  }
  getch();
}
}
//*****************************
// функция получения данных из регистров EAX, EBX, ECX, EDX
void soder_reg (long m)
{
//******ассемблерная вставка*****
asm{ .586
  mov EAX, m
  cpuid
  mov [a],EAX
  mov [b],EBX
  mov [c],ECX
  mov [d],EDX
  }
}
//----------------------------------------------
void binar_32 (unsigned long ch)
{ bit cod;
  cod.ss=ch; //заносим значение
  cout<< "No bits: 313029282726252423222120" ;
  cout<<"191817161514131211109 8 7 6 5 4 3 2 1 0"<<endl;
  cout<< "Val. of bits";
  cout<<'\t'<<cod.dword.a31<<' '<<cod.dword.a30;
  cout<<' '<<cod.dword.a29<<' '<<cod.dword.a28;
  cout<<' '<<cod.dword.a27<<' '<<cod.dword.a26;
  cout<<' '<<cod.dword.a25<<' '<<cod.dword.a24;
  cout<<' '<<cod.dword.a23<<' '<<cod.dword.a22;
  cout<<' '<<cod.dword.a21<<' '<<cod.dword.a20;
  cout<<' '<<cod.dword.a19<<' '<<cod.dword.a18;
  cout<<' '<<cod.dword.a17<<' '<<cod.dword.a16;
  cout<<' '<<cod.dword.a15<<' '<<cod.dword.a14;
  cout<<' '<<cod.dword.a13<<' '<<cod.dword.a12;
  cout<<' '<<cod.dword.a11<<' '<<cod.dword.a10;
  cout<<' '<<cod.dword.a9<<' '<<cod.dword.a8;
  cout<<' '<<cod.dword.a7<<' '<<cod.dword.a6;
  cout<<' '<<cod.dword.a5<<' '<<cod.dword.a4;
  cout<<' '<<cod.dword.a3<<' '<<cod.dword.a2;
  cout<<' '<<cod.dword.a1<<' '<<cod.dword.a0<<endl;
}
//----------------------------------------------
void binar (unsigned char ch)
{ union { unsigned char ss;
  struct { unsigned a0:1; unsigned a1:1;
  unsigned a2:1; unsigned a3:1;
  unsigned a4:1; unsigned a5:1;
  unsigned a6:1; unsigned a7:1;
  } byte;
  } cod;
  cod.ss=ch; //заносим значение
  cout<< "\nNombers bits: 7 6 5 4 3 2 1 0"<<endl;
  cout<< "Values of bits";
  cout<<'\t'<<cod.byte.a7<<' '<<cod.byte.a6;
  cout<<' '<<cod.byte.a5<<' '<<cod.byte.a4;
  cout<<' '<<cod.byte.a3<<' '<<cod.byte.a2;
  cout<<' '<<cod.byte.a1<<' '<<cod.byte.a0<<endl;
}
//*****************************************
void dan_cpu_0(int a, int b, int c,int d) //ЕАХ=0
{
  cout<<"max EAX= "<<a<<endl;
  cout<<"ide CPU= ";
  switch (toascii(b)){
  case 0x47: cout<<'G'; break; //Intel
  case 0x41: cout<<'A'; break; //AMD
  }
  switch (toascii(c)){
  case 0x6E: cout<<'l'; break;
  case 0x44: cout<<'D'; break;
  }
  switch (toascii(d)){
  case 0x69: cout<<'I'<<" CPU Intel"; break;
  case 0x65: cout<<'e'<<" CPU AMD"; break;
  }
  cout<<endl;
}
//*****************************************
void dan_cpu_1(unsigned long a, unsigned long d)//ЕАХ=1
{ bit cod, cod1;
  long r=0, s=0, t=0, u=0;
  cod.ss=a; //заносим значение eax
  cout.setf(ios::dec);
  r=cod.dword.a0 + cod.dword.a1 + cod.dword.a2 + cod.dword.a3;
  cout<<"Modificasion CPU (stepping ID) = "<<r<<endl;
  s=cod.dword.a4 + cod.dword.a5 + cod.dword.a6 + cod.dword.a7;
  t=cod.dword.a8 + cod.dword.a9 + cod.dword.a10 + cod.dword.a11;
  cout<<"Model CPU and family CPU = ";
  if ((t==2)&&(s==1)) cout<<" Pentium III 566"<<endl;
  if ((t==4)&&((s==0)||(s==1))) cout<<" I486DX"<<endl;
  if ((t==4)&&(s==2)) cout<<" I486SX"<<endl;
  if ((t==5)&&(s==2)) cout<<" Pentium 75-200"<<endl;
  if ((t==5)&&(s==4)) cout<<" Pentium MMX 166-200"<<endl;
  if ((t==6)&&(s==1)) cout<<" Pentium Pro"<<endl;
  if ((t==6)&&(s==3)) cout<<" Pentium II model 3, Pentium II OverDrive"<<endl;
  if ((t==6)&&(s==5)) cout<<" Pentium II model 5, Pentium II Xeon"<<endl;
  if ((t==6)&&(s==6)) cout<<" Celeron model 6"<<endl;
  if ((t==6)&&(s==7)) cout<<" Pentium III, Pentium III Xeon"<<endl;
  if ((t==6)&&(s==3)) cout<<" Pentium II model 3"<<endl;
  cout<<"Type CPU = ";
  u=cod.dword.a12 + cod.dword.a13;
  switch (u){
  case 0: cout<<u<<" Type OEM"<<endl; break;
  case 1: cout<<u<<" Type Overdrive"<<endl; break;
  case 2: cout<<u<<" Type Dual"<<endl; break;
  }
 getch();
  cod1.ss=d; //заносим значение edx
  cout<<"FPU= "<<cod1.dword.a0<<endl;
  cout<<"VME= "<<cod1.dword.a1<<endl;
  cout<<"DE= "<<cod1.dword.a2<<endl;
  cout<<"PSE= "<<cod1.dword.a3<<endl;
  cout<<"TSC= "<<cod1.dword.a4<<endl;
  cout<<"MSR= "<<cod1.dword.a5<<endl;
  cout<<"PAE= "<<cod1.dword.a6<<endl;
  cout<<"MCE= "<<cod1.dword.a7<<endl;
  cout<<"CX8= "<<cod1.dword.a8<<endl;
  if (bx==0x41)
  cout<<"PGE= "<<cod1.dword.a9<<endl;
  else
  cout<<"APIC= "<<cod1.dword.a9<<endl;
  cout<<"SEP= "<<cod1.dword.a11<<endl;
  cout<<"MTRR= "<<cod1.dword.a12<<endl;
  cout<<"PGE= "<<cod1.dword.a13<<endl;
  cout<<"MCA= "<<cod1.dword.a14<<endl;
  if (cod1.dword.a0)
  cout<<"SMOV= "<<cod1.dword.a15<<endl;
  cout<<"PAT= "<<cod1.dword.a16<<endl;
  cout<<"36 adress - 4Mb page= "<<cod1.dword.a17<<endl;
  cout<<"PPN= "<<cod1.dword.a18<<endl;
  cout<<"CLFLUSH= "<<cod1.dword.a19<<endl;
  cout<<"Inf. to buf.= "<<cod1.dword.a21<<endl;
  cout<<"MSR-registers= "<<cod1.dword.a22<<endl;
  cout<<"MMX= "<<cod1.dword.a23<<endl;
 getch();
  cout<<"FXSR= "<<cod1.dword.a24<<endl;
  cout<<"SSE= "<<cod1.dword.a25<<endl;
  cout<<"SSE2= "<<cod1.dword.a26<<endl;
  cout<<"self cnoop= "<<cod1.dword.a27<<endl;
  cout<<"Hiper Treading= "<<cod1.dword.a28<<endl;
  cout<<"The temperature control= "<<cod1.dword.a29<<endl;
  cout<<"CPU Intel Itanium= "<<cod1.dword.a30<<endl;
  cout<<"FERR#/PBE#= "<<cod1.dword.a31<<endl;
  }
//*****************************************
void dan_cpu_2(unsigned long a, unsigned long b,unsigned long c,unsigned long d)//ЕАХ=2
{ bit cod, cod1, cod2, cod3;
  long r=0;
  cod.ss=a; //заносим значение eax
  cout.setf(ios::dec);
  r=cod.dword.a0 + cod.dword.a1 + cod.dword.a2 + cod.dword.a3+
  +cod.dword.a4 + cod.dword.a5 + cod.dword.a6 + cod.dword.a7;
  cout<<"Amoun CPUID= "<<r<<endl;
  cout<<"EBX="<< b<<endl;
  cout<<"ECX="<< c<<endl;
  cout<<"EDX="<< d<<endl;
  cod1.ss=b;//заносим значение ebx
  cod2.ss=c;//заносим значение ecx
  cod3.ss=d;//заносим значение edx
  if ((cod1.dword.a31==0) && (cod2.dword.a31==0) && (cod3.dword.a31==0))
  cout<<"Information = true"<<endl;
}
//*******************************************************

Додаток Б. програма і методика випробувань

На малюнках які зображені нижче зображені результати роботи програми при введенні різних значень в регістр EAX.


Рис. 1 – Перша сторінка екрану при введенні в регістр ЕАХ значення 0




Рис. 2 – Друга сторінка екрану при введенні в регістр ЕАХ значення 0

Рис. 3 – Перша сторінка екрану при введенні в регістр ЕАХ значення 1

 

Рис. 4 – Друга сторінка екрану при введенні в регістр ЕАХ значення 1

 Рис. 5 – Друга сторінка екрану при введенні в регістр ЕАХ значення 1


Рис. 5 – Третя сторінка екрану при введенні в регістр ЕАХ значення 1

 Рис. 6 – Перша сторінка екрану при введенні в регістр ЕАХ значення 2



Рис. 7 – Друга сторінка екрану при введенні в регістр ЕАХ значення 2



 
Додаток В. Інструкція користувача

Програма працює в середовищі Windows з процесорами вище 486, які обробляють інструкцію cpuid (для параметру n = 0 або 1) та для процесорів 6-го покоління та вище (для параметру n=2).
Для початку користувачу необхідно запустити файл на виконання "File_reg32_1.exe". Файл автономний, тому не потрубує додаткових бібліотек в процесі роботи.
При запуску програми спочатку користувачеві пропонується ввести значення n для інструкції cpuid (рис. В.1)

 
Рис. В.1 Введення початкових даних

На рис. В.2 - В.4 - показані вигляди вікон при виборі різних значень n/
 
Рис. В.2 - Користувач ввів значення n = 0

 
Рис. В.3 - Користувач ввів значення n = 1

 
Рис. В.4 - Користувач ввів значення n = 2




Другие работы по теме: