Навигация
Главная
Поиск
Форум
FAQ's
Ссылки
Карта сайта
Чат программистов

Статьи
-Delphi
-C/C++
-Turbo Pascal
-Assembler
-Java/JS
-PHP
-Perl
-DHTML
-Prolog
-GPSS
-Сайтостроительство
-CMS: PHP Fusion
-Инвестирование

Файлы
-Для программистов
-Компонеты для Delphi
-Исходники на Delphi
-Исходники на C/C++
-Книги по Delphi
-Книги по С/С++
-Книги по JAVA/JS
-Книги по Basic/VB/.NET
-Книги по PHP/MySQL
-Книги по Assembler
-PHP Fusion MOD'ы
-by Kest
Professional Download System
Реклама
Услуги

Автоматическое добавление статей на сайты на Wordpress, Joomla, DLE
Заказать продвижение сайта
Программа для рисования блок-схем
Инженерный калькулятор онлайн
Таблица сложения онлайн
Популярные статьи
OpenGL и Delphi... 65535
Бип из системно... 65535
Пример работы с... 65535
ТЕХНОЛОГИИ ДОСТ... 65535
Организация зап... 65535
Вызов хранимых ... 65535
Создание отчето... 65535
Эмулятор микроп... 65535
Подключение Mic... 65535
Создание потоко... 65535
Приложение «Про... 65535
21 ошибка прогр... 65535
Гостевая книга ... 65535
Форум на вашем ... 65535
HACK F.A.Q 65535
Содержание сайт... 65535
Invision Power ... 65535
Программируемая... 65535
Оператор выбора... 65535
Модуль Forms 65535
Реклама
Сейчас на сайте
Гостей: 2
На сайте нет зарегистрированных пользователей

Пользователей: 13,152
новичок: Chavo
Новости
Реклама
Выполняем курсовые и лабораторные по разным языкам программирования
Подробнее - курсовые и лабораторные на заказ
Delphi, Turbo Pascal, Assembler, C, C++, C#, Visual Basic, Java, GPSS, Prolog, 3D MAX, Компас 3D
Заказать программу для Windows Mobile, Symbian

Моделирование работы класса персональных компьютеров на GPSS + Отчет + Б...
Обратное размещение элементов ЭВС на Delphi + Пояснительная записка
Принадлежит ли точка пересечению двух окружностей на Turbo Pascal + Отче...

Реклама



Подписывайся на YouTube канал о программировании, что бы не пропустить новые видео!

ПОДПИСЫВАЙСЯ на канал о программировании
Измерение времени и синхронизация
Сергей Андрианов
18.01.2002


Когда программы работают в режиме реального времени, то для них важно, чтобы скорость исполнения не зависела от производительности компьютера. Наиболее простой и один из самых эффективных способов добиться этого — синхронизация с кадровой разверткой (см. «Мир ПК», № 7/01, с. 87), однако он не универсален. Исключить влияние мощности ПК на скорость работы программы можно двумя способами: выводить кадры с фиксированной частотой, а остальное время находиться в ожидании либо выводить кадры в максимально возможном темпе, а затем измерять их длительность, согласовывая с нею изменения, происходящие на экране.

