#include "insignia.h"
#include "host_def.h"
/*			INSIGNIA MODULE SPECIFICATION
			-----------------------------
SccsID		: @(#)emm_mngr.c	1.19 11/10/92 Copyright Insignia Solutions Ltd.
FILE NAME	: emm_mngr.c
MODULE NAME	: 'Middle layer' of Expanded Memory Manager

	THIS PROGRAM SOURCE FILE  IS  SUPPLIED IN CONFIDENCE TO THE
	CUSTOMER, THE CONTENTS  OR  DETAILS  OF  ITS OPERATION MUST
	NOT BE DISCLOSED TO ANY  OTHER PARTIES  WITHOUT THE EXPRESS
	AUTHORISATION FROM THE DIRECTORS OF INSIGNIA SOLUTIONS INC.

DESIGNER	: J.P.Box
DATE		: April '88

PURPOSE		: Contains all the routines that communicate with
		the arrays and data structures that hold the 
		necessary Expanded Memory Manager Data.


The Following Routines are defined:
		1. init_expanded_memory()
		2. free_expanded_memory()
		3. get_new_handle()
		4. free_handle()
		5. reallocate_handle()
		6. handle_ok()
		7. set_no_pages()
		8. set_EM_pageno()
		9. set_map()
		10. set_name()
		11. get_no_pages()
		12. get_EM_pageno()
		13. get_map()
		14. get_name()
		15. alloc_page()
		16. free_page()
		17. map_page()
		18. unmap_page()
		19. map_saved()
		20. save_map()
		21. restore_map()
		22. copy_exchange_data()
		23. page_status()
	The following routines just return variables to the top layer				
		24. get_total_pages()
		25. get_unallocated_pages()
		26. get_base_address()
		27. get_total_handles()
		28. get_total_open_handles()
		29. get_no_phys_pages()
		30. get_page_seg()
		31. get_map_size()

=========================================================================

AMMENDMENTS	:

=========================================================================
*/


#ifdef LIM

#ifdef SEGMENTATION
/*
 * The following #include specifies the code segment into which this
 * module will by placed by the MPW C compiler on the Mac II running
 * MultiFinder.
 */
#include "SOFTPC_LIM.seg"
#endif



#include <stdio.h>
#include <string.h>
#include <sys\types.h>

#include "xt.h"
#include "cpu.h"
#include "sas.h"
#include "host_emm.h"
#include "emm.h"
#include "gmi.h"
#include "debug.h"
#ifndef PROD
#include "trace.h"
#endif
#include "timer.h"

#ifdef NTVDM
#include "error.h"
#endif	/* NTVDM */

typedef enum
{
	BYTE_OP,
	WORD_OP,
	STR_OP
} MM_LIM_op_type;

/*	Local Variables			*/
static long
	handle[MAX_NO_HANDLES];		/* Array containing unique ID's	*/
					/* for each handle, these are	*/
					/* usually pointers, but this 	*/
					/* is host dependant		*/
static short
	total_pages = 0,		/* no. of EM pages available	*/
	unallocated_pages = 0,		/* no. of unallocated EM pages	*/
	total_handles,			/* no of handles available	*/
	total_open_handles,		/* no. of allocated handles	*/
	EM_page_mapped[MAX_NO_PAGES],	/* Expanded Memory pages	*/
					/* currently mapped in		*/
	page_offset,			/* offset in handle data at 	*/
					/* which page numbers start	*/
	map_size,			/* no of bytes rq'd to store map*/
	no_phys_pages;			/* no. of phys. pages available	*/

static unsigned int
	EM_start,			/* start segment for EM mapping	*/
	EM_end;			/* 1st segment past end of EM	*/

static unsigned short
	physical_page[MAX_NO_PAGES];	/* array containing segment	*/
					/* addresses of physical pages	*/

static char
	name[NAME_LENGTH];		/* for storing handle name	*/

/*
===========================================================================

FUNCTION	: init_expanded_memory

PURPOSE		: This routine calls the routine to allocate the expanded
		memory pages and then sets up the arrays and variables that
		are used by the Expanded Memory Manager(EMM).

RETURNED STATUS	: SUCCESS - manager initialised succesfully
		  FAILURE - Failure to allocate space for Expanded Memory
		  	    pages.

DESCRIPTION	:

=========================================================================
*/
GLOBAL int init_expanded_memory IFN2(int, size, 	/* size of area in megabytes */
				     int, mem_limit	/* limit of conventional memory
							 * 256, 512 or 640KB */ )

