На этом шаге мы рассмотрим пример использования ассемблерного модуля в C-программе.
На этом шаге рассматривается пример простейшего калькулятора. Для ассемблера бывает сложно найти библиотеки с определенными процедурами. Предлагаемая схема очень проста. По сути, программа на языке С (или другом языке высокого уровня) является неким каркасом. Она вызывает процедуру на языке ассемблера, которая и выполняет основные действия. Кроме того, в С-модуль можно поместить и другие процедуры, которые легче написать на С и которые будут вызываться из ассемблера. Здесь как раз и приводится такой пример. В С-модуль помещаются процедуры, которые преобразуют строки в вещественные числа, выполняют над ними действия и затем преобразуют результат опять в строку.
C-модуль (pr91.cpp), версия Borland C++ 4.5:
#include <windows.h> #include <stdio.h> #include <math.h> //Вызов главной ассемблерной процедуры. extern "C" { void far _export __stdcall MAIN1(); } //Сложить. extern "C" { void far _export __stdcall sum(char *, char *, char *); } //Вычесть. extern "C" { void far _export __stdcall su(char *, char *, char *); } //Умножить. extern "C" { void far _export __stdcall mu(char *, char *, char *); } //Разделить. extern "C" { void far _export __stdcall dii(char *, char *, char *); } int WINAPI WinMain (HINSTANCE, HINSTANCE, LPSTR, int) { MAIN1(); return 0; } extern "C" __stdcall void sum(char * s1, char * s2, char * s) { float f1,f2,f; f1=atof(s1); f2=atof(s2); f=f1+f2; sprintf (s,"%f",f); strcat (s," +") ; return; } extern "C" __stdcall void su(char *s1, char *s2, char *s) { float f1,f2,f; f1=atof(s1); f2=atof(s2); f=f1-f2; sprintf (s,"%f",f); strcat (s," -") ; return; } extern "C" __stdcall void mu(char *s1, char *s2, char *s) { float f1,f2,f; f1=atof(s1) ; f2=atof(s2); f=f1*f2; sprintf (s,"%f",f); strcat(s," *"); return; } extern "C" __stdcall void dii(char *s1, char *s2, char *s) { float f1,f2,f; f1=atof(s1); f2=atof(s2); if(f2!=0) { f=f1/f2; sprintf(s,"%f",f); strcat(s," /") ; } else strcpy(s,"Ошибка деления"); return; }
Файл pr91_1.rc:
//Определение констант. //Стили окна. #define WS_SYSMENU 0x00080000L #define WS_MINIMIZEBOX 0x00020000L #define DS_3DLOOK 0x0004L //Текст в окне редактирования прижат к левому краю. #define ES_LEFT 0x0000L //Стиль всех элементов на окне. #define WS_CHILD 0x40000000L //Элементы на окне должны быть изначально видимы. #define WS_VISIBLE 0x10000000L //Бордюр вокруг элемента. #define WS_BORDER 0x00800000L //При помощи TAB можно по очереди активизировать элементы. #define WS_TABSTOP 0x00010000L #define SS_LEFT 0x00000000L #define BS_PUSHBUTTON 0x00000000L #define BS_CENTER 0x00000300L #define DS_LOCALEDIT 0x20L //Запрещается ввод с клавиатуры. #define ES_READONLY 0x0800L //Идентификаторы кнопок. #define IDC_BUTTON1 101 #define IDC_BUTTON2 102 #define IDC_BUTTON3 103 #define IDC_BUTTON4 104 //Определение диалогового окна. DIAL1 DIALOG 0, 0, 170, 110 STYLE WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | DS_3DLOOK CAPTION "Calculator" FONT 8, "Arial" { CONTROL "", 1, "edit", ES_LEFT | WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP, 9, 8, 128, 12 CONTROL "", 2, "edit", ES_LEFT | WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP, 9, 27, 128, 12 CONTROL "", 3, "edit", ES_LEFT | WS_CHILD | ES_READONLY | WS_VISIBLE | WS_BORDER | WS_TABSTOP, 9, 76, 127, 12 CONTROL "+", IDC_BUTTON1, "button", BS_PUSHBUTTON | BS_CENTER | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 11, 48, 15, 14 CONTROL "-", IDC_BUTTON2, "button", BS_PUSHBUTTON | BS_CENTER | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 34, 48, 15, 14 CONTROL "*", IDC_BUTTON3, "button", BS_PUSHBUTTON | BS_CENTER | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 56, 48, 15, 14 CONTROL "/", IDC_BUTTON4, "button", BS_PUSHBUTTON | BS_CENTER | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 80, 48, 15, 14 }
Файл pr91_1.inc:
;Константы. ;Сообщение приходит при закрытии окна. WM_CLOSE equ 10h ;Сообщение приходит при создании окна. WM_INITDIALOG equ 110h ;Сообщение приходит при событии с элементом на окне. WM_COMMAND equ 111h ;Сообщение посылки текста элементу. WM_SETTEXT equ 0Ch ;Сообщение получения текста от элемента. WM_GETTEXT equ 0Dh ;Прототипы внешних процедур. IFDEF MASM EXTERN ExitProcess@4:NEAR EXTERN GetModuleHandleA@4:NEAR EXTERN DialogBoxParamA@20:NEAR EXTERN EndDialog@8:NEAR EXTERN SendDlgItemMessageA@20:NEAR ELSE EXTERN ExitProcess:NEAR EXTERN GetModuleHandleA:NEAR EXTERN DialogBoxParamA:NEAR EXTERN EndDialog:NEAR EXTERN SendDlgItemMessageA:NEAR SendDlgItemMessageA@20 = SendDlgItemMessageA ExitProcess@4 = ExitProcess GetModuleHandleA@4 = GetModuleHandleA DialogBoxParamA@20 = DialogBoxParamA EndDialog@8 = EndDialog ENDIF ;Структуры. ;Структура сообщения. MSGSTRUCT STRUC MSHWND DD ? ;Идентификатор окна, получающего сообщение. MSMESSAGE DD ? ;Идентификатор сообщения. MSWPARAM DD ? ;Доп. информация о сообщении. MSLPARAM DD ? ;Доп. информация о сообщении. MSTIME DD ? ;Время посылки сообщения. MSPT DD ? ;Положение курсора во время посылки сообщения. MSGSTRUCT ENDS
Файл pr91_1.asm:
.386P ;Плоская модель. .MODEL FLAT, STDCALL include pr91_1.inc EXTERN sum:NEAR EXTERN su:NEAR EXTERN mu:NEAR EXTERN dii:NEAR PUBLIC MAIN1 ;Директивы компоновщику для подключения библиотек. IFDEF MASM ;Для компоновщика LINK.EXE. includelib \masm32\lib\user32.lib includelib \masm32\lib\kernel32.lib ELSE ;Для компоновщика TLINK32.EXE. includelib e:\tasm\lib\import32.lib ENDIF ;------------------------------------------------ ;Сегмент данных. _DATA SEGMENT DWORD PUBLIC USE32 'DATA' MSG MSGSTRUCT <?> HINST DD 0 ;Дескриптор приложения. PA DB 'DIAL1',0 S1 DB 50 DUP(0) S2 DB 50 DUP(0) S DB 50 DUP(0) _DATA ENDS ;Сегмент кода. _TEXT SEGMENT DWORD PUBLIC USE32 'CODE' ;Процедура, вызываемая из Си-модуля. MAIN1 PROC ;Получить дескриптор приложения. PUSH 0 CALL GetModuleHandleA@4 MOV [HINST], EAX ;------------------------------------- PUSH 0 PUSH OFFSET WNDPROC PUSH 0 PUSH OFFSET PA PUSH [HINST] CALL DialogBoxParamA ;------------------------------------- RET MAIN1 ENDP ;------------------------------------- ;------------------------------------- ;Процедура окна. ;Расположение параметров в стеке: ;[ЕВР+014Н] LPARAM ;[ЕВР+10Н] WAPARAM ;[ЕВР+0СН] MES ;[ЕВР+8] HWND WNDPROC PROC PUSH EBP MOV EBP, ESP ;------------------------------------- CMP DWORD PTR [EBP+0CH],WM_CLOSE JNE L1 ;Здесь реакция на закрытие окна. ;Закрыть диалог. PUSH 0 PUSH DWORD PTR [EBP+08H] CALL EndDialog@8 JMP FINISH L1: CMP DWORD PTR [EBP+0CH],WM_INITDIALOG JNE L2 ;Здесь заполнить окна редактирования, если надо. JMP FINISH L2: CMP DWORD PTR [EBP+0CH],WM_COMMAND JNE FINISH CMP WORD PTR [EBP+10H],101 JNE NO_SUM ;Первое слагаемое. PUSH OFFSET S1 PUSH 50 PUSH WM_GETTEXT PUSH 1 PUSH DWORD PTR [EBP+08H] CALL SendDlgItemMessageA ;Dторое слагаемое. PUSH OFFSET S2 PUSH 50 PUSH WM_GETTEXT PUSH 2 PUSH DWORD PTR [EBP+08H] CALL SendDlgItemMessageA ;Сумма. PUSH OFFSET S PUSH OFFSET S2 PUSH OFFSET S1 CALL sum ;Вывести сумму. PUSH OFFSET S PUSH 50 PUSH WM_SETTEXT PUSH 3 PUSH DWORD PTR [EBP+08H] CALL SendDlgItemMessageA JMP FINISH NO_SUM: CMP WORD PTR [EBP+10H],102 JNE NO_SUB ;Уменьшаемое. PUSH OFFSET S1 PUSH 50 PUSH WM_GETTEXT PUSH 1 PUSH DWORD PTR [EBP+08H] CALL SendDlgItemMessageA ;Вычитаемое. PUSH OFFSET S2 PUSH 50 PUSH WM_GETTEXT PUSH 2 PUSH DWORD PTR [EBP+08H] CALL SendDlgItemMessageA ;Разность. PUSH OFFSET S PUSH OFFSET S2 PUSH OFFSET S1 CALL su ;Вычислить разность. PUSH OFFSET S PUSH 50 PUSH WM_SETTEXT PUSH 3 PUSH DWORD PTR [EBP+08H] CALL SendDlgItemMessageA JMP FINISH NO_SUB: CMP WORD PTR [EBP+10H],103 JNE NO_MULT ;Первый множитель. PUSH OFFSET S1 PUSH 50 PUSH WM_GETTEXT PUSH 1 PUSH DWORD PTR [EBP+08H] CALL SendDlgItemMessageA ;Второй множитель. PUSH OFFSET S2 PUSH 50 PUSH WM_GETTEXT PUSH 2 PUSH DWORD PTR [EBP+08H] CALL SendDlgItemMessageA ;Произведение. PUSH OFFSET S PUSH OFFSET S2 PUSH OFFSET S1 CALL mu ;Вывести произведение. PUSH OFFSET S PUSH 50 PUSH WM_SETTEXT PUSH 3 PUSH DWORD PTR [EBP+08H] CALL SendDlgItemMessageA JMP FINISH NO_MULT: CMP WORD PTR [EBP+10H],104 JNE FINISH ;Делимое. PUSH OFFSET S1 PUSH 50 PUSH WM_GETTEXT PUSH 1 PUSH DWORD PTR [EBP+08H] CALL SendDlgItemMessageA ;Делитель. PUSH OFFSET S2 PUSH 50 PUSH WM_GETTEXT PUSH 2 PUSH DWORD PTR [EBP+08H] CALL SendDlgItemMessageA ;Деление. PUSH OFFSET S PUSH OFFSET S2 PUSH OFFSET S1 CALL dii ;Вывести результат деления. PUSH OFFSET S PUSH 50 PUSH WM_SETTEXT PUSH 3 PUSH DWORD PTR [EBP+08H] CALL SendDlgItemMessageA JNE FINISH FINISH: MOV EAX,0 POP EBP RET 16 WNDPROC ENDP _TEXT ENDS END
Результат работы приложения изображен на рисунке 1:
Рис.1. Результат работы приложения
Трансляция:
tasm32 /ml pr91_1.asm
Затем в среде Borland C++ 4.5 создадим новый проект (у нас он называется pr91.ide) и добавим в него созданный OBJ-файл, а также файл ресурсов (рисунок 2):
Рис.2. Структура проекта
Откомпилировать проект нужно для платформы Win32:
Рис.3. Окно TargetExpert проекта
На следующем шаге мы рассмотрим использование встроенного ассемблера .