/*	
 *	clipuvc.c
 *
 *	USB Video Class (UVC) Device
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2015 Naoyuki Sawa
 *
 *	* Tue May 12 21:55:19 JST 2015 Naoyuki Sawa
 *	- 1st リリース。
 */
#include "clip.h"
/****************************************************************************
 *	定数
 ****************************************************************************/
//'VS Uncompressed Frame Type Descriptor'と'Probe/Commit'の、フレームレートを定義します。
//上記のフレームレートは、amcap.exe等の設定画面に表示されるフレームレートと、amcap.exe等がaviファイルを作成する時のフレームレートにのみ影響します。
//上記のフレームレートは、amcap.exe等で表示する、見た目のフレームレートには影響しません。
//見た目のフレームレートは、デバイスがデータを送出する速度に依存します。
#undef  FPS	60
#define FPS	20	//実験したところ、全力で送出すると20fpsぐらい出るようなので、20fpsと定義する事にした。本当はUSBの速度に依存してfpsを決めるのではなく、fpsを先に決めてそれに従ったタイミングで送出すべきである。
/****************************************************************************
 *	グローバル変数
 ****************************************************************************/
ST_UVC stUVC;
/****************************************************************************
 *	ローカル関数宣言
 ****************************************************************************/
static void get_descriptor(const DEVICE_REQUEST* req, const unsigned char* dat, int len);
static void set_configuration(const DEVICE_REQUEST* req, const unsigned char* dat, int len);
static void set_interface(const DEVICE_REQUEST* req, const unsigned char* dat, int len);
static void class_request(const DEVICE_REQUEST* req, const unsigned char* dat, int len);
static void ep2_txdone();
static void new_pceLCDTrans();
#ifndef PIECE
static int ep2_txdone_sub(const uint8_t* txbuf, int txpos, int len);
static void new_pceLCDTrans_sub(uint8_t* dst, const uint8_t* src, int len);
#else //PIECE
/*static*/ int ep2_txdone_sub(const uint8_t* txbuf, int txpos, int len);
/*static*/ void new_pceLCDTrans_sub(uint8_t* dst, const uint8_t* src, int len);
#endif//PIECE
/****************************************************************************
 *	USBフック情報
 ****************************************************************************/
static const USBINF usb_inf = {
	//USBC mode
	.mode			= D12_ENDP_NONISO,
	//USBC raw event
	.ep2_txdone		= ep2_txdone,
	//Device request
	.get_descriptor		= get_descriptor,
	.set_configuration	= set_configuration,
	.set_interface		= set_interface,
	//Class request
	.class_request		= class_request,
};
/****************************************************************************
 *	ディスクリプタ
 ****************************************************************************/
