;²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²
;		   D3X(tm) DOS-Extender v0.90 unleash `h' alpha
;				      core
;					
;			Copyright (c) 1998-2004 Daniel Borca
;					
;				 dborca@yahoo.com
;			 http://www.geocities.com/dborca
;²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²


%include	"lang.mac"
;%include	"swat.inc"

;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
;	CONFIG
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
;%define	EXC0f			;exception 15
;%define	PAGED			;enable paging
;%define	SSIRQ			;switch stacks at IRQs
;%define	TDMAP			;top-down physical mapping
%define	SYMLINK				;support symbolic links (DJGPP)
EXTRALINEAR	equ	4*1024*1024	;used for physical to linear mapping

;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
;	DEFINES, EQUATES, MACROS & STRUCTURES
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
%ifdef	I21API
%define	extAPI
%endif
%ifdef	I33API
%define	extAPI
%endif

%ifdef	SYMLINK
%define	SYMLINK		(TARGET==1)
%else
%define	SYMLINK		0
%endif

%ifdef	PAGED
%define	VCPIreq		call myVCPIr
%else
%define	VCPIreq		int 67h
%endif

Locked_frame	equ	200h
Stack_16_sz	equ	200h
Stack_Lk_sz	equ	1000h
Stack_Sp_sz	equ	100h		;Must fit in Stack_16_sz and Locked_frame
DefMasterPIC	equ	08h
DefSlavePIC	equ	70h
%define	Num_IRQ		(8*2)
%define	Num_IRQ2	(Num_IRQ+2)	;Fake: 1ch, 23h
%define	Num_Exc		17
%define	Num_RMCB	16
MaxAvSel	equ	20h		;Max available LDT selectors
MaxXHndl	equ	80h		;Max extended memory blocks

DPMI_sels	equ	2
%define	DPMI_sel1	Flat_D_desc

struc		Addr16b
	Ofs16b	resw	1
	Seg16b	resw	1
endstruc

struc		Addr32b
	Ofs32b	resd	1
	Sel16b	resw	1
endstruc

mask_P		equ	80h
mask_DPL	equ	60h
mask_S		equ	10h
mask_G		equ	80h
mask_NUL	equ	20h
mask_L2		equ	0fh

PT_P		equ	01h		;present (else not)
PT_W		equ	02h		;writable (else read-only)
PT_U		equ	04h		;user mode (else kernel mode)
PT_CD		equ	10h		;page caching disabled (else enabled)
PT_A		equ	20h		;accessed (else not)
PT_D		equ	40h		;dirty (else clean)

PT_E		equ	4		;hi-byte: end block marker

struc		gate
	gateO1	resw	1
	gateSel	resw	1
		resb	1
	gateTyp	resb	1
	gateO2	resw	1
endstruc

struc		descr
	L1	resw	1
	B1	resw	1
	B2	resb	1
	access	resb	1
	granted	resb	1
	B3	resb	1
endstruc

struc		TSS
	tssBack	resw	2
	tssEsp0	resd	1
	tssSs0	resw	2
	tssEsp1	resd	1
	tssSs1	resw	2
	tssEsp2	resd	1
	tssSs2	resw	2
	tssCr3	resd	1
	tssEip	resd	1
	tssEfl	resd	1
	tssEax	resd	1
	tssEcx	resd	1
	tssEdx	resd	1
	tssEbx	resd	1
	tssEsp	resd	1
	tssEbp	resd	1
	tssEsi	resd	1
	tssEdi	resd	1
	tssEs	resw	2
	tssCs	resw	2
	tssSs	resw	2
	tssDs	resw	2
	tssFs	resw	2
	tssGs	resw	2
	tssLDT	resw	2
	tssT	resw	1
	tssIO	resw	1
		resd	64
	tssStk:
endstruc

struc		DPMI_R
	dpmiEdi:
	dpmiDi	resw	1
	dpmiDiH	resw	1
	dpmiEsi:
	dpmiSi	resw	1
	dpmiSiH	resw	1
	dpmiEbp:
	dpmiBp	resw	1
	dpmiBpH	resw	1
	dpmiRes	resd	1
	dpmiEbx:
	dpmiBx:
	dpmiBl	resb	1
	dpmiBh	resb	1
	dpmiBxH	resw	1
	dpmiEdx:
	dpmiDx:
	dpmiDl	resb	1
	dpmiDh	resb	1
	dpmiDxH	resw	1
	dpmiEcx:
	dpmiCx:
	dpmiCl	resb	1
	dpmiCh	resb	1
	dpmiCxH	resw	1
	dpmiEax:
	dpmiAx:
	dpmiAl	resb	1
	dpmiAh	resb	1
	dpmiAxH	resw	1
	dpmiFl	resw	1
	dpmiEs	resw	1
	dpmiDs	resw	1
	dpmiFs	resw	1
	dpmiGs	resw	1
	dpmiIp	resw	1
	dpmiCs	resw	1
	dpmiSp	resw	1
	dpmiSs	resw	1
endstruc

struc		VCPI_SW
	vcpiCr3	resd	1
	vcpiGdt	resd	1
	vcpiIdt	resd	1
	vcpiLdt	resw	1
	vcpiT	resw	1
	vcpiEip	resd	1
	vcpiCs	resw	1
endstruc

struc		excregs
	eCs	resw	1
	eEip	resd	1
	eErr	resw	1
	eEfl	resd	1
	eEax	resd	1
	eEbx	resd	1
	eEcx	resd	1
	eEdx	resd	1
	eEsi	resd	1
	eEdi	resd	1
	eEbp	resd	1
	eDs	resw	1
	eEs	resw	1
	eFs	resw	1
	eGs	resw	1
	eSs	resw	1
	eEsp	resd	1
	eCR2	resd	1
endstruc

struc		rmcb
	cbType	resb	1		;0=unused, 1=internal, 2=DPMI defined
	cbCode	resb	Addr32b_size
	cbRegs	resb	Addr32b_size
endstruc

struc		x_hndl
	x_flag	resb	1
	x_addr	resd	1
	x_size	resd	1
endstruc

%macro		clTLB	1
		mov	%1,cr3
		mov	cr3,%1
%endmacro

%macro		exitErr	2
		mov	si,%1
		mov	al,%2
		jmp	stub_error
%endmacro

%if	TARGET==0
%define	stubtype	'1'

struc		d3x1_hdr
	sign	resd	1		;signature 'D3X1'
	hdrsize	resd	1		;header size (bytes)
	binsize	resd	1		;binary size (bytes)
	addsize	resd	1		;additional memory (bytes)
	entry	resd	1		;entry point
	tos	resd	1		;top of stack
endstruc
%elif	TARGET==1
%define	stubtype	'd'

s_vaddr		equ	12
s_size		equ	16
s_scnptr	equ	20
%elif	TARGET==2
%define	stubtype	'w'

struc		le_hdr
LESignature	resd	1	; must be 0x454C since order is little endian
LEFormatLevel	resd	1	; not really needed
LECpuType	resw	1
LETargetSystem	resw	1	; should be 01 (OS/2)
LEModuleVersion	resd	1	; who cares...
LEModuleType	resd	1	; Must be flat
LENumberPages	resd	1
LEEntrySection	resd	1
LEEntryOffset	resd	1
LEStackSection	resd	1
LEInitialESP	resd	1
LEPageSize	resd	1
LEBytesLastPage	resd	1
LEFixupSize	resd	1
LEFixupChecks	resd	1
LELoaderSize	resd	1
LELoaderChecks	resd	1
LEObjectTable	resd	1
LEObjectEntries	resd	1
LEObjectPMTable	resd	1
LEIterateData	resd	1
LEResourceTable	resd	1
LEResources	resd	1
LEResNamesTable	resd	1
LEEntryTable	resd	1
LEModuleDirTab	resd	1
LEModuleDirs	resd	1
LEFixupPageTab	resd	1
LEFixupRecords	resd	1
LEImpModTable	resd	1
LEImpModNum	resd	1
LEImpProcTable	resd	1
LEPPchecksumTab	resd	1
LEDataPages	resd	1
LEPreloadPages	resd	1
LENonResTable	resd	1
LENonResSize	resd	1
LENonResChecks	resd	1
LEAutoData	resd	1
LEDebugStart	resd	1
LEDebugSize	resd	1
LEPrelInstPages	resd	1
LEDemdInstPages	resd	1
LEExtraHeap	resd	1
LEReserved	resd	1
endstruc

struc		obj_tbl
OTVirtualSize	resd	1
OTRelocBase	resd	1
OTObjectFlags	resd	1
OTPageMapIndex	resd	1
OTPageMapSize	resd	1
OTReserved	resd	1
endstruc

struc		opm_tbl
OPMTUnknown	resb	1
OPMTFixupTabIdx	resw	1	; big endian!
OPMTType	resb	1	; 0 = no reloc 3 = reloc
endstruc

struc		fix_rec
FRType		resw	1
FROffset	resw	1
FRObject	resb	1
FRItem		resw	1
endstruc
%endif

;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
;	CODE & DATA 16bit
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
segment		_REAL	private use16 class=CODE

		align	4
stubinfo:
stubinfo_magic:					; char [16]
%if	TARGET==0
		db	"d3x1stub, v 1.00"	; version may change, [0..7] won't
%elif	TARGET==1
		db	"go32stub, v 2.04"	; version may change, [0..7] won't
%elif	TARGET==2
		db	"o2LEstub, v 0.90"	; version may change, [0..7] won't
%endif
stubinfo_size:					; unsigned long
		dd	stubinfo_end-stubinfo	; bytes in structure
stubinfo_minstack:				; unsigned long
		dd	200000h			; minimum amount of DPMI stack space (2M)
stubinfo_memory_handle:				; unsigned long
		dd	0			; DPMI memory handle
stubinfo_initial_size:				; unsigned long
		dd	0			; size of initial segment
stubinfo_minkeep:				; unsigned short
		dw	16384			; amount of automatic real-mode buffer
stubinfo_ds_selector:				; unsigned short
		dw	0			; our DS selector (used for transfer buffer)
stubinfo_ds_segment:				; unsigned short
		dw	0			; our DS segment (used for simulated calls)
stubinfo_psp_selector:				; unsigned short
		dw	0			; PSP selector
stubinfo_cs_selector:				; unsigned short
		dw	0			; to be freed
stubinfo_env_size:				; unsigned short
		dw	0			; number of bytes of environment
%if	TARGET==0
stubinfo_platform:				; platform
		dd	0			; any system bits are passed here
%elif	TARGET==1
stubinfo_basename:				; char [8]
		resb	8			; base name of executable to load (asciiz if < 8)
stubinfo_argv0:					; char [16]
		resb	16			; used ONLY by the application (asciiz if < 16)
stubinfo_dpmi_server:				; char [16]
		resb	16			; used by stub to load DPMI server if no DPMI
						; already present. Not used by us
%endif
		align	4
stubinfo_end:

;°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±
;	EntryPoint
;°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±
..start:
		cld
		push	cs
		pop	ds
	;Print via STDERR
;		mov	ah,40h
;		mov	bx,2
;		mov	cx,CopyRightL
;		mov	dx,CopyRight
;		int	21h
	;Clear BSS area
		push	es
		push	ds
		pop	es
		mov	di,lastbyte
		mov	cx,lastbss-lastbyte
		xor	al,al
		rep	stosb
		pop	es
		jmp	extender

exit:
		push	ax
		mov	ax,cs
		cmp	ax,1234h
REAL		equ	$-2
		jne	_exit
	;!@# Hack alert {
	;	If `exit' called from PM:
	;		if we!=DPMI, we'll never get here and don't care
	;		if we==DPMI, we got here by `main_switch' and DS==CS
	;	If `exit' called from RM:
	;		DS must equal CS
		call	cleanup
		cmp	byte [terminate],-1
		jne	_exit
		mov	ax,3
		int	10h
		mov	si,error_exit
		call	print_f
	;!@# Hack alert }
_exit:
		pop	ax
		mov	ah,4ch
		int	21h

;°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±
;	Error exits
;°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±
_need_386:
		exitErr	_386,-1
_need_dos3:
		exitErr	_dos,-2
_need_mem:
		exitErr	_mem,-3
_need_dpmi:
		exitErr	_dpmi,-4
_need_vcpi:
		exitErr	_vcpi,-5
_need_interf:
		exitErr	_interf,-6
_need_a20:
		exitErr	_a20,-7
_need_img:
		exitErr	_img,-8
_need_mem2:
		exitErr	_mem2,-9

stub_error:
		push	ax
		push	si
		mov	si,_error
		call	msg_stub_error
		pop	si
		call	msg_stub_error
		mov	dl,13
		int	21h
		mov	dl,10
		int	21h
		pop	ax
		jmp	exit

proc	near,	msg_stub_error
		mov	ah,02h
	beg_stub_error:
		cs	lodsb
		or	al,al
		jz	end_stub_error
		mov	dl,al
		int	21h
		jmp	beg_stub_error
	end_stub_error:
		ret
endp

;°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±
;	Initialization
;°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±
extender:
	;Check CPU and DOS ver
		call	detect_cpu
		cmp	dx,3
		jb	_need_386
		mov	byte [cpu_id],dl
		mov	ah,30h
		int	21h
		cmp	al,3
		jb	_need_dos3
		mov	word [REAL],cs		;allow cleanup at exit
		mov	word [stubinfo_ds_segment],_STACK+(Stack_16_sz/16)
		mov	word [PSP_Segment],es

	;Build exception reflection tables
		mov	cx,Num_Exc
		mov	si,Exc_Ints
		mov	di,Exc_Excs
		mov	ax,Interrupt0
		mov	dx,Real_C_desc - GDT
	tloop:
		mov	word [si+Ofs32b],ax
		mov	word [si+Sel16b],dx
		mov	word [di+Ofs32b],Exception_Terminate
		mov	word [di+Sel16b],dx
		add	si,Addr32b_size
		add	di,Addr32b_size
		add	ax,Interrupt1 - Interrupt0
		loop	tloop

	;Build GDT
		mov	ecx,cs
		shl	ecx,4
		lea	edx,[GDT]
		add	edx,ecx
		mov	dword [_GDTR+2],edx
		lea	edx,[LDT]
		add	edx,ecx
		callp	fixDescrBase, word LDT_desc, edx
		lea	edx,[tss1]
		add	edx,ecx
		callp	fixDescrBase, word TSS1_desc, edx
		lea	edx,[tss2]
		add	edx,ecx
		callp	fixDescrBase, word TSS2_desc, edx
		lea	edx,[tss8]
		add	edx,ecx
		callp	fixDescrBase, word TSS8_desc, edx

		callp	fixDescrBase2, word Locked_desc, word _LOCKED
		callp	fixDescrBase2, word PSP_desc, es
		callp	fixDescrBase2, word Env_desc, word [es:2ch]
		callp	fixDescrBase2, word Real_C_desc, cs
		callp	fixDescrBase2, word Real_D_desc, cs

	;Build IDT
		lea	edx,[IDT]
		add	edx,ecx
		mov	dword [_IDTR+2],edx

		mov	cx,256
		mov	si,IDT
		mov	ax,Interrupt0
	MakeIDT:
		mov	word [si+gateSel],Real_C_desc - GDT
		mov	byte [si+gateTyp],8eh
		mov	word [si+gateO1],ax
		add	ax,Interrupt1 - Interrupt0
		add	si,gate_size
		loop	MakeIDT

%ifdef	SSIRQ
		call	setup_irqs
%endif

		mov	cx,Num_Exc
		mov	si,IDT
		mov	ax,xInterrupt0
	MakeXIDT:
		mov	word [si+gateO1],ax
		add	ax,xInterrupt1 - xInterrupt0
		add	si,gate_size
		loop	MakeXIDT

		mov	word [IDT+8*gate_size+gateSel],TSS8_desc - GDT
		mov	word [IDT+8*gate_size+gateTyp],85h

	;Build TSSs
		mov	cx,3
		mov	si,tss1
	common_tss:
		mov	word [si+tssEfl],3002h
		mov	word [si+tssEs],PSP_desc - GDT
		mov	word [si+tssCs],Real_C_desc - GDT
		mov	word [si+tssSs],Real_D_desc - GDT
		mov	word [si+tssDs],Real_D_desc - GDT
		mov	word [si+tssFs],Flat_D_desc - GDT
		mov	word [si+tssGs],Flat_C_desc - GDT
		mov	word [si+tssLDT],LDT_desc - GDT
		mov	word [si+tssIO],TSS_size
		add	si,word TSS_size
		loop	common_tss

		mov	dword [tss2+tssEip],exec_bin
		mov	dword [tss2+tssEsp],tss2+tssStk

		mov	dword [tss8+tssEip],Double_fault
		mov	dword [tss8+tssEsp],tss8+tssStk
		mov	dword [tss8+tssEbp],tss8+tssStk

	;We don't change rings through gates, therefore this is superfluous
	;	mov	word [tss1+tssSs0],Real_D_desc - GDT
	;	mov	dword [tss1+tssEsp0],tss1+tssStk
	;	mov	word [tss2+tssSs0],Real_D_desc - GDT
	;	mov	dword [tss2+tssEsp0],tss2+tssStk
	;	mov	word [tss8+tssSs0],Real_D_desc - GDT
	;	mov	dword [tss8+tssEsp0],tss8+tssStk

	;Test DPMI presence
		mov	ax,1687h
		int	2fh
		test	ax,ax
		jz	DPMInterface
	;Test VCPI presence
		call	detect_VCPI
		or	ah,ah
		jz	VCPInterface
	;Test unconventional protected mode
		smsw	ax
		test	al,1
		jnz	_need_interf
	;Verify XMS presence
		mov	ax,4300h
		int	2fh
		cmp	al,80h
		jne	setup_pm
		mov	byte [platform],1
		mov	ax,4310h
		int	2fh
		mov	word [XMS_Driver+Ofs16b],bx
		mov	word [XMS_Driver+Seg16b],es

	;Setup protected mode
	setup_pm:
		call	startup
