www.pudn.com > Nestopia137src.zip > NstBrdBandai.cpp


//////////////////////////////////////////////////////////////////////////////////////// 
// 
// Nestopia - NES/Famicom emulator written in C++ 
// 
// Copyright (C) 2003-2007 Martin Freij 
// 
// This file is part of Nestopia. 
// 
// Nestopia is free software; you can redistribute it and/or modify 
// it under the terms of the GNU General Public License as published by 
// the Free Software Foundation; either version 2 of the License, or 
// (at your option) any later version. 
// 
// Nestopia is distributed in the hope that it will be useful, 
// but WITHOUT ANY WARRANTY; without even the implied warranty of 
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
// GNU General Public License for more details. 
// 
// You should have received a copy of the GNU General Public License 
// along with Nestopia; if not, write to the Free Software 
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
// 
//////////////////////////////////////////////////////////////////////////////////////// 
 
#include  
#include "../NstMapper.hpp" 
#include "../NstClock.hpp" 
#include "../NstPrpBarcodeReader.hpp" 
#include "../NstFile.hpp" 
#include "NstBrdBandai.hpp" 
 
namespace Nes 
{ 
	namespace Core 
	{ 
		namespace Boards 
		{ 
			class Bandai::Eeprom 
			{ 
			public: 
 
				template 
				class X24C0X; 
 
				explicit Eeprom(Type); 
 
				void Load(); 
				void Save() const; 
 
				Pointer< X24C0X<128> > x24C01; 
				Pointer< X24C0X<256> > x24C02; 
 
			private: 
 
				File file; 
			}; 
 
			template 
			class Bandai::Eeprom::X24C0X 
			{ 
			public: 
 
				enum 
				{ 
					SIZE = N 
				}; 
 
				void Reset(); 
				void Set(uint,uint); 
				void LoadState(State::Loader&); 
				void SaveState(State::Saver&,dword) const; 
 
			private: 
 
				void Start(); 
				void Stop(); 
				void Rise(uint); 
				void Fall(); 
 
				enum Mode 
				{ 
					MODE_IDLE, 
					MODE_DATA, 
					MODE_ADDRESS, 
					MODE_READ, 
					MODE_WRITE, 
					MODE_ACK, 
					MODE_NOT_ACK, 
					MODE_ACK_WAIT, 
					MODE_MAX 
				}; 
 
				struct 
				{ 
					uint scl; 
					uint sda; 
				}   line; 
 
				Mode mode; 
				Mode next; 
 
				struct 
				{ 
					uint bit; 
					uint address; 
					uint data; 
				}   latch; 
 
				ibool rw; 
				uint output; 
				byte mem[SIZE]; 
 
			public: 
 
				X24C0X() 
				{ 
					std::memset( mem, 0, SIZE ); 
				} 
 
				void SetScl(uint scl) 
				{ 
					Set( scl, line.sda ); 
				} 
 
				void SetSda(uint sda) 
				{ 
					Set( line.scl, sda ); 
				} 
 
				uint Read() const 
				{ 
					return output; 
				} 
 
				void Load(const byte* NST_RESTRICT in) 
				{ 
					std::memcpy( mem, in, SIZE ); 
				} 
 
				void Save(byte* NST_RESTRICT out) const 
				{ 
					std::memcpy( out, mem, SIZE ); 
				} 
			}; 
 
			template 
			void Bandai::Eeprom::X24C0X::Reset() 
			{ 
				line.scl      = 0; 
				line.sda      = 0; 
				mode          = MODE_IDLE; 
				next          = MODE_IDLE; 
				latch.bit     = 0; 
				latch.address = 0; 
				latch.data    = 0; 
				rw            = false; 
				output        = 0x10; 
			} 
 
