/*	
 *	cliptia.h
 *
 *	P/ECE TIA (Television Interface Adapter) Emulator
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2005 Naoyuki Sawa
 *
 *	* Fri Mar 11 03:00:00 JST 2005 Naoyuki Sawa
 *	- 作成開始。
 */
#ifndef __CLIP_TIA_H__
#define __CLIP_TIA_H__

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

/* TIA_ASMシンボル定義を読み込みます。
 * TIA_ASMシンボルの未定義/定義によるC/アセンブラコードの切り替えは、
 * Cソースファイル(*.c)とアセンブラソースファイル(*.s)から参照するので、
 * 別のインクルードファイルに分けています。
 * (C言語特有の定義を含むインクルードファイルをそのままアセンブラソースに
 *  インクルードすると、アセンブルエラーになってしまうため。)
 */
#include "cliptiaa.h"

/********** これ以降は書き換えないでください **********/

/* P/ECE以外ではアセンブラコードは使用できません。
 */
#ifndef PIECE
#undef TIA_ASM
#endif /*PIECE*/

/****************************************************************************
 *	TIA (Television Interface Adapter)
 ****************************************************************************/

//			  ++-------------------------- horizontalblank
//			  || +++---------------------- actual TV picture width
//			  || |||   +------------------ vertical sync
//			  || |||   | ++--------------- vertical blank
//			  || |||   | || +++----------- actual TV picture height
//			  || |||   | || ||| ++-------- overscan
//			  || |||   | || ||| ||
#define TIA_CLOCK	((68+160)*(3+37+192+30)*60) /* 60 frames per second */
#define VCS_CLOCK	(TIA_CLOCK/3)	/* 3 clock counts per machine cycle */

#define TIABUFLEN 320 /* =16000/50 … 1[tick](=1/50[sec])当たりのサンプル数 */

#ifdef PIECE
#define TIA_GRAYSCALE
#endif /*PIECE*/

/* ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★ */
/* ★アセンブラルーチンから直接参照しているので、フォーマット固定です。★ */
/* ★フォーマットを変更したときは、framtiaa.sの定義も追従してください。★ */
/* ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★ */

typedef struct _TIAPLAYER {
	unsigned char position;			/* +0,1: 0..159 */
	unsigned char size;			/* +1,1: 0=x, 1=x_x, 2=x___x, 3=x_x_x, 4=x_______x, 5=XX, 6=x___x___x, 7=XXXX */
	unsigned char reflect;			/* +2,1: 0=no reflect, 1=reflect */
	unsigned char delay;			/* +3,1: 0=no delay, 1=delay */
	unsigned char graphics[2];		/* +4,2: 8bit graphics ([0]=no delay, [1]=delay) */
	unsigned char _resv[2];			/* +6,2: (予約) */
} TIAPLAYER;					/* =8 */

typedef struct _TIAMISSILE {
	unsigned char position;			/* +0,1: 0..159 */
	unsigned char size;			/* +1,1: 0=1clock, 1=2clock, 2=4clock, 3=8clock */
	unsigned char reset;			/* +2,1: 0=no missile-player reset, 1=missile-player reset */
	unsigned char graphics;			/* +3,1: 0=disable, 1=enable */
} TIAMISSILE;					/* =4 */

typedef struct _TIABALL {
	unsigned char position;			/* +0,1: 0..159 */
	unsigned char size;			/* +1,1: 0=1clock, 1=2clock, 2=4clock, 3=8clock */
	unsigned char delay;			/* +2,1: 0=no delay, 1=delay */
	unsigned char graphics[2];		/* +3,2: 0=disable, 1=enable ([0]=no delay, [1]=delay) */
	unsigned char _resv[3];			/* +5,3: (予約) */
} TIABALL;					/* =8 */

typedef struct _TIAPLAYFIELD {
	unsigned char reflect;			/* +0,1: 0=no relfect, 1=reflect */
	unsigned char _resv[3];			/* +1,3: (予約) */
	unsigned int graphics;			/* +4,4: 20bit graphics */
} TIAPLAYFIELD;					/* =8 */

