game.c 7.01 KB

/*
 * Main program for R4300 for a disk write program. Since this program is
 * using host io protocol and host io protocol uses Int 1 and cart int, we
 * have to do a certain tricky technique to share these interrupts between
 * host io and 64 disk drive. The method I used here is "changing" the 
 * interrupt service routine of Int 1 and the message queue that the message
 * from cartridge will be sent. See the source code of osLeoSwitch() for
 * more information.
 */
#include "hostio.h"
#include "../ddwrite.h"

/*
 * Stacks for the threads as well as message queues for synchronization
 * This stack is ridiculously large, and could also be reclaimed once
 * the main thread is started.
 */
u64	bootStack[STACKSIZE/sizeof(u64)];

static void	idle(void *);
static void	mainproc(void *);

static OSThread	idleThread;
static u64	idleThreadStack[STACKSIZE/sizeof(u64)];

static OSThread	mainThread;
static u64	mainThreadStack[5 * STACKSIZE/sizeof(u64)];

/*
 * Message queues
 */
#define NUM_PI_MSGS     8
static OSMesg PiMessages[NUM_PI_MSGS];
static OSMesgQueue PiMessageQ;

static OSMesgQueue	diskQ;		/* post queue */
static OSMesg		diskQBuf;

#define NUM_CMD_MSGS	8
static OSMesg           cmdQBuf[8];

/*
 * Prototypes
 */
static	s32   	error_handle(LEOError);
static	void 	getDiskInfo(ctrlblk *sdata);

s32 getdata(void *addr, u32 nbytes);

/*
 * Definition for error handling routine
 */
#define	RETRY	0
#define	FATAL	1

/*
 * These variables are defined in each file to align 16bytes.
 * These are for communication to Indy.
 */
extern	u64	rdramBuf[];
extern	ctrlblk	data;
extern	s32	transbyte;

void
boot(void)
{
    
  osInitialize();
  
  osCreateThread(&idleThread, 1, idle, (void *)0,
		 idleThreadStack+STACKSIZE/sizeof(u64), 10);
  osStartThread(&idleThread);
  
  /* never reached */
}

static void
idle(void *arg)
{
  /*
   * Create main thread
   */
  osCreateThread(&mainThread, 3, mainproc, arg,
		 mainThreadStack+STACKSIZE/sizeof(u64), 10);
  osStartThread(&mainThread);
  
  /*
   * Become the idle thread
   */
  osSetThreadPri(0, 0);
  
  for (;;);
}

/*
 * This is the main routine of the app.
 */
static void
mainproc(void *arg)
{
  LEOError	error;
  LEOCmd	cmdBlock;
  DDCmd		command;
  
  osCreateMesgQueue(&diskQ, &diskQBuf, 1);

  osCreatePiManager((OSPri)OS_PRIORITY_PIMGR, &PiMessageQ, PiMessages,
		    NUM_PI_MSGS);

  error = (LEOError)LeoCreateLeoManager((OSPri)OS_PRIORITY_LEOMGR-1,
					(OSPri)OS_PRIORITY_LEOMGR,
					cmdQBuf, NUM_CMD_MSGS);

  if (error == LEO_ERROR_DEVICE_COMMUNICATION_FAILURE)
  {
    osSyncPrintf("leowrite: Drive doesn't exist\n");
    for(;;);
  }
  
  /*
   * Clear "reset" flag.
   */
  LeoResetClear();

  while(1)			/* main loop */
  {
    /*
     * Send 'Ready' to Indy
     */
    osWriteHost(rdramBuf, 4);

    /*
     * First, we get the type, filesize and offset.
     */
    osReadHost(&data, sizeof(ctrlblk));

    if (data.cmdFlags & END_COMMAND)
    {
      /*
       * End of leowrite. Stop the motor of the drive
       */
      do
      {
	LeoSpdlMotor(&cmdBlock, SLEEP, &diskQ);
	osRecvMesg(&diskQ, (OSMesg)&error, OS_MESG_BLOCK);
    
	if ( (error == 0) || 
	    (error == LEO_ERROR_MEDIUM_NOT_PRESENT) )
	  break;
	if (error_handle(error) == FATAL)
	{
	  osSyncPrintf("Fatal error\n");
	  for(;;);
	}
    
      } while(1);
    
      /*
       * Send back the end command
       */
      osWriteHost(&data, sizeof(ctrlblk));
  
      /* Wait host to kill me */
      for(;;);

    }
  
    if (data.cmdFlags & SEND_ME_INFO)
    {
      getDiskInfo(&data);
    
      if ( !(data.cmdFlags & SYS_UNFORMATTED) ) /* if sys is unformatted, might hang */
      {
	/*
	 * Stop the motor of the drive
	 */
	do
	{
	  LeoSpdlMotor(&cmdBlock, SLEEP, &diskQ);
	  osRecvMesg(&diskQ, (OSMesg)&error, OS_MESG_BLOCK);
    
	  if ( (error == 0) || 
	      (error == LEO_ERROR_MEDIUM_NOT_PRESENT) )
	    break;
	  if (error_handle(error) == FATAL)
	  {
	    osSyncPrintf("Fatal error\n");
	    for(;;);
	  }
    
	} while(1);
      }
    
      osWriteHost(&data, sizeof(ctrlblk));

      continue;
      
    } /* if (SEND_ME_INFO) */
  
    /*
     * write の準備
     */
    command.startLBA = (u32)data.offset;
    command.diskType = data.type;
    command.loadAddr = data.address;
    command.idAddr = &data.diskID;

    command.flags = 0;
    if (! (data.cmdFlags & SPECD_ID_FILE) )
    {
      /* ID が指定されていないときは、シリアル番号のみ上書きする */
      command.flags |= DDWRITE_FLG_IDSERIAL;
      
    }
    
    command.iplSize = 0;
    command.writeBuf = (void *)rdramBuf;
    command.writeSize = BUFSIZE;
    command.getdata = getdata;
    command.totalSize = (u32)data.filesize;
    if (data.cmdFlags & DO_FORMAT)
      command.flags |= DDWRITE_FLG_FORMAT;

    /*
     * write する
     */
    error = (LEOError)ddWrite(&command);
    
    if (error)
    {
      osSyncPrintf("ライト中にエラー発生。error no = %d\n", error);
      for(;;);
    }
    else
      osSyncPrintf("Good\n");

    /*
     * Send end mark to host
     */
    transbyte = -1;
    osWriteHost(&transbyte, sizeof(transbyte));

  } /* while */
  
} /* mainproc() */


