/*	
 *	clipppu.h
 *
 *	P/ECE PPU (RICOH RP2C02) Emulator
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2005 Naoyuki Sawa
 *
 *	* Sun Jan 30 21:25:00 JST 2005 Naoyuki Sawa
 *	- 作成開始。
 */
#ifndef __CLIP_PPU_H__
#define __CLIP_PPU_H__

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

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

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

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

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

#ifdef PIECE
#define PPU_GRAYSCALE
#endif /*PIECE*/

/* ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★ */
/* ★アセンブラルーチンから直接参照しているので、フォーマット固定です。★ */
/* ★フォーマットを変更したときは、framppua.sの定義も追従してください。★ */
/* ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★ */
typedef struct _PPU {
	/*{{Registers*/
	unsigned char ppu_control1;		// + 0, 1: $2000 PPU Control Register #1 (W)
						//                  D7: Execute NMI on VBlank
						//                         0 = Disabled
						//                         1 = Enabled
						//                  D6: PPU Master/Slave Selection --+
						//                         0 = Master                +-- UNUSED
						//                         1 = Slave               --+
						//                  D5: Sprite Size
						//                         0 = 8x8
						//                         1 = 8x16
						//                  D4: Background Pattern Table Address
						//                         0 = $0000 (VRAM)
						//                         1 = $1000 (VRAM)
						//                  D3: Sprite Pattern Table Address
						//                         0 = $0000 (VRAM)
						//                         1 = $1000 (VRAM)
						//                  D2: PPU Address Increment
						//                         0 = Increment by 1
						//                         1 = Increment by 32
						//               D1-D0: Name Table Address
						//                        00 = $2000 (VRAM)
						//                        01 = $2400 (VRAM)
						//                        10 = $2800 (VRAM)
						//                        11 = $2C00 (VRAM)
	unsigned char ppu_control2;		// + 1, 1: $2001 PPU Control Register #2 (W)
						//               D7-D5: Full Background Color (when D0 == 1)
						//                       000 = None  +------------+
						//                       001 = Green              | NOTE: Do not use more than one type
						//                       010 = Blue               |
						//                       100 = Red   +------------+
						//                       (※でも、背景色はImagePalette#0で指定するんですよね? ここの設定はどこに反映されるのでしょうか?)
						//               D7-D5: Color Intensity (when D0 == 0)
						//                       000 = None            +--+
						//                       001 = Intensify green    | NOTE: Do not use more than one type
						//                       010 = Intensify blue     |
						//                       100 = Intensify red   +--+
						//                  D4: Sprite Visibility
						//                         0 = Sprite not displayed
						//                         1 = Sprites visible
						//                  D3: Background Visibility
						//                         0 = Background not displayed
						//                         1 = Background visible
						//                  D2: Sprite Clipping
						//                         0 = Sprites invisible in left 8-pixel column
						//                         1 = No clipping
						//                  D1: Background Clipping
						//                         0 = BG invisible in left 8-pixel column
						//                         1 = No clipping
						//                  D0: Display Type
						//                         0 = Color display
						//                         1 = Monochrome display
	unsigned char ppu_status;		// + 2, 1: $2002 PPU Status Register (R)
						//                  D7: VBlank Occurrence
						//                         0 = Not Occuring
						//                         1 = In VBlank
						//                  D6: Sprite #0 Occrance
						//                         0 = Sprite #0 not founc
						//                         1 = PPU has hit sprite #0
						//                  D5: Scanline Sprite Count
						//                         0 = Eight (8) sprites or less on current scanline
						//                         1 = More than 8 sprites on current scanline
						//                  D4: VRAM Write Flag
						//                         0 = Writes to VRAM are respected
						//                         1 = Writes to VRAM are ignored
						//                NOTE: D7 is set to 0 after read occurs. (※誤りです。VBlank期間終了まで1を維持するのが正解です。)
						//                NOTE: After a read occurs, $2005 is reset, hence the next write to $2005 will be Horizontal.
						//                NOTE: After a read occurs, $2006 is reset, hence the next write to $2006 will be the high byte portion.
	unsigned char sprram_address;		// + 3, 1: $2003 SPR-RAM Address Register (W)
						//               D7-D0: 8-bit address in SPR-RAM to access via $2004.
						//         $2004 SPR-RAM I/O Register (W)
						//               D7-D0: 8-bit data written to SPR-RAM.
	unsigned short vram_address1;		// + 4, 2: $2005 VRAM Address Register #1 (W2)
						//                  Commonly used to "pan/scroll" the screen (sprites excluded) horizontally and vertically.
						//                  However, there is no actual panning hardware inside the NES.
						//                  This register controls VRAM addressing lines.
	unsigned short vram_address2;		// + 6, 2: $2006 VRAM Address Register #2 (W2)
						//                  Commonly used to specify the 16-bit address in VRAM to access via $2007.
						//                  However, this register controls VRAM addressing bits,
						//                  and therefore should be used with knowledge of how it works, and when it works.
						//         $2007 VRAM I/O Register (RW)
						//               D7-D0: 8-bit data read/written from/to VRAM.
	/*}}Registers*/

	/*{{Public*/
	unsigned char flags;			// + 8, 1: D7: VBlank開始時に、ppu_update()がPPU.flags[7](PPU_FLAGS_INTERRUPT)をセットします。
						//             アプリケーションプログラムから、PPU_FLAGS_INTERRUPTを監視してください。
						//             ppu_update()呼び出し後に、PPU_FLAGS_INTERRUPTがセットされていることを検出したら、
						//             経過サイクル数分のCPU実行を行う★前★(←重要!!)に、NMIを発行してください。
						//             NMIを発行したら、PPU_FLAGS_INTERRUPTを手動でクリアしてください。
						//             PPUエミュレーションルーチンの関数は、PPU_FLAGS_INTERRUPTを自動でクリアしません。
						//             NMI発行後、通常の手順と同様に、経過サイクル数分のCPU実行を行ってください。
						//         D0: 0 = Horizontal mirroring, 1 = Vertical mirroring (Four-screen mirroringは未対応です)
	/*}}Public*/

	/*{{Private*/
	unsigned char vram_buffer;		// + 9, 1: VRAM読み出し遅延の再現用 (仕様)
	unsigned char scan_start;		// +10, 1: 走査区間の開始ライン (0..240) (ppu_update()のコメントを参照してください)
	unsigned char scan_end;			// +11, 1: 走査区間の終了ライン (0..240) (ppu_update()のコメントを参照してください)
	unsigned char* vbuff/*[256*240]*/;	// +12, 4: 現在の描画ターゲット
	unsigned char clut[32];			// +16,32: ColorLookUpTable (パレット展開)
	/*}}Private*/
} PPU;

