#ifndef _HW_EEPROM_C_
#define _HW_EEPROM_C_
-#ifndef STATIC_INLINE_HW_EEPROM
-#define STATIC_INLINE_HW_EEPROM STATIC_INLINE
-#endif
-
#include "device_table.h"
#ifdef HAVE_STRING_H
#endif
#endif
-/* EEPROM - electricaly erasable programable memory
- Description:
+/* DEVICE
+
+
+ eeprom - JEDEC? compatible electricaly erasable programable device
+
+
+ DESCRIPTION
+
This device implements a small byte addressable EEPROM.
Programming is performed using the same write sequences as used by
- modern EEPROM components. Writes occure in real time, the device
- returning a progress value until the programing has been completed.
+ standard modern EEPROM components. Writes occure in real time, the
+ device returning a progress value until the programing has been
+ completed.
+
+ It is based on the AMD 29F040 component.
+
+
+ PROPERTIES
+
+
+ reg = <address> <size> (required)
+
+ Determine where the device lives in the parents address space.
+
+
+ nr-sectors = <integer> (required)
+
+ When erasing an entire sector is cleared at a time. This specifies
+ the number of sectors in the EEPROM component.
+
+
+ sector-size = <integer> (required)
+
+ The number of bytes in a sector. When erasing, memory chunks of
+ this size are cleared.
+
+ NOTE: The product nr-sectors * sector-size does not need to map the
+ size specified in the reg property. If the specified size is
+ smaller part of the eeprom will not be accessible while if it is
+ larger the addresses will wrap.
+
+
+ byte-write-delay = <integer> (required)
+
+ Number of clock ticks before the programming of a single byte
+ completes.
+
+
+ sector-start-delay = <integer> (required)
+
+ When erasing sectors, the number of clock ticks after the sector
+ has been specified that the actual erase process commences.
+
+
+ erase-delay = <intger> (required)
+
+ Number of clock ticks before an erase program completes
+
+
+ manufacture-code = <integer> (required)
+
+ The one byte value returned when the auto-select manufacturer code
+ is read.
+
+
+ device-code = <integer> (required)
+
+ The one byte value returned when the auto-select device code is
+ read.
+
+
+ input-file = <file-name> (optional)
+
+ Initialize the eeprom using the specified binary file.
+
+
+ output-file = <file-name> (optional)
- Properties:
+ When ever the eeprom is updated, save the modified image into the
+ specified file.
- reg = <address> <size>. Determine where the device lives in the
- parents address space.
- nr-sectors = <integer>. When erasing an entire sector is cleared
- at a time. This specifies the number of sectors in the EEPROM
- component.
+ EXAMPLES
- byte-write-delay = <integer>. Number of clock ticks before the
- programming of a single byte completes.
- sector-start-delay = <integer>. When erasing sectors, the number
- of clock ticks after the sector has been specified and the actual
- erase process commences.
+ Enable tracing of the eeprom:
- erase-delay = <intger>. Number of clock ticks before an erase
- program completes. */
+ | bash$ psim -t eeprom-device \
+
+
+ Configure something very like the Amd Am29F040 - 512byte EEPROM
+ (but a bit faster):
+
+ | -o '/eeprom@0xfff00000/reg 0xfff00000 0x80000' \
+ | -o '/eeprom@0xfff00000/nr-sectors 8' \
+ | -o '/eeprom@0xfff00000/sector-size 0x10000' \
+ | -o '/eeprom@0xfff00000/byte-write-delay 1000' \
+ | -o '/eeprom@0xfff00000/sector-start-delay 100' \
+ | -o '/eeprom@0xfff00000/erase-delay 1000' \
+ | -o '/eeprom@0xfff00000/manufacture-code 0x01' \
+ | -o '/eeprom@0xfff00000/device-code 0xa4' \
+
+
+ Initialize the eeprom from the file <</dev/zero>>:
+
+ | -o '/eeprom@0xfff00000/input-file /dev/zero'
+
+
+ BUGS
+
+
+ */
typedef enum {
read_reset,
write_nr_6,
byte_program,
byte_programming,
- chip_erase, chip_erasing,
- sector_erase, sector_erasing,
+ chip_erase,
+ sector_erase,
sector_erase_suspend,
- sector_erase_resume,
-} eeprom_states;
+ autoselect,
+} hw_eeprom_states;
-typedef struct _eeprom_device {
+static const char *
+state2a(hw_eeprom_states state)
+{
+ switch (state) {
+ case read_reset: return "read_reset";
+ case write_nr_2: return "write_nr_2";
+ case write_nr_3: return "write_nr_3";
+ case write_nr_4: return "write_nr_4";
+ case write_nr_5: return "write_nr_5";
+ case write_nr_6: return "write_nr_6";
+ case byte_program: return "byte_program";
+ case byte_programming: return "byte_programming";
+ case chip_erase: return "chip_erase";
+ case sector_erase: return "sector_erase";
+ case sector_erase_suspend: return "sector_erase_suspend";
+ case autoselect: return "autoselect";
+ }
+ return NULL;
+}
+
+typedef struct _hw_eeprom_device {
+ /* general */
+ hw_eeprom_states state;
unsigned8 *memory;
unsigned sizeof_memory;
- unsigned sector_size;
+ unsigned erase_delay;
+ signed64 program_start_time;
+ signed64 program_finish_time;
+ unsigned8 manufacture_code;
+ unsigned8 device_code;
+ unsigned8 toggle_bit;
+ /* initialization */
+ const char *input_file_name;
+ const char *output_file_name;
+ /* for sector and sector programming */
+ hw_eeprom_states sector_state;
+ unsigned8 *sectors;
unsigned nr_sectors;
- unsigned byte_write_delay;
+ unsigned sizeof_sector;
unsigned sector_start_delay;
- unsigned erase_delay;
- signed64 programme_start_time;
- unsigned program_byte_address;
- eeprom_states state;
-} eeprom_device;
-
-static void *
-eeprom_create(const char *name,
- const device_unit *unit_address,
- const char *args,
- device *parent)
-{
- eeprom_device *eeprom = ZALLOC(eeprom_device);
- return eeprom;
-}
+ unsigned sector_start_time;
+ /* byte and byte programming */
+ unsigned byte_write_delay;
+ unsigned_word byte_program_address;
+ unsigned8 byte_program_byte;
+} hw_eeprom_device;
-typedef struct _eeprom_reg_spec {
+typedef struct _hw_eeprom_reg_spec {
unsigned32 base;
unsigned32 size;
-} eeprom_reg_spec;
+} hw_eeprom_reg_spec;
static void
-eeprom_init_address(device *me,
- psim *system)
+hw_eeprom_init_data(device *me)
{
- eeprom_device *eeprom = (eeprom_device*)device_data(me);
- const device_property *reg = device_find_array_property(me, "reg");
- const eeprom_reg_spec *spec = reg->array;
- int nr_entries = reg->sizeof_array / sizeof(*spec);
+ hw_eeprom_device *eeprom = (hw_eeprom_device*)device_data(me);
+
+ /* have we any input or output files */
+ if (device_find_property(me, "input-file") != NULL)
+ eeprom->input_file_name = device_find_string_property(me, "input-file");
+ if (device_find_property(me, "output-file") != NULL)
+ eeprom->input_file_name = device_find_string_property(me, "output-file");
- if ((reg->sizeof_array % sizeof(*spec)) != 0)
- error("devices/%s reg property of incorrect size\n", device_name(me));
- if (nr_entries > 1)
- error("devices/%s reg property contains multiple specs\n",
- device_name(me));
+ /* figure out the sectors in the eeprom */
+ if (eeprom->sectors == NULL) {
+ eeprom->nr_sectors = device_find_integer_property(me, "nr-sectors");
+ eeprom->sizeof_sector = device_find_integer_property(me, "sector-size");
+ eeprom->sectors = zalloc(eeprom->nr_sectors);
+ }
+ else
+ memset(eeprom->sectors, 0, eeprom->nr_sectors);
/* initialize the eeprom */
if (eeprom->memory == NULL) {
- eeprom->sizeof_memory = BE2H_4(spec->size);
+ eeprom->sizeof_memory = eeprom->sizeof_sector * eeprom->nr_sectors;
eeprom->memory = zalloc(eeprom->sizeof_memory);
}
else
- memset(eeprom->memory, eeprom->sizeof_memory, 0);
-
- /* figure out the sectors in the eeprom */
- eeprom->nr_sectors = device_find_integer_property(me, "nr-sectors");
- eeprom->sector_size = eeprom->sizeof_memory / eeprom->nr_sectors;
- if (eeprom->sector_size * eeprom->nr_sectors != eeprom->sizeof_memory)
- error("device/%s nr-sectors does not evenly divide eeprom\n",
- device_name(me));
+ memset(eeprom->memory, 0, eeprom->sizeof_memory);
+ if (eeprom->input_file_name != NULL) {
+ int i;
+ FILE *input_file = fopen(eeprom->input_file_name, "r");
+ if (input_file == NULL) {
+ perror("eeprom");
+ device_error(me, "Failed to open input file %s\n", eeprom->input_file_name);
+ }
+ for (i = 0; i < eeprom->sizeof_memory; i++) {
+ if (fread(&eeprom->memory[i], 1, 1, input_file) != 1)
+ break;
+ }
+ fclose(input_file);
+ }
/* timing */
eeprom->byte_write_delay = device_find_integer_property(me, "byte-write-delay");
eeprom->sector_start_delay = device_find_integer_property(me, "sector-start-delay");
eeprom->erase_delay = device_find_integer_property(me, "erase-delay");
- device_attach_address(device_parent(me),
- device_name(me),
- attach_callback,
- 0 /*address space*/,
- BE2H_4(spec->base),
- eeprom->sizeof_memory,
- access_read_write_exec,
- me);
+ /* misc */
+ eeprom->manufacture_code = device_find_integer_property(me, "manufacture-code");
+ eeprom->device_code = device_find_integer_property(me, "device-code");
+}
+
+
+static void
+invalid_read(device *me,
+ hw_eeprom_states state,
+ unsigned_word address,
+ const char *reason)
+{
+ DTRACE(eeprom, ("Invalid read to 0x%lx while in state %s (%s)\n",
+ (unsigned long)address,
+ state2a(state),
+ reason));
}
+static void
+invalid_write(device *me,
+ hw_eeprom_states state,
+ unsigned_word address,
+ unsigned8 data,
+ const char *reason)
+{
+ DTRACE(eeprom, ("Invalid write of 0x%lx to 0x%lx while in state %s (%s)\n",
+ (unsigned long)data,
+ (unsigned long)address,
+ state2a(state),
+ reason));
+}
+static void
+dump_eeprom(device *me,
+ hw_eeprom_device *eeprom)
+{
+ if (eeprom->output_file_name != NULL) {
+ int i;
+ FILE *output_file = fopen(eeprom->output_file_name, "w");
+ if (output_file == NULL) {
+ perror("eeprom");
+ device_error(me, "Failed to open output file %s\n",
+ eeprom->output_file_name);
+ }
+ for (i = 0; i < eeprom->sizeof_memory; i++) {
+ if (fwrite(&eeprom->memory[i], 1, 1, output_file) != 1)
+ break;
+ }
+ fclose(output_file);
+ }
+}
+
+
+/* program a single byte of eeprom */
+
+static void
+start_programming_byte(device *me,
+ hw_eeprom_device *eeprom,
+ unsigned_word address,
+ unsigned8 new_byte)
+{
+ unsigned8 old_byte = eeprom->memory[address];
+ DTRACE(eeprom, ("start-programing-byte - address 0x%lx, new 0x%lx, old 0x%lx\n",
+ (unsigned long)address,
+ (unsigned long)new_byte,
+ (unsigned long)old_byte));
+ eeprom->byte_program_address = address;
+ /* : old new : ~old : new&~old
+ : 0 0 : 1 : 0
+ : 0 1 : 1 : 1 -- can not set a bit
+ : 1 0 : 0 : 0
+ : 1 1 : 0 : 0 */
+ if (~old_byte & new_byte)
+ invalid_write(me, eeprom->state, address, new_byte, "setting cleared bit");
+ /* : old new : old&new
+ : 0 0 : 0
+ : 0 1 : 0
+ : 1 0 : 0
+ : 1 1 : 1 */
+ eeprom->byte_program_byte = new_byte & old_byte;
+ eeprom->memory[address] = ~new_byte & ~0x24; /* LE-bits 5:3 zero */
+ eeprom->program_start_time = device_event_queue_time(me);
+ eeprom->program_finish_time = (eeprom->program_start_time
+ + eeprom->byte_write_delay);
+}
+
+static void
+finish_programming_byte(device *me,
+ hw_eeprom_device *eeprom)
+{
+ DTRACE(eeprom, ("finish-programming-byte - address 0x%lx, byte 0x%lx\n",
+ (unsigned long)eeprom->byte_program_address,
+ (unsigned long)eeprom->byte_program_byte));
+ eeprom->memory[eeprom->byte_program_address] = eeprom->byte_program_byte;
+ dump_eeprom(me, eeprom);
+}
+
+
+/* erase the eeprom completly */
+
+static void
+start_erasing_chip(device *me,
+ hw_eeprom_device *eeprom)
+{
+ DTRACE(eeprom, ("start-erasing-chip\n"));
+ memset(eeprom->memory, 0, eeprom->sizeof_memory);
+ eeprom->program_start_time = device_event_queue_time(me);
+ eeprom->program_finish_time = (eeprom->program_start_time
+ + eeprom->erase_delay);
+}
+
+static void
+finish_erasing_chip(device *me,
+ hw_eeprom_device *eeprom)
+{
+ DTRACE(eeprom, ("finish-erasing-chip\n"));
+ memset(eeprom->memory, 0xff, eeprom->sizeof_memory);
+ dump_eeprom(me, eeprom);
+}
+
+
+/* erase a single sector of the eeprom */
+
+static void
+start_erasing_sector(device *me,
+ hw_eeprom_device *eeprom,
+ unsigned_word address)
+{
+ int sector = address / eeprom->sizeof_sector;
+ DTRACE(eeprom, ("start-erasing-sector - address 0x%lx, sector %d\n",
+ (unsigned long)address, sector));
+ ASSERT(sector < eeprom->nr_sectors);
+ eeprom->sectors[sector] = 1;
+ memset(eeprom->memory + sector * eeprom->sizeof_sector,
+ 0x4, eeprom->sizeof_sector);
+ eeprom->program_start_time = device_event_queue_time(me);
+ eeprom->sector_start_time = (eeprom->program_start_time
+ + eeprom->sector_start_delay);
+ eeprom->program_finish_time = (eeprom->sector_start_time
+ + eeprom->erase_delay);
+
+}
+
+static void
+finish_erasing_sector(device *me,
+ hw_eeprom_device *eeprom)
+{
+ int sector;
+ DTRACE(eeprom, ("finish-erasing-sector\n"));
+ for (sector = 0; sector < eeprom->nr_sectors; sector++) {
+ if (eeprom->sectors[sector]) {
+ eeprom->sectors[sector] = 0;
+ memset(eeprom->memory + sector * eeprom->sizeof_sector,
+ 0xff, eeprom->sizeof_sector);
+ }
+ }
+ dump_eeprom(me, eeprom);
+}
+
+
+/* eeprom reads */
+
+static unsigned8
+toggle(hw_eeprom_device *eeprom,
+ unsigned8 byte)
+{
+ eeprom->toggle_bit = eeprom->toggle_bit ^ 0x40; /* le-bit 6 */
+ return eeprom->toggle_bit ^ byte;
+}
+
+static unsigned8
+read_byte(device *me,
+ hw_eeprom_device *eeprom,
+ unsigned_word address)
+{
+ /* may need multiple iterations of this */
+ while (1) {
+ switch (eeprom->state) {
+
+ case read_reset:
+ return eeprom->memory[address];
+
+ case autoselect:
+ if ((address & 0xff) == 0x00)
+ return eeprom->manufacture_code;
+ else if ((address & 0xff) == 0x01)
+ return eeprom->device_code;
+ else
+ return 0; /* not certain about this */
+
+ case byte_programming:
+ if (device_event_queue_time(me) > eeprom->program_finish_time) {
+ finish_programming_byte(me, eeprom);
+ eeprom->state = read_reset;
+ continue;
+ }
+ else if (address == eeprom->byte_program_address) {
+ return toggle(eeprom, eeprom->memory[address]);
+ }
+ else {
+ /* trash that memory location */
+ invalid_read(me, eeprom->state, address, "not byte program address");
+ eeprom->memory[address] = (eeprom->memory[address]
+ & eeprom->byte_program_byte);
+ return toggle(eeprom, eeprom->memory[eeprom->byte_program_address]);
+ }
+
+ case chip_erase:
+ if (device_event_queue_time(me) > eeprom->program_finish_time) {
+ finish_erasing_chip(me, eeprom);
+ eeprom->state = read_reset;
+ continue;
+ }
+ else {
+ return toggle(eeprom, eeprom->memory[address]);
+ }
+
+ case sector_erase:
+ if (device_event_queue_time(me) > eeprom->program_finish_time) {
+ finish_erasing_sector(me, eeprom);
+ eeprom->state = read_reset;
+ continue;
+ }
+ else if (!eeprom->sectors[address / eeprom->sizeof_sector]) {
+ /* read to wrong sector */
+ invalid_read(me, eeprom->state, address, "sector not being erased");
+ return toggle(eeprom, eeprom->memory[address]) & ~0x8;
+ }
+ else if (device_event_queue_time(me) > eeprom->sector_start_time) {
+ return toggle(eeprom, eeprom->memory[address]) | 0x8;
+ }
+ else {
+ return toggle(eeprom, eeprom->memory[address]) & ~0x8;
+ }
+
+ case sector_erase_suspend:
+ if (!eeprom->sectors[address / eeprom->sizeof_sector]) {
+ return eeprom->memory[address];
+ }
+ else {
+ invalid_read(me, eeprom->state, address, "sector being erased");
+ return eeprom->memory[address];
+ }
+
+ default:
+ invalid_read(me, eeprom->state, address, "invalid state");
+ return eeprom->memory[address];
+
+ }
+ }
+ return 0;
+}
+
static unsigned
-eeprom_io_read_buffer(device *me,
- void *dest,
- int space,
- unsigned_word addr,
- unsigned nr_bytes,
- cpu *processor,
- unsigned_word cia)
+hw_eeprom_io_read_buffer(device *me,
+ void *dest,
+ int space,
+ unsigned_word addr,
+ unsigned nr_bytes,
+ cpu *processor,
+ unsigned_word cia)
{
- eeprom_device *eeprom = (eeprom_device*)device_data(me);
+ hw_eeprom_device *eeprom = (hw_eeprom_device*)device_data(me);
int i;
for (i = 0; i < nr_bytes; i++) {
- unsigned_word address = (addr + nr_bytes) % eeprom->sizeof_memory;
- eeprom->memory[address] = eeprom_io_read_byte(address);
+ unsigned_word address = (addr + i) % eeprom->sizeof_memory;
+ unsigned8 byte = read_byte(me, eeprom, address);
+ ((unsigned8*)dest)[i] = byte;
}
return nr_bytes;
}
+
+/* eeprom writes */
+
static void
-eeprom_io_write_byte()
+write_byte(device *me,
+ hw_eeprom_device *eeprom,
+ unsigned_word address,
+ unsigned8 data)
{
- switch (state) {
- case read_reset:
- if (address == 0x5555 && data = 0xaa)
- state = first_write;
- else
- state = read_reset;
- break;
- case first_write:
- if (address == 0x2aaa && data == 0x55)
- state = second_write;
- else
- state = read_reset; /* issue warning */
- break;
- case second_write:
- if (address == 0x5555 && data == 0xf0)
- state = read_reset;
- else if (address == 0x5555 && data == 0x90)
- state = auto_select;
- else if (address == 0x5555 && data == 0xa0)
- state = byte_program;
- else if (address == 0x5555 && data == 0x80)
- state = third_write;
- else
- state = read_reset;
- break;
- case fourth_write:
- if (address == 0x5555 && data == 0xaa)
- state = fith_write;
- else
- state = read_reset;
- break;
- case fith_write:
- if (address == 0x2aaa && data == 0x55)
- state = sixth_write;
- else
- state = read_reset;
- break;
- case sixth_write:
- if (address == 0x5555 && data == 0x10)
- state = chip_erase;
- else
- sector_erase();
- break;
- case auto_select:
- if (data == 0xf0)
- state = read_reset;
- else if (address == 0x5555 && data == 0xaa)
- state = second_write;
- else
- state = read_reset; /* issue warning */
- break;
- case sector_erase:
- if (data == 0xb0)
- state = sector_erase_suspend;
- else
- state = sector_erase; /* ignore */
- break;
- case sector_erase_suspend:
- if (data == 0x30)
- state = sector_erase;
- else
- state = sector_erase_suspend; /* ignore */
- break;
- case byte_program:
- /* perform the byte program */
- program_address = address;
- program_start = some_time();
- toggle = 0;
- /* but only make things `0' and never 1 */
- byte[address] = data;
- state = byte_programming;
- break;
- case byte_programming:
- if (finished)
- state = read_reset;
- else
- state = byte_programming;
- break;
+ /* may need multiple transitions to process a write */
+ while (1) {
+ switch (eeprom->state) {
+
+ case read_reset:
+ if (address == 0x5555 && data == 0xaa)
+ eeprom->state = write_nr_2;
+ else if (data == 0xf0)
+ eeprom->state = read_reset;
+ else {
+ invalid_write(me, eeprom->state, address, data, "unexpected");
+ eeprom->state = read_reset;
+ }
+ return;
+
+ case write_nr_2:
+ if (address == 0x2aaa && data == 0x55)
+ eeprom->state = write_nr_3;
+ else {
+ invalid_write(me, eeprom->state, address, data, "unexpected");
+ eeprom->state = read_reset;
+ }
+ return;
+
+ case write_nr_3:
+ if (address == 0x5555 && data == 0xf0)
+ eeprom->state = read_reset;
+ else if (address == 0x5555 && data == 0x90)
+ eeprom->state = autoselect;
+ else if (address == 0x5555 && data == 0xa0) {
+ eeprom->state = byte_program;
+ }
+ else if (address == 0x5555 && data == 0x80)
+ eeprom->state = write_nr_4;
+ else {
+ invalid_write(me, eeprom->state, address, data, "unexpected");
+ eeprom->state = read_reset;
+ }
+ return;
+
+ case write_nr_4:
+ if (address == 0x5555 && data == 0xaa)
+ eeprom->state = write_nr_5;
+ else {
+ invalid_write(me, eeprom->state, address, data, "unexpected");
+ eeprom->state = read_reset;
+ }
+ return;
+
+ case write_nr_5:
+ if (address == 0x2aaa && data == 0x55)
+ eeprom->state = write_nr_6;
+ else {
+ invalid_write(me, eeprom->state, address, data, "unexpected");
+ eeprom->state = read_reset;
+ }
+ return;
+
+ case write_nr_6:
+ if (address == 0x5555 && data == 0x10) {
+ start_erasing_chip(me, eeprom);
+ eeprom->state = chip_erase;
+ }
+ else {
+ start_erasing_sector(me, eeprom, address);
+ eeprom->sector_state = read_reset;
+ eeprom->state = sector_erase;
+ }
+ return;
+
+ case autoselect:
+ if (data == 0xf0)
+ eeprom->state = read_reset;
+ else if (address == 0x5555 && data == 0xaa)
+ eeprom->state = write_nr_2;
+ else {
+ invalid_write(me, eeprom->state, address, data, "unsupported address");
+ eeprom->state = read_reset;
+ }
+ return;
+
+ case byte_program:
+ start_programming_byte(me, eeprom, address, data);
+ eeprom->state = byte_programming;
+ return;
+
+ case byte_programming:
+ if (device_event_queue_time(me) > eeprom->program_finish_time) {
+ finish_programming_byte(me, eeprom);
+ eeprom->state = read_reset;
+ continue;
+ }
+ /* ignore it */
+ return;
+
+ case chip_erase:
+ if (device_event_queue_time(me) > eeprom->program_finish_time) {
+ finish_erasing_chip(me, eeprom);
+ eeprom->state = read_reset;
+ continue;
+ }
+ /* ignore it */
+ return;
+
+ case sector_erase:
+ if (device_event_queue_time(me) > eeprom->program_finish_time) {
+ finish_erasing_sector(me, eeprom);
+ eeprom->state = eeprom->sector_state;
+ continue;
+ }
+ else if (device_event_queue_time(me) > eeprom->sector_start_time
+ && data == 0xb0) {
+ eeprom->sector_state = read_reset;
+ eeprom->state = sector_erase_suspend;
+ }
+ else {
+ if (eeprom->sector_state == read_reset
+ && address == 0x5555 && data == 0xaa)
+ eeprom->sector_state = write_nr_2;
+ else if (eeprom->sector_state == write_nr_2
+ && address == 0x2aaa && data == 0x55)
+ eeprom->sector_state = write_nr_3;
+ else if (eeprom->sector_state == write_nr_3
+ && address == 0x5555 && data == 0x80)
+ eeprom->sector_state = write_nr_4;
+ else if (eeprom->sector_state == write_nr_4
+ && address == 0x5555 && data == 0xaa)
+ eeprom->sector_state = write_nr_5;
+ else if (eeprom->sector_state == write_nr_5
+ && address == 0x2aaa && data == 0x55)
+ eeprom->sector_state = write_nr_6;
+ else if (eeprom->sector_state == write_nr_6
+ && address != 0x5555 && data == 0x30) {
+ if (device_event_queue_time(me) > eeprom->sector_start_time) {
+ DTRACE(eeprom, ("sector erase command after window closed\n"));
+ eeprom->sector_state = read_reset;
+ }
+ else {
+ start_erasing_sector(me, eeprom, address);
+ eeprom->sector_state = read_reset;
+ }
+ }
+ else {
+ invalid_write(me, eeprom->state, address, data, state2a(eeprom->sector_state));
+ eeprom->state = read_reset;
+ }
+ }
+ return;
+
+ case sector_erase_suspend:
+ if (data == 0x30)
+ eeprom->state = sector_erase;
+ else {
+ invalid_write(me, eeprom->state, address, data, "not resume command");
+ eeprom->state = read_reset;
+ }
+ return;
+
+ }
}
}
static unsigned
-eeprom_io_write_buffer(device *me,
- const void *source,
- int space,
- unsigned_word addr,
- unsigned nr_bytes,
- cpu *processor,
- unsigned_word cia)
+hw_eeprom_io_write_buffer(device *me,
+ const void *source,
+ int space,
+ unsigned_word addr,
+ unsigned nr_bytes,
+ cpu *processor,
+ unsigned_word cia)
{
- eeprom_device *eeprom = (eeprom_device*)device_data(me);
+ hw_eeprom_device *eeprom = (hw_eeprom_device*)device_data(me);
int i;
for (i = 0; i < nr_bytes; i++) {
- unsigned_word address = (addr + nr_bytes) % eeprom->sizeof_memory;
- eeprom_io_read_byte(address, eeprom->memory[address]);
+ unsigned_word address = (addr + i) % eeprom->sizeof_memory;
+ unsigned8 byte = ((unsigned8*)source)[i];
+ write_byte(me, eeprom, address, byte);
}
return nr_bytes;
}
+/* An instance of the eeprom */
+
+typedef struct _hw_eeprom_instance {
+ unsigned_word pos;
+ hw_eeprom_device *eeprom;
+ device *me;
+} hw_eeprom_instance;
+
+static void
+hw_eeprom_instance_delete(device_instance *instance)
+{
+ hw_eeprom_instance *data = device_instance_data(instance);
+ zfree(data);
+}
+
+static int
+hw_eeprom_instance_read(device_instance *instance,
+ void *buf,
+ unsigned_word len)
+{
+ hw_eeprom_instance *data = device_instance_data(instance);
+ int i;
+ if (data->eeprom->state != read_reset)
+ DITRACE(eeprom, ("eeprom not idle during instance read\n"));
+ for (i = 0; i < len; i++) {
+ ((unsigned8*)buf)[i] = data->eeprom->memory[data->pos];
+ data->pos = (data->pos + 1) % data->eeprom->sizeof_memory;
+ }
+ return len;
+}
-static device_callbacks const eeprom_callbacks = {
- { eeprom_init_address, },
+static int
+hw_eeprom_instance_write(device_instance *instance,
+ const void *buf,
+ unsigned_word len)
+{
+ hw_eeprom_instance *data = device_instance_data(instance);
+ int i;
+ if (data->eeprom->state != read_reset)
+ DITRACE(eeprom, ("eeprom not idle during instance write\n"));
+ for (i = 0; i < len; i++) {
+ data->eeprom->memory[data->pos] = ((unsigned8*)buf)[i];
+ data->pos = (data->pos + 1) % data->eeprom->sizeof_memory;
+ }
+ dump_eeprom(data->me, data->eeprom);
+ return len;
+}
+
+static int
+hw_eeprom_instance_seek(device_instance *instance,
+ unsigned_word pos_hi,
+ unsigned_word pos_lo)
+{
+ hw_eeprom_instance *data = device_instance_data(instance);
+ if (pos_lo >= data->eeprom->sizeof_memory)
+ device_error(data->me, "seek value 0x%lx out of range\n",
+ (unsigned long)pos_lo);
+ data->pos = pos_lo;
+ return 0;
+}
+
+static const device_instance_callbacks hw_eeprom_instance_callbacks = {
+ hw_eeprom_instance_delete,
+ hw_eeprom_instance_read,
+ hw_eeprom_instance_write,
+ hw_eeprom_instance_seek,
+};
+
+static device_instance *
+hw_eeprom_create_instance(device *me,
+ const char *path,
+ const char *args)
+{
+ hw_eeprom_device *eeprom = device_data(me);
+ hw_eeprom_instance *data = ZALLOC(hw_eeprom_instance);
+ data->eeprom = eeprom;
+ data->me = me;
+ return device_create_instance_from(me, NULL,
+ data,
+ path, args,
+ &hw_eeprom_instance_callbacks);
+}
+
+
+
+static device_callbacks const hw_eeprom_callbacks = {
+ { generic_device_init_address,
+ hw_eeprom_init_data },
{ NULL, }, /* address */
- { eeprom_io_read_buffer, eeprom_io_write_buffer }, /* IO */
+ { hw_eeprom_io_read_buffer,
+ hw_eeprom_io_write_buffer }, /* IO */
+ { NULL, }, /* DMA */
+ { NULL, }, /* interrupt */
+ { NULL, }, /* unit */
+ hw_eeprom_create_instance,
};
-const device_descriptor eeprom_device_descriptor[] = {
- { "eeprom", eeprom_create, &eeprom_callbacks },
+static void *
+hw_eeprom_create(const char *name,
+ const device_unit *unit_address,
+ const char *args)
+{
+ hw_eeprom_device *eeprom = ZALLOC(hw_eeprom_device);
+ return eeprom;
+}
+
+
+
+const device_descriptor hw_eeprom_device_descriptor[] = {
+ { "eeprom", hw_eeprom_create, &hw_eeprom_callbacks },
{ NULL },
};