unsigned char MEM48[6];
unsigned char GDT[]={
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,    //GDT entry 0 (null segment)
0xFF,0xFF,0x00,0x00,0x00,0x92,0xCF,0xFF};   //GDT entry 1 (seg 0, limit 4GB)
void INITCPU32(void)
{
	asm mov MEM48[0],16
	asm mov eax,seg GDT
	asm shl eax,4
	asm mov bx,offset GDT
	asm movzx ebx,bx
	asm add eax,ebx
	asm mov dword ptr MEM48[2],eax
	asm lgdt pword ptr MEM48        //Load global descriptor table address

	asm mov bx,08h                  //Load bx to point to GDT entry 1
	asm push ds
	asm cli                         //Disable interrupts
	asm mov eax,cr0                 //Switch to protected mode 
	asm or eax,1
	asm mov cr0,eax                  
	asm jmp PROTECTION_ENABLED      //Clear executionpipe
	PROTECTION_ENABLED:
	asm mov gs,bx                   //Load segment shadow-registers
	asm mov fs,bx                   //with GDT entry 1 (4GB segment limit)
	asm mov es,bx
	asm mov ds,bx                   
	asm and al,0FEh                 //Switch back to real-mode without
	asm mov cr0,eax                 //resetting the CPU
	asm jmp PROTECTION_DISABLED     //Clear executionpipe
	PROTECTION_DISABLED:
	asm sti                         //Enable interrupts
	asm pop ds
}

unsigned long XMSDRIVERADDRESS;

unsigned int _virtual86(void)
{
	asm mov eax,cr0
	asm and ax,1
}
#define XMScall(func,d)\
	asm mov ah,func;\
	asm mov dx,d;\
	asm call dword ptr XMSDRIVERADDRESS;
unsigned int XMSinit(void)
{
	asm mov ax,0x4300
	asm int 0x2F
	asm cmp al,0x80
	asm je XMSOK1
	return(1);
	XMSOK1:
	asm mov ax,0x4310
	asm int 0x2F
	asm mov ax,es
	asm shl eax,16
	asm mov ax,bx
	asm mov XMSDRIVERADDRESS[0],bx
	asm mov XMSDRIVERADDRESS[2],es
	XMScall(0,0);
	asm cmp ah,2
	asm jae XMSOK2
	return(1);
	XMSOK2:
	return(0);
}
unsigned int XMSlocal_enable_A20(void)
{
	XMScall(0x05,0);
}
unsigned int XMSqueryfree(void)
{
	XMScall(0x08,0);
	asm mov ax,dx
}
unsigned int XMSalloc(unsigned int kbytes)
{
	XMScall(0x09,kbytes);
	asm cmp ax,0x0001
	asm je ALLOCOK
	return(-1);
	ALLOCOK:
	asm mov ax,dx
}
unsigned int XMSfree(unsigned int handle)
{
	XMScall(0x0A,handle);
	asm cmp ax,0x0001
	asm je FREEOK
	return(1);
	FREEOK:
	asm mov ax,bx
}
unsigned long XMSlock(unsigned int handle)
{
	XMScall(0x0C,handle);
	asm mov ax,bx
}
void XMSunlock(unsigned int handle)
{
	XMScall(0x0D,handle);
}

void main(void)
{
	unsigned int xmshandle;
	unsigned long xmsaddress;
	if(_virtual86()) {printf("Machine in virtual 8086 mode, remove QEMM and other such drivers.\n");exit(1);}
	INITCPU32();
	if(XMSinit()) {printf("XMS-driver not found or too old\n");exit(1);}
	if(XMSqueryfree()<100) {printf("Not enough free XMS-memory\n");exit(1);}
	XMSlocal_enable_A20();
	xmshandle=XMSalloc(100);
	xmsaddress=XMSlock(xmshandle);
	printf("Writing 100KB to XMS\n");
	asm xor ax,ax
	asm mov es,ax
	asm xor edi,edi
	asm mov ebx,xmsaddress
	XFILL:  asm mov dword ptr es:[ebx+edi],012345678h
		asm add edi,4                   // Write 100000bytes to
		asm cmp edi,100000              // extended memory
		asm jb XFILL
	asm xor edi,edi
	printf("Checking the written data\n");
	XCHK:   asm cmp dword ptr es:[ebx+edi],012345678h
		asm jne READERR                 // Check, that data is
		asm add edi,4                   // correct
		asm cmp edi,100000
		asm jb XCHK
		asm jmp NOERRORS
	READERR:
	printf("Extended memory use failed\n");
	asm jmp ERRORS
	NOERRORS:
	printf("Success\n");
	ERRORS:
	XMSunlock(xmshandle);
	XMSfree(xmshandle);
}
