File:  [Tom Morton FrontierVM] / frontvm / hardware / hostcall.c
Revision 1.1.1.2 (vendor branch): download - view: text, annotated - select for diffs
Tue Apr 24 17:57:39 2018 UTC (8 years, 3 months ago) by root
Branches: frontvm, MAIN
CVS tags: frontvm2-20061120, HEAD
Tom Morton

/*
  Hatari - gemdos.c

  This file is distributed under the GNU Public License, version 2 or at
  your option any later version. Read the file gpl.txt for details.

  GEMDOS intercept routines.
  These are used mainly for hard drive redirection of high level file routines.

  Now case is handled by using glob. See the function
  GemDOS_CreateHardDriveFileName for that. It also knows about symlinks.
  A filename is recognized on its eight first characters, do don't try to
  push this too far, or you'll get weirdness ! (But I can even run programs
  directly from a mounted cd in lower cases, so I guess it's working well !).
*/

#include <sys/stat.h>
#include <time.h>
#include <dirent.h>
#include <ctype.h>
#include <unistd.h>
#include <glob.h>
#include <SDL/SDL.h>
#include <SDL/SDL_endian.h>

#include "main.h"
#include "configuration.h"
#include "file.h"
#include "../m68000.h"
#include "hostcall.h"
#include "memAlloc.h"
#include "misc.h"
#include "screen.h"
#include "audio.h"
#include "input.h"
#include "keymap.h"
#include "shortcut.h"
#include "fe2.h"

//#define GEMDOS_VERBOSE
// uncomment the following line to debug filename lookups on hd
// #define FILE_DEBUG 1

#define ENABLE_SAVING             /* Turn on saving stuff */

#define INVALID_HANDLE_VALUE -1

#ifndef MAX_PATH
#define MAX_PATH 256
#endif

/* GLOB_ONLYDIR is a GNU extension for the glob() function and not defined
 * on some systems. We should probably use something different for this
 * case, but at the moment it we simply define it as 0... */
#ifndef GLOB_ONLYDIR
#warning GLOB_ONLYDIR was not defined.
#define GLOB_ONLYDIR 0
#endif


/* structure with all the drive-specific data for our emulated drives */
EMULATEDDRIVE emudrive;

typedef struct
{
  BOOL bUsed;
  FILE *FileHandle;
  char szActualName[MAX_PATH];   /* used by F_DATIME (0x57) */
} FILE_HANDLE;

typedef struct
{
  BOOL bUsed;
  int  nentries;                   /* number of entries in fs directory */
  int  centry;                     /* current entry # */
  struct dirent **found;           /* legal files */
  char path[MAX_PATH];             /* sfirst path */
} INTERNAL_DTA;

FILE_HANDLE  FileHandles[MAX_FILE_HANDLES];
INTERNAL_DTA InternalDTAs[MAX_DTAS_FILES];
int DTAIndex;                                 /* Circular index into above */
DTA *pDTA;                                    /* Our GEMDOS hard drive Disc Transfer Address structure */
unsigned char GemDOS_ConvertAttribute(mode_t mode);

/* to write shit with endian corrrection */
static inline u32 do_get_mem_long(u32 *a)
{
#ifdef __i386__
	u32 val = *a;
	__asm__ ("bswap	%0\n":"=r"(val):"0"(val));
	return val;
#elif LITTLE_ENDIAN
    u8 *b = (u8 *)a;
    return (*b << 24) | (*(b+1) << 16) | (*(b+2) << 8) | (*(b+3));
#else
    return *a;
#endif
}

static inline u16 do_get_mem_word(u16 *a)
{
#ifdef __i386__
	u16 val = *a;
	__asm__ ("xchgb %b0,%h0" : "=q" (val) :  "0" (val));
	return val;
#elif LITTLE_ENDIAN
    u8 *b = (u8 *)a;
    return (*b << 8) | (*(b+1));
#else
    return *a;
#endif
}

static inline u8 do_get_mem_byte(u8 *a)
{
    return *a;
}

static inline void do_put_mem_long(u32 *a, u32 v)
{
#ifdef __i386__
	__asm__ ("bswap	%0\n":"=r"(v):"0"(v));
	*a = v;
#elif LITTLE_ENDIAN
    u8 *b = (u8 *)a;
    
    *b = v >> 24;
    *(b+1) = v >> 16;    
    *(b+2) = v >> 8;
    *(b+3) = v;
#else
    *a = v;
#endif
}

static inline void do_put_mem_word(u16 *a, u16 v)
{
#ifdef __i386__
	__asm__ ("xchgb %b0,%h0" : "=q" (v) :  "0" (v));
	*a = v;
#elif LITTLE_ENDIAN
    u8 *b = (u8 *)a;
    
    *b = v >> 8;
    *(b+1) = v;
#else
    *a = v;
#endif
}

static inline void do_put_mem_byte(u8 *a, u8 v)
{
    *a = v;
}

/*-------------------------------------------------------*/
/*
  Routines to convert time and date to MSDOS format.
  Originally from the STonX emulator. (cheers!)
*/
unsigned short time2dos (time_t t)
{
	struct tm *x;
	x = localtime (&t);
	return (x->tm_sec>>1)|(x->tm_min<<5)|(x->tm_hour<<11);
}

unsigned short date2dos (time_t t)
{
	struct tm *x;
	x = localtime (&t);
	return x->tm_mday | ((x->tm_mon+1)<<5) | ( ((x->tm_year-80>0)?x->tm_year-80:0) << 9);
}


/*-----------------------------------------------------------------------*/
/*
  Populate a DATETIME structure with file info
*/
BOOL GetFileInformation(char *name, DATETIME *DateTime)
{
  struct stat filestat;
  int n;
  struct tm *x;

  n = stat(name, &filestat);
  if( n != 0 ) return(FALSE);
  x = localtime( &filestat.st_mtime );

  DateTime->word1 = 0;
  DateTime->word2 = 0;

  DateTime->word1 |= (x->tm_mday & 0x1F);         /* 5 bits */
  DateTime->word1 |= (x->tm_mon & 0x0F)<<5;       /* 4 bits */
  DateTime->word1 |= (((x->tm_year-80>0)?x->tm_year-80:0) & 0x7F)<<9;      /* 7 bits*/

  DateTime->word2 |= (x->tm_sec & 0x1F);          /* 5 bits */
  DateTime->word2 |= (x->tm_min & 0x3F)<<5;       /* 6 bits */
  DateTime->word2 |= (x->tm_hour & 0x1F)<<11;     /* 5 bits */

  return(TRUE);
}


