/*
 *		EMM handler routines used in dviout/dviprt
 *		Dec 13, by SHIMA
 */

int emm_installed( void );
int get_page_frame_address ( uchar far **pf_ptr );
int count_unallocated_pages ( void );
int search_handle_name( char *handle_name, unsigned int *emm_handle_ptr );
int set_handle_name( char *handle_name );
int map_expand_multi_pages( void );
int allocate_expanded_memory_pages ( int pages_needed, 
											unsigned int *emm_handle_ptr );
int deallocate_expanded_memory_pages( void );
int move_expand_memory( uint s_page, uint s_off, uint d_page, uint d_off, long length );

struct LOG_TO_PHYS_MAP {
	int	log;
	int	phys;
};

#define EMM_INT                 0x67  /* EMM interrupt number */

#define GET_PAGE_FRAME          0x41  /* EMM get page frame */
                                      /* function number */
#define GET_UNALLOC_PAGE_COUNT  0x42  /* EMM get unallocated */
                                      /* page count */
                                      /* function number */
#define ALLOCATE_PAGES          0x43  /* EMM allocate pages */
                                      /* function number */
#define DEALLOCATE_PAGES        0x45  /* EMM deallocate pages */
                                      /* function number */
#define	MAP_MULTI_PAGES		0x5000  /* EMM Map/Unmap Multiple Handle Pages */
									  /* function number */
#define	MOVE_MEMORY			0x5700;

#define	LENGTH_PAGES			4	  /* number of pages to use at a time */
#define DEVICE_NAME_LENGTH      8     /* length of a device */
                                      /* name string */

#define TRUE                    1
#define FALSE                   0
#define	MAXPAGE					0x800

struct LOG_TO_PHYS_MAP log_to_phys_map[LENGTH_PAGES]= {
	{-1,0}, {1,1}, {2,2}, {3,3}
};

uchar far *pf_addr;

static union REGS input_regs, output_regs;
static struct SREGS segment_regs;

char far *build_ptr( unsigned int, unsigned int );
#define build_ptr(x,y)	MK_FP(x,y)


             /*------------------------------------------------------------*/
             /* Function which determines whether EMM device driver        */
             /* is installed.                                              */
             /*------------------------------------------------------------*/
int emm_installed( void )
{
    char *EMM_device_name = "EMMXXXX0";
    char far *int_67_device_name_ptr;
	int	 i;
              /*--------------------------------------------------------*/
              /* AH = DOS get interrupt vector function.                */
              /*--------------------------------------------------------*/
    input_regs.h.ah = 0x35;

              /*--------------------------------------------------------*/
              /* AL = EMM interrupt vector number.                      */
              /*--------------------------------------------------------*/
    input_regs.h.al = EMM_INT;
    intdosx (&input_regs, &output_regs, &segment_regs);

              /*--------------------------------------------------------*/
              /* Upon return ES:0Ah points to location where            */
              /* device name should be.                                 */
              /*--------------------------------------------------------*/
    int_67_device_name_ptr = build_ptr(segment_regs.es, 0x0A);

              /*--------------------------------------------------------*/
              /* Compare memory with EMM device name.                   */
              /*--------------------------------------------------------*/
	for( i=0; i < DEVICE_NAME_LENGTH; i++ ){
		if(EMM_device_name[i] != int_67_device_name_ptr[i] )
			return(FALSE);
	}
    return (TRUE);
}


          /*------------------------------------------------------------*/
          /* Routine to map logical pages to physical pages.			*/
          /*------------------------------------------------------------*/
int map_expand_multi_pages( void )
{
    input_regs.x.ax = MAP_MULTI_PAGES;
    input_regs.x.cx = LENGTH_PAGES;
    input_regs.x.si = FP_OFF( log_to_phys_map );
    segment_regs.ds = FP_SEG( log_to_phys_map );
    int86 (EMM_INT, &input_regs, &output_regs);
    if (output_regs.h.ah != 0)
		error(MEMORY_FAULT, "Internal error in EMS mapping(%d %d %d %d)",
			log_to_phys_map[0].phys,
			log_to_phys_map[1].phys,
			log_to_phys_map[2].phys,
			log_to_phys_map[3].phys
	 	);
	return (TRUE);
}


