//========================================================================================
//  FUK.java
//    en:FLOPPY Ukun
//    ja:ふろっぴU君
//  Copyright (C) 2025 TNB製作所
//========================================================================================

package xeij;

import java.awt.event.*;  //ActionEvent
import java.util.*;  //Arrays
import javax.swing.*;  //JMenu

import com.fazecast.jSerialComm.*;  //SerialPort

public class FUK {

	private static final int DMAC_CH0 = 0x00e84000;
	private static int TTT = 20000000 * 2;
	

	//設定
	public static final boolean FUK_ON = true;  //true=ふろっぴU君に対応する
	public static boolean fukOnRequest;  //true=リセット後接続する
	public static boolean fukOn;  //true=接続している

	//ポート
	public static final int FUK_VID = 0x04d8;  //ベンダーID
	public static final int FUK_PID = 0xe6b1;  //プロダクトID
	private static SerialPort fukPort1;  //ポート1
	private static SerialPort fukPort2;  //ポート2

	//メニュー
	public static JMenu fukMenu;  //ふろっぴU君メニュー
	public static JCheckBoxMenuItem fukConnectCheckBox;  //接続チェックボックス

	//fukInit ()
	//  初期化
	public static void fukInit () {
		fukOnRequest = Settings.sgsGetOnOff ("fuk");
		fukOn = false;
		fukPort1 = null;
		fukPort2 = null;
	}  //fukInit

	//fukTini ()
	//  後始末
	public static void fukTini () {
		if (fukOn) {
			fukDisconnect ();
		 	fukOn = false;
		}
		Settings.sgsPutOnOff ("fuk", fukOnRequest);
	}  //fukTini

	//fukGetMenu ()
	//  メニューを返す
	public static JMenu fukGetMenu () {
		if (fukMenu != null) {
	 		return fukMenu;
		}
		//アクションリスナー
		ActionListener actionListener = new ActionListener () {
			@Override public void actionPerformed (ActionEvent ae) {
				Object source = ae.getSource ();
				String command = ae.getActionCommand ();
				switch (command) {
				case "Connect":  //接続
				  fukOnRequest = ((JCheckBoxMenuItem) ae.getSource ()).isSelected ();
				  break;
				default:
				  System.out.println ("unknown action command " + command);
				}
			}  //actionPerformed
		};  //actionListener
		//メニュー
		fukMenu = Multilingual.mlnText (
		ComponentFactory.createMenu (
			"FLOPPY Ukun",
			fukConnectCheckBox = Multilingual.mlnText (
			  	ComponentFactory.createCheckBoxMenuItem (fukOnRequest, "Connect", actionListener),
			  	"ja", "接続")
			),
		"ja", "ふろっぴU君");
		return fukMenu;
	}  //fukGetMenu

	//fukReset ()
	//  リセット
	public static void fukReset () {
   		//System.out.println ("#reset\n");
		TickerQueue.tkqRemoveAll();
		isReadCommandR = false;
		if (fukOnRequest) {
			if (fukPort1 != null) {  //接続している
				fukPort1.writeBytes (new byte[] { (byte) 0x40 }, 1);
				//
				fukQueueInit ();
				cmdCount = 0;
				lastCmd = 0;
				tickCounter = XEiJ.mpuClockTime;
				mode = EMode.Mode_None;
				initDriveOption();
				driveOption_actuaDrive = 0;
				driveOption_drive = 0;
				tickSenseDeviceStatus = XEiJ.mpuClockTime + 10000 * TTT;
				for(int i = 0; i < 8; i++) { senseDeviceStatus[i] = -1; }
				isForceReadyStatus = false;
				isNotifyForceReady = false;
				is_do_fdc_intr = false;
				return;
			}
			if (!fukConnect ()) {
				fukOnRequest = false;
				//if (fukConnectCheckBox != null) {
				//  	fukConnectCheckBox.setSelected (false);
				//}
			}
		} else {
		  	fukDisconnect ();
		}
		fukOn = fukOnRequest;
	}  //fukReset

