Д. Правиков Замедление работы программ Аннотация: В статье описаны три способа искусственного замедления работы программ. Одним из достоинств программ всегда являлась высокая скорость их выполнения, причем как идеал рассматривался реальный масштаб времени. Тем не менее иногда, особенно при использовании старого программного обеспечения на новых мощных компьютерах, бывает необходимо искусственно замедлить скорость выполнения программ, как правило, игровых, создатели которых не рассчитывали на столь быстрый и значительный прогресс вычислительной техники. Для решения этой проблемы можно предложить три способа. Первый способ основан на непосредственном управлении скоростью центрального процессора на машинах Compaq 386. Функция 0F0h прерывания 16h позволяет варьировать скорость процессора, используя следующие подфункции: AL 00h - установить эквивалент 6 МГц 80286; 01h - установить эквивалент 8 МГц 80286; 02h - установить эквивалент 16 МГц; 03h - переключение между эквивалентом 8 МГц и скоростью, задаваемой переключателями на системной плате; 08h - полные 16 МГц, за исключением эквивалента 8 МГц при доступе к гибким дискам; 09h - непосредственное задание скорости; CX - номер скорости от 1 до 50. К сожалению, данный способ ограничен только одним типом машин, поэтому не годится для широкого применения. Более общими являются оставшиеся два способа. Один из этих способов основан на обработке прерывания от таймера, инициируемого 18,2 раза в секунду. Это аппаратное прерывание делит время работы центрального процессора на кванты, причем меньшая часть кванта используется для обработки прерывания от таймера, а оставшаяся большая часть - для работы системных и прикладных программ. Идея замедления в этом случае очень проста. Увеличение доли времени на обработку прерывания от таймера приводит к уменьшению доли на полезную работу. В настоящее время существует целый ряд программ замедления, основанных на этом принципе. Гораздо более интересным способом, с точки зрения системного программирования, является замедление c помощью пошагового выполнения программ. Принцип замедления в этом случае основан на том, что при взведенном T флаге центрального процессора после выполнения каждой команды программы генерируется прерывание, обработка которого позволяет замедлить работу программы. Говоря другими словами, программа замедляется оттого, что она выполняется как бы под отладчиком. В листинге приведен исходный текст программы, позволяющей загрузить определенный COM файл, выполнить его в пошаговом режиме и вернуть управление в основную программу. В целом данная программа довольно близка к примеру в [1], иллюстрирующему запуск дочернего процесса, но вместе с тем имеет ряд отличий, на которые хотелось бы обратить ваше внимание. Основное отличие заключается в том, что вместо подфункции "загрузить и выполнить" (AL=0) используется другая подфункция (AL=1), которую можно охарактеризовать как "загрузить, но не выполнить". Вследствие использования этой подфункции приходится дополнительно решать задачи оформления адреса возврата и определения точки входа в дочернюю программу. Оформление адреса возврата осуществляется следующим образом. В PSP (program segment prefics) дочерней программы, сегментный адрес которого можно получить с помощью функции 62h DOS (обратите внимание: эта функция возвращает сегментый адрес PSP последней загруженной а не текущей программы), адрес завершения корректируется так, чтобы он указывал на нужную точку в родительской программе. Этого достаточно для того, чтобы по завершении дочернего процесса управление вернулось в родительский процесс. Определение точки входа в дочернюю программу описано в [2] следующим образом. Перед вызовом подфункции "загрузить, но не выполнить" в регистры ES:BX помещают адрес блока параметров (подробнее назначение и содержимое этого блока см. [1]). После выполнения функции в блок параметров помещается адрес точки входа в программу, а регистры ES:BX должны указывать опять на этот блок. В действительности же они на него не указывают, поэтому, чтобы получить адрес точки входа в программу, необходимо регистр BX снова установить на блок параметров. Имея адрес точки входа, нетрудно передать программе управление. Для этого сначала переустанавливаются регистры стека, а затем в новый стек помещаются сначала флаги, затем сегмент и смещение точки входа, после чего дается команда возврата из прерывания. Если в стеке модифицировать слово флагов так, чтобы на месте T флага стояла 1, то дочерняя программа будет выполняться в пошаговом режиме. Единственная тонкость в этом месте - надлежащим образом оформить сегментные регистры ES и DS. В заключение хотелось бы сделать несколько замечаний. Приведенная в приложении программа максимально упрощена, но вполне достаточна, чтобы замедлить до нормального уровня популярную игру Digger на машинах с 286 процессором. Дальнейшее развитие этой программы можно продолжить в следующих направлениях. Во-первых, добавить обработку 01h прерывания (то есть увеличить время выполнения каждой команды) для значительного замедления на более мощных машинах. В приводимом варианте считается, что вектор 01h указывает на команду iret. Во-вторых, следует ввести обработку командной строки, и в-третьих, чтобы сделать программу более универсальной, можно добавить небольшую ветку для правильного оформления сегментных регистров при загрузке EXE файлов. Литература 1. Р.Джордейн. Справочник программиста персональных компьютеров типа IBM PC, XT и AT. - М.: Финансы и статистика, 1992. - С.47-51. 2. Dave Williams. Programmer's Technical Reference for MS-DOS and IBM PC. 1989. Листинг ; Программа замедления игры Digger ; .MODEL SMALL .STACK 400h .DATA filename db 'digger.com',0 parameters dw 20 dup(0) keep_ss dw ? keep_sp dw ? .CODE Start: mov ax,@data mov ds,ax ;-------------------------------------- Перераспределение памяти mov bx,ZSEG mov ax,es sub bx,ax mov ah,4Ah int 21h push ds pop es mov bx,OFFSET parameters ; BX -> блок параметров ;-------------------------------------- Сохранение указателя стека mov keep_ss,ss mov keep_sp,sp mov dx,OFFSET filename ; DX -> имя файла mov ax,4B01h ; Загрузка, но не выполнение int 21h ;-------------------------------------- Установка адеса возврата mov ah,62h int 21h mov ds,bx lea bx,Continue mov ds:[0Ah],bx mov ds:[12h],cs mov bx,OFFSET parameters ;-------------------------------------- Переустановка стека mov ss,es:[bx+10h] mov sp,es:[bx+0Eh] ;-------------------------------------- Установка T флага pushf pop ax or ax,100h push ax push es:[bx+14h] push es:[bx+12h] ;-------------------------------------- Для COM программ CS=DS=ES mov ds,es:[bx+14h] mov es,es:[bx+14h] xor ax,ax xor bx,bx iret ;-------------------------------------- Возврат из дочерней программы Continue: mov ax,@data mov ds,ax mov ss,keep_ss mov sp,keep_sp mov ax,4C00h ; Завершение программы int 21h ZSEG segment ZSEG ends END Start