#include "bhole.h"
#include "mm.h"

void make_mcb(unsigned int pos,unsigned short owner,unsigned short size)
	/* routine: make_mcb
	 * purpose: creates an MCB at 'pos', the MCB is owned by 'owner'
	 *          and has size of 'size' paragraphs
	 */
{
	SET_MEM_BYTE2(pos  ,'M');
	SET_MEM_WORD2(pos+1,owner);
	SET_MEM_WORD2(pos+3,size);
} 

void init_mcbs()
{
	/* TODO: my MCBs are in fact not containing the program's name
	 *       part, i only use the process ID thing
	 */
	make_mcb(FIRSTMCB_POS,PSPSEG,FIRSTMEMB_SIZE);
	make_mcb(FIRSTMCB_POS + ((FIRSTMEMB_SIZE+1) << 4),0,0xA000 - (PSPSEG + 0x10) - 1);
}

/* TODO: FUCKME: is this shit interrupting the findfile("magic.exe") thing */
/* TODO: * all memory routines should do signature checkups.
 *       * should we improve signature checkups to be done before ANY
 *         processing of data that's assumed to be an MCB?
 */



__inline unsigned int skip_used_mcbs(unsigned int ofs)
	/* routine: skip_used_mcbs
	 * purpose: skips used memory blocks and returns the address
	 *          of the nearest free memory block
	 * params : int ofs - offset to an MCB
	 * returns: * success - address of the nearest free memory block after
	 *                      the offset (including the offset itself)
	 *          * failure to find block - 0xFFFFFFFF
	 *            (this failure means we have passed all the 640k limit
	 *             of conventional memory and found no sign of a free
	 *             memory block)
	 * NOTICE: the reason this function is __inline'd
	 *         is because it's called only in one place (int21_memalloc)
	 *         if it's called from more places maybe the __inline
	 *         should be removed
	 */
{
	if (ofs >= 0xA0000) 
		return 0xFFFFFFFF;
	while(GET_MEM_WORD2(ofs+1) != 0)
		/* while this MCB is not marked as "free memory" */
	{
		/* signature check */
		if (GET_MEM_WORD2(ofs) != 'M')
			fprintf(report_file,
			"BHole: ALERT!!! bad MCB signature at %x\n",
			ofs);
		/* go to the next one */
		ofs+= (GET_MEM_WORD2(ofs+3)<<4) + 16;
		/* if we have checked the entire memory
		 * report it
		 */
		if (ofs >= 0xA0000) 
			return 0xFFFFFFFF;
	}
	return ofs;
}

__inline unsigned int count_free_paras(unsigned int ofs,const unsigned int limit)
	/* routine: count_free_paras
	 * purpose: counts the number of free paragraphs
	 *          in this memory block and in the blocks straight after it
	 *          in case they are free as well
	 * params : int ofs   - offset to MCB of a free memory block
	 *          int limit - after the number of free paragraphs
	 *                      is above this value,
	 *                      we will not go forward and seek
	 *                      for a next free block if it exists
	 *                      we will return the number we have found 
	 * NOTICE: the reason this function is __inline'd
	 *         is because it's called only in one place (int21_memalloc)
	 *         if it's called from more places maybe the __inline
	 *         should be removed
	 * returns: the number of free paragraphs
	 */
{
	unsigned int cnt = 0;
	unsigned int t2;
	/* since this MCB is marked as "free memory" calculate it's size field 
	 * as starting size (the MCB itself cannot be "sacrificed"
	 */
	cnt+=t2=GET_MEM_WORD2(ofs+3); 
	/* and move on */
	ofs+=(t2<<4)+16;
		
	while(GET_MEM_WORD2(ofs+1) == 0 && cnt < limit && ofs < 0xA0000)
		/* while this MCB is marked as "free memory" */
	{
		/* signature check */
		if (GET_MEM_WORD2(ofs) != 'M')
			fprintf(report_file,
			"BHole: ALERT!!! bad MCB signature at %x\n",
			ofs);
		
		/* count the size of this free block and
		 * also count it's MCB's size
		 * GET_MEM_WORD2(ofs+3) == size of block itself
		 * 1                    == size of MCB
		 * and move on to the next one
		 */
		cnt+=t2=(GET_MEM_WORD2(ofs+3) + 1);
		ofs+=(t2<<4);
	}
	return cnt;
}
	
	
	
	
	
	
	