//------------------ Device Descriptor ------------------
static const uint8_t TBL_DeviceDescriptor[]={
0x12,						//bLength			0x12		18 bytes
DEVICE_DESCRIPTOR_TYPE,				//bDescriptorType		0x01		Device Descriptor
USB_UINT16(0x0200),				//bcdUSB			0x0200		USB Version 2.00
0xEF,						//bDeviceClass			0xEF		Miscellaneous					┐
0x02,						//bDeviceSubClass		0x02								├Interface Association Descriptor. The usage of this class code triple is defined in the Interface Association Descriptor ECN that is provided on www.usb.org . This class code may only be used in Device Descriptors.(http://www.usb.org/developers/defined_class/#BaseClassEFh)
0x01,						//bDeviceProtocol		0x01		IAD						┘
EP0_PACKET_SIZE,				//bMaxPacketSize0		0x10		16 bytes
USB_UINT16(PCE_VENDOR_ID),			//idVendor			PCE_VENDOR_ID
USB_UINT16(UVC_PRODUCT_ID),			//idProduct			UVC_PRODUCT_ID
USB_UINT16(0x0001),				//bcdDevice			0x0001
0x01,						//iManufacturer			0x01		String Descriptor 1
0x02,						//iProduct			0x02		String Descriptor 2
0x00,						//iSerialNumber			0x00		No String Descriptor
0x01,						//bNumConfigurations		0x01
};
//-------------- Configuration Descriptor ---------------
static const uint8_t TBL_ConfigurationDescriptor[]={
0x09,						//bLength			0x09		9 bytes
CONFIGURATION_DESCRIPTOR_TYPE,			//bDescriptorType		0x02		Configuration Descriptor
USB_UINT16(0x00AA),				//wTotalLength					? bytes
0x02,						//bNumInterfaces		0x02
0x01,						//bConfigurationValue		0x01
0x00,						//iConfiguration		0x00		No String Descriptor
0x80,						//bmAttributes			0x80		Bus Powered
100/2,						//MaxPower			0x32		100 mA
//------------------- IAD Descriptor --------------------
0x08,						//bLength			0x08		8 bytes
0x0B,						//bDescriptorType		0x0B
0x00,						//bFirstInterface		0x00
0x02,						//bInterfaceCount		0x02
UVC_CC_VIDEO,					//bFunctionClass		0x0E		Video
UVC_SC_VIDEO_INTERFACE_COLLECTION,		//bFunctionSubClass		0x03		Video Interface Collection
UVC_PC_PROTOCOL_UNDEFINED,			//bFunctionProtocol		0x00		PC_PROTOCOL_UNDEFINED protocol
0x02,						//iFunction			0x02		String Descriptor 2
//---------------- Interface Descriptor -----------------
0x09,						//bLength			0x09		9 bytes
INTERFACE_DESCRIPTOR_TYPE,			//bDescriptorType		0x04		Interface Descriptor
0x00,						//bInterfaceNumber		0x00
0x00,						//bAlternateSetting		0x00
0x00,						//bNumEndpoints			0x00		0 Endpoint
UVC_CC_VIDEO,					//bInterfaceClass		0x0E		Video
UVC_SC_VIDEOCONTROL,				//bInterfaceSubClass		0x01		Video Control
0x00,						//bInterfaceProtocol		0x00
0x02,						//iInterface			0x02		String Descriptor 2
//------- Video Control Interface Header Descriptor -----
0x0D,						//bLength			0x0D		13 bytes
UVC_CS_INTERFACE,				//bDescriptorType		0x24		Video Control Interface
UVC_VC_HEADER,					//bDescriptorSubtype		0x01		Video Control Header
USB_UINT16(0x0100),				//bcdUVC			0x0100		UVC Version 1.00				USB_Video_Example 1.1.pdfには'0x0110'と書いてあるが'0x0110'にするとWindowsXpでもWindows7でも動かなかった。'0x0100'にすればWindowsXpでもWindows7でも動いた。
USB_UINT16(0x0033),				//wTotalLength					? bytes
USB_UINT32(24000000),				//dwClockFreq			24000000	24 MHz
0x01,						//bInCollection			0x01		1 VideoStreaming interface
0x01,						//baInterfaceNr[1]		0x01
//-------- Video Control Input Terminal Descriptor ------
0x12,						//bLength			0x12		18 bytes
UVC_CS_INTERFACE,				//bDescriptorType		0x24		Video Control Interface
UVC_VC_INPUT_TERMINAL,				//bDescriptorSubtype		0x02		Input Terminal
0x01,						//bTerminalID			0x01
USB_UINT16(UVC_ITT_CAMERA),			//wTerminalType			0x0201		ITT_CAMERA
0x00,						//bAssocTerminal		0x00		Not associated with an Output Terminal
0x00,						//iTerminal			0x00
USB_UINT16(0x0000),				//wObjectiveFocalLengthMin	0x0000
USB_UINT16(0x0000),				//wObjectiveFocalLengthMax	0x0000
USB_UINT16(0x0000),				//wOcularFocalLength		0x0000
0x03,						//bControlSize			0x03
USB_UINT24(0x000000),				//bmControls			0x000000
//-------- Video Control Processing Unit Descriptor -----
0x0B,						//bLength			0x0B		11 bytes
UVC_CS_INTERFACE,				//bDescriptorType		0x24		Video Control Interface
UVC_VC_PROCESSING_UNIT,				//bDescriptorSubtype		0x05		Processing Unit
0x02,						//bUnitID			0x02
0x01,						//bSourceID			0x01
USB_UINT16(0x0000),				//wMaxMultiplier		0x0000
0x02,						//bControlSize			0x02
USB_UINT16(0x0000),				//bmControls			0x0000
0x00,						//iProcessing			0x00
//------- Video Control Output Terminal Descriptor ------
0x09,						//bLength			0x09		9 bytes
UVC_CS_INTERFACE,				//bDescriptorType		0x24		Video Control Interface
UVC_VC_OUTPUT_TERMINAL,				//bDescriptorSubtype		0x03		Output Terminal
0x03,						//bTerminalID			0x03
USB_UINT16(0x0101),				//wTerminalType			0x0101		TT_STREAMING
0x00,						//bAssocTerminal		0x00		Not associated with an Input Terminal
0x02,						//bSourceID			0x02								実験したところ、ここに0x01(Input Terminal)を指定しても動作するようです。つまり、Input TerminalからOutput Terminalへ直結可能で、Processing Unitは不要です。しかし今回の実装は一般的な構成に倣って、(何もしないが)Processing Unitを挟む事にしました。
0x00,						//iTerminal			0x00
//---------------- Interface Descriptor -----------------
0x09,						//bLength			0x09		9 bytes
INTERFACE_DESCRIPTOR_TYPE,			//bDescriptorType		0x04		Interface Descriptor
0x01,						//bInterfaceNumber		0x01
0x00,						//bAlternateSetting		0x00
0x01,						//bNumEndpoints			0x01		1 Endpoint
0x0E,						//bInterfaceClass		0x0E		Video
0x02,						//bInterfaceSubClass		0x02		Video Streaming
0x00,						//bInterfaceProtocol		0x00
0x00,						//iInterface			0x00		No String Descriptor
//---- VC-Specific VS Video Input Header Descriptor -----
0x0E,						//bLength			0x0E		14 bytes
UVC_CS_INTERFACE,				//bDescriptorType		0x24		Video Streaming Interface
UVC_VC_HEADER,					//bDescriptorSubtype		0x01		Input Header
0x01,						//bNumFormats			0x01
USB_UINT16(0x004D),				//wTotalLength					? bytes
0x82,						//bEndpointAddress		0x82		Direction=IN, EndpointID=2
0x00,						//bmInfo			0x00		Dynamic Format Change not supported
0x03,						//bTerminalLink			0x03
0x00,						//bStillCaptureMethod		0x00
0x00,						//nbTriggerSupport		0x00
0x00,						//bTriggerUsage			0x00
0x01,						//nbControlSize			0x01
0x00,						//Video Payload Format 1	0x00
//------- VS Uncompressed Format Type Descriptor --------
0x1B,						//bLength			0x1B		27 bytes
UVC_CS_INTERFACE,				//bDescriptorType		0x24		Video Streaming Interface
UVC_VS_FORMAT_UNCOMPRESSED,			//bDescriptorSubtype		0x04		Uncompressed Format Type
0x01,						//bFormatIndex			0x01
0x01,						//bNumFrameDescriptors		0x01
USB_UINT32(0x32595559),				//guidFormat			{32595559-0000-0010-8000-00AA00389B71} (YUY2)			'NV12'にするとWindowsXpでもWindows7でもamcap.exeで'This graph cannot preview'エラーになる。
USB_UINT16(0x0000),				//
USB_UINT16(0x0010),				//
0x80,0x00,0x00,0xAA,				//
0x00,0x38,0x9B,0x71,				//
0x10,						//bBitsPerPixel			0x10
0x01,						//bDefaultFrameIndex		0x01
0x00,						//bAspectRatioX			0x00
0x00,						//bAspectRatioY			0x00
0x00,						//bmInterlaceFlags		0x00
0x00,						//bCopyProtect			0x00		No restrictions
//-------- VS Uncompressed Frame Type Descriptor --------
0x1E,						//bLength			0x1E		30 bytes
UVC_CS_INTERFACE,				//bDescriptorType		0x24		Video Streaming Interface
UVC_VS_FRAME_UNCOMPRESSED,			//bDescriptorSubtype		0x05		Uncompressed Frame Type
0x01,						//bFrameIndex			0x01
0x00,						//bmCapabilities		0x01
USB_UINT16(DISP_X),				//wWidth			
USB_UINT16(DISP_Y),				//wHeight			
USB_UINT32(DISP_X*DISP_Y*2*FPS),		//dwMinBitRate					┐
USB_UINT32(DISP_X*DISP_Y*2*FPS),		//dwMaxBitRate					├動作には関係無いみたい(?)です。		1ピクセル2バイトというのはYUV2のピクセルフォーマットです。
USB_UINT32(DISP_X*DISP_Y*2),			//dwMaxVideoFrameBufferSize			┘
USB_UINT32(10000000/FPS),			//dwDefaultFrameInterval			Frame interval in 100 ns units. -> ? fps
0x01,						//bFrameIntervalType		0x01
USB_UINT32(10000000/FPS),			//adwFrameInterval[1]				Frame interval in 100 ns units. -> ? fps
//------- VS Color Matching Descriptor Descriptor -------
0x06,						//bLength			0x06		6 bytes
UVC_CS_INTERFACE,				//bDescriptorType		0x24		Video Streaming Interface
UVC_VS_COLORFORMAT,				//bDescriptorSubtype		0x0D		Color Matching
0x01,						//bColorPrimaries		0x01		BT.709, sRGB
0x01,						//bTransferCharacteristics	0x01		BT.709
0x01,						//bMatrixCoefficients		0x04		BT.709
//----------------- Endpoint Descriptor -----------------
0x07,						//bLength			0x07		7 bytes
ENDPOINT_DESCRIPTOR_TYPE,			//bDescriptorType		0x05		Endpoint Descriptor
0x82,						//bEndpointAddress		0x82		Direction=IN, EndpointID=2
0x02,						//bmAttributes			0x02		TransferType=Bulk
USB_UINT16(EP2_NONISO_PACKET_SIZE),		//wMaxPacketSize		0x0040		64 bytes
0x00,						//bInterval			0x00		ignored
};
//------------------ String Descriptors -----------------
static const uint8_t* const TBL_StringDescriptors[]={
(const uint8_t []){
0x04,						//bLength			0x04		4 bytes
STRING_DESCRIPTOR_TYPE,				//bDescriptorType		0x03		String Descriptor
USB_UINT16(0x0409),				//Language ID[0]		0x0409		English - United States
},
(const uint8_t []){
0x16,						//bLength			0x1C		28 bytes
STRING_DESCRIPTOR_TYPE,				//bDescriptorType		0x03		String Descriptor
USB_UINT16('P'),				//						'Piece Lab.'
USB_UINT16('i'),				//
USB_UINT16('e'),				//
USB_UINT16('c'),				//
USB_UINT16('e'),				//
USB_UINT16(' '),				//
USB_UINT16('L'),				//
USB_UINT16('a'),				//
USB_UINT16('b'),				//
USB_UINT16('.'),				//
},
(const uint8_t []){
0x22,						//bLength			0x28		40 bytes
STRING_DESCRIPTOR_TYPE,				//bDescriptorType		0x03		String Descriptor
USB_UINT16('P'),				//						'P/ECE USB device'
USB_UINT16('/'),				//
USB_UINT16('E'),				//
USB_UINT16('C'),				//
USB_UINT16('E'),				//
USB_UINT16(' '),				//
USB_UINT16('U'),				//
USB_UINT16('S'),				//
USB_UINT16('B'),				//
USB_UINT16(' '),				//
USB_UINT16('d'),				//
USB_UINT16('e'),				//
USB_UINT16('v'),				//
USB_UINT16('i'),				//
USB_UINT16('c'),				//
USB_UINT16('e'),				//
}};
/****************************************************************************
 *	
 ****************************************************************************/
