?resize TRUE
?include "pmode.h--"
?alignword TRUE
?include "write.h--"
word errmsgtbl={#errmsg0,#errmsg1,#errmsg2,#errmsg3,#errmsg4,#errmsg5,#errmsg6};
char errmsg0="Not enough low memory!\n";
char errmsg1="80386 or better not detected!\n";
char errmsg2="System already in protected mode and no VCPI or DPMI found!\n";
char errmsg3="DPMI host is not 32bit!\n";
char errmsg4="Could not enable A20 gate!\n";
char errmsg5="Could not enter DPMI 32bit protected mode!\n";
char errmsg6="Could not allocate needed DPMI selectors!\n";

char msg0="Hello World from Protected Mode...\n"
          "Press any key to continue...\n$";
char msg1="Hello World from a Real Mode Callback...\n"
          "Press any key to exit...\n$";

struct _REGDATA_
{
	dword reg_edi;
	dword reg_esi;
	dword reg_ebp;
	dword rez;
	dword reg_ebx;
	dword reg_edx;
	dword reg_ecx;
	dword reg_eax;
	word reg_flags;
	word reg_es;
	word reg_ds;
	word reg_fs;
	word reg_gs;
	word reg_ip;
	word reg_cs;
	word reg_sp;
	word reg_ss;
}reg;

word dataselector;
word segpdata;

main()
inline
{
	_pm_info();          // get information
	IF(CARRYFLAG){
startf1:
		SI=AX;       // print error message for code AX
		SI+=AX;
		WRITESTR(errmsgtbl[SI]);
		EXIT(byte -1);
	}
	segpdata=GETMEM(BX);
	IF(AX==0)GOTO startf1;
	ES=AX;
	reg.reg_ds=DS;        // set DS:DX for DOS string put
	_pm_init();           // enter protected mode
	$jc startf1           // if error, go to error message
	dataselector=DS;      // now in protected mode, preserve data selector
	ES=DS;
	EDI=#reg;             // offset of register structure
	CX=0;                 // no parameters on stack
	BX=0x21;              // call interrupt 21h
	AX=0x300;             // INT 31h function 0300h
	DSBYTE[#reg.reg_eax[1]]=9;// function code 9, put string
	DSWORD[#reg.reg_edx]=#msg0;
	reg.reg_ss=0;         // SS:SP = 0, PMODE will provide stack
	reg.reg_sp=0;
	$int 0x31             // do the call to real mode
	AH=0;                 // wait for keypress
	$int 0x16
	ES=SS;                // ES = SS for register structure
	ESP-=sizeof(reg);     // stack space for register structure
	EDI=ESP;              // EDI = offset of register structure
	DS=CS;                // DS:ESI = callback routine
	ESI=#callbacke;
	AX=0x303;             // INT 31h function 0303h
	$int 0x31             // allocate real mode callback
	ESP-=sizeof(reg);     // stack space for register structure
	EDI=ESP;              // EDI = offset of register structure
	SSDWORD[ESP+0x2e]=0;  // SS:SP = 0 in register structure
	SSWORD[ESP+0x2a]=DX;  // IP is offset of real mode callback
	SSWORD[ESP+0x2c]=CX;  // CS is segment of real mode callback
	SSWORD[ESP+0x20]=0;   // zero FLAGS in register structure
	CX=0;                 // no parameters on stack
	BH=0;                 // BH must be 0
	AX=0x301;             // INT 31h function 0301h
	$int 0x31             // call real mode callback
	ESP+=sizeof(reg)*2;   // free stack space
	EXIT();              //exit to DOS
}

callbacke()
inline
{
	$cld
	$adrsize
	$lodsd
	ESDWORD[EDI+0x2a]=EAX;  //  stack, and update return address
	ESWORD[EDI+0x2e]+=4;    // adjust SP in register structure
	$push ES
	$push EDI               // preserve register structure ES:EDI
	ES=CSWORD[#dataselector];// ES = main stream data selector
	EDI=#reg;               // offset of register structure
	CX=0;                   // no parameters on stack
	BX=0x21;                // call interrupt 21h
	AX=0x300;               // INT 31h function 0300h
	ESWORD[#reg.reg_edx]=#msg1;// offset of new string, other fields
                                //  were set previously
	$int 0x31               // do the call to real mode
	AH=0;                   // wait for keypress
	$int 0x16
	$pop EDI
	$pop ES                 // restore register structure ES:EDI
	$iretd                  // done with callback
}
