/**********************************************************************/
/*                           E M M C                                  */
/*--------------------------------------------------------------------*/
/* Tarea          : contiene algunas funciones para acceder a la      */
/*                  memoria EMS (Expanded Memory)                     */
/*--------------------------------------------------------------------*/
/* Autor          : MICHAEL TISCHER                                   */
/* desarrollado el: 30.08.1988                                        */
/* £ltimo update  : 29.01.1995                                        */
/*--------------------------------------------------------------------*/
/* modelo de memoria: alguno con puntero FAR a los datos, es          */
/* decir Compact, Large o Huge                                        */
/**********************************************************************/

/*== enlazar archivos Include  =======================================*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <conio.h>
#include <dos.h>
#include "EMMC.h"

/*== Typedefs ========================================================*/

typedef unsigned char BYTE;                  /*nos construimos un byte*/
typedef unsigned int WORD;
typedef BYTE BOOL;                            /*como BOOLEAN en Pascal*/

/*== Macros ==========================================================*/

/*-- MK_FP crea de una direcci¢n de segmento y Offset un -------------*/
/*-- puntero FAR a un objeto                                   -------*/

#ifdef MK_FP                                   /*¨ya se defini¢ MK_FP?*/
 #undef MK_FP
#endif
#define MK_FP(seg, ofs) ((void far *) ((unsigned long) (seg)<<16|(ofs)))

/*-- PAGE_ADR devuelve un puntero a la p gina f¡sica X dentro del ----*/
/*-- Page-Frame de la memoria EMS                                 ----*/

#define PAGE_ADR(x) ((void *) MK_FP(ems_frame_seg() + ((x) << 10), 0))

/*== Constantes ======================================================*/

#define TRUE  1                  /*Constantes para el trabajo con BOOL*/
#define FALSE 0

#define EMS_INT 0x67    /*n£mero de interrupci¢n para acceder a la EMM*/
#define EMS_ERR -1                      /*se devuelve en caso de error*/

/*== variables globales===============================================*/

BYTE emm_ec;                /*aqu¡ se guardan los c¢digos de error EMM*/

/***********************************************************************/
/*  Funci¢n          : E M S _ I N S T                                  */
/**--------------------------------------------------------------------**/
/*  Tarea            : Determina si hay instalada memoria EMS, y un     */
/*                     driver EMS (EMM) asociado.                       */
/*  Par metro entrada: ninguno                                          */
/*  Valor de retorno : TRUE, si hay memoria EMS instalada, sino         */
/*                     FALSE.                                           */
/************************************************************************/

BOOL ems_inst()
 {
  static char emm_name[] = { 'E', 'M', 'M', 'X', 'X', 'X', 'X', '0' };
  union REGS regs;          /*reg. de proc. para la llamada de interr.*/
  struct SREGS sregs;       /*reg. de segm. para la llamada de interr.*/

  /*-- Construir puntero a nombre en cabecera de un contr. de disp. --*/

  regs.x.ax = 0x3567;          /*n§ de func.: obtener vector int. 0x67*/
  intdosx(&regs, &regs, &sregs);      /*llamar la interr. del DOS 0x21*/
  return !memcmp( MK_FP(sregs.es, 10), emm_name, sizeof emm_name );
 }

/************************************************************************/
/*  Funci¢n          : E M S _ N U M _ P A G E                          */
/**--------------------------------------------------------------------**/
/*  Tarea            : Obtiene el n§ total de p ginas EMS.              */
/*  Par metro entrada: ninguno                                          */
/*  Valor de retorno : EMS_ERR en caso de error, sino el n£mero de      */
/*                     p ginas EMS.                                     */
/************************************************************************/
int ems_num_page()
 {
  union REGS regs;          /*reg. de proc. para la llamada de interr.*/

  regs.h.ah = 0x42;     /*n£mero de funci¢n: obtener n£mero de p ginas*/
  int86(EMS_INT, &regs, &regs);                           /*llamar EMM*/
  if ((int) (emm_ec = regs.h.ah))            /*¨ha aparecido un error?*/
	return(EMS_ERR);                                /*S¡, mostrar error*/
  else                                                  /*no hay error*/
	return( regs.x.dx );             /*devolver n£mero total de p ginas*/
 }