int get_page_frame_address ( uchar far **pf_ptr )
{
    input_regs.h.ah = GET_PAGE_FRAME;
    int86 (EMM_INT, &input_regs, &output_regs);
    if (output_regs.h.ah != 0)       /* check EMM status */
        return (FALSE);
    else
       *pf_ptr = build_ptr(output_regs.x.bx, 0);
    return (TRUE);
}

		  /*------------------------------------------------------------*/
		  /* Routine to search the handle name directory for a handle   */
		  /* with a particular name.                                    */
		  /*------------------------------------------------------------*/
int search_handle_name( char *handle_name, unsigned int *emm_handle_ptr )
{
    segment_regs.ds = FP_SEG( handle_name );
    input_regs.x.si = FP_OFF( handle_name );
	input_regs.x.ax = 0x5401;
	int86x(EMM_INT, &input_regs, &output_regs, &segment_regs);
    if (output_regs.h.ah == 0){
		*emm_handle_ptr = output_regs.x.dx;
        return (TRUE);
	}
    else
        return (FALSE);
}

          /*------------------------------------------------------------*/
          /* Function which returns the number of unallocated pages     */
          /*------------------------------------------------------------*/
int count_unallocated_pages ( void )
{
    input_regs.h.ah = GET_UNALLOC_PAGE_COUNT;
    int86 (EMM_INT, &input_regs, &output_regs);
    if (output_regs.h.ah != 0 )
        return (FALSE);
    else
        return (output_regs.x.bx);
}

          /*------------------------------------------------------------*/
          /* Function which allocates expanded memory pages and passes  */
          /* back to the main EMM handle.                               */
          /*------------------------------------------------------------*/
int allocate_expanded_memory_pages ( int pages_needed, 
											unsigned int *emm_handle_ptr )
{
    input_regs.h.ah = ALLOCATE_PAGES;
    input_regs.x.bx = pages_needed;
    int86 (EMM_INT, &input_regs, &output_regs);
    if (output_regs.h.ah == 0) {
        *emm_handle_ptr = output_regs.x.dx;
        return (TRUE);
    }
    else
        return (FALSE);
}


          /*------------------------------------------------------------*/
          /* Routine to release all expanded memory pages allocated     */
          /* by an EMM handle.                                          */
          /*------------------------------------------------------------*/
int deallocate_expanded_memory_pages( void )
{
    input_regs.h.ah = DEALLOCATE_PAGES;
    int86 (EMM_INT, &input_regs, &output_regs);
    if (output_regs.h.ah == 0)
        return (TRUE);
    else
        return (FALSE);
}


		  /*------------------------------------------------------------*/
          /*   Routine to assign an eight character name to a handle.   */
		  /*------------------------------------------------------------*/
int set_handle_name( char *handle_name )
{
    segment_regs.ds = FP_SEG( handle_name );
    input_regs.x.si = FP_OFF( handle_name );
	input_regs.x.ax = 0x5301;
	int86x(EMM_INT, &input_regs, &output_regs, &segment_regs );
    if (output_regs.h.ah == 0)
        return (TRUE);
    else
        return (FALSE);
}


struct	move_s_d_ems {
	long	region_length;
	char	s_type;
	uint	s_handle;
	uint	s_offset;
	uint	s_seg_page;
	char	d_type;
	uint	d_handle;
	uint	d_offset;
	uint	d_seg_page;
};

         /*-------------------------------------------------------------*/
         /*  Routine to move memory in expanded memory regions          */
         /*-------------------------------------------------------------*/
int move_expand_memory( uint s_page, uint s_off, uint d_page, uint d_off,
 long length )
{
	struct	move_s_d_ems s_d;

	s_d.region_length = length;
	s_d.s_type = (s_page < MAXPAGE)?1:0;
	s_d.d_type = (d_page < MAXPAGE)?1:0;
	s_d.s_handle = s_d.d_handle = input_regs.x.dx;
	s_d.s_seg_page = s_page;
	s_d.d_offset = d_off;
	s_d.d_seg_page = d_page;
	s_d.s_offset = s_off;
	segment_regs.ds = FP_SEG( &s_d );
	input_regs.x.si = FP_OFF( &s_d );
	input_regs.x.ax = MOVE_MEMORY;
	int86x(EMM_INT, &input_regs, &output_regs, &segment_regs );
    if (output_regs.h.ah != 0)
		error(MEMORY_FAULT,
			"Internal error in moving EMS memory(%04X:%04X->%04X:%04X)",
			s_page, s_off, d_page, d_off);
    return (TRUE);
}