	//fukConnect ()
	//  接続する
	public static boolean fukConnect () {
   		//System.out.println ("#connet\n");
		if (fukPort1 != null) {  //接続している
			return true;
		}
		//ふろっぴU君のポートを探す
		SerialPort port1 = null;
		SerialPort port2 = null;
		for (SerialPort port : SerialPort.getCommPorts ()) {  //すべてのシリアル通信ポートについて
		  	if (port.getVendorID () == FUK_VID &&  //ベンダーIDが一致
		      	port.getProductID () == FUK_PID) {  //プロダクトIDが一致
		    	if (port1 == null) {  //1個目
		      		port1 = port;  //ポート1(仮)
		   		} else if (port2 == null) {  //2個目
		      		port2 = port;  //ポート2(仮)
		    	} else {  //3個目
		      		//すかじーU君改が複数接続されている
		      		//ポート1とポート2の組み合わせが分からないので「先に見つかった方」は採用できない
		     		System.out.println (Multilingual.mlnJapanese ?
		                          "ふろっぴ U 君のポートが多すぎます" :
		                          "Too many FLOPPY UKun");
		      		return false;
		    	}
		  	}
		}
		if (port2 == null) {  //見つからない
		  	System.out.println (Multilingual.mlnJapanese ?
		                      "ふろっぴ U 君のポートが見つかりません" :
		                      "FLOPPY UKun not found");
		  	return false;
		}
		//ポートを開く
		if (!port1.openPort (0, 65536, 65536)) {  //ポート1(仮)を開けない
		  	System.out.println (Multilingual.mlnJapanese ?
		                      "ふろっぴ U 君のポートを開けません" :
		                      "Cannot open FLOPPY UKun");
		  	return false;
		}
		if (!port2.openPort (0, 65536, 65536)) {  //ポート2(仮)を開けない
		  	port1.closePort ();
		  	System.out.println (Multilingual.mlnJapanese ?
		                      "ふろっぴ U 君のポートを開けません" :
		                      "Cannot open FLOPPY UKun");
		  	return false;
		}
		//パラメータを設定する
		//  9600bpsに設定されているので480Mbpsに変更する
		port1.setComPortParameters (480000000, 8, SerialPort.ONE_STOP_BIT, SerialPort.NO_PARITY);
		port2.setComPortParameters (480000000, 8, SerialPort.ONE_STOP_BIT, SerialPort.NO_PARITY);
  	  	int n1 = port1.bytesAvailable ();
	  	if (n1 > 0) {
		  	byte[] b1 = new byte[n1];
	  		port1.readBytes (b1, n1);
		}
  	  	int n2 = port2.bytesAvailable ();
	  	if (n2 > 0) {
		  	byte[] b2 = new byte[n1];
	  		port2.readBytes (b2, n1);
		}
		//タイムアウトを設定する
		//  readBytesは50msを上限として少なくとも1バイト受信するまでブロックする
		//  writeBytesは送信バッファが一杯になるまでブロックしない
		port1.setComPortTimeouts (SerialPort.TIMEOUT_READ_BLOCKING, 50, 0);
		port2.setComPortTimeouts (SerialPort.TIMEOUT_READ_BLOCKING, 50, 0);
		//ポート1とポート2を判別する
		//  50ms以内に応答がないと失敗する
		byte[] b = new byte[] { 0x00, 0x00 };
		port1.writeBytes (b, 1, 0);  //ポート1(仮)へ0x00を送信する
		port1.readBytes (b, 1, 0);  //ポート1(仮)から1バイト受信する
		port2.writeBytes (b, 1, 1);  //ポート2(仮)へ0x00を送信する
		port2.readBytes (b, 1, 1);  //ポート2(仮)から1バイト受信する
		if (b[0] == 'f' && b[1] == 'F') {  //(仮)は合っている
			;
		} else if (b[0] == 'F' && b[1] == 'f') {  //(仮)は逆
		  	//ポート1とポート2を入れ替える
		  	SerialPort port = port1;
		  	port1 = port2;
		  	port2 = port;
		} else {  //不明
		  	System.out.printf("1='%c', 2='%c'\n", b[0], b[1]);
		  	port1.closePort ();
		  	port2.closePort ();
		  	System.out.println (Multilingual.mlnJapanese ?
		                      "ふろっぴ U 君のポートを判別できません" :
		                      "Unable to identify FLOPPY UKun");
		  	return false;
		}
		//タイムアウトを再設定する
		//  readBytesは指定された長さを受信するまでブロックする。以降はSerialPortEventのgetReceivedDataを使う
		//  writeBytesは送信バッファが一杯になるまでブロックしない
		port1.setComPortTimeouts (SerialPort.TIMEOUT_READ_BLOCKING, 0, 0);
		port2.setComPortTimeouts (SerialPort.TIMEOUT_READ_BLOCKING, 0, 0);
		//初期化する
		//  ポート1へ0x80,0xfeを送信する
		//  0xffのエスケープは用いない
		port1.writeBytes (new byte[] { (byte) 0x80 }, 1);
		port1.writeBytes (new byte[] { (byte) 0xfe }, 1);
		port1.writeBytes (new byte[] { (byte) 0x40 }, 1);
		//完了
		fukPort1 = port1;
		fukPort2 = port2;
		System.out.printf (Multilingual.mlnJapanese ?
		                   "ふろっぴ U 君 (%s,%s) に接続しました\n" :
		                   "FLOPPY UKun (%s,%s) connected\n",
		                   fukPort1.getSystemPortName (),
		                   fukPort2.getSystemPortName ());
		//受信キュー
		fukQueueInit ();
		//遅延送信
		fukPoolInit ();
		//シリアルポートデータリスナーを登録する
		fukPort1.addDataListener (fukDataListener1);
		fukPort2.addDataListener (fukDataListener2);
		cmdCount = 0;
		lastCmd = 0;
		tickCounter = XEiJ.mpuClockTime;
		mode = EMode.Mode_None;
		initDriveOption();
		driveOption_actuaDrive = 0;
		driveOption_drive = 0;
		tickSenseDeviceStatus = XEiJ.mpuClockTime + 10000;
		for(int i = 0; i < 8; i++) { senseDeviceStatus[i] = -1; }
		isForceReadyStatus = false;
		isNotifyForceReady = false;
		return true;
	}  //fukConnect

	//fukDisconnect ()
	//  切断する
	public static void fukDisconnect () {
   		//System.out.println ("#disconnect\n");
		if (fukPort1 == null) {  //接続していない
		  	return;
		}
		//シリアルポートデータリスナーを解除する
		fukPort1.removeDataListener ();
		fukPort2.removeDataListener ();
		//ポートを閉じる
		System.out.printf (Multilingual.mlnJapanese ?
		                   "ふろっぴ U 君 (%s,%s) を切り離しました\n" :
		                   "FLOPPY UKun (%s,%s) disconnected\n",
		                   fukPort1.getSystemPortName (),
		                   fukPort2.getSystemPortName ());
		fukPort1.clearRTS ();
		fukPort2.clearRTS ();
		fukPort1.clearDTR ();
		fukPort2.clearDTR ();
		fukPort1.closePort ();
		fukPort2.closePort ();
		fukPort1 = null;
		fukPort2 = null;
	}  //fukDisconnect

	//fukPeek (a)
	//  ピーク
	public static int fukPeek (int a) {
   		//System.out.println ("#peek\n");
		//    a &= 0x1f;
		//    //最後に読み書きした値をそのまま返す
		return fukRead((byte)(a & 0x0F));
	}  //fukPeek
	
