/*****************************************************************************
 * FILE: cdosx32.c							     *
 *									     *
 * DESC:								     *
 *	- int 0x21 dos call handler					     *
 *									     *
 * Copyright (C) 1993,1994						     *
 *	Rainer Schnitker, Heeper Str. 283, 33607 Bielefeld		     *
 *	email: rainer@mathematik.uni-bielefeld.de			     *
 *									     *
 *****************************************************************************/

#include "DPMI.H"
#include "PRINTF.H"
#include "RMLIB.H"
#include "PROCESS.H"
#include "FS.H"
#include "SIGNALS.H"
#include "START32.H"
#include "CDOSX32.H"
#include "COPY32.H"
#include "TERMIO.H"
#include "DOSERRNO.H"
#include "DJIO.H"
#include "RSX.H"

/* define segment offset usage */
#ifdef __EMX__
#define SET_SEG_OFF(pointer, seg, off) \
    { seg = (((unsigned) (pointer) & ~0xFFF) >> 4) + ds16real; \
	off = (unsigned) (pointer) & 0xFFF; }

#else				/* 16 bit compiler */
#define SET_SEG_OFF(pointer, seg, off) \
    { seg = ds16real; off = (WORD) (pointer) ;}
#endif

#ifdef __EMX__
#define SET_SEG_OFF_64(pointer, seg, off) \
    { seg = (((unsigned) (pointer) & ~0xFFFF) >> 4) + ds16real; \
	off = (unsigned) (pointer) & 0xFFFF; }

#else				/* 16 bit compiler */
#define SET_SEG_OFF_64(pointer, seg, off) \
    { seg = ds16real; off = (WORD) (pointer) ;}
#endif

extern WORD _psp;		/* PSP after protected mode switch */

static char iobuffer[IOBUF_SIZE + 3];
char *iobuf = iobuffer; 	/* I/O buffer for real-mode operations */
DWORD iobuf_addr;

DWORD iobuf_linear;		/* linear address I/O for djgpp */

void align_iobuf(void)
{
    iobuf = (char *) ((unsigned) (iobuf + 3) & ~3);
    iobuf_addr = ((DWORD) ds16real << 4) + (DWORD) (UINT) iobuf;
    iobuf_linear = iobuf_addr + 4096L;	/* if we use this buffer */

    if (opt_printall)
	printf("lin address of iobuf %lX\n", iobuf_linear);
}


#define CONVERT_BX_DOSHANDLE {	\
    int h = get_dos_handle(BX); \
    if (h < 0) {		\
	EAX = EMX_EBADF;	\
	return CARRY_ON;	\
    }				\
    else BX = h; }


static void put_regs(TRANSLATION *tr)
{
    tr->eax = EAX;
    tr->ebx = EBX;
    tr->ecx = ECX;
    tr->edx = EDX;
}

static void get_regs(TRANSLATION *tr)
{
    EAX = tr->eax;
    EBX = tr->ebx;
    ECX = tr->ecx;
    EDX = tr->edx;
    FLAGS = tr->flags;
}

static char rm_stack[512];
unsigned real_mode_stack = (unsigned) (rm_stack + 510);

/* call Real-Mode INT0x21 */
static int realdos(TRANSLATION *tr)
{
    SET_SEG_OFF_64(real_mode_stack, tr->ss, tr->sp);

    tr->flags = 0x3200;
    tr->eax &= 0xFFFF;
    tr->cs = cs16real;
    tr->es = tr->ds;

    /* DPMI-Call to Real-Mode DOS */
    SimulateRMint(0x21, 0, 0, tr);

    /* return Carry-bit */
    return (tr->flags & 1);
}

static int illegal_syscall()
{
    puts("Illegal Parameter in syscall");
    send_signal(npz, SIGSEGV);
    EAX = EMX_EINTR;
    return CARRY_ON;
}

#define TEST_ILLEGAL( OFFSET, LENGHT ) \
if (verify_illegal(npz, OFFSET, LENGHT)) return illegal_syscall();

#define TEST_ILLEGAL_WRITE( OFFSET, LENGHT ) \
if (verify_illegal_write(npz, OFFSET, LENGHT)) return illegal_syscall();