Первый способ оправдывает себя тогда, когда изображение выстраивается быстро, а приемлемую частоту обновления кадров (FPS — frames per second) обеспечивает даже «слабый» компьютер. Мощные же ПК при работе такой программы большую часть времени будут, как правило, простаивать, но для данной цели это несущественно. Второй способ применяется в тех случаях, когда построение кадра требует значительных вычислительных ресурсов. При этом для данной программы принимаются такие минимальные системные требования, при которых частота обновления кадров была бы приемлемой для просмотра изображения. А высокопроизводительные ПК будут работать с еще большей частотой обновления, обеспечивая плавное перемещение объектов по экрану и, следовательно, лучшее восприятие. Однако особенно увлекаться этим также не стоит, поскольку изменять содержимое видеопамяти с частотой более высокой, чем способен отобразить видеоадаптер на экране, вряд ли целесообразно, ведь это не просто бессмысленно, а даже вредно — могут появиться помехи, ухудшающие изображение. Поэтому оптимальной была бы линейная зависимость частоты обновления экрана от мощности ПК при частотах ниже частоты кадровой развертки и постоянная частота обновления, равная кадровой на более мощных машинах. Второе уже можно реализовать, а вот для первого нужно хотя бы научиться измерять время на ПК. Это делается различными способами, в том числе и с помощью стандартной процедуры определения времени — GetTime, но она, к сожалению, имеет определенные недостатки:
работает довольно долго (причины — ниже);
вместо одной величины выдает сразу четыре, что затрудняет анализ и сравнение;
точность измерения не равна единице младшего разряда, что неудобно, поскольку вводит в заблуждение относительно реального времени и способствует возникновению дополнительной погрешности;
в полночь происходит сброс часов в 0;
невысокая точность измерения.

Чтобы понять, как со всем этим бороться, следует представить, каким образом происходит отсчет времени в ПК.

В одной из микросхем на системной плате установлен специальный таймер, представляющий собой генератор опорной частоты 1,19 МГц и двух-трех программируемых делителей. Поскольку коэффициент деления может изменяться от 2 до 65 536 (216), то диапазон возможных выходных частот простирается примерно от 18 Гц до 600 кГц. Выход одного из делителей связан с динамиком ПК. (В Borland Pascal звук реализуется процедурой sound.) Другой делитель как раз и используется для измерения времени, причем его выход связан с контроллером прерываний. Этот делитель запрограммирован на максимально возможный коэффициент, поэтому период следования импульсов составляет примерно 0,055 с, а частота будет равна 18,2 Гц. Среди переменных BIOS есть и отвечающая за текущее время. Она расположена в ОЗУ по адресу 0040h:006c. Каждое прерывание на единицу увеличивает ее значение, которое и будет «основным» временем в компьютере. Когда же мы запрашиваем GetTime, то системная функция берет это значение и вычисляет часы, минуты, секунды и сотые доли секунд. Естественно, на все преобразования требуется определенное время, причем величина 0,055 с в сотых долях точно не выражается и потому округляется до 0,05 или 0,06 с.

В нашем случае было бы логично использовать именно значение переменной BIOS, поскольку тогда пропадают сразу три из пяти перечисленных недостатков. Этот способ самый простой и быстрый. Правда, программисту придется мыслить не в привычных секундах, а в 1/18 ее долях, что, впрочем, не слишком высокая плата за скорость и удобство. Думаю, будет лучше, если время станет отсчитываться не от начала суток, а с момента запуска программы или с другого существенного для нее события, например с момента загрузки миссии в игре.

Кроме того, счетчик BIOS обнуляется в полночь. Ведь будет неприятно, если кто-то, допоздна засидевшись за написанной вами игрой, вдруг «подвесит» свой компьютер только из-за того, что ваша программа, скажем, ожидает момента времени 24:00:01. Потому-то и предусмотрен в процедуре переход на новые сутки, ликвидирующий еще один недостаток. Чтобы определить время, нужно подключить приведенный в листинге 1 модуль директивой uses и использовать функцию Clock, возвращающую количество 55-миллисекундных интервалов, прошедших с момента запуска программы. Эта функция работает в десятки раз быстрее, чем GetTime.

В модуль также включена процедура ResetTime, сбрасывающая показания счетчиков в 0. Кроме того, присутствует процедура GetFPS, позволяющая измерять количество кадров в секунду, формируемых вашей программой. Чтобы эта процедура корректно работала, ее надо вызывать один раз в течение кадра.

