#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/times.h>
#include <signal.h>
#include <time.h>
#include <sys/time.h>
#include <errno.h>
#include <dirent.h>
#include <glob.h>

#include "riscos.h"
#include "module-utility.h"

#define ENV_EXIT 0xffee0001

// Given a pointer to a heap, find the associated Heap manager class
// Return NULL if no match.
Heap *OS::FindHeap (void *ptr)
{
  for (vector<Heap *>::iterator x = user_heaps.begin ();
       x != user_heaps.end (); x++)
    if (ptr == (*x)->Base ())
      return *x;

  return NULL;
}

void OS::Canonicalise (const char *from, string &to)
{
  // Canonicalise a file path.  The realpath function is rather heavy
  // on the system call usage.  UnixLib has a tendency to call for
  // canonicalisation many times, and GNU Java reads a lot of files.
  // We attempt to optimise this by storing a few results.

  // Contain the path up to the last directory
  string base = from;

  // Contain the filename, if it exists.
  string basename = "";

  struct stat sb;
  if (stat (from, &sb))
    {
      //printf ("Canonicalise: stat failed\n");
      to = from;
      return;
    }

  if (! S_ISDIR (sb.st_mode))
    {
      // Get directory basename
      string::size_type pos = base.rfind ('/');
      if (pos != string::npos)
	{
	  basename = base.substr (pos + 1, base.size ());
	  base.erase (pos, base.size ());
	}
    }

  //printf ("Canonicalise: from='%s', base='%s', basename='%s'\n",
  //	  from, base.c_str (), basename.c_str ());

  // Now see if we have a match
  for (vector<struct canonicalise>::iterator x = clist.begin ();
       x != clist.end (); x++)
    {
      if (base == x->from)
	{
	  to = x->to + "/" + basename;
	  //printf ("Canonicalise: matched '%s': '%s'\n",
	  //	  from, to.c_str ());
	  return;
	}
    }

  // No match.  Canonicalise the basename.  We don't cache the full
  // filename because we could potentially ask for canonicalisation of
  // many files within one directory.
  struct canonicalise c;
  c.from = base;

  char buf[1024];
  realpath (from, buf);
  //printf ("Canonicalise: realpath: from='%s', buf='%s'\n", from, buf);
  c.to = buf;
  clist.push_back (c);

  to = c.to + "/" + basename;

  //printf ("Canonicalise: caching. from = '%s', to = '%s'\n",
  //base.c_str (), c.to.c_str ());
}

OSDir::OSDir (const char *dirname)
{
  if (dirname == NULL)
    name = get_current_dir_name ();
  else
    name = strdup (dirname);

  DIR *d = opendir (name);
  struct dirent *ent;

  // Fill FileInfo structures with globbed information
  entries.clear ();
  while ((ent = readdir (d)) != NULL)
    {
      // Skip the . and .. directories as RISC OS will fake these.
      char *n = ent->d_name;
      if (n[0] == '.' && n[1] == '\0')
	continue;

      if (n[0] == '.' && n[1] == '.' && n[2] == '\0')
	continue;

      FileHandle *f = new FileHandle;
      f->SetFile (dirname, n);
      entries.push_back (f);
    }
  closedir (d);
}

OSDir::~OSDir ()
{
  if (name)
    free (name);
  for (int x = 0; x < Count (); x++)
    delete entries[x];
}


void OS::DelEnvironment (const char *name)
{
  for (vector<env_vars_t>::iterator x = environ_vars.begin ();
       x != environ_vars.end (); x++)
    {
      if (strcmp (x->name, name) == 0)
	{
	  free (x->name);
	  if (x->type != 1)
	    free (x->str_value);
	  environ_vars.erase (x);
	  break;
	}
    }
}

// Helper functions for OS environment variable
// initialisation.
void OS::SetEnvironment (const char *name, int value)
{
  env_vars_t v;
  DelEnvironment (name);
  v.type = 1; // Integer
  v.name = strdup (name);
  v.int_value = value;
  environ_vars.push_back (v);
}

void OS::SetEnvironment (const char *name, const char *value)
{
  env_vars_t v;
  DelEnvironment (name);
  v.type = 0; // String
  v.name = strdup (name);
  v.str_value = strdup (value);
  environ_vars.push_back (v);
}

// Initialise the UtilityModule.
void OS::Initialise (void)
{
  // Allocate 1 page for private memory.
  pmem = memc->rma_heap->Alloc (0x1000);

  // Initialise the Exit handler
  environment[ENVH_EXIT][0] = ENV_EXIT;

  // The MemoryLimit handler.
  environment[ENVH_MEMORYLIMIT][0] = memc->memory_size;

  // Application space handler
  environment[ENVH_APPLICATION_SPACE][0] = memc->memory_size;

  // Place to dump registers when an exception occurs
  environment[ENVH_EXCEPTION_REGISTERS][0] = (uint32_t) pmem + 0x80;

  // We use the key 'deadbeef' in the cpu_loop to detect the
  // default exception handler address.
  environment[ENVH_UNDEFINED_INSTRUCTION][0] = 0xdeadbeef;
  environment[ENVH_PREFETCH_ABORT][0] = 0xdeadbeef;
  environment[ENVH_DATA_ABORT][0] = 0xdeadbeef;
  environment[ENVH_ADDRESS_EXCEPTION][0] = 0xdeadbeef;
  environment[ENVH_OTHER_EXCEPTIONS][0] = 0xdeadbeef;

  escape_state = false;
  escape_key = 27;

  vducount = 0; // The VDU queue.

  cmd_line = NULL;

  // Keep a copy of the time RISC OS was started.
  gettimeofday (&os_start_time, NULL);

  tickers.clear ();
  environ_vars.clear ();
  user_heaps.clear ();

  // Set a default path.  This should allow collect2
  // to find a suitable linker.
  SetEnvironment ("UnixEnv$PATH", "/home/riscos/bin:/home/riscos/env/gccsdk/bin");
  // We're operating on a UNIX filesystem so name
  // translation becomes uninteresting.
  SetEnvironment ("UnixEnv$nonametrans", 1);

  //SetEnvironment ("UnixEnv$LANG", "en_GB.UTF-8");
  SetEnvironment ("UnixEnv$TERM", "linux");
  SetEnvironment ("UnixEnv$TMPDIR", "/tmp");
  SetEnvironment ("UnixEnv$TMP", "/tmp");
  SetEnvironment ("UnixEnv$TEMP", "/tmp");

}

