Sav
Posts: 2180
|
Частенько в коде Героев встречаются команды типа:
lea eax, [eax + 8*eax]
.
Я правильно понимаю, что они нужны для упрощения написания вычислений (вместо:
push ebx
push edx
mov ebx, eax
mov edx, 0
mov eax, 8
mul ebx
add eax, ebx
pop edx
pop ebx
)?
При выполнении есть ли разница между этими командами?
И такой тупой вопрос: можно ли в Иде изменять команды не через Hex-view и как?
|
|
04.10.2010 01:15 |
|
etoprostoya
Posts: 1809
|
Да, примерно так и есть. Разница очевидна - с командой LEA ты не изменяешь другие регистры и не работаешь со стеком.
|
|
04.10.2010 01:39 |
|
Sav
Posts: 2180
|
А когда процессор одинаково быстро выполняет эту lea-команду и "нормальный" код?
|
|
04.10.2010 01:48 |
|
etoprostoya
Posts: 1809
|
LEA-команды выполняются за один такт обычно или даже две-три такие команды за такт, а "нормальный код", который ты привёл, во много раз дольше, в лучшем случае в пять-десять раз.
|
|
04.10.2010 16:32 |
|
Sav
Posts: 2180
|
Но процессору всё равно же надо вычислить, чему равно, eax+8*eax, он что, как-то по-особому вычисляет это в lea-команде?
|
|
04.10.2010 17:49 |
|
etoprostoya
Posts: 1809
|
Именно по-особому.
К тому же в твоём "нормальном" примере изменяются три регистра, а значит их нельзя использовать в других командах, что замедляет выполнение программы (регистры-то часто используемые). Используется стек, то есть идёт обращение к кеш-памяти, на что отводятся дополнительные такты процессора, а в случае с полным кешем, этот кеш нужно выгружать в оперативную память (десятки тактов). Используются три подряд команды mov, что полностью блокирует на несколько тактов блок ввода-вывода, хотя это и не важно, так как дальше идёт длительная команда MUL. Эта команда MUL сама по себе плохая, особенно если в твоём случае блокирует использование сразу трёх регистров и все конвееры процессора или ядра останавливаются, ожидая завершения этой команды.
А команда LEA специально оптимизирована для вычисления формул вида
REG = REG+REG*2N+CONST.
Современные процессоры вычисляют за такт несколько таких команд.
|
|
04.10.2010 19:38 |
|
Sav
Posts: 2180
|
Тогда понятно и то, почему там eax+8*eax вместо 9*eax.
Спасибо.
|
|
04.10.2010 20:08 |
|
ZVS
Posts: 500
|
Если память мне не изменяет, то множители (2^N) могут быть только 2, 4 и 8. Этот модификатор команды появился только с i386. Ну и понятно, что умножение на 2^N суть сдвиг на N битов влево. Т.е. *9 и *8 две большие разницы (должно быть). Хотя современные процессоры и умножают уже за 1-2 такта.
|
|
05.10.2010 00:51 |
|
etoprostoya
Posts: 1809
|
(05.10.2010 00:51)ZVS Wrote: Если память мне не изменяет, то множители (2^N) могут быть только 2, 4 и 8.
Ну, может быть и 1. То есть РЕГ = РЕГ + РЕГ. Ноль не считается.
Не знаю как самые-самые последние процессоры, но пару лет назад умножение было не меньше 3-4 тактов. Правда могло проходить сразу два умножения параллельно. Это про 32-разрядное умножение.
|
|
05.10.2010 14:03 |
|
ZVS
Posts: 500
|
РЕГ+РЕГ - это стандартный модификатор, который был еще в 80x86, а множители появились, начиная с i386 специально для быстрого обращения к элементам массивов.
|
|
05.10.2010 14:56 |
|
Sav
Posts: 2180
|
Может, кому-нибудь пригодится:
по адресу [6919480]+1170n+137020, где n - номер героя,
находится байт, в котором хранится 1, если герой спит (кнопка "Усыпить/разбудить героя" на карте приключений), иначе - 0.
(This post was last modified: 16.10.2010 17:02 by Sav.)
|
|
16.10.2010 17:02 |
|
SAG
Posts: 173
|
(01.09.2010 15:15)MOP Wrote: Вот длл с более подробным (откомментированным) исходником. Создаёт триггер на лечение Палаткой.
большая просьба прислать мне почтой на randommaps (@) yandex (.) ru. ибо период полураспада файлообменников очень мал и сцылка уже нерабочая
|
|
24.10.2010 12:36 |
|
Sav
Posts: 2180
|
Вот исходник:
LIBRARY Tent;
{!INFO
MODULENAME = 'Tent'
VERSION = '1.0'
AUTHOR = 'Master Of Puppets'
}
USES Win, Utils, SysUtils, VPUtils;
//PROCEDURE HookCode(P: POINTER; NewAddr: POINTER; UseCall: BOOLEAN); external 'angel' name 'HookCode';
CONST
(* HookCode constants *)
C_HOOKTYPE_JUMP = FALSE;
C_HOOKTYPE_CALL = TRUE;
C_OPCODE_JUMP = $E9;
C_OPCODE_CALL = $E8;
C_UNIHOOK_SIZE = 5;
TYPE
THookRec = RECORD Opcode: BYTE; Ofs: INTEGER;
END; // .record THookRec
VAR
Temp: INTEGER;
PROCEDURE WriteAtCode(P: POINTER; Buf: POINTER; Count: INTEGER);
BEGIN
Win.VirtualProtect(P, Count, PAGE_READWRITE, @Temp);
Win.CopyMemory(P, Buf, Count);
Win.VirtualProtect(P, Count, Temp, NIL);
END; // .procedure WriteAtCode
PROCEDURE HookCode(P: POINTER; NewAddr: POINTER; UseCall: BOOLEAN);
VAR
HookRec: THookRec;
BEGIN
IF UseCall THEN BEGIN HookRec.Opcode:=C_OPCODE_CALL;
END // .if
ELSE BEGIN HookRec.Opcode:=C_OPCODE_JUMP;
END; // .else
HookRec.Ofs:=INTEGER(NewAddr)-INTEGER(P)-C_UNIHOOK_SIZE;
WriteAtCode(P, @HookRec, 5);
END; // .procedure HookCode
PROCEDURE HOOK_tent; ASSEMBLER; {$FRAME-}
ASM
MOV EAX, $50C7C0
CALL EAX //вызываем функцию вычисления HP-очков, которую мы затёрли своим хуком.
MOV DWORD PTR DS:[$91DA38],-1 //заносим в адрес переменной x1 значение -1 - на случай, если мы не хотим вообще ничего менять в принципе лечения
MOV [$91DA3C], EAX //заносим в адрес переменной x2 исходное значение кол-ва хит-пойнтов, для подробностей и возможных вычислений
PUSHAD //сохраняем регистры
PUSH 29500 //номер ERM-функции. Меняйте на любой доступный.
MOV EAX, $74CE30
CALL EAX //вызываем C_FUNC_ZVS_CALLFU
ADD ESP, 4
POPAD //выталкиваем регистры
CMP DWORD PTR DS:[$91DA38],-1 //проверяем, изменил ли скриптописец кол-во очков HP для лечения
JE @@Default //если не изменил - продолжить код без изменения
MOV EAX,DWORD PTR DS:[$91DA38] // заменить значение очков на значение из x1
@@Default:
PUSH $478538 //адрес возврата в процедуру
END;
BEGIN
HookCode(POINTER($478533), @HOOK_tent, C_HOOKTYPE_JUMP);
END.
Я уже отправил SAG-у файл на почту.
(This post was last modified: 24.10.2010 16:17 by Sav.)
|
|
24.10.2010 15:50 |
|
GhostManSD
Posts: 1054
|
Подскажите, пожалуйста, как затереть/вызвать определенное диалоговое окно (алгоритм). Например, дабы вместо окна таверны вызывалось окно рынка.
Κακῆς ἀπ' ἀρχῆς γίγνεται τέλος κακόν.
|
|
24.10.2010 21:48 |
|
Дьякон
Posts: 395
|
Элементарно. Менять таблицу jmp-ов для case - а идентификатора зданий.
Страус труп (с) Бьерн
|
|
24.10.2010 22:57 |
|