Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

Использование встроенного в С асма.

279
31 января 2005 года
bave
456 / / 07.03.2004
КТо-нибудь, подскажите пожалуйста особенности использования встроенного ассемблера на С.
Из всех ассемблеров я более менее знаю TASM,
но вообще низкоуровневое программирование я
знаю достаточно плохо, чтобы мне встроенный в
C асм казался китайской грамотой.
Буду очень благодарен если кто-нибудь как можно подробней распишет как организовать ассемблерную
процедуру и другое отличие встроренного асма от
тасма.
1.7K
08 февраля 2005 года
Envel
206 / / 29.11.2004
Цитата:
Originally posted by bave
КТо-нибудь, подскажите пожалуйста особенности использования встроенного ассемблера на С.
Из всех ассемблеров я более менее знаю TASM,
но вообще низкоуровневое программирование я
знаю достаточно плохо, чтобы мне встроенный в
C асм казался китайской грамотой.
Буду очень благодарен если кто-нибудь как можно подробней распишет как организовать ассемблерную
процедуру и другое отличие встроренного асма от
тасма.


Все зависит от того, каким ты компилятором пользуешься. Если gcc - тут найдешь явные отличия, лучше почитать об этом используя google.com (например, тут: http://tigcc.ticalc.org/doc/gnuasm.html). Если же пользуешься компилятором Borland, то там ассемблер ничем не отличается от такового в tasm (один производитель, как-никак). Даже есть кое-какие достоинства, например, переменные можно указывать именами, а не высчитывать адреса (годиться для любого встраиваемого ассемблера). Синтаксис выглядит так:

Код:
char test[]="Hello, world!$";
void func()
...
char *ptr=new char[80];
strcpy(ptr,test);
asm {
lds dx,ptr
mov ah,9
int 0x21
}
...
}

при этом компилятор сам заботиться о сохранении всех нужных регистров.
Существуют также следующие формы asm:
 
Код:
asm("mov ax,1");
_asm {}
__asm {}

и подобные разновидности (в основном в компиляторах Microsoft.
279
16 февраля 2005 года
bave
456 / / 07.03.2004
Спасибо за ответ, а то я уже было отчаялся :-)
я то думал, что у всех языков семейства C
работа с асмом одинаковая.

Ну, вообщем, восновном я использую майкрософтовский VC++5.0.
Где можно почитать, как там асм использовать встроенный.

asm("mov ax,1"); //????! - а как насчёт аргументы в функцию передать, регистры сохранить, нужные
значения в них записать, выполнить прерывание
и ..... .
1.7K
16 февраля 2005 года
Envel
206 / / 29.11.2004
Цитата:
Originally posted by bave
Спасибо за ответ, а то я уже было отчаялся :-)
я то думал, что у всех языков семейства C
работа с асмом одинаковая.

Ну, вообщем, восновном я использую майкрософтовский VC++5.0.
Где можно почитать, как там асм использовать встроенный.

asm("mov ax,1"); //????! - а как насчёт аргументы в функцию передать, регистры сохранить, нужные
значения в них записать, выполнить прерывание
и ..... .


Про asm в VC читай в MSDN или в хелпе к vc. Регистры он сам по-моему сохраняет (анализирует твой код на asm), но можно и самому. Как это делать - изучай asm (команды push,pop). Передача параметров - через стек, читай соглашения о вызовах для компиляторов Microsoft (google.com в помощь).
Как вызывать прерывание я тебе уже написал (смотри пример с int 0x21)

487
21 февраля 2005 года
ddnh_bc
301 / / 16.09.2003
Цитата:
Originally posted by bave
Спасибо за ответ, а то я уже было отчаялся :-)
я то думал, что у всех языков семейства C
работа с асмом одинаковая.

Ну, вообщем, восновном я использую майкрософтовский VC++5.0.
Где можно почитать, как там асм использовать встроенный.

asm("mov ax,1"); //????! - а как насчёт аргументы в функцию передать, регистры сохранить, нужные
значения в них записать, выполнить прерывание
и ..... .



Вообще-то, MSVC дает тебе готовый стаб функции - тобишь настраивает стэк и ebp. Все остальное по твоему усмотрению. Получается вот такая рыба:

C:

 
Код:
int func(int v1,int v2){}

ASM как это выглядит:
 
Код:
sub func
push ebp
mov ebp, esp
sub esp, <размер под локальные переменные>
//здесь пользовательский код
mov esp, ebp
pop ebp
ret


Если тебе это не нужно - объявляешь функцию как __declspec(naked).

Вот пример из MSDN:

Код:
// nkdfastcl.cpp
__declspec(naked) int __fastcall  power(int i, int j)
{
    /* calculates i^j, assumes that j >= 0 */

    /* prolog */
    __asm {
        push   ebp
        mov      ebp, esp
        sub      esp, __LOCAL_SIZE
       // store ECX and EDX into stack locations allocated for i and j
        mov   i, ecx
        mov   j, edx
    }
     
    {
        int k=1; // return value
        while (j-- > 0) k *= i;
        __asm { mov eax, k };
    }

    /* epilog */
    __asm  
    {
        mov      esp, ebp
        pop      ebp
        ret
    }
}

