Metropoli BBS
VIEWER: pmode.asm MODE: TEXT (CP437)
; PMODE v3.07 DPMI/VCPI/XMS/raw protected mode interface kernel.
; Copyright (c) 1994, Tran (a.k.a. Thomas Pytel).
;
; Changed to compile under TASM for DJGPP by m.grimrath@tu-bs.de
; Assemble case sensitive on all symbols!

	P386
	IDEAL
	ASSUME	cs:_TEXT, ds:DGROUP, es:nothing, ss:nothing
	GROUP DGROUP _DATA, _BSS

;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
; Configuration options
;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
; [fold]  [

; Set to one to call exception 7 through a trap gate. An improvement of
; 10% in execution speed is possible using wmemu387.
; However, if one of the pics uses ints 0 to 7 (highly unlikely but supported)
; then an IRQ7 or IRQ15 will probably crash the computer.
; Also we cannot tell 'int 7' from exception 7.
FAST_FPU_EMU	=	1

; Set to 1 to have PMODE/DJ extensions to the DPMI standard.
PMODEDJ_EXT	=	0

; [fold]  ]

;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
; Makros
;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
; [fold]  [

off		EQU	offset
pdseg		EQU	fs	; take advantage of the 2 additional
coreseg		EQU	gs	; segment registers!

; Macros to build up standard selectors
	MACRO init_sel		; Initialize selector
sel_aktcnt	=	0
	ENDM
	MACRO mk_sel selec	; Add one selectors
selec		=	sel_aktcnt*8
sel_aktcnt	=	sel_aktcnt+1
	ENDM
	MACRO mk_msel selec,num ; Make multiple selectors
selec		=	sel_aktcnt*8
sel_aktcnt	=	sel_aktcnt+num
	ENDM
	MACRO get_num_sel lab	; returns the current number of selectors
lab	=	sel_aktcnt
	ENDM

; a 32 addressing nop (for a 386 bug)
	MACRO dnop
	DB 67h
	nop
	ENDM

; Makes a linear address from seg:ofs
	MACRO ptr2lin seg,ofs,r1,r2,er1,er2
	mov r1,seg
	movzx er1,r1
	shl er1,4
	mov r2,ofs
	movzx er2,r2
	add er1,er2
	ENDM

; Make a segment from a linear address
	MACRO lin2seg segm,lin,ereg,reg
	IFDIF <lin>,<ereg>
	mov ereg,lin
	ENDIF
	shr ereg,4
	mov segm,reg
	ENDM

; Sets a memory area to zero
	MACRO setmemw seg,ofs,size
	IFDIF <seg>,<es>
	push seg
	pop es
	ENDIF
	IFDIF <ofs>,<di>
	mov di,ofs
	ENDIF
	mov cx,(size) / 2
	sub ax,ax
	cld
	rep stosw
	nop		; 386 bug
	ENDM

; Copies memory areas with ESI and EDI
	MACRO dmemcpyw sseg,sofs,dseg,dofs,size
	IFDIF <sofs>,<esi>
	mov esi,sofs   ; Put this HERE, or else you can't use esp
	ENDIF
	IFDIF <dofs>,<edi>
	mov edi,dofs   ; as macro argument
	ENDIF
	IFDIF <size>,<ecx>
	mov ecx,(size) / 2
	ENDIF
	push ds es
	IFDIF <dseg>,<es>
	push dseg
	ENDIF
	IFDIF <sseg>,<ds>
	push sseg
	pop ds
	ENDIF
	IFDIF <dseg>,<es>
	pop es
	ENDIF
	cld
	DB 67h		; To use ECX, ESI and EDI
	rep movsw
	dnop		; 386 bug
	pop es ds
	ENDM

; Copies memory areas with SI and DI
	MACRO memcpyw sseg,sofs,dseg,dofs,size
	IFDIF <sofs>,<si>
	mov si,sofs   ; Put this HERE, or else you can't use esp
	ENDIF
	IFDIF <dofs>,<di>
	mov di,dofs   ; as macro argument
	ENDIF
	IFDIF <size>,<cx>
	mov cx,(size) / 2
	ENDIF
	push ds es
	IFDIF <dseg>,<es>
	push dseg
	ENDIF
	IFDIF <sseg>,<ds>
	push sseg
	pop ds
	ENDIF
	IFDIF <dseg>,<es>
	pop es
	ENDIF
	cld
	rep movs [WORD PTR es:di],[WORD PTR ds:si]
	pop es ds	; these pops cover 386 bug
	ENDM

; to save one line of source code
	MACRO copy dst,src,reg
	mov reg,src
	mov dst,reg
	ENDM

; not to write so many lines
	MACRO init_dsc base,sel,limit,rights
	IFDIF <base>,<ax>
	mov ax,base
	ENDIF
	IFDIF <sel>,<bx>
	mov bx,sel
	ENDIF
	IFDIF <limit>,<ecx>
	mov ecx,limit
	ENDIF
	IFDIF <rights>,<dx>
	mov dx,rights
	ENDIF
	call vxr_initsetdsc
	ENDM

; zero extends 16 bit offset to 32, so it can be linked without 32 records
	MACRO do16to32 mnem,p1,p2
	DB 66h	; 32 bit operand mode
	mnem p1,p2
	DW 0
	ENDM
; [fold]  ]

;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
; Constants and structures
;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
; [fold]  [

; As you might notice, all structures start with a capital letter
; When changing the source, please keep this style!

RMSTACKLEN	=	0C00h	; real mode stack length, in bytes
RMONESTACKLEN	=	200h
PMSTACKLEN	=	0C00h	; protected mode stack length, in bytes
PMONESTACKLEN	=	200h
VCPISTACKLEN	=	100h	; just for switching
CALLBACKS	=	16	; number of real mode callbacks
NUM_EXCEPTIONS	=	16	; # of handled exceptions

	init_sel		; Global descriptortable
	mk_sel	SELNULL		; NULL selector
	mk_sel	SELCORE		; selector of entire memory space
	mk_sel	SELCS		; Selector for Codesegment
	mk_sel	SELDS		; Selector for Datasegment
	mk_sel	SELREAL		; real mode attributes selector
	mk_sel	SELALIASCS	; to write to CS
	mk_sel	SELPRIVAT	; to access privat data
	mk_sel	SELINT24	; for INT 24h callback
	mk_sel	SELVCPITSS	; TSS selector for VCPI
	mk_sel	SELVCPI		; VCPI call code selector
	mk_sel	SELVCPI1
	mk_sel	SELVCPI2	; reserved for VCPI host
	mk_msel SELTASKS,NUM_EXCEPTIONS
   get_num_sel	SYSSELECTORS	; number of system-selectors
GDT_NUM_ENTRIES =      128	; # of entries in the gdt

; A 32bit Task status structure
STRUC Tss
backlink	DW	?,?
esp0		DD	?
ss0		DW	?,?
esp1		DD	?
ss1		DW	?,?
esp2		DD	?
ss2		DW	?,?
cr3		DD	?
eip		DD	?
eflags		DD	?
eax		DD	?
ecx		DD	?
edx		DD	?
ebx		DD	?
esp		DD	?
ebp		DD	?
esi		DD	?
edi		DD	?
es		DW	?,?
cs		DW	?,?
ss		DW	?,?
ds		DW	?,?
fs		DW	?,?
gs		DW	?,?
ldt		DW	?,?
trap		DW	?
iomapbase	DW	?
ENDS

; DPMI register structure
STRUC Dpmi_regs
LABEL edi DWORD
di	DW	?
di_hi	DW	?
LABEL esi DWORD
si	DW	?
si_hi	DW	?
LABEL ebp DWORD
bp	DW	?
bp_hi	DW	?
res	DD	?
LABEL ebx DWORD
LABEL bx WORD
bl	DB	?
bh	DB	?
bx_hi	DW	?
LABEL edx DWORD
LABEL dx WORD
dl	DB	?
dh	DB	?
dx_hi	DW	?
LABEL ecx DWORD
LABEL cx WORD
cl	DB	?
ch	DB	?
cx_hi	DW	?
LABEL eax DWORD
LABEL ax WORD
al	DB	?
ah	DB	?
ax_hi	DW	?
flags	DW	?
es	DW	?
ds	DW	?
fs	DW	?
gs	DW	?
ip	DW	?
cs	DW	?
sp	DW	?
ss	DW	?
ENDS

; to access a descriptor entry more easily
STRUC Descriptor
limit0_15	DW ?
base0_15	DW ?
base16_23	DB ?
LABEL types WORD
types0		DB ?
types1		DB ?
base24_31	DB ?
ENDS

; Almost the same as a descriptor, but its a gate
STRUC Gate
offset0		DW	?
sel		DW	?
reserved	DB	?
gtype		DB	?
offset1		DW	?
ENDS

; for DPMI call memory information
STRUC Dpmi_meminfo
available_memory		DD	?
available_pages			DD	?
available_lockable_pages	DD	?
linear_space			DD	?
unlocked_pages			DD	?
available_physical_pages	DD	?
total_physical_pages		DD	?
free_linear_space		DD	?
max_pages_in_paging_file	DD	?
reserved			DD 3 DUP(?)
ENDS

; also for easy access
STRUC Farptr16		; A 16 bit far ptr
LABEL	ptr DWORD
offset	DW	?
sel	DW	?
ENDS

STRUC Farptr32		; A 32 Bit far ptr
LABEL	ptr FWORD
offset	DD	?
sel	DW	?
pad	DW	?	; one for align
ENDS

STRUC Temp		; for temporary variables
LABEL	d0 DWORD
LABEL	w0 WORD
b0	DB	?
b1	DB	?
LABEL	w1 WORD
b2	DB	?
b3	DB	?
ENDS

STRUC Callback
	DB 0Fh,0A8h			; PUSH gs
	DB 0Fh,0A0h			; PUSH fs
	DB 1Eh				; PUSH ds
	DB 06h				; PUSH es
	DB 9Ch				; PUSHF
	DB 66h,60h			; PUSHAD instruction
	DB 68h				; PUSH WORD instruction
ds	DW 0				; immediate 0 used as free flag
	DB 66h,68h			; PUSH DWORD instruction
esi	DD 0				;    immediate data
	DB 0B9h				; MOV CX,? instruction
es	DW 0				;    immediate data
	DB 66h,68h			; PUSH DWORD instruction
edi	DD 0				;    immediate data
	DB 0EAh				; JMP FAR PTR ?:? intruction
ptr	Farptr16 ?			;    immediate data
ENDS

; The data structure for privat data
STRUC Pdata
rmstack		DB RMSTACKLEN DUP(?)
pmstack		DB PMSTACKLEN DUP(?)
vcpistack	DB VCPISTACKLEN DUP(?)
vcpitss		Tss ?
exceptiontasks	Tss NUM_EXCEPTIONS DUP(?)
exceptions	Farptr32 32 DUP(?)
lowint		Farptr32 NUM_EXCEPTIONS DUP(?)
		DB 256 DUP(?)
LABEL exceptionstack WORD
idt		Gate 256 DUP(?)
gdt		Descriptor GDT_NUM_ENTRIES DUP(?)
LABEL after WORD
ENDS
; if running under VCPI, this is (page aligned) followed by the
; pagedirectory and the pagetables.

; [fold]  ]

;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
; DATA
;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
	SEGMENT _DATA PUBLIC USE16 'DATA'

PUBLIC pm_exit					; user procedure at exit
; [fold]  [

lvcpistruc	DD	?		; VCPI switch structure linear address
LABEL vcpi_switch_struct WORD
vcpi_cr3	DD	?		; VCPI CR3 value for protected mode
vcpi_gdtaddx	DD	?		; linear addx of GDT limit and base
vcpi_idtaddx	DD	?		; linear addx of IDT limit and base
vcpi_selldt	DW	0		; LDT selector for protected mode
vcpi_seltss	DW	SELVCPITSS	; TSS selector for protected mode
vcpi_eip	DW	off v_rmtopmswpm; destination EIP in protected mode
		DW	0
vcpi_cs		DW	SELCS		; destination CS in protected mode

emspage		DW	-1		; For some VCPI Managers

picbasetab	DB	88h,90h,98h,0A0h,0A8h,0B0h,0B8h,68h,78h,-1

initrouttbl	DW	r_init,x_init,v_init,d_init
exitrouttbl	DW	r_exit,x_exit,v_exit ; exit functions. DPMI not needed
pm_exit		DW	ret_only		; Just a ret

int31functbl	DW	0000h, 0001h, 0002h, 0003h, 0006h
		DW	0007h, 0008h, 0009h, 000ah, 000bh
		DW	000ch, 000eh, 000fh
		DW	0100h, 0101h, 0102h
		DW	0200h, 0201h, 0202h, 0203h
		DW	0204h, 0205h
		DW	0300h, 0301h, 0302h, 303h, 304h
		DW	0305h, 0306h
		DW	0400h
		DW	0500h, 0501h, 0502h, 0503h, 050ah
		DW	0600h, 0601h, 0602h, 0603h, 0604h
		DW	0702h, 0703h
		DW	0800h, 0801h
		DW	0900h, 0901h, 0902h
IF PMODEDJ_EXT EQ 1
		DW	0A00h
ENDIF
		DW	0E00h, 0E01h
INT31FUNCNUM	= ($ - int31functbl) / 2

int31routtbl	DW	int310000, int310001, int310002, int310003, int310006
		DW	int310007, int310008, int310009, int31000a, int31000b
		DW	int31000c, int31000e, int31000f
		DW	int310100, int310101, int310102
		DW	int310200, int310201, int310202, int310203
		DW	int310204, int310205
		DW	int310300, int310301, int310302, int310303, int310304
		DW	int310305, int310306
		DW	int310400
int31mrouttbl	DW	int310500v,int310501v,int310502v,int310503v,int31050av
		DW	int310600, int310601, int310602, int310603, int310604
		DW	int310702, int310703
int31phystbl	DW	int310800v,int310801v
		DW	int310900, int310901, int310902
IF PMODEDJ_EXT EQ 1
		DW	int310a00
ENDIF
		DW	int310e00, int310e01

int31mxrouttbl	DW	int310500x,int310501x,int310502x,int310503x,int31050ax

int31mrrouttbl	DW	int310500r, int310501r, int310502r, int310503r
		DW	int31050ar

int31mnrouttbl	DW	int310500rnomem, int31fail8013, int31fail8023
		DW	int31fail8023, int31fail8023

int31physxrtbl	DW	int310800xr, int310801xr

IF PMODEDJ_EXT EQ 1
apitab:
APITAB_SIZE	= $ - apitab
pmodedj_id	DB	'DJGPP',0
PMODEDJ_ID_SIZE = $ - pmodedj_id
ENDIF
		ALIGN 4

; Placed here, coz my TASM had problems putting this in the code
common_callbacks Callback CALLBACKS DUP(<>)

segmentbases	DW 16*2 DUP(0)		  ; segment bases for function 0002h
segmentbases_SIZE = ($ - segmentbases)

rawextmemused	DW	1		; raw extended memory used in K
rawextmembase	DD	0ffffffffh	; raw extended memory base
rawextmemtop	DD	0		; raw extended memory top

rmidtlimit	DW	3ffh		; real mode IDT limit
rmidtbase	DD	0		; real mode IDT base
	ENDS

	SEGMENT _BSS PUBLIC USE16 'BSS'
gdtlimit	DW	?		; GDT limit
gdtbase		DD	?		; GDT base
idtlimit	DW	?		; IDT limit
idtbase		DD	?		; IDT base

SetMasterPIC	DW	?		; offset of PIC set routine
ResetMasterPIC	DW	?		; offset of PIC reset routine
SetA20		DW	?		; offset of A20 set routine
ResetA20	DW	?		; offset of A20 reset routine

pmstackbase	DD	?		; bottom of protected mode stack area
pmstacktop	DD	?		; top of protected mode stack area
rmstackbase	DW	?		; bottom of real mode stack area
rmstacktop	DW	?		; top of real mode stack area

processortype	DB	?		; processor type
pmodetype	DB	?		; protected mode type
reprogrampic	DB	?		; if first PIC should be reprogrammed
from_us		DB	?		; if int call by PMODE
picslaveold	DB	?		; old PIC slave base interrupt
picmasterold	DB	?		; old PIC master base interrupt
picslavenew	DB	?		; New PIC slave base
picmasternew	DB	?		; New PIC master base

temp0		Temp	?		; temporary variables
temp1		Temp	?		;     "        "

olda20		DB	?
detectionflags	DB	?
oldCR0L		DB	?		; low byte of CR0
newEM_MP	DB	?		; user specified EM and MP
xms_call	Farptr16 ?
oldint15vector	DD	?
oldint2fvector	DD	?

pagedir		DD	?		; linear address of pagedirectory
pagetablebase	DD	?		; base of page table area
pagetabletop	DD	?		; top of page table area
pagetablefree	DD	?		; base of available page table area
pagetabledelta	DD	?		; # of KB for allocates pagetables

privatedataseg	DW	?		; segment of privat data

dpmiepmode	Farptr16 ?		; DPMI protected mode entry

vcpiswitchstack Farptr32 ?		; VCPI temporary mode switch stack
vcpi_service	Farptr32 ?		; VCPI code pointer

rmtopmswrout	DW	?		; offset of real to protected routine
pmtormswrout	DW	?		; offset of protected to real routine
rmstackparmtop	DW	?		; for functions 0300h, 0301h, 0302h
		DB 128 DUP(?)		; little stack at exit
LABEL endstack WORD
		ENDS

; [fold]  ]

;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
; DETECT/INIT CODE
;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒

	SEGMENT _TEXT PUBLIC USE16 'CODE'

PUBLIC pm_info		; procedure to get info
PUBLIC pm_init		; switches to protected mode

