/*
 * Main MSDOS emulation code - I can see this file getting too big quite
 * fast, and will probably split it up.
 *
 * Copywrite 1993, Hamish Coleman
 */

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <ctype.h>
#include <fcntl.h>
#include <signal.h>
#include <termio.h>
#include <termcap.h>
#include <sys/stat.h>
#include <time.h>
#include <sys/times.h>
#include <sys/time.h>
#include <limits.h>
#include <linux/fd.h>
#include <sys/vfs.h>
#include "global.h"
#include "msdos.h"
#include "vm.h"
#include "termio.h"
#include "linuxfs.h"
 
#define ui unsigned int
#define us unsigned short
#define uc unsigned char

/*
 * HELP! - my C pointer/structure/typedeffing is very bad (and
 * besides it doesnt work)
 *
 */

#ifdef 0
#ifdef 0
#define mem(type,seg,ofs)	type( (int)(*(us *)(seg)<<4)\
				    + (int)(*(us *)(ofs)) )
#endif
				    
#define mem(type,seg,ofs)	type( (int)((seg)<<4) + (ofs) )


#define mcb(seg)	mem((mcb_type *),seg,0)
#define psp(seg)	mem((psp_type *),seg,0)
#endif

typedef struct far_pointer {
	us offset;
	us segment;
	} far_t;
	
typedef struct mcb_type {
	uc magic;
	us owner;
	us size;
	uc unused[3];
	uc name[8];
	} mcb_t;

typedef struct psp_type {
	uc int_20[2];			/*  0 */
	us mem_size;			/*  2 */
	uc reserved;			/*  4 */
	uc dos_call[5];			/*  5 */
	far_t old22;			/*  A */
	far_t old23;			/*  E */
	far_t old24;			/* 12 */
	us parent;			/* 16 */
	uc jft[20];			/* 18 */
	us environ;			/* 2c */
	far_t saved_stack;		/* 2e */
	us jft_size;			/* 32 */
	far_t jft_ptr;			/* 34 */
	far_t prev_psp;			/* 38 */
	uc unused1[4];			/* 3c */
	uc unused2[2];			/* 40 */
	uc unused3[6];			/* 42 */
	uc unused4;			/* 48 */
	uc unused5[7];			/* 49 */
	uc int21_req[3];		/* 50 */
	uc unused6[9];			/* 53 */
	uc fcb1[16];			/* 5c */
	uc fcb2[16];			/* 6c */
	uc unused7[4];			/* 7c */
	uc cmd_length;			/* 80 */
	uc cmd_line[0x79];		/* 81 */
	} psp_t;
	
us mem_head;
us current_psp;
us current_eom;
us dta_seg;
us dta_ofs;
char cwd[160];
	
void boot_dos(int argc, char **argv)
{
int fh;
us tmp_psp;
int i;
char s[127] = " ";
mcb_t * ht;

	ht = (mcb_t *)0x1000;
	mem_head = 0x1000;
	tmp_psp = 0x101;
	dta_seg = 0;
	dta_ofs = 0;
	
	SETIVEC(0x20,0xff01,0x20*4);
	SETIVEC(0x21,0xff01,0x21*4);
	
	ht->magic = 'Z';
	ht->owner = 0x101;
	ht->size = 0x9f00;
/*
	mcb(mem_head)->magic = 'Z';
	mcb(mem_head)->owner = 0x101;
	mcb(mem_head)->size = 0x9f00;
	
	memset( psp(tmp_psp)->int_20 ,0,0x100);
	psp(tmp_psp)->int_20 = 0xcd;
	psp(tmp_psp)->int_20[1] = 0x20;
	psp(tmp_psp)->mem_size = 0x9f00;
	psp(tmp_psp)->environ = 0;
*/
	*(char *)0x1010 = (char)0xcd;
	*(char *)0x1011 = (char)0x20;
	*(short *)0x1100C = (short)0;
	*(short *)0x1100E = (short)0;

	if (argc<2) {
		debmsg("Stupid people:- no command to run\n");
		error=80;
		return;
	}
	
	for(i=2;i<argc;i++) {
		strcat(s,argv[i]);
		strcat(s," ");
	}
	s[strlen(s)-1] = '\x0d';

	*(char *)0x1090 = (char)strlen(s)-1;
	memcpy((char *)0x1091,s,127);
/*	
	psp(tmp_psp)->cmd_length = strlen(s);
	memcpy(psp(tmp_psp)->cmd_line,s,127);
*/	
	if ( (fh=open(argv[1],O_RDONLY)) <0 ) {
		debmsg("open failed!\n");
		printf("Bad command or filename\n");
		error=80;
		return;
	}
	read(fh,(char *)0x1110,65535);
	close(fh);
	
	_regs.eip = 0x100;
	_regs.cs = _regs.ds = _regs.es = _regs.fs = _regs.gs = _regs.ss = 0x101;
	_regs.esp = 0xfffc;
	current_psp = tmp_psp;
	current_eom = 640*(1024/16);
	getcwd(cwd,160);
	debmsg("DOS booted.\n\n");
}

