Использование встроенного в С асма.
Из всех ассемблеров я более менее знаю TASM,
но вообще низкоуровневое программирование я
знаю достаточно плохо, чтобы мне встроенный в
C асм казался китайской грамотой.
Буду очень благодарен если кто-нибудь как можно подробней распишет как организовать ассемблерную
процедуру и другое отличие встроренного асма от
тасма.
КТо-нибудь, подскажите пожалуйста особенности использования встроенного ассемблера на С.
Из всех ассемблеров я более менее знаю TASM,
но вообще низкоуровневое программирование я
знаю достаточно плохо, чтобы мне встроенный в
C асм казался китайской грамотой.
Буду очень благодарен если кто-нибудь как можно подробней распишет как организовать ассемблерную
процедуру и другое отличие встроренного асма от
тасма.
Все зависит от того, каким ты компилятором пользуешься. Если gcc - тут найдешь явные отличия, лучше почитать об этом используя google.com (например, тут: http://tigcc.ticalc.org/doc/gnuasm.html). Если же пользуешься компилятором Borland, то там ассемблер ничем не отличается от такового в tasm (один производитель, как-никак). Даже есть кое-какие достоинства, например, переменные можно указывать именами, а не высчитывать адреса (годиться для любого встраиваемого ассемблера). Синтаксис выглядит так:
void func()
...
char *ptr=new char[80];
strcpy(ptr,test);
asm {
lds dx,ptr
mov ah,9
int 0x21
}
...
}
при этом компилятор сам заботиться о сохранении всех нужных регистров.
Существуют также следующие формы asm:
_asm {}
__asm {}
и подобные разновидности (в основном в компиляторах Microsoft.
я то думал, что у всех языков семейства C
работа с асмом одинаковая.
Ну, вообщем, восновном я использую майкрософтовский VC++5.0.
Где можно почитать, как там асм использовать встроенный.
asm("mov ax,1"); //????! - а как насчёт аргументы в функцию передать, регистры сохранить, нужные
значения в них записать, выполнить прерывание
и ..... .
Спасибо за ответ, а то я уже было отчаялся :-)
я то думал, что у всех языков семейства C
работа с асмом одинаковая.
Ну, вообщем, восновном я использую майкрософтовский VC++5.0.
Где можно почитать, как там асм использовать встроенный.
asm("mov ax,1"); //????! - а как насчёт аргументы в функцию передать, регистры сохранить, нужные
значения в них записать, выполнить прерывание
и ..... .
Про asm в VC читай в MSDN или в хелпе к vc. Регистры он сам по-моему сохраняет (анализирует твой код на asm), но можно и самому. Как это делать - изучай asm (команды push,pop). Передача параметров - через стек, читай соглашения о вызовах для компиляторов Microsoft (google.com в помощь).
Как вызывать прерывание я тебе уже написал (смотри пример с int 0x21)
Спасибо за ответ, а то я уже было отчаялся :-)
я то думал, что у всех языков семейства C
работа с асмом одинаковая.
Ну, вообщем, восновном я использую майкрософтовский VC++5.0.
Где можно почитать, как там асм использовать встроенный.
asm("mov ax,1"); //????! - а как насчёт аргументы в функцию передать, регистры сохранить, нужные
значения в них записать, выполнить прерывание
и ..... .
Вообще-то, MSVC дает тебе готовый стаб функции - тобишь настраивает стэк и ebp. Все остальное по твоему усмотрению. Получается вот такая рыба:
C:
ASM как это выглядит:
push ebp
mov ebp, esp
sub esp, <размер под локальные переменные>
//здесь пользовательский код
mov esp, ebp
pop ebp
ret
Если тебе это не нужно - объявляешь функцию как __declspec(naked).
Вот пример из MSDN:
__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()
{
}
Как выглядит вызов функции:
{
push <v2>
push <v1>
call func
mov <result>, eax
}
Ну и еще - по умолчанию VC использует _cdecl calling convention. Тобишь - аргументы передаются через стэк - результат возвращается в eax.
там и asm встроенный без заморрочек был.
Куча вопросов и даже не знаю с чего начать:
1. Почему все регистры на 'е' - начинаются,
вот не bx, а ebx например.
2. В С int - это описатель целого типа, интеджер,
а как тогда прерывание то вызывать.
3. А почему при входе в функцию, только регистр
базы сохраняется..
4............ и т. д.
Помоему я разберусь, если смогу найти
на русском языке какое - нибудь руководство
по встроенному асемблеру, самый главный вопрос
где его найти. С примерами и т. п., чтоб
на учебник похоже было.
Вот каждый раз думаю - писал бы я на паскале всё,
там и 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.
Вот каждый раз думаю - писал бы я на паскале всё,
там и asm встроенный без заморрочек был.
Куча вопросов и даже не знаю с чего начать:
1. Почему все регистры на 'е' - начинаются,
вот не bx, а ebx например.
2. В С int - это описатель целого типа, интеджер,
а как тогда прерывание то вызывать.
3. А почему при входе в функцию, только регистр
базы сохраняется..
4............ и т. д.
Помоему я разберусь, если смогу найти
на русском языке какое - нибудь руководство
по встроенному асемблеру, самый главный вопрос
где его найти. С примерами и т. п., чтоб
на учебник похоже было.
В С как и в паскале специально и существует разделение на язык высокого уровня и ассемблер.
Когда ты в С пишешь:
__asm { - то все что будет находится внутри фигурных скобок уже будет интерпретироваться компилятором как чистый ассемблерный код - но правда есть небольшие отличия. Например можно использовать такие конструкции:
{
__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 ebx
push edx
......
pop edx
pop ebx
pop eax
Ну на худой конец:
......
popad
Это же элементарно.
Это же элементарно.
Элементарно то, что знаешь.
Вы направляете человека не по верному пути, усложняя то, что я написал выше. Это не очень хорошо.
В свою очередь, задавшему вопрос, порекомендую еще раз обратиться к литературе и внимательно почитать нужные разделы (начать лучше с ассемблера).