/*
 *  Copyright (C) 2002-2004  The DOSBox Team
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Library General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

// Pharlap Special Infos:
// - Dont hook hardware ints
// - CMOS Memory Size has to return 0 otherwise it tries to map physical memory
// - Set PM Int Vector : Use it on current idt, not only dpmi one (ultima 8 mouse check fails otherwise)
// - Ultima 8 wants 40 Files (Hack in PSP::SetNumFiles
// - Ultima 8 and Bioforge work with DPL 3
// - Crusaders 1+2 seem to need DPL 0, which is bad...
// - Bioforge uses more than 2048 descriptos in ldt (4096)

#include <stdlib.h>
#include <string.h>
#include <vector>
#include "dosbox.h"
#include "dos_inc.h"
#include "callback.h"
#include "mem.h"
#include "regs.h"
#include "dos_system.h"
#include "setup.h"
#include "inout.h"
#include "cpu.h"
#include "bios.h"
#include "paging.h"

#include "debug.h"

//#define DPMI_LOG		LOG(LOG_MISC,LOG_ERROR)
#define DPMI_LOG		

#define DPMI_LOG_ERROR	LOG(LOG_MISC,LOG_ERROR)
//#define DPMI_LOG_ERROR

#define DPMI_ALLOC_NEEDEDMEM_HIGH 1

#define DPMI_DPL		3

#define GDT_ZERO		0
#define GDT_LDT			((0x1 << 3) | DPMI_DPL)
#define GDT_CODE		((0x2 << 3) | DPMI_DPL)
#define GDT_PROTCODE	((0x3 << 3) | DPMI_DPL)
#define GDT_DOSDATA		((0x4 << 3) | DPMI_DPL)
#define GDT_ENVIRONMENT	((0x5 << 3) | DPMI_DPL)

#define GDT_DOSSEG40	(0x40)

/* Amount of descriptors in each table */ 
#define GDT_SIZE		32
#define IDT_SIZE		256
#define LDT_SIZE		4096
#define INT_SIZE		256

#define TOTAL_SIZE		((GDT_SIZE+IDT_SIZE+LDT_SIZE+INT_SIZE)*8)

#define LDT_ENTRY(BLAH_) (BLAH_ << 3)

#define LDT_FIRSTSELECTOR  16

#define DPMI_ERROR_UNSUPPORTED					0x8001
#define DPMI_ERROR_DESCRIPTOR_UNAVAILABLE		0x8011
#define DPMI_ERROR_LINEAR_MEMORY_UNAVAILABLE	0x8012
#define DPMI_ERROR_PHYSICAL_MEMORY_UNAVAILABLE	0x8013
#define DPMI_ERROR_CALLBACK_UNAVAILABLE			0x8015
#define DPMI_ERROR_INVALID_SELECTOR				0x8022
#define DPMI_ERROR_INVALID_VALUE				0x8022
#define DPMI_ERROR_INVALID_HANDLE				0x8023
#define DPMI_ERROR_INVALID_CALLBACK				0x8024
#define DPMI_ERROR_INVALID_LINEAR_ADDRESS		0x8025

#define DPMI_XMSHANDLES_MAX				256
#define DPMI_XMSHANDLE_FREE				0xFFFF
#define DPMI_EXCEPTION_MAX				0x20
#define DPMI_PAGE_SIZE					(4*1024)
#define DPMI_REALMODE_CALLBACK_MAX		32
#define DPMI_REALMODE_STACKSIZE			4096
#define DPMI_PROTMODE_STACK_MAX			3
#define DPMI_PROTMODE_STACKSIZE			(4*1024)
#define DPMI_REALVEC_MAX				17
#define DPMI_SAVESTACK_MAX				1024

#define DPMI_CB_APIMSDOSENTRY_OFFSET	256*8
#define DPMI_CB_ENTERREALMODE_OFFSET	257*8
#define DPMI_CB_SAVESTATE_OFFSET		258*8
#define DPMI_CB_EXCEPTION_OFFSET		259*8
#define DPMI_CB_EXCEPTIONRETURN_OFFSET	260*8
#define DPMI_CB_VENDORENTRY_OFFSET		261*8

static bool g_hookHardwareInts = true;

void CMOS_SetRegister(Bitu regNr, Bit8u val);

static Bitu rmIndexToInt[DPMI_REALVEC_MAX] = 
{ 0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x1C };

// General functions
void CALLBACK32_SCF(bool val) 
{
	Bitu v_esp = 0;
	if (cpu.stack.big) v_esp = reg_esp; else v_esp = reg_sp;
	Bit32u tempf=mem_readd(SegPhys(ss)+v_esp+8) & 0xFFFFFFFE;
	Bit32u newCF=(val==true);
	mem_writed(SegPhys(ss)+v_esp+8,(tempf | newCF));
};

#define DPMI_CALLBACK_SCF(b) if (dpmi.client.bit32) CALLBACK32_SCF(b); else CALLBACK_SCF(b)

// **********************************************
// SetDescriptor Class
// **********************************************

#pragma pack(1)
class SetDescriptor : public Descriptor {
public:
	void Save(PhysPt address) {
		Bit32u* data = (Bit32u*)&saved;
		mem_writed(address,*data);
		mem_writed(address+4,*(data+1));
	}

	void SetBase(Bitu _base) { 
		saved.seg.base_24_31=_base >> 24;
		saved.seg.base_16_23=_base >> 16;
		saved.seg.base_0_15=_base;
	}
	void SetLimit (Bitu _limit) {
		if (_limit<1048576) saved.seg.g=false;
		else {
			saved.seg.g=true;
			_limit>>=12;
		}
		saved.seg.limit_0_15=_limit;
		saved.seg.limit_16_19=_limit>>16;
	}
	void SetOffset(Bitu _offset) {
		saved.gate.offset_0_15=_offset;
		saved.gate.offset_16_31=(_offset>>16);
	}
	void SetSelector(Bitu _selector) {
		saved.gate.selector=_selector;
	}
	void SetType(Bitu _type) {
		saved.seg.type=_type;
	}
	void Clear(void) {
		saved.fill[0]=saved.fill[1]=0;
	}
};
#pragma pack()

// **********************************************
// Shared Memory
// **********************************************

typedef struct SSharedMem {

	std::string name;
	Bitu		handle;
	Bitu		pages;

} TSharedMem;

std::list<TSharedMem*> g_sharedMemList;

// **********************************************
// DPMI Class
// **********************************************

class DPMI {

public:
	DPMI								(void);
	~DPMI								(void);

	// Settp/Startup methods
	void		Setup					(void);
	Bitu		Entrypoint				(void);
	RealPt		HookInterrupt			(Bitu num, Bitu intHandler);
	void		RestoreHookedInterrupt	(Bitu num, RealPt oldVec);
	void		CreateStackSpace		(void);
	bool		HasClient				(void)		{ return dpmi.client.have; };
	void		Terminate				(void);
	void		Reactivate				(void);

	// DPMI Services
	Bitu		AllocateLDTDescriptor	(Bitu count,Bitu & base);
	Bitu		AllocateLDTDescriptor2	(Bitu count,Bitu & base); // TEMP
	bool		AllocateMem				(Bitu size, Bitu& outHandle, Bitu& linear);
	Bitu		CreateAlias				(Bitu selector, Bit16u& alias);
	void		ReloadSegments			(Bitu selector);
	bool		SetAccessRights			(Bitu selector, SetDescriptor& desc, Bitu rights);
	bool		SetProtInterrupt		(Bitu num, Bitu selector, Bitu offset);
	
	// Special Interrupt handlers
	Bitu		Int2fHandler			(void);
	Bitu		Int31Handler			(void);

	// Exceptions
	Bitu		CreateException			(void);
	void		CreateException			(Bitu num, Bitu errorCode);
	Bitu		ExceptionReturn			(void);

	// Realmode callbacks
	bool		AllocateRealModeCallback(Bitu codeSel,Bitu codeOff,Bitu dataSel, Bitu dataOff, Bitu& segment, Bitu& offset);
	Bitu		RealModeCallback		(void);
	Bitu		RealModeCallbackReturn	(void);

	// Real mode reflection callbacks
	void		PrepareReflectToReal	(Bitu num);
	Bitu		CallRealIRETFrame		(bool callAsInt);
	Bitu		CallRealIRETFrameReturn	(void);
	Bitu		SimulateInt				(void);
	Bitu		SimulateIntReturn		(void);
	Bitu		ptorHandler				(void); 
	Bitu		ptorHandlerReturn		(void);
	Bitu		Int21Handler			(void);
	Bitu		Int21HandlerReturn		(void);
	Bitu		HWIntDefaultHandler		(void);
	void		RemoveIntCallbacks		(void);
	void		RestoreIntCallbacks		(void);

	// Switching modes
	void		SaveRegisterState		(Bitu num);
	void		LoadRegisterState		(Bitu num);	
	Bitu		EnterProtMode			(void);
	Bitu		EnterRealMode			(void);
	Bitu		RealSaveState			(void);
	Bitu		ProtSaveState			(void);

	// virtual interrupt flag
	bool		GetVirtualIntFlag		(void);
	void		SetVirtualIntFlag		(bool on);

	// Internal Stack for saving processor status
	void		PushStack				(Bitu val)	{ saveStack[savePtr++] = val;  };
	Bitu		PopStack				(void)		{ return saveStack[--savePtr]; };
	void		SaveSegments			(void);
	void		SaveRegister			(void);
	void		RestoreSegments			(void);
	void		RestoreRegister			(void);
	
	void		CopyRegistersToBuffer	(PhysPt data);
	void		LoadRegistersFromBuffer	(PhysPt data);

	void		ProvideRealModeStack	(PhysPt prStack, Bitu toCopy);
	void		UpdateRealModeStack		(void);

	// xms handle information
	void		SetXMSHandle			(Bitu handle);
	void		ClearXMSHandles			(void);
	void		FreeXMSHandle			(Bitu handle);
	
	// shared memory
	void		SetSharedMem			(const char* name, Bitu handle, Bitu pages);
	bool		GetSharedMem			(const char* name, Bitu& handle, Bitu& pages);
	bool		IsSharedMem				(Bitu handle);
	bool		RemoveSharedMem			(Bitu handle);

	// special msdos api stuff
	void		SetupPharlapSelectors	(void);
	bool		Pharlap_AllocateMem		(Bitu size, Bitu& outHandle, Bitu& linear);
	Bitu		GetSegmentFromSelector	(Bitu selector);
	bool		GetMsdosSelector		(Bitu realseg, Bitu realoff, Bitu &protsel, Bitu &protoff);
	void		API_Init_MSDOS			(void);
	Bitu		API_Entry_MSDOS			(void);
	Bitu		API_Int21_MSDOS			(void);

	// debug
	void		Debug_ShowDescriptors	(void);

private:
	Bitu		Mask					(Bitu value);

	Bitu saveStack[DPMI_SAVESTACK_MAX];
	Bitu savePtr;
	Bitu rm_ss, rm_sp;

	struct {
		struct {
			bool have;
			bool bit32;
			Bitu psp;
		} client;
		Bit32s mem_handle;		/* Handle for GDT/IDT */
		Bitu   idtBase,idtLimit;
		struct {
			PhysPt base;
			Bitu limit;
		} gdt,idt,ldt;
		struct {
			bool	inCall;
			bool	inUse;
			bool	stop;
			Bitu	callCount;
			Bitu	id;
			Bitu	dataSelector,dataOffset;
			Bitu	codeSelector,codeOffset;
			Bitu	realSegment ,realOffset;
		} rmCallback[DPMI_REALMODE_CALLBACK_MAX];

		RealPt	realModeVec	[DPMI_REALVEC_MAX];
		Bitu	oldRealVec	[DPMI_REALVEC_MAX];
		Bitu	defaultHWIntFromProtMode[DPMI_REALVEC_MAX];

		PhysPt	ptorint_base;		/* Base of pmode int handlers that reflect to realmode */
		Bitu	exceptionSelector[DPMI_EXCEPTION_MAX],exceptionOffset[DPMI_EXCEPTION_MAX];

		Bitu	xmsHandles[DPMI_XMSHANDLES_MAX];
		Bitu	protStack;

		Bitu	protStackSelector[DPMI_PROTMODE_STACK_MAX];
		Bitu	realStackSelector[DPMI_PROTMODE_STACK_MAX];
		Bitu	dataSelector	 [DPMI_PROTMODE_STACK_MAX]; 
		Bitu	protStackCurrent;

		Bitu	vIntFlag;

		bool	pharlap;
		bool	suppressRMCB;

		// Pharlap stuff
		Bitu	initialcs,initialds;
		Bitu	initialenv;
	} dpmi;

	Bit32u*	modIntTable;
	DPMI*	prevDPMI;
	std::vector<Bitu> dosSelectorList;
	
	Bitu	dtaAddress;
	Bitu	save_cs[2],save_ds[2],save_es[2],save_fs[2],save_gs[2],save_ss[2];
	Bitu	save_eax[2],save_ebx[2],save_ecx[2],save_edx[2],save_esi[2],save_edi[2];
	Bitu	save_ebp[2],save_esp[2],save_eip[2],save_fl[2];
};

struct {
	Bitu entry;
	Bitu ptorint;
	Bitu ptorintReturn;
	Bitu int31;
	Bitu int21;
	Bitu int21Return;
	Bitu int2f;
	Bitu enterpmode;
	Bitu enterrmode;
	Bitu protsavestate;
	Bitu realsavestate;
	Bitu simint;
	Bitu simintReturn;
	Bitu rmIntFrame;
	Bitu rmIntFrameReturn;
	Bitu rmCallbackReturn;
	Bitu exception;
	Bitu exceptionret;
	// Special callbacks for special dos extenders
	Bitu apimsdosentry;
	Bitu int21msdos;
} callback;

// ************************************************
// DPMI static functions
// ************************************************

#define SWITCH_TO_REALMODE 	CPU_SET_CRX(0,cpu.cr0 & ~CR0_PROTECTION); \
							CPU_SIDT(dpmi.idtLimit,dpmi.idtBase); \
							CPU_LIDT(256*4,0);

#define SWITCH_TO_PROTMODE	CPU_SET_CRX(0,cpu.cr0 | CR0_PROTECTION); \
							CPU_LIDT(dpmi.idtLimit,dpmi.idtBase);


static DPMI*	activeDPMI	= 0;
static Bit32u	originalIntTable[256];

bool DPMI_IsActive(void)
{
	return (cpu.cr0 & CR0_PROTECTION) && activeDPMI && activeDPMI->HasClient();
}

void DPMI_SetVirtualIntFlag(bool on) 
{
	if (activeDPMI) activeDPMI->SetVirtualIntFlag(on);
}

void DPMI_CreateException(Bitu num, Bitu errorCode)
{
	if (activeDPMI) activeDPMI->CreateException(num,errorCode);
}

// ************************************************
// DPMI Methods
// ************************************************

DPMI::DPMI(void)
{
	memset(&dpmi,0,sizeof(dpmi));
	savePtr			= 0; 
	dtaAddress		= 0; 
	rm_ss = rm_sp	= 0;
	modIntTable		= 0;
	prevDPMI		= activeDPMI;
};

DPMI::~DPMI(void)
{
	MEM_ReleasePages(dpmi.mem_handle);	
	dosSelectorList.clear();
	// TODO: Free all memory allocated with DOS_GetMemory
	// Activate previous dpmi
	activeDPMI = prevDPMI;
	// Restore hookHardwareInts ability which may be changed by a pharlap program
	if (!activeDPMI) g_hookHardwareInts = true;
};

void DPMI::ClearXMSHandles(void)
{
	for (Bitu i=0; i<DPMI_XMSHANDLES_MAX; i++) dpmi.xmsHandles[i]=DPMI_XMSHANDLE_FREE;
};

void DPMI::SetSharedMem(const char* name, Bitu handle, Bitu pages)
{
	TSharedMem* smem = new TSharedMem();
	smem->name	= name;
	smem->handle= handle;
	smem->pages = pages;
	g_sharedMemList.push_back(smem);
};

bool DPMI::GetSharedMem(const char* name, Bitu& handle, Bitu& pages)
{
	TSharedMem* smem;
	std::list<TSharedMem*>::iterator i;
	for(i = g_sharedMemList.begin(); i != g_sharedMemList.end(); i++) {
		smem = static_cast<TSharedMem*>(*i);
		if (smem->name.compare(name)==0) {
			handle = smem->handle;
			pages  = smem->pages;
			return true;
		}

	};
	return false;
};

bool DPMI::IsSharedMem(Bitu handle)
{
	std::list<TSharedMem*>::iterator i;
	for(i = g_sharedMemList.begin(); i != g_sharedMemList.end(); i++) {
		if ((*i)->handle==handle) return true;
	};
	return false;
};

bool DPMI::RemoveSharedMem(Bitu handle)
{
	TSharedMem* smem;
	std::list<TSharedMem*>::iterator i;
	for(i = g_sharedMemList.begin(); i != g_sharedMemList.end(); i++) {
		smem = static_cast<TSharedMem*>(*i);
		if (smem->handle==handle) {
			g_sharedMemList.remove(*i);
			delete smem;
			return true;
		}

	};
	return false;
};

void DPMI::SetXMSHandle(Bitu handle) {
	for (Bitu i=0; i<DPMI_XMSHANDLES_MAX; i++) {
		if (dpmi.xmsHandles[i]==DPMI_XMSHANDLE_FREE) {
			dpmi.xmsHandles[i]=handle;
			return;
		}
	};
	E_Exit("DPMI: No more DPMI XMS Handles available.");
};

void DPMI::FreeXMSHandle(Bitu handle) {
	
	RemoveSharedMem(handle);
	for (Bitu i=0; i<DPMI_XMSHANDLES_MAX; i++) {
		if (dpmi.xmsHandles[i]==handle) {
			dpmi.xmsHandles[i]=DPMI_XMSHANDLE_FREE;
			break;
		}
	}
};

void DPMI::SaveSegments(void) 
{
	if (savePtr+5>=DPMI_SAVESTACK_MAX) E_Exit("DPMI: Stack too small.");
	saveStack[savePtr++] = SegValue(ds);
	saveStack[savePtr++] = SegValue(es);
	saveStack[savePtr++] = SegValue(fs);
	saveStack[savePtr++] = SegValue(gs);
	saveStack[savePtr++] = SegValue(ss);
}