;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; [fold]  [
; pm_info: Get protected mode info
; In:
;   AX =
;     Bit  0 = 0: Check for DPMI first, else check for VCPI first
;     Bit  1	: ignored and reserved for pm_init
;
;     Bit  8 = 1: forbid raw
;     Bit  9 = 1: forbid XMS
;     Bit 10 = 1: forbid VCPI
;     Bit 11 = 1: forbid DPMI
;
; Out:
;   AX = return code:
;     0000h = successful
;     0001h = no 80386+ detected
;     0002h = system already in protected mode and no VCPI or DPMI found
;     0003h = no way to switch into protected mode
;   CF = set on error, if no error:
;     BX = number of paragraphs needed for protected mode data (may be 0)
;     CL = processor type:
;	02h = 80286
;	03h = 80386
;	04h = 80486
;	05h = 80586
;	06h-FFh = reserved for future use
;     CH = protected mode type:
;	00h = raw
;	01h = XMS
;	02h = VCPI
;	03h = DPMI
;
; Remark by MG: spaghetticode!
;
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
pm_info:
	push dx si di bp ds es bx cx	; preserve registers

	mov [detectionflags],ah		; store detection flags
	test al,1			; check order of DPMI/VCPI detection
	jz short @@infof0

	call @@detect_VCPI		; check for VCPI first
	call @@detect_DPMI		; check for DPMI second
	jmp short @@infof2		; neither found, go on to XMS check

@@infof0:
	call @@detect_DPMI		; check for DPMI first
	call @@detect_VCPI		; check for VCPI second

;-----------------------------------------------------------------------------
@@infof2:
	smsw ax				; AX = machine status word
	test al,1			; is system in protected mode?
	mov ax,2			; error code in case protected mode
	jnz short @@infofail		; if in protected mode, fail

	mov ch,1			; pmode type XMS
	test [detectionflags],2		; XMS allowed?
	jnz short @@chkr
	mov ax,4300h			; check for XMS
	int 2fh
	cmp al,80h			; XMS present?
	jz short @@w1			; if so, go on

@@chkr: sub ch,ch			; set type to raw
	test [detectionflags],1		; raw allowed?
	jz short @@w1			; if so

	mov ax,3			; else set error code
	jmp short @@infofail		; and fail

@@w1:	sub bx,bx			; no special memory needed

;-----------------------------------------------------------------------------
@@infof1:
	add bx,(SIZE Pdata+15) / 16	; Size of privatdata structure
	jmp short @@infook		; go to done ok

;-----------------------------------------------------------------------------
@@infofail:
	pop cx bx			; restore BX and CX
	stc				; carry set, failed
	jmp short @@infodone

;-----------------------------------------------------------------------------
@@infook:
	mov [pmodetype],ch		; store pmode type

	add sp,4			; skip BX and CX on stack
	xor ax,ax			; success code, also clear carry flag
	clc				; Signal success

;-----------------------------------------------------------------------------
@@infodone:
	pop es ds bp di si dx		; restore other registers
	ret				; return

;─────────────────────────────────────────────────────────────────────────────
; [fold]  [
@@detect_DPMI: ; detect a DPMI host
	pop bp				; pop return address from stack

	test [detectionflags],8		; DPMI allowed?
	jnz short @@detect_DPMIdone	; if not, quit

	sub ax,ax
	mov es,ax
	mov ax,[es:02Fh*4]
	or ax,[es:02Fh*4+2]		; check if multiplex set
	jz short @@detect_DPMIdone

	mov ax,1687h			; check for DPMI
	int 2fh

	or ax,ax			; DPMI present?
	jnz short @@detect_DPMIdone	; if no, exit routine

	mov ax,3			; error code in case DPMI not 32bit
	test bl,1			; is DPMI 32bit?
	jz short @@detect_DPMIdone	; if not, this DPMI host is no good

	mov ax,1			; error code in case no processor 386+
	cmp cl,3			; is processor 386+? (redundant)
	jb short @@infofail		; if no, fail

	mov [dpmiepmode.offset],di	; store DPMI initial mode switch addx
	mov [dpmiepmode.sel],es

	mov bx,si			; BX = number of paragraphs needed
	mov ch,3			; pmode type is 3 (DPMI)

	jmp @@infook			; go to done ok

@@detect_DPMIdone:
	jmp bp				; return to calling routine
; [fold]  ]

;─────────────────────────────────────────────────────────────────────────────
; [fold]  [
@@detect_VCPI: ; detect a VCPI server
	pop bp				; pop return address from stack

	call @@detect_processor		; get processor type

	mov ax,1			; error code in case no processor 386+
	cmp cl,3			; is processor 386+?
	jb short @@infofail		; if no, no VCPI
	mov [processortype],cl		; store processor type

	test [detectionflags],4		; VCPI allowed?
	jnz @@detect_VCPIdone		; if not, ignore

	xor ax,ax			; get INT 67h vector
	mov es,ax
	mov ax,[es:67h*4]
	or ax,[es:67h*4+2]		; is vector NULL
	jz short @@detect_VCPIdone	; if yes, no VCPI

	cmp	[emspage],-1		; if already EMS Page allocated,
	jnz	short @@no_alloc	; skip
	mov	ah,40H			;Get Status
	int	67h
	cmp	ah,0
	jne	short @@no_alloc	;maybe only EMS turned off
	mov	ah,42H			;Get Unallocate Page Count
	int	67h
	cmp	ah,0
	jne	short @@no_alloc
	cmp	bx,dx			;Other Program EMS Page Used?
	jne	short @@no_alloc	;Used!!
	mov	bx,1
	mov	ah,43H			;Allocate Pages(1 Page Only),
	int	67h			;so EMS has to be turned on
	cmp	ah,0
	jne	short @@no_alloc	;if page alloc failed
	mov	[emspage],dx		; store EMS page

@@no_alloc:
	mov ax,0de00h			; call VCPI installation check
	int 67h
	or ah,ah			; AH returned as 0?
	jnz short @@detect_VCPIdone2	; if no, no VCPI

	; determine here # of max pagetables! + pagedir
	mov ax,0DE03h			; # of free pages
	int 67h
	add edx,1023+1024+2048		; align to next pagetable +
	and dx,NOT 1023			;   first MB + 8 MB extra linear
	shl edx,2			; # of bytes for page tables
	mov [pagetabledelta],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
	mov ch,2			; pmode type is 2 (VCPI)

	jmp @@infof1			; go to figure other memory needed

@@detect_VCPIdone2:
	mov dx,[emspage]
	cmp dx,-1
	jz short @@detect_VCPIdone	; free allocated EMS page
	mov ah,45h
	int 67h
	mov [emspage],-1		; reset to 'no emspage allocated'
@@detect_VCPIdone:
	jmp bp				; return to calling routine
; [fold]  ]

;─────────────────────────────────────────────────────────────────────────────
; [fold]  [
@@detect_processor: ; get processor: 286, 386, 486, or 586
	xor cl,cl			; processor type 0 in case of exit

	pushf				; transfer FLAGS to BX
	pop bx

	mov ax,bx			; try to clear high 4 bits of FLAGS
	and ah,0fh

	push ax				; transfer AX to FLAGS
	popf
	pushf				; transfer FLAGS back to AX
	pop ax

	and ah,0f0h			; isolate high 4 bits
	cmp ah,0f0h
	je short @@detect_processordone ; if bits are set, CPU is 8086/8

	mov cl,2			; processor type 2 in case of exit

	or bh,0f0h			; try to set high 4 bits of FLAGS

	push bx				; transfer BX to FLAGS
	popf
	pushf				; transfer FLAGS to AX
	pop ax

	and ah,0f0h			; isolate high 4 bits
	jz short @@detect_processordone ; if bits are not set, CPU is 80286

	inc cx				; processor type 3 in case of exit

	push eax ebx edx		; preserve 32bit registers

	pushfd				; transfer EFLAGS to EBX
	pop ebx

	mov eax,ebx			; try to flip AC bit in EFLAGS
	xor eax,40000h

	push eax			; transfer EAX to EFLAGS
	popfd
	pushfd				; transfer EFLAGS back to EAX
	pop eax

	xor eax,ebx			; AC bit fliped?
	jz short @@detect_try_cpuid	; if no, CPU is 386

	inc cx				; processor type 4 in case of exit
@@detect_try_cpuid:

	mov eax,ebx			; try to flip ID bit in EFLAGS
	xor eax,200000h

	push eax			; transfer EAX to EFLAGS
	popfd
	pushfd				; transfer EFLAGS back to EAX
	pop eax

	xor eax,ebx			; ID bit fliped?
	jz short @@detect_processordone2; if no, return 386 or 486

; Use cpuid instruction if supported
; cpuid is supported on some new 486, some new Nx586, Pentium, Am5x86,
; Cyrix 6x86 and all newer processors. I'm not sure about Cyrix 5x86.
; Nx586 returns 5, Am5x86 returns 4, Cyrix 6x86 returns 6.
	mov eax,0			; check if CPUID accepts 1 in eax
	db 0fh,0a2h			; CPUID
	or eax,eax			; eax for distant future compatibility
	mov cx,5			; processor type 5 in case of exit
	jz short @@detect_processordone2; cpuid can not be used further

	mov eax,1			; get chip type and supported features
	db 0fh,0a2h			; CPUID
	and ah,0fh			; clear type field
	movzx cx,ah			; return family field.

@@detect_processordone2:
	pop edx ebx eax			; restore 32bit registers

@@detect_processordone:
	ret				; return
; [fold]  ]

; [fold]  ]

