/************************************************************************\
 * File Version Information
 * $Header: /Altair32v2/emt_traps.c 26    11/08/06 9:21p Racini $
 ************************************************************************/
/************************************************************************\
  MITS Altair Emulator
  Invalid OpCode Trap Host Interface 

  Modifications for the Altair32 Emulator
  Copyright (c) 2001-2006 Richard A. Cini

	****************************
	*****  N   O   T   E   *****
	****************************
	This module is required to enable file import/export support within
	the debugger *and* for the read/write file utilities contained on
	the CP/M disks.


Change Log:
  2002/01/31  RAC -- RELEASE MARKER -- v2.2
  2002/08/23  RAC -- RELEASE MARKER -- v2.30.10
  2002/11/15  RAC -- RELEASE MARKER -- v2.40.2100
  2003/04/26  RAC -- RELEASE MARKER -- v2.50.2045
  2004/07/30  RAC -- RELEASE MARKER -- v3.00.0135
  2006/05/12  RAC -- RELEASE MARKER -- v3.10.0200
  2006/11/15  RAC -- RELEASE MARKER -- v3.20.0400
\*************************************************************************
 * PREVIOUS COPYRIGHT NOTICES
 * Copyright (c) 1996, Timothy Mann
 *
 * This software may be copied, modified, and used for any purpose
 * without fee, provided that (1) the above copyright notice is
 * retained, and (2) modified versions are clearly marked as having
 * been modified, with the modifier's name and the date included.
 *
 * Last modified on Wed May 17 22:12:22 PDT 2000 by mann
 * Features to make transferring files into and out of the emulator
 *  easier.  
 *
 * JTB 9/28/01:
 *	This code has been modified by Jim Battle for use in
 *	Solace with the permission of Timothy Mann.  If you
 *	want to make a derived work from this source code, it
 *	would probably be best if you started with Tim's original
 *	code, and would also be good to contact Tim for the OK.
 *	Having said that, you are free to make use of my
 *	modifications to Tim's code in an unrestricted fashion.
\************************************************************************/

#include <windows.h>
#include <stdio.h>		// C-lib
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>		// flags
#include <io.h>			// w32: get open()
#include <sys/types.h>	// w32: get off_t
#include <sys/stat.h>	// stat codes
#include "altair32.h"
#include "i8080.h"
#include "emt_traps.h"


// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Map xtrs defines to Marat Fayzullin's equivalents

#define REG_BC (R->nBC.W)
#define REG_DE (R->nDE.W)
#define REG_HL (R->nHL.W)
#define REG_A  (R->nAF.B.h)
#define REG_F  (R->nAF.B.l)
#define REG_B  (R->nBC.B.h)
#define REG_C  (R->nBC.B.l)

#define ZERO_MASK (Z_FLAG)

// Map xtrs defines to Solace's equivalents

// return pointer to memory image at specified location
// this doesn't deal with things that may be memory mapped.
static byte*
mem_pointer(int address, int writing)
{
    ASSERT(address >= 0x0000 && address <= 0xFFFF);
    return &Mem[address];
}

// read from address -- may be memory mapped
// RAC -- removed memory mapped handler since we don't have
// dynamic I/O in the Altair32
static byte
mem_read(int address)
{
    ASSERT(address >= 0x0000 && address <= 0xFFFF);
    return GetMem((word)address);
}

// write to address -- may be memory mapped
static void
mem_write(int address, int value)
{
    ASSERT(address >= 0x0000 && address <= 0xFFFF);
    ASSERT(value >= 0x00 && value <= 0xFF);

    SetMem((word)address, (byte)value);
}


// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

/* New emulator traps */

//#define MAX_OPENDIR 32
//DIR *dir[MAX_OPENDIR] = { NULL, };

typedef struct tagOPENDISK{
  int fd;
  int inuse;
} OpenDisk;
#define MAX_OPENDISK 32
OpenDisk od[MAX_OPENDISK];