%ifndef	PAGED
	;Load the D3X application
		call	load_exe
%else
		jmp	build_pagetables
%endif
	enter_pm:
%ifdef	SWAT
		SWAT_INI
%endif
	;Enter UNreal (protected)
		call	main_switch

	shut_down:
		pop	bp,bp
		mov	al,byte [tss2+tssEax]
		jmp	exit

;°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±
;	DPMInterface
;		In:	ES:DI	= protected mode entry point
;			SI	= number of paragraphs to allocate for DPMI
;°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±
DPMInterface:
		mov	byte [platform],3
	;Save DPMI protected mode entry point
		mov	word [DPMI_Entry+Ofs16b],di
		mov	word [DPMI_Entry+Seg16b],es
	;Resize memory to fit DPMI needs
		mov	es,word [stubinfo_ds_segment]
		add	word [stubinfo_ds_segment],si
	;Load the D3X application
		call	load_exe
	;Call real to protected mode entry point for 32bit
		mov	ax,1
		call	far [DPMI_Entry]
		jump	c,_need_dpmi
		push	es
	;Set the protection ring on my descriptors
		push	ds
		pop	es
		mov	bx,cs
		lar	ax,bx
		and	ah,mask_DPL
		mov	si,DPMI_sel1
		mov	cx,DPMI_sels
	.setDPL:
		or	byte [si+access],ah
		add	si,descr_size
		loop	.setDPL
	;Allocate selectors
		xor	ax,ax
		mov	cx,DPMI_sels
		int	31h
		go	c,_need_dpmi
		mov	bx,ax
	;Build selectors
		lea	edi,[DPMI_sel1]
	.set_Descr:
		push	bx
		mov	ax,000ch
		int	31h
		go	c,_need_dpmi
		mov	ax,0003h
		int	31h
		add	bx,ax
		add	edi,descr_size
		loop	.set_Descr
	;Save allocated selectors
		pop	gs		;Flat code
		pop	fs		;Flat data
		pop	es		;PSP selector
		jmp	exec_bin2

;°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±
;	VCPInterface
;°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±
VCPInterface:
		mov	byte [platform],2

	;Read PIC mappings from VCPI server
		mov	ax,0de0ah
		int	67h
		mov	word [MasterPIC],bx
		mov	word [SlavePIC],cx

	build_pagetables:
	; determine here # of max pagetables + pagedir
		mov	ax,0de03h			; # of free pages
                VCPIreq
	; align to next pagetable + first MB + ExtraLinear
		add	edx,1023+1024+(((EXTRALINEAR+4096*1024-1) & ~(4096*1024-1))/(1024*4))
		and	dx,~1023
		shl	edx,2				; # of bytes for page tables
		mov	dword [pt_delta],edx		; store size of pagetables
		shr	edx,12-8			; calc # of paragraphs
		mov	bx,dx				; if highword is not zero, you would
							; still have enough mem!
		add	bx,(4096+4096) / 16		; page directory and page align

		movzx	eax,word [stubinfo_ds_segment]
		add	word [stubinfo_ds_segment],bx
	;Load the D3X application
		call	load_exe

		shl	eax,4
		add	eax,4095
		and	ax,~4095			; align data area on page
		mov	dword [pagedir],eax

		add	eax,1000h			; skip pagedir
		mov	dword [pt_base],eax		; set base of page table area
		add	eax,dword [pt_delta]		; size of pagetables
		mov	dword [pt_top],eax		; set top of page table area

		mov	eax,dword [pagedir]
		shr	eax,4
		mov	gs,ax				; GS = segment of page directory
		mov	es,ax				; ES = segment of page directory
		add	ax,100h
		mov	fs,ax				; FS = segment of first page table

		xor	ax,ax
		mov	di,ax
		mov	cx,4096
		cld
		rep	stosw				; Clear pagedir & first pagetable

		push	fs
		pop	es				; ES = segment of first pagetable
		xor	di,di
		mov	si,VCPI1_desc			; descriptors for VCPI host
		mov	ax,0de01h
                VCPIreq
		test	ah,ah
		jnz	_need_vcpi

		movzx	eax,di				; set base of usable page table area
		add	ax,4095
		and	ax,~4095			; align to next pagetable
		add	eax,dword [pt_base]
		mov	dword [pt_free],eax

		mov	dword [VCPI_Entry+Ofs32b],ebx

	; For safety clear reserved bits 9-11
	ClearResBits:
		and	byte [es:di+1],0f1h
		sub	di,4
		jnc	ClearResBits

	; Link pagetables in pagedirectory
		mov	si,es				; DX = current page table segment
		xor	ebx,ebx				; index in page dir, also loop counter
		mov	ebp,dword [pt_delta]
		shr	ebp,12				; # of pagetables
		jmp	LinkPage

	ClearPage:
		xor	di,di				; clear page table
		mov	cx,800h
		xor	ax,ax
		rep	stosw
	LinkPage:
		mov	cx,si
		shr	cx,8				; get page number
		mov	ax,0de06h			; get physical address
                VCPIreq
		and	dh,0f1h				; For safety clear reserved bits 9-11
		or	dl,PT_U | PT_W | PT_P		; set present, User and R/W bit
		mov	dword [gs:ebx*4],edx		; store in page directory
		add	si,100h				; increment page table segment
		mov	es,si
		inc	bx				; increment index in page directory
		cmp	bx,bp				; at end of page tables?
		jb	ClearPage			; if no, loop

		mov	ecx,dword [pagedir]
		shr	ecx,12
		mov	ax,0de06h
		VCPIreq					; get physical address

		mov	dword [tss1+tssCr3],edx
		mov	dword [tss2+tssCr3],edx
		mov	dword [tss8+tssCr3],edx

	;Build VCPI switch structure
		mov	dword [VCPI+vcpiCr3],edx
		mov	ecx,cs
		shl	ecx,4
		lea	edx,[_GDTR]
		add	edx,ecx
		mov	dword [VCPI+vcpiGdt],edx
		lea	edx,[_IDTR]
		add	edx,ecx
		mov	dword [VCPI+vcpiIdt],edx

		lea	edx,[VCPI]
		add	edx,ecx
		mov	dword [_VCPI_],edx

		jmp	enter_pm

;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
;	Detect VCPI presence
;		Out:	AH	= 0 if VCPI present, 84h if not
;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
proc	near,	detect_VCPI
		mov	ax,3567h
		int	21h
		mov	ax,es
		or	ax,bx
		jz	VCPI_bad
		mov	bx,1
		mov	ah,43h
		int	67h
		or	ah,ah
		jne	.test_vcpi
		mov	word [VCPI_tHandle],dx
	.test_vcpi:
		mov	ax,0de00h
		int	67h
		or	ah,ah
		jz	VCPI_end
	VCPI_done:
		mov	dx,word [VCPI_tHandle]
		cmp	dx,-1
		je	VCPI_bad
		mov	ah,45h				; release EMS test handle
		int	67h
		mov	word [VCPI_tHandle],-1
	VCPI_bad:
		mov	ah,84h
	VCPI_end:
		ret
endp

%ifdef	PAGED
;°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±
;	Pseudo VCPI server (our paging)
;°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±
proc	near,	myVCPIr
		cmp	byte [platform],2
		jne	pseudo_vcpi
		int	67h
		ret
	pseudo_vcpi:
		cmp	al,01
		je	vcpi_getinterface
		cmp	al,03
		je	vcpi_getfreepages
		cmp	al,06
		je	vcpi_getphysaddr
		mov	ah,8fh
		ret
	vcpi_getinterface:
		push	eax,ecx,si
		push	di,es
		push	ds
		pop	es
		mov	di,si
		mov	si,Real_C_desc
		movsd
		movsd
		mov	es,word [page_array]
		xor	di,di
		mov	ecx,dword [max_pages]
		add	ecx,31
		shr	ecx,5
		xor	eax,eax
		rep	stosd
		shl	dword [page_array],4
		pop	es,di
		mov	ch,1
		mov	al,PT_D | PT_A | PT_U | PT_W | PT_P
	.map_1M:
		stosd
		add	eax,1000h
		loop	.map_1M
		lea	ebx,[myVCPIp]
		pop	si,ecx,eax
		xor	ah,ah
		ret
	vcpi_getfreepages:
		mov	edx,dword [raw_top]
		sub	edx,dword [raw_base]
		shr	edx,12
		xor	ah,ah
		ret
	vcpi_getphysaddr:
		movzx	edx,cx
		shl	edx,12
		xor	ah,ah
		ret
endp

proc	far,	myVCPIp
		cmp	al,04
		je	vcpi_alloc_physical
		cmp	al,05
		je	vcpi_free_physical
		mov	ah,8fh
		retfd
	vcpi_alloc_physical:
		push	ecx,esi,fs
		mov	ax,Flat_D_desc - GDT
		mov	fs,ax
		mov	esi,dword [page_array]
		mov	ecx,dword [plast]
		mov	ah,88h
	.loop:
		cmp	ecx,dword [max_pages]
		jnb	.ret_a
		call	bt_array
		jnz	.next
		mov	dword [plast],ecx
		call	bs_array
		mov	edx,ecx
		shl	edx,12
		add	edx,dword [raw_base]
		xor	ah,ah
	.ret_a:
		pop	fs,esi,ecx
		retfd
	.next:
		inc	ecx
		jmp	.loop
	vcpi_free_physical:
		push	ecx,fs
		mov	cx,Flat_D_desc - GDT
		mov	fs,cx
		mov	esi,dword [page_array]
		mov	ecx,edx
		sub	ecx,dword [raw_base]
		shr	ecx,12
		call	bt_array
		mov	ah,8ah
		jz	.ret_f
		call	br_array
		xor	ah,ah
		cmp	dword [plast],ecx
		jb	.ret_f
		mov	dword [plast],ecx
	.ret_f:
		pop	fs,ecx
		retfd

proc	near,	bt_array
		push	ecx
		push	cx
		mov	al,80h
		and	cl,7
		ror	al,cl
		pop	cx
		shr	ecx,3
		test	byte [fs:esi+ecx],al
		pop	ecx
		ret
endp

proc	near,	bs_array
		push	ecx
		push	cx
		mov	al,80h
		and	cl,7
		ror	al,cl
		pop	cx
		shr	ecx,3
		or	byte [fs:esi+ecx],al
		pop	ecx
		ret
endp

proc	near,	br_array
		push	ecx
		push	cx
		mov	al,7fh
		and	cl,7
		ror	al,cl
		pop	cx
		shr	ecx,3
		and	byte [fs:esi+ecx],al
		pop	ecx
		ret
endp

endp
%endif

;°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±
;	Non-DPMI Initialization
;°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±

;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
;	Initialize descriptors
;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
proc	near,	fixDescrBas
		mov	word [bx+B1],ax
		shr	eax,16
		mov	byte [bx+B2],al
		mov	byte [bx+B3],ah
		ret
endp

proc	__stdcall,near, fixDescrBase
	arg	%$address,4
	arg	%$descriptor,2
		mov	bx,word [bp+%$descriptor]
		mov	eax,dword [bp+%$address]
		call	fixDescrBas
		ret
endp

proc	__stdcall,near, fixDescrBase2
	arg	%$realseg,2
	arg	%$descriptor,2
		mov	bx,word [bp+%$descriptor]
		movzx	eax,word [bp+%$realseg]
		shl	eax,4
		call	fixDescrBas
		ret
endp

;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
;	Startup & cleanup
;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
proc	near,	startup
		cli

		call	Test_A20
		je	.s_0
		mov	bl,1
		call	Switch_A20
		test	al,al
		jz	_need_a20
		mov	byte [A20],al
	.s_0:

%ifdef	PAGED
		cmp	byte [platform],1
		je	x_startup
%endif
		cmp	byte [platform],0
		jne	endstartup
	r_startup:
		call	get_extmem_raw
		jae	endstartup
		push	ax
		mov	ax,3515h
		int	21h
		mov	word [old15+Ofs16b],bx
		mov	word [old15+Seg16b],es
		mov	ax,2515h
		mov	dx,new15
		int	21h
		pop	ax
%ifndef	PAGED
		mov	bx,X_HDescr
		mov	cx,MaxXHndl
	.s_1:
		mov	byte [bx+x_flag],4
		add	bx,x_hndl_size
		loop	.s_1
		mov	edx,dword [raw_top]
		sub	edx,eax
		mov	byte [X_HDescr+x_flag],1
		mov	dword [X_HDescr+x_addr],eax
		mov	dword [X_HDescr+x_size],edx
	endstartup:
		ret
%else
	xr_startup:
		add	eax,4095
		and	ax,~4095
		mov	dword [raw_base],eax
		call	vcpi_getfreepages
		mov	dword [max_pages],edx
		add	edx,127
		shr	edx,7		;bits to paragraphs
		mov	ax,word [stubinfo_ds_segment]
		add	word [stubinfo_ds_segment],dx
		mov	word [page_array],ax
	endstartup:
		ret
	x_startup:
		xor	eax,eax
		mov	ah,88h
		call	far [XMS_Driver]
		cmp	bl,80h
		jne	.xmsok1
		mov	ah,08h
		call	far [XMS_Driver]
	.xmsok1:
		mov	esi,eax
		mov	edx,eax
		shr	eax,16
		mov	ah,09h
		jz	.xmsok2
		mov	ah,89h
	.xmsok2:
		call	far [XMS_Driver]
		test	ax,ax
		jz	endstartup
		mov	word [xms_handle],dx
		mov	ah,0ch
		call	far [XMS_Driver]
		push	dx,bx
		pop	eax
		shl	esi,10
		add	esi,eax
		mov	dword [raw_base],eax
		mov	dword [raw_top],esi
		jmp	xr_startup
%endif
endp

proc	near,	cleanup
		mov	dx,020h
		call	clean_pic
		mov	dx,0a0h
		call	clean_pic

		call	reset_hwints

		mov	si,X_H
		mov	cx,MaxXHndl
	.c_0:
		lodsd
		test	eax,eax
		jz	.c_1
		push	cx,si
		xchg	esi,eax
		call	clean_free
		pop	si,cx
	.c_1:
		loop	.c_0

		call	VCPI_done

%ifdef	PAGED
		cmp	word [xms_handle],0
		je	.c_2
		mov	dx,word [xms_handle]
		mov	ah,0dh
		call	far [XMS_Driver]
		mov	ah,0ah
		call	far [XMS_Driver]
	.c_2:
%endif

		push	ds
		lds	dx,[old15]
		mov	ax,ds
		or	ax,dx
		jz	.c_3
		mov	ax,2515h
		int	21h
	.c_3:
		pop	ds

		xor	bl,bl
		cmp	byte [A20],bl
		je	.c_4
		call	Switch_A20
	.c_4:
		ret
endp

proc	near,	get_extmem_raw
		xor	eax,eax
		mov	ah,88h
		int	15h
		test	ax,ax
		jz	.no_raw_mem
		shl	eax,10
		add	eax,100000h
		mov	dword [raw_top],eax
		mov	ax,0e801h
		int	15h
		jc	.vdisk_1
		cmp	ax,3c00h
		jne     .vdisk_1
		inc	bh
		shl	ebx,16
		mov	dword [raw_top],ebx
	.vdisk_1:
		xor	cx,cx
		mov	es,cx
		les	bx,[es:4*19h]
		mov	eax,100000h
		cmp	dword [es:bx+12h],'VDIS'
		jne	.vdisk_2
		mov	eax,dword [es:bx+2ch]
		add	eax,3ffh		;round to kb
		and	eax,0fffc00h		;address is only 24bit
	.vdisk_2:
		dec	cx
		mov	es,cx
		cmp	dword [es:13h],'VDIS'
		jne	.set_base
		movzx	ebx,word [es:2eh]
		shl	ebx,10
		cmp	eax,ebx
		ja	.set_base
		mov	eax,ebx
	.set_base:
		mov	dword [raw_base],eax
		cmp	eax,dword [raw_top]
	.no_raw_mem:
		ret
endp

proc	near,	clean_pic
		mov	al,0bh
		out	dx,al
		jmps	$+2
		in	al,dx
		test	al,al
		jz	pic_clean
		mov	al,20h
		out	dx,al
	pic_clean:
		ret
endp