			template 
			void Bandai::Eeprom::X24C0X::SaveState(State::Saver& state,const dword baseChunk) const 
			{ 
				state.Begin( baseChunk ); 
 
				const byte data[6] = 
				{ 
					line.scl | line.sda, 
					uint(mode << 0) | uint(next << 4), 
					latch.address, 
					latch.data, 
					latch.bit, 
					output | (rw ? 0x80 : 0x00) 
				}; 
 
				state.Begin( AsciiId<'R','E','G'>::V ).Write( data ).End(); 
				state.Begin( AsciiId<'R','A','M'>::V ).Compress( mem ).End(); 
 
				state.End(); 
			} 
 
			template 
			void Bandai::Eeprom::X24C0X::LoadState(State::Loader& state) 
			{ 
				while (const dword chunk = state.Begin()) 
				{ 
					switch (chunk) 
					{ 
						case AsciiId<'R','E','G'>::V: 
						{ 
							State::Loader::Data<6> data( state ); 
 
							line.scl = data[0] & 0x20; 
							line.sda = data[0] & 0x40; 
 
							if ((data[1] & 0xF) < MODE_MAX) 
								mode = static_cast(data[1] & 0xF); 
 
							if ((data[1] >> 4) < MODE_MAX) 
								next = static_cast(data[1] >> 4); 
 
							latch.address = data[2] & (SIZE-1); 
							latch.data = data[3]; 
							latch.bit = NST_MAX(data[4],8); 
 
							rw = data[5] & 0x80; 
							output = data[5] & 0x10; 
							break; 
						} 
 
						case AsciiId<'R','A','M'>::V: 
 
							state.Uncompress( mem ); 
							break; 
					} 
 
					state.End(); 
				} 
			} 
 
			template<> 
			void Bandai::Eeprom::X24C0X<128>::Start() 
			{ 
				mode = MODE_ADDRESS; 
				latch.bit = 0; 
				latch.address = 0; 
				output = 0x10; 
			} 
 
			template<> 
			void Bandai::Eeprom::X24C0X<256>::Start() 
			{ 
				mode = MODE_DATA; 
				latch.bit = 0; 
				output = 0x10; 
			} 
 
			template 
			void Bandai::Eeprom::X24C0X::Stop() 
			{ 
				mode = MODE_IDLE; 
				output = 0x10; 
			} 
 
			template<> 
			void Bandai::Eeprom::X24C0X<128>::Rise(const uint bit) 
			{ 
				NST_ASSERT( bit <= 1 ); 
 
				switch (mode) 
				{ 
					case MODE_ADDRESS: 
 
						if (latch.bit < 7) 
						{ 
							latch.address &= ~(1U << latch.bit); 
							latch.address |= bit << latch.bit++; 
						} 
						else if (latch.bit < 8) 
						{ 
							latch.bit = 8; 
 
							if (bit) 
							{ 
								next = MODE_READ; 
								latch.data = mem[latch.address]; 
							} 
							else 
							{ 
								next = MODE_WRITE; 
							} 
						} 
						break; 
 
					case MODE_ACK: 
 
						output = 0x00; 
						break; 
 
					case MODE_READ: 
 
						if (latch.bit < 8) 
							output = (latch.data & 1U << latch.bit++) ? 0x10 : 0x00; 
 
						break; 
 
					case MODE_WRITE: 
 
						if (latch.bit < 8) 
						{ 
							latch.data &= ~(1U << latch.bit); 
							latch.data |= bit << latch.bit++; 
						} 
						break; 
 
					case MODE_ACK_WAIT: 
 
						if (bit == 0) 
							next = MODE_IDLE; 
 
						break; 
				} 
			} 
 
			template<> 
			void Bandai::Eeprom::X24C0X<128>::Fall() 
			{ 
				switch (mode) 
				{ 
					case MODE_ADDRESS: 
 
						if (latch.bit == 8) 
						{ 
							mode = MODE_ACK; 
							output = 0x10; 
						} 
						break; 
 
					case MODE_ACK: 
 
						mode = next; 
						latch.bit = 0; 
						output = 0x10; 
						break; 
 
					case MODE_READ: 
 
						if (latch.bit == 8) 
						{ 
							mode = MODE_ACK_WAIT; 
							latch.address = (latch.address+1) & 0x7F; 
						} 
						break; 
 
					case MODE_WRITE: 
 
						if (latch.bit == 8) 
						{ 
							mode = MODE_ACK; 
							next = MODE_IDLE; 
							mem[latch.address] = latch.data; 
							latch.address = (latch.address+1) & 0x7F; 
						} 
						break; 
				} 
			} 
 
