#include "sim-main.h"
#include "hw-main.h"
#include "sim-assert.h"
#include "sim-events.h"
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
enum
{
RESET_PORT
};
static const struct hw_port_descriptor m68hc11eepr_ports[] =
{
{ "reset", RESET_PORT, 0, input_port, },
{ NULL, },
};
struct m68hc11eepr
{
address_word base_address;
int attach_space;
unsigned size;
int mapped;
unsigned long eeprom_wcycle;
uint16 eeprom_waddr;
uint8 eeprom_wbyte;
uint8 eeprom_wmode;
uint8* eeprom;
unsigned long eeprom_min_cycles;
const char* file_name;
};
static hw_io_read_buffer_method m68hc11eepr_io_read_buffer;
static hw_io_write_buffer_method m68hc11eepr_io_write_buffer;
static hw_ioctl_method m68hc11eepr_ioctl;
static int
m6811eepr_memory_rw (struct m68hc11eepr *controller, int mode)
{
const char *name = controller->file_name;
int fd;
size_t size;
size = controller->size;
fd = open (name, mode, 0644);
if (fd < 0)
{
if (mode == O_RDONLY)
{
memset (controller->eeprom, 0xFF, size);
return 0;
}
return -1;
}
if (mode == O_RDONLY)
{
if (read (fd, controller->eeprom, size) != size)
{
close (fd);
return -1;
}
}
else
{
if (write (fd, controller->eeprom, size) != size)
{
close (fd);
return -1;
}
}
close (fd);
return 0;
}
static void
attach_m68hc11eepr_regs (struct hw *me,
struct m68hc11eepr *controller)
{
unsigned_word attach_address;
int attach_space;
unsigned attach_size;
reg_property_spec reg;
if (hw_find_property (me, "reg") == NULL)
hw_abort (me, "Missing \"reg\" property");
if (!hw_find_reg_array_property (me, "reg", 0, ®))
hw_abort (me, "\"reg\" property must contain one addr/size entry");
hw_unit_address_to_attach_address (hw_parent (me),
®.address,
&attach_space,
&attach_address,
me);
hw_unit_size_to_attach_size (hw_parent (me),
®.size,
&attach_size, me);
hw_attach_address (hw_parent (me), M6811_IO_LEVEL,
io_map, M6811_PPROG, 1, me);
hw_attach_address (hw_parent (me), M6811_IO_LEVEL,
io_map, M6811_CONFIG, 1, me);
if (hw_find_property (me, "file") == NULL)
controller->file_name = "m6811.eeprom";
else
controller->file_name = hw_find_string_property (me, "file");
controller->attach_space = attach_space;
controller->base_address = attach_address;
controller->eeprom = (char*) hw_malloc (me, attach_size + 1);
controller->eeprom_min_cycles = 10000;
controller->size = attach_size + 1;
controller->mapped = 0;
m6811eepr_memory_rw (controller, O_RDONLY);
}
static void
m68hc11eepr_port_event (struct hw *me,
int my_port,
struct hw *source,
int source_port,
int level)
{
SIM_DESC sd;
struct m68hc11eepr *controller;
sim_cpu *cpu;
controller = hw_data (me);
sd = hw_system (me);
cpu = STATE_CPU (sd, 0);
switch (my_port)
{
case RESET_PORT:
{
HW_TRACE ((me, "EEPROM reset"));
m6811eepr_memory_rw (controller, O_RDONLY);
cpu->ios[M6811_PPROG] = 0;
if (cpu->cpu_use_local_config)
cpu->ios[M6811_CONFIG] = cpu->cpu_config;
else
cpu->ios[M6811_CONFIG] = controller->eeprom[controller->size-1];
controller->eeprom_wmode = 0;
controller->eeprom_waddr = 0;
controller->eeprom_wbyte = 0;
if (!(cpu->ios[M6811_CONFIG] & M6811_EEON))
{
if (controller->mapped)
hw_detach_address (hw_parent (me), M6811_EEPROM_LEVEL,
controller->attach_space,
controller->base_address,
controller->size - 1,
me);
controller->mapped = 0;
}
else
{
if (!controller->mapped)
hw_attach_address (hw_parent (me), M6811_EEPROM_LEVEL,
controller->attach_space,
controller->base_address,
controller->size - 1,
me);
controller->mapped = 1;
}
break;
}
default:
hw_abort (me, "Event on unknown port %d", my_port);
break;
}
}
static void
m68hc11eepr_finish (struct hw *me)
{
struct m68hc11eepr *controller;
controller = HW_ZALLOC (me, struct m68hc11eepr);
set_hw_data (me, controller);
set_hw_io_read_buffer (me, m68hc11eepr_io_read_buffer);
set_hw_io_write_buffer (me, m68hc11eepr_io_write_buffer);
set_hw_ports (me, m68hc11eepr_ports);
set_hw_port_event (me, m68hc11eepr_port_event);
#ifdef set_hw_ioctl
set_hw_ioctl (me, m68hc11eepr_ioctl);
#else
me->to_ioctl = m68hc11eepr_ioctl;
#endif
attach_m68hc11eepr_regs (me, controller);
}
static io_reg_desc pprog_desc[] = {
{ M6811_BYTE, "BYTE ", "Byte Program Mode" },
{ M6811_ROW, "ROW ", "Row Program Mode" },
{ M6811_ERASE, "ERASE ", "Erase Mode" },
{ M6811_EELAT, "EELAT ", "EEProm Latch Control" },
{ M6811_EEPGM, "EEPGM ", "EEProm Programming Voltable Enable" },
{ 0, 0, 0 }
};
extern io_reg_desc config_desc[];
static void
m68hc11eepr_info (struct hw *me)
{
SIM_DESC sd;
uint16 base = 0;
sim_cpu *cpu;
struct m68hc11eepr *controller;
uint8 val;
sd = hw_system (me);
cpu = STATE_CPU (sd, 0);
controller = hw_data (me);
base = cpu_get_io_base (cpu);
sim_io_printf (sd, "M68HC11 EEprom:\n");
val = cpu->ios[M6811_PPROG];
print_io_byte (sd, "PPROG ", pprog_desc, val, base + M6811_PPROG);
sim_io_printf (sd, "\n");
val = cpu->ios[M6811_CONFIG];
print_io_byte (sd, "CONFIG ", config_desc, val, base + M6811_CONFIG);
sim_io_printf (sd, "\n");
val = controller->eeprom[controller->size - 1];
print_io_byte (sd, "(*NEXT*) ", config_desc, val, base + M6811_CONFIG);
sim_io_printf (sd, "\n");
if (controller->eeprom_wmode)
{
if (controller->eeprom_waddr == controller->size - 1)
sim_io_printf (sd, " Programming CONFIG register ");
else
sim_io_printf (sd, " Programming: 0x%04x ",
controller->eeprom_waddr + controller->base_address);
sim_io_printf (sd, "with 0x%02x\n",
controller->eeprom_wbyte);
}
sim_io_printf (sd, " EEProm file: %s\n",
controller->file_name);
}
static int
m68hc11eepr_ioctl (struct hw *me,
hw_ioctl_request request,
va_list ap)
{
m68hc11eepr_info (me);
return 0;
}
static unsigned
m68hc11eepr_io_read_buffer (struct hw *me,
void *dest,
int space,
unsigned_word base,
unsigned nr_bytes)
{
SIM_DESC sd;
struct m68hc11eepr *controller;
sim_cpu *cpu;
HW_TRACE ((me, "read 0x%08lx %d", (long) base, (int) nr_bytes));
sd = hw_system (me);
controller = hw_data (me);
cpu = STATE_CPU (sd, 0);
if (space == io_map)
{
unsigned cnt = 0;
while (nr_bytes != 0)
{
switch (base)
{
case M6811_PPROG:
case M6811_CONFIG:
*((uint8*) dest) = cpu->ios[base];
break;
default:
hw_abort (me, "reading wrong register 0x%04x", base);
}
dest = (uint8*) (dest) + 1;
base++;
nr_bytes--;
cnt++;
}
return cnt;
}
if ((cpu->ios[M6811_PPROG] & M6811_EELAT) != 0
&& cpu_is_running (cpu))
{
sim_memory_error (cpu, SIM_SIGBUS, base,
"EEprom not configured for reading");
}
base = base - controller->base_address;
memcpy (dest, &controller->eeprom[base], nr_bytes);
return nr_bytes;
}
static unsigned
m68hc11eepr_io_write_buffer (struct hw *me,
const void *source,
int space,
unsigned_word base,
unsigned nr_bytes)
{
SIM_DESC sd;
struct m68hc11eepr *controller;
sim_cpu *cpu;
uint8 val;
HW_TRACE ((me, "write 0x%08lx %d", (long) base, (int) nr_bytes));
sd = hw_system (me);
controller = hw_data (me);
cpu = STATE_CPU (sd, 0);
if (space != io_map && nr_bytes != 1)
{
sim_memory_error (cpu, SIM_SIGBUS, base,
"EEprom write error (only 1 byte can be programmed)");
return 0;
}
if (nr_bytes != 1)
hw_abort (me, "Cannot write more than 1 byte to EEPROM device at a time");
val = *((const uint8*) source);
if (space == io_map && base == M6811_PPROG)
{
uint8 wrong_bits;
uint16 addr;
addr = base + cpu_get_io_base (cpu);
wrong_bits = (cpu->ios[M6811_PPROG] ^ val) & val;
wrong_bits &= (M6811_EELAT | M6811_EEPGM);
if (wrong_bits == (M6811_EEPGM|M6811_EELAT))
{
sim_memory_error (cpu, SIM_SIGBUS, addr,
"Wrong eeprom programing value");
return 0;
}
if ((val & M6811_EELAT) == 0)
{
val = 0;
}
if ((val & M6811_EEPGM) && !(cpu->ios[M6811_PPROG] & M6811_EELAT))
{
sim_memory_error (cpu, SIM_SIGBUS, addr,
"EEProm high voltage applied after EELAT");
}
if ((val & M6811_EEPGM) && controller->eeprom_wmode == 0)
{
sim_memory_error (cpu, SIM_SIGSEGV, addr,
"EEProm high voltage applied without address");
}
if (val & M6811_EEPGM)
{
controller->eeprom_wcycle = cpu_current_cycle (cpu);
}
else if (cpu->ios[M6811_PPROG] & M6811_PPROG)
{
int i;
unsigned long t = cpu_current_cycle (cpu);
t -= controller->eeprom_wcycle;
if (t < controller->eeprom_min_cycles)
{
sim_memory_error (cpu, SIM_SIGILL, addr,
"EEprom programmed only for %lu cycles",
t);
}
if (!(cpu->ios[M6811_PPROG] & M6811_ERASE))
{
controller->eeprom[controller->eeprom_waddr]
&= controller->eeprom_wbyte;
}
else if ((cpu->ios[M6811_PPROG] & M6811_BYTE)
|| controller->eeprom_waddr == controller->size - 1)
{
controller->eeprom[controller->eeprom_waddr] = 0xff;
}
else if (cpu->ios[M6811_BYTE] & M6811_ROW)
{
size_t max_size;
max_size = controller->size;
controller->eeprom_waddr &= 0xFFF0;
for (i = 0; i < 16
&& controller->eeprom_waddr < max_size; i++)
{
controller->eeprom[controller->eeprom_waddr] = 0xff;
controller->eeprom_waddr ++;
}
}
else
{
size_t max_size;
max_size = controller->size;
for (i = 0; i < max_size; i++)
{
controller->eeprom[i] = 0xff;
}
}
if (m6811eepr_memory_rw (controller, O_WRONLY | O_CREAT) != 0)
{
sim_memory_error (cpu, SIM_SIGABRT, addr,
"EEPROM programing failed: errno=%d", errno);
}
controller->eeprom_wmode = 0;
}
cpu->ios[M6811_PPROG] = val;
return 1;
}
if (space == io_map && base == M6811_CONFIG)
{
base = controller->size - 1;
}
else
{
base = base - controller->base_address;
}
if (cpu_is_running (cpu))
{
if ((cpu->ios[M6811_PPROG] & M6811_EELAT) == 0)
{
sim_memory_error (cpu, SIM_SIGSEGV, base,
"EEprom not configured for writing");
return 0;
}
if (controller->eeprom_wmode != 0)
{
sim_memory_error (cpu, SIM_SIGSEGV, base,
"EEprom write error");
return 0;
}
controller->eeprom_wmode = 1;
controller->eeprom_waddr = base;
controller->eeprom_wbyte = val;
}
else
{
controller->eeprom[base] = val;
m6811eepr_memory_rw (controller, O_WRONLY);
}
return 1;
}
const struct hw_descriptor dv_m68hc11eepr_descriptor[] = {
{ "m68hc11eepr", m68hc11eepr_finish },
{ "m68hc12eepr", m68hc11eepr_finish },
{ NULL },
};