        /*  DOS MCB compatible DOS memory manager for DOSRUN           *
         *  (C)1999 LGB, Gabor Lenart, lgb@vlug.vein.hu                *
         *  Can be distributed under the General Public License (GPL)  */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/mman.h>
#include "memory.h"
#include "derrno.h"
#include "config.h"


struct mcb_struct *mcb_first;
extern int pid;  /* Process ID (PSP segment) of current program */


void dosmem_setowner ( int addr , int owner )
{
  struct mcb_struct *p=(struct mcb_struct *)(addr-MCB_SIZE);
  p->owner=owner;
}



struct mcb_struct *dosmem_search ( int size , int mode )
{
  struct mcb_struct *p=mcb_first,*best=NULL;
  int bestsize;
  switch (mode) {
    case SEARCH_BEST  : bestremain=40960;   /* max 640K/16 */
                        break;
    case SEARCH_WORST : bestremain=0;
                        break;
  }
  for (;;) {
    int remain=p->size-size;
    if (!p->owner&&remain>=0)
      switch (mode) {
        case SEARCH_FIRST : return p;
        case SEARCH_LAST  : best=p;
	                    break;
        case SEARCH_BEST  : if (remain<bestremain) {
                              bestremain=remain;
       	                      best=p;
			    }
			    break;
	case SEARCH_WORST : if (remain>bestremain) {
	                      bestremain=remain;
			      best=p;
			    }
			    break;
      }
    if (p->type!='M') break; else p=MCB_NEXT(p);
  }
  if (p->type!='Z') {
    fprintf(stderr,"MCB list corrupted !!\n");
    return NULL;
  }
  return best;
}



void dosmem_init ( int base , int ramsize )
{
  if (mmap(NULL,0x110000,PROT_READ|PROT_WRITE|PROT_EXEC,MAP_FIXED|MAP_PRIVATE|MAP_ANON,-1,0)) {
    perror("anonymous mmap()-ing 1M+64K failed");
    exit(1);
  }
  bzero(NULL,0x110000);
  mcb_first=(struct mcb_struct*)base;
  mcb_first->size=(ramsize-base-MCB_SIZE)>>4;
  mcb_first->owner=0;
  mcb_first->type='Z';
}



static int optimize_blocks ( void )
{
  struct mcb_struct *p=mcb_first,*q;
  for (;;) {
    if (!p->owner) {
      q=p;
      while (p->type=='M'&&!MCB_NEXT(p)->owner) q=MCB_NEXT(q);
      if (p!=q) p->size=((int)q-(int)p+q->size)>>4;
    }
    if (p->type!='M') break; else p=MCB_NEXT(p);
  }
  if (p->type!='Z') {
    fprintf(stderr,"MCB list corrupted !!\n");
#ifdef CONFIG_DEBUG_MCB_PROBLEMS
    dosmem_dump();
#endif
    return 1;
  }
  return 0;
}



int dosmem_alloc ( int size , int user , int mode )
{
  struct mcb_struct *p=search(size,mode);
  if (p==NULL) return 0; /* not enough memory ? */
  if (p->size-size>=...) {   /* split block ! */
    struct mcb_struct *q=p+(size<<4)+MCB_SIZE;
    q->size=p->size-size-MCB_PSIZE;
    q->type=p->type;
    q->owner=0;
    p->type='M';
    p->size=size;
  }
  p->owner=user;
  return (int)p+MCB_SIZE;
}



int dosmem_free ( int addr , int user )
{
  struct mcb_struct *p=(struct mcb_struct*)(addr-MCB_SIZE);
  if (p->owner!=user) return 1;
  if (p->type!='M'&&p->type!='Z') {
    fprintf(stderr,"Trying free non MCB struct !!\n");
    return 1;
  }
  p->owner=0;
  return optimize_blocks();
}



int dosmem_realloc ( int addr , int newsize , int user )
{
  struct mcb_struct *p=(struct mcb_struct*)(addr-MCB_SIZE);
  int remain;
  if (p->type!='M'&&p->type!='Z') {
    fprintf(stderr,"Trying free non MCB struct !!\n");
    return 1;
  }
  if (p->owner!=user) return 1;
  if (newsize==p->size) return 0; /* actual size, no need to work */
  if (newsize>p->size) {  /* GROW */
    if (p->type=='Z'||!MCB_NEXT(p)->owner) return 1; /* no free block after this one */
    remain=MCB_NEXT(p)->size+p->size+MCB_SIZE-newsize;
    if (remain<0) return 0; /* not enough space in the two free blocks */
    if (remain>=MCB_SIZE*4) {
      struct mcb_struct *new=(struct mcb_struct *)((int)p+(newsize<<4)+MCB_SIZE);
      new->type=p->type;
      p->size=newsize;
      new->owner=0;
      new->size=(remain-MCB_SIZE)>>4;
    } else {
      p->size+=p->next->size+MCB_SIZE;
      p->next=p->next->next;
    }
    return 0;
  } else {             /* SHRINK */
  }
}



void dosmem_dump ( void )
{
  struct mcb_struct *p=mcb_first;
  fprintf(stderr,"base     size     next     used     magic\n");
  for (;;) {
    fprintf(stderr,"%08X %08X %08X %08X %08X ",(int)p,p->size,(int)p->next,p->used,p->magic);
    if (p->type!='M') break; else p=MCB_NEXT(p);
  }
  if (p->type!='Z') fprintf(stderr,"dosmem_dump: MCB list corrupted !!\n");
}