;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; [fold]  [
; pm_init: switch into protected mode
; In:
;   AX = Bit  0 = 1 Check for VCPI first
;	 Bit  1 = 1 Reprogram first PIC if possible
;	 Bit  8 = 1 forbid raw
;	 Bit  9 = 1 forbid XMS
;	 Bit 10 = 1 forbid VCPI
;	 Bit 11 = 1 forbid DPMI
;   ES = real mode segment for protected mode data (ignored if not needed)
; Out:
;   AX = return code:
;     0000h = successful
;     0001h = no 80386+ detected
;     0002h = system already in protected mode and no VCPI or DPMI found
;     0003h = (changed) no way to switch into protected mode
;     0004h = could not enable A20 gate
;     0005h = DPMI - could not enter 32bit protected mode
;   CF = set on error, if no error running in protected mode:
;     ESP = high word clear
;     CS = 16bit selector for real mode CS with limit of 64k
;     SS = selector for real mode SS with limit of 64k
;     DS = selector for real mode DS with limit of 64k
;     ES = selector for PSP with limit of 100h
;     FS = 0 (NULL selector)
;     GS = 0 (NULL selector)
;  Rem: DS == SS, if DS == SS on entry
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; [fold]  [
pm_init:
	push ax
	call pm_info
	pop ax
	jnc short @@initf0		; error?
	ret				; yup, abort

@@initf0:				; no error, init protected mode
	test ax,2
	setnz [reprogrampic]

	push esi edi ebp
	cld

	setmemw es,<off 0+Pdata.gdt>,<GDT_NUM_ENTRIES * SIZE Descriptor>
					; clear GDT with all 0
	mov [privatedataseg],es		; save data segment
	movzx bx,[pmodetype]		; jump to appropriate init code
	shl bx,1
	jmp [bx + initrouttbl]
; [fold]  ]

;═════════════════════════════════════════════════════════════════════════════
; [fold]  [
d_init: ; DPMI protected mode init
	mov ax,1			; enter DPMI protected mode
	call [dpmiepmode.ptr]
	sub bx,bx
	jnc short init_done		; error? if not, finished here
	mov bx,5			; error entering protected mode, set

;-----------------------------------------------------------------------------
init_done:				; return with return code
	mov ax,bx
	pop ebp edi esi
	ret
; [fold]  ]

;─────────────────────────────────────────────────────────────────────────────
; This assumes, that the extender runs in 1 Code and 1 Datasegment.
; (This applies for model tiny, small, compact, and sometimes for tpascal)
; The original source put here created a codesegment descriptor
; to return from this procedure. But as pm_init is near-called, there is no
; need for such a code.
; [fold]  [
dvxr_init: ; DPMI/VCPI/XMS/raw common init tail
	sub bx,bx			; error code = 0 (no error)
	mov fs,bx			; changed, when switching by raw
	mov gs,bx
	clc				; signal success
	jmp	init_done
; [fold]  ]

;═════════════════════════════════════════════════════════════════════════════
; In: ES = segment for host private data buffer
; [fold]  [
v_init: ; VCPI protected mode init
	mov [SetMasterPIC],off v_setpic
	mov [ResetMasterPIC],off v_resetpic
	mov [SetA20],off v_seta20
	mov [ResetA20],off v_reseta20

	mov ax,0de0ah			; get PIC mappings
	int 67h
	mov [picmasterold],bl
	mov [picslaveold],cl

	mov ax,0de07h			; get CR0
	int 67h
	mov [oldCR0L],bl		; save old state
	sar bl,1
	and bl,3			; keep EM and MP
	mov [newEM_MP],bl

	ptr2lin es,<off 0+Pdata.after>,ax,bx,eax,ebx
	add eax,4095
	and ax,NOT 4095			; align data area on page
	mov [pagedir],eax		; store

	add eax,1000h			; skip pagedir
	mov [pagetablebase],eax		; set base of page table area
	add eax,[pagetabledelta]	; size of pagetables
	mov [pagetabletop],eax		; set top of page table area

	lin2seg es,[pagedir],eax,ax
	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

	setmemw es,0,2*4096		; clear page dir and first page table
	copy es,fs,ax			; ES = segment of first pagetable

	push ds
	mov ds,[privatedataseg]
	mov si,off SELVCPI+Pdata.gdt	; descriptors for VCPI host
	xor di,di			; get VCPI protected mode interface
	mov ax,0de01h
	int 67h
	pop ds				; restore old ds

	movzx eax,di			; set base of usable page table area
	add ax,4095
	and ax,NOT 4095			; align to next pagetable
	add eax,[pagetablebase]
	mov [pagetablefree],eax

	mov [vcpi_service.sel],SELVCPI	; store protected mode VCPI call CS
	mov [vcpi_service.offset],ebx	; store protected mode VCPI call EIP

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

	; --------- Link pagetables in pagedirectory ----------
	mov si,es			; DX = current page table segment
	xor ebx,ebx			; index in page dir, also loop counter
	mov ebp,[pagetabledelta]
	shr ebp,12			; # of pagetables
	jmp short @@v_initl1f0

@@v_initl1:
	xor di,di			; clear page table
	mov cx,800h
	xor ax,ax
	rep stosw
	nop		; 386 bug

@@v_initl1f0:
	mov cx,si
	shr cx,8			; get page number
	mov ax,0de06h			; get physical address
	int 67h
	and dh,0f1h			; For safety clear reserved bits 9-11
	or dl,7				; set present, User and R/W bit
	mov [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 @@v_initl1			; if no, loop

;-----------------------------------------------------------------------------

	mov ecx,[pagedir]
	shr ecx,12
	mov ax,0de06h
	int 67h				; get physical address
	mov [vcpi_cr3],edx
	ptr2lin ds,<off gdtlimit>,ax,bx,eax,ebx
	mov [vcpi_gdtaddx],eax
	ptr2lin ds,<off idtlimit>,ax,bx,eax,ebx
	mov [vcpi_idtaddx],eax
	ptr2lin ds,<off vcpi_switch_struct>,ax,bx,eax,ebx
	mov [lvcpistruc],eax
	mov [rmtopmswrout],off v_rmtopmsw
	mov [pmtormswrout],off v_pmtormsw
; [fold]  ]

;─────────────────────────────────────────────────────────────────────────────
; [fold]  [
vxr_init: ; VCPI/XMS/raw common init tail
	mov [from_us],0			; init
	mov pdseg,[privatedataseg]	; access private data

	sub edx,edx
	mov dx,off critical_error	; Standard exception handler
	mov si,off 0 + Pdata.exceptions
	mov ax,NUM_EXCEPTIONS
@@lp:	mov [pdseg:si + Farptr32.sel],SELCS
	mov [pdseg:si + Farptr32.offset],edx
	mov [pdseg:si + Farptr32.pad],0
	add si,SIZE Farptr32
	dec ax
	jnz @@lp
	DB 66h				; NMI is passed down to real mode
	mov ax,off NMI_exception
	DW 0
	mov [pdseg:off (2*SIZE Farptr32) + Pdata.exceptions],eax

IF FAST_FPU_EMU EQ 1
	mov ax,[pdseg:7*SIZE Farptr32 + Pdata.exceptions.sel]
	mov [cs:FPU_emu_vector.sel],ax
	mov eax,[pdseg:7*SIZE Farptr32 + Pdata.exceptions.offset]
	mov [cs:FPU_emu_vector.offset],eax
ENDIF

	copy [picslavenew],[picslaveold],al
	copy [picmasternew],[picmasterold],al
	test [reprogrampic],-1		; reprogram first PIC?
	jz short @@pice
	cmp [picmasternew],8		; default value for first PIC?
	jnz short @@pice		; if not, keep hands off!
@@gopic:mov di,off picbasetab
	copy es,0,ax			; ES -> Base of IVT
	jmp short @@lpde
@@lpd:	mov eax,[es:edx*4]		; get first interrupt vector
	mov cl,7
@@lpc:	inc dx
	cmp eax,[es:edx*4]		; contest the same?
	jnz short @@lpde		; if not, try next
	dec cl				; Rem: This is faster than 'loop'!
	jnz @@lpc			; Compare next
	sub dx,7			; yeah, found a place
	jmp short @@repr
@@lpde: movzx edx,[BYTE PTR di]
	inc di
	cmp dl,-1
	jnz @@lpd

@@repr: cmp dl,-1			; search successful?
	jz short @@pice			; if not, everything's the same
	mov [picmasternew],dl
	call [SetMasterPIC]		; else set new pic base

@@pice: ptr2lin pdseg,<off 0+Pdata.idt>,ax,bx,eax,ebx
	mov [idtbase],eax		; set IDT base address
	mov [idtlimit],(256 * SIZE Gate)-1

	mov [rmstackbase],off 0+Pdata.rmstack		; set top and base of
	mov [rmstacktop],off RMSTACKLEN + Pdata.rmstack ; real mode stack

	; ------------------ Set up IDT -----------------
	sub di,di
	mov si,off common_int_handler
	sub cx,cx
@@vxr_idt_loop1:
	mov [pdseg:di + Pdata.idt.offset0],si
	mov [pdseg:di + Pdata.idt.offset1],0
	mov [pdseg:di + Pdata.idt.reserved],0
	mov [pdseg:di + Pdata.idt.sel],SELCS
	mov [pdseg:di + Pdata.idt.gtype],8Fh	; trap gate
	add di,SIZE Gate
	add si,INT_HANDLER_SIZE
	inc cx
	cmp cx,256
	jne @@vxr_idt_loop1

	; --------------- Set up hardware IRQ handlers --------------
	mov cl,8
	mov bx,off common_hardirq_handler
	movzx dx,[picmasterold]
	shl dx,2		; rm offset in IVT
	movzx di,[picmasternew]
	shl di,3		; offset in IDT
@@hi1:	mov [cs:bx+2],dx	; store rm offset
	mov [pdseg:di + Pdata.idt.offset0],bx
	mov [pdseg:di + Pdata.idt.gtype],8Eh	; interrupt gate
	add di,SIZE Gate
	add bx,HARDIRQ_HANDLER_SIZE
	add dx,4
	dec cl
	jne @@hi1
	mov cl,8
	movzx dx,[picslaveold]
	shl dx,2		; rm offset in IVT
	movzx di,[picslavenew]
	shl di,3		; offset in IDT
@@hi2:	mov [cs:bx+2],dx	; store rm offset
	mov [pdseg:di + Pdata.idt.offset0],bx
	mov [pdseg:di + Pdata.idt.gtype],8Eh	; interrupt gate
	add di,SIZE Gate
	add bx,HARDIRQ_HANDLER_SIZE
	add dx,4
	dec cl
	jne @@hi2

	; --------------- Set up exception handler tasks ------------
	sub di,di
	mov si,SELTASKS
	sub cx,cx
	mov bx,off 0 + Pdata.lowint
@@vxr_idt_loop2:
	mov ax,[pdseg:di + Pdata.idt.offset1]
	shl eax,16
	mov ax,[pdseg:di + Pdata.idt.offset0]
	mov [pdseg:bx + Farptr32.offset],eax
	mov ax,[pdseg:di + Pdata.idt.sel]
	mov [pdseg:bx + Farptr32.sel],ax
	mov [pdseg:bx + Farptr32.pad],0

	mov [pdseg:di + Pdata.idt.offset0],0
	mov [pdseg:di + Pdata.idt.offset1],0
	mov [pdseg:di + Pdata.idt.reserved],0
	mov [pdseg:di + Pdata.idt.sel],si
	mov [pdseg:di + Pdata.idt.gtype],85h	; set to Task Gate

	add di,SIZE Gate
	add si,SIZE Descriptor
	add bx,SIZE Farptr32
	inc cx
	cmp cx,NUM_EXCEPTIONS
	jne @@vxr_idt_loop2

IF FAST_FPU_EMU EQ 1
	; 'FPU not present' called by trap gate
	DB 66h
	mov [WORD PTR pdseg:(7*SIZE Gate) + Pdata.idt.offset0],off FPU_emulation
	DW 0
	mov [pdseg:(7*SIZE Gate) + Pdata.idt.reserved],0
	mov [pdseg:(7*SIZE Gate) + Pdata.idt.sel],SELCS
	mov [pdseg:(7*SIZE Gate) + Pdata.idt.gtype],8Fh
ENDIF

	; Set up special ints
	mov [pdseg:8*31h+Pdata.idt.offset0],off int31 ; protected mode INT 31h
	mov [pdseg:8*21h+Pdata.idt.offset0],off int21 ; protected mode INT 21h
	mov [pdseg:8*23h+Pdata.idt.offset0],off int23pm ; pm INT 23h
	mov [pdseg:8*24h+Pdata.idt.offset0],off int24pm ; pm INT 24h
	mov [pdseg:8*2fh+Pdata.idt.offset0],off int2fpm ; pm INT 2fh

;-----------------------------------------------------------------------------

	ptr2lin pdseg,<off 0+Pdata.pmstack>,ax,bx,eax,ebx
	mov [pmstackbase],eax
	ptr2lin pdseg,<off PMSTACKLEN + Pdata.pmstack>,ax,bx,eax,ebx
	mov [pmstacktop],eax

;-----------------------------------------------------------------------------

	ptr2lin pdseg,<off 0 + Pdata.gdt>,ax,bx,eax,ebx
	mov [gdtbase],eax		; set GDT base address
	mov [gdtlimit],(GDT_NUM_ENTRIES * SIZE Descriptor) - 1

;-----------------------------------------------------------------------------

	ptr2lin pdseg,<off 0 + Pdata.vcpitss>,ax,bx,eax,ebx
	mov [pdseg:SELVCPITSS + Pdata.gdt.base0_15],ax
	shr eax,16
	mov [pdseg:SELVCPITSS + Pdata.gdt.base16_23],al
	mov [pdseg:SELVCPITSS + Pdata.gdt.base24_31],ah
	mov [pdseg:SELVCPITSS + Pdata.gdt.limit0_15],SIZE Tss-1
	mov [pdseg:SELVCPITSS + Pdata.gdt.types],0089h

	mov [vcpiswitchstack.offset],off VCPISTACKLEN+Pdata.vcpistack
	mov [vcpiswitchstack.sel],SELPRIVAT

;-----------------------------------------------------------------------------

	setmemw [privatedataseg],<off 0+Pdata.vcpitss>,<SIZE Tss>
					; clear Switch TSS
	mov es,[privatedataseg]
	mov eax,[vcpi_cr3]		; set CR3 in TSS
	mov [es:Pdata.vcpitss.cr3],eax
	mov [es:Pdata.vcpitss.iomapbase],SIZE Tss

;-----------------------------------------------------------------------------

	mov [WORD PTR pdseg:-2+Pdata.exceptionstack],0
	mov di,off 0 + Pdata.exceptiontasks	; set up exception tasks
	sub esi,esi
	mov si,off common_exception_without_error_handler
	mov edx,[vcpi_cr3]			; EDX = page directory
	sub ecx,ecx
@@exceptiontaskloop:
	push cx di
	setmemw pdseg,di,<SIZE Tss>		; Clear exception task
	pop di cx

	mov [pdseg:di + Tss.ss0],SELPRIVAT
	mov [pdseg:di + Tss.esp0],off 0+Pdata.exceptionstack
	mov [pdseg:di + Tss.ss1],SELPRIVAT
	mov [pdseg:di + Tss.esp1],off 0+Pdata.exceptionstack
	mov [pdseg:di + Tss.ss2],SELPRIVAT
	mov [pdseg:di + Tss.esp2],off 0+Pdata.exceptionstack
	mov [pdseg:di + Tss.ss],SELPRIVAT
	mov [pdseg:di + Tss.esp],off 0+Pdata.exceptionstack

	mov [pdseg:di + Tss.cs],SELCS
	mov [pdseg:di + Tss.eip],esi

	mov [pdseg:di + Tss.ds],SELPRIVAT	; es and ds are needed
	mov [pdseg:di + Tss.es],SELDS

	mov [pdseg:di + Tss.eflags],0

	mov [pdseg:di + Tss.cr3],edx
	mov [pdseg:di + Tss.iomapbase],SIZE Tss

	mov [pdseg:di + Tss.ebx],ecx		; store exception number

	push cx
	and cl,7
	mov ah,1
	shl ah,cl				; ah is mask for pic
	pop cx

	mov al,cl				; al is high 5 bits of cx used
	and al,0f8h				; to compare to pic base
	mov [pdseg:di + Tss.eax],eax
	mov [pdseg:di + Tss.edx],off 0 + Pdata.exceptionstack

	cmp al,8			; exceptions 8, 9-0eh have error
	jne short @@go_on		; and we can skip the checking
	cmp cl,9
	je short @@go_on
	cmp cl,0fh
	je short @@go_on
	sub eax,eax
	mov ax,off common_exception_with_error_handler
	mov [pdseg:di + Tss.eip],eax
@@go_on:

	mov [pdseg:di + Tss.ecx],0	; to be used as default error code

	add di,SIZE Tss
	inc cx
	cmp cx,NUM_EXCEPTIONS
	jne @@exceptiontaskloop

;-----------------------------------------------------------------------------

	ptr2lin pdseg,<off 0+Pdata.exceptiontasks>,ax,bx,eax,ebx
	mov bx,SELTASKS
	mov ecx,SIZE Tss-1		; Limit
	mov dx,1089h			; waiting Task
	sub si,si
@@taskgateloop:
	push eax dx
	mov [pdseg:bx + Pdata.gdt.limit0_15],cx ; limit = CX
	mov [pdseg:bx + Pdata.gdt.base0_15],ax
	shr eax,16
	mov [pdseg:bx + Pdata.gdt.base16_23],al
	mov [pdseg:bx + Pdata.gdt.base24_31],ah
	mov eax,ecx
	shr eax,8
	and dh,0F0h				; Clear limit16_19
	or dh,ah				; Set it
	mov [pdseg:bx + Pdata.gdt.types],dx	; access rights = DX
	add bx,8
	pop dx eax

	add eax,SIZE Tss
	inc si
	cmp si,NUM_EXCEPTIONS
	jne @@taskgateloop

;-----------------------------------------------------------------------------

	init_dsc 0,SELCORE,0FFFFFh,0D092h

	init_dsc cs,SELCS,0FFFFh,109Ah	; Make selector for CS
	init_dsc 0,SELREAL,ecx,1092h	; set real mode attributes descriptor
	init_dsc cs,SELALIASCS,ecx,5092h; To write to CS in protected mode
	init_dsc ds,SELDS,ecx,dx	; Make selector for DS
	init_dsc ax,SELINT24,ecx,dx	; Make selector for INT 24 callback
	init_dsc pdseg,SELPRIVAT,<SIZE Pdata>,dx


	mov bx,SIZE Descriptor*SYSSELECTORS ; BX = base of free descriptors
	push bx				; store CS selector
	init_dsc cs,bx,0FFFFh,109Ah	; make caller cs selector
	push bx bx			; store SS and DS selectors

	init_dsc ss,bx,ecx,5092h	; set caller SS descriptor

	mov ax,ds
	mov si,ss			; SS = DS?
	cmp ax,si
	jz short @@skip			; if so, they have to be equal!
	pop ax				; else set new selector for DS
	push bx
	init_dsc ds,bx,ecx,dx
@@skip:
	push bx				; get PSP segment
	mov ah,51h
	int 21h
	mov es,bx			; set caller environment descriptor
	mov si,bx
	pop bx

	mov ax,[es:2ch]
	or ax,ax			; is environment seg 0?
	jz short @@vxr_initf0		; if yes, dont convert to descriptor
	mov [es:2ch],bx			; store selector value in PSP
	init_dsc ax,bx,ecx,dx

@@vxr_initf0:
	init_dsc si,bx,0FFh,dx		; set caller PSP descriptor

;-----------------------------------------------------------------------------

	lea ecx,[ebx-8]			; CX = ES descriptor, just set
	pop ax				; AX = DS descriptor, from datasegment
	pop dx				; DX = SS descriptor, from stack
	pop si				; target CS, from codesegment
	mov di,off @@vxr_initf2		; target EIP
	movzx ebx,sp			; EBX = SP, current SP - same stack
	movzx edi,di

	jmp [rmtopmswrout]		; jump to mode switch routine

@@vxr_initf2:
	copy coreseg,SELCORE,ax
	copy pdseg,SELDS,ax

	mov eax,[coreseg:4*2Fh]		; check if multiplex set
	mov [pdseg:oldint2fvector],eax
	or eax,eax
	jnz short @@ok
	DB 66h
	mov [WORD PTR coreseg:4*2Fh],off int2frm ; set multiplex
	DW _TEXT
@@ok:
	DB 66h
	mov [WORD PTR coreseg:4*23h],off int23rm ; set new INT 23h handler
	DW _TEXT
	DB 66h
	mov [WORD PTR coreseg:4*24h],off int24rm ; set new INT 24h handler
	DW _TEXT
	push ds
	copy ds,SELALIASCS,ax
	mov bx,off oldint21vector	; setting direct makes tlink puke
	mov eax,[coreseg:4*21h]		; get DOS vector
	mov [ds:bx],eax			; save
	pop ds
	DB 66h
	mov [WORD PTR coreseg:4*21h],off dos21rm ; set new DOS vector
	DW _TEXT

	mov eax,[coreseg:4*15h]		; get INT 15h vector
	mov [pdseg:oldint15vector],eax	; store INT 15h vector

	mov esi,[pdseg:rawextmembase]	; ESI = raw base of extended memory
	cmp esi,[pdseg:rawextmemtop]	; is there any raw extended memory?
	jae dvxr_init			; if no, go DPMI/VCPI/XMS/raw init

	DB 66h
	mov [WORD PTR coreseg:4*15h],off int15 ; MOV [4*15],_TEXT:offset int15
	DW _TEXT			; set new INT 15h handler

	mov edi,[pdseg:rawextmemtop]	; EDI = raw top of extended memory
	mov eax,edi			; EAX = size of extended memory
	sub eax,esi
	sub eax,10h			; subtract memory control block size
	mov [coreseg:edi-16],eax	; store size in memory control block
	xor eax,eax
	mov [coreseg:edi-12],eax	; no next memory control block
	mov [coreseg:edi-8],eax		; no previous memory control block
	mov [coreseg:edi-4],al		; memory block is free

	jmp dvxr_init			; go to DPMI/VCPI/XMS/raw init tail

;─────────────────────────────────────────────────────────────────────────────
; set descriptor for VCPI/XMS/raw init
; In
;   ax	  = real mode segment
;   bx	  = Selector
;   ecx	  = limit
;   dx	  = access rights/type
;   pdseg = segment to privat data
; Out
;   bx+8, trashes eax
vxr_initsetdsc:
	push dx
	movzx eax,ax				; EAX = base of segment
	shl eax,4
	mov [pdseg:bx + Pdata.gdt.limit0_15],cx ; limit = CX
	mov [pdseg:bx + Pdata.gdt.base0_15],ax
	shr eax,16
	mov [pdseg:bx + Pdata.gdt.base16_23],al
	mov [pdseg:bx + Pdata.gdt.base24_31],ah
	mov eax,ecx
	shr eax,8
	and dh,0F0h				; Clear limit16_19
	or dh,ah				; Set it
	mov [pdseg:bx + Pdata.gdt.types],dx	; access rights = DX
	add bx,8
	pop dx
	ret
; [fold]  ]

;═════════════════════════════════════════════════════════════════════════════
; [fold]  [
x_init: ; XMS protected mode init
	push es				; preserve ES, INT 2Fh destroys it

	mov ax,4310h			; get XMS driver address
	int 2fh

	mov [xms_call.offset],bx	; store XMS driver address
	mov [xms_call.sel],es

	pop es				; restore ES (buffer segment)

	call x_seta20			; enable A20

	mov bx,4			; error code 0004h in case of error
	cmp ax,1			; error enabling A20?
	jc init_done			; if yes, exit with error 0004h

	mov [SetA20],off x_seta20	; procedure to enable a20 under XMS
	mov [ResetA20],off x_reseta20	; resets a20 to startup state

	mov si,off int31mxrouttbl	; set XMS memory allocation functions
; [fold]  ]

;─────────────────────────────────────────────────────────────────────────────
; [fold]  [
xr_init: ; XMS/raw common init tail
	push es				; preserve ES

	push ds				; ES = DS for table copy
	pop es

	mov di,off int31mrouttbl
	mov cx,5
	memcpyw ds,si,es,di,cx		; copy memory allocation function

	mov eax,[dword ptr int31physxrtbl] ; copy physical mapping functions
	mov [dword ptr int31phystbl],eax

	pop es				; restore ES (buffer segment)

	mov [rmtopmswrout],off xr_rmtopmsw ; set XMS/raw mode switch addresses
	mov [pmtormswrout],off xr_pmtormsw

	mov [SetMasterPIC],off xr_setpic ; set XMS/raw PIC program address
	mov [ResetMasterPIC],off xr_resetpic

	mov [picmasterold],8		; assuming standard PIC mappings
	mov [picslaveold],70h

	mov ebx,cr0			; get CR0
	mov [oldCR0L],bl		; save old state
	sar bl,1
	and bl,3
	mov [newEM_MP],bl

	jmp vxr_init			; go to VCPI/XMS/raw continue init
; [fold]  ]

;═════════════════════════════════════════════════════════════════════════════
; [fold]  [
r_init: ; raw protected mode init
	mov [SetA20],off r_seta20	; procedure to enable a20 under raw
	mov [ResetA20],off r_reseta20	; resets a20 to startup state

	mov ah,88h			; how much extended memory free
	int 15h

	mov si,off int31mnrouttbl	; SI -> no memory allocation functions
	or ax,ax			; if none, done with raw init
	jz xr_init

	movzx eax,ax			; convert AX K to ptr to top of mem
	shl eax,10
	add eax,100000h
	mov [rawextmemtop],eax

	call r_seta20			; enable A20
	mov bx,4
	jc init_done

	push es				; preserve ES (buffer segment)

	xor cx,cx			; ES -> 0 (interrupt vector table)
	mov es,cx
	les bx,[dword ptr es:4*19h]	; ES:BX -> int vector table

	mov eax,100000h			; initial free extended memory base
	cmp [dword ptr es:bx+12h],'SIDV'; VDISK memory allocation?
	jne short @@r_initf0		; if present, get base of free mem

	mov eax,[dword ptr es:bx+2ch]	; get first free byte of extended mem
	add eax,0fh			; align on paragraph
	and eax,0fffff0h		; address is only 24bit

@@r_initf0:
	dec cx				; ES -> 0ffffh for ext mem addressing
	mov es,cx

	cmp [dword ptr es:13h],'SIDV'   ; VDISK memory allocation?
	jne short @@r_initf1		; if present, get base of free mem

	movzx ebx,[word ptr es:2eh]	; get first free K of extended memory
	shl ebx,10			; adjust K to bytes

	cmp eax,ebx			; pick larger of 2 addresses
	ja short @@r_initf1

	mov eax,ebx

@@r_initf1:
	pop es				; restore ES (buffer segment)

	mov si,off int31mnrouttbl	; SI -> no memory allocation functions
	cmp eax,[rawextmemtop]		; any valid free extended memory
	jae xr_init			; if none, done with raw init

	mov [rawextmembase],eax
	mov si,off int31mrrouttbl	; set raw memory allocation functions

	jmp xr_init			; go to XMS/raw continue init
; [fold]  ]

; [fold]  ]

;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
; PROTECTED MODE KERNEL CODE
;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
; [fold]  [

;═════════════════════════════════════════════════════════════════════════════
; [fold]  [
; VCPI real to protected switch
v_rmtopmsw:
	pushf				; store FLAGS
	cli
	push DGROUP
	pop ds
	pop [temp0.w0]			; move FLAGS from stack to temp
	mov [temp0.w1],ax		; store AX (protected mode DS)
	mov [temp1.w0],si		; store SI (protected mode CS)
	mov esi,[lvcpistruc]		; ESI = linear addx of VCPI structure
	mov ax,0de0ch			; VCPI switch to protected mode
	int 67h
v_rmtopmswpm:
	mov ax,SELDS
	mov ds,ax
	mov es,cx			; load protected mode es
	xor eax,eax
	mov fs,ax			; load protected mode FS with NULL
	mov gs,ax			; load protected mode GS with NULL
	mov ax,[temp0.w0]
	and ax,0bfffh			; clear NT flag
	push eax
	popfd
	mov ss,dx			; load protected mode SS:ESP
	mov esp,ebx
	push [temp1.d0]			; push cs
	push edi			; push eip
	mov ds,[temp0.w1]		; caller ds
	DB 66h
	retf				; go to target addx in protected mode
; [fold]  ]

;═════════════════════════════════════════════════════════════════════════════
; [fold]  [
; VCPI protected to real switch
v_pmtormsw:
	pushf				; store FLAGS
	cli
	push SELDS
	pop ds
	pop [temp0.w0]			; store flags
	mov [temp1.w0],ax		; save AX (real mode DS)
	lss esp,[vcpiswitchstack.ptr]	; set up switchstack
	sub ax,ax
	push eax			; real mode GS
	push eax			; real mode FS
	push [temp1.d0]			; real mode DS
	push ecx			; real mode ES
	push edx			; real mode SS
	push 0 bx			; real mode ESP
	push eax			; reserved
	DB 66h
	push _TEXT			; push dword _TEXT
	DW 0				; real mode entry CS
	mov ax,_TEXT
	DB 66h
	push off @@v_pmtormswf0		; push dword value
	DW 0				; real mode entry IP
	copy ds,SELCORE,ax		; linear mem selector
	copy es,SELDS,ax		; data selector
	mov ax,0de0ch			; VCPI switch to real mode (V86)
	call [es:vcpi_service.ptr]
@@v_pmtormswf0:
	push [temp0.w0]			; store old FLAGS
	push si				; store target CS in real mode
	push di				; store target IP in real mode
	iret				; go to target addx in real mode
; [fold]  ]

;═════════════════════════════════════════════════════════════════════════════
; [fold]  [
; XMS/raw real to protected switch
xr_rmtopmsw:
	push DGROUP
	pop ds
	pushfd				; store EFLAGS
	cli
	push [vcpi_seltss]
	push si				; save SI
	lidt [fword ptr idtlimit]	; load protected mode IDT
	lgdt [fword ptr gdtlimit]	; load protected mode GDT
	mov si,[vcpi_seltss]
	mov ds,[privatedataseg]
	mov [ds:si + Pdata.gdt.types],0089h ; Clear busy bit
	mov esi,cr0			; switch to protected mode
	or si,1
	mov cr0,esi
	db 0eah				; JMP FAR PTR SELCODE:$+4
	dw $+4,SELCS			;  (clear prefetch que)
	pop si
	mov ds,ax			; load protected mode DS
	mov es,cx			; load protected mode ES
	xor ax,ax
	mov fs,ax			; load protected mode FS with NULL
	mov gs,ax			; load protected mode GS with NULL
	lldt ax				; load protected mode LDT
	pop ax				; set up task register
	ltr ax				; load protected mode TR
	clts				; free coprocessor
	pop eax
	mov ss,dx			; load protected mode SS:ESP
	mov esp,ebx
	and ah,0bfh			; set NT=0 in old EFLAGS
	push eax			; set current EFLAGS
	popfd
	push esi			; store protected mode target CS
	push edi			; store protected mode target EIP
	DB 66h
	retf				; go to targed addx in protected mode
; [fold]  ]

;═════════════════════════════════════════════════════════════════════════════
; [fold]  [
; XMS/raw protected to real switch
xr_pmtormsw:
	push SELDS
	pop ds
	pushf				; store FLAGS
	cli
	push ax				; store AX (real mode DS)
	pop [temp0.w0]			; move real mode DS from stack to temp
	pop [temp0.w1]			; move FLAGS from stack to temp
	lidt [fword ptr rmidtlimit]	; load real mode IDT
	mov ax,SELREAL			; load descriptors with real mode seg
	mov ds,ax			;  attributes
	mov es,ax
	mov fs,ax
	mov gs,ax
	mov ss,ax			; load descriptor with real mode attr
	movzx esp,bx			; load real mode SP, high word 0
	mov eax,cr0			; switch to real mode
	and al,0feh
	mov cr0,eax
	db 0eah				; JMP FAR PTR PMODE_TEXT:$+4
	dw $+4,_TEXT			;  (clear prefetch que)
	mov ss,dx			; load real mode SS
	mov es,cx			; load real mode ES
	xor ax,ax
	mov fs,ax			; load real mode FS with NULL
	mov gs,ax			; load real mode GS with NULL
	copy ds,DGROUP,ax		; datasegment
	push [temp0.w1]			; store old FLAGS
	mov ds,[temp0.w0]		; load real mode DS
	push si				; store real mode target CS
	push di				; store real mode target IP
	iret				; go to target addx in real mode
; [fold]  ]

;═════════════════════════════════════════════════════════════════════════════
; [fold]  [
; General interrupt handler
common_int_handler:
count = 0
	REPT 256
	DB 68h
	DW count*4			; push rm IVT offet
	DB 0E9h
	DW @@int_handler - 2 - $	; jump @@int_handler
count = count +1
	ENDM
INT_HANDLER_SIZE = ($ - common_int_handler) / 256
@@int_handler:
	push 0 ds es fs gs
	pushad				; store general registers
	pushf
	and [WORD PTR esp],NOT 300h	; Clear IF and TF
	popf

	copy ds,SELDS,ax
	copy pdseg,SELPRIVAT,ax
	mov dx,[rmstacktop]
	movzx ebx,dx
	sub dx,RMONESTACKLEN
	cmp dx,[rmstackbase]		; exceeded real mode stack space?
	jb critical_error		; if yes, critical error (hang)
	mov [rmstacktop],dx		; update for possible reentrancy

	movzx esi,[word ptr esp+42]	; get intvector offset
	copy coreseg,SELCORE,ax
	sub bx,50			; space on real mode stack
	mov ax,[esp+50]			; get flags
	and ax,NOT 300h			; Clear IF and TF (like in real mode)
	mov [pdseg:bx+32],ax		; put FLAGS
	mov [pdseg:bx+42],ax
	copy [pdseg:bx+34],[coreseg:esi],eax
	mov [word ptr pdseg:bx+38],off @@inte
	mov [word ptr pdseg:bx+40],_TEXT
	mov [pdseg:bx+44],esp		; protected mode stack
	mov [pdseg:bx+48],ss		; to return to protected mode

	dmemcpyw ss,esp,pdseg,ebx,32	; copy to real mode stack

	mov dx,[privatedataseg]
	mov si,_TEXT
	mov di,off @@intrirqf0
	jmp [pmtormswrout]		; switch to real mode

@@intrirqf0:
	popad				; get general registers
	popf
	retf				; jump into rm int
@@inte: pushf
	pushad				; store general registers
	copy ds,DGROUP,ax
	mov bp,sp
	mov dx,[bp+38]		; protected mode SS
	mov ebx,[bp+34]		; protected mode ESP
	mov si,SELCS		; protected mode CS
	mov di,off @@intrirqf1
	movzx edi,di		; protected mode EIP
	mov cx,SELDS		; protected mode ES
	mov ax,cx		; protected mode DS
	jmp [rmtopmswrout]	; jump back to protected mode

@@intrirqf1:
	copy pdseg,SELPRIVAT,ax

	add [rmstacktop],RMONESTACKLEN		; adjust realmode stack
	movzx ebx,[rmstacktop]
	sub bx,40
	and [WORD PTR esp+50],200h		; Change all except IF
	mov ax,[pdseg:bx+32]			; get real mode flags
	and ax,NOT 200h				; Clear IF
	or [esp+50],ax				; set return flags
	dmemcpyw pdseg,ebx,ss,esp,32		; copy general registers

	popad
	pop gs fs es ds				; restore all registers
	add esp,4				; kill interrupt #
	iretd
; [fold]  ]

;═════════════════════════════════════════════════════════════════════════════
; [fold]  [
; General hardware IRQ handler (faster than above)
common_hardirq_handler:
	REPT 16
	push large 2000h		; address of rm int vect
	jmp short @@hard_handler
	ENDM
HARDIRQ_HANDLER_SIZE = ($ - common_hardirq_handler) / 16
@@hard_handler:
	push ds es fs gs
	pushad				; store general registers

	copy ds,SELDS,ax
	copy pdseg,SELPRIVAT,ax
	mov dx,[rmstacktop]
	movzx ebx,dx
	sub dx,RMONESTACKLEN
	cmp dx,[rmstackbase]		; exceeded real mode stack space?
	jb critical_error		; if yes, critical error (hang)
	mov [rmstacktop],dx		; update for possible reentrancy

	mov bp,[esp+40]			; get intvector
	sub bx,6			; space on real mode stack
	mov [pdseg:bx+2],esp		; protected mode stack
	mov [pdseg:bx+0],ss		; to return to protected mode
	mov dx,[privatedataseg]		; set dx to real mode ss

	sub cx,cx			; so 'es' points to IVT
	mov ax,DGROUP
	mov si,_TEXT
	mov di,off @@intrirqf0
	jmp [pmtormswrout]		; switch to real mode

@@intrirqf0:
	pushf				; pushf for iret return
	call [DWORD PTR es:bp]		; jump into rm int handler
; regs won't change, as this is an hardware int
@@entr: pop dx				; protected mode SS
	pop ebx				; protected mode ESP
	mov si,SELCS			; protected mode CS
	DB 66h
	mov di,off @@intrirqf1		; mov edi,off @@intrirqf1
	DW 0				; protected mode EIP
	mov cx,SELDS			; protected mode ES
	mov ax,cx			; protected mode DS
	jmp [rmtopmswrout]		; jump back to protected mode

@@intrirqf1:
	add [rmstacktop],RMONESTACKLEN	; adjust realmode stack
	popad
	pop gs fs es ds			; restore all registers
	add esp,4			; kill interrupt #
	iretd
; [fold]  ]

;═════════════════════════════════════════════════════════════════════════════
; [fold]  [
; handler for NMI interrupt.
NMI_exception:
	pushfd
	push large cs
	push 0
	call common_int_handler + 2*INT_HANDLER_SIZE
	DB 66h
	retf
; [fold]  ]

;═════════════════════════════════════════════════════════════════════════════
; [fold]  [
; special handler for emulating the FPU.
IF FAST_FPU_EMU EQ 1
EX_SIZE EQU 100		; bytes to be made available on the user stack
			; djgpp v2 requires 92 bytes (exception structure size)
			; in the release version, while beta4 needs 0
FPU_emulation:
	sub esp,EX_SIZE+6	; leave required bytes plus slot for ds and edi
	push ss
	push esp
	add [DWORD PTR esp],EX_SIZE+20	; esp before exception
	push [DWORD PTR esp+EX_SIZE+20] ; move eflags
	push [DWORD PTR esp+EX_SIZE+20] ; move cs
	push [DWORD PTR esp+EX_SIZE+20] ; move eip
	push eax			; dummy error code
	push large cs			; return segment
	db 66h
	push off FPU_emulation_end	; return address
	dw 0
	db 66h, 0EAh			; jmp far to 32 bit offset
FPU_emu_vector Farptr32 <0,0,0>

FPU_emulation_end:
	add esp,4			; kill error code
	mov [esp+18],edi		; store edi and ds
	mov [esp+22],ds
	sub [dword ptr esp+12],12	; make room for iret frame
	lds edi,[esp+12]
	pop [DWORD PTR ds:edi]		; get EIP
	pop [DWORD PTR ds:edi+4]	; get CS
	pop [DWORD PTR ds:edi+8]	; get EFLAGS
	lds edi,[esp+6]			; restore ds and edi
	lss esp,[esp]			; switch to requested stack
	iretd
ENDIF
; [fold]  ]

;═════════════════════════════════════════════════════════════════════════════
; [fold]  [
; Exception handler code
; We get here through a task switch. ds is SELPRIVAT, es is SELDS, bx is
; exception number, al is bl & 0f8h, ah is 1<<(bl&7) (both used for user
; int identification), dx is Pdata.exceptionstack, ecx is 0.
; ints are disabled, for details, see vxr_init

	ASSUME ds:NOTHING
common_exception_with_error_handler:
	push 2
	popf				; Clear NT flag, disable ints

	cmp sp,dx			; Error code on stack?
	jz short do_user_int		; yes, its an exception
	pop ecx				; pop error code
	jmp do_exception

common_exception_without_error_handler:
	push 2
	popf				; Clear NT flag, disable ints

	mov dx,020h			; port for master pic
	cmp al,[es:picmasternew]
	je short @@test_hw_int
	mov dx,0a0h			; port for slave pic
	cmp al,[es:picslavenew]
	jne short @@test_sw_int

; This code assumes that the appropriate hardware int is enabled. I may add
; code to fix that, but the probability of this causing trouble is extremely
; low and only possible in strange configurations.
@@test_hw_int:
	mov al,0bh			; read ISR
	out dx,al			; dx is pic port
	jmp short $+2
	in al,dx
	cmp al,ah			; mask is already in ah
	je short do_user_int		; it is an exception

; There is a probability of wrong identification here if an exception occurs
; after an instruction that ends with the sequence 0cdxxh where xx is the
; exception number.
@@test_sw_int:
	mov edi,[ds:Pdata.vcpitss.eip]
	cmp edi,2			; is it the beginning of the segment
	jb do_exception			;  so that int instruction doesn't fit?
	mov ax,[ds:Pdata.vcpitss.cs]
	mov fs,ax
	mov ax,[word ptr fs:edi-2]
	cmp al,0cdh			; is it an int instruction?
	jne short do_exception
	cmp ah,bl			; is the int number correct?
	jne short do_exception

; Here we serve a hardware or software int if it uses the same vector with an
; exception. The registers are restored and we switch to the user task.
do_user_int:
	mov di,[es:vcpi_seltss]
	mov [ds:di + Pdata.gdt.types],0089h
	ltr di				; now we are in user task
	shl bx,3
	mov [ds:bx + SELTASKS + Pdata.gdt.types],0089h ; clear busy bit

	mov ss,[ds:Pdata.vcpitss.ss]	; CPU disables ints for next ins.
	mov esp,[ds:Pdata.vcpitss.esp]	; switch to user stack

; interrupt frame
	push [ds:Pdata.vcpitss.eflags]
	push [DWORD PTR ds:Pdata.vcpitss.cs]
	push [ds:Pdata.vcpitss.eip]
; handler address
	push [DWORD PTR ds:bx + Pdata.lowint.sel]
	push [ds:bx + Pdata.lowint.offset]

	mov eax,[ds:Pdata.vcpitss.eax]
	mov ecx,[ds:Pdata.vcpitss.ecx]
	mov edx,[ds:Pdata.vcpitss.edx]
	mov ebx,[ds:Pdata.vcpitss.ebx]
	mov ebp,[ds:Pdata.vcpitss.ebp]
	mov esi,[ds:Pdata.vcpitss.esi]
	mov edi,[ds:Pdata.vcpitss.edi]
	mov es,[ds:Pdata.vcpitss.es]
	mov fs,[ds:Pdata.vcpitss.fs]
	mov gs,[ds:Pdata.vcpitss.gs]
	mov ds,[ds:Pdata.vcpitss.ds]

	clts
	DB 66h		; a 32 far return
	retf

; Here we serve an exception. Error code is in ecx.
do_exception:
	shl bx,3
	mov di,[es:vcpi_seltss]
	mov [ds:di + Pdata.gdt.types],0089h
	ltr di				; now we are in user task
	mov [ds:bx + SELTASKS + Pdata.gdt.types],0089h ; clear busy bit

	push SELCORE			; selector for ss
	mov eax,[es:pmstacktop]
	push eax			; top of pmstack
	sub eax,PMONESTACKLEN		; set EAX to next stack location
	mov [es:pmstacktop],eax		; update ptr for possible reenterancy

	cmp eax,[es:pmstackbase]	; exceeded protected mode stack space?
	jbe critical_error		; if yes, critical error (hang)

	lss esp,[esp]
	push [DWORD PTR ds:Pdata.vcpitss.ss]  ; build up stack like DPMI says
	push [ds:Pdata.vcpitss.esp]
	push [ds:Pdata.vcpitss.eflags]
	push [DWORD PTR ds:Pdata.vcpitss.cs]
	push [ds:Pdata.vcpitss.eip]
	push ecx			; store error code
	push large SELCS
	DB 66h
	push off @@back			; push return address
	DW 0
	push [DWORD PTR ds:bx + Pdata.exceptions.sel] ; push address of
	push [ds:bx + Pdata.exceptions.offset]	; handler

	mov eax,[ds:Pdata.vcpitss.eax]
	mov ecx,[ds:Pdata.vcpitss.ecx]
	mov edx,[ds:Pdata.vcpitss.edx]
	mov ebx,[ds:Pdata.vcpitss.ebx]
	mov ebp,[ds:Pdata.vcpitss.ebp]
	mov esi,[ds:Pdata.vcpitss.esi]
	mov edi,[ds:Pdata.vcpitss.edi]
	mov es,[ds:Pdata.vcpitss.es]
	mov fs,[ds:Pdata.vcpitss.fs]
	mov gs,[ds:Pdata.vcpitss.gs]
	mov ds,[ds:Pdata.vcpitss.ds]

	DB 66h		; a 32 far return
	retf

@@back:			; if come here, jump back to caller
	sub [DWORD PTR esp+16],18	; return interrupt frame in user stack
	push edi
	push ds
	mov edi,[esp+22]		; user esp
	mov ds,[esp+26]			; user ss
	pop [WORD PTR ds:edi]		; task's DS
	pop [DWORD PTR ds:edi+2]	; task's EDI
	add esp,4			; Kill error code
	pop [DWORD PTR ds:edi+6]	; return eip to user stack
	pop [DWORD PTR ds:edi+10]	; return cs to user stack
	pop [DWORD PTR ds:edi+14]	; return eflags to user stack

	lss esp,[esp]			; switch to user stack
	push SELDS			; restore the pmstack from the user
	pop ds				; stack in case ints are enabled
	add [ds:pmstacktop],PMONESTACKLEN
	pop ds
	pop edi
	clts
	iretd

	ASSUME ds:DGROUP
; [fold]  ]

;═════════════════════════════════════════════════════════════════════════════
; [fold]  [
; callback handler
; The callbacks are not 100% DPMI compliant. If the callback code doesn't
; update the cs:ip fields of the call structure the callback should be
; entered immediately again. We will hang the computer instead, but this
; practice shouldn't be a real problem.
callback:
	cli
	cld
	mov ax,sp
	add ax,10+32+2+8		; sp when called
	push ss ax			; store for later use
	copy ds,DGROUP,ax

	mov ebx,[pmstacktop]
	sub ebx,PMONESTACKLEN		; set EBX to next stack location
	mov [pmstacktop],ebx		; update ptr for possible reenterancy

	cmp ebx,[pmstackbase]		; exceeded protected mode stack space?
	jbe critical_error		; if yes, critical error (hang)

	add ebx,PMONESTACKLEN		; EBX = top of pmstack
	xor eax,eax			; EAX = base address of SS
	mov ax,ss
	shl eax,4
	movzx ebp,sp			; EBP = current linear SS:SP
	add ebp,eax

	mov ax,SELCORE			; DS selector for protected mode
	mov dx,ax			; SS selector = DS selector
	mov si,SELCS			; target protected mode CS:EIP
	sub edi,edi
	mov di,offset @@callbackf0

	jmp [rmtopmswrout]		; go to protected mode

@@callbackf0:
	mov edi,[ebp+4]			; get register structure
	lea esi,[ebp+4+10]		; pointer to saved registers
	dmemcpyw ds,esi,es,edi,32+2+8	; copy register to struct
	sub edi,32+2+8			; correct edi
	mov [es:edi+Dpmi_regs.ip],off critical_error
	mov [es:edi+Dpmi_regs.cs],_TEXT ; in case cs:ip are not updated...
	mov eax,[ebp]			; get original ss:sp
	mov [DWORD PTR es:edi+Dpmi_regs.sp],eax

	pushfd				; push flags for IRETD from callback
	db 66h				; push 32bit CS for IRETD
	push cs
	dw 6866h,@@callbackf1,0		; push 32bit EIP for IRETD

	push 0				; pad stack
	push [WORD PTR ebp+4+4+4]	; get callback cs
	push [DWORD PTR ebp+4+4]	; get callback eip

	db 66h				; 32bit RETF to callback
	retf

@@callbackf1:
	push es				; DS:ESI = register structure
	pop ds
	mov esi,edi
	copy es,SELCORE,ax		; ES -> 0 (beginning of memory)
	copy fs,SELDS,ax		; selector for Datasegment
	add [fs:pmstacktop],PMONESTACKLEN ; correct stack

	ptr2lin [ds:esi+Dpmi_regs.ss],[ds:esi+Dpmi_regs.sp],di,ax,edi,eax
	sub edi,32+2+8+4		; Space for regs on rm stack + jump
	dmemcpyw ds,esi,es,edi,32+2+8+4 ; copy regs + jump
	sub esi,32+2+8+4		; correct ESI

	mov bx,[ds:esi+Dpmi_regs.sp]
	sub bx,32+2+8+4			; BX = real mode SP
	mov dx,[ds:esi+Dpmi_regs.ss]	; DX = real mode SS
	mov si,_TEXT			; SI = real mode CS
	mov di,off @@callbackf2		; DI = real mode IP
	jmp [fs:pmtormswrout]		; switch to real mode

@@callbackf2:
	mov bp,sp
	mov eax,[bp+Dpmi_regs.res]	; get old highword of esp
	mov ax,sp
	mov esp,eax			; restore old esp

	popad				; get callback return general regs
	popf				; get callback flags
	pop es ds fs gs			; get callback return segment regs
	retf				; go to callback return CS:IP
; [fold]  ]

;═════════════════════════════════════════════════════════════════════════════
; [fold]  [
; real mode INT 15h handler
int15:	push ds
	push DGROUP
	pop ds
	cmp ah,88h			; function 88h?
	je short @@int15f0		; if yes, need to process

	pushf
	call [oldint15vector]
	push bp
	pushf
	mov bp,sp
	pop [WORD PTR bp+10]		; store flags
	pop bp ds
	iret

@@int15f0:
	pushf				; call old int 15h handler
	call [oldint15vector]

	sub ax,[rawextmemused]		; adjust AX by extended memory used

	push bp				; clear carry flag on stack for IRET
	mov bp,sp
	and [byte ptr bp+8],0feh
	pop bp

	pop ds
	iret				; return with new AX extended memory
; [fold]  ]

;═════════════════════════════════════════════════════════════════════════════
; [fold]  [
; Int 21 (shelling out)
dos21rm:
	cmp ax,4B00h			; dos exec call
	je short @@doit
	cmp ax,4B03h
	je short @@doit
	cmp ax,4B05h
	je short @@doit
	jmp short @@exit

@@doit: push ax gs fs ds es		; store registers on stack
	pushad
	copy ds,DGROUP,ax
	test [from_us],-1
	jnz short @@nous		; int not from us

	inc [from_us]			; for reentrancy
	call [ResetMasterPIC]		; reset to default
	call [ResetA20]
	mov bp,sp
	mov ax,[bp+46]
	mov [bp+40],ax
	popad
	pop es ds fs gs
	call [cs:oldint21vector.ptr]	; call old
	push gs fs ds es		; save return
	pushad
	pushf
	copy ds,DGROUP,ax
	call [SetMasterPIC]
	call [SetA20]
	dec [from_us]
	pop ax				; get flags
	mov bp,sp
	mov [bp+44],ax			; store for iret
	popad
	pop es ds fs gs
	iret				; return to caller

@@nous: popad
	pop es ds fs gs ax
@@exit: DB 0EAh				; jmp far ?:?
oldint21vector Farptr16 ?
; [fold]  ]

;═════════════════════════════════════════════════════════════════════════════
; [fold]  [
; Int 23 (ctrl-c)
int23pm: ; default pm handler
	iretd
int23rm: ; callbacks to protected mode
	push gs fs ds es
	pushad
	cld
	cli
	copy ds,DGROUP,ax
	mov bp,ss			; save real mode stack for later
	shl ebp,16
	mov bp,sp

	xor eax,eax			; EAX = base address of SS
	mov ax,ss
	shl eax,4
	movzx ebx,sp			; EBX = stack in prot. mode
	add ebx,eax

	mov ax,SELDS			; DS selector for protected mode
	mov cx,ax			; ES selector for protected mode
	mov dx,SELCORE			; SS selector
	mov si,SELCS			; target protected mode CS:EIP
	DB 66h
	mov di,off @@callbackf0		; mov edi,off @@callbackf0
	DW 0
	jmp [rmtopmswrout]		; go to protected mode

@@callbackf0:
	push ebp
	int 23h				; call pm ctrl-c handler
	pop bx				; BX = real mode SP
	pop dx				; DX = real mode SS
	mov si,_TEXT			; SI = real mode CS
	mov di,off @@callbackf2		; DI = real mode IP
	jmp [pmtormswrout]		; switch to real mode

@@callbackf2:
	mov bp,sp
	mov eax,[bp + Dpmi_regs.res]	; get old highword of esp
	mov ax,sp
	mov esp,eax			; restore old esp

	popad				; get callback return general regs
	pop es ds fs gs			; get callback return segment regs
	iret
; [fold]  ]

;═════════════════════════════════════════════════════════════════════════════
; [fold]  [
; Int 24 (critical error)
int24pm: ; default pm handler
	mov al,3	; fail is default action. I hope it will never
	iretd		; happen that dos aborts on return
int24rm: ; callbacks to protected mode
	push gs fs ds es
	pushad
	cld
	cli
	copy ds,DGROUP,ax
	mov bp,ss			; save real mode stack for later
	shl ebp,16
	mov bp,sp

	xor eax,eax			; EAX = base address of SS
	mov ax,ss
	shl eax,4
	movzx ebx,sp			; EBX = stack in prot. mode
	add ebx,eax

	mov ax,SELDS			; DS selector for protected mode
	mov cx,ax			; ES selector for protected mode
	mov dx,SELCORE			; SS selector
	mov si,SELCS			; target protected mode CS:EIP
	DB 66h
	mov di,off @@callbackf0		; mov edi,off @@callbackf0
	DW 0
	jmp [rmtopmswrout]		; go to protected mode

@@callbackf0:
	movzx edx,[esp + Dpmi_regs.bp]
	shl edx,4
	mov ecx,edx
	shr ecx,16
	mov bx,SELINT24
	mov ax,0007h
	int 31h				; set base of INT 24 selector

	mov ax,[esp + Dpmi_regs.ax]
	mov si,[esp + Dpmi_regs.si]
	mov di,[esp + Dpmi_regs.di]
	push ebp
	mov bp,SELINT24
	int 24h				; call pm critical error handler
	pop bx				; BX = real mode SP
	pop dx				; DX = real mode SS
	mov [esp + Dpmi_regs.al],al	; store return value
	mov si,_TEXT			; SI = real mode CS
	mov di,off @@callbackf2		; DI = real mode IP
	jmp [pmtormswrout]		; switch to real mode

@@callbackf2:
	mov bp,sp
	mov eax,[bp + Dpmi_regs.res]	; get old highword of esp
	mov ax,sp
	mov esp,eax			; restore old esp

	popad				; get callback return general regs
	pop es ds fs gs			; get callback return segment regs
	iret
; [fold]  ]

;═════════════════════════════════════════════════════════════════════════════
; [fold]  [
; internal int 2f, ax=1680h, ax=1686h or ax=168ah
int2fpm:
IF PMODEDJ_EXT EQ 1
	cmp ax,168ah
	je short @@int2f168a
ENDIF
	cmp ax,1680h
	je short @@int2f1680
	cmp ax,1686h
	jne common_int_handler+2fh*INT_HANDLER_SIZE ; if no, go to INT 2fh
@@int2f1686: ; Get CPU Mode
	xor ax,ax		; we are runniing in protected mode
	iretd
@@int2f1680: ; Release Current Virtual Machine's Time Slice
	pushfd
	push large cs
	push 0
	call common_int_handler+2fh*INT_HANDLER_SIZE ; pass down to real mode
	test al,al		; is it supported?
	je short @@int2f1680ok	; yes, we are done.
	sti			; give processor a chance to cool down :)
	hlt			; wait for int.
	xor al,al
@@int2f1680ok:
	iretd
IF PMODEDJ_EXT EQ 1
@@int2f168a: ; Get Vendor-Specific API Entry Point
	mov ax,0a00h
	int 31h			; try the extension
	mov ax,1600h		; prepare ax for succesful exit
	iretd
ENDIF
; [fold]  ]

;═════════════════════════════════════════════════════════════════════════════
; [fold]  [
; real mode INT 2F handler
int2frm: iret
; [fold]  ]

;═════════════════════════════════════════════════════════════════════════════
; [fold]  [
; VCPI/XMS/raw save/restore status (called from real mode)
vxr_saverestorerm:
	retf				; no save/restore needed, 16bit RETF
; [fold]  ]

;═════════════════════════════════════════════════════════════════════════════
; [fold]  [
; VCPI/XMS/raw save/restore status (called from protected mode)
vxr_saverestorepm:
	db 66h,0cbh			; no save/restore needed, 32bit RETF
; [fold]  ]

;═════════════════════════════════════════════════════════════════════════════
; [fold]  [
; critical error routine
critical_error:				; some unrecoverable error
	cli				; make sure we are not interrupted
	in al,61h			; beep
	or al,3
	out 61h,al
	jmp $				; now hang
; [fold]  ]

;═════════════════════════════════════════════════════════════════════════════
; [fold]  [
; PIC reprogramming

;─────────────────────────────────────────────────────────────────────────────
; [fold]  [
; Resets the master PIC base
v_resetpic:
	pushf
	cli
	call pic_repr			; reprogramming allowed?
	movzx bx,[picmasterold]
	movzx cx,[picslaveold]
	mov ax,0DE0Bh			; VCPI set pic mappings
	int 67h
	jmp short xr_ddd
xr_resetpic:
	pushf
	cli
	call pic_repr			; reprogramming allowed?
xr_ddd: mov cl,[picmasterold]		; set master PIC base to default
	call Change_PIC
	movzx di,[picmasternew]		; Clear entries in the IVT
	shl di,2
	setmemw 0,di,8*4
	popf
	ret
; [fold]  ]

;─────────────────────────────────────────────────────────────────────────────
; [fold]  [
; Sets the master PIC base
v_setpic:
	pushf
	cli
	call pic_repr			; reprogramming allowed?
	movzx bx,[picmasternew]
	movzx cx,[picslavenew]
	mov ax,0DE0Bh			; VCPI set pic mappings
	int 67h
	jmp short xr_eee
xr_setpic:
	pushf
	cli
	call pic_repr			; reprogramming allowed?
xr_eee: mov dx,off pic_ints
	mov cx,8
	movzx bx,[picmasternew]
	shl bx,2
	sub ax,ax
	mov es,ax
@@ww1:	mov [es:bx+0],dx		; set offset
	mov [es:bx+2],cs		; set segment
	add bx,4			; next entry
	add dx,PIC_INT_SIZE		; next int wrapper
	dec cx
	jnz @@ww1
	mov cl,[picmasternew]
	call Change_PIC
	popf
	ret
; [fold]  ]

;-----------------------------------------------------------------------------
; [fold]  [
; Returns to caller, if reprogramming of the master PIC is not allowed.
pic_repr:
	pop bx				; pop return address
	test [reprogrampic],-1
	jz short @@oops
	mov al,[picmasternew]
	cmp al,[picmasterold]		; same?
	jz short @@oops
	jmp bx				; repromming allowed, return
@@oops: popf				; return to caller
	ret
; [fold]  ]

;─────────────────────────────────────────────────────────────────────────────
; [fold]  [
; Reprograms the first PIC
; IN: CL = new master PIC base; Interrupts disabled
Change_PIC:
	in	al,21h
	mov	di,ax			; save old state
	call	@@wt
	mov	al,0FFh
	out	21h,al			; disable master ints

	call	@@wt
	mov	al,11h
	out	20h,al
	call	@@wt
	mov	al,cl			; set new base
	out	21h,al
	call	@@wt
	mov	al,4
	out	21h,al
	call	@@wt
	mov	al,1
	out	21h,al			; init finished here
	call	@@wt
	mov	al,0FFh
	out	21h,al			; disable master ints again

	mov	ax,di
	out	21h,al			; restore master ints
	ret
@@wt:	in	al,80h			; a safe ioport
	ret
; [fold]  ]

;─────────────────────────────────────────────────────────────────────────────
; [fold]  [
; Redirection interrupt handlers.
; Needed so the Compi (hopefully) doesn't crash in real mode when the master
; PIC base is changed.
pic_ints:
count = 8
	REPT 8
	push eax
	push bp ds
	sub bp,bp
	mov ds,bp
	mov eax,[ds:count*4]
	mov bp,sp
	xchg eax,[ss:bp+4]
	pop ds bp
	retf
count = count +1
	ENDM
PIC_INT_SIZE = ($ - pic_ints) / 8
; [fold]  ]

; [fold]  ]

;═════════════════════════════════════════════════════════════════════════════
; [fold]  [
; A20 control

;─────────────────────────────────────────────────────────────────────────────
; [fold]  [
v_seta20: ; Under VCPI A20 is controlled complety by the Memory Manager
v_reseta20:
ret_only:
	ret
; [fold]  ]

;─────────────────────────────────────────────────────────────────────────────
; [fold]  [
x_seta20: ; XMS set A20
	mov ah,7
	call [xms_call.ptr]		; get current state of A20
	mov [olda20],al			; store

	mov ah,3			; enable A20
	call [xms_call.ptr]
	ret
; [fold]  ]

;─────────────────────────────────────────────────────────────────────────────
; [fold]  [
x_reseta20: ; XMS reset A20
	mov ah,[olda20]		; get old state of A20
	xor ah,1
	add ah,3
	call [xms_call.ptr]	; set to old state
	ret
; [fold]  ]

;─────────────────────────────────────────────────────────────────────────────
; [fold]  [
r_seta20: ; raw set A20
	clc				; assume success
	pushf				; save intena flag
	cli
	call enablea20test		; get old state
	mov [olda20],al			; is A20 already enabled?
	jz short @@enablea20done	; if yes, done

	in al,92h			; PS/2 A20 enable
	or al,2
	jmp short $+2
	jmp short $+2
	jmp short $+2
	out 92h,al

	call enablea20test		; is A20 enabled?
	jz short @@enablea20done	; if yes, done

	call enablea20kbwait		; AT A20 enable
	jnz short @@enablea20f0

	mov al,0d1h
	out 64h,al

	call enablea20kbwait
	jnz short @@enablea20f0

	mov al,0dfh
	out 60h,al

	call enablea20kbwait

@@enablea20f0:				; wait for A20 to enable
	mov cx,800h			; do 800h tries

@@enablea20l0:
	call enablea20test		; is A20 enabled?
	jz short @@enablea20done	; if yes, done

	in al,40h			; get current tick counter
	jmp short $+2
	jmp short $+2
	jmp short $+2
	in al,40h
	mov ah,al

@@enablea20l1:				; wait a single tick
	in al,40h
	jmp short $+2
	jmp short $+2
	jmp short $+2
	in al,40h
	cmp al,ah
	je @@enablea20l1

	loop @@enablea20l0		; loop for another try

	mov bp,sp			; error, A20 did not enable
	or [WORD PTR bp],1		; set carry

@@enablea20done:
	popf
	ret
; [fold]  ]

;─────────────────────────────────────────────────────────────────────────────
; [fold]  [
r_reseta20: ; raw reset A20
	ret ; i dont know how to switch off A20
; [fold]  ]

;-----------------------------------------------------------------------------
; [fold]  [
enablea20kbwait: ; wait for safe to write to 8042
	xor cx,cx
@@enablea20kbwaitl0:
	jmp short $+2
	jmp short $+2
	jmp short $+2
	in al,64h			; read 8042 status
	test al,2			; buffer full?
	loopnz @@enablea20kbwaitl0	; if yes, loop
	ret
; [fold]  ]

;-----------------------------------------------------------------------------
; [fold]  [
enablea20test: ; test for enabled A20
	push ds es

	xor ax,ax			; set A20 test segments 0 and 0ffffh
	mov ds,ax
	dec ax
	mov es,ax

	mov al,[ds:0]			; get byte from 0:0
	mov ah,al			; preserve old byte
	not al				; modify byte
	xchg al,[es:10h]		; put modified byte to 0ffffh:10h
	cmp ah,[ds:0]			; set zero if byte at 0:0 not modified
	mov [es:10h],al			; put back old byte at 0ffffh:10h
	sete al
	cbw

	pop es ds
	ret				; return, zero if A20 enabled
; [fold]  ]

; [fold]  ]

;═════════════════════════════════════════════════════════════════════════════
; [fold]  [
; internal int 21, ah=4C
int21:	cmp ah,4ch					; AH = 4Ch?
	jne common_int_handler+21h*INT_HANDLER_SIZE	; if no, go to INT 21h

	push ax				; save return value
	copy ds,SELDS,ax
	copy coreseg,SELCORE,ax
	mov eax,[oldint15vector]	; put back old INT 15h handler
	mov [coreseg:4*15h],eax
	mov eax,[oldint2fvector]	; put back old multiplex
	mov [coreseg:4*2fh],eax
	mov eax,[cs:oldint21vector.ptr] ; put back old INT 21h handler
	mov [coreseg:4*21h],eax

	mov ebx,cr0			; restore EM bit
	and ebx,NOT 4
	mov al,[oldCR0L]
	and al,4
	or bl,al
	mov cr0,ebx

	mov ax,DGROUP
	mov cx,ax
	mov dx,ax
	mov bx,off endstack-2
	pop [WORD PTR bx]		; put returnvalue on real mode stack
	mov si,_TEXT
	mov di,off @@come
	jmp [pmtormswrout]		; switch to real mode

@@come: movzx bx,[pmodetype]
	add bx,bx
	call [exitrouttbl+bx]		; deinit

	mov es,[privatedataseg]
	mov ah,49h
	int 21h				; free private data
	call [pm_exit]			; user defined cleanup routine
	pop ax
	int 21h				; quit

;-----------------------------------------------------------------------------
; [fold]  [
; deinit VCPI
v_exit: call [ResetMasterPIC]		; default PIC mappings
	mov dx,[emspage]		; emspage allocated?
	cmp dx,-1
	jz short @@ok1			; if not, skip
	mov ah,45h
	int 67h				; free emspage
@@ok1:	ret
; [fold]  ]

;-----------------------------------------------------------------------------
; [fold]  [
; deinit XMS/raw
x_exit:
r_exit: call [ResetMasterPIC]		; default PIC mappings
	call [ResetA20]			; default A20 state
	ret
; [fold]  ]

; [fold]  ]

; [fold]  ]