/*
** INT 0x21 handler returns:
**
** CARRY_ON :	error, set carry-bit, errno in eax
** CARRY_OFF:	no error, clear carry-bit
** CARRY_NON:	carry-bit set by realdos -> dos error code in ax,
**		translate ax to errno
*/
int int21normal(void)
{
    static DWORD user_dta;	/* prot-mode DTA address */
    TRANSLATION tr;		/* registers set for mode-switching */
    int err, new_handle;
    unsigned char rAH, rAL;

    rAH = (BYTE) (AX >> 8);
    rAL = (BYTE) EAX;

    switch (rAH) {
	/*
	** register based functions
	** no changes needed - only ax,dx,flags (bx,cx) used
	*/
    case 0x01:			/* read char */
    case 0x02:			/* write char */
    case 0x03:			/* read char stdaux */
    case 0x04:			/* write char stdaux */
    case 0x05:			/* read char prn */
    case 0x06:			/* con output&input */
    case 0x07:			/* char input & echo */
    case 0x08:			/* char output & echo */
    case 0x0b:			/* get stdin stat */
    case 0x0c:			/* flush buffer & read std input */
    case 0x0d:			/* disk reset */
    case 0x0e:			/* select drive */
    case 0x19:			/* get drive */
    case 0x2a:			/* get system date (cx) */
    case 0x2b:			/* set system date (cx) */
    case 0x2c:			/* get time (cx) */
    case 0x2d:			/* set time (cx) */
    case 0x2e:			/* set verify */
    case 0x30:			/* get DOS version (bx,cx) */
    case 0x33:			/* extended break check */
    case 0x36:			/* get free disk space (bx,cx) */
    case 0x37:			/* get&set switch char */
    case 0x54:			/* get verify flag */
    case 0x58:			/* get/set UMB link state */
    case 0x5c:			/* un-&lock file */
    case 0x66:			/* code page */
	put_regs(&tr);
	realdos(&tr);
	get_regs(&tr);
	return CARRY_NON;
    case 0x44:			/* IOCTL */
	switch (rAL) {
	case 0x00:		/* get device info */
	case 0x01:		/* set device info */
	case 0x06:		/* get input status */
	case 0x07:		/* get output status */
	case 0x0a:		/* check handle remote */
	    CONVERT_BX_DOSHANDLE;
	    /* fall through */
	case 0x08:		/* check block device remove */
	case 0x09:		/* check block device remote */
	case 0x0b:		/* set sharing */
	case 0x0e:		/* get log drive map */
	case 0x0f:		/* set log drive map */
	case 0x10:		/* query ioctl handle */
	case 0x11:		/* query ioctl drive */
	    put_regs(&tr);
	    realdos(&tr);
	    get_regs(&tr);
	    return CARRY_NON;
	default:
	    EAX = EMX_EIO;
	    return CARRY_ON;
	}

	/*
	** simple file handle functions
	** convert handle
	*/
    case 0x57:			/* get file state (bx,cx) */
    case 0x68:			/* fflush file (bx) */
	CONVERT_BX_DOSHANDLE;
	put_regs(&tr);
	realdos(&tr);
	get_regs(&tr);
	return CARRY_NON;

    case 0x3e:			/* close file (bx) */
	err = sys_close(BX);
	if (err < 0) {
	    EAX = emx_errno;
	    return CARRY_ON;
	} else {
	    EAX = 0;
	    return CARRY_OFF;
	}

    case 0x42:			/* lseek file */
	{
	    long pos;

	    if (npz->p_flags & PF_EMX_FILE)
		pos = EDX;
	    else
		pos = (ECX << 16) | (EDX & 0xFFFF);

	    if ((EAX = sys_lseek(BX, pos, rAL))== -1L) {
		EAX = emx_errno;
		return CARRY_ON;
	    }
	    if (npz->p_flags & PF_DJGPP_FILE) {
		EDX = EAX >> 16;
	    }
	    return CARRY_OFF;
	}

    case 0x45:			/* dup file (bx) */
	if ((new_handle = sys_dup(BX)) < 0) {
	    EAX = (long) -new_handle;
	    return CARRY_ON;
	}
	EAX = (long) new_handle;
	return CARRY_OFF;

    case 0x46:			/* dup2 file (bx,cx) */
	if ((new_handle = sys_dup2(BX, CX)) < 0) {
	    EAX = (long) -new_handle;
	    return CARRY_ON;
	}
	EAX = (long) new_handle;
	return CARRY_OFF;

	/*
	** ASCiiZero functions
	**
	** copy name -> real_buffer , call realdos
	*/

    case 0x09:			/* string to output */
	TEST_ILLEGAL(EDX, 2);
	getstr32_16(DS, EDX, iobuf, '$');
	tr.eax = EAX;
	SET_SEG_OFF(iobuf, tr.ds, tr.edx);
	realdos(&tr);
	return CARRY_NON;

    case 0x39:			/* MKDIR name */
    case 0x3a:			/* RMDIR name */
    case 0x3b:			/* CHDIR name */
    case 0x41:			/* UNLINK name */
    case 0x43:			/* ATTRIB name */
	TEST_ILLEGAL(EDX, 2);
	if (rAH == 0x3c)	/* fix umask bug */
	    ECX &= ~1L;
	put_regs(&tr);
	strcpy32_16(DS, EDX, iobuf);
	SET_SEG_OFF(iobuf, tr.ds, tr.edx);
	realdos(&tr);
	get_regs(&tr);
	return CARRY_NON;

    case 0x3c:			/* CREATE name */
    case 0x3d:			/* OPEN name */
    case 0x5b:			/* CREATE New file */
	/*
	** only DOS handles !!
	** others with EMX: 0x7F ; DJGPP 0xFF
	*/
	if (rAH == 0x3c)	/* fix umask bug */
	    ECX &= ~1L;
	TEST_ILLEGAL(EDX, 2);
	strcpy32_16(DS, EDX, iobuf);
	put_regs(&tr);
	SET_SEG_OFF(iobuf, tr.ds, tr.edx);
	if (!realdos(&tr)) {
	    new_handle = get_empty_proc_filp();
	    npz->filp[new_handle]->f_mode = FMODE_READ | FMODE_WRITE;
	    npz->filp[new_handle]->f_doshandle = (BYTE) tr.eax;
	    npz->filp[new_handle]->f_op = & msdos_fop;
	    tr.eax = (long) new_handle;
	}
	get_regs(&tr);
	return CARRY_NON;

    case 0x56:			/* RENAME oldname name (strlen < 1024) */
	TEST_ILLEGAL(EDX, 2);
	TEST_ILLEGAL(EDI, 2);
	strcpy32_16(DS, EDX, iobuf);
	strcpy32_16(DS, EDI, iobuf + 1024);
	put_regs(&tr);
	SET_SEG_OFF(iobuf, tr.ds, tr.edx);
	tr.es = tr.ds;
	tr.edi = tr.edx + 1024;
	realdos(&tr);
	get_regs(&tr);
	return CARRY_NON;

    case 0x5a:			/* CREATE TEMP name */
	TEST_ILLEGAL_WRITE(EDX, 2);
	put_regs(&tr);
	strcpy32_16(DS, EDX, iobuf);
	SET_SEG_OFF(iobuf, tr.ds, tr.edx);
	if (!realdos(&tr)) {
	    strcpy16_32(DS, EDX, iobuf);
	    new_handle = get_empty_proc_filp();
	    npz->filp[new_handle]->f_mode = FMODE_READ | FMODE_WRITE;
	    npz->filp[new_handle]->f_doshandle = (BYTE) tr.eax;
	    npz->filp[new_handle]->f_op = & msdos_fop;
	    tr.eax = (long) new_handle;
	}
	get_regs(&tr);
	return CARRY_NON;

    case 0x6c:			/* EXTENDED OPEN/CREATE file */
	TEST_ILLEGAL(ESI, 2);
	strcpy32_16(DS, ESI, iobuf);
	put_regs(&tr);
	SET_SEG_OFF(iobuf, tr.ds, tr.esi);
	if (!realdos(&tr)) {
	    new_handle = get_empty_proc_filp();
	    npz->filp[new_handle]->f_mode = FMODE_READ | FMODE_WRITE;
	    npz->filp[new_handle]->f_doshandle = (BYTE) tr.eax;
	    npz->filp[new_handle]->f_op = & msdos_fop;
	    tr.eax = (long) new_handle;
	}
	get_regs(&tr);
	return CARRY_NON;

	/*
	** buffer functions
	**
	** copy data before or after realdos
	*/
    case 0x0a:			/* BUFFERED INPUT */
	{
	    BYTE no_chars;

	    TEST_ILLEGAL_WRITE(EDX, 4);
	    put_regs(&tr);
	    no_chars = (BYTE) read32(DS, EDX);
	    iobuf[0] = no_chars;/* no of chars */
	    SET_SEG_OFF(iobuf, tr.ds, tr.edx);
	    realdos(&tr);
	    cpy16_32(DS, EDX, iobuf, (DWORD) no_chars + 2);	/* copy of chars */
	    return CARRY_OFF;
	}

    case 0x3f:			/* READ from file */
	if (EDX != iobuf_linear) {
	    TEST_ILLEGAL(EDX, ECX);
	}
	if (ECX == 0) {
	    EAX = 0;
	    return CARRY_OFF;
	}
	if ((npz->p_flags & PF_DJGPP_FILE) && EDX == 0) {
	    printf("read.o not ok ; run DPMIFIX from DJGPP 1.10\n");
	    EAX = EMX_EIO;
	    return CARRY_ON;
	}

	EAX = sys_read(BX, EDX, ECX);
	if ((long) EAX < 0) {
	    EAX = - (long)EAX;
	    return CARRY_ON;
	} else
	    return CARRY_OFF;

    case 0x40:			/* WRITE to file */
	if (EDX != iobuf_linear) {
	    TEST_ILLEGAL_WRITE(EDX, ECX);
	}
	EAX = sys_write(BX, EDX, ECX);
	if ((long)EAX < 0) {
	    EAX = - (long)EAX;
	    return CARRY_ON;
	} else
	    return CARRY_OFF;

    case 0x38:			/* GET COUNTRY INFO */
	if (DX != 0xFFFF) {
	    TEST_ILLEGAL_WRITE(EDX, 34);
	    put_regs(&tr);
	    SET_SEG_OFF(iobuf, tr.ds, tr.edx);
	    if (!realdos(&tr))
		cpy16_32(DS, EDX, iobuf, 34L);
	    get_regs(&tr);
	} else {
	    put_regs(&tr);
	    realdos(&tr);
	    get_regs(&tr);
	}
	return CARRY_NON;

    case 0x47:			/* GET CURR DIRECTORY */
	TEST_ILLEGAL_WRITE(ESI, 64);
	put_regs(&tr);
	SET_SEG_OFF(iobuf, tr.ds, tr.esi);
	if (!realdos(&tr))
	    cpy16_32(DS, ESI, iobuf, 64L);
	get_regs(&tr);
	return CARRY_NON;

    case 0x4e:			/* FINDFIRST */
	/* DTA: iobuf byte 0-42 , wild _string: iobuf + 64 */
	if (npz->p_flags & PF_EMX_FILE) {
	    /* set dta address to iobuf */
	    user_dta = ESI;
	    SET_SEG_OFF(iobuf, tr.ds, tr.edx);
	    tr.eax = 0x1a00;
	    realdos(&tr);
	}
	TEST_ILLEGAL(EDX, 2);
	TEST_ILLEGAL_WRITE(user_dta, 43);
	strcpy32_16(DS, EDX, iobuf + 64);
	tr.eax = EAX;
	tr.ecx = ECX;
	SET_SEG_OFF((iobuf+64), tr.ds, tr.edx);
	if (realdos(&tr))
	    EAX = tr.eax;
	else
	    cpy16_32(DS, user_dta, iobuf, 43);
	FLAGS = tr.flags;
	return CARRY_NON;

    case 0x4f:			/* FINDNEXT */
	/* DTA: iobuf byte 0-42 */
	if (npz->p_flags & PF_EMX_FILE) {
	    user_dta = ESI;
	    SET_SEG_OFF(iobuf, tr.ds, tr.edx);
	    tr.eax = 0x1a00;
	    /* set dta address */
	    realdos(&tr);
	}
	TEST_ILLEGAL_WRITE(user_dta, 43);
	/* put user dta in iobuf */
	cpy32_16(DS, user_dta, iobuf, 43);
	tr.eax = EAX;
	if (realdos(&tr))
	    EAX = tr.eax;
	else
	    cpy16_32(DS, user_dta, iobuf, 43);
	FLAGS = tr.flags;
	return CARRY_NON;

	/*
	** some special handling
	*/
    case 0x1a:			/* SET DTA */
	TEST_ILLEGAL(EDX, 2);
	user_dta = EDX;
	tr.eax = 0x1a00;
	SET_SEG_OFF(iobuf, tr.ds, tr.edx);
	realdos(&tr);
	return CARRY_OFF;

    case 0x2f:			/* GET DTA */
	EBX = user_dta;
	return CARRY_OFF;

    case 0x62:			/* GET PSP */
	EBX = (DWORD) _psp;
	return CARRY_OFF;

	/*
	** functions complete changed
	** need to call DPMI-functions
	*/
    case 0x48:			/* ALLOC MEM */
    case 0x49:			/* FREE MEM */
	EAX = EMX_EIO;
	return CARRY_ON;

    case 0x4a:			/* RESIZE MEM */
	if (npz->p_flags & PF_EMX_FILE) {
	    EAX = EMX_EIO;
	    return CARRY_ON;
	} else {
	    if (EAX & 0xff)
		EAX = getmem(EBX, npz);
	    else
		EAX = npz->brk_value;
	    if (EAX == -1)
		EAX = 0;
	    return CARRY_OFF;	/* sbrk.s didn't check carry */
	}

    case 0x4c:
	return do_exit4c(0);

    case 0x4d:
	{
	    unsigned status = 0;
	    /* sys_wait(&status); */
	    AX = status >> 8;	/* al = return code */
	    return CARRY_OFF;
	}

    default:
	printf("Warning: Not implemented DOS function ah=%02X\n", rAH);
	EAX = EMX_EIO;
	return CARRY_ON;

    }				/* switch R_AH */
}