/*-----------------------------------------------------------------------*/
/*
  Populate the DTA buffer with file info
*/
int PopulateDTA(char *path, struct dirent *file)
{
  char tempstr[MAX_PATH];
  struct stat filestat;
  int n;

  sprintf(tempstr, "%s/%s", path, file->d_name);
  n = stat(tempstr, &filestat);
  if(n != 0) return(FALSE); /* return on error */

  if(!pDTA) return(FALSE); /* no DTA pointer set */
  Misc_strupr(file->d_name);    /* convert to atari-style uppercase */
  strncpy(pDTA->dta_name,file->d_name,TOS_NAMELEN); /* FIXME: better handling of long file names */
  do_put_mem_long ((u32*)pDTA->dta_size, (long)filestat.st_size);
  do_put_mem_word ((u16*)pDTA->dta_time, time2dos(filestat.st_mtime));
  do_put_mem_word ((u16*)pDTA->dta_date, date2dos(filestat.st_mtime));
  pDTA->dta_attrib = GemDOS_ConvertAttribute(filestat.st_mode);

  return(TRUE);
}


/*-----------------------------------------------------------------------*/
/*
  Clear a used DTA structure.
*/
void ClearInternalDTA(){
  int i;

  /* clear the old DTA structure */
  if(InternalDTAs[DTAIndex].found != NULL){
    for(i=0; i <InternalDTAs[DTAIndex].nentries; i++)
      free(InternalDTAs[DTAIndex].found[i]);
    free(InternalDTAs[DTAIndex].found);
  }
  InternalDTAs[DTAIndex].bUsed = FALSE;
}


/*-----------------------------------------------------------------------*/
/*
  Match a file to a dir mask.
*/
static int match (char *pat, char *name)
{
  /* make uppercase copies */
  char p0[MAX_PATH], n0[MAX_PATH];
  strcpy(p0, pat);
  strcpy(n0, name);
  Misc_strupr(p0);
  Misc_strupr(n0);

  if(name[0] == '.') return(FALSE);                   /* no .* files */
  if (strcmp(pat,"*.*")==0) return(TRUE);
  else if (strcasecmp(pat,name)==0) return(TRUE);
  else
    {
      char *p=p0,*n=n0;
      for(;*n;)
	{
	  if (*p=='*') {while (*n && *n != '.') n++;p++;}
	  else if (*p=='?' && *n) {n++;p++;}
	  else if (*p++ != *n++) return(FALSE);
	}
      if (*p==0)
	{
	  return(TRUE);
	}
    }
  return(FALSE);
}

/*-----------------------------------------------------------------------*/
/*
  Parse directory from sfirst mask
  - e.g.: input:  "hdemudir/auto/mask*.*" outputs: "hdemudir/auto"
*/
void fsfirst_dirname(char *string, char *new){
  int i=0;

  sprintf(new, string);
  /* convert to front slashes. */
  i=0;
  while(new[i] != '\0'){
    if(new[i] == '\\') new[i] = '/';
    i++;
  }
  while(string[i] != '\0'){new[i] = string[i]; i++;} /* find end of string */
  while(new[i] != '/') i--; /* find last slash */
  new[i] = '\0';

}

/*-----------------------------------------------------------------------*/
/*
  Parse directory mask, e.g. "*.*"
*/
void fsfirst_dirmask(char *string, char *new){
  int i=0, j=0;
  while(string[i] != '\0')i++;   /* go to end of string */
  while(string[i] != '/') i--;   /* find last slash */
  i++;
  while(string[i] != '\0')new[j++] = string[i++]; /* go to end of string */
  new[j++] = '\0';
}

/*-----------------------------------------------------------------------*/
/*
  Initialize GemDOS/PC file system
*/
void GemDOS_Init(void)
{
  int i;

  /* Clear handles structure */
  Memory_Clear(FileHandles, sizeof(FILE_HANDLE)*MAX_FILE_HANDLES);
  /* Clear DTAs */
  for(i=0; i<MAX_DTAS_FILES; i++)
  {
    InternalDTAs[i].bUsed = FALSE;
    InternalDTAs[i].nentries = 0;
    InternalDTAs[i].found = NULL;
  }
  DTAIndex = 0;
}

/*-----------------------------------------------------------------------*/
/*
  Reset GemDOS file system
*/
void GemDOS_Reset()
{
  int i;

  /* Init file handles table */
  for(i=0; i<MAX_FILE_HANDLES; i++)
  {
    /* Was file open? If so close it */
    if (FileHandles[i].bUsed)
      fclose(FileHandles[i].FileHandle);

    FileHandles[i].FileHandle = NULL;
    FileHandles[i].bUsed = FALSE;
  }

  for(i=0; i<MAX_DTAS_FILES; i++)
  {
    InternalDTAs[i].bUsed = FALSE;
    InternalDTAs[i].nentries = 0;
    InternalDTAs[i].found = NULL;
  }

  /* Reset */
  pDTA = NULL;
  DTAIndex = 0;
}


/*-----------------------------------------------------------------------*/
/*
  Initialize a GEMDOS drive.
  Only 1 emulated drive allowed, as of yet.
*/
void GemDOS_InitDrives()
{
    /* set emulation directory string */
    strcpy(emudrive.hd_emulation_dir, "./");

    /* remove trailing slash, if any in the directory name */
    File_CleanFileName(emudrive.hd_emulation_dir);

    emudrive.hd_letter = 2;
}


/*-----------------------------------------------------------------------*/
/*
  Un-init the GEMDOS drive
*/
void GemDOS_UnInitDrives()
{
  GemDOS_Reset();        /* Close all open files on emulated drive*/
}


/*-----------------------------------------------------------------------*/
/*
  Return free PC file handle table index, or -1 if error
*/
int GemDOS_FindFreeFileHandle(void)
{
  int i;

  /* Scan our file list for free slot */
  for(i=0; i<MAX_FILE_HANDLES; i++) {
    if (!FileHandles[i].bUsed)
      return(i);
  }

  /* Cannot open any more files, return error */
  return(-1);
}

/*-----------------------------------------------------------------------*/
/*
  Check ST handle is within our table range, return TRUE if not
*/
BOOL GemDOS_IsInvalidFileHandle(int Handle)
{
  BOOL bInvalidHandle=FALSE;

  /* Check handle was valid with our handle table */
  if ( (Handle<0) || (Handle>=MAX_FILE_HANDLES) )
    bInvalidHandle = TRUE;
  else if (!FileHandles[Handle].bUsed)
    bInvalidHandle = TRUE;

  return(bInvalidHandle);
}

int baselen(char *s) {
  /* Returns the length of the basename of the file passed in parameter
     (ie the file without extension) */
  char *ext = strchr(s,'.');
  if (ext) return ext-s;
  return strlen(s);
}

