Merge pull request #6751 from disean/nmc93cxx_rewrite

nmc93cxx: Rewrite the nmc93cxx emulation
This commit is contained in:
Miran Grča
2026-01-25 06:49:33 +01:00
committed by GitHub
7 changed files with 669 additions and 229 deletions

View File

@@ -1,25 +1,72 @@
#include <86box/vid_ati_eeprom.h>
/*
* 86Box A hypervisor and IBM PC system emulator that specializes in
* running old operating systems and software designed for IBM
* PC systems and compatibles from 1981 through fairly recent
* system designs based on the PCI bus.
*
* This file is part of the 86Box distribution.
*
* Header of the emulation of the National Semiconductors NMC93Cxx EEPROMs
* (16 bits or 8 bits).
*
* Authors: Cacodemon345
*
* Copyright 2023 Cacodemon345
*/
#pragma once
typedef struct nmc93cxx_eeprom_t {
ati_eeprom_t dev;
uint8_t addrbits;
uint16_t size;
char filename[1024];
} nmc93cxx_eeprom_t;
/* Forward declaration to hide internal device state from users. */
typedef struct nmc93cxx_eeprom_t nmc93cxx_eeprom_t;
/* EEPROM device type used to specify the size of data array. */
typedef enum nmc93cxx_eeprom_type {
/*
* Standard 93CX6 class of 16-bit EEPROMs.
*
* Type / Bits per cell / Number of cells
*/
NMC_93C06_x16_16,
NMC_93C46_x16_64,
NMC_93C56_x16_128,
NMC_93C57_x16_128,
NMC_93C66_x16_256,
NMC_93C76_x16_512,
NMC_93C86_x16_1024,
/*
* Some manufacturers use pin 6 as an "ORG" pin which,
* when pulled low, configures memory for 8-bit accesses.
*
* Type / Bits per cell / Number of cells
*/
NMC_93C46_x8_128,
NMC_93C56_x8_256,
NMC_93C57_x8_256,
NMC_93C66_x8_512,
NMC_93C76_x8_1024,
NMC_93C86_x8_2048,
} nmc93cxx_eeprom_type;
/* EEPROM device parameters. */
typedef struct nmc93cxx_eeprom_params_t {
uint16_t nwords;
char *filename;
uint16_t *default_content;
/* Device type */
nmc93cxx_eeprom_type type;
/* Name of EEPROM image file */
const char *filename;
/*
* Optional pointer to the default data buffer.
* The buffer size should match the size of EEPROM data array specified by nmc93cxx_eeprom_type.
*/
const void *default_content;
} nmc93cxx_eeprom_params_t;
/* Read from the EEPROM. */
uint16_t nmc93cxx_eeprom_read(nmc93cxx_eeprom_t *eeprom);
/* Read the state of the data output (DO) line. */
bool nmc93cxx_eeprom_read(nmc93cxx_eeprom_t *dev);
/* Write to the EEPROM. */
void nmc93cxx_eeprom_write(nmc93cxx_eeprom_t *eeprom, int eecs, int eesk, int eedi);
/* Set the state of the input lines. */
void nmc93cxx_eeprom_write(nmc93cxx_eeprom_t *dev, bool eecs, bool eesk, bool eedi);
/* Get EEPROM data array. */
uint16_t *nmc93cxx_eeprom_data(nmc93cxx_eeprom_t *eeprom);
/* Returns pointer to the current EEPROM data array. */
const uint16_t *nmc93cxx_eeprom_data(nmc93cxx_eeprom_t *dev);
extern const device_t nmc93cxx_device;

View File

@@ -6,267 +6,676 @@
*
* This file is part of the 86Box distribution.
*
* Emulation of National Semiconductors NMC93Cxx EEPROMs.
* Emulation of National Semiconductors NMC93Cxx EEPROMs (16 bits or 8 bits).
*
* Authors: Cacodemon345
*
* Copyright 2023 Cacodemon345
*/
/* Ported over from QEMU */
/* Ported over from the MAME eepromser.cpp implementation. Copyright 2013 Aaron Giles */
#ifdef ENABLE_NMC93CXX_EEPROM_LOG
#include <stdarg.h>
#endif
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stddef.h>
#include <wchar.h>
#include <time.h>
#define HAVE_STDARG_H
#include <assert.h>
#include <86box/86box.h>
#include <86box/device.h>
#include <86box/timer.h>
#include <86box/nvr.h>
#include <86box/log.h>
#include <86box/nmc93cxx.h>
#include <86box/plat_unused.h>
#define WRITE_TIME_US 1750
#define WRITE_ALL_TIME_US 8000
#define ERASE_TIME_US 1000
#define ERASE_ALL_TIME_US 8000
typedef enum EepromCommand {
COMMAND_INVALID,
COMMAND_READ,
COMMAND_WRITE,
COMMAND_ERASE,
COMMAND_LOCK,
COMMAND_UNLOCK,
COMMAND_WRITEALL,
COMMAND_ERASEALL,
COMMAND_COPY_EEPROM_TO_RAM,
COMMAND_COPY_RAM_TO_EEPROM
} EepromCommand;
typedef enum EepromState {
STATE_IN_RESET,
STATE_WAIT_FOR_START_BIT,
STATE_WAIT_FOR_COMMAND,
STATE_READING_DATA,
STATE_WAIT_FOR_DATA,
STATE_WAIT_FOR_COMPLETION
} EepromState;
typedef enum EepromEvent {
EVENT_CS_RISING_EDGE = 1 << 0,
EVENT_CS_FALLING_EDGE = 1 << 1,
EVENT_CLK_RISING_EDGE = 1 << 2,
EVENT_CLK_FALLING_EDGE = 1 << 3
} EepromEvent;
struct nmc93cxx_eeprom_t {
/* Command completion timer */
pc_timer_t cmd_complete_timer;
/* Write tick */
uint32_t write_tick;
/* State of the CS line */
bool cs_state;
/* State of the CLK line */
bool clk_state;
/* State of the DI line */
bool di_state;
/* READY/BUSY status during a programming operation */
bool is_busy;
/* Internal device state */
EepromState state;
/* Accumulator of command+address bits */
uint32_t command_address_accum;
/* Current address extracted from command */
uint32_t address;
/* Holds data coming in/going out */
uint32_t shift_register;
/* Number of bits accumulated */
uint32_t bits_accum;
/* Current command */
EepromCommand command;
/* Number of memory cells */
uint16_t cells;
/* Number of bits per cell */
uint16_t data_bits;
/* Number of address bits in a command */
uint8_t command_address_bits;
/* Number of address bits in an address */
uint8_t address_bits;
/* Are we locked against writes? */
bool is_locked;
/* Tick of the last CS rising edge */
uint32_t last_cs_rising_edge_tick;
/* Device logging */
void *log;
/* Name of EEPROM image file */
char filename[1024];
/* EEPROM image words, must be the last structure member */
uint16_t array_data[];
};
#ifdef ENABLE_NMC93CXX_EEPROM_LOG
int nmc93cxx_eeprom_do_log = ENABLE_NMC93CXX_EEPROM_LOG;
static void
nmc93cxx_eeprom_log(int lvl, const char *fmt, ...)
nmc93cxx_eeprom_log(nmc93cxx_eeprom_t *dev, int lvl, const char *fmt, ...)
{
va_list ap;
if (nmc93cxx_eeprom_do_log >= lvl) {
va_start(ap, fmt);
pclog_ex(fmt, ap);
log_out(dev->log, fmt, ap);
va_end(ap);
}
}
#else
# define nmc93cxx_eeprom_log(lvl, fmt, ...)
# define nmc93cxx_eeprom_log(dev, lvl, fmt, ...)
#endif
#define MAKE_CASE(x) case x: return #x;
static char*
nmc93cxx_eeprom_state_to_name(EepromState state)
{
switch (state) {
MAKE_CASE(STATE_IN_RESET);
MAKE_CASE(STATE_WAIT_FOR_START_BIT);
MAKE_CASE(STATE_WAIT_FOR_COMMAND);
MAKE_CASE(STATE_READING_DATA);
MAKE_CASE(STATE_WAIT_FOR_DATA);
MAKE_CASE(STATE_WAIT_FOR_COMPLETION);
default: return "<UNK>";
}
}
static char*
nmc93cxx_eeprom_cmd_to_name(EepromCommand command)
{
switch (command) {
MAKE_CASE(COMMAND_INVALID);
MAKE_CASE(COMMAND_READ);
MAKE_CASE(COMMAND_WRITE);
MAKE_CASE(COMMAND_ERASE);
MAKE_CASE(COMMAND_LOCK);
MAKE_CASE(COMMAND_UNLOCK);
MAKE_CASE(COMMAND_WRITEALL);
MAKE_CASE(COMMAND_ERASEALL);
MAKE_CASE(COMMAND_COPY_EEPROM_TO_RAM);
MAKE_CASE(COMMAND_COPY_RAM_TO_EEPROM);
default: return "<UNK>";
}
}
#undef MAKE_CASE
static void
nmc93cxx_eeprom_set_state(nmc93cxx_eeprom_t *dev, EepromState state)
{
if (dev->state != state) {
nmc93cxx_eeprom_log(dev, 2, "EEPROM: New state %s\n", nmc93cxx_eeprom_state_to_name(state));
}
dev->state = state;
}
static void
nmc93cxx_eeprom_cmd_complete_timer_callback(void *priv)
{
nmc93cxx_eeprom_t *dev = priv;
dev->is_busy = false;
}
static void
nmc93cxx_eeprom_start_cmd_timer(nmc93cxx_eeprom_t *dev, double period)
{
dev->is_busy = true;
timer_on_auto(&dev->cmd_complete_timer, period);
}
static void *
nmc93cxx_eeprom_init(const device_t *info)
{
uint16_t nwords = 64;
uint8_t addrbits = 6;
uint8_t filldefault = 1;
nmc93cxx_eeprom_params_t *params_details = (nmc93cxx_eeprom_params_t *) info->local;
nmc93cxx_eeprom_t *eeprom = NULL;
if (info->local == 0)
return NULL;
nmc93cxx_eeprom_t *dev;
bool fill_default = true;
uint16_t cells, nwords, data_bits;
uint8_t addrbits;
nwords = params_details->nwords;
/* Check for mandatory parameters */
assert(params_details != NULL);
assert(params_details->filename != NULL);
switch (nwords) {
case 16:
case 64:
switch (params_details->type) {
/* 16-bit EEPROMs */
case NMC_93C06_x16_16:
cells = 16;
addrbits = 6;
data_bits = 16;
break;
case 128:
case 256:
case NMC_93C46_x16_64:
cells = 64;
addrbits = 6;
data_bits = 16;
break;
case NMC_93C56_x16_128:
cells = 128;
addrbits = 8;
data_bits = 16;
break;
case NMC_93C57_x16_128:
cells = 128;
addrbits = 7;
data_bits = 16;
break;
case NMC_93C66_x16_256:
cells = 256;
addrbits = 8;
data_bits = 16;
break;
case NMC_93C76_x16_512:
cells = 512;
addrbits = 10;
data_bits = 16;
break;
case NMC_93C86_x16_1024:
cells = 1024;
addrbits = 10;
data_bits = 16;
break;
/* 8-bit EEPROMs */
case NMC_93C46_x8_128:
cells = 128;
addrbits = 7;
data_bits = 8;
break;
case NMC_93C56_x8_256:
cells = 256;
addrbits = 9;
data_bits = 8;
break;
case NMC_93C57_x8_256:
cells = 256;
addrbits = 8;
data_bits = 8;
break;
case NMC_93C66_x8_512:
cells = 512;
addrbits = 9;
data_bits = 8;
break;
case NMC_93C76_x8_1024:
cells = 1024;
addrbits = 11;
data_bits = 8;
break;
case NMC_93C86_x8_2048:
cells = 1024;
addrbits = 11;
data_bits = 8;
break;
default:
nwords = 64;
addrbits = 6;
/* Invalid parameter passed to the device */
assert(false);
break;
}
eeprom = calloc(1, sizeof(nmc93cxx_eeprom_t) + ((nwords + 1) * 2));
if (!eeprom)
/* The "ORG" pin can select the x8 or x16 memory organization */
if (data_bits == 16) {
nwords = cells;
} else {
nwords = cells / 2;
assert(data_bits == 8);
}
dev = calloc(1, offsetof(nmc93cxx_eeprom_t, array_data[nwords]));
if (!dev)
return NULL;
eeprom->size = nwords;
eeprom->addrbits = addrbits;
/* Output DO is tristate, read results in 1. */
eeprom->dev.out = 1;
dev->cells = cells;
dev->command_address_bits = addrbits;
dev->data_bits = data_bits;
dev->is_locked = true;
dev->command = COMMAND_INVALID;
dev->log = log_open("nmc93cxx");
nmc93cxx_eeprom_set_state(dev, STATE_IN_RESET);
if (params_details->filename) {
FILE *fp = nvr_fopen(params_details->filename, "rb");
strncpy(eeprom->filename, params_details->filename, sizeof(eeprom->filename) - 1);
if (fp) {
filldefault = !fread(eeprom->dev.data, sizeof(uint16_t), nwords, fp);
fclose(fp);
}
/* Load the EEPROM image */
snprintf(dev->filename, sizeof(dev->filename), "%s", params_details->filename);
FILE *fp = nvr_fopen(dev->filename, "rb");
if (fp) {
fill_default = !fread(dev->array_data, dev->data_bits / 8, dev->cells, fp);
fclose(fp);
}
if (fill_default && params_details->default_content) {
memcpy(dev->array_data, params_details->default_content, dev->cells * (dev->data_bits / 8));
}
if (filldefault) {
memcpy(eeprom->dev.data, params_details->default_content, nwords * sizeof(uint16_t));
/* Compute address bits */
uint32_t count = dev->cells - 1;
dev->address_bits = 0;
while (count != 0) {
count >>= 1;
dev->address_bits++;
}
return eeprom;
timer_add(&dev->cmd_complete_timer, nmc93cxx_eeprom_cmd_complete_timer_callback, dev, 0);
return dev;
}
static uint32_t
nmc93cxx_eeprom_cell_read(nmc93cxx_eeprom_t *dev, uint32_t address)
{
if (dev->data_bits == 16) {
return dev->array_data[address];
} else {
return *((uint8_t *)dev->array_data + address);
}
}
static void
nmc93cxx_eeprom_save(nmc93cxx_eeprom_t *eeprom)
nmc93cxx_eeprom_cell_write(nmc93cxx_eeprom_t *dev, uint32_t address, uint32_t data)
{
FILE *fp = nvr_fopen(eeprom->filename, "wb");
if (!fp)
return;
fwrite(eeprom->dev.data, 2, eeprom->size, fp);
fclose(fp);
if (dev->data_bits == 16) {
dev->array_data[address] = data;
} else {
*((uint8_t *)dev->array_data + address) = data;
}
}
static void
nmc93cxx_eeprom_handle_cmd_write(nmc93cxx_eeprom_t *dev, uint32_t address, uint32_t data)
{
nmc93cxx_eeprom_log(dev, 1, "EEPROM: WR %08lX <-- %X\n", address, data);
nmc93cxx_eeprom_cell_write(dev, address, data);
nmc93cxx_eeprom_start_cmd_timer(dev, WRITE_TIME_US);
}
static void
nmc93cxx_eeprom_handle_cmd_write_all(nmc93cxx_eeprom_t *dev, uint32_t data)
{
nmc93cxx_eeprom_log(dev, 1, "EEPROM: Write all operation %X\n", data);
for (uint32_t address = 0; address < (1 << dev->address_bits); ++address) {
nmc93cxx_eeprom_cell_write(dev, address, nmc93cxx_eeprom_cell_read(dev, address) & data);
}
nmc93cxx_eeprom_start_cmd_timer(dev, WRITE_ALL_TIME_US);
}
static void
nmc93cxx_eeprom_handle_cmd_erase(nmc93cxx_eeprom_t *dev, uint32_t address)
{
nmc93cxx_eeprom_log(dev, 1, "EEPROM: Erase data at %08lx\n", address);
nmc93cxx_eeprom_cell_write(dev, address, ~0);
nmc93cxx_eeprom_start_cmd_timer(dev, ERASE_TIME_US);
}
static void
nmc93cxx_eeprom_handle_cmd_erase_all(nmc93cxx_eeprom_t *dev)
{
nmc93cxx_eeprom_log(dev, 1, "EEPROM: Erase all operation\n");
for (uint32_t address = 0; address < (1 << dev->address_bits); ++address) {
nmc93cxx_eeprom_cell_write(dev, address, ~0);
}
nmc93cxx_eeprom_start_cmd_timer(dev, ERASE_ALL_TIME_US);
}
static void
nmc93cxx_eeprom_parse_command_and_address(nmc93cxx_eeprom_t *dev)
{
dev->address = dev->command_address_accum & ((1 << dev->command_address_bits) - 1);
/* Extract the command portion and handle it */
switch (dev->command_address_accum >> dev->command_address_bits) {
/* Opcode 0 needs two more bits to decode the operation */
case 0:
switch (dev->address >> (dev->command_address_bits - 2)) {
case 0: dev->command = COMMAND_LOCK; break;
case 1: dev->command = COMMAND_WRITEALL; break;
case 2: dev->command = COMMAND_ERASEALL; break;
case 3: dev->command = COMMAND_UNLOCK; break;
}
dev->address = 0;
break;
case 1: dev->command = COMMAND_WRITE; break;
case 2: dev->command = COMMAND_READ; break;
case 3: dev->command = COMMAND_ERASE; break;
default:
dev->command = COMMAND_INVALID;
break;
}
if (dev->address >= (1 << dev->address_bits)) {
nmc93cxx_eeprom_log(dev, 1, "EEPROM: out-of-range address 0x%X provided (maximum should be 0x%X)\n", dev->address, (1 << dev->address_bits) - 1);
}
}
static void
nmc93cxx_eeprom_execute_command(nmc93cxx_eeprom_t *dev)
{
/* Parse into a generic command and reset the accumulator count */
nmc93cxx_eeprom_parse_command_and_address(dev);
dev->bits_accum = 0;
nmc93cxx_eeprom_log(dev, 1, "EEPROM: Execute command %s\n", nmc93cxx_eeprom_cmd_to_name(dev->command));
switch (dev->command) {
/*
* Advance to the READING_DATA state; data is fetched after first CLK
* reset the shift register to 0 to simulate the dummy 0 bit that happens prior
* to the first clock
*/
case COMMAND_READ:
dev->shift_register = 0;
nmc93cxx_eeprom_set_state(dev, STATE_READING_DATA);
break;
/* Reset the shift register and wait for enough data to be clocked through */
case COMMAND_WRITE:
case COMMAND_WRITEALL:
dev->shift_register = 0;
nmc93cxx_eeprom_set_state(dev, STATE_WAIT_FOR_DATA);
break;
/* Erase the parsed address (unless locked) and wait for it to complete */
case COMMAND_ERASE:
if (dev->is_locked) {
nmc93cxx_eeprom_log(dev, 1, "EEPROM: Attempt to erase while locked\n");
nmc93cxx_eeprom_set_state(dev, STATE_IN_RESET);
break;
}
nmc93cxx_eeprom_handle_cmd_erase(dev, dev->address);
nmc93cxx_eeprom_set_state(dev, STATE_WAIT_FOR_COMPLETION);
break;
/* Lock the chip; return to IN_RESET state */
case COMMAND_LOCK:
dev->is_locked = true;
nmc93cxx_eeprom_set_state(dev, STATE_IN_RESET);
break;
/* Unlock the chip; return to IN_RESET state */
case COMMAND_UNLOCK:
dev->is_locked = false;
nmc93cxx_eeprom_set_state(dev, STATE_IN_RESET);
break;
/* Erase the entire chip (unless locked) and wait for it to complete */
case COMMAND_ERASEALL:
if (dev->is_locked) {
nmc93cxx_eeprom_log(dev, 1, "EEPROM: Attempt to erase all while locked\n");
nmc93cxx_eeprom_set_state(dev, STATE_IN_RESET);
break;
}
nmc93cxx_eeprom_handle_cmd_erase_all(dev);
nmc93cxx_eeprom_set_state(dev, STATE_WAIT_FOR_COMPLETION);
break;
default:
nmc93cxx_eeprom_log(dev, 1, "execute_command called with invalid command %d\n", dev->command);
assert(false);
break;
}
}
static void
nmc93cxx_eeprom_execute_write_command(nmc93cxx_eeprom_t *dev)
{
nmc93cxx_eeprom_log(dev, 1, "EEPROM: Execute write command %s\n", nmc93cxx_eeprom_cmd_to_name(dev->command));
switch (dev->command) {
/* Reset the shift register and wait for enough data to be clocked through */
case COMMAND_WRITE:
if (dev->is_locked) {
nmc93cxx_eeprom_log(dev, 1, "EEPROM: Attempt to write to address 0x%X while locked\n", dev->address);
nmc93cxx_eeprom_set_state(dev, STATE_IN_RESET);
break;
}
nmc93cxx_eeprom_handle_cmd_write(dev, dev->address, dev->shift_register);
nmc93cxx_eeprom_set_state(dev, STATE_WAIT_FOR_COMPLETION);
break;
/*
* Write the entire EEPROM with the same data; ERASEALL is required before so we
* AND against the already-present data
*/
case COMMAND_WRITEALL:
if (dev->is_locked) {
nmc93cxx_eeprom_log(dev, 1, "EEPROM: Attempt to write all while locked\n");
nmc93cxx_eeprom_set_state(dev, STATE_IN_RESET);
break;
}
nmc93cxx_eeprom_handle_cmd_write_all(dev, dev->shift_register);
nmc93cxx_eeprom_set_state(dev, STATE_WAIT_FOR_COMPLETION);
break;
default:
nmc93cxx_eeprom_log(dev, 1, "execute_command called with invalid command %d\n", dev->command);
assert(false);
break;
}
}
static void
nmc93cxx_eeprom_handle_event(nmc93cxx_eeprom_t *dev, EepromEvent event)
{
switch (dev->state) {
/* CS is not asserted; wait for a rising CS to move us forward, ignoring all clocks */
case STATE_IN_RESET:
if (event == EVENT_CS_RISING_EDGE)
nmc93cxx_eeprom_set_state(dev, STATE_WAIT_FOR_START_BIT);
break;
/*
* CS is asserted; wait for rising clock with a 1 start bit; falling CS will reset us
* note that because each bit is written independently, it is possible for us to receive
* a false rising CLK edge at the exact same time as a rising CS edge; it appears we
* should ignore these edges (makes sense really)
*/
case STATE_WAIT_FOR_START_BIT:
if ((event == EVENT_CLK_RISING_EDGE) && dev->di_state && !dev->is_busy && (dev->write_tick != dev->last_cs_rising_edge_tick)) {
dev->command_address_accum = 0;
dev->bits_accum = 0;
nmc93cxx_eeprom_set_state(dev, STATE_WAIT_FOR_COMMAND);
} else if (event == EVENT_CS_FALLING_EDGE)
nmc93cxx_eeprom_set_state(dev, STATE_IN_RESET);
break;
/* CS is asserted; wait for a command to come through; falling CS will reset us */
case STATE_WAIT_FOR_COMMAND:
if (event == EVENT_CLK_RISING_EDGE) {
/* If we have enough bits for a command + address, check it out */
dev->command_address_accum = (dev->command_address_accum << 1) | dev->di_state;
if (++dev->bits_accum == 2 + dev->command_address_bits)
nmc93cxx_eeprom_execute_command(dev);
} else if (event == EVENT_CS_FALLING_EDGE)
nmc93cxx_eeprom_set_state(dev, STATE_IN_RESET);
break;
/* CS is asserted; reading data, clock the shift register; falling CS will reset us */
case STATE_READING_DATA:
if (event == EVENT_CLK_RISING_EDGE) {
uint32_t bit_index = dev->bits_accum++;
/* Wrapping the address on multi-read */
if (((bit_index % dev->data_bits) == 0) && (bit_index == 0))
{
uint32_t addr = (dev->address + dev->bits_accum / dev->data_bits) & ((1 << dev->address_bits) - 1);
uint32_t data = nmc93cxx_eeprom_cell_read(dev, addr);
nmc93cxx_eeprom_log(dev, 1, "EEPROM: RD %08lX --> %X\n", addr, data);
dev->shift_register = data << (32 - dev->data_bits);
} else {
dev->shift_register = (dev->shift_register << 1) | 1;
}
} else if (event == EVENT_CS_FALLING_EDGE) {
nmc93cxx_eeprom_set_state(dev, STATE_IN_RESET);
if (dev->bits_accum > (dev->data_bits + 1))
nmc93cxx_eeprom_log(dev, 1, "EEPROM: Overclocked read by %d bits\n", dev->bits_accum - dev->data_bits);
else if (dev->bits_accum < dev->data_bits)
nmc93cxx_eeprom_log(dev, 1, "EEPROM: CS deasserted in READING_DATA after %d bits\n", dev->bits_accum);
}
break;
/* CS is asserted; waiting for data; clock data through until we accumulate enough; falling CS will reset us */
case STATE_WAIT_FOR_DATA:
if (event == EVENT_CLK_RISING_EDGE) {
dev->shift_register = (dev->shift_register << 1) | dev->di_state;
if (++dev->bits_accum == dev->data_bits)
nmc93cxx_eeprom_execute_write_command(dev);
} else if (event == EVENT_CS_FALLING_EDGE) {
nmc93cxx_eeprom_set_state(dev, STATE_IN_RESET);
nmc93cxx_eeprom_log(dev, 1, "EEPROM: CS deasserted in STATE_WAIT_FOR_DATA after %d bits\n", dev->bits_accum);
}
break;
/* CS is asserted; waiting for completion; watch for CS falling */
case STATE_WAIT_FOR_COMPLETION:
if (event == EVENT_CS_FALLING_EDGE)
nmc93cxx_eeprom_set_state(dev, STATE_IN_RESET);
break;
default:
break;
}
}
void
nmc93cxx_eeprom_write(nmc93cxx_eeprom_t *eeprom, int eecs, int eesk, int eedi)
nmc93cxx_eeprom_write(nmc93cxx_eeprom_t *dev, bool eecs, bool eesk, bool eedi)
{
uint8_t tick = eeprom->dev.count;
uint8_t eedo = eeprom->dev.out;
uint16_t address = eeprom->dev.address;
uint8_t command = eeprom->dev.opcode;
assert(dev != NULL);
nmc93cxx_eeprom_log(1, "CS=%u SK=%u DI=%u DO=%u, tick = %u\n",
eecs, eesk, eedi, eedo, tick);
dev->write_tick++;
if (!eeprom->dev.oldena && eecs) {
/* Start chip select cycle. */
nmc93cxx_eeprom_log(1, "Cycle start, waiting for 1st start bit (0)\n");
tick = 0;
command = 0x0;
address = 0x0;
} else if (eeprom->dev.oldena && !eecs) {
/* End chip select cycle. This triggers write / erase. */
if (!eeprom->dev.wp) {
uint8_t subcommand = address >> (eeprom->addrbits - 2);
if (command == 0 && subcommand == 2) {
/* Erase all. */
for (address = 0; address < eeprom->size; address++)
eeprom->dev.data[address] = 0xffff;
nmc93cxx_eeprom_log(dev, 3, "EEPROM: CS=%u SK=%u DI=%u DO=%u, tick = %u\n",
eecs, eesk, eedi, nmc93cxx_eeprom_read(dev), dev->write_tick);
nmc93cxx_eeprom_save(eeprom);
} else if (command == 3) {
/* Erase word. */
eeprom->dev.data[address] = 0xffff;
nmc93cxx_eeprom_save(eeprom);
} else if (tick >= (2 + 2 + eeprom->addrbits + 16)) {
if (command == 1) {
/* Write word. */
eeprom->dev.data[address] &= eeprom->dev.dat;
nmc93cxx_eeprom_save(eeprom);
} else if (command == 0 && subcommand == 1) {
/* Write all. */
for (address = 0; address < eeprom->size; address++)
eeprom->dev.data[address] &= eeprom->dev.dat;
if (dev->cs_state != eecs) {
dev->cs_state = eecs;
nmc93cxx_eeprom_save(eeprom);
}
}
/* Remember the rising edge tick so we don't process CLK signals at the same time */
if (eecs) {
dev->last_cs_rising_edge_tick = dev->write_tick;
}
/* Output DO is tristate, read results in 1. */
eedo = 1;
} else if (eecs && !eeprom->dev.oldclk && eesk) {
/* Raising edge of clock shifts data in. */
if (tick == 0) {
/* Wait for 1st start bit. */
if (eedi == 0) {
nmc93cxx_eeprom_log(1, "Got correct 1st start bit, waiting for 2nd start bit (1)\n");
tick++;
} else {
nmc93cxx_eeprom_log(1, "wrong 1st start bit (is 1, should be 0)\n");
tick = 2;
#if 0
~ assert(!"wrong start bit");
#endif
}
} else if (tick == 1) {
/* Wait for 2nd start bit. */
if (eedi != 0) {
nmc93cxx_eeprom_log(1, "Got correct 2nd start bit, getting command + address\n");
tick++;
} else {
nmc93cxx_eeprom_log(1, "1st start bit is longer than needed\n");
}
} else if (tick < 2 + 2) {
/* Got 2 start bits, transfer 2 opcode bits. */
tick++;
command <<= 1;
if (eedi) {
command += 1;
}
} else if (tick < 2 + 2 + eeprom->addrbits) {
/* Got 2 start bits and 2 opcode bits, transfer all address bits. */
tick++;
address = ((address << 1) | eedi);
if (tick == 2 + 2 + eeprom->addrbits) {
nmc93cxx_eeprom_log(1, "Address = 0x%02x (value 0x%04x)\n",
address, eeprom->dev.data[address]);
if (command == 2) {
eedo = 0;
}
address = address % eeprom->size;
if (command == 0) {
/* Command code in upper 2 bits of address. */
switch (address >> (eeprom->addrbits - 2)) {
case 0:
nmc93cxx_eeprom_log(1, "write disable command\n");
eeprom->dev.wp = 1;
break;
case 1:
nmc93cxx_eeprom_log(1, "write all command\n");
break;
case 2:
nmc93cxx_eeprom_log(1, "erase all command\n");
break;
case 3:
nmc93cxx_eeprom_log(1, "write enable command\n");
eeprom->dev.wp = 0;
break;
default:
break;
}
} else {
/* Read, write or erase word. */
eeprom->dev.dat = eeprom->dev.data[address];
}
}
} else if (tick < 2 + 2 + eeprom->addrbits + 16) {
/* Transfer 16 data bits. */
tick++;
if (command == 2) {
/* Read word. */
eedo = ((eeprom->dev.dat & 0x8000) != 0);
}
eeprom->dev.dat <<= 1;
eeprom->dev.dat += eedi;
} else {
nmc93cxx_eeprom_log(1, "additional unneeded tick, not processed\n");
}
nmc93cxx_eeprom_handle_event(dev, eecs ? EVENT_CS_RISING_EDGE : EVENT_CS_FALLING_EDGE);
}
dev->di_state = eedi;
if (dev->clk_state != eesk) {
dev->clk_state = eesk;
nmc93cxx_eeprom_handle_event(dev, eesk ? EVENT_CLK_RISING_EDGE : EVENT_CLK_FALLING_EDGE);
}
/* Save status of EEPROM. */
eeprom->dev.count = tick;
eeprom->dev.oldena = eecs;
eeprom->dev.oldclk = eesk;
eeprom->dev.out = eedo;
eeprom->dev.address = address;
eeprom->dev.opcode = command;
}
uint16_t
nmc93cxx_eeprom_read(nmc93cxx_eeprom_t *eeprom)
bool
nmc93cxx_eeprom_read(nmc93cxx_eeprom_t *dev)
{
/* Return status of pin DO (0 or 1). */
return eeprom->dev.out;
assert(dev != NULL);
if (dev->state == STATE_WAIT_FOR_START_BIT) {
/* Read the state of the READY/BUSY line */
return !dev->is_busy;
} else if (dev->state == STATE_READING_DATA) {
/* Read the current output bit */
return ((dev->shift_register & 0x80000000) != 0);
}
/* The DO pin is tristated */
return true;
}
static void
nmc93cxx_eeprom_close(void *priv)
{
nmc93cxx_eeprom_t *eeprom = (nmc93cxx_eeprom_t *) priv;
FILE *fp = nvr_fopen(eeprom->filename, "wb");
nmc93cxx_eeprom_t *dev = priv;
FILE *fp = nvr_fopen(dev->filename, "wb");
if (fp) {
fwrite(eeprom->dev.data, 2, eeprom->size, fp);
fwrite(dev->array_data, dev->data_bits / 8, dev->cells, fp);
fclose(fp);
}
free(priv);
log_close(dev->log);
free(dev);
}
uint16_t *
nmc93cxx_eeprom_data(nmc93cxx_eeprom_t *eeprom)
const uint16_t *
nmc93cxx_eeprom_data(nmc93cxx_eeprom_t *dev)
{
assert(dev != NULL);
/* Get EEPROM data array. */
return &eeprom->dev.data[0];
return &dev->array_data[0];
}
const device_t nmc93cxx_device = {

View File

@@ -182,7 +182,7 @@ nic_config_reset(void *priv)
{
nic_t *dev = (nic_t *) priv;
uint8_t *data = (uint8_t *) nmc93cxx_eeprom_data(dev->eeprom);
const uint8_t *data = (const uint8_t *) nmc93cxx_eeprom_data(dev->eeprom);
dev->config1 = (data[0x00] & 0x7f) | 0x80;
dev->config2 = (data[0x01] & 0xdf);
@@ -425,7 +425,7 @@ page3_read(nic_t *dev, uint32_t off, UNUSED(unsigned int len))
case 0xd: /* CONFIG4 */
if (dev->board == NE2K_RTL8019AS_PNP) {
uint8_t *data = (uint8_t *) nmc93cxx_eeprom_data(dev->eeprom);
const uint8_t *data = (const uint8_t *) nmc93cxx_eeprom_data(dev->eeprom);
ret = data[0x03];
}
break;
@@ -464,7 +464,7 @@ page3_write(nic_t *dev, uint32_t off, uint32_t val, UNUSED(unsigned len))
if ((val & 0xc0) == 0x80)
nmc93cxx_eeprom_write(dev->eeprom, !!(val & 0x08), !!(val & 0x04), !!(val & 0x02));
else if ((val & 0xc0) == 0x40) {
uint8_t *data = (uint8_t *) nmc93cxx_eeprom_data(dev->eeprom);
const uint8_t *data = (const uint8_t *) nmc93cxx_eeprom_data(dev->eeprom);
dev->config1 = (data[0x00] & 0x7f) | 0x80;
dev->config2 = (data[0x01] & 0xdf);
@@ -1391,18 +1391,15 @@ nic_init(const device_t *info)
nmc93cxx_eeprom_params_t params;
char filename[1024] = { 0 };
params.nwords = 64;
params.default_content = (uint16_t *) dev->eeprom_data;
params.type = NMC_93C46_x16_64;
params.default_content = dev->eeprom_data;
params.filename = filename;
int inst = device_get_instance();
snprintf(filename, sizeof(filename), "nmc93cxx_eeprom_%s_%d.nvr", info->internal_name, inst);
dev->eeprom = device_add_inst_params(&nmc93cxx_device, inst, &params);
if (dev->eeprom == NULL) {
free(dev);
return NULL;
}
if (info->local == NE2K_RTL8019AS_PNP) {
uint8_t *data = (uint8_t *) nmc93cxx_eeprom_data(dev->eeprom);
const uint8_t *data = (const uint8_t *) nmc93cxx_eeprom_data(dev->eeprom);
dev->config1 = (data[0x00] & 0x7f) | 0x80;
dev->config2 = (data[0x01] & 0xdf);

View File

@@ -3286,15 +3286,11 @@ nic_init(const device_t *info)
for (uint32_t i = 0; i < 6; i++)
s->phys[MAC0 + i] = mac_bytes[i];
params.nwords = 64;
params.default_content = (uint16_t *) s->eeprom_data;
params.type = NMC_93C46_x16_64;
params.default_content = s->eeprom_data;
params.filename = filename;
snprintf(filename, sizeof(filename), "nmc93cxx_eeprom_%s_%d.nvr", info->internal_name, s->inst);
s->eeprom = device_add_inst_params(&nmc93cxx_device, s->inst, &params);
if (s->eeprom == NULL) {
free(s);
return NULL;
}
s->nic = network_attach(s, (uint8_t *) &s->phys[MAC0], rtl8139_do_receive, rtl8139_set_link_status);
timer_add(&s->timer, rtl8139_timer, s, 0);

View File

@@ -1642,16 +1642,12 @@ nic_init(const device_t *info)
}
if (info->local != 3) {
params.nwords = 64;
params.default_content = (uint16_t *) s->eeprom_data;
params.type = NMC_93C46_x16_64;
params.default_content = s->eeprom_data;
params.filename = filename;
int inst = device_get_instance();
snprintf(filename, sizeof(filename), "nmc93cxx_eeprom_%s_%d.nvr", info->internal_name, inst);
s->eeprom = device_add_inst_params(&nmc93cxx_device, inst, &params);
if (s->eeprom == NULL) {
free(s);
return NULL;
}
}
s->tulip_pci_bar[0].addr_regs[0] = 1;

View File

@@ -22,6 +22,7 @@
#include <stdarg.h>
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
@@ -2091,19 +2092,17 @@ esp_pci_write(UNUSED(int func), int addr, UNUSED(int len), uint8_t val, void *pr
{
esp_t *dev = (esp_t *) priv;
uint8_t valxor;
int eesk;
int eedi;
esp_log("%04X:%08X: ESP PCI: Write value %02X to register %02X\n", CS, cpu_state.pc, val, addr);
if (!dev->local) {
if ((addr >= 0x80) && (addr <= 0xFF)) {
if (addr == 0x80) {
eesk = val & 0x80 ? 1 : 0;
eedi = val & 0x40 ? 1 : 0;
nmc93cxx_eeprom_write(dev->eeprom, 1, eesk, eedi);
bool eesk = !!(val & 0x80);
bool eedi = !!(val & 0x40);
nmc93cxx_eeprom_write(dev->eeprom, true, eesk, eedi);
} else if (addr == 0xc0)
nmc93cxx_eeprom_write(dev->eeprom, 0, 0, 0);
nmc93cxx_eeprom_write(dev->eeprom, false, false, false);
// esp_log("ESP PCI: Write value %02X to register %02X\n", val, addr);
return;
}
@@ -2300,8 +2299,8 @@ dc390_init(const device_t *info)
dev->eeprom_data[EE_CHKSUM1] = checksum & 0xff;
dev->eeprom_data[EE_CHKSUM2] = checksum >> 8;
params.nwords = 64;
params.default_content = (uint16_t *) dev->eeprom_data;
params.type = NMC_93C46_x16_64;
params.default_content = dev->eeprom_data;
params.filename = filename;
snprintf(filename, sizeof(filename), "nmc93cxx_eeprom_%s_%d.nvr", info->internal_name, dev->eeprom_inst);
dev->eeprom = device_add_inst_params(&nmc93cxx_device, dev->eeprom_inst, &params);

View File

@@ -12237,15 +12237,11 @@ s3_init(const device_t *info)
checksum = s3_calc_crc16(64, s3->eeprom_data);
s3->eeprom_data[0x00] = checksum;
params.nwords = 64;
params.type = NMC_93C46_x16_64;
params.default_content = s3->eeprom_data;
params.filename = filename;
snprintf(filename, sizeof(filename), "nmc93cxx_eeprom_%s_%d.nvr", info->internal_name, s3->eeprom_inst);
s3->eeprom = device_add_inst_params(&nmc93cxx_device, s3->eeprom_inst, &params);
if (s3->eeprom == NULL) {
free(s3);
return NULL;
}
}
s3->accel.multifunc[0xd] = 0xd000;