#include "DJIO.H"

/*
** read for dos handles
*/
ARGUSER cdosx_read(int handle, ARGUSER buf, ARGUSER count)
{
    long org_bytes;
    int iob_bytes;
    int ret_bytes;

    /* termio check */
    if ((npz->p_flags & PF_TERMIO) && rm_isatty(handle)) {
	if ((ret_bytes = termio_read(DS, buf, (int)count)) < 0) {
	    emx_errno = -ret_bytes;
	    return -1;
	} else
	    return ret_bytes;
    }

    if ((npz->p_flags & PF_DJGPP_FILE) && buf == (ARGUSER)iobuf_linear)
	return dj_read(handle, iobuf+4096, (unsigned)count);

    org_bytes = count;
    while (count > 0) {
	iob_bytes = (count <= IOBUF_SIZE) ? (int) count : IOBUF_SIZE;

	if (npz->p_flags & PF_DJGPP_FILE) {
	    if ((ret_bytes = dj_read(handle, iobuf, iob_bytes)) == -1)
		return -(long)errno_djgpp(emx_errno);
	} else {
	    if ((ret_bytes = rm_read(handle, iobuf, iob_bytes)) == -1)
		return -(long)emx_errno;
	}
	cpy16_32(DS, buf, iobuf, (long) ret_bytes);
	count -= (long) ret_bytes;
	if (ret_bytes < iob_bytes)  /* EOF */
	    break;
	buf += ret_bytes;
    }
    return (org_bytes - count);
}