void DPMI::SaveRegister(void) 
{
	SaveSegments();
	if (savePtr+8>=DPMI_SAVESTACK_MAX) E_Exit("DPMI: Stack too small.");
	saveStack[savePtr++] = reg_eax;
	saveStack[savePtr++] = reg_ebx;
	saveStack[savePtr++] = reg_ecx;
	saveStack[savePtr++] = reg_edx;
	saveStack[savePtr++] = reg_esi;
	saveStack[savePtr++] = reg_edi;
	saveStack[savePtr++] = reg_ebp;
	saveStack[savePtr++] = reg_esp;
};

void DPMI::RestoreSegments(void) 
{
	CPU_SetSegGeneral(ss,saveStack[--savePtr]);
	CPU_SetSegGeneral(gs,saveStack[--savePtr]);
	CPU_SetSegGeneral(fs,saveStack[--savePtr]);
	CPU_SetSegGeneral(es,saveStack[--savePtr]);
	CPU_SetSegGeneral(ds,saveStack[--savePtr]);
};

void DPMI::RestoreRegister(void) 
{
	reg_esp = saveStack[--savePtr];
	reg_ebp = saveStack[--savePtr];
	reg_edi = saveStack[--savePtr];
	reg_esi = saveStack[--savePtr];
	reg_edx = saveStack[--savePtr];
	reg_ecx = saveStack[--savePtr];
	reg_ebx = saveStack[--savePtr];
	reg_eax = saveStack[--savePtr];
	RestoreSegments();
};

void DPMI::CopyRegistersToBuffer(PhysPt data)
{
	// Save Values in structure
	mem_writed(data+0x00, reg_edi);
	mem_writed(data+0x04, reg_esi);
	mem_writed(data+0x08, reg_ebp);
	mem_writed(data+0x0C, 0x0000);
	mem_writed(data+0x10, reg_ebx);
	mem_writed(data+0x14, reg_edx);
	mem_writed(data+0x18, reg_ecx);
	mem_writed(data+0x1C, reg_eax);
	mem_writew(data+0x20, reg_flags);
	mem_writew(data+0x22, SegValue(es));
	mem_writew(data+0x24, SegValue(ds));
	mem_writew(data+0x26, SegValue(fs));
	mem_writew(data+0x28, SegValue(gs));
	mem_writew(data+0x2A, reg_ip);
	mem_writew(data+0x2C, SegValue(cs));
	mem_writew(data+0x2E, reg_sp);
	mem_writew(data+0x30, SegValue(ss));
}

void DPMI::LoadRegistersFromBuffer(PhysPt data)
{
	reg_edi = mem_readd(data+0x00);
	reg_esi = mem_readd(data+0x04);
	reg_ebp = mem_readd(data+0x08);
	reg_ebx = mem_readd(data+0x10);
	reg_edx = mem_readd(data+0x14);
	reg_ecx	= mem_readd(data+0x18);
	reg_eax	= mem_readd(data+0x1C);
	CPU_SetFlagsw(mem_readw(data+0x20));
	SegSet16(es,mem_readw(data+0x22));
	SegSet16(ds,mem_readw(data+0x24));
	SegSet16(fs,mem_readw(data+0x26));
	SegSet16(gs,mem_readw(data+0x28));
	reg_esp	= mem_readw(data+0x2E);
	SegSet16(ss,mem_readw(data+0x30));	
	if (!dpmi.client.bit32) {
		reg_eax &= 0xFFFF;
		reg_ebx &= 0xFFFF;
		reg_ecx &= 0xFFFF;
		reg_edx &= 0xFFFF;
		reg_edi &= 0xFFFF;
		reg_esi &= 0xFFFF;
		reg_ebp &= 0xFFFF;
		reg_esp &= 0xFFFF;
	};
};

void DPMI::ProvideRealModeStack(PhysPt prStack, Bitu toCopy) 
{
	// Check stack, if zero provide it
	if ((SegValue(ss)==0) && (reg_sp==0)) {
		SegSet16(ss,rm_ss);
		reg_esp = rm_sp;
	} else {
		if (SegValue(ss)==rm_ss) reg_esp = rm_sp;
	};
	// We have to be in realmode here
	if (toCopy>0) {
		Bitu numBytes = toCopy*2;
		if (reg_esp<numBytes) E_Exit("DPMI:CopyStack: SP invalid.");
		PhysPt targetStack = SegValue(ss)+reg_esp-numBytes;
		MEM_BlockCopy(targetStack,prStack,numBytes);
		reg_esp -= numBytes;
	}
};

void DPMI::UpdateRealModeStack()
{
	if (SegValue(ss)==rm_ss) {
		if (reg_esp>DPMI_REALMODE_STACKSIZE) E_Exit("DPMI:Realmode stack out of range: %04X",reg_esp);
		rm_sp = reg_sp;
	}
};

Bitu DPMI::AllocateLDTDescriptor(Bitu count,Bitu & base) {
	
	SetDescriptor test;
	Bitu i=16, found=0;
	PhysPt address = dpmi.ldt.base + LDT_FIRSTSELECTOR*8;
	while (i<LDT_SIZE) {
		test.Load(address);
		if (test.saved.seg.p)	found=0;
		else					found++;	
		i++;
		address+=8;
		if (found==count) {
			/* Init allocated descriptors */
			test.Clear();
			test.SetType(DESC_DATA_EU_RW_NA);
			test.saved.seg.p   = 1;
			test.saved.seg.big = dpmi.client.bit32;
			test.saved.seg.dpl = DPMI_DPL;
			base	= ((i-found) << 3)|(4|DPMI_DPL);	/* Make it an LDT Entry */
			address	= dpmi.ldt.base+(base & ~7);
			for (;found>0;found--) {
				test.Save(address);
				address+=8;
			}
			return true;
		}
	}
	return false;
}

Bitu DPMI::AllocateLDTDescriptor2(Bitu count,Bitu & base) {
	
	static Bitu allocated = 0;

	SetDescriptor desc;
	Bitu nr = LDT_FIRSTSELECTOR + allocated;
	if (nr+count < LDT_SIZE) {
		desc.Clear();
		desc.SetType(DESC_DATA_EU_RW_NA);
		desc.saved.seg.p   = 1;
		desc.saved.seg.big = dpmi.client.bit32;
		desc.saved.seg.dpl = DPMI_DPL;
		base = (nr << 3)|(4|DPMI_DPL);	/* Make it an LDT Entry */
		allocated += count;
		Bitu address = dpmi.ldt.base+(base & ~7);
		for (;count>0;count--) {
			desc.Save(address);
			address+=8;
		}		
		return true;
	};
	return false;
}

Bitu DPMI::CreateAlias(Bitu selector, Bit16u& alias)
{
	Descriptor oldDesc;
	Bitu base;
	if (!cpu.gdt.GetDescriptor(selector,oldDesc))	{ alias = DPMI_ERROR_INVALID_SELECTOR; return false; };
	if (!AllocateLDTDescriptor(1,base))				{ alias = DPMI_ERROR_DESCRIPTOR_UNAVAILABLE; return false; };
	SetDescriptor desc;
	desc.Clear();
	desc.SetLimit(oldDesc.GetLimit());
	desc.SetBase (oldDesc.GetBase());
	desc.SetType (DESC_DATA_ED_RW_A);
	desc.saved.seg.p=1;
	desc.saved.seg.dpl = DPMI_DPL;
	desc.Save    (dpmi.ldt.base+(base & ~7));
	alias = base;
	return true;
};

void DPMI::ReloadSegments(Bitu selector)
{
	if (SegValue(cs)==selector) CPU_SetSegGeneral(cs,selector);
	if (SegValue(ds)==selector) CPU_SetSegGeneral(ds,selector);
	if (SegValue(es)==selector) CPU_SetSegGeneral(es,selector);
	if (SegValue(fs)==selector) CPU_SetSegGeneral(fs,selector);
	if (SegValue(gs)==selector) CPU_SetSegGeneral(gs,selector);
	if (SegValue(ss)==selector) CPU_SetSegGeneral(ss,selector);
};

Bitu DPMI::CreateException(void)
{
	// Division by Zero
	CreateException(0,0);
	return CBRET_NONE;
};

void DPMI::CreateException(Bitu num, Bitu errorCode)
{
	// Assuming Stack looks like a int has occured
	Bitu stack_eip,stack_cs,stack_flags;
	if (dpmi.client.bit32) {
		// Clean up stack
		stack_eip	= CPU_Pop32();
		stack_cs	= CPU_Pop32();
		stack_flags = CPU_Pop32();
		// Push values for exception handler
		CPU_Push32(SegValue(ss));
		CPU_Push32(reg_esp);
		CPU_Push32(stack_flags);
		CPU_Push32(stack_cs);
		CPU_Push32(stack_eip);						
		CPU_Push32(errorCode);
		CPU_Push32(GDT_PROTCODE);					// return cs
		CPU_Push32(DPMI_CB_EXCEPTIONRETURN_OFFSET);	// return eip
	} else {
		// Clean up stack
		stack_eip	= CPU_Pop16();
		stack_cs	= CPU_Pop16();
		stack_flags = CPU_Pop16();
		// Push values for exception handler
		CPU_Push16(SegValue(ss));
		CPU_Push16(reg_sp);
		CPU_Push16(stack_flags);
		CPU_Push16(stack_cs);
		CPU_Push16(stack_eip);						
		CPU_Push16(errorCode);
		CPU_Push16(GDT_PROTCODE);					// return cs
		CPU_Push16(DPMI_CB_EXCEPTIONRETURN_OFFSET);	// return eip
	};
	DPMI_LOG("DPMI: Exception occured : %04X (%04X:%08X)",num,dpmi.exceptionSelector[num],dpmi.exceptionOffset[num]);
	CPU_JMP(dpmi.client.bit32,dpmi.exceptionSelector[num],dpmi.exceptionOffset[num]);
};

Bitu DPMI::ExceptionReturn(void) 
{
	Bitu error;
	// Restore Registers
	Bitu newcs;
	if (dpmi.client.bit32) {
		error	= CPU_Pop32();
		reg_eip = CPU_Pop32();
		newcs	= CPU_Pop32();
		CPU_SetFlagsd(CPU_Pop32());
		reg_esp = CPU_Pop32();
		CPU_SetSegGeneral(ss,CPU_Pop32());
	} else {
		error	= CPU_Pop16();
		reg_eip = CPU_Pop16();
		newcs	= CPU_Pop16();
		CPU_SetFlagsw(CPU_Pop16());
		reg_esp = CPU_Pop16();
		CPU_SetSegGeneral(ss,CPU_Pop16());
	};
	DPMI_LOG("DPMI: Return from Exception. Jump to %04X:%08X",SegValue(cs),reg_eip);
	CPU_JMP(dpmi.client.bit32,newcs,reg_eip);
	return 0;
};

void DPMI::RemoveIntCallbacks()
// When switching dpmi clients, remove active callbacks from hardware int
{
	Bitu i;
	modIntTable = new Bit32u[256];
	// read and store interrupt table
	for (i=0; i<256; i++) modIntTable[i] = mem_readd(i*4);
	// set a clean interrupt table
	for (i=0; i<256; i++) mem_writed(i*4,originalIntTable[i]);
};

void DPMI::RestoreIntCallbacks()
{
	if (modIntTable) {
		// restore modified interrupt table
		for (int i=0; i<256; i++) mem_writed(i*4,modIntTable[i]);
		delete[] modIntTable; modIntTable = 0;
	}
};

bool DPMI::AllocateRealModeCallback(Bitu codeSel,Bitu codeOff,Bitu dataSel, Bitu dataOff, Bitu& segment, Bitu& offset)
{
	Bitu num = 0;
	for	(Bitu i=0; i<DPMI_REALMODE_CALLBACK_MAX; i++) if (!dpmi.rmCallback[i].inUse) { num = i; break; };
	if (num<DPMI_REALMODE_CALLBACK_MAX) {
		RealPt entry = CALLBACK_RealPointer(dpmi.rmCallback[num].id);
		dpmi.rmCallback[num].codeSelector	= codeSel;
		dpmi.rmCallback[num].codeOffset		= codeOff;
		dpmi.rmCallback[num].dataSelector	= dataSel;
		dpmi.rmCallback[num].dataOffset		= dataOff;
		dpmi.rmCallback[num].realSegment	= segment = RealSeg(entry);
		dpmi.rmCallback[num].realOffset		= offset  = RealOff(entry);
		dpmi.rmCallback[num].inCall			= false;
		dpmi.rmCallback[num].inUse			= true;
		dpmi.rmCallback[num].callCount		= 0;
		return true;
	} 
	return false;
};

Bitu DPMI::RealModeCallback(void)
{
	// Call protected mode function		
	Bitu num = mem_readw(PhysPt(SegPhys(cs)+reg_eip-2));
	num -= dpmi.rmCallback[0].id;
	if ((num>=DPMI_REALMODE_CALLBACK_MAX) || !dpmi.rmCallback[num].inUse) E_Exit("DPMI: Illegal Realmode callback %02X.",num);

	if (dpmi.rmCallback[num].inCall) DPMI_LOG("DPMI: Recursive Realmode callback %02X",num);
	if (dpmi.protStackCurrent>DPMI_PROTMODE_STACK_MAX) E_Exit("DPMI: Too many recursive Realmode callbacks. Stack failure.");

	PushStack(num);

	DPMI_LOG("DPMI: Realmode Callback %02X (%04X:%08X) enter",num,dpmi.rmCallback[num].codeSelector,dpmi.rmCallback[num].codeOffset);
	dpmi.rmCallback[num].inCall= true;
	dpmi.rmCallback[num].callCount++;

	// Important! Update realmode stack
	UpdateRealModeStack();	
	// Setup stack selector of real mode stack
	SetDescriptor desc;
	if (cpu.gdt.GetDescriptor(dpmi.realStackSelector[dpmi.protStackCurrent],desc)) {
		desc.SetBase (SegValue(ss)<<4);
		desc.SetLimit(0xFFFF);
		desc.Save	 (dpmi.ldt.base+(dpmi.realStackSelector[dpmi.protStackCurrent] & ~7));
	} else E_Exit("DPMI: RealmodeCB: Could not provide real mode stack descriptor.");
	/* Switch to protected mode */
	SWITCH_TO_PROTMODE
	// Setup dataSelector	
	Descriptor data;
	Bitu dataSelector;
	if (dpmi.rmCallback[num].dataSelector==0x0000)	dataSelector = dpmi.dataSelector[dpmi.protStackCurrent];
	else											dataSelector = dpmi.rmCallback[num].dataSelector;
	if (!cpu.gdt.GetDescriptor(dataSelector,data)) E_Exit("DPMI: Init RM-Callback failed.");

	DPMI_LOG("DPMI: CB: Writing RegData at = %04X:%04X",dataSelector,dpmi.rmCallback[num].dataOffset);
	// Prepare data buffer
	CopyRegistersToBuffer(PhysPt(data.GetBase()+dpmi.rmCallback[num].dataOffset));
	DPMI_LOG("DPMI: CB: Stored cs:ip = %04X:%04X",SegValue(cs),reg_ip);
	// setup registers for protected mode func
	CPU_SetSegGeneral(ds,dpmi.realStackSelector[dpmi.protStackCurrent]);	// DS:ESI = RM Stack
	reg_esi = reg_esp;				
	CPU_SetSegGeneral(es,dataSelector);				// ES:EDI = RM Register data
	reg_edi = dpmi.rmCallback[num].dataOffset;
	// SS:ESP = API stack
	CPU_SetSegGeneral(ss,dpmi.protStackSelector[dpmi.protStackCurrent++]);	
	reg_esp = DPMI_PROTMODE_STACKSIZE;
	// prepare stack for iret
	if (dpmi.client.bit32) CPU_Push32(reg_flags); else CPU_Push16(reg_flags);
	// Setup cs:ip to return to DPMI_ReturnFromRealModeCallback
	CPU_SetSegGeneral(cs,GDT_CODE);
	reg_eip = RealOff(CALLBACK_RealPointer(callback.rmCallbackReturn));
	// call protected mode func
	SetVirtualIntFlag(false);
	SETFLAGBIT(IF,false);
	SETFLAGBIT(TF,false);
	CPU_Push32(reg_flags);
	CPU_CALL(dpmi.client.bit32,dpmi.rmCallback[num].codeSelector,dpmi.rmCallback[num].codeOffset);
	return 0;
};

Bitu DPMI::RealModeCallbackReturn(void)
{
	// returning from protected mode function, now back to real mode
	Bitu num = PopStack();
	DPMI_LOG("DPMI: Realmode Callback leave %02X",num);
	dpmi.suppressRMCB = false;
	dpmi.rmCallback[num].inCall = false;
	dpmi.rmCallback[num].stop   = false;
	dpmi.rmCallback[num].callCount--;
	PhysPt data = PhysPt(SegPhys(es)+reg_edi);
	DPMI_LOG("DPMI: CB: Reading RegData at = %04X:%04X",SegValue(es),reg_edi);
	/* Swtich to real mode */
	SWITCH_TO_REALMODE
	dpmi.protStackCurrent--;
	// Restore Registers		
	LoadRegistersFromBuffer(data);		
	Bitu newCS = mem_readw(data+0x2C);
	Bitu newIP = mem_readw(data+0x2A);	
	UpdateRealModeStack();
	SetVirtualIntFlag(true);
	DPMI_LOG("DPMI: CB: Retored cs:ip = %04X:%04X (%d)",newCS,newIP);
	CPU_JMP(false,newCS,newIP);
	return 0;
};

static Bitu count = 0;

Bitu DPMI::CallRealIRETFrame(bool callAsInt)
{
	Bitu calledIP = mem_readd(SegPhys(ss)+reg_esp);
	Bitu calledCS = mem_readd(SegPhys(ss)+reg_esp+4);
	DPMI_LOG("DPMI: ENTER REAL PROC IRETF %04X:%08X",calledCS,calledIP);
	// Save changed registers
	PushStack(SegValue(cs));
	SaveRegister();
	Bitu toCopy = reg_cx;
	// Load Registers
	PhysPt data		= SegPhys(es) + reg_edi;
	PhysPt prStack	= SegPhys(ss) + reg_esp;
	LoadRegistersFromBuffer(data);
	PushStack(data);
	/* Switch to real mode */
	SWITCH_TO_REALMODE
	// Provide Stack
	ProvideRealModeStack(prStack,toCopy);
	// Push flags
	if (callAsInt) CPU_Push16(reg_flags);
	// Setup IP
	Bitu newCS = mem_readw(data+0x2C);
	Bitu newIP = mem_readw(data+0x2A);
	// Setup cs:ip to return to DPMI_CallRealIRETFrame callback
	SegSet16(cs,RealSeg(CALLBACK_RealPointer(callback.rmIntFrameReturn)));
	reg_ip = RealOff(CALLBACK_RealPointer(callback.rmIntFrameReturn));
	SetVirtualIntFlag(false);
	SETFLAGBIT(IF,false);
	SETFLAGBIT(TF,false);
	CPU_CALL(false,newCS,newIP);
	return 0;
}

