/*  newfs.c  */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "kernel.h"
#include "newfs.h"
#include "swis.h"


/***************************************************************************/

#if TRACE_UPCALLS

extern void UpCallRout(void);
extern int NewFS_UpCall(_kernel_swi_regs *r);

static BOOL inside_upcall = FALSE;

static int private_private;

static int missed = 0;
static int miss_r0[20];
static int miss_r9[20];

static char mess[256];

#endif


/***************************************************************************/

#if TRACE_UPCALLS

int NewFS_UpCall(_kernel_swi_regs *r)

{
  if (!inside_upcall /* &&
      !(r->r[0]==3 && r->r[9]!=259 && r->r[9]!=512 && r->r[9]!=1  &&
        strcmp((char*)r->r[1], strchr(logfile,':')+1)==0) */ )
  {
    char *s = mess;
    int i;

    inside_upcall = TRUE;
    for (i=0; i<missed; i++)
    {
      sprintf(s, "%d/%d ", miss_r0[i], miss_r9[i]);
      s += strlen(s);
    }
    sprintf(s, "UpCall (missed %d) %d", missed, r->r[0]);
    s += strlen(s);
    missed = 0;
    if (r->r[0] == 3)
    {
      sprintf(s, ": reason code = %d", r->r[9]);
      s += strlen(s);
      if (r->r[9] != 259 && r->r[9] != 512)
        sprintf(s, " for object '%s'", (char*)r->r[1]);
      else
        sprintf(s, " for handle %d", r->r[1]);
      s += strlen(s);
    }
    sprintf(s, "\n");
    debug(mess);
    inside_upcall = FALSE;
  }
  else
  {
    miss_r0[missed] = r->r[0];
    miss_r9[missed] = r->r[9];
    missed++;
  }

  return 0;
}

#endif

/***************************************************************************/

static _kernel_oserror *declare_fs(void *private)

  /* Register NewFS with FileSwitch */

{
  int info_block[INFO_BLOCK_SIZE];
  _kernel_oserror *err;

 /* set up image filing system information block */
  info_block[0] = 0;                 /* IFS information word */
  info_block[1] = IMAGE_TYPE;
  info_block[2] = ((int)NewFS_Open     - (int)module_base);
  info_block[3] = ((int)NewFS_GetBytes - (int)module_base);
  info_block[4] = ((int)NewFS_PutBytes - (int)module_base);
  info_block[5] = ((int)NewFS_Args     - (int)module_base);
  info_block[6] = ((int)NewFS_Close    - (int)module_base);
  info_block[7] = ((int)NewFS_File     - (int)module_base);
  info_block[8] = ((int)NewFS_Func     - (int)module_base);

 /* register this module as an image filing system */
  err = _swix(OS_FSControl, I0|I1|I2|I3,
              35,                  /* register image filing system */
              (int)module_base,
              (int)info_block - (int)module_base,
              (int)private
             );

  return err;
}

/***************************************************************************/

void fs_service(int service_number, _kernel_swi_regs *rset, void *private)

  /* Provide handlers for the services we intercept */

{

 /* Note: We should NOT perform any IO from these functions */

  switch (service_number)
  {
    case 0x11: /* Service_Memory - see PRM page 3-68 */

     /* if the "current active object pointer" is equal to this module's
         base address, we must stop memory controller remapping by claiming
         the service call */
      if (rset->r[2] == (int)module_base)
        rset->r[1] = 0; /* claim this service */
      break;


    case 0x12: /* Service_StartUpFS - see PRM page 2-22 */

     /* select NewFS - but don't claim the service */
      _swix(OS_FSControl, I0|I1,
            14,      /* FSControl_SelectFS */
            (int)module_name()
           );
      break;


    case 0x27: /* Service_Reset - see PRM page 3-69 */
      break;


    case 0x40: /* Service_FSRedeclare see PRM page 2-23 */
      declare_fs(private);
      break;


    case 0x42: /* Service_LookupFileType - see PRM page 1-263 */
      break;


    case 0x5c: /* Service_WimpSaveDesktop - see PRM page 3-78 */
      break;


    case 0x68: /* Service_CloseFile - see PRM page 2-24 */
      break;


    case 0x69: /* Service_IdentifyDisc - see PRM page 2-218 */
     /* claim the call if the disc is recognised as a NewFS disc */
      if (identify_disc(
                        (disc_rec*)rset->r[5],  /* disc record address */
                        (char*)rset->r[2],      /* "current format" buffer */
                        rset->r[3],             /* ... and its length */
                        &rset->r[6],            /* location containing
                                                    sector cache handle */
                        rset->r[8],             /* FileCore private word */
                        &rset->r[2]             /* location to hold image
                                                    filetype if recognised */
                      ))
        rset->r[1] = 0;
      break;


    case 0x6a: /* Service_EnumerateFormats - see PRM page 2-494 */
      {
        _kernel_oserror *err;

        err = add_format_info((format_spec**)(&rset->r[2]));

       /* claim service call only if an error occurred */
        if (err != NULL)
        {
          rset->r[0] = (int)err;
          rset->r[1] = 0;
        }
      }
      break;


    case 0x6b: /* Service_IdentifyFormat - see PRM page 2-277 */
      {
        int format_arg = 0;
        int layout_arg = 0;

        if (identify_format((char*)rset->r[0], &format_arg, &layout_arg))
        {
          rset->r[1] = 0;  /* claim service call */

         /* this is the SWI to call when negotiating a physical format
             for the disc; when called, r3 will equal format_arg */
          rset->r[2] = NEWFS_SWI_BASE + SWI_NEWFS_DISCFORMAT;
          rset->r[3] = format_arg;

         /* this is the SWI to call when laying out a structure onto the
             formatted disc; when called, r0 will equal layout_arg */
          rset->r[4] = NEWFS_SWI_BASE + SWI_NEWFS_LAYOUTSTRUCTURE;
          rset->r[5] = layout_arg;
        }
      }       
      break;


    case 0x6c: /* Service_DisplayFormatHelp - see PRM page 2-278 */
      {
        _kernel_oserror *err = display_format_help();

       /* claim service call only if an error occurred */
        if (err != NULL)
        {
          rset->r[0] = (int)err;
          rset->r[1] = 0;
        }
      }
      break;
  }

  return;
}