typedef struct _TIAAUDIO {
	unsigned char volume;			/* + 0,1: lowest:0..15:highest */
	unsigned char frequency;		/* + 1,1: frequency divider 1..32 */
	//
	unsigned char  polar;			/* + 2,1: 0:minus or 1:plus */
	unsigned char  div31;			/* + 3,1: 30..0, 0or13->pluse */
	         int   count;			/* + 4,4: frequency counter */
	unsigned short lfsr9;			/* + 8,2: 9-bit LFSR (tap=0,5) */
	unsigned char  lfsr5;			/* +10,1: 5-bit LFSR (tap=0,3) */
	unsigned char  lfsr4;			/* +11,1: 4-bit LFSR (tap=0,1) */
	//
	unsigned char _resv[4];			/* +12,4: (予約) */
} TIAAUDIO;					/* =16 */

typedef struct _TIA {
	int now;				/* +  0, 4: TIAクロックカウンタ (起動時からの通算) */
	int last_x;				/* +  4, 4: 次回更新開始X座標 (0..(  68+160   -1)) */
	int last_y;				/* +  8, 4:             Y     (0..(3+37+192+30-1)) */
	int scan_x;				/* + 12, 4: 現在の走査線X座標 (0..(  68+160   -1)) */
	int scan_y;				/* + 16, 4:             Y     (0..(3+37+192+30-1)) */
	unsigned char clut[4];			/* + 20, 4: [0]=COLUP0, [1]=COLUP1, [2]=COLUPF, [3]=COLUBK */
	unsigned short collision;		/* + 24, 2: D01=M0-P1  (CXM0P [7]), D00=M0-P0  (CXM0P [6]) */
						/*          D03=M1-P0  (CXM1P [7]), D02=M1-P1  (CXM1P [6]) */
						/*          D05=P0-PF  (CXP0FB[7]), D04=P0-BL  (CXP0FB[6]) */
						/*          D07=P1-PF  (CXP1FB[7]), D06=P1-BL  (CXP1FB[6]) */
						/*          D09=M0-PF  (CXM0FB[7]), D08=M0-BL  (CXM0FB[6]) */
						/*          D11=M1-PF  (CXM1FB[7]), D10=M1-BL  (CXM1FB[6]) */
						/*          D13=BL-PF  (CXBLPF[7]), D12=unused (CXBLPF[6]) */
						/*          D15=P0-P1  (CXPPMM[7]), D14=M0-M1  (CXPPMM[6]) */
	unsigned char i4;			/* + 26, 1: INPT4の入力ポート状態 (1=Hi=Off,0=Lo=On) */
	unsigned char i5;			/* + 27, 1: INPT5の入力ポート状態 (1=Hi=Off,0=Lo=On) */
	unsigned char* vbuff/*[160*192]*/;	/* + 28, 4: 仮想画面バッファ (外部定義、tia_reset()で指定) */

	unsigned char _resv[24];		/* + 32,24: (予約) */

	/*{{movable objects*/
	TIAPLAYER player[2];			/* + 56,16: Player0, Player1 */
	TIAMISSILE missile[2];			/* + 72, 8: Missile0, Missile1 */
	TIABALL ball;				/* + 80, 8: Ball */
	TIAPLAYFIELD playfield;			/* + 88, 8: Playfield */
	/*}}movable objects*/

	/*{{AUDIO==========*/
	TIAAUDIO audio[2];			/* + 96,32: Audio circuits */
	/*}}AUDIO==========*/

	/*{{書き込みレジスタ*/
	unsigned char VSYNC;			// +128, 1: $00  VSYNC   0000 00x0  vertical sync set-clear
	unsigned char VBLANK;			// +129, 1: $01  VBLANK  xx00 00x0  vertical blank set-clear
	//unsigned char WSYNC;			//          $02  WSYNC   ---- ----  wait for leading edge of horizontal blank
	//unsigned char RSYNC;			//          $03  RSYNC   ---- ----  reset horizontal sync counter
	unsigned char NUSIZ0;			// +130, 1: $04  NUSIZ0  00xx 0xxx  number-size player-missile 0
	unsigned char NUSIZ1;			// +131, 1: $05  NUSIZ1  00xx 0xxx  number-size player-missile 1
	unsigned char COLUP0;			// +132, 1: $06  COLUP0  xxxx xxx0  color-lum player 0
	unsigned char COLUP1;			// +133, 1: $07  COLUP1  xxxx xxx0  color-lum player 1
	unsigned char COLUPF;			// +134, 1: $08  COLUPF  xxxx xxx0  color-lum playfield
	unsigned char COLUBK;			// +135, 1: $09  COLUBK  xxxx xxx0  color-lum background
	unsigned char CTRLPF;			// +136, 1: $0A  CTRLPF  00xx 0xxx  control playfield ball size & collisions
	unsigned char REFP0;			// +137, 1: $0B  REFP0   0000 x000  reflect player 0
	unsigned char REFP1;			// +138, 1: $0C  REFP1   0000 x000  reflect player 1
	unsigned char PF0;			// +139, 1: $0D  PF0     xxxx 0000  playfield register byte 0
	unsigned char PF1;			// +140, 1: $0E  PF1     xxxx xxxx  playfield register byte 1
	unsigned char PF2;			// +141, 1: $0F  PF2     xxxx xxxx  playfield register byte 2
	//unsigned char RESP0;			//          $10  RESP0   ---- ----  reset player 0
	//unsigned char RESP1;			//          $11  RESP1   ---- ----  reset player 1
	//unsigned char RESM0;			//          $12  RESM0   ---- ----  reset missile 0
	//unsigned char RESM1;			//          $13  RESM1   ---- ----  reset missile 1
	//unsigned char RESBL;			//          $14  RESBL   ---- ----  reset ball
	unsigned char AUDC0;			// +142, 1: $15  AUDC0   0000 xxxx  audio control 0
	unsigned char AUDC1;			// +143, 1: $16  AUDC1   0000 xxxx  audio control 1
	unsigned char AUDF0;			// +144, 1: $17  AUDF0   000x xxxx  audio frequency 0
	unsigned char AUDF1;			// +145, 1: $18  AUDF1   000x xxxx  audio frequency 1
	unsigned char AUDV0;			// +146, 1: $19  AUDV0   0000 xxxx  audio volume 0
	unsigned char AUDV1;			// +147, 1: $1A  AUDV1   0000 xxxx  audio volume 1
	//unsigned char GRP0;			//          $1B  GRP0    xxxx xxxx  graphics player 0
	//unsigned char GRP1;			//          $1C  GRP1    xxxx xxxx  graphics player 1
	unsigned char ENAM0;			// +148, 1: $1D  ENAM0   0000 00x0  graphics (enable) missile 0
	unsigned char ENAM1;			// +149, 1: $1E  ENAM1   0000 00x0  graphics (enable) missile 1
	unsigned char ENABL;			// +150, 1: $1F  ENABL   0000 00x0  graphics (enable) ball
	unsigned char HMP0;			// +151, 1: $20  HMP0    xxxx 0000  horizontal motion player 0
	unsigned char HMP1;			// +152, 1: $21  HMP1    xxxx 0000  horizontal motion player 1
	unsigned char HMM0;			// +153, 1: $22  HMM0    xxxx 0000  horizontal motion missile 0
	unsigned char HMM1;			// +154, 1: $23  HMM1    xxxx 0000  horizontal motion missile 1
	unsigned char HMBL;			// +155, 1: $24  HMBL    xxxx 0000  horizontal motion ball
	//unsigned char VDELP0;			//          $25  VDELP0  0000 000x  vertical delay player 0
	//unsigned char VDELP1;			//          $26  VDELP1  0000 000x  vertical delay player 1
	//unsigned char VDELBL;			//          $27  VDELBL  0000 000x  vertical delay ball
	unsigned char RESMP0;			// +156, 1: $28  RESMP0  0000 00x0  reset missile 0 to player 0
	unsigned char RESMP1;			// +157, 1: $29  RESMP1  0000 00x0  reset missile 1 to player 1
	//unsigned char HMOVE;			//          $2A  HMOVE   ---- ----  apply horizontal motion
	//unsigned char HMCLR;			//          $2B  HMCLR   ---- ----  clear horizontal motion registers
	//unsigned char CXCLR;			//          $2C  CXCLR   ---- ----  clear collision latches
	/*}}書き込みレジスタ*/

	/*{{読み出しレジスタ*/
	//unsigned char CXM0P;			//          $0   CXM0P   xx00 0000  read collision M0-P1 M0-P0
	//unsigned char CXM1P;			//          $1   CXM1P   xx00 0000  read collision M1-P0 M1-P1
	//unsigned char CXP0FB;			//          $2   CXP0FB  xx00 0000  read collision P0-PF P0-BL
	//unsigned char CXP1FB;			//          $3   CXP1FB  xx00 0000  read collision P1-PF P1-BL
	//unsigned char CXM0FB;			//          $4   CXM0FB  xx00 0000  read collision M0-PF M0-BL
	//unsigned char CXM1FB;			//          $5   CXM1FB  xx00 0000  read collision M1-PF M1-BL
	//unsigned char CXBLPF;			//          $6   CXBLPF  x000 0000  read collision BL-PF unused
	//unsigned char CXPPMM;			//          $7   CXPPMM  xx00 0000  read collision P0-P1 M0-M1
	unsigned char INPT0;			// +158, 1: $8   INPT0   x000 0000  read pot port
	unsigned char INPT1;			// +159, 1: $9   INPT1   x000 0000  read pot port
	unsigned char INPT2;			// +160, 1: $A   INPT2   x000 0000  read pot port
	unsigned char INPT3;			// +161, 1: $B   INPT3   x000 0000  read pot port
	unsigned char INPT4;			// +162, 1: $C   INPT4   x000 0000  read input
	unsigned char INPT5;			// +163, 1: $D   INPT5   x000 0000  read input
	/*}}読み出しレジスタ*/
} TIA;						// =164
#define SIZEOF_TIA				    164 /* tia_reset()時にサイズを検査します */