/* PPU.ppu_control1 */
#define PPU_CONTROL1_NMI_ON_VBLANK			0x80
#define PPU_CONTROL1_MASTER_SLAVE_SELECTION		0x40
#define PPU_CONTROL1_SPRITE_SIZE			0x20
#define PPU_CONTROL1_BACKGROUND_PATTERN_TABLE_ADDRESS	0x10
#define PPU_CONTROL1_SPRITE_PATTERN_TABLE_ADDRESS	0x08
#define PPU_CONTROL1_PPU_ADDRESS_INCREMENT		0x04
#define PPU_CONTROL1_NAME_TABLE_ADDRESS			0x03

/* PPU.ppu_control2 */
#define PPU_CONTROL2_FULL_BACKGROUND_COLOR		0xe0
#define PPU_CONTROL2_COLOR_INTENSITY			0xe0
#define PPU_CONTROL2_SPRITE_VISIBILITY			0x10
#define PPU_CONTROL2_BACKGROUND_VISIBILITY		0x08
#define PPU_CONTROL2_SPRITE_CLIPPING			0x04
#define PPU_CONTROL2_BACKGROUND_CLIPPING		0x02
#define PPU_CONTROL2_DISPLAY_TYPE			0x01

/* PPU.ppu_status */
#define PPU_STATUS_VBLANK_OCCURRENCE			0x80
#define PPU_STATUS_SPRITE0_OCCURRENCE			0x40
#define PPU_STATUS_SCANLINE_SPRITE_COUNT		0x20
#define PPU_STATUS_VRAM_WRITE_FLAG			0x10

/* PPU.flags */
#define PPU_FLAGS_INTERRUPT				0x80
#define PPU_FLAGS_MIRRORING				0x01

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

/* iNES形式のROMイメージファイルヘッダです。
 * PPUエミュレーションとは関係ありませんが、とりあえずここで宣言しておくことにします。
 */