void UVC_Start() {
	//確実に終了しておく。
	UVC_Stop();
	//pceLCDTrans()をフックする。
	stUVC.old_pceLCDTrans = pceVectorSetKs(KSNO_LCDTrans, new_pceLCDTrans);
	//USBのフックを開始する。
	usb_start(&usb_inf);
}
/*--------------------------------------------------------------------------*/
void UVC_Stop() {
	//まだUVC_Start()が呼ばれていなければ何もせずに帰る。
	if(!stUVC.old_pceLCDTrans) { return; }
	//USBのフックを終了する。
	usb_stop();
	//pceLCDTrans()のフックを解除する。
	pceVectorSetKs(KSNO_LCDTrans, stUVC.old_pceLCDTrans);
	//構造体をクリアする。
	memset(&stUVC, 0, sizeof stUVC);
}
/****************************************************************************
 *	
 ****************************************************************************/
static void get_descriptor(const DEVICE_REQUEST* req, const unsigned char* dat, int len) {
	int type  = HIBYTE(req->wValue);
	int index = LOBYTE(req->wValue);
	switch(type) {
	case DEVICE_DESCRIPTOR_TYPE:
		{
			usb_ep0_tx(&TBL_DeviceDescriptor, sizeof TBL_DeviceDescriptor);
			return;
		}
		break;
	case CONFIGURATION_DESCRIPTOR_TYPE:
		if(index == 0) {
			usb_ep0_tx(&TBL_ConfigurationDescriptor, sizeof TBL_ConfigurationDescriptor);
			return;
		}
		break;
	case STRING_DESCRIPTOR_TYPE:
		if(index < ARRAY_SIZE(TBL_StringDescriptors)) {
			usb_ep0_tx(TBL_StringDescriptors[index], TBL_StringDescriptors[index][0]);
			return;
		}
		break;
	}
	usb_ep0_stall();	//未対応
}
/*--------------------------------------------------------------------------*/
static void set_configuration(const DEVICE_REQUEST* req, const unsigned char* dat, int len) {
	D12_SetEndpointEnable(req->wValue == 1);
	usb_ep0_tx(NULL, 0);	//ACK
}
/*--------------------------------------------------------------------------*/
static void set_interface(const DEVICE_REQUEST* req, const unsigned char* dat, int len) {
	usb_ep0_tx(NULL, 0);	//ACK
}
/****************************************************************************
 *	
 ****************************************************************************/