proc	near,	reset_hwints
		xor	ax,ax
		mov	es,ax
	;This is an `inverse function' for _irqno
		mov	cx,Num_IRQ+2
	hw_loop:
		dec	cx
		jl	out_hw_loop
		mov	bx,cx
		cmp	cx,Num_IRQ+1
		je	_23h
		cmp	cx,Num_IRQ
		je	_1ch
		cmp	cx,Num_IRQ/2
		jae	_2ndpic
	_1stpic:
		add	bx,word [MasterPIC]
		jmp	reset_hw
	_2ndpic:
		add	bx,word [SlavePIC]
		sub	bx,Num_IRQ/2
		jmp	reset_hw
	_1ch:
		mov	bl,1ch
		jmp	reset_hw
	_23h:
		mov	bl,23h
		jmp	reset_hw
	reset_hw:
	;Restore hardware ints the user forgot hooked (timer, keyb ...)
		mov	si,cx
		shl	si,2
		mov	eax,dword [hwint_rmcb+si]
		test	eax,eax
		jz	hw_loop
		shl	bx,2
		mov	dword [es:bx],eax
		jmp	hw_loop
	out_hw_loop:
		ret
endp

proc	near,	clean_free
		cmp	byte [platform],2
		je	vc_free
%ifndef	PAGED
		cmp	byte [platform],1
		je	xc_free
	;In raw mode, freeing is done in block, by revectoring int 15h
		ret
	xc_free:
		call	_free_xmcb
%endif
		ret
	vc_free:
		xor	ecx,ecx
		push	esi
		mov	eax,esi
		shr	eax,4
		mov	es,ax
		and	si,0fh
		xchg	ecx,dword [es:si]
		pop	esi
		add	esi,4
		mov	edx,ecx
		and	dx,0f000h
		mov	ax,0de05h
		int	67h
		test	ch,PT_E
		jz	vc_free
		ret
endp

new15:
		cmp	ah,88h
		jne	out15
		xor	ax,ax
		iret
out15:
		db	0eah
old15		resb	Addr16b_size

;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
;	A20 gate
;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
proc	near,	Switch_A20
		cmp	byte [platform],1
		je	.X_A20Sw
		cmp	byte [platform],0
		je	.R_A20Sw
	.V_A20Sw:
		mov	al,bl
		ret
	.X_A20Sw:
		mov	ah,bl
		xor	ah,1
		add	ah,5
		call	far [XMS_Driver]
		ret
	.R_A20Sw:
		mov	cl,bl
		shl	bl,1
	;Try port 92h
		in	al,92h
		and	al,~2
		or	al,bl
		jmps	$+2
		out	92h,al
	;Now check
		call	Test_A20
		cmp	al,cl
		je	a20_ok
	;Try 8042
		call	wait_1to0
		mov	al,0d1h
		out	64h,al
		call	wait_1to0
		mov	al,0dfh & ~2
		or	al,bl
		out	60h,al
		call	wait_1to0
		mov	al,0ffh
		out	64h,al
		call	wait_1to0
		call	Test_A20
	a20_ok:
		ret
endp

proc	near,	wait_1to0
		xor	cx,cx
	loop_1to0:
		jmps	$+2
		in	al,64h			; port 64h, keyboard status
		and	al,2
		loopnz	loop_1to0
		ret
endp

proc	near,	Test_A20
		push	ds
		xor	ax,ax
		mov	ds,ax
		dec	ax
		mov	es,ax
		mov	al,byte [00h]
		mov	ah,al
		not	al
		xchg	al,byte [es:10h]
		xchg	bl,byte [00h]
		cmp	ah,bl
		xchg	bl,byte [00h]
		mov	byte [es:10h],al
		sete	al
		pop	ds
		ret
endp

;°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±
;	Main switch routine
;°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±
proc	near,	main_switch
		push	bp
	init_application:
		cli
		mov	bx,-1
		and	byte [TSS1_desc+access],0fdh

		mov	word [real_stack+Ofs16b],sp
		mov	word [real_stack+Seg16b],ss
		movzx	esp,sp

		cmp	byte [platform],2
		jb	RMtoPM_raw
		mov	esi,dword [_VCPI_]
		mov	ax,0de0ch
		int	67h

	RMtoPM_raw:
		lgdt	[_GDTR]
		lidt	[_IDTR]

%ifndef	PAGED
		mov	eax,cr0
		and	eax,7ffffff6h
		or	al,1
%else
		mov	eax,dword [tss1+tssCr3]
		mov	cr3,eax
		mov	eax,cr0
		and	al,0f6h
		or	eax,80000001h
%endif
		mov	cr0,eax
		db	0eah
		dw	$+4
		dw	Real_C_desc - GDT

		mov	ax,TSS1_desc - GDT
		ltr	ax
		mov	ax,LDT_desc - GDT
		lldt	ax

	vcpi_protected:
		mov	ax,Real_D_desc - GDT
		mov	ds,ax
		mov	es,ax
		mov	fs,ax
		mov	gs,ax
		mov	ss,ax
		mov	sp,tss1+tssStk

		jmpt	TSS2_desc - GDT

		cmp	byte [platform],2
		jb	PMtoRM_raw
		clts
		mov	eax,_REAL	;high word of EAX is zeroed
		push	eax
		push	eax
		push	eax
		push	eax
		mov	ax,word [real_stack+Seg16b]
		push	eax
		mov	ax,word [real_stack+Ofs16b]
		push	eax
		pushfd
		push	dword _REAL
		push	dword vcpi_real
		mov	ax,Flat_D_desc - GDT
		mov	ds,ax
		mov	ax,0de0ch
		call	dword far [cs:VCPI_Entry]

	PMtoRM_raw:
		mov	ax,Real_D_desc - GDT
		mov	es,ax
		mov	fs,ax
		mov	gs,ax

		mov	eax,cr0
		and	eax,7ffffff6h
		mov	cr0,eax
		db	0eah
		dw	$+4
		dw	_REAL

		mov	ax,cs
		mov	ds,ax
		lss	sp,[real_stack]

		lidt	[_IDTR_]

	vcpi_real:
		cmp	byte [terminate],0
		jne	shut_down
		test	bx,bx
		js	exit_application
		call	simulate_int
		jmp	init_application

	exit_application:
		pop	bp
		ret
endp

;°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±
;	Simulate real interrupt
;		In:	BL	= interrupt number
;°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±
proc	near,	simulate_int
		cmp	bl,31h
		je	rDPMI_Functions

		mov	ax,word [tss2+tssEfl]
		and	ax,0011111011010101b		;User flags
		push	ax
		push	cs

		call	do_int

		pushf
		call	to_user

		pop	ax
		mov	dx,0000111011010101b		;User flags
		mov	cx,word [tss2+tssEfl]
		and	ax,dx
		not	dx
		and	cx,dx
		or	ax,cx
		mov	word [tss2+tssEfl],ax
		ret

	do_int:
		and	ah,~2
		push	ax

		call	_irqno
		jc	IVT
		shl	bp,2
		mov	eax,dword [ds:hwint_rmcb+bp]
		test	eax,eax
		jnz	do_it
	IVT:
		xor	ax,ax
		mov	es,ax
		shl	bx,2
		mov	eax,dword [es:bx]
	do_it:
		push	eax

		call	from_user

		iret

proc	near,	to_user
		cli
		mov	dword [tss2+tssEax],eax
		mov	dword [tss2+tssEbx],ebx
		mov	dword [tss2+tssEcx],ecx
		mov	dword [tss2+tssEdx],edx
		mov	dword [tss2+tssEsi],esi
		mov	dword [tss2+tssEdi],edi
		mov	dword [tss2+tssEbp],ebp
		ret
endp

proc	near,	from_user
		mov	eax,dword [tss2+tssEax]
		mov	ebx,dword [tss2+tssEbx]
		mov	ecx,dword [tss2+tssEcx]
		mov	edx,dword [tss2+tssEdx]
		mov	esi,dword [tss2+tssEsi]
		mov	edi,dword [tss2+tssEdi]
		mov	ebp,dword [tss2+tssEbp]
		ret
endp

;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
;	Real mode DPMI services
;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
rDPMI_Functions:
		mov	ax,word [tss2+tssEax]

		cmp	ax,0001h
		je	DPMI_FreeLDTSel
		cmp	ax,0100h
		je	DPMI_AllocDOS
		cmp	ax,0101h
		je	DPMI_FreeDOS
		cmp	ax,0102h
		je	DPMI_ResizeDOS
		cmp	ax,0300h
		je	DPMI_Simulate_RFrame
		cmp	ax,0301h
		je	DPMI_Simulate_RFrame
		cmp	ax,0302h
		je	DPMI_Simulate_RFrame
%ifndef	PAGED
		cmp	ax,0500h
		je	DPMI_GetMemory
		cmp	ax,0501h
		je	DPMI_AllocMemory
		cmp	ax,0502h
		je	DPMI_FreeMemory
		cmp	ax,0503h
		je	DPMI_ResizeMemory
%endif
	rDPMI_Fail:
		or	byte [tss2+tssEfl],1
	rDPMI_Quit:
		ret

DPMI_FreeLDTSel:
		mov	bx,word [tss2+tssEbx]
	FreeSelector:
		call	_free_LDT_sel
		jc	rDPMI_Fail
		mov	si,tss2+tssEs
		mov	cx,4
	FreeSelLoop:
		cmp	word [si],bx
		jne	FreeSelSkip
		mov	word [si],0
	FreeSelSkip:
		cmp	cx,4
		jne	FreeSelNext
		add	si,8
	FreeSelNext:
		add	si,4
		loop	FreeSelLoop
		jmp	rDPMI_Quit
DPMI_AllocDOS:
		mov	cx,1
		call	_alloc_LDT_sel
		jc	rDPMI_Fail
		mov	bx,word [tss2+tssEbx]
		mov	ah,48h
		int	21h
		mov	word [tss2+tssEax],ax
		jc	AllocDOSFail
		mov	word [tss2+tssEdx],di
		movzx	edx,bx
		and	di,~7
		lea	bx,[LDT+di]
		callp	fixDescrBase2, bx, ax
		jmp	DOSBlockLim
	AllocDOSFail:
		mov	word [tss2+tssEbx],bx
		mov	bx,di
		call	_free_LDT_sel
		jmp	rDPMI_Fail
DPMI_FreeDOS:
		mov	bx,word [tss2+tssEdx]
		call	_verify_sel
		jc	rDPMI_Fail
		mov	ah,byte [LDT+bx+B3]
		mov	al,byte [LDT+bx+B2]
		shl	eax,16
		mov	ax,word [LDT+bx+B1]
		shr	eax,4
		mov	es,ax
		mov	ah,49h
		int	21h
		jc	FreeDOSFail
		mov	bx,word [tss2+tssEdx]
		jmp	FreeSelector
	FreeDOSFail:
		mov	word [tss2+tssEax],ax
		jmp	rDPMI_Fail
DPMI_ResizeDOS:
		mov	bx,word [tss2+tssEdx]
		call	_verify_sel
		jc	rDPMI_Fail
		mov	di,bx
		mov	dh,byte [LDT+bx+B3]
		mov	dl,byte [LDT+bx+B2]
		shl	edx,16
		mov	dx,word [LDT+bx+B1]
		shr	edx,4
		mov	es,dx
		mov	bx,word [tss2+tssEbx]
		mov	ah,4ah
		int	21h
		jc	ResizeDOSFail
		movzx	edx,bx
	DOSBlockLim:
		shl	edx,4
		dec	edx
		mov	word [LDT+di+L1],dx
		shr	edx,16
		and	dl,mask_L2
		and	byte [LDT+di+granted],~mask_L2
		or	byte [LDT+di+granted],dl
		jmp	rDPMI_Quit
	ResizeDOSFail:
		mov	word [tss2+tssEax],ax
		mov	word [tss2+tssEbx],bx
		jmp	rDPMI_Fail
DPMI_Simulate_RFrame:
		mov	dx,sp
		sub	sp,Stack_Sp_sz
	;Here DR is set by `pSimulate_RFrame'
		test	al,al
		jnz	User_CS_IP
		xor	bx,bx
		mov	es,bx
		mov	bl,byte [tss2+tssEbx]
		shl	bx,2
		mov	ebx,dword [es:bx]
		mov	dword [DR+dpmiIp],ebx
	User_CS_IP:
		cmp	dword [DR+dpmiSp],0
		jnz	User_Stack
		mov	word [DR+dpmiSs],ss
		mov	word [DR+dpmiSp],dx
	User_Stack:
		and	word [DR+dpmiFl],3ed7h
		or	word [DR+dpmiFl],3002h
		mov	cx,word [tss2+tssEcx]
		jcxz	No_User_Parms
		shl	cx,1
		sub	word [DR+dpmiSp],cx
		mov	esi,dword [tss2+tssEsp]
		mov	bx,word [tss2+tssSs]
		movzx	edi,word [DR+dpmiSp]
		movzx	edx,word [DR+dpmiSs]
		shl	edx,4
		add	edi,edx
		mov	dx,Flat_D_desc - GDT
		call	memcopy
	No_User_Parms:
		push	dword [tmp_stack]	;Save for reentrancy
		mov	word [tmp_stack+Ofs16b],sp
		mov	word [tmp_stack+Seg16b],ss
		lss	sp,[DR+dpmiSp]
		cmp	al,1
		je	RetF_Frame
		push	word [DR+dpmiFl]
		and	word [DR+dpmiFl],0fcffh
	RetF_Frame:
		push	cs
		push	word ReturnFromFrame
		push	word [DR+dpmiFl]
		push	word [DR+dpmiCs]
		push	word [DR+dpmiIp]
		call	rest_regs
		iret
	ReturnFromFrame:
		call	save_regs
		lss	sp,[tmp_stack]			;Restore stack
		pop	dword [tmp_stack]
		add	sp,Stack_Sp_sz
	;Restore RM registers
		mov	bx,Real_D_desc - GDT
		lea	esi,[DR]
		mov	dx,word [tss2+tssEs]
		mov	edi,dword [tss2+tssEdi]
		mov	cx,DPMI_R_size-4*2	;Don't update CS:IP or SS:SP
		call	memcopy
		jmp	rDPMI_Quit
%ifndef	PAGED
DPMI_GetMemory:
		xor	eax,eax
		mov	ah,88h
		call	far [XMS_Driver]
		cmp	bl,80h
		jne	.xmsok
		mov	ah,08h
		call	far [XMS_Driver]
	.xmsok:
		shl	eax,10
		mov	edx,eax
		shr	edx,12
		push	edx
		push	edx
		push	eax
		mov	edi,ss
		shl	edi,4
		movzx	esi,sp
		add	esi,edi
		mov	bx,Flat_D_desc - GDT
		mov	dx,word [tss2+tssEs]
		mov	edi,dword [tss2+tssEdi]
		mov	cx,3*4
		call	memcopy
		add	sp,3*4
		jmp	rDPMI_Quit
DPMI_AllocMemory:
		mov	dx,word [tss2+tssEbx]
		shl	edx,16
		mov	dx,word [tss2+tssEcx]
		call	_malloc
	retblock:
		jc	rDPMI_Fail
		mov	word [tss2+tssEdi],ax
		shr	eax,16
		mov	word [tss2+tssEsi],ax
		mov	word [tss2+tssEcx],dx
		shr	edx,16
		mov	word [tss2+tssEbx],dx
		jmp	rDPMI_Quit
DPMI_FreeMemory:
		mov	si,word [tss2+tssEsi]
		shl	esi,16
		mov	si,word [tss2+tssEdi]
		call	_mfree
		jc	rDPMI_Fail
		jmp	rDPMI_Quit
DPMI_ResizeMemory:
		mov	dx,word [tss2+tssEbx]
		shl	edx,16
		mov	dx,word [tss2+tssEcx]
		mov	si,word [tss2+tssEsi]
		shl	esi,16
		mov	si,word [tss2+tssEdi]
		call	_realloc
		jmp	retblock
%endif

;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
;	RM DPMI aid functions
;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
proc	near,	_malloc
		test	edx,edx				;!@# Don't allow >7FFFFFFFh
		jle	malloc_fail
		xor	eax,eax
		call	_test_x
		jne	malloc_fail
		push	di,es
		call	_alloc_xmcb
		pop	es,di
		jc	malloc_fail
		call	_inject_x
		clc
		ret
	malloc_fail:
		stc
		ret
endp

proc	near,	_mfree
		mov	eax,esi
		test	eax,eax
		jz	mfree_fail
		call	_test_x
		jne	mfree_fail
		push	di,es
		call	_free_xmcb
		pop	es,di
		jc	mfree_fail
		call	_inject_x
		clc
		ret
	mfree_fail:
		stc
		ret
endp

proc	near,	_realloc
		test	edx,edx				;!@# Don't allow >7FFFFFFFh
		jle	realloc_fail
		mov	eax,esi
		test	eax,eax
		jz	realloc_fail
		call	_test_x
		jne	realloc_fail
		push	di,es
		call	_realloc_xmcb
		pop	es,di
		jc	realloc_fail
		call	_inject_x
		clc
		ret
	realloc_fail:
		stc
		ret
endp

proc	near,	_alloc_xmcb
%ifndef	PAGED
		cmp	byte [platform],2
		je	alloc_xmcb_v
		cmp	byte [platform],1
		je	alloc_xmcb_x
		cmp	byte [platform],0
		je	alloc_xmcb_r
	alloc_xmcb_fail:
		stc
		ret
	alloc_xmcb_v:
%endif
		mov	ax,Flat_D_desc - GDT
		mov	es,ax
		add	edx,4095
		shr	edx,12
		mov	ebx,edx
		call	vcpi_malloc
		jc	alloc_xmcb_fail
		clTLB	eax
		mov	edx,esi
		sub	edx,dword [pt_base]
		shl	edx,10
		mov	eax,esi
%ifndef	PAGED
		jmp	alloc_xmcb_done
	alloc_xmcb_x:
		add	edx,1023
		shr	edx,10
		cmp	edx,0ffffh
		mov	ah,09h
		jbe	.xmsok
		mov	ah,89h
	.xmsok:
		call	far [XMS_Driver]
		test	ax,ax
		jz	alloc_xmcb_fail
		push	0,dx
		mov	ah,0ch
		call	far [XMS_Driver]
		shl	edx,16
		mov	dx,bx		;Base address in EDX
		pop	eax		;Handle in EAX
		jmp	alloc_xmcb_done
	alloc_xmcb_r:
		add	edx,1023
		and	dx,~1023	;Make it kb granular
		mov	ebx,edx
		call	raw_malloc
		jc	alloc_xmcb_fail
		mov	edx,dword [si+x_addr]
		mov	eax,esi
	alloc_xmcb_done:
%endif
		clc
		ret
%ifdef	PAGED
	alloc_xmcb_fail:
		stc
		ret
%endif
endp

proc	near,	_free_xmcb
%ifndef	PAGED
		cmp	byte [platform],2
		je	free_xmcb_v
		cmp	byte [platform],1
		je	free_xmcb_x
		cmp	byte [platform],0
		je	free_xmcb_r
	free_xmcb_fail:
		stc
		ret
	free_xmcb_v:
%endif
		mov	ax,Flat_D_desc - GDT
		mov	es,ax
		call	vcpi_mfree
		clTLB	eax
%ifndef	PAGED
		jmp	free_xmcb_done
	free_xmcb_x:
		mov	dx,si
		mov	ah,0dh
		call	far [XMS_Driver]
		mov	ah,0ah
		call	far [XMS_Driver]
		jmp	free_xmcb_done
	free_xmcb_r:
		call	raw_mfree
	free_xmcb_done:
%endif
		xor	eax,eax
		ret
endp