// The VDU driver
void OS::vdu (unsigned char ch)
{
  if (vducount == 0 && ch >= 32)
    {
      WriteCh (ch);
      return;
    }

  vduq[vducount++] = ch;

  int vdulen = 1;
  if (vduq[0] == 1)
    vdulen = 2;
  else if (vduq[0] == 17)
    vdulen = 2;
  else if (vduq[0] == 18)
    vdulen = 3;
  else if (vduq[0] == 19)
    vdulen = 6;
  else if (vduq[0] == 22)
    vdulen = 2;
  else if (vduq[0] == 23)
    vdulen = 10;
  else if (vduq[0] == 24)
    vdulen = 9;
  else if (vduq[0] == 25)
    vdulen = 6;
  else if (vduq[0] == 28)
    vdulen = 5;
  else if (vduq[0] == 29)
    vdulen = 5;
  else if (vduq[0] == 31)
    vdulen = 3;
  if (vdulen == vducount)
    {
      vducount = 0;
      switch (vduq[0])
        {
        case 0:         /* Nothing */
        case 1:         /* Send next to printer only */
        case 2:         /* Printer on */
        case 3:         /* Printer off */
        case 4:         /* Split text and graphics cursors */
        case 5:         /* Join  text and graphics cursors */
        case 6:         /* Enable screen output */
          break;

        case 7:         /* Bell */
          WriteCh (7);
          break;
        case 8:         /* Back space */
          WriteCh (8);
          break;
        case 9:         /* Horizontal tab */
          WriteCh (9);
          break;
        case 10:                /* Line feed */
          WriteCh (10);
          break;
        case 11:                /* Vertical tab */
          WriteCh (11);
          break;
        case 12:                /* Form feed/Clear screen */
          WriteCh (12);
          break;
        case 13:                /* Carriage return */
          WriteCh (13);
          break;

        case 14:                /* Page mode on */
        case 15:                /* Page mode off */
        case 16:                /* Clear graphics window */
        case 17:                /* Set text colour */
        case 18:                /* Set graphics colour */
        case 19:                /* Set palette */
        case 20:                /* Restore default colours */
        case 21:                /* Disable screen display */
          break;

        case 22:                /* Change display mode */
          //mode (vduq[1]);
          break;
        case 23:                /* Miscellaneous commands */
        case 24:                /* Define graphics window */
          break;

        case 25:                /* General plot command */
          //doplot (vduq[1], vduq[2] + 256 * vduq[3], vduq[4] + 256 * vduq[5]);
          break;

        case 26:                /* Restore default windows */
        case 27:                /* No operation */
          break;

        case 28:                /* Define text window */
          //window (vduq[4], vduq[2]);
          break;

        case 29:                /* Set graphics origin */
          break;

        case 30:                /* Home text cursor */
          //gotoxy (0, 0);
          break;
        case 31:                /* Position text cursor */
          //gotoxy (vduq[1], vduq[2]);
          break;
        }
    }
}

// SWI implementation

bool OS::SWI (unsigned int swinum, bool xbit)
{
  switch (swinum)
    {
    case OS_WriteC: WriteC (); break;
    case OS_WriteS: WriteS (); break;
    case OS_Write0: Write0 (); break;
    case OS_NewLine: NewLine (); break;
    case OS_ReadC: ReadC (); break;
    case OS_CLI: CLI (); break;
    case OS_Byte: Byte (); break;
    case OS_Word: Word (); break;
    case OS_File: File (); break;
    case OS_Args: Args (); break;
    case OS_BGet: BGet (); break;
    case OS_BPut: BPut (); break;
    case OS_GBPB: GBPB (); break;
    case OS_Find: Find (); break;
    case OS_GetEnv: GetEnv (); break;
    case OS_Exit: Exit (); break;
    case OS_IntOn: IntOn (); break;
    case OS_IntOff: IntOff (); break;
    case OS_Heap: SWI_Heap (); break;
    case OS_Module: SWI_Module (); break;
    case OS_ReadVarVal: ReadVarVal (); break;
    case OS_SetVarVal: SetVarVal (); break;
    case OS_BinaryToDecimal: BinaryToDecimal (); break;
    case OS_FSControl: FSControl (); break;
    case OS_ReadEscapeState: ReadEscapeState (); break;
    case OS_GenerateError: GenerateError (); break;
    case OS_ReadVduVariables: ReadVduVariables (); break;
    case OS_UpCall: UpCall (); break;
    case OS_ValidateAddress: ValidateAddress (); break;
    case OS_CallAfter: CallAfter (); break;
    case OS_CallEvery: CallEvery (); break;
    case OS_RemoveTickerEvent: RemoveTickerEvent (); break;
    case OS_ChangeEnvironment: ChangeEnvironment (); break;
    case OS_ReadMonotonicTime: ReadMonotonicTime (); break;
    case OS_WriteN: WriteN (); break;
    case OS_ReadMemMapInfo: ReadMemMapInfo (); break;
    case OS_ReadSysInfo: ReadSysInfo (); break;
    case OS_ReadDynamicArea: ReadDynamicArea (); break;
    case OS_ChangeRedirection: ChangeRedirection (); break;
    case OS_DynamicArea: DynamicArea (); break;
    case OS_PlatformFeatures: PlatformFeatures (); break;
    case OS_SynchroniseCodeAreas: SynchroniseCodeAreas (); break;
    case 0x70: Debug (); break;
    case OS_ConvertHex1: ConvertHex1 (); break;
    case OS_ConvertHex2: ConvertHex2 (); break;
    case OS_ConvertHex4: ConvertHex4 (); break;
    case OS_ConvertHex6: ConvertHex6 (); break;
    case OS_ConvertHex8: ConvertHex8 (); break;
    case OS_ConvertCardinal1: ConvertCardinal1 (); break;
    case OS_ConvertCardinal2: ConvertCardinal2 (); break;
    case OS_ConvertCardinal3: ConvertCardinal3 (); break;
    case OS_ConvertCardinal4: ConvertCardinal4 (); break;
    case OS_ConvertInteger1: ConvertInteger1 (); break;
    case OS_ConvertInteger2: ConvertInteger2 (); break;
    case OS_ConvertInteger3: ConvertInteger3 (); break;
    case OS_ConvertInteger4: ConvertInteger4 (); break;
    default:
      return false;
      break;
    }
  return true;
}

void OS::WriteC (void)
{
  vdu (READ_REG (0));
}

void OS::WriteS (void)
{
  char *buf = (char *) READ_REG (15) - 4;
  while (*buf)
    {
      vdu (*buf);
      buf ++;
    }
  unsigned int align = (unsigned int) buf;
  if (align & 3)
    align += 4 - (align & 3); // Realign to a 4 byte boundary
  WRITE_REG (15, align);
}

void OS::Write0 (void)
{
  char *buf = (char *) READ_REG (0);
  while (*buf)
    {
      vdu (*buf);
      buf ++;
    }
}

void OS::NewLine (void)
{
  vdu (13);
  vdu (10);
}

void OS::ReadC (void) // 0x4
{
  int32_t r0;
  if (escape_state)
    {
      SET_C ();
      r0 = 0x1b;
    }
  else
    {
      r0 = GetKey ();
      if (r0 == escape_key)
	{
	  escape_state = true;
	  SET_C ();
	  r0 = 0x1b;
	}
      else
	CLEAR_C ();
    }
  WRITE_REG (0, r0);
} 

extern uint32_t loadelf (const char *filename);

