Совмещение функций на Ассемблере с программой на C++Builder

Страницы работы

Содержание работы

Совмещение функций на Ассемблере с программой на C++Builder

При написании многих программ в среде C++Builder иногда возникает необходимость вставить в программу функцию на языке низкого уровня, в частности, Ассемблера. Возникла такая ситуация недавно и у меня. Эта необходимость обуславливается несколькими причинами: 1. Скорость выполнения фрагмента на Ассемблере. Ассемблер - это язык низкого уровня, синтаксис которого образуют непосредственно команды процессора, которым присвоены понятные человеку мнемоники. На выполнение одной команды, процессор тратит от 0,5 такта процессорного времени, в то время, как команда на языке высокого уровня при компиляции образует несколько команд на все том же Ассемблере, на выполнение которых тратится, соответственно, больше времени. 2. Фрагмент на Ассемблере не способствует раздутию кода. Как я писал выше, при компиляции программ с языка высокого уровня, команды и операторы компилируются в стандартный набор команд на Ассемблере. Примером тому могут служить 2 файла, с расширением .com (к примеру), написанный на "голом" Ассемблере и программа, написанная на языке высокого уровня (C++ или Pascal) и затем скомпилированная в исполняемый файл .exe, выполняющие одни и те же действия. 3. Необходимость использования своего, низкоуровневого (для повышения быстродействия (п. 1), экономии памяти (п. 2) или того и другого) обработчика какого-либо события, в случае, если стандартные, предлагаемые средствами разработки программ, в частности, C++Builder, не подходят под требования программиста. Мне необходимо было написать программу, которая бы обрабатывала информацию, находящуюся на LPT-порте компьютера. Нужно было высокое быстродействие обработчика, т.к. к LPT-порту была подключена микросхема, управлять которой мне и нужно было. Функции WinAPI мне не подходили. Я написал программу-интерфейс для передачи данных между микросхемой и управляющей программой на Ассемблере, но тут возникла сложность интеграции функций на Ассемблере в программу на C++Builder. Безуспешно поискав немного литературу по данному вопросу в Интернете, я решил пробовать сам. В древней книге по Ассемблеру я нашел, как это делается в общих чертах для языка C++. Пару вечеров поработал, и вот что у меня получилось: Функция вывода байта BYTE в порт WORD void OutPort(WORD,BYTE): PUBLIC _OutPort _OutPort PROC ARG AdrPort:WORD,DataPort:BYTE push ebp mov ebp,esp mov ax,AdrPort mov dx,ax out dx,al pop ebp ret _OutPort ENDP Вызов её из C++ происходит обычным образом. Все функции объединяются в один файл, который присоединяется к проекту. Структура этого файла следующая: .486         ; Используемый процессор. В большинстве случаев этого достаточно .MODEL SMALL  ; Модель памяти. Зависит от функций. .DATA                ; Ели нужен отдельный сегмент данных EXTRN _a:BYTE  ; Если имеются функции, возвращающие значения .CODE                  ; Начало сегмента данных PUBLIC _OutPort ; Указание, что функция "видима" из других модулей _OutPort PROC     ; Начало функции "ближнего" типа OutPort ARG AdrPort:WORD,DataPort:BYTE ; Описание аргументов функции push ebp           ; Следующие 2 строки необходимы mov ebp,esp      ; Для загрузки аргументов mov ax,AdrPort  ; Собственно тело функции mov dx,ax out dx,al pop ebp              ; Восстановление стека ret                  ; Возврат в программу _OutPort ENDP ; Конец функции .CODE             ; Следующий сегмент данных (без его описания почему-то не                    ; компилировалось) PUBLIC _InPort _InPort PROC   ; Функция ввода значения из порта ARG AdrPort:WORD push ebp mov ebp,esp mov ax,AdrPort mov dx,ax in al,dx        ; Возвращаемое значение ВСЕГДА должно быть в регистре AX pop ebp ret _InPort ENDP END            ; Конец модуля с функциями Первая строка процедуры необходима для сохранения в стеке регистра BP, так как вызывающий код C++ считает, что во время работы процедуры регистр BP не изменяется. Вообще необходимо сохранять в самом начале процедуры используемые регистры, кроме AX, если функция возвращает значение, а перед выходом из процедуры их восстанавливать. Это поможет избежать многих ошибок, незаметных на первый взгляд. Осталось описать используемые функции в заголовочном файле программы, и все: extern "C" { void OutPort(WORD,BYTE); BYTE InPort(WORD); } Делается это в самом конце заголовочного файла перед директивой #endif. Скомпилированный проект, как говорится, готов к употреблению.

Похожие материалы

Информация о работе