proc	near,	_realloc_xmcb
%ifndef	PAGED
		cmp	byte [platform],2
		je	realloc_xmcb_v
		cmp	byte [platform],1
		je	realloc_xmcb_x
		cmp	byte [platform],0
		je	realloc_xmcb_r
	realloc_xmcb_fail:
		stc
		ret
	realloc_xmcb_v:
%endif
		mov	ax,Flat_D_desc - GDT
		mov	es,ax
		add	edx,4095
		shr	edx,12
		mov	ebx,edx
		call	vcpi_realloc
		jc	realloc_xmcb_fail
		clTLB	eax
		mov	edx,esi
		sub	edx,dword [pt_base]
		shl	edx,10
		mov	eax,esi
%ifndef	PAGED
		jmp	realloc_xmcb_done
	realloc_xmcb_x:
		add	edx,1023
		shr	edx,10
		xchg	esi,edx
		mov	ah,0dh
		call	far [XMS_Driver]
		mov	ebx,esi
		shr	esi,16
		mov	ah,0fh
		jz	.xmsok
		mov	ah,8fh
	.xmsok:
		call	far [XMS_Driver]
		test	ax,ax
		jz	realloc_xmcb_fail
		push	edx
		mov	ah,0ch
		call	far [XMS_Driver]
		shl	edx,16
		mov	dx,bx		;Base address in EDX
		pop	eax		;Handle in EAX
		jmp	realloc_xmcb_done
	realloc_xmcb_r:
		add	edx,1023
		and	dx,~1023	;Make it kb granular
		mov	ebx,edx
		call	raw_realloc
		jc	realloc_xmcb_fail
		mov	edx,dword [si+x_addr]
		mov	eax,esi
	realloc_xmcb_done:
%endif
		clc
		ret
%ifdef	PAGED
	realloc_xmcb_fail:
		stc
		ret
%endif
endp

proc	near,	_test_x
		push	ds
		pop	es
		mov	di,X_H
		mov	cx,MaxXHndl
		cld
		repne	scasd
		ret
endp

proc	near,	_inject_x
		sub	di,4
		stosd
		ret
endp

proc	near,	vcpi_malloc
		call	vcpi_lmalloc
		cmp	edx,1
		jc	.vm_fail
		cmp	edx,ebx
		jb	.vm_1
		call	vcpi_pmalloc
		jmp	.vm_done
	.vm_1:
		mov	ebx,edx
		call	vcpi_pmalloc
		jc	.vm_fail
		call	vcpi_mfree
		mov	eax,ebx
		stc
	.vm_fail:
	.vm_done:
		ret
endp

proc	near,	vcpi_lmalloc
		mov	edi,dword [pt_free]
		mov	ecx,dword [pt_top]
		sub	ecx,edi
		shr	ecx,2
		xor	eax,eax
		cdq
		push	ebx
	.vlm_1:
		jecxz	.vlm_done
		repne	a32 scasd
		jne	.vlm_done
		mov	ebp,ecx
		lea	ebx,[edi-4]
		repe	a32 scasd
		jne	.vlm_2
		inc	ebp
	.vlm_2:
		sub	ebp,ecx
		cmp	ebp,edx
		jb	.vlm_1
		mov	esi,ebx
		mov	edx,ebp
		cmp	ebp,dword [esp]
		jb	.vlm_1
	.vlm_done:
		pop	ebx
		ret
endp

proc	near,	vcpi_pmalloc
		mov	edi,esi
		xor	ebp,ebp
	.vpm_1:
		mov	ax,0de04h
		call	dword far [VCPI_Entry]
		or	ah,ah
		jz	.vpm_2
		cmp	ebp,1
		jc	.vpm_done
		or	byte [es:edi-3],PT_E
		call	vcpi_mfree
		stc
		jmp	.vpm_done
	.vpm_2:
		and	dh,0f0h
		mov	dl,PT_U | PT_W | PT_P
		mov	dword [es:edi],edx
		add	edi,4
		inc	ebp
		cmp	ebp,ebx
		jb	.vpm_1
		or	byte [es:edi-3],PT_E
		clc
	.vpm_done:
		mov	eax,ebp
		ret
endp

proc	near,	vcpi_mfree
		mov	edi,esi
	.vmf_1:
		xor	ecx,ecx
		xchg	ecx,dword [es:edi]
		add	edi,4
		mov	edx,ecx
		and	dx,0f000h
		mov	ax,0de05h
		call	dword far [VCPI_Entry]
		test	ch,PT_E
		jz	.vmf_1
		ret
endp

proc	near,	vcpi_realloc
	;In:	ES	= flat data
	;	EBX	= new size in pages
	;	ESI	= old handle
		mov	edi,esi				; EDI = ESI, ptr to linear block start
		xor	ebp,ebp				; EBP = running block size in pages
	.vra_getsize:
		add	edi,4				; increment page table ptr
		inc	ebp				; increment block size
		test	byte [es:edi-3],PT_E		; last page of block?
		jz	.vra_getsize			; if no, loop
		sub	ebx,ebp				; EBX = change in block size
		jz	.vra_done			; if no change, done
		jc	.vra_shrink		; if block made smaller, just free top
		mov	ecx,dword [pt_top]		; ECX = count of pages to search
		sub	ecx,edi
		shr	ecx,2
		mov	edx,ecx				; EDX = current count
		xor	eax,eax				; EAX = search unit, free entry (0)
		jecxz	.vra_trybelow			; if no entries above, try below
		repe	a32 scasd			; check for free entries above block
		je	.vra_1				; if previous entry free, go on
		dec	edx				; previous entry not free, minus one
	.vra_1:
		sub	edx,ecx				; EDX = number of free pages in block
		cmp	edx,ebx				; enough linear memory?
		jb	.vra_trybelow			; if no, try below in linear memory
		push	esi				; preserve start of block
		lea	esi,[esi+ebp*4]			; ESI -> start of new block for alloc
		call	vcpi_pmalloc			; try to allocate physical memory
		mov	edi,esi				; EDI -> start of new block
		pop	esi				; restore start of old block
		jc	.vra_fail			; if alloc failed, error 8013h
		and	byte [es:edi-3],~PT_E		; clear last bit in old block end
		jmp	.vra_done			; go to done
	.vra_trybelow:
		mov	ecx,esi				; ECX = count of pages to search up
		sub	ecx,dword [pt_free]
		shr	ecx,2
		or	ecx,ecx				; any linear memory below?
		jz	.vra_alloc			; if no, try to allocate
		push	ebp				; preserve size of original block
		lea	edi,[esi-4]			; EDI = ESI, ptr to linear block start
		mov	ebp,ecx				; EBP = current count
		std					; search is up
		repe	a32 scasd			; check for free entries after block
		cld
		je	.vra_2				; if previous entry free, go on
		dec	ebp				; previous entry not free, minus one
	.vra_2:
		sub	ebp,ecx				; EBP = number of free pages in block
		lea	eax,[ebp+edx]			; free size below + free size above
		pop	ebp				; restore original block size
		cmp	eax,ebx				; enough linear memory?
		jb	.vra_alloc			; if no, try to allocate
		push	esi				; preserve original block address
		sub	ebx,edx				; EBX = number of pages needed below
		lea	eax,[ebx*4]			; get base of block below
		sub	esi,eax
		push	edx,ebp				; preserve some vars
		call	vcpi_pmalloc			; try to allocate physical memory
		pop	ebp,edx				; restore some vars
		mov	edi,esi				; EDI -> base of block below
		pop	esi				; restore base of original block
		jc	.vra_fail			; if alloc failed, error 8013h
		or	edx,edx				; any pages needed above?
		jz	.vra_3				; if no, go on
		push	esi,edi,ebp			; preserve some vars
		mov	ebx,edx				; EBX = size of block below
		lea	esi,[esi+ebp*4]			; ESI -> start of block above
		call	vcpi_pmalloc			; try to allocate physical memory
		pop	ebp,edi,esi			; restore some vars
		jnc	.vra_4				; if allocated ok, go on
		mov	esi,edi				; ESI -> allocated block below
		call	vcpi_mfree			; free allocated block below
		jmp	.vra_fail			; fail, error 8013h
	.vra_4:
		and	byte [es:esi-3],~PT_E		; clear last bit in below block end
	.vra_3:
		and	byte [es:esi+ebp*4-3],~PT_E	; clear last bit in old block end
		push	edi				; preserve new block start
		mov	edx,edi				; EDX = base of move area
		lea	ebx,[esi-4]			; EBX = current location in move area
	.vra_shiftpage:
		mov	edi,ebx				; set up to shift up a page
		mov	esi,ebx
		mov	ecx,ebp
		push	ds				; save data selector
		push	es
		pop	ds				; ds = core selector
		a32	lodsd				; shift old pages a page down in table
		rep	a32 movsd
		a32	stosd
		pop	ds				; ds now data selector again
		sub	ebx,4				; decrement to next page to shift
		cmp	ebx,edx				; more pages to shift?
		jae	.vra_shiftpage			; if yes, loop
		pop	esi				; restore new block start address
		jmp	.vra_done			; go to done
	.vra_alloc:
		add	ebx,ebp				; restore EBX as requested size
		push	esi,ebp				; preserve some vars
		call	vcpi_lmalloc			; check for linear memory block
		pop	ebp,edi				; restore some vars
		cmp	edx,ebx				; enough linear memory?
		jb	.vra_fail			; if no, error 8012h
		sub	ebx,ebp				; EBX = extra pages needed
		push	esi				; preserve for later copy
		lea	esi,[esi+ebp*4]			; ESI -> start of extra space needed
		push	edi,ebp				; preserve some vars
		call	vcpi_pmalloc			; try to allocate physical memory
		pop	ecx,esi,edi			; restore some vars
		jc	.vra_fail			; if not enough mem, error 8013h
		push	edi,esi,ecx			; preserve, new and old block, size
		push	ds
		push	es
		pop	ds
		rep	a32 movsd			; copy old block pages
		pop	ds
		and	byte [es:edi-3],~PT_E		; clear last bit in old block end
		pop	ecx,edi				; restore to clear old block
		xor	eax,eax				; new page table entry is free (0)
		rep	a32 stosd			; clear old page table block
		pop	esi				; restore new block address
		jmp	.vra_done			; go to done
	.vra_shrink:
		sub	edi,4				; decrement page table ptr
		xor	edx,edx				; new page table entry is free (0)
		xchg	edx,dword [es:edi]		; swap EDX with page table entry
		and	dx,0f000h			; mask off low 12 bits
		mov	ax,0de05h			; VCPI free a page
		call	dword far [VCPI_Entry]
		inc	ebx				; increment negative change counter
		jnz	.vra_shrink			; if more pages to free, loop
		or	byte [es:edi-3],PT_E		; set next page up as last of block
	.vra_done:
		clc
		ret
	.vra_fail:
		stc
		ret
endp

%ifndef	PAGED
proc	near,	raw_malloc
		xor	edx,edx
		xor	eax,eax
		xor	esi,esi
		mov	cx,MaxXHndl
		mov	di,X_HDescr
		push	ebx
		mov	ebx,-1
	.rma_run:
		cmp	byte [di+x_flag],1		;Test free
		jne	.rma_next
		mov	ebp,dword [di+x_size]
		add	edx,ebp			;EDX = running total of free
		cmp	ebp,eax
		jb	.rma_1
		mov	eax,ebp			;EAX = largest free block
	.rma_1:
		cmp	ebp,dword [esp]		;Current block fits?
		jb	.rma_next		;If no then pass
		cmp	ebx,ebp			;Is it smaller than previous?
		jb	.rma_next		;If no, pass
		mov	si,di			;Save pointer
		mov	ebx,ebp
	.rma_next:
		add	di,x_hndl_size
		loop	.rma_run
		pop	ebx
		or	si,si
		jz	.rma_fail
; EBX = requested size
; SI = matching block address
; EAX = largest available block
; EDX = running total of free bytes
		cmp	dword [si+x_size],ebx
		je	.rma_finish			;Finish if exact size
		mov	cx,MaxXHndl
		mov	di,X_HDescr
	.rma_unused:
		cmp	byte [di+x_flag],4		;Test unused
		je	.rma_alloc_block
		add	di,x_hndl_size
		loop	.rma_unused
	.rma_fail:
		stc
	.rma_done:
		ret
	.rma_alloc_block:
		mov	ecx,dword [si+x_addr]
		add	ecx,ebx
		mov	dword [di+x_addr],ecx
		mov	ecx,ebx
		xchg	dword [si+x_size],ebx
		sub	ebx,ecx
		mov	dword [di+x_size],ebx
		mov	byte [di+x_flag],1
	.rma_finish:
		mov	byte [si+x_flag],2
		clc
		jmp	.rma_done
endp

proc	near,	raw_mfree
	;High word of handle not used (=0000)
		mov	byte [si+x_flag],1		;Mark free
	;Merge free blocks
	.rmf_merge_begin:
		mov	ebx,dword [si+x_addr]
		mov	eax,ebx
		add	eax,dword [si+x_size]
		mov	cx,MaxXHndl
		mov	di,X_HDescr
	.rmf_merge_loop:
		cmp	byte [di+x_flag],1
		jne	.rmf_merge_next
		mov	edx,dword [di+x_addr]
		cmp	edx,eax
		je	.rmf_merge_1
		add	edx,dword [di+x_size]
		cmp	edx,ebx
		je	.rmf_merge_0
	.rmf_merge_next:
		add	di,x_hndl_size
		loop	.rmf_merge_loop
		ret
	.rmf_merge_0:
		xchg	si,di
	.rmf_merge_1:
		mov	edx,dword [si+x_size]
		add	edx,dword [di+x_size]
		mov	dword [si+x_size],edx
		mov	byte [di+x_flag],4
		jmp	.rmf_merge_begin
endp

proc	near,	raw_realloc
		push	si
	;Backup block descriptors
		mov	si,X_HDescr
		mov	di,X_HDescr_bak
		mov	cx,MaxXHndl*x_hndl_size
		rep	movsb
		pop	si
	;Save old address and size
		push	dword [si+x_addr]
		push	dword [si+x_size]
	;Free old block
		push	edx
		call	raw_mfree
		pop	ebx
	;Try allocate new block
		call	raw_malloc
	;Fetch old size and address
		pop	ecx,eax
	;If can't allocate, restore the handles and fail
		jc	.rra_fail
	;Copy the smallest of the two (in protected mode)
		cmp	ecx,dword [si+x_size]
		jbe	.rra_1
		mov	ecx,dword [si+x_size]
	.rra_1:
		mov	edi,dword [si+x_addr]
		xchg	esi,eax
		cmp	edi,esi
		je	.rra_done
		push	ds
		mov	dx,Flat_D_desc - GDT
		mov	ds,dx
		mov	es,dx
		jb	.rra_moveb
		std
		add	esi,ecx
		add	edi,ecx
		dec	esi
		dec	edi
	.rra_moveb:
		mov	dl,cl
		and	dl,3
		shr	ecx,2
		rep	a32 movsd
		mov	cl,dl
		rep	a32 movsb
		cld
		pop	ds
	.rra_done:
		mov	esi,eax
		clc
		ret
	.rra_fail:
	;Restore block descriptors
		mov	di,X_HDescr
		mov	si,X_HDescr_bak
		mov	cx,MaxXHndl*x_hndl_size
		rep	movsb
		stc
		ret
endp
%endif

proc	near,	save_regs
		push	ds
		pushf
		cli
		push	cs
		pop	ds
		pop	word [DR+dpmiFl]
		and	byte [DR+dpmiFl+1],3fh
		mov	dword [DR+dpmiEax],eax
		mov	dword [DR+dpmiEbx],ebx
		mov	dword [DR+dpmiEcx],ecx
		mov	dword [DR+dpmiEdx],edx
		mov	dword [DR+dpmiEsi],esi
		mov	dword [DR+dpmiEdi],edi
		mov	dword [DR+dpmiEbp],ebp
		mov	word [DR+dpmiEs],es
		mov	word [DR+dpmiFs],fs
		mov	word [DR+dpmiGs],gs
		pop	word [DR+dpmiDs]
		ret
endp

proc	near,	rest_regs
		mov	eax,dword [DR+dpmiEax]
		mov	ebx,dword [DR+dpmiEbx]
		mov	ecx,dword [DR+dpmiEcx]
		mov	edx,dword [DR+dpmiEdx]
		mov	esi,dword [DR+dpmiEsi]
		mov	edi,dword [DR+dpmiEdi]
		mov	ebp,dword [DR+dpmiEbp]
		mov	es,word [DR+dpmiEs]
		mov	fs,word [DR+dpmiFs]
		mov	gs,word [DR+dpmiGs]
		mov	ds,word [DR+dpmiDs]
		ret
endp

;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
;	Copy CX bytes from BX:ESI to DX:EDI
;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
proc	near,	memcopy
	loc	%$t_info,64
		pusha
	;Save TSS2 info
		mov	si,tss2+tssEip
		push	ss
		pop	es
		lea	di,[bp+%$t_info]
		mov	cx,32
		cld
		rep	movsw
		popa
		pusha
	;Setup TSS2 for copy
		mov	word [tss2+tssCs],Real_C_desc - GDT
		mov	dword [tss2+tssEip],_memcopy
		mov	word [tss2+tssEcx],cx
		mov	word [tss2+tssEcx+2],0
		mov	word [tss2+tssDs],bx
		mov	dword [tss2+tssEsi],esi
		mov	word [tss2+tssEs],dx
		mov	dword [tss2+tssEdi],edi
		mov	dword [tss2+tssEfl],3002h
	;Execute copy in protected mode
		call	main_switch
	;Restore TSS2
		push	ds
		push	ss
		pop	ds
		lea	si,[bp+%$t_info]
		push	cs
		pop	es
		mov	di,tss2+tssEip
		mov	cx,32
		cld
		rep	movsw
		pop	ds
		popa
		ret
_memcopy:
		rep	a32 movsb
		jmpt	TSS1_desc - GDT
endp

endp