	//fukRead (a)
	//  リード
	public static int fukRead (byte cmd) {

//		System.out.printf (">(%02X)", cmd);

  		synchronized (mLock1) {
			check_ints1();
		}
		if (cmd == 0x01) {
			// Status(0x00E94001 レジスタ内容)
			boolean ff = false;
			if (is_do_fdc_intr) {
				ff = true;
				is_do_fdc_intr = false;
			}
			int s = -1;
			long w = 2000;
			final long SW = 200;
			switch (mode) {
			case Mode_SenseDeivceStatus_WaitStatus90:
				s = 0x90;
				mode = EMode.Mode_SenseDeivceStatus_WaitStatusD0;
				break;
			case Mode_SenseDeivceStatus_WaitStatusD0:
				s = 0xD0;
				break;
			case Mode_SenseDeivceStatus_CheckStatus:
			case Mode_SenseDeivceStatus_ReturnStatus:
				s = 0xD0;
				break;
			case Mode_SenseDeivceStatus_WaitStatus80:
				s = 0x80;
				mode = EMode.Mode_None;
				break;
			case Mode_SenseInterruptStatus_StatusD0:
				switch (modeCounter) {
				case 0:
					s = 0xD0; 
					break;
				default: 
					w  = SW;
					mode = EMode.Mode_SenseInterruptStatus_NoWait;
					break;
				}
				break;
			case Mode_SenseInterruptStatus_NoWait:
				w = SW;
				break;
			case Mode_Seek_StatusD0:
				if (++modeCounter < 3) {
					s = 0x90;
				}
				else {
					mode = EMode.Mode_Seek_NoWait;
					w = SW;
				}
				break;
			case Mode_Seek_NoWait:
				w = SW;
				break;
			case Mode_Recalibrate_Status90:
				if (++modeCounter < 2) {
					s = 0x90;
				}
				else {
					mode = EMode.Mode_Recalibrate_NoWait;
					w = SW;
				}
				break;
			case Mode_Recalibrate_NoWait:
				w = SW;
				break;
			case Mode_ReadData_Command:
				s = 0x90;
				break;
			case Mode_ReadData_Receive:
				s = 0x10;
				break;
			case Mode_ReadData_SoftReceive:
				s = 0xF0;
				break;
			case Mode_ReadData_Response:
				s = 0xD0;
				break;
			case Mode_ReadData_After:
				switch (modeCounter) {
				case 1:
					s = 0xD0;
					break;
				default:
					s = 0x80;
					break;
				}
				break;
			case Mode_WriteData_Command:
				s = 0x90;
				break;
			case Mode_WriteData_Send:
				s = 0x10;
				break;
			case Mode_WriteData_Response:
				s = 0xD0;
				break;
			case Mode_ReadId_Command:
				if (++modeCounter < 2) {
					s = 0x90;
				}
				else {
					mode = EMode.Mode_ReadId_Wait;
					w = SW;
				}
				break;
			case Mode_ReadId_Wait:
				w = SW;
				break;
			case Mode_ReadId_Result:
				if (modeCounter >= 0) {
					if (--modeCounter > 0) {
						s = 0xD0;
					}
					else {
						mode = EMode.Mode_None;
					}
				}
				else {
					s = 0xD0;
				}
				break;
			default:
				break;
			}

//	    	System.out.printf ("%10d - fukRead(0x%02X) = ", XEiJ.mpuClockTime/ TTT / 1000, cmd);

			long ll = XEiJ.mpuClockTime - tickCounter;
			if (s >= 0) {
				//もし憶測値ありなら、それを返す。
				byte b = (byte)s;
				lastCmd = cmd;
				lastStatus = b;
//		   			System.out.printf ("[%02X,%s\n", b, mode.toString());
				return b;
			}
			if (ff) {
				w = 0;
			}
			if (lastCmd == cmd && w != 0 && (XEiJ.mpuClockTime - tickCounter) / TTT < w) {
				//同じコマンドが一定の時間内に実行されていたら同じ値を返す。
				//TLOG("  ★Read-C(0x%02X[%s]) = 0x%02X", cmd, GetRegisterName(cmd), lastStatus);
//			    	System.out.printf ("[%02X]b\n", lastStatus);
				return lastStatus;
			}
//		  	System.out.printf ("c\n");
			lastCmd = cmd;
			tickCounter = XEiJ.mpuClockTime;
		}
		else {
			lastCmd = cmd;
		}
		if (cmd == 0x03) {
			// Command(0x00E94003 レジスタ内容)
			if (mode == EMode.Mode_SenseDeivceStatus_ReturnStatus) {
				//データの戻りをキャッシュ値にする
				mode = EMode.Mode_None;
				byte r = (byte)senseDeviceStatus[modeCounter];
///		    	System.out.printf ("c. 0x%02X\n", r);
				return r;
			}
			else if (mode == EMode.Mode_ReadData_SoftReceive) {
				byte r = recvData[readDataCounter++];
				if (readDataCounter == recvDataSize - 7) {
					mode = EMode.Mode_ReadData_Response;
					readDataCounter = 0;
				}
				//TLOG("  ★Read-C(0x%02X[%s]) = 0x%02X", cmd, GetRegisterName(cmd), r);
///		    	System.out.printf ("d. 0x%02X\n", r);
				return r;
			}
			else if (mode == EMode.Mode_ReadData_Response) {
				// 読み込みモードのレスポンス中
				byte r = recvData[recvDataSize - 7 + readDataCounter];
				readDataCounter++;
				if (readDataCounter >= 7) {
					mode = EMode.Mode_ReadData_After;
					if (hasAllReadData) {
					}
					else {
					}
					addQueue((byte)'c');
				}
///		    	System.out.printf ("e. 0x%02X\n", r);
				return r;
			}
//			else if (mode == EMode.Mode_WriteData_Response) {
//				// 書き込みモードのレスポンス中
//				if (++writeDataCounter >= 7) {
//					mode = EMode.Mode_None;
//					addQueue((byte)'c');
//				}
//			}
			else if (mode == EMode.Mode_ReadData_After) {
				modeCounter++;
///		    	System.out.printf ("f. 0x%02X", 0x80);
				return 0x80;
			}
		}
		else if (cmd == 0x05) {
			// (0x00E94005 レジスタ内容)
			if (driveOption_drive == 0) {
				driveOption_drive = driveOption_actuaDrive;
				initDriveOption();
			}
			if (driveOption_drive == 0) {
				//ドライブ指定がない
///		    	System.out.printf ("g. 0x%02X\n", 0);
				return 0;
			}
			if (driveOption_result[driveOption_drive] >= 0) {
				//そのドライブは記憶している
				byte r = (byte)driveOption_result[driveOption_drive];
///		    	System.out.printf ("h. 0x%02X\n", r);
				return r;
			}
			//そのドライブは記憶していない
			if (driveOption_actuaDrive != driveOption_drive) {
				//想定のドライブと設定されているドライブが違う
				driveOption_actuaDrive = driveOption_drive;
				driveOption_isExec = true;
		  		synchronized (mLock1) {
					byte d = (byte)(driveOption_ctrl[driveOption_drive] | driveOption_drive);
					//Send(0x05 | 0x80); //Option
					//Send(d);
					fukPoolAdd2(0x05 | 0x80, d);
				}
			}
		}
		//リードコマンドを送信する
//		System.out.printf("  ★Read(0x%02X)", cmd);
		int r = -1;
  		synchronized (mLock1) {
			fukPoolAdd1 (cmd);  //リードコマンド
			fukPoolFlush ();
		}
		//読み出し中
		//受信キューからデータを読み出す
		int c = 0;
		while (true) {
	 		synchronized (mLock1) {
				r = fukQueueData ();
			}
			if (r >= 0) {
				break;
			}
			sleep(10);
/*
			c++;
			if (c > 64) {
				c = 0;
		 		synchronized (mLock1) {
//			 		fukPort1.writeBytes (new byte[] {cmd, 1}, 1);
					System.out.printf ("*");
				}
			}
*/
		}
//		System.out.printf("  =   0x%02X\n", r);
		if (r >= 0) {
			if (cmd == 0x01) {
				//Status(0x00E94001 レジスタ内容)
				if (mode == EMode.Mode_ReadId_Wait && r == 0xD0) {
					mode = EMode.Mode_ReadId_Result;
					modeCounter = -1;
				}
				if (mode == EMode.Mode_Seek_NoWait) {
					if (r == 0x90) {
						if (equalStatusCounter++ > 5) {
							equalStatusCounter = 0;
							r = 0x80;
						}
					}
/*
					else if ((r & 0x0F) != 0) {
//						System.out.printf ("   -@@@ %d\n", equalStatusCounter);
						if (equalStatusCounter++ > 100) {
//						System.out.printf ("   @@@\n");
							equalStatusCounter = 0;
					  		synchronized (mLock1) {
								fukPoolAdd1 (0x41);
								fukPoolFlush ();
							}
							addQueue((byte)'C');
							addQueue((byte)'c');
//							IOInterrupt.ioiFdcRise();
//							IOInterrupt.ioiFdcFall();
						}
					}
*/
					else {
						equalStatusCounter = 0;
					}
				}
 				lastStatus = (byte)r;
			}
			if (cmd == 0x03) {
				//command(0x00E94003 レジスタ内容)
				if (mode == EMode.Mode_ReadId_Result && modeCounter < 0) {
					modeCounter = 7;
				}
				else if (mode == EMode.Mode_SenseDeivceStatus_CheckStatus) {
					//データの戻りを記憶する
					senseDeviceStatus[modeCounter] = (short)r;
					mode = EMode.Mode_None;
				}
				else if (mode == EMode.Mode_SenseInterruptStatus_StatusD0) {
					if (modeCounter++ == 0) {
	//					TLOG("	      - OldRes = 0x%02X", r);
						if ((r & 0x18) == 0x18) {
							r &= 0xEF;
	//						TLOG("	      - NewRes = 0x%02X", r);
						}
					}
				}
				else if (mode == EMode.Mode_WriteData_Response) {
					// 書き込みモードのレスポンス中
					if (++writeDataCounter >= 7) {
						mode = EMode.Mode_None;
						addQueue((byte)'c');
					}
				}
			}
			if (cmd == 0x05) {
				//(0x00E94005 レジスタ内容)
				driveOption_result[driveOption_actuaDrive] = r;
			}
			return (byte)(r);
		}
		return 0xFF;
	}  //fukRead