/*-----------------------------------------------------------------------*/
/*
  Use hard-drive directory, current ST directory and filename to create full path
*/
void GemDOS_CreateHardDriveFileName(int Drive,char *pszFileName,char *pszDestName)
{
  /*  int DirIndex = Misc_LimitInt(Drive-2, 0,ConfigureParams.HardDisc.nDriveList-1); */
  char *s,*start;

  if(pszFileName[0] == '\0') return; /* check for valid string */

  /* case full filename "C:\foo\bar" */
  s=pszDestName; start=NULL;

  if(pszFileName[1] == ':') {
    sprintf(pszDestName, "%s%s", emudrive.hd_emulation_dir, File_RemoveFileNameDrive(pszFileName));
  }
  /* case referenced from root:  "\foo\bar" */
  else if(pszFileName[0] == '\\'){
    sprintf(pszDestName, "%s%s", emudrive.hd_emulation_dir, pszFileName);
  }
  /* case referenced from current directory */
  else {
    sprintf(pszDestName, "%s%s",  emudrive.fs_currpath, pszFileName);
    start = pszDestName + strlen(emudrive.fs_currpath)-1;
  }

  /* convert to front slashes. */
  while((s = strchr(s+1,'\\'))) {
    if (!start) {
      start = s;
      continue;
    }
    {
      glob_t globbuf;
      char old1,old2,dest[256];
      int len,j,found,base_len;

      *start++ = '/';
      old1 = *start; *start++ = '*';
      old2 = *start; *start = 0;
      glob(pszDestName,GLOB_ONLYDIR,NULL,&globbuf);
      *start-- = old2; *start = old1;
      *s = 0;
      len = strlen(pszDestName);
      base_len = baselen(start);
      found = 0;
      for (j=0; j<globbuf.gl_pathc; j++) {
	/* If we search for a file of at least 8 characters, then it might
	   be a longer filename since the ST can access only the first 8
	   characters. If not, then it's a precise match (with case). */
	if (!(base_len < 8 ? strcasecmp(globbuf.gl_pathv[j],pszDestName) :
	      strncasecmp(globbuf.gl_pathv[j],pszDestName,len))) {
	  /* we found a matching name... */
	  sprintf(dest,"%s%c%s",globbuf.gl_pathv[j],'/',s+1);
	  strcpy(pszDestName,dest);
	  j = globbuf.gl_pathc;
	  found = 1;
	}
      }
      globfree(&globbuf);
      if (!found) {
	/* didn't find it. Let's try normal files (it might be a symlink) */
	*start++ = '*';
	*start = 0;
	glob(pszDestName,0,NULL,&globbuf);
	*start-- = old2; *start = old1;
	for (j=0; j<globbuf.gl_pathc; j++) {
	  if (!strncasecmp(globbuf.gl_pathv[j],pszDestName,len)) {
	    /* we found a matching name... */
	    sprintf(dest,"%s%c%s",globbuf.gl_pathv[j],'/',s+1);
	    strcpy(pszDestName,dest);
	    j = globbuf.gl_pathc;
	    found = 1;
	  }
	}
	globfree(&globbuf);
	if (!found) {           /* really nothing ! */
	  *s = '/';
	  fprintf(stderr,"no path for %s\n",pszDestName);
	}
      }
    }
    start = s;
  }

  if (!start) start = strrchr(pszDestName,'/'); // path already converted ?

  if (start) {
    *start++ = '/';     /* in case there was only 1 anti slash */
    if (*start && !strchr(start,'?') && !strchr(start,'*')) {
      /* We have a complete name after the path, not a wildcard */
      glob_t globbuf;
      char old1,old2;
      int len,j,found,base_len;

      old1 = *start; *start++ = '*';
      old2 = *start; *start = 0;
      glob(pszDestName,0,NULL,&globbuf);
      *start-- = old2; *start = old1;
      len = strlen(pszDestName);
      base_len = baselen(start);
      found = 0;
      for (j=0; j<globbuf.gl_pathc; j++) {
	/* If we search for a file of at least 8 characters, then it might
	   be a longer filename since the ST can access only the first 8
	   characters. If not, then it's a precise match (with case). */
	if (!(base_len < 8 ? strcasecmp(globbuf.gl_pathv[j],pszDestName) :
	      strncasecmp(globbuf.gl_pathv[j],pszDestName,len))) {
	  /* we found a matching name... */
	  strcpy(pszDestName,globbuf.gl_pathv[j]);
	  j = globbuf.gl_pathc;
	  found = 1;
	}
      }
#if FILE_DEBUG
      if (!found) {
	/* It's often normal, the gem uses this to test for existence */
	/* of desktop.inf or newdesk.inf for example. */
	fprintf(stderr,"didn't find filename %s\n",pszDestName);
      }
#endif
      globfree(&globbuf);
    }
  }

#if FILE_DEBUG
 fprintf(stderr,"conv %s -> %s\n",pszFileName,pszDestName);
#endif
}

/*-----------------------------------------------------------------------*/
/*
  Covert from FindFirstFile/FindNextFile attribute to GemDOS format
*/
unsigned char GemDOS_ConvertAttribute(mode_t mode)
{
  unsigned char Attrib=0;

  /* FIXME: More attributes */
  if(S_ISDIR(mode)) Attrib |= GEMDOS_FILE_ATTRIB_SUBDIRECTORY;

/* FIXME */
/*
  // Look up attributes
  if (dwFileAttributes&FILE_ATTRIBUTE_READONLY)
    Attrib |= GEMDOS_FILE_ATTRIB_READONLY;
  if (dwFileAttributes&FILE_ATTRIBUTE_HIDDEN)
    Attrib |= GEMDOS_FILE_ATTRIB_HIDDEN;
  if (dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)
    Attrib |= GEMDOS_FILE_ATTRIB_SUBDIRECTORY;
*/
  return(Attrib);
}


/*-----------------------------------------------------------------------*/
/*
  GEMDOS Set Disc Transfer Address (DTA)
  Call 0x1A
*/
void GemDOS_SetDTA()
{
	unsigned long Params;
	
	Params = GetReg (REG_A7);
	Params -= SIZE_WORD;
	
  /* Look up on stack to find where DTA is! Store as PC pointer */
  pDTA = (DTA *)STRAM_ADDR(STMemory_ReadLong(Params+SIZE_WORD));
}

/*-----------------------------------------------------------------------*/
/*
  GEMDOS Create file
  Call 0x3C
*/
void GemDOS_Create()
{
  char szActualFileName[MAX_PATH];
  char *pszFileName;
  char *rwflags[] = { "w+", /* read / write (truncate if exists) */
		      "wb"  /* write only */
  };
  int Drive,Index,Mode;
	unsigned long Params;
	
	Params = GetReg (REG_A7);
	Params -= SIZE_WORD;
	

  /* Find filename */
  pszFileName = (char *)STRAM_ADDR(STMemory_ReadLong(Params+SIZE_WORD));
  Mode = STMemory_ReadWord(Params+SIZE_WORD+SIZE_LONG);
  Drive = 2;
    /* And convert to hard drive filename */
    GemDOS_CreateHardDriveFileName(Drive,pszFileName,szActualFileName);

    /* Find slot to store file handle, as need to return WORD handle for ST */
    Index = GemDOS_FindFreeFileHandle();
    if (Index==-1) {
      /* No free handles, return error code */
      SetReg (REG_D0, GEMDOS_ENHNDL);       /* No more handles */
      return;
    }
    else {
#ifdef ENABLE_SAVING

      FileHandles[Index].FileHandle = fopen(szActualFileName, rwflags[Mode&0x01]);

      if (FileHandles[Index].FileHandle != NULL) {
        /* Tag handle table entry as used and return handle */
        FileHandles[Index].bUsed = TRUE;
        SetReg (REG_D0, Index+BASE_FILEHANDLE);  /* Return valid ST file handle from range 6 to 45! (ours start from 0) */
        return;
      }
      else {
        SetReg (REG_D0, GEMDOS_EFILNF);     /* File not found */
        return;
      }
#else
      SetReg (REG_D0, GEMDOS_EFILNF);       /* File not found */
      return;
#endif
    }
}

