suspendrdp.c 3.69 KB
/*---------------------------------------------------------------------
  File : suspendrdp.c

  Date       Dec 5, 1996.
  Author     Yoshitaka Yasumoto.(yasu@rd3.nintendo.co.jp)
  Copyright  Nintendo, Co., Ltd. 1996.
  ---------------------------------------------------------------------*/
#define		F3DNCom_GBI
#include	<ultra64.h>

/*
 *  RDP を停止させるためのルーチンです.
 *
 *  動作原理: 
 *    予め DL に停止可能位置を示すためのタグ (XTag) を埋め込んでおき,
 *  RDP を停止させたいときに RDP の処理位置から順に XTag をサーチし
 *  見つかった XTag を FullSync コマンドに置き換えます. 
 *
 *  使用方法:
 *    F3DNCom_GBI を定義すると, RDP の Sync 系コマンド ( FullSync,
 *  LoadSync, PipeSync, TileSync ) と NoOp コマンドに XTag が埋め込ま
 *  れます. これらのコマンドの位置が RDP の停止可能位置となります. 
 *  停止処理を円滑に行なうために, これらの命令をうまく配置することが
 *  必要です. 実際には gsSPVertex() の前とかに gsDPNoOp() を置いたり
 *  するのが良いでしょう. むろん Texture のロードコマンドがあればそこに
 *  は Sync 系のコマンドがあるでしょうから, わざわざ gsDPNoOp を追加す
 *  る必要はありません.
 *
 *  fifo バッファの書換えに IO_WRITE を使用していますが, 別にデータキャッ
 * シュ経由でも問題はありません. その際はキャッシュの振る舞いにご注意
 * ください. (osWritebackDCache/osInvalDCache)
 *
 */
void	osRDPSuspend(OSTask *taskp)
{
  u32	dpc_cur, dpc_end, buf_start, buf_end;
  u32	i, j;
  
  /*
   * fifo バッファの情報の取得 (引数で直接渡してもよいでしょう)
   */
  buf_start = ((u32)taskp->t.output_buff     )&0x00ffffff;
  buf_end   = ((u32)taskp->t.output_buff_size)&0x00ffffff;
  
  /*
   * RDP 処理停止
   */
  IO_WRITE(DPC_STATUS_REG, DPC_SET_FREEZE);
  
  /*
   * RDP 停止確認
   */  
  while (!(IO_READ(DPC_STATUS_REG) & DPC_STATUS_FREEZE));
  
  /*
   * RDP レジスタ値取得
   */
  dpc_cur  = IO_READ(DPC_CURRENT_REG);
  dpc_end  = IO_READ(DPC_END_REG);
  
  /*
   * 既に RDP が終了しているか, あるいは残りが少ないかのチェック
   */
  if (dpc_end < dpc_cur || dpc_cur + 176 < dpc_end){

    /*
     *  XTag を探す
     *  + DP コマンドは 8 バイトアラインされている
     *  + 安全のために 3 角形 1 つ分後ろから検索を始める
     */
    for (i = dpc_cur + 176; i != dpc_end; i += 8){

      /*
       *  バッファの最後のデータは当てにならない
       *  + 最悪で NCom_Rej 使用時の 3 角形 4 つ分が意味のないデータになる
       */
      if (i >= buf_end - 176*4 + 8 && i > dpc_end){
	i = buf_start;
      }

      /*
       * XTag 値と fifo 内データの比較
       */
      if (IO_READ(i+4) == GDP_XTAG_LOW &&
	  (IO_READ(i) & 0x00ffffff) == GDP_XTAG_HI){
	
	/*
	 * 見つかった XTag を Full Sync コマンドに置き換える
	 */
	IO_WRITE(i, 0x29000000);
	
	/*
	 * DPC_END と XTag の位置の関係の判定
	 */
	if (i <= dpc_end){
	  
	  /*
	   * XTag が DPC_END より前なら, DPC_END への書き込みが
	   * すぐに反映される
	   */
	  IO_WRITE(DPC_END_REG, i + 8);
	  
	} else {
	  
	  /*
	   * XTag が DPC_END の後ろなら, DPC_END への書き込みは
	   * 現在の fifo 領域処理後に反映される.
	   * このとき fifo バッファ先頭から参照されるので DPC_END に
	   * fifo の先頭アドレスを書き込むことで現在の fifo 領域処理後に
	   * 停止させることができる.
	   */
	  IO_WRITE(DPC_END_REG, buf_start);
	  
	  /*
	   * XTag 以降の fifo 領域の DP コマンドを NoOp に置き換える
	   */
	  for (j = i + 8; j < buf_end; j += 8){
	    
	    /*
	     * NoOp に置き換える
	     */
	    IO_WRITE(j, 0x0000000);
	  }
	}
	break;
      }
    }
  }

#if 0	/* DEBUG 用 */
  osSyncPrintf("S=%06x E=%06x C=%06x XTag=%06x ",
	       buf_start, dpc_end, dpc_cur, i);  
  for (i = 0; i < 0x80; i += 4){
    if (!(i & 0x3f)) osSyncPrintf("\n    ");
    osSyncPrintf("%08x ", IO_READ(dpc_cur+i));
  }
  osSyncPrintf("\n");
#endif

  /*
   * RDP 処理再開
   */
  IO_WRITE(DPC_STATUS_REG, DPC_CLR_FREEZE);
}