	/// コマンド別長さ
	private static final byte s_commandLength[] = {
		1,  //0x00 INVALID
		1,  //0x01 INVALID
		9,  //0x02 READ DIAGNOSTIC
		3,  //0x03 SPECIFY
		2,  //0x04 SENSE DEVICE STATUS
		9,  //0x05 WRITE DATA
		9,  //0x06 READ DATA
		2,  //0x07 RECALIBRATE
		1,  //0x08 SENSE INTERRUPT STATUS
		9,  //0x09 WRITE DELETED DATA
		2,  //0x0a READ ID
		1,  //0x0b INVALID
		9,  //0x0c READ DELETED DATA
		6,  //0x0d WRITE ID
		1,  //0x0e INVALID
		3,  //0x0f SEEK
		1,  //0x10 INVALID
		9,  //0x11 SCAN EQUAL
		1,  //0x12 INVALID
		1,  //0x13 INVALID
		1,  //0x14 RESET STANDBY
		1,  //0x15 SET STANDBY
		1,  //0x16 SOFTWARE RESET
		1,  //0x17 INVALID
		1,  //0x18 INVALID
		9,  //0x19 SCAN LOW OR EQUAL
		1,  //0x1a INVALID
		1,  //0x1b INVALID
		1,  //0x1c INVALID
		9,  //0x1d SCAN HIGH OR EQUAL
		1,  //0x1e INVALID
		1,  //0x1f INVALID
	};
	