;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
; INT 31h INTERFACE
;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
; [fold]  [

;═════════════════════════════════════════════════════════════════════════════
; [fold]  [
int31: ; protected mode INT 31h handler
	cli
	cld
	push ds es fs gs		; push regs needed
	pushad
	copy coreseg,SELCORE,ax
	copy pdseg,SELPRIVAT,ax
	copy ds,SELDS,ax		; set up selectors
	mov ax,[esp+Dpmi_regs.ax]	; restore ax

	push bx
	mov bx,(INT31FUNCNUM - 1) * 2	; number of functions to check
@@int31l0:
	cmp ax,[int31functbl+bx]	; found function value?
	jne short @@int31l0c

	mov bx,[int31routtbl+bx]	; yes, go to appropriate handler
	xchg bx,[esp]
	ret

@@int31l0c:
	sub bx,2			; no, continue loop
	jnc @@int31l0

	pop bx				; no function found
	jmp short int31fail8001		; error 8001h
; [fold]  ]

;-----------------------------------------------------------------------------
; [fold]  [
; int 31 fail labels
int31fail8024:				; INT 31h return fail with error 8024h
	mov [word ptr esp+28],8024h	; set AX on stack to 8024h for POPAD
	jmp short int31fail

;-----------------------------------------------------------------------------
int31fail8023:				; INT 31h return fail with error 8023h
	mov [word ptr esp+28],8023h	; set AX on stack to 8023h for POPAD
	jmp short int31fail

;-----------------------------------------------------------------------------
int31fail8022:				; INT 31h return fail with error 8022h
	mov [word ptr esp+28],8022h	; set AX on stack to 8022h for POPAD
	jmp short int31fail

;-----------------------------------------------------------------------------
int31fail8021:				; INT 31h return fail with error 8021h
	mov [word ptr esp+28],8021h	; set AX on stack to 8021h for POPAD
	jmp short int31fail

;-----------------------------------------------------------------------------
int31fail8016:				; INT 31h return fail with error 8016h
	mov [word ptr esp+28],8016h	; set AX on stack to 8016h for POPAD
	jmp short int31fail

;-----------------------------------------------------------------------------
int31fail8015:				; INT 31h return fail with error 8015h
	mov [word ptr esp+28],8015h	; set AX on stack to 8015h for POPAD
	jmp short int31fail

;-----------------------------------------------------------------------------
int31fail8013:				; INT 31h return fail with error 8013h
	mov [word ptr esp+28],8013h	; set AX on stack to 8013h for POPAD
	jmp short int31fail

;-----------------------------------------------------------------------------
int31fail8012:				; INT 31h return fail with error 8012h
	mov [word ptr esp+28],8012h	; set AX on stack to 8012h for POPAD
	jmp short int31fail

;-----------------------------------------------------------------------------
int31fail8011:				; INT 31h return fail with error 8011h
	mov [word ptr esp+28],8011h	; set AX on stack to 8011h for POPAD
	jmp short int31fail

;-----------------------------------------------------------------------------
int31fail8010:				; INT 31h return fail with error 8010h
	mov [word ptr esp+28],8010h	; set AX on stack to 8010h for POPAD
	jmp short int31fail

;-----------------------------------------------------------------------------
int31fail8003:				; INT 31h return fail with error 8003h
	mov [word ptr esp+28],8003h	; set AX on stack to 8003h for POPAD
	jmp short int31fail

;-----------------------------------------------------------------------------
int31fail8001:				; INT 31h return fail with error 8001h
	mov [word ptr esp+28],8001h	; set AX on stack to 8001h for POPAD
	jmp short int31fail

;-----------------------------------------------------------------------------
int31failcx:				; INT 31h return fail with CX,AX
	mov [word ptr esp+24],cx	; put CX onto stack for POPAD

;-----------------------------------------------------------------------------
int31failax:				; INT 31h return fail with AX
	mov [word ptr esp+28],ax	; put AX onto stack for POPAD

;-----------------------------------------------------------------------------
int31fail:				; INT 31h return fail, pop all regs
	popad
	pop gs fs es ds

;-----------------------------------------------------------------------------
int31failnopop:				; INT 31h return fail with carry set
	or [byte ptr esp+8],1		; set carry in EFLAGS on stack
	iretd

;-----------------------------------------------------------------------------
int31okedx:				; INT 31h return ok with EDX,CX,AX
	mov [esp+20],edx		; put EDX onto stack for POPAD
	jmp short int31okcx

;-----------------------------------------------------------------------------
int31okdx:				; INT 31h return ok with DX,CX,AX
	mov [esp+20],dx			; put DX onto stack for POPAD
	jmp short int31okcx

;-----------------------------------------------------------------------------
int31oksinoax:				; INT 31h return ok SI,DI,BX,CX
	mov ax,[esp+28]			; get old value of AX for restore

;-----------------------------------------------------------------------------
int31oksi:				; INT 31h return ok SI,DI,BX,CX,AX
	mov [esp+4],si			; put SI onto stack for POPAD
	mov [esp],di			; put DI onto stack for POPAD
	mov [esp+16],bx			; put BX onto stack for POPAD

;-----------------------------------------------------------------------------
int31okcx:				; INT 31h return ok with CX,AX
	mov [esp+24],cx			; put CX onto stack for POPAD

;-----------------------------------------------------------------------------
int31okax:				; INT 31h return ok with AX
	mov [esp+28],ax			; put AX onto stack for POPAD

;-----------------------------------------------------------------------------
int31ok:				; INT 31h return ok, pop all regs
	popad
	pop gs fs es ds

;-----------------------------------------------------------------------------
int31oknopop:				; INT 31h return ok with carry clear
	and [byte ptr esp+8],0feh	; clear carry in EFLAGS on stack
	iretd
; [fold]  ]
; [fold]  ]

