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

void main(void) {

	extern int getcpu(void);
	unsigned cpu_type, ver;
	unsigned err;
	char ver_hi, ver_lo, verems;
	unsigned hostdata_seg, hostdata_size, dpmi_flags;
	void (far *pm_entry)();

	union REGS regs;
	struct SREGS segregs;


	printf("\n*MemoScop* v 1.0, (C) Frolov A.V., 1992\n"
		 "---------------------------------------\n");

// Определяем тип центрального процессора. Если программа
// работает на процессоре i8086, завершаем выполнение,
// так как в этом случае интерфейсы с защищенным режимом
// недоступны.

	printf("Тип процессора: 80%d\n", (cpu_type = getcpu()));
	if(cpu_type == 86) {
		printf("\nНа этом процессоре нам работать не интересно...");
		exit(0);
	}

// Определяем размер доступной через прерывание INT 15h
// расширенной памяти.

	printf("\n------------------------ Уровень BIOS -------------------------\n");

	regs.h.ah = 0x88;
	int86(0x15, &regs, &regs);
	printf("Размер расширенной памяти, доступной через INT 15:"
	" \t%d Кбайт\n", regs.x.ax);

// Проверяем, установлен ли драйвер HIMEM.SYS,
// если установлен, выводим его версию.

	printf("\n------------------------ Уровень XMM --------------------------\n");


	if (XMM_Installed()) {
		printf("Установлен драйвер HIMEM.SYS");
		ver = XMM_Version();
		printf(", версия: %4X, изменения: %4X\n",
			(short)ver, (short)(ver >> 16));
		printf("Размер свободной расширенной памяти, доступной через XMM:"
					 " %ld Кбайт\n", (long)XMM_QueryLargestFree());
		printf("Общий размер расширенной памяти, доступной через XMM:"
					 " \t  %ld Кбайт\n", (long)XMM_QueryTotalFree());

	}
	else printf("\nДрайвер HIMEM.SYS не установлен.");


	printf("\n------------------------ Уровень EMS/VCPI ----------------------\n");

// Проверяем наличие драйвера EMS/VCPI

	if(ems_init()) printf("Драйвер EMS/VCPI не загружен\n");
	else {
		printf("Драйвер EMS/VCPI загружен, ");

// Выводим номер версии драйвера

		if((err = ems_ver(&verems)) != 0) {
			printf("\nОшибка %02.2X при определении версии EMM", err);
			exit(-1);
		}
		printf("версия EMM: %02.2X", verems);

// Определяем присутствие VCPI и его версию

		if(vcpi_ver(&ver_hi, &ver_lo) != 0) {
			printf("\nДрайвер EMM не поддерживает VCPI\n");
		}
		else printf("\nВерсия VCPI: %02.2X.%02.2X\n", ver_hi, ver_lo);
	}


	printf("\n------------------------ Уровень DPMI --------------------------");


// Проверяем доступность и параметры сервера DPMI

	regs.x.ax = 0x1687;
	int86x(0x2F, &regs, &regs, &segregs);
	if(regs.x.ax != 0) {
		printf("\nСервер DPMI не активен"); exit(-1);
	}

// Определяем версию сервера DPMI

	printf("\nВерсия сервера DPMI: \t\t\t%d.%d\n",
		regs.h.dh, regs.h.dl);

// Определяем тип процессора

	printf("Тип процессора:\t\t\t\t");
	if(regs.h.cl == 2) printf("80286");
	else if(regs.h.cl == 3) printf("80386");
	else if(regs.h.cl == 4) printf("80486");

// Определяем возможность работы с 32-разрядными
// программами

	dpmi_flags = regs.x.bx;
	printf("\nПоддержка 32-разрядных программ:\t");
	if(dpmi_flags && 1) printf("ПРИСУТСТВУЕТ");
	else printf("ОТСУТСТВУЕТ");

// Определяем размер области памяти для сервера DPMI

	hostdata_size = regs.x.si;
	printf("\nРазмер памяти для сервера DPMI:\t\t%d байт",
		hostdata_size * 16);

// Определяем адрес точки входа в защищённый режим

	FP_SEG(pm_entry) = segregs.es;
	FP_OFF(pm_entry) = regs.x.di;
	printf("\nАдрес точки входа в защищённый режим: \t%Fp\n",
		pm_entry);

}

/**
*.Name         ems_init
*.Title        Функция проверяет установку драйвера EMS
*
*.Descr        Эта функция проверяет наличие драйвера EMS
*
*.Proto        int ems_init(void);
*
*.Params       Не используются
*
*.Return       0 - драйвер EMS установлен;
*              1 - драйвер EMS не установлен.
*
*.Sample       ems_test.c
**/

int ems_init(void) {

	void (_interrupt _far *EMS_driver_adr)(void);
	char _far *EMS_driver_name;
	char test_name[8];
	int i;

	EMS_driver_adr = _dos_getvect(0x67);

	FP_SEG(EMS_driver_name) = FP_SEG (EMS_driver_adr);
	FP_OFF(EMS_driver_name) = 10;

	for(i=0; i<8; i++) test_name[i] = EMS_driver_name[i];

	if(strncmp(test_name, "EMMXXXX0", 8) == 0) return(0);
	else return(1);

}

/**
*.Name         ems_ver
*.Title        Определение версии драйвера EMS
*
*.Descr        Эта функция возвращает номер версии
*              драйвера EMS в двоично-десятичном формате.
*
*.Proto        int ems_ver(char *ver);
*
*.Params       char *ver - указатель на байт, в который
*                 будет записан номер версии.
*
*.Return       Номер версии драйвера EMS в формате BCD
*
*.Sample       ems_test.c
**/

int ems_ver(char *ver) {

	union REGS reg;

	reg.x.ax = 0x4600;
	int86(0x67, &reg, &reg);

	*ver = reg.h.al;
	return(reg.h.ah);
}

int vcpi_ver(char *ver_hi, char *ver_lo) {

	union REGS reg;

	reg.x.ax = 0xDE00;
	int86(0x67, &reg, &reg);

	*ver_hi = reg.h.bh;
	*ver_lo = reg.h.bl;
	return(reg.h.ah);
}
