Придумал тут такую фишку. Может кому интересно будет. Думал над организацией регистровых вызовов. Таких, чтобы, в процедуре можно было указывать те параметры которые, нужно помещать не в стэк, а в соответствующие регистры. А при вызове stdcall этой процедуры компилятор сам бы корректно генерировал необходимый код. Например: proc CheckPoint Param1, Param2:ecx, Param3, Param4:edx mov eax, [Param1] mov ebx, Param2 mov esi, [Param3] mov edi, Param4 ret endp proc Main stdcall CheckPoint, 1, 2, 3, 4 ret endp чтобы в после раскрытия получился бы такой код proc Main mov edx, 4 push 3 mov ecx, 2 push 1 call CheckPoint retn 8 endp а внутри процедуры код получился бы такой proc CheckPoint Param1, Param2:ecx, Param3, Param4:edx mov eax, [Param1] mov ebx, ecx mov esi, [Param3] mov edi, edx ret endp
Вот что у меня получилось ;Надстройка над стандартным макросом proc ;Обеспечивает поддержку регистрового объявления аргументов ;Например: proc Test Arg1, Arg2:edx,Arg3, Arg4:ecx ;Здесь Arg1, и Arg3 - передаются через стэк ;Arg2 - edx ;Arg4 - ecx macro proc [args] { common match name params, args> \{ define@call name,<params \} } ;Этот макрос объявляет регистровые переменные, анализируя списко параметров процедуры macro DefineRegVariables@proc [args] { forward match argname:reg, args \{ argname equ reg \} restore reg } ;Этот макрос определяет все переменные процедуры ;В него параметры уже передаются корректно. Имя процедуры - один параметр, ;а аргументы процедру другой параметр macro define@call name, statement { common define@prototype name,<statement> DefineRegVariables@proc statement macro resetargs@proc \{ resetargs <name#.ArgNameList> \} define@proc name, <name#.ParamList> } ;Этот макрос определяет списки аргументов процедры. ;Список Параметров - Для хранения списка параметров, который будет передан оригинальному макросу proc ;Списов Типов аргументов - Для возможности корректного вызова процедуры ;Список имен аргументов - Для объявления/удаления необходимых переменных внутри процедуры ;Все списки заканчиваются символом @@. Я ТАК ЗАХОТЕЛ!!! macro define@prototype name, statement { common TConstList name#.ParamList TConstList name#.ArgTypeList TConstList name#.ArgNameList setargs@proc name, statement name#.ArgTypeList.Append @@ } ;Этот макрос заполняет списки, объявленные в макросе define@prototype ;ParamList заполняется списком параметров, которые будут переданы оригинальному макросу proc ;ArgTypeList заполняется типами аргументов. Если аргумент регистровый то, именем регистра, а если стэковый ;,то значением StackArg ;ArgName заполняется списком имен регистровых переменных macro setargs@proc name, [arg] { forward macro name#.PushParam \{ name#.ParamList.Append arg name#.ArgTypeList.ReverseAppend <StackArg> \} match argname:reg, arg \{ name#.ArgTypeList.ReverseAppend reg name#.ArgNameList.Append argname purge name#.PushParam macro name#.PushParam \\{ \\} \} name#.PushParam purge name#.PushParam } ;Этот макрос удалят объявленные переменные. macro resetargs arg { purge resetargs@proc match all,arg \{ restore all \} } ;Этот макрос завершает процедуру чистит внутренние переменные и вызывает оригинальный макрос endp macro endp { resetargs@proc endp }
Теперь для того чтобы корректно вызывать данный код. Можно модифицировать процедуру stdcall ;Этот макрос формирует код для корректного вызова процедур в Stdcall ;Поддерживаются регистровые вызовы macro stdcall name,[arg] { common CallList equ name#.ArgTypeList reverse match Param=,ResultList, CallList \{ if Param in Registers argmov Param, arg else if (~Param eq @) pushd arg end if CallList equ ResultList \} common call name }
К сожалению данная штука работает только если процедура объявлена непосредственно перед ее использованием. Связано это с тем что если вызвать процедуру до ее объявления, то списки не будут определены Поэтому можно добавить еще вот такую штуку ;Объявляет прототип процедуры. Это позволяет осуществлять корректный вызов процедур до ее объявления macro prototype [args] { common match name params, args> \{ define@prototype name,<params \} } Такой код prototype CheckPoint Param1, Param2:ecx, Param3, Param4:edx proc Main stdcall 1,2,3,4 ret endp proc CheckPoint Param1, Param2:ecx, Param3, Param4:edx ... ret endp Будет корректно скомпилирован
Great Я ничего не знаю про fastcall в Fasm. И примеров с его использованием не видел. По-крайне мере поиск по словам в его Файлах ничего не дал Может версия старая?
Файл proc64.inc Код (Text): macro fastcall proc,[arg] { common local stackspace,argscount,counter if argscount and 1 stackspace = (argscount+1)*8 else stackspace = argscount*8 end if counter = 0 if stackspace sub rsp,stackspace end if forward if ~ arg eq counter = counter + 1 if counter <= 4 if arg eqtype 0 size@arg = 8 else if arg eqtype byte 0 virtual at 0 mov [rbx],arg local opcode load opcode byte from 0 if opcode and 0F8h = 48h size@arg = 8 else if opcode = 66h size@arg = 2 else if opcode = 0C7h size@arg = 4 else size@arg = 1 end if end virtual else virtual at 0 inc arg local opcode load opcode byte from 0 if opcode = 67h load opcode byte from 1 end if if opcode and 0F8h = 48h size@arg = 8 else if opcode = 66h size@arg = 2 else if opcode = 0FFh size@arg = 4 else size@arg = 1 end if end virtual end if if counter = 1 if size@arg = 8 if ~ arg eq rcx mov rcx,arg end if else if size@arg = 4 if ~ arg eq ecx mov ecx,arg end if else if size@arg = 2 if ~ arg eq cx mov cx,arg end if else if size@arg = 1 if ~ arg eq cl mov cl,arg end if end if else if counter = 2 if size@arg = 8 if ~ arg eq rdx mov rdx,arg end if else if size@arg = 4 if ~ arg eq edx mov edx,arg end if else if size@arg = 2 if ~ arg eq dx mov dx,arg end if else if size@arg = 1 if ~ arg eq dl mov dl,arg end if end if else if counter = 3 if size@arg = 8 if ~ arg eq r8 mov r8,arg end if else if size@arg = 4 if ~ arg eq r8d mov r8d,arg end if else if size@arg = 2 if ~ arg eq r8w mov r8w,arg end if else if size@arg = 1 if ~ arg eq r8b mov r8b,arg end if end if else if counter = 4 if size@arg = 8 if ~ arg eq r9 mov r9,arg end if else if size@arg = 4 if ~ arg eq r9d mov r9d,arg end if else if size@arg = 2 if ~ arg eq r9w mov r9w,arg end if else if size@arg = 1 if ~ arg eq r9b mov r9b,arg end if end if end if else if arg eqtype [0] | arg eqtype byte [0] virtual at 0 inc arg local opcode load opcode byte from 0 end virtual if opcode and 0F8h = 48h mov rax,arg mov [rsp+(counter-1)*8],rax else if opcode = 66h mov ax,arg mov [rsp+(counter-1)*8],ax else if opcode = 0FFh mov eax,arg mov [rsp+(counter-1)*8],eax else mov al,arg mov [rsp+(counter-1)*8],al end if else if arg eqtype 0 mov qword [rsp+(counter-1)*8],arg else mov [rsp+(counter-1)*8],arg end if end if end if common argscount = counter call proc if stackspace add rsp,stackspace end if }
n0name Да я тоже вчера у себя его нашел. Но он только в 64 битной реализации. Да и он же совсем по другому работает. В данном макросе если у тебя параметров <=4, то он использует регистры. А у меня макрос позволяет самому задавать регистры на какие хочешь параметры. К примеру есть объект, наследованный от кучи других объектов, и каждый наследник вызывает какие-то свои функции предка. Объекты очень удобно адресовать регистром. При этом в методах объекта, регистр, который указывает на сам объект очень редко изменяется. В методах объектов первый параметр указывает на сам собственно объект. Вот его, то и можно назначить тем или иным регистром. А компилятор уже сам адаптирует код для корректного вызова этих самых методов. И если в больших и громоздких методах роль такой оптимизации не так заметна, то в маленьких объектах типа двусвязных списках, деревьях графах, код получается намного меньше и эффективней.
это и есть fastcall а насчёт сабжа, хз, возможно, однако оптимизация работы с такими структурами это разовое дело, и можно всё это сделать вручную.
Проще так (по мотивам какой-то реализации ТЕА): Код (Text): arg1 equ eax arg2 equ ebx arg3 equ ecx ... lea arg1,[some_data] call func ; или любой другой способ вызова. func работает с eax,ebx,ecx ; какой-то код, регистры сменились arg1 equ esi lea arg1,[x] call func ; func работает с esi,ebx,ecx ... func: cmp arg1,1 add arg2,arg1 ...