6 ответов

Вы можете получить доступ к переменным по их имени и скопировать их в регистры. Вот пример из MSDN:

Int power2(int num, int power) { __asm { mov eax, num ; Get first argument mov ecx, power ; Get second argument shl eax, cl ; EAX = EAX * (2 to the power of CL) } // Return with result in EAX }

Компилятор microsoft очень плохо оптимизируется при подключении встроенной сборки. Он должен создавать резервные копии регистров, потому что если вы используете eax, тогда он не переместит eax в другой свободный регистр, он будет продолжать использовать eax. Ассемблер GCC намного продвинулся вперед на этом фронте.

Чтобы обойти это, Microsoft предложила intrinsics . Это гораздо лучший способ сделать вашу оптимизацию, поскольку она позволяет компилятору работать с вами. Поскольку Крис упоминал, что встроенная сборка не работает под x64 с компилятором MS, поэтому на этой платформе вы ДЕЙСТВИТЕЛЬНО лучше просто используете встроенные средства.

Они просты в использовании и обеспечивают хорошую производительность. Я признаю, что я часто могу выжать из него еще несколько циклов, используя внешний ассемблер, но они чертовски хороши для улучшения производительности, которое они обеспечивают.

В реестрах ничего нет. как выполняется блок _asm. Вам нужно переместить материал в регистры. Если есть переменная: "a", тогда вам нужно

Asm { mov eax, [a] }

Стоит отметить, что VS2010 поставляется с ассемблером Microsoft. Щелкните правой кнопкой мыши по проекту, перейдите к правилам сборки и включите правила сборки ассемблера, а среда IDE обработает файлы.asm.

это несколько лучшее решение, так как VS2010 поддерживает 32-битные и 64-битные проекты, а ключевое слово __asm ​​НЕ работает в 64-битных сборках. Вы ДОЛЖНЫ использовать внешний ассемблер для 64-битного кода:/

Я предпочитаю писать целые функции в сборке, а не с помощью сборки inline . Это позволяет поменять языковые функции высокого уровня на сборку в процессе сборки. Кроме того, вам не нужно беспокоиться о том, что оптимизация компилятора мешает.

Прежде чем писать одну строку сборки, распечатайте список языков ассемблера для своей функции. Это дает вам основание для разработки или модификации. Другим полезным инструментом является переплетение сборки с исходным кодом. Это расскажет вам, как компилятор кодирует конкретные утверждения.

Если вам нужно вставить встроенную сборку для большой функции, создайте новую функцию для кода, который необходим для встроенного. Снова замените на С++ или сборку во время сборки.

Это мои предложения, ваш пробег май-Вар (YMMV).

Мне очень нравится сборка, поэтому я не собираюсь быть здесь. Похоже, что вы профилировали свой код и нашли "горячую точку", что является правильным способом запуска. Я также предполагаю, что 200 строк, о которых идет речь, не используют много конструкций высокого уровня, таких как vector .

Все, что сказал: если бы я был вами, я бы прошел через этот код в отладчике VS, используя представление "Разборка". Если вам комфортно читать код, когда вы идете вперед, это хороший знак. После этого выполните компиляцию Release (Debug отключит оптимизацию) и создайте список ASM для этого модуля. Тогда, если вы думаете, что видите место для улучшения... у вас есть место для начала. Ответы других людей связаны с документацией MSDN, которая действительно очень скудная, но все же разумное начало.

Что такое Ассемблер

Ассемблер - низкоуровневый язык программирования. Для каждого процессора существует свой ассемблер. Программируя на ассемблере вы непосредственно работаете с аппаратурой компьютера. Исходный текст на языке ассемблера состоит из команд (мнемоник), которые после компиляции преобразуются в коды команд процессора.

Разработка программ на ассемблере - очень тяжёлая штука. Взамен потраченному времени вы получаете эффективную программу. Программы на ассемблере пишут, когда важен каждый такт процессора. На ассемблере вы даёте конкретные команды процессору и никакого лишнего мусора. Этим и достигается высокая скорость выполнения вашей программы.

Чтобы грамотно использовать ассемблер необходимо знать программную модель микропроцессорной системы. С точки зрения программиста микропроцессорная система состоит из:

  1. Микропроцессора
  2. Памяти
  3. Устройств ввода/вывода.

Программная модель хорошо описана в литературе .

Синтаксис Ассемблера

Общий формат строки программы на ассемблере

<Метка>: <Оператор> <Операнды> ; <Комментарий>

Поле метки. Метка может состоять из символов и знаков подчёркивания. Метки используются в операциях условного и безусловного перехода.

Поле оператора. В этом поле содержится мнемоника команды. Например мнемоника mov

Поле операндов. Операнды могут присутствовать только если присутствует Оператор (поле оператора). Операндов может не быть, а может быть несколько. Операндами могут быть данные, над которыми необходимо выполнить какие-то действия (переслать, сложить и т.д.).

Поле комментария. Комментарий нужен для словесного сопровождения программы. Всё, что стоит за символом ; считается комментарием.

Первая программа на языке Ассемблера

В этой статье будет использоваться ассемблер для i80x86 процессора и использоваться следующее программное обеспечение:

  • TASM - Borland Turbo Assembler - компилятор
  • TLINK - Borland Turbo Linker - редактор связей (компоновщик)

Если быть конкретным, то Tasm 2.0.

По традиции наша первая программа будет выводить строку "Hello world!" на экран.

Файл sample.asm

Model small ; Модель памяти.stack 100h ; Установка размера стека.data ; Начало сегмента данных программы HelloMsg DB "Hello World!",13,10,"$" .code ; Начало сегмента кода mov ax,@DATA ; Пересылаем адрес сегмента данных в регистр AX mov ds,ax ; Установка регистра DS на сегмент данных mov ah,09h ; DOS функция вывода строки на экран mov dx,offset HelloMsg ; Задаём смещение к началу строки int 21h ; Выводим строку mov ax,4C00h ; DOS функция выхода из программы int 21h ; Выход из программы end

Как вы могли заметить, что программа разделена на сегменты: сегмент данных, сегмент кода и есть ещё стековый сегмент.

Рассмотрим всё по порядку.

Директива.model small задаёт модель памяти. Модель small - это 1 сегмент для кода, 1 сегмент для данных и стека т.е. данные и стек находятся в одном сегменте. Бывают и другие модели памяти, например: tiny, medium, compact. В зависимости от выбранной вами модели памяти сегменты вашей программы могут перекрываться или могут иметь отдельные сегменты в памяти.

Директива.stack 100h задаёт размер стека. Стек необходим для сохранения некоторой информации с последующим её восстановлением. В частности стек используется при прерываниях. В этом случае содержимое регистра флагов FLAGS, регистра CS и регистра IP сохраняются в стеке. Далее идёт выполнение прерывающей программы, а потом идёт восстановление значений этих регистров.

  • Регистр флагов FLAGS содержит признаки, которые формируются после выполнения команды процессором.
  • Регистр CS (Code Segment) содержит адрес сегмента кода.
  • Регистр IP (Instruction Pointer) - указатель команд. Он содержит адрес команды, которая должная выполниться следующей (Адрес относительно сегмента кода CS).

Более подробное описание выходит за рамки простой статьи.

Директива.data определяет начало сегмента данных вашей программы. В сегменте данных определяются "переменные" т.е. идёт резервирование памяти под необходимые данные. После.data идёт строка
HelloMsg DB "Hello World!",13,10,"$"

Здесь HelloMsg - это символьное имя, которое соответствует началу строки "Hello World!" (без кавычек). То есть это адрес первого символа нашей строки относительно сегмента данных. Директива DB (Define Byte) определяет область памяти доступную по-байтно. 13,10 - коды символов Новая строка и Возврат каретки, а символ $ необходим для корректной работы DOS функции 09h. Итак, наша строка будет занимать в памяти 15 байт.

Директива.code определяет начало сегмента кода (CS - Code Segment) программы. Далее идут строки программы содержащие мнемоники команд.

Расскажу о команде mov.

mov <приёмник>, <источник>

Команда mov - команда пересылки. Она пересылает содержимое источника в приёмник. Пересылки могут быть регистр-регистр, регистр-память, память-регистр, а вот пересылки память-память нет т.е. всё проходит через регистры процессора.

Чтобы работать с данными необходимо настроить регистр сегмента данных. Настройка состоит в том, что мы записываем адрес сегмента данных @DATA в регистр DS (Data Segment). Непосредственно записать адрес в этот регистр нельзя - такова архитектура, поэтому мы используем регистр AX. В AX мы записываем адрес сегмента кода

а потом пересылаем содержимое регистра AX в регистр DS.

После этого регистр DS будет содержать адрес начала сегмента данных. По адресу DS:0000h будет содержаться символ H. Я предполагаю, что вы знаете о сегментах и смещениях.

Адрес состоит из двух составляющих <Сегмент>:<Смещение>, где Сегмент это 2 байта и смещение - 2 байта. Получается 4 байта для доступа к любой ячейке памяти.

mov ah,09h
mov dx,offset HelloMsg
int 21h

Тут мы в регистр AH записываем число 09h - номер функции 21-го прерывания, которая выводит строку на экран.

В следующей строке мы в регистр DX записываем адрес(смущение) к началу нашей строки.

Далее мы вызываем прерывание 21h - это прерывание функций DOS. Прерывание - когда выполняющаяся программа прерывается и начинает выполнятся прерывающая программа. По номеру прерывания определяется адрес подпрограммы DOS, которая выводит строку символов на экран.

У вас наверняка возникнет вопрос: А почему мы записываем номер функции 09h в регистр AH ? И почему смещение к строке записываем в регистр DX ?
Ответ простой: для каждой функции определены конкретные регистры, которые содержат входные данные для этой функции. Посмотреть какие регистры нужны конкретным функциям вы можете в help"е.

mov ax,4C00h
int 21h

mov ax,4C00h - пересылаем номер функции в регистр AX. Функция 4C00h - выход из программы.

int 21h - выполняем прерывание (собственно выходим)

end - конец программы.

После директивы end компилятор всё игнорирует, поэтому можете там писать всё, что угодно:)