/***************************************************************************/

_kernel_oserror *fs_commands(char *argv, int argc,
                                 int command, void *private)

{
 /* replace [0d] at end of argv by null terminator */
  {
    char *p = argv;

    while (*p >= 32) p++;

    *p = 0;
  }

  switch (command)
  {
    case COMM_CHECKIMAGE:        /* checks structure of a NewFS image */
      check_image(argv);
      break;

    case COMM_CRIMAGE:
      {
        char *name = strtok(argv, " ");
        char *s = strtok(NULL, " ");
        int size = atoi(s);
        int block_size = 64;
        char *image_name = "******";

        if (s[strlen(s)-1] == 'k') size <<= 10;

        if (argc >= 3)
          image_name = strtok(NULL, " ");

        if (argc == 4)
          block_size = atoi(strtok(NULL, " "));

        create_image(name, image_name, size, block_size);
      }
      break;

    case COMM_LISTIMAGE:         /* lists contents of a NewFS image */
      list_image(argv);
      break;

    case COMM_LISTCBS:           /* lists NewFS control blocks etc. */
      list_control_blocks();
      break;

    case COMM_NEWFSLOG:
      specify_logfile_name(argv);/* change name of logfile */
      break;
  }

  return NULL;
}

/***************************************************************************/

_kernel_oserror *fs_swi(int swi_no, _kernel_swi_regs *rset, void *private)

  /* Vector through to the relevant SWI handler */

{
  switch (swi_no)
  {
    case SWI_NEWFS_LAYOUTSTRUCTURE:
      return NewFS_LayoutStructure(rset, private);

    case SWI_NEWFS_DISCFORMAT:
      return NewFS_DiscFormat(rset, private);

    default:
      return global_errorV("NewFS unknown SWI number %d", swi_no);
  }
}

/***************************************************************************/

static void shutdown_fs(void)

  /* Module finalisation code */

{
  debug("shutdown_fs called\n");

 /* De-register NewFS from FileSwitch */
  _swix(OS_FSControl, I0|I1,
        36,             /* remove image filing system */
        IMAGE_TYPE
       );

#if TRACE_UPCALLS

 /* release UpCall vector */
  _swix(OS_Release, I0|I1|I2,
        0x1d,
        UpCallRout,
        private_private
       );

#endif

  return;
}

/***************************************************************************/

int init_fs(char *cmd_tail, int podule_base, void *private)

  /* Module initialisation code */

{
  _kernel_oserror *err;

  debug("\ninit_fs called\n");

 /* initialisation of various static variables */
  _syserr = &err_blk;

  image_list = NULL;
  file_list = NULL;

 /* register NewFS with FileSwitch */
  err = declare_fs(private);
  if (err != NULL)
    debug("Problem (%08x) declaring NewFS: '%s'\n",
                            err->errnum, err->errmess);

 /* arrange for NewFS to de-register upon exit */
  atexit(shutdown_fs);

 /* set filetype variable appropriately */
  _swix(OS_SetVarVal, I0|I1|I2|I3|I4,
        (int)"File$Type_"IMAGE_TYPE_TEXT,   /* var name */
        (int)"NewFS",                       /* value to be assigned */
        5,                                  /* length of value */
        0,
        4                       /* value is literal string */
       );

#if TRACE_UPCALLS

  private_private = (int)private;  /* for shutdown to release vector */
 /* claim UpCall vector */
  _swix(OS_Claim, I0|I1|I2,
        0x1d,
        UpCallRout,
        private_private
       );

#endif

  return 0;
}

/***************************************************************************/