#if 0
void do_emt_system(I8080 *R)
{
  int res;
  res = system(mem_pointer(REG_HL, 0));
  if (res == -1) {
    REG_A = errno;
    REG_F &= ~ZERO_MASK;
  } else {
    REG_A = 0;
    REG_F |= ZERO_MASK;
  }
  REG_BC = res;
}

void do_emt_mouse(I8080 *R)
{
  int x, y;
  unsigned int buttons, sens;
  switch (REG_B) {
  case 1:
    trs_get_mouse_pos(&x, &y, &buttons);
    REG_HL = x;
    REG_DE = y;
    REG_A = buttons;
    if (REG_A) {
      REG_F &= ~ZERO_MASK;
    } else {
      REG_F |= ZERO_MASK;
    }
    break;
  case 2:
    trs_set_mouse_pos(REG_HL, REG_DE);
    REG_A = 0;
    REG_F |= ZERO_MASK;
    break;
  case 3:
    trs_get_mouse_max(&x, &y, &sens);
    REG_HL = x;
    REG_DE = y;
    REG_A = sens;
    if (REG_A) {
      REG_F &= ~ZERO_MASK;
    } else {
      REG_F |= ZERO_MASK;
    }
    break;
  case 4:
    trs_set_mouse_max(REG_HL, REG_DE, REG_C);
    REG_A = 0;
    REG_F |= ZERO_MASK;
    break;
  case 5:
    REG_A = trs_get_mouse_type();
    if (REG_A) {
      REG_F &= ~ZERO_MASK;
    } else {
      REG_F |= ZERO_MASK;
    }
    break;
  default:
    error("undefined emt_mouse function code");
    break;
  }
}

void do_emt_getddir(I8080 *R)
{
  if (REG_HL + REG_BC > 0x10000 ||
      REG_HL + strlen(trs_disk_dir) + 1 > REG_HL + REG_BC) {
    REG_A = EFAULT;
    REG_F &= ~ZERO_MASK;
    REG_BC = 0xFFFF;
    return;
  }
  strcpy(mem_pointer(REG_HL, 1), trs_disk_dir);
  REG_A = 0;
  REG_F |= ZERO_MASK;
  REG_BC = strlen(trs_disk_dir);
}

void do_emt_setddir(I8080 *R)
{
  trs_disk_dir = strdup(mem_pointer(REG_HL, 0));
  if (trs_disk_dir[0] == '~' &&
      (trs_disk_dir[1] == '/' || trs_disk_dir[1] == '\0')) {
    char* home = getenv("HOME");
    if (home) {
      char *p = (char*)malloc(strlen(home) + strlen(trs_disk_dir) + 2);
      sprintf(p, "%s/%s", home, trs_disk_dir+1);
      free(trs_disk_dir);
      trs_disk_dir = p;
    }
  }
  REG_A = 0;
  REG_F |= ZERO_MASK;
}
#endif

// this is different from Tim Mann's implementation of this function,
// but it does more closely match his do_emt_opendisk().
// if the filename provided isn't an absolute path, then the file opened
// is made relative to the starting directory, rather than just the current
// working directory (which changes as the filebrowser is used to move
// around).
void do_emt_open(I8080 *R)
{
	int fd;
	char *name = (char *)mem_pointer(REG_HL, 0);
	char *qname;

	qname = HOST_MakeAbsolutePath(name);

	fd = open(qname, REG_BC, REG_DE);
	if (fd >= 0) {
		REG_A = 0;
		REG_F |= ZERO_MASK;
	} else {
		REG_A = errno;
		REG_F &= ~ZERO_MASK;
	}
	REG_DE = fd;
	free(qname);
}


void do_emt_close(I8080 *R)
{
	int res;
	res = close(REG_DE);
	if (res >= 0) {
		REG_A = 0;
		REG_F |= ZERO_MASK;
	} else {
		REG_A = errno;
		REG_F &= ~ZERO_MASK;
	}
}