{	
	short
		pages_above_640,	/* no of mappable locations	*/
		pages_below_640,	/*  available either side of 640*/
		EM_page_no,		/* page no. within exp. memory	*/
		physical_page_no;	/* page no. within map region	*/

	unsigned short
		base;			/* start segment of mappable 	*/
					/* memory below 640 KB		*/

	int	i, j;			/* loop counters		*/

#ifdef NTVDM
        word block_base;            /* base of block found between ROMS */
	unsigned int	block_len;	/* length " "      "     "      "   */

	if (host_get_lim_block(&block_base, &block_len) == FAILURE)
	{
	    unallocated_pages = 0;
	    total_pages = (short)0xffff;	/* impossible value for real LIM */
	    EM_start = 0xd000;
	    EM_end = 0xe000;
	    return(FAILURE);
	}
#endif	/* NTVDM */

	/* get space for expanded memory pages	*/

	if(host_initialise_EM(size) != SUCCESS)
	{
#ifdef NTVDM
	    host_error(EG_EXPANDED_MEM_FAILURE, ERR_QU_CO, NULL);
#endif	/* NTVDM */
	    return(FAILURE);
	}

	/* Initialise EMM variables	*/

	EM_start = 0xd000;
	EM_end   = 0xe000;
	total_pages = unallocated_pages = size * 0x100000 / EMM_PAGE_SIZE;

	/* always allow max handles (Used to be 32 handles/Meg expanded mem) */
	total_handles = MAX_NO_HANDLES;
	total_open_handles = 0;
	for(i = 0; i < total_handles; i++)
		handle[i] = (long) NULL;

#ifdef NTVDM
	pages_above_640 = block_len / EMM_PAGE_SIZE;
	EM_start = block_base;
	EM_end = EM_start + (block_len >> 4);
#else
	pages_above_640 = (effective_addr(EM_end,0) - effective_addr(EM_start,0)) / EMM_PAGE_SIZE;
#endif	/* NTVDM */

	pages_below_640 = ((640 - mem_limit) * 1024) / EMM_PAGE_SIZE;
	no_phys_pages = pages_above_640 + pages_below_640;

	map_size = no_phys_pages * NSIZE;
	page_offset = MAP_OFFSET + map_size;	

	/*
	 * set up addresses and mapping status of physical pages
	 */
	for( i = 0; i < pages_above_640; i++ )
	{
		physical_page[i] = EM_start + (i * EMM_PAGE_SIZE >> 4);
		EM_page_mapped[i] = EMPTY;
	}
	base = mem_limit * 64;

	for(i = pages_above_640, j = 0; i < no_phys_pages; i++)
	{
		physical_page[i] = base + (j++ * EMM_PAGE_SIZE >> 4);
		EM_page_mapped[i] = EMPTY;
	}
	/*
	 * Allocate handle 0 with any pages required for back filling
	 */
	if(get_new_handle(pages_below_640) != 0)
		return(FAILURE);

	for(i = 0, physical_page_no = pages_above_640; i < pages_below_640; i++)
	{
		if((EM_page_no = alloc_page()) == FAILURE)
			return (FAILURE);

		set_EMpage_no(0, i, EM_page_no);

		if(map_page(EM_page_no, physical_page_no++) == FAILURE)
			return(FAILURE);
	}
	set_no_pages(0, pages_below_640);

	/*
	 *	Set up necessary variables in Top level EMM function code
	 */
	reset_emm_funcs();

	/*
	** Map the address space taken up by LIM to RAM.
	** Without LIM it would be ROM.
	** The range seems to be fixed at segment D000 to F000.
	** Assumed that AT's have GMI and XT's do not.
	** XT's can use the old fashioned memset calls in
	** delta:manager:init_struc.c
	*/
	sas_connect_memory(effective_addr(EM_start,0) , effective_addr(EM_end,0) -1 , SAS_RAM );
	sure_note_trace3(LIM_VERBOSE,"initialised EMM, total pages= %#x, pages above 640= %#x, pages below 640 = %#x",no_phys_pages, pages_above_640, pages_below_640);

	return(SUCCESS);
}

/*
===========================================================================

FUNCTION	: free_expanded_memory

PURPOSE		: This routine calls frees all memory allocated for the
		expanded memory manager and resets the variables that
		are used by the Expanded Memory Manager(EMM).

RETURNED STATUS	: SUCCESS - 

DESCRIPTION	: If total_pages = 0, this indicates that expanded
		memory hasn't been initialised, so the routine simply
		does nothing and returns.

=========================================================================
*/
GLOBAL void free_expanded_memory IFN0()

{	
	short 	handle_no;

	if(total_pages == 0)
		return;

	/* free space allocated for each handle	*/

	handle_no = 0;
	while(total_open_handles > 0)
	{
		while(!handle_ok(handle_no))
			handle_no++;

		free_handle(handle_no++);
	}
	/*
	 *	Free space for expanded memory pages
	 */
	host_deinitialise_EM();

	total_pages = 0;

	return;
}

/*
===========================================================================

FUNCTION	: get_new_handle()

PURPOSE		: Finds the next free handle no., allocates storage space
		for recording the EMM data associated with this handle,
		and stores the 'storage ID' in the handle array.

RETURNED STATUS	: SUCCESS - new handle allocated successfully
		 FAILURE - Error occurred in trying to allocate storage
		           space for handle data

DESCRIPTION	: see emm.h for a description of space required for
		 storing handle data e.g. PAGE_OFFSET & NSIZE


=========================================================================
*/
GLOBAL short get_new_handle IFN1(short, no_pages)	/* No.of pages to store in handle */

{
	short	i;			/* loop count */
	short	handle_no;
	int	data_size;		/* no. of bytes of data storage */
	long	storage_ID;		/* host dependant storage	*/
					/* identifier, usually a ptr.	*/

	sure_note_trace2(LIM_VERBOSE,"new handle request, current total handles= %d, pages requested = %d",total_handles, no_pages);

	handle_no = 0;

	do
		if (handle[handle_no] == (long) NULL)
			break;
	while(++handle_no < total_handles);

	if(handle_no >= total_handles)
		return(FAILURE);

	data_size = page_offset + (no_pages * NSIZE);

	if ((storage_ID = host_allocate_storage(data_size)) == (long) NULL)
		return(FAILURE);

	handle[handle_no] = storage_ID;

	for (i=0 ; i < no_phys_pages ; i++) {
		set_map_no(handle_no, i, FREE);
	}

	total_open_handles++;

	sure_note_trace1(LIM_VERBOSE,"allocation OK, return handle=%d",handle_no);

	return(handle_no);
}

/*
===========================================================================

FUNCTION	: free_handle

PURPOSE		: frees the storage space allocated to the handle number.
		  Decrements the handles open count

RETURNED STATUS	: SUCCESS - space freed
		  FAILURE - unable to free space

DESCRIPTION	: 

=========================================================================
*/
GLOBAL int free_handle IFN1(short, handle_no)	/* No.of handle to be freed */

{
	long	storage_ID;		/* host dependant storage	*/
					/* identifier, usually a ptr.	*/

	sure_note_trace2(LIM_VERBOSE, "free handle %d request, total handles = %d",handle_no, total_handles);

	storage_ID = handle[handle_no];

	if(host_free_storage(storage_ID) != SUCCESS)
		return(FAILURE);	

	handle[handle_no] = (long) NULL;

	total_open_handles--;

	return(SUCCESS);
}