/*-----------------------------------------------------------------------*/
/*
  GEMDOS Open file
  Call 0x3D
*/
void GemDOS_Open()
{
  char szActualFileName[MAX_PATH];
  char *pszFileName;
  char *open_modes[] = { "rb", "wb", "r+" };  /* convert atari modes to stdio modes */
  int Drive,Index,Mode;
	unsigned long Params;
	
	Params = GetReg (REG_A7);
	Params -= SIZE_WORD;
	

  /* Find filename */
  pszFileName = (char *)STRAM_ADDR(STMemory_ReadLong(Params+SIZE_WORD));
  Mode = STMemory_ReadWord(Params+SIZE_WORD+SIZE_LONG);
  Drive = 2;

    /* And convert to hard drive filename */
    GemDOS_CreateHardDriveFileName(Drive,pszFileName,szActualFileName);
    /* Find slot to store file handle, as need to return WORD handle for ST  */
    Index = GemDOS_FindFreeFileHandle();
    if (Index == -1) {
      /* No free handles, return error code */
      SetReg (REG_D0, GEMDOS_ENHNDL);       /* No more handles */
      return;
    }

    /* Open file */
    FileHandles[Index].FileHandle =  fopen(szActualFileName, open_modes[Mode&0x03]);

    sprintf(FileHandles[Index].szActualName,"%s",szActualFileName);

    if (FileHandles[Index].FileHandle != NULL) {
      /* Tag handle table entry as used and return handle */
      FileHandles[Index].bUsed = TRUE;
      SetReg (REG_D0, Index+BASE_FILEHANDLE);  /* Return valid ST file handle from range 6 to 45! (ours start from 0) */
      return;
    }
    SetReg (REG_D0, GEMDOS_EFILNF);     /* File not found/ error opening */
    return;
}

/*-----------------------------------------------------------------------*/
/*
  GEMDOS Close file
  Call 0x3E
*/
void GemDOS_Close()
{
  int Handle;
	unsigned long Params;
	
	Params = GetReg (REG_A7);
	Params -= SIZE_WORD;
	

  /* Find our handle - may belong to TOS */
  Handle = STMemory_ReadWord(Params+SIZE_WORD)-BASE_FILEHANDLE;

  /* Check handle was valid */
  if (GemDOS_IsInvalidFileHandle(Handle)) {
    /* No assume was TOS */
    return;
  }
  else {
    /* Close file and free up handle table */
    fclose(FileHandles[Handle].FileHandle);
    FileHandles[Handle].bUsed = FALSE;
    /* Return no error */
    SetReg (REG_D0, GEMDOS_EOK);
    return;
  }
}

/*-----------------------------------------------------------------------*/
/*
  GEMDOS Read file
  Call 0x3F
*/
void GemDOS_Read()
{
  char *pBuffer;
  unsigned long nBytesRead,Size,CurrentPos,FileSize;
  long nBytesLeft;
  int Handle;
	unsigned long Params;
	
	Params = GetReg (REG_A7);
	Params -= SIZE_WORD;
	

  /* Read details from stack */
  Handle = STMemory_ReadWord(Params+SIZE_WORD)-BASE_FILEHANDLE;
  Size = STMemory_ReadLong(Params+SIZE_WORD+SIZE_WORD);
  pBuffer = (char *)STRAM_ADDR(STMemory_ReadLong(Params+SIZE_WORD+SIZE_WORD+SIZE_LONG));

  /* Check handle was valid */
  if (GemDOS_IsInvalidFileHandle(Handle)) {
    /* No -  assume was TOS */
    return;
  }
  else {

    /* To quick check to see where our file pointer is and how large the file is */
    CurrentPos = ftell(FileHandles[Handle].FileHandle);
    fseek(FileHandles[Handle].FileHandle, 0, SEEK_END);
    FileSize = ftell(FileHandles[Handle].FileHandle);
    fseek(FileHandles[Handle].FileHandle, CurrentPos, SEEK_SET);

    nBytesLeft = FileSize-CurrentPos;

    /* Check for End Of File */
    if (nBytesLeft == 0) {
      /* FIXME: should we return zero (bytes read) or an error? */
       SetReg (REG_D0, 0);
      return;
    }
    else {
      /* Limit to size of file to prevent windows error */
      if (Size>FileSize)
        Size = FileSize;
      /* And read data in */
      nBytesRead = fread(pBuffer, 1, Size, FileHandles[Handle].FileHandle);

      /* Return number of bytes read */
      SetReg (REG_D0, nBytesRead);

      return;
    }
  }
}

/*-----------------------------------------------------------------------*/
/*
  GEMDOS Write file
  Call 0x40
*/
void GemDOS_Write()
{
  char *pBuffer;
  unsigned long Size,nBytesWritten;
  int Handle;
	unsigned long Params;
	
	Params = GetReg (REG_A7);
	Params -= SIZE_WORD;
	

#ifdef ENABLE_SAVING
  /* Read details from stack */
  Handle = STMemory_ReadWord(Params+SIZE_WORD)-BASE_FILEHANDLE;
  Size = STMemory_ReadLong(Params+SIZE_WORD+SIZE_WORD);
  pBuffer = (char *)STRAM_ADDR(STMemory_ReadLong(Params+SIZE_WORD+SIZE_WORD+SIZE_LONG));

  /* Check handle was valid */
  if (GemDOS_IsInvalidFileHandle(Handle)) {
    /* No assume was TOS */
    return;
  }
  else {

    nBytesWritten = fwrite(pBuffer, 1, Size, FileHandles[Handle].FileHandle);
    if (nBytesWritten>=0) {

      SetReg (REG_D0, nBytesWritten);      /* OK */
    }
    else
      SetReg (REG_D0, GEMDOS_EACCDN);      /* Access denied(ie read-only) */

    return;
  }
#endif

  return;
}

/*-----------------------------------------------------------------------*/
/*
  GEMDOS UnLink(Delete) file
  Call 0x41
*/
void GemDOS_UnLink()
{
#ifdef ENABLE_SAVING
  char szActualFileName[MAX_PATH];
  char *pszFileName;
  int Drive;
	unsigned long Params;
	
	Params = GetReg (REG_A7);
	Params -= SIZE_WORD;
	

  /* Find filename */
  pszFileName = (char *)STRAM_ADDR(STMemory_ReadLong(Params+SIZE_WORD));
  Drive = 2;
    /* And convert to hard drive filename */
    GemDOS_CreateHardDriveFileName(Drive,pszFileName,szActualFileName);

    /* Now delete file?? */
    if ( unlink(szActualFileName)==0 )
      SetReg (REG_D0, GEMDOS_EOK);          /* OK */
    else
      SetReg (REG_D0, GEMDOS_EFILNF);       /* File not found */

    return;
#endif

}