/****************************************************************************
 *	グローバル変数
 ****************************************************************************/

extern TIA tia; /* TIA core */

#ifdef TIA_GRAYSCALE
extern const unsigned char tia_grayscale_table[16/*Color*/ * 8/*Luminosity*/];
#else /*TIA_GRAYSCALE*/
extern const unsigned char tia_color_table[16/*Color*/ * 8/*Luminosity*/][3/*R,G,B*/];
#endif /*TIA_GRAYSCALE*/

extern const unsigned char tia_div3_table[256];
extern const unsigned char tia_priority_table[2][1 << 6];
extern const unsigned short tia_collision_table[1 << 6];

extern int (*tia_write_table[0x2c+1])(int data);
extern int (*tia_read_table[0xf+1])();

/****************************************************************************
 *	内部関数
 ****************************************************************************/

void tia_scan_progress(int now);
void tia_partial_update();
void tia_line_update(unsigned y, unsigned x1, unsigned x2);

void tia_volume_shift(short wbuff[/*TIABUFLEN*/]);			/* 30KHz=3.58MHz/114, 10KHz=3.58MHz/3/114=1.19MHz/114 */
void tia_audio_mix0(TIAAUDIO* audio, short wbuff[/*TIABUFLEN*/]);	/*                                               1 -> */
void tia_audio_mix1(TIAAUDIO* audio, short wbuff[/*TIABUFLEN*/]);	/* 30KHz/Freq pulse ----------------> Poly4 output -> */
void tia_audio_mix2(TIAAUDIO* audio, short wbuff[/*TIABUFLEN*/]);	/* 30KHz/Freq pulse -> Div31 pulse -> Poly4 output -> */
void tia_audio_mix3(TIAAUDIO* audio, short wbuff[/*TIABUFLEN*/]);	/* 30KHz/Freq pulse -> Poly5 pulse -> Poly4 output -> */
void tia_audio_mix4(TIAAUDIO* audio, short wbuff[/*TIABUFLEN*/]);	/* 30KHz/Freq pulse ----------------> Polar toggle -> */
//void tia_audio_mix5(TIAAUDIO* audio, short wbuff[/*TIABUFLEN*/]);	/* 30KHz/Freq pulse ----------------> Polar toggle -> */ /* 4と同じ */
void tia_audio_mix6(TIAAUDIO* audio, short wbuff[/*TIABUFLEN*/]);	/* 30KHz/Freq pulse -> Div31 pulse -> Polar toggle -> */ /* 6とEの違いは、30KHz/10KHzのみ */
void tia_audio_mix7(TIAAUDIO* audio, short wbuff[/*TIABUFLEN*/]);	/* 30KHz/Freq pulse -> Poly5 pulse -> Polar toggle -> */ /* 7とFの違いは、30KHz/10KHzのみ */
void tia_audio_mix8(TIAAUDIO* audio, short wbuff[/*TIABUFLEN*/]);	/* 30KHz/Freq pulse ----------------> Poly9 output -> */
void tia_audio_mix9(TIAAUDIO* audio, short wbuff[/*TIABUFLEN*/]);	/* 30KHz/Freq pulse ----------------> Poly5 output -> */
void tia_audio_mixA(TIAAUDIO* audio, short wbuff[/*TIABUFLEN*/]);	/* 30KHz/Freq pulse -> Div31 pulse -> Poly5 output -> */
//void tia_audio_mixB(TIAAUDIO* audio, short wbuff[/*TIABUFLEN*/]);	/* 30KHz/Freq pulse -> Poly5 pulse -> Poly5 output -> */ /* 0と同じ (Poly5[0]=1時にPoly5[0]で出力値を更新するので、結果的に1固定) */
void tia_audio_mixC(TIAAUDIO* audio, short wbuff[/*TIABUFLEN*/]);	/* 10KHz/Freq pulse ----------------> Polar toggle -> */ /* 4とCの違いは、30KHz/10KHzのみ */
//void tia_audio_mixD(TIAAUDIO* audio, short wbuff[/*TIABUFLEN*/]);	/* 10KHz/Freq pulse ----------------> Polar toggle -> */ /* Cと同じ */
void tia_audio_mixE(TIAAUDIO* audio, short wbuff[/*TIABUFLEN*/]);	/* 10KHz/Freq pulse -> Div31 pulse -> Polar toggle -> */ /* 6とEの違いは、30KHz/10KHzのみ */
void tia_audio_mixF(TIAAUDIO* audio, short wbuff[/*TIABUFLEN*/]);	/* 10KHz/Freq pulse -> Poly5 pulse -> Polar toggle -> */ /* 7とFの違いは、30KHz/10KHzのみ */