Если вы дочитали до конца, то вы герой!

Майко Г.В. Ассемблер для IBM PC: - М.: "Бизнес-Информ", "Сирин" 1999 г. - 212 с.

высокого уровня

Несмотря на огромные возможности языков высокого уровня, иногда возникает необходимость применения Ассемблера в программах на ЯВУ. Наиболее распространены два подхода:

1) в тексте программы, написанной на языке высокого уровня, делаются ассемблерные вставки на встроенном ассемблере;

2) подключаются внешние ассемблерные модули: файл с процедурами пишется на внешнем ассемблере, компилируется в объектный файл OBJ, который подключается к проекту на ЯВУ.

Ассемблерные вставки применяются:

Для повышения быстродействия программы. Хотя большинство компиляторов делают оптимизацию кода, некоторым задачам даже этого мало. Тогда участки кода, критичные ко времени, пишут на ассемблере.

Для вызова команд, не используемых компилятором ЯВУ. С появлением новых поколений процессоров добавляются новые, более эффективные, машинные команды. Однако компиляторы ЯВУ при переводе программы с языка высокого уровня в машинные коды стараются использовать устаревшие команды i386-го, чтобы обеспечить максимальную совместимость со всеми, даже старыми, компьютерами. А если нужны специализированные команды новых процессоров (MMX, XMM, SSE, SSE II), приходится писать на ассемблере.