			template<> 
			void Bandai::Eeprom::X24C0X<256>::Rise(const uint bit) 
			{ 
				NST_ASSERT( bit <= 1 ); 
 
				switch (mode) 
				{ 
					case MODE_DATA: 
 
						if (latch.bit < 8) 
						{ 
							latch.data &= ~(1U << (7-latch.bit)); 
							latch.data |= bit << (7-latch.bit++); 
						} 
						break; 
 
					case MODE_ADDRESS: 
 
						if (latch.bit < 8) 
						{ 
							latch.address &= ~(1U << (7-latch.bit)); 
							latch.address |= bit << (7-latch.bit++); 
						} 
						break; 
 
					case MODE_READ: 
 
						if (latch.bit < 8) 
							output = (latch.data & 1U << (7-latch.bit++)) ? 0x10 : 0x00; 
 
						break; 
 
					case MODE_WRITE: 
 
						if (latch.bit < 8) 
						{ 
							latch.data &= ~(1U << (7-latch.bit)); 
							latch.data |= bit << (7-latch.bit++); 
						} 
						break; 
 
					case MODE_NOT_ACK: 
 
						output = 0x10; 
						break; 
 
					case MODE_ACK: 
 
						output = 0x00; 
						break; 
 
					case MODE_ACK_WAIT: 
 
						if (bit == 0) 
						{ 
							next = MODE_READ; 
							latch.data = mem[latch.address]; 
						} 
						break; 
				} 
			} 
 
			template<> 
			void Bandai::Eeprom::X24C0X<256>::Fall() 
			{ 
				switch (mode) 
				{ 
					case MODE_DATA: 
 
						if (latch.bit == 8) 
						{ 
							if ((latch.data & 0xA0) == 0xA0) 
							{ 
								latch.bit = 0; 
								mode = MODE_ACK; 
								rw = latch.data & 0x01; 
								output = 0x10; 
 
								if (rw) 
								{ 
									next = MODE_READ; 
									latch.data = mem[latch.address]; 
								} 
								else 
								{ 
									next = MODE_ADDRESS; 
								} 
							} 
							else 
							{ 
								mode = MODE_NOT_ACK; 
								next = MODE_IDLE; 
								output = 0x10; 
							} 
						} 
						break; 
 
					case MODE_ADDRESS: 
 
						if (latch.bit == 8) 
						{ 
							latch.bit = 0; 
							mode = MODE_ACK; 
							next = (rw ? MODE_IDLE : MODE_WRITE); 
							output = 0x10; 
						} 
						break; 
 
					case MODE_READ: 
 
						if (latch.bit == 8) 
						{ 
							mode = MODE_ACK_WAIT; 
							latch.address = (latch.address+1) & 0xFF; 
						} 
						break; 
 
					case MODE_WRITE: 
 
						if (latch.bit == 8) 
						{ 
							latch.bit = 0; 
							mode = MODE_ACK; 
							next = MODE_WRITE; 
							mem[latch.address] = latch.data; 
							latch.address = (latch.address+1) & 0xFF; 
						} 
						break; 
 
					case MODE_NOT_ACK: 
 
						mode = MODE_IDLE; 
						latch.bit = 0; 
						output = 0x10; 
						break; 
 
					case MODE_ACK: 
					case MODE_ACK_WAIT: 
 
						mode = next; 
						latch.bit = 0; 
						output = 0x10; 
						break; 
				} 
			} 
 
			template 
			void Bandai::Eeprom::X24C0X::Set(const uint scl,const uint sda) 
			{ 
				if (line.scl && sda < line.sda) 
				{ 
					Start(); 
				} 
				else if (line.scl && sda > line.sda) 
				{ 
					Stop(); 
				} 
				else if (scl > line.scl) 
				{ 
					Rise( sda >> 6 ); 
				} 
				else if (scl < line.scl) 
				{ 
					Fall(); 
				} 
 
				line.scl = scl; 
				line.sda = sda; 
			} 
 
