Читайте также:
|
|
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <dos.h>
#include "paral.h"
char *MemBuf, // указатель на дублирующий буфер
*BackGroundBmp, // указатель на битовую карту фона
*ForeGroundBmp, // указатель на битовую карту
// ближнего плана
*VideoRam; // указатель на видеобуфер
PcxFile pcx; // структура данных
// для чтения PCX-файла
int volatile KeyScan; // заполняется обработчиком
// прерывания клавиатуры
int frames=0, // количество нарисованных кадров
PrevMode; // исходный видеорежим
int background, // позиция прокрутки фона
foreground, //позиция прокрутки битовой карты
// ближнего плана position; // общее расстояние прокрутки
void _interrupt (*OldInt9)(void); // указатель на обработчик
// прерывания клавиатуры BIOS
// Функция загружает 256 - цветный PCX-файл
int ReadPcxFile(char *filename,PcxFile *pcx)
{
long i;
int mode=NORMAL,nbytes;
char abyte,*p;
FILE *f;
f=fopen(filename,"rb");
if(f==NULL)
return FCX_NOFILE;
fread(&pcx->hdr,sizeof(PcxHeader),1, f);
pcx_width=1+pcx->hdr.xmax-pcx->hdr.xmin;
pcx->height=1+pcx->hdr.ymax-pcx->hdr.ymin;
pcx->imagebytes=(unsigned int) (pcx->width*pcx->height);
if(pcx->imagebytes > PCX_MAX_SIZE) return PCX_TOOBIG;
pcx->bitmap= (char*)malloc (pcx->imagebytes);
if(pcx->bitmap == NULL) return PCX_NOMEM;
p=pcx->bitmap;
for(i=0;i<pcx->imagebytes;i++)
{
if(mode == NORMAL)
{
abyte=fgetc(f);
if((unsigned char)abyte > 0xbf)
{ nbytes=abyte & 0x3f;
abyte=fgetc(f);
if(--nbytes > 0)
mode=RLE;
}
}
else if(-—nbytes == 0) mode=NORMAL;
*p++=abyte;
}
fseek(f,-768L,SEEK_END); // получить палитру,из PCX-файла
fread(pcx->pal,768,1,f);
p=pcx->pal;
for(i=0;i<768;i++) // битовый сдвиг цветов в палитре
*р++=*р >>2;
fclose(f);
return PCX_OK;
}
// Новый обработчик прерывания клавиатуры для программы прокрутки
// Он используется для интерактивной прокрутки изображения.
// если стандартный обработчик прерывания 9h не будет заблокирован
// длительное нажатие на клавиши управления курсором приведет
// к переполнению буфера клавиатуры и появлению крайне неприятного
// звука из динамика.
void _interrupt Newlnt9(void)
{
register char x;
KeyScan=inp(0х60);// прочитать код клавиши
x=inp(0x61); // сообщить клавиатуре, что символ обработан
outp(0x61, (х|0х80));
outp(0х61,х);
outp(0х20,0х20); // сообщить о завершении прерывания
if(KeyScan == RIGHT_ARROW_REL ||// проверка кода клавиши
KeyScan == LEFT_ARROW_REL) KeyScan=0;
}
// Функция восстанавливает исходный обработчик прерываний клавиатуры
void RestoreKeyboard(void)
{
_dos_setvect(KEYBOARD,OldInt9); // восстанавливаем
// обработчик BIOS
}
// Эта функция сохраняет прежнее значение вектора прерывания // клавиатуры и устанавливает новый обработчик нашей программы.
void InitKeyboard(void)
{
OldInt9= _dos_getvect(KEYBOARD); // сохраняем адрес
// обработчика BIOS
_dos_setvect(KEYBOARD,NewInt9); // устанавливаем новый
// обработчик прерывания 9h
}
// Эта функция использует функции BIOS для установки в регистрах
// видеоконтроллера значений, необходимых для работы с цветами,
// определяемыми массивом раl[]
void SetAllRgbPalette(char *pal)
{
struct SREGS s;
union REGS r;
segread(&s); // читаем текущее значение сегментных регистров
s.es=FP_SEG((void far*)pal); // в ES загружаем сегмент ра1[]
r.x.dx=FP OFF((void far*}pal);// в DX загружаем смещение pal[]
r.x.ax=0xl012; // готовимся к.вызову подфункции // 12h функции BIOS 10h
r.x.bx=0; /;/ номер начального регистра палитры
r.х.сх=256; // номер последнего изменяемого регистра
int86x(0xl0,&r,&r,&s);// вызов видео BIOS
}
// Функция устанавливает режим 13h
// Это MCGA-совместимыЙ режим 320х200х256 цветов
void InitVideo()
{
union REGS r;
r.h.ah=0x0f; // функция Ofh - установка видеорежима
int86(0xl0,&r,&r); // вызов видео BIOS
PrevMode=r.h.al; // сохраняем старое значение режима
r.x.ax=0xl3; // устанавливаем режим 13h
int86(0х10,&r,sr); // вызов видео BIOS
VideoRam=MK_FP(0xa000,0); // создаем указатель на видеопамять
}
//Эта функция восстанавливает исходный видеорежим
void RestoreVideo()
{
union REGS r;
r.x,ax=PrevMode; //исходный видеорежим
int86(0х10,&r,&r); // вызов видео BIOS
}
// Функция загрузки битовых карт слоев
int InitBitmaps()
{
int r;
// начальное положение линии деления
background=foreground=1;
// читаем битовую карту фона
r=ReadPcxFile("backgrnd.pcx",&pcx);
// проверка на ошибки чтения if(r!= РСХ_ОК)
return FALSE;
// запоминаем указатель на битовую карту
BackGroundBmp=pcx.bitmap;
// устанавливаем палитру
SetAllRgbPalette(pcx.pal);
// читаем битовую карту переднего слоя
r=ReadPcxFile("foregrnd.pcx",&pcx);
// проверка на ошибки чтения
if (r!= РСХ_ОК) return FALSE;
//запоминаем указатель на битовую карту
ForeGroundBmp=pcx.bitmap;
// создаем буфер в памяти
MemBuf=malloc(MEMBLK);
// проверка на ошибки распределения памяти
if(MemBuf == NULL) return FALSE;
memset(MemBuf,0,MEMBLK); // очистка буфера
return TRUE;
// все в порядке!
}
// функция освобождает выделенную память
void FreeMem()
(
free(MemBuf);
free(BackGroundBmp);
free(ForeGroundBmp);
}
// Функция рисует слои параллакса.
// Порядок отрисовки определяется координатой слоя по оси Z.
void DrawLayers()
{
OpaqueBlt(BackGroundBmp,0,100,background);
TransparentBlt(ForeGroundBmp,50,100,foreground);
}
// Эта функция осуществляет анимацию. Учтите, что это наиболее
// критичная по времени часть программы. Для оптимизации отрисовки
// как сама функция, так и те функции, которые она вызывает,
// следует переписать на ассемблере. Как правило, это увеличивает
// быстродействие на 100 процентов.
void AnimLoop()
{
while(KeyScan!= ESC_PRESSED) // пока не нажата клавиша ESC
(
switch(KeyScan) // определяем, какая клавиша была нажата
{
case RIGHT_ARROW_PRESSED: //нажата "стрелка вправо"
position--; // изменяем позицию
if(position < 0) // останавливаем прокрутку,
// если дошли до конца
{
position=0;
break;
} backgrpund —=1; // прокручиваем фон влево на 2 пикселя
if(background < 1) // дошли до конца?
background+=VIEW_WIDTH; //...если да - возврат к началу
foreground-=2; // прокручиваем верхний
// слой влево на 4 пикселя
if(foreground < 1) // дошли до конца?
foreground+=VIEW_WIDTH; //...если да - возврат к началу
break;
case LEFT_ARROW_PRESSED: // нажата "стрелка влево"
position++; // изменяем текущую позицию прокрутки
if(position > TOTAL_SCROLL) // останавливаем прокрутку,
// если дошли до конца
{
position=TOTAL_SCROLL;
break;
}
background+=l; // прокручиваем фон вправо на 2 пикселя
if(background > VIEW_WIDTH-1) // дошли до конца?
background-=VIEW_WIDTH; //...если да - возврат к началу
foreground+=2; // прокручиваем верхний слой
// вправо на 4 пикселя
if(foreground > VIEW_WIDTH-1) // дошли до конца?
foreground-=VIEW_WIDTH; //...если да - возврат к началу
break;
default: // игнорируем остальные клавиши
break;
}
DrawLayers(); // рисуем слои в буфере в
// оперативной памяти
memcpy(VideoRam,MemBuf,MEMBLK); // копируем буфер в
// видеопамять
frames++; // увеличиваем счетчик кадров
) }
//эта функция осуществляет необходимую инициализацию
void Initialize()
{
position=0;
InitVideo(); // устанавливаем видеорежим 13h
InitKeyboard(); // устанавливаем наш обработчик
// прерывания клавиатуры
if(!InitBitmaps()) // загружаем битовые карты
{
CleanUp(); //освобождаем память
printf("\nError loading bitmaps\n");
exit(1);
} }
// функция выполняет всю необходимую очистку
void Cleanup()
{
RestoreVideo(); // восстанавливаем исходный видеорежим
RestoreKeyboard(); // восстанавливаем обработчик
// прерывания клавиатуры BIOS
FreeMem(); // освобождаем всю выделенную память
}
// Это начало программы. Функция вызывает процедуры инициализации.
// Затем читает текущее значение системного таймера и запускает
// анимацию. Потом вновь читается значение системного таймера.
// Разница между исходным и конечным значениями таймера
// используется для вычисления скорости анимации.
int main()
{
clock_t begin,fini;
Initialize(}; // проводим инициализацию
begin=clock(); // получаем исходное значение таймера
AnimLoop(); // выполняем анимацию
fini=clock(); // получаем значение таймера
CleanUp(); // восстанавливаем измененные параметры
printf("Frames: %d\nfps: %f\n",frames,
(float)CLK_TCK*frames/(fini-begin));
return 0;
}
Оптимизированные версии OpaqueBlt() и TransparentBlt()
Листинг 17.4 содержит оптимизированные ассемблерные версии подпрограмм OpaqueBlt() и TransparentBlt(), которые имеются и на дискете (BLIT.ASM). Эти подпрограммы можно использовать вместо соответствующих функций на Си, что увеличит быстродействие программы примерно на 30 процентов.
Демонстрационная программа PARAL использует ассемблерные версии этих подпрограмм. Они были написаны с тем расчетом, чтобы могли работать с процессором 286. Поскольку они заполняют буфер системной памяти, их можно оптимизировать и дальше, применив 32-битовые команды перемещения банных.
Дата добавления: 2015-07-12; просмотров: 92 | Нарушение авторских прав
<== предыдущая страница | | | следующая страница ==> |
Листинг 17.2 Демонстрационная программа повторяемого смещения. | | | Листинг 17.4. Оптимизированная подпрограмма (BLIT.ASM). |