void do_emt_read(I8080 *R)
{
	int size;

	if ((int)REG_HL + (int)REG_BC > 0x10000) {
		REG_A = EFAULT;
		REG_F &= ~ZERO_MASK;
		REG_BC = 0xFFFF;
		return;
	}

	size = read(REG_DE, mem_pointer(REG_HL, 1), REG_BC);
	if (size >= 0) {
		REG_A = 0;
		REG_F |= ZERO_MASK;
	} else {
		REG_A = errno;
		REG_F &= ~ZERO_MASK;
	}
	REG_BC = size;
}


void do_emt_write(I8080 *R)
{
	int size;

	if ((int)REG_HL + (int)REG_BC > 0x10000) {
		REG_A = EFAULT;
		REG_F &= ~ZERO_MASK;
		REG_BC = 0xFFFF;
		return;
	}

	size = write(REG_DE, mem_pointer(REG_HL, 0), REG_BC);
	if (size >= 0) {
		REG_A = 0;
		REG_F |= ZERO_MASK;
	} else {
		REG_A = errno;
		REG_F &= ~ZERO_MASK;
	}
	REG_BC = size;
}


void do_emt_lseek(I8080 *R)
{
	int i;
	off_t offset;

	if ((int)REG_HL + 8 > 0x10000) {
		REG_A = EFAULT;
		REG_F &= ~ZERO_MASK;
		return;
	}

	offset = 0;
	for (i=0; i<8; i++) {
		offset = offset + (mem_read(REG_HL + i) << i*8);
	}
	offset = lseek(REG_DE, offset, REG_BC);
	if (offset != (off_t) -1) {
		REG_A = 0;
		REG_F |= ZERO_MASK;
	} else {
		REG_A = errno;
		REG_F &= ~ZERO_MASK;
	}
	for (i=REG_HL; i<8; i++) {
		mem_write(REG_HL + i, offset & 0xff);
		offset >>= 8;
	}
}


void do_emt_strerror(I8080 *R)
{
	char *msg;
	int size;

	if ((int)REG_HL + (int)REG_BC > 0x10000) {
		REG_A = EFAULT;
		REG_F &= ~ZERO_MASK;
		REG_BC = 0xFFFF;
		return;
	}

	errno = 0;
	msg = strerror(REG_A);
	size = strlen(msg);
	if (errno != 0) {
		REG_A = errno;
		REG_F &= ~ZERO_MASK;
	} else if ((int)REG_BC < size + 2) {
		REG_A = ERANGE;
		REG_F &= ~ZERO_MASK;
		size = REG_BC - 1;
	} else {
		REG_A = 0;
		REG_F |= ZERO_MASK;
	}
	memcpy(mem_pointer(REG_HL, 1), msg, size);
	mem_write(REG_HL + size++, '\r');
	mem_write(REG_HL + size, '\0');
	if (errno == 0) {
		REG_BC = size;
	} else {
		REG_BC = 0xFFFF;
	}
}


#if 0
void do_emt_time(I8080 *R)
{
  time_t now = time(0);
  if (REG_A == 1) {
#if __alpha
    struct tm *loctm = localtime(&now);
    now += loctm->tm_gmtoff;
#else
    struct tm loctm = *(localtime(&now));
    struct tm gmtm = *(gmtime(&now));
    int daydiff = loctm.tm_mday - gmtm.tm_mday;
    now += (loctm.tm_sec - gmtm.tm_sec)
      + (loctm.tm_min - gmtm.tm_min) * 60
      + (loctm.tm_hour - gmtm.tm_hour) * 3600;
    switch (daydiff) {
    case 0:
    case 1:
    case -1:
      now += 24*3600 * daydiff;
      break;
    case 30:
    case 29:
    case 28:
    case 27:
      now -= 24*3600;
      break;
    case -30:
    case -29:
    case -28:
    case -27:
      now += 24*3600;
      break;
    default:
      error("trouble computing local time in emt_time");
    }
#endif
  } else if (REG_A != 0) {
    error("unsupported function code to emt_time");
  }
  REG_BC = (now >> 16) & 0xffff;
  REG_DE = now & 0xffff;
}


