/*****************************************************************************
 * FILE: start32							     *
 *									     *
 * DESC:								     *
 *	- DPMI-switches for DPMI 0.9					     *
 *	- init some protected mode interrupts				     *
 *	- init exception handlers					     *
 *	- clean up for exit						     *
 *									     *
 * Copyright (C) 1993,1994						     *
 *	Rainer Schnitker, Heeper Str. 283, 33607 Bielefeld		     *
 *	email: rainer@mathematik.uni-bielefeld.de			     *
 *									     *
 *****************************************************************************/

#include "DPMI.H"
#include "DPMI10.H"
#include "PRINTF.H"
#include "PROCESS.H"
#include "RSX.H"
#include "ADOSX32.H"
#include "CDOSX32.H"
#include "EXCEP32.H"
#include "START32.H"
#include "COPY32.H"
#include "SYSDEP.H"

/* global extender segments,selectors */
UINT cs16real, ds16real;	/* 16-bit segments for extender */
UINT code16sel, data16sel;	/* 16-bit cs,ds for extender */
UINT stack16sel;		/* 16-bit stack sel */
DWORD stackp16; 		/* 16-bit stack offset (use long!) */
UINT sel_incr;			/* increment to next selector */
UINT dosmem_sel;		/* selector for the first MB */
UINT fpu_status;
WORD DPMIversion = 0;
WORD previous_client_cs;

char dpmi10;			/* DPMI 1.0 server */
char dpmi10_map;		/* DPMI 1.0 mapping supported */
char dpmi10_page;		/* DPMI 1.0 access/dirty supported */
char dpmi10_zero;		/* DPMI 1.0 demand zero supported */

/* private vars */
static WORD DPMIdata_para_needed;
static WORD DPMIdata_segm_address;
static char fpu_status_init;

/*
** back to real-mode, terminate ( int0x21 must set to orginal )
*/
#ifndef __EMX__
extern void ProtectedModeSwitch(void);
extern void RealModeSwitch(void);
extern void _cexit(void);
#endif

void protected_to_real(WORD errorlevel)
{
    if (fpu_status_init)
	DpmiSetCoproStatus(fpu_status & 3);

#ifdef _MSC_VER
    RealModeSwitch();		/* clean up in real mode */
    _cexit();
    ProtectedModeSwitch();
#endif
#ifdef _TURBOC_
    RealModeSwitch();		/* clean up interrupt vector */
    _restorezero();
    ProtectedModeSwitch();
#endif

    dos_exit(errorlevel);
    /* program ends here */
}

void init_real_mode(void)
{
#ifdef __EMX__
    DWORD base_addr;	/* rsx32 is in protected mode */

    code16sel = GetCS();
    data16sel = stack16sel = GetDS();

    GetBaseAddress(code16sel, &base_addr);
    cs16real = (UINT) (base_addr >> 4);
    GetBaseAddress(data16sel, &base_addr);
    ds16real = (UINT) (base_addr >> 4);

#else			/* rsx16 is in real mode */
    cs16real = GetCS();
    ds16real = GetDS();
#endif
}

/*
** switch to protected-mode via DPMI-host
*/
int real_to_protected(WORD mode)
{
#ifndef __EMX__
    WORD DPMIflags;
    BYTE processor;
    DWORD PM_jump;		/* switch to protmode jump */

    clearregs();
    /* setbuf(stdout,NULL); */

    if (GetDpmiEntryPoint(&PM_jump, &DPMIdata_para_needed,
			  &DPMIflags, &DPMIversion, &processor)) {
	puts("No DPMI-host found!");
	return -1;
    }
    if (mode == 1 && !(DPMIflags & 1)) {
	puts("32bit programs not supported\n");
	return -1;
    }
    if (DPMIdata_para_needed) { /* get DPMI ring 0 stack */
	DPMIdata_segm_address = GetDpmiHostParagraph(DPMIdata_para_needed);
	if (!DPMIdata_segm_address) {
	    puts("Can't alloc memory for the DPMI-host-stack");
	    return -1;
	}
    }
    if (DpmiEnterProtectedMode(PM_jump, mode, DPMIdata_segm_address)) {
	puts("can't switch to Protected Mode");
	return -1;
    }
    /* Now we are in Protected Mode */

    /* lock DPMI-stack */
    if (DPMIdata_para_needed) {
	LockLinRegion((DWORD) DPMIdata_segm_address << 4,
		      (DWORD) DPMIdata_para_needed << 4);
    }
    /* get prot-mode extender segments */
    code16sel = GetCS();
    data16sel = stack16sel = GetDS();
#endif
    return 0;
}

