// SCSI Readキャッシュ

#include <scsi.h>
#include <string.h>	// memcpy()
#include <iocslib.h>	// DMAMOVE()
#include "ascsi.h"
#include "defines.h"

#define	DEBUG	0

int S_CacheRead(S_CACHE_BUFF *scb,unlong sec,long blocks,unchar *buff)
// キャッシュから読み出す
{
long j;
unshort i;
unchar *b;
int ret;
	do {
		#if DEBUG
		printf("sec=$%x\n",sec);
		#endif
		// キャッシュに入っているかどうかを調べる
		for (j=0;j<scb->nextblock;j++) {
			if (scb->sectors[j].sec==sec) { // あった
				i=scb->sectors[j].ii; // そのキャッシュ内ブロック番号
				b=&scb->cache[((long)i)*scb->sec1]; // そのキャッシュのバッファーアドレス
				#if DEBUG
				printf("見つかった:[%d]\n",i);
				#endif
				goto NEXT;
			}
		}
		// キャッシュ内に存在しない＝ディスクから読み込む
		#if DEBUG
		printf("キャッシュにない\n");
		#endif
		// 次回読み込み位置(この時点でj==scb->nextblockである)
		j=(j==scb->allblock? scb->allblock-1:scb->nextblock++);
		i=scb->sectors[j].ii;
		b=&scb->cache[((long)i)*scb->sec1];
		#if DEBUG
		printf("読み込みブロック=%d\n",i);
		printf("sec=$%x,id=%d,blksz=%d\n",sec,scb->id,scb->blksz);
		#endif
		
		//ret=S_SecRead(sec,1,scb->id,scb->blksz,b);の強制S_READEXT()版
		{
		int retry = 0;
			do {
				// 読み込み
				ret=S_READEXT(sec,1,scb->id,scb->blksz,b);
				if (!ret) break; // 正常終了
				// エラー時のリトライ
				ret=S_Retry(scb->id,ret,&retry);
			} while (ret==0); // リトライ用
		}
		#if DEBUG
		printf("ret=%d\n",ret);
		#endif
		if (ret) return(ret);
	    NEXT:; // キャッシュから指定領域へコピー
	    	// 高速化のためDMAを使う
		DMAMOVE(buff,b,0b10000101,scb->sec1);
	    	buff+=scb->sec1;
// 68030以上ではこちらの方が速いかも
//	  	{short sec1;
//		sec1=scb->sec1;
//		while (sec1-->0) *buff++=*b++;
//		}
		// scb->sectors[]で今読み込んだ番号を先頭([0])に移動する
		// j = sectors[今読み込んだところ]
		// i = sectors[j].ii
		// sectors[j]の内容はsec,iにあるので保存不要
		if (scb->nextblock>1) {
			// (1) 現在の[0〜j-1]を[1〜j]にコピー
		    	// 高速化のためDMAを使う
			DMAMOVE( (unchar*)&scb->sectors[j+1].sec-1
				,(unchar*)&scb->sectors[j-1+1].sec-1
				,0b10001010
				,sizeof(S_CACHE1)*j);
			#if 0
			// 68030以上ではこちらの方が速いかも
			for (;j>=1;j--) {
				scb->sectors[j].sec=scb->sectors[j-1].sec;
				scb->sectors[j].ii =scb->sectors[j-1].ii;
			}
			#endif
		} // 最初は移動不要
		// (2) [0]に[j]の内容を入れる
		scb->sectors[0].sec=sec;
		scb->sectors[0].ii=i;
		#if DEBUG
		{long k;
		for (k=0;k<scb->nextblock;k++) {
			printf("%d:sec=$%x,ii=%d\n",k,scb->sectors[k].sec,scb->sectors[k].ii);
		}
		}
		#endif
		// 次のセクターへ
		sec++;
	} while (--blocks>0);
	// 正常終了
	return(0);
}

#if DEBUG
void main()
{
#define	ID	12
#define MAX	5
#define	CLK	1
S_CACHE_BUFF scb;
int ret;
unchar buff[2048];
unlong sec;
	#if 0 // 一番効率の悪いキャッシュ例
	printf("MAX=%d\n",MAX);
	ret=S_CacheOpen(&scb,ID,MAX,S_CACHE_READ);
	#else // 効率の良いキャッシュ例
	printf("MAX=%d\n",MAX+2);
	ret=S_CacheOpen(&scb,ID,MAX+2,S_CACHE_READ);
	#endif
	if (ret) {
		printf("error=%d\n",ret);
		exit(1);
	}
	printf("最初\n");
	for (sec=0;sec<MAX+1;sec++) {
		ret=S_CacheRead(&scb,sec,CLK,buff);
		if (ret) {
			printf("error=%d\n",ret);
			S_CacheClose(&scb);
			exit(2);
		}
	}
	printf("２回目(途中まで）\n");
	for (sec=0;sec<MAX/2;sec++) {
		ret=S_CacheRead(&scb,sec,CLK,buff);
		if (ret) {
			printf("error=%d\n",ret);
			S_CacheClose(&scb);
			exit(2);
		}
	}
	printf("３回目\n");
	for (sec=0;sec<MAX+1;sec++) {
		ret=S_CacheRead(&scb,sec,CLK,buff);
		if (ret) {
			printf("error=%d\n",ret);
			S_CacheClose(&scb);
			exit(2);
		}
	}
	S_CacheClose(&scb);
}
#endif