/*-----------------------------------------------------------------------*/
/*
  GEMDOS Search Next
  Call 0x4F
*/
void GemDOS_SNext()
{
  struct dirent **temp;
  int Index;
	unsigned long Params;
	
	Params = GetReg (REG_A7);
	Params -= SIZE_WORD;
	

  /* Was DTA ours or TOS? */
  if (do_get_mem_long ((u32*)pDTA->magic)==DTA_MAGIC_NUMBER) {

    /* Find index into our list of structures */
    Index = do_get_mem_word ((u16*)pDTA->index)&(MAX_DTAS_FILES-1);

    if(InternalDTAs[Index].centry >= InternalDTAs[Index].nentries){
      SetReg (REG_D0, GEMDOS_ENMFIL);    /* No more files */
      return;
    }

    temp = InternalDTAs[Index].found;
    if(PopulateDTA(InternalDTAs[Index].path, temp[InternalDTAs[Index].centry++]) == FALSE){
      fprintf(stderr,"\tError setting DTA.\n");
      return;
    }

    SetReg (REG_D0, GEMDOS_EOK);
    return;
  }

  return;
}


/*-----------------------------------------------------------------------*/
/*
  GEMDOS Find first file
  Call 0x4E
*/
void GemDOS_SFirst()
{
  char szActualFileName[MAX_PATH];
  char tempstr[MAX_PATH];
  char *pszFileName;
  struct dirent **files;
  unsigned short int Attr;
  int Drive;
  DIR *fsdir;
  int i,j,k;
	unsigned long Params;
	
	Params = GetReg (REG_A7);
	Params -= SIZE_WORD;
	

  /* Find filename to search for */
  pszFileName = (char *)STRAM_ADDR(STMemory_ReadLong(Params+SIZE_WORD));
  Attr = STMemory_ReadWord(Params+SIZE_WORD+SIZE_LONG);

  Drive = 2;

    /* And convert to hard drive filename */
    GemDOS_CreateHardDriveFileName(Drive,pszFileName,szActualFileName);

    /* Populate DTA, set index for our use */
    do_put_mem_word ((u16*)pDTA->index,DTAIndex);
    do_put_mem_long ((u32*)pDTA->magic,DTA_MAGIC_NUMBER); /* set our dta magic num */

    if(InternalDTAs[DTAIndex].bUsed == TRUE) ClearInternalDTA();
    InternalDTAs[DTAIndex].bUsed = TRUE;

    /* Were we looking for the volume label? */
    if (Attr&GEMDOS_FILE_ATTRIB_VOLUME_LABEL) {
      /* Volume name */
      strcpy(pDTA->dta_name,"EMULATED.001");
      SetReg (REG_D0, GEMDOS_EOK);          /* Got volume */
      return;
    }

    /* open directory */
    fsfirst_dirname(szActualFileName, InternalDTAs[DTAIndex].path);
    fsdir = opendir(InternalDTAs[DTAIndex].path);

    if( fsdir == NULL ){
      SetReg (REG_D0, GEMDOS_EPTHNF);        /* Path not found */
      return;
    }
    /* close directory */
    closedir( fsdir );

    InternalDTAs[DTAIndex].nentries = scandir(InternalDTAs[DTAIndex].path, &files, 0, alphasort);
    if( InternalDTAs[DTAIndex].nentries < 0 ){
      SetReg (REG_D0, GEMDOS_EFILNF);        /* File (directory actually) not found */
      return;
    }

    InternalDTAs[DTAIndex].centry = 0;        /* current entry is 0 */
    fsfirst_dirmask(szActualFileName, tempstr); /* get directory mask */

    /* Create and populate a list of matching files. */

    j = 0;                     /* count number of entries matching mask */
    for(i=0;i<InternalDTAs[DTAIndex].nentries;i++)
      if(match(tempstr, files[i]->d_name)) j++;

    if (j==0) {
      return;
    }

    InternalDTAs[DTAIndex].found = (struct dirent **)malloc(sizeof(struct dirent *) * j);

    /* copy the dirent pointers for files matching the mask to our list */
    k = 0;
    for(i=0;i<InternalDTAs[DTAIndex].nentries;i++)
      if(match(tempstr, files[i]->d_name)){
	InternalDTAs[DTAIndex].found[k] = files[i];
	k++;
      }

    InternalDTAs[DTAIndex].nentries = j; /* set number of legal entries */

    if(InternalDTAs[DTAIndex].nentries == 0){
      /* No files of that match, return error code */
      SetReg (REG_D0, GEMDOS_EFILNF);        /* File not found */
      return;
    }

    /* Scan for first file (SNext uses no parameters) */
    GemDOS_SNext(0);
    /* increment DTA index */
    DTAIndex++;
    DTAIndex&=(MAX_DTAS_FILES-1);

    return;
}


void Call_Memset ()
{
	int adr, count;
	unsigned long Params;
	
	Params = GetReg (REG_A7);
	Params -= SIZE_WORD;
	
	count = STMemory_ReadLong (Params+SIZE_WORD);
	adr = STMemory_ReadLong (Params+SIZE_WORD+SIZE_LONG);
	memset (STRam+adr, 0, count);
}

void Call_MemsetBlue ()
{
	int adr, count;
	unsigned long Params;
	
	Params = GetReg (REG_A7);
	Params -= SIZE_WORD;
	
	count = STMemory_ReadLong (Params+SIZE_WORD);
	adr = STMemory_ReadLong (Params+SIZE_WORD+SIZE_LONG);
	memset (STRam+adr, 0xe, count);
}

void Call_Memcpy ()
{
	int dest, src, count;
	unsigned long Params;
	
	Params = GetReg (REG_A7);
	Params -= SIZE_WORD;
	

	dest = STMemory_ReadLong (Params + SIZE_WORD);
	src = STMemory_ReadLong (Params + SIZE_WORD + SIZE_LONG);
	count = STMemory_ReadLong (Params + SIZE_WORD + 2*SIZE_LONG);

	memcpy (STRam+dest, STRam+src, count);
}

static const char mouse_bmp[256] = {
 0, 0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
 0,15, 0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
 0,15,15, 0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
 0,15,15,15, 0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
 0,15,15,15,15, 0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
 0,15,15,15,15,15, 0,-1,-1,-1,-1,-1,-1,-1,-1,-1,
 0,15,15,15,15, 0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
 0,15,15,15,15, 0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
 0,15, 0,15,15,15, 0,-1,-1,-1,-1,-1,-1,-1,-1,-1,
 0, 0,-1, 0,15,15, 0,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1, 0,15,15,15, 0,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1, 0,15,15, 0,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1, 0,15,15,15, 0,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1, 0,15, 0,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1, 0,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
};

Uint8 under_mouse[256];