/****************************************************************************
 *	アプリケーション用関数
 ****************************************************************************/

/* TIAコアをリセットします。
 * [in]
 *	now		サイクルカウンタ。(VCS_CLOCK単位)
 *	vbuff		仮想画面バッファ。(160x192ピクセル)
 * [note]
 *	* アプリケーションプログラムは、少なくとも最初に一度、この関数を呼び出してください。
 *	  その後は、TIAリセットを行う場合に、必要に応じて呼び出してください。
 */
void tia_reset(int now, unsigned char vbuff[/*160*192*/]);

/* TIAへ1バイト書き込みます。
 * [in]
 *	now		サイクルカウンタ。(VCS_CLOCK単位)
 *	addr		書き込むアドレス。
 *	data		書き込むデータ。
 * [out]
 *	戻り値		WSYNCへの書き込み時に、停止したサイクル数を返します。(VCS_CLOCK単位)
 *			それ以外のレジスタへの書き込み時は、0を返します。
 */
int tia_write(int now, int addr, int data);

/* PPUから1バイト読み出します。
 * [in]
 *	now		サイクルカウンタ。(VCS_CLOCK単位)
 *	addr		読み出すアドレス。
 * [out]
 *	戻り値		読み出したデータ。
 */
int tia_read(int now, int addr);

