Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

Непонятки с адресацией


Страницы:   1 2

DMitryFox ©   (2005-08-24 15:31:00) 

Люди подскажите, почему может не работать нижеследующий код и как можно исправить ситуацию:

 TForm1 = class(TForm)
   Button1: TButton;
   procedure Button1Click(Sender: TObject);
   procedure FormCreate(Sender: TObject);
 private
   ptest: pointer;
 end;

.....

procedure TForm1.FormCreate(Sender: TObject);
begin
 ptest := VirtualAlloc(0, $100, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
 asm
         xorps   xmm0, xmm0
         mov     [eax], ptest
         movapd  [eax], xmm0 // ERROR !!! Access Violation!!!
 end;
end;


Причем, если  
ptest := VirtualAlloc.....
Перенести в процедуру Button1Click, то все работает нормально.
Иными словами, мне необходимо при создании объекта навыделять памяти через VirtualAlloc для дальнейшего использования в разных методах...

Спасибо


DMitryFox   (2005-08-24 19:28:00) [1]

Всем спасбо, вроде во всем, что интерисовало разобрался


DMitryFox   (2005-08-24 18:53:00) [2]


> alpet ©   (24.08.05 18:39) [25]
>
> А где HeapAlloc? :)

А что это ? сорри, чайник я...
пойду срочно Фаронова почитаю :)


alpet   (2005-08-24 18:39:00) [3]

DMitryFox   (24.08.05 18:17) [24]

А где HeapAlloc? :)


DMitryFox   (2005-08-24 18:17:00) [4]


> нафига тебе VirtualAlloc, если ты тоже самое можешь реализовать
> НЕ выходя за рамки ОР ?
>
> если ты НЕ в состоянии расчитать требуемые в дан.случае
> для GetMem()параметры, то нехрена тебе делать в WinAPI.

Так что-ли ?
 
   ptest := GetMemory($100);
   integer(ptest) := ((integer(ptest) + 15) div 16) * 16;

или наверное понятнее будет:
   ptest := GetMemory($100);
   ptest := pointer(((integer(ptest) + 15) div 16) * 16);

не, мне больше нравится так:

   ptest := GetMemory($100);
   while (integer(ptest) mod 16) <> 0 do
     inc(integer(ptest));

;)


alpet   (2005-08-24 18:13:00) [5]

DMitryFox   (24.08.05 17:53) [22]

Переменная  self в стеке присутствует в случае использования:

procedure TForm1.Func1;
begin
asm
 mov ebx, self   // mov ebx, [ebp - 4]
 ...  
end;
end;

и в eax, если тело функции состоит целиком из inline-assembler.

procedure TForm1.Func1;
asm
mov ebx, self
end;


потому что
beginкомпилируется в код пролога функции:
push   ebp              ;; save ebp
mov    ebp, esp         ;; set to stack ptr
push   ecx              ;; allocate 4 bytes in stack
mov    [ebp - 4], eax   ;; save eax to local var "self"

Но уже плюс что разобрался ;)


DMitryFox   (2005-08-24 17:53:00) [6]


> Есть такой указатель self, что на данные твоего обьекта
> указывает. При вызове метода класса он передается, как ни
> странно в eax. Обращение [self.ptest] (или self.ptest) компилятор
> преобразует соответственно в [eax + ptest]. Поэтому ты должен
> беречь содержимое этого регистра, а не травмировать.

Можно смело травмировать eax:
         
          mov     ebx, self
            mov     ebx, [ebp-4]
          mov     ecx, TForm1([ebx]).ptest
            mov     ecx, [ebx+$00002fc]

Судя по коду указатель на self можно достать со стека


alpet   (2005-08-24 17:32:00) [7]

Есть такой указатель self, что на данные твоего обьекта указывает. При вызове метода класса он передается, как ни странно в eax. Обращение [self.ptest] (или self.ptest) компилятор преобразует соответственно в [eax + ptest]. Поэтому ты должен беречь содержимое этого регистра, а не травмировать.


{ ptest = $B100000  }
mov     eax, [eax+ptest]// eax = self
   mov     eax, [eax + 4]  // eax = ptest
mov     ecx, self.ptest   // eax is not self !!!
   mov     ecx, [eax + 4]  // ecx =  ???? PDWORD (ecx)^ may be 0
   mov     edx, [ecx]  // cause av


DMitryFox   (2005-08-24 17:23:00) [8]


> procedure TClass1.Test;
> asm
> push      ebx
> push      edx
> mov       ebx, ptest
> mov       edx, [ptest] ; cause av
> mov       eax, [self.ptest] ; yes is right!
> test      eax, eax
> pop       edx
> pop       ebx
> end;