#ifdef 0
/*
 * All these memory allocation functions _should_ work, given a method
 * of accessing the memory areas with typedeffing and structures etc..
 * (ie: they dont work :)
 *
 */
 
us dos_split_block(seg,size)
us seg;
us size;
{
	us head = seg - 1;
	us next;
	
	if (size > mcb(head)->size) {
		debmsg("\tBad split_block : size > mcb->size\n");
		show_regs();
		error=20;
	}
	
	next = seg + size;
	mcb(next)->magic = mcb(head)->magic;
	mcb(next)->owner = 0;
	mcb(next)->size = mcb(head)->size - size - 1;
	
	mcb(head)->magic = "M";
	mcb(head)->size = size;
	
	return next +1;
}

us dos_alloc_mem(size)
us size;
{
us cp;
us found;
us fin;

	fin = 0;
	cp = mem_head;
	found = 0;
	while ( (fin==0) && (found==0) ) {
		if ( (mcb(cp)->owner==0) && (mcb(cp)->size>=size) ) {
			found = cp;
			fin = 1;
		}
		if ( mcb(cp)->magic == "Z" ) {
			fin = 1;
		}
		cp += mcb(cp)->size +1;
	}
	if (!found) {
		return 0;
	}
	if (mcb(found)->size!=size) {
		dos_split_block(found+1,size);
	}
	mcb(found)->owner = current_psp;
	return found +1;
}

void dos_join_seg(seg)
us seg;
{
us next;
	next = mcb(seg-1)->size+1;
	mcb(seg-1)->magic = mcb(next)->magic;
	mcb(seg-1)->size += mcb(next)->size +1;
}

void dos_dealloc_mem(seg)
us seg;
{
us tmp;
us cp;

	cp = seg -1;
	mcb(cp)->owner = 0;
	tmp = cp + mcb(cp)->size+1;
	
	if(mcb(tmp)->owner==0) {
		dos_join_seg(seg);
	}
	
	tmp = cp = mem_head;
	while (cp != (seg-1)) {
		tmp = cp;
		cp += mcb(tmp)->size +1;
	}
	
	if ( (tmp != cp) && (mcb(tmp)->owner==0) ) {
		dos_join_seg(tmp-1);
	}
}

us dos_max_mem_size(void)
{
us cp;
us maxm;

	maxm = 0;
	cp = mem_head;
	while (mcb(cp)->magic!='Z') {
		maxm = maxm<mcb(cp)->size? mcb(cp)->size:maxm;
		cp = mcb(cp)->size+1;
	}
	maxm = maxm<mcb(cp)->size? mcb(cp)->size:maxm;
	return maxm;
}
#endif 

int dos2unix(char *uni, char *dos)
{
	char drive;

	if (dos[1] == ':') {
		drive = *dos++;
		if (drive != 'C') 
			return -1;
		*dos++;
	}

	for (;;) {
		switch (*dos) {
			case '\\':
				*uni++ = '/';
				break;
			case '\0':
				*uni = '\0';
				if (*--uni == '.') *uni = '\0';
				return 0;
			default :
				*uni++ = tolower(*dos);
		}
		dos++;
	}
}