/**********************************************************************/
/*  Funci¢n          : E M S _ F R E E _ P A G E                        */
/**--------------------------------------------------------------------**/
/*  Tarea            : Obtiene la cantidad de p ginas EMS libres.       */
/*  Par metro entrada: ninguno                                          */
/*  Valor de retorno : EMS_ERR en caso de error, sino el n£mero de      */
/*                     p ginas EMS libres.                              */
/***********************************************************************/

int ems_free_page()
 {
  union REGS regs;          /*reg. de proc. para la llamada de interr.*/

  regs.h.ah = 0x42;     /*n£mero de funci¢n: obtener n£mero de p ginas*/
  int86(EMS_INT, &regs, &regs);                           /*llamar EMM*/
  if ((int) (emm_ec = regs.h.ah))            /*¨ha aparecido un error?*/
	return(EMS_ERR);                                /*S¡, mostrar error*/
  else                                                  /*no hay error*/
	return( regs.x.bx );            /*devolver n£mero de p ginas libres*/
 }

/**********************************************************************/
/*  Funci¢n          : E M S _ F R A M E _ S E G                        */
/**--------------------------------------------------------------------**/
/*  Tarea            : Obtiene la dir. de segmento del EMS-Page-Frame   */
/*  Par metro entrada: ninguno                                          */
/*  Valor de retorno : EMS_ERR en caso de error, sino la direcci¢n de   */
/*                     segmento del Page-Frame.                         */
/***********************************************************************/

WORD ems_frame_seg()
 {
  union REGS regs;          /*reg. de proc. para la llamada de interr.*/

  regs.h.ah = 0x41;          /*n§ de funci¢n: obt.dir.segm. Page-Frame*/
  int86(EMS_INT, &regs, &regs);                           /*llamar EMM*/
  if ((int) (emm_ec = regs.h.ah))            /*¨ha aparecido un error?*/
	return(EMS_ERR);                                /*S¡, mostrar error*/
  else                                                  /*no hay error*/
	return( regs.x.bx );               /*devolver direcci¢n de segmento*/
 }

/**********************************************************************/
/*  Funci¢n          : E M S _ A L L O C                                */
/**--------------------------------------------------------------------**/
/*  Tarea            : Aloja el n£mero de p ginas indicado y devuelve   */
/*                     un Handle para acceder a estas p ginas.          */
/*                                                                      */
/*  Par metro entrada: PAGES : el n£mero de p ginas a alojar            */
/*                             (de 16 KBytes cada uno)                  */
/*  Valor de retorno : EMS_ERR en caso de error, sino el Handle EMS.    */
/***********************************************************************/

int ems_alloc(int pages)
 {
  union REGS regs;          /*reg. de proc. para la llamada de interr.*/

  regs.h.ah = 0x43;                 /*n£mero de funci¢n: aljar p ginas*/
  regs.x.bx = pages;                /*fijar n£mero de p ginas a alojar*/
  int86(EMS_INT, &regs, &regs);                           /*llamar EMM*/
  if ((int) (emm_ec = regs.h.ah))            /*¨ha aparecido un error?*/
	return(EMS_ERR);                                /*S¡, mostrar error*/
  else                                                  /*no hay error*/
	return( regs.x.dx );                          /*devolver Handle-EMS*/
 }

/**********************************************************************/
/*  Funci¢n          : E M S _ M A P                                    */
/**--------------------------------------------------------------------**/
/*  Tarea            : Proyecta una de las p ginas alojadas bajo        */
/*                     el Handle indicado, a un p gina f¡sica del       */
/*                     del Page-Frame.                                  */
/*  Par metro entrada: HANDLE: el Handle devuelto por EMS_ALLOC         */
/*                     LOGP  : la p gina l¢gica (0 a n-1)               */
/*                     PHYSP : la p gina f¡sica (0 a 3)                 */
/*  Valor de retorno : FALSE en caso de error, sino  TRUE.              */
/***********************************************************************/