;°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±
;	Real-mode Call-backs
;°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±
		%assign	x 0
		%rep	Num_IRQ2+Num_RMCB
RMCB %+ x:	call	RMCB_ID
		%assign	x x+1
		%endrep
RMCB_ID:
		call	save_regs
		pop	ax

		mov	word [DR+dpmiSp],sp
		mov	word [DR+dpmiSs],ss

		mov	dx,_LOCKED
		mov	ss,dx			;Don't rely on user stack
		mov	esp,dword [locked_top]

		sub	dword [locked_top],Locked_frame
		inc	byte [in_rmcb]
		call	common_rmcb
		dec	byte [in_rmcb]
		add	dword [locked_top],Locked_frame

		lss	sp,[DR+dpmiSp]
		push	word [DR+dpmiFl]
		push	word [DR+dpmiCs]
		push	word [DR+dpmiIp]
		call	rest_regs
		iret

proc	near,	common_rmcb
	loc	%$t_info,64

	;Save TSS2 info
		mov	si,tss2+tssEip
		push	ss
		pop	es
		lea	di,[bp+%$t_info]
		mov	cx,32
		cld
		rep	movsw

	;Setup RMCB call frame
		sub	ax,RMCB1
		mov	bl,RMCB1 - RMCB0
		div	bl
		imul	ax,rmcb_size
		xchg	ax,bx
		mov	word [tss2+tssEbp],bx

	;Setup TSS2 for the great Call-back
		mov	al,byte [rmcbs+bx+cbType]
		mov	byte [tss2+tssEax],al
		mov	dword [tss2+tssEfl],3002h
		mov	word [tss2+tssCs],Real_C_desc - GDT
		mov	dword [tss2+tssEip],rmcb_task
		mov	word [tss2+tssSs],Locked_desc - GDT
		mov	ecx,dword [locked_top]
		mov	dword [tss2+tssEsp],ecx
		mov	word [tss2+tssFs],Real_D_desc - GDT
		mov	dword [tss2+tssEbx],DR
		mov	word [tss2+tssDs],Flat_D_desc - GDT
		movzx	ecx,word [DR+dpmiSs]
		shl	ecx,4
		movzx	edx,word [DR+dpmiSp]
		add	ecx,edx
		mov	dword [tss2+tssEsi],ecx

		cmp	al,2
		je	not_internal_rmcb
		mov	ax,word [tss2+tssSs]
		mov	word [tss2+tssEs],ax		; We want regs stored on stack
		sub	dword [tss2+tssEsp],((DPMI_R_size+3) & ~3)
		mov	eax,dword [tss2+tssEsp]
		mov	dword [tss2+tssEdi],eax		; ES:EDI == SS:ESP
		jmp	enter_rmcb
	not_internal_rmcb:
		mov	eax,dword [rmcbs+bx+cbRegs+Ofs32b]
		mov	dword [tss2+tssEdi],eax
		mov	ax,word [rmcbs+bx+cbRegs+Sel16b]
		mov	word [tss2+tssEs],ax
enter_rmcb:
		sub	dword [locked_top],Locked_frame
		call	main_switch
		add	dword [locked_top],Locked_frame
	;Restore TSS2
		push	ds
		push	ss
		pop	ds
		lea	si,[bp+%$t_info]
		push	cs
		pop	es
		mov	di,tss2+tssEip
		mov	cx,32
		cld
		rep	movsw
		pop	ds
		ret
rmcb_task:
		push	esi,edi
		mov	esi,ebx			;Copy RM regs to PM
		mov	ecx,DPMI_R_size/2
		rep	fs a32 movsw
		pop	edi,esi
		push	ebx,fs			;Save for return restore
		push	esi,ds			;Real stack for us if internal int
		push	ax

		pushfd
		call	dword far [fs:rmcbs+bp+cbCode]

		pop	ax
		pop	ds,esi
		cmp	al,2
		je	not_internal
		a32	lodsd			;Get CS:IP from real stack in eax
		mov	dword [es:edi+dpmiIp],eax
		a32	lodsw				; Get flags
		mov	word [es:edi+dpmiFl],ax
		add	word [es:edi+dpmiSp],6		; Adjust RM SP
	not_internal:
		push	es
		pop	ds
		mov	esi,edi			; ds:esi now = es:edi
		pop	es,edi			; es:edi is now reg struct
		mov	ecx,DPMI_R_size/2	; Size of reg struct in words
		rep	a32 movsw
	;Invalidate int no for `main_switch' return
		push	es
		pop	ds
		mov	bx,-1
jmp_TSS1_raw:
		and	byte [TSS2_desc+access],0fdh
		mov	ax,TSS1_desc - GDT
		ltr	ax
		mov	ss,word [tss1+tssSs]
		mov	esp,dword [tss1+tssEsp]
		push	dword [tss1+tssCs]
		push	dword [tss1+tssEip]
		retfd
endp

;°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±
;	Interrupt handling
;°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±

;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
;	All interrupts
;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
		%assign	x 0
		%rep	256
Interrupt %+ x:	call	Common_int_handler
		%assign	x x+1
		%endrep
Common_int_handler:
		cli
		push	ds,cx,ax

		mov	cx,Real_D_desc - GDT
		mov	ds,cx
		mov	ch,ah
		mov	ax,word [esp+3*2]
		sub	ax,Interrupt1
		mov	cl,Interrupt1 - Interrupt0
		div	cl
		mov	word [esp+3*2],ax

		mov	ah,ch
		cmp	ax,4c21h
		sete	byte [terminate]
		cmp	al,31h
		pop	ax,cx
		je	Int31_handler

	PM2RM_Bypass:
		call	to_user
		pop	word [tss2+tssDs]
		mov	word [tss2+tssEs],es
		mov	word [tss2+tssFs],fs
		mov	word [tss2+tssGs],gs
		pop	bx
		pop	dword [tss2+tssEip]
		pop	word [tss2+tssCs]
		pop	ax
		pop	dword [tss2+tssEfl]
		mov	word [tss2+tssSs],ss
		mov	dword [tss2+tssEsp],esp
		jmp	jmp_TSS1_raw

;°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±
;	INT 31h
;°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±
Int31_handler:
		push	fs,gs,es,ss
		pushad
		cld
		push	(Flat_D_desc - GDT)
		pop	fs
		mov	ebp,esp
		and	byte [ebp+8*4+5*2+2+2*4],~1
		push	bx
		mov	bx,Int31_fns
	pDPMI_Search:
		cmp	ax,word [bx]
		je	pDPMI_Found
		inc	bx
		inc	bx
		cmp	bx,Int31_ofs
		jne	pDPMI_Search
		pop	bx
	pDPMI_Bypass:
		popad
		pop	ss,es,gs,fs
		push	(Real_D_desc - GDT)
		pop	ds
		jmp	PM2RM_Bypass
	pDPMI_Found:
		mov	bx,word [Int31_ofs+bx-Int31_fns]
		xchg	word [esp],bx
		RETn
	pDPMI_Fail:
		or	byte [esp+8*4+5*2+2+2*4],1
	pDPMI_Quit:
		popad
		pop	ss,es,gs,fs,ds
		add	esp,2
		iretd

DPMI_AllocLDTSel:
		jcxz	pDPMI_Fail
		call	_alloc_LDT_sel
		jc	pDPMI_Fail
		dec	cx
		mov	word [pointer_inc],cx
		mov	word [ebp+dpmiAx],di
		jmp	pDPMI_Quit
DPMI_GetPointerInc:
		xor	ax,ax
		cmp	word [pointer_inc],ax
		je	setpointerinc
		dec	word [pointer_inc]
		mov	al,8
	setpointerinc:
		mov	word [ebp+dpmiAx],ax
		jmp	pDPMI_Quit
DPMI_GetSegBase:
		call	_get_descriptor
		jc	pDPMI_Fail
		mov	ax,word [si+B1]
		mov	word [ebp+dpmiDx],ax
		mov	al,byte [si+B2]
		mov	ah,byte [si+B3]
		mov	word [ebp+dpmiCx],ax
		jmp	pDPMI_Quit
DPMI_SetSegBase:
		call	_verify_sel
		jc	pDPMI_Fail
		mov	word [LDT+bx+B1],dx
		mov	byte [LDT+bx+B2],cl
		mov	byte [LDT+bx+B3],ch
		jmp	pDPMI_Quit
DPMI_SetSegLim:
		call	_verify_sel
		jc	pDPMI_Fail
		cmp	cx,0fh
		jbe	SetLim
		mov	ax,dx
		and	ax,0fffh
		cmp	ax,0fffh
		jne	pDPMI_Fail
		shrd	dx,cx,12
		shr	cx,12
		or	cl,mask_G
	SetLim:
		mov	word [LDT+bx+L1],dx
		and	byte [LDT+bx+granted],~(mask_L2|mask_G)
		or	byte [LDT+bx+granted],cl
		jmp	pDPMI_Quit
DPMI_SetSegAcc:
		call	_verify_sel
		jc	pDPMI_Fail
		call	_test_access
		jc	pDPMI_Fail
		mov	byte [LDT+bx+access],cl
		and	byte [LDT+bx+granted],mask_L2
		or	byte [LDT+bx+granted],ch
		jmp	pDPMI_Quit
DPMI_CreateAlias:
		call	_get_descriptor
		jc	pDPMI_Fail
		mov	cx,1
		call	_alloc_LDT_sel
		jc	pDPMI_Fail
		mov	word [ebp+dpmiAx],di
		push	ds
		pop	es
		and	di,~7
		add	di,LDT
		movsd
		movsd
		mov	byte [di-8+access],92h
		jmp	pDPMI_Quit
DPMI_GetDescriptor:
		call	_get_descriptor
		jc	pDPMI_Fail
		movzx	esi,si
		a32	movsd
		a32	movsd
		jmp	pDPMI_Quit
DPMI_SetDescriptor:
		call	_verify_sel
		jc	pDPMI_Fail
		push	ds,es
		pop	ds,es
		mov	esi,edi
		lea	edi,[LDT+bx]
		mov	cl,byte [esi+access]
		mov	ch,byte [esi+granted]
		call	_test_access
		jc	pDPMI_Fail
		a32	movsd
		a32	movsd
		jmp	pDPMI_Quit
DPMI_GetRInterrupt:
		xor	bh,bh
		shl	bx,2
		mov	dx,word [fs:bx+Ofs16b]
		mov	cx,word [fs:bx+Seg16b]
		mov	word [ebp+dpmiDx],dx
		mov	word [ebp+dpmiCx],cx
		jmp	pDPMI_Quit
DPMI_SetRInterrupt:
		xor	bh,bh
		shl	bx,2
		mov	word [fs:bx+Ofs16b],dx
		mov	word [fs:bx+Seg16b],cx
		jmp	pDPMI_Quit
DPMI_GetXVector:
		cmp	bl,Num_Exc
		jnb	pDPMI_Fail
		xor	bh,bh
		imul	bx,Addr32b_size
		mov	edx,dword [Exc_Excs+bx+Ofs32b]
		mov	cx,word [Exc_Excs+bx+Sel16b]
		mov	dword [ebp+dpmiEdx],edx
		mov	word [ebp+dpmiCx],cx
		jmp	pDPMI_Quit
DPMI_SetXVector:
		cmp	bl,Num_Exc
		jnb	pDPMI_Fail
		xor	bh,bh
		imul	bx,Addr32b_size
		mov	dword [Exc_Excs+bx+Ofs32b],edx
		mov	word [Exc_Excs+bx+Sel16b],cx
		jmp	pDPMI_Quit
DPMI_GetPInterrupt:
		xor	bh,bh
%ifdef	SSIRQ
		push	bp
		call	_irqno
		mov	ax,bp
		pop	bp
		jc	Get_Interrupt
		cmp	ax,Num_IRQ
		jae	Get_Interrupt
		imul	bx,ax,Addr32b_size
		mov	edx,dword [Irq_Ints+bx+Ofs32b]
		mov	cx,word [Irq_Ints+bx+Sel16b]
		jmp	RetPInt
	Get_Interrupt:
%endif
		cmp	bl,Num_Exc
		jb	Get_Exc_Int
	Get_Reg_Int:
		shl	bx,3
		mov	dx,word [IDT+bx+gateO2]
		shl	edx,16
		mov	dx,word [IDT+bx+gateO1]
		mov	cx,word [IDT+bx+gateSel]
		jmp	RetPInt
	Get_Exc_Int:
		imul	bx,Addr32b_size
		mov	edx,dword [Exc_Ints+bx+Ofs32b]
		mov	cx,word [Exc_Ints+bx+Sel16b]
	RetPInt:
		mov	dword [ebp+dpmiEdx],edx
		mov	word [ebp+dpmiCx],cx
		jmp	pDPMI_Quit
DPMI_SetPInterrupt:
		xor	bh,bh
		call	_hook_hw
%ifdef	SSIRQ
		cmp	bp,Num_IRQ
		jb	pDPMI_Quit
%endif
		cmp	bl,Num_Exc
		jb	Set_Exc_Int
	Set_Reg_Int:
		shl	bx,3
		mov	word [IDT+bx+gateO1],dx
		shr	edx,16
		mov	word [IDT+bx+gateO2],dx
		mov	word [IDT+bx+gateSel],cx
		jmp	pDPMI_Quit
	Set_Exc_Int:
		imul	bx,Addr32b_size
		mov	dword [Exc_Ints+bx+Ofs32b],edx
		mov	word [Exc_Ints+bx+Sel16b],cx
		jmp	pDPMI_Quit
DPMI_pSimulate_RFrame:
	;Save an extra mode swap (memcopy) for `Simulate_RFrame'
		push	ds,es
		pop	ds,es
		mov	esi,edi
		lea	edi,[DR]
		mov	ecx,DPMI_R_size/2
		rep	a32 movsw
		jmp	pDPMI_Bypass
DPMI_AllocRMCB:
		mov	si,Num_IRQ2*rmcb_size
		mov	cx,Num_RMCB
	rmcbs_loop:
		cmp	byte [rmcbs+si+cbType],0
		jne	rmcb_next
		mov	byte [rmcbs+si+cbType],2
		mov	eax,dword [ebp+dpmiEsi]
		mov	dword [rmcbs+si+cbCode+Ofs32b],eax
		mov	ax,word [ebp+8*4+4*2]
		mov	word [rmcbs+si+cbCode+Sel16b],ax
		mov	eax,dword [ebp+dpmiEdi]
		mov	dword [rmcbs+si+cbRegs+Ofs32b],eax
		mov	ax,word [ebp+dpmiEs]
		mov	word [rmcbs+si+cbRegs+Sel16b],ax
		mov	word [ebp+dpmiCx],_REAL
		mov	bl,rmcb_size
		xchg	ax,si
		div	bl
		imul	ax,RMCB1 - RMCB0
		add	ax,RMCB0
		mov	word [ebp+dpmiDx],ax
		jmp	pDPMI_Quit
	rmcb_next:
		add	si,rmcb_size
		loop	rmcbs_loop
		jmp	pDPMI_Fail
DPMI_FreeRMCB:
		cmp	cx,_REAL
		jne	pDPMI_Fail
		mov	ax,dx
		sub	ax,RMCB0
		mov	bl,RMCB1 - RMCB0
		div	bl
		imul	ax,rmcb_size
		xchg	ax,si
		cmp	byte [rmcbs+si+cbType],2
		jne	pDPMI_Fail
		mov	byte [rmcbs+si+cbType],0
		jmp	pDPMI_Quit
DPMI_GetVersion:
		mov	word [ebp+dpmiAx],005ah
		movzx	ax,byte [platform]
		or	al,1
		mov	word [ebp+dpmiBx],ax
		mov	al,byte [cpu_id]
		mov	byte [ebp+dpmiCl],al
		mov	ah,byte [MasterPIC]
		mov	al,byte [SlavePIC]
		mov	word [ebp+dpmiDx],ax
		jmp	pDPMI_Quit
DPMI_pGetMemory:
		push	edi,es
		mov	eax,-1
		mov	ecx,12
		rep	a32 stosd
		mov	ebx,eax
%ifndef	PAGED
		cmp	byte [platform],2
		je	V_GetMemory
		cmp	byte [platform],0
		je	R_GetMemory
		pop	es,edi
		jmp	pDPMI_Bypass
	R_GetMemory:
		call	raw_malloc
		jmp	pGM_@@0
	V_GetMemory:
%endif
		push	fs
		pop	es
		call	vcpi_malloc
		shl	eax,12
	pGM_@@0:
		pop	es,edi
		a32	stosd
		shr	eax,12
		a32	stosd
		a32	stosd
		jmp	pDPMI_Quit
DPMI_pAllocMemory:
%ifndef	PAGED
		cmp	byte [platform],1
		je	pDPMI_Bypass
%endif
		push	bx,cx
		pop	edx
		call	_malloc
	pretblock:
		jc	pDPMI_Fail
		mov	word [esp+dpmiDi],ax
		shr	eax,16
		mov	word [esp+dpmiSi],ax
		mov	word [esp+dpmiCx],dx
		shr	edx,16
		mov	word [esp+dpmiBx],dx
		jmp	pDPMI_Quit
DPMI_pFreeMemory:
%ifndef	PAGED
		cmp	byte [platform],1
		je	pDPMI_Bypass
%endif
		push	si,di
		pop	esi
		call	_mfree
		jc	pDPMI_Fail
		jmp	pDPMI_Quit
DPMI_pResizeMemory:
%ifndef	PAGED
		cmp	byte [platform],1
		je	pDPMI_Bypass
%endif
		push	bx,cx
		pop	edx
		push	si,di
		pop	esi
		call	_realloc
		jmp	pretblock
DPMI_GetPageSize:
		mov	word [ebp+dpmiBx],0
		mov	word [ebp+dpmiCx],4096
		jmp	pDPMI_Quit
DPMI_PhysicalMap:
		push	fs
		pop	es
		push	bx,cx
		pop	eax
		cmp	eax,100000h
		jb	pDPMI_Fail
		push	si,di
		pop	ebx
		test	ebx,ebx
		jz	pDPMI_Fail
		and	ecx,0fffh
		add	ebx,ecx
		jc	pDPMI_Fail
		add	ebx,4095
		jc	pDPMI_Fail