/* Latched Input Portsの入力状態を設定します。
 * [in]
 *	i4		I4ポートの入力状態。(押されていたら0、離されていたら1)
 *	i5		I5ポートの入力状態。(押されていたら0、離されていたら1)
 * [note]
 *	* Latched Input Portsの入力状態は、負論理であることに注意してください。
 */
void tia_set_i4_i5(int i4, int i5);

/* 出力サンプリングレート=SPEAKER_OUTPUT[Hz]で、320サンプルのミキシングを行います。
 * [in]
 *	wbuff		出力サンプルバッファ。
 */
void tia_mix(short wbuff[/*TIABUFLEN*/]);

#ifdef TIA_GRAYSCALE
/* 160x192x16諧調グレイスケールを、128x88x16諧調グレイスケールへ縮小コピーします。
 * [in]
 *	src		160x192x16諧調グレイスケール画面バッファ。
 *	dst		128x 88x16諧調グレイスケール画面バッファ。
 *
 * [note]
 *	* srcとdstは同じアドレスを指していても構いません。
 *	  最終的に128x88の画面イメージだけが必要ならば、160x192と128x88の画面バッファを共通化して、
 *	  メモリを節約できます。
 */
void tia_reduce(unsigned char dst[/*128*88*/], const unsigned char src[/*160*192*/]);
#endif TIA_GRAYSCALE