/*
** test DPMI server
*/
int test_dpmi_capabilities(void)
{
    if (!DPMIversion) {
	DPMIVERSION ver;

	GetDPMIVersion(&ver);
	DPMIversion = ((WORD)ver.major << 8) | ver.minor;

	if (opt_printall)
	    printf("DPMI version %d.%d\n", ver.major, ver.minor);
    }

    if (!opt_force_dpmi09) {
	DPMICAP cap;
	if ((DPMIversion >> 8) >= 1)
	    dpmi10 = 1;
	if (!GetDPMICapabilities(&cap, iobuf)) {
	    if (cap.bits & 1)
		dpmi10_page = 1;
	    if (cap.bits & 8)
		dpmi10_map = 1;
	    if (cap.bits & 16)
		dpmi10_zero = 1;
	    if (dpmi10_page & dpmi10_map)	/* give 0.9x a chance */
		dpmi10 = 1;
	}
    }

    sel_incr = SelInc();

    /* build selector for first megabyte */
    AllocLDT(1, &dosmem_sel);
    SetBaseAddress(dosmem_sel, 0L);
    SetAccess(dosmem_sel, APP_DATA_SEL, BIG_BIT | GRANULAR_BIT);
    SetLimit(dosmem_sel, 1024L * 1024L - 1L);

    return 0;
}

static POINTER16_32 int21v;	/* old int21h handler address */
static POINTER16_32 int10v;	/* old int10h handler address */
static POINTER16_32 int33v;	/* old int33h handler address */
static POINTER16_32 ctrlcv;	/* old control-c handler */
static POINTER16_32 timerv;	/* old timer handler */
static POINTER16_32 debintv;	/* old int3 handler */
static POINTER16_32 excep0v, excep1v, excep2v, excep3v, excep4v, excep5v,
 excep6v, excep7v, excep8v, excep9v, excep10v, excep11v, excep12v, excep13v, excep14v,
 excep15v, excep16v, excep17v;

int hangin21;

