/*
 * Exec
 *
 * Copyright 1994 Michael Beck
 */

#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include "global.h"
#include "dostype.h"
#include "msdos.h"

void dos_set_active_psp(us psp)
{ current_psp = psp; }

us dos_get_active_psp(void)
{ return current_psp; }

void dos_copy_psp(us psp)
{
	/* this is NOT dos-like, but needed because our first exec
	   comes from the scratch */
	if (current_psp)
		memcpy(PSP(psp), PSP(current_psp), sizeof(psp_t));
	else
		memset(PSP(psp), 0, sizeof(psp_t));
	/* now reset some values */
	PSP(psp)->int_20[0] = 0xcd;
	PSP(psp)->int_20[1] = 0x20;
	/* need to reset old22 - old24 */
}

void dos_create_new_psp(us psp, us endadr)
{
	dos_copy_psp(psp);
	PSP(psp)->mem_size = endadr;
	PSP(psp)->parent = current_psp;
	/* now should copy the JFT */
	current_psp = psp;
}

int dos_check_exectype(int fh, exehead_t *exe, int *buflen, int *filelen, us *needmem)
{
	int len;

	if ((len = read(fh, exe, sizeof(exehead_t))) < 0) {
		debmsg("check_exectype: couldn't read; error %d\n", len);
		return -2;
	}
	if (len == sizeof(exehead_t)) 
		if (exe->magic[0] == 'M' && exe->magic[1] == 'Z') {
			debmsg("check_exectype: it'a a .EXE!\n"); 
			*filelen = (exe->pagecnt * 512) - (exe->hdrsize * 16);
			/* 16 paragraphs needed for PSP */
			*needmem = (us)(*filelen >> 4) + exe->minmem + 16;
			lseek(fh, exe->hdrsize * 16, SEEK_SET);
			return TYPE_EXE;
		}
	/* else it must be a COM */
	*filelen = 0x10000;
	*needmem = 0x1000;	/* 64K for COM */
	*buflen = len;
	return TYPE_COM;
}

void do_exe_reloc(int fh, us progstart, exehead_t *exe, ssip_t *ssip)
{
	far_t patch;
	int i;

	debmsg("Relocation-table starts at $%x\n", exe->tabloff);
	debmsg("Relocation-table has $%x entries\n", exe->relocnt);
	lseek(fh, exe->tabloff, SEEK_SET);
	for (i = 0; i < exe->relocnt; ++i) {
		read(fh, &patch, sizeof(far_t));
		patch.segment += progstart;
		*USPTR(patch) += progstart;
	}
	ssip->rSS  = exe->reloSS + progstart;
	ssip->rESP = exe->exeSP;
	ssip->rCS  = exe->reloCS + progstart;
	ssip->rEIP = exe->exeIP;
	debmsg("EXE-start $%x:%x\n", ssip->rCS, ssip->rEIP);
	debmsg("Stack at  $%x:%x\n", ssip->rSS, ssip->rESP);
}

int dos_exec_load(char *path, char *cmdline, ssip_t *ssip)
{
	us tmp_psp, progstart, needed_mem;
	int ret, clen, fh, proglen;
	int type, buflen;
	static char buffer[sizeof(exehead_t)]; 
	exehead_t *exe = (exehead_t *)buffer;

	if ( (fh = open(path, O_RDONLY)) < 0 ) {
		debmsg("Exec: open failed!\n");
		return -2;
	}
	
	type = dos_check_exectype(fh, exe, &buflen, &proglen, &needed_mem);

	if ((ret = dos_alloc_mem(needed_mem, NULL, FALSE)) < 0) {
		debmsg("Exec: failed to allocate $%x paragraphs\n", needed_mem);
		close(fh);
		return ret;
	}
	tmp_psp = (us)ret;

	dos_create_new_psp(tmp_psp, MCB(tmp_psp - 1)->size + tmp_psp);
	PSP(tmp_psp)->environ = 0;	/* no environment yet */

	/* set the owner of the allocated block ! */
	MCB(tmp_psp - 1)->owner = tmp_psp;

	SET_DTA(tmp_psp, 0x80);

	clen = strlen(cmdline);
	PSP(tmp_psp)->cmd_length = clen;
	memcpy(PSP(tmp_psp)->cmd_line, cmdline, 127);
	PSP(tmp_psp)->cmd_line[clen] = '\x0D';	/* CR */
	progstart = tmp_psp + 0x10;	/* sizeof(psp_t) >> 4; */
	debmsg("Exec: Loading at $%x:0 $%x bytes\n", progstart, proglen);

	if (type == TYPE_COM) {

		memcpy(PTR(progstart, 0), buffer, buflen);
		read(fh, PTR(progstart, buflen), proglen - buflen);

		ssip->rSS  = tmp_psp;
		ssip->rESP = 0xFFFE;		/* still not perfect */
		/* push a 0 at stack */
		*MEM((us *), tmp_psp, 0xFFFE) = 0x0000;

		ssip->rCS  = tmp_psp;
		ssip->rEIP = 0x100;		/* COM start */
	}
	else {
		read(fh, PTR(progstart, 0), proglen);
		do_exe_reloc(fh, progstart, exe, ssip);
	}

	close(fh);

	return (int)tmp_psp;
}

int dos_exec(char *path, char *cmdline)
{
	ssip_t ssip;
	int ret;

	if ((ret = dos_exec_load(path, cmdline, &ssip)) < 0)
		return ret;
	_regs.ds = _regs.es = _regs.fs = _regs.gs = (us)ret;
	_regs.esp = ssip.rESP;
	_regs.ss  = ssip.rSS;
	_regs.eip = ssip.rEIP;
	_regs.cs  = ssip.rCS;
	return 0;
}