void OS::CLI (void) // 0x5
{
  char cmd[256], cmdname[256];
  char *addr = (char *) READ_REG (0);
  if (strlen (addr) >= 256)
    {
      BuildError (1, "Too long");
      return;
    }

  // Remove leading '/' since this is meant for the CLI
  strcpy (cmd, addr);

  // Get command name
  {
    int x = 0;
    char *p = cmd;
    while (*p && *p != ' ')
      cmdname[x++] = *p++;
    cmdname[x] = '\0';
    //printf ("OS_CLI: name='%s', line='%s'\n", cmdname, cmd);
  }

  if (cmd_line)
    free (cmd_line);
  cmd_line = (char *) malloc (strlen (cmd) + 1);
  if (cmd_line == NULL)
    printf ("malloc failed: %s\n", strerror (errno));
  strcpy (cmd_line, cmd);

  // At this point we should ask each module whether it can implement
  // the command.  If it cannot, then we try the disk.
  if (strcmp (cmdname, "RMEnsure") != 0)
    {
      // Before loading the new application, ensure that all existing
      // pages are writable, invalidating any active translation buffers.
      // Otherwise we can end up with random pages set readonly in the
      // new application.
      page_unprotect_range ((uint8_t *) memc->wimpslot, memc->memory_size);

#if 0
      WRITE_REG (1, (uint32_t) memc->wimpslot);
      WRITE_REG (2, memc->memory_size);
      riscos->SWI (OS_SynchroniseCodeAreas);
#endif

      // First check if it is an AIF image
      int fd = open (cmdname, O_RDONLY);
      int buf[6];
      read (fd, buf, sizeof (buf));
      close (fd);
      // Just recognise the first two instructions being MOV r0, r0
      if (buf[0] == 0xe1a00000
	  && buf[1] == 0xe1a00000)
	{
	  // Looks to be an uncompressed AIF image
	  WRITE_REG (0, 255);
	  WRITE_REG (1, (uint32_t) cmdname);
	  WRITE_REG (2, 0x8000);
	  riscos->SWI (OS_File);

	  // AIF programs always load and start at 0x8000
	  WRITE_REG (15, 0x8000);
	}
      else
	{
	  uint32_t entry = loadelf (cmdname);
	  // Set PC to new program's entry point.
	  WRITE_REG (15, entry);
	}
    }
  CLEAR_V ();
}

void OS::Byte (void) // 0x6
{
  switch (READ_REG (0))
    {
    case 2: case 3: break;
    case 124: escape_state = false; break;
    case 125: escape_state = true; break;
    case 126:
      if (escape_state)
	{
	  WRITE_REG (1, 255);
	  escape_state = false;
	}
      else
	WRITE_REG (1, 0);
    break;

    case 127: // Has end-of-file been reached
      {
	int fd = READ_REG (1);
	if (fileswitch[fd]->eof == true)
	  WRITE_REG (1, 0);
	else
	  WRITE_REG (1, 1);
      }
      break;

    case 128:
      WRITE_REG (0, 0);
      WRITE_REG (1, 0);
      WRITE_REG (2, 0);
      break;

    case 129:
      if (READ_REG (1) == 0 && READ_REG (2) == 0xff)
	{
	  // OS version identifier
	  WRITE_REG (1, 0xa4); // RISC OS version 3.11
	  WRITE_REG (2, 0);
	}

      if (READ_REG (2) < 0x80)
	{
	  if (escape_state)
	    {
	      WRITE_REG (1, 0);
	      WRITE_REG (2, 0x1b);
	    }
	  else
	    {
	      WRITE_REG (1, InKey (READ_REG (1), READ_REG (2)));
	      if (READ_REG (1) == 0xffffffff)
		WRITE_REG (2, 255);
	      else
		WRITE_REG (2, 0);
	    }
	}
      break;

    case 161: // Read CMOS RAM
      {
        int byte = READ_REG(1);

        switch (byte)
          {
            case 0: // Econet station number
              WRITE_REG (2, 0);
              break;

            default:
              printf ("OS_Byte 161: Unknown CMOS byte: %d\n", byte);
              break;
          }
      }
      break;

    case 218: WRITE_REG (1, 0); break;
    case 220:
      {
	int a = escape_key;
	escape_key = (escape_key & READ_REG (2)) ^ READ_REG (1);
	WRITE_REG (1, a);
      }
      break;

    case 229: break;
    default:
      fprintf (stderr, "Unknown OS_byte: r0:%d r1:%d r2:%d\n",
	       READ_REG (0), READ_REG (1), READ_REG (2));
      break;
    }
}

void OS::Word (void) // 0x7
{
  if (READ_REG (0) == 1)
    {
      // Read system clock
      uint32_t cl = (uint32_t) clock ();
      unsigned char *addr = (unsigned char *) READ_REG (1);
      addr[0] = (cl & 0x000000ff);
      addr[1] = (cl & 0x0000ff00) >> 8;
      addr[2] = (cl & 0x00ff0000) >> 16;
      addr[3] = (cl & 0xff000000) >> 24;
      addr[4] = 0;
    }
  else if (READ_REG (0) == 14)
    {
      unsigned char *addr = (unsigned char *) READ_REG (1);
      if (addr[0] == 3)
	{
	  unsigned int high, low;
	  cvt_unix_time (time (0), &high, &low);
	  addr[0] = (low & 0x000000ff);
	  addr[1] = (low & 0x0000ff00) >> 8;
	  addr[2] = (low & 0x00ff0000) >> 16;
	  addr[3] = (low & 0xff000000) >> 24;
	  addr[4] = high & 0xff;
	}
    }
  else
    printf ("OS_Word: Unimplemented reason code %d\n", READ_REG (0));
}