Область применения ассемблерных вставок ограничена возможностями компилятора. Например, во встроенном ассемблере запрещается вызывать некоторые привилегированные команды; «неизвестные» компилятору команды, появившиеся уже после его выпуска. В то время как использование внешнего ассемблера не имеет ограничений.

3.3. Встроенный ассемблер

Системы программирования Delphi, C++ Builder, Visual C++ позволяют вставлять в текст программы на языке высокого уровня участки кода, написанные на ассемблере. Этот ассемблер называется встроенным, и имеет незначительные синтаксические отличия от ассемблера TASM или MASM. Подробнее про него можно прочитать в справочной системе ЯВУ. Здесь приводятся краткие сведения.

    C ++ Builder, Visual C++ :

Ассемблерный текст заключается в блок _asm{…}

cmp eax, SomeVariable

В ассемблерных вставках можно использовать ранее объявленные переменные, функции и другие идентификаторы.

    Delphi :

Ассемблерные команды заключаются между словами asm end ;

cmp eax, SomeVariable

Внутри ассемблерного блока можно обращаться по именам к переменным, функциям, процедурам и меткам. Переменные, объявленные внутри блока директивами DB, DW и т.п. будут размещены в сегменте кода, а не данных. Это нужно учесть, чтобы компилятор не стал исполнять их значения как машинные коды – это может привести к ошибке исполнения программы.