%ifndef	PAGED
		cmp	byte [platform],2
		jne	pDPMI_Quit
%endif
		shr	ebx,12
%ifdef TDMAP
		mov	ebp,ecx
		xor	edx,edx
		mov	esi,dword [pt_top]
	.pm_scan:
		cmp	esi,dword [pt_free]
		je	.pm_check
		sub	esi,4
		cmp	dword [es:esi],edx
		jnz	.pm_scan
		mov	ecx,ebx
		jmp	.pm_next
	.pm_inner:
		cmp	esi,dword [pt_free]
		je	.pm_check
		sub	esi,4
		cmp	dword [es:esi],edx
		jnz	.pm_scan
	.pm_next:
		dec	ecx
		jnz	.pm_inner
		mov	edx,ebx
	.pm_check:
%else
		push	ecx,eax
		call	vcpi_lmalloc
		pop	eax,ebp
%endif
		cmp	edx,ebx
		jb	pDPMI_Fail
		mov	edi,esi
		and	ah,0f0h
		mov	al,PT_CD | PT_U | PT_W | PT_P
	.pmap:
		mov	dword [es:edi],eax
		add	eax,4096
		add	edi,4
		dec	ebx
		jne	.pmap
		or	byte [es:edi-3],PT_E
		mov	ecx,esi
		sub	ecx,dword [pt_base]
		shl	ecx,10
		add	ecx,ebp
		mov	word [esp+dpmiCx],cx
		shr	ecx,16
		mov	word [esp+dpmiBx],cx
		clTLB	eax
		jmp	pDPMI_Quit
DPMI_DisableInts:
		mov	al,byte [ebp+8*4+5*2+2+2*4+1]
		and	byte [ebp+8*4+5*2+2+2*4+1],~2
		jmp	ReturnInts
DPMI_EnableInts:
		mov	al,byte [ebp+8*4+5*2+2+2*4+1]
		or	byte [ebp+8*4+5*2+2+2*4+1],2
		jmp	ReturnInts
DPMI_GetInts:
		mov	al,byte [ebp+8*4+5*2+2+2*4+1]
	ReturnInts:
		test	al,2
		setnz	byte [ebp+dpmiAl]
		jmp	pDPMI_Quit

;°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±
;	INT 31h dispatcher data
;°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±
Int31_fns	dw	0000h,0003h,0006h,0007h,0008h,0009h,000ah,000bh,000ch
		dw	0200h,0201h,0202h,0203h,0204h,0205h
		dw	0300h,0301h,0302h,0303h,0304h
		dw	0400h
		dw	0500h,0501h,0502h,0503h
		dw	0600h,0601h,0604h
		dw	0800h
		dw	0900h,0901h,0902h

Int31_ofs	dw	DPMI_AllocLDTSel
		dw	DPMI_GetPointerInc
		dw	DPMI_GetSegBase
		dw	DPMI_SetSegBase
		dw	DPMI_SetSegLim
		dw	DPMI_SetSegAcc
		dw	DPMI_CreateAlias
		dw	DPMI_GetDescriptor
		dw	DPMI_SetDescriptor
		dw	DPMI_GetRInterrupt
		dw	DPMI_SetRInterrupt
		dw	DPMI_GetXVector
		dw	DPMI_SetXVector
		dw	DPMI_GetPInterrupt
		dw	DPMI_SetPInterrupt
		dw	DPMI_pSimulate_RFrame
		dw	DPMI_pSimulate_RFrame
		dw	DPMI_pSimulate_RFrame
		dw	DPMI_AllocRMCB
		dw	DPMI_FreeRMCB
		dw	DPMI_GetVersion
		dw	DPMI_pGetMemory
		dw	DPMI_pAllocMemory
		dw	DPMI_pFreeMemory
		dw	DPMI_pResizeMemory
		dw	pDPMI_Quit
		dw	pDPMI_Quit
		dw	DPMI_GetPageSize
		dw	DPMI_PhysicalMap
		dw	DPMI_DisableInts
		dw	DPMI_EnableInts
		dw	DPMI_GetInts

;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
;	PM DPMI aid functions
;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
proc	near,	_alloc_LDT_sel
		mov	ax,MaxAvSel
		sub	ax,cx
		jc	alloc_fail
		xor	di,di
	test_space:
		mov	bx,di
		mov	dx,cx
	test_frees:
		test	byte [LDT+bx+access],mask_P
		jnz	advance
		add	bx,descr_size
		dec	dx
		jnz	test_frees
		mov	bx,di
		mov	dx,cx
	alloc_sel:
		mov	dword [LDT+bx],0
		mov	dword [LDT+bx+4],9200h
		add	bx,descr_size
		dec	dx
		jnz	alloc_sel
		or	di,4
		clc
		ret
	advance:
		add	di,descr_size
		dec	ax
		jnl	test_space
	alloc_fail:
		stc
		ret
endp

proc	near,	_free_LDT_sel
		push	bx
		call	_verify_sel
		jc	free_fail
		mov	dword [LDT+bx],0
		and	dword [LDT+bx+4],7f00h
	free_fail:
		pop	bx
		ret
endp

proc	near,	_verify_sel
		mov	al,bl
		and	al,7
		cmp	al,4
		jne	verify_fail
		and	bl,~7
		mov	ax,bx
		shr	ax,3
		cmp	ax,MaxAvSel
		jae	verify_fail
		test	byte [LDT+bx+access],mask_S
		jz	verify_fail
		ret
	verify_fail:
		stc
		ret
endp

proc	near,	_get_descriptor
		test	bl,100b
		jz	sel_in_GDT
	sel_in_LDT:
		call	_verify_sel
		jc	rights_fail
		lea	si,[LDT+bx]
		ret
	sel_in_GDT:
		test	bl,11b
		jnz	rights_fail
		cmp	bx,Real_C_desc - GDT
		jb	rights_fail
		cmp	bx,Flat_C_desc - GDT
		ja	rights_fail
		lea	si,[GDT+bx]
		clc
		ret
	rights_fail:
		stc
		ret
endp

proc	near,	_test_access
		test	cx,(mask_NUL << 8) | mask_DPL
		jnz	access_bad
		test	cl,mask_P | mask_S
		jz	access_bad
		jpo	access_bad
		test	cl,8
		jz	access_ok
		test	cl,4
		jnz	access_bad
		test	cl,2
		jz	access_bad
	access_ok:
		and	ch,~mask_L2
		ret
	access_bad:
		stc
		ret
endp

proc	near,	_hook_hw
		push	bx,cx,edx
	;Test if IRQ
		call	_irqno
		jc	hook_hw_done
%ifdef	SSIRQ
		cmp	bp,Num_IRQ
		jae	hook_hw_init
		imul	si,bp,Addr32b_size
		mov	dword [Irq_Ints+si+Ofs32b],edx
		mov	word [Irq_Ints+si+Sel16b],cx
	hook_hw_init:
%endif
		mov	di,bx
		shl	di,2
	;Get IRQ RMCB structure address
		imul	ax,bp,rmcb_size
		mov	si,cs
		xchg	ax,si
	;!@# Hack alert {
	;	Call-back for int23 is never released
	;	The restoring is done by reset_hwints
		cmp	bl,23h
		je	hook_hw
	;!@# Hack alert }
		cmp	cx,ax
		je	unhook_hw
	hook_hw:
	;If already hooked, setup Call-back without hooking Call-back
		mov	dword [rmcbs+si+cbCode+Ofs32b],edx
		mov	word [rmcbs+si+cbCode+Sel16b],cx
	;Test if Call-back available, hook Call-back
		cmp	byte [rmcbs+si+cbType],0
		jne	hook_hw_done
	;Setup Call-back structure
		mov	byte [rmcbs+si+cbType],1
	;Save old real-int vector
		mov	eax,dword [fs:di]
		mov	si,bp
		shl	si,2
		mov	dword [hwint_rmcb+si],eax
	;Hook real-int
		imul	ax,bp,RMCB1 - RMCB0
		add	ax,RMCB0
		mov	word [fs:di+Ofs16b],ax
		mov	word [fs:di+Seg16b],_REAL
	hook_hw_done:
		pop	edx,cx,bx
		ret
	unhook_hw:
	;If not call-backed, don't reset real-int
		cmp	byte [rmcbs+si+cbType],0
		je	hook_hw_done
	;Reset real IVT
		mov	byte [rmcbs+si+cbType],0
		mov	si,bp
		shl	si,2
		xor	eax,eax
		xchg	eax,dword [hwint_rmcb+si]
		mov	dword [fs:di],eax
		jmp	hook_hw_done
endp

proc	near,	_irqno
		mov	ax,bx
		mov	bp,ax		;IRQ number
		and	ax,0f8h

		cmp	ax,word [MasterPIC]
		jne	test_slavepic
		sub	bp,ax
		clc
		ret
	test_slavepic:
		cmp	ax,word [SlavePIC]
		jne	test_fake_irq
		sub	bp,ax
		add	bp,Num_IRQ/2
		clc
		ret
	test_fake_irq:
		cmp	bx,1ch
		jne	test_fake_irq2
		mov	bp,Num_IRQ
		ret
	test_fake_irq2:
		cmp	bx,23h
		jne	no_hardirq
		mov	bp,Num_IRQ+1
		ret
	no_hardirq:
		stc
		ret
endp

;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
;	Exception ints
;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
		%assign	x 0
		%rep	Num_Exc
xInterrupt %+ x:call	xInterrupt_ID
		%assign	x x+1
		%endrep
xInterrupt_ID:
		cli
		pushad
		pushf
		push	ds
		mov	ax,Real_D_desc - GDT
		mov	ds,ax

		mov	ax,word [esp+2*2+8*4]
		sub	ax,xInterrupt1
		mov	cl,xInterrupt1 - xInterrupt0
		div	cl
		mov	bx,ax

		cmp	bl,02h				;NMI passed down
		je	Go_interrupt
%ifndef	EXC0f
		cmp	bl,0fh
		je	Go_interrupt
%endif

		mov	ch,al
		mov	cl,al
		and	cx,0f807h
		mov	dx,020h
		cmp	ch,byte [MasterPIC]
		je	Check_hw
		mov	dx,0a0h
		cmp	ch,byte [SlavePIC]
		jne	No_hw_int
	Check_hw:
		mov	al,0bh
		out	dx,al
		jmps	$+2
		in	al,dx
		inc	cl
		shr	al,cl
		jc	Go_interrupt

	No_hw_int:
		mov	edi,dword [esp+2*2+8*4+2]	;Load EIP/error code
		xor	bp,bp

		cmp	ch,8			;0..7,16 have no error
		jne	Check_sw_int
		cmp	bx,9			;9 has no error
		je	Check_sw_int
		cmp	bx,0fh			;15 has no error
		je	Check_sw_int

	;3rd dword on user stack resembles a valid code selector?
		mov	dx,bx
		mov	bx,word [esp+2*2+8*4+2+8]
		call	_get_descriptor
		mov	bx,dx
		jc	Go_interrupt
		test	byte [si+access],8
		jz	Go_interrupt

		dec	bp
	Go_exception:
		cmp	bl,07h
		jne	Do_exception
		smsw	ax
		test	al,1000b
		jz	Do_exception
		clts
		o32	POP ds		;also discard FLAGS
		popad
		add	esp,2
Do_userback:
		iretd

	Check_sw_int:
		cmp	edi,2
		jb	Go_exception
	;!@# Hack alert {
	;	RF set only for faults, while INTnn is a trap
		test	byte [esp+2*2+8*4+2+10],1
		jnz	Go_exception
	;!@# Hack alert }
		mov	ah,bl
		mov	al,0cdh
		mov	dx,ds
		mov	ds,word [esp+2*2+8*4+2+4]
		cmp	ax,word [edi-2]
		mov	ds,dx
		jne	Go_exception
	Go_interrupt:
		imul	bx,Addr32b_size
		mov	eax,dword [Exc_Ints+bx+Ofs32b]
		mov	dword [tmp_ivec+Ofs32b],eax
		mov	ax,word [Exc_Ints+bx+Sel16b]
		mov	word [tmp_ivec+Sel16b],ax

		pop	ds
		popf
		popad
		lea	esp,[esp+2]
		jmp	dword far [cs:tmp_ivec]

;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
;	Exceptions
;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
Do_exception:
		pop	word [tss2+tssDs]
		pop	ax
		pop	dword [tss2+tssEdi]
		pop	dword [tss2+tssEsi]
		pop	dword [tss2+tssEbp]
		pop	eax
		pop	dword [tss2+tssEbx]
		pop	dword [tss2+tssEdx]
		pop	dword [tss2+tssEcx]
		pop	dword [tss2+tssEax]
		pop	ax
		xor	edi,edi
		test	bp,bp
		jz	exc_noerr
		pop	edi
	exc_noerr:
		pop	dword [tss2+tssEip]
		pop	word [tss2+tssCs]
		pop	ax
		pop	dword [tss2+tssEfl]
		mov	dword [tss2+tssEsp],esp
		mov	word [tss2+tssSs],ss
		str	ax

	exception_processor:
	;Switch to a safe area in locked stack (and correct it for reentrancy)
		mov	cx,Locked_desc - GDT
		mov	ss,cx
		mov	esp,dword [locked_top]
		sub	dword [locked_top],Locked_frame

	;Build stack like DPMI says
	;!@# Hack alert {
	;	According to DPMI, this is a reserved
	;	field and should not be modified by user
	;	We store here the exception number, just in
	;	case we are terminating after nested exceptions
		push	bx
	;!@# Hack alert }
		push	word [tss2+tssSs]
		push	dword [tss2+tssEsp]
		push	dword [tss2+tssEfl]
		push	ax
		push	word [tss2+tssCs]
		push	dword [tss2+tssEip]
		push	edi
		o32	PUSH cs
		push	dword Exception_Back2User

	;Build exception jump frame (RETF-based)
		jz	Internal_exception
		cmp	ax,TSS2_desc - GDT
		je	Exception_Chain
	Internal_exception:
	;The locked stack is exhausted or internal exception
		o32	PUSH cs
		push	dword Exception_Terminate
		jmp	Exception_Go
	Exception_Chain:
		imul	bx,Addr32b_size
		push	dword [Exc_Excs+bx+Sel16b]
		push	dword [Exc_Excs+bx+Ofs32b]
		jmp	Exception_Go

Exception_Back2User:
		push	ds
		push	(Real_D_desc - GDT)
		pop	ds
		call	to_user
		mov	word [tss2+tssEs],es
		mov	word [tss2+tssFs],fs
		mov	word [tss2+tssGs],gs
		pop	word [tss2+tssDs]
		pop	eax
		pop	dword [tss2+tssEip]
		pop	word [tss2+tssCs]
		pop	ax
		pop	dword [tss2+tssEfl]
		pop	dword [tss2+tssEsp]
		pop	word [tss2+tssSs]
		pop	ax
		add	dword [locked_top],Locked_frame
	;Task switch: we can't touch the user stack
		and	byte [TSS2_desc+access],0fdh
		mov	ax,TSS8_desc - GDT
		ltr	ax
		mov	ss,word [tss8+tssSs]
		mov	esp,dword [tss8+tssEsp]
		mov	ebp,esp
		push	ds,ds,ds
		pop	es,fs,gs
		jmpt	TSS2_desc - GDT
Double_fault:
		and	byte [TSS8_desc+access],0fdh
		and	byte [TSS2_desc+access],0fdh
		mov	ax,TSS2_desc - GDT
		ltr	ax
		mov	es,word [tss2+tssEs]
		mov	fs,word [tss2+tssFs]
		mov	gs,word [tss2+tssGs]
		cmp	ebp,esp
		jne	tss8_exc08
		mov	ss,word [tss2+tssSs]
		mov	esp,dword [tss2+tssEsp]
		push	dword [tss2+tssEfl]
		push	dword [tss2+tssCs]
		push	dword [tss2+tssEip]
		push	dword [Exc_Ints+8*Addr32b_size+Sel16b]
		push	dword [Exc_Ints+8*Addr32b_size+Ofs16b]
	Exception_Go:
		mov	eax,dword [tss2+tssEfl]
		and	ah,3ch
		push	eax
		popfd
		call	from_user
		mov	ds,word [tss2+tssDs]
		retfd
	tss8_exc08:
		mov	bx,8
		pop	edi
		mov	ax,word [tss8+tssBack]
		jmp	exception_processor

Exception_Terminate:
		add	esp,8			;Discard return address
		push	ds
		push	(Real_D_desc - GDT)
		pop	ds
		mov	dword [ER+eEax],eax
		mov	dword [ER+eEbx],ebx
		mov	dword [ER+eEcx],ecx
		mov	dword [ER+eEdx],edx
		mov	dword [ER+eEsi],esi
		mov	dword [ER+eEdi],edi
		mov	dword [ER+eEbp],ebp
		mov	word [ER+eEs],es
		mov	word [ER+eFs],fs
		mov	word [ER+eGs],gs
		pop	word [ER+eDs]

		pop	word [ER+eErr]
		pop	ax
		pop	dword [ER+eEip]
		pop	word [ER+eCs]
		pop	ax
		pop	dword [ER+eEfl]
		pop	dword [ER+eEsp]
		pop	word [ER+eSs]
		pop	ax

		cmp	byte [in_rmcb],0
		je	exc_not_in_rmcb
		mov	word [error_message2],_IN_RMCB_
	exc_not_in_rmcb:

		mov	bx,ax
		shl	bx,1
		mov	dx,word [En+bx]
		mov	word [error_message],dx
		mov	edx,cr2
		mov	dword [ER+eCR2],edx

		add	al,0e0h
		mov	byte [terminate],-1
		jmpt	TSS1_desc - GDT

%ifdef	SSIRQ
;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
;	IRQ reflection (for stack switching)
;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
		%assign	x 0
		%rep	Num_IRQ