;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
; DESCRIPTOR FUNCTIONS
;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
; [fold]  [

;-----------------------------------------------------------------------------
; [fold]  [
int31testsel: ; test for valid selector BX
	pop bp				; pop return address

	movzx ebx,bx
	mov edi,[gdtbase]
	cmp bx,[gdtlimit]		; selector BX out of range?
	ja int31fail8022		; if yes, fail with error 8022h

	test bl,7			; index in LDT? Or wrong RPL?
	jnz int31fail8022		; Only GDT allowed
	and bl,0f8h			; mask offset table index and RPL
	test [byte ptr coreseg:6+ebx+edi],10h; is descriptor used?
	jz int31fail8022		; if descriptor not used, fail 8022h

	jmp bp				; return ok
; [fold]  ]

;-----------------------------------------------------------------------------
; [fold]  [
int31testaccess: ; test access bits in CX
	pop bp				; pop return address

	test ch,20h			; test MUST BE 0 bit in CH
	jnz int31fail8021		; if not 0, error 8021h

	test cl,90h			; test present and MUST BE 1 bits
	jz int31fail8021		; if both 0, error 8021h
	jpo int31fail8021		; if unequal, error 8021h

	test cl,60h			; test DPL
	jnz int31fail8021		; if not 0, error 8021h

	test cl,8			; if code, more tests needed
	jz short @@int31testselok	; if data, skip code tests

	test cl,2			; readable?
	jz int31fail8021
	test cl,4			; non-conform?
	jnz int31fail8021

@@int31testselok:
	jmp bp				; return ok
; [fold]  ]

;─────────────────────────────────────────────────────────────────────────────
; [fold]  [
int310000: ; allocate descriptors
	or cx,cx			; if CX = 0, error 8021h
	jz int31fail8021

	mov edx,[gdtbase]		; get base of GDT
	movzx eax,[gdtlimit]		; EAX = last selector index
	and al,0f8h

	mov bx,cx			; BX = number of selectors to find
@@int310000l0:
	test [byte ptr coreseg:edx+eax+6],10h; is descriptor used?
	jnz short @@int310000l0f0

	dec bx				; found free descriptor, dec counter
	jnz short @@int310000l0f1	; continue if need to find more

	mov ebx,eax			; found all descriptors requested
@@int310000l1:
	mov [dword ptr coreseg:edx+ebx],0	; set entire new descriptor
	mov [dword ptr coreseg:edx+ebx+4],109200h
	add bx,8			; increment selector index
	dec cx				; dec counter of descriptors to mark
	jnz @@int310000l1		; loop if more to mark

	jmp int31okax			; return ok, with AX

@@int310000l0f0:
	mov bx,cx			; reset number of selectors to find

@@int310000l0f1:
	sub ax,8			; dec current selector counter
	cmp ax,8*SYSSELECTORS		; more descriptors to go?
	jae @@int310000l0		; if yes, loop

	jmp int31fail8011		; did not find descriptors
; [fold]  ]

;─────────────────────────────────────────────────────────────────────────────
; [fold]  [
int310001: ; free descriptor
	call int31testsel		; test for valid selector BX
	mov ax,ss
	cmp bx,ax			; free stackselector?
	jz int31fail8022		;   never do this!
	cmp bx,[esp+32+8+4]		; free codeselector?
	jz int31fail8022

	and [byte ptr coreseg:edi+ebx+6],0efh; mark descriptor as free
	and [byte ptr coreseg:edi+ebx+5],07Fh; clear present bit

	mov cx,4			; zero any segregs loaded with BX
	lea ebp,[esp+32]		; EBP -> selectors on stack
@@int310001l0:
	cmp [word ptr ebp],bx		; selector = BX?
	jne short @@int310001l0f0	; if no, continue loop

	mov [word ptr ebp],0		; zero selector on stack

@@int310001l0f0:
	add ebp,2			; increment selector ptr
	loop @@int310001l0		; loop

	jmp int31ok			; return ok
; [fold]  ]

;─────────────────────────────────────────────────────────────────────────────
; [fold]  [
int310002: ; segment to selector
	mov si,off segmentbases		; check, if segment already mapped
@@lp1:	mov ax,[si + 0]			; load selector to ax
	test ax,ax			; selector valid?
	jz short @@skp
	cmp bx,[si + 2]			; compare segment values
	jz int31okax
@@skp:	add si,4
	cmp si,off segmentbases + segmentbases_SIZE
	jb @@lp1

	mov si,off segmentbases		; search for a free entry
@@lp2:	test [word ptr si],-1		; this field free?
	jz short @@skp2			; if so, use it
	add si,4
	cmp si,off segmentbases + segmentbases_SIZE
	jb @@lp2
	jmp int31fail8010		; no entry free

@@skp2: mov [si + 2],bx			; store segment
	movzx edi,bx			; convert to linear address
	shl edi,4
	mov cx,1
	sub ax,ax
	int 31h				; allocate selector
	jc int31failax
	mov [si + 0],ax			; store selector

	mov bx,ax
	sub cx,cx
	mov dx,-1
	mov ax,8
	int 31h				; set descriptor limit

	mov dx,di
	shr edi,16
	mov cx,di
	mov ax,7
	int 31h				; set descriptor base - cannot fail

	mov cx,4092h
	mov ax,9
	int 31h				; set access rights

	mov ax,bx			; return selector
	jmp int31okax
; [fold]  ]

;─────────────────────────────────────────────────────────────────────────────
; [fold]  [
int310003: ; get selector increment value
	mov ax,8			; selector increment value is 8
	jmp int31okax			; return ok, with AX
; [fold]  ]

;─────────────────────────────────────────────────────────────────────────────
; [fold]  [
int310006: ; get segment base address
	call int31testsel		; test for valid selector BX

	mov dx,[word ptr coreseg:edi+ebx+2]  ; low word of 32bit linear addr
	mov cl,[byte ptr coreseg:edi+ebx+4]  ; high word of 32bit linear addr
	mov ch,[byte ptr coreseg:edi+ebx+7]

	jmp int31okdx			; return ok, with DX, CX, AX
; [fold]  ]

;─────────────────────────────────────────────────────────────────────────────
; [fold]  [
int310007: ; set segment base address
	call int31testsel		; test for valid selector BX

	mov [word ptr coreseg:edi+ebx+2],dx  ; low word of 32bit linear addr
	mov [byte ptr coreseg:edi+ebx+4],cl  ; high word of 32bit linear addr
	mov [byte ptr coreseg:edi+ebx+7],ch

	jmp int31ok			; return ok
; [fold]  ]

;─────────────────────────────────────────────────────────────────────────────
; [fold]  [
int310008: ; set segment limit
	call int31testsel		; test for valid selector BX

	cmp cx,0fh			; limit greater than 1M?
	jbe short @@int310008f0

; Current DJGPP code doesn't work with limit check, so...
;	mov ax,dx			; yup, limit greater than 1M
;	and ax,0fffh
;	cmp ax,0fffh			; low 12 bits set?
;	jne int31fail8021		; if no, error 8021h

	shrd dx,cx,12			; DX = low 16 bits of page limit
	shr cx,12			; CL = high 4 bits of page limit
	or cl,80h			; set granularity bit in CL

@@int310008f0:
	mov [word ptr coreseg:edi+ebx],dx    ; put low word of limit
	and [byte ptr coreseg:edi+ebx+6],70h ; mask off G and high nybble of limit
	or [byte ptr coreseg:edi+ebx+6],cl   ; put high nybble of limit

	jmp int31ok			; return ok
; [fold]  ]

;─────────────────────────────────────────────────────────────────────────────
; [fold]  [
int310009: ; set descriptor access rights
	call int31testsel		; test for valid selector BX

	call int31testaccess		; test access bits in CX

	or ch,10h			; set AVL bit, descriptor used
	and ch,0f0h			; mask off low nybble of CH
	and [byte ptr coreseg:edi+ebx+6],0fh ; mask off high nybble access rights
	or [byte ptr coreseg:edi+ebx+6],ch   ; or in high access rights byte
	mov [byte ptr coreseg:edi+ebx+5],cl  ; put low access rights byte

	jmp int31ok			; return ok
; [fold]  ]

;─────────────────────────────────────────────────────────────────────────────
; [fold]  [
int31000a: ; create alias descriptor
	call int31testsel		; test for valid selector BX

	mov ax,0000h			; allocate descriptor
	mov cx,1
	int 31h
	jc int31fail8011		; if failed, descriptor unavailable

	push ax				; preserve allocated selector

	movzx edi,ax			; EDI = target selector
	mov esi,[gdtbase]		; ESI -> GDT

	copy [coreseg:esi+edi],[coreseg:esi+ebx],eax
	mov eax,[coreseg:esi+ebx+4]	; copy descriptor data
	mov ah,92h
	mov [coreseg:esi+edi+4],eax

	pop ax				; restore allocated selector

	jmp int31okax			; return ok, with AX
; [fold]  ]

;─────────────────────────────────────────────────────────────────────────────
; [fold]  [
int31000b: ; get descriptor
	call int31testsel		; test for valid selector BX

	lea esi,[edi+ebx]		; ESI -> descriptor in GDT
	mov edi,[esp]			; get EDI buffer ptr from stack
	copy [es:edi+0],[coreseg:esi+0],eax ; copy descriptor
	copy [es:edi+4],[coreseg:esi+4],eax

	jmp int31ok			; return ok
; [fold]  ]

;─────────────────────────────────────────────────────────────────────────────
; [fold]  [
int31000c: ; set descriptor
	call int31testsel		; test for valid selector BX

	mov esi,[esp]			; ESI = EDI buffer ptr from stack
	mov cx,[es:esi+5]		; get access rights from descriptor
	call int31testaccess		; test access bits in CX

	add edi,ebx			; adjust EDI to descriptor in GDT
	copy [coreseg:edi],[es:esi],eax
	mov eax,[es:esi+4]
	or eax,100000h			; set descriptor AVL bit
	mov [coreseg:edi+4],eax

	jmp int31ok			; return ok
; [fold]  ]

;─────────────────────────────────────────────────────────────────────────────
; [fold]  [
int31000e: ; get multiple descriptors
	mov ax,000bh			; function 000bh, get descriptor

;-----------------------------------------------------------------------------
int31000ef:				; common to funcions 000eh and 000fh
	or cx,cx			; if CX = 0, return ok immediately
	jz int31ok

	mov dx,cx			; DX = number of descriptors
	xor cx,cx			; CX = successful counter
@@int31000efl0:
	mov bx,[es:edi]			; BX = selector to get
	add edi,2

	int 31h				; get/set descriptor
	jc int31failcx			; if error, fail with AX and CX

	add edi,8			; increment descriptor ptr
	inc cx				; increment successful copy counter
	dec dx				; decrement loop counter
	jnz @@int31000efl0		; if more descriptors to go, loop

	jmp int31ok			; return ok
; [fold]  ]

;─────────────────────────────────────────────────────────────────────────────
; [fold]  [
int31000f: ; set multiple descriptors
	mov ax,000ch			; function 000ch, set descriptor

	jmp int31000ef			; go to common function
; [fold]  ]

; [fold]  ]