Если внутри ассемблерного блока нужны переходы по меткам, их делают локальными – имена таких меток следует начинать с символа @. Область действия локальной метки ограничена ассемблерным блоком:

test SomeVariable , 0Fh

... // какие-то команды

@M1: // это локальная метка

3.4. Подключение внешних ассемблерных модулей в программы на языках высокого уровня

Этап 1 . Создание ассемблерного файла с экспортируемой процедурой. Чтобы подключение ассемблерного модуля к программе на ЯВУ было корректным, он должен удовлетворять правилам, в соответствии с которыми создает программу компилятор ЯВУ. Так, должны совпадать имена сегментов, конвенции вызова и т.д. Каркас ассемблерного модуля для подключения к Delphi, C++ Builder и Visual C++, приведен в листинге 1.

Листинг 1 – Каркас ассемблерного модуля для подключения к программам Win32.

.486 ; 32-разрядные приложения

. model flat ; в программах Win32 используется линейная модель

; памяти (flat)

. bss

; в этом сегменте описываются неинициализированные данные

. const

; в этом сегменте описываются типизированные константы

. data

; в этом сегменте описываются переменные с начальными значениями

. code

PUBLIC Имя_процедуры ; чтобы процедуру можно было вызывать из

; программы на ЯВУ, её нужно объявить экспортируемой

; реализация процедуры

Имя_процедуры proc near ; все процедуры – ближние

push ebp ; если процедура с параметрами, то в начале процедуры

; регистр

mov ebp , esp ; затем установить ebp = esp для обращения к параметрам

; здесь тело процедуры

pop ebp ; в конце восстанавливаем ebp

ret N ; если процедура освобождает стек из-под параметров сама, то

; N – число байтов, которое занимает стековый кадр,

; N всегда кратно 4.

Имя_процедуры endp

end ; конец модуля

Директива PUBLIC применяется, чтобы сделать имя процедуры «видимой» за пределами модуля, чтобы её «нашел» компилятор ЯВУ.

Компиляторы С++ различают в именах функций большие и маленькие буквы. Delphi, обычно не чувствительная к регистру, при импорте процедур из внешних OBJ и DLL файлов, буквы большие и малые различает. Поэтому при объявлении процедуры в ассемблерном модуле её имя следует писать так, как оно будет вызываться в ЯВУ.

Замечания по написанию тела процедуры – в п.3.5.

Этап 2 . Компиляция ассемблерного модуля в файл OBJ.

При компиляции следует использовать ключ /ml , чтобы заглавные и строчные буквы различались:

TASM.EXE /ml имя_файла .asm

Если этого не сделать, то процедуру MyProc язык высокого уровня увидит как MYPROC , поскольку TASM по умолчанию все экспортируемые имена записывает заглавными буквами.

Этап 3 . Подключение объектного файла к программе на ЯВУ.

!!! Предварительно объектный файл скопируйте в папку с проектом программы на ЯВУ.

    C ++ Builder :

Объектный модуль можно подключить тремя способами:

1) добавить к проекту через меню Project / Add To Project, тип файлов "OBJ".