/*
** write for dos handles
*/
ARGUSER cdosx_write(int handle, ARGUSER buf, ARGUSER count)
{
    long org_bytes;
    int iob_bytes;
    int ret_bytes;

    if (!count)
	return (ARGUSER) rm_write(handle, &iobuf, 0);

    if ((npz->p_flags & PF_DJGPP_FILE) && buf == (ARGUSER) iobuf_linear)
	return dj_write(handle, iobuf+4096, (unsigned)count);

    org_bytes = count;
    while (count > 0) {
	iob_bytes = (count <= IOBUF_SIZE) ? (int) count : IOBUF_SIZE;
	cpy32_16(DS, buf, iobuf, (long)iob_bytes);

	if (npz->p_flags & PF_DJGPP_FILE) {
	    if ((ret_bytes = dj_write(handle, iobuf, iob_bytes)) == -1)
		return -(long)errno_djgpp(emx_errno);
	} else {
	    if ((ret_bytes = rm_write(handle, iobuf, iob_bytes)) == -1)
		return -(long)emx_errno;
	}
	count -= (long) ret_bytes;
	if (ret_bytes < iob_bytes)  /* disk full */
	    break;
	buf += ret_bytes;
    }
    return (org_bytes - count);
}

typedef union {
    unsigned long flat;
    struct FarPtr {
	unsigned short off16;
	unsigned short seg16;
    } farptr;
} PTR;