;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
; DOS MEMORY FUNCTIONS
;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
; [fold]  [

;-----------------------------------------------------------------------------
; [fold]  [
int31010getdosbase:
	pop bp				; return address

	mov si,dx			; save selector for later use
	mov bx,si
	mov ax,6
	int 31h				; Get base of selector
	jc int31failax			; fail, if selector invalid

	test dx,0Fh			; not paragraph aligned?
	jnz int31failax			; not a valid segment address

	shrd dx,cx,4			; make segment address
	test cx,0FFF0h			; above first MB border?
	jnz int31fail8021		; if so, fail

	jmp bp				; else selector and address ok
; [fold]  ]

;-----------------------------------------------------------------------------
; [fold]  [
int31010issueint:
	pop bp

	mov [esp + Dpmi_regs.es],dx
	mov [esp + Dpmi_regs.ss],0
	mov [esp + Dpmi_regs.sp],0	; no own stack
	mov edi,esp
	copy es,ss,ax
	mov bx,21h			; dos int, no reset of PICs and A20
	sub cx,cx			; no stack params to copy
	mov ax,300h
	int 31h				; make dos interrupt
	jnc short @@ok
	add esp,SIZE Dpmi_regs
	jmp int31failax
@@ok:	mov cx,[esp + Dpmi_regs.flags]
	mov ax,[esp + Dpmi_regs.ax]
	mov bx,[esp + Dpmi_regs.bx]
	add esp,SIZE Dpmi_regs		; correct stack
	test cx,1			; allocation successful?
	jz short @@away
	mov [esp + Dpmi_regs.bx],bx	; return max. # of paragraphs
	jmp int31failax

@@away: jmp bp
; [fold]  ]

;─────────────────────────────────────────────────────────────────────────────
; [fold]  [
int310100: ; Allocate DOS memory block
	sub esp,SIZE Dpmi_regs		; space for real mode registers
	mov [esp + Dpmi_regs.ax],4800h	; dos function allocate memory
	mov [esp + Dpmi_regs.bx],bx	; # of paragraphs
	call int31010issueint		; attempt to allocate memory
	mov si,ax			; save segment for later use

	sub ax,ax			; Allocate Descriptor
	mov cx,1			; 1 is enough for 32bit code
	int 31h				; try to get one
	jc short @@failfree		; fail, if no descriptor free
	mov bx,ax			; BX = DOS mem selector

	mov cx,4092h			; data descriptor
	mov ax,9
	int 31h				; set descriptor type

	movzx ecx,[esp + Dpmi_regs.bx]	; get size
	shl ecx,4
	dec ecx
	mov dx,cx
	shr ecx,16
	mov ax,8
	int 31h				; Set descriptor limit

	mov cx,si			; CX = highword of linear address
	mov dx,cx			; DX = lowword of linear address
	shr cx,12
	shl dx,4
	mov ax,7
	int 31h				; set descriptor base

	mov ax,si			; initial real mode segment
	mov [esp + Dpmi_regs.dx],bx	; store selector
	jmp int31okax			; return successful

@@failfree:
	sub esp,SIZE Dpmi_regs		; space for real mode registers
	mov [esp + Dpmi_regs.ax],4900h	; dos function free memory
	mov dx,si			; segment
	call int31010issueint		; free memory
	jmp int31fail8011		; and fail
; [fold]  ]

;─────────────────────────────────────────────────────────────────────────────
; [fold]  [
int310101: ; Free DOS memory block
	call int31010getdosbase		; check selector and get segment addr

	mov bx,si
	mov ax,1
	int 31h				; free associated selector

	sub esp,SIZE Dpmi_regs		; space for real mode registers
	mov [esp + Dpmi_regs.ax],4900h	; dos function free memory
	call int31010issueint		; attempt to free memory

	jmp int31okax			; else return no error
; [fold]  ]

;─────────────────────────────────────────────────────────────────────────────
; [fold]  [
int310102: ; Resize DOS memory block
	mov di,bx			; save size for later use

	call int31010getdosbase		; check selector and segment addr

	sub esp,SIZE Dpmi_regs		; space for real mode registers
	mov [esp + Dpmi_regs.ax],4A00h	; dos function resize memory
	mov [esp + Dpmi_regs.bx],di	; new size
	call int31010issueint		; attempt to resize memory

	movzx ecx,[esp + Dpmi_regs.bx]	; get size again
	shl ecx,4
	dec ecx
	mov dx,cx
	shr cx,16
	mov bx,[esp + Dpmi_regs.dx]	; get selector
	mov ax,8
	int 31h				; Set descriptor limit

	jmp int31okax
; [fold]  ]

; [fold]  ]

;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
; INTERRUPT FUNCTIONS
;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
; [fold]  [

;─────────────────────────────────────────────────────────────────────────────
; [fold]  [
int310200: ; get real mode interrupt vector
	movzx ebx,bl			; EBX = BL (interrupt number)
	mov dx,[coreseg:ebx*4]		; load real mode vector offset
	mov cx,[coreseg:ebx*4+2]	; load real mode vector segment

	jmp int31okdx			; return ok, with AX, CX, DX
; [fold]  ]

;─────────────────────────────────────────────────────────────────────────────
; [fold]  [
int310201: ; set real mode interrupt vector
	movzx ebx,bl			; EBX = BL (interrupt number)
	mov [coreseg:ebx*4],dx		; set real mode vector offset
	mov [coreseg:ebx*4+2],cx	; set real mode vector segment

	jmp int31ok			; return ok
; [fold]  ]

;─────────────────────────────────────────────────────────────────────────────
; [fold]  [
int310202: ; get exception handler
	cmp bl,31			; Check exception number
	ja int31fail8021

	movzx ebx,bl
	mov cx,[pdseg:ebx*SIZE Farptr32 + Pdata.exceptions.sel]
	mov edx,[pdseg:ebx*SIZE Farptr32 + Pdata.exceptions.offset]

	mov ax,202h			; no error code
	jmp int31okedx
; [fold]  ]

;─────────────────────────────────────────────────────────────────────────────
; [fold]  [
int310203: ; set exception handler
	cmp bl,31			; Check exception number
	ja int31fail8021

	xchg bx,cx			; swap int number with int selector
	call int31testsel		; test for valid selector BX

	movzx ecx,cl
	mov [pdseg:ecx*SIZE Farptr32 + Pdata.exceptions.pad],0
	mov [pdseg:ecx*SIZE Farptr32 + Pdata.exceptions.sel],bx
	mov [pdseg:ecx*SIZE Farptr32 + Pdata.exceptions.offset],edx

IF FAST_FPU_EMU EQ 1
	copy es,SELALIASCS,ax
	mov ax,[pdseg:7*SIZE Farptr32 + Pdata.exceptions.sel]
	mov [es:FPU_emu_vector.sel],ax
	mov eax,[pdseg:7*SIZE Farptr32 + Pdata.exceptions.offset]
	mov [es:FPU_emu_vector.offset],eax
ENDIF
	jmp int31ok
; [fold]  ]

;─────────────────────────────────────────────────────────────────────────────
; [fold]  [
int310204: ; get protected mode interrupt vector
	movzx ebx,bl			; EBX = BL (interrupt number)
	cmp bl,NUM_EXCEPTIONS
	jae short @@ok3
	mov cx,[pdseg:ebx*SIZE Farptr32 + Pdata.lowint.sel]
	mov edx,[pdseg:ebx*SIZE Farptr32 + Pdata.lowint.offset]

	mov ax,204h
	jmp int31okedx

@@ok3:	mov dx,[pdseg:ebx*8+Pdata.idt.offset1]	; get high word of offset
	shl edx,16
	mov dx,[pdseg:ebx*8+Pdata.idt.offset0]	; get low word of offset
	mov cx,[pdseg:ebx*8+Pdata.idt.sel]	; get selector

	mov ax,204h
	jmp int31okedx			; return ok, with AX, CX, EDX
; [fold]  ]

;─────────────────────────────────────────────────────────────────────────────
; [fold]  [
int310205: ; set protected mode interrupt vector
	xchg bx,cx			; swap int number with int selector
	call int31testsel		; test for valid selector BX

	movzx ecx,cl			; ECX = CL (interrupt number)

; This code sets the requested int number and if it is a hardware int on the
; master pic, then the actual irq int is set as well. The only problem is that
; requests to set one of the actual irq's will fail with error 8003h (system
; integrity). One possible solution is to reprogram the offending pic in such
; a case, but I won't bother for the time being.
	mov al,cl
	and al,0f8h
	cmp al,[picmasterold]
	jne short @@notmaster
; set master pic int
	call @@setint
	sub cl,[picmasterold]
	add cl,[picmasternew]
	call @@setint
	jmp int31ok

@@notmaster:
	cmp al,[picmasternew]
	je int31fail8003 ; we should reprogram the pic to another base instead
	call @@setint
	jmp int31ok

;-----------------------------------------------------------------------------
@@setint:
	cmp cl,NUM_EXCEPTIONS
	jae short @@setint1
	mov [pdseg:ecx*SIZE Farptr32 + Pdata.lowint.pad],0
	mov [pdseg:ecx*SIZE Farptr32 + Pdata.lowint.sel],bx
	mov [pdseg:ecx*SIZE Farptr32 + Pdata.lowint.offset],edx
	ret
@@setint1:
	mov [pdseg:ecx*8+Pdata.idt.offset0],dx	; set low word of offset
	shld eax,edx,16
	mov [pdseg:ecx*8+Pdata.idt.offset1],ax	; set high word of offset
	mov [pdseg:ecx*8+Pdata.idt.sel],bx	; set selector
	ret
; [fold]  ]

;─────────────────────────────────────────────────────────────────────────────
; [fold]  [
int310900:  ; get and disable interrupt state
	btc [word ptr esp+48],9		; test and clear IF bit in EFLAGS
	setc al				; set AL = carry (IF flag from EFLAGS)

	jmp int31okax			; return ok
; [fold]  ]

;─────────────────────────────────────────────────────────────────────────────
; [fold]  [
int310901: ; get and enable interrupt state
	bts [word ptr esp+48],9		; test and set IF bit in EFLAGS
	setc al				; set AL = carry (IF flag from EFLAGS)

	jmp int31okax			; return ok
; [fold]  ]

;─────────────────────────────────────────────────────────────────────────────
; [fold]  [
int310902: ; get interrupt state
	bt [word ptr esp+48],9		; just test IF bit in EFLAGS
	setc al				; set AL = carry (IF flag from EFLAGS)

	jmp int31okax			; return ok
; [fold]  ]

; [fold]  ]

;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
; REAL/PROTECTED MODE TRANSLATION FUNCTIONS
;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
; [fold]  [

;─────────────────────────────────────────────────────────────────────────────
; [fold]  [
int310300: ; simulate real mode interrupt
	movzx eax,bl			; get real mode INT CS:IP
	mov eax,[coreseg:eax*4]
	mov [DWORD PTR es:edi+Dpmi_regs.ip],eax
; [fold]  ]

;─────────────────────────────────────────────────────────────────────────────
int310301: ; call real mode FAR procedure
					; same start as function 0302h
;─────────────────────────────────────────────────────────────────────────────
int310302: ; call real mode IRET procedure

;-----------------------------------------------------------------------------
; [fold]  [
int3103: ; common to 0300h, 0301h, and 0302h
	movzx ebx,[es:edi+Dpmi_regs.sp] ; EBX = SP from register structure
	movzx edx,[es:edi+Dpmi_regs.ss] ; EDX = SS from register structure

	mov ax,bx			; check if caller provided stack
	or ax,dx
	jnz short @@int3103f3		; if yes, go on to set stack

	mov dx,[privatedataseg]		; real mode SS
	mov bx,[rmstacktop]		; BX = top of real mode stack
	mov ax,bx			; save top of stack
	sub bx,RMONESTACKLEN		; adjust BX to next stack location

	cmp bx,[rmstackbase]		; exceeded real mode stack space?
	jb int31fail8012		; if yes, error 8012h

	mov [rmstacktop],bx		; update ptr for possible reenterancy
	mov bx,ax			; restore top of stack

@@int3103f3:
	shl edx,4			; convert seg:ofs form to linear
	mov edi,edx			; address
	add edi,ebx			; EDI = top of real mode stack

	sub edi,10
	lea ax,[bx-10]			; AX = top of stack parms
	xchg ax,[rmstackparmtop]	; preserve and set new top of stack
	push ax				;  parms for possible reenterancy

	mov [coreseg:edi+8],cx		; store # of parameters
	mov [coreseg:edi+6],ss		; store SS:ESP on real mode stack
	mov [coreseg:edi+2],esp		; ESP when switching to real mode
	mov [coreseg:edi+0],es		; store ES on real mode stack

	movzx ecx,cx
	lea esi,[esp+ecx*2+2+32+8+12]	; start of parameters on stack
	mov ebx,ecx
	jmp short @@lp1in
@@lp1:	sub esi,2
	mov ax,[ss:esi]
	sub edi,2
	mov [coreseg:edi],ax		; copy parameters
@@lp1in:dec ebx
	jns @@lp1

	mov esi,[esp+2+Dpmi_regs.edi]	; call struct
	mov ax,[es:esi+Dpmi_regs.flags] ; AX = flags for real mode
	cmp [esp+2+Dpmi_regs.ax],301h	; if function 301h, no space for flags
	jz short @@w1
	and ah,0fch			; 0300h or 0302h, clear IF and TF flag
	mov [es:esi+Dpmi_regs.flags],ax
	sub edi,2			; push flags for IRET stack frame
	mov [coreseg:edi],ax
@@w1:
	mov [WORD PTR coreseg:edi-4],off @@int3103f1
	mov [WORD PTR coreseg:edi-2],_TEXT
	copy <[DWORD PTR coreseg:edi-8]>,<[DWORD PTR es:esi+Dpmi_regs.ip]>,eax

	sub edi,8+42
	dmemcpyw es,esi,coreseg,edi,42	; copy registers + flags
	sub edi,42			; correct edi

	sub edi,edx
	mov ax,DGROUP			; AX  = Real Mode DS
	movzx ebx,di			; EBX = Real Mode ESP
	shr edx,4			; DX  = Real Mode SS
	mov si,_TEXT			; SI  = Real Mode CS
	mov di,off @@int3103f0		; DI  = Real Mode IP
	jmp [pmtormswrout]		; switch to real mode

@@int3103f0:				; real mode INT, FAR, or IRET call
	popad				; load regs with call values
	popf				; load flags with call value
	pop es ds fs gs			; load sregs with call values
	retf				; go to call address

@@int3103f1:
	push gs fs ds es		; store registers on stack
	pushf				; store flags on stack
	cli
	pushad
	copy ds,DGROUP,ax
	mov bp,[rmstackparmtop]

	mov cx,[bp+0]			; get protected mode ES from stack
	mov ebx,[bp+2]			; get protected mode SS:ESP from stack
	mov dx,[bp+6]

	mov bp,ss			; EBP = linear ptr to SS
	movzx ebp,bp
	shl ebp,4

	mov ax,SELDS			; DS selector value for protected mode
	mov si,SELCS			; target CS:EIP in protected mode
	do16to32 mov,di,<off @@int3103f2>
	jmp [rmtopmswrout]		; go back to protected mode

@@int3103f2:
	copy coreseg,SELCORE,ax
	copy pdseg,SELPRIVAT,ax

	movzx eax,[rmstackparmtop]
	add ebp,eax			; EBP = linear address of rm stack
	pop [rmstackparmtop]

	movzx eax,[WORD PTR coreseg:ebp+8] ; get paramcount
	add eax,eax
	sub ebp,eax
	lea esi,[ebp-42]
	mov edi,[esp+Dpmi_regs.edi]	; get structure offset from stack
	dmemcpyw coreseg,esi,es,edi,42	; copy regs and flags

	cmp [dword ptr es:edi+4],0	; stack provided by caller?
	jne int31ok			; if yes, done now

	add [rmstacktop],RMONESTACKLEN	; restore top of real mode stack
	jmp int31ok			; return ok
; [fold]  ]

;─────────────────────────────────────────────────────────────────────────────
; [fold]  [
int310303: ; allocate real mode callback address
	mov dx,CALLBACKS		; DX = total number of callbacks
	mov bx,off common_callbacks	; BX = base of callbacks
	jmp short @@in
@@int310303l0:
	cmp [bx+Callback.ds],0		; is this callback free?
	jz short @@int310303f0		; if yes, allocate

	add bx,SIZE Callback		; increment ptr to callback
@@in:	dec dx				; decrement loop counter
	jns @@int310303l0		; if more callbacks to check, loop

	jmp int31fail8015		; no free callback, error 8015h

@@int310303f0:
	mov cx,[esp+38]			; CX = caller DS from stack
	mov [bx+Callback.ds],cx		; store callback parms in callback
	mov [bx+Callback.esi],esi
	mov [bx+Callback.es],es
	mov [bx+Callback.edi],edi
	mov [bx+Callback.ptr.offset],off callback
	mov [bx+Callback.ptr.sel],_TEXT

	mov dx,bx			; DX = offset of callback
	mov cx,DGROUP			; CX = segment of callback

	jmp int31okdx			; return ok, with DX, CX, AX
; [fold]  ]

;─────────────────────────────────────────────────────────────────────────────
; [fold]  [
int310304: ; free real mode callback address
	cmp cx,DGROUP			; valid callback segment?
	jne int31fail8024		; if no, error 8024h

	mov bx,dx			; BX = offset of callback

	xor ax,ax			; check if valid offset
	xchg dx,ax
	sub ax,off common_callbacks
	mov cx,SIZE Callback
	div cx

	or dx,dx			; is there a remainder
	jnz int31fail8024		; if yes, not valid, error 8024h

	cmp ax,CALLBACKS		; callback index out of range?
	jae int31fail8024		; if yes, not valid, error 8024h

	mov [bx+Callback.ds],0		; set callback free

	jmp int31ok			; return ok
; [fold]  ]

; [fold]  ]

;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
; MISC FUNCTIONS
;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
; [fold]  [

;─────────────────────────────────────────────────────────────────────────────
; [fold]  [
int310305: ; get state save/restore addresses
	add esp,20h			; adjust stack

	xor ax,ax			; size needed is none
	mov bx,_TEXT			; real mode seg of same RETF
	mov cx,off vxr_saverestorerm	; same offset of 16bit RETF
	mov si,cs			; selector of routine is this one
	DB 66h
	mov di,off vxr_saverestorepm	; offset of simple 32bit RETF
	DW 0
	pop gs fs es ds			; restore segment registers
	jmp int31oknopop		; return ok, dont pop registers
; [fold]  ]

;─────────────────────────────────────────────────────────────────────────────
; [fold]  [
int310306: ; get raw mode switch addresses
	add esp,20h			; adjust stack

	mov si,cs			; selector of pmtorm rout is this one
	movzx edi,[pmtormswrout]		; offset in this seg of rout
	mov bx,_TEXT			; real mode seg of rmtopm rout
	mov cx,[rmtopmswrout]		; offset of rout in real mode

	pop gs fs es ds			; restore segment registers
	jmp int31oknopop		; return ok, dont pop registers
; [fold]  ]

;─────────────────────────────────────────────────────────────────────────────
; [fold]  [
int310400: ; get version
	add esp,20h			; adjust stack

	mov ax,90			; return version 0.90
	mov bx,3			; capabilities
	mov cl,[processortype]		; processor type
	mov dl,[picslaveold]		; master and slave PIC default
	mov dh,[picmasterold]		;  values for this PC

	pop gs fs es ds			; restore segment registers
	jmp int31oknopop		; return ok, dont pop registers
; [fold]  ]

; [fold]  ]

