/*->c.ram     */

/* Ovation!   (c) D. J. Pilling,     April 1989                       */
/*                   Filing System  Interface                         */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <ctype.h>
#include <time.h>
#include <stdarg.h>


#include "h.os"
#include "h.wimp"
#include "h.sprite"
#include "h.bbc"


#include "h.wos"
#include "h.main"

#include "h.ram"


/*****************************************************************************/
                         /* RAM Transfer code */


#define RINFP  ((FILE *)1)
#define ROUTFP ((FILE *)2)

#define Wimp_TransferBlock 0x400F1

int tranblock(char * destbuff,int desttask,char * ourbuff,int bytes)
{
 os_regset   rx;
 os_error  * ep;

 rx.r[0]=taskhandle;        /* our taskhandle         */
 rx.r[1]=(int)ourbuff;      /* our buffer             */
 rx.r[2]=desttask;          /* destination taskhandle */
 rx.r[3]=(int)destbuff;     /* destination buffer     */
 rx.r[4]=bytes;

 ep=os_swix(Wimp_TransferBlock,&rx);
 if(ep) {report(ep);return(1);}

 return(0);
}

/*****************************************************************************/
                       /* Receiving stuff via RAM */

int ramread=0;
int readsink=0;

char myrambuff[0x1000];
int  usedrambuff=0;


char * readbuff;
char * readptr;
int    readsize;
char * readtop;

int    readextent;

int    readmyref=-1;  /* our ref no. for read messages              */

int    readyourref;   /* the other sides ref no. for read messages  */
int    readtask;

int    readdone;


void armfetch(wimp_msgstr * msg)
{
 readyourref=msg->hdr.my_ref;
 readtask=msg->hdr.task;
}


void sendramfetch(void)
{
 wimpevent.data.msg.hdr.your_ref=readyourref;
 wimpevent.data.msg.hdr.action=6;
 wimpevent.data.msg.data.ramfetch.addr=readbuff;
 wimpevent.data.msg.data.ramfetch.nbytes=readsize;
 wimp_sendmessage(18,&wimpevent.data.msg,readtask);
 readmyref=wimpevent.data.msg.hdr.my_ref;
}


/* we have received a ram transmit and thus load our buffer */

void ramloadbuffer(void)
{
 readptr=readbuff;
 readtop=readbuff+wimpevent.data.msg.data.ramtransmit.nbyteswritten;
}


/* called when readmyref has not been acked */

void readack(void)
{
 readsink=1;
 readdone=1;
}

/* called when a RAMFETCH returns succesffully */

void readmess(void)
{
 if(wimpevent.data.msg.hdr.action==7)
 {
  ramloadbuffer();
  armfetch(&wimpevent.data.msg);
  readdone=1;
 }
}


/* called when the buffer empties to get some more */

void ramreadbuff(void)
{
 if((readtop-readbuff)<readsize)
 {
  readsink=1;
  return;
 }

 sendramfetch();
 readdone=0;
 while(!readdone) poll(0);
}


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

/* return the extent of a file */

int rext(FILE * fp)
{
 int len;

 if(fp==RINFP)
    return(readextent);
 else
    {
     fseek(fp,0,SEEK_END);
     len=(int)ftell(fp);
     fseek(fp,0,SEEK_SET);
     return(len);
    }
}


int rread(void * buff,int size,int number,FILE * fp)
{
 int bytes;
 int chunk;
 int bytesread=0;

 if(fp==RINFP)
  {
   if((!readsink) && size && number)
    {
     bytes=size*number;
     while(bytes)
     {
      if(readptr>=readtop)
               {
                ramreadbuff();
                if(readsink) break;
               }

      if((readtop-readptr)>bytes) chunk=bytes;
      else                        chunk=readtop-readptr;

      memcpy(buff,readptr,chunk);
      buff=(void *)(((char *)buff)+chunk);
      readptr+=chunk;
      bytes-=chunk;
      bytesread+=chunk;

      if(readptr>=readtop)
               {
                ramreadbuff();
                if(readsink) break;
               }
     }
     return(bytesread/size);
    }
   else
   return(0);
  }
 return(fread(buff,size,number,fp));
}


int rgetc(FILE * fp)
{
 int c;

 if(fp==RINFP)
 {
  if(!readsink)
   {
    c=*readptr++;
    if(readptr==readtop) ramreadbuff();
   }
  else c=EOF;

  return(c);
 }
 return(fgetc(fp));
}


/*****************************************************************************/
                       /* Sending stuff via RAM */


int ramwrite=0;
int writesink=0;


int writemyref=-1;  /* our ref. for write messages         */

int writeyourref;   /* other sides ref. for write messages */
int writetask;


char * writebuff;
char * writeptr;
int writesize;


char * writedestbuff;
char * writedestptr;
int    writedestsize;

int    writedone;


void armsend(wimp_msgstr * msg)
{
 writetask=msg->hdr.task;
 writeyourref=msg->hdr.my_ref;
 writedestsize=msg->data.ramfetch.nbytes;
 writedestbuff=msg->data.ramfetch.addr;
 writedestptr=writedestbuff;
}


void writeack(void)
{
 writesink=1;
 writedone=1;
}


void writemess(void)
{
 if(wimpevent.data.msg.hdr.action==6)
 {
  armsend(&wimpevent.data.msg);
 }
 writedone=1;
}