Bitu DPMI::CallRealIRETFrameReturn(void)
{
	UpdateRealModeStack();
	// returning from realmode func
	DPMI_LOG("DPMI: LEAVE REAL PROC IRETF %d",count);
	/* Switch to protected mode */
	SWITCH_TO_PROTMODE
	// Save registers into real mode structure
	CopyRegistersToBuffer(PopStack());
	// Restore changed Resgisters
	RestoreRegister();
	Bitu newcs = PopStack();
	
	CPU_JMP(dpmi.client.bit32,newcs,reg_eip);

	SetVirtualIntFlag(true);
	DPMI_CALLBACK_SCF(false);
	return 0;
};

Bitu DPMI::SimulateInt(void)
{
	Bitu num = reg_bl;
	DPMI_LOG("DPMI: SIM INT %02X %04X called. cs = %04X",num,reg_ax,SegValue(cs));
	// Save changed registers
	PushStack(SegValue(cs));
	SaveRegister();
	Bitu toCopy = reg_cx;
	// Load Registers
	PhysPt data = SegPhys(es) + reg_edi;
	PhysPt prStack = SegPhys(ss) + reg_esp;
	LoadRegistersFromBuffer(data);
	PushStack(data);
	/* Switch to real mode */
	SWITCH_TO_REALMODE
	// Provide Stack
	ProvideRealModeStack(prStack,toCopy);
	// prepare for return
	SegSet16(cs,RealSeg(CALLBACK_RealPointer(callback.simintReturn)));
	reg_ip = RealOff(CALLBACK_RealPointer(callback.simintReturn));
	// Push flags from structure on stack
	DPMI_LOG("DPMI: SimInt1: StackInfo %04X:%04X (%02X %02X)",SegValue(ss),reg_esp,mem_readb(0xD0100+0x01FA),mem_readb(0xD0100+0x01FB));
	reg_flags = mem_readw(data+0x20);
	CPU_SW_Interrupt(num,0);
	DPMI_LOG("DPMI: SimInt2: StackInfo %04X:%04X (%02X %02X)",SegValue(ss),reg_esp,mem_readb(0xD0100+0x01FA),mem_readb(0xD0100+0x01FB));
	return 0;
};

Bitu DPMI::SimulateIntReturn(void)
{
	// returning from realmode func
	DPMI_LOG("DPMI: SIM INT return");
	DPMI_LOG("DPMI: SimIntRet1: StackInfo %04X:%04X (%02X %02X)",SegValue(ss),reg_esp,mem_readb(0xD0100+0x01FA),mem_readb(0xD0100+0x01FB));

	UpdateRealModeStack();
	/* Switch to protected mode */
	SWITCH_TO_PROTMODE
	// Save registers into real mode structure
	CopyRegistersToBuffer(PopStack());
	// Restore changed Resgisters
	RestoreRegister();
	Bitu newcs = PopStack();
	DPMI_LOG("DPMI: SimIntRet: JUMP to %04X:%08X",newcs,reg_eip);
	CPU_JMP(dpmi.client.bit32,newcs,reg_eip);
	SetVirtualIntFlag(true);
	// Free last realmode stack
	DPMI_CALLBACK_SCF(false);
	DPMI_LOG("DPMI: SimIntRet2: StackInfo %04X:%04X (%02X %02X)",SegValue(ss),reg_esp,mem_readb(0xD0100+0x01FA),mem_readb(0xD0100+0x01FB));
	return 0;
};

void DPMI::PrepareReflectToReal(Bitu num)
{
	// Save segment and stack register
	SaveSegments();
	PushStack(reg_esp); 
	PushStack(num);
	PushStack(reg_eip);
	PushStack(SegValue(cs));
	/* Swtich to real mode */
	SWITCH_TO_REALMODE
	// Setup cs:ip to return to intreturn
	Bitu retcs = RealSeg(CALLBACK_RealPointer(callback.ptorintReturn));
	Bitu retip = RealOff(CALLBACK_RealPointer(callback.ptorintReturn));
	
	SegSet16(cs,RealSeg(CALLBACK_RealPointer(callback.ptorintReturn)));
	reg_ip = RealOff(CALLBACK_RealPointer(callback.ptorintReturn));
	// setup stack
	SegSet16(ss,rm_ss);
	reg_esp = rm_sp;
}

Bitu DPMI::ptorHandler(void) 
{
	/* Pmode interrupt handler that maps the interrupt to realmode */
	Bitu num = reg_eip >> 3;
//	if (!dpmi.vIntFlag) {
//		if ((num>=0x08) && (num<=0x0F)) return 0;
//		if ((num>=0x70) && (num<=0x77)) return 0;
//	};
	PrepareReflectToReal(num);	
//	if (num==0x0F)
	DPMI_LOG("DPMI: INT %02X %04X called.",num,reg_ax);
	// Prepare flags for real int
	// CPU_SetFlagsw(reg_flags & 0x3ED5); // 0011111011010101b
	CPU_SW_Interrupt(num,0);
	return 0;
} 

Bitu DPMI::ptorHandlerReturn(void) 
// Return from reflected real mode int
{
	UpdateRealModeStack();
	/* Switch to protected mode */
	SWITCH_TO_PROTMODE
	// Restore Registers
	Bitu newcs	= PopStack();
	reg_eip		= PopStack();
	Bitu num	= PopStack();
	reg_esp		= PopStack(); 
	RestoreSegments();
//	if (num==0x0F) 
		DPMI_LOG("DPMI: INT %02X RETURN",num);	
	// hardware ints exit here
	if (((num>=0x08) && (num<=0x0F)) || ((num>=0x70) && (num<=0x77))) {
		SetVirtualIntFlag(true);	
		CPU_JMP(dpmi.client.bit32,newcs,reg_eip);
		return 0;
	}
	// Change flags on stack to reflect possible results from ints
	if (dpmi.client.bit32) {
		Bit32u oldFlags  = mem_readd(SegPhys(ss)+reg_esp+8) & ~FMASK_TEST;	// leave only flags that cannot be changed by int 
		Bit32u userFlags = reg_flags & FMASK_NORMAL;							// Mask out illegal flags not to change by int (0011111011010101b)
		mem_writed(SegPhys(ss)+reg_esp+8,oldFlags|userFlags);
	} else {
		Bit16u oldFlags  = mem_readw(SegPhys(ss)+reg_sp+4) & ~FMASK_TEST;		// leave only flags that cannot be changed by int 
		Bit16u userFlags = reg_flags & FMASK_NORMAL;							// Mask out illegal flags not to change by int (0011111011010101b)
		mem_writew(SegPhys(ss)+reg_sp+4,oldFlags|userFlags);
	};
	SetVirtualIntFlag(true);
	CPU_JMP(dpmi.client.bit32,newcs,reg_eip);
	return 0;
}

Bitu DPMI::Int21Handler(void) 
{
	// Check for exit
	if (reg_ah==0x4C) {
		DPMI_LOG("DPMI: INT 21: Terminating.");		
		Terminate();
	} 
	// Save segment and stack register
	PushStack(SegValue(ss));
	PushStack(reg_esp);
	PushStack(SegValue(ds));
	PushStack(SegValue(es));
	PushStack(SegValue(cs));

	/* Swtich to real mode */
	SWITCH_TO_REALMODE
	// Setup cs:ip to return to intreturn
	SegSet16(cs,RealSeg(CALLBACK_RealPointer(callback.int21Return)));
	reg_ip = RealOff(CALLBACK_RealPointer(callback.int21Return));
	// setup stack
	SegSet16(ss,rm_ss);
	reg_esp = rm_sp;
	// Call realmode interrupt
	DPMI_LOG("DPMI: INT 21 %04X called.",reg_ax);
	CPU_SW_Interrupt(0x21,0);
	if (reg_ah==0x4C) {
		// Shut doen dpmi and restore previous one
		delete this;
		if (activeDPMI) activeDPMI->Reactivate();
	}
	return 0;
};

Bitu DPMI::Int21HandlerReturn(void) 
{
	UpdateRealModeStack();
	/* Switch to protected mode */
	SWITCH_TO_PROTMODE
	// Restore Registers
	Bitu newcs = PopStack();
	CPU_SetSegGeneral(es,PopStack());
	CPU_SetSegGeneral(ds,PopStack());
	reg_esp = PopStack(); 
	CPU_SetSegGeneral(ss,PopStack());		
	// Set carry flag
	DPMI_CALLBACK_SCF(reg_flags & 1);
	DPMI_LOG("DPMI: INT 21 RETURN");	
	SetVirtualIntFlag(true);
	CPU_JMP(dpmi.client.bit32,newcs,reg_eip);
	return 0;
}

Bitu DPMI::HWIntDefaultHandler()
// Wir sind hier im Protected mode
// a) durch einen INTerrupt im protected mode 
// b) durch einen INTerrupt im real mode (durch RMCB)
{
	Bitu index	= mem_readw(PhysPt(SegPhys(cs)+reg_eip-2)) - dpmi.defaultHWIntFromProtMode[0];
	if (index>=DPMI_REALVEC_MAX) E_Exit("DPMI: Illegal realmode interrupt callback: %02X",index);
	Bitu num	= rmIndexToInt[index];

	RealPt vec  = RealGetVec(num);

	if (dpmi.rmCallback[index].callCount==0) {
		// INT PROT (Use Handler is already done).
		// Wenn rmcb noch in Realmode Int table installiert, dann originalMethode aufrufef
		if (vec==dpmi.realModeVec[index]) {
			// originalroutine aufrufen
			dpmi.rmCallback[index].stop = false;
			PrepareReflectToReal(num);
			CPU_Push16(reg_flags);
			SetVirtualIntFlag(false);
			SETFLAGBIT(IF,false);
			SETFLAGBIT(TF,false);
			CPU_CALL(false,RealSeg(dpmi.oldRealVec[index]),RealOff(dpmi.oldRealVec[index]));
		} else {
			// user real mode handler in real mode int table aktiv. 
			// Moeglich, dass dieser den RMCB von Hand noch aufruft
			// dann wird aber callCount>0 sein (da RMCB aktiv) und 
			// dann die alte Routine aufgerufen...
			// RMCB sperren, um einen erneuten Aufruf des User Handlers zu vermeiden,..

			// This is a hack for cybermage wich wont work otherwise. But why ?
			if (num==0x0F) {
				if (dpmi.suppressRMCB) {
					dpmi.suppressRMCB = false;
					return 0;
				} else {
					dpmi.suppressRMCB = true;
				}
			};					
			PrepareReflectToReal(num);
			SetVirtualIntFlag(false);
			SETFLAGBIT(IF,false);
			SETFLAGBIT(TF,false);
			CPU_Push16(reg_flags);
			CPU_CALL(false,RealSeg(vec),RealOff(vec));
		}
	} else {
		// INT REAL (vom RMCB aktiviert)
		// Falls user handler schon aktiv war (int von prot->reflected to real)
		// rufe original routine auf
		if (dpmi.rmCallback[index].stop) {
			dpmi.rmCallback[index].stop = false;
			PrepareReflectToReal(num);
			CPU_Push16(reg_flags);
			SetVirtualIntFlag(false);
			SETFLAGBIT(IF,false);
			SETFLAGBIT(TF,false);
			CPU_CALL(false,RealSeg(dpmi.oldRealVec[index]),RealOff(dpmi.oldRealVec[index]));			
		} else {
			// User routine wurde noch nicht aktiviert, callback aber ausgefhrt
			// falls spezieller protected mode handler aktiviert wurde, 
			// wird dieser jetzt aufgerufen (user routine im protected mode)			
			Descriptor gate;
			gate.Load(dpmi.idt.base+num*8);
			if ((gate.GetSelector()!=GDT_CODE) || (gate.GetOffset()!=RealOff(dpmi.defaultHWIntFromProtMode[index]))) {
				dpmi.rmCallback[index].stop = true;	// vermeide rekursion
				CPU_JMP(dpmi.client.bit32,gate.GetSelector(),gate.GetOffset());
			} else {
				// kein spezieller Protmode handler - Rufe originalroutine auf
				PrepareReflectToReal(num);
				CPU_Push16(reg_flags);
				SetVirtualIntFlag(false);
				SETFLAGBIT(IF,false);
				SETFLAGBIT(TF,false);
				CPU_CALL(false,RealSeg(dpmi.oldRealVec[index]),RealOff(dpmi.oldRealVec[index]));			
			};
		};
	}	
	return 0;
};

void DPMI::SaveRegisterState(Bitu num)
// Copy Current Registers to structure
{
	return;
	save_cs[num] = SegValue(cs);
	save_ds[num] = SegValue(ds);
	save_es[num] = SegValue(es);
	save_fs[num] = SegValue(fs);
	save_gs[num] = SegValue(gs);
	save_ss[num] = SegValue(ss);
	save_eip[num] = reg_eip;
	save_eax[num] = reg_eax;
	save_ebx[num] = reg_ebx;
	save_ecx[num] = reg_ecx;
	save_edx[num] = reg_edx;
	save_esi[num] = reg_esi;
	save_edi[num] = reg_edi;
	save_ebp[num] = reg_ebp;
	save_esp[num] = reg_esp;
	save_fl [num] = reg_flags;	
};

void DPMI::LoadRegisterState(Bitu num)
// Copy Current Registers to structure
{
	return;
	CPU_SetSegGeneral(fs,save_fs[num]);
	CPU_SetSegGeneral(gs,save_gs[num]);
	reg_eax = save_eax[num];
	reg_ebx = save_ebx[num];
	reg_ecx = save_ecx[num];
	reg_edx = save_edx[num];
	reg_esi = save_esi[num];
	reg_edi = save_edi[num];
	reg_flags = save_fl [num];	
};

Bitu DPMI::EnterProtMode(void) {

	/* Save real mode register state */
	SaveRegisterState(0);

	/* Switch to protected mode */
	SWITCH_TO_PROTMODE

	CPU_SetSegGeneral(ds,reg_ax);
	CPU_SetSegGeneral(es,reg_cx);
	CPU_SetSegGeneral(ss,reg_dx);
	CPU_SetSegGeneral(ds,reg_ax);

	if (dpmi.client.bit32) { 
		reg_esp = reg_ebx;
		CPU_JMP(true,reg_si,reg_edi);
	} else {
		reg_sp = reg_bx; 
		CPU_JMP(false,reg_si,reg_di);
	};
	
	/* Load prot mode register state (all other unchanged registers */
	LoadRegisterState(1);

	DPMI_LOG("DPMI: Switch to protected mode.");
	return 0;
}

Bitu DPMI::EnterRealMode(void) {

	/* Save Prot Mode Registers */
	SaveRegisterState(1);

	/* Swtich to real mode */
	SWITCH_TO_REALMODE
	// (E)BP will be preserved across the mode switch call so it can be used as a pointer.
	// TODO: If interrupts are disabled when the mode switch procedure is invoked, 
	// they will not be re-enabled by the DPMI host (even temporarily).
	SegSet16(ds,reg_ax);
	SegSet16(es,reg_cx);
	SegSet16(ss,reg_dx);
	SegSet16(fs,0);
	SegSet16(gs,0);
	if (dpmi.client.bit32) { 
		reg_esp = reg_ebx;
		CPU_JMP(true,reg_si,reg_edi);
	} else {
		reg_sp = reg_bx; 
		CPU_JMP(false,reg_si,reg_di);
	};

	/* Load real mode register state (all other unchanged registers) */
	LoadRegisterState(0);
	DPMI_LOG("DPMI: Switch to real mode.");
	return CBRET_NONE;
};

Bitu DPMI::RealSaveState(void) 
{
	return CBRET_NONE;
	/* Save Protected mode state */
	if (reg_al==0) {		
		PhysPt data = SegPhys(es) + reg_edi;
		mem_writew(data+ 0,save_cs[1]);
		mem_writew(data+ 2,save_ds[1]);
		mem_writew(data+ 4,save_es[1]);
		mem_writew(data+ 6,save_fs[1]);
		mem_writew(data+ 8,save_gs[1]);
		mem_writew(data+10,save_ss[1]);
		mem_writed(data+12,save_eax[1]);
		mem_writed(data+16,save_ebx[1]);		
		mem_writed(data+20,save_ecx[1]);		
		mem_writed(data+24,save_edx[1]);		
		mem_writed(data+28,save_esi[1]);		
		mem_writed(data+32,save_edi[1]);		
		mem_writed(data+36,save_ebp[1]);		
		mem_writed(data+40,save_esp[1]);		
		mem_writed(data+44,save_fl [1]);		
		DPMI_LOG("DPMI: Prot Save State.");
	} else if (reg_al==1) {
		/* restore state of prot mode registers */
		PhysPt data = SegPhys(es) + reg_edi;
		save_cs [1] = mem_readw(data+ 0);
		save_ds [1] = mem_readw(data+ 2);
		save_es [1] = mem_readw(data+ 4);
		save_fs [1] = mem_readw(data+ 6);
		save_gs [1] = mem_readw(data+ 8);
		save_ss [1] = mem_readw(data+10);
		save_eax[1] = mem_readd(data+12);
		save_ebx[1] = mem_readd(data+16);
		save_ecx[1] = mem_readd(data+20);
		save_edx[1] = mem_readd(data+24);
		save_edi[1] = mem_readd(data+28);
		save_esi[1] = mem_readd(data+32);
		save_ebp[1] = mem_readd(data+36);
		save_esp[1] = mem_readd(data+40);
//		save_eip[1] = mem_readd(data+44);
		save_fl [1] = mem_readd(data+44);
		DPMI_LOG("DPMI: Prot Restore State.");
	};
	return CBRET_NONE;
};