qInterrupt %+ x:call	qCommonHead
		%assign	x x+1
		%endrep
qCommonHead:
		cli
		push	ds,eax,bx,dx
	; get address of the real IRQ
		mov	bx,Real_D_desc - GDT
		mov	ds,bx
		mov	ax,word [esp+2*2+1*4+1*2]
		sub	ax,qInterrupt1
		mov	bl,qInterrupt1 - qInterrupt0
		div	bl
		imul	ax,Addr32b_size
		mov	bx,ax
		mov	eax,dword [Irq_Ints+bx+Ofs32b]
		mov	dword [tmp_ivec+Ofs32b],eax
		mov	ax,word [Irq_Ints+bx+Sel16b]
		mov	word [tmp_ivec+Sel16b],ax
	; save user stack
		mov	dx,ss
		mov	eax,esp
	; now switch to the new stack
		mov	bx,Locked_desc - GDT
		mov	ss,bx
		mov	esp,dword [locked_top]
		sub	dword [locked_top],Locked_frame
		mov	ds,dx
	; push everything to the new stack
		o32	PUSH ds
		add	eax,2*2+1*4+1*2+2+3*4
		push	eax
		push	dword [eax-3*4+2*4]
		push	dword [eax-3*4+1*4]
		push	dword [eax-3*4]
		push	eax
		push	dword [eax-3*4+2*4]
	; restore thrashed registers
		push	word [eax-3*4-2-1*2]
		push	dword [eax-3*4-2-1*2-1*4]
		push	word [eax-3*4-2-1*2-1*4-1*2]
		push	word [eax-3*4-2-1*2-1*4-2*2]
		pop	dx,bx,eax,ds
	; go to user handler
		o32	PUSH cs
		push	dword Exception_Back2User
		jmp	dword far [cs:tmp_ivec]

proc	near,	setup_irqs
		pusha
		mov	ax,qInterrupt0
		xor	di,di
	;setup first PIC
		mov	dx,word [MasterPIC]
		mov	bx,dx
		shl	bx,3
		mov	cx,Num_IRQ/2
	.loop1st:
		mov	bp,word [IDT+bx+gateO1]
		mov	word [IDT+bx+gateO1],ax
		mov	word [Irq_Ints+di+Ofs32b],bp
		mov	word [Irq_Ints+di+Sel16b],Real_C_desc - GDT
		cmp	dx,Num_Exc
		jae	.loop1go
		mov	si,dx
		imul	si,Addr32b_size
		mov	word [Exc_Ints+si+Ofs32b],ax
	.loop1go:
		inc	dx
		add	ax,qInterrupt1 - qInterrupt0
		add	bx,gate_size
		add	di,Addr32b_size
		loop	.loop1st
	;setup slave PIC
		mov	dx,word [SlavePIC]
		mov	bx,dx
		shl	bx,3
		mov	cx,Num_IRQ/2
	.loop2nd:
		mov	bp,word [IDT+bx+gateO1]
		mov	word [IDT+bx+gateO1],ax
		mov	word [Irq_Ints+di+Ofs32b],bp
		mov	word [Irq_Ints+di+Sel16b],Real_C_desc - GDT
		cmp	dx,Num_Exc
		jae	.loop2go
		mov	si,dx
		imul	si,Addr32b_size
		mov	word [Exc_Ints+si+Ofs32b],ax
	.loop2go:
		add	ax,qInterrupt1 - qInterrupt0
		add	bx,gate_size
		add	di,Addr32b_size
		loop	.loop2nd
		popa
		ret
endp
%endif

;°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±
;	Miscellaneous procedures
;°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±

;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
;	Get CPU type
;		Out:	DX	= (2=808x/8018x/286, 3=386, 4=486 etc.)
;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
proc	near,	detect_cpu
		mov	dx,2
		pushf
		pop	ax
		mov	cx,ax
		xor	ah,40h
		push	ax
		popf
		pushf
		pop	ax
		push	cx
		popf
		xor	ah,ch
		jz	.ret_cpu
		inc	dx
		mov	bx,sp
		and	sp,~3
		pushfd
		pop	eax
		mov	ecx,eax
		btc	eax,18
		push	eax
		popfd
		pushfd
		pop	eax
		push	ecx
		popfd
		mov	sp,bx
		xor	eax,ecx
		jz	.ret_cpu
		inc	dx
		mov	eax,ecx
		btc	eax,21
		push	eax
		popfd
		pushfd
		pop	eax
		push	ecx
		popfd
		xor	eax,ecx
		jz	.ret_cpu
		inc	dx
		xor	eax,eax
		cpuid
		test	eax,eax
		jz	.ret_cpu
		xor	eax,eax
		inc	ax
		cpuid
		mov	dl,ah
		and	dx,0fh
	.ret_cpu:
		ret
endp

;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
;	Print formatted text
;		In:	DS:SI	= format
;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
proc	near,	print_f
		pushad
		cld

		mov	di,si
	loop0:
		inc	di
		cmp	byte [di-1],0
		jne	loop0

	read_ch:
		lodsb
		or	al,al
		jz	end_string
		cmp	al,'%'
		je	chk_format
	write_ch:
		mov	ah,0eh
		int	10h
		jmp	read_ch
	end_string:
		popad
		ret

	chk_format:
		lodsb
		cmp	al,'%'
		je	write_ch
		cmp	al,'s'
		je	write_string
		cmp	al,'w'
		je	write_word
		cmp	al,'d'
		je	write_double
		dec	si
		dec	si
		lodsb
		jmp	write_ch

	write_string:
		push	si
		mov	si,word [di]
		call	print_f
		pop	si
		inc	di
		inc	di
		jmp	read_ch
	write_word:
		mov	bl,16
		mov	cx,2
		mov	bp,4
		movzx	eax,word [di]
		jmp	write_number
	write_double:
		mov	bl,16
		mov	cx,4
		mov	bp,8
		mov	eax,dword [di]
		jmp	write_number

write_number:
		movzx	ebx,bl
		add	di,cx
		xor	cx,cx

	divide_number:
		xor	edx,edx
		div	ebx
		cmp	dl,09h
		jbe	digit_ok
		add	dl,'A'-'9'-1
	digit_ok:
		add	dx,0e30h
		push	dx
		inc	cx
		test	eax,eax
		jnz	divide_number

		sub	bp,cx
		jle	display_number
		mov	ax,0e30h
	make_pad:
		int	10h
		dec	bp
		jnz	make_pad
	display_number:
		pop	ax
		int	10h
		loop	display_number

		jmp	read_ch
endp

;°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±
;	The loader
;°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±
proc	near,	load_exe
		pushad
		push	es
		mov	es,word [PSP_Segment]
	;Allocate a transferbuffer
		mov	bx,word [stubinfo_ds_segment]
		mov	ax,es
		sub	bx,ax

		mov	ax,word [stubinfo_minkeep]
		shr	ax,4
		add	bx,ax
		mov	ah,4ah
		int	21h
		jc	_need_mem
	;Scan environment for the stub's full name after environment
		push	ds
		mov	es,word [es:2ch]
		xor	di,di
		mov	cx,-1
		xor	al,al
	scan_environment:
		repne	scasb
		scasb
		jne	scan_environment
		scasw
		push	di
		repne	scasb
		mov	word [stubinfo_env_size],di
		mov	word [Env_desc+L1],di
%if	SYMLINK
		cmp	byte [stubinfo_basename+0],0
		je	no_symlink
		pop	si
		push	es
		pop	ds
		push	ss
		pop	es
	back_scan:
		dec	di
		mov	al,byte [di]
		cmp	al,'/'
		je	copy_path
		cmp	al,'\'
		je	copy_path
		cmp	al,':'
		jne	back_scan
	copy_path:
		mov	cx,di
		sub	cx,si
		inc	cx
		xor	di,di
		push	di
		rep	movsb
		mov	cx,8
		mov	si,stubinfo_basename
	copy_basename:
		cs	lodsb
		test	al,al
		jz	end_basename
		stosb
		loop	copy_basename
	end_basename:
		mov	eax,'.EXE'
		stosd
		xor	al,al
		stosb
	no_symlink:
%endif
	;Open the file
		mov	cl,31h
		push	es
		pop	ds
		pop	dx
		mov	ax,3d00h
		int	21h
		pop	ds
%if	SYMLINK
		go	c,_need_img
%endif
		mov	word [exeFile],ax
		mov	bx,ax
	;Compute our length
		mov	ah,3fh
		mov	cx,6
		mov	dx,exeHeader
		int	21h
		jump	c,_need_img
		cmp	ax,cx
		jump	ne,_need_img
%if	SYMLINK
		xor	edx,edx
		cmp	word [exeHeader+0],'MZ'
		jne	.seekimage
		mov	dx,word [exeHeader+4]
%else
		movzx	edx,word [exeHeader+4]
%endif
		movzx	eax,word [exeHeader+2]
		neg	ax
		and	ax,511
		shl	edx,9
		sub	edx,eax
	.seekimage:
		mov	dword [inExe],edx
	;Seek for integrated image
		mov	ax,4200h
		push	edx
		pop	dx,cx
		int	21h
%if	TARGET==0
	;Now read d3x header
		mov	ah,3fh
		mov	dx,d3x1Header
		mov	cx,d3x1_hdr_size
		int	21h
		go	c,_need_img
		cmp	cx,ax
		go	ne,_need_img
	;Here test if D3X
		cmp	dword [d3x1Header+sign],'D3X1'
		go	ne,_need_img
	;Save what we need
		mov	eax,dword [d3x1Header+binsize]
		add	eax,dword [d3x1Header+addsize]
		add	eax,4095
		and	ax,~4095
%elif	TARGET==1
	;Now read coff header
		mov	ah,3fh
		mov	dx,coffHeader
		mov	cx,coffHeaderL
		int	21h
		go	c,_need_img
		cmp	cx,ax
		go	ne,_need_img
	;Here test if COFF
		cmp	word [coffHeader+00h],014ch
		go	ne,_need_img
	;Set some variables from coff header
		mov	eax,dword [bss_section+s_vaddr]
		add	eax,dword [bss_section+s_size]
		mov	ebx,00010001h
		cmp	eax,ebx
		jae	.f1
		mov	eax,ebx
	.f1:
		add	eax,0000ffffh
		xor	ax,ax
%elif	TARGET==2
	;Now read le header
		mov	ah,3fh
		mov	dx,leHeader
		mov	cx,le_hdr_size
		int	21h
		go	c,_need_img
		cmp	cx,ax
		go	ne,_need_img
	;Here test if LE
		cmp	dword [leHeader+LESignature],'LE'
		go	ne,_need_img
	;Compute needed memory (from object table)
		mov	ax,4200h
		mov	ecx,dword [leHeader+LEObjectTable]
		add	ecx,dword [inExe]
		mov	dx,cx
		shr	ecx,16
		int	21h
		mov	esi,dword [leHeader+LEObjectEntries]
		mov	cx,obj_tbl_size
		xor	edi,edi
		mov	dx,objEntry
	.object_loop:
		mov	ah,3fh
		int	21h
		go	c,_need_img
		cmp	cx,ax
		go	ne,_need_img
		mov	ebp,dword [objEntry+OTVirtualSize]
		add	ebp,4095
		and	bp,~4095
		add	edi,ebp
		dec	esi
		jnz	.object_loop
		xchg	eax,edi
%endif
		mov	dword [stubinfo_initial_size],eax
%ifdef	I21API
	;Set DTA
		mov	ah,1ah
		mov	dx,tmp_dta
		int	21h
%endif
		pop	es
		popad
		ret
endp

exec_bin:
		mov	word [es:2ch],Env_desc - GDT

		mov	ax,0205h
		mov	bl,23h
		mov	cx,cs
		lea	edx,[Do_userback]
		int	31h
exec_bin2:
		sti

	;Save PSP selector in stubinfo
		mov	word [stubinfo_psp_selector],es

%ifdef	extAPI
		mov	word [host_ds],ds
		mov	word [flat_ds],fs
%endif
%ifdef	I21API
		mov	ax,0204h
		mov	bl,21h
		int	31h
		mov	dword [old21+Ofs32b],edx
		mov	word [old21+Sel16b],cx
		mov	ax,0205h
		mov	cx,cs
		lea	edx,[new21]
		int	31h
	;DTA -> PSP:80
		mov	byte [user_dta+Ofs32b],80h
		mov	word [user_dta+Sel16b],es
%endif
%ifdef	I33API
	;not my problem if mouse not installed
		mov	ax,0204h
		mov	bl,33h
		int	31h
		mov	dword [old33+Ofs32b],edx
		mov	word [old33+Sel16b],cx
		mov	ax,0205h
		mov	cx,cs
		lea	edx,[new33]
		int	31h
	;allocate call-back
		push	ds
		mov	ax,0303h
		push	ds
		pop	es
		lea	edi,[mouse_dr]
		push	cs
		pop	ds
		lea	esi,[_mouse]
		int	31h
		pop	ds
		go	c,_need_dpmi
		mov	word [mouse_cb+Ofs16b],dx
		mov	word [mouse_cb+Seg16b],cx
%endif

	;Allocate DPMI memory for program
		mov	ax,0501h
		mov	bx,word [stubinfo_initial_size+2]
		mov	cx,word [stubinfo_initial_size]
		int	31h
		jump	c,_need_mem2

		mov	word [client_memory+2],bx
		mov	word [client_memory],cx
		mov	word [stubinfo_memory_handle+2],si
		mov	word [stubinfo_memory_handle],di

	;Allocate 4 selectors (2 for stubinfo + 2 for program)
		xor	ax,ax
%if	TARGET<2
		mov	cx,4
%else
		mov	cx,1
%endif
		int	31h
		jump	c,_need_dpmi
		mov	bx,ax
%if	TARGET<2
		mov	ebp,dword [client_memory]
		mov	esi,dword [stubinfo_initial_size]
		mov	di,0c09ah
		call	setup_selector
		mov	word [client_cs],bx
		mov	ax,0003h
		int	31h
		add	bx,ax
		mov	ebp,dword [client_memory]
		mov	esi,dword [stubinfo_initial_size]
		mov	di,0c092h
		call	setup_selector
		mov	word [client_ds],bx
		mov	ax,0003h
		int	31h
		add	bx,ax
		movzx	ebp,word [stubinfo_ds_segment]
		shl	ebp,4
		movzx	esi,word [stubinfo_minkeep]
		mov	di,009ah
		call	setup_selector
		mov	word [stubinfo_cs_selector],bx
		mov	ax,0003h
		int	31h
		add	bx,ax
%else
		mov	word [client_cs],gs
		mov	word [client_ds],fs
%endif
		movzx	ebp,word [stubinfo_ds_segment]
		shl	ebp,4
		movzx	esi,word [stubinfo_minkeep]
		mov	di,4092h
		call	setup_selector
		mov	word [stubinfo_ds_selector],bx

	;Load the program data
%if	TARGET==0
		mov	esi,dword [d3x1Header+hdrsize]
		add	esi,dword [inExe]
		xor	edi,edi
		mov	ecx,dword [d3x1Header+binsize]
		call	read_section

		mov	es,word [client_ds]
		mov	edi,dword [d3x1Header+binsize]
		mov	ecx,dword [d3x1Header+addsize]
		xor	al,al
		rep	a32 stosb
%elif	TARGET==1
		mov	esi,dword [text_section+s_scnptr]
		add	esi,dword [inExe]
		mov	edi,dword [text_section+s_vaddr]
		mov	ecx,dword [text_section+s_size]
		call	read_section

		mov	esi,dword [data_section+s_scnptr]
		add	esi,dword [inExe]
		mov	edi,dword [data_section+s_vaddr]
		mov	ecx,dword [data_section+s_size]
		call	read_section

		mov	es,word [client_ds]
		mov	edi,dword [bss_section+s_vaddr]
		mov	ecx,dword [bss_section+s_size]
		xor	eax,eax
		shr	ecx,2
		rep	a32 stosd
%elif	TARGET==2
	;read loader
	;!@# Hack alert {
	;	Originally, Watcom used to encompass the fixups
	;	in the loadersize, but OpenWatcom does not. Now, we need:
	;		LELoaderSize >= LEImpModTable - LEObjectTable + 1
	;	LEObjectTable is a small offset (0C4h), so we don't care!
	;	mov	ebp,dword [leHeader+LELoaderSize]
		mov	ebp,dword [leHeader+LEImpModTable]
	;!@# Hack alert }
		mov	ax,0501h
		push	ebp
		pop	cx,bx
		int	31h
		go	c,_need_mem2
		push	si,di
		push	bx,cx
		pop	eax
		mov	ecx,ebp
		mov	edi,eax
		mov	esi,dword [leHeader+LEObjectTable]
		sub	eax,esi
		add	dword [leHeader+LEObjectTable],eax
		add	dword [leHeader+LEObjectPMTable],eax
		add	dword [leHeader+LEFixupPageTab],eax
		add	dword [leHeader+LEFixupRecords],eax
		add	esi,dword [inExe]
		call	read_section

	;zero out the memory
		push	fs
		pop	es
		mov	edi,dword [client_memory]
		mov	ecx,dword [stubinfo_initial_size]
		shr	ecx,2
		xor	eax,eax
		rep	a32 stosd

	;start processing objects: hope the header is OK for ESI !@#
		mov	esi,dword [leHeader+LEDataPages]
		mov	edi,dword [client_memory]
		mov	ebp,dword [leHeader+LEObjectEntries]
		mov	ebx,dword [leHeader+LENumberPages]
		mov	edx,dword [leHeader+LEObjectTable]
	;esi = file pointer
	;edi = relocation base (where to load)
	;ebp = # of objects in file
	;ebx = # of pages in file
	;edx -> ObjectTable
	load_object:
		mov	ecx,dword [fs:edx+OTPageMapSize]
		xor	eax,eax
	;zero, because we store alias selector
		xchg	eax,dword [fs:edx+OTPageMapIndex]
		shl	eax,2
		add	eax,dword [leHeader+LEObjectPMTable]
	;use reserved field to store actual object load address
		mov	dword [fs:edx+OTReserved],edi
		push	edi			; store load address
		jecxz	next_object
	readpage:
		push	ecx
		mov	ecx,dword [leHeader+LEPageSize]
		cmp	ebx,1
		ja	notlastpage
		mov	ecx,dword [leHeader+LEBytesLastPage]
	notlastpage:
	;check against Object Page Table (load or relocation only)
		cmp	byte [fs:eax+OPMTType-4],0
		jnz	afterread
		pushad
		mov	bx,word [exeFile]
		call	read_section
		popad
	afterread:
		add	esi,ecx
		add	edi,ecx
		add	eax,opm_tbl_size
		pop	ecx
		dec	ebx
		loop	readpage
	next_object:
		pop	edi
		mov	eax,dword [fs:edx+OTVirtualSize]	; get vsize of current section
		add	eax,4095				; align to pages
		and	ax,~4095
		add	edi,eax					; Set to start of next object
		add	edx,obj_tbl_size
		dec	ebp
		jnz	load_object