			class Bandai::DatachJointSystem : public Peripherals::BarcodeReader 
			{ 
			public: 
 
				explicit DatachJointSystem(Cpu&); 
 
				void Reset(); 
				void SaveState(State::Saver&,dword) const; 
				void LoadState(State::Loader&); 
 
			private: 
 
				enum 
				{ 
					CC_INTERVAL = 1000 
				}; 
 
				bool SubTransfer(cstring,uint,byte*); 
 
				NES_DECL_HOOK( Transfer ); 
 
				Cpu& cpu; 
				Cycle cycles; 
				uint data; 
 
				bool IsDigitsSupported(uint count) const 
				{ 
					return count == MIN_DIGITS || count == MAX_DIGITS; 
				} 
 
			public: 
 
				void VSync() 
				{ 
					if (cycles != Cpu::CYCLE_MAX) 
					{ 
						if (cycles >= cpu.GetFrameCycles()) 
							cycles -= cpu.GetFrameCycles(); 
						else 
							cycles = 0; 
					} 
				} 
 
				uint GetData() const 
				{ 
					return data; 
				} 
			}; 
 
			#ifdef NST_MSVC_OPTIMIZE 
			#pragma optimize("s", on) 
			#endif 
 
			Bandai::DatachJointSystem::DatachJointSystem(Cpu& c) 
			: cpu(c), cycles(Cpu::CYCLE_MAX), data(0x00) {} 
 
			Bandai::Eeprom::Eeprom(const Type t) 
			: 
			x24C01 (t == TYPE_DATACH || t == TYPE_LZ93D50_E2401 ? new X24C0X<128> : NULL), 
			x24C02 (t == TYPE_DATACH || t == TYPE_LZ93D50_E2402 ? new X24C0X<256> : NULL) 
			{ 
			} 
 
			Bandai::Bandai(Context& c,const Type t) 
			: 
			Mapper (c,t == TYPE_WRAM ? CROM_MAX_256K|NMT_VERTICAL : CROM_MAX_256K|WRAM_DEFAULT|NMT_VERTICAL), 
			irq    (c.cpu), 
			datach (t == TYPE_DATACH ? new DatachJointSystem(c.cpu) : NULL), 
			eeprom (t == TYPE_DATACH || t == TYPE_LZ93D50_E2401 || t == TYPE_LZ93D50_E2402 ? new Eeprom(t) : NULL), 
			type   (t) 
			{ 
				if (eeprom) 
					eeprom->Load(); 
			} 
 
			Bandai::~Bandai() 
			{ 
			} 
 
			Bandai::Device Bandai::QueryDevice(DeviceType devType) 
			{ 
				if (devType == DEVICE_BARCODE_READER && datach) 
				{ 
					return &*datach; 
				} 
				else 
				{ 
					return Mapper::QueryDevice( devType ); 
				} 
			} 
 
			void Bandai::Eeprom::Load() 
			{ 
				byte buffer[256+128]; 
				std::memset( buffer, 0, sizeof(buffer) ); 
 
				file.Load( File::LOAD_EEPROM, buffer, (x24C02 ? 256 : 0) + (x24C01 ? 128 : 0) ); 
 
				if (x24C02) 
					x24C02->Load( buffer ); 
 
				if (x24C01) 
					x24C01->Load( buffer + (x24C02 ? 256 : 0) ); 
			} 
 
			void Bandai::Eeprom::Save() const 
			{ 
				byte buffer[256+128]; 
 
				if (x24C02) 
					x24C02->Save( buffer ); 
 
				if (x24C01) 
					x24C01->Save( buffer + (x24C02 ? 256 : 0) ); 
 
				file.Save( File::SAVE_EEPROM, buffer, (x24C02 ? 256 : 0) + (x24C01 ? 128 : 0) ); 
			} 
 