void OS::File (void) // 0x08
{
  uint32_t reason_code = READ_REG (0);
  char *fname = (char *) READ_REG (1);

  switch (reason_code)
    {
    case 0: case 10: // Save a block of memory as a file
      {
	FILE *file = fopen (fname, "w");
	if (file == NULL)
	  BuildError (1, "Can't open file for writing");
	else
	  {
	    uint32_t size = READ_REG (5) - READ_REG (4);
	    char *buffer = (char *) READ_REG (4);
	    fwrite (buffer, size, sizeof (char), file);
	    fclose (file);
	  }
      }
      break;

    case 1: case 2: case 3: case 4: case 9:
    case 18: // Write catalogue information
      {
	FileHandle f;
	f.SetFile (fname);
	int status = 0;

	if (reason_code == 1 || reason_code == 2)
	  f.load_address = READ_REG (2);
	else if (reason_code == 18)
	  {
	    status = f.SetType (READ_REG (2));
	  }
	if (reason_code == 1 || reason_code == 3)
	  f.exec_address = READ_REG (3);
	if (reason_code == 1 || reason_code == 4)
	  {
	    f.attributes = READ_REG (5);
	    status = f.SetAttributes ();
	  }

	if (status)
	  BuildError (1, strerror (errno));
      }     
      break;
	
    case 5: case 13: case 15: case 17: // Read catalogue information
      {
	FileHandle info;
	info.SetFile (fname);
	WRITE_REG (0, info.object_type);
	if (info.object_type != 0)
	  {
	    WRITE_REG (2, info.load_address);
	    WRITE_REG (3, info.exec_address);
	    WRITE_REG (4, info.object_length); // R4 = file size
	    WRITE_REG (5, info.attributes);
	  }
      }
      break;

    case 6: // Delete a named object
      {
	FileHandle info;
	info.SetFile (fname);
	WRITE_REG (0, info.object_type);
	if (info.object_type != 0)
	  {
	    WRITE_REG (2, info.load_address);
	    WRITE_REG (3, info.exec_address);
	    WRITE_REG (4, info.object_length); // R4 = file size
	    WRITE_REG (5, info.attributes);
	    if (info.object_type == 1)
	      {
		if (remove (info.canon_name))
		  BuildError (1, strerror (errno));
	      }
	    else if (info.object_type == 2)
	      {
		if (rmdir (info.canon_name))
		  BuildError (1, strerror (errno));
	      }
	  }
      }
      break;

    case 7: case 11: // Create an empty file
      {
	FILE *handle = fopen (fname, "w");
	if (! handle)
	  BuildError (2, "Could not create file for writing");
	else
	  {
	    int length = READ_REG (5) - READ_REG (4);
	    while (length > 0)
	      {
		fputc (0, handle);
		length --;
	      }
	    fclose (handle);
	  }
      }
      break;

    case 8: // Create a directory
      {
	if (mkdir (fname, 0755))
	  BuildError (2, "Cannot create directory");
      }
      break;

    case 12: case 14: case 16: case 255: // Load a named file
      {
	FileHandle info;
	info.SetFile (fname);
	if (info.object_type == 0)
	  BuildError (2, "Can't open file for reading");
	else
	  {
	    int fd = open (info.canon_name, O_RDONLY);
	    void *buffer = (void *) READ_REG (2);
	    read (fd, buffer, info.object_length);
	    close (fd);

	    WRITE_REG (2, info.load_address);
	    WRITE_REG (3, info.exec_address);
	    WRITE_REG (4, info.object_length); // R4 = file size
	    WRITE_REG (5, info.attributes);
	  }
      }
      break;


    default:
      printf ("OS_File: unimplemented reason code %d\n", reason_code);
      break;
    }
}

void OS::Args (void) // 0x09
{
  unsigned int reason_code = READ_REG (0);
  int handle = READ_REG (1);
  if (! fileswitch.ValidFile (handle))
    {
      BuildError (1, "Invalid file handle");
      return;
    }
  switch (reason_code)
    {
    case 0: // Read the temporary file system number or a file's sequential ptr
      if (handle == 0)
	WRITE_REG (0, 0); // RISCiXFS
      else
	WRITE_REG (2, fileswitch[handle]->Offset ());
      break;
    case 1: // Write an open file's sequential file pointer
      fileswitch.Seek (handle, READ_REG (2));
      break;
    case 2: // Read an open file's extent
      WRITE_REG (2, fileswitch[handle]->Extent ());
      break;
    case 3: // Write an open file's extent
      fileswitch[handle]->SetExtent (READ_REG (2));
      break;
    case 5: // Read an open file's end-of-file status
      if (fileswitch[handle]->eof == true)
	WRITE_REG (2, 1);
      else
	WRITE_REG (2, 0);
      break;
    case 7: // Convert a file handle to a canonicalised name
      {
	unsigned int bufsiz = READ_REG (5);
	char *dstbuf = (char *) READ_REG (2);
	if (dstbuf != NULL)
	  {
	    const char *p = fileswitch[handle]->canon_name;
	    while (*p && bufsiz > 0)
	      {
		*dstbuf ++ = *p ++;
		bufsiz --;
	      }
	    *dstbuf = '\0';
	    WRITE_REG (5, bufsiz);
	  }
	else
	  {
	    WRITE_REG (5, -strlen (fileswitch[handle]->canon_name));
	  }
      }
      break;
    case 254: // Reads information on a file handle.
      if (fileswitch[handle] == NULL)
	WRITE_REG (0, (1 << 11)); // Stream is unallocated
      else
	{
	  int result = fileswitch[handle]->Info ();
	  WRITE_REG (0, result);
	  
	  int info = 0;
	  info |= (1 << 28) | (1 << 27) | (1 << 26)
	    | (1 << 25) | (254 << 8);
	  WRITE_REG (2, info); // Filing system information word
	}
      break;
    case 255: // Flush data to file
      fileswitch.Flush (handle);
      break;
    default:
      printf ("OS_Args: Unimplemented reason code %d\n", reason_code);
      break;
    }
}

void OS::BGet (void) // 0x0a
{
  int handle = READ_REG (1);
  if (! fileswitch.ValidFile (handle))
    {
      BuildError (1, "Invalid file handle");
      return;
    }

  if (fileswitch[handle]->eof == true)
    {
      // If the error-on-next-read flag is set on entry, then return
      // an error.
      BuildError (1, "End of file");
    }

  unsigned char c;
  if (fileswitch[handle]->Read (&c, 1) == -1)
    SET_C ();
  else
    CLEAR_C ();
  WRITE_REG (0, c);
}

void OS::BPut (void) // 0x0b
{
  int handle = READ_REG (1);
  if (! fileswitch.ValidFile (handle))
    {
      BuildError (1, "Invalid file handle");
      return;
    }
  unsigned char c = (unsigned char) (READ_REG (0) & 0xff);
  if (fileswitch[handle]->Write (&c, 1) == -1)
    BuildError (1, strerror (errno));
}