int resize_first_mblk(int newsize)
	/* routine: resize_first_mblk
	 * purpose: changes the size field of the first MCB, also
	 *          marks the data afterwards with another MCB
	 *          which is marked as "unallocated"
	 * params : newsize - size measured in paragraphs
	 * notice : current version can only increase sizes
	 * returns: 1 in success,
	 *          -(maximum_size) in failure
	 *          notice: maximum_size means the best size
	 *                  we can give the block in a resize
	 */
{
	unsigned int ofs;
	signed int tmpsize = newsize - GET_MEM_WORD2(FIRSTMCB_POS+3);
	unsigned int tmp;
	
	if (newsize < GET_MEM_WORD2(FIRSTMCB_POS+3)) 
	{
		make_mcb(FIRSTMCB_POS + ((newsize+1) << 4),0,-tmpsize - 1);
		SET_MEM_WORD2(FIRSTMCB_POS+3,newsize);		
		return 1;
	}
	
	if (newsize == GET_MEM_WORD2(FIRSTMCB_POS+3))
	{
		fputs("BHole: got \'fake\' realloc\n",report_file);
		return 1;
	}
	
	/* we now get ofs to the next memory block */
	ofs = FIRSTMCB_POS + (GET_MEM_WORD2(FIRSTMCB_POS+3) << 4) + 16;

	/* this loop handles the case in which we have 
	 * more than 1 free blocks right after our MCB,
	 * we check if we can kill these blocks together to get enough
	 * (i.e. we need 20 paragraphs, block 1 is free and has 5 paras
	 *       block 2 is free and has 10 paras, block 3 is free and has
	 *       8 paras)
	 * also, in the calculation, we take the size of the block
	 * and add one to it (cause the reallocation will cause
	 * the MCB header itself to "sacrifice" itself
	 */
	fprintf(report_file,"BHole: tmpdebug: doing realloc, ofs %x, owner %x\n",ofs,GET_MEM_WORD2(ofs+1));
	while(tmpsize > 0 && GET_MEM_WORD2(ofs+1) == 0 && ofs < 0xA0000)
	/* as long as we still need to find room
	 * and we only encounter free memory blocks
	 * continue adding them to the calculation
	 */
	{
		if (GET_MEM_WORD2(ofs) != 'M')
		{
			fprintf(report_file,"BHole: ALERT!!! bad MCB signature at %x\n",ofs);
		}
		fprintf(report_file,"BHole: tmpdebug: tmpsize == %d, doing tmpsize-=%d\n",tmpsize,GET_MEM_WORD2(ofs+3)+1);
		tmpsize-= (tmp=GET_MEM_WORD2(ofs+3)+1);
		ofs+=(tmp << 4);
	}
	/* TODO: this could be done more efficient with "break" statements
	 *       placed inside the loop for the ofs >= 0xA0000
	 *       and the GET_MEM_WORD(ofs+1) != 0
	 */
	if (tmpsize > 0)
	{
		fputs("BHole: failed to do full resize of first MCB\n",report_file);
		/* TODO: should partial resize be done or not be done? */
		SET_MEM_WORD2(FIRSTMCB_POS+3,newsize - tmpsize);
		return tmpsize - newsize;
	}
	/* (-tmpsize) now contains the number
	 * of memory after the resized memory block
	 * that should still be marked as free
	 */
	
	if (tmpsize > -1)
	{
		SET_MEM_WORD2(FIRSTMCB_POS+3,newsize);	
	}
	else
	{
		/* in this case we have to resize this MCB
		 * and create a second MCB for the empty memory
		 * after this MCB
		 */
		/* FUCKME: should this be minus 1 or plus 1 in the end */
		make_mcb(FIRSTMCB_POS + ((newsize+1) << 4),0,-tmpsize - 1);
		SET_MEM_WORD2(FIRSTMCB_POS+3,newsize);
		fprintf(report_file,"BHole: realloc: resized to %x\n",newsize);
	}
	return 1;
}