Bitu DPMI::ProtSaveState(void) 
{
	return CBRET_NONE;
	if (reg_al==0) {		
		/* Save State of real mode registers */
		PhysPt data = SegPhys(es) + reg_edi;
		mem_writew(data+ 0,save_cs[0]);	
		mem_writew(data+ 2,save_ds[0]);
		mem_writew(data+ 4,save_es[0]);
		mem_writew(data+ 6,save_fs[0]);
		mem_writew(data+ 8,save_gs[0]);
		mem_writew(data+10,save_ss[0]);
		mem_writed(data+12,save_eax[0]);
		mem_writed(data+16,save_ebx[0]);		
		mem_writed(data+20,save_ecx[0]);		
		mem_writed(data+24,save_edx[0]);		
		mem_writed(data+28,save_esi[0]);		
		mem_writed(data+32,save_edi[0]);		
		mem_writed(data+36,save_ebp[0]);		
		mem_writed(data+40,save_esp[0]);		
		mem_writed(data+44,save_eip[0]);		
		mem_writed(data+48,save_fl [0]);		
		DPMI_LOG("DPMI: Real Save State.");
	} else if (reg_al==1) {
		/* restore state of real mode registers */
		PhysPt data = SegPhys(es) + reg_edi;
		save_cs [0] = mem_readw(data+ 0);
		save_ds [0] = mem_readw(data+ 2);
		save_es [0] = mem_readw(data+ 4);
		save_fs [0] = mem_readw(data+ 6);
		save_gs [0] = mem_readw(data+ 8);
		save_ss [0] = mem_readw(data+10);
		save_eax[0] = mem_readd(data+12);
		save_ebx[0] = mem_readd(data+16);
		save_ecx[0] = mem_readd(data+20);
		save_edx[0] = mem_readd(data+24);
		save_edi[0] = mem_readd(data+28);
		save_esi[0] = mem_readd(data+32);
		save_ebp[0] = mem_readd(data+36);
		save_esp[0] = mem_readd(data+40);
		save_eip[0] = mem_readd(data+44);
		save_fl [0] = mem_readd(data+48);
		DPMI_LOG("DPMI: Real Restore State.");
	};
	return CBRET_NONE;
};

bool DPMI::GetVirtualIntFlag(void)
// only to call from int 31 cos it uses the pushed flags on int stack
{
	if (dpmi.client.bit32)	return (mem_readd(SegPhys(ss)+reg_esp+8) & FLAG_IF)>0;
	else					return (mem_readd(SegPhys(ss)+reg_sp+4) & FLAG_IF)>0; 
};

void DPMI::SetVirtualIntFlag(bool on)
{
	dpmi.vIntFlag = 1; //on;
};

bool DPMI::AllocateMem(Bitu size, Bitu& outHandle, Bitu& linear)
{
	Bitu pages	= (size/DPMI_PAGE_SIZE) + ((size%DPMI_PAGE_SIZE)>0);	// Convert to 4KB pages
	outHandle	= MEM_AllocatePages(pages,true);
	linear		= outHandle*DPMI_PAGE_SIZE;
	if (outHandle!=0) SetXMSHandle(outHandle);
	return (outHandle!=0);
};

bool DPMI::SetAccessRights(Bitu selector, SetDescriptor& desc, Bitu rights)
{
	// must equal caller DPL
	if (((rights & 0x60)>>5)!=DPMI_DPL) {
		DPMI_LOG("DPMI: Set Rights %04X : %04X failure (dpl=%02X)",selector,rights,(rights & 0x60)>>5);
//		return false;
	}
	// must be 1
	if ((rights & 0x10)==0)	{
		DPMI_LOG_ERROR("DPMI: Set Rights %04X : %04X failure (must be 1)",selector,rights);
		return false;
	};
	// must be 0
	if (dpmi.client.bit32 && desc.saved.seg.p && (rights & 0x2000)) {
		DPMI_LOG_ERROR("DPMI: Set Rights %04X : %04X failure (must be 0)",selector,rights);
		return false;	
	};
	// all tests passed, set rights for 16 + 32 Bit
	desc.SetType (rights&0x1F);
	desc.saved.seg.dpl = (rights&0x60)>>5;
	desc.saved.seg.dpl = DPMI_DPL;
	desc.saved.seg.p   = (rights&0x80)>0;
	// extended rights for 32 Bit apps
	if (dpmi.client.bit32) {
		desc.saved.seg.avl = (rights&0x1000)>0;
		desc.saved.seg.r   = (rights&0x2000)>0;
		desc.saved.seg.big = (rights&0x4000)>0;
		desc.saved.seg.g   = (rights&0x8000)>0;
	};	
	return true;
};