void do_emt_opendir(I8080 *R)
{
  int i;
  for (i = 0; i < MAX_OPENDIR; i++) {
    if (dir[i] == NULL) break;
  }
  if (i == MAX_OPENDIR) {
    REG_DE = 0xffff;
    REG_A = EMFILE;
    return;
  }
  dir[i] = opendir(mem_pointer(REG_HL, 0));
  if (dir[i] == NULL) {
    REG_DE = 0xffff;
    REG_A = errno;
    REG_F &= ~ZERO_MASK;
  } else {
    REG_DE = i;
    REG_A = 0;
    REG_F |= ZERO_MASK;
  }
}


void do_emt_closedir(I8080 *R)
{
  int i = REG_DE;
  int ok;
  if (i < 0 || i >= MAX_OPENDIR || dir[i] == NULL) {
    REG_A = EBADF;
    REG_F &= ~ZERO_MASK;
    return;
  }	
  ok = closedir(dir[i]);
  dir[i] = NULL;
  if (ok >= 0) {
    REG_A = 0;
    REG_F |= ZERO_MASK;
  } else {
    REG_A = errno;
    REG_F &= ~ZERO_MASK;
  }
}


void do_emt_readdir(I8080 *R)
{
  int size, i = REG_DE;
  struct dirent *result;

  if (i < 0 || i >= MAX_OPENDIR || dir[i] == NULL) {
    REG_A = EBADF;
    REG_F &= ~ZERO_MASK;
    REG_BC = 0xFFFF;
    return;
  }	
  if (REG_HL + REG_BC > 0x10000) {
    REG_A = EFAULT;
    REG_F &= ~ZERO_MASK;
    REG_BC = 0xFFFF;
    return;
  }
  result = readdir(dir[i]);
  if (result == NULL) {
    REG_A = errno;
    REG_F &= ~ZERO_MASK;
    REG_BC = 0xFFFF;
    return;
  }
  size = strlen(result->d_name);
  if (size + 1 > REG_BC) {
    REG_A = ERANGE;
    REG_F &= ~ZERO_MASK;
    REG_BC = 0xFFFF;
    return;
  }
  strcpy(mem_pointer(REG_HL, 1), result->d_name);
  REG_A = 0;
  REG_F |= ZERO_MASK;
  REG_BC = size;
}


void do_emt_chdir(I8080 *R)
{
  int ok = chdir(mem_pointer(REG_HL, 0));
  if (ok < 0) {
    REG_A = errno;
    REG_F &= ~ZERO_MASK;
  } else {
    REG_A = 0;
    REG_F |= ZERO_MASK;
  }
}


void do_emt_getcwd(I8080 *R)
{
  char *result;
  if (REG_HL + REG_BC > 0x10000) {
    REG_A = EFAULT;
    REG_F &= ~ZERO_MASK;
    REG_BC = 0xFFFF;
    return;
  }
  result = getcwd(mem_pointer(REG_HL, 1), REG_BC);
  if (result == NULL) {
    REG_A = errno;
    REG_F &= ~ZERO_MASK;
    REG_BC = 0xFFFF;
    return;
  }
  REG_A = 0;
  REG_F |= ZERO_MASK;
  REG_BC = strlen(result);
}