#pragma pack(1)
typedef struct {
    char    signatur[4];
    char    version[2];
    PTR     oem_name;
    char    capabilities[4];
    PTR     modes;
    char    blocks[2];
} VESAINFO;
#pragma pack()

static unsigned real_mode_vio10(TRANSLATION *pTrans)
{
    pTrans->sp = pTrans->ss = 0;
    pTrans->flags = 0x3200;
    return SimulateRMint(0x10, 0, 0, pTrans);
}
static unsigned real_mode_mou33(TRANSLATION *pTrans)
{
    pTrans->sp = pTrans->ss = 0;
    pTrans->flags = 0x3200;
    return SimulateRMint(0x33, 0, 0, pTrans);
}

/*
** support for vesa modes ah = 0x4F, al = 00 - 08
*/
static void vio10(void)
{
    unsigned char rAH, rAL;
    TRANSLATION tr;
    VESAINFO *pInfo;
    DWORD offset;

    rAH = (BYTE) (AX >> 8);
    rAL = (BYTE) EAX;

    if (rAH != 0x4F)
	return;

    switch (rAL) {
	case 0x00:
	    SET_SEG_OFF(iobuf, tr.es, tr.edi);
	    put_regs(&tr);
	    real_mode_vio10(&tr);
	    if ((tr.eax & 0xFF) != 0x4f)
		break;
	    else
		EAX = tr.eax;	    /* success */

	    pInfo = (VESAINFO *) iobuf;
	    offset = ((DWORD) pInfo->oem_name.farptr.seg16 << 4)
		     + (DWORD) pInfo->oem_name.farptr.off16;
	    pInfo->oem_name.flat = (dpmi10) ?
		offset + DPMI_PRG_DATA :
		offset - npz->memaddress;
	    offset = ((DWORD) pInfo->modes.farptr.seg16 << 4)
		     + (DWORD) pInfo->modes.farptr.off16;
	    pInfo->modes.flat = (dpmi10) ?
		offset + DPMI_PRG_DATA :
		offset - npz->memaddress;

	    cpy16_32(ES, EDI, iobuf, 256);
	    break;

	case 0x01:
	    SET_SEG_OFF(iobuf, tr.es, tr.edi);
	    put_regs(&tr);
	    real_mode_vio10(&tr);
	    if ((tr.eax & 0xFF) != 0x4f)
		break;
	    else
		EAX = tr.eax;

	    * (DWORD *) (iobuf + 0x0C) = 0; /* zero far function */
	    cpy16_32(ES, EDI, iobuf, 256);
	    break;

	case 0x02: /* set SVGA mode */
	case 0x03: /* get SVGA mode */
	case 0x05: /* return ax */
	case 0x06: /* return ax,bx,cx,dx */
	case 0x07: /* return ax */
	case 0x08: /* return ax,bx */
	    put_regs(&tr);
	    real_mode_vio10(&tr);
	    get_regs(&tr);
	    break;

	case 0x04: /* unsupported */
	default:
	    printf("Not supported Vesa call %X", AX);
	    return;
    }
}