char *wildcard(char *exp, char *dos)
{
	char name[20];
	char ext[20];
	char * cp;
	
	name[0]='\0';
	ext[0]='\0';
	cp = name;
	while (*dos) {
		switch (*dos) {
			case '*':
				strcat(cp,"????????");
				cp += 8;
				break;
			case '.':
				*cp = '\0';
				cp = ext;
				break;
			default:
				*cp++ = tolower(*dos);
		}
		dos++;
	}
	*cp = '\0';
	if (! ext[0])
		strcpy(ext,"???");
	strncpy(exp,name,8);
	strcat(exp,".");
	strncat(exp,ext,3);
	return exp;
}

int unix2dos(char *dos, char *uni)
{
	for (;;) {
		switch (*uni) {
			case '/':
				*dos++ = '\\';
				break;
			case '\0':
				*dos = '\0';
				return 0;
			default :
				*dos++ = toupper(*uni);
		}
		uni++;
	}
}

void msdos(void)
{
	char *csp, *p; 
	int c;
	int nr = HI(ax);

	emulate_iret();
Restart:
	switch(nr) {
		case 0: /* terminate */
			debmsg("Terminated.\n");
			show_regs();
			error = 10;
			break;
		case 2: /* char out */
			char_out(LO(dx),7, screen);
			break;
		case 1: /* read and echo char */
		case 8: /* read char */
		case 7: /* read char, do not check <ctrl> C */
			while (ReadKeyboard(&c, WAIT) != 1);
			*(char *)&_regs.eax = c;
			if (nr == 1) char_out(c,7, screen);
			break;
		case 6: /* Direct console in/out */ /**** Needs ^C checking */
			if (LO(dx) != 0xff) {
				write(1,&_regs.edx,1);
				LO(ax) = LO(dx);
				_regs.eflags |= ZF;
				break;
			}
			if (ReadKeyboard(&c,NOWAIT) != 1) {
				_regs.eflags |= ZF;
				break;
			}
			*(char *)&_regs.eax = c;
			_regs.eflags &= ~ZF;
			break;
		case 9: /* str out */
			csp = SEG_ADR((char *), ds, dx);
			for (p = csp; *p != '$';) char_out(*p++,7, screen);
			break;
		case 0x0a: /* read string */
			csp = SEG_ADR((char *), ds, dx);
			ReadString(((unsigned char *)csp)[0], csp +1);
			break;
		case 0x0b: /* Get stdin Status */ /**** Needs ^C checking */
			if (ReadKeyboard(&c,TEST) != 1) {
				LO(ax) = 0;
			} else {
				LO(ax) = 0xff;
			}
			break;
		case 0x0c: /* clear key buffer, do int AL */
			while (ReadKeyboard(&c, NOWAIT) == 1);
			nr = _regs.eax & 0xff;
			_regs.eax = (nr << 8) | nr;
			goto Restart;
		case 0x0d: /* Disk Reset */
			break;
		case 0x0e: /* Change Drive */
			if (LO(dx) != 2) {
				debmsg("\tDrive Changed to drive %i!!\n",LO(dx));
			}
			LO(ax) = 3;
			break;
		case 0x0f: /* FCB open */
		case 0x10: /* FCB close */
		case 0x11: /* FCB find-first */
		case 0x12: /* FCB find-next */
		case 0x13: /* FCB delete */
		case 0x14: /* FCB read */
		case 0x15: /* FCB write */
		case 0x16: /* FCB creat */
		case 0x17: /* FCB rename */
		case 0x21: /* FCB read random */
		case 0x22: /* FCB write random */
		case 0x23: /* FCB get-size */
		case 0x24: /* FCB set random number */
		case 0x27: /* FCB random block read */
		case 0x28: /* FCB random block write */
		case 0x29: /* FCB parse filename */
			debmsg("\tFCB functions not currently implemented\n");
			show_regs();
			error=1;
			break;
		case 0x18: /* "Null function for CPM compatibility" !! */
		case 0x1d: /* "Null function for CPM compatibility" !! */
		case 0x1e: /* "Null function for CPM compatibility" !! */
		case 0x20: /* "Null function for CPM compatibility" !! */
			debmsg("CPM Null function called!!!\n");
			show_regs();
			break;
		case 0x19: /* get current drive */
			/* only ONE drive - the Unix file system, so - pretend to have a HDD */
			LO(ax) = 0x2;
			break;
		case 0x1a: /* set DTA address */
			dta_seg = _regs.ds &0xffff;
			dta_ofs = _regs.edx & 0xffff;
			debmsg("\tDta = %04x:%04x\n",_regs.ds,_regs.edx);
			break;
		case 0x1b: /* statfs for current drive */
		case 0x1c: /* statfs for specific drive */
			{
			struct statfs buf;
			statfs(cwd,&buf);
			_regs.ecx = 512;
			LO(ax) = buf.f_bsize % 512;
			_regs.edx = buf.f_bavail;
			_regs.ds = _regs.ebx = 0;
			break;
			}
		case 0x1f: /* get DPB for default drive */
			LO(dx) = 2;
		case 0x32: /* get DPB for specific drive */
			debmsg("\tNot implemented\n");
			_regs.eax = 0xff;  /* invalid drive error */
			break;
		case 0x25: /* set int Vector */
			debmsg("\tWarning: int %02x, set - most int's wont work\n",LO(ax));
			SETIVEC(LO(ax),_regs.ds,_regs.edx & 0xffff);
			break;
		case 0x2a: /* get DOS date */
			debmsg("\tUnimplemented\n");
			break;
		case 0x2b: /* set DOS date */
			/* ignore  - WE know that the time IS right ;-) */
			if ( (_regs.ecx & 0xffff)==0x4445 ) {
				debmsg("\tDetect Desqview ..\n");
				_regs.eax = 0xff;   /* trying to detect desqview - return not present */
			} else
				_regs.eax = 0x00;
			break;
		case 0x2c: /* get DOS time */
			debmsg("\tUnimplemented\n");
			show_regs();
			break;
		case 0x2d: /* set DOS time */
			/* ignore - WE know that the time IS right */
			_regs.eax = 0x00;
			break;
		case 0x2e: /* set verify flag */
			/* do nothing */
			break;
		case 0x2f: /* get DTA address */
			_regs.ebx = dta_ofs;
			_regs.es = dta_seg;
			break;
		case 0x30: /* get DOS ver */
#if 0
			_regs.eax = 0x010b; /* version 0b.01 */
			_regs.ebx = 0xf011; /* oem = f0 , .. user = */
			_regs.ecx = 0x1111; /* 11:1111 */
#else
			_regs.eax = 0x0005;
			_regs.ebx = 0x0000;
			_regs.ecx = 0x0000;
			show_regs();
#endif
			break;
		case 0x33: /* get/set Break Flag */
			debmsg("\t\tnot implemented\n");
			break;
		case 0x35: /* get int vector */
			debmsg("\tWarning: int %02x, get - most int's wont work\n",LO(ax));
			GETIVEC(LO(ax),_regs.es,_regs.ebx);
			break;
		case 0x36: /* Get free Disk Space */
			{
			/* this will give wierd info and doesnt check for overflows */
			struct statfs buf;
			statfs(cwd,&buf);
			_regs.ecx = 512;
			_regs.eax = buf.f_bsize % 512;
			_regs.ebx = buf.f_bavail;
			_regs.edx = buf.f_blocks;
			break;
			}
		case 0x37: /* "availdev" /dev/ prefix */
			LO(ax) = 0xff;
			LO(dx) = 0;
			break;
		case 0x39: /* MKDIR */
			{
			char unixname[100];
			
			_regs.eflags &= ~CF;
			if ( dos2unix(unixname,SEG_ADR((char *),ds,dx))) {
				_regs.eflags |= CF;
				_regs.eax = 2;
				debmsg("\tInvalid d-name\n");
				break;
			}
			debmsg("\tMKDIR on %s\n",unixname);
			_regs.eax = mkdir(unixname,077);
			if (_regs.eax < 0) {
				_regs.eflags |= CF;
				debmsg("\tError %i\n",_regs.eax);
			}
			break;
			}
		case 0x3a: /* RMDIR */
			{
			char unixname[100];
			
			_regs.eflags &= ~CF;
			if ( dos2unix(unixname,SEG_ADR((char *),ds,dx))) {
				_regs.eflags |= CF;
				_regs.eax = 2;
				debmsg("\tInvalid d-name\n");
				break;
			}
			debmsg("\tRMDIR on %s\n",unixname);
			_regs.eax = rmdir(unixname);
			if (_regs.eax < 0) {
				_regs.eflags |= CF;
				debmsg("\tError %i\n",_regs.eax);
			}
			break;
			}
		case 0x3b: /* CHDIR */
			{
			char unixname[160];
			_regs.eflags &= ~CF;
			if ( dos2unix(unixname,SEG_ADR((char *),ds,dx))) {
				_regs.eflags |= CF;
				_regs.eax = 2;
				debmsg("\tInvalid d-name\n");
				break;
			}
			debmsg("\tchdir on %04x:%04x\n",_regs.ds,_regs.edx);
			debmsg("\tCHDIR to %s\n",unixname);
			_regs.eax = chdir(unixname);
			if (_regs.eax < 0) {
				_regs.eflags |= CF;
				debmsg("\tError %i\n",_regs.eax);
				_regs.eax = 3;
			} else 
				strcpy(cwd,unixname);
			break;
			}
			
			/* Note: many of the file handling semantics for the
			 * following file functions are plain kludges that are
			 * more convinient for checking if things work, rather
			 * than emulating DOS
			 */
		case 0x3c: /* create file H */
			{
			char unixname[100];
			
			_regs.eflags &= ~CF;
			if (dos2unix(unixname,SEG_ADR((char *),ds,dx))) {
				_regs.eflags |= CF;
				_regs.eax = 2;
				debmsg("\tInvalid f-name!\n");
				break;
			}
			debmsg("\tCreat on %s\n",unixname);
			_regs.eax = creat(unixname,S_IREAD | S_IWRITE);
			if (_regs.eax > 0xfff0) {
				_regs.eflags |= CF;
				_regs.eax = 2;
				debmsg("\tFile table overflow!\n");
			}
			if (_regs.eax == -1) {
				_regs.eflags |= CF;
				_regs.eax = 2;
				debmsg("\tHmm: error?\n");
				show_regs();
			}
			}
			break;
		case 0x3d: /* open file H */ /**** Needs file-locking ?? */
			{
			char unixname[100];
			int flags;
			
			_regs.eflags &= ~CF;
			if (dos2unix(unixname,SEG_ADR((char *),ds,dx))) {
				_regs.eflags |= CF;
				_regs.eax = 2;
				debmsg("\tInvalid f-name!\n");
				break;
			}
			debmsg("\tOpen on %s\n",unixname);
			
			switch (LO(ax)&0x07) {
				case 0:
					flags = O_RDONLY;
					break;
				case 1:
					flags = O_WRONLY;
					break;
				case 2:
					flags = O_RDWR;
					break;
				default:
					debmsg("\tUnknown open mode 0x%02x\n",LO(ax)&0x07);
					flags = O_RDWR;
			}
			_regs.eax = open(unixname,flags);
			if (_regs.eax > 0xfff0) {
				_regs.eflags |= CF;
				_regs.eax = 2;
				debmsg("\tFile table overflow!\n");
			}
			if (_regs.eax == -1) {
				_regs.eflags |= CF;
				switch (errno) {
					case EISDIR:
					case ETXTBSY:
					case EACCES:
					case EROFS:
						_regs.eax = 5; /* Access denied */
						break;
					case ENOENT:
					case ENOTDIR:
						_regs.eax = 2; /* File not found */
						break;
					case EMFILE:
					case ENFILE:
						_regs.eax = 4; /* no handles avail */
						break;
					default:
						_regs.eax = 5;
				}
				debmsg("\tHmm: error?\n");
			}
			break;
			}
		case 0x3e: /* close file H */
			_regs.eflags &= ~CF;
			_regs.eax = close(_regs.ebx);
			if (_regs.eax) {
				debmsg("\tError closing file!\n");
				_regs.eflags |= CF;
			}
			break;
		case 0x3f: /* read from handle */
			/*debmsg("\tWarning: this may fail\n");*/
			_regs.eflags &= ~CF;
			_regs.eax = read(_regs.ebx,SEG_ADR((char *),ds,dx),_regs.ecx & 0xffff);
			if (_regs.eax == -1) {
				_regs.eflags |= CF;
				_regs.eax = 5; /* access denied */
			}
			break;
		case 0x40: /* write to handle */
			/*debmsg("\tWarning: this may fail\n");*/
			_regs.eax = write(_regs.ebx,SEG_ADR((char *),ds,dx),_regs.ecx & 0xffff);
			if (_regs.eax == -1) {
				_regs.eflags |= CF;
				_regs.eax = 5; /* access denied */
			}
			break;
		case 0x41: /* Unlink */
			{
			char unixname[100];
			
			_regs.eflags &= ~CF;
			if ( dos2unix(unixname,SEG_ADR((char *),ds,dx))) {
				_regs.eflags |= CF;
				_regs.eax = 2;
				debmsg("\tInvalid f-name\n");
				break;
			}
			debmsg("\tUnlink on %s\n",unixname);
			/*
			_regs.eax = unlink(unixname);
			*/
			if (_regs.eax < 0) {
				_regs.eflags |= CF;
				debmsg("\tError %i\n",_regs.eax);
			}
			}
			break;
		case 0x42: /* lseek */
			_regs.eax = lseek(_regs.ebx,(_regs.ecx << 16)+(_regs.edx & 0xffff),LO(ax));
			if (_regs.eax == -1) {
				_regs.eflags |= CF;
				_regs.eax = 1;
				break;
			}
			_regs.edx = _regs.eax >> 16;
			_regs.eax &= 0xffff;
			break;
		case 0x43: /* get/set attribs */ /**** FIXME! */
			if (LO(ax) == 0) {
				debmsg("\tNot implemented\n");
				_regs.eflags &= ~CF;
				_regs.ecx = 0;
				break;
			} else {
				debmsg("\tNot implemented\n");
				_regs.eflags &= ~CF;
				_regs.eax = 2;
				break;
			}
		case 0x45: /* dup */
			_regs.eflags &= ~CF;
			_regs.eax = dup(_regs.ebx & 0xffff);
			if (_regs.eax <0) {
				_regs.eflags |= CF;
				_regs.eax = 2;
			}
			break;
		case 0x46: /* dup2 */
			_regs.eflags &= ~CF;
			_regs.eax = dup2(_regs.ebx & 0xffff,_regs.ecx & 0xffff);
			if (_regs.eax <0) {
				_regs.eflags |= CF;
				_regs.eax = 2;
			}
			break;
		case 0x47: /* CWD */
			getcwd(cwd,160);
			unix2dos(SEG_ADR((char *),ds,si),cwd);
			strcpy(SEG_ADR((char *),ds,si),SEG_ADR((char*),ds,si+1));
			debmsg("\tcwd = %s\n",SEG_ADR((char *),ds,si));
			break;
#ifdef 0
		case 0x48: /* alloc mem */
			_regs.eax = dos_alloc_mem(_regs.ebx & 0xffff);
			if (_regs.eax==0) {
				_regs.ebx = dos_max_mem_size();
				_regs.eax = 8;
				_regs.eflags |= CF;
			} else {
				_regs.eflags &= ~CF;
			}
			break;
		case 0x49: /* dealloc mem */
			dos_dealloc_mem(_regs.es);
			_regs.eflags &= ~CF;
			break;
		case 0x4a: /* SETBLOCK */
			dos_split_block(_regs.es,_regs.ebx & 0xffff);
			_regs.eflags &= ~CF;
			break;
#endif
		case 0x48: /* alloc */ /**** I am an ERROR! */
			if ((640*(1024/16) - current_eom) >= _regs.ebx) {
				_regs.eflags &= ~CF;
				_regs.eax = current_eom;
				current_eom += _regs.ebx;
				debmsg("\talloc(0x%04x) = 0x%04x\n",_regs.ebx,_regs.eax);
				break;
			}
			_regs.eflags |= CF;
			_regs.eax = 07;
			_regs.ebx = (640*(1024/16)) - current_eom; 
			break;			
		case 0x49: /* dealloc */ /**** I am an ERROR! */
			debmsg("\tdealloc(0x%04x)\n",(_regs.es &0xffff));
			if ((_regs.es &0xffff) <current_eom)
				_regs.eflags &= ~CF;
			else {
				_regs.eflags |= CF;
				_regs.eax = 9;
			}
			break;
		case 0x4a: /* SetBlock */ /**** I am an ERROR! */
			_regs.eflags &= ~CF;
			debmsg("\tcpsp=0x%04x,es=0x%04x,size=0x%04x\n",current_psp,_regs.es,_regs.ebx);
			if ((_regs.es &0xffff) == (current_psp &0xffff)) {
				current_eom = (_regs.es & 0xffff) + (_regs.ebx & 0xffff);
				debmsg("\tWARNING: this doesnt work fully!!\n");
				break;
			}
			goto default_msdos;
		case 0x4c: /* terminate with error code */
			debmsg("Terminated.\n");
			show_regs();
			printf("\nExit code = %i\n",LO(ax));
			error=10;
			break;
		case 0x4e: /* find first */  /**** this needs fixing */
			{
			char *cp;
			char buf[80]= "./";

			_regs.eflags &= ~CF;
			if (dos2unix(findpath,SEG_ADR((char *),ds,dx))) {
				_regs.eax = 0x0f;
				_regs.eflags |= CF;
				debmsg("not a path?\n");
				break;
			}
			if (strrchr(findpath,'/')==NULL) {
				strcat(buf,findpath);
				strcpy(findpath,buf);
			}
			cp = findpath + strlen(findpath);
			while (*--cp != '/');
			*cp++ = '\0';
			wildcard(findname,cp);                
			strncpy((char *)((dta_seg <<4)+dta_ofs),"This is just some junk to fill things!",21);
			debmsg("\tFind_first %s  %x --- %s, %s\n", SEG_ADR((char *),ds,dx), _regs.ecx, findpath, findname);
			_regs.eax = dir_entry(1, _regs.ecx, (struct fentry *)((dta_seg <<4) + dta_ofs));
			search_mode = _regs.ecx;
			if (_regs.eax) _regs.eflags |= CF;
			}
			break;
		case 0x4f: /* find next */
			_regs.eflags &= ~CF;
			_regs.eax = dir_entry(0, search_mode, (struct fentry *)((dta_seg <<4) + dta_ofs));
			if (_regs.eax) _regs.eflags |= CF;
			break;
		case 0x50: /* set PSP address */
			current_psp = _regs.ebx & 0xffff;
			break;
		case 0x51: /* get PSP address */
		case 0x62:
			_regs.ebx = current_psp;
			break;
		case 0x54: /* get verify flag */
			/* assume that Unix has better file-writes than DOS ;-) */
			_regs.eax = 0x01;
			break;
		case 0x56: /* Rename */
			{
			char unixname1[100];
			char unixname2[100];
			
			_regs.eflags &= ~CF;
			if ( dos2unix(unixname1,SEG_ADR((char *),ds,dx))) {
				_regs.eflags |= CF;
				_regs.eax = 2;
				debmsg("\tInvalid f-name\n");
				break;
			}
			if ( dos2unix(unixname2,SEG_ADR((char *),es,di))) {
				_regs.eflags |= CF;
				_regs.eax = 2;
				debmsg("\tInvalid f-name\n");
				break;
			}
			debmsg("\trename on %s to %s\n",unixname1,unixname2);
			_regs.eax = rename(unixname1,unixname2);
			if (_regs.eax < 0) {
				_regs.eflags |= CF;
				debmsg("\tError %i\n",_regs.eax);
			}
			}
			break;
		case 0x57: /* get/set file date and time */
			debmsg("\tNot implemented\n");
			_regs.eflags &= ~CF;
			_regs.ecx = _regs.ebx = 0;
			break;

		default:
default_msdos:
			show_regs();
			error = 1;
			break;
	}
}