void OS::GBPB (void) // 0x0c
{
  unsigned int reason_code = READ_REG (0);

  // These reason codes are for reading or writing bytes to an open file.
  if (reason_code >= 1 && reason_code <= 4)
    {
      off_t fp = (off_t) READ_REG (4);
      int handle = READ_REG (1);
      if (! fileswitch.ValidFile (handle))
	{
	  BuildError (1, "Invalid file handle");
	  return;
	}
      //printf ("OS_GBPB: reason=%d handle=%d addr=%08x bytes=%d\n",
      //	      reason_code, handle, READ_REG (2), READ_REG (3));

      // For these reason codes, the user can specify a new
      // file offset.  Update our internal version.  For a small
      // optimisation, only perform this task if the new offset is
      // actually different from the old.
      if (reason_code == 1 || reason_code == 3)
	{
	  int s = fileswitch.Seek (handle, fp);

	  // For read operations, if the specified pointer is larger
	  // than the current file extent, then no bytes are read and
	  // the sequential pointer is not updated.
	  if (reason_code == 3 && s == 1)
	    return;

	  // Increment reason_code to reduce the complexity of comparisons
	  // later.
	  reason_code ++;
	}

      // The buffer to read or write data to.
      char *buffer = (char *) READ_REG (2);

      // The number of bytes to read or write to that buffer.
      uint32_t bytes = (uint32_t) READ_REG (3);

      // The number of bytes that were actually read or written.
      ssize_t c;
      if (reason_code == 2)
	c = fileswitch.Write (handle, buffer, bytes);
      else
	{
	  c = fileswitch.Read (handle, buffer, bytes);
	  if (c == -1)
	    {
	      SET_C ();
	      return;
	    }
	}
      WRITE_REG (2, (uint32_t) buffer + c);
      WRITE_REG (3, bytes - c);
      WRITE_REG (4, fileswitch[handle]->Offset ());

      //      printf ("OS_GBPB: buffer=%p bytes=%d c=%d R4=%d\n",
      //	      buffer, bytes, c, READ_REG (4));
      
      // Write operations always clear the carry flag
      CLEAR_C ();

      //printf ("bytes = %d, c = %d, reason_code = %d\n", bytes, c, reason_code);
      // However for read operations we will set the carry flag when
      // we have failed to read the required amount.
      if (reason_code == 4 && bytes - c != 0)
	SET_C ();
    }
  else if (reason_code >= 9 && reason_code <= 12)
    {
      // Read entries and file information from a specified directory
      const char *dirname = (const char *) READ_REG (1);
      char *buffer = (char *) READ_REG (2);
      int bufsiz = READ_REG (5); // Buffer size
      int numobjs = READ_REG (3); // Number of objects to read
      int offset = READ_REG (4); // Offset of first item;

      if (offset == -1)
	{
	  WRITE_REG (3, 0);
	  SET_C ();
	  if (dir)
	    delete dir;
	  return;
	}
      //printf ("OS_GBPB: bufsiz=%d, numobjs=%d, offset=%d\n",
      //bufsiz, numobjs, offset);

      if (offset == 0)
	{
	  // New directory read.  Delete old cached dir and instantiate
	  // a new dir.
	  if (dir)
	    delete dir;
	  dir = new OSDir (dirname);
	}

      int total_objs = dir->Count ();
      //printf ("OS_GBPB: total objs=%d\n", total_objs);
      
      int x;
      for (x = 0; x < numobjs && (x + offset) < total_objs; x++)
	{
	  FileHandle *entry = dir->GetEntry (x + offset);

	  if (reason_code == 9)
	    {
	      // Here we only write out filenames, terminated with a null
	      // char.

	      // Check if we have enough space to store name
	      if (bufsiz < entry->object_name_len + 1)
		break; // No

	      strcpy (buffer, entry->object_name);

	      // Point to next position to store name
	      buffer += entry->object_name_len + 1;
	      bufsiz -= entry->object_name_len + 1;
	    }
	  else
	    {
	      // The other reason codes
	      if (bufsiz < 29 + entry->object_name_len + 1)
		break; // Not enough space.

	      int *b = (int *) buffer;
	      b[0] = entry->load_address;
	      b[1] = entry->exec_address;
	      b[2] = entry->object_length;
	      b[3] = entry->attributes;
	      b[4] = entry->object_type;
	      if (reason_code == 10)
		{
		  strcpy (buffer + 20, entry->object_name);
		  buffer += 20;
		}
	      else if (reason_code == 11)
		{
		  b[5] = 0; // System internal name
		  memcpy (buffer + 24, entry->rotime, 5);
		  strcpy (buffer + 29, entry->object_name);
		  buffer += 29;
		}
	      else if (reason_code == 12)
		{
		  b[5] = entry->object_type;
		  strcpy (buffer + 24, entry->object_name);
		  buffer += 24;
		}
	      buffer += entry->object_name_len + 1;
	      // Word align buffer
	      if ((unsigned int) buffer & 3)
		buffer += 4 - ((unsigned int) buffer & 3);

	      bufsiz -= (unsigned int) buffer - (unsigned int) b;
	    }
	}

      //printf ("OS_GBPB: items read (r3)=%d, r4=%d, numobjs=%d\n",
      //	      x, x + offset, numobjs);
      WRITE_REG (3, x); // Number of items read
      if ((x + offset) < total_objs)
	{
	  // There was not enough space to store all data
	  WRITE_REG (4, x + offset); // Value to use on next call
	}
      else
	{
	  WRITE_REG (4, 0xffffffff); // Indicate that reading is finished
	  delete dir; // Free cached directory
	  dir = NULL;
	}

      // C flag is clear if R3 == 0
      if (x == 0)
	CLEAR_C ();
      else
	SET_C ();
    }
  else
    fprintf (stderr, "OS_GBPB: Unimplemented reason code %d\n", reason_code);

}

void OS::Find (void) // 0xd
{
  unsigned int reason_code = READ_REG (0);
  if (reason_code == 0) // Close one or all open files
    {
      unsigned int handle = READ_REG (1);
      fileswitch.Close (handle);
      return;
    }

  char *fname = (char *) READ_REG (1);

  reason_code &= 0xf0;
  int flags = 0;
  int handle, err = 0;
  if (reason_code == 0x40)
    err = fileswitch.Open (fname, 0, handle);
  else if (reason_code == 0x80)
    err = fileswitch.Open (fname, 1, handle);
  else if (reason_code == 0xc0)
    err = fileswitch.Open (fname, 2, handle);

  //printf ("OS_Find: reason=%08x, fname='%s', handle=%d, err=%d, length=%d\n",
  //	    reason_code, fname, handle, err,
  //	    fileswitch[handle]->Extent ());

  if (err)
    {
      if (errno == ENOENT)
	{
	  WRITE_REG (0, 0);
	  SET_V ();
	}
      else
	BuildError (2, "File not found");
    }
  else
    {
      WRITE_REG (0, handle);
    }
}

void OS::GetEnv (void) // 0x10
{
  char *addr = (char *) ((uint32_t) pmem + 0x10);
  strcpy (addr, cmd_line);
  
  WRITE_REG (0, (target_addr_t) addr);  // Pointer to program command line
  WRITE_REG (1, environment[ENVH_MEMORYLIMIT][0]); // Memory limit

  //printf ("OS_GetEnv: memlimit=%08x\n", environment[ENVH_MEMORYLIMIT][0]);

  // Write the real time for program start
  unsigned int high, low;
  addr = (char *) pmem;
  cvt_unix_time (time (0), &high, &low);
  addr[0] = (low & 0x000000ff);
  addr[1] = (low & 0x0000ff00) >> 8;
  addr[2] = (low & 0x00ff0000) >> 16;
  addr[3] = (low & 0xff000000) >> 24;
  addr[4] = high & 0xff;
  WRITE_REG (2, (target_addr_t) addr);
}

void OS::Exit (void) // 0x11
{
  //printf ("OS_Exit called: r0=%08x, r1=%08x, r2=%08x\n",
  //	  READ_REG (0), READ_REG (1), READ_REG (2));
  int return_code = READ_REG (2);
  if (environment[ENVH_EXIT][0])
    {
      // If all user exit handlers have been restored, then the main
      // process is terminating.  Exit the emulator
      if (environment[ENVH_EXIT][0] == ENV_EXIT)
	throw return_code;

      if (READ_REG (1) == 0x58454241) // ABEX
	{
	  // UnixLib expects that the return code
	  // should be a string.
	  char buf[32];
	  sprintf (buf, "%d", return_code);
	  SetEnvironment ("Sys$ReturnCode", buf);
	}

      WRITE_REG (15, environment[ENVH_EXIT][0]);
      WRITE_REG (12, environment[ENVH_EXIT][1]);
      //printf ("OS_Exit: starting execution at %08x\n",
      //READ_REG (PC_AHEAD_IND));
    }
  else
    throw return_code;
}

void OS::IntOn (void) // 0x13
{
  //interrupt_flag = false;
}

void OS::IntOff (void) // 0x14
{
  //interrupt_flag = true;
}

