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 | 
	
		
	 |