class	X24C01
{
public:
	X24C01() {
		now_state  = X24C01_IDLE;
		next_state = X24C01_IDLE;
		addr       = 0;
		data       = 0;
		sda        = 0xFF;
		scl_old    = 0;
		sda_old    = 0;

		pEEPDATA   = NULL;
	}

	void	Reset( LPBYTE ptr ) {
		now_state  = X24C01_IDLE;
		next_state = X24C01_IDLE;
		addr       = 0;
		data       = 0;
		sda        = 0xFF;
		scl_old    = 0;
		sda_old    = 0;

		pEEPDATA   = ptr;
	}

	void	Write( BYTE scl_in, BYTE sda_in ) {
		// Clock line
		BYTE	scl_rise = ~scl_old & scl_in;
		BYTE	scl_fall = scl_old & ~scl_in;
		// Data line
		BYTE	sda_rise = ~sda_old & sda_in;
		BYTE	sda_fall = sda_old & ~sda_in;

		BYTE	scl_old_temp = scl_old;
		BYTE	sda_old_temp = sda_old;

		scl_old = scl_in;
		sda_old = sda_in;

		// Start condition?
		if( scl_old_temp && sda_fall ) {
			now_state = X24C01_ADDRESS;
			bitcnt    = 0;
			addr      = 0;
			sda       = 0xFF;
			return;
		}

		// Stop condition?
		if( scl_old_temp && sda_rise ) {
			now_state = X24C01_IDLE;
			sda       = 0xFF;
			return;
		}

		// SCL ____---- RISE
		if( scl_rise ) {
			switch( now_state ) {
				case	X24C01_ADDRESS:
					if( bitcnt < 7 ) {
						// 本来はMSB->LSB
						addr &= ~(1<<bitcnt);
						addr |= (sda_in?1:0)<<bitcnt;
					} else {
						if( sda_in ) {
							next_state = X24C01_READ;
							data       = pEEPDATA[addr&0x7F];
						} else {
							next_state = X24C01_WRITE;
						}
					}
					bitcnt++;
					break;
				case	X24C01_ACK:
					sda = 0;	// ACK
					break;
				case	X24C01_READ:
					if( bitcnt < 8 ) {
						// 本来はMSB->LSB
						sda = (data&(1<<bitcnt))?1:0;
					}
					bitcnt++;
					break;
				case	X24C01_WRITE:
					if( bitcnt < 8 ) {
						// 本来はMSB->LSB
						data &= ~(1<<bitcnt);
						data |= (sda_in?1:0)<<bitcnt;
					}
					bitcnt++;
					break;

				case	X24C01_ACK_WAIT:
					if( !sda_in ) {
						next_state = X24C01_IDLE;
					}
					break;
			}
		}

		// SCL ----____ FALL
		if( scl_fall ) {
			switch( now_state ) {
				case	X24C01_ADDRESS:
					if( bitcnt >= 8 ) {
						now_state = X24C01_ACK;
						sda       = 0xFF;
					}
					break;
				case	X24C01_ACK:
					now_state = next_state;
					bitcnt    = 0;
					sda       = 0xFF;
					break;
				case	X24C01_READ:
					if( bitcnt >= 8 ) {
						now_state = X24C01_ACK_WAIT;
						addr      = (addr+1)&0x7F;
					}
					break;
				case	X24C01_WRITE:
					if( bitcnt >= 8 ) {
						now_state  = X24C01_ACK;
						next_state = X24C01_IDLE;
						pEEPDATA[addr&0x7F] = data;
						addr       = (addr+1)&0x7F;
					}
					break;
			}
		}
	}

	BYTE	Read() {
		return	sda;
	}

	// For State save
	void	Load( LPBYTE p )
	{
		now_state  = *((INT*)&p[0]);
		next_state = *((INT*)&p[4]);
		bitcnt     = *((INT*)&p[8]);
		addr       = p[12];
		data       = p[13];
		sda        = p[14];
		scl_old    = p[15];
		sda_old    = p[16];
	}

	void	Save( LPBYTE p )
	{
		*((INT*)&p[0]) = now_state;
		*((INT*)&p[4]) = next_state;
		*((INT*)&p[8]) = bitcnt;
		p[12]          = addr;
		p[13]          = data;
		p[14]          = sda;
		p[15]          = scl_old;
		p[16]          = sda_old;
	}

protected:
	enum	{
		X24C01_IDLE = 0,	// Idle
		X24C01_ADDRESS,		// Address set
		X24C01_READ,		// Read
		X24C01_WRITE,		// Write
		X24C01_ACK,		// Acknowledge
		X24C01_ACK_WAIT,	// Acknowledge wait
	};

	INT	now_state, next_state;
	INT	bitcnt;
	BYTE	addr, data;
	BYTE	sda;
	BYTE	scl_old, sda_old;

	LPBYTE	pEEPDATA;
private:
};

class	X24C02
{
public:
	X24C02() {
		now_state  = X24C02_IDLE;
		next_state = X24C02_IDLE;
		addr       = 0;
		data       = 0;
		rw         = 0;
		sda        = 0xFF;
		scl_old    = 0;
		sda_old    = 0;

		pEEPDATA   = NULL;
	}

	void	Reset( LPBYTE ptr ) {
		now_state  = X24C02_IDLE;
		next_state = X24C02_IDLE;
		addr       = 0;
		data       = 0;
		rw         = 0;
		sda        = 0xFF;
		scl_old    = 0;
		sda_old    = 0;

		pEEPDATA   = ptr;
	}