void OS::SWI_Heap (void) // 0x1d PRM1-368
{
  int reason_code = READ_REG (0);
  void *ptr = (void *) READ_REG (1); // Pointer to heap

  if (reason_code == 0)
    {
      // Initialise the heap
      Heap *h = new Heap ();
      uint32_t size = READ_REG (3);
      h->Initialise (ptr, size);
      user_heaps.push_back (h);
      return;
    }

  Heap *h = FindHeap (ptr);
  if (! h)
    {
      BuildError (1, "Heap does not exist");
      return;
    }

  if (reason_code == 1)
    {
      // Describe heap
      uint32_t maxsize, free;
      h->Describe (maxsize, free);
      WRITE_REG (2, maxsize);
      WRITE_REG (3, free);
    }
  else if (reason_code == 2)
    {
      // Get heap block
      uint32_t size = READ_REG (3);
      void *bptr = h->Alloc (size);
      WRITE_REG (2, (uint32_t) bptr);
    }
  else if (reason_code == 3)
    {
      // Free heap block
      void *bptr = (void *) READ_REG (2);
      h->Free (bptr);
    }
  else if (reason_code == 4)
    {
      // Extend or shrink heap block
      void *bptr = (void *) READ_REG (2);
      uint32_t newsize = READ_REG (3);

      // By extending the block, the pointer is allowed to move.
      // The client application should account for this.
      bptr = h->Extend (bptr, newsize);
      WRITE_REG (2, (uint32_t) bptr);
    }
  else if (reason_code == 6)
    {
      // Read block size.
      void *bptr = (void *) READ_REG (2);

      // Read the size of a block in the heap, we return the size
      // including any internal overhead.
      uint32_t bsize = h->BlockSize (bptr);
      WRITE_REG (3, bsize);
    }
  else
    {
      printf ("OS_Heap: unimplemented %d\n", reason_code);
    }
}

void OS::SWI_Module (void) // 0x1e PRM1-224
{
  int reason_code = READ_REG (0);
  switch (reason_code)
    {
    case 0: // Run
    case 1: // Load
      {
	const char *path = (const char *) READ_REG (1);
	FileHandle h;
	h.SetFile (path);
	if (h.object_type == 0)
	  BuildError (1, "File not found");
	else
	  {
	    // Allocate space for the RMA module
	    void *ptr = memc->rma_heap->Alloc (h.object_length);
	    // Read RMA module into memory
	    int fd = open (h.canon_name, O_RDONLY);
	    read (fd, ptr, h.object_length);
	    close (fd);

	    //printf ("OS_Module: loaded '%s' at %08x\n", h.canon_name, ptr);

	    riscos->InitModule ((uint32_t *) ptr, h.object_length);

	    // Call the module initialisation routine
	    WRITE_REG (10, (uint32_t) path);
	    // I/O base or instantiation number.  Indicate that the
	    // module was loaded from the filing system or ROM or is
	    // already in memory.
	    WRITE_REG (11, 0);
	    riscos->ModuleCall (arm, (uint32_t *) ptr, 1);

	    if (reason_code == 0)
	      {
		// Enter the module
		WRITE_REG (0, (uint32_t) path);
		riscos->ModuleCall (arm, (uint32_t *) ptr, 0);
	      }
	  }
      }
      break;
    case 5: // Describe
      {
	uint32_t largest, total_free;
	memc->rma_heap->Describe (largest, total_free);
	WRITE_REG (2, largest);
	WRITE_REG (3, total_free);
      }
      break;
    case 6: // Claim
      {
	uint32_t size = READ_REG (3);
	void *ptr = memc->rma_heap->Alloc (size);
	if (ptr == NULL)
	  BuildError (1, "No room in RMA");
	else
	  WRITE_REG (2, (uint32_t) ptr);
      }
      break;
    case 7: // Free
      {
	void *ptr = (void *) READ_REG (2);
	memc->rma_heap->Free (ptr);
      }
      break;
    case 10: // Insert module from memory
      {
	uint32_t *ptr = (uint32_t *) READ_REG (1);
	// The word immediately before the module start contains
	// the module length in bytes
	uint32_t size = ptr[-1];
	riscos->InitModule (ptr, size);
      }
      break;
    case 13: // Extend block
      {
	void *ptr = (void *) READ_REG (2);
	uint32_t size = READ_REG (3);
	ptr = memc->rma_heap->Extend (ptr, size);
	if (ptr == NULL)
	  BuildError (1, "No room in RMA");
	else
	  WRITE_REG (2, (uint32_t) ptr);
      }
      break;
    default:
      printf ("OS_Module: unimplemented %d\n", reason_code);
      break;
    }
}

void OS::ReadVarVal (void) // 0x23
{
  // R0 = pointer to variable name
  // R1 = Pointer to buffer to hold variable name
  // R2 = maximum length of buffer
  // R3 = context pointer, or 0
  // R4 = 3 if an expanded string is to be converted
  char *buf = (char *) READ_REG (0);
  int r4 = READ_REG (4);
  vector<env_vars_t>::iterator x;

  //printf ("OS_ReadVarVal: '%s', r1 = %x, r2 = %x\n", buf,
  //	  READ_REG (1), READ_REG (2));

  // Search for a matching environment variable name
  bool found = false;
  for (x = environ_vars.begin (); x != environ_vars.end (); x++)
    if (strcmp (x->name, buf) == 0)
      {
	found = true;
	break;
      }

  if (! found)
    {
      //printf ("OS_ReadVarVal: '%s' does not exist\n", buf);
      WRITE_REG (2, 0); // Variable does not exist
      BuildError (1, "Environment variable does not exist.");
      return;
    }

  // Variable does exist.
  if (r4 == 1 && x->type == 1)
    {
      unsigned int *addr = (unsigned int *) READ_REG (1);
      *addr = x->int_value;
      return;
    }

  // Variable type is a string.
  WRITE_REG (4, 0);
  if (READ_REG (2) & (1 << 31))
    {
      // Just return the length it is bitwise NOT the length
      // of the string.
      WRITE_REG (2, ~strlen (x->str_value));
    }
  else
    {
      if (READ_REG (2) < strlen (x->str_value))
	/* Not enough space for the variable.  */
	BuildError (1, "Buffer too small");
      else
	/* Place the environment variable into the buffer
	   pointed to by R1.  */
	strcpy ((char *) READ_REG (1), x->str_value);
    }
}

void OS::SetVarVal (void) // 0x24
{
  unsigned int r4 = READ_REG (4);
  env_vars_t var;
  char *envvar = (char *) READ_REG (0);

  //  printf ("OS_SetVarVal: r0=%08x r1=%08x r2=%08x r3=%08x r4=%08x, var='%s'\n",
  //	  READ_REG (0), READ_REG (1), READ_REG (2), READ_REG (3),
  //	  READ_REG (4), envvar);

  // If top bit of R2 is set, then we just delete the environment
  // variable.
  if (READ_REG (2) & (1 << 31))
    {
      DelEnvironment (envvar);
      return;
    }

  var.name = strdup (envvar);
  var.str_value = NULL;
  var.type = r4;
  if (r4 == 0 || r4 == 4)
    {
      // Variable type: string or literal string
      char *val = (char *) READ_REG (1);
      var.str_value = strdup (val);
    }
  else if (r4 == 1)
    {
      // Variable type is a number.
      uint32_t *addr = (uint32_t *) READ_REG (1);
      var.int_value = *addr;
    }

  // Replace the environement variable, if it already existed.
  DelEnvironment (var.name);
  environ_vars.push_back (var);
}