int main()
{
}


Как выглядит вызов функции:
 
Код:
__asm
{
    push <v2>
    push <v1>
    call func
    mov <result>, eax
}


Ну и еще - по умолчанию VC использует _cdecl calling convention. Тобишь - аргументы передаются через стэк - результат возвращается в eax.
279
22 февраля 2005 года
bave
456 / / 07.03.2004
Вот каждый раз думаю - писал бы я на паскале всё,
там и asm встроенный без заморрочек был.

Куча вопросов и даже не знаю с чего начать:
1. Почему все регистры на 'е' - начинаются,
вот не bx, а ebx например.
2. В С int - это описатель целого типа, интеджер,
а как тогда прерывание то вызывать.
3. А почему при входе в функцию, только регистр
базы сохраняется..
4............ и т. д.

Помоему я разберусь, если смогу найти
на русском языке какое - нибудь руководство
по встроенному асемблеру, самый главный вопрос
где его найти. С примерами и т. п., чтоб
на учебник похоже было.
1.7K
23 февраля 2005 года
Envel
206 / / 29.11.2004
Цитата:
Originally posted by bave
Вот каждый раз думаю - писал бы я на паскале всё,
там и asm встроенный без заморрочек был.

Куча вопросов и даже не знаю с чего начать:
1. Почему все регистры на 'е' - начинаются,
вот не bx, а ebx например.
2. В С int - это описатель целого типа, интеджер,
а как тогда прерывание то вызывать.
3. А почему при входе в функцию, только регистр
базы сохраняется..
4............ и т. д.

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


Купи руководство по ассемблеру (как самостоятельному средству), тогда 99% вопросов исчезнут. А те, что остануться можно устранить книгой "Теория и практика С++" Г. Шилдт, BHV, СПб. ("Expert C++").
1. e - признак 32-хразрядного регистра. Если пишешь только под 16-тибитный DOS, все будет так же.
2. Все, что внутри asm {}, передается ассемблеру (под час с изменением, например в gcc), там int уже далеко не означает тип переменной.
3. Почитай "Соглашения о вызовах" и "Передача параметров через стек" в указанной выше литературе. По крайней мере в google.com.
По английски вроде так звучит: Calling Conventions.

487
24 февраля 2005 года
ddnh_bc
301 / / 16.09.2003
Цитата:
Originally posted by bave
Вот каждый раз думаю - писал бы я на паскале всё,
там и asm встроенный без заморрочек был.

Куча вопросов и даже не знаю с чего начать:
1. Почему все регистры на 'е' - начинаются,
вот не bx, а ebx например.
2. В С int - это описатель целого типа, интеджер,
а как тогда прерывание то вызывать.
3. А почему при входе в функцию, только регистр
базы сохраняется..
4............ и т. д.

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



В С как и в паскале специально и существует разделение на язык высокого уровня и ассемблер.
Когда ты в С пишешь:
__asm { - то все что будет находится внутри фигурных скобок уже будет интерпретироваться компилятором как чистый ассемблерный код - но правда есть небольшие отличия. Например можно использовать такие конструкции:

 
Код:
void func(int v1, char *v2)
{
__asm
{
   xor eax, eax
   mov edi, v2
   mov ecx, v1
   mov edx, 0x2E
}
}


Тоесть получается такая небольшая смесь C и асма.

По поводу того - почему сохраняется только ebp.
При входе в функцию ebp у тебя настраивается на область локальных переменных функции (см. пример выше). В результате - [ebp-4] - первая локальная переменная - за ней все остальные. [ebp+8] - первый аргумент, переданный в функцию через стек.
+8 потому что [ebp] - его предыдущее значение, а [ebp+4] - адрес возврата.
А теперь сам подумай - на кой черт сохранять остальные регистры, к примеру, если они в дальнейшем не будут изменяться.

А если ты например сделаешь объявление функции как __fastcall - в этом случае у тебя 3 первых аргумента будут передаваться через регистры. И сторить их при входе в функцию - это уже абсолютная глупость.

В конце-концов - неужели так трудно написать:

 
Код:
push eax
push ebx
push edx
......
pop edx
pop ebx
pop eax


Ну на худой конец:
 
Код:
pushad
......
popad


Это же элементарно.
1.7K
25 февраля 2005 года
Envel
206 / / 29.11.2004
Цитата:
Originally posted by ddnh_bc

Это же элементарно.


Элементарно то, что знаешь.
Вы направляете человека не по верному пути, усложняя то, что я написал выше. Это не очень хорошо.
В свою очередь, задавшему вопрос, порекомендую еще раз обратиться к литературе и внимательно почитать нужные разделы (начать лучше с ассемблера).

Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог