mirror of
https://github.com/RetroDECK/Supermodel.git
synced 2024-11-22 22:05:38 +00:00
392 lines
9.5 KiB
C
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;
|
|
}
|