Bitu DPMI::Int31Handler(void) 
{
	switch (reg_ax) {
	
		case 0x0000:{// Allocate LDT Descriptors
					Bitu base;
					Descriptor desc;
					if (AllocateLDTDescriptor(reg_cx,base)) { 
						reg_ax = base;
						DPMI_LOG("DPMI: 0000: Allocate %d descriptors: %04X",reg_cx,base);
						DPMI_CALLBACK_SCF(false);
					} else {
						DPMI_LOG_ERROR("DPMI: 0000: Allocate %d descriptors failure",reg_cx);
						reg_ax = DPMI_ERROR_DESCRIPTOR_UNAVAILABLE; 
						DPMI_CALLBACK_SCF(true);
					};
					}; break;
		case 0x0001:{// Free Descriptor
					SetDescriptor desc;
					if (cpu.gdt.GetDescriptor(reg_bx,desc)) {
						desc.saved.seg.p = 0;
						desc.Save	(dpmi.ldt.base+(reg_bx & ~7));
						DPMI_LOG("DPMI: 0001: Free Descriptor: %04X",reg_bx);
						DPMI_CALLBACK_SCF(false);					
					} else {
						DPMI_LOG_ERROR("DPMI: 0001: Free Descriptor failure : %04X",reg_bx);
						reg_ax = DPMI_ERROR_INVALID_SELECTOR;
						DPMI_CALLBACK_SCF(true);					
					};
					}; break;
		case 0x0002:{// Segment to Descriptor
					SetDescriptor desc; Bitu base;
					if (AllocateLDTDescriptor(1,base)) { 
						desc.Load	 (dpmi.ldt.base+(base & ~7));
						desc.SetLimit(0xFFFF);
						desc.SetBase (reg_bx<<4);
						desc.saved.seg.dpl=3;
						desc.Save	 (dpmi.ldt.base+(base & ~7));
						reg_ax = base;
						DPMI_LOG("DPMI: 0000: Seg %04X to Desc: %04X",reg_bx,base);
						DPMI_CALLBACK_SCF(false);
					} else {
						// No more Descriptors available
						DPMI_LOG_ERROR("DPMI: 0002: No more Descriptors available.");
						reg_ax = DPMI_ERROR_DESCRIPTOR_UNAVAILABLE; 
						DPMI_CALLBACK_SCF(true);
					};					
					}; break;
		case 0x0003:// Get Next Selector Increment Value
					reg_ax = 8;				
					DPMI_LOG("DPMI: 0003: Get Selector Inc Value: %04X",reg_ax);
					DPMI_CALLBACK_SCF(false);
					break;
		case 0x0004:// undocumented (reserved) lock selector 
		case 0x0005:// undocumented (reserved) unlock selector 
					DPMI_LOG("DPMI: 0004: Undoc: (un)lock selector",reg_ax);
					DPMI_CALLBACK_SCF(true);					
					break;
		case 0x0006:{ // Get Segment Base Address
					SetDescriptor desc;
					if (cpu.gdt.GetDescriptor(reg_bx,desc)) {
						DPMI_LOG("DPMI: 0006: Get Base %04X : B:%08X",reg_bx,desc.GetBase());
						reg_cx = (Bit16u)(desc.GetBase()>>16);
						reg_dx = (Bit16u)(desc.GetBase()&0xFFFF);
						DPMI_CALLBACK_SCF(false);					
					} else {
						DPMI_LOG_ERROR("DPMI: 0006: Invalid Selector: %04X",reg_bx);
						reg_ax = DPMI_ERROR_INVALID_SELECTOR;
						DPMI_CALLBACK_SCF(true);					
					};
					}; break;
		case 0x0007:{// Set Segment base address
					SetDescriptor desc;
					if (cpu.gdt.GetDescriptor(reg_bx,desc)) {
						Bitu base;
						if (!dpmi.client.bit32) base = (reg_cl<<16)+reg_dx;
						else					base = (reg_cx<<16)+reg_dx;
						desc.SetBase(base);
						desc.Save	(dpmi.ldt.base+(reg_bx & ~7));
						ReloadSegments(reg_bx);
						DPMI_CALLBACK_SCF(false);
						DPMI_LOG("DPMI: 0007: Set Base %04X : B:%08X",reg_bx,base);
					} else {
						DPMI_LOG_ERROR("DPMI: 0007: Invalid Selector: %04X",reg_bx);
						reg_ax = DPMI_ERROR_INVALID_SELECTOR;
						DPMI_CALLBACK_SCF(true);					
					};
					}; break;
		case 0x0008:{// Set Segment limit
					SetDescriptor desc;				
					if ((!dpmi.client.bit32) && (reg_cx!=0)) {
						// 16-bit DPMI implementations can not set segment limits greater 
						// than 0FFFFh (64K) so CX must be zero when calling						
						DPMI_LOG_ERROR("DPMI: 0008: Set Segment Limit invalid: %04X (%04X%04X)",reg_bx,reg_cx,reg_dx);
						reg_ax = DPMI_ERROR_INVALID_VALUE;
						DPMI_CALLBACK_SCF(true);
					} else if (cpu.gdt.GetDescriptor(reg_bx,desc)) {
						desc.SetLimit((reg_cx<<16)+reg_dx);
						desc.Save	 (dpmi.ldt.base+(reg_bx & ~7));
						ReloadSegments(reg_bx);
						DPMI_CALLBACK_SCF(false);
						DPMI_LOG("DPMI: 0008: Set Limit %08X",(reg_cx<<16)+reg_dx);
					} else {
						DPMI_LOG_ERROR("DPMI: 0008: Invalid Selector: %04X",reg_bx);
						reg_ax = DPMI_ERROR_INVALID_SELECTOR;
						DPMI_CALLBACK_SCF(true);					
					};
					}; break;
		case 0x0009:{// Set Descriptor Access Rights
					SetDescriptor desc;
					Bit8u rcl = reg_cl;
					Bit8u rch = reg_ch;
					if (cpu.gdt.GetDescriptor(reg_bx,desc)) {
						if (!SetAccessRights(reg_bx,desc,reg_cx)) {
							DPMI_LOG_ERROR("DPMI: 0009: Set Rights %04X : failure",reg_bx);
							reg_ax = DPMI_ERROR_INVALID_VALUE;
							DPMI_CALLBACK_SCF(true);
							break;
						};
						desc.Save(dpmi.ldt.base+(reg_bx & ~7));
						ReloadSegments(reg_bx);
						DPMI_CALLBACK_SCF(false);
						DPMI_LOG("DPMI: 0009: Set Rights %04X : %04X",reg_bx,reg_cx);
					} else {
						DPMI_LOG_ERROR("DPMI: 0009: Set Rights %04X : invalid selector",reg_bx);
						reg_ax = DPMI_ERROR_DESCRIPTOR_UNAVAILABLE;
						DPMI_CALLBACK_SCF(true);
					};
					}; break;
		case 0x000A:{// Create Alias Descriptor
					Descriptor desc;
					if (CreateAlias(reg_bx, reg_ax)) {
						DPMI_LOG("DPMI: 000A: Create Alias : %04X - %04X",reg_bx,reg_ax);
						DPMI_CALLBACK_SCF(false);
					} else {
						DPMI_CALLBACK_SCF(true);
						DPMI_LOG_ERROR("DPMI: 000A: Invalid Selector: %04X",reg_bx);
					}; }; break;
		case 0x000B:{//Get Descriptor
					SetDescriptor desc;
					if (cpu.gdt.GetDescriptor(reg_bx,desc)) {
						desc.Save(SegPhys(es)+Mask(reg_edi));
						DPMI_CALLBACK_SCF(false);
						DPMI_LOG("DPMI: 000B: Get Descriptor %04X : B:%08X L:%08X",reg_bx,desc.GetBase(),desc.GetLimit());
					} else {
						DPMI_LOG_ERROR("DPMI: 000B: Get Descriptor %04X : failure",reg_bx);
						reg_ax = DPMI_ERROR_DESCRIPTOR_UNAVAILABLE;
						DPMI_CALLBACK_SCF(true);
					};
					}; break;
		case 0x000C:{//Set Descriptor
					SetDescriptor desc;
					if (cpu.gdt.GetDescriptor(reg_bx,desc)) {
						desc.Load   (SegPhys(es)+Mask(reg_edi));
						Bitu rights = (mem_readb(SegPhys(es)+Mask(reg_edi)+6)<<8) + mem_readb(SegPhys(es)+Mask(reg_edi)+5);
						if (!SetAccessRights(reg_bx,desc,rights)) {
							DPMI_LOG_ERROR("DPMI: 000C: Set Rights %04X : failure",reg_bx);
							reg_ax = DPMI_ERROR_INVALID_VALUE;
							DPMI_CALLBACK_SCF(true);
							break;
						};
						// TEMP : Test
/*						if (!dpmi.client.bit32) {
							if (desc.GetLimit()>0xFFFF) {
								DPMI_LOG_ERROR("DPMI: 000C: Set Limit %04X : failure (%08X)",reg_bx,desc.GetLimit());
								desc.SetLimit(0xFFFF);
							}
							if (desc.GetBase ()>0xFFFFFF) {
								DPMI_LOG_ERROR("DPMI: 000C: Set Base %04X : failure (%08X)",reg_bx,desc.GetBase());
								desc.SetBase(desc.GetBase() % 0xFFFFFF);
							}
						}*/
						desc.Save(dpmi.ldt.base+(reg_bx & ~7));
						ReloadSegments(reg_bx);
						DPMI_LOG("DPMI: 000B: Set Descriptor %04X : B:%08X L:%08X : P %01X",reg_bx,desc.GetBase(),desc.GetLimit(),desc.saved.seg.p);
						DPMI_CALLBACK_SCF(false);
					} else {
						DPMI_LOG_ERROR("DPMI: 000C: Set Descriptor %04X failed",reg_bx);
						reg_ax = DPMI_ERROR_DESCRIPTOR_UNAVAILABLE;
						DPMI_CALLBACK_SCF(true);
					};
					}; break;
		case 0x000D:{ // Allocate specific LDT Descriptor : TODO: Support it
					DPMI_LOG("DPMI: 000D: Alloc Specific LDT Selector: %04X",reg_bx);
					SetDescriptor desc; 
					if (cpu.gdt.GetDescriptor(reg_bx,desc)) {
//						if (!desc.saved.seg.p) {
							desc.saved.seg.p = 1;
//							desc.SetLimit(0xDEADAAAA);
//							desc.SetBase (0xDEADBBBB);
							desc.Save	 (dpmi.ldt.base+(reg_bx & ~7));						
							DPMI_CALLBACK_SCF(false);					 
							break;
//						} else {
//							DPMI_LOG_ERROR("DPMI: 000D: Invalid Selector: %04X",reg_bx);
//							reg_ax = DPMI_ERROR_DESCRIPTOR_UNAVAILABLE;
//						};
					} else  reg_ax = DPMI_ERROR_INVALID_SELECTOR;
					DPMI_CALLBACK_SCF(true);					 
					}; break;
		case 0x0100:{// Allocate DOS Memory Block
					Bit16u blocks = reg_bx;
					DPMI_LOG("DPMI: 0100: Allocate DOS Mem: (%04X Blocks)",blocks);
					if (DOS_AllocateMemory(&reg_ax,&blocks)) {
						// Allocate Selector for block
						SetDescriptor desc; Bitu base; Bitu numDesc;
						numDesc = reg_bx/0x1000 + ((reg_bx%0x1000)>0);
						if (AllocateLDTDescriptor(numDesc,base)) { 
							reg_dx = base;
							// First selector
							if (numDesc>1) {								
								Bitu descBase = reg_ax*16;
								Bitu length	  = reg_bx*16;
								desc.Load	 (dpmi.ldt.base+(base & ~7));
								desc.SetBase (descBase);
								desc.SetLimit(dpmi.client.bit32?length:0xFFFF);
								desc.Save	 (dpmi.ldt.base+(base & ~7));
								for (Bitu i=1; i<numDesc; i++) {							
									base     += 8;
									descBase += 0x10000;
									length   -= 0x10000;
									desc.Load	 (dpmi.ldt.base+(base & ~7));
									desc.SetBase (descBase);
									desc.SetLimit((length<=0x10000)?length-1:0xFFFF);
									desc.Save	 (dpmi.ldt.base+(base & ~7));
								}
							} else {
								// one descriptor
								desc.Load	 (dpmi.ldt.base+(base & ~7));
								desc.SetBase (reg_ax*16);
								desc.SetLimit(reg_bx*16);
								desc.Save	 (dpmi.ldt.base+(base & ~7));
							};
							DPMI_LOG("DPMI: 0100: Allocation success: (%04X)",blocks);
							DPMI_CALLBACK_SCF(false);
						} else {
							// No more Descriptors available
							DPMI_LOG_ERROR("DPMI: 0100: Allocation failure: %04X (No Descriptor)",blocks);
							reg_ax = DPMI_ERROR_DESCRIPTOR_UNAVAILABLE; 
							DPMI_CALLBACK_SCF(true);
						};
					} else {
						DPMI_LOG("DPMI: 0100: Allocation failure : %04X (R:%04X)",reg_bx,blocks);
						reg_bx = blocks;
						reg_ax = 0x008; // Insufficient memory
						DPMI_CALLBACK_SCF(true);
					};
					};break;
		case 0x0101:{// Free DOS Memory Block
					SetDescriptor desc;
					if (cpu.gdt.GetDescriptor(reg_dx,desc)) {
						Bitu sel = reg_dx;
						Bitu seg = desc.GetBase()>>4;
						DOS_MCB mcb(seg-1);
						Bitu size = mcb.GetSize()*16;
						if (DOS_FreeMemory(seg)) {
							while (size>0) {
								desc.Load(dpmi.ldt.base+(sel & ~7));
								desc.saved.seg.p = 0;
								desc.Save(dpmi.ldt.base+(sel & ~7));
								size -= (size>=0x10000)?0x10000:size;
								sel+=8;
							};
							DPMI_CALLBACK_SCF(false);
							DPMI_LOG("DPMI: 0101: Free Dos Mem: %04X",reg_dx);
							break;
						}
					} 
					DPMI_LOG_ERROR("DPMI: 0101: Invalid Selector: %04X",reg_bx);
					reg_ax = DPMI_ERROR_INVALID_SELECTOR;
					DPMI_CALLBACK_SCF(true);
					};break;
		case 0x0200:{// Get Real Mode Interrupt Vector		
					RealPt vec = RealGetVec(reg_bl);
					reg_cx = RealSeg(vec);
					reg_dx = RealOff(vec);
					DPMI_LOG("DPMI: 0200: Get Real Int Vector %02X (%04X:%04X)",reg_bl,reg_cx,reg_dx);
					DPMI_CALLBACK_SCF(false);
					}; break;
		case 0x0201:{// Set Real Mode Interrupt Vector		
					DPMI_LOG("DPMI: 0201: Set Real Int Vector %02X (%04X:%04X)",reg_bl,reg_cx,reg_dx);
					RealSetVec(reg_bl,RealMake(reg_cx,reg_dx));
					DPMI_CALLBACK_SCF(false);
					}; break;
		case 0x0202:// Get Processor Exception Handler Vector
					if (reg_bl<DPMI_EXCEPTION_MAX) {
						reg_cx  = dpmi.exceptionSelector[reg_bl];
						if (dpmi.client.bit32)	reg_edx = dpmi.exceptionOffset[reg_bl];
						else					reg_dx	= dpmi.exceptionOffset[reg_bl];
						DPMI_LOG("DPMI: 0202: Get Exception Vector %02X (%04X:%08X)",reg_bl,reg_cx,reg_edx);
						DPMI_CALLBACK_SCF(false);
					} else {
						DPMI_LOG_ERROR("DPMI: Get Exception Vector failed : %02X",reg_bl);
						DPMI_CALLBACK_SCF(true);
					};
					break;
		case 0x0203:// Set Processor Exception Handler Vector
					if (reg_bl<DPMI_EXCEPTION_MAX) {
						dpmi.exceptionSelector[reg_bl]	= reg_cx;
						dpmi.exceptionOffset[reg_bl]	= Mask(reg_edx);
						DPMI_LOG("DPMI: 0203: Set Exception Vector %02X (%04X:%08X)",reg_bl,reg_cx,reg_edx);
						DPMI_CALLBACK_SCF(false);
					} else {
						DPMI_LOG_ERROR("DPMI: Set Exception Vector failed : %02X",reg_bl);
						DPMI_CALLBACK_SCF(true);
					};
					break;
		case 0x0204:{// Get Protected Mode Interrupt Vector
					SetDescriptor gate;
					if (dpmi.pharlap) {
						// TEMP: Unknown if this is correct
						gate.Load(cpu.idt.GetBase()+reg_bl*8);
						reg_cx  = gate.GetSelector();
						if (dpmi.client.bit32)	reg_edx = gate.GetOffset();
						else					reg_dx	= gate.GetOffset();
					} else {
						gate.Load(dpmi.idt.base+reg_bl*8);
						reg_cx  = gate.GetSelector();
						if (dpmi.client.bit32)	reg_edx = gate.GetOffset();
						else					reg_dx	= gate.GetOffset();
					}
					DPMI_LOG("DPMI: 0204: Get Prot Int Vector %02X (%04X:%08X)",reg_bl,reg_cx,reg_edx);
					DPMI_CALLBACK_SCF(false);
					}; break;
		case 0x0205:{// Set Protected Mode Interrupt Vector			
					DPMI_LOG("DPMI: 0205: Set Prot Int Vector %02X (%04X:%08X)",reg_bl,reg_cx,reg_edx);
					SetProtInterrupt(reg_bl,reg_cx,reg_edx);
					DPMI_CALLBACK_SCF(false);
					}; break;
		case 0x0300:// Simulate Real Mode Interrupt
					SimulateInt();
					break;
		case 0x0301:// Call Real Mode Procedure With RET Frame
					CallRealIRETFrame(false);
					break;
		case 0x0302:// Call Real Mode Procedure With IRET Frame
					CallRealIRETFrame(true);
					break;
		case 0x0303:{//Allocate Real Mode Callback Address
					Bitu num = 0;
					for	(Bitu i=0; i<DPMI_REALMODE_CALLBACK_MAX; i++) if (!dpmi.rmCallback[i].inUse) { num = i; break; };
					if (num<DPMI_REALMODE_CALLBACK_MAX) {
						Bitu segment, offset;
						if (AllocateRealModeCallback(SegValue(ds),Mask(reg_esi),SegValue(es),Mask(reg_edi),segment,offset)) {
							reg_cx = segment; reg_dx = offset;
							DPMI_LOG("DPMI: 0303: Allocate Callback (%04X:%04X)",reg_cx,reg_dx);
							DPMI_CALLBACK_SCF(false);
							break;
						}
					} 
					DPMI_LOG_ERROR("DPMI: 0303: Callback unavailable.");
					reg_ax = DPMI_ERROR_CALLBACK_UNAVAILABLE;
					DPMI_CALLBACK_SCF(true);
					}; break;		
		case 0x0304:{//Free Real Mode Call-Back Address
					Bitu num = DPMI_REALMODE_CALLBACK_MAX;
					for	(Bitu i=0; i<DPMI_REALMODE_CALLBACK_MAX; i++) {
						if ((dpmi.rmCallback[i].realSegment==reg_cx) && (dpmi.rmCallback[i].realOffset==reg_dx)) {
							num = i; break;
						}
					}
					if (num<DPMI_REALMODE_CALLBACK_MAX) {
						DPMI_LOG("DPMI: 0304: Free Callback (%04X:%04X)",reg_cx,reg_dx);
						dpmi.rmCallback[num].inUse = false;
						DPMI_CALLBACK_SCF(false);
					} else {
						DPMI_LOG_ERROR("DPMI: 0304: Invalid Callback");
						reg_ax = DPMI_ERROR_INVALID_CALLBACK;
						DPMI_CALLBACK_SCF(true);					
					}
					};break;
		case 0x0305:{// Get State Save/Restore Addresses
					RealPt entry = CALLBACK_RealPointer(callback.realsavestate);
					reg_bx = RealSeg(entry);
					reg_cx = RealOff(entry);
					reg_si = GDT_PROTCODE;
					if (dpmi.client.bit32)	reg_edi= DPMI_CB_SAVESTATE_OFFSET;
					else					reg_di = DPMI_CB_SAVESTATE_OFFSET;
					reg_ax = 0; // 20 bytes buffer needed
					DPMI_CALLBACK_SCF(false);
					DPMI_LOG("DPMI: 0305: Get State Save/Rest : R:%04X:%04X P:%04X:%08X",reg_bx,reg_cx,reg_si,reg_edi);
					}; break;	
		case 0x0306:{// Get raw mode switch address
					RealPt entry=CALLBACK_RealPointer(callback.enterpmode);
					reg_bx = RealSeg(entry);
					reg_cx = RealOff(entry);
					reg_si = GDT_PROTCODE;
					if (dpmi.client.bit32)	reg_edi= DPMI_CB_ENTERREALMODE_OFFSET;
					else					reg_di = DPMI_CB_ENTERREALMODE_OFFSET;
					DPMI_CALLBACK_SCF(false);
					DPMI_LOG("DPMI: 0306: Get Raw Switch      : R:%04X:%04X P:%04X:%08X",reg_bx,reg_cx,reg_si,reg_edi);
					}; break;	
		case 0x0400:// Get Version
					DPMI_LOG("DPMI: 0400: Get Version");
					reg_ax = 90;		// 0.9
					reg_bx = 0x0003;	// 32 Bit DPMI
					reg_cl = 0x04;		// 486
					reg_dx = 0x0870;	// FIXME: Read this from ports
					DPMI_CALLBACK_SCF(false);					
					break;
		case 0x0401: // Get DPMI Capabilities - always fails in 0.9
					DPMI_LOG("DPMI: 0401: Get Capabilities");
					reg_ax = 0x08;		// CONVENTIONAL MEMORY MAPPING capability supported
//					reg_ax = 0x00;
					DPMI_CALLBACK_SCF(false);					
					break;
		case 0x0500:{// Get Free Memory Information
					PhysPt data = SegPhys(es) + Mask(reg_edi);
					Bitu large	= MEM_FreeLargest();		
					Bitu total	= MEM_FreeTotal();			
					Bitu size	= large;
					mem_writed(data+0x00,large*DPMI_PAGE_SIZE);	// Size in bytes
					mem_writed(data+0x04,large);				// total number of pages
					mem_writed(data+0x08,large);				// largest block in pages
					mem_writed(data+0x0C,size);					// total linear address space in pages
					mem_writed(data+0x10,total);				// num of unlocked pages - no info
					mem_writed(data+0x14,total);				// num of physical pages not in use
					mem_writed(data+0x18,size);					// total num of physical pages
					mem_writed(data+0x1C,total);				// free linear address space in pages
					mem_writed(data+0x20,0xFFFFFFFF);			// size of paging file in pages
					mem_writed(data+0x24,0xFFFFFFFF);			// reserved
					mem_writed(data+0x28,0xFFFFFFFF);			// reserved
					mem_writed(data+0x2C,0xFFFFFFFF);			// reserved
					DPMI_CALLBACK_SCF(false);
					DPMI_LOG("DPMI: 0500: Get Mem Info (%d KB total)",total*4);
					}; break;
		case 0x0501:{// Allocate Memory	
					Bitu handle,linear;
					Bitu length = (reg_bx<<16)+reg_cx;

					bool res = false;
					if (dpmi.pharlap)	res = Pharlap_AllocateMem(length,handle,linear);
					else				res = AllocateMem(length,handle,linear);
					if (res) {
						reg_si = handle>>16; 
						reg_di = handle&0xFFFF;
						reg_bx = linear>>16;
						reg_cx = linear&0xFFFF;
						DPMI_CALLBACK_SCF(false);
						// TEMP
//						Bitu total  = MEM_FreeLargest();				// in KB					
//						DPMI_LOG_ERROR("DPMI: 0501: Allocation success: H:%04X%04X (%d KB) (R:%d KB)",reg_si,reg_di,length/1024 + ((length%1024)>0),total*4);
					} else {
						reg_ax = DPMI_ERROR_PHYSICAL_MEMORY_UNAVAILABLE;
						DPMI_CALLBACK_SCF(true);
						// TEMP
						Bitu total = MEM_FreeLargest();				// in KB					
						DPMI_LOG("DPMI: 0501: Allocation failure (%d KB) (R:%d KB)",length/1024 + ((length%1024)>0),total*4);
					};
					}; break;
		case 0x0502://Free Memory Block
					DPMI_LOG("DPMI: 0502: Free Mem: H:%04X%04X",reg_si,reg_di);
					MEM_ReleasePages((reg_si<<16)+reg_di);					
					FreeXMSHandle((reg_si<<16)+reg_di);
					DPMI_CALLBACK_SCF(false);
					break;
		case 0x0503:{//Resize Memory Block
					Bitu linear,newHandle;
					Bitu newByte		= (reg_bx<<16)+reg_cx;
					Bitu newSize		= (newByte/DPMI_PAGE_SIZE)+((newByte & (DPMI_PAGE_SIZE-1))>0);
					MemHandle handle	= (reg_si<<16)+reg_di;
					DPMI_LOG("DPMI: 0503: Resize Memory: H:%08X (%d KB)",handle,newSize*4);
					if (MEM_ReAllocatePages(handle,newSize,true)) {
						linear = handle * DPMI_PAGE_SIZE;
						reg_si = (Bit16u)(handle>>16);
						reg_di = (Bit16u)(handle&0xFFFF);
						reg_bx = (Bit16u)(linear>>16);
						reg_cx = (Bit16u)(linear&0xFFFF);
						DPMI_CALLBACK_SCF(false);					
					} else if (AllocateMem(newByte,newHandle,linear)) {							
						// Not possible, try to allocate
						DPMI_LOG("DPMI: 0503: Reallocated Memory: %d KB",newSize*4);
						reg_si = (Bit16u)(newHandle>>16);
						reg_di = (Bit16u)(newHandle&0xFFFF);
						reg_bx = (Bit16u)(linear>>16);
						reg_cx = (Bit16u)(linear&0xFFFF);
						// copy contents
						Bitu size = MEM_AllocatedPages(handle);
						if (newSize<size) size = newSize;
						MEM_BlockCopy(linear,handle*DPMI_PAGE_SIZE,size*DPMI_PAGE_SIZE);
						// Release old handle
						MEM_ReleasePages(handle);
						FreeXMSHandle(handle);
						DPMI_CALLBACK_SCF(false);
					} else {
						DPMI_LOG_ERROR("DPMI: 0503: Memory unavailable . %08X",newSize);
						reg_ax = DPMI_ERROR_PHYSICAL_MEMORY_UNAVAILABLE;
						DPMI_CALLBACK_SCF(true);
					};
					}; break;
		case 0x0506:// Get Page Attributes
					DPMI_LOG("DPMI: 0506: Get Page Attributes");
					reg_ax = DPMI_ERROR_UNSUPPORTED;
					DPMI_CALLBACK_SCF(true);
					break;
		case 0x0507:// Set Page Attributes
					DPMI_LOG("DPMI: 0507: Set Page Attributes");
					DPMI_CALLBACK_SCF(false);
					break;
		case 0x0509:{//Map Conventional Memory in Memory Block
					Bitu xmsAddress	= Mask(reg_esi)*DPMI_PAGE_SIZE;
					Bitu handle		= Mask(reg_esi);
					Bitu offset		= Mask(reg_ebx);
					Bitu numPages	= Mask(reg_ecx);
					Bitu linearAdr	= Mask(reg_edx);
					if ((linearAdr & (DPMI_PAGE_SIZE-1)) || ((xmsAddress+offset) & (DPMI_PAGE_SIZE-1))) {
						// Not page aligned
						DPMI_LOG_ERROR("DPMI: Cannot map conventional memory (address not page aligned).");
						reg_ax = DPMI_ERROR_INVALID_LINEAR_ADDRESS;
						DPMI_CALLBACK_SCF(true);					
						break;
					}
					linearAdr/=DPMI_PAGE_SIZE;
					xmsAddress/=DPMI_PAGE_SIZE;
					for (;numPages;numPages--) PAGING_MapPage(linearAdr++,xmsAddress++);
					DPMI_CALLBACK_SCF(false);
					}; break;
		case 0x0600:{//Lock Linear Region
					DPMI_LOG("DPMI: 0600: Lock Linear Region");
					Bitu address = (reg_bx<<16)+reg_cx;
					Bitu size	 = (reg_si<<16)+reg_di;
					DPMI_CALLBACK_SCF(false);
					}; break;
		case 0x0601://unlock Linear Region
					DPMI_LOG("DPMI: 0601: Unlock Linear Region");
					DPMI_CALLBACK_SCF(false);
					break;
		case 0x0602:// Mark Real Mode Region as Pageable
					DPMI_LOG("DPMI: 0602: Mark Realmode Region pageable");
					DPMI_CALLBACK_SCF(false);					
					break;
		case 0x0603:// Relock Real Mode Region
					DPMI_LOG("DPMI: 0603: Relock Realmode Region");
					DPMI_CALLBACK_SCF(false);					
					break;
		case 0x0604:// Get page size
					reg_bx=0; reg_cx=DPMI_PAGE_SIZE;
					DPMI_LOG("DPMI: 0604: Get Page Size: %04X",reg_cx);
					DPMI_CALLBACK_SCF(false);
					break;
		case 0x0701:// Undocumented discard page contents 
					DPMI_LOG("DPMI: 0701: Discard Page contents");
					DPMI_CALLBACK_SCF(true);
					break;					
		case 0x0702://Mark Page as Demand Paging Candidate
					DPMI_LOG("DPMI: 0702: Mark page as demand paging candidate");
					DPMI_CALLBACK_SCF(false);
					break;
		case 0x0703://Discard Page contents
					DPMI_LOG("DPMI: 0703: Discard Page contents");
					DPMI_CALLBACK_SCF(false);
					break;
		case 0x0800:{//Physical Address Mapping
					// bx and cx remain the same linear address = physical address
					Bitu phys	= (reg_bx<<16) + reg_cx;
					Bitu linear	= (reg_bx<<16) + reg_cx;
					Bitu size   = (reg_si<<16) + reg_di;
					//MEM_MapPagesDirect(linear/DPMI_PAGE_SIZE,phys/DPMI_PAGE_SIZE,size/DPMI_PAGE_SIZE);
					//Bitu linear = phys;
					reg_bx		= linear>>16; 
					reg_cx		= linear & 0xFFFF;
					DPMI_LOG_ERROR("DPMI: 0800: Phys-adr-map not supported : Start:%08X (Size:%08X) - Linear:%08X.",phys,size,linear);
					DPMI_CALLBACK_SCF(false);
					}; break;
		case 0x0801:// Free physical address mapping
					DPMI_LOG("DPMI: 0801: Free physical address mapping");
					DPMI_CALLBACK_SCF(false);
					break;
		case 0x0900://Get and Disable Virtual Interrupt State
					reg_al = dpmi.vIntFlag;
					dpmi.vIntFlag = 0;
					DPMI_LOG("DPMI: 0900: Get and disbale vi : %01X",reg_al);
					DPMI_CALLBACK_SCF(false);					
					break;
		case 0x0901://Get and Enable Virtual Interrupt State
					reg_al = dpmi.vIntFlag;
					dpmi.vIntFlag = 1;
					DPMI_LOG("DPMI: 0901: Get and enable vi  : %01X",reg_al);
					DPMI_CALLBACK_SCF(false);					
					break;
		case 0x0902:{//Get Virtual Interrupt State
					reg_al = 0; //dpmi.vIntFlag;
					DPMI_LOG("DPMI: 0902: Get vi             : %01X",reg_al);
					DPMI_CALLBACK_SCF(false);
					}; break;
		case 0x0A00:{//Get Vendor Specific API Entry Point
					char name[256];
					MEM_StrCopy(SegPhys(ds)+Mask(reg_esi),name,255);
					LOG(LOG_MISC,LOG_WARN)("DPMI: Get API: %s",name);
					if (strcmp(name,"MS-DOS")==0) {
						CPU_SetSegGeneral(es,GDT_PROTCODE);
						if (dpmi.client.bit32)	reg_edi = DPMI_CB_APIMSDOSENTRY_OFFSET;
						else					reg_di	= DPMI_CB_APIMSDOSENTRY_OFFSET;
						API_Init_MSDOS();
						DPMI_CALLBACK_SCF(false);
					} else if (strstr(name,"HWINT_SUPPORT")!=0) {
						reg_ax = DPMI_ERROR_UNSUPPORTED;
						DPMI_CALLBACK_SCF(true);
					} else if (strstr(name,"CE_SUPPORT")!=0) {
						reg_ax = DPMI_ERROR_UNSUPPORTED;
						DPMI_CALLBACK_SCF(true);
					} else if (strstr(name,"PHARLAP")!=0) {
						CPU_SetSegGeneral(es,GDT_PROTCODE);
						if (dpmi.client.bit32)	reg_edi = DPMI_CB_APIMSDOSENTRY_OFFSET;
						else					reg_di	= DPMI_CB_APIMSDOSENTRY_OFFSET;
						API_Init_MSDOS();
						DPMI_CALLBACK_SCF(false);
						dpmi.pharlap = true;
					} else {
						reg_ax = DPMI_ERROR_UNSUPPORTED;
						DPMI_CALLBACK_SCF(true);
					}
					}; break;
		case 0x0D00:{//Allocate Shared Memory
					char name[256];
					PhysPt data		= SegPhys(es)+Mask(reg_edi);
					Bitu length		= mem_readd(data);
					Bitu pages		= (length/DPMI_PAGE_SIZE)+((length%DPMI_PAGE_SIZE)>0);
					Bitu handle		= mem_readd(data+0x08);
					Bitu linear		= mem_readd(data+0x0C);
					Bitu strOffset	= mem_readd(data+0x10);
					Bitu strSelect	= mem_readw(data+0x14);

					Descriptor desc;
					if (!cpu.gdt.GetDescriptor(strSelect,desc)) {
						DPMI_LOG_ERROR("DPMI: 0D00: shared memory: invalid name selector");
						reg_ax = DPMI_ERROR_INVALID_VALUE;
						DPMI_CALLBACK_SCF(true);
						return false;
					};	
					MEM_StrCopy(desc.GetBase()+strOffset,name,256);

					// Already allocated ?
					if (!GetSharedMem(name,handle,pages)) {
						if (!AllocateMem(length,handle,linear)) {
							DPMI_LOG_ERROR("DPMI: 0D00: Allocation shared failure %s (%d KB)",name,pages*4);
							reg_ax = DPMI_ERROR_PHYSICAL_MEMORY_UNAVAILABLE;
							DPMI_CALLBACK_SCF(true);
							break;						
						};
						// Init first paragraph with zeros
						for (Bitu i=0; i<16; i++) mem_writeb(linear+i,0);
						SetSharedMem(name,handle,pages);
						DPMI_LOG("DPMI: 0D00: Allocate shared memory %s (%d KB) ",name,pages*4);
					} else {
						linear = handle*DPMI_PAGE_SIZE;
						DPMI_LOG("DPMI: 0D00: Reuse shared memory %s (%d KB) ",name,pages*4);
					};

					mem_writed(data+0x04,pages*DPMI_PAGE_SIZE);
					mem_writed(data+0x08,handle);
					mem_writed(data+0x0C,linear);
					DPMI_CALLBACK_SCF(false);
					}; break; 
		case 0x0B00:// Set debug watchpoint
		case 0x0B01:// Clear debug watchpoint
					DPMI_CALLBACK_SCF(true);	
					break;
		case 0x0E00:// Get Coprocessor Status
					DPMI_LOG("DPMI: 0E00: Get Coprocessor status");
					reg_ax = 0x45; // nope, no coprocessor
					DPMI_CALLBACK_SCF(false);	
					break;
		case 0x0E01:// Set Coprocessor Emulation
					DPMI_LOG("DPMI: 0E01: Set Coprocessor emulation");
					DPMI_CALLBACK_SCF(true);	// failure
					break;
		default	   :LOG(LOG_MISC,LOG_ERROR)("DPMI: Unsupported func %04X",reg_ax);
					reg_ax = DPMI_ERROR_UNSUPPORTED;	
					DPMI_CALLBACK_SCF(true);	// failure
					break;
	};
	return 0;
}

