#include	<stdio.h>
#include	<conio.h>
#include	<dos.h>

// Макрокоманда FP_MAKE составляет дальний указатель
// из сегмента и смещения
#define FP_MAKE(seg,off) ((void far *)                         \
					((((unsigned long) (unsigned)(seg)) << 16L) |   \
					((unsigned long) (unsigned) (off))))

// Структура для определения символа и его атрибута
typedef struct _VIDEOBUF_ {
	unsigned char chr;
	unsigned char attr;
} VIDEOBUF;

// Описание функций
void	SetVideoMode( unsigned char vmode );
void	SetBlinkIntensity( unsigned char mode );
int	GetColumn(void);
int	GetVideoBuf(int);
int	Hello(void);
int	main( int, char ** );

//==============================================================
// Главная функция программы
//==============================================================
int main( int argc, char * argv[] )
{
	union REGS	inregs, outregs;
	VIDEOBUF	_far *vbuf, _far *ptr_vbuf;

	unsigned char	background, foreground;
	unsigned char	char_attr;

	int	vmode, bl_in_mode;
	char	szText[4];

	// Проверка командной строки программы
	if( argc != 3 )
	{
		Hello();
		return -1;
	}
	sscanf(argv[1],"%d",&vmode);
	sscanf(argv[2],"%d",&bl_in_mode);

	// Если указан графический режим, завершаем программу
	if(vmode > 3 && vmode != 7)
		return(-2);

	// Если неправильно указан параметр <интенсивность>, завершаем программу
	if((bl_in_mode != 0)&&(bl_in_mode != 1))
		return(-3);

	// Устанавливаем новый режим работы видеоадаптера, указанный
	// параметром <режим>
	SetVideoMode((unsigned char) vmode );

	// Выбираем как будут интерпритироваться атрибуты символов.
	// Если параметр <интенсивность> равен 0 атрибуты управляют
	// интенсивностью цвета символов, если параметр равен 1
	// атрибуты управляют миганием символов
	SetBlinkIntensity((unsigned char) bl_in_mode );

	// Определяем адрес начала активной страницы видеопамяти
	ptr_vbuf = vbuf=
		(VIDEOBUF _far *)FP_MAKE(GetVideoBuf((unsigned char) vmode ),0);

	// Отображаем на экране массив символов, имеющих различные
	// атрибуты
	for( background=0; background<16; background++)
	{
		for( foreground=0; foreground<16; foreground++)
		{
			char_attr = (unsigned char)((background<<4) | foreground);
			sprintf( szText, "%02X", char_attr );

			// Отображаем на экране символ. Записываем в видеопамять
			// код символа и его атрибут
			ptr_vbuf->chr = szText[0];	ptr_vbuf->attr = char_attr;
			ptr_vbuf++;

			ptr_vbuf->chr = szText[1]; ptr_vbuf->attr = char_attr;
			ptr_vbuf++;
		}
		ptr_vbuf = vbuf = vbuf + GetColumn();
	}

	// Ожидаем нажатие на любую клавишу клавиатуры
	getch();

	return 0;
}


//==============================================================
// Функция возвращает сегментный адрес активной страницы видеопамяти
// (учитывается значение регистров смещения адреса видеопамяти)
//==============================================================
int GetVideoBuf(int vmode) {

	unsigned vbase;
	unsigned adr_CRT;
	unsigned high;
	unsigned low;
	unsigned offs;

	// В зависимости от режима видеоадаптера базовый адрес
	// видеопамяти может быть 0xb000 или 0xb800
	vbase = (vmode == 7) ? 0xb000 : 0xb800;

	// Определяем адрес порта контроллера ЭЛТ
	adr_CRT = *(unsigned _far *)(FP_MAKE(0x40,0x63));

	// Считываем содержимое регистров начального адреса
	outp(adr_CRT,0xc);
	high = inp(adr_CRT+1);

	outp(adr_CRT,0xd);
	low = inp(adr_CRT+1);

	offs = ((high << 8) + low) >> 4;

	// Добавляем к базовому адресу видеопамяти смещение, взятое из
	// регистров начального адреса контроллера ЭЛТ
	vbase += offs;

	return(vbase);
}

//==============================================================
// Функция возвращает количество символов в строке
//==============================================================

int GetColumn(void) {

	// Считываем содержимое переменной BIOS, расположенной по
	// адресу 0000:044Ah. В ней записано количество символов
	// в строке для текущего режима
	return(*(int _far *)(FP_MAKE(0x40,0x4a)));
}

//==============================================================
// Функция устанавливает режим работы видеоадаптера, заданный
// параметром vmode
//==============================================================
void SetVideoMode( unsigned char vmode ) {

	union  REGS    inregs, outregs;

	// Устанавливаем режим vmode
	inregs.h.ah = 0x0;
	inregs.h.al = vmode;
	int86( 0x10, &inregs, &outregs );
}

//==============================================================
// Функция управляет назначением атрибутов символов.
//  mode = 0 атрибут управляет интенсивностью цвета символов
//  mode = 1 атрибут управляет миганием символов
//==============================================================
void SetBlinkIntensity( unsigned char mode ) {

	union  REGS    inregs, outregs;

	inregs.h.ah = 0x10;
	inregs.h.al = 0x3;
	inregs.h.bl = mode;
	int86( 0x10, &inregs, &outregs );
}

//==============================================================
// Функция выводит на экран краткую справку о программе
//==============================================================
int Hello(void) {

	printf("\nCopyright (C)Frolov G.V.,1995. E-mail: frolov@glas.apc.org\n"
			 "\nФормат вызова: TEXTATTR <режим> <интенсивность>"
			 "\n   <режим>: любые текстовые режимы"
			 "\n   <интенсивность>: 0 - интенсивность цвета, 1 - мигание символа"
			 );
}