BOOL ems_map(int handle, int logp, BYTE physp)
 {
  union REGS regs;          /*reg. de proc. para la llamada de interr.*/

  regs.h.ah = 0x44;                  /*n£mero de funci¢n: fijar Maping*/
  regs.h.al = physp;                             /*fijar p gina f¡sica*/
  regs.x.bx = logp;                              /*fijar p gina l¢gica*/
  regs.x.dx = handle;                               /*fijar Handle EMS*/
  int86(EMS_INT, &regs, &regs);                           /*llamar EMM*/
  return (!(emm_ec = regs.h.ah));
 }

/**********************************************************************/
/*  Funci¢n          : E M S _ F R E E                                  */
/**--------------------------------------------------------------------**/
/*  Tarea            : Devuelve la memoria alojada bajo un Handle       */
/*                     de nuevo.                                        */
/*  Par metro entrada: HANDLE: el Handle devuelto por EMS_ALLOC         */
/*  Valor de retorno : FALSE en caso de error, sino  TRUE.              */
/***********************************************************************/
BOOL ems_free(int handle)
 {
  union REGS regs;          /*reg. de proc. para la llamada de interr.*/

  regs.h.ah = 0x45;               /*n£mero de funci¢n: liberar p ginas*/
  regs.x.dx = handle;                               /*fijar Handle EMS*/
  int86(EMS_INT, &regs, &regs);                           /*llamar EMM*/
  return (!(emm_ec = regs.h.ah));     /*si AH contiene 0, todo est  ok*/
 }

/**********************************************************************/
/*  Funci¢n          : E M S _ V E R S I O N                            */
/**--------------------------------------------------------------------**/
/*  Tarea            : Obtiene el n£mero de versi¢n EMM.                */
/*  Par metro entrada: ninguno                                          */
/*  Valor de retorno : EMS_ERR en caso de error, sino el n£mero de      */
/*                     versi¢n EMM.                                     */
/*  Info             : En el n£mero de versi¢n, 10 est  por 1.0, 11     */
/*                     por 1.1, 34 por 3.4, etc.                        */
/***********************************************************************/

BYTE ems_version()
 {
  union REGS regs;          /*reg. de proc. para la llamada de interr.*/

  regs.h.ah = 0x46;           /*n£mero de funci¢n: obtener versi¢n EMS*/
  int86(EMS_INT, &regs, &regs);                           /*llamar EMM*/
  if ((int) (emm_ec = regs.h.ah))            /*¨ha aparecido un error?*/
	return(EMS_ERR);                                /*S¡, mostrar error*/
  else             /*sin error, calcula n£mero de versi¢n de cifra BCD*/
	return( (regs.h.al & 15) + (regs.h.al >> 4) * 10);
 }

/**********************************************************************/
/*  Funci¢n          : E M S _ S A V E _ M A P                          */
/**--------------------------------------------------------------------**/
/*  Tarea            : Guarda la proyecci¢n (Mapping) entre p ginas     */
/*                     l¢gicas y f¡sicas.                               */
/*  Par metro entrada: HANDLE: el Handle devuelto por EMS_ALLOC         */
/*  Valor de retorno : FALSE en caso de error, sino  TRUE.              */
/***********************************************************************/

BOOL ems_save_map(int handle)
 {
  union REGS regs;          /*reg. de proc. para la llamada de interr.*/

  regs.h.ah = 0x47;               /*n£mero de funci¢n: guardar Mapping*/
  regs.x.dx = handle;                               /*fijar Handle EMS*/
  int86(EMS_INT, &regs, &regs);                           /*llamar EMM*/
  return (!(emm_ec = regs.h.ah));     /*si AH contiene 0, todo est  ok*/
 }