	//fukWrite (a, d)
	//  ライト
	public static void fukWrite (byte cmd, byte prm) 
	{
		if (cmd == 0x23/*OPM*/) {
			//(0x00E90003 レジスタ内容)
			if ((prm & 0x40) != 0) {
				isForceReadyStatus = true;
				isNotifyForceReady = false;
			}
			else {
				if (isNotifyForceReady) {
					isNotifyForceReady = false;
					isForceReadyStatus = false;
					sendForceReady(false);
				}
			}
			return;
		}
		lastCmd = 0;
		if (cmd == 0x03) {
			// Command Register(0x00E94003 レジスタ内容)
			if (--cmdCount <= 0) {
				cmdCount = s_commandLength[prm & 0x1F];
//		    	System.out.printf ("☆Command [0x%02X]\n", prm);
				//
				if (activeCommandList[0] == 0x08 && mode == EMode.Mode_ReadData_After && prm == 0x08/*SENSE INTERRUPT STATUS*/) {
					//TLOG("  ＠＠");
					mode = EMode.Mode_SenseInterruptStatus_StatusD0;
				}
				else if (mode == EMode.Mode_ReadData_After && prm == 0x08/*SENSE INTERRUPT STATUS*/) {
					//TLOG("  ★Write-C2(0x%02X[%s], 0x%02X)", 0x03, GetRegisterName(0x03 | 0x80), prm);
					activeCommandCount = 0;
					activeCommandList[activeCommandCount++] = prm;
					modeCounter++;
					// ここではコマンドを出さない。
					return;
				}

				//
				mode = EMode.Mode_None;
				modeCounter = 0;
				//
				if (prm == 0x04/*SENSE DEVICE STATUS_*/) {
					mode = EMode.Mode_SenseDeivceStatus_WaitStatus90;
				}
				else if (prm == 0x08/*SENSE INTERRUPT STATUS*/) {
					mode = EMode.Mode_SenseInterruptStatus_StatusD0;
				}
				else if (prm == 0x0F/*SEEK*/) {
					mode = EMode.Mode_Seek_StatusD0;
				}
				else if (prm == 0x07/*RECALIBRATE*/) {
					mode = EMode.Mode_Recalibrate_Status90;
					if (isForceReadyStatus && !isNotifyForceReady) {
						isNotifyForceReady = true;
						sendForceReady(true);
					}
				}
				else if (isReadTransCommand(prm)) {
					mode = EMode.Mode_ReadData_Command;
					readDataCounter = 9;
				}
				else if (isWriteTransCommand(prm)) {
					sendMTC();
					mode = EMode.Mode_WriteData_Command;
					writeDataCounter = 9;
				}
				else if (isWriteIdTransCommand(prm)) {
					sendMTC();
					mode = EMode.Mode_WriteData_Command;
					writeDataCounter = 6;
				}
				else if ((prm & 0xBF) == 0x0A/*READ ID*/) {
					mode = EMode.Mode_ReadId_Command;
				}
				activeCommandCount = 0;
			}
			activeCommandList[activeCommandCount++] = prm;
			//
			if (mode == EMode.Mode_SenseDeivceStatus_WaitStatus90) {
				// ここではコマンドを出さない。
				return;
			} 
			else if (mode == EMode.Mode_SenseDeivceStatus_WaitStatusD0) {
				if (activeCommandCount == 2) {
					if ((XEiJ.mpuClockTime - tickSenseDeviceStatus) / TTT > 500) {
						tickSenseDeviceStatus = XEiJ.mpuClockTime;;
						for(int i = 0; i < 8; i++) { senseDeviceStatus[i] = -1; }
					}
					if (senseDeviceStatus[prm & 7] < 0) {
						//キャッシュがない
				  		synchronized (mLock1) {
							fukPoolAdd2(0x03 | 0x80, 0x04);
							//Send((byte)(0x03 | 0x80));
							//Send(0x04/*SENSE DEVICE STATUS_*/);
							fukPoolAdd2(0x03 | 0x80, prm);
							//Send((byte)(0x03 | 0x80));
							//Send(prm);
					  		fukPoolFlush ();
						}
						modeCounter = prm & 7;
						mode = EMode.Mode_SenseDeivceStatus_CheckStatus; //データの戻りを記憶する
					}
					else {
						//キャッシュあり
						modeCounter = prm;
						mode = EMode.Mode_SenseDeivceStatus_ReturnStatus; //データの戻りをキャッシュ値にする
					}
				}
				return;
			}
		}
		else if (cmd == 0x05) {
			if ((XEiJ.mpuClockTime - driveOption_tick) / TTT > 500) {
				initDriveOption();
			}
			if ((prm & 0x0F) != 0) {
				//指示だ
				driveOption_drive = (byte)(prm & 0x0F);
				if ((prm & 0x20) != 0) {
					tickSenseDeviceStatus = XEiJ.mpuClockTime;
					initDriveOption();
				}
				if (driveOption_ctrl[driveOption_drive] != (prm & 0xF0)) {
					//前回と違う指示だ
					driveOption_ctrl[driveOption_drive] = (prm & 0xF0);
					driveOption_isExec = true;
					driveOption_actuaDrive = driveOption_drive;
				}
				else {
					//同じ指示なら終わり
					return;
				}
			}
			else {
				//解除だ
				if (driveOption_isExec) {
					//コマンド実行している
					driveOption_drive = 0;
					driveOption_actuaDrive = 0;
					driveOption_isExec = false;
				}
				else {
					//してない＆ドライブ同じ
					driveOption_drive = 0;
					driveOption_actuaDrive = 0;
					return;
				}
			}
		}
		//
		if (cmd == 0x03) {
			//(0x00E94003 レジスタ内容)
			if (mode == EMode.Mode_ReadData_Command) {
				if (--readDataCounter == 0) {
					mode = EMode.Mode_ReadData_Receive;
					readDataCounter = 0;
				}
			}
			else if (mode == EMode.Mode_WriteData_Command) {
				if (--writeDataCounter == 0) {
					mode = EMode.Mode_WriteData_Send;
				}
			}
		}
//		System.out.printf("  ★Write(0x%02X, 0x%02X)\n", cmd, prm);
  		synchronized (mLock1) {
			fukPoolAdd2 (0x80 | cmd, prm);  //ライトコマンドとデータ
		}
	}  //fukWrite

	/**
	 * 読み込み系コマンド？
	 *	@param cmd コマンド
	 *	@retval true 読み込み系
	 *	@retval false 以外
	 */
	private static boolean isReadTransCommand(byte cmd)
	{
		boolean f = false;
		if ((cmd & 0x1F) == 0x06) f = true;//READ DATA
		if ((cmd & 0x1F) == 0x0C) f = true;//READ DELETED DATA
		if ((cmd & 0xBF) == 0x02) f = true;//READ DIAGNOSTIC
		return f;
	}

	/**
	 * 書き込み系コマンド？
	 *	@param cmd コマンド
	 *	@retval true 書き込み系
	 *	@retval false 以外
	 */
	private static boolean isWriteTransCommand(byte cmd)
	{
		boolean f = false;
		if ((cmd & 0x3F) == 0x05) f = true;//WRITE DATA
		if ((cmd & 0x3F) == 0x09) f = true;//WRITE DELETED DATA
		if ((cmd & 0x1F) == 0x11) f = true;//SCAN EQUAL
		if ((cmd & 0x1F) == 0x19) f = true;//SCAN LOW OR EQUAL
		if ((cmd & 0x1F) == 0x1D) f = true;//SCAN HIGH OR EQUAL
		return f;
	}

	/**
	 * 書き込みID系コマンド？
	 *	@param cmd コマンド
	 *	@retval true 書き込み系
	 *	@retval false 以外
	 */
	private static boolean isWriteIdTransCommand(byte cmd)
	{
		boolean f = false;
		if ((cmd & 0xBF) == 0x0D) f = true;//WRITE ID
		return f;
	}

	/**
	 * MTC値、送信
	 */
	private static void sendMTC()
	{
		int mtc = HD63450.dmaReadWord(DMAC_CH0 + 0x0A);
   // 	System.out.printf ("        MTC = %d\n", mtc);
  		synchronized (mLock1) {
			fukPoolAdd2(0x30 | 0x80, mtc & 0xFF);
			fukPoolAdd2(0x31 | 0x80, mtc / 0x100);
		}
	}

	/**
	 * ForceReady設定、送信
	 */
	private static void sendForceReady(boolean isReady)
	{
		byte b = (byte)(isReady ? 0x40 : 0x00);
  		synchronized (mLock1) {
			fukPoolAdd2(0x23 | 0x80, b);
		}
	}

	private static void clearDmac()
	{
		HD63450.dmaWriteByte(DMAC_CH0 + 0x07 , 0x10);
		HD63450.dmaWriteWord(DMAC_CH0 + 0x0A , 0);//MTC
	}
	
	private static void check_ints1()
	{
		while (true) {
			int r = fukQueueData ();
			if (r < 0) {
				break;
			}
			byte b = (byte)r;
			if (b == 'C') {
//		    	System.out.printf ("interrupt FDC true\n");
				IOInterrupt.ioiFdcRise();
//				ioiFdc(true);
				//is_do_fdc_intr = true;
			}
			else if (b == 'c') {
//		    	System.out.printf ("interrupt FDC false\n");
				IOInterrupt.ioiFdcFall();
//				ioiFdc(false);
				//is_do_fdc_intr = true;
			}
		}
	}

