гос / sp-lect (1)
.pdfinvoke GetProcAddress,hDll,$CTA0("@fcstrcpy@8") or eax,eax
jz exit
mov fcstrcpy,eax lea edi,amemcpy mov ebx,101
@@: invoke GetProcAddress,hDll,ebx or eax,eax
jz exit
mov dword ptr [edi],eax lea edi,[edi+4]
inc ebx
cmp ebx,105 jbe @B
lea edi,buffer lea esi,str1 push esi push edi
call astrcpy add esp,8
invoke StdOut,$CTA0("\nstrcpy:\t\t") invoke StdOut,edi
push offset str3 push offset str2 push edi
push 2 call astrcat add esp,16
invoke StdOut,$CTA0("\nstrcat:\t\t") invoke StdOut,edi
push 15 push 'x' push edi
call amemset add esp,12
invoke StdOut,$CTA0("\nmemset:\t\t") invoke StdOut,edi
mov ecx,edi mov dl,'y' push 10
call fcmemset
invoke StdOut,$CTA0("\nfcmemset:\t") invoke StdOut,edi
add esp,4 push esi call astrlen inc eax push eax push esi push edi
call amemcpy add esp,12
invoke StdOut,$CTA0("\nmemcpy:\t\t") invoke StdOut,edi
push esi call astrlen mov ebx,eax
push $CTA0("\nstrlen:\t\tLength of string '") push edi
call astrcpy add esp,12
push $CTA0("' equal ") push esi
push edi push 2 call astrcat
push edi call astrlen
lea esi,[edi+eax] invoke dwtoa,ebx,esi invoke StdOut,edi mov ecx,edi
lea edx,str1 call fcstrcpy
invoke StdOut,$CTA0("\nfcstrcpy:\t") invoke StdOut,edi
invoke StdIn,edi,lengthof buffer invoke FreeLibrary,hDll
exit: invoke ExitProcess,0 end start
7.6.5 Зв’язування Microsoft Visual C++ із динамічними бібліотеками
7.6.5.1 Раннє зв’язування
Зовнішньо раннє зв’язування проекту Microsoft Visual C++ із динамічною бібліотекою майже не відрізняється від зв’язування із статичною бібліотекою (див. розділ 7.5.3). Єдина відмінність полягає у використанні кваліфікатора
__declspec(dllexport) в описі прототипу функції.
Для раннього зв’язування програмний файл DLL (з розширенням dll) не потрібен і використовується тільки файл бібліотеки імпорту з розширенням lib, який автоматично створюється в результаті компонування DLL.
Приклад 7.7 Додаток на C (Microsoft Visual C++ 2008) dllstaticlinkcpp.exe,
що демонструє використання динамічної бібліотеки dlldemo.dll (див. Приклад 7.4) шляхом раннього зв’язування.
; Файл dllstaticlinkcpp.cpp
#include <iostream>
#include <cstdio>
#pragma comment(linker, "/DEFAULTLIB:dlldemo.lib")
extern "C"
{
__declspec(dllexport) unsigned int __stdcall astrlen(char* lpstr); __declspec(dllexport) void __cdecl amemcpy(char*, char*,unsigned int); __declspec(dllexport) void __cdecl astrcpy(char* lpdst, char* lpsrc); __declspec(dllexport) void __fastcall fcstrcpy(char* lpdst, char* lpsrc); __declspec(dllexport) void __cdecl amemset(char*, char,unsigned int); __declspec(dllexport) void __fastcall fcmemset(char*, char, unsigned int); __declspec(dllexport) void __cdecl astrcat(unsigned int strcount,...);
}
using namespace std; int main()
{
char str1[]="First string"; char str2[]="+second string"; char str3[]="+third string"; char buffer[80]; astrcpy(buffer,str1);
cout<<"astrcpy:\t"<<buffer<<endl;
astrcat(2,buffer,str2,str3);
cout<<"astrcat:\t"<<buffer<<endl;
amemset(buffer,'x',15);
cout<<"amemset:\t"<<buffer<<endl;
fcmemset(buffer,'y',10);
cout<<"fcmemset:\t"<<buffer<<endl;
amemcpy(buffer,str1,astrlen(str1)+1);
cout<<"amemcpy:\t"<<buffer<<endl; cout<<"strlen:\t\tLength of string '"<<str1\ <<"' equal "<<astrlen(str1)<<endl; fcstrcpy(buffer,str1); cout<<"fcstrcpy:\t"<<buffer<<endl;
return 0;
}
7.6.5.2 Пізнє зв’язування
Пізнє зв’язування з DLL на С вимагає попереднього визначення типів FARPROC для кожної функції з подальшим приведенням результату, що повертає GetProcAddress на цей тип.
Приклад 7.8 Додаток dlldynamiclinkcpp.exe, що демонструє використання динамічної бібліотеки dlldemo.dll (див. Приклад 7.4) шляхом пізнього зв’язування.
; Файл dlldynamiclinkcpp.cpp
#include <windows.h>; LPSTR, HINSTANCE, NULL, MAKEINTRESOURCE
#include <iostream> #include <cstdio> using namespace std;
typedef DWORD (__stdcall *dllstrlen)(LPSTR);
typedef void (__cdecl *dllmemcpy)(LPSTR lpdst, LPSTR lpsrc,DWORD n); typedef void (__cdecl *dllstrcpy)(char*, char*);
typedef void (__fastcall *dllfcstrcpy)(char* lpdst, char* lpsrc);
typedef void (__cdecl *dllmemset)(char* lpdest, char chr,unsigned int count); typedef void (__fastcall *dllfcmemset)(char*, char,unsigned int);
typedef void (__cdecl *dllstrcat)(unsigned int strcount,...); int main()
{
char str1[]="First string"; char str2[]="+second string"; char str3[]="+third string"; char buffer[80];
HINSTANCE hDll; dllmemcpy pmemcpy; dllstrcpy pstrcpy; dllfcstrcpy pfcstrcpy; dllmemset pmemset; dllfcmemset pfcmemset; dllstrcat pstrcat;
dllstrlen pstrlen; if((hDll=LoadLibrary("dlldemo.dll"))!=NULL)
{
pstrcpy=(dllstrcpy)GetProcAddress(hDll,"astrcpy");
pstrcpy(buffer,str1);
cout<<"astrcpy:\t"<<buffer<<endl;
pstrcat=(dllstrcat)GetProcAddress(hDll,"astrcat");
pstrcat(2,buffer,str2,str3);
cout<<"astrcat:\t"<<buffer<<endl;
pmemset=(dllmemset)GetProcAddress(hDll,"amemset");
pmemset(buffer,'x',15);
cout<<"amemset:\t"<<buffer<<endl;
pfcmemset=(dllfcmemset)GetProcAddress(hDll,
MAKEINTRESOURCE(104));
pfcmemset(buffer,'y',10);
cout<<"fcmemset:\t"<<buffer<<endl;
pmemcpy=(dllmemcpy)GetProcAddress(hDll,"amemcpy"); pstrlen=(dllstrlen)GetProcAddress(hDll,"_astrlen@4"); pmemcpy(buffer,str1,pstrlen(str1)+1); cout<<"amemcpy:\t"<<buffer<<endl; cout<<"strlen:\t\tLength of string '"<<str1
<<"' equal "<<pstrlen(str1)<<endl; pfcstrcpy=(dllfcstrcpy)GetProcAddress(hDll,"fcstrcpy"); pfcstrcpy(buffer,str1); cout<<"fcstrcpy:\t"<<buffer<<endl;
} else {
MessageBox(0,"Не можу завантажити бібліотеку", NULL,MB_OK | MB_ICONERROR);
return(-1);
}
return 0;
}
8 ПРОГРАМУВАННЯ МАТЕМАТИЧНОГО СПІВПРОЦЕСОРУ
Для виконання операції з плаваючою комою у процесорах Intel64 використовується спеціальний пристрій – Floating Point Unit (FPU). Це спеціалізований процесор з власними регістрами і власним набором команд, який спочатку постачався у вигляді окремої мікросхеми (і8087, і80287, і80387), що отримала назву математичного співпроцесора і уставлялася в спеціально передбачене для неї рознімання на системній платі, а починаючи з і80486DX4100 – вбудовується в мікросхему основного (CPU) процесора.
Не зважаючи на фізичне поєднання основного процесора і математичного співпроцесора в одному корпусі інтегральної мікросхеми, логічно це два різні обчислювальні пристрої. Процесор і математичний співпроцесор можуть працювати паралельно над виконанням різних команд, однак повної паралельності в їх роботі немає, бо процесор забезпечує математичному співпроцесору інтерфейс з пам’яттю. Синхронізація доступу до пам’яті з боку процесора і співпроцесора забезпечується в більшості випадків апаратними засобами і при програмуванні є абсолютно прозорою.
8.1 Програмна модель математичного співпроцесору
Під програмною моделлю математичного співпроцесору, так само як і під програмною моделлю основного процесору, розуміють його організацію з точки зору можливостей програмування. Рисунок 8.1 відображає складові програмної моделі математичного співпроцесору.
Рисунок 8.1 Зважаючи на те, математичний співпроцесор є складовою частиною
основного процесору, в контексті його програмної моделі не розглядаються
режими роботи, які від «успадковує» від основного процесору і тому вони не представлені на рисунку.
Зважаючи на те, що основний процесор забезпечує математичному співпроцесору інтерфейс з пам’яттю, способи організації і адресації пам’яті для них співпадають. Вони представлені на рисунку тому, що в системі команд співпроцесору є команди, які передбачають використання операндів в пам’яті. Для таких операндів будуть використовуватися відомі вже з попередніх розділів курсу способи адресації пам’яті.
8.1.1 Типи даних математичного співпроцесора
Математичний співпроцесор підтримує наступні типи даних:
1.Двійкові цілі (знаковий біт у старшому розряді) числа в трьох форматах: а) 16-бітне ціле число від -32 768 до 32 767; б) 32-бітне коротке ціле від -2 · 109 до +2 · 109; в) 64-бітне довге ціле від -9 · 1018 до +9 · 1018;
2.Числа з плаваючою комою в трьох форматах:
а) коротке 32-бітне дійсне число від 10-38 до 10+38; б) довге 64-бітне дійсне число від 10-308 до 10+308;
в) розширене 80-бітне дійсне число від 10-4932 до 10+4932;
3.Упаковані цілі BCD-числа в форматі 9+1 – в дев’яти молодших байтах розміщуються числові розряди, а в десятому байті використовується тільки старший біт для кодування знаку)
4.Спеціальні числові значення
а) позитивний і негативний нуль; б) позитивна і негативна нескінченність; в) денормалізовані дійсні числа;
г) сигнальні не числа SNAN і тихі не числа QNAN
Визначення в програмі 16/32/64-бітних двійкових цілих виконується звичним способом за допомогою директив, відповідно, WORD / DWORD / QWORD.
Визначення в програмі 32/64/80-бітних чисел з плаваючою комою може виконуватися за допомогою директив, відповідно, (DD | DWORD | REAL4) / (DQ | QWORD | REAL8) / (DT | REAL10) двома способами – в природній або в експоненціальній формі. В будь якій формі в записі числа повинен бути присутнім символ «.» десяткової крапки, навіть якщо число не має дробової частини:
X1 DD 123.
Y1 QWORD 123456.0
Z1 DT 123.456
X2 REAL4 1.23e+2
Y2 REAL8 –0.123456+e6
Z2 REAL10 123456.0e-3
Визначення в програмі упакованих цілих BCD-чисел виконується звичним способом за допомогою директиви DT. Спеціальні числові значення з’являються у результаті обчислень і, як правило, попередньо не визначаються.
8.1.2Склад і призначення регістрів співпроцесора
Впрограмній моделі математичного співпроцесора (Рисунок 8.2) можна виділити три групи регістрів. Основу моделі складає група з восьми 80- розрядних регістрів даних R0–R7 в яких зберігаються числа з плаваючою комою в розширеному форматі: старший (79-й) біт визначає знак числа, в бітах 78–64 розташована так звана характеристика числа, а в бітах 63–0 розміщується його мантиса. Мантиса зберігається у нормалізованому вигляді, у якому вона завжди починається з одиниці. При нормалізації кома в запису числа переміщується вправо або вліво так, щоб в цілій частини числа була єдина одиниця. Кількість перенесень коми визначає порядок числа s який зберігається
уполі характеристики як значення q=s+16383.
Рисунок 8.2
При завантаженні з пам’яті в регістри R0-R7 даних будь-яких інших типів
(див. 8.1.1) вони автоматично перетворюються в розширений формат числа з
плаваючою комою і уся їх подальша обробка відбувається в цьому форматі. При вивантаженні результатів у пам’ять, якщо вони зберігаються там не
як числа з плаваючою комою в розширеному форматі, то автоматично відбувається потрібне перетворення. При перетворенні у більш короткі формати чисел с плаваючою комою «зайві» розряди після округлення
відкидаються.
З кожним фізичним регістром R0–R7 жорстко зв’язане (Рисунок 8.3) відповідне двох бітове поле в регістрі тегів співпроцесора (TR, Tag Register). Двох бітове число, що зберігається у відповідних бітах TR, у кожний момент
часу надає інформацію про вміст відповідного регістру: |
|
00 – |
в регістрі знаходиться припустиме не нульове значення; |
01 – |
в регістрі знаходиться нульове значення; |
10 – |
в регістрі знаходиться спеціальне числове значення; |
11 – |
регістр порожній. |
|
Регістри даних |
математичного співпроцесора організовано |
у вигляді |
стеку |
і команди співпроцесора звертаються до регістрів даних |
R0–R7 за |
|
логічними номерами |
ST(0)–ST(7). Логічний номер ST(0) позначає верхівку |
||
стеку |
математичного співпроцесору, а ST(1), ST(2), …, ST(7) – |
елементи в |
|
глибині стеку. |
|
|