Рекомендации по улучшению работоспособности файлов,
           содержащих модуль 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