/** ** Supermodel ** A Sega Model 3 Arcade Emulator. ** Copyright 2011 Bart Trzynadlowski, Nik Henson ** ** This file is part of Supermodel. ** ** Supermodel 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 3 of the License, or (at your option) ** any later version. ** ** Supermodel 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 Supermodel. If not, see . **/ /* * 93C46.cpp * * Implementation of the 93C46 serial EEPROM device. * * To-Do List * ---------- * - Manual says that when a READ command is issued, the data will be * preceded by a leading 0. This seems to cause problems. Perhaps DO * should just be set to 0? */ #include #include "Supermodel.h" /****************************************************************************** Save States ******************************************************************************/ void C93C46::SaveState(CBlockFile *SaveState) { SaveState->NewBlock("93C46", __FILE__); SaveState->Write(regs, sizeof(regs)); SaveState->Write(&CS, sizeof(CS)); SaveState->Write(&CLK, sizeof(CLK)); SaveState->Write(&DI, sizeof(DI)); SaveState->Write(&DO, sizeof(DO)); SaveState->Write(&bitBufferOut, sizeof(bitBufferOut)); SaveState->Write(&bitBufferIn, sizeof(bitBufferIn)); SaveState->Write(&bitsOut, sizeof(bitsOut)); SaveState->Write(&receiving, sizeof(receiving)); SaveState->Write(&addr, sizeof(addr)); SaveState->Write(&busyCycles, sizeof(busyCycles)); SaveState->Write(&locked, sizeof(locked)); } void C93C46::LoadState(CBlockFile *SaveState) { if (OKAY != SaveState->FindBlock("93C46")) { ErrorLog("Unable to load EEPROM state. File is corrupted."); return; } SaveState->Read(regs, sizeof(regs)); SaveState->Read(&CS, sizeof(CS)); SaveState->Read(&CLK, sizeof(CLK)); SaveState->Read(&DI, sizeof(DI)); SaveState->Read(&DO, sizeof(DO)); SaveState->Read(&bitBufferOut, sizeof(bitBufferOut)); SaveState->Read(&bitBufferIn, sizeof(bitBufferIn)); SaveState->Read(&bitsOut, sizeof(bitsOut)); SaveState->Read(&receiving, sizeof(receiving)); SaveState->Read(&addr, sizeof(addr)); SaveState->Read(&busyCycles, sizeof(busyCycles)); SaveState->Read(&locked, sizeof(locked)); } /****************************************************************************** Emulation Functions ******************************************************************************/ // Reverse the bit ordering static UINT16 ReverseBits16(UINT16 data) { UINT16 dataOut = 0, hi, lo; for (int i = 0; i < 16/2; i++) { // Isolate corresponding bits from high and low halves of word lo = (data>>i)&1; hi = (data>>(15-i))&1; // Swap them dataOut |= (lo<<(15-i)); dataOut |= (hi<>16)&0x3F] = bitBufferIn&0xFFFF; DO = 1; // ready (write completed) DebugLog("93C46: WRITE %X=%04X (lock=%d)\n", (bitBufferIn>>16)&0x3F, bitBufferIn&0xFFFF, locked); } else if ((bitBufferIn&0xFFF00000) == 0x01100000) // WRALL (write all) { if (!locked) { for (int i = 0; i < 64; i++) regs[i] = bitBufferIn&0xFFFF; } DO = 1; DebugLog("93C46: WRALL %04X (lock=%d)\n", bitBufferIn&0xFFFF, locked); } else if ((bitBufferIn&0xFFFFFFC0) == 0x1C0) // ERASE { if (!locked) regs[bitBufferIn&0x3F] = 0xFFFF; DO = 1; DebugLog("93C46: ERASE %X (lock=%d)\n", bitBufferIn&0x3F, locked); } else if ((bitBufferIn&0xFFFFFFF0) == 0x120) // ERALL (erase all) { if (!locked) { for (int i = 0; i < 64; i++) regs[i] = 0xFFFF; DebugLog("93C46: ERALL (lock=%d)\n", locked); } DO = 1; } } else // the chip is reading out data (transmitting) { // Shift out to DO pin DO = bitBufferOut&1; bitBufferOut >>= 1; ++bitsOut; // If we've shifted out an entire 16-bit word, load up the next address (no preceding 0) if (bitsOut == 16) { addr = (addr+1)&0x3F; bitBufferOut = ReverseBits16(regs[addr]); bitsOut = 0; DebugLog("93C46: Next word loaded: %X\n", addr); } } } } unsigned C93C46::Read(void) { // When not transmitting, DO indicates whether busy or not if (receiving) { if (busyCycles > 0) // simulate programming delay { --busyCycles; return 0; // busy } else return 1; // ready to accept new command } // Transmit data return DO; } void C93C46::Clear(void) { memset(regs, 0xFF, sizeof(regs)); } void C93C46::Reset(void) { receiving = true; locked = true; bitBufferIn = 0; bitBufferOut = 0; addr = 0; busyCycles = 0; CS = 0; } /****************************************************************************** Configuration, Initialization, and Shutdown ******************************************************************************/ void C93C46::Init(void) { // this function really only exists for consistency with other device classes } C93C46::C93C46(void) { memset(regs, 0xFF, sizeof(regs)); DebugLog("Built 93C46 EEPROM\n"); } C93C46::~C93C46(void) { DebugLog("Destroyed 93C46 EEPROM\n"); }