			void Bandai::Irq::Reset(const bool hard) 
			{ 
				if (hard) 
				{ 
					latch = 0; 
					count = 0; 
				} 
			} 
 
			void Bandai::DatachJointSystem::Reset() 
			{ 
				BarcodeReader::Reset(); 
				cycles = Cpu::CYCLE_MAX; 
				data = 0x00; 
				cpu.AddHook( Hook(this,&DatachJointSystem::Hook_Transfer) ); 
			} 
 
			void Bandai::SubReset(const bool hard) 
			{ 
				irq.Reset( hard, hard ? false : irq.Connected() ); 
 
				if (datach) 
					datach->Reset(); 
 
				if (eeprom) 
				{ 
					if (eeprom->x24C01) 
						eeprom->x24C01->Reset(); 
 
					if (eeprom->x24C02) 
						eeprom->x24C02->Reset(); 
				} 
 
				switch (type) 
				{ 
					case TYPE_WRAM: 
 
						for (dword i=0x8000; i <= 0xFFFF; i += 0x10) 
						{ 
							Map( i + 0x0, &Bandai::Poke_8000_B ); 
							Map( i + 0x8, &Bandai::Poke_8008_B ); 
							Map( i + 0x9, NMT_SWAP_VH01        ); 
							Map( i + 0xA, &Bandai::Poke_800A   ); 
							Map( i + 0xB, &Bandai::Poke_800B   ); 
							Map( i + 0xC, &Bandai::Poke_800C   ); 
						} 
						break; 
 
					case TYPE_DATACH: 
 
						for (uint i=0x6000; i <= 0x7FFF; i += 0x100) 
							Map( i, &Bandai::Peek_6000_C ); 
 
						for (dword i=0x6000; i <= 0xFFFF; i += 0x10) 
						{ 
							Map( uint(i) + 0x0, uint(i) + 0x7, &Bandai::Poke_8000_C ); 
							Map( uint(i) + 0x8,                PRG_SWAP_16K_0       ); 
							Map( uint(i) + 0x9,                NMT_SWAP_VH01        ); 
							Map( uint(i) + 0xA,                &Bandai::Poke_800A   ); 
							Map( uint(i) + 0xB,                &Bandai::Poke_800B   ); 
							Map( uint(i) + 0xC,                &Bandai::Poke_800C   ); 
							Map( uint(i) + 0xD,                &Bandai::Poke_800D_C ); 
						} 
						break; 
 
					default: 
 
						if (eeprom) 
						{ 
							for (uint i=0x6000; i <= 0x7FFF; i += 0x100) 
								Map( i, eeprom->x24C01 ? &Bandai::Peek_6000_A1 : &Bandai::Peek_6000_A2 ); 
						} 
 
						for (dword i=0x6000; i <= 0xFFFF; i += 0x10) 
						{ 
							Map( i + 0x0, CHR_SWAP_1K_0      ); 
							Map( i + 0x1, CHR_SWAP_1K_1      ); 
							Map( i + 0x2, CHR_SWAP_1K_2      ); 
							Map( i + 0x3, CHR_SWAP_1K_3      ); 
							Map( i + 0x4, CHR_SWAP_1K_4      ); 
							Map( i + 0x5, CHR_SWAP_1K_5      ); 
							Map( i + 0x6, CHR_SWAP_1K_6      ); 
							Map( i + 0x7, CHR_SWAP_1K_7      ); 
							Map( i + 0x8, PRG_SWAP_16K_0     ); 
							Map( i + 0x9, NMT_SWAP_VH01      ); 
							Map( i + 0xA, &Bandai::Poke_800A ); 
							Map( i + 0xB, &Bandai::Poke_800B ); 
							Map( i + 0xC, &Bandai::Poke_800C ); 
 
							if (eeprom) 
								Map( i + 0xD, eeprom->x24C01 ? &Bandai::Poke_800D_A1 : &Bandai::Poke_800D_A2 ); 
						} 
						break; 
				} 
			} 
 
