#include <stdio.h>
#include <conio.h>
#include <string.h>
#include <dos.h>
#include "vesa.h"
#include "sysp.h"

// Описание функций
int	main( int argc, char * argv[] );
int	GetVESAModeInfo(unsigned, VESAINFO *, VESAMODEINFO	* );
int	AnalyseResult(union REGS);
int	SetSVGAMode(unsigned);
void	WritePixel16Bit(VESAMODEINFO*, unsigned, unsigned, unsigned int);

unsigned int
PixelRGB( VESAMODEINFO*, unsigned int, unsigned int, unsigned int);


// Главная функция
int main( int argc, char * argv[] )
{
	// Структуры для получения информации о VBE
	VESAINFO			vesaInfo;
	VESAMODEINFO	modeInfo;
	unsigned			modeVESA;

	FILE	*fileVesaReport;
	int   iResult,x,y;
	unsigned	int	uRed, uGreen, uBlue;
	unsigned	int	uiColorRGB;



	// Проверка командной строки программы
	if( argc != 2 )
	{
		printf("\nФормат вызова: VESATEST <режим>"
			 "\n  <режим>: режим видеоадаптера");

		return -1;
	}

	sscanf(argv[1],"%x", &modeVESA);

	// Записываем в структуры vesaInfo и modeInfo
	// информацию о VBE и видеоадаптере
	iResult = GetVESAModeInfo(modeVESA, &vesaInfo, &modeInfo);

	if(iResult != 0) {
		printf("\nВыбранный вами режим не поддерживается");
		return -1;
	}

	if(
		// Атрибуты окна A - доступно для записи
		((modeInfo.ucWinAAttr & 5) != 5) ||
		// Количество слоев видеопамяти - один
		(modeInfo.ucNumPlanes != 1) ||
		// Количество бит на пиксел
		//		(modeInfo.ucNumPixelBits != 0xF) ||
		// Тип модели памяти (плоская, последовательная)
		(modeInfo.ucTypeMemModel != 6))
	{

		printf("\nПрограмма не расcчитана на этот режим");
		return -1;
	}

	iResult = SetSVGAMode(modeVESA);
	if(iResult != 0) {
		printf("\nОшибка пр установке режима видеоадаптера");
		return -1;
	}

	for(x = 0; x<479; x++ ) {
		uRed = uGreen = uBlue = x;
		uiColorRGB = PixelRGB( &modeInfo, uRed, 0, 0);
		WritePixel16Bit(&modeInfo, x, x, uiColorRGB);
	}


	// Ожидаем, когда пользователь нажмет на клавиатуру
	getch();
	for(x = 0; x<479; x++ ) {
		uRed = uGreen = uBlue = x;
		uiColorRGB = PixelRGB( &modeInfo, 0, uGreen, 0);
		WritePixel16Bit(&modeInfo, x, x, uiColorRGB);
	}
	// Ожидаем, когда пользователь нажмет на клавиатуру
	getch();
	for(x = 0; x<479; x++ ) {
		uRed = uGreen = uBlue = x;
		uiColorRGB = PixelRGB( &modeInfo, 0, 0, uBlue);
		WritePixel16Bit(&modeInfo, x, x, uiColorRGB);
	}
	// Ожидаем, когда пользователь нажмет на клавиатуру
	getch();

	for(x = 0; x<479; x++ ) {
		uRed = uGreen = uBlue = x;
		uiColorRGB = PixelRGB( &modeInfo, uRed, uGreen, uBlue);
		WritePixel16Bit(&modeInfo, x, x, uiColorRGB);
	}
	// Ожидаем, когда пользователь нажмет на клавиатуру
	getch();

	// Возвращаемся в стандартный текстовый режим
	_asm {
		mov	ax,3
		int	10h
	}

	return 0;
}