void Call_BlitCursor ()
{
	int x, y, adr, org_x, org_y;
	Uint8 *pixbase, *pix;
	const char *bmp;
	Uint8 *save;
	unsigned long Params;
	
	Params = GetReg (REG_A7);
	Params -= SIZE_WORD;
	
	org_x = STMemory_ReadLong (Params+SIZE_WORD);
	org_y = STMemory_ReadLong (Params+SIZE_WORD+SIZE_LONG);
	adr = STMemory_ReadLong (Params+SIZE_WORD+2*SIZE_LONG);

	pixbase = STRam + adr + (org_y*SCREENBYTES_LINE) + org_x;
	bmp = mouse_bmp;
	save = under_mouse;

	for (y=0; y<16; y++) {
		if (y + org_y > SCREEN_HEIGHT_HBL) break;
		pix = pixbase;
		pixbase += SCREENBYTES_LINE;
		for (x=0; x<16; x++, pix++, bmp++, save++) {
			if (x+org_x >= SCREENBYTES_LINE) continue;
			*save = *pix;
			if (*bmp != -1) *pix = *bmp;
		}
	}
}

void Call_RestoreUnderCursor ()
{
	int x, y, adr, org_x, org_y;
	Uint8 *pixbase, *pix;
	char *bmp;
	unsigned long Params;
	
	Params = GetReg (REG_A7);
	Params -= SIZE_WORD;
	
	org_x = STMemory_ReadLong (Params+SIZE_WORD);
	org_y = STMemory_ReadLong (Params+SIZE_WORD+SIZE_LONG);
	adr = STMemory_ReadLong (Params+SIZE_WORD+2*SIZE_LONG);

	pixbase = STRam + adr + (org_y*SCREENBYTES_LINE) + org_x;
	bmp = under_mouse;

	for (y=0; y<16; y++) {
		if (y + org_y > SCREEN_HEIGHT_HBL) break;
		pix = pixbase;
		pixbase += SCREENBYTES_LINE;
		for (x=0; x<16; x++, pix++, bmp++) {
			if (x+org_x >= SCREENBYTES_LINE) continue;
			if (*bmp != -1) *pix = *bmp;
		}
	}
}

void Call_PutPix ()
{
	int col, org_x, scr;
	char *pix;
	unsigned long Params;
	
	Params = GetReg (REG_A7);
	Params -= SIZE_WORD;
	
	col = STMemory_ReadWord (Params+SIZE_WORD)>>2;
	org_x = (unsigned short) GetReg (REG_D4);
	scr = GetReg (REG_A3);

	/* hack to fix screen line. frontier's logic still thinks
	 * there are 160 bytes per line */
	/* which screen buffer it is based on */
	if (scr & 0x100000) {
		scr -= 0x100000;
		scr *= 2;
		scr += 0x100000;
	} else {
		scr -= 0xf0000;
		scr *= 2;
		scr += 0xf0000;
	}
	pix = (char *)STRam + scr + org_x;
	*pix = col;
	return;
}

void Call_FillLine ()
{
	int org_x,len,scr,col;
	char *pix;
	unsigned long Params;
	
	Params = GetReg (REG_A7);
	Params -= SIZE_WORD;
	

	col = STMemory_ReadWord (Params+SIZE_WORD)>>2;
	org_x = (unsigned short) GetReg (REG_D4);
	len = (~GetReg (REG_D5)) & 0xffff;
	scr = GetReg (REG_A3);
	
	/* hack to fix screen line. frontier's logic still thinks
	 * there are 160 bytes per line */
	/* which screen buffer it is based on */
	if (scr & 0x100000) {
		scr -= 0x100000;
		scr *= 2;
		scr += 0x100000;
	} else {
		scr -= 0xf0000;
		scr *= 2;
		scr += 0xf0000;
	}
	pix = (char *)STRam + scr;
	org_x = SCREENBYTES_LINE;
	while (org_x--) {
		*pix = col;
		pix++;
	}
}

/*
 * This is used by the scanner code to draw object stalks
 * which are below the plane of the scanner.
 * The mask d7 indicates which pixels in the plane to set,
 * and they are set if their current colour is zero.
 *
 * This implementation isn't the way the function is really
 * supposed to be implemented (colour and draw mask was
 * combined in d6 but the colour mask is wrong now for
 * non-planar screen).
 */
void Call_BackHLine ()
{
	int i,scr,col,bitfield;
	char *pix;
	unsigned long Params;
	
	Params = GetReg (REG_A7);
	Params -= SIZE_WORD;
	

	col = STMemory_ReadWord (Params+SIZE_WORD)>>2;
	scr = GetReg (REG_A3);
	bitfield = (unsigned short) GetReg (REG_D7);
	
	/* hack to fix screen line. frontier's logic still thinks
	 * there are 160 bytes per line */
	/* which screen buffer it is based on */
	if (scr & 0x100000) {
		scr -= 0x100000;
		scr *= 2;
		scr += 0x100000;
	} else {
		scr -= 0xf0000;
		scr *= 2;
		scr += 0xf0000;
	}
	pix = STRam + scr;
	for (i=15; i>=0; i--) {
		if ((bitfield & (1<<i)) && (*pix == 0)) *pix = col;
		pix++;
	}
}

void Call_OldHLine ()
{
	int org_x,len,scr,col;
	char *pix;
	unsigned long Params;
	
	Params = GetReg (REG_A7);
	Params -= SIZE_WORD;
	

	col = STMemory_ReadWord (Params+SIZE_WORD)>>2;
	//printf ("col=%d, d4=%d, (idx) d5=%d, (scr_line) a3=%p\n", col, Regs[REG_D4]&0xffff, Regs[REG_D5]&0xffff, (void*)Regs[REG_A3]);
	org_x = (unsigned short) GetReg (REG_D4);
	len = (unsigned short) GetReg (REG_D5);
	scr = GetReg (REG_A3);
	
	/* hack to fix screen line. frontier's logic still thinks
	 * there are 160 bytes per line */
	/* which screen buffer it is based on */
	if (scr & 0x100000) {
		scr -= 0x100000;
		scr *= 2;
		scr += 0x100000;
	} else {
		scr -= 0xf0000;
		scr *= 2;
		scr += 0xf0000;
	}
	len = len/2;
	/* horizontal line */
	pix = STRam + scr + org_x;
	while (len--) {
		*pix = col;
		pix++;
	}
}

void Call_HLine ()
{
	int org_x,len,scr,col;
	char *pix;

	col = (GetReg(REG_D1) & 0xffff)>>2;
	org_x = GetReg(REG_D4) & 0xffff;
	len = GetReg(REG_D5) & 0xffff;
	scr = GetReg(REG_A3);
	
	/* horizontal line */
	pix = STRam + scr + org_x;
	while (len--) {
		*pix = col;
		pix++;
	}
}

/*
 * Blits frontier format 4-plane thingy
 */
