Навигация
Главная
Поиск
Форум
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
21 ошибка прогр... 65535
HACK F.A.Q 65535
Бип из системно... 65535
Гостевая книга ... 65535
Invision Power ... 65535
Пример работы с... 65535
Содержание сайт... 65535
ТЕХНОЛОГИИ ДОСТ... 65535
Организация зап... 65535
Вызов хранимых ... 65535
Создание отчето... 65535
Имитационное мо... 65535
Программируемая... 65535
Эмулятор микроп... 65535
Подключение Mic... 65535
Создание потоко... 65535
Приложение «Про... 65535
Оператор выбора... 65535
Реклама
Сейчас на сайте
Гостей: 4
На сайте нет зарегистрированных пользователей

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

Принадлежит ли точка пересечению двух окружностей на Turbo Pascal + Отче...
База данных студентов на Turbo Pascal (Списки) + Пояснительная записка
Информационная система - продуктовый магазин на Turbo Pascal (База данны...

Измерение времени и синхронизация
Сергей Андрианов
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 October 31 2008 20:15:11 · 0 Комментариев · 11553 Прочтений · Для печати

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


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



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

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

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

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

Пароль



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

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

Случайные загрузки
Учебник для продв...
Text3D
Карта сайта
Панель "Случайное...
Импорт новостей ...
32 урока по Delphi
Zoom [Исходник на...
ADVstatusbar
Delphi 6 программ...
PRNDbgrid
Исправление проц...
Ehlib
Основы программир...
Работа с картотеками
iChat v.7.0 Final...
Алгоритмы шифрова...
PBFoldder
Проигрыватель Mp3
Длинный заголовок...
ScrollCredit

Топ загрузок
Приложение Клие... 100770
Delphi 7 Enterp... 97743
Converter AMR<-... 20258
GPSS World Stud... 17013
Borland C++Buil... 14181
Borland Delphi ... 10255
Turbo Pascal fo... 7370
Калькулятор [Ис... 5967
Visual Studio 2... 5205
Microsoft SQL S... 3661
Случайные статьи
Выполнение лаборат...
Особенности модаль...
Казино ROX
Что позволяет подд...
Структура простого...
ОГП обладает приор...
107.100.
будет проводиться ...
Присваивайте назва...
администрирование—...
Перенос индикаторо...
Какой программист ...
Связывание текстов...
Физическая память
СМЕШИВАНИЕ МЕТАДАН...
Какими недостаткам...
АНБ знает все секр...
Бесплатные игровые...
nonvar(X)
Вулкан Делюкс - дл...
Компиляция модулей
Разработка микропр...
Комбинаторы с типами
Исключения
Идеалы программиро...
Статистика



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


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