Работает - все круто.
пробую:
procedure TForm1.FormCreate(Sender: TObject);
begin
 ptest := VirtualAlloc(0, $100, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
 asm
         mov     ecx, self.ptest
         xorps   xmm0, xmm0
         movapd  [ecx], xmm0 // Access violation !!!
 end;

end;

Причем если ptest объявить в процедуре, то все работает...
Пробую дальше:
 asm
         mov     eax, [ebp-4]
         mov     eax, [eax+ptest]
         mov     ecx, self.ptest
         movapd  [eax], xmm0 // yes is right ;)
         movapd  [ecx], xmm0 // Access violation...          
 end;

значения еах и есх - разные, ptest объявлена в классе...


icWasya   (2005-08-24 17:23:00) [9]

По умолчанию напрямую можно обращаться только к глобальным переменным и к локальным данным и параметрам текущей процедуры. К полям объекта доступ идёт через косвенную адресацию.

Скорее всего везде, где есть обращение к полям TForm1 нужно вместо


asm
   ...
    mov     eax, ptest
   ...
end;

писать

asm
   ...
    mov     ebx, self
    mov     eax, TForm1([ebx]).ptest; //  что бы точно понимать, что же мы делаем
   ...
end;


Кроме того, надо помнить, что по умолчанию несколько параметров передаются через регистры. В данном случае
procedure TForm1.Button1Click(Sender: TObject);
- метод с одним параметром - Self=EAX, Sender=EDX;
При входе в процедуру скорее всего эти  регистры будут сохранены, но оптимизатор может думать по другому - если не найдёт явного использования Self, то EAX сохраняться не будет, и придётся это делать самому, например, используя не ячейку в стеке, а какой нибудь регистр.


procedure TForm1.Button1Click(Sender: TObject);
asm
   mov ebx,eax; // сохраняем

   xorps   xmm0, xmm0
   //mov     [eax], ptest
    mov     eax, TForm1([ebx]).ptest;

    movapd  [eax], xmm0 //
end;


Digitman   (2005-08-24 17:20:00) [10]


> Нафига мне это делать, если я могу спокойно пользоваться
> VirtualAlloc ?


нафига тебе VirtualAlloc, если ты тоже самое можешь реализовать НЕ выходя за рамки ОР ?

если ты НЕ в состоянии расчитать требуемые в дан.случае для GetMem()параметры, то нехрена тебе делать в WinAPI.


alpet   (2005-08-24 16:58:00) [11]

DMitryFox   (24.08.05 16:52) [15]

program temp;
uses Windows;
type TClass1 = class
public
ptest: Pointer;
constructor Create;
destructor Destroy; override;
procedure  Test;
end;

{ TClass1 }

constructor TClass1.Create;
begin
ptest := VirtualAlloc (nil, 4096, MEM_COMMIT, PAGE_READWRITE);
end;

destructor TClass1.Destroy;
begin
VirtualFree (ptest, 4096, MEM_DECOMMIT);
end;

procedure TClass1.Test;
asm
push      ebx
push      edx
mov       ebx, ptest
mov       edx, [ptest] ; cause av
mov       eax, [self.ptest] ; yes is right!
test      eax, eax
pop       edx
pop       ebx
end;

var c: TClass1;
begin
c := TClass1.Create;
c.Test;
c.Free;
end.


Compiler results:

mov  ebx, ptest
mov  ebx, $00000004
mov edx, [ptest] ; cause av
mov  edx, [$00000004]
mov eax, [self.ptest] ; yes is right!
mov  eax, [eax + $00000004]
....


DMitryFox   (2005-08-24 16:54:00) [12]


> Конечно может. Этот "кто-то" называется отладчик.

Он может ответить на вопрос, почему вместо одной команды
mov eax, ptestя должен использовать наворот
mov eax, [bp - 4]
mov eax, [eax + ptest]

что за адрес содержится в ptest ?
что лежит на стеке-4 ?


DMitryFox   (2005-08-24 16:52:00) [13]


> не вижу проблем.
>
> - выдели с пом. GetMem() блок памяти размером не меньшим
> чем (n + 1) * 16байт
>
> - для полученного адреса предоставленного тебе менеджером
> памяти блока расчитай/найди первый же адрес кратный 16-ти

Нафига мне это делать, если я могу спокойно пользоваться VirtualAlloc ? что и делаю... вопрос в другом, см первый пост.
Почему я из асма в другом методе не могу достучаться напрямую до указателя ?


alpet   (2005-08-24 16:50:00) [14]

Кстати - при использовании VirtualAlloc нет смысла выделять меньше страницы памяти (4Кбайт).


Digitman   (2005-08-24 16:48:00) [15]

не вижу проблем.

- выдели с пом. GetMem() блок памяти размером не меньшим чем (n + 1) * 16байт

- для полученного адреса предоставленного тебе менеджером памяти блока расчитай/найди первый же адрес кратный 16-ти


alpet   (2005-08-24 16:48:00) [16]

DMitryFox   (24.08.05 16:34) [11]

Попробуй посмотреть в Watch какое значение получается в регистре eax,  и насколько оно отличается от ptest.

procedure TForm1.Set1;
asm
mov       eax, [self.ptest]
mov       dword ptr [eax], 1
end;


DMitryFox   (2005-08-24 16:34:00) [17]

I>
> выравнивание на границу слова КАКОЙ разрядности тебе требуется
> ?!

SSE требует для работы выравнивание данных на 16 байт - что тут непонятного...


Игорь Шевченко   (2005-08-24 16:33:00) [18]


> Может кто-нибуть пояснить, плиз ?


Конечно может. Этот "кто-то" называется отладчик.


Digitman   (2005-08-24 16:30:00) [19]


> {$ALIGN 16} нужен


не надо цитировать "умные слова" !

выравнивание на границу слова КАКОЙ разрядности тебе требуется ?!


DMitryFox   (2005-08-24 16:24:00) [20]


> эт чего такое ? что за галиматья ?
>
> говори по-русски - выравнивание на границу "слова" КАКОЙ
> разрядности тебе нужно ? 32 ? 64 ?

{$ALIGN 16} нужен.
но в D7 такого нет...


Страницы:   1 2

Наверх

Титульная страница Поиск, карта сайта Написать письмо