/*
** support for mouse calls
*/
static void mou33(void)
{
    TRANSLATION tr;
    unsigned char rAH = (BYTE) EAX;

    if (rAH > 0x35) /* not supported */
	return;

    switch (rAH) {
	case 0x0C:	/* Define IRQ routine	    */
	case 0x14:	/* Exchange IRQ routines    */
	case 0x16:	/* Save state		    */
	case 0x17:	/* Restore state	    */
	case 0x18:	/* Set event handler	    */
	case 0x19:	/* Return event handler     */
	case 0x29:	/* Enumerate video modes    */
	case 0x2B:	/* Load acceleration prof.  */
	case 0x2C:	/* Get acceleration prof.   */
	case 0x2D:	/* Select acc. profile	    */
	case 0x2E:	/* Set acc. profile names   */
	case 0x33:	/* Switch settings etc.     */
	case 0x34:	/* Get initialization file  */
	    break; /* not supported */

	case 0x09:	/* define graphic cursor es:dx */
	    put_regs(&tr);
	    cpy32_16(ES, EDX, iobuf, 32);
	    SET_SEG_OFF(iobuf, tr.es, tr.edx);
	    real_mode_mou33(&tr);
	    EAX = tr.eax;
	    break;

	case 0x12:	/* graphic cursor block es:dx ; bh*ch*4 bytes */
	    put_regs(&tr);
	    cpy32_16(ES, EDX, iobuf, BH * CH * 4);
	    SET_SEG_OFF(iobuf, tr.es, tr.edx);
	    real_mode_mou33(&tr);
	    get_regs(&tr);
	    break;

	default:
	    put_regs(&tr);
	    real_mode_mou33(&tr);
	    get_regs(&tr);
	    break;
    }
}

void prot_mode_interrupt(void)
{
    int int_no = (int) npz->regs.faultno;

    if (int_no == 0x10)
	return vio10();
    else if (int_no == 0x33)
	return mou33();
}
