// StoraXX.C - Memory Subsystem
// Written by Stefan Pfeiffer, 951021
// Last modification: 960530
// (C) 1995 by Stefan Pfeiffer, DL1ELY

#include "storaxx.h"
#include <dos.h>
#include <memory.h>
#include <string.h>


#define ERROR_LOG



// Variablen fr EMS
// EMS vorhanden?
unsigned char ems_inst = 0;
// Soll EMS genutzt werden?
unsigned char use_ems = 1;
void far *ems_frame[4];

// Variablen fr XMS
// XMS vorhanden?
unsigned char xms_inst = 0;
// XMS nutzen?
unsigned char use_xms = 1;
// Einsprungpunkt fr den XMM
void (far * xms_service)(void);



// Sicherungsvariablen
unsigned char dos_umb_link;


long alloc_counter = 0;


struct xms_cb block;



void xinit(char use_umb)
{
   struct REGPACK r;
   void far *ptr;

#ifdef ERROR_LOG
   error_log("xinit()","Initializing...");
#endif

/*   if(_osmajor > 4)
   {
#ifdef ERROR_LOG
      error_log("xinit()","DOS >= 4.00 found!");
#endif
      if(use_umb)
      {
#ifdef ERROR_LOG
         error_log("xinit()","Set UMB Link");
#endif
         r.r_ax = 0x5802;
         intr(DOS_INT,&r);
         dos_umb_link = r.r_bx&0xFF;
    
         r.r_ax = 0x5803;
         r.r_bx = 0x0001;
         intr(DOS_INT,&r);
      }
#ifdef ERROR_LOG
      else error_log("xinit()","No UMBs used!");
#endif
   };
  
#ifdef ERROR_LOG
   error_log("xinit()","Looking fr XMS");
#endif
   r.r_ax = 0x4300;
   intr(XMS_INT,&r);
   xms_inst = (r.r_ax&0xFF) == 0x80;

   if(xms_inst)
   {
#ifdef ERROR_LOG
      error_log("xinit()","XMS found");
#endif
      r.r_ax = 0x4310;
      intr(XMS_INT,&r);
#ifdef ERROR_LOG
      error_log("xinit()","Getting XMS-Data");
#endif
      xms_service = MK_FP(r.r_es,r.r_bx);
   };

#ifdef ERROR_LOG  
   error_log("xinit()","Looking for EMS");
#endif

   ptr = getvect(EMM_INT);
   ptr = MK_FP(FP_SEG(ptr),10);

   if(!_fstrncmp((char far *)ptr,(char far *)EMM_SIG,8)) ems_inst = 1;

   if(!ems_inst)
   {
#ifdef ERROR_LOG
      error_log("xinit()","EMS not found");
#endif
   }
   else
   {
#ifdef ERROR_LOG
      error_log("xinit()","Getting EMS-Data");
#endif
      r.r_ax = 0x4000;
      intr(EMM_INT,&r);
      ems_inst = (r.r_ax>>8 == 0);

      if(ems_inst)
      {
#ifdef ERROR_LOG
         error_log("xinit()","EMS OK");
#endif
         r.r_ax = 0x4100;
         intr(EMM_INT,&r);
         if(r.r_ax>>8)
         {
#ifdef ERROR_LOG
            error_log("xinit()","EMS error: %X",r.r_ax>>8);
#endif
            ems_inst = 0;
         }
         else
         {
#ifdef ERROR_LOG
            error_log("xinit()","Pageframe = %X",r.r_bx);
#endif
            ems_frame[0] = MK_FP(r.r_bx,0x0000);
            ems_frame[1] = MK_FP(r.r_bx,0x4000);
            ems_frame[2] = MK_FP(r.r_bx,0x8000);
            ems_frame[3] = MK_FP(r.r_bx,0xC000);
         };
      };
   };*/
};


// Fhrt die Speicherverwaltung herunter
void xshutdown(void)
{
   struct REGPACK r;

   if(_osmajor > 4)
   {
#ifdef ERROR_LOG
      error_log("xshutdown()","Resetting UMB-link");
#endif
      r.r_ax = 0x5803;
      r.r_bx = dos_umb_link;
      intr(DOS_INT,&r);
   };  

};


// Alloziert paras Paragraphen bers DOS, liefert das Segment
// zurck, oder 0, falls nicht genug Speicher frei.
unsigned _xdosalloc(unsigned paras)
{
   struct REGPACK r;
  
   r.r_ax = 0x4800;
   r.r_bx = paras;
   intr(DOS_INT,&r);
  
#ifdef ERROR_LOG
   error_log("_xdosalloc()","%d paragraphs allocated at %X",paras,r.r_ax);
#endif
   if(!(r.r_flags&1)) return(r.r_ax); else return(0);
};

unsigned low_get_free(void)
{
   struct REGPACK r;
   
   r.r_ax = 0x4800;
   r.r_bx = 0xFFFF;

   intr(DOS_INT,&r);

   return(r.r_bx);
};

// Gibt den bei Segment seg allozierten DOS-Speicher wieder frei.
void _xdosfree(unsigned seg)
{
   struct REGPACK r;

#ifdef ERROR_LOG
   error_log("_xdosfree()","Freeing memory at %X.",seg);
#endif

   r.r_ax = 0x4900;
   r.r_es = seg;
   intr(DOS_INT,&r);

#ifdef ERROR_LOG
   error_log("_xdosfree()","OK!");
#endif
};

// Liefert den freien XMS-Speicher (in KB) zurck.
unsigned xms_get_free()
{
#ifdef ERROR_LOG
   error_log("xms_get_free()","Called");
#endif
   _AX = 0x0800;
   xms_service();
   return(_AX);
};