%endif

	;Close the file
		mov	ah,3eh
		mov	bx,word [exeFile]
		int	21h

%if	TARGET==0
	;Set the user stack
		mov	ss,word [client_ds]
		mov	esp,dword [d3x1Header+tos]
	;Push entry (IRETD based) address
		push	dword 3202h
		push	dword [client_cs]
		push	dword [d3x1Header+entry]

		mov	al,byte [platform]
		mov	byte [stubinfo_platform],al
%elif	TARGET==1
	;Use the transferbuffer as initial COFF stack: not 100% ok !@#
		mov	ss,word [stubinfo_ds_selector]
		movzx	esp,word [stubinfo_minkeep]
	;Push entry (IRETD based) address
		push	dword 3202h
		push	dword [client_cs]
		push	dword [aoutHeader+10h]

		mov	fs,word [stubinfo_ds_selector]
%elif	TARGET==2
	;raw data loaded, now do fixups
		mov	eax,dword [leHeader+LEObjectTable]
		mov	edi,dword [leHeader+LEFixupPageTab]
		mov	ecx,dword [leHeader+LEFixupRecords]

		push	ds,fs
		pop	ds,es
	;Quick and dirty fix for UPX
		mov	esi,eax
	FixupObject:
		cmp	dword [eax+OTPageMapSize],0
		je	ZeroPagesObject
		mov	edx,dword [eax+OTReserved]
	FixupPage:
		push	eax
		mov	ebx,dword [edi]
		cmp	ebx,dword [edi+4]
		jnc	FixupDone
	FixupRecord:
		mov	bp,word [ebx+ecx+FRType]
		and	bp,00efh
	; sort of an inner loop [ebx+ecx] = start of fixup record
		push	edi
		movzx	edi,byte [ebx+ecx+FRObject]
		cmp	byte [ebx+ecx+FRType+1],10h
		jnz	check16bit
		mov	eax,dword [ebx+ecx+FRItem]
		jmp	targetselected
	check16bit:
		cmp	byte [ebx+ecx+FRType+1],0
		jnz	_need_img
		movzx	eax,word [ebx+ecx+FRItem]
	targetselected:
		lea	edi,[edi*2+edi-3]
		lea	edi,[edi*8+esi]
		cmp	bp,3
		jnz	FuNoFarCall16
		call	get_obj_sel
		shl	eax,16
		mov	ax,word [ebx+ecx+5]
		movsx	edi,word [ebx+ecx+FROffset]		;can be negative!
		mov	dword [edi+edx],eax
		jmp	OnlyOfs
	FuNoFarCall16:
		cmp	bp,2
		jnz	FuNoSel16
		call	get_obj_sel
		movsx	edi,word [ebx+ecx+FROffset]		;can be negative!
		mov	word [edi+edx],ax
		movzx	edi,byte [ebx+ecx+FRType+1]
		shr	edi,3
		add	ebx,edi
		pop	edi
		add	ebx,5
		cmp	ebx,dword [edi+4]
		jc	FixupRecord
		jmp	FixupDone
	FuNoSel16:
		test	byte [ebx+ecx+FRType],10h
		jnz	DontAddOff32
		add	eax,dword [edi+OTReserved]
	DontAddOff32:
		push	edi
		movsx	edi,word [ebx+ecx+FROffset]		;can be negative!
		cmp	bp,8
		jnz	NoRel16
		sub	eax,edx
		sub	eax,edi
		sub	eax,4
	NoRel16:
		mov	word [edi+edx],ax
		cmp	bp,5
		jz	Fu16_16
		mov	dword [edi+edx],eax
	Fu16_16:
		pop	edi
		cmp	bp,7
		jz	OnlyOfs
		cmp	bp,5
		jz	OnlyOfs
		cmp	bp,8
		jz	OnlyOfs
		cmp	bp,6
		jnz	_need_img
		push	edi
		call	get_obj_sel
		movsx	edi,word [ebx+ecx+FROffset]		;can be negative!
		mov	word [edi+edx+4],ax
		pop	edi
	OnlyOfs:
		movzx	edi,byte [ebx+ecx+FRType+1]
		shr	edi,3
		add	ebx,edi
		pop	edi
		add	ebx,7
		cmp	ebx,dword [edi+4]
		jc	FixupRecord
	FixupDone:
		pop	eax
		add	edi,4
		add	edx,dword [es:leHeader+LEPageSize]
		dec	dword [eax+OTPageMapSize]
		jnz	FixupPage
	ZeroPagesObject:
		add	eax,obj_tbl_size
		dec	dword [es:leHeader+LEObjectEntries]
		jnz	FixupObject

		mov	edi,dword [es:leHeader+LEEntrySection]
		lea	edi,[edi*2+edi-3]
		lea	edi,[edi*8+esi]
		call	get_obj_sel
		push	es
		pop	ds
		mov	cx,ax
		mov	ebx,dword [leHeader+LEEntryOffset]
		cmp	ax,word [client_cs]
		jne	entry16
		add	ebx,dword [fs:edi+OTReserved]
	entry16:

		mov	edi,dword [leHeader+LEStackSection]
		lea	edi,[edi*2+edi-3]
		lea	edi,[edi*8+esi]
		mov	eax,dword [leHeader+LEInitialESP]
		add	eax,dword [fs:edi+OTReserved]

		pop	di,si

	;Set the user stack
		mov	ss,word [client_ds]
		mov	esp,eax
	;Push entry (IRETD based) address
		push	dword 3202h
		push	ecx
		push	ebx

		mov	es,word [stubinfo_psp_selector]
%endif

%if	TARGET<2
	;Copy stubinfo to transferbuffer
		mov	es,word [stubinfo_ds_selector]
		xor	di,di
		mov	si,stubinfo
		mov	cx,word [stubinfo_size]
		rep	movsb
%else
	;free resident loader
		mov	ax,0502h
		int	31h
%endif

		mov	ds,word [client_ds]
		iretd

%if	TARGET==2
proc	near,	get_obj_sel
		test	byte [edi+OTObjectFlags+1],10h
		jnz	.alias
		mov	ax,fs
		test	byte [edi+OTObjectFlags],4		;executable?
		jz	.done
		mov	ax,gs
	.done:
		ret
	.alias:
		mov	ax,word [edi+OTPageMapIndex]
		test	ax,ax
		jnz	.done
		pushad
		xor	ax,ax
		mov	cx,1
		int	31h
		jc	_need_dpmi
		mov	bx,ax
		mov	word [edi+OTPageMapIndex],ax
		mov	ebp,dword [edi+OTReserved]
		mov	esi,dword [edi+OTVirtualSize]
		test	byte [edi+OTObjectFlags],4		;executable?
		mov	di,8092h
		jz	.makesel
		mov	di,809ah
	.makesel:
		call	setup_selector
		popad
		jmp	.alias
endp
%endif

proc	near,	read_section
%if	TARGET==1
		mov	eax,esi
		and	eax,511
		add	ecx,eax
		and	si,~511
		and	di,~511
%endif
		mov	ebp,ecx

		mov	word [tmp_dr+dpmiFl],3202h
		mov	bx,word [exeFile]
		mov	word [tmp_dr+dpmiBx],bx
		mov	word [tmp_dr+dpmiDx],0
		mov	ax,word [stubinfo_ds_segment]
		mov	word [tmp_dr+dpmiDs],ax

		mov	ax,4200h
		push	esi
		pop	dx,cx
		int	21h

	read_loop:
		mov	byte [tmp_dr+dpmiAh],3fh
		movzx	eax,word [stubinfo_minkeep]
		cmp	eax,ebp
		jbe	read_bytes
		mov	eax,ebp
	read_bytes:
		mov	word [tmp_dr+dpmiCx],ax
		push	edi
		mov	ax,0300h
		mov	bx,21h
		xor	cx,cx
		push	ds
		pop	es
		lea	edi,[tmp_dr]
		int	31h
		pop	edi
		jc	_need_dpmi
		test	byte [tmp_dr+dpmiFl],1
		jump	nz,_need_img
		movzx	ecx,word [tmp_dr+dpmiAx]
		cmp	cx,word [tmp_dr+dpmiCx]
		go	nz,_need_img

		sub	ebp,ecx

		movzx	esi,word [stubinfo_ds_segment]
		shl	esi,4
		mov	es,word [client_ds]
%if	TARGET==0
		rep	fs a32 movsb
%elif	TARGET==1
		shr	cx,2
		rep	fs a32 movsd
%elif	TARGET==2
		mov	al,cl
		shr	cx,2
		and	al,3
		rep	fs a32 movsd
		mov	cl,al
		rep	fs a32 movsb
%endif

		test	ebp,ebp
		jnz	read_loop

		ret
endp

proc	near,	setup_selector
		mov	ax,0007h
		push	ebp
		pop	dx,cx
		int	31h
		mov	ax,0009h
		mov	cx,cs
		shl	cx,5
		and	cx,mask_DPL
		or	cx,di
		int	31h
		mov	ax,0008h
		dec	esi
		push	esi
		pop	dx,cx
		int	31h
		ret
endp

%ifdef	extAPI
%include	"extapi.inc"
%endif

;°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±
;	Extender data area
;°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±

;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
;	Messages
;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
;CopyRight	db	"D3X/",stubtype," DOS extender v0.90.h (alpha)",13,10
;		db	"Copyright (c) 1998-2004 Daniel Borca",13,10
;CopyRightL	equ	$-CopyRight

_error		db	'D3X: error 0xf',0
_386		db	'f: need 386+ CPU',0
_dos		db	'e: need DOS 3.0+',0
_mem		db	'd: no DOS memory',0
_dpmi		db	'c: DPMI host error',0
_vcpi		db	'b: VCPI server error',0
_interf		db	'a: V86 but no DPMI/VCPI',0
_a20		db	'9: A20 gate failure',0
_img		db	'8: unable to spawn image',0
_mem2		db	'7: no DPMI memory',0

E0		db	'Divide error',0
E1		db	'Debug',0
E2		db	'NMI',0
E3		db	'Breakpoint',0
E4		db	'Overflow',0
E5		db	'Bounds check',0
E6		db	'Invalid opcode',0
E7		db	'FPU unavailable',0
E8		db	'Double fault',0
E9		db	'FPU overrun',0
E10		db	'Invalid TSS',0
E11		db	'Segment not present',0
E12		db	'Stack fault',0
E13		db	'General protection fault',0
E14		db	'Page fault',0
E15		db	'#15',0
E16		db	'FPU error',0

En:
		%assign	x 0
		%rep	Num_Exc
		dw	E %+ x
		%assign	x x+1
		%endrep

;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
;	Descriptor tables
;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
		align	4
GDT:
NULL_desc	istruc	descr
		iend
VCPI1_desc	istruc	descr
		iend
VCPI2_desc	istruc	descr
		iend
VCPI3_desc	istruc	descr
		iend
TSS1_desc	istruc	descr
		at	L1,	dw TSS_size-1
		at	access,	db 89h
		iend
TSS2_desc	istruc	descr
		at	L1,	dw TSS_size-1
		at	access,	db 89h
		iend
TSS8_desc	istruc	descr
		at	L1,	dw TSS_size-1
		at	access,	db 89h
		iend
LDT_desc	istruc	descr
		at	L1,	dw LDT_Length-1
		at	access,	db 82h
		iend
BIOS_desc	istruc	descr
		at	L1,	dw 2ffh
		at	B1,	dw 400h
		at	access,	db 92h
		iend
Locked_desc	istruc	descr
		at	L1,	dw Stack_Lk_sz-1
		at	access,	db 92h
		iend
Real_D_desc	istruc	descr
		at	L1,	dw -1
		at	access,	db 92h
		iend
Real_C_desc	istruc	descr
		at	L1,	dw -1
		at	access,	db 9ah
		iend
PSP_desc	istruc	descr
		at	L1,	dw 0ffh
		at	access,	db 92h
		iend
Env_desc	istruc	descr
		at	access,	db 92h
		iend
Flat_D_desc	istruc	descr
		at	L1,	dw -1
		at	access,	db 92h
		at	granted,db 0cfh
		iend
Flat_C_desc	istruc	descr
		at	L1,	dw -1
		at	access,	db 9ah
		at	granted,db 0cfh
		iend
%ifdef	SWAT
		SWAT_GDT
%endif
GDT_Length	equ	$-GDT

_GDTR		dw	GDT_Length-1
		dd	0
_IDTR		dw	07ffh
		dd	0
_IDTR_		dw	03ffh
		dd	0

;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
;	VCPI
;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
VCPI		istruc	VCPI_SW
		at	vcpiLdt,dw LDT_desc - GDT
		at	vcpiT,	dw TSS1_desc - GDT
		at	vcpiEip,dd vcpi_protected
		at	vcpiCs,	dw Real_C_desc - GDT
		iend
VCPI_Entry	istruc	Addr32b
		at	Sel16b,	dw VCPI1_desc - GDT
		iend
VCPI_tHandle	dw	-1

;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
;	Interrupt engine
;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
MasterPIC	dw	DefMasterPIC
SlavePIC	dw	DefSlavePIC

locked_top	dd	Stack_Lk_sz

;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
;	Error exit
;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
error_exit	db	'%s%s at %w:%d error=%w eflags=%d',13,10
		db	'eax=%d ebx=%d ecx=%d edx=%d esi=%d edi=%d',13,10
		db	'ebp=%d ds=%w es=%w fs=%w gs=%w stack=%w:%d cr2=%d',13,10
error_exit_end	db	0
error_message	dw	0
error_message2	dw	error_exit_end
ER		resb	excregs_size

_IN_RMCB_	db	' in RMCB',0

;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
;	End of our data
;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
		align	16			;512 for DJGPP
lastbyte:
	;Last generated byte. All data after this point is uninitialized

;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
;	Tables (initialized by hand)
;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
IDT		resb	256*gate_size		;IDT is here aligned

LDT:
		resb	MaxAvSel*descr_size
LDT_Length	equ	$-LDT

tss1		resb	TSS_size		;mode transition (main_switch)
tss2		resb	TSS_size		;application
tss8		resb	TSS_size		;double_fault and int08h

Exc_Ints	resb	Num_Exc*Addr32b_size
Exc_Excs	resb	Num_Exc*Addr32b_size
%ifdef	SSIRQ
Irq_Ints	resb	Num_IRQ*Addr32b_size
%endif

;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
;	Misc
;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
A20		resb	1			;set if A20 needs to be restored

terminate	resb	1
real_stack	resb	Addr16b_size
tmp_stack	resb	Addr16b_size

tmp_ivec	resb	Addr32b_size

;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
;	Platforms
;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
PSP_Segment	resw	1
cpu_id		resb	1
platform	resb	1

DPMI_Entry	resb	Addr16b_size

_VCPI_		resd	1

pagedir		resd	1			;linear address of pagedirectory
pt_base		resd	1			;base of page table area
pt_top		resd	1			;top of page table area
pt_free		resd	1			;base of available page table area
pt_delta	resd	1			;# of KB for allocated pagetables

XMS_Driver	resb	Addr16b_size

raw_base	resd	1
raw_top		resd	1

;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
;	RMCB
;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
rmcbs		resb	(Num_IRQ2+Num_RMCB)*rmcb_size
hwint_rmcb	resb	Num_IRQ2*Addr16b_size

in_rmcb		resb	1

;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
;	Internal DPMI host data
;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
DR		resb	DPMI_R_size

pointer_inc	resw	1

X_H		resd	MaxXHndl
%ifndef	PAGED
X_HDescr	resb	MaxXHndl*x_hndl_size
X_HDescr_bak	resb	MaxXHndl*x_hndl_size
%else
xms_handle	resd	1
page_array	resd	1
max_pages	resd	1
plast		resd	1
%endif

;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
;	Loader data area
;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
inExe		resd	1
exeFile		resw	1

exeHeader	resw	3

tmp_dr		resb	DPMI_R_size
client_memory	resd	1
client_cs	resw	1
client_ds	resw	1

%if	TARGET==0
d3x1Header	resb	d3x1_hdr_size
%elif	TARGET==1
coffHeader	resb	20
aoutHeader	resb	28
text_section	resb	40
data_section	resb	40
bss_section	resb	40
coffHeaderL	equ	$-coffHeader
%elif	TARGET==2
leHeader	resb	le_hdr_size
objEntry	resb	obj_tbl_size
%endif

%ifdef	extAPI
host_ds		resw	1
flat_ds		resw	1
%endif
%ifdef	I21API
user_dta	resb	Addr32b_size
tmp_dta		resb	43
%endif
%ifdef	I33API
mouse_		resb	Addr32b_size
mouse_cb	resb	Addr16b_size
mouse_dr	resb	DPMI_R_size
%endif

lastbss:

;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
;	STACK
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
segment		_LOCKED	align=16 private class=STACK
		resb	Stack_Lk_sz

segment		_STACK	align=16 stack class=STACK
		resb	Stack_16_sz