			void Bandai::BaseLoad(State::Loader& state,const dword baseChunk) 
			{ 
				NST_VERIFY( baseChunk == (AsciiId<'B','A','N'>::V) ); 
 
				if (baseChunk == AsciiId<'B','A','N'>::V) 
				{ 
					while (const dword chunk = state.Begin()) 
					{ 
						switch (chunk) 
						{ 
							case AsciiId<'I','R','Q'>::V: 
							{ 
								State::Loader::Data<5> data( state ); 
 
								irq.Connect( data[0] & 0x1 ); 
								irq.unit.latch = data[1] | data[2] << 8; 
								irq.unit.count = data[3] | data[4] << 8; 
								break; 
							} 
 
							case AsciiId<'B','R','C'>::V: 
 
								NST_VERIFY( datach ); 
 
								if (datach) 
									datach->LoadState( state ); 
 
								break; 
 
							case AsciiId<'C','0','1'>::V: 
 
								NST_VERIFY( eeprom && eeprom->x24C01 ); 
 
								if (eeprom && eeprom->x24C01) 
									eeprom->x24C01->LoadState( state ); 
 
								break; 
 
							case AsciiId<'C','0','2'>::V: 
 
								NST_VERIFY( eeprom && eeprom->x24C02 ); 
 
								if (eeprom && eeprom->x24C02) 
									eeprom->x24C02->LoadState( state ); 
 
								break; 
						} 
 
						state.End(); 
					} 
				} 
			} 
 
			void Bandai::DatachJointSystem::LoadState(State::Loader& state) 
			{ 
				BarcodeReader::Reset(); 
				cycles = Cpu::CYCLE_MAX; 
 
				while (const dword chunk = state.Begin()) 
				{ 
					if (chunk == AsciiId<'C','Y','C'>::V) 
						cycles = state.Read16(); 
					else 
						BarcodeReader::LoadState( state, chunk ); 
 
					state.End(); 
				} 
 
				if (IsTransferring()) 
				{ 
					data = Latch(); 
 
					if (cycles > CC_INTERVAL) 
						cycles = CC_INTERVAL; 
 
					cycles = cpu.GetCycles() + cpu.GetClock() * cycles; 
				} 
				else 
				{ 
					cycles = Cpu::CYCLE_MAX; 
					data = 0x00; 
				} 
			} 
 
			void Bandai::BaseSave(State::Saver& state) const 
			{ 
				state.Begin( AsciiId<'B','A','N'>::V ); 
 
				const byte data[5] = 
				{ 
					irq.Connected() ? 0x1 : 0x0, 
					irq.unit.latch & 0xFF, 
					irq.unit.latch >> 8, 
					irq.unit.count & 0xFF, 
					irq.unit.count >> 8 
				}; 
 
				state.Begin( AsciiId<'I','R','Q'>::V ).Write( data ).End(); 
 
				if (datach && datach->IsTransferring()) 
					datach->SaveState( state, AsciiId<'B','R','C'>::V ); 
 
				if (eeprom) 
				{ 
					if (eeprom->x24C01) 
						eeprom->x24C01->SaveState( state, AsciiId<'C','0','1'>::V ); 
 
					if (eeprom->x24C02) 
						eeprom->x24C02->SaveState( state, AsciiId<'C','0','2'>::V ); 
				} 
 
				state.End(); 
			} 
 
			void Bandai::DatachJointSystem::SaveState(State::Saver& state,const dword baseChunk) const 
			{ 
				NST_ASSERT( IsTransferring() && cycles != Cpu::CYCLE_MAX ); 
 
				state.Begin( baseChunk ); 
 
				uint next; 
 
				if (cycles > cpu.GetCycles()) 
					next = (cycles - cpu.GetCycles()) / (cpu.GetRegion() == Region::NTSC ? Clocks::RP2A03_CC : Clocks::RP2A07_CC); 
				else 
					next = 0; 
 
				state.Begin( AsciiId<'C','Y','C'>::V ).Write16( next ).End(); 
				BarcodeReader::SaveState( state ); 
 
				state.End(); 
			} 
 
