/*	
 *	cliplot.c
 *
 *	抽選処理
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2014 Naoyuki Sawa
 *
 *	* Tue May 29 00:54:45 JST 2012 Naoyuki Sawa
 *	- 1st リリース。
 *	* Sat Aug 03 00:18:29 JST 2013 Naoyuki Sawa
 *	- 抽選表のデータ構造を変更し、抽選列の数を追加しました。
 *	  LOT_draw_table()に不正な抽選列インデクスを指定した時、エラー停止するようにしました。
 *	- tool/MkLotTbl.awk を変更し、新しいデータ構造に沿った抽選表を出力するようにしました。
 *	  もう、古いデータ構造の抽選表を使用する事はありません。
 *	* Tue Jan 14 23:30:11 JST 2014 Naoyuki Sawa
 *	- 抽選表のデータ構造を変更し、抽選列のidを追加しました。
 *	  LOT_draw_table()の仕様を変更し、抽選列をインデクスでなくidとスキップ回数で指定するようにしました。
 *	- tool/MkLotTbl.awk を変更し、新しいデータ構造に沿った抽選表を出力するようにしました。
 *	  もう、古いデータ構造の抽選表を使用する事はありません。
 *	* Fri Jan 31 01:01:32 JST 2014 Naoyuki Sawa
 *	- LOT_get_column_weight()とLOT_get_table_weight()を追加しました。
 *	* Fri Feb 28 01:16:14 JST 2014 Naoyuki Sawa
 *	- LOT_get_table_size()とLOT_get_table_column_id()を追加しました。
 */
#include "clip.h"

/****************************************************************************
 *	抽選処理
 ****************************************************************************/