/****************************************************************************
 *	PIA (Peripheral Interface Adaptor) (MOS6532, RIOTとも呼ばれます)
 ****************************************************************************/

#define PIA_CLOCK	VCS_CLOCK

/* 本当は、TIAとPIAは完全に別の、それぞれ独立したLSIです。
 * ATARI VCSにおいては、TIAとPIAが、もっとも重要な構成部品です。
 * PIAは規模が小さいので、TIAと同じモジュールに含めることにしました。
 */
typedef struct _PIA {
	/*{{レジスタ*/
	unsigned char SWCHA;			// $280  SWCHA   Port A; input or output (read or write)
	unsigned char SWACNT;			// $281  SWACNT  Port A DDR; 0 = input, 1 = output
	unsigned char SWCHB;			// $282  SWCHB   Port B; console switches (read only)
	unsigned char SWBCNT;			// $283  SWBCNT  Port B DDR; hardwired as input
	//unsigned char INTIM;			// $284  INTIM   Timer output (read only)
	//unsigned char TIM1T;			// $294  TIM1T   set    1 clock interval (838   nsec/interval)
	//unsigned char TIM8T;			// $295  TIM8T   set    8 clock interval (  6.7 usec/interval)
	//unsigned char TIM64T;			// $296  TIM64T  set   64 clock interval ( 53.6 usec/interval)
	//unsigned char T1024T;			// $297  T1024T  set 1024 clock interval (858.2 usec/interval)
	/*}}レジスタ*/

	int now;		/* 前回アクセス時のサイクルカウンタを記憶 */
	int shift;		/* INTIM読み出し時のタイマカウンタ右シフト数 */
	unsigned timer;		/* タイマカウンタ (VCS_CLOCK単位ダウンカウンタ) */
} PIA;

extern PIA pia;

/* PIAコアをリセットします。
 * [in]
 *	now		サイクルカウンタ。(VCS_CLOCK単位)
 * [note]
 *	* アプリケーションプログラムは、少なくとも最初に一度、この関数を呼び出してください。
 *	  その後は、PIAリセットを行う場合に、必要に応じて呼び出してください。
 */
void pia_reset(int now);

/* PIAへ1バイト書き込みます。
 * [in]
 *	now		サイクルカウンタ。(VCS_CLOCK単位)
 *	addr		書き込むアドレス。
 *	data		書き込むデータ。
 */
void pia_write(int now, int addr, int data);

/* PPUから1バイト読み出します。
 * [in]
 *	now		サイクルカウンタ。(VCS_CLOCK単位)
 *	addr		読み出すアドレス。
 * [out]
 *	戻り値		読み出したデータ。
 */
int pia_read(int now, int addr);

#endif /*__CLIP_TIA_H__*/