Можно продемонстрировать возможности нового модуля, внеся его в директиву uses основной программы, описав две дополнительные переменные (S: string {для вывода сообщений на экран}; FPS: single {темп вывода, кадров в секунду}) и заменив основную программу текстом из листинга 2. Мы также ввели новую функцию sign, возвращающую знак аргумента. Вообще-то непонятно, почему разработчики Turbo Pascal ею пренебрегли, ведь она является парной к abs.

Варьируя значение аргумента процедуры delay, специально введенной для снижения частоты формирования кадров, можно увидеть, что средняя скорость перемещения спрайта по экрану не изменяется, хотя при уменьшении темпа вывода кадров плавность движения исчезает.

К сожалению, предложенный способ определения времени имеет некоторые недостатки, основной из которых — низкая точность, обусловленная малой частотой обновления показаний компьютерных часов. С этим можно справиться, если выполнить одно из следующих действий:
перепрограммировать частоту прерываний таймера, изменив коэффициент деления. Однако это гораздо более сложная процедура, чреватая неприятными последствиями из-за того, что требуется обеспечивать корректное функционирование системных часов, а кроме того, ее нельзя использовать во многих многозадачных операционных системах;
читать регистры таймера, содержащие данные о времени с точностью более 1 мкс. Однако обычно это делается очень медленно, а во многих многозадачных операционных системах попросту невозможно;
воспользоваться командой процессора rdtsc — пожалуй, часто это наилучший вариант. Недостатки: работает только на процессорах не ниже Pentium, требует предварительной калибровки и не реализуется в 16-разрядных программах, в частности созданных при помощи Turbo Pascal.
Листинг 1
unit timer18;
interface

function Clock : longint;
{время, прошедшее с запуска программы в 1/18 с}
procedure ResetTime; {сброс всех показаний времени в 0}
function GetFPS : single;
{текущее число кадров в секунду}
{необходимо вызывать 1 раз в каждом кадре}
implementation

var
ZeroClock : longint; {время запуска программы}
LastFPSclock : longint; {->- последнего запроса FPS}
Days : longint; {номер дня}
LastClock : longint; {время предыдущего запроса}
NumberFrames : integer; {счетчик кадров}
FPS : single; {число кадров в секунду}

function Clock : longint;
{время, прошедшее с запуска программы в 1/18 с}
var t : longint;
begin
t := meml[Seg0040:$6c] - ZeroClock;
if t < LastClock then begin {переход на след. сутки}
inc(Days);
end;
LastClock := t;
Clock := t + days*1573040; {1 сутки - 1573040}
end;
procedure ResetTime; {сброс всех показаний времени в 0}
begin
ZeroClock := meml[Seg0040:$6c];
LastFPSclock := 0;
NumberFrames := 0;
FPS := 0;
Days := 0;
LastClock := 0;
end;

function GetFPS : single;
{текущее число кадров в секунду}
var
c : longint;
e : single;
begin
inc(NumberFrames);
c := Clock;
if (c - LastFPSclock) >= 18 then begin
{интервал времени измерения не меньше 1 с}
FPS := NumberFrames / (c - LastFPSclock) * 18.2;
NumberFrames := 0;
LastFPSclock := c;
end;
GetFPS := FPS;
end;

begin
ResetTime;
end.
Листинг 2
function sign(a:single):integer;
begin
if a = 0 then
sign := 0
else
if a > 0 then
sign := 1
else
sign := -1;
end;

