Supermodel/core/eeprom.c
Ville Linde a1c9f6cf92
2006-07-12 13:53:14 +00:00

392 lines
9.5 KiB
C

/*
* Sega Model 3 Emulator
* Copyright (C) 2003 Bart Trzynadlowski, Ville Linde, Stefano Teso
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License Version 2 as published
* by the Free Software Foundation.
*
* This program 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 this program (license.txt); if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* eeprom.c
*
* 93C46 EEPROM emulation courtesy of R. Belmont.
*
* NOTE: Save state code assumes enums will be the same across all compilers
* and systems. The eeprom_store[] array is endian-dependent.
*/
/***************************************************************************
eeprom.cpp - handles a serial eeprom with 64 16-bit addresses and 4-bit commands
as seen in st-v, system 32, many konami boards, etc.
originally written: May 20, 2000 by R. Belmont for "sim"
update: Jan 27, 2001 (RB): implemented "erase" opcode,
made opcode fetch check for start bit
like the real chip.
Mar 26, 2001 (FF): added get_image to access the eeprom
Jan 28, 2004 (Bart): Interface changes to work with Supermodel.
Crudely simulated busy status by setting olatch to 0 in
ES_DESELECT.
***************************************************************************/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "model3.h"
#define LOG_EEPROM 0 // log channel for EEPROM
static INT32 eeprom_state;
static UINT16 eeprom_store[64]; // 6 address bits times 16 bits per location
static UINT8 opcode; // current opcode
static UINT8 adr; // current address in eeprom_store
static UINT8 curbit; // current bit # in read/write/addr fetch operation
static UINT8 lastclk; // last clock
static UINT8 dlatch; // input data latch
static UINT8 olatch; // output data latch
static UINT16 curio;
// states for the eeprom state machine
enum
{
ES_DESELECT = 0,// chip select not asserted
ES_ASSERT, // chip select asserted, waiting for opcode
ES_OPCODE0, // got opcode bit 0
ES_OPCODE1, // got opcode bit 1
ES_OPCODE2, // got opcode bit 2
ES_ADRFETCH, // fetching address
ES_READBITS, // reading bits, 1 out every time clock goes high
ES_WRITEBITS, // writing bits, 1 in every time clock goes high
ES_WAITING // done with read/write, waiting for deselect to finalize
};
/*
* UINT8 eeprom_read_bit(void);
*
* Reads a data bit from the EEPROM.
*
* Returns:
* Latched data bit.
*/
UINT8 eeprom_read_bit(void)
{
return olatch;
}
/*
* void eeprom_set_ce(UINT8 ce);
*
* Passes the status of the chip enable.
*
* Parameters:
* ce = Chip enable bit (must be 0 or 1.)
*/
void eeprom_set_ce(UINT8 ce)
{
// chip enable
if (ce)
{
// asserting CE - if deselected,
// select. if selected already,
// ignore
if (eeprom_state == ES_DESELECT)
{
// printf("EEPROM: asserting chip enable\n");
eeprom_state = ES_ASSERT;
opcode = 0;
}
}
else
{
// deasserting CE
// printf("EEPROM: deasserting chip enable\n");
eeprom_state = ES_DESELECT;
}
}
/*
* void eeprom_set_data(UINT8 data);
*
* Sets the data bit latch.
*
* Parameters:
* data = Data bit (must be 0 or 1.)
*/
void eeprom_set_data(UINT8 data)
{
dlatch = (data&0x01); // latch the data
}
/*
* void eeprom_set_clk(UINT8 clk);
*
* Sets the clock pin.
*
* Parameters:
* clk = Clock (zero or non-zero.)
*/
void eeprom_set_clk(UINT8 clk)
{
// things only happen on the rising edge of the clock
if ((!lastclk) && (clk))
{
switch (eeprom_state)
{
case ES_DESELECT:
olatch = 0;
break;
case ES_ASSERT:
case ES_OPCODE0:
case ES_OPCODE1:
case ES_OPCODE2:
// first command bit must be a "1" in the real device
if ((eeprom_state == ES_OPCODE0) && (dlatch == 0))
{
#if LOG_EEPROM
LOG("model3.log", "EEPROM: waiting for start bit\n");
#endif
olatch = 1; // assert READY
}
else
{
opcode <<= 1;
opcode |= dlatch;
#if LOG_EEPROM
LOG("model3.log", "EEPROM: opcode fetch state, currently %x\n", opcode);
#endif
eeprom_state++;
}
curbit = 0;
adr = 0;
break;
case ES_ADRFETCH: // fetch address
adr <<= 1;
adr |= dlatch;
#if LOG_EEPROM
LOG("model3.log", "EEPROM: address fetch state, cur addr = %x\n", adr);
#endif
curbit++;
if (curbit == 6)
{
switch (opcode)
{
case 4: // write enable - go back to accept a new opcode immediately
opcode = 0;
eeprom_state = ES_ASSERT;
//printf("EEPROM: write enable\n");
break;
case 5: // write
eeprom_state = ES_WRITEBITS;
olatch = 0; // indicate not ready yet
curbit = 0;
curio = 0;
break;
case 6: // read
eeprom_state = ES_READBITS;
curbit = 0;
curio = eeprom_store[adr];
//printf("EEPROM: read %04x from address %d\n", curio, adr);
break;
case 7: // erase location to ffff and assert READY
eeprom_store[adr] = 0xffff;
olatch = 1;
eeprom_state = ES_WAITING;
//printf("EEPROM: erase at address %d\n", adr);
break;
default:
#if LOG_EEPROM
LOG("model3.log", "Unknown EEPROM opcode %d!\n", opcode);
#endif
eeprom_state = ES_WAITING;
olatch = 1; // assert READY anyway
break;
}
}
break;
case ES_READBITS:
olatch = ((curio & 0x8000)>>15);
curio <<= 1;
curbit++;
if (curbit == 16)
{
eeprom_state = ES_WAITING;
}
break;
case ES_WRITEBITS:
curio <<= 1;
curio |= dlatch;
curbit++;
if (curbit == 16)
{
olatch = 1; // indicate success
eeprom_store[adr] = curio;
#if LOG_EEPROM
LOG("model3.log", "EEPROM: Wrote %04x to address %d\n", curio, adr);
#endif
eeprom_state = ES_WAITING;
}
break;
case ES_WAITING:
break;
}
}
lastclk = clk;
}
/*
* void eeprom_save_state(FILE *fp);
*
* Save the EEPROM state.
*
* Parameters:
* fp = File to save to.
*/
void eeprom_save_state(FILE *fp)
{
if (fp != NULL)
{
fwrite(&eeprom_state, sizeof(INT32), 1, fp);
fwrite(eeprom_store, sizeof(UINT16), 64, fp);
fwrite(&opcode, sizeof(UINT8), 1, fp);
fwrite(&adr, sizeof(UINT8), 1, fp);
fwrite(&curbit, sizeof(UINT8), 1, fp);
fwrite(&lastclk, sizeof(UINT8), 1, fp);
fwrite(&dlatch, sizeof(UINT8), 1, fp);
fwrite(&olatch, sizeof(UINT8), 1, fp);
fwrite(&curio, sizeof(UINT16), 1, fp);
}
}
/*
* void eeprom_load_state(FILE *fp);
*
* Load the EEPROM state.
*
* Parameters:
* fp = File to load from.
*/
void eeprom_load_state(FILE *fp)
{
if (fp != NULL)
{
fread(&eeprom_state, sizeof(INT32), 1, fp);
fread(eeprom_store, sizeof(UINT16), 64, fp);
fread(&opcode, sizeof(UINT8), 1, fp);
fread(&adr, sizeof(UINT8), 1, fp);
fread(&curbit, sizeof(UINT8), 1, fp);
fread(&lastclk, sizeof(UINT8), 1, fp);
fread(&dlatch, sizeof(UINT8), 1, fp);
fread(&olatch, sizeof(UINT8), 1, fp);
fread(&curio, sizeof(UINT16), 1, fp);
}
}
/*
* INT eeprom_load(CHAR *fname);
*
* Loads data from the file specified. If the file does not exist, the EEPROM
* data is initialized with all 1's.
*
* Parameters:
* fname = File name.
*
* Returns:
* MODEL3_OKAY = Success.
* MODEL3_ERROR = Error reading from the file.
*/
INT eeprom_load(CHAR *fname)
{
FILE *fp;
INT error_code = MODEL3_ERROR;
message(0, "loading EEPROM from %s", fname);
if ((fp = fopen(fname, "rb")) != NULL)
{
error_code = fread(eeprom_store, sizeof(UINT16), 64, fp);
fclose(fp);
if (error_code == 64)
return MODEL3_OKAY;
}
memset(eeprom_store, 0xFF, sizeof(UINT16) * 64);
return error_code;
}
/*
* INT eeprom_save(CHAR *fname);
*
* Writes out the EEPROM data to the specified file.
*
* Parameters:
* fname = File name to write.
*
* Returns:
* MODEL3_OKAY = Success.
* MODEL3_ERROR = Unable to open or write file.
*/
INT eeprom_save(CHAR *fname)
{
FILE *fp;
INT i = MODEL3_ERROR;
message(0, "saving EEPROM to %s", fname);
if ((fp = fopen(fname, "wb")) != NULL)
{
i = fwrite(eeprom_store, sizeof(UINT16), 64, fp);
fclose(fp);
if (i == 64)
return MODEL3_OKAY;
}
return i;
}
/*
* void eeprom_reset(void);
*
* Resets the EEPROM emulation.
*/
void eeprom_reset(void)
{
eeprom_state = ES_DESELECT;
lastclk = 0;
}