;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
; LOCK MEMORY FUNCTIONS
;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
; [fold]  [

; So far all of these functions only clear the carry

;─────────────────────────────────────────────────────────────────────────────
int310600: ; Lock Linear Region
	jmp int31ok

;─────────────────────────────────────────────────────────────────────────────
int310601: ; Unlock Linear Region
	jmp int31ok

;─────────────────────────────────────────────────────────────────────────────
int310602: ; Mark Real Mode Region as Pageable
	jmp int31ok

;─────────────────────────────────────────────────────────────────────────────
int310603: ; Relock Real Mode Region
	jmp int31ok

;─────────────────────────────────────────────────────────────────────────────
int310604: ; Get Page Size
	sub bx,bx
	mov cx,4096
	jmp int31oksinoax

; [fold]  ]

;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
; DEMAND PAGING PERFORMANCE TUNING SERVICES
;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
; [fold]  [

; So far all of these functions only clear the carry

;─────────────────────────────────────────────────────────────────────────────
int310702: ; Mark Page as Demand Paging Candidate
	jmp int31ok

;─────────────────────────────────────────────────────────────────────────────
int310703: ; Discard Page Contents
	jmp int31ok

; [fold]  ]

;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
; PMODE/DJ EXTENSIONS
;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
; [fold]  [

IF PMODEDJ_EXT EQ 1
;═════════════════════════════════════════════════════════════════════════════
; [fold]  [
int310a00: ; PMODE/DJ DPMI extensions
	mov ds,[esp + 32 + 6]
	copy es,SELDS,ax
	do16to32 mov di,<off pmodedj_id>
	mov ecx,PMODEDJ_ID_SIZE
	repe cmps [BYTE PTR ds:esi],[BYTE PTR es:edi]
	dnop
	jnz int31fail8001		; dont wanna talk to us
	mov [esp + 32 + 4],cs
	do16to32 mov <[WORD PTR esp + Dpmi_regs.edi]>,<off @@api>
	jmp int31ok
; [fold]  ]

;-----------------------------------------------------------------------------
; [fold]  [
@@api: ; extension entry
	stc
	DB 66h
	retf
; [fold]  ]
ENDIF
; [fold]  ]

;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
; COPROCESSOR FUNCTIONS
;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
; [fold]  [

;─────────────────────────────────────────────────────────────────────────────
; [fold]  [
int310e00: ; get coprocessor status
	mov al,[processortype]
	mov bl,[oldCR0L]
	cmp al,3			; is it 386?
	jne short @@ok1
	test bl,10h			; ET bit
	jnz short @@ok1
	mov al,2			; is 287
@@ok1:	shl al,4
	and bl,6
	shl bl,1
	or al,bl
	or al,[newEM_MP]
	sub ah,ah
	jmp int31okax
; [fold]  ]

;─────────────────────────────────────────────────────────────────────────────
; [fold]  [
int310e01: ; set coprocessor emulation
	mov eax,cr0
	and al,0fbh			; clear bit 3 (EM)
	and bl,3			; keep EM and MP bits
	mov [newEM_MP],bl
	shl bl,1			; move to place
	and bl,4			; clear MP
	or al,bl
	mov cr0,eax
	jmp int31ok
; [fold]  ]

; [fold]  ]

;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
; VCPI EXTENDED MEMORY FUNCTIONS
;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
; [fold]  [

;-----------------------------------------------------------------------------
; [fold]  [
int310500vsiditoesi: ; convert handle SI:DI to ptr ESI
	pop bp				; pop return address

	test di,3			; is handle (ptr) aligned on dword?
	jnz int31fail8023		; if no, error 8023h

	shl esi,16			; ESI = SI:DI
	mov si,di

	cmp esi,[pagetablefree]		; handle too low?
	jb int31fail8023		; if yes, error 8023h

	cmp esi,[pagetabletop]		; handle too high?
	jae int31fail8023		; if yes, error 8023h

	test [byte ptr coreseg:esi+1],2 ; is page first in allocated block?
	jz int31fail8023		; if no, error 8023h

	jmp bp				; return ok
; [fold]  ]

;-----------------------------------------------------------------------------
; [fold]  [
int310500vbxcxtoebx: ; convert BX:CX bytes to EBX pages
	pop bp				; pop return address

	shl ebx,16			; EBX = BX:CX
	mov bx,cx

	or ebx,ebx			; check for invalid value
	jz int31fail8021		; if invalid value, error 8021h

	add ebx,0fffh			; convert EBX to page count
	shr ebx,12

	jmp bp				; return ok
; [fold]  ]

;-----------------------------------------------------------------------------
; [fold]  [
int310500vpmalloc: ; allocate physical memory block
	mov edi,esi			; EDI = ESI, ptr to linear block start
	xor ebp,ebp			; EBP = running allocated page count

@@int310500vpmallocl0:
	mov ax,0de04h			; VCPI allocate a page
	call [vcpi_service.ptr]

	or ah,ah			; got a page?
	jz short @@int310500vpmallocl0f0; if yes, go on

	cmp ebp,1			; allocated any pages?
	jc short @@int310500vpmallocdone; if no, fail immediately

	or [byte ptr coreseg:edi-3],4	; set last allocated page as last

	call int310500vfree		; free what was allocated

	stc				; carry set, failed
	jmp short @@int310500vpmallocdone	; go to done

@@int310500vpmallocl0f0:
	and dh,0f0h			; clear 4 bits of page table entry
	mov dl,7			; set page as user/writeable/present
	mov [coreseg:edi],edx		; store page in page table
	add edi,4			; increment page table ptr

	inc ebp				; increment allocated page count
	cmp ebp,ebx			; allocated all needed pages?
	jb @@int310500vpmallocl0	; if no, loop

	or [byte ptr coreseg:esi+1],2	; set first allocated page as first
	or [byte ptr coreseg:edi-3],4	; set last allocated page as last
	clc				; carry clear, success

@@int310500vpmallocdone:
	mov eax,ebp			; EAX = number of pages allocated
	ret				; return
; [fold]  ]

;-----------------------------------------------------------------------------
; [fold]  [
int310500vlmalloc: ; check for linear memory block
	mov edi,[pagetablefree]		; EDI = search ptr in page table

	mov ecx,[pagetabletop]		; ECX = count of pages to search
	sub ecx,edi
	shr ecx,2

	xor edx,edx			; EDX = largest linear block found
	xor eax,eax			; EAX = search unit, free entry (0)

	push ebx			; preserve EBX, memory requested

@@int310500vlmallocl0:
	jecxz short @@int310500vlmallocdone	; if no more entries, done

	repne scas [dword ptr es:edi]	; search for first next free entry
	dnop
	jne short @@int310500vlmallocdone	; if no more free, go on

	mov ebp,ecx			; EBP = current count
	lea ebx,[edi-4]			; EBX = start of free block

	repe scas [dword ptr es:edi]	; search for end of free linear block
	dnop
	jne short @@int310500vlmallocl0f0 ; if previous entry not free, go on

	inc ebp				; previous entry free, extra one

@@int310500vlmallocl0f0:
	sub ebp,ecx			; EBP = number of free pages in block

	cmp ebp,edx			; new block larger than last largest?
	jb @@int310500vlmallocl0	; if no, loop

	mov esi,ebx			; ESI = ptr to largest block found
	mov edx,ebp			; size of new largest block found

	cmp ebp,[esp]			; block sufficient for memory request?
	jb @@int310500vlmallocl0	; if no, loop

@@int310500vlmallocdone:
	pop ebx				; restore EBX, memory requested
	ret				; return
; [fold]  ]

;-----------------------------------------------------------------------------
; [fold]  [
int310500vmalloc: ; allocate linear+physical mem block
	call int310500vlmalloc		; try to allocate linear memory block

	cmp edx,1			; found ANY free linear area?
	jc short @@int310500vmallocdone ; if no, done

	cmp edx,ebx			; linear block enough for request?
	jb short @@int310500vmallocf0	; if no, go to physical memory check

	call int310500vpmalloc		; try to allocate physical mem

	mov cl,1			; error is not enough physical memory
	jmp short @@int310500vmallocdone; go to done

@@int310500vmallocf0:
	mov ebx,edx			; only linear block size physical mem

	call int310500vpmalloc		; try to allocate physical memory
	jc short @@int310500vmallocfaillinear	; if failed, done

	call int310500vfree		; success, so must free block

	mov eax,ebx			; can allocate this much total memory
	stc				; carry set, failed

@@int310500vmallocfaillinear:
	mov cl,0			; error is not enough linear memory

@@int310500vmallocdone:
	ret				; return
; [fold]  ]

;-----------------------------------------------------------------------------
; [fold]  [
int310500vfree: ; free linear+physical memory block
	mov edi,esi			; EDI = ESI, ptr to linear block start

@@int310500vfreel0:
	xor ecx,ecx			; new page table entry is free (0)
	xchg ecx,[coreseg:edi]		; swap ECX with page table entry
	add edi,4			; increment page table ptr

	mov edx,ecx			; EDX = page table entry
	and dx,0f000h			; mask off low 12 bits

	mov ax,0de05h			; VCPI free a page
	call [vcpi_service.ptr]

	test ch,4			; last page of block?
	jz @@int310500vfreel0		; if no, loop

	ret				; return
; [fold]  ]

;─────────────────────────────────────────────────────────────────────────────
; [fold]  [
int310500v: ; VCPI get free memory information
	push coreseg			; ES = core for VCPI malloc functions
	pop es

	mov ebx,0ffffffffh		; try to allocate an impossible amount
	call int310500vmalloc

	shl eax,12			; returned EAX is highest possible

	jmp int310500xsetbuf		; put memory information in buffer
; [fold]  ]

;─────────────────────────────────────────────────────────────────────────────
; [fold]  [
int310501v: ; VCPI allocate memory block
	push coreseg			; ES=coreseg for VCPI malloc functions
	pop es

	call int310500vbxcxtoebx	; convert BX:CX bytes to EBX pages

	call int310500vmalloc		; try to allocate requested amount
	jnc short int310501vaddxnhandle ; if successful, go to done

	or cl,cl			; error is not enough linear memory?
	jz int31fail8012		; if yes, error 8012h
	jmp int31fail8013		; error is physical, error 8013h

int310501vaddxnhandle:
	mov ecx,esi			; figure address of block from handle
	sub ecx,[pagetablebase]
	shl ecx,10
	shld ebx,ecx,16

	mov di,si			; SI:DI = ESI, handle
	shr esi,16

	mov eax,[vcpi_cr3]		; reload CR3 to clear TLB
	mov cr3,eax

	jmp int31oksinoax		; return ok, with SI, DI, BX, CX
; [fold]  ]

;─────────────────────────────────────────────────────────────────────────────
; [fold]  [
int310502v: ; VCPI free memory block
	call int310500vsiditoesi	; convert handle SI:DI to ptr ESI

	call int310500vfree		; free memory block

	mov eax,[vcpi_cr3]		; reload CR3 to clear TLB
	mov cr3,eax

	jmp int31ok			; return ok
; [fold]  ]

;─────────────────────────────────────────────────────────────────────────────
; [fold]  [
int310503v: ; VCPI resize memory block
	push coreseg			; ES=coreseg for VCPI malloc functions
	pop es

	call int310500vbxcxtoebx	; convert BX:CX bytes to EBX pages

	call int310500vsiditoesi	; convert handle SI:DI to ptr ESI

	mov edi,esi			; EDI = ESI, ptr to linear block start
	xor ebp,ebp			; EBP = running block size in pages

@@int310503vl0:
	add edi,4			; increment page table ptr
	inc ebp				; increment block size

	test [byte ptr coreseg:edi-3],4 ; last page of block?
	jz @@int310503vl0		; if no, loop

	sub ebx,ebp			; EBX = change in block size
	jz int310501vaddxnhandle	; if no change, done

	jc @@int310503vf0		; if block made smaller, just free top

	mov ecx,[pagetabletop]		; 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 short @@int310503vf3	; if no entries above, try below

	repe scas [dword ptr es:edi]	; check for free entries above block
	dnop
	je short @@int310503vf2		; if previous entry free, go on

	dec edx				; previous entry not free, minus one

@@int310503vf2:
	sub edx,ecx			; EDX = number of free pages in block

	cmp edx,ebx			; enough linear memory?
	jb short @@int310503vf3		; 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 int310500vpmalloc		; try to allocate physical memory
	mov edi,esi			; EDI -> start of new block
	pop esi				; restore start of old block
	jc int31fail8013		; if alloc failed, error 8013h

	and [byte ptr coreseg:edi-3],0fbh ; clear last bit in old block end
	and [byte ptr coreseg:edi+1],0fdh ; clear first bit in new block start

	jmp int310501vaddxnhandle	; go to done

@@int310503vf3:
	mov ecx,esi			; ECX = count of pages to search up
	sub ecx,[pagetablefree]
	shr ecx,2

	or ecx,ecx			; any linear memory below?
	jz @@int310503vf1		; 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 scas [dword ptr es:edi]	; check for free entries after block
	dnop
	cld
	je short @@int310503vf4		; if previous entry free, go on

	dec ebp				; previous entry not free, minus one

@@int310503vf4:
	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 @@int310503vf1		; 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 int310500vpmalloc		; 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 int31fail8013		; if alloc failed, error 8013h

	or edx,edx			; any pages needed above?
	jz short @@int310503vf6		; 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 int310500vpmalloc		; try to allocate physical memory
	pop ebp edi esi			; restore some vars
	jnc short @@int310503vf5	; if allocated ok, go on

	mov esi,edi			; ESI -> allocated block below
	call int310500vfree		; free allocated block below

	jmp int31fail8013		; fail, error 8013h

@@int310503vf5:
	and [byte ptr coreseg:esi-3],0fbh ; clear last bit in below block end
	and [byte ptr coreseg:esi+ebp*4+1],0fdh ; clear first bit in above block start

@@int310503vf6:
	and [byte ptr coreseg:edi+1],0fdh	; clear first bit in below block start
	and [byte ptr coreseg:esi+ebp*4-3],0fbh ; 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

@@int310503vl1:
	mov edi,ebx			; set up to shift up a page
	mov esi,ebx
	mov ecx,ebp

	push ds				; save data selector
	push coreseg
	pop ds				; ds = core selector
	lods [dword ptr ds:esi]		; shift old pages a page down in table
	rep movs [dword ptr es:edi],[dword ptr ds:esi]
	stos [dword ptr es:edi]
	dnop		; 386 bug
	pop ds				; ds now data selector again

	sub ebx,4			; decrement to next page to shift
	cmp ebx,edx			; more pages to shift?
	jae @@int310503vl1		; if yes, loop

	pop esi				; restore new block start address

	jmp int310501vaddxnhandle	; go to done

@@int310503vf1:
	add ebx,ebp			; restore EBX as requested size

	push esi ebp			; preserve some vars
	call int310500vlmalloc		; check for linear memory block
	pop ebp edi			; restore some vars

	cmp edx,ebx			; enough linear memory?
	jb int31fail8012		; 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 int310500vpmalloc		; try to allocate physical memory
	pop ecx esi edi			; restore some vars
	jc int31fail8013		; if not enough mem, error 8013h

	push edi esi ecx		; preserve, new and old block, size

	push ds
	push coreseg
	pop ds
	rep movs [dword ptr es:edi],[dword ptr ds:esi]	; copy old block pages
	dnop				; 386 bug
	pop ds

	and [byte ptr coreseg:edi-3],0fbh ; clear last bit in old block end
	and [byte ptr coreseg:edi+1],0fdh ; clear first bit in new block start

	pop ecx edi			; restore to clear old block

	xor eax,eax			; new page table entry is free (0)
	rep stos [dword ptr es:edi]	; clear old page table block
	dnop		; 386 bug

	pop esi				; restore new block address

	jmp int310501vaddxnhandle	; go to done

@@int310503vf0:
	sub edi,4			; decrement page table ptr

	xor edx,edx			; new page table entry is free (0)
	xchg edx,[coreseg:edi]		; swap EDX with page table entry
	and dx,0f000h			; mask off low 12 bits

	mov ax,0de05h			; VCPI free a page
	call [vcpi_service.ptr]

	inc ebx				; increment negative change counter
	jnz @@int310503vf0		; if more pages to free, loop

	or [byte ptr coreseg:edi-3],4	; set next page up as last of block

	jmp int310501vaddxnhandle	; go to done
; [fold]  ]

;─────────────────────────────────────────────────────────────────────────────
; [fold]  [
int31050av: ; VCPI get memory block size and base
	shrd ecx,esi,16			; figure address of block from handle
	mov cx,di
	sub ecx,[pagetablebase]
	shl ecx,10
	shld ebx,ecx,16

	call int310500vsiditoesi	; convert handle SI:DI to ptr ESI

	xor edi,edi			; EDI = running page count

@@int31050avl0:
	inc edi				; increment page count

	mov eax,[coreseg:esi]		; EAX = page table entry
	add esi,4
	test ah,4			; is this the last page of the block?
	jz short @@int31050avl0		; if no, loop

	shl edi,12			; convert EDI pages to bytes
	shld esi,edi,16			; SI:DI = EDI, size in bytes

	jmp int31oksinoax		; return ok, with SI, DI, BX, CX
; [fold]  ]

;─────────────────────────────────────────────────────────────────────────────
; [fold]  [
int310800v: ; VCPI physical address mapping
	push coreseg
	pop es
	shl ebx,16
	mov bx,cx
	cmp ebx,100000h			; below 1st MB?
	jb int31fail8021		; DPMI says to fail then

	mov bx,si
	shl ebx,16
	mov bx,di			; get size
	or ebx,ebx			; size 0?
	jz int31fail8021		; if invalid value, error 8021h

	and ecx,0FFFh			; mask lower 12 bits of phys addr
	add ebx,ecx			; add page offset to size
	jc int31fail8021
	add ebx,4095
	jc int31fail8021
	shr ebx,12			; get # of pages

	push ecx			; save page offset
	call int310500vlmalloc		; allocate linear address space
	pop ebp
	cmp edx,ebx			; linear block enough for request?
	jb int31fail8012		; if no, fail

; fill PTEs will correct values
	mov edi,esi			; edi = addr of PTE
	mov ax,[esp + Dpmi_regs.bx]
	shl eax,16
	mov ax,[esp + Dpmi_regs.cx]	; get physical address
	and ah,0f0h			; clear 4 bits of page table entry
	mov al,17h			; user/writeable/present/PCD
@@loop: mov [coreseg:edi],eax		; set page address
	add eax,4096			; next page in real memory
	add edi,4			; next PTE
	dec ebx
	jne short @@loop

	or [byte ptr coreseg:esi+1],2	; set first allocated page as first
	or [byte ptr coreseg:edi-3],4	; set last allocated page as last

	mov ecx,esi			; figure address of block from handle
	sub ecx,[pagetablebase]
	shl ecx,10
	add ecx,ebp			; add page offset
	shld ebx,ecx,16

	mov eax,[vcpi_cr3]		; reload CR3 to clear TLB
	mov cr3,eax

	mov si,[esp + Dpmi_regs.si]
	mov di,[esp + Dpmi_regs.di]
	jmp int31oksinoax
; [fold]  ]

;─────────────────────────────────────────────────────────────────────────────
; [fold]  [
int310801v: ; VCPI free physical address mapping
	shl ebx,16
	mov bx,cx			; make linear address
	shr ebx,10
	and bl,0FCh
	add ebx,[pagetablebase]		; EBX = start of PTE

@@loop: sub eax,eax
	xchg eax,[coreseg:ebx]		; mark this PTE as free
	add ebx,4
	test ah,4			; last page?
	jz short @@loop			; if not, loop

	jmp int31ok
; [fold]  ]

; [fold]  ]