/**********************************************************************/
/*  Funci¢n          : E M S _ R E S T O R E _ M A P                    */
/**--------------------------------------------------------------------**/
/*  Tarea            : Recupera una proyecci¢n entre p ginas l¢gicas    */
/*                     y f¡sicas guardada anteriormente mediante        */
/*                     EMS_SAVE_MAP.                                    */
/*  Par metro entrada: HANDLE: el Handle devuelto por EMS_ALLOC         */
/*  Valor de retorno : FALSE en caso de error, sino  TRUE.              */
/***********************************************************************/

BOOL ems_restore_map(int handle)
 {
  union REGS regs;          /*reg. de proc. para la llamada de interr.*/

  regs.h.ah = 0x48;             /*n£mero de funci¢n: Recuperar Mapping*/
  regs.x.dx = handle;                               /*fijar Handle EMS*/
  int86(EMS_INT, &regs, &regs);                           /*llamar EMM*/
  return (!(emm_ec = regs.h.ah));     /*si AH contiene 0, todo est  ok*/
 }

/**********************************************************************/
/*  Funci¢n          : P R I N T _ E R R                                */
/**--------------------------------------------------------------------**/
/*  Tarea            : Visualiza un mensaje de error EMS en la pantalla */
/*                     y termina el programa.                           */
/*  Par metro entrada: ninguno                                          */
/*  Valor de retorno : ninguno                                          */
/*  Info             : Esta funci¢n s¢lo se puede llamar si durante     */
/*                     la llamada anterior de una funci¢n del EMM       */
/*                     ha aparecido un error.                           */
/***********************************************************************/

void print_err()
 {
  static char nid[] = "no identificable";
  static char *err_vec[] =
	{  "Error en el driver EMS (EMM destruido)",                 /*0x80*/
		"Error en el hardware EMS",                               /*0x81*/
		nid,                                                      /*0x82*/
		"Handle EMM no v lido",                                   /*0x83*/
		"funci¢n EMS llamada no existe",                          /*0x84*/
		"no quedan Handles EMS disponibles",                      /*0x85*/
		"Error al guardar o recuperar el Mapping",                /*0x86*/
		"m s p ginas pedidas de las que existen f¡sicamente",     /*0x87*/
		"m s p ginas pedidas de las que quedan libres",           /*0x88*/
		"pedidad cero p ginas",                                   /*0x89*/
		"p gina l¢gica no pertenece al Handle",                   /*0x8A*/
		"n£mero de p gina f¡sico no v lido",                      /*0x8B*/
		"Zona de memoria mapping est  llena",                     /*0x8C*/
		"Guardado del mapping ya se realiz¢",                     /*0x8D*/
		"Recuperar el mapping sin guardarlo previamente"
	};

  printf("\nATENCION! Error al acceder a la memoria EMS!r\n");
  printf("         ... %s\n", (emm_ec<0x80 || emm_ec>0x8E) ?
										 nid : err_vec[emm_ec-0x80]);
  exit( 1 );                   /*terminar programa con c¢digo de error*/
 }

/**********************************************************************/
/*  Funci¢n          : V R _ A D R                                    */
/**------------------------------------------------------------------**/
/*  Tarea            : Devuelve un puntero a la RAM de v¡deo.         */
/*  Par metro entrada: ninguno                                        */
/*  Valor de retorno : Puntero VOID a la RAM de v¡deo.                */
/**********************************************************************/

void *vr_adr()
 {
  union REGS regs;          /*reg. de proc. para la llamada de interr.*/

  regs.h.ah = 0x0f;          /*n£mero de funci¢n:obtener modo de v¡deo*/
  int86(0x10, &regs, &regs); /*llamar interrupci¢n de la BIOS de v¡deo*/
  return ( MK_FP((regs.h.al==7) ? 0xb000 : 0xb800, 0) );
 }