/*
 * getdata 関数。関数のポインタを ddwrite に渡す。
 */
s32 getdata(void *addr, u32 nbytes)
{
  /*
   * Ask host to transfer <transbyte> bytes
   */
  osWriteHost((void *)&nbytes, sizeof(nbytes));
  
  osReadHost(addr, nbytes);
  
  return 0;

}


/*
 * Error handling routine. In this version, I don't check the ID blocks.
 * XXX need to fix
 */
s32 error_handle(LEOError result)
{
  LEOCmd	cmdBlock;
  LEODiskID	dummyDiskID;
  s32		res = 0;
  
  /*
   * If the error is not one of these two, consider it as fatal.
   */
  if (!( (result == LEO_ERROR_MEDIUM_NOT_PRESENT) ||
      (result == LEO_ERROR_MEDIUM_MAY_HAVE_CHANGED ) ))
    return FATAL;
  
  /*
   * Since this version doesn't check the ID, I don't care if the disk
   * might have been changed.
   */
  if (result == LEO_ERROR_MEDIUM_MAY_HAVE_CHANGED)
    return RETRY;
  
  osSyncPrintf("Insert a disk\n");

  /*
   *  Wait for the users to insert the disk.
   *  (myLeoReadDiskID command doesn't check the system area).
   */
  do
  {
    myLeoReadDiskID(&cmdBlock, &dummyDiskID, &diskQ);
    osRecvMesg(&diskQ, (OSMesg *)&res, OS_MESG_BLOCK);
    
    if ( res != LEO_ERROR_MEDIUM_NOT_PRESENT )
      break;

  } while(1);
  
  return RETRY;

} /* error_handle() */


void getDiskInfo(ctrlblk *sdata)
{
  LEOCmd	cmdBlock;
  LEOError	error = 0;
  DDInfo	info;
  s32		ddGetInfoResult;

  ddGetInfoResult = ddGetInfo(&info);

  if (ddGetInfoResult == LEO_ERROR_UNRECOVERED_READ_ERROR)
  {
    sdata->cmdFlags |= SYS_UNFORMATTED;
    return;
  }
  
  sdata->offset 	= (s32)info.iplSize;
  sdata->type 		= info.diskType;
  sdata->address 	= info.loadAddr;

  do{
    LeoReadDiskID(&cmdBlock, &sdata->diskID, &diskQ);
    osRecvMesg(&diskQ, (OSMesg)&error, OS_MESG_BLOCK);

    if(error == 0)
      return;
    else if(error == LEO_ERROR_UNRECOVERED_READ_ERROR)
    {
      sdata->cmdFlags |= ID_UNFORMATTED;
      return;
    }
    else if (error_handle(error) == FATAL)
    {
      osSyncPrintf("Fatal error\n");
      for(;;);
    }
  } while(1);

  return;
  
} /* getDiskInfo */