			bool Bandai::DatachJointSystem::SubTransfer(cstring const string,uint length,byte* NST_RESTRICT output) 
			{ 
				if (length != MAX_DIGITS && length != MIN_DIGITS) 
					return false; 
 
				static const byte prefixParityType[10][6] = 
				{ 
					{8,8,8,8,8,8}, {8,8,0,8,0,0}, 
					{8,8,0,0,8,0}, {8,8,0,0,0,8}, 
					{8,0,8,8,0,0}, {8,0,0,8,8,0}, 
					{8,0,0,0,8,8}, {8,0,8,0,8,0}, 
					{8,0,8,0,0,8}, {8,0,0,8,0,8} 
				}; 
 
				static const byte dataLeftOdd[10][7] = 
				{ 
					{8,8,8,0,0,8,0}, {8,8,0,0,8,8,0}, 
					{8,8,0,8,8,0,0}, {8,0,0,0,0,8,0}, 
					{8,0,8,8,8,0,0}, {8,0,0,8,8,8,0}, 
					{8,0,8,0,0,0,0}, {8,0,0,0,8,0,0}, 
					{8,0,0,8,0,0,0}, {8,8,8,0,8,0,0} 
				}; 
 
				static const byte dataLeftEven[10][7] = 
				{ 
					{8,0,8,8,0,0,0}, {8,0,0,8,8,0,0}, 
					{8,8,0,0,8,0,0}, {8,0,8,8,8,8,0}, 
					{8,8,0,0,0,8,0}, {8,0,0,0,8,8,0}, 
					{8,8,8,8,0,8,0}, {8,8,0,8,8,8,0}, 
					{8,8,8,0,8,8,0}, {8,8,0,8,0,0,0} 
				}; 
 
				static const byte dataRight[10][7] = 
				{ 
					{0,0,0,8,8,0,8}, {0,0,8,8,0,0,8}, 
					{0,0,8,0,0,8,8}, {0,8,8,8,8,0,8}, 
					{0,8,0,0,0,8,8}, {0,8,8,0,0,0,8}, 
					{0,8,0,8,8,8,8}, {0,8,8,8,0,8,8}, 
					{0,8,8,0,8,8,8}, {0,0,0,8,0,8,8} 
				}; 
 
				byte code[16]; 
 
				for (uint i=0; i < length; ++i) 
				{ 
					if (string[i] >= '0' && string[i] <= '9') 
						code[i] = string[i] - '0'; 
					else 
						return false; 
				} 
 
				for (uint i=0; i < 1+32; ++i) 
					*output++ = 8; 
 
				*output++ = 0; 
				*output++ = 8; 
				*output++ = 0; 
 
				uint sum = 0; 
 
				if (length == MAX_DIGITS) 
				{ 
					for (uint i=0; i < 6; ++i) 
					{ 
						if (prefixParityType[code[0]][i]) 
						{ 
							for (uint j=0; j < 7; ++j) 
								*output++ = dataLeftOdd[code[i+1]][j]; 
						} 
						else 
						{ 
							for (uint j=0; j < 7; ++j) 
								*output++ = dataLeftEven[code[i+1]][j]; 
						} 
					} 
 
					*output++ = 8; 
					*output++ = 0; 
					*output++ = 8; 
					*output++ = 0; 
					*output++ = 8; 
 
					for (uint i=7; i < 12; ++i) 
					{ 
						for (uint j=0; j < 7; ++j) 
							*output++ = dataRight[code[i]][j]; 
					} 
 
					for (uint i=0; i < 12; ++i) 
						sum += (i & 1) ? (code[i] * 3) : (code[i] * 1); 
				} 
				else 
				{ 
					for (uint i=0; i < 4; ++i) 
					{ 
						for (uint j=0; j < 7; ++j) 
							*output++ = dataLeftOdd[code[i]][j]; 
					} 
 
					*output++ = 8; 
					*output++ = 0; 
					*output++ = 8; 
					*output++ = 0; 
					*output++ = 8; 
 
					for (uint i=4; i < 7; ++i) 
					{ 
						for (uint j=0; j < 7; ++j) 
							*output++ = dataRight[code[i]][j]; 
					} 
 
					for (uint i=0; i < 7; ++i) 
						sum += (i & 1) ? (code[i] * 1) : (code[i] * 3); 
				} 
 
				sum = (10 - (sum % 10)) % 10; 
 
				for (uint i=0; i < 7; ++i) 
					*output++ = dataRight[sum][i]; 
 
				*output++ = 0; 
				*output++ = 8; 
				*output++ = 0; 
 
				for (uint i=0; i < 32; ++i) 
					*output++ = 8; 
 
				cycles = cpu.GetCycles() + cpu.GetClock() * CC_INTERVAL; 
 
				return true; 
			} 
 