//=================================================
// Функция GetVESAModeInfo
// Заполняет структуры типа VESAINFO и VESAMODEINFO
// сведениями о VBE и режиме modeVESA
//=================================================
int
GetVESAModeInfo(unsigned modeVESA, VESAINFO *ptrVesaInfo,
											  VESAMODEINFO	*ptrModeInfo)
{
	union REGS regs;
	struct SREGS segregs;
	int	iResult, i;

	// Подготавливаем буфер для VBE версии 2.0
	strcpy( ptrVesaInfo->cSignature, "VBE2" );

	// Получаем данные о реализации VBE и видеоадаптере
	regs.h.ah = 0x4f;
	regs.h.al = 0x00;
	segregs.es = FP_SEG((void far *) ptrVesaInfo);
	regs.x.di = FP_OFF((void far *) ptrVesaInfo);
	int86x(0x10, &regs, &regs, &segregs);

	// Проверяем результат вызова функции VBE
	if((regs.h.al != 0x4f) || (regs.h.ah != 0)) return -1;

	// Теперь получаем информацию для выбранного режима VESA
	for( i=0; i<50; i++ )	 {

		// Получаем очередной номер режима VESA
		if(ptrVesaInfo->fpModeTable[i] == 0xFFFF)		break;
		if(ptrVesaInfo->fpModeTable[i] != modeVESA)	continue;

		// Получаем информацию о режиме vesaInfo.fpModeTable[i]
		regs.h.ah = 0x4f;
		regs.h.al = 0x01;
		regs.x.cx = ptrVesaInfo->fpModeTable[i];
		segregs.es = FP_SEG((void far *) ptrModeInfo);
		regs.x.di = FP_OFF((void far *) ptrModeInfo);
		int86x(0x10, &regs, &regs, &segregs);

		// Проверяем результат вызова функции VBE
		if((regs.h.al != 0x4f) || (regs.h.ah != 0)) return -1;
		else return 0;
	}
	return iResult;
}

//=================================================
// Функция SetSVGAMode
// Устанавливает режим vmode
//=================================================
int
SetSVGAMode(unsigned vmode)
{
	union REGS regs;
	int	iResult;

	regs.h.ah = 0x4f;
	regs.h.al = 0x02;
	regs.x.bx = vmode;

	int86(0x10, &regs, &regs);

	if((regs.h.al == 0x4f) && (regs.h.ah == 0))	return 0;
	else return -1;
}

//=================================================
// Функция WritePixel16Bit
// Отображает на экране пиксел (x,y) цветом color
// Переменная ptrModeInfo должна содержать указатель
// на заполненную структуру VESAMODEINFO
//=================================================
void WritePixel16Bit(VESAMODEINFO	*ptrModeInfo,
					unsigned x, unsigned y, unsigned int color) {

	unsigned char	_far *ptr_vbuf;
	unsigned long	ulWinPos, ulOffset, ulWinOffset;
	unsigned 		uWinPos;

	// Вычисляем смещение пиксела от начала видеопамяти
	ulOffset = (unsigned long) y *
		(unsigned long) ptrModeInfo->usBytesPerScanLine + (unsigned long) x * 2;

	// Вычисляем положение окна доступа для отображения пиксела (x,y)
	uWinPos = ((unsigned long) ulOffset /
					(unsigned long) (ptrModeInfo->usWinGranularity)) / 1024;

	// Перемещаем окно A в положение uWinPos
	_asm {
		mov	ax,4F05h
		mov	bh,0
		mov	bl,0
		mov	dx,uWinPos
		int	10h
	}

	// Определяем адрес окна доступа A
	ptr_vbuf =	(unsigned char _far *) FP_MAKE(ptrModeInfo->usBegSegA,0);

	// Определяем смещение пиксела (x,y) относительно окна доступа
	ulWinOffset = ulOffset %
		((unsigned long)(ptrModeInfo->usWinGranularity)*1024);

	// Устанавливаем новый цвет пиксела
	*((unsigned int*)(ptr_vbuf + ulWinOffset)) = (unsigned int)color;
}


//=================================================
// Функция WritePixel16Bit
// Отображает на экране пиксел (x,y) цветом color
// Переменная ptrModeInfo должна содержать указатель
// на заполненную структуру VESAMODEINFO
//=================================================
unsigned int
PixelRGB( VESAMODEINFO	*ptrModeInfo,
					unsigned int uRed,
					unsigned int uGreen,
					unsigned int uBlue
				 )
{
	unsigned int	uiValueRGB;
	unsigned int   redMask, greenMask, blueMask;

	redMask = greenMask = blueMask = 0xFFFF;

	redMask ^= redMask << ptrModeInfo->ucRedSize;
	greenMask ^= greenMask << ptrModeInfo->ucGreenSize;
	blueMask ^= blueMask << ptrModeInfo->ucBlueSize;


	uiValueRGB = ((uRed&redMask) << ptrModeInfo->ucRedField) |
					 ((uGreen&greenMask) << ptrModeInfo->ucGreenField) |
					 ((uBlue&blueMask) << ptrModeInfo->ucBlueField);


	return uiValueRGB;
}
