Рекомендации по улучшению работоспособности файлов, содержащих модуль CRT из Паскаля версии 7 фирмы Borland. Version 5.01 (WIN-1251) Copyright © 22.09-16.11.1999 by Fyodor Menshikov ОГЛАВЛЕНИЕ Лицензионное соглашение Введение Признак проблемы Компиляторы, создающие такой код Компиляторы, НЕ создающие такой код Достоинства метода Противопоказания Устранение ошибки в exe-файле Непосредственно инструкции Возможные проблемы Устранение ошибки в разрабатываемых программах Введение Непосредственно инструкции Объяснение исправления exe-файлов (ассемблерные коды) Объяснение исправления tpl-файлов (структура tpl-файлов) Связаться с автором Лицензионное соглашение 1. Автор рекомендации не несёт ответственности за любой вред, нанесённый вследствие применения описанных здесь действий. 2. Разрешается свободное распространение немодифицированной версии данного документа в любой форме и на любых носителях при условии, что из распространения не извлекается выгода. 3. Запрещается распространять рекомендацию с любыми модификациями без разрешения Автора (вопросы и пожелания посылать по адресу mfv@mail.ru) Признак проблемы: На быстрых компьютерах (Pentium II и старше) программа сразу кончается с надписью Runtime error 200 at XXXX:YYYY (XXXX, YYYY - четырёхзначные шестнадцатеричные числа) Компиляторы, создающие такой код: Borland (с входящим в его состав Turbo) Pascal 7.00(от 27.10.92) и 7.01 (от 09.02.93). Компиляторы, НЕ создающие такой код: Самостоятельная версия Turbo Pascal 7.00 (без BP)от 05.10.92 Borland Pascal 7.00 от 01.10.92 В них используется другой алгоритм Delay. Он корректен на достаточно медленных машинах(486), никогда не даёт Runtime Error 200, но на быстрых компьютерах задержка там меньше расчётной. Число байт, занимаемых инициализацией и самой Delay там такое же, как и в исправляемом алгоритме, а функциональность регистров изменена, так что, при соответствующей квалификации, Вы сможете сами исправить в нём работу Delay. Достоинства метода: Если Вы будете сравнивать мой метод с чьим-нибудь аналогичного назначения, обратите внимание на следующие показатели: 1. Погрешность Delay на любом компьютере от 8088 до в миллионы и миллиарды раз быстрее Pentium II будет около трёх процентов, что в реальных задачах более чем приемлемо. Некоторые методы на быстрых компьютерах исправляют ошибку, не беспокоясь о корректной работе Delay после этого. 2.1. Длина исправленных процедур в точности равна длине исходных процедур. Это даёт возможность применять метод везде, где использовался сбойный алгоритм, в том числе и для изменения библиотек Борланд Паскаля прямо в tpl-файле без перекомпиляции любого рода. Некоторые методы требуют добавить код, т.е. увеличить файл. 2.2. Для хранения ключевых данных используются те же два байта, что и в исходном варианте. Большинству других методов или нужно больше места, или Delay на быстрых машинах искажён. 3. Вставляемый код представляет собой инструкции 8086 процессора и пойдёт на любом PC. 4. Идея алгоритма подобна соответствующей идее оригинального алгоритма Delay, т.е. при загрузке файла идёт инициализация, при обращении результатами этой инициализации пользуются. Тем самым время загрузки и любого Delay в программе точно соответствуют ожидаемому времени работы процедур. 5. Данный метод является чисто алгоритмическим, что гарантирует отсутствие побочных эффектов, в отличие от некоторых других, использующих вектора прерываний и т.п., т.е. функции, которые плохо поддерживаются современными операционными системами для ДОС-программ. 6. Метод работает для реального и защищённого режимов. 7. Ошибка 200 не может возникнуть даже в принципе. Противопоказания: Если при вызове Delay в стеке не найдётся двух-трёх десятков байт, программа сломается с выдачей сообщения "Runtime error 202 at ...". Но ситуации почти полного заполнения стека не характерны для TP/BP. Если переполнение стека всё-таки возникает, используйте любой из разработанных мной других методов. Точные цифры: на Pentium II глубина рекурсии-9(используется 18байт стека), при увеличении скорости в 2 раза стек должен содержать на 2 байта больше. Например, однопроцессорный монстр, который будет быстрее Pentium II в миллиард раз, потребует 80байт свободного стека при его характерном размере в Борланд Паскале 32kb (кстати, из некоторых соображений, никакой процессор быстрее Pentium II более чем в миллион раз быть не может). Устранение ошибки в exe-файле. 1. Найти в файле цепочку байтов b8 e4 ff 99 e8 3c 02 f7 d0 f7 d2 b9 37 00 f7 f1(real mode BP/TP) -- -- -- -- -- 56 -- -- -- -- -- -- -- -- -- --(protected mode) заменить на 48 40 d0 ec e8 35 02 fe c4 78 f6 26 3a 1d 74 f4(real mode BP/TP) -- -- -- -- -- 4f -- -- -- -- -- -- -- -- -- --(protected mode) 2. Найти в файле цепочку байтов (примерно 230h байтов после первой цепочки) 13 8e 06 44 00 33 ff 26 8a 1d a1 XX YY 33 d2 e8 05 00 заменить на (содержимое байтов XX и YY нужно скопировать на новое место) 0c a1 XX YY e8 12 00 fe cc 75 f9 e2 f4 ca 02 00 b9 37 3. Непосредственно после второй цепочки e2 f6 ca 02 00 2d 01 00 83 da 00 72 05 26 3a 1d 74 f3 заменить на 00 e8 03 00 e2 fb c3 fe c8 74 06 e8 f9 ff e8 f6 ff 40 Проблемы, возникающие при использовании рекомендации: 1. Если одинаковых цепочек несколько, скорее всего, нужно выбрать первые 2. Если цепочек нет, возможно, этот файл а) компилировался не рассматриваемыми компиляторами б) сжат программами типа LZEXE в) уже неудачно исправлен Устранение ошибки в разрабатываемых программах. Если Вы используете TP/BP для разработки новых проектов, выполнять вышеописанные указания для каждого exe-файла достаточно утомительно. Поэтому лучше исправить сам модуль CRT, входящий в комплект поставки BP. CRT для TP/real mode BP находится в turbo.tpl CRT для protected mode BP находится в tpp.tpl Внимание! Испытано только на turbo.tpl/tpp.tpl (7.00 от 27.10.92 и 7.01 от 09.02.93) 1. Найти в файле цепочку байтов b8 e4 ff 99 e8 3c 02 f7 d0 f7 d2 b9 37 00 f7 f1(turbo.tpl) -- -- -- -- -- 56 -- -- -- -- -- -- -- -- -- --(tpp.tpl) заменить на 48 40 d0 ec e8 35 02 fe c4 78 f6 26 3a 1d 74 f4(turbo.tpl) -- -- -- -- -- 4f -- -- -- -- -- -- -- -- -- --(tpp.tpl) 2. Найти в файле цепочку байтов (примерно 230h байтов после первой цепочки) 13 8e 06 00 00 33 ff 26 8a 1d a1 00 00 33 d2 e8 05 00 заменить на 0c a1 00 00 e8 12 00 fe cc 75 f9 e2 f4 ca 02 00 b9 37 3. Непосредственно после второй цепочки e2 f6 ca 02 00 2d 01 00 83 da 00 72 05 26 3a 1d 74 f3 заменить на 00 e8 03 00 e2 fb c3 fe c8 74 06 e8 f9 ff e8 f6 ff 40 4. Найти в файле цепочку байтов (после третьей цепочки) 00 d0 00 00 42 00 77 02 0b 90 08 00 00 00 7f 02(turbo.tpl) -- -- -- -- 22 -- 91 -- -- -- -- -- -- -- 99 --(tpp.tpl) заменить на(указаны только байты, подлежащие изменению) __ __ __ __ __ __ 38 00 __ __ __ __ __ __ 76 __(turbo.tpl) __ __ __ __ __ __ 38 00 __ __ __ __ __ __ 90 __(tpp.tpl) Объяснение исправления exe-файлов. (на примере real mode) (все числа шестнадцатеричные) ******************************> Исходный вариант <***************************** //Инициализация Delay 0071 8E064400 mov es,[0044] 0075 BF6C00 mov di,006C 0078 268A1D mov bl,es:[di] 007B 263A1D cmp bl,es:[di] 007E 74FB je 007B 0080 268A1D mov bl,es:[di] 0083 B8E4FF mov ax,FFE4 0086 99 cwd 0087 E83C02 call 02C6 008A F7D0 not ax 008C F7D2 not dx 008E B93700 mov cx,0037 //Вот здесь-то и было деление на ноль 0091 F7F1 div cx 0093 A36000 mov [0060],ax //Непосредственно Delay 02A8 8BDC mov bx,sp 02AA 368B4F04 mov cx,ss:[bx+04] 02AE E313 jcxz 02C3 02B0 8E064400 mov es,[0044] 02B4 33FF xor di,di 02B6 268A1D mov bl,es:[di] 02B9 A16000 mov ax,[0060] 02BC 33D2 xor dx,dx 02BE E80500 call 02C6 02C1 E2F6 loop 02B9 02C3 CA0200 retf 0002 //Подпрограмма считает число циклов до тика 02C6 2D0100 sub ax,0001 02C9 83DA00 sbb dx,0000 02CC 7205 jb 02D3 02CE 263A1D cmp bl,es:[di] 02D1 74F3 je 02C6 02D3 C3 ret ******************************************************************************* ****************************> Исправленный вариант <*************************** //Инициализация Delay 0071 8E064400 mov es,[0044] //До этого фрагмента ax = 0001 0075 BF6C00 mov di,006C 0078 268A1D mov bl,es:[di] 007B 263A1D cmp bl,es:[di] 007E 74FB je 007B 0080 268A1D mov bl,es:[di] 0083 48 dec ax 0084 40 inc ax 0085 D0EC shr ah,1 0087 E83502 call 02BF 008A FEC4 inc ah 008C 78F6 js 0084 008E 263A1D cmp bl,es:[di] 0091 74F4 je 0087 0093 A36000 mov [0060],ax //Непосредственно Delay 02A8 8BDC mov bx,sp 02AA 368B4F04 mov cx,ss:[bx+04] 02AE E30C jcxz 02BC 02B0 A16000 mov ax,[0060] 02B3 E81200 call 02C8 02B6 FECC dec ah 02B8 75F9 jne 02B3 02BA E2F4 loop 02B0 02BC CA0200 retf 0002 //Процедура вызывает (*) 37hex=55 раз 02BF B93700 mov cx,0037 02C2 E80300 call 02C8 02C5 E2FB loop 02C2 02C7 C3 ret //(*) 02C8 FEC8 dec al 02CA 7406 je 02D2 02CC E8F9FF call 02C8 02CF E8F6FF call 02C8 02D2 40 inc ax 02D3 C3 ret ******************************************************************************* Объяснение исправления tpl-файлов. tpl-файл состоит из модулей (идентичных tpu или tpp файлам), просто записанных друг за другом. Структура tpu(tpp)-файла: сначала описание интерфейсной части модуля, затем код собственно процедур модуля, затем идёт таблица, описывающая связь процедур модуля с импортируемыми модулями и использование сегмента данных для глобальных переменных. Описание интерфейсной части модуля нас не интересует - её изменять не надо. Код процедур модуля похож на код exe-файла, поэтому пункты 1-3 инструкции практически идентичны соответствующим пунктам инструкции для exe-файлов. Только некоторые адреса из ds:[...] заменены на нули. Их заполнение идёт на этапе создания exe-файла компилятором. В четвёртом пункте изменяется таблица использования сегмента данных. Она состоит из записей по 8байт. Детально в значении полей этих записей я не разобрался. Но кое-что я заметил. Первые четыре байта обозначают тип адреса, который будет встроен в код (например, это может быть ближний вызов, дальний вызов, смещение некоторой переменной - всё это может относиться как к самому модулю, так и к импортируемым). Следующие два байта означают смещение переменной модуля (возможно, импортируемого) относительно базового для данного модуля адреса в сегменте данных. Следующие (последние) два байта имеют отношение к расположению кода, куда нужно вставить данную информацию. Полностью связи я не понял, но в последовательных записях, относящихся к одной процедуре, разность реального значения регистра ip и значения данных байтов является константой. Кроме того, последовательность этих записей соответствует последовательности мест, куда их подставляют. Проблема возникала с оператором "mov ax,[0060]", передвинутым в процедуре Delay и с оператором "mov es,[0044]", исчезнувшим из той же процедуры. Первую проблему я решил уменьшением значения последнего поля записи на 9 - число байтов, соответствующее перемещению данного оператора в моём варианте по сравнению с исходным. Вторую проблему я решил, изменив последнее поле записи на значение, равное соответствующему полю записи, указывающей на другой оператор "mov es,[0044]". Таким образом, на тот оператор стало два указателя, а неиспользованных не осталось. Написать автору - mfv@mail.ru На страничку автора - /pages/pm98/mfv/index.htm