Bitu DPMI::Int2fHandler(void) 
{
	// Only available in ProtectedMode
	// LOG(LOG_MISC,LOG_WARN)("DPMI: 0x2F %04x",reg_ax);
	switch (reg_ax) {	
	case 0x1686:		/* Get CPU Mode */
		reg_ax = 0;
		break;
	case 0x168A:	// Only available in protected mode
		// Get Vendor-Specific API Entry Point
		char name[256];
		MEM_StrCopy(SegPhys(ds)+Mask(reg_esi),name,255);
		LOG(LOG_MISC,LOG_WARN)("DPMI: 0x2F 0x168A: Get Specific API :%s",name);
		if (strcmp(name,"MS-DOS")==0) {
			CPU_SetSegGeneral(es,GDT_PROTCODE);
			if (dpmi.client.bit32)	reg_edi = DPMI_CB_APIMSDOSENTRY_OFFSET;
			else					reg_di	= DPMI_CB_APIMSDOSENTRY_OFFSET;
			reg_al  = 0x00;	// Success, whatever they want...
			API_Init_MSDOS();
		};
		break;
	default : // reflect to real
			  ptorHandler();
			  break;
	}
	return 0;
};

// *********************************************************************
// Callbacks and Callback-Returns
// *********************************************************************

static Bitu DPMI_Exception(void)				{ if (activeDPMI) return activeDPMI->CreateException();			return 0;};
static Bitu DPMI_ExceptionReturn(void)			{ if (activeDPMI) return activeDPMI->ExceptionReturn();			return 0;};
static Bitu DPMI_RealModeCallback(void)			{ if (activeDPMI) return activeDPMI->RealModeCallback();		return 0;};
static Bitu DPMI_RealModeCallbackReturn(void)	{ if (activeDPMI) return activeDPMI->RealModeCallbackReturn();	return 0;};
static Bitu DPMI_CallRealIRETFrame(void)		{ if (activeDPMI) return activeDPMI->CallRealIRETFrame(true);	return 0;};
static Bitu DPMI_CallRealIRETFrameReturn(void)	{ if (activeDPMI) return activeDPMI->CallRealIRETFrameReturn(); return 0;};
static Bitu DPMI_SimulateInt(void)				{ if (activeDPMI) return activeDPMI->SimulateInt();				return 0;};
static Bitu DPMI_SimulateIntReturn(void)		{ if (activeDPMI) return activeDPMI->SimulateIntReturn();		return 0;};
static Bitu DPMI_ptorHandler(void)				{ if (activeDPMI) return activeDPMI->ptorHandler();				return 0;};
static Bitu DPMI_ptorHandlerReturn(void)		{ if (activeDPMI) return activeDPMI->ptorHandlerReturn();		return 0;};
static Bitu DPMI_Int21Handler(void)				{ if (activeDPMI) return activeDPMI->Int21Handler();			return 0;};
static Bitu DPMI_Int21HandlerReturn(void)		{ if (activeDPMI) return activeDPMI->Int21HandlerReturn();		return 0;};
static Bitu DPMI_HWIntDefaultHandler(void)		{ if (activeDPMI) return activeDPMI->HWIntDefaultHandler();		return 0;};
static Bitu DPMI_EnterProtMode(void)			{ if (activeDPMI) return activeDPMI->EnterProtMode();			return 0;};
static Bitu DPMI_EnterRealMode(void)			{ if (activeDPMI) return activeDPMI->EnterRealMode();			return 0;};
static Bitu DPMI_RealSaveState(void)			{ if (activeDPMI) return activeDPMI->RealSaveState();			return 0;};
static Bitu DPMI_ProtSaveState(void)			{ if (activeDPMI) return activeDPMI->ProtSaveState();			return 0;}; 
static Bitu DPMI_Int2fHandler(void)				{ if (activeDPMI) return activeDPMI->Int2fHandler();			return 0;}; 
static Bitu DPMI_Int31Handler(void)				{ if (activeDPMI) return activeDPMI->Int31Handler();			return 0;}; 
static Bitu DPMI_API_Int21_MSDOS(void)			{ if (activeDPMI) return activeDPMI->API_Int21_MSDOS();			return 0;};
static Bitu DPMI_API_Entry_MSDOS(void)			{ if (activeDPMI) return activeDPMI->API_Entry_MSDOS();			return 0;};


// ****************************************************************
// Setup stuff
// ****************************************************************

bool DPMI::SetProtInterrupt(Bitu num, Bitu selector, Bitu offset)
{
	// Nobody messes with the div0 vector
	if (num==0) return true;

	SetDescriptor gate;
	gate.Clear();
	gate.saved.seg.p=1;
	gate.SetSelector(selector);
	gate.SetOffset	(Mask(offset));
	gate.SetType	(dpmi.client.bit32?DESC_386_INT_GATE:DESC_286_INT_GATE);
	gate.saved.seg.dpl = DPMI_DPL;
	gate.Save(dpmi.idt.base+num*8);
	// Special Pharlap stuff
	if (dpmi.pharlap) {
		gate.Save(cpu.idt.GetBase()+num*8);						
		DPMI_LOG("DPMI: 0205: Pharlap: Set Prot Int Vector %02X (%04X:%08X)",reg_bl,reg_cx,reg_edx);
	}
	return true;
};

RealPt DPMI::HookInterrupt(Bitu num, Bitu intHandler)
{
	// Setup realmode hook	
	RealPt	oldVec;
	Bitu	segment, offset;
	// Allocate Realmode callback
	RealPt func = CALLBACK_RealPointer(intHandler);
	if (AllocateRealModeCallback(GDT_CODE,RealOff(func),0x0000,0x0000,segment,offset)) {
		oldVec		= RealGetVec(num);
		RealSetVec(num,RealMake(segment,offset));
	} else E_Exit("DPMI: Couldnt allocate Realmode-Callback for INT %04X",num);
	// Setup protmode hook
	func = CALLBACK_RealPointer(intHandler);
	SetProtInterrupt(num,GDT_CODE,RealOff(func));
	return oldVec;
}

void DPMI::RestoreHookedInterrupt(Bitu num, RealPt oldVec)
{
	//.Restore hooked int 
	RealSetVec(num,oldVec);
	RealPt func = CALLBACK_RealPointer(callback.ptorint);
	SetDescriptor gate;
	gate.Load		(dpmi.idt.base+num*8);
	gate.SetSelector(GDT_CODE);
	gate.SetOffset	(RealOff(func));
	gate.Save		(dpmi.idt.base+num*8);
}

void DPMI::Debug_ShowDescriptors(void)
{
	char out1[512];
	SetDescriptor desc;
	Bitu i=0;
	PhysPt address = dpmi.ldt.base;
	while (i<LDT_SIZE) {
		desc.Load(address);
		sprintf(out1,"%04X: b:%08X type: %02X parbg",(i<<3)|4|DPMI_DPL,desc.GetBase(),desc.saved.seg.type);
		DPMI_LOG(out1);
		sprintf(out1,"      l:%08X dpl : %01X  %1X%1X%1X%1X%1X",desc.GetLimit(),desc.saved.seg.dpl,desc.saved.seg.p,desc.saved.seg.avl,desc.saved.seg.r,desc.saved.seg.big,desc.saved.seg.g);
		DPMI_LOG(out1);
		address+=8; i++;
	};
};

void DPMI::Terminate(void) 
{
//	Debug_ShowDescriptors();
	Bitu i;
	cpu.cpl	= 0;
	dpmi.client.have = false;
	// 1. Clear the LDT 
	for (i=0; i<LDT_SIZE*8; i++) mem_writeb(dpmi.ldt.base+i,0);
	// 2.Deallocate Callbacks
	for (i=0; i<DPMI_REALMODE_CALLBACK_MAX; i++) dpmi.rmCallback[i].inUse = false;

	// 3.Deallocate XMS Memory
	for (i=0; i<DPMI_XMSHANDLES_MAX; i++) {
		if ((dpmi.xmsHandles[i]!=0xFFFF) && !IsSharedMem(dpmi.xmsHandles[i]))
			MEM_ReleasePages(dpmi.xmsHandles[i]);
	}

	if (g_hookHardwareInts) {
		// 4.Restore hooked ints
		for (i=0; i<DPMI_REALVEC_MAX; i++) {
			if (RealGetVec(rmIndexToInt[i])!=0)
				RestoreHookedInterrupt(rmIndexToInt[i],dpmi.oldRealVec[i]);
		}
	}
};

void DPMI::CreateStackSpace(void)
{
	// Alloocate protected mode stack
	Bitu i,base;
	SetDescriptor desc;
	if (!AllocateLDTDescriptor(DPMI_PROTMODE_STACK_MAX,base)) E_Exit("DPMI: Couldnt allocate protected mode stack for callbacks");
	for (i=0; i<DPMI_PROTMODE_STACK_MAX; i++) {
		dpmi.protStackSelector[i] = base;
		desc.Load	 (dpmi.ldt.base+(base & ~7));
		desc.SetLimit(DPMI_PROTMODE_STACKSIZE-1);
		desc.SetBase (dpmi.protStack + i*DPMI_PROTMODE_STACKSIZE);
		desc.Save	 (dpmi.ldt.base+(base & ~7));		
		base += 8;
	};
	// Allocate Descriptors for real mode stack in realmode callback func
	if (!AllocateLDTDescriptor(DPMI_PROTMODE_STACK_MAX,base)) E_Exit("DPMI: Couldnt allocate real mode stack for callbacks");
	for (i=0; i<DPMI_PROTMODE_STACK_MAX; i++) {
		dpmi.realStackSelector[i] = base;
		base += 8;
	};
	// Allocate Descriptors for real mode datasegment in realmode callback func
	if (!AllocateLDTDescriptor(DPMI_PROTMODE_STACK_MAX,base)) E_Exit("DPMI: Couldnt allocate data area for callbacks");
	for (i=0; i<DPMI_PROTMODE_STACK_MAX; i++) {
		// We need mem & descriptor for register data area
		dpmi.dataSelector[i] = base;
		desc.Load	 (dpmi.ldt.base+(base & ~7));
		desc.SetLimit(63);
		desc.SetBase (DOS_GetMemory(64/16)<<4);
		desc.Save	 (dpmi.ldt.base+(base & ~7));		
		base += 8;
	};

	dpmi.protStackCurrent = 0;	
};

static Bitu DPMI_EntryPoint(void) 
{
	if (!activeDPMI) {
		// First client - read int table
		for (Bitu i=0; i<256; i++) originalIntTable[i] = mem_readd(i*4);
	} else {
		// Another client already running, remove active callbacks from int table
		activeDPMI->RemoveIntCallbacks();
	}
	activeDPMI = new DPMI();		
	return activeDPMI->Entrypoint();
}

Bitu DPMI::Entrypoint(void)
{
	/* This should switch to pmode */
	if (dpmi.client.have) E_Exit("DPMI:Already have a client");

	LOG(LOG_MISC,LOG_ERROR)("DPMI: Entrypoint (%d Bit)",(reg_ax & 1) ? 32:16);

	MEM_A20_Enable(true);
	// Create gdt, ldt, idt and other stuff
	Setup();
	
	// Save Realmode Registers
	SaveRegisterState(0);
	
	dpmi.client.have  = true;
	dpmi.client.bit32 = reg_ax & 1;
	
	// Clear XMS Handles
	ClearXMSHandles();
	/* Clear the LDT */
	Bitu i;
	for (i=0;i<LDT_SIZE*8;i++) mem_writeb(dpmi.ldt.base+i,0);
	/* Setup IDT */
	SetDescriptor gate;
	Bitu selIntType = dpmi.client.bit32 ? DESC_386_INT_GATE:DESC_286_INT_GATE;
	for (i=0;i<256;i++) {
		gate.Clear();
		gate.SetSelector(GDT_PROTCODE);
		gate.SetOffset	(i*8);
		gate.SetType	(selIntType);
		gate.saved.seg.p   = 1;
		gate.saved.seg.dpl = DPMI_DPL;
		gate.Save(dpmi.idt.base+i*8);
	}
	
	/* Load GDT and IDT */
	CPU_LIDT(dpmi.idt.limit,dpmi.idt.base);
	CPU_LGDT(dpmi.gdt.limit,dpmi.gdt.base);
	/* Switch to pmode */
	Bitu old_cr0=CPU_GET_CRX(0);
	CPU_SET_CRX(0,old_cr0 | 1);
	/* Setup LDT and rest of descriptors for client startup */
	SetDescriptor desc;
	Bitu first;
	AllocateLDTDescriptor(7,first);
	CPU_LLDT(GDT_LDT);
	// Set code segments according to dpmi client
	Descriptor code;
	if (cpu.gdt.GetDescriptor(GDT_CODE,code)) {
		// DMPI callback code (0xC800)
		code.saved.seg.big = dpmi.client.bit32;
		code.Save(dpmi.gdt.base+(GDT_CODE & ~7));
	} else E_Exit("DPMI: cannot initialize code selector 1.");
	if (cpu.gdt.GetDescriptor(GDT_PROTCODE|DPMI_DPL,code)) {
		// DMPI callback code (XMS)
		code.saved.seg.big = dpmi.client.bit32;
		code.Save(dpmi.gdt.base+(GDT_PROTCODE & ~7));
	} else E_Exit("DPMI: cannot initialize code selector 2.");

	/* Setup Selector for PSP, which will be ES */
	DOS_PSP psp(dos.psp);
	dpmi.client.psp=psp.GetSegment();		
	desc.Clear();
	desc.SetLimit(0xff);
	desc.SetBase (dpmi.client.psp << 4);
	desc.SetType (DESC_DATA_ED_RW_A);
	desc.saved.seg.p   = 1;
	desc.saved.seg.dpl = DPMI_DPL;
	desc.Save(dpmi.ldt.base+(first & ~7));
	CPU_SetSegGeneral(es,first);
	first+=8;
	/* Setup Selector for environment */
	Bitu adr = psp.GetEnvironment()<<4;
	if ((!adr) && psp.GetParent()) {
		DOS_PSP parent = DOS_PSP(psp.GetParent());
		adr = parent.GetEnvironment()<<4;
	} 
	if (!adr) E_Exit("DPMI: Couldnt get environment.");
	
	dpmi.initialenv = adr;
	desc.Clear();
	desc.SetBase (adr);
	desc.SetLimit(0xFF);
	desc.SetType (DESC_DATA_ED_RW_A);
	desc.saved.seg.p   = 1;
	desc.saved.seg.big = 1;
	desc.saved.seg.dpl = DPMI_DPL;
	desc.Save    (dpmi.ldt.base+(first & ~7));
	psp.SetEnvironment(first);
	first+=8;
	/* Setup Selector for DS */
	dpmi.initialds = SegValue(ds);
	desc.Clear();
	desc.SetLimit(0xffff);
	desc.SetBase (SegValue(ds) << 4);
	desc.SetType (DESC_DATA_ED_RW_A);
	desc.saved.seg.p   = 1;
	desc.saved.seg.dpl = DPMI_DPL;
	desc.Save(dpmi.ldt.base+(first & ~7));
	CPU_SetSegGeneral(ds,first);
	first+=8;
	/* Get the CS;IP of the stack before changing it */
	Bitu old_cs,old_eip;
	old_eip=mem_readw(SegPhys(ss)+reg_sp);
	dpmi.initialcs = old_cs = mem_readw(SegPhys(ss)+reg_sp+2);
	reg_esp+=4;
	/* Setup Selector for SS and clear high word of ESP */
	desc.Clear();
	desc.SetLimit(0xffff);
	desc.SetBase(SegValue(ss) << 4);
	desc.SetType(DESC_DATA_ED_RW_A);
	desc.saved.seg.p   = 1;
	desc.saved.seg.big = dpmi.client.bit32;
	desc.saved.seg.dpl = DPMI_DPL;
	desc.Save(dpmi.ldt.base+(first & ~7));
	CPU_SetSegGeneral(ss,first);
	reg_esp=reg_sp;
	first+=8;	
	/* Setup CS:IP and jmp there */
	desc.Clear();
	desc.SetLimit(0xffff);
	desc.SetBase(old_cs << 4);
	desc.SetType(DESC_CODE_R_NC_A);
	desc.saved.seg.p   = 1;
	desc.saved.seg.big = 0;		//Start up in 16-bit segment
	desc.saved.seg.dpl = DPMI_DPL;
	desc.Save(dpmi.ldt.base+(first & ~7));
	// Init exception handlers
	for (i=0; i<DPMI_EXCEPTION_MAX; i++) {
		dpmi.exceptionSelector[i] = GDT_PROTCODE;
		dpmi.exceptionOffset[i] = DPMI_CB_EXCEPTION_OFFSET;
	};

	// Create Real and ProtMode Stacks
	CreateStackSpace();

	if (g_hookHardwareInts) {
		// Hook Interrupts in real mode to reflect them to protected mode
		Bitu num;
		for (i=0; i<DPMI_REALVEC_MAX; i++) {
			num = rmIndexToInt[i];
			dpmi.oldRealVec[i]	= HookInterrupt(num, dpmi.defaultHWIntFromProtMode[i]);
			dpmi.realModeVec[i] = RealGetVec(num);
		};
	};

	dpmi.vIntFlag = 1;
	cpu.cpl = DPMI_DPL;
	/* The final jump to start up the code */
	CPU_JMP(false,first,old_eip);
	return 0;
}