void do_emt_misc(I8080 *R)
{
  switch (REG_A) {
  case 0:
    trs_disk_change_all();
    REG_HL = trs_disk_changecount;
    break;
  case 1:
    trs_exit();
    break;
  case 2:
    trs_debug();
    break;
  case 3:
    trs_reset(0);
    break;
  case 4:
    REG_HL = trs_disk_changecount;
    break;
  case 5:
    REG_HL = trs_model;
    break;
  case 6:
    REG_HL = trs_disk_getsize(REG_BC);
    break;
  case 7:
    trs_disk_setsize(REG_BC, REG_HL);
    break;
  case 8:
    REG_HL = trs_disk_getstep(REG_BC);
    break;
  case 9:
    trs_disk_setstep(REG_BC, REG_HL);
    break;
  case 10:
    REG_HL = grafyx_get_microlabs();
    break;
  case 11:
    grafyx_set_microlabs(REG_HL);
    break;
  case 12:
    REG_HL = z80_state.delay;
    REG_BC = trs_autodelay;
    break;
  case 13:
    z80_state.delay = REG_HL;
    trs_autodelay = REG_BC;
    break;
  case 14:
    REG_HL = stretch_amount;
    break;
  case 15:
    stretch_amount = REG_HL;
    break;
  case 16:
    REG_HL = trs_disk_doubler;
    break;
  case 17:
    trs_disk_doubler = REG_HL;
    break;
  case 18:
    REG_HL = sb_get_volume();
    break;
  case 19:
    sb_set_volume(REG_HL);
    break;
  case 20:
    REG_HL = trs_disk_truedam;
    break;
  case 21:
    trs_disk_truedam = REG_HL;
    break;
  default:
    error("unsupported function code to emt_misc");
    break;
  }
}


void do_emt_ftruncate(I8080 *R)
{
  int i, result;
  off_t offset;
  if (REG_HL + 8 > 0x10000) {
    REG_A = EFAULT;
    REG_F &= ~ZERO_MASK;
    return;
  }
  offset = 0;
  for (i=0; i<8; i++) {
    offset = offset + (mem_read(REG_HL + i) << i*8);
  }
  result = ftruncate(REG_DE, offset);
  if (result == 0) {
    REG_A = 0;
    REG_F |= ZERO_MASK;
  } else {
    REG_A = errno;
    REG_F &= ~ZERO_MASK;
  }
}
#endif


void do_emt_opendisk(I8080 *R)
{
	int fd;
	char *name = (char *)mem_pointer(REG_HL, 0);
	char *qname;

	if (HOST_IsAbsolutePath(name)) {
		qname = strdup(name);
	} else {
		int newlen = strlen(winstate.szFilesDir) + strlen(name) + 1;
		if (newlen > _MAX_PATH) {
			REG_A = 1;
			REG_F &= ~ZERO_MASK;
			return;
		}
		qname = (char *)malloc(newlen);
		strcpy(qname, winstate.szFilesDir);
		strcat(qname, name);
	}

	fd = open(qname, REG_BC, REG_DE);
	if (fd >= 0) {
		REG_A = 0;
		REG_F |= ZERO_MASK;
	} else {
		REG_A = errno;
		REG_F &= ~ZERO_MASK;
	}
	REG_DE = fd;
	free(qname);
}


#if 0
void do_emt_closedisk(I8080 *R)
{
  int i;
  int res;
  if (REG_DE == 0xffff) {
    for (i = 0; i < MAX_OPENDISK; i++) {
      if (od[i].inuse) {
	close(od[i].fd);
	od[i].inuse = 0;
      }
    }
    REG_A = 0;
    REG_F |= ZERO_MASK;
    return;
  }

  for (i = 0; i < MAX_OPENDISK; i++) {
    if (od[i].inuse && od[i].fd == REG_DE) break;
  }
  if (i == MAX_OPENDISK) {
    REG_A = EBADF;
    REG_F &= ~ZERO_MASK;
    return;
  }
  od[i].inuse = 0;
  res = close(od[i].fd);
  if (res >= 0) {
    REG_A = 0;
    REG_F |= ZERO_MASK;
  } else {
    REG_A = errno;
    REG_F &= ~ZERO_MASK;
  }
}
#endif
/* end of file: emt_traps.c */