void OS::BinaryToDecimal (void) // 0x28 
{
  char *buf = (char *) READ_REG (1);
  if (buf == NULL)
    BuildError (2, "Bad buffer");
  else
    {
      int buflen = READ_REG (2);
      int len = snprintf (buf, buflen, "%d", READ_REG (0)) + 1;
      if (len > buflen)
	BuildError (2, "Buffer overflow");
      else
	WRITE_REG (2, len);
    }
}

void OS::FSControl (void) // 0x29
{
  unsigned int reason_code = READ_REG (0);
  char *fname = (char *) READ_REG (1);

  switch (reason_code)
    {
    case 0: // Set the current directory
      if (chdir (fname))
	BuildError (1, strerror (errno));
      break;
    case 25: // Rename object
      {
	char *fname1 = (char *) READ_REG (2);
	if (rename (fname, fname1))
	  BuildError (1, strerror (errno));
      }
    case 33: // Convert a filing system number to a name
      {
	char *buf = (char *) READ_REG (2);
	strcpy (buf, "ADFS");
      }
      break;
    case 37: // Convert a pathname to a canonicalised name
      {
	FileHandle f;

	// Quick hack to convert RISC OS's @ to a .
	// Should really do something to convert all RISC OS names.
	if (*fname == '@' && fname[1] == '\0')
	  fname = ".";
	else
	  {
	    f.SetFile (fname);
	    if (f.object_type != 0)
	      fname = (char *) f.canon_name;
	  }

	unsigned int bufsiz = READ_REG (5);
	char *dstbuf = (char *) READ_REG (2);
	if (dstbuf != NULL)
	  {
	    const char *p = fname;
	    while (*p && bufsiz > 0)
	      {
		*dstbuf ++ = *p ++;
		bufsiz --;
	      }
	    *dstbuf = '\0';
	    WRITE_REG (5, bufsiz);
	  }
	else
	  {
	    WRITE_REG (5, -strlen (fname));
	  }
      }
      break;

    default:
      fprintf (stderr,
	       "OS_FSControl: unimplemented reason code %d\n", reason_code);
      break;
    }
}

void OS::ReadEscapeState (void)
{
  if (escape_state)
    SET_C ();
  else
    CLEAR_C ();
}

void OS::GenerateError (void) // 0x2b
{
  SET_V ();
#if 0
  if (xbit)
    SET_V ();
  else
    {
      char *ptr = (char *) (READ_REG (0) + 4);
      printf ("OS_GenerateError: error='%s'\n", ptr);
      throw (1);
    }
#endif
}

void OS::ReadVduVariables (void) // 0x31
{
  int *input = (int *) READ_REG (0);
  int *output = (int *) READ_REG (1);
  while (*input != -1)
    {
      int var = *input;
      switch (var)
	{
	case 128: /* Left hand column of the graphics window */
	  *output ++ = 0;
	  break;
	case 129: /* Bottom row of the graphics window */
	  *output ++ = 0;
	  break;
	case 130: /* Right hand column of the graphics window */
	  *output ++ = 1280;
	  break;
	case 131: /* Top row of the graphics window */
	  *output ++ = 1024;
	  break;
	case 132: /* Left hand column of the text window */
	  *output ++ = 0;
	  break;
	case 133: /* Bottom row of the text window */
	  *output ++ = 25;
	  break;
	case 134: /* Right hand column of the text window */
	  *output ++ = 80;
	  break;
	case 135: /* Top row of the text window */
	  *output ++ = 0;
	  break;
	case 155: /* Text foreground colour.  */
	  *output ++ = 7;
	  break;
	case 156: /* Text background colour.  */
	  *output ++ = 0;
	  break;
	default:
	  printf ("OS_ReadVduVariables: unsupported code %d\n", var);
	  break;
	}
      input ++;
    }
}

void OS::UpCall (void) // 0x33
{
  unsigned int reason = READ_REG (0);

  switch (reason)
    {
    case 6: /* TaskWindow sleep */
      break;

    default:
      printf ("OS_UpCall: unhandled reason code: %d\n", reason);
    }
}


void OS::ValidateAddress (void) // 0x3a PRM1-379
{
  // R0 = minimum addr
  // R1 = maximum addr
  target_addr_t low, high;
  low = READ_REG (0);
  high = READ_REG (1);
  if (low > environment[ENVH_MEMORYLIMIT][0]
      || high > environment[ENVH_MEMORYLIMIT][0])
    SET_C ();
  else
    CLEAR_C ();
}

void OS::CallAfter (void) // 0x3b
{
  ticker_event_t t;
  t.call_time = clock () + READ_REG (0);
  t.call_address = READ_REG (1);
  t.r12 = READ_REG (2);
  t.delay = 0;
  tickers.push_back (t);
}

void OS::CallEvery (void) // 0x3c
{
  ticker_event_t t;
  t.call_time = 0;
  t.call_address = READ_REG (1);
  t.r12 = READ_REG (2);
  t.delay = READ_REG (0);
  tickers.push_back (t);
}

void OS::RemoveTickerEvent (void) // 0x3d
{
  target_addr_t addr = READ_REG (0);
  uint32_t r12 = READ_REG (1);
  for (vector<ticker_event_t>::iterator x = tickers.begin ();
       x != tickers.end (); x++)
    if (x->call_address == addr && x->r12 == r12)
      {
	tickers.erase (x);
	break;
      }
}

void OS::ChangeEnvironment (void) // 0x40
{
  uint32_t r0, r1, r2, r3, a, b, c;
  r0 = READ_REG (0);
  r1 = READ_REG (1);
  r2 = READ_REG (2);
  r3 = READ_REG (3);
  a = environment[r0][0];
  b = environment[r0][1];
  c = environment[r0][2];

  if (r1 != 0)
    environment[r0][0] = r1;
  if (r2 != 0)
    environment[r0][1] = r2;
  if (r3 != 0)
    environment[r0][2] = r3;
  WRITE_REG (1, a);
  WRITE_REG (2, b);
  WRITE_REG (3, c);
}

void OS::ReadMonotonicTime (void) // 0x42
{
  // Convert the value returned by clock into centiseconds.
  struct timeval tv, tv1;

  gettimeofday (&tv, NULL);
  timersub (&tv, &os_start_time, &tv1);

  WRITE_REG (0, (tv1.tv_sec * 100) + (tv1.tv_usec / 10000));
  //printf ("MonotonicTime: sec=%d, usec=%d, r0=%d\n",
  //	  tv1.tv_sec, tv1.tv_usec, READ_REG (0));
}

void OS::WriteN (void) // 0x46
{
  char *addr = (char *) READ_REG (0);
  unsigned int count = READ_REG (1);
  while (count--)
    {
      vdu (*addr);
      addr ++;
    }
}