static const uint8_t TBL_VideoProbeAndCommitGetCur[]={
USB_UINT16(0x0000),				//bmHint
0x01,						//bFormatIndex
0x01,						//bFrameIndex
USB_UINT32(10000000/FPS),			//dwFrameInterval				Frame interval in 100 ns units. -> ? fps
USB_UINT16(0x0000),				//wKeyFrameRate
USB_UINT16(0x0000),				//wPFrameRate
USB_UINT16(0x0000),				//wCompQuality
USB_UINT16(0x0000),				//wCompWindowSize
USB_UINT16(0x0000),				//wDelay
USB_UINT32(  DISP_X*DISP_Y*2),			//dwMaxVideoFrameSize										1ピクセル2バイトというのはYUV2のピクセルフォーマットです。
USB_UINT32(2+DISP_X*DISP_Y*2),			//dwMaxPayloadTransferSize			'2'はペイロードヘッダの分です。			もしアイソクロナス転送を使う場合は、この値はwMaxPacketSizeと同じでなければなりません。そうしないとamcap.exeがエラーになります。バルク転送を使う場合は、その制限はありません。
};
/*--------------------------------------------------------------------------*/
static const uint8_t TBL_VideoProbeAndCommitGetLen[]={
sizeof TBL_VideoProbeAndCommitGetCur,
};
/*--------------------------------------------------------------------------*/
static const uint8_t TBL_VideoProbeAndCommitGetInfo[]={
(1<<0)|(1<<1),				//GET/SET requests are supported.
};
/*--------------------------------------------------------------------------*/
static void class_request(const DEVICE_REQUEST* req, const unsigned char* dat, int len) {
	const int ControlSelector     = HIBYTE(req->wValue);
	const int InterfaceOrEndpoint = LOBYTE(req->wIndex);
	const int EntityIdOrZero      = HIBYTE(req->wIndex);
	switch(InterfaceOrEndpoint) {
	case 0:	//VideoControl Interface
		switch(EntityIdOrZero) {
		case 1:	//Input Terminal (Camera)
		case 2:	//Processing Unit
		case 3:	//Output Terminal
			/** no job **/
			break;
		}
		break;
	case 1:	//VideoStreaming Interface
		switch(ControlSelector) {
		case UVC_VS_PROBE_CONTROL:
		case UVC_VS_COMMIT_CONTROL:
			switch(req->bRequest) {
			case UVC_SET_CUR:
				usb_ep0_tx(NULL, 0);	//ACK
				if(ControlSelector == UVC_VS_COMMIT_CONTROL) { ep2_txdone(); }	//データ送出開始
				return;
			case UVC_GET_CUR:
			case UVC_GET_MIN:
			case UVC_GET_MAX:
			case UVC_GET_DEF:
			case UVC_GET_RES:													//WindowsXpとWindows7でamcap.exeを使った場合はこのリクエストは来なかった。
				usb_ep0_tx(TBL_VideoProbeAndCommitGetCur, sizeof TBL_VideoProbeAndCommitGetCur);
				return;
			case UVC_GET_LEN:													//WindowsXpとWindows7でamcap.exeを使った場合はこのリクエストは来なかった。
				usb_ep0_tx(TBL_VideoProbeAndCommitGetLen, sizeof TBL_VideoProbeAndCommitGetLen);
				return;
			case UVC_GET_INFO:													//WindowsXpとWindows7でamcap.exeを使った場合はこのリクエストは来なかった。
				usb_ep0_tx(TBL_VideoProbeAndCommitGetInfo, sizeof TBL_VideoProbeAndCommitGetInfo);
				return;
			}
			break;
	    //	case UVC_VS_STILL_PROBE_CONTROL:
	    //	case UVC_VS_STILL_COMMIT_CONTROL:
	    //	case UVC_VS_STILL_IMAGE_TRIGGER_CONTROL:
	    //	case UVC_VS_STREAM_ERROR_CODE_CONTROL:
	    //	case UVC_VS_GENERATE_KEY_FRAME_CONTROL:
	    //	case UVC_VS_UPDATE_FRAME_SEGMENT_CONTROL:
	    //	case UVC_VS_SYNCH_DELAY_CONTROL:
		}
		break;
	}
	//The Device may support class-specific requests.
	//The device shall return STALL if an unrecognized or unsupported device-specific request is received.
	usb_ep0_stall();	//未対応
}
/*--------------------------------------------------------------------------*/
//USB_Video_Class_1.1.pdf「Figure 2-11 Sample Bulk Read (Single Transfer per Sample)」の転送方式を使いました。
static void ep2_txdone() {
	int len, end;
#if 0
	//ダブルバッファに空きが有る限り繰り返す。少なくとも片面,可能ならば両面に書き込めるはずである。
	//1面目に書き込んだバッファが2面目の書き込み中に転送完了したら、3面以上書き込める可能性も有る。
	while(!(D12_SelectEndpoint(EP2IN) & D12_FULLEMPTY)) {
#else
	//↑本当はこうしたかったのだが、実際に試してみると、割り込み処理の負荷が高くなり過ぎて前面の処理が止まってしまったので…
	//↓やむを得ずこうした。片面しか利用できていないが、見た目のフレームレートにはさほど違いは無いようだ。
	if(!(D12_SelectEndpoint(EP2IN) & D12_FULLEMPTY)) {
#endif
		len = ((DISP_X * DISP_Y) - stUVC.txpos) * 2/*YUV2*/;	//現在の定義で(len=0)になる事は有りませんが、(len=0)になる定義にしても正しく動作します。
		if(    len > EP2_NONISO_PACKET_SIZE) { len = EP2_NONISO_PACKET_SIZE; }
		end = (len < EP2_NONISO_PACKET_SIZE);
		D12OutCmd(D12_WRITE_BUFFER);
		D12OutDat(  0);//ダミー
		D12OutDat(len);//送信バイト数				//現在の定義で(len=0)になる事は有りませんが、(len=0)になる定義にしても正しく動作します。
		if(!stUVC.txpos) {
			//'Single Transfer per Sample'の先頭にペイロードヘッダを格納する。
			D12OutDat(0x02);		//bHeaderLength
			D12OutDat(0x82|stUVC.FrameID);	//bmHeaderInfo	D0: Frame ID, D1: End of Frame, D2: Presentation Time, D3: Source Clock Reference, D4: Reserved, D5: Still Image, D6: Error, D7: End of header
			len -= 2;
		}
		if(len) {	//ep2_txdone_sub()側でdo〜whileループを使い易いように、呼び出し側(len=0)を除外しておく事にした。
			stUVC.txpos = ep2_txdone_sub(stUVC.txbuf, stUVC.txpos, len);
		}
		D12_ValidateBuffer(EP2IN);
		if(end) {
			stUVC.txpos    = 0;
			stUVC.FrameID ^= 0x01;	//bmHeaderInfo	D0: Frame ID
		}
	}
}
/*--------------------------------------------------------------------------*/
#ifndef PIECE
static int ep2_txdone_sub(const uint8_t* txbuf, int txpos, int len) {
	do {
		int c = txbuf[txpos >> 2];
		c >>= ((txpos &  3) << 1);
		c  &= 3;
	    //{{高速化
	    //	c  |= (c << 2);
	    //	c  |= (c << 4);
	    //↓高速化
		c = mltu_h(c, 0x55);
	    //}}高速化
		D12OutDat(c);		//Y
		D12OutDat(0x80);	//U or V
		txpos++;
	} while(len -= 2);
	return txpos;
}
#else //PIECE
/*static*/ int ep2_txdone_sub(const uint8_t* txbuf, int txpos, int len);
asm("
		.code
		.align		1
ep2_txdone_sub:
		;//%r12 := txbuf
		;//%r13 := txpos
		;//%r14 := len
		pushn		%r3				;//
		ld.w		%r0, %r12			;//%r0  := txbuf
		ld.w		%r1, %r13			;//%r1  := txpos
		ld.w		%r2, %r14			;//%r2  := len
		xld.w		%r3, 0x55			;//%r3  := 0x55
ep2_txdone_sub_LOOP:						;//do {
		ld.w		%r12, %r1			;//  %r12 :=             txpos
		sra		%r12, 2				;//  %r12 :=             txpos >> 2
		add		%r12, %r0			;//  %r12 :=      &txbuf[txpos >> 2]
		ld.ub		%r12, [%r12]			;//  %r12 := c   = txbuf[txpos >> 2]
		xand		%r9, %r1, 3			;//  %r9  :=            (txpos &  3)
		sra		%r12, %r9			;//  %r12 := c >>=      (txpos &  3)
		sra		%r12, %r9			;//  %r12 := c >>=      (txpos &  3)
		and		%r12, 3				;//  %r12 := c  &= 3
		mltu.h		%r12, %r3			;//  %alr := c  *= 0x55
		xcall.d		D12OutDat			;//  D12OutDat(c)
		ld.w		%r12, %alr			;//  %r12 :=   c			*delay*
	;//	xld.w		%r12, 0x80			;//  %r12 :=   0x80	─┐
		ld.w		%r12, 1				;//  %r12 :=      1	←┤高速化
		xcall.d		D12OutDat			;//  D12OutDat(0x80)	　│
		sla		%r12, 7				;//  %r12 :=   0x80	←┘		*delay*
		sub		%r2, 2				;//  %r2  := len -= 2
		jrne.d		ep2_txdone_sub_LOOP		;//} while(  len)
		add		%r1, 1				;//  %r1  := txpos++			*delay*
		ld.w		%r10, %r1			;//%r10 := txpos
		popn		%r3				;//
		ret						;//return  txpos
");
#endif//PIECE
/****************************************************************************
 *	
 ****************************************************************************/
static void new_pceLCDTrans() {
	//仮想画面のスナップショットを取得する。
	new_pceLCDTrans_sub(
		stUVC.txbuf,			//dst
		pceLCDSetBuffer(INVALIDPTR),	//src
		DISP_X * DISP_Y);		//len
	//フックする前のpceLCDTrans()を呼び出す。
	(*stUVC.old_pceLCDTrans)();
}
/*--------------------------------------------------------------------------*/
#ifndef PIECE
static void new_pceLCDTrans_sub(uint8_t* dst, const uint8_t* src, int len) {
	do {
		int c  = *src++;
		    c |= *src++ << 2;
		    c |= *src++ << 4;
		    c |= *src++ << 6;
		*dst++ = ~c;
	} while(len -= 4);
}
#else //PIECE
/*static*/ void new_pceLCDTrans_sub(uint8_t* dst, const uint8_t* src, int len);
asm("
		.code
		.align		1
new_pceLCDTrans_sub:						;//do {
		;//%r12 := dst
		;//%r13 := src
		;//%r14 := len
		ld.ub		%r4, [%r13]+			;//  %r4  := c  = *src++
		ld.ub		%r5, [%r13]+			;//  %r5  := c1 = *src++
		ld.ub		%r6, [%r13]+			;//  %r6  := c2 = *src++
		ld.ub		%r7, [%r13]+			;//  %r7  := c3 = *src++
		sla		%r5, 2				;//  %r5  := c1 <<= 2
		sla		%r6, 4				;//  %r6  := c2 <<= 4
		sla		%r7, 6				;//  %r7  := c3 <<= 6
		or		%r4, %r5			;//  %r4  := c |= c1
		or		%r4, %r6			;//  %r4  := c |= c2
		or		%r4, %r7			;//  %r4  := c |= c3
		not		%r4, %r4			;//  %r4  := c = ~c
		ld.b		[%r12]+, %r4			;//  *dst++ = c
		sub		%r14, 4				;//  %r14 := len -= 4
		jrne		new_pceLCDTrans_sub		;//} while(  len)
		ret						;//
");
#endif//PIECE