begin
GetPal(p[0],0,256);
FadeOut(p);
CreateSprite('sprt01.bmp',0,0,1,1);
r.ax := $13; { устанавливаем режим }
intr($10,r); { 320х200х256 цветов }
scr := ptr(SegA000,0);
BlackPal;
PutBackGround; {рисуем фон}
FadeIn(p);
GetBuffer; {сохраняем фон под спрайтом}
PutSprite; {и рисуем на его месте спрайт}
repeat {теперь спрайт будет двигаться по экрану}
{до тех пор, пока мы не нажмем на клавишу}
PutBuffer; {восстанавливаем фон}
FPS := GetFPS;
if FPS > 1 then begin {изменяем приращение}
Sprt.dx := sign(Sprt.dx)*round(70/FPS);
Sprt.dy := sign(Sprt.dy)*round(70/FPS);
end;
CalcSpritePosition;
GetBuffer; {сохраняем фон}
PutSprite; {рисуем спрайт}
inc(TextColor);
SetTextParm(TextColor div 16, (TextColor + 48) div 16,1);
PutText(56,16,'Демонстрационная');
SetTextParm(TextColor and $F,0,0);
PutText(192,16,'программа');
SetTextParm(1,14,1);
str(Clock:3,s);
PutText(128,172,'Time:'+s);
SetTextParm(15,0,1);
str(FPS:0:1,s);
PutText(120,184,' '+s+' fps ');
delay(100); {для регулирования частоты кадров}
WaitVerticalRetrace;
{ожидаем обратный ход луча кадровой развертки}
until keypressed;
readkey; {чистим буфер клавиатуры}
FadeOut(p);
r.ax := $3;
intr($10,r); {возвращаемся в текстовый режим}
DestroySprite;
end.
Опубликовал Kest Октябрь 31 2008 23:15:11 · 0 Комментариев · 10730 Прочтений · Для печати

• Не нашли ответ на свой вопрос? Тогда задайте вопрос в комментариях или на форуме! •


Комментарии
Нет комментариев.
Добавить комментарий
Имя:



smiley smiley smiley smiley smiley smiley smiley smiley smiley
Запретить смайлики в комментариях

Введите проверочный код:* =
Рейтинги
Рейтинг доступен только для пользователей.

Пожалуйста, залогиньтесь или зарегистрируйтесь для голосования.

Нет данных для оценки.
Гость
Имя

Пароль



Вы не зарегистрированны?
Нажмите здесь для регистрации.

Забыли пароль?
Запросите новый здесь.
Поделиться ссылкой
Фолловь меня в Твиттере! • Смотрите канал о путешествияхКак приготовить мидии в тайланде?
Загрузки
Новые загрузки
iChat v.7.0 Final...
iComm v.6.1 - выв...
Visual Studio 200...
CodeGear RAD Stud...
Шаблон для новост...

Случайные загрузки
ComboBox97
Еext Editor
Blobs [Исходник н...
Java 2. Наиболее ...
Cooltray
База игр
«Философия» прогр...
База Allsubmitter...
BIOS
Правила программи...
Exe in exe
PHP: настольная к...
Реализация ЭЦП по...
XPmenu
Формирование отче...
БД студентов
С. Г. Горнаков - ...
VksButton
SMExport
Battle.Net - мони...

Топ загрузок
Приложение Клие... 100682
Delphi 7 Enterp... 95258
Converter AMR<-... 20187
GPSS World Stud... 16684
Borland C++Buil... 13898
Borland Delphi ... 9709
Turbo Pascal fo... 7229
Калькулятор [Ис... 5595
Visual Studio 2... 5127
FreeSMS v1.3.1 3621
Случайные статьи
PDF (Acrobat)
Лабораторное занят...
Структурированные ...
Казино франк бонусы
расположены в разн...
Пакеты обновлений
inf и Hisecdc
Виртуальное казино...
Модели управления ...
Общение в ICQ
Использование DTD-...
Как поставить кате...
Уникальное казино ...
Интеграция журнала...
Строки. Изменение ...
Анализ посещаемост...
Метод toUri
Последовательный п...
Обработка транзакц...
Как сделать анализ...
Метрика
Необходимые действ...
Пример сеанса рабо...
Протокол РРРоЕ
Глава 25. Когда...
Статистика



Друзья сайта
Программы, игры


Полезно
В какую объединенную сеть входит классовая сеть? Суммирование маршрутов Занимают ли таблицы память маршрутизатора?