static bool DPMI_Multiplex(void) {
	switch (reg_ax) {	
	case 0x1600:		// Windows check
		reg_al = 0x00;	// No windows
		return true;
	case 0x1686:		/* Get CPU Mode */
		reg_ax = 1;
		return true;
	case 0x1687:{		/* Get Mode switch entry point */
		DPMI_LOG("DPMI: 0x2F 0x1687: Get DPMI entry point.");
		reg_ax=0;		/* supported */
		reg_bx=1;		/* Support 32-bit */
		reg_cl=4;		/* 486 */
		reg_dh=00;		/* dpmi 0.90 */
		reg_dl=90;
		reg_si=0;		/* No need for private data */
		RealPt entry=CALLBACK_RealPointer(callback.entry);
		SegSet16(es,RealSeg(entry));
		reg_di=RealOff(entry);
		return true; };
	
	case 0xED00:/* TNT DOS Extender Detection */
/*				DPMI_LOG("DPMI:INT 2F:TNT Extender detection");
				reg_ax = 0xEDFF;	// Installed
				reg_si = 0x5048;	// "PH"
				reg_di = 0x4152;	// "AR"
				reg_cx = 0x0400;	// Majr/Minor Version
				reg_dx = 0x0001;	// DPMI
				reg_bx = 0x0100;		// DPMI Version
				return true;*/
	case 0xED03:
	case 0xF100:// Seems like a Pharlap extender game wants to start up here,
				// so set cmos extended memory informtion to null to avoid mapping
				// of physical memory pharlap likes to do....
				CMOS_SetRegister(0x17,0x00);
				CMOS_SetRegister(0x18,0x00);
				CMOS_SetRegister(0x30,0x00);
				CMOS_SetRegister(0x31,0x00);
				// Dont hook hardware ints, pharlap doesnt like that
				g_hookHardwareInts = false;
				DPMI_LOG("DPMI:INT 2F: Pharlap Detection : %04X.",reg_ax);
	};
	return false;
}

void DPMI_ShutDown(Section* sec)
{
	// Delete global shared mem list
	std::list<TSharedMem*>::iterator i;
	for(i=g_sharedMemList.begin(); i != g_sharedMemList.end(); i++) 
		delete static_cast<TSharedMem*>(*i);
	(g_sharedMemList.clear)();
};

void DPMI_Init(Section* sec) 
{
	Section_prop * section=static_cast<Section_prop *>(sec);
	if (!section->Get_bool("dpmi")) return;

	BIOS_ZeroExtendedSize();
	memset(&callback,0,sizeof(callback));

	/* setup Real mode Callbacks */
	callback.entry=CALLBACK_Allocate();
	CALLBACK_Setup(callback.entry,DPMI_EntryPoint,CB_RETF,"Entrypoint");
	callback.enterpmode=CALLBACK_Allocate();
	CALLBACK_Setup(callback.enterpmode,DPMI_EnterProtMode,CB_RETF,"Enter PMode");
	callback.realsavestate=CALLBACK_Allocate();
	CALLBACK_Setup(callback.realsavestate,DPMI_RealSaveState,CB_RETF,"Save RealState");
	callback.simint=CALLBACK_Allocate();
	CALLBACK_Setup(callback.simint,DPMI_SimulateInt,CB_IRET,"Sim INT");
	callback.simintReturn=CALLBACK_Allocate();
	CALLBACK_Setup(callback.simintReturn,DPMI_SimulateIntReturn,CB_IRET,"Sim INT RET");
	callback.rmIntFrame=CALLBACK_Allocate();
	CALLBACK_Setup(callback.rmIntFrame,DPMI_CallRealIRETFrame,CB_IRET,"Call REAL IRET");
	callback.rmIntFrameReturn=CALLBACK_Allocate();
	CALLBACK_Setup(callback.rmIntFrameReturn,DPMI_CallRealIRETFrameReturn,CB_IRET,"Call REAL IRET RET");
	callback.ptorint=CALLBACK_Allocate();
	CALLBACK_Setup(callback.ptorint,DPMI_ptorHandler,CB_IRET,"INT Handler");
	callback.ptorintReturn=CALLBACK_Allocate();
	CALLBACK_Setup(callback.ptorintReturn,DPMI_ptorHandlerReturn,CB_IRET,"INT Handler RET");
	callback.int21Return=CALLBACK_Allocate();
	CALLBACK_Setup(callback.int21Return,DPMI_Int21HandlerReturn,CB_IRET,"INT 21 RET");
	callback.rmCallbackReturn=CALLBACK_Allocate();
	CALLBACK_Setup(callback.rmCallbackReturn,DPMI_RealModeCallbackReturn,CB_IRET,"RMCB RET");
	callback.int21msdos=CALLBACK_Allocate();
	CALLBACK_Setup(callback.int21msdos,DPMI_API_Int21_MSDOS,CB_IRET,"MSDOS INT 21 API");
	
	/* Setup multiplex */
	DOS_AddMultiplexHandler(DPMI_Multiplex);

	/* shutdown function */
	sec->AddDestroyFunction(&DPMI_ShutDown);
}
	
void DPMI::Reactivate()
{
	/* Load GDT and IDT */
	CPU_LIDT(dpmi.idt.limit,dpmi.idt.base);
	CPU_LGDT(dpmi.gdt.limit,dpmi.gdt.base);
	CPU_LLDT(GDT_LDT);
	cpu.cpl = DPMI_DPL;
	RestoreIntCallbacks();
};

void DPMI::Setup()
{	
	Bitu i;
	Bitu xmssize		= (TOTAL_SIZE|(DPMI_PAGE_SIZE-1))+1;
	Bitu protStackSize	= ((DPMI_PROTMODE_STACK_MAX*DPMI_PROTMODE_STACKSIZE)|(DPMI_PAGE_SIZE-1))+1;
	Bitu numPages		= ((xmssize+protStackSize) >> 12);

#if DPMI_ALLOC_NEEDEDMEM_HIGH
	// Allocate the GDT,LDT,IDT Stack space (High Mem) 
	Bitu max		= MEM_FreeLargest();
	Bitu temphandle = MEM_AllocatePages(max-numPages,true);
	dpmi.mem_handle = MEM_AllocatePages(numPages,true);
	if (dpmi.mem_handle==0) {
		LOG_MSG("DPMI:Can't allocate XMS memory, disabling dpmi support.");
		return;
	}
	Bitu address = dpmi.mem_handle*DPMI_PAGE_SIZE;;
	MEM_ReleasePages(temphandle);	
#else
	// load LDT and stuff in low mem (
	Bit16u segment;
	Bit16u blocks = numPages*4096/16;
	if (!DOS_AllocateMemory(&segment,&blocks)) {
		LOG_MSG("DPMI:Can't allocate XMS memory, disabling dpmi support.");
		return;		
	};
	Bitu address = segment * 16;
#endif
	// Allocate real mode stack space
	rm_ss = DOS_GetMemory(DPMI_REALMODE_STACKSIZE/16);
	rm_sp = DPMI_REALMODE_STACKSIZE;
	// Get Begin of protected mode stack 
	dpmi.protStack = address + xmssize;
	/* Clear the memory */
	PhysPt w;
	for (w=address;w<xmssize;w++) mem_writeb(w,0);
	dpmi.gdt.base=address;
	dpmi.gdt.limit=(GDT_SIZE*8)-1;
	address+=GDT_SIZE*8;
	dpmi.idt.base=address;
	dpmi.idt.limit=(IDT_SIZE*8)-1;
	address+=IDT_SIZE*8;
	address+=4;
	dpmi.ldt.base=address;
	dpmi.ldt.limit=(LDT_SIZE*8)-1;
	address+=LDT_SIZE*8;
	dpmi.ptorint_base=address;
	address+=INT_SIZE*8;
	/* Setup LDT and CODE descriptors in GDT */
	SetDescriptor ldt;
	ldt.Clear();
	ldt.SetBase	(dpmi.ldt.base);
	ldt.SetLimit(dpmi.ldt.limit);
	ldt.SetType	(DESC_LDT);
	ldt.saved.seg.p	  = 1;
	ldt.saved.seg.dpl = DPMI_DPL;
	ldt.Save(dpmi.gdt.base+(GDT_LDT & ~7));
	SetDescriptor code;
	/* Setup Code Descriptor for real mode calls */
	code;
	code.Clear();
	code.SetBase (CB_SEG<<4);
	code.SetLimit(0xFFFF);
	code.SetType (DESC_CODE_R_NC_A);
	code.saved.seg.p   = 1;
	code.saved.seg.big = 1;
	code.saved.seg.dpl = DPMI_DPL;
	code.Save    (dpmi.gdt.base+(GDT_CODE & ~7));
	/* Setup Code Descriptor for protected mode calls */
	code.Clear();
	code.SetBase (dpmi.ptorint_base);
	code.SetLimit(0xFFFF);
	code.SetType (DESC_CODE_R_NC_A);
	code.saved.seg.p   = 1;
	code.saved.seg.big = 1;
	code.saved.seg.dpl = DPMI_DPL;
	code.Save    (dpmi.gdt.base+(GDT_PROTCODE & ~7));
	/* Setup data Descriptor to access first megabyte */
	code.Clear();
	code.SetBase (0);
	code.SetLimit(0xFFFFF);
	code.SetType (DESC_DATA_ED_RW_A);
	code.saved.seg.p   = 1;
	code.saved.seg.big = 1;
	code.saved.seg.dpl = DPMI_DPL;
	code.Save    (dpmi.gdt.base+(GDT_DOSDATA & ~7));
	/* Setup data Descriptor to access Dos Segment 0x40 */
	code.Clear();
	code.SetBase (0x40<<4);
	code.SetLimit(0xFFFF);
	code.SetType (DESC_DATA_ED_RW_A);
	code.saved.seg.p   = 1;
	code.saved.seg.big = 0;
	code.saved.seg.dpl = 0;
	code.Save    (dpmi.gdt.base+(GDT_DOSSEG40 & ~7));

	if (g_hookHardwareInts) {
		// Setup Hardware Interrupt handler
		for (i=0; i<DPMI_REALVEC_MAX; i++) {
			dpmi.defaultHWIntFromProtMode[i]=CALLBACK_Allocate();
			CALLBACK_Setup(dpmi.defaultHWIntFromProtMode[i],DPMI_HWIntDefaultHandler,CB_IRET,"HWINT Hook");
		};
	}

	// Init Realmode Callbacks
	for (i=0; i<DPMI_REALMODE_CALLBACK_MAX; i++) {
		dpmi.rmCallback[i].id = CALLBACK_Allocate();
		CALLBACK_Setup(dpmi.rmCallback[i].id,DPMI_RealModeCallback,CB_IRET,"Std RMCB");	
		dpmi.rmCallback[i].inUse = false;
	};

	/* Setup some callbacks used only in pmode */
	callback.apimsdosentry=CALLBACK_Allocate();
	CALLBACK_SetupAt(callback.apimsdosentry,DPMI_API_Entry_MSDOS,CB_RETF,dpmi.ptorint_base+DPMI_CB_APIMSDOSENTRY_OFFSET,"MSDOS API Entry");
	callback.enterrmode=CALLBACK_Allocate();
	CALLBACK_SetupAt(callback.enterrmode,DPMI_EnterRealMode,CB_RETF,dpmi.ptorint_base+DPMI_CB_ENTERREALMODE_OFFSET,"Enter RMODE");
	callback.protsavestate=CALLBACK_Allocate();
	CALLBACK_SetupAt(callback.protsavestate,DPMI_ProtSaveState,CB_RETF,dpmi.ptorint_base+DPMI_CB_SAVESTATE_OFFSET,"Save Prot State");
	callback.exception=CALLBACK_Allocate();
	CALLBACK_SetupAt(callback.exception,DPMI_Exception,CB_RETF,dpmi.ptorint_base+DPMI_CB_EXCEPTION_OFFSET,"Exception");
	callback.exceptionret=CALLBACK_Allocate();
	CALLBACK_SetupAt(callback.exceptionret,DPMI_ExceptionReturn,CB_RETF,dpmi.ptorint_base+DPMI_CB_EXCEPTIONRETURN_OFFSET,"Exception RET");

	/* Setup special table for ints/exception handling */
	w=dpmi.ptorint_base;
	for (i=0;i<256;i++) {	
		mem_writeb(w,0xFE);						//GRP 4
		mem_writeb(w+1,0x38);					//Extra Callback instruction
		mem_writew(w+2,callback.ptorint);		//The immediate word
		mem_writeb(w+4,0xcf);					//IRET
		w+=8;
	}
	// Setup int 0 (div0 exception)
	mem_writeb(dpmi.ptorint_base,0xFE);						//GRP 4
	mem_writeb(dpmi.ptorint_base+1,0x38);					//Extra Callback instruction
	mem_writew(dpmi.ptorint_base+2,callback.exception);		//The immediate word
	mem_writeb(dpmi.ptorint_base+4,0xcf);					//IRET

	// Set Special 0x31 and 0x21 Handler
	callback.int31=CALLBACK_Allocate();
	CALLBACK_SetupAt(callback.int31,DPMI_Int31Handler,CB_IRET,dpmi.ptorint_base+0x31*8,"DPMI INT 31");
	callback.int21=CALLBACK_Allocate();
	CALLBACK_SetupAt(callback.int21,DPMI_Int21Handler,CB_IRET,dpmi.ptorint_base+0x21*8,"DPMI INT 21");
	callback.int2f=CALLBACK_Allocate();
	CALLBACK_SetupAt(callback.int2f,DPMI_Int2fHandler,CB_IRET,dpmi.ptorint_base+0x2f*8,"DPMI INT 2F");

}

// *********************************************************************
// Special Extender capabilities : MS-DOS
// *********************************************************************

void DPMI::SetupPharlapSelectors(void)
{
	SetDescriptor desc; 
	Bitu base,limit,type;
	Bitu sel=0x04; 
	while (sel<=0x4C) {
		switch (sel) {
			case 0x04	:	// Writable data segment ot the program psp
			case 0x24	:	base	= dpmi.client.psp << 4;
							limit	= 0xFF;
							type	= DESC_DATA_ED_RW_A;
							break;
			case 0x0C	:	// Readable code segment (initially loaded into cs)
							base	= dpmi.initialcs << 4;
							limit	= 0xFFFF;
							type	= DESC_CODE_R_NC_A;
							break;
			case 0x14	:	// Writeable data segment (initially loaded into ds,es...)
							base	= dpmi.initialds << 4;
							limit	= 0xFFFF;
							type	= DESC_DATA_ED_RW_A;
							break;
			case 0x1C	:	// Writeable data segment to screen area (TODO: will be switched if func 10 00 is executed)
			case 0x44	:	// FIXME
							base	= 0xA0000;
							limit	= 0xFFFF;
							type	= DESC_DATA_ED_RW_A;
							break;
			case 0x2C	:	// writeable data segment to the env block
							base	= dpmi.initialenv;
							limit	= 0xFF;
							type	= DESC_DATA_ED_RW_A;
							break;
			case 0x34	:	// writeable data segment that maps the first mb of dos memory
							base	= 0x00000;
							limit	= 0xFFFFF;
							type	= DESC_DATA_ED_RW_A;
							break;
			case 0x3C	:	// Writable data segment maps waitek fpu memory
			case 0x4C	:	// Writable data segment maps cyrix fpu memory
							base	= 0x00000000;
							limit	= 0x00000000;
							type	= DESC_DATA_ED_RW_A;
							break;
		}
		if (cpu.gdt.GetDescriptor(sel,desc)) {
			desc.Clear();
			desc.SetBase (base);
			desc.SetLimit(limit);
			desc.SetType (type);
			desc.saved.seg.p   = 1;
			desc.saved.seg.dpl = DPMI_DPL;
			desc.Save(dpmi.ldt.base+(sel & ~7));
		}
		sel += 8;
	}					
}

bool DPMI::Pharlap_AllocateMem(Bitu size, Bitu& outHandle, Bitu& linear)
{
	DPMI_LOG("PHARLAP Allocation");

	Bitu pages		= (size/DPMI_PAGE_SIZE) + ((size%DPMI_PAGE_SIZE)>0);	// Convert to 4KB pages
	Bitu maxPages	= MEM_FreeLargest();
	if (maxPages<pages) return false;

	Bitu tempHandle = MEM_AllocatePages(maxPages-pages,true);
	outHandle		= MEM_AllocatePages(pages,true);
	linear			= outHandle*DPMI_PAGE_SIZE;
	if (outHandle!=0) {
		SetXMSHandle(outHandle);
	}
	
	MEM_ReleasePages(tempHandle);
	return (outHandle!=0);
};