			#ifdef NST_MSVC_OPTIMIZE 
			#pragma optimize("", on) 
			#endif 
 
			NES_HOOK(Bandai::DatachJointSystem,Transfer) 
			{ 
				while (cycles <= cpu.GetCycles()) 
				{ 
					data = Fetch(); 
 
					if (data != END) 
					{ 
						cycles += cpu.GetClock() * CC_INTERVAL; 
					} 
					else 
					{ 
						data = 0x00; 
						cycles = Cpu::CYCLE_MAX; 
						break; 
					} 
				} 
			} 
 
			NES_PEEK(Bandai,6000_A1) 
			{ 
				return eeprom->x24C01->Read(); 
			} 
 
			NES_PEEK(Bandai,6000_A2) 
			{ 
				return eeprom->x24C02->Read(); 
			} 
 
			NES_PEEK(Bandai,6000_C) 
			{ 
				return datach->GetData() | (eeprom->x24C01->Read() & eeprom->x24C02->Read()); 
			} 
 
			NES_POKE_D(Bandai,8000_B) 
			{ 
				data = data << 4 & 0x10; 
				prg.SwapBanks( data | (prg.GetBank() & 0x0F), data | 0xF ); 
			} 
 
			NES_POKE_D(Bandai,8008_B) 
			{ 
				prg.SwapBank( (prg.GetBank() & 0x10) | (data & 0x0F) ); 
			} 
 
			NES_POKE_AD(Bandai,8000_C) 
			{ 
				if (!chr.Source().Writable()) 
					chr.SwapBank( (address & 0x7) << 10, data ); 
 
				eeprom->x24C01->SetScl( data << 2 & 0x20 ); 
			} 
 
			NES_POKE_D(Bandai,800A) 
			{ 
				irq.Update(); 
				irq.unit.count = irq.unit.latch + 1; 
				irq.Connect( data & 0x1 ); 
				irq.ClearIRQ(); 
			} 
 
			NES_POKE_D(Bandai,800B) 
			{ 
				irq.Update(); 
				irq.unit.latch = (irq.unit.latch & 0xFF00) | data << 0; 
			} 
 
			NES_POKE_D(Bandai,800C) 
			{ 
				irq.Update(); 
				irq.unit.latch = (irq.unit.latch & 0x00FF) | data << 8; 
			} 
 
			NES_POKE_D(Bandai,800D_A1) 
			{ 
				eeprom->x24C01->Set( data & 0x20, data & 0x40 ); 
			} 
 
			NES_POKE_D(Bandai,800D_A2) 
			{ 
				eeprom->x24C02->Set( data & 0x20, data & 0x40 ); 
			} 
 
			NES_POKE_D(Bandai,800D_C) 
			{ 
				eeprom->x24C02->Set( data & 0x20, data & 0x40 ); 
				eeprom->x24C01->SetSda( data & 0x40 ); 
			} 
 
			bool Bandai::Irq::Clock() 
			{ 
				count = (count - 1U) & 0xFFFF; 
				return count == 0; 
			} 
 
			void Bandai::Sync(Event event,Input::Controllers*) 
			{ 
				if (event == EVENT_END_FRAME) 
				{ 
					irq.VSync(); 
 
					if (datach) 
						datach->VSync(); 
				} 
				else if (event == EVENT_POWER_OFF) 
				{ 
					if (eeprom) 
						eeprom->Save(); 
				} 
			} 
		} 
	} 
}