/*
===========================================================================

FUNCTION	: reallocate_handle

PURPOSE		: changes the number of pages allocated to a given handle

RETURNED STATUS	: SUCCESS - handle reallocated
		  FAILURE - unable to get space for new handle data

DESCRIPTION	: 

=========================================================================
*/
GLOBAL int reallocate_handle IFN3(short, handle_no, 	/* handle to be reallocated */
				  short, old_page_count,/* current pages in handle  */
				  short, new_page_count)/* required pages for handle*/

{
	long	storage_ID;		/* host dependant storage	*/
					/* identifier, usually a ptr.	*/

	short	size,			/* size of handle data area	*/
		new_size;		/* size of new handle data area	*/


	size = page_offset + (old_page_count * NSIZE);
	new_size = page_offset + (new_page_count * NSIZE);
	storage_ID = handle[handle_no];

	sure_note_trace3(LIM_VERBOSE,"reallocate pages for handle %d, old size=%#x, new size= %#x",handle_no, size, new_size);

	if((storage_ID = host_reallocate_storage(storage_ID, size, new_size)) ==
		(long) NULL)
		return(FAILURE);	

	handle[handle_no] = storage_ID;

	return(SUCCESS);
}

/*
===========================================================================

FUNCTION	: handle_ok

PURPOSE		: checks to see if the handle no. is valid - this should
		be called before every routine that uses a handle number
		to retrieve or set data in the handle data area

RETURNED STATUS	: TRUE	- Handle no. is valid
		FALSE	- Handle no. is invalid

DESCRIPTION	: 

=========================================================================
*/
GLOBAL boolean handle_ok IFN1(short, handle_no)