void OS::ReadMemMapInfo (void) // 0x51
{
  WRITE_REG (0, 4096); // 4K pages
  WRITE_REG (1, (environment[ENVH_MEMORYLIMIT][0] / 4096));
}

void OS::ReadSysInfo (void) // 0x58 - PRM1-719
{
  int reason_code = READ_REG (0);
  switch (reason_code)
    {
    case 0: // Read the configured screen size in bytes
      WRITE_REG (0, memc->memory_map[MEMC_CHUNK_SCREEN].size);
      break;
    case 1: // Read the configured mode/wimpmode, monitortype and sync
      WRITE_REG (0, 21); // Mode
      WRITE_REG (1, 1); // MonitorType
      WRITE_REG (2, 0); // Sync
      break;
    case 2: // Read the presence of various chips and machine ID
      WRITE_REG (0, 0); // No IOEB ASIC
      WRITE_REG (1, 0); // No 82C710
      WRITE_REG (2, 0); // NO LCD ASIC
      WRITE_REG (3, 0); // No machine ID
      WRITE_REG (4, 0); // No machine ID
      break;
    default:
      printf ("OS_ReadSysInfo: unimplemented code %d\n", reason_code);
      break;
    }
}

void OS::ReadDynamicArea (void) // 0x5c
{
  uint32_t r0 = READ_REG (0);
  int reason_code;
  uint32_t area_start;
  uint32_t area_size;
  uint32_t max_size;

  // Read the space allocation of a dynamic area
  // R0 = area to read

  if (r0 == 0xffffffff)
    reason_code = -1;
  else
    reason_code = r0 & 0x8f;
  if (reason_code == -1)
    {
      area_start = 0x8000;
      area_size = environment[ENVH_MEMORYLIMIT][0];

      // For RISC OS, the max size should be 0x1bf8000.  As
      // we currently do not support Dynamic Areas, we set this much
      // higher.
      max_size = environment[ENVH_MEMORYLIMIT][0];
      //printf ("OS_ReadDynamicArea: area_start=%08x, max_size=%08x\n",
      //	      area_start, max_size);
    }
  else
    {
      int m = 0;
      // Translate a dynamic area number into a MEMC number
      if (reason_code == 0) { m = MEMC_CHUNK_SYSTEM; }
      else if (reason_code == 1) { m = MEMC_CHUNK_RMA; }
      else if (reason_code == 2) { m = MEMC_CHUNK_SCREEN; }
      else if (reason_code == 3) { m = MEMC_CHUNK_SPRITE; }
      else if (reason_code == 4) { m = MEMC_CHUNK_FONT; }
      else if (reason_code == 5) { m = MEMC_CHUNK_RAMFS; }
      else if (reason_code == 6) { m = MEMC_CHUNK_FREE; }
      else
	{
	  printf ("OS_ReadDynamicArea: unknown: r0 = %d\n", r0);
	  SET_V ();
	  return;
	}

      area_start = (uint32_t) memc->memory_map[m].base;
      area_size = (uint32_t) memc->memory_map[m].size;
      max_size = (uint32_t) memc->memory_map[m].maxsize;
    }

  WRITE_REG (0, area_start);
  WRITE_REG (1, area_size);
  if (r0 & (1 << 7))
    WRITE_REG (2, max_size);
  CLEAR_V ();
}

void OS::ChangeRedirection (void) // 0x5e PRM 1-931
{
  // Currently we say that no file handles are redirected.
  WRITE_REG (0, 0);
  WRITE_REG (1, 0);
}

void OS::DynamicArea (void) // 0x66
{
  // For the time being, say that we do not support dynamic areas.
  // But because I do not know the SWI call details.
  SET_V ();
}

void OS::PlatformFeatures (void) // 0x6d
{
  if (READ_REG (0) == 0)
    WRITE_REG (0, 0);
}


extern "C" void tb_invalidate_page_range(target_ulong start, target_ulong end);
void OS::SynchroniseCodeAreas (void) // 0x6e
{
  target_ulong start = READ_REG (1);
  target_ulong last = READ_REG (2);
  target_ulong addr, last1;

  if (last < start)
    return;
  addr = start;
  for(;;)
    {
      last1 = ((addr + TARGET_PAGE_SIZE) & TARGET_PAGE_MASK) - 1;
      if (last1 > last)
	last1 = last;
      tb_invalidate_page_range(addr, last1 + 1);
      if (last1 == last)
	break;
      addr = last1 + 1;
    }
}

void OS::Debug (void) // 0x70
{
  unsigned int *ip = (unsigned int *) READ_REG (12);
  printf ("OS_Debug: r0=%08x, r1=%08x, r2=%08x, ip=%08x, ip[72]=%08x\n",
	  READ_REG (0), READ_REG (1), READ_REG (2), ip, ip[18]);
}


inline void OS::ConvertNameNumber (unsigned int val, const char *fmt)
{
  char *buf = (char *) READ_REG (1);
  if (buf == NULL)
    BuildError (2, "Bad buffer");
  else
    {
      int bufsize = READ_REG (2);
      int len = snprintf (buf, bufsize, fmt, val) + 1;
      WRITE_REG (0, (uint32_t) buf);
      WRITE_REG (1, (uint32_t) buf + len);
      WRITE_REG (2, bufsize - len);
    }
}

void OS::ConvertHex1 (void) // 0xd0
{
  ConvertNameNumber (READ_REG (0) & 0xf, "%01X");
}

void OS::ConvertHex2 (void) // 0xd1
{
  ConvertNameNumber (READ_REG (0) & 0xff, "%02X");
}

void OS::ConvertHex4 (void) // 0xd2
{
  ConvertNameNumber (READ_REG (0) & 0xffff, "%04X");
}

void OS::ConvertHex6 (void) // 0xd3
{
  ConvertNameNumber (READ_REG (0) & 0xffffff, "%06X");
}

void OS::ConvertHex8 (void) // 0xd4
{
  ConvertNameNumber (READ_REG (0), "%08X");
}

void OS::ConvertCardinal1 (void) // 0xd5
{
  ConvertNameNumber (READ_REG (0) & 0xff, "%u");
}

void OS::ConvertCardinal2 (void) // 0xd6
{
  ConvertNameNumber (READ_REG (0) & 0xffff, "%u");
}

void OS::ConvertCardinal3 (void) // 0xd7
{
  ConvertNameNumber (READ_REG (0) & 0xffffff, "%u");
}

void OS::ConvertCardinal4 (void) // 0xd8
{
  ConvertNameNumber (READ_REG (0), "%u");
}

void OS::ConvertInteger1 (void) // 0xd9
{
  ConvertNameNumber (READ_REG (0) & 0xff, "%d");
}

void OS::ConvertInteger2 (void) // 0xda
{
  ConvertNameNumber (READ_REG (0) & 0xffff, "%d");
}

void OS::ConvertInteger3 (void) // 0xdb
{
  ConvertNameNumber (READ_REG (0) & 0xffffff, "%d");
}

void OS::ConvertInteger4 (void) // 0xdc
{
  ConvertNameNumber (READ_REG (0), "%d");
}