int hangin_extender(void)
{
    UINT alias_cs;
    int stackp;

    /* entry stack for interrupts, exceptions */
    stackp16 = (UINT) & stackp;

    /* store ds-sel in code for some interrupt handler */
    if (CreatAlias(code16sel, &alias_cs))
	return -1;

    /* store 16bit data selector in load_ds function */
    store32(alias_cs, (DWORD) (UINT) extender_ds, (DWORD) data16sel);

    /* hang in int 0x21 */
    if (GetProtModeVector32(0x21, &int21v.sel, &int21v.off) == -1) {
	puts("error:can't get int21");
	return -1;
    }
    if (SetProtModeVector32(0x21, code16sel, (DWORD) (UINT) doscall) == -1) {
	puts("error:can't set int21");
	return -1;
    }
    hangin21 = 1;
    previous_client_cs = int21v.sel;
    align_iobuf();

    /* chain to default int 0x21 */
    store32(alias_cs, (DWORD) (unsigned) &int21vsel, (DWORD) int21v.sel);
    store32(alias_cs, (DWORD) (unsigned) &int21voff, (DWORD) int21v.off);

    /* set new bios int10 for vesa calls */
    if (GetProtModeVector32(0x10, &int10v.sel, &int10v.off)<0)
	puts("int10 fail:get");
    if (SetProtModeVector32(0x10, code16sel, (DWORD) (UINT) bioscall)<0)
	puts("int10 fail:set");
    store32(alias_cs, (DWORD) (unsigned) &int10vsel, (DWORD) int10v.sel);
    store32(alias_cs, (DWORD) (unsigned) &int10voff, (DWORD) int10v.off);

    FreeLDT(alias_cs);

    /* MOUSE */
    GetProtModeVector32(0x33, &int33v.sel, &int33v.off);
    SetProtModeVector32(0x33, code16sel, (DWORD) (UINT) mousecall);


    /* get all exception vectors */
    GetExceptionVector32(0, &excep0v.sel, &excep0v.off);
    GetExceptionVector32(1, &excep1v.sel, &excep1v.off);
    GetExceptionVector32(2, &excep2v.sel, &excep2v.off);
    GetExceptionVector32(3, &excep3v.sel, &excep3v.off);
    GetExceptionVector32(4, &excep4v.sel, &excep4v.off);
    GetExceptionVector32(5, &excep5v.sel, &excep5v.off);
    GetExceptionVector32(6, &excep6v.sel, &excep6v.off);
    GetExceptionVector32(7, &excep7v.sel, &excep7v.off);
    GetExceptionVector32(8, &excep8v.sel, &excep8v.off);
    GetExceptionVector32(9, &excep9v.sel, &excep9v.off);
    GetExceptionVector32(10, &excep10v.sel, &excep10v.off);
    GetExceptionVector32(11, &excep11v.sel, &excep11v.off);
    GetExceptionVector32(12, &excep12v.sel, &excep12v.off);
    GetExceptionVector32(13, &excep13v.sel, &excep13v.off);
    GetExceptionVector32(14, &excep14v.sel, &excep14v.off);
    GetExceptionVector32(15, &excep15v.sel, &excep15v.off);
    GetExceptionVector32(16, &excep16v.sel, &excep16v.off);
    GetExceptionVector32(17, &excep17v.sel, &excep17v.off);

    /* set all exception vectors */
    if (!opt_debugrsx) {
	SetExceptionVector32(1, code16sel, (DWORD) (UINT) (excep1_386));
	SetExceptionVector32(3, code16sel, (DWORD) (UINT) (excep3_386));
    }
    SetExceptionVector32(0, code16sel, (DWORD) (UINT) (excep0_386));
    SetExceptionVector32(2, code16sel, (DWORD) (UINT) (excep2_386));
    SetExceptionVector32(4, code16sel, (DWORD) (UINT) (excep4_386));
    SetExceptionVector32(5, code16sel, (DWORD) (UINT) (excep5_386));
    SetExceptionVector32(6, code16sel, (DWORD) (UINT) (excep6_386));
    SetExceptionVector32(7, code16sel, (DWORD) (UINT) (excep7_386));
    SetExceptionVector32(8, code16sel, (DWORD) (UINT) (excep8_386));
    SetExceptionVector32(9, code16sel, (DWORD) (UINT) (excep9_386));
    SetExceptionVector32(10, code16sel, (DWORD) (UINT) (excep10_386));
    SetExceptionVector32(11, code16sel, (DWORD) (UINT) (excep11_386));
    SetExceptionVector32(12, code16sel, (DWORD) (UINT) (excep12_386));
    SetExceptionVector32(13, code16sel, (DWORD) (UINT) (excep13_386));
    SetExceptionVector32(15, code16sel, (DWORD) (UINT) (excep15_386));
    SetExceptionVector32(16, code16sel, (DWORD) (UINT) (excep16_386));
    SetExceptionVector32(17, code16sel, (DWORD) (UINT) (excep17_386));
    if (dpmi10)
	SetExceptionVector32(14, code16sel, (DWORD) (UINT) (page_fault));
    else
	SetExceptionVector32(14, code16sel, (DWORD) (UINT) (excep14_386));

    /* TIMER */
    GetProtModeVector32(0x1C, &timerv.sel, &timerv.off);
    SetProtModeVector32(0x1C, code16sel, (DWORD) (UINT) timer_handler);

    /* CTRL-C */
    GetProtModeVector32(0x23, &ctrlcv.sel, &ctrlcv.off);
    SetProtModeVector32(0x23, code16sel, (DWORD) (UINT) prot_cbrk);

    /* debug int3 */
    if (!opt_debugrsx) {
	GetProtModeVector32(0x03, &debintv.sel, &debintv.off);
	SetProtModeVector32(0x03, code16sel, (DWORD) (UINT) debug_entry);
    }
    return 0;
}