void Call_BlitBmp ()
{
	int width, height, org_x, org_y, bmp, scr;
	char *bmp_pix, *scr_pix, *ybase;
	int xpoo, i, ypoo, plane_incr;
	
	short word0, word1, word2, word3;
	unsigned long Params;
	
	Params = GetReg (REG_A7);
	Params -= SIZE_WORD;
	

	
	width = STMemory_ReadWord (Params+SIZE_WORD);
	height = STMemory_ReadWord (Params+SIZE_WORD*2);
	org_x = STMemory_ReadWord (Params+SIZE_WORD*3);
	org_y = STMemory_ReadWord (Params+SIZE_WORD*4);
	bmp = STMemory_ReadLong (Params+SIZE_WORD*5);
	scr = STMemory_ReadLong (Params+SIZE_WORD*5 + SIZE_LONG);

	/* width is in words (width/16) */
	//printf ("Blit %dx%d to %d,%d, bmp 0x%x, scr 0x%x.\n", width, height, org_x, org_y, bmp, scr);
	bmp_pix = STRam + bmp + 4;
	ybase = STRam + scr + (org_y*SCREENBYTES_LINE) + org_x;

	/* These checks were in the original blit routine */
	if (org_x < 0) return;
	if (org_y < 0) return;
	if (height > 200) return;
	if (width > 320) return;
	
	plane_incr = 2*height*width;
	
	ypoo = height;
	while (ypoo--) {
		scr_pix = (char *)ybase;
		ybase += SCREENBYTES_LINE;
		for (xpoo = width; xpoo; xpoo--) {
			word0 = SDL_SwapBE16 (*((short*)bmp_pix));
			bmp_pix += plane_incr;
			word1 = SDL_SwapBE16 (*((short*)bmp_pix));
			bmp_pix += plane_incr;
			word2 = SDL_SwapBE16 (*((short*)bmp_pix));
			bmp_pix += plane_incr;
			word3 = SDL_SwapBE16 (*((short*)bmp_pix));
			
			for (i=0; i<16; i++) {
				*scr_pix = (word0 >> (15-i))&0x1;
				*scr_pix |= ((word1 >> (15-i))&0x1)<<1;
				*scr_pix |= ((word2 >> (15-i))&0x1)<<2;
				*scr_pix |= ((word3 >> (15-i))&0x1)<<3;
				scr_pix++;
			}
			bmp_pix -= 3*plane_incr;
			bmp_pix += 2;
		}
	}
}

#define SCR_W	320

void Call_DrawStrShadowed ()
{
	unsigned char *str;
	
	str = GetReg (REG_A0) + STRam;

	SetReg (REG_D1, DrawStr (
			GetReg (REG_D1), GetReg (REG_D2),
			GetReg (REG_D0), str, TRUE));
}

void Call_DrawStr ()
{
	unsigned char *str;
	
	str = GetReg (REG_A0) + STRam;

	SetReg (REG_D1, DrawStr (
			GetReg (REG_D1), GetReg (REG_D2),
			GetReg (REG_D0), str, FALSE));
}

void Call_SetMainPalette ()
{
	Uint32 pal_ptr;
	int i;
	unsigned long Params;

	Params = GetReg (REG_A7);
	Params -= SIZE_WORD;
	
	
	pal_ptr = STMemory_ReadLong (Params+SIZE_WORD);
	
	for (i=0; i<16; i++) {
		MainPalette[i] = STMemory_ReadWord (pal_ptr);
		//printf ("%hx ", MainPalette[i]);
		pal_ptr+=2;
	}
	//printf ("\n");
}

void Call_SetCtrlPalette ()
{
	Uint32 pal_ptr;
	int i;
	unsigned long Params;
	
	Params = GetReg (REG_A7);
	Params -= SIZE_WORD;
	
	
	pal_ptr = STMemory_ReadLong (Params+SIZE_WORD);
	
	for (i=0; i<16; i++) {
		CtrlPalette[i] = STMemory_ReadWord (pal_ptr);
		pal_ptr+=2;
	}
}

int len_working_ext_pal;
unsigned short working_ext_pal[240];

void Call_InformScreens ()
{
	unsigned long Params;
	
	Params = GetReg (REG_A7);
	Params -= SIZE_WORD;
	
	physcreen2 = STMemory_ReadLong (Params+SIZE_WORD);
	logscreen2 = STMemory_ReadLong (Params+SIZE_WORD+SIZE_LONG);
	physcreen = STMemory_ReadLong (Params+SIZE_WORD+2*SIZE_LONG);
	logscreen = STMemory_ReadLong (Params+SIZE_WORD+3*SIZE_LONG);
}

/* also copies the extended palette into main palette */
void Call_SetScreenBase ()
{
	int i;
	unsigned long Params;
	
	Params = GetReg (REG_A7);
	Params -= SIZE_WORD;
	
	VideoBase = STMemory_ReadLong (Params+SIZE_WORD);
	VideoRaster = STRam + VideoBase;

	for (i=0; i<len_working_ext_pal; i++) {
		MainPalette[16+i] = working_ext_pal[i];
	}
	len_main_palette = 16 + len_working_ext_pal;
}

void Call_MakeExtPalette ()
{
	int col_list, len, col_idx, col_val, i;
	unsigned long Params;
	
	Params = GetReg (REG_A7);
	Params -= SIZE_WORD;
	

	col_list = STMemory_ReadLong (Params+SIZE_WORD);

	len = STMemory_ReadWord (col_list) >> 2;
	len_working_ext_pal = len;
	col_list+=2;
	//printf ("%d colours.\n", len+2);
	for (i=0; i<len; i++) {
		col_val = STMemory_ReadWord (col_list);
		working_ext_pal[i] = col_val;
		col_list += 2;
		col_idx = STMemory_ReadWord (col_list);
		/* offset dynamic colours into extended palette
		 * range (colours 16+) */
		col_idx += 16<<2;
		STMemory_WriteWord (col_list, col_idx);
		col_list += 2;
	}
}
void Call_DumpRegs ()
{
	int i;
	printf ("D: ");
	for (i=0; i<8; i++) {
		printf ("$%x ", GetReg (i));
	} printf ("\n");
	printf ("A: ");
	for (i=0; i<8; i++) {
		printf ("$%x ", GetReg (i+8));
	} printf ("\n");
}


void Call_DumpDebug ()
{
	int i, j;

	printf ("Debug info. PC @ 68k line %d.\n", line_no);
	
	Call_DumpRegs ();
	
	printf ("Stack:");
	j = GetReg (15);
	for (i=0; i<8; i++) {
		j+=4;
		printf (" $%x", STMemory_ReadLong (j));
	}
	putchar ('\n');
}

void Call_NotifyOfScrFlip ()
{
	bScreenContentsChanged = TRUE;
}

/* mouse pos in d3,d4 */
void Call_NotifyMousePos ()
{
#if 0
	int x, y;
	
	x = GetReg (REG_D3) & 0xffff;
	y = GetReg (REG_D4) & 0xffff;

	x *= ScreenDraw.MouseScale;
	y *= ScreenDraw.MouseScale;
	
	SDL_EventState (SDL_MOUSEMOTION, SDL_DISABLE);
	SDL_WarpMouse (x, y);
	SDL_EventState (SDL_MOUSEMOTION, SDL_ENABLE);
	SDL_ShowCursor (SDL_ENABLE);	
#endif /* 0 */
}

