Здравствуйте, Sergey, Вы писали:
S>Выкидывать-то зачем? Пусть валяется, никому не помешает.
Скорее всего "выкинуть" — изменить ESP
Вобщем, так работает (запускала в Wnidows 2000 SP4, хотя имхо должно работать везде на x86, вплодь до DOS-а, только регистры поменять на 16-ти разрядные..):
#include <stdio.h>
#include <malloc.h>
int __cdecl f1(char* str, ...)
{
// до начала выполнения стек выглядит так (сверху вниз):
//
// параметры
// адрес возврата (слово)
// -----------------
// свободно
//
// esp содержит указатель на начало последнего
// слова в стеке, т.е. на начало адреса возврата
//
// пролог функции:
// push ebp
// mov ebp, esp
//
// теперь стек такой:
//
// параметры
// адрес возврата (слово)
// регистр ebp (слово)
// -----------------
// свободно
//
// а ebp указывает на начало сохранённого регистра ebp
//
// далее граница стека (видимо, из дебажных соображений?) сдвигается
// на 0x48 байт, в эту область пихаются локальные переменные, остаток
// заполняется 0xCCCC, а после этой области сохраняются регистры
//
// итого, стек выглядит так:
//
// параметры
// адрес возврата (слово)
// регистр ebp (слово)
// локальные перемнные
// пустота с 0xCC
// сохранённые регистры
// -----------------
// свободно
//
// В нормальном оторажении (адреса снизу вверх):
//
// свободно
// -----------------------
// esp -> сохранённые регистры
// пустота с 0xCC
// локальные пременные
// ebp -> регистр ebp (слово)
// адрес возврата (слово)
// параметры
//
// значит, интересная (копируемая) область начинается с esp и идет до ebp + 8
void* stack_copy = NULL;
int stack_len = 0;
//
// Вычисление длинны копируемой области
//
__asm
{
mov stack_len, ebp
sub stack_len, esp
}
stack_len += 2 * sizeof(int); // + ebp + адрес возврата
// Выделяем память
stack_copy = (char*)malloc(stack_len);
__asm
{
// параметры movsb:
// es::esi - строка источник
// es::edi - строка приёмник
// ecx - длина
//
// Копируем стек
mov ecx, stack_len
mov esi, esp
mov edi, stack_copy
push ss
pop es
rep movsb
// длину области памяти и её адрес нужно хранить в регистрах,
// иначе f2 их испортит. Только не в eax, он испльзуется для
// возвращения результата. ecx и edx тоже портятся.. Вообще,
// во всех (пяти) просмотренных прологах функций почему-то
// сохраняются только ebp, ebx, esi и edi. Это что ли
// соглашение такое? Никому нельзя верить..
mov ebx, stack_len
mov esi, stack_copy
// Очищаем стек
add esp, ebx
// вызываем scanf. Поскольку она тоже __cdecl, парамтры
// для неё передаются гарантированно в том же порядке
// и она не очищает стек.
call scanf
// Возвращаем стек на место
sub esp, ebx
// Копируем всё обратно
// mov ecx, stack_len - сделано заранее
// mov esi, stack_copy - сделано заранее
mov ecx, ebx
mov edi, esp
rep movsb
}
free(stack_copy);
return 0;
}
void main()
{
int a, b;
printf("enter a and b\n");
f1("%d%d", &a, &b);
printf("a = %d b = %d\n", a, b);
}