void xms_info(struct xms_ib *xms)
{
   unsigned hlp;
   unsigned xms_rev;

   xms->inst = xms_inst;
   if(xms_inst)
   {
      xms->used = use_xms; 
      xms->free = xms_get_free();

      _AX = 0;
      xms_service();

      hlp = _AX;
      xms_rev = _BX;

      xms->ver_lo = hlp&0xFF;
      xms->ver_hi = hlp >> 8;

      xms->rev_hi = xms_rev >> 8;
      xms->rev_lo = xms_rev&0xFF;
   };
   return;
};

// Alloziert einen XMS-Speicherblock
// Liefert den Handle zurck
unsigned int xms_alloc(unsigned int size)
{
   unsigned hlp;

#ifdef ERROR_LOG
   error_log("_xms_alloc()","Called");
#endif
   _DX = size;
   _AX = 0x0900;
   xms_service();
   if(_AX)
   {
      hlp = _DX;
#ifdef ERROR_LOG
      error_log("_xms_alloc()","Handle = %d",hlp);
#endif
      return(hlp);
   }
   else
   {
#ifdef ERROR_LOG
      error_log("_xms_alloc()","Error!");
#endif
      return(0);
   };
};

// Gibt den XMS-Speicherblock mit dem Handle handle frei
void xms_free(unsigned int handle)
{
#ifdef ERROR_LOG
   error_log("_xms_free()","Called, handle = %d",handle);
#endif
   _DX = handle;
   _AX = 0x0A00;
   xms_service();
};

// Bewegt einen Speicherbereich in den XMS-Block
void xms_moveto(void far *ptr, unsigned handle, unsigned long offset, unsigned long size)
{
#ifdef ERROR_LOG
   error_log("_xms_moveto()","Called");
#endif
   if(size&1) size++;
   block.size = size;
   block.src_handle = 0;
   block.src_ofs = (unsigned long)ptr;
   block.dest_handle = handle;
   block.dest_ofs = offset;

   asm {
      mov ax,0b00h
      mov si,offset block
      call dword ptr [offset xms_service]
   };
};

// Holt einen Speicherbreich aus dem XMS-Block
void xms_movefrom(unsigned handle, unsigned long offset, void far *ptr, unsigned long size)
{
#ifdef ERROR_LOG
   error_log("_xms_movefrom()","Called");
#endif
   if(size&1) size++;
   block.size = size;
   block.src_handle = handle;
   block.src_ofs = offset;
   block.dest_handle = 0;
   block.dest_ofs = (unsigned long)ptr;

   asm {
      mov ax,0b00h
      mov si,offset block
      call dword ptr [offset xms_service]
   };
};

unsigned ems_get_free()
{
   union REGS r;

#ifdef ERROR_LOG
   error_log("_ems_get_free()","Called");
#endif
   r.x.ax = 0x4200;
   int86(EMM_INT,&r,&r);
  
   if(r.h.ah) 
   {
#ifdef ERROR_LOG
      error_log("_ems_get_free()","EMS Error: %X",r.h.ah);
#endif
      ems_inst = 0;
      return(0);
   }
   else return(r.x.bx);
};


unsigned ems_alloc(unsigned pages)
{
   union REGS r;

#ifdef ERROR_LOG
   error_log("_ems_alloc()","Called");
#endif
   r.x.ax = 0x4300;
   r.x.bx = pages;
   int86(EMM_INT,&r,&r);
 
   if(r.h.ah)
   {
#ifdef ERROR_LOG
      error_log("_ems_alloc()","EMS Error: %X",r.h.ah);
#endif
      ems_inst = 0;
      return(0);
   }
   else
   {
#ifdef ERROR_LOG
      error_log("_ems_alloc()","Handle = %d",r.x.dx);  
#endif
      return(r.x.dx);
   };
};


void ems_info(struct ems_ib *ems)
{
   struct REGPACK r;

   ems->inst = ems_inst;
   if(ems->inst)
   {
      ems->used = use_ems;
      ems->free = ems_get_free()*16;
      ems->startseg = FP_SEG(ems_frame[0]);

      r.r_ax = 0x4600;
      intr(EMM_INT,&r);
      ems->ver_lo = r.r_ax & 0x0F;
      ems->ver_hi = (r.r_ax >> 4) & 0x0F;
   };
   return;
};
 
void ems_setmap(unsigned handle, unsigned logical, unsigned char physical)
{
   union REGS r;

#ifdef ERROR_LOG
   error_log("_ems_setmap()","Called");
#endif
   r.h.ah = 0x44;
   r.h.al = physical;
   r.x.bx = logical;
   r.x.dx = handle;
   int86(EMM_INT,&r,&r);

   if(r.h.ah)  
   {
#ifdef ERROR_LOG
      error_log("_ems_setmap()","EMS Error: %X",r.h.ah);
#endif
      ems_inst = 0;
   };

   return;
};

void ems_free(unsigned handle)
{
   union REGS r;

#ifdef ERROR_LOG
   error_log("_ems_free()","Called, handle = %d",handle);
#endif
   r.h.ah = 0x45;
   r.x.dx = handle;
   int86(EMM_INT,&r,&r);

   if(r.h.ah)
   {
#ifdef ERROR_LOG
      error_log("_ems_free()","EMS Error: %X",r.h.ah);
#endif
      ems_inst = 0;
   };
   return;
};


void far *lowalloc(unsigned long bytes)
{
   unsigned seg;

   seg = _xdosalloc((unsigned)(bytes+15)>>4);
   if(seg)
   {
      alloc_counter++;
      return(MK_FP(seg,0));
   }
   else return(NULL);
};


void lowfree(void far *p)
{
   if(p)
   {
      _xdosfree(FP_SEG(p));
      alloc_counter--;
   };
};