typedef struct _INESHEADER {
	         char nes[3];			// + 0, 3 'NES'
	         char _1a;			// + 3, 1 $1A
	unsigned char prgrom_page_count;	// + 4, 1 16K PRG-ROM page count
	unsigned char chrrom_page_count;	// + 5, 1 8K CHR-ROM page count
	unsigned char rom_control1;		// + 6, 1 ROM Control Byte #1
						//          %####VTSM
						//           |  ||||+- 0=Horizontal mirroring
						//           |  ||||   1=Vertical mirroring
						//           |  |||+-- 1=SRAM enabled
						//           |  ||+--- 1=512-byte trainer present
						//           |  |+---- 1=Four-screen mirroring
						//           +--+----- Mapper # (lower 4-bits)
	unsigned char rom_control2;		// + 7, 1 ROM Control Byte #2
						//          %####0000
						//           +--+----- Mapper # (upper 4-bits)
	unsigned char _00[8];			// + 8, 8 $00
						// +16,.. Actual 16K PRG-ROM pages (in linear order).
						//        If a trainer exists, it precedesthe first PRG-ROM page.
						// +..,.. CHR-ROM pages (in ascending order).
} INESHEADER;

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

extern PPU ppu;				/* PPU core */
extern unsigned char ppu_vram[0x4000];	/* VRAM (16KB) */
extern unsigned char ppu_sprram[0x100];	/* SPR-RAM (256B) */

#ifdef PPU_GRAYSCALE
extern const unsigned char ppu_grayscale_table[64];
#else /*PPU_GRAYSCALE*/
extern const unsigned char ppu_color_table[64][3/*R,G,B*/];
#endif /*PPU_GRAYSCALE*/

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

void ppu_vram_write(int addr, int data);
void ppu_draw_bg();
void ppu_draw_sprite     (int priority);
void ppu_draw_sprite_8x8 (int priority);
void ppu_draw_sprite_8x16(int priority);
void ppu_draw_chr      (int xpos, int ypos, int code, int palno);
void ppu_draw_chr_revx (int xpos, int ypos, int code, int palno);
void ppu_draw_chr_revy (int xpos, int ypos, int code, int palno);
void ppu_draw_chr_revxy(int xpos, int ypos, int code, int palno);

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

/* PPUコアをリセットします。
 * [in]
 *	mirroring	  D0: 0 = 水平ミラー, 1 = 垂直ミラー
 *			D7-1: 無視します。
 *			(INESヘッダのrom_control1フィールド値をそのまま渡してください。)
 * [note]
 *	* アプリケーションプログラムは、少なくとも最初に一度、この関数を呼び出してください。
 *	  その後は、PPUリセットを行う場合に、必要に応じて呼び出してください。
 */
void ppu_reset(int mirroring);

/* PPUから1バイト読み出します。
 * [in]
 *	addr		読み出すアドレス。($2000..$2007)
 *			D2:0以外はマスクするので、0..7でも構いません。
 */
int ppu_read(int addr);

/* PPUへ1バイト書き込みます。
 * [in]
 *	ppu		PPU構造体。
 *	addr		書き込むアドレス。($2000..$2007)
 *			D2:0以外はマスクするので、0..7でも構いません。
 *	data		書き込むデータ。
 */
void ppu_write(int addr, int data);

/* 画面を描画します。
 * [in]
 *	vbuff		256x240画面バッファ。(NULL:内部処理のみ、実描画無し)
 * [out]
 *	戻り値		経過サイクル数。(NTSCカラーサブキャリアクロック単位)
 *			ファミコンのCPUクロックはNTSCカラーサブキャリアの半分なので、
 *			戻り値の1/2だけ、CPUを実行してください。
 * [note]
 *	* PPU_GRAYSCALEが定義されていなければ、64パレットカラー出力となります。
 *	  PPU_GRAYSCALEが定義されていると、43諧調グレイスケール出力となります。
 *	  43諧調の理由については、clipppu.c内、ppu_grayscale_table[]のコメントを参照してください。
 *	* VBlank割り込みが発生すると、PPU_FLAGS_INTERRUPTを1にセットします。
 *	  アプリケーションプログラムからPPU_FLAGS_INTERRUPTを監視し、NMIを発行してください。
 *	  詳しくは、PPU.flagsフィールドのコメントを参照してください。
 */
int ppu_update(unsigned char vbuff[/*256*240*/]);

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

#endif /*__CLIP_PPU_H__*/