/*
** init fpu
*/
int init_fpu(void)
{
    /* Get FPU status, if user allow this (not -e0) */
    if (opt_force_copro != 1) {
	if (DpmiGetCoproStatus(&fpu_status))
	    fpu_status = 0;
	if (opt_printall)
	    printf("fpu_status = 0x%X\n", fpu_status);
    }

    /*
    ** copro = 0 , with -e option : if no 387, set copro = 2
    ** copro = 1 , default : enable FPU
    ** copro = 3 , if no FPU : enable kernel FPU emulation
    */
    if (copro == 0 && !npx_present)	/* catch missing 387 */
	if (! opt_force_copro)
	    copro = 2;
    if (copro) {
	fpu_status_init = 1;
	if (DpmiSetCoproStatus(copro))
	    puts("Warning: No DPMI-server FPU support");
    }
    return 0;
}

static void free_emu_mem(void)
{
    UnlockLinRegion(RSX_PROCESS.memaddress, RSX_PROCESS.membytes);
    if (rsx387_in_dosmem)
	FreeDosMem((WORD) RSX_PROCESS.memhandle);
    else
	FreeMem(RSX_PROCESS.memhandle);
    FreeLDT(RSX_PROCESS.code32sel);
    FreeLDT(RSX_PROCESS.data32sel);
    FreeLDT(RSX_PROCESS.data32sel + sel_incr);
}

/* clean up interrupt handlers, exceptions, memory ... */
void clean_up(void)
{
    flush_all_buffers();
    clearregs();

    if (opt_printall)
	puts("cleanup now");

    if (DPMIdata_para_needed)
	UnlockLinRegion((DWORD) DPMIdata_segm_address << 4,
			(DWORD) DPMIdata_para_needed << 4);

    /* unset 387-usage (otherwise: crash) */
    if (fpu_status_init) {
	DpmiSetCoproStatus(fpu_status & 3);
	if (copro == 3 && emu_sel != 0)
	    free_emu_mem();
    }
    fpu_status_init = 0;
    copro = 0;

    SetProtModeVector32(0x21, int21v.sel, int21v.off);
    SetProtModeVector32(0x10, int10v.sel, int10v.off);
    SetProtModeVector32(0x1C, timerv.sel, timerv.off);
    SetProtModeVector32(0x23, ctrlcv.sel, ctrlcv.off);
    SetProtModeVector32(0x03, debintv.sel, debintv.off);

    if (!opt_debugrsx) {
	SetExceptionVector32(1, excep1v.sel, excep1v.off);
	SetExceptionVector32(3, excep3v.sel, excep3v.off);
    }
    SetExceptionVector32(0, excep0v.sel, excep0v.off);
    SetExceptionVector32(2, excep2v.sel, excep2v.off);
    SetExceptionVector32(4, excep4v.sel, excep4v.off);
    SetExceptionVector32(5, excep5v.sel, excep5v.off);
    SetExceptionVector32(6, excep6v.sel, excep6v.off);
    SetExceptionVector32(7, excep7v.sel, excep7v.off);
    SetExceptionVector32(8, excep8v.sel, excep8v.off);
    SetExceptionVector32(9, excep9v.sel, excep9v.off);
    SetExceptionVector32(10, excep10v.sel, excep10v.off);
    SetExceptionVector32(11, excep11v.sel, excep11v.off);
    SetExceptionVector32(12, excep12v.sel, excep12v.off);
    SetExceptionVector32(13, excep13v.sel, excep13v.off);
    SetExceptionVector32(14, excep14v.sel, excep14v.off);
    SetExceptionVector32(15, excep15v.sel, excep15v.off);
    SetExceptionVector32(16, excep16v.sel, excep16v.off);
    SetExceptionVector32(17, excep17v.sel, excep17v.off);
}
