Шаг 91.
Пример совместного использования ассемблера и C

    На этом шаге мы рассмотрим пример использования ассемблерного модуля в 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 проекта

    На следующем шаге мы рассмотрим использование встроенного ассемблера .




Предыдущий шаг Содержание Следующий шаг