	void	Write( BYTE scl_in, BYTE sda_in ) {
		// Clock line
		BYTE	scl_rise = ~scl_old & scl_in;
		BYTE	scl_fall = scl_old & ~scl_in;
		// Data line
		BYTE	sda_rise = ~sda_old & sda_in;
		BYTE	sda_fall = sda_old & ~sda_in;

		BYTE	scl_old_temp = scl_old;
		BYTE	sda_old_temp = sda_old;

		scl_old = scl_in;
		sda_old = sda_in;

		// Start condition?
		if( scl_old_temp && sda_fall ) {
			now_state = X24C02_DEVADDR;
			bitcnt    = 0;
			sda       = 0xFF;
			return;
		}

		// Stop condition?
		if( scl_old_temp && sda_rise ) {
			now_state = X24C02_IDLE;
			sda       = 0xFF;
			return;
		}

		// SCL ____---- RISE
		if( scl_rise ) {
			switch( now_state ) {
				case	X24C02_DEVADDR:
					if( bitcnt < 8 ) {
						data &= ~(1<<(7-bitcnt));
						data |= (sda_in?1:0)<<(7-bitcnt);
					}
					bitcnt++;
					break;
				case	X24C02_ADDRESS:
					if( bitcnt < 8 ) {
						addr &= ~(1<<(7-bitcnt));
						addr |= (sda_in?1:0)<<(7-bitcnt);
					}
					bitcnt++;
					break;
				case	X24C02_READ:
					if( bitcnt < 8 ) {
						sda = (data&(1<<(7-bitcnt)))?1:0;
					}
					bitcnt++;
					break;
				case	X24C02_WRITE:
					if( bitcnt < 8 ) {
						data &= ~(1<<(7-bitcnt));
						data |= (sda_in?1:0)<<(7-bitcnt);
					}
					bitcnt++;
					break;
				case	X24C02_NAK:
					sda = 0xFF;	// NAK
					break;
				case	X24C02_ACK:
					sda = 0;	// ACK
					break;
				case	X24C02_ACK_WAIT:
					if( !sda_in ) {
						next_state = X24C02_READ;
						data       = pEEPDATA[addr];
					}
					break;
			}
		}

		// SCL ----____ FALL
		if( scl_fall ) {
			switch( now_state ) {
				case	X24C02_DEVADDR:
					if( bitcnt >= 8 ) {
						if( (data & 0xA0) == 0xA0 ) {
							now_state  = X24C02_ACK;
							rw         = data & 0x01;
							sda        = 0xFF;
							if( rw ) {
								// Now address read
								next_state = X24C02_READ;
								data       = pEEPDATA[addr];
							} else {
								next_state = X24C02_ADDRESS;
							}
							bitcnt = 0;
						} else {
							now_state  = X24C02_NAK;
							next_state = X24C02_IDLE;
							sda        = 0xFF;
						}
					}
					break;
				case	X24C02_ADDRESS:
					if( bitcnt >= 8 ) {
						now_state = X24C02_ACK;
						sda       = 0xFF;
						if( rw ) {
							// Readでは絶対来ないが念の為
							next_state = X24C02_IDLE;
						} else {
							// to Data Write
							next_state = X24C02_WRITE;
						}
						bitcnt = 0;
					}
					break;
				case	X24C02_READ:
					if( bitcnt >= 8 ) {
						now_state = X24C02_ACK_WAIT;
						addr      = (addr+1)&0xFF;
					}
					break;
				case	X24C02_WRITE:
					if( bitcnt >= 8 ) {
						pEEPDATA[addr] = data;
						now_state  = X24C02_ACK;
						next_state = X24C02_WRITE;
						addr       = (addr+1)&0xFF;
						bitcnt = 0;
					}
					break;
				case	X24C02_NAK:
					now_state = X24C02_IDLE;
					bitcnt    = 0;
					sda       = 0xFF;
					break;
				case	X24C02_ACK:
					now_state = next_state;
					bitcnt    = 0;
					sda       = 0xFF;
					break;
				case	X24C02_ACK_WAIT:
					now_state = next_state;
					bitcnt    = 0;
					sda       = 0xFF;
					break;
			}
		}
	}

	BYTE	Read() {
		return	sda;
	}

	// For State save
	void	Load( LPBYTE p )
	{
		now_state  = *((INT*)&p[0]);
		next_state = *((INT*)&p[4]);
		bitcnt     = *((INT*)&p[8]);
		addr       = p[12];
		data       = p[13];
		rw         = p[14];
		sda        = p[15];
		scl_old    = p[16];
		sda_old    = p[17];
	}

	void	Save( LPBYTE p )
	{
		*((INT*)&p[0]) = now_state;
		*((INT*)&p[4]) = next_state;
		*((INT*)&p[8]) = bitcnt;
		p[12]          = addr;
		p[13]          = data;
		p[14]          = rw;
		p[15]          = sda;
		p[16]          = scl_old;
		p[17]          = sda_old;
	}

protected:
	enum	{
		X24C02_IDLE = 0,	// Idle
		X24C02_DEVADDR,		// Device address set
		X24C02_ADDRESS,		// Address set
		X24C02_READ,		// Read
		X24C02_WRITE,		// Write
		X24C02_ACK,		// Acknowledge
		X24C02_NAK,		// Not Acknowledge
		X24C02_ACK_WAIT,	// Acknowledge wait
	};

	INT	now_state, next_state;
	INT	bitcnt;
	BYTE	addr, data, rw;
	BYTE	sda;
	BYTE	scl_old, sda_old;

	LPBYTE	pEEPDATA;
private:
};