	private static void sleep(int ms)
	{
		try {
			Thread.sleep(0, ms * 1000);
		} catch (Exception e) {
		}
	}

	private static void onReceivedData()
	{
		//読み込み受信モードでスタンバイ出来た時
		int mtc = HD63450.dmaReadWord(DMAC_CH0 + 0x0A);
    	//System.out.printf ("        MTC = 0x%08X\n", mtc);
		clearDmac();
		if (recvDataSize >= 7) {
			//リザルトも含まれる
			if (mtc == 0) {
				mode = EMode.Mode_ReadData_SoftReceive;
			}
			else {
				mode = EMode.Mode_ReadData_Response;
			}
		}
		else {
			// RESULTもない時！！！
			recvDataSize = 7;
			recvData[0] = (byte)(0x40 | (activeCommandList[1] & 0x07));
			recvData[1] = 0;
			recvData[2] = 0;
			recvData[3] = activeCommandList[3];
			recvData[4] = activeCommandList[4];
			recvData[5] = activeCommandList[5];
			recvData[6] = activeCommandList[6];
			mode = EMode.Mode_ReadData_Response;
		}
		addQueue((byte)'C');
		readDataCounter = 0;
	}

	//fukDataListener1
	//  ポート1データリスナー
	//  getReceivedData()について
	//    LISTENING_EVENT_DATA_AVAILABLEのときgetReceivedData()はnullを返す
	//    LISTENING_EVENT_DATA_RECEIVEDのときgetReceivedData()は受信バッファを先読みして返す
	//    受信バッファを先読みするだけで取り除かないのでこれだけでは2回目のイベントが発生しない
	public static final SerialPortDataListener fukDataListener1 = new SerialPortDataListener () {
		@Override public int getListeningEvents () {
		  	return SerialPort.LISTENING_EVENT_DATA_AVAILABLE;
		}  //getListeningEvents
		@Override public void serialEvent (SerialPortEvent spe) {
		  	if (spe.getEventType () != SerialPort.LISTENING_EVENT_DATA_AVAILABLE) {
		    	return;
			}
		  	//データを受信する
	  		synchronized (mLock1) {
			  	int n = fukPort1.bytesAvailable ();
			  	byte[] b = new byte[n];
			  	fukPort1.readBytes (b, n);
			  	int w = fukQueueWrite;  //受信キューに書き込んだ長さ
			  	int r = fukQueueRead;  //受信キューから読み出した長さ
			  	//受信した長さが受信キューに書き込める長さを超えていてはならない
			  	if ((FUK_QUEUE_SIZE - (w - r)) < n) {
			    	System.out.printf ("%08x fukDataListener1.serialEvent() queue is full\n", XEiJ.regPC0);
			    	return;
			  	}
			  	//受信したデータを受信キューに移す
			  	int i = w & (FUK_QUEUE_SIZE - 1);  //受信キューに書き込む位置
			  	int k = Math.min (n, FUK_QUEUE_SIZE - i);  //1回目に書き込む長さ
			  	System.arraycopy (b, 0,  //from
			                    fukQueueArray, i,  //to
			                    k);  //length
			  	if (k < n) {  //受信キューの末尾と先頭を跨ぐとき
			    	System.arraycopy (b, k,  //from
			                      fukQueueArray, 0,  //to
			                      n - k);  //length
			  	}
			  	fukQueueWrite = w += n;
			}
		}  //serialEvent
	};  //fukDataListener1

	public static void addQueue(byte d)
	{
  		synchronized (mLock1) {
			int n = 1;
		  	int w = fukQueueWrite;  //受信キューに書き込んだ長さ
		  	int r = fukQueueRead;  //受信キューから読み出した長さ
		  	//受信した長さが受信キューに書き込める長さを超えていてはならない
		  	if ((FUK_QUEUE_SIZE - (w - r)) < n) {
		    	System.out.printf ("%08x fukDataListener1.serialEvent() queue is full\n", XEiJ.regPC0);
		    	return;
		  	}
		  	byte[] b = new byte[1];
		  	b[0] = d;
		  	//受信したデータを受信キューに移す
		  	int i = w & (FUK_QUEUE_SIZE - 1);  //受信キューに書き込む位置
		  	int k = Math.min (n, FUK_QUEUE_SIZE - i);  //1回目に書き込む長さ
		  	System.arraycopy (b, 0,  //from
		                    fukQueueArray, i,  //to
		                    k);  //length
		  	if (k < n) {  //受信キューの末尾と先頭を跨ぐとき
		    	System.arraycopy (b, k,  //from
		                      fukQueueArray, 0,  //to
		                      n - k);  //length
		  	}
		  	fukQueueWrite = w += n;
		}
	}
	
	private static int mar = 0;
	private static int mtc = 0;
	private static boolean isReadCommandR = false;	// RコマンドR中？
	private static int rccnt = 0;
	private static int recvDataPos = 0;