void sendramtran(void)
{
 wimp_msgstr msg;
 msg.hdr.action=7;
 msg.hdr.size=28;
 msg.hdr.your_ref=writeyourref;

 msg.data.ramtransmit.addr=writedestbuff;
 msg.data.ramtransmit.nbyteswritten=writedestptr-writedestbuff;
 wimp_sendmessage(18,&msg,writetask);
 writemyref=msg.hdr.my_ref;

}



/* sends the contents of the buffer */

void ramwritebuff(int ends)    /* set if this is last write */
{
 int  chunk;
 int  onemoretime;

 /* do ram transfer */

 chunk=writeptr-writebuff;

 while(1)
 {
  if(chunk>(writedestsize-(writedestptr-writedestbuff)))
                          chunk=writedestsize-(writedestptr-writedestbuff);

  if(tranblock(writedestptr,writetask,writebuff,chunk))
     {
      writesink=1;
      return;
     }
  writedestptr+=chunk;

  if(chunk<writesize)
  {
   /* compact our buffer */
   memcpy(writebuff,writebuff+chunk,(writeptr-writebuff)-chunk);
   writeptr-=chunk;
  }
  else
  {
   writeptr=writebuff;
  }

  if(((writedestptr-writedestbuff)<writedestsize) && !ends) return;

  if(ends && ((writedestptr-writedestbuff)==writedestsize)) onemoretime=1;
                                              else          onemoretime=0;

  /* tell the other application we've done ram transfer */

  sendramtran();

  writedone=0;
  while(!writedone) poll(0);

  /* note that requested chunk could be zero always and loop forever */

  chunk=writeptr-writebuff;
  if((!onemoretime && !chunk) || (!ends && chunk<writesize) || writesink)break;
 }
}

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


int rwrite(void * buff,int size,int number,FILE * fp)
{
 int bytes;
 int chunk;

 if(fp==ROUTFP)
  {
   if(!writesink)
    {
      bytes=size*number;
      while(bytes)
      {
        chunk=writesize-(writeptr-writebuff);
        if(chunk>bytes) chunk=bytes;
        memcpy(writeptr,buff,chunk);
        writeptr+=chunk;
        buff=(void *)(((char *)buff)+chunk);
        if((writeptr-writebuff)>=writesize)
        {
         ramwritebuff(0);
         if(writesink) break;  /* something went wrong, abort */
        }
        bytes-=chunk;
      }
    }
   return(number);
  }
 return(fwrite(buff,size,number,fp));
}


int rputc(char c,FILE * fp)
{
 if(fp==ROUTFP)
 {
  if(!writesink)
   {
    *writeptr=c;
    writeptr++;
    if((writeptr-writebuff)>=writesize)
     {
      ramwritebuff(0);
     }
   }
  return(c);
 }
 return(fputc(c,fp));
}



int rfprintf(FILE * fp,char * format, ...)
{
 va_list args;
 char string[256];
 int  len;

 va_start(args, format);
 vsprintf(string, format, args);
 va_end(args);

 len=strlen(string);

 rwrite(string,len,1,fp);
 return(len);
}




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


FILE * ropen(char * filename,char * mode)
{
 if((!strcmp(mode,"wb") && ramwrite)) return(ROUTFP);
 else
 if((!strcmp(mode,"rb") && ramread )) return(RINFP);
                                                           
 return(fopen(filename,mode));
}



int rclose(FILE * fp)
{
 if(fp==ROUTFP)
 {
  if(!writesink) ramwritebuff(1);
  ramwrite=0;
  return(0);
 }
 else
 if(fp==RINFP)
 {
  ramread=0;
  return(0);
 }
 else
  return(fclose(fp));
}



int rameof(FILE * fp)
{
 if(fp==ROUTFP)
               {
                return(0);
               }
 else
 if(fp==RINFP)
               {
                return(readsink);
               }

 return(feof(fp));
}



/* can't use readsink for errors, because set on last read before eof */
/* should only set if try to read past end of file                    */


int rerror(FILE * fp)
{
  if(fp==ROUTFP)
               {
                return(0);
               }
 else
 if(fp==RINFP)
               {
                return(0);
               }

 return(ferror(fp));
}



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

/* is the next file opened for read going to be from RAM */

/*

int pendingramread(void)
{
 return(ramread);
}

 */

/* the next file to be open for read, will come from RAM */

void ramnextreadfile(void)
{
 ramread=1;
 readsink=usedrambuff=0;
 armfetch(&wimpevent.data.msg);
 readbuff=myrambuff;
 readsize=sizeof(myrambuff);
 sendramfetch();
}


/* is the next file opened for write going to be to RAM */


int pendingramwrite(void)
{
 return(ramwrite);
}


/* the next file to be open for write, will go to RAM */

void ramnextwritefile(void)
{
 ramwrite=1;
 writesink=usedrambuff=0;
 armsend(&wimpevent.data.msg);  /* init's on current wimp message */
 writebuff=myrambuff;
 writesize=sizeof(myrambuff);
 writeptr=writebuff;
}


/* filing system behaves as normal */

void ramoff(void)
{
 ramwrite=0;
 ramread=0;
}


int ramfp(FILE * f)
{
 return(f==RINFP || f==ROUTFP);
}