Bitu DPMI::GetSegmentFromSelector(Bitu selector)
{
	Bitu base = 0xDEAD;
	SetDescriptor desc;
	if (cpu.gdt.GetDescriptor(selector,desc)) {
		base = desc.GetBase();
		if ((base>0xFFFFF) || (base & 0x0F)) E_Exit("DPMI:MSDOS: Invalid Selector (convert to segment not possible)");
		base >>= 4;
	} else E_Exit("DPMI:MSDOS: Invalid Selector (not found)");
	return base;
};

bool DPMI::GetMsdosSelector(Bitu realseg, Bitu realoff, Bitu &protsel, Bitu &protoff)
{
	// Check if selector is already in list
	for (Bitu i=0; i<dosSelectorList.size(); i++) {
		Bitu dossel = dosSelectorList[i];
		SetDescriptor desc;
		desc.Load	(dpmi.ldt.base+(dossel & ~7));
		if (desc.GetBase()==realseg<<4) {
			protoff = realoff;
			protsel = dossel;
			return true;
		}
	};
	// Allocate new one
	if (AllocateLDTDescriptor(1,protsel)) {
		SetDescriptor desc;
		desc.Load		(dpmi.ldt.base+(protsel & ~7));
		desc.SetBase	(realseg<<4);
		desc.SetLimit	(0xFFFF);
		desc.Save		(dpmi.ldt.base+(protsel & ~7));
		dosSelectorList.push_back(protsel);
	} else E_Exit("DPMI:MSDOS: No more selectors.");
	protoff = realoff;
	return true;
};

void DPMI::API_Init_MSDOS(void)
{
	// Enable special Int 21 Handler....
	RealPt func = CALLBACK_RealPointer(callback.int21msdos);
	SetDescriptor gate;
	gate.Load		(dpmi.idt.base+0x21*8);
	gate.SetSelector(GDT_CODE);
	gate.SetOffset	(RealOff(func));
	gate.Save		(dpmi.idt.base+0x21*8);			
	// Setup pharlap special descriptors
	// if (dpmi.pharlap) SetupPharlapSelectors();
};

Bitu DPMI::API_Entry_MSDOS(void) 
{
	LOG(LOG_MISC,LOG_WARN)("DPMI: MSDOS Extension API Entry: ax:%04X.",reg_ax);
	switch (reg_ax) {
		case 0x0000:// Get MS-DOS Extension Version
					reg_ax = 0x0000;
					SETFLAGBIT(CF,false);
					break;
		case 0x0100:// Get Selector to Base of LDT
					// Note that the DPMI host has the option of either failing
					// this call, or to return a read-only descriptor (we fail it).
					SETFLAGBIT(CF,true);
					break;
		default:	SETFLAGBIT(CF,true);
					LOG(LOG_MISC,LOG_ERROR)("DPMI:MSDOS-API:Unknown ax on entry point %04X.",reg_ax);
					break;
	}
	return 0;
};

Bitu DPMI::Mask(Bitu value) 
{
	if (dpmi.client.bit32)	return value;
	else					return value & 0xFFFF;
};

Bitu DPMI::API_Int21_MSDOS(void)
{
//	DPMI_LOG_ERROR("DPMI:MSDOS-API:INT 21 %04X",reg_ax);
	Bitu protsel,protoff,seg,off;
	Bitu sax = reg_ax;
	switch (reg_ah) {
	
		case 0x1a:	/* Set Disk Transfer Area Address */
					dtaAddress = SegPhys(ds) + Mask(reg_edx);
					break;
		case 0x25:	{ // Set Protected mode Interrupt Vector
					if (dpmi.pharlap) {
						//LOG(LOG_MISC,LOG_ERROR)
						switch (reg_al) {
							
							case 0x05:	// Set Real mode Int Vector
										RealSetVec(reg_cl,Mask(reg_ebx));
										DPMI_CALLBACK_SCF(false);
										break;

							default  :	E_Exit("DPMI:PHARLAP:System call %04X",reg_ax);
						};
						
					} else {
						// ms dos api
						SetDescriptor gate;
						gate.Clear();
						gate.saved.seg.p=1;
						gate.SetSelector(SegValue(ds));
						gate.SetOffset	(Mask(reg_edx));
						gate.SetType	(dpmi.client.bit32?DESC_386_INT_GATE:DESC_286_INT_GATE);
						gate.saved.seg.dpl = DPMI_DPL;
						gate.Save(dpmi.idt.base+reg_al*8);
					}
					}; break;
		case 0x35:	{ // Get Protected Mode Interrupt Vector
					SetDescriptor gate;
					gate.Load(dpmi.idt.base+reg_al*8);
					CPU_SetSegGeneral(es,gate.GetSelector());
					if (dpmi.client.bit32)	reg_ebx = gate.GetOffset();
					else					reg_bx	= gate.GetOffset();
					DPMI_CALLBACK_SCF(false);
					}; break;

		case 0x2f:	/* Get Disk Transfer Area */
					seg = RealSeg(dos.dta);
					off = RealOff(dos.dta);
					GetMsdosSelector(seg,off,protsel,protoff);
					CPU_SetSegGeneral(es,protsel);
					if (dpmi.client.bit32)	reg_ebx = protoff;
					else					reg_bx	= protoff;
					break;
	
		case 0x34 : // Get INDOS Flag address
					seg = RealSeg(dos.tables.indosflag);
					off = RealOff(dos.tables.indosflag);
					GetMsdosSelector(seg,off,protsel,protoff);
					CPU_SetSegGeneral(es,protsel);
					reg_bx = protoff;
					break;
		case 0x38: /* Get/set Country code */
					if (dpmi.client.bit32) {
						E_Exit("DPMI:MSDOS-API:function %04X not yet supported.",reg_ax);
					} else {
						// reflect to real mode	
						DPMI_Int21Handler();
					}
					break;
		case 0x39:{ // MKDIR Create directory 
					char name1[256];
					MEM_StrCopy(SegPhys(ds)+Mask(reg_edx),name1,255);
					if (DOS_MakeDir(name1)) {
						CALLBACK_SCF(false);
					} else {
						reg_ax=dos.errorcode;
						CALLBACK_SCF(true);
					}
					}; break;
		case 0x3b:	{ /* CHDIR Set current directory */
					char name1[256];
					MEM_StrCopy(SegPhys(ds)+Mask(reg_edx),name1,255);
					if  (DOS_ChangeDir(name1)) {
						LOG(LOG_MISC,LOG_ERROR)("DOS: Set Directory %s ",name1);
						CALLBACK_SCF(false);
					} else {
						LOG(LOG_MISC,LOG_ERROR)("DOS: Set Directory %s failed",name1);
						reg_ax=dos.errorcode;
						CALLBACK_SCF(true);
					}
					}; break;
		case 0x3c:	{ /* CREATE Create of truncate file */
					char name1[256];
					MEM_StrCopy(SegPhys(ds)+Mask(reg_edx),name1,255);
					if (DOS_CreateFile(name1,reg_cx,&reg_ax)) {
						LOG(LOG_MISC,LOG_ERROR)("DOS: Create file %s success (%d)",name1,reg_ax);
						DPMI_CALLBACK_SCF(false);
					} else {
						LOG(LOG_MISC,LOG_ERROR)("DOS: Create file %s failed",name1);
						reg_ax=dos.errorcode;
						DPMI_CALLBACK_SCF(true);
					}
					}; break;
		case 0x3d:	{ /* OPEN Open existing file */
					char name1[256];
					Bitu savedax = reg_ax;
					MEM_StrCopy(SegPhys(ds)+Mask(reg_edx),name1,255);
					if (DOS_OpenFile(name1,reg_al,&reg_ax)) {
						DPMI_LOG("DOS: Open success: %s (%d)",name1,reg_ax);
						DPMI_CALLBACK_SCF(false);
					} else {
						DPMI_LOG("DOS: Open failure: %s ",name1);
						reg_ax=dos.errorcode;
						DPMI_CALLBACK_SCF(true);
					}
					};break;
		case 0x3f:	/* READ Read from file or device */
					{ 
						if (Mask(reg_ecx)>0xFFFF) E_Exit("DPMI:DOS: Read file size > 0xffff");							

						Bit16u toread = Mask(reg_ecx);
						dos.echo = true;
						if (DOS_ReadFile(reg_bx,dos_copybuf,&toread)) {
							MEM_BlockWrite(SegPhys(ds)+Mask(reg_edx),dos_copybuf,toread);
							if (dpmi.client.bit32) reg_eax=toread; else reg_ax=toread;
//							LOG(LOG_MISC,LOG_ERROR)("READ FILE Handle:%d Size:%d Read:%d",reg_bx,toread,reg_eax);
							DPMI_CALLBACK_SCF(false);

						} else {
							LOG(LOG_MISC,LOG_ERROR)("DOS: Read file %d failed",reg_bx);
							reg_ax=dos.errorcode;
							DPMI_CALLBACK_SCF(true);
						}
						dos.echo=false;
						break;
					}
		case 0x40:	{/* WRITE Write to file or device */
					Bit16u towrite = Mask(reg_ecx);
					MEM_BlockRead(SegPhys(ds)+Mask(reg_edx),dos_copybuf,towrite);
//					if (reg_bx<=5) LOG(LOG_MISC,LOG_ERROR)("INT 21 40: %s",(char *)dos_copybuf);
					if (DOS_WriteFile(reg_bx,dos_copybuf,&towrite)) {
						if (dpmi.client.bit32)	reg_eax=towrite;
						else					reg_ax =towrite;
	   					DPMI_CALLBACK_SCF(false);
					} else {
						DPMI_LOG("DPMI:MSDOS:Write %d file failure.",reg_bx);
						reg_ax=dos.errorcode;
						DPMI_CALLBACK_SCF(true);
					}
					}; break;
		case 0x41:	{ /* UNLINK Delete file */
					char name1[256];
					MEM_StrCopy(SegPhys(ds)+Mask(reg_edx),name1,255);
					if (DOS_UnlinkFile(name1)) {
						DPMI_CALLBACK_SCF(false);
					} else {
						reg_ax=dos.errorcode;
						DPMI_CALLBACK_SCF(true);
					}
					}; break;
		case 0x42:	/* LSEEK Set current file position */
					{
						Bit32u pos=(reg_cx<<16) + reg_dx;
						Bit32u topos = pos;
						if (DOS_SeekFile(reg_bx,&pos,reg_al)) {
							reg_dx=(Bit16u)(pos >> 16);
							reg_ax=(Bit16u)(pos & 0xFFFF);
							DPMI_CALLBACK_SCF(false);
							if ((reg_al==0) && (topos!=pos)) {
								LOG(LOG_MISC,LOG_ERROR)("SEEK FILE Handle:%d Seek:%d Pos:%d",reg_bx,topos,pos);
								LOG(LOG_MISC,LOG_ERROR)("SEEK FILE ERROR : Pos differs");
							}
						} else {
							LOG(LOG_MISC,LOG_ERROR)("DOS: Seek file %d failed",reg_bx);
							reg_ax=dos.errorcode;
							DPMI_CALLBACK_SCF(true);
						}
						break;
					}
		case 0x43:	{ /* Get/Set file attributes */
					char name1[256];
					MEM_StrCopy(SegPhys(ds)+Mask(reg_edx),name1,255);
					switch (reg_al)
					case 0x00:				/* Get */
					{
						if (DOS_GetFileAttr(name1,&reg_cx)) {
							DPMI_CALLBACK_SCF(false);
						} else {
							DPMI_CALLBACK_SCF(true);
							reg_ax=dos.errorcode;
						}
						break;
					case 0x01:				/* Set */
						DPMI_LOG("DOS:Set File Attributes for %s not supported",name1);
						DPMI_CALLBACK_SCF(false);
						break;
					default:
						E_Exit("DOS:0x43:Illegal subfunction %2X",reg_al);
					}
					}; break;

		case 0x47:	{ /* CWD Get current directory */
					char name1[256];
					if (DOS_GetCurrentDir(reg_dl,name1)) {
						MEM_BlockWrite(SegPhys(ds)+Mask(reg_esi),name1,strlen(name1)+1);	
						LOG(LOG_MISC,LOG_ERROR)("DOS: Get Dir %s ",name1);
						reg_ax=0x0100;
						CALLBACK_SCF(false);
					} else {
						LOG(LOG_MISC,LOG_ERROR)("DOS: Get Dir failed %s ",name1);
						reg_ax=dos.errorcode;
						CALLBACK_SCF(true);
					}
					}; break;

		case 0x4E:	{/* Get first dir entry */
					char name1[256];
					MEM_StrCopy(SegPhys(ds)+Mask(reg_edx),name1,255);
					if (DOS_FindFirst(name1,reg_cx)) {
						LOG(LOG_MISC,LOG_ERROR)("DOS: Find Dir entry %s success",name1);
						DPMI_CALLBACK_SCF(false);	
						// Copy result to internal dta
						if (dtaAddress) MEM_BlockCopy(dtaAddress,PhysMake(RealSeg(dos.dta),RealOff(dos.dta)),dpmi.pharlap?43:128);
						reg_ax=0;			/* Undocumented */
					} else {
						LOG(LOG_MISC,LOG_ERROR)("DOS: Find Dir entry %s failure",name1);
						reg_ax=dos.errorcode;
						DPMI_CALLBACK_SCF(true);
					};
					}; break;		 
		case 0x4f:	/* FINDNEXT Find next matching file */
					// Copy data to dos dta
					if (dtaAddress) MEM_BlockCopy(PhysMake(RealSeg(dos.dta),RealOff(dos.dta)),dtaAddress,dpmi.pharlap?43:128);
					if (DOS_FindNext()) {
						LOG(LOG_MISC,LOG_ERROR)("DOS: FindNext Dir entry success");
						CALLBACK_SCF(false);
						// Copy result to internal dta
						if (dtaAddress) MEM_BlockCopy(dtaAddress,PhysMake(RealSeg(dos.dta),RealOff(dos.dta)),dpmi.pharlap?43:128);
						reg_ax=0xffff;			/* Undocumented */
					} else {
						LOG(LOG_MISC,LOG_ERROR)("DOS: FindNext Dir entry failure");
						reg_ax=dos.errorcode;
						CALLBACK_SCF(true);
					};
					break;		
		case 0x50:	/* Set current PSP */
					if (dpmi.pharlap)	dos.psp = reg_bx; // pharlap uses real mode paragraph address
					else				
					dos.psp = GetSegmentFromSelector(reg_bx);
					DPMI_LOG("DPMI:MSDOS:0x50:Set current psp:%04X",reg_bx);					
					break;	
		case 0x51:	/* Get current PSP */
					if (dpmi.pharlap)	reg_bx = dos.psp; // pharlap uses real mode paragraph address
					else {
						GetMsdosSelector(dos.psp,0x0000,protsel,protoff);
						reg_bx = protsel;
					};
					DPMI_LOG("DPMI:MSDOS:0x51:Get current psp:%04X",reg_bx);					
					break;
		case 0x55 : { // Neuen PSP erstellen
					Bitu segment = GetSegmentFromSelector(reg_dx);
					DOS_ChildPSP(segment,reg_si);
					dos.psp = segment;
					DPMI_LOG("DPMI:MSDOS:0x55:Create new psp:%04X",segment);					
					}; break;
		case 0x56:	{ /* RENAME Rename file */
					char name1[256+1];
					char name2[256+1];
					MEM_StrCopy(SegPhys(ds)+Mask(reg_edx),name1,256);
					MEM_StrCopy(SegPhys(es)+Mask(reg_edi),name2,256);
					if (DOS_Rename(name1,name2)) {
						DPMI_CALLBACK_SCF(false);			
					} else {
						reg_ax=dos.errorcode;
						DPMI_CALLBACK_SCF(true);
					}
					}; break;		
		case 0x5D : // Get Address of dos swappable area
					// FIXME: This is totally faked...
					// FIXME: Add size in bytes (at least pharlap)
					// FIXME: Depending on al, two functions (pharlap)
					GetMsdosSelector(0xDEAD,0xDEAD,protsel,protoff);
					CPU_SetSegGeneral(ds,protsel);
					reg_si = protoff;
					DPMI_LOG("DPMI:MSDOS:0x5D:Get Addres of DOS SwapArea:%04X",reg_si);					
					break;
		case 0x62 :	/* Get Current PSP Address */
					GetMsdosSelector(dos.psp,0x0000,protsel,protoff);
					reg_bx = protsel;
					DPMI_LOG("DPMI:MSDOS:0x62:Get current psp:%04X",reg_bx);	
					break;
		case 0x68:  // Flush file to disc
					DPMI_CALLBACK_SCF(false);
					break;

		case 0x09:
		case 0x0A:
		case 0x0C:
		case 0x1B:
		case 0x1C:
		case 0x26: // Pharlap != MS-DOS
//		case 0x30: // Pharlap extended information
		case 0x31:  
		case 0x32:  
		case 0x3A:
		case 0x48: // Pharlap = 4KB mem pages
		case 0x49:
		case 0x4A: // Pharlap = 4KB mem pages
		case 0x4B:
		case 0x52:
		case 0x53:
		case 0x59:
		case 0x5A:
		case 0x5B:
		case 0x5E:
		case 0x5F:
		case 0x60:
//		case 0x62:
		case 0x65:
		case 0x6C:
					E_Exit("DPMI:MSDOS-API:function %04X not yet supported.",reg_ax);
					break;
		
		// *** PASS THROUGH ***
		case 0x44:	if ((reg_al==0x02) || (reg_al==0x03) || (reg_al==0x04) || (reg_al==0x05) || (reg_al==0x0C) || (reg_al==0x0D)) {
						E_Exit("DPMI:MSDOS-API:function %04X not yet supported.",reg_ax);
					};
		case 0x07: case 0x0B: case 0x0E: case 0x19: case 0x2A: case 0x2C: case 0x2D: case 0x30: 
		case 0x36: case 0x3E: case 0x4C: case 0x58: case 0x67:
					{
						// reflect to real mode	
						DPMI_Int21Handler();
					};
					break;
		default:	E_Exit("DPMI:MSDOS-API:Missing function %04X",reg_ax);

	};
	return 0;
};