	private static boolean checkReceiveData()
	{
  	  	int n = fukPort2.bytesAvailable ();
	  	if (n == 0) {
	    	return false;
	  	}
	  	byte[] recv = new byte[n];
	  	fukPort2.readBytes (recv, n);
	  	int l = n;//recv.length;  //受信した長さ
//	   	System.out.printf ("★★★★★ recv2 = %d\n", l);
    	sleep(10);
		int i = 0;
		while (i < l) {
			byte b = recv[i++];
			if (isReadCommandR) {
				if (rccnt == 0) {
					recvDataSize = b;
				}
				else if (rccnt == 1) {
					recvDataSize |= (b << 8);
					recvDataPos = 0;
				}
				else {
					if (recvDataPos < mtc) {
						MainMemory.mmrWb((int)(mar + recvDataPos), b);
					}
					recvData[recvDataPos++] = b;
					//Tlog("★recv 0x%02X (%d)", b, recvDataSize);
					//tickCmdR = XEiJ.mpuClockTime;
					if (recvDataPos == recvDataSize) {
						//全データ＋RESULTを受信
						TickerQueue.tkqRemove(onRecvTimeOut);
						isReadCommandR = false;
						hasAllReadData = true;
						onReceivedData();
//								System.out.printf ("->out cmd r mode\n");
					}
				}
				rccnt++;
				continue;
			}
			if (b == 'C') {
				//ioiFdc(true);
			}
			else if (b == 'c') {
				//ioiFdc(false);
			}
			else if (b == 'D') {
				ioiFdd(true);
				tickSenseDeviceStatus = XEiJ.mpuClockTime;
			}
			else if (b == 'd') {
				ioiFdd(false);
			}
			else if (b == 'R') {
//		    	System.out.printf ("★★★★★ ints2 'R'\n");
				if (mode == EMode.Mode_ReadData_Receive) {
					mar = HD63450.dmaReadLong(DMAC_CH0 + 0x0C);
					mtc = HD63450.dmaReadWord(DMAC_CH0 + 0x0A);
					isReadCommandR = true;
					recvDataSize = 0;
					rccnt = 0;
					//tickCmdR = XEiJ.mpuClockTime;
					TickerQueue.tkqAdd (onRecvTimeOut, XEiJ.mpuClockTime + 50000L * TTT);
//			    			System.out.printf ("->in cmd r mode 0x%08X, %d\n", mar, mtc);
				}
			}	
			else if (b == 'W') {
				if (mode == EMode.Mode_WriteData_Send) {
					mar = HD63450.dmaReadLong(DMAC_CH0 + 0x0C); // MTR
					writeDataCounter = HD63450.dmaReadWord(DMAC_CH0 + 0x0A);//MTC
			    	//System.out.printf ("★★★★★ ints2 'W' (0x%08X, %d)\n", mar, writeDataCounter);
					int j = 0;
					writeData[j++] = 0x4D;	//転送開始識別コマンド
					for(int k = 0; k < writeDataCounter; k++) {
						writeData[j++] = MainMemory.mmrRbs(mar++);
					}
					fukPort2.writeBytes(writeData, j);
					sleep(10);
				}
			}
			else if (b == 'w') {
				if (mode == EMode.Mode_WriteData_Send) {
					//書き込み送信モードでスタンバイ出来た時
			    	//System.out.printf ("★★★★★ ints2 'w'\n");
					clearDmac();
					mode = EMode.Mode_WriteData_Response;
					writeDataCounter = 0;
					addQueue((byte)'C');
//					ioiFdc(true);
				}
			}
			else if (b == 't') {
				clearDmac();
				mode = EMode.Mode_None;
			}
			else {
//		    	System.out.printf ("★★★★★ HATENA '0x%02X'\n", b);
			}
		}
		return true;
	}

	//onRecvTimeOut
	//  受信タイムアウト処理
	public static final TickerQueue.Ticker onRecvTimeOut = new TickerQueue.Ticker () {
		@Override protected void tick () {
			//データが足りない
			if (mode == EMode.Mode_ReadData_Receive) {
				while (true) {
					sleep(500);
		 	 	  	int n = fukPort2.bytesAvailable ();
		  			if (n == 0) {
		    			break;
		    		}
				  	byte[] recv = new byte[n];
				  	fukPort2.readBytes (recv, n);
				}
				recvDataSize = 0;
				hasAllReadData = false;
				isReadCommandR = false;
				onReceivedData();
				System.out.printf ("FUK; timeout cmd r mode\n");
			}
		}  //tick
	};

	//fukDataListener2
	//  ポート2データリスナー
	public static final SerialPortDataListener fukDataListener2 = new SerialPortDataListener () {
		@Override public int getListeningEvents () {
		  	return SerialPort.LISTENING_EVENT_DATA_AVAILABLE;
		}  //getListeningEvents
		@Override public void serialEvent (SerialPortEvent spe) {
		  	if (spe.getEventType () != SerialPort.LISTENING_EVENT_DATA_AVAILABLE) {
		    	return;
		  	}
		  	//データを受信する
			while (checkReceiveData()) {
				sleep(1);
			}
		}  //serialEvent
	};  //fukDataListener2

	//受信キュー
	//  ポート1から受信したデータは直ちに受信キューに移される
	//  受信キューは溢れてはならない
	public static final int FUK_QUEUE_SIZE = 1048576;  //キューの長さ。1MB
	public static final byte[] fukQueueArray = new byte[FUK_QUEUE_SIZE];  //キューの配列
	public static volatile int fukQueueWrite;  //書き込んだ長さ
	public static volatile int fukQueueRead;  //読み出した長さ

	//fukQueueInit ()
	//  初期化
	public static void fukQueueInit () {
		fukQueueWrite = 0;
		fukQueueRead = 0;
	}  //fukQueueInit

	//d = fukQueueData ()
	//  受信キューからデータを読み出す
	//  d  読み出したデータ。-1=受信キューが空
	public static int fukQueueData () {
		int w = fukQueueWrite;
		int r = fukQueueRead;
		if ((w - r) == 0) {  //受信キューが空
	  		return -1;
		}
		int d = 0xff & fukQueueArray[r++ & (FUK_QUEUE_SIZE - 1)];
		fukQueueRead = r;
		return d;
	}  //fukQueueData

	//遅延送信
	//  送信データを送信プールに溜めて一定時間送信がないか受信を行うときにまとめて送信する
	//  送信と受信の間隔が足りなくなる場合は個別に間隔保証を行うこと
	//  ティッカーはコアと同じスレッドで命令の後に呼び出されるので追加や削除で衝突する心配はない
	public static final int FUK_POOL_SIZE = 0x1000;  //送信プールのサイズ。511以下
	public static final long FUK_POOL_SPAN = XEiJ.TMR_FREQ / 500;  //一定時間。2ms
	public static final byte[] fukPoolBuffer = new byte[FUK_POOL_SIZE];  //送信プール
	public static volatile int fukPoolLength;  //送信プールにあるデータの長さ

	//fukPoolInit ()
	//  初期化
	public static void fukPoolInit () {
		//fukPoolBuffer = new byte[FUK_POOL_SIZE];
		fukPoolLength = 0;
	}  //fukPoolInit

	//fukPoolAdd1 (d)
	//  送信プールにデータを1個加える
	public static void fukPoolAdd1 (int d1) {
		if (FUK_POOL_SIZE - 1 < fukPoolLength) {
		  	fukPoolFlush ();
		}
		fukPoolBuffer[fukPoolLength++] = (byte) d1;
//    	System.out.printf ("->add(0x%02X)->",d1);
		TickerQueue.tkqRemove(fukPoolTicker);
		TickerQueue.tkqAdd (fukPoolTicker, XEiJ.mpuClockTime + FUK_POOL_SPAN);
	}  //fukPoolAdd1