/**********************************************************************/
/**                        PROGRAMA PRINCIPAL                        **/
/**********************************************************************/
////
////void main()
////{
//// int  pagenum,                                 /*n£mero de p ginas EMS*/
////		handle,                   /*Handle para acceder a la memoria EMS*/
////		i;                                           /*contador de bucle*/
//// WORD pageseg ;                 /*direcci¢n de segmento del Page-Frame*/
//// BYTE emmver;                            /*n£mero de versi¢n de la EMM*/
////
//// printf("EMMC  -  (c) 1988, 92 by MICHAEL TISCHER\n\n");
//// if ( ems_inst() )                   /*¨Est  instalada la memoria EMS?*/
////  {                                                               /*Si*/
////	/*-- Visualizar informaciones sobre la memoria EMS ----------------*/
////
////	if ( (int) (emmver = ems_version()) == EMS_ERR) /*Obten. n§ versi¢n*/
////	 print_err();    /*Error: visualizar men. error y term. el programa*/
////	else                                                 /*no hay error*/
////	 printf("N£mero de versi¢n EMM        : %d.%d\n",
////			  emmver/10, emmver%10);
////
////	if ( (pagenum = ems_num_page()) == EMS_ERR)   /*obten. n§  p ginas*/
////	 print_err();   /*Error: vis. men. de error y terminar el programa*/
////	printf("N£mero de p ginas EMS        : %d (%d KByte)\n",
////			  pagenum, pagenum << 4);
////
////	if ( (pagenum = ems_free_page()) == EMS_ERR)
////	 print_err();      /*Error: vis. men. error y terminar el programa*/
////	printf("... libres                   : %d (%d KByte)\n",
////			 pagenum, pagenum << 4);
////
////	if ( (int) (pageseg = ems_frame_seg()) == EMS_ERR)
////	 print_err();      /*Error: vis. men. error y terminar el programa*/
////	printf("Direcc. segmento del Page-Frame: %X\n", pageseg);
////
////	printf("\nAhora se aloja una p gina de la memoria EMS, y el con-\n");
////	printf("tenido de la pantalla de la RAM de v¡deo se copia a\n");
////	printf("esta p gina.\n");
////	printf("                    ... por favor pulse una tecla\n");
////	getch();                                        /*esperar una tecla*/
////
////	/*-- alojar una p gina y poyectar sobre la primera p gina l¢gica --*/
////	/*-- en el Page-Frame                                           ---*/
////
////	if ( (handle = ems_alloc(1)) == EMS_ERR)
////	 print_err();       /*Error: vis. men. error y terminar el programa*/
////	if ( !ems_map(handle, 0, 0) )                       /*fijar Mapping*/
////	 print_err();    /*Error: vis. men. de error y terminar el programa*/
////
////	/*-- Copiar 4000 bytes de la RAM de v¡deo a la memoria EMS     ----*/
////
////	memcpy(PAGE_ADR(0), vr_adr(), 4000);
////
////	for (i=0; i<24; ++i)                           /*borrar la pantalla*/
////	 printf("\n");
////
////	printf("El antiguo contenido de la pantalla ha sido borrado y\n");
////	printf("con ello se ha perdido. Pero como ha sido guardado en\n");
////	printf("la memoria EMS, se puede volver a copiar de all¡ a la\n");
////	printf("RAM de v¡deo.\n");
////	printf("                        ... por favor pulse una tecla\n");
////	getch();                                        /*esperar una tecla*/
////
////	/*-- copiar de nuevo el contenido de la RAM de v¡deo de la memo----*/
////	/*-- ria EMS y devolver la memoria EMS alojada                 ----*/
////
////	memcpy(vr_adr(), PAGE_ADR(0), 4000);    /*copiar la V-RAM de vuelta*/
////	if ( !ems_free(handle) )                          /*liberar memoria*/
////	 print_err();       /*Error: vis. men. error y terminar el programa*/
////	printf("FIN");
////  }
//// else                                  /*el driver EMS no se descubri¢*/
////  printf("ATENCION: No hay memoria EMS instalada.\n");
////}