2) директивой

#pragma link "имя_файла.obj"

3) директивой

USEOBJ("имя_файла.obj")

    Visual C++

В VC++ 6.0 имя OBJ-файла нужно вписать в параметры командной строки компоновщика: для этого выбрать меню Project / Settings, открыть закладку Link и вписать имя OBJ-файла в параметры командной строки “Object/Library modules”.

В VC++.NET выбирается меню Project/Properties, в дереве свойств папка C/C++ → Command Line и имя OBJ-файла вписывается в строку “Addition options”.

    Delphi :

Для подключения объектного файла в начало программы (между строками Program … или Unit … и строкой Uses …) добавляется директива:

{$LINK имя_файла.obj}

или {$L имя_файла.obj}

Этап 4 . Объявление в программе импортируемой процедуры.

С помощью описанных ниже директив имя ассемблерной процедуры становится видимым в программе на ЯВУ, и её можно далее вызывать, как обычную процедуру или функцию.

    C ++ Builder , Visual C++ :

Заголовок (прототип) ассемблерной функции объявляется с квалификаторами extern C ” :

extern "C" int __stdcall MyFunc(int x,y);

Если внешних функций несколько, их можно перечислить внутри секции extern :

extern "C"{

int __stdcall MyFunc(int x,y);

int __cdecl YourFunc(void* param);

Опция extern указывает компилятору, что тело функции следует искать в объектных файлах или библиотеках. Квалификатор "С" нужен, чтобы компилятор С++ не искажал имена функций (иначе он «от-себя» добавляет в имя @, подчеркивания и пр., и в таком виде пытается найти имя в подключенных модулях).

    Delphi :

В секции описания процедур и функций помещается заголовок ассемблерной подпрограммы, а вместо тела пишется зарезервированное слово external . Так компилятор понимает, что тело подпрограммы нужно искать во внешнем OBJ-файле. Например

Procedure MyProc(X,Y:integer); stdcall; external ;

Function MyFunc(X,Y:integer):integer; cdecl; external ;

Если предполагается, что внешняя подпрограмма имеет переменное количество аргументов, то записывается директива varargs . Эту директиву можно использоваться только совместно с конвенцией cdecl .

Многие из нас изучали ассемблер в университете, но почти всегда это ограничивалось простыми алгоритмами под DOS. При разработке программ для Windows может возникнуть необходимость написать часть кода на ассемблер, в этой статье я хочу рассказать вам, как использовать ассемблер в ваших программах под Visual Studio 2005.

Создание проекта

В статье мы рассмотрим как вызывать ассемблер из С++ кода и обратно, передавать данные, а также использовать отладчик встроенный в Visual Studio 2005 для отладки кода на ассемблер.

Для начала нам нужно создать проект. Включаем Visual Studio, выбираем File > New > Project. В Visual Studio нет языка ассемблер в окне выбора типа проекта, поэтому создаем С++ Win32 проект. В окне настроек нового проекта выбираем «Empty Project».

По умолчанию Visual Studio не распознает файлы с кодом на ассемблер. Для того чтобы включить поддержку ассемблер нам необходимо настроить в проекте условия сборки указав какой программой необходимо компилировать файлы *.asm. Для этого выбираем пункт меню «Custom Build Rules...».

В открывшемся окне мы можем указать специальные правила компиляции для различных файлов, Visual Studio 2005 уже имеет готовое правило для файлов *.asm, нам необходимо лишь включить его, установив напротив правила «Microsoft Macro Assembler» галочку.

Добавление исходного кода

Перейдем к написанию исходного кода нашего проекта. Начнем с добавления исходного кода на c++. Добавим новый файл в папку Source Files. В качестве Template выбираем C++ File и вводим желаемое имя файла, например main.cpp. Напишем функцию, которая будет считывать имя введенное пользователем, оформив это в виде функции readName() которая будет возвращать ссылку на считанное имя. Мы получим примерно следующее содержимое файла:

#include void main () { printf("Hello, what is your name?\n"); } void* readName() { char name; scanf("%s", &name); return &name; }

Теперь, когда мы знаем имя пользователя мы можем вывести приветствие, его будет выводить функция sayHello() которую мы напишем на ассемблер, чтобы использовать эту функцию сначала мы должны указать что она будет определена в другом файле, для этого добавим блок к main.cpp:

Extern "C" { void sayHello(); }

Этот блок говорит компилятору, что функция sayHello() будет объявлена в другом файле и будет иметь правила вызова «C». Компилятор C++ искажает имена функций так, что указание правил вызова обязательно. Кроме того мы хотим использовать функцию readName() из функции sayHello(), для этого необходимо добавить extern «C» перед определением функции readName(), это позволит вызывать эту функцию из других файлов используя правила вызова «C».

Пришло время добавить код на ассемблер, для этого добавим в Source Folder новый файл. Выбираем тип Text File (.txt) и в поле название заменяем.txt на.asm, назовем наш файл hello.asm. Объявим функцию sayHello() и укажем внешние функции, которые мы хотим использовать. Получим следующий код:

686 .MODEL FLAT, C .STACK .DATA helloFormat BYTE "Hello %s!",10,13,0 .CODE readName PROTO C printf PROTO arg1:Ptr Byte, printlist: VARARG sayHello PROC invoke readName invoke printf, ADDR helloFormat, eax ret sayHello ENDP END

Теперь мы можем запустить проект, для этого просто выбираем Debug > Start Without Debugging или нажимаем комбинацию Ctrl-F5. Если все сделано верно, вы увидите окно программы:

Немного усложним задачу, попробуем написать на ассемблер функцию принимающую параметр и возвращающую значение. Для примера напишем функцию calcSumm() которая будет принимать целое число и возвращать сумму его цифр. Изменим наш код на С++ добавив в него информацию о функции calcSumm, ввод числа и собственно вызов функции. Добавим функцию в файл hello.asm, возвращаемое значение помещается в eax, параметры объявляются после ключевого слова PROC. Все параметры можно использовать в коде процедуры, они автоматически извлекутся из стека. Также в процедурах можно использовать локальные переменные. Вы не можете использовать эти переменные вне процедуры. Они сохранены в стеке и удаляются при возврате из процедуры:

686 .MODEL FLAT, C .STACK .DATA helloFormat BYTE "Hello %s!",10,13,0 .CODE readName PROTO C printf PROTO arg1:Ptr Byte, printlist: VARARG sayHello PROC invoke readName invoke printf, ADDR helloFormat, eax ret sayHello ENDP calcSumm PROC a:DWORD xor esi, esi mov eax, a mov bx, 10 @div: xor edx, edx div bx add esi, edx cmp ax, 0 jne @div mov eax, esi ret calcSumm ENDP END

Запустив проект мы увидим следующий результат выполнения:

Отладка

Конечно в данной задаче нет ничего сложного и она вовсе не требует использования ассемблер. Более интересным будет рассмотреть, а что же нам дает Visual Studio для разработки на ассемблер. Попробуем включить режим отладки и установим точку остановки в hello.asm, запустим проект, мы увидим следующее:

Окно Disassembly (Debug > Windows > Disassembly) показываем команды ассемблер для данного объектного файла. Код который мы написали на С++ показывается черным цветом. Disassembled code показывается серым после соответствующего ему кода на C++/ассемблер. Окно Disassembly позволяет отлаживать код и осуществлять stepping по нему.

Окно регистров (Debug > Windows > Registers) позволяет посмотреть значение регистров.

Окно памяти (Debug > Windows > Memory) позволяет посмотреть дамп памяти, слева мы видим шестнадцатеричные адрес, справа шеснадцатеричные значения соответствующих ячеек памяти, можно перемещаться, вводя адрес в соответствующее поле в верху окна.


Давно хотел разобраться с этой темой. И вот наконец собрался.

Дело в том, что инструкции процессора Интел и синтаксис вставок ассемблерного кода в программы на Visual C++ не будут работать в Dev-C++ .

Потому что Dev-C++ использует компилятор GCC (бесплатный компилятор языка С++). Этот компилятор имеет встроенный ассемблер, но это не MASM и не TASM с привычным . Это ассемблер AT&T, синтаксис которого очень сильно отличается от синтаксиса MASM/TASM и подобных.

Кроме того, если в Паскале или Visual C++ вы просто используете ключевые слова - операторные скобки (в Паскале это asm...end, в Visual C++ это __asm {...}), и между этими скобками пишите инструкции ассемблера как вы привыкли, то с компилятором GCC это не проканает.

Я сначала никак не мог понять, почему. Но когда немного познакомился с , то понял.

Оказывается, в компиляторе GCC, как и в Паскале и в Visual C++, есть ключевые слова asm и __asm. Вот только это вовсе не операторные скобки!!!

По сути это функции, которые вызываются с определённым набором параметров. И в эти функции в качестве параметров передаются инструкции ассемблера!

Вот уж воистину - зачем просто, если можно сложно!

В общем, использование встроенного ассемблера GCC - это целая наука. Если интересно её освоить, то можете начать вот с (это мой перевод английского оригинала).

А здесь я просто в самых общих чертах покажу, как можно использовать вставки на ассемблере в Dev-C++ (это будет также справедливо для других средств разработки, использующих компилятор GCC).

Ассемблер AT&T

Как я уже сказал, этот ассемблер сильно отличается от привычных нам . Здесь я не буду об этом говорить. Если кому интересно, то основные отличия описаны .

Вставка на ассемблере в Dev-C++

Основной формат вставки кода ассемблера показан ниже:

asm("Здесь код на ассемблере" );

/* помещает содержимое ecx в eax */ asm("movl %ecx %eax"); /* помещает байт из bh в память, на которую указывает eax */ __asm__("movb %bh (%eax)");

Как вы могли заметить, здесь используются два варианта встраивания ассемблера: asm и __asm__. Оба варианта правильные. Следует использовать __asm__, если ключевое слово asm конфликтует с каким-либо участком вашей программы (например, в вашей программе есть переменная с именем asm).

Если встраивание кода на ассемблере содержит более одной инструкции, то мы пишем по одной инструкции в строке в двойных кавычках, а также суффикс ’\n’ и ’\t’ для каждой инструкции.

Asm__ ("movl %eax, %ebx\n\t" "movl $56, %esi\n\t" "movl %ecx, $label(%edx,%ebx,$4)\n\t" "movb %ah, (%ebx)");

Однако в большинстве случаев требуется обмен данными между кодом на ассемблере и переменными, которые объявлены в исходных кодах на языке высокого уровня.

Это тоже возможно. Общий формат ассемблерной вставки для компилятора GCC такой:

Asm (assembler template: output operands /* не обязательно */ : input operands /* не обязательно */ : list of clobbered registers /* не обязательно */);

Не буду здесь подробно всё это расписывать, так как это уже сделано . Там же вы найдёте все подробности использования встроенного ассемблера компилятора GCC (ну хотя не все, а основные).

Я же здесь приведу пример, и на этом успокоюсь.

Для начала не очень хороший пример.

Int x = 0, y = 0; cout

Не очень хороший он потому, что мы изменяем значения регистров, а потом получаем значение регистра eax в переменную х. Но при этом мы не заботимся о том, что состояние этих регистров может быть изменено где-то в другом месте. Так что это может привести к потенциальным неожиданностям.

Теперь попробуем сделать всё чуть более правильно (хотя и не идеально).

Int y = 15, z = 10; cout

Здесь в ассемблерный код мы передаём значения переменных y и z. Значение у помещается в регистр еах (на это указывает буква “a”), а значение z помещается в регистр ebx (на это указывает буква “b”).

Сам ассемблерный код выполняет сложение значений регистров eax и ebx, и помещает результат в eax. А уже этот результат выводится в переменную y. То, что у - это выходная переменная, определяет модификатор “=”.

Ну вот как-то так. Это, конечно, в самых общих чертах. Если кого интересуют подробности, то см. .