#define __USE_SYSBASE
#include <proto/exec.h>
#include <exec/memory.h>
#include <exec/execbase.h>
#include <string.h>

#include "mem.h"
#include "opts.h"

#define MAX_MEMSEGS  32

static struct memseg memlist[MAX_MEMSEGS];
static ULONG memsegs;

ULONG get_mem(fmem, fmemsz, cmemsz)
	VOID **fmem;
	ULONG *fmemsz, *cmemsz;
{
	struct MemHeader *mh, *nmh;
	ULONG segsz, seg, eseg, nmem;
	BYTE mempri;
	int i;

	nmem = 0;
	mempri = -128;
	*fmemsz = 0;
	*cmemsz = 0;

	/*
	 * walk thru the exec memory list
	 */
	Forbid();
	for (mh  = (void *) SysBase->MemList.lh_Head; 
	    nmh = (void *) mh->mh_Node.ln_Succ; mh = nmh, nmem++) {
		memlist[nmem].ms_attrib = mh->mh_Attributes;
		memlist[nmem].ms_pri = mh->mh_Node.ln_Pri;
		seg = (ULONG)mh->mh_Lower;
		eseg = (ULONG)mh->mh_Upper;
		segsz = eseg - seg;
		memlist[nmem].ms_size = segsz;
		memlist[nmem].ms_start = seg;

		if (mh->mh_Attributes & MEMF_CHIP) {
			/* 
			 * there should hardly be more than one entry for 
			 * chip mem, but handle it the same nevertheless 
			 * cmem always starts at 0, so include vector area
			 */
			if (seg <= 8192) memlist[nmem].ms_start = seg = 0;
			/*
			 * round to multiple of 512K
			 */
			segsz = (segsz + 512 * 1024 - 1) & -(512 * 1024);
			memlist[nmem].ms_size = segsz;
			if (segsz > *cmemsz)
				*cmemsz = segsz;
			continue;
		}
		/* 
		 * some heuristics..
		 */
		seg &= -__LDPGSZ;
		eseg = (eseg + __LDPGSZ - 1) & -__LDPGSZ;

		/*
		 * get the mem back stolen by incore kickstart on 
		 * A3000 with V36 bootrom.
		 */
		if (eseg == 0x07f80000)
			eseg = 0x08000000;

		/*
		 * or by zkick on a A2000.
		 */
		if (seg == 0x280000 &&
		    strcmp(mh->mh_Node.ln_Name, "zkick memory") == 0)
			seg = 0x200000;

		segsz = eseg - seg;
		memlist[nmem].ms_start = seg;
		memlist[nmem].ms_size = segsz;
		/*
		 *  If this segment is smaller than 2M,
		 *  don't use it to load the kernel
		 */
		if (segsz < 2 * 1024 * 1024)
			continue;
		/*
		 * if p_flag is set, select memory by priority 
		 * instead of size
		 */
		if (segsz > *fmemsz &&
		    (!(options & USE_FIRST_SEGMENT) || mempri <= mh->mh_Node.ln_Pri)) {
			*fmemsz = segsz;
			*fmem = (void *)seg;
			mempri = mh->mh_Node.ln_Pri;
		}
	}
	Permit();

	if (options & TEST_ONLY) {
		for (i=0; i<nmem; ++i) {
			print("mem segment ");
			printlong(i+1);
			print(" start=");
			printaddr(memlist[i].ms_start);
			print(" size=");
			printaddr(memlist[i].ms_size);
			print(" attribute=");
			printhex(memlist[i].ms_attrib);
			print(" pri=");
			printlong(memlist[i].ms_pri);
			printchar('\n');
		}
	}

	memsegs      = nmem;

	return sizeof(ULONG) + nmem * sizeof(struct memseg);
}

VOID copy_mem(UBYTE *buf)
{
	*(ULONG *)buf = memsegs;
	buf += sizeof(LONG);
	memcpy(buf, &memlist, memsegs * sizeof(struct memseg));
}
