Supermodel/Src/Model3/93C46.cpp

266 lines
7.1 KiB
C++
Raw Normal View History

2011-04-24 01:14:00 +00:00
/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
** Copyright 2011 Bart Trzynadlowski, Nik Henson
2011-04-24 01:14:00 +00:00
**
** 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 <http://www.gnu.org/licenses/>.
**/
/*
* 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 "93C46.h"
#include <cstring>
2011-04-24 01:14:00 +00:00
#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 corrupt.");
2011-04-24 01:14:00 +00:00
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<<i);
}
return dataOut;
}
void C93C46::Write(unsigned pinCS, unsigned pinCLK, unsigned pinDI)
{
unsigned prevCLK;
//printf("EEPROM: CS=%d CLK=%d DI=%d\n", pinCS, pinCLK, pinDI);
prevCLK = CLK;
// Save current inputs
CS = !!pinCS;
CLK = !!pinCLK;
DI = !!pinDI;
// Active high CS. When it's brought low, reset control logic.
if (CS == 0)
{
bitBufferIn = 0; // this must be cleared each time (only leading 0's can exist prior to commands)
receiving = true; // ready to accept commands
2011-04-24 01:14:00 +00:00
busyCycles = 5; // some applications require the chip to take time while writing
return;
}
// Rising clock edge
if (!prevCLK && CLK)
{
if (receiving == true) // is the chip receiving commands?
2011-04-24 01:14:00 +00:00
{
// Shift in a new bit
bitBufferIn <<= 1;
bitBufferIn |= DI;
// Detect commands
if ((bitBufferIn&0xFFFFFFC0) == 0x180) // READ
{
addr = bitBufferIn&0x3F;
bitBufferOut = ReverseBits16(regs[addr]); // reverse so that D15 is shifted out first
//bitBufferOut <<= 1; // a leading 0 precedes the first word read (causes problems)
bitsOut = 0; // how many bits read out
receiving = false; // transmitting data now
2011-04-24 01:14:00 +00:00
DebugLog("93C46: READ %X\n", addr);
}
else if (bitBufferIn == 0x13) // WEN (write enable)
{
locked = false;
2011-04-24 01:14:00 +00:00
DebugLog("93C46: WEN\n");
}
else if (bitBufferIn == 0x10) // WDS (write disable)
{
locked = true;
2011-04-24 01:14:00 +00:00
DebugLog("93C46: WDS\n");
}
else if ((bitBufferIn&0xFFC00000) == 0x01400000) // WRITE
{
if (!locked)
regs[(bitBufferIn>>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;
2011-04-24 01:14:00 +00:00
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");
}