	//fukPoolAdd2 (d1, d2)
	//  送信プールにデータを2個加える
	public static void fukPoolAdd2 (int d1, int d2) {
		if (FUK_POOL_SIZE - 2 < fukPoolLength) {
		  	fukPoolFlush ();
		}
		fukPoolBuffer[fukPoolLength++] = (byte) d1;
		fukPoolBuffer[fukPoolLength++] = (byte) d2;
//    	System.out.printf ("->add(0x%02X)->",d1);
//    	System.out.printf ("->add(0x%02X)->",d2);
		TickerQueue.tkqRemove(fukPoolTicker);
		TickerQueue.tkqAdd (fukPoolTicker, XEiJ.mpuClockTime + FUK_POOL_SPAN);
	}  //fukPoolAdd2

	//fukPoolTicker
	//  最後の送信から一定時間後に送信プールから送信するティッカー
	public static final TickerQueue.Ticker fukPoolTicker = new TickerQueue.Ticker () {
		@Override protected void tick () {
	  		//送信プールから送信する
	  		synchronized (mLock1) {
		  		fukPoolFlush ();
		  	}
			TickerQueue.tkqRemove(fukPoolTicker);
		}  //tick
	};  //fukPoolTicker

	//fukPoolFlush ()
	//  送信プールから送信する
	public static void fukPoolFlush () {
		TickerQueue.tkqRemove(fukPoolTicker);
		if (0 < fukPoolLength) {
//	    	System.out.printf ("->flush =%d", fukPoolLength);
	 		fukPort1.writeBytes (fukPoolBuffer, fukPoolLength);
  			fukPoolLength = 0;
		}
	}  //fukPoolFlush

	private static Object mLock1 = new Object();

	/**
	 * ドライブオプション関係初期化
	 */
	private static void initDriveOption()
	{
		driveOption_tick = XEiJ.mpuClockTime;
		for (int i = 0; i < 16; i++) {
			driveOption_ctrl[i] = -1;
			driveOption_result[i] = -1;
		}
		driveOption_isExec = true;
	}

	public static final long FUK_FDD_SPAN = XEiJ.TMR_FREQ / 10;  //一定時間。100ms

	private static void ioiFdd(boolean f)
	{
		if (f) {
			TickerQueue.tkqAdd (ioiFddRise, XEiJ.mpuClockTime + FUK_FDD_SPAN);
		} else {
			TickerQueue.tkqAdd (ioiFddFall, XEiJ.mpuClockTime + FUK_FDD_SPAN);
	   	}
	}

	private static final TickerQueue.Ticker ioiFddRise = new TickerQueue.Ticker () {
		@Override protected void tick () {
//	    	System.out.printf ("interrupt FDD true\n");
			IOInterrupt.ioiFddRise();
		}
	};

	private static final TickerQueue.Ticker ioiFddFall = new TickerQueue.Ticker () {
		@Override protected void tick () {
//	    	System.out.printf ("interrupt FDD false\n");
			IOInterrupt.ioiFddFall();
		}
	};


	private static byte recvData[] = new byte[0x10000];		///< 受信データ
	private static volatile int recvDataSize;			///< 受信データ数
	private static volatile long	tickQueue;			///< キュー送信用TICK
	private static int				cmdCount;				///< コマンドカウント
	private static byte			lastCmd;				///< 最後に処理したコマンド
	private static long			tickCounter;		///< 0x01 コマンドの高速アクセス軽減用TICK
	private static byte			lastStatus;			///< 最後に返したStatus値
	private static byte			activeCommandList[] = new byte[16];///< アクティブコマンド
	private static int			activeCommandCount;	///< アクティブコマンド長さ
	private static boolean			hasAllReadData;		///< 受信成功？
	private static boolean			isForceReadyStatus;	///< 強制READY状態？
	private static boolean			isNotifyForceReady;	///< 強制READY通知？
	private static boolean is_do_fdc_intr = false;
	
	private static long		driveOption_tick;					///< タイム
	private static byte		driveOption_actuaDrive;				///< 指定されているドライブ
	private static byte		driveOption_drive;					///< 下位4bit 指定しているDRIVE 0～0x0F
	private static int			driveOption_ctrl[] = new int[16];	///< オプションの上位4bit
	private static int			driveOption_result[] = new int[16];	///< ドライバオプション結果値
	private static boolean		driveOption_isExec;					// 実施したか？！

	private static long		tickSenseDeviceStatus;///< センスデバイスのカウンタ
	private static int			senseDeviceStatus[] = new int[8] ;	///< 現在のセンスデバイスのステータス
	private static int equalStatusCounter = 0;	

	private static byte writeData[] = new byte[0x10000];


	private static enum EMode {
		Mode_None,							///< 処理なし

		Mode_SenseDeivceStatus_WaitStatus90,///< Statusには90を返す。
		Mode_SenseDeivceStatus_WaitStatusD0,///< StatusにはDOを返す,
		Mode_SenseDeivceStatus_CheckStatus, ///< データの戻りを記憶する
		Mode_SenseDeivceStatus_ReturnStatus,///< データの戻りをキャッシュ値にする
		Mode_SenseDeivceStatus_WaitStatus80,///< Statusには80を返す。
	
		Mode_SenseInterruptStatus_StatusD0,	///< StatusにはD0を返す。
		Mode_SenseInterruptStatus_NoWait,	///< NOWAIT
	
		Mode_Seek_StatusD0,					///< StatusにはD0を返す。
		Mode_Seek_NoWait,					///< NOWAIT

		Mode_Recalibrate_Status90,			///< Statusには90を返す。
		Mode_Recalibrate_NoWait,			///< NOWAIT

		Mode_ReadData_Command,				///< コマンド処理中。Statusには0x90を返す。
		Mode_ReadData_Receive,				///< 受信処理中。Statusには0x10を返す。
		Mode_ReadData_SoftReceive,			///< 受信処理中。Statusには0xF0を返す。
		Mode_ReadData_Response,				///< レスポンス処理中。Statusには0xD0を返す。
		Mode_ReadData_After,				///< レスポンス終わり。0x80を返す。

		Mode_WriteData_Command,				///< コマンド処理中。Statusには0x90を返す。
		Mode_WriteData_Send,				///< 送信処理中。Statusには0xB0を返す。
		Mode_WriteData_Response,			///< レスポンスより中。Statusには0xD0を返す。

		Mode_ReadId_Command,				///< コマンド処理中。Statusには0x90を返す。
		Mode_ReadId_Wait,					///< 待ち。Statusには0x70を返す。
		Mode_ReadId_Result,					// リザルト処理中。Statusには0xD0を返す。

	};
	private static volatile EMode	mode = EMode.Mode_None;					///< 現在のモード
	private static int				modeCounter;			///< 汎用カウンタ
	private static volatile int	readDataCounter;		///< 読み込み汎用カウンタ
	private static volatile int	writeDataCounter;
	
}  //class FUK