#ifndef PIECE
int LOT_draw_column(const unsigned short* column, int column_size, int rndval) {
	int hitval, weight;
	for(;;) {
		/* 抽選列の終端に到達したら、-1を返します。 */
		if(--column_size < 0) { return -1; }
		/*  当選値   を取得します。 */
		hitval = *column++;
		/* (重み値-1)を取得します。 */
		weight = *column++;
		/* 乱数値から重み値を引き、0未満になったら当選です。 */
		if((rndval -= (weight + 1)) < 0) { return hitval; }
	}
}
#else /*PIECE*/
int LOT_draw_column(const unsigned short* column, int column_size, int rndval);
asm("
		.code
		.align		1
		.global		LOT_draw_column
LOT_draw_column:
		;// %r12 := column
		;// %r13 := column_size
		;// %r14 := rndval
LOT_draw_column_LOOP:
		sub		%r13, 1			;// %r13 := column_size--
		jrlt		LOT_draw_column_MISS	;// if(column_size < 0) return -1
		;//
		ld.uh		%r10, [%r12]+		;// %r10 := hitval = *column++
		ld.uh		%r11, [%r12]+		;// %r11 := weight = *column++
;//{{高速化
;//		add		%r11, 1			;// %r11 :=           (weight + 1)
;//		sub		%r14, %r11		;// %r14 := rndval -= (weight + 1)
;//		jrge		LOT_draw_column_LOOP	;// if(rndval <  0) return hitval
;//↓高速化
		sub		%r14, %r11		;// %r14 := rndval -= (weight    )
		jrgt.d		LOT_draw_column_LOOP	;// if(rndval <= 0) return hitval
		sub		%r14, 1			;// %r14 := rndval -= (         1)	*delay*
;//}}高速化
		ret					;// return  hitval
LOT_draw_column_MISS:
		ret.d					;// return  -1
		ld.w		%r10, -1		;// %r10 := -1				*delay*
");
#endif/*PIECE*/

/*--------------------------------------------------------------------------*/

#ifndef PIECE
int LOT_get_column_weight(const unsigned short* column, int column_size, int hitval_sel) {
	int hitval, weight;
	for(;;) {
		/* 抽選列の終端に到達したら、0を返します。 */
		if(--column_size < 0) { return 0; }
		/*  当選値   を取得します。 */
		hitval = *column++;
		/* (重み値-1)を取得します。 */
		weight = *column++;
		/* 当選値が一致したら、重み値(1〜65536)を返します。 */
		if(hitval == hitval_sel) { return (weight + 1); }
	}
}
#else /*PIECE*/
int LOT_get_column_weight(const unsigned short* column, int column_size, int hitval_sel);
asm("
		.code
		.align		1
		.global		LOT_get_column_weight
LOT_get_column_weight:
		;// %r12 := column
		;// %r13 := column_size
		;// %r14 := hitval_sel
LOT_get_column_weight_LOOP:
		sub		%r13, 1				;// %r13 := column_size--
		jrlt		LOT_get_column_weight_MISS	;// if(column_size < 0) return 0
		;//
		ld.uh		%r11, [%r12]+			;// %r11 := hitval = *column++
		ld.uh		%r10, [%r12]+			;// %r10 := weight = *column++
		cmp		%r11, %r14			;// if(hitval == hitval_sel)
		jrne		LOT_get_column_weight_LOOP
		ret.d						;//   return  (weight + 1)
		add		%r10, 1				;//   %r10 := (weight + 1)	*delay*
LOT_get_column_weight_MISS:
		ret.d						;// return  0
		ld.w		%r10, 0				;// %r10 := 0			*delay*
");
#endif/*PIECE*/

/*--------------------------------------------------------------------------*/

#ifndef PIECE
static int LOT_seek_and_call(const unsigned short* table, int id_skip, int param, int (*fn)(const unsigned short* column, int column_size, int param)) {
	int id, skip, table_size, column_id, column_size;
	/* 抽選列のidとスキップ回数を分離します。 */
	id   = (unsigned short)(id_skip >>  0); /* 0〜65535 */
	skip = (unsigned short)(id_skip >> 16); /* 0〜65535 */
	/* 抽選表の列数を取得します。 */
	table_size = *table++;
	for(;;) {
		/* 抽選表の最終列を超えていたら、エラー停止します。 */
		if(--table_size < 0) { DIE(); } /* データエラー: 抽選表の中に、指定されたidを持つ抽選列が、(skip+1)回以上出現しなかった。 */
		/* 抽選列のidを取得します。 */
		column_id   = *table++;
		/* 抽選列の要素数を取得します。 */
		column_size = *table++;
		/* 指定されたidを持つ抽選列が見つかったら、スキップ回数を減らし0未満になったら、関数を実行します。 */
		if((column_id == id) && (--skip < 0)) { return (*fn)(table, column_size, param); }
		/* 抽選列をスキップします。 */
		table += column_size * 2/*{hitval,weight}*/;
	}
}
#else /*PIECE*/
/*static*/ int LOT_seek_and_call(const unsigned short* table, int id_skip, int param, int (*fn)(const unsigned short* column, int column_size, int param));
asm("
		.code
		.align		1
LOT_seek_and_call:
		;// %r12        := table
		;// %r13[15: 0] := id
		;// %r13[31:16] := skip
		;// %r14        := param
		;// %r15        := fn
		ld.uh		%r4, %r13		;// %r4  := id   = (unsigned short)(id_skip >>  0) /* 0〜65535 */
		ld.w		%r5, %r13		;// %r5  := skip = (unsigned short)(id_skip >> 16) /* 0〜65535 */
		xsrl		%r5, 16
		ld.uh		%r6, [%r12]+		;// %r6  := table_size = *table++
LOT_seek_and_call_LOOP:					;// for(;;) {
		sub		%r6, 1			;//   %r6  := table_size--
		jrlt		LOT_seek_and_call_DIE	;//   if(table_size < 0) { DIE() } /* データエラー: 抽選表の中に、指定されたidを持つ抽選列が、(skip+1)回以上出現しなかった。 */
		ld.uh		%r9, [%r12]+		;//   %r9  := column_id   = *table++
		ld.uh		%r13, [%r12]+		;//   %r13 := column_size = *table++
		cmp		%r9, %r4		;//   if((column_id == id)
		jrne		3
		 sub		%r5, 1			;//   && (--skip < 0)) { break }
		 jrlt		LOT_seek_and_call_RET
		sla		%r13, 2			;//   %r13 :=          column_size * 4
		jp.d		LOT_seek_and_call_LOOP	;// }
		add		%r12, %r13		;//   %r12 := table += column_size * 4	*delay*
LOT_seek_and_call_RET:
		jp		%r15			;// return (*fn)(table, column_size, param)
");
static void __attribute__((noreturn,unused)) LOT_seek_and_call_DIE() { DIE(); /*抽選列インデクスが不正*/ } /*asmブロックから参照*/
#endif/*PIECE*/

/*--------------------------------------------------------------------------*/

#ifndef PIECE
int LOT_draw_table(const unsigned short* table, int id_skip, int rndval) {
	return LOT_seek_and_call(table, id_skip, rndval, LOT_draw_column);
}
#else /*PIECE*/
int LOT_draw_table(const unsigned short* table, int id, int rndval);
asm("
		.code
		.align		1
		.global		LOT_draw_table
LOT_draw_table:
		xld.w		%r15, LOT_draw_column
		jp		LOT_seek_and_call
");
#endif/*PIECE*/

/*--------------------------------------------------------------------------*/

#ifndef PIECE
int LOT_get_table_weight(const unsigned short* table, int id_skip, int hitval_sel) {
	return LOT_seek_and_call(table, id_skip, hitval_sel, LOT_get_column_weight);
}
#else /*PIECE*/
int LOT_get_table_weight(const unsigned short* table, int id_skip, int hitval_sel);
asm("
		.code
		.align		1
		.global		LOT_get_table_weight
LOT_get_table_weight:
		xld.w		%r15, LOT_get_column_weight
		jp		LOT_seek_and_call
");
#endif/*PIECE*/

/*--------------------------------------------------------------------------*/

#ifndef PIECE
int LOT_get_table_column_id(const unsigned short* table, int i_column) {
	int table_size, column_size, id, skip;
	const unsigned short* column;
	/* 抽選表の列数を取得します。 */
	table_size = *table++;
	/* 抽選表の列数が、抽選列の順番(0〜)以下である場合は、エラー停止します。 */
	if(table_size <= i_column) { DIE(); }
	/* (i_column)番目の抽選列の、idを取得します。 */
	column = table;
	while(--i_column >= 0) {
		               column++; /*column_id*/
		column_size = *column++;
		               column += column_size * 2/*{hitval,weight}*/;
	}
	id = *column;
	/* (i_column)番目の抽選列よりも前に、(i_column)番目の抽選列と同じidの抽選列が存在する場合、その列数をスキップ回数として数えます。 */
	skip = 0;
	while(table != column) {
		           if(*table++ == id) { skip += (1<<16); } /*column_id*/
		column_size = *table++;
		               table += column_size * 2/*{hitval,weight}*/;
	}
	/* 抽選列のidと、スキップ回数を返します。 */
	return id | skip;
}
#else /*PIECE*/
asm("
		.code
		.align		1
		.global		LOT_get_table_column_id
LOT_get_table_column_id:
		ld.uh		%r15, [%r12]+			;// %r15 := table_size = *table++;
		cmp		%r15, %r13			;// if(table_size <= i_column) { DIE() }
		jrle		LOT_get_table_column_id_DIE
		ld.w		%r14, %r12			;// %r14 := column
		;// %r12 := table	抽選列[0]を指している
		;// %r13 := i_column
		;// %r14 := column	抽選列[0]を指している
LOT_get_table_column_id_LOOP1:
		sub		%r13, 1				;// while(--i_column >= 0) {
		jrlt		LOT_get_table_column_id_EXIT1
		add		%r14, 2				;//   %r14 :=                column++
		ld.uh		%r15, [%r14]+			;//   %r15 := column_size = *column++
		sla		%r15, 2				;//   %r15 :=                          (column_size * 4)
		jp.d		LOT_get_table_column_id_LOOP1	;// }
		add		%r14, %r15			;//   %r14 :=                column += (column_size * 4)	*delay*
LOT_get_table_column_id_EXIT1:
		ld.uh		%r10, [%r14]			;// %r10 := id = *column
		ld.w		%r11, 0				;// %r11 := skip = 0
		;// %r10 := id		抽選列[i_column]のcolumn_id
		;// %r11 := skip
		;// %r12 := table	抽選列[0]を指している
		;// %r14 := column	抽選列[i_column]を指している
LOT_get_table_column_id_LOOP2:
		cmp		%r12, %r14			;// while(table != column) {
		jreq		LOT_get_table_column_id_EXIT2
		ld.uh		%r13, [%r12]+			;//   %r13 := column_id   = *table++
		ld.uh		%r15, [%r12]+			;//   %r15 := column_size = *table++
		cmp		%r13, %r10			;//   if(column_id == id) {
		jrne.d		LOT_get_table_column_id_SKIP
		 sla		%r15, 2				;//   %r15 :=                         (column_size * 4)		*delay*
		 xadd		%r11, %r11, 0x10000		;//     %r11 := skip += (1<<16)							「ここで1加算し最後に16ビットシフトしてorする」よりも「ここで(1<<16)加算し最後にそのままorする」方が一命令節約できる。一般的な抽選表ではskip=0であることが多くここを通ることは少ないので性能低下はほぼ無いであろう。
LOT_get_table_column_id_SKIP:
		jp.d		LOT_get_table_column_id_LOOP2	;// }
		add		%r12, %r15			;//   %r12 :=                table += (column_size * 4)		*delay*
LOT_get_table_column_id_EXIT2:
		ret.d						;// return  id | skip
		or		%r10, %r11			;// %r10 := id | skip						*delay*
");
static void __attribute__((noreturn,unused)) LOT_get_table_column_id_DIE() { DIE(); }
#endif/*PIECE*/