{

#ifdef NTVDM
/* some *** applicaitons feed us a negtive handle number. Catch it and
   throw it to the hell*/

	if ((unsigned short)handle_no >= (unsigned short)total_handles) {
#else
	if(handle_no >= total_handles) {
#endif
		sure_note_trace1(LIM_VERBOSE,"invalid handle %d",handle_no);
		return(FALSE);
	}

	if(handle[handle_no] == (long) NULL){
		sure_note_trace1(LIM_VERBOSE,"invalid handle %d",handle_no);
		return(FALSE);
	}

	return(TRUE);
}

/*
===========================================================================

FUNCTION	: set_no_pages

PURPOSE		: sets the no of pages variable in the specified handle

RETURNED STATUS	: 

DESCRIPTION	: 

=========================================================================
*/
GLOBAL void set_no_pages IFN2(short, handle_no, short, no_pages)

{
	long	storage_ID;		/* host dependant storage	*/
					/* identifier, usually a ptr.	*/
	byte	*ptr;			/* pointer to storage area	*/

	storage_ID = handle[handle_no];		
	ptr = USEBLOCK(storage_ID);

	*(short *)ptr = no_pages;

	FORGETBLOCK(storage_ID)

	return;
}

/*
===========================================================================

FUNCTION	: set_EMpage_no

PURPOSE		: sets Expanded Memory page that is used for the specified
		logical page into the handle data storage area

RETURNED STATUS	: 

DESCRIPTION	: 

=========================================================================
*/
GLOBAL void set_EMpage_no IFN3(short, handle_no, 
			       short, logical_page_no, 
			       short, EM_page_no)

{
	long	storage_ID;		/* host dependant storage	*/
					/* identifier, usually a ptr.	*/
	byte	*ptr;			/* pointer to storage area	*/

	storage_ID = handle[handle_no];

	ptr = USEBLOCK(storage_ID);
	/*
	 * offset pointer to correct position
	 */

	ptr += (page_offset +(logical_page_no * NSIZE));
	*(short *)ptr = EM_page_no;

	FORGETBLOCK(storage_ID)

	return;
}

/*
===========================================================================

FUNCTION	: set_map_no

PURPOSE		: sets Expanded Memory page number in the map section of
		the handle data storage area

RETURNED STATUS	: 

DESCRIPTION	: 

=========================================================================
*/
GLOBAL void set_map_no IFN3(short, handle_no, 
			    unsigned char, physical_page_no, 
			    short, EM_page_no)

{
	long		storage_ID;	/* host dependant storage	*/
					/* identifier, usually a ptr.	*/
	unsigned char	*ptr;		/* pointer to storage area	*/

	storage_ID = handle[handle_no];

	ptr = USEBLOCK(storage_ID);
	/*
	 * offset pointer to correct position
	 */

	ptr += (MAP_OFFSET +(physical_page_no * NSIZE));
	*(short *)ptr = EM_page_no;

	FORGETBLOCK(storage_ID)

	return;
}

/*
===========================================================================

FUNCTION	: set_name

PURPOSE		: writes a name into the name section of the handle data
		 storage area

RETURNED STATUS	: 

DESCRIPTION	: 

=========================================================================
*/
GLOBAL void set_name IFN2(short, handle_no, 
		          char *, new_name)

{
	long	storage_ID;		/* host dependant storage	*/
					/* identifier, usually a ptr.	*/
	unsigned char	*ptr;		/* pointer to storage area	*/


	storage_ID = handle[handle_no];

	ptr = USEBLOCK(storage_ID);
	/*
	 * offset pointer to correct position
	 */

	ptr += NAME_OFFSET;
	strncpy((char *)ptr, new_name, NAME_LENGTH);

	FORGETBLOCK(storage_ID)

	return;
}

/*
===========================================================================

FUNCTION	: get_no_pages

PURPOSE		: gets the number of pages assigned to the specified handle

RETURNED STATUS	: no of pages returned

DESCRIPTION	: 

=========================================================================
*/
GLOBAL short get_no_pages IFN1(short, handle_no)

{
	long	storage_ID;		/* host dependant storage	*/
					/* identifier, usually a ptr.	*/
	byte	*ptr;			/* pointer to storage area	*/
	short 	no_pages;		/* no. of pages in handle	*/

	storage_ID = handle[handle_no];

	ptr = USEBLOCK(storage_ID);

	no_pages = *(short *)ptr;

	FORGETBLOCK(storage_ID)

	return(no_pages);
}

/*
===========================================================================

FUNCTION	: get_EMpage_no

PURPOSE		: returns the Expanded Memory page no. used for the
		 specified logical page in the given handle

RETURNED STATUS	: Expanded Memory page no. returned

DESCRIPTION	: 

=========================================================================
*/
GLOBAL short get_EMpage_no IFN2(short, handle_no, 
				short, logical_page_no)

{
	long	storage_ID;		/* host dependant storage	*/
					/* identifier, usually a ptr.	*/
	byte	*ptr;			/* pointer to storage area	*/
	short	EM_page_no;		/* Expanded Memory page number	*/

	storage_ID = handle[handle_no];

	ptr = USEBLOCK(storage_ID);
	/*
	 * offset pointer to correct position
	 */

	ptr += (page_offset +(logical_page_no * NSIZE));
	EM_page_no = *(short *)ptr;

	FORGETBLOCK(storage_ID)

	return(EM_page_no);
}

/*
===========================================================================

FUNCTION	: get_map_no

PURPOSE		: returns the Expanded Memory page no. saved in the map
		attached to the given handle

RETURNED STATUS	: page no. in map returned

DESCRIPTION	: 

=========================================================================
*/
GLOBAL short get_map_no IFN2(short, handle_no, 
			     unsigned char, physical_page_no)

{
	long		storage_ID;	/* host dependant storage	*/
					/* identifier, usually a ptr.	*/
	unsigned char	*ptr;		/* pointer to storage area	*/
	short		EM_page_no;	/* Expanded Memory page number	*/

	storage_ID = handle[handle_no];

	ptr = USEBLOCK(storage_ID);
	/*
	 * offset pointer to correct position
	 */

	ptr += (MAP_OFFSET +(physical_page_no * NSIZE));
	EM_page_no = *(short *)ptr;

	FORGETBLOCK(storage_ID)

	return(EM_page_no);
}

/*
===========================================================================

FUNCTION	: get_name

PURPOSE		: returns a pointer to the name assigned to the given handle

RETURNED STATUS	: 

DESCRIPTION	: 

=========================================================================
*/
GLOBAL char *get_name IFN1(short, handle_no)

{
	long		storage_ID;	/* host dependant storage	*/
					/* identifier, usually a ptr.	*/
	unsigned char	*ptr;		/* pointer to storage area	*/

	storage_ID = handle[handle_no];

	ptr = USEBLOCK(storage_ID);
	/*
	 * offset pointer to correct position
	 */

	ptr += NAME_OFFSET;
	strncpy(name, (char *)ptr, NAME_LENGTH);

	FORGETBLOCK(storage_ID)

	return(name);
}

/*
===========================================================================

FUNCTION	: alloc_page

PURPOSE		: allocates a page from expanded memory

RETURNED 	: >=0 = SUCCESS -  EM page no. returned
		  <0  = FAILURE - error occured in trying to allocate page

DESCRIPTION	: 

=========================================================================
*/
GLOBAL short alloc_page IFN0()

{
	short	EM_page_no;		/* EM_page_no to be returned	*/


	if ((EM_page_no = host_alloc_page()) == FAILURE)
		return(FAILURE);

	unallocated_pages--;

	return(EM_page_no);
}

/*
===========================================================================

FUNCTION	: free_page

PURPOSE		: frees a page of expanded memory for further use

RETURNED 	: SUCCESS - page freed successfully
		  FAILURE - unable to free page

DESCRIPTION	: 

=========================================================================
*/
GLOBAL int free_page IFN1(short, EM_page_no)

{

	short	physical_page_no;

	if (EM_page_no > total_pages)
		return(FAILURE);

	/* Removed from mapped pages table */

	for (physical_page_no=0; physical_page_no < no_phys_pages; physical_page_no++) {

		if (EM_page_mapped[physical_page_no] == EM_page_no) {
			EM_page_mapped[physical_page_no] = UNMAPPED;
			break;
		}

	}
				
	if (host_free_page(EM_page_no) != SUCCESS)
		return(FAILURE);

	unallocated_pages++;

	return(SUCCESS);
}

/*
========================================================================

FUNCTION	: page_already_mapped

PURPOSE		: function to determine whether a EMM page is already
		  mapped to a different physical page within intel
		  memory 
				
RETURNED 	: count of number of pages in addition to the page
		  passed which are mapped to the same logical page.
		  The page number of one of these mirror pages is
		  also returned via the pointer passed as an argument.

DESCRIPTION	: 
			
========================================================================
*/

GLOBAL ULONG
page_already_mapped IFN2(short, EM_page_no, 
			unsigned char *, physical_page_no)

{
	unsigned char	page, orig_page;
	ULONG	map_count;

	map_count = 0;
	orig_page = *physical_page_no;

	for( page = 0; page < (unsigned char) no_phys_pages; page++ )
	{
		if ((EM_page_mapped[page] == EM_page_no) &&
						(page != orig_page ))
		{
			sure_note_trace2( LIM_VERBOSE,
				"log page %x mapped to phys page %x",
						EM_page_no, page);

			*physical_page_no = page;
			map_count++;
		}
	}

	return( map_count );
}


LOCAL VOID
connect_MM_LIM_page IFN2( USHORT, segment, SHORT, EM_page_no )
{
	ULONG eff_addr;

#ifdef PROD
	UNUSED(EM_page_no);
#endif
	
	assert2( NO, "Connecting multi-mapped page, %d, at %x",
								EM_page_no, segment );

	eff_addr = effective_addr( segment, 0 );
	sas_connect_memory( eff_addr, eff_addr + EMM_PAGE_SIZE - 1,
									SAS_MM_LIM );
}

LOCAL VOID
disconnect_MM_LIM_page IFN4( USHORT, segment, SHORT, EM_page_no,
		ULONG, map_count, unsigned char, physical_page_no )
{
	ULONG eff_addr;

#ifdef PROD
	UNUSED(EM_page_no);
#endif
	
	sure_note_trace2(LIM_VERBOSE,
		"Unmapping multi-mapped page, %d, at %x",
						EM_page_no, segment );

	eff_addr = effective_addr( segment, 0 );
	sas_connect_memory( eff_addr, eff_addr + EMM_PAGE_SIZE - 1, SAS_RAM );

	if( map_count == 1 )
	{
		/*
		 * We have to disconnect the last page of this group,
		 * by connecting it as SAS_RAM.
		 */ 

		segment = physical_page[physical_page_no];
		eff_addr = effective_addr( segment, 0 );

		sure_note_trace2(LIM_VERBOSE,
			"Unmapping last multi-mapped page, %d, at %x",
								EM_page_no, segment );

		sas_connect_memory( eff_addr, eff_addr + EMM_PAGE_SIZE - 1,
										SAS_RAM );
	}
}

/*
========================================================================

FUNCTION	: map_page

PURPOSE		: maps a page from expanded memory into Intel physical
		address space

RETURNED 	: SUCCESS - page mapped successfully
		  FAILURE - unable to map page

DESCRIPTION	: 

========================================================================
*/
GLOBAL int map_page IFN2(short, EM_page_no, 
			 unsigned char, physical_page_no)

{
	USHORT	segment;	/* segment address of page in	*/
				/* physical address space	*/
	unsigned char	phys_page;
	ULONG		map_count;
			
	segment = physical_page[physical_page_no];

	/*
	 *	make sure that a page is not already mapped in
	 * 	if it is - return it to Expanded Memory
	 */
	sure_note_trace2(LIM_VERBOSE,
		"map page %#x to phys page %#x",
			EM_page_no,physical_page_no);

	if(EM_page_mapped[physical_page_no] != EMPTY)
	{
		sure_note_trace1(LIM_VERBOSE,
				"phys page already mapped to page %#x",
						EM_page_mapped[physical_page_no]);

		if(EM_page_mapped[physical_page_no] == EM_page_no) 
		{
			sure_note_trace0(LIM_VERBOSE,
					"remap of same page, so do nothing");

			return(SUCCESS);
		}

		/*
		 * We want to return the current contents of this physical
		 * page to the logical page ( to sync up the logical page ).
		 * We have to check first that this physical page is not a
		 * mirror of some other page - if it is we have to disconnect
		 * it from the group of pages it is mirroring.
		 */

		phys_page = physical_page_no;

		if( map_count = page_already_mapped(
				EM_page_mapped[physical_page_no], &phys_page))
		{
			disconnect_MM_LIM_page( segment, EM_page_no,
								map_count, phys_page );
		}

		/*
		 * We can now unmap the physical page and indicate
		 * that it is really unmapped.
                 */

#ifndef MONITOR
		if(host_unmap_page(segment,
				EM_page_mapped[physical_page_no]) != SUCCESS)
		{
			return(FAILURE);
                }
#endif

		EM_page_mapped [physical_page_no] = EMPTY;
	}

	/* 
	 * If this logical page is already mapped, make sure the
	 * new mapping has an up to date copy 
	 */
	   
	phys_page = physical_page_no;

	if (page_already_mapped(EM_page_no, &phys_page)) 
	{
		/*
		 * We now want to get the LIM logical page up to date with
		 * the physical pages that are currently mapped to it. We
		 * don't want to set EM_page_mapped [phys_page] to EMPTY
		 * after the host_unmap_page().  If we did we wouldn't notice
		 * that we had a multiply-mapped page and the patch up code
		 * wouldn't get called.
		 */

		host_update_logical_page( physical_page[phys_page],
									EM_page_no );

		/*
		 * Connect new page and "mirror" page as MM_LIM.  This may
		 * mean some pages get connected as MM_LIM multiple times
		 * - inefficient but not wrong otherwise.  This connection
		 * has to be made for all hosts - even those that can do
		 * mapping themselves.  This is to make sure that the CPU
		 * data structures associated with all pages get updated
		 * when a multi-mapped write occurs.
		 */

		connect_MM_LIM_page( segment, EM_page_no );

		connect_MM_LIM_page( physical_page[phys_page], EM_page_no );
	}
		
	if(host_map_page(EM_page_no, segment) != SUCCESS)
		return(FAILURE);

	EM_page_mapped[physical_page_no] = EM_page_no;

	sure_note_trace0(LIM_VERBOSE,"map OK");
	return(SUCCESS);
}

/*
========================================================================

FUNCTION	: unmap_page

PURPOSE		: unmaps a page from Intel physical address space back to
		expanded memory

RETURNED 	: SUCCESS - page unmapped successfully
		  FAILURE - error in unmapping page

DESCRIPTION	: 

========================================================================
*/
GLOBAL int unmap_page IFN1(unsigned char, physical_page_no)

{
	short		EM_page_no;	/* EM_page_no currently mapped	*/
	unsigned short	segment;	/* segment address of page in	*/
					/* physical address space	*/
	SHORT		phys_page;
	ULONG		map_count;

	sure_note_trace1( LIM_VERBOSE,
				"unmap phys page %#x",physical_page_no);

	segment = physical_page[physical_page_no];

	if((EM_page_no = EM_page_mapped[physical_page_no]) == EMPTY)
	{
		/*
		 * Already done
		 */
		sure_note_trace0( LIM_VERBOSE,
					"already unmapped, so do nothing");

		return(SUCCESS);
	}

	phys_page = physical_page_no;

	if( map_count = page_already_mapped( EM_page_no, (unsigned char *)&phys_page ))
	{
		disconnect_MM_LIM_page( segment, EM_page_no,
								map_count, phys_page );
	}

	if(host_unmap_page(segment, EM_page_no) != SUCCESS)
		return(FAILURE);

	EM_page_mapped[physical_page_no] = EMPTY;

	sure_note_trace0(LIM_VERBOSE,"unmap OK");
	return(SUCCESS);
}

/*
===========================================================================

FUNCTION	: map_saved

PURPOSE		: Checks to see if a map has been saved for the specified
		handle

RETURNED STATUS	: TRUE  -  A map is saved for this handle
		FALSE	-  No map has been saved

DESCRIPTION	: checks the first entry in the map for the value 'FREE'

=========================================================================
*/
GLOBAL boolean map_saved IFN1(short, handle_no)

{
	long		storage_ID;	/* host dependant storage	*/
					/* identifier, usually a ptr.	*/
	unsigned char	*ptr;		/* pointer to storage area	*/
	short		status;		/* value read from map		*/

	storage_ID = handle[handle_no];
	ptr = USEBLOCK(storage_ID);

	/*
	 * offset pointer to correct position
	 */

	ptr += MAP_OFFSET;
	status = *(short *)ptr;

	FORGETBLOCK(storage_ID)

	return((status == FREE) ? FALSE : TRUE);
}


/*
===========================================================================

FUNCTION	: save_map

PURPOSE		: takes a copy of the EM_page_mapped array and store it in
		the map section of the handle data storage area

RETURNED STATUS	: SUCCESS - everything OK
		  FAILURE - invalid segment no. passed in src array

DESCRIPTION	: if handle_no is >= 0 the map is stored in the data area
			assigned to that handle
		  if handle_no == -1 the map is stored in the array pointed
		  	to by dst_segment:dst_offset
		  if handle_no == -2 only the pages specified by the segment
		  	addresses in the src array (pointed to by 
		  	src_segment:src_offset) are saved in the dst array
		  	(pointed to by dst_segment:dst_offset).

=========================================================================
*/
GLOBAL int save_map IFN5(short, handle_no, 
			 unsigned short, dst_segment,
			 unsigned short, dst_offset,
			 unsigned short, src_segment,
			 unsigned short, src_offset)

{
	unsigned short	offset,		/* temp offset variable		*/
			segment;	/* segment address to be saved	*/
	short		i,		/* loop counter			*/
			page_no,	/* physical page no.		*/
			no_to_save;	/* no of pages in src array	*/

	if(handle_no >= 0)
		for (i = 0; i < no_phys_pages; i++)
			set_map_no(handle_no, i, EM_page_mapped[i]);

	else if(handle_no == -1)
		for(i = 0; i < no_phys_pages; i++)
		{
			write_intel_word(dst_segment, dst_offset, EM_page_mapped[i]);
			dst_offset +=2;
		}

	else if(handle_no == -2)
	{
		offset = dst_offset;
		for(i  = 0; i < no_phys_pages; i++)
		{
#ifdef NTVDM
			write_intel_word(dst_segment, offset, LEAVE);
#else
			write_intel_word(dst_segment, offset, EMPTY);
#endif
			offset += 2;
		}
		read_intel_word(src_segment, src_offset, (word *)&no_to_save);
		for (i = 0; i < no_to_save; i++)
		{
			src_offset += 2;
			read_intel_word(src_segment, src_offset, &segment);
			/*
			 *	Find Physical page no.
			 */
			page_no = 0;
			do
				if(segment == physical_page[page_no])
					break;
			while(++page_no < no_phys_pages);

			if(page_no >= no_phys_pages)
				return (FAILURE);
			/*
			 *	Save EM page number in destination array	
			 */
			offset = dst_offset + (page_no * 2);
			write_intel_word(dst_segment, offset, EM_page_mapped[page_no]);
		}
	}
	return(SUCCESS);
}

/*
===========================================================================

FUNCTION	: restore_map

PURPOSE		: reads the specified map and returns 2 arrays specifying
		which pages have to be mapped out and which ones have to be
		mapped in

RETURNED STATUS	: SUCCESS - Map read successfully


DESCRIPTION	: A +ve handle number indicates that the map is stored
		within the handle data area.
		If the handle number is -ve the map will be read from the 
		data pointed to by segment:offset

		Only page out - if there is a page currently mapped in and
		it is not being replaced by a copy of itself or an empty
		page
		Only page in - if new page is different to existing one
		and it is not empty

=========================================================================
*/
#ifdef ANSI
GLOBAL int restore_map (short handle_no, 
			unsigned short segment, 
			unsigned short offset, 
			short pages_out[], 
			short pages_in[])
#else
GLOBAL int restore_map (handle_no, segment, offset, pages_out, pages_in)
short handle_no;
unsigned short segment; 
unsigned short offset; 
short pages_out[]; 
short pages_in[];
#endif	/* ANSI */
{
	short	i,		/* loop counter				*/
		new_page,	/* page number read from map		*/
		old_page;	/* existing page number			*/

	for(i = 0; i < no_phys_pages; i++)
	{
		if(handle_no >= 0)
			new_page = get_map_no(handle_no, i);
		else
		{
			read_intel_word(segment, offset, (word *)&new_page);
			offset += 2;
			if(new_page < LEAVE || new_page >= total_pages)
				return(FAILURE);
		}
		old_page = EM_page_mapped[i];

/*
		if(old_page != EMPTY && new_page != EMPTY && old_page != new_page )
*/
/* need to do unmap to empty state case to update the page copy in the LIM 
   space in case of new maps of that page to other LIM slots. */
#ifdef NTVDM
		if(old_page != EMPTY && old_page != new_page && new_page != LEAVE)
#else
		if(old_page != EMPTY && old_page != new_page)
#endif
			pages_out[i] = old_page;
		else
			pages_out[i] = EMPTY;
#ifdef NTVDM
		if(new_page != EMPTY && new_page != old_page && new_page != LEAVE)
#else
		if(new_page != EMPTY && new_page != old_page)
#endif
			pages_in[i] = new_page;
		else
			pages_in[i] = EMPTY;
	}
	if(handle_no >= 0)
		set_map_no(handle_no, 0, FREE);

	return(SUCCESS);
}

/*
===========================================================================

FUNCTION	: copy_exchange_data

PURPOSE		: copies or exchanges data between conventional and
		expanded memory

RETURNED STATUS	: SUCCESS - everything ok
		FAILURE - Error ocurred in copying data

DESCRIPTION	: type - uses a bit pattern, bit 0 represents destination,
		bit 1 represents source, a set bit means expanded, a clear
		bit means conventional memory
		bit 2 represents exchange if set or move if it is clear

		e.g. 	0 (0000) = move conventional to conventional
		 	1 (0001) = move conventional to expanded
		 	6 (0110) = exchange expanded to conventional
		 	7 (0111) = exchange expanded to expanded

=========================================================================
*/
GLOBAL int copy_exchange_data IFN8(unsigned char, type, 
				   short, src_handle, 
				   unsigned short, src_seg_page, 
				   unsigned short, src_offset,
				   short, dst_handle, 
				   unsigned short, dst_seg_page, 
				   unsigned short, dst_offset, 
				   unsigned long, length)

{
	short		dst_EMpage,	/* EM page no . of destination	*/
			src_EMpage;	/* EM page no. of source	*/
	int		page_no;	/* phys. page no. of mapped page*/

	/* 
	 * First check to see if the expanded memory page is mapped
	 * if it is - change the type to deal directly with the 
	 * physical page that it is mapped to
	 */
	if( type & 1)
	{
		dst_EMpage = get_EMpage_no(dst_handle, dst_seg_page);
		if((page_no = page_status(dst_EMpage)) != UNMAPPED )
		{
			dst_seg_page = physical_page[page_no];
			type &= 6;
		}
	}
	if( type & 2)
	{
		src_EMpage = get_EMpage_no(src_handle, src_seg_page);
		if((page_no = page_status(src_EMpage)) != UNMAPPED )
		{
			src_seg_page = physical_page[page_no];
			type &= 5;
		}
	}

	switch(type)
	{
		case 0:	if(host_copy_con_to_con(length, src_seg_page, src_offset,
			   dst_seg_page, dst_offset) != SUCCESS)
				return(FAILURE);
			break;

		case 1:	if(host_copy_con_to_EM(length, src_seg_page, src_offset,
			   dst_EMpage, dst_offset) != SUCCESS)
			   	return(FAILURE);
			break;

		case 2:	if(host_copy_EM_to_con(length, src_EMpage, src_offset,
			   dst_seg_page, dst_offset) != SUCCESS)
			   	return(FAILURE);
			break;

		case 3:	if(host_copy_EM_to_EM(length, src_EMpage, src_offset,
			   dst_EMpage, dst_offset) != SUCCESS)
			   	return(FAILURE);
			break;

		case 4:	if(host_exchg_con_to_con(length, src_seg_page, src_offset,
			   dst_seg_page, dst_offset) != SUCCESS)
				return(FAILURE);
			break;

		case 5:	if(host_exchg_con_to_EM(length, src_seg_page, src_offset,
			   dst_EMpage, dst_offset) != SUCCESS)
			   	return(FAILURE);
			break;

		case 6:	if(host_exchg_con_to_EM(length, dst_seg_page, dst_offset,
			   src_EMpage, src_offset) != SUCCESS)
			   	return(FAILURE);
			break;

		case 7:	if(host_exchg_EM_to_EM(length, src_EMpage, src_offset,
			   dst_EMpage, dst_offset) != SUCCESS)
			   	return(FAILURE);
			break;

		default: return(FAILURE);
	}
	return(SUCCESS);
}

/*
========================================================================

FUNCTION	: page_status

PURPOSE		: checks if a particular EM page is mapped or not

RETURNED STATUS	: page_no - physical page no returned if mapped
		  UNMAPPED - returned if not mapped

DESCRIPTION	:

========================================================================
*/

GLOBAL int page_status IFN1(short, EMpage_no)
{
	short physical_page_no = 0;
			/* position of page in physical memory	*/

	do
		if(EM_page_mapped[physical_page_no] == EMpage_no)
			break;
	while(++physical_page_no < no_phys_pages );

	if(physical_page_no >= no_phys_pages)
		return(UNMAPPED);
	else
		return(physical_page_no);
}

/*
========================================================================

FUNCTION	: phys_page_from_addr 
		  
PURPOSE		: determines the physical page number of a LIM page
		  from its Intel address.
		  		
RETURNED STATUS	: The physical page containing the LIM address.

DESCRIPTION	:
			
=======================================================================
*/

LOCAL SHORT
phys_page_from_addr IFN1( sys_addr, address )

{
	sys_addr	start;

	start = effective_addr( EM_start, 0x0 );

	return( (ULONG)(( address - start ) / EMM_PAGE_SIZE ));
}

/*
========================================================================

FUNCTION	: get_total_pages
		  get_unallocated_pages
		  get_base_address
		  get_total_handles
		  get_total_open_handles
		  get_no_phys_pages
		  get_page_seg
		  get_map_size

PURPOSE		: simply returns the reqested variables, to avoid 
		having to use globals


RETURNED STATUS	: the following variables are returned , depending upon 
		the routine called:-
			total_pages
			unallocated_pages
			base_address
			total_handles
			total_open_handles
			no_phys_pages
			physical_page[i]
			map_size

DESCRIPTION	: 

========================================================================
*/

#ifdef SEGMENTATION
/*
 * The following #include specifies the code segment into which this
 * module will by placed by the MPW C compiler on the Mac II running
 * MultiFinder.
 */
#include "SOFTPC_LIM2.seg"
#endif

GLOBAL short get_total_pages IFN0()
{
	return(total_pages);
}

#ifdef SEGMENTATION
/*
 * The following #include specifies the code segment into which this
 * module will by placed by the MPW C compiler on the Mac II running
 * MultiFinder.
 */
#include "SOFTPC_LIM.seg"
#endif

GLOBAL short get_unallocated_pages IFN0()
{
	return(unallocated_pages);
}

GLOBAL unsigned short get_base_address IFN0()
{
	return(EM_start);
}

GLOBAL short get_total_handles IFN0()
{
	return(total_handles);
}

GLOBAL short get_total_open_handles IFN0()
{
	return(total_open_handles);
}

GLOBAL short get_no_phys_pages IFN0()
{
	return(no_phys_pages);
}

GLOBAL unsigned short get_page_seg IFN1(unsigned char, page_no)
{
	return(physical_page[page_no]);
}

GLOBAL short get_map_size IFN0()
{
	return(map_size);
}

/*
========================================================================

FUNCTION	: LIM_b_write,
		  LIM_w_write,
		  LIM_str_write
		  patch_pages
		  
PURPOSE		: LIM byte, word & string - called from write check
		  failure code in the CPU when a write to a multi-mapped
		  LIM page is detected.
		  patch_pages - generic code called from the other
		  three routines.
		  		
RETURNED STATUS	: None.

DESCRIPTION	: 
			
========================================================================
*/

LOCAL VOID
patch_one_page_partial IFN4( sys_addr, intel_addr, sys_addr, eff_addr,
			MM_LIM_op_type, type, ULONG, data )

{
	ULONG		check_len;

	UNUSED( intel_addr );	/* Used in patch_one_page_full() */

	switch( type )
	{
		case BYTE_OP:
			check_len = 1;
			break;

		case WORD_OP:
			check_len = 2;
			break;

		case STR_OP:
			check_len = data;
			break;
	}

	sas_overwrite_memory( eff_addr, check_len );
}

LOCAL VOID
patch_one_page_full IFN4( sys_addr, intel_addr, sys_addr, eff_addr,
			MM_LIM_op_type, type, ULONG, data )

{
	sys_addr	check_addr;
	ULONG		check_len;

	switch( type )
	{
		case BYTE_OP:
			check_addr = eff_addr;
			check_len = 1;
			sas_store_no_check( eff_addr, data );
			break;

		case WORD_OP:
			check_addr = eff_addr;
			check_len = 2;
			sas_storew_no_check( eff_addr, data );
			break;

		case STR_OP:
			check_addr = eff_addr;
			check_len = data;
			do
			{
				sas_store_no_check( eff_addr, 
					sas_hw_at_no_check(
							intel_addr ));
				intel_addr++;
				eff_addr++;
			}
			while( --data );
			break;
	}

	sas_overwrite_memory( check_addr, check_len );
}

LOCAL VOID
patch_pages IFN6( MM_LIM_op_type, type, ULONG, offset,
			SHORT, EM_page_no, SHORT, phys_page_no, 
			ULONG, data, sys_addr, intel_addr )

{
	LONG		cnt01;
	sys_addr	eff_addr;

	for( cnt01 = 0; cnt01 < get_no_phys_pages(); cnt01++ )
	{
		if(( EM_page_mapped[cnt01] == EM_page_no ) && 
							( cnt01 != phys_page_no ))
		{
			eff_addr = effective_addr( get_page_seg(cnt01),
										offset );

			host_patch_one_page( intel_addr, eff_addr, type, data );

			sure_note_trace1(LIM_VERBOSE,
					"MM LIM write type %d", type );
			sure_note_trace2(LIM_VERBOSE, 
					"log page 0x%x, phs page 0x%x",
								EM_page_no, cnt01 );
		}
	}
}

GLOBAL VOID
LIM_b_write IFN1( sys_addr, intel_addr )

{
	ULONG		limdata;
	SHORT		EM_page_no, phys_page_no;
	word		offset;

	phys_page_no = phys_page_from_addr( intel_addr );

	offset = intel_addr -
				effective_addr( get_page_seg(phys_page_no), 0x0 );

	EM_page_no = EM_page_mapped[phys_page_no];

	/*
	 * Get the data written in order to patch up this
	 * page's buddy pages.
	 */

	limdata = (ULONG) sas_hw_at_no_check( intel_addr );
	patch_pages( BYTE_OP, offset, EM_page_no, phys_page_no,
								limdata, intel_addr );

	/*
	 * Tell the CPU that this page has been written to.
	 */

	sas_overwrite_memory( intel_addr, 1 );
}

GLOBAL VOID
LIM_w_write IFN1( sys_addr, intel_addr )

{
	ULONG		limdata; 
	SHORT		EM_page_no, phys_page_no;
	word		offset;

	phys_page_no = phys_page_from_addr( intel_addr );

	offset = intel_addr -
				effective_addr( get_page_seg(phys_page_no), 0x0 );

	EM_page_no = EM_page_mapped[phys_page_no];

	limdata = (ULONG) sas_w_at_no_check( intel_addr );
	patch_pages( WORD_OP, offset, EM_page_no, phys_page_no,
								limdata, intel_addr );

	sas_overwrite_memory( intel_addr, 2 );
}

GLOBAL VOID
LIM_str_write IFN2( sys_addr, intel_addr, ULONG, length )

{
   	SHORT		EM_page_no, phys_page_no;
	word		offset;

	phys_page_no = phys_page_from_addr( intel_addr );

	offset = intel_addr -
				effective_addr( get_page_seg(phys_page_no), 0x0 );

	EM_page_no = EM_page_mapped[phys_page_no];

	patch_pages( STR_OP, offset, EM_page_no, phys_page_no,
								length, intel_addr );

	sas_overwrite_memory( intel_addr, length );
}

#ifndef PROD
/*
===========================================================================

FUNCTION	: print_handle_data

PURPOSE		: used for debugging only - prints all the data stored 
		for a given handle

RETURNED STATUS	: none

DESCRIPTION	: 

=========================================================================
*/
GLOBAL print_handle_data IFN1(short, handle_no)

{
	long	storage_ID;
	byte	*ptr;
	short	no_pages, i;
	char	*name_ptr;
	short	*map_ptr;
	short	*page_ptr;

	if ((storage_ID = handle[handle_no]) == 0)
	{
		printf("Unassigned handle - No. %d\n",handle_no);
		return;
	}
	ptr = USEBLOCK(storage_ID);
	name_ptr = (char *)ptr + NAME_OFFSET;
	map_ptr = (short *)(ptr + MAP_OFFSET);
	page_ptr = (short *)(ptr + page_offset);

	no_pages = *(short *)ptr;
	printf("Handle No. %d\n",handle_no);
	printf("No. of Pages = %d\n",no_pages);
	printf("Name         = '");
	for(i=0;i<8;i++)
		printf("%c",*name_ptr++);
	printf("'\n");
	printf("Map = ");
	for(i=0;i<no_phys_pages;i++)
		printf(" %d",*map_ptr++);
	printf("\n");
	for(i=0;i<no_pages;i++)
		printf("Page (%d)     = %d\n",i,*page_ptr++);

	FORGETBLOCK(storage_ID);

	return;
}
#endif	/* !PROD	*/
#endif 	/* LIM		*/