void Call_DrawQuad ()
{
	int high, equal, i, j;
	struct Point pts[4];

	pts[0].x = GetReg(REG_D0);
	pts[0].y = GetReg(REG_D1);
	pts[1].x = GetReg(REG_D2);
	pts[1].y = GetReg(REG_D3);
	pts[2].x = GetReg(REG_D4);
	pts[2].y = GetReg(REG_D5);
	pts[3].x = GetReg(REG_D6);
	pts[3].y = GetReg(REG_D7);
	
	/* the two points with highest y actually seem to be
	 * drawn only to y-1 by the frontier routines. weird. */
	for (i=0; i<4; i++) {
		high = 0;
		equal = -1;
		for (j=0; j<4; j++) {
			if (i == j) continue;
			if (pts[i].y > pts[j].y) high++;
			if (pts[i].y == pts[j].y) equal = j;
		}
		if ((high == 2) && (equal != -1)) {
			pts[equal].y--;
			pts[i].y--;
			break;
		}
	}
	DrawPoly (pts, 4, GetReg (REG_A0)/4);

	/*char *poop;
	for (i=0; i<4; i++) {
		poop = LOGSCREEN2 + (SCREENBYTES_LINE*(int)pts[i].y) + (int)pts[i].x;
		*poop = 0;
	}*/
}

void Call_DrawTriangle ()
{
	DrawTriangle ((short)GetReg (REG_D0), (short)GetReg (REG_D1),
			(short)GetReg (REG_D2), (short)GetReg (REG_D3),
			(short)GetReg (REG_D4), (short)GetReg (REG_D5),
			((short)GetReg (REG_A0))/4);
}

static void Call_Idle ()
{
	usleep (0);
}

void Call_HostUpdate ()
{
	/* Clear any key presses which are due to be de-bounced (held for one ST frame) */
	Keymap_DebounceAllKeys();
	/* Check 'Function' keys, so if press F12 we update screen correctly to Window! */
	ShortCut_CheckKeys();
	/* And handle any messages, check for quit message */
	Main_EventHandler();         /* Process messages, set 'bQuitProgram' if user tries to quit */
	/* Pass NULL interrupt function to quit cleanly */
	//if (bQuitProgram) Int_AddAbsoluteInterrupt(4, 0L);
}

/* d0.b = exception number, a0 = handler. */
static void SetExceptionHandler ()
{
	/* only 32 handlers */
	exception_handlers[GetReg(0) & 31] = GetReg (8);
}

int DumpMess (int pos, int line)
{
	if (GetXFlag ()) putchar ('X');
	if (GetZFlag ()) putchar ('Z');
	if (GetNFlag ()) putchar ('N');
	if (GetVFlag ()) putchar ('V');
	if (GetCFlag ()) putchar ('C');
	return 0;
	printf (" $%x $%x $%x $%x $%x $%x $%x $%x*$%x $%x $%x $%x $%x $%x $%x $%x:%d\n",
			GetReg (0),
			GetReg (1),
			GetReg (2),
			GetReg (3),
			GetReg (4),
			GetReg (5),
			GetReg (6),
			GetReg (7),
			GetReg (8),
			GetReg (9),
			GetReg (10),
			GetReg (11),
			GetReg (12),
			GetReg (13),
			GetReg (14),
			GetReg (15),line_no);
}
static int _X, _Z, _N, _V, _C;
static int PrevRegs[16];

int changed ()
{
	int i;
	if (GetXFlag () != _X) return 1;
	if (GetZFlag () != _Z) return 1;
	if (GetNFlag () != _N) return 1;
	if (GetVFlag () != _V) return 1;
	if (GetCFlag () != _C) return 1;
	for (i=0; i<16; i++) {
		if (PrevRegs[i] != GetReg (i)) return 1;
	}
	return 0;
}

void DumpRegsChanged ()
{
	int i;

	//if (!changed ()) return;

	_X = GetXFlag ();
	_Z = GetZFlag ();
	_V = GetVFlag ();
	_N = GetNFlag ();
	_C = GetCFlag ();
	
	if (_X) putchar ('X');
	if (_Z) putchar ('Z');
	if (_N) putchar ('N');
	if (_V) putchar ('V');
	if (_C) putchar ('C');
	
	for (i=0; i<16; i++) {
		if (PrevRegs[i] != GetReg (i)) {
			printf (" %c%d:%x->%x", (i<8?'d':'a'), (i<8?i:i-8), PrevRegs[i], GetReg (i));
			PrevRegs[i] = GetReg (i);
		}
	}
	printf (" @%d\n", line_no);
}


HOSTCALL hcalls [] = {
	&SetExceptionHandler,
	&Call_Memset,		/* 0x1 */
	&Call_MemsetBlue,		/* 0x2 */
	&Call_BlitCursor,		/* 0x3 */
	&Call_RestoreUnderCursor,	/* 0x4 */
	&Call_BlitBmp,		/* 0x5 */
	&Call_OldHLine,		/* 0x6 */
	&Call_HostUpdate,		/* 0x7 */
	&Call_Memcpy,		/* 0x8 */
	&Call_PutPix,		/* 0x9 */
	&Call_BackHLine,		/* 0xa */
	&Call_FillLine,		/* 0xb */
	&Call_SetMainPalette,	/* 0xc */
	&Call_SetCtrlPalette,	/* 0xd */
	&Call_SetScreenBase,		/* 0xe */
	&Screen_Draw,			/* 0xf */
	&Call_DumpRegs,		/* 0x10 */
	&Call_MakeExtPalette,	/* 0x11 */
	&Call_PlaySFX,			/* 0x12 */
	&Call_GetMouseInput,		/* 0x13 */
	&Call_GetKeyboardEvent,		/* 0x14 */
	NULL,			/* 0x15 */
	&Call_HLine,			/* 0x16 */
	&Call_NotifyOfScrFlip,		/* 0x17 */
	&Call_NotifyMousePos,		/* 0x18 */
	&Call_InformScreens,		/* 0x19 */
	&GemDOS_SetDTA,
	&Call_DrawStrShadowed,		/* 0x1b */
	&Call_DrawStr,			/* 0x1c */
	&Call_DrawTriangle,		/* 0x1d */
	&Call_DrawQuad,			/* 0x1e */
	&Call_Idle,			/* 0x1f */
	&Call_DumpDebug,		/* 0x20 */
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,				/* 0x30 */
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	&GemDOS_Create,
	&GemDOS_Open,
	&GemDOS_Close,
	&GemDOS_Read,
	&GemDOS_Write,			/* 0x40 */
	&GemDOS_UnLink,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	&GemDOS_SFirst,
	&GemDOS_SNext,
	NULL,				/* 0x50 */
};

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.