;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
; XMS EXTENDED MEMORY FUNCTIONS
;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
; [fold]  [

;-----------------------------------------------------------------------------
; [fold]  [
int310500xsetbuf: ; Fills the memory information buffer of the caller
	mov es,[esp+24h]		; get ES:EDI buffer ptr from stack
	mov edi,[esp]

	stos [dword ptr es:edi]		; store block size in output buffer
	shr eax,12			; convert from bytes to pages
	stos [dword ptr es:edi]		; store in output buffer max unlock  
	stos [dword ptr es:edi]		; store in output buffer max locked
	dnop
	mov ecx,09h			; fill rest of buffer with 0ffffffffh
	mov eax,0ffffffffh
	rep stos [dword ptr es:edi]
	dnop

	jmp int31ok			; return ok
; [fold]  ]

;-----------------------------------------------------------------------------
; [fold]  [
int310500xcallxms: ; make a call to real mode XMS driver
	pop bp				; pop return address
	sub esp,32h			; stack space for register structure

	mov [esp+1ch],ax		; put AX in register structure
	mov [esp+10h],bx		; put BX in register structure
	mov [esp+18h],cx		; put CX in register structure
	mov [esp+14h],dx		; put DX in register structure

	mov [word ptr esp+20h],0	; zero FLAGS in register structure
	mov [dword ptr esp+2eh],0	; zero SS:SP in register structure
	mov eax,[xms_call.ptr]		; put XMS driver address in CS:IP in
	mov [esp+2ah],eax		;  register structure

	push ss				; ES:EDI -> register structure
	pop es
	mov edi,esp

	xor cx,cx			; copy 0 words as stack parameters
	xor bh,bh			; doesnt really need to be here
	mov ax,301h			; call real mode FAR procedure
	int 31h

	mov ax,[esp+1ch]		; get AX from register structure
	mov bx,[esp+10h]		; get BX from register structure
	mov cx,[esp+18h]		; get CX from register structure
	mov dx,[esp+14h]		; get DX from register structure

	lea esp,[esp+32h]		; adjust ESP without changing FLAGS
	jc int31fail8010		; if INT 31h failed, error 8010h

	jmp bp				; return ok
; [fold]  ]

;-----------------------------------------------------------------------------
; [fold]  [
int310500xbxcxtodx: ; convert BX:CX bytes to DX K
	pop bp				; pop return address

	mov dx,cx			; check for invalid value, BX=CX=0
	or dx,bx
	jz int31fail8021		; if invalid value, error 8021h

	add cx,1023+15			; adjust for size in K and align
	adc bx,0

	test bh,0fch			; memory request too high
	jnz int31fail8013		; if yes, error 8013h

	shrd cx,bx,10			; CX = memory in K
	mov dx,cx

	jmp bp				; return ok
; [fold]  ]

;-----------------------------------------------------------------------------
; [fold]  [
int310500xerror: ; XMS error, return with DPMI error
	cmp bl,0a0h			; out of memory?
	je int31fail8013		; if yes, error 8013h

	cmp bl,0a1h			; handles exhausted?
	je int31fail8016		; if yes, error 8016h

	cmp bl,0a2h			; invalid handle?
	je int31fail8023		; if yes, error 8023h

	jmp int31fail8010		; else, error 8010h
; [fold]  ]

;─────────────────────────────────────────────────────────────────────────────
; [fold]  [
int310500x: ; XMS get free memory information
	mov ah,8			; get largest free memory block in K
	call int310500xcallxms

	movzx eax,ax			; EAX = free memory in bytes
	shl eax,10

	sub eax,15			; adjust by extra alignment size
	jnc int310500xsetbuf		; if no overflow, put info to buffer

	xor eax,eax			; overflow, so no memory available
	jmp int310500xsetbuf		; put memory information in buffer
; [fold]  ]

;─────────────────────────────────────────────────────────────────────────────
; [fold]  [
int310501x: ; XMS allocate memory block
	call int310500xbxcxtodx		; convert BX:CX bytes to DX K
	mov cx,dx			; preserve size of block in K

	mov ah,9			; allocate DX K of XMS memory
	call int310500xcallxms
	or ax,ax			; error?
	jz int310500xerror		; if yes, convert XMS error to DPMI

	mov si,dx			; get DPMI handle from XMS handle

int310501xgotmem:
	mov ah,0ch			; lock memory block
	call int310500xcallxms
	or ax,ax			; error?
	jnz short @@int310501xf0	; if no, go on

	push bx				; yup, preserve error number

	mov ah,0ah			; free block causing lock error
	call int310500xcallxms

	pop bx				; restore error number
	jmp int310500xerror		; XMS error, return with DPMI error

@@int310501xf0:
	mov di,cx			; low word of DPMI handle is size in K

	mov cx,bx			; XMS linear address to DPMI regs
	mov bx,dx

	add cx,0fh			; align linear address on paragraph
	adc bx,0
	and cl,0f0h

	jmp int31oksinoax		; return ok, with SI, DI, BX, CX
; [fold]  ]

;─────────────────────────────────────────────────────────────────────────────
; [fold]  [
int310502x: ; XMS free memory block
	mov dx,si			; get XMS handle from DPMI handle

	mov ah,0dh			; unlock memory block
	call int310500xcallxms
	or ax,ax			; error?
	jz int310500xerror		; if XMS error, return with DPMI error

	mov ah,0ah			; free memory block
	call int310500xcallxms
	or ax,ax			; error?
	jz int310500xerror		; if yes, convert XMS error to DPMI

	jmp int31ok			; return ok
; [fold]  ]

;─────────────────────────────────────────────────────────────────────────────
; [fold]  [
int310503x: ; XMS resize memory block
	call int310500xbxcxtodx		; convert BX:CX bytes to DX K
	mov cx,dx			; preserve size of block in K
	mov dx,si			; get XMS handle from DPMI handle

	mov ah,0dh			; unlock memory block
	call int310500xcallxms
	or ax,ax			; error?
	jz int310500xerror		; if XMS error, return with DPMI error

	mov bx,cx			; BX = new size in K
	mov ah,0fh			; resize memory block
	call int310500xcallxms
	mov dx,si			; get XMS handle again
	or ax,ax			; error in resize?
	jnz int310501xgotmem		; if no, go to memory block code

	push bx				; yup, preserve error number

	mov ah,0ch			; lock memory block
	call int310500xcallxms

	pop bx				; restore error number
	jmp int310500xerror		; XMS error, return with DPMI error
; [fold]  ]

;─────────────────────────────────────────────────────────────────────────────
; [fold]  [
int31050ax: ; XMS get memory block size and base
	mov dx,si			; get XMS handle from DPMI handle
	mov si,di			; SI = size of block in K

	mov ah,0dh			; unlock memory block
	call int310500xcallxms
	or ax,ax			; error?
	jz int310500xerror		; if XMS error, return with DPMI error

	mov ah,0ch			; lock memory block
	call int310500xcallxms
	or ax,ax			; error?
	jz int310500xerror		; if XMS error, return with DPMI error

	xor di,di			; convert size in K to size in bytes
	shrd di,si,6
	shr si,6

	mov cx,bx			; XMS linear address to DPMI regs
	mov bx,dx

	mov ax,cx			; figure out alignment stub
	dec ax
	and ax,0fh
	xor al,0fh

	sub di,ax			; subtract alignment stub from size
	sbb si,0

	add cx,0fh			; align linear address on paragraph
	adc bx,0
	and cl,0f0h

	jmp int31oksinoax		; return ok, with SI, DI, BX, CX
; [fold]  ]

;─────────────────────────────────────────────────────────────────────────────
; [fold]  [
int310800xr: ; physical address mapping (both XMS and raw)
	shl ebx,16
	mov bx,cx
	cmp ebx,100000h		; below 1st MB?
	jb int31fail8021	; DPMI says to fail then

	or si,di
	jz int31fail8021	; size is zero
	jmp int31ok		; physical and linear address is the same
; [fold]  ]

;─────────────────────────────────────────────────────────────────────────────
int310801xr: ; free physical address mapping (both XMS and raw)
	jmp int31ok		; physical and linear address is the same

; [fold]  ]

;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
; RAW EXTENDED MEMORY FUNCTIONS
;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
; [fold]  [

;-----------------------------------------------------------------------------
; [fold]  [
int310500rnomem: ; no free extended memory present
	xor eax,eax			; 0 available extended memory
	jmp int310500xsetbuf		; put memory information in buffer
; [fold]  ]

;-----------------------------------------------------------------------------
; [fold]  [
int310500rbxcxtoebx: ; convert BX:CX bytes to EBX bytes
	pop bp				; pop return address

	shl ebx,16			; EBX = BX:CX
	mov bx,cx

	or ebx,ebx			; check for invalid value
	jz int31fail8021		; if invalid value, error 8021h

	add ebx,0fh			; align EBX on paragraph
	and bl,0f0h

	jmp bp				; return ok
; [fold]  ]

;-----------------------------------------------------------------------------
; [fold]  [
int310500rfindmcb: ; find MCB for handle SI:DI
	pop bp				; pop return address

	shl esi,16			; ESI = handle SI:DI = address of MCB
	mov si,di

	mov edi,[rawextmemtop]		; EDI -> first memory control block

@@int310500rfindmcbl0:
	cmp edi,esi			; found MCB?
	jne short @@int310500rfindmcbl0f0	; if no, keep looking

	cmp [byte ptr coreseg:edi-4],0	; memory block free?
	je int31fail8023		; if yes, error 8023h

	jmp bp				; return ok, found MCB

@@int310500rfindmcbl0f0:
	mov edi,[coreseg:edi-12]	; EDI -> next memory control block
	or edi,edi			; is there another MCB?
	jnz @@int310500rfindmcbl0	; if yes, loop

	jmp int31fail8023		; fail, error 8023h
; [fold]  ]

;-----------------------------------------------------------------------------
; [fold]  [
int310500radjustused: ; adjust INT 15h extended memory used
	mov eax,[rawextmemtop]		; EAX -> first memory control block

@@int310500radjustusedl0:
	cmp [dword ptr coreseg:eax-12],0	; last memory control block?
	jz short @@int310500radjustusedf0	; if yes, go to set new used K

	mov eax,[coreseg:eax-12]	; EAX -> next memory control block
	jmp @@int310500radjustusedl0	; loop

@@int310500radjustusedf0:
	cmp [byte ptr coreseg:eax-4],0		; memory block free?
	je short @@int310500radjustusedf1	; if no, go on

	sub eax,[coreseg:eax-16]	; used, adjust by size of block

@@int310500radjustusedf1:
	sub eax,10h			; adjust by size of MCB
	and eax,0fffffc00h		; align on K
	sub eax,[rawextmemtop]		; size of extended memory used
	neg eax
	shr eax,10			; convert from bytes to K

	mov [rawextmemused],ax		; adjust INT 15h extended memory used

@@int310500radjustuseddone:
	ret				; return
; [fold]  ]

;-----------------------------------------------------------------------------
; [fold]  [
int310500rlinkmcb: ; link memory blocks at ESI and EDI
	mov eax,[coreseg:esi-16]	; combine two block sizes
	add eax,10h
	add [coreseg:edi-16],eax	; add size of next block to this one
	mov ecx,[coreseg:esi-12]	; copy next MCB field
	mov [coreseg:edi-12],ecx
	jecxz short @@int310500rlinkmcbf0	; if no next MCB, done

	mov [coreseg:ecx-8],edi		; set prev MCB in next MCB to this MCB

@@int310500rlinkmcbf0:
	ret				; return
; [fold]  ]

;─────────────────────────────────────────────────────────────────────────────
; [fold]  [
int310500r: ; raw get free memory information
	mov edi,[rawextmemtop]		; EDI -> first memory control block

	xor eax,eax			; running highest free memory block
@@int310500rl0:
	cmp [byte ptr coreseg:edi-4],0	; is block free?
	jne short @@int310500rl0f0	; if no, loop

	mov ebx,[coreseg:edi-16]	; EBX = size of block
	cmp eax,ebx			; last free block larger?
	ja short @@int310500rl0f0	; if yes, loop

	mov eax,ebx			; found larger block, new largest

@@int310500rl0f0:
	mov edi,[coreseg:edi-12]	; EDI -> next memory control block
	or edi,edi			; is there another MCB?
	jnz @@int310500rl0		; if yes, loop

	jmp int310500xsetbuf		; put memory information in buffer
; [fold]  ]

;─────────────────────────────────────────────────────────────────────────────
; [fold]  [
int310501r: ; raw allocate memory block
	call int310500rbxcxtoebx	; convert BX:CX bytes to EBX bytes

	mov edi,[rawextmemtop]		; EDI -> first memory control block

@@int310501rl0:
	cmp [byte ptr coreseg:edi-4],0	; is block free?
	je short @@int310501rl0f2	; if yes, check block

@@int310501rl0f0:
	mov edi,[coreseg:edi-12]	; EDI -> next memory control block
	or edi,edi			; is there another MCB?
	jnz @@int310501rl0		; if yes, loop

	jmp int31fail8013		; fail, error 8013h

@@int310501rl0f2:
	lea ecx,[coreseg:edi-10h]	; ECX -> possible new MCB
	sub ecx,ebx

	mov eax,[coreseg:edi-16]	; EAX = size of block
	sub eax,ebx			; enough free memory in block?
	jc short @@int310501rl0f0	; if no, loop

	jz short @@int310501rl0f1	; if exactly same size, continue

	sub eax,10h			; adjust for size of new created MCB
	mov [coreseg:ecx-16],eax	; put size of new block in new MCB
	mov eax,ecx			; set next MCB in old MCB as new one
	xchg [coreseg:edi-12],eax	; copy next MCB from old to new MCB
	mov [coreseg:ecx-12],eax
	or eax,eax			; is there a next MCB?
	jz short @@int310501rl0f3	; if no, go on

	mov [coreseg:eax-8],ecx		; set prev MCB in next MCB to new MCB

@@int310501rl0f3:
	mov [coreseg:ecx-8],edi		; set prev MCB in new MCB as old one
	mov [byte ptr coreseg:ecx-4],0	; set new MCB as free
	mov [coreseg:edi-16],ebx	; set size of allocated block

@@int310501rl0f1:
	mov [byte ptr coreseg:edi-4],1	; set block as allocated

int310501raddxnhandle:
	shld ebx,ecx,16			; BX:CX = ECX, address of block
	shld esi,edi,16			; SI:DI = EDI, handle (address of MCB)

	call int310500radjustused	; adjust INT 15h extended memory used

	jmp int31oksinoax		; return ok, with SI, DI, BX, CX
; [fold]  ]

;─────────────────────────────────────────────────────────────────────────────
; [fold]  [
int310502r: ; raw free memory block
	call int310500rfindmcb		; find MCB for handle SI:DI

	mov [byte ptr coreseg:edi-4],0	; set this memory block as free

	mov esi,[coreseg:edi-12]	; ESI -> next memory control block
	or esi,esi			; is there next MCB?
	jz short @@int310502rf0		; if no, go on

	cmp [byte ptr coreseg:esi-4],0	; is next memory block free?
	jne short @@int310502rf0	; if no, go on

	call int310500rlinkmcb		; link two memory blocks

@@int310502rf0:
	mov esi,[coreseg:edi-8]		; ESI -> previous memory control block
	or esi,esi			; is there previous MCB?
	jz short @@int310502rf1		; if no, go on

	cmp [byte ptr coreseg:esi-4],0	; is previous memory block free?
	jne short @@int310502rf1	; if no, go on

	xchg esi,edi			; change mcb order for function
	call int310500rlinkmcb		; link two memory blocks

@@int310502rf1:
	call int310500radjustused	; adjust INT 15h extended memory used

	jmp int31ok			; return ok
; [fold]  ]

;─────────────────────────────────────────────────────────────────────────────
; [fold]  [
int310503r: ; raw resize memory block
	call int310500rbxcxtoebx	; convert BX:CX bytes to EBX bytes
	mov edx,ebx			; EDX = size of new block

	call int310500rfindmcb		; find MCB for handle SI:DI

	copy ds,SELCORE,ax		; for easier block copy
	copy es,ds,ax			; ES = DS for possible block copy

	sub ebx,[esi-16]		; EBX = change in block size
	jz @@int310503rf0		; if no change, done

	jc @@int310503rf1		; if block made smaller, just free top

	xor eax,eax			; running memory counter

	mov ecx,[esi-12]		; ECX -> next MCB
	jecxz short @@int310503rf4	; if no next MCB, check previous MCB

	cmp [byte ptr ecx-4],0		; next MCB free?
	jne short @@int310503rf4	; if not, check previous MCB

	mov eax,[ecx-16]		; EAX = amount of free memory in block
	add eax,10h			;  including memory control block

@@int310503rf4:
	mov ecx,[esi-8]			; ECX -> previous MCB
	jecxz short @@int310503rf5	; if no previous MCB, check memory

	cmp [byte ptr ecx-4],0		; previous MCB free?
	jne short @@int310503rf5	; if not, check memory

	add eax,[ecx-16]		; add amount of free memory in block
	add eax,10h			;  including memory control block

@@int310503rf5:
	cmp eax,ebx			; enough to resize within area
	jb @@int310503rf3		; if no, try to allocate

	or ecx,ecx			; is there a previous MCB?
	jz @@int310503rf6		; if no, resize to next

	cmp [byte ptr ecx-4],0		; previous MCB free?
	jne short @@int310503rf6	; if not, resize to next

	mov ebp,[ecx-16]		; EBP = size of previous block, try to
	add ebp,10h			;  resize within previous block

	sub ebp,ebx			; EBP = prev block size - needed size
	jbe short @@int310503rf7	; if prev block too small, go on

	mov edi,ecx			; EDI -> new MCB for new memory block
	sub edi,ebp			;  resize will fit entirely

	lea eax,[ebp-10h]		; EAX = new size of free block
	mov [ecx-16],eax		; store mew free size in prev MCB
	mov [edi-16],edx		; store new size in new MCB
	mov [ecx-12],edi		; set next MCB in prev MCB to new MCB
	mov [edi-8],ecx			; set prev MCB in new MCB to prev MCB
	mov [byte ptr edi-4],1		; set new MCB as used

	mov ecx,[esi-12]		; copy next MCB field
	mov [edi-12],ecx
	or ecx,ecx			; is there a next MCB
	jz @@int310503rf0		; if no, go to block readjust

	mov [ecx-8],edi			; set prev MCB in next MCB to new MCB

	jmp @@int310503rf0		; go to block readjust

@@int310503rf7:
	mov edi,ecx			; EDI -> new MCB for new memory block

	lea eax,[edx+ebp]		; EAX = size of new block
	mov [edi-16],eax		; store mew free size in new MCB
	mov [byte ptr edi-4],1		; set new MCB as used

	mov ecx,[esi-12]		; copy next MCB field
	mov [edi-12],ecx
	jecxz short @@int310503rf8	; if no next MCB, go on

	mov [ecx-8],edi			; set prev MCB in next MCB to new MCB

@@int310503rf8:
	add ebp,ebx			; EBP = size of block just acquired
	sub ebx,ebp			; EBX = new size still needed
	jz @@int310503rf0		; if no more space needed, done

	mov esi,edi			; ESI -> new MCB

@@int310503rf6:
	mov edi,[esi-12]		; EDI -> next MCB

	mov ecx,[edi-12]		; copy next MCB field
	mov [esi-12],ecx
	jecxz short @@int310503rf9	; if no next MCB, go on

	mov [ecx-8],esi			; set prev MCB in next MCB to this MCB

@@int310503rf9:
	mov eax,[edi-16]		; EAX = size of next memory block
	add eax,10h

	sub edi,eax			; EDI -> start of next memory block
	sub eax,ebx			; EAX = amount of free space left

	mov ecx,[esi-16]		; ECX = old size of this memory block
	mov [esi-16],edx		; store new size in this MCB
	mov [byte ptr esi-4],1		; set this MCB as used

	sub esi,ecx			; ESI -> start of this memory block
	sub esi,10h

	shr ecx,2			; copy this memory block down in mem
	rep movs [dword ptr es:edi],[dword ptr ds:esi]
	dnop

	add esi,10h			; adjust ESI to top MCB
	mov edi,esi			; EDI -> top MCB

	mov ebx,eax			; EBX = negative of space free at top
	neg ebx
	jz @@int310503rf0		; in no space free, go to done

	jmp short @@int310503rf1	; set new MCBs for moved block

@@int310503rf3:
	mov ebp,edi			; preserve old block MCB address
	sub edx,ebx			; EDX = size of old block

	mov bx,[esp+16]			; BX:CX = new block size from stack
	mov cx,[esp+24]
	mov ax,501h			; try to allocate new block
	int 31h
	jc int31failax			; if could not, fail with error AX

	shrd eax,esi,16			; EAX -> new block MCB
	mov ax,di

	shrd edi,ebx,16			; EDI -> start of new block
	mov di,cx

	lea esi,[ebp-10h]		; ESI -> start of old block
	sub esi,edx

	mov ecx,edx			; copy memory from old block to new
	shr ecx,2
	rep movs [dword ptr es:edi],[dword ptr ds:esi]
	dnop

	mov edx,eax			; EDX -> new block MCB

	shld esi,ebp,16			; SI:DI = handle of old block
	mov di,bp
	mov ax,0502h			; free old block
	int 31h

	mov edi,edx			; EDI -> new block MCB for done

	jmp short @@int310503rf0	; go to done

@@int310503rf1:
	lea edi,[esi+ebx]		; EDI -> new MCB for new memory block

	lea eax,[ebx+10h]		; EAX = size of freed block
	neg eax
	mov [esi-16],eax		; store freed size in old MCB
	mov [edi-16],edx		; store new size in new MCB

	mov ecx,[esi-12]		; copy next MCB field
	mov [edi-12],ecx
	jecxz short @@int310503rf2	; if no next MCB, go on

	mov [ecx-8],edi			; set prev MCB in next MCB to new MCB

@@int310503rf2:
	mov [esi-12],edi		; set next MCB in old MCB as new MCB
	mov [edi-8],esi			; set prev MCB in new MCB as old MCB
	mov [byte ptr edi-4],1		; set new MCB as used
	mov [byte ptr esi-4],0		; set old MCB as free

	mov ecx,[esi-8]			; ECX -> prev MCB of old MCB
	jecxz short @@int310503rf0	; if no prev MCB, done

	cmp [byte ptr ecx-4],0		; is previous MCB free?
	jne short @@int310503rf0	; if no, dont link

	push edi			; preserve new MCB
	mov edi,ecx			; top MCB = prev MCB for link function
	call int310500rlinkmcb		; link two memory blocks
	pop edi				; restore new MCB

@@int310503rf0:
	mov ecx,[edi-16]		; ECX = base address of new block
	neg ecx
	lea ecx,[ecx+edi-10h]

	push SELDS
	pop ds				; restore old DS
	jmp int310501raddxnhandle	; return address and handle
; [fold]  ]

;─────────────────────────────────────────────────────────────────────────────
; [fold]  [
int31050ar: ; raw get memory block size and base
	call int310500rfindmcb		; find MCB for handle SI:DI

	mov edi,[coreseg:esi-16]	; EDI = size of memory block
	lea ecx,[coreseg:esi-10h]	; get base address of memory block
	sub ecx,edi

	shld ebx,ecx,16			; BX:CX = ECX, address of block
	shld esi,edi,16			; SI:DI = EDI, size of memory block

	jmp int31oksinoax		; return ok, with SI, DI, BX, CX
; [fold]  ]

; [fold]  ]
	ENDS
	END

; [fold]  142
[ RETURN TO DIRECTORY ]