#include "sim-main.h"
#include "hw-main.h"
#include "dv-sockser.h"
#include "sim-assert.h"
enum
{
RESET_PORT
};
static const struct hw_port_descriptor m68hc11spi_ports[] =
{
{ "reset", RESET_PORT, 0, input_port, },
{ NULL, },
};
struct m68hc11spi
{
unsigned char tx_char;
int tx_bit;
unsigned char mode;
unsigned char rx_char;
unsigned char rx_clear_scsr;
unsigned char clk_pin;
unsigned int clock;
struct hw_event* spi_event;
};
static hw_io_read_buffer_method m68hc11spi_io_read_buffer;
static hw_io_write_buffer_method m68hc11spi_io_write_buffer;
static hw_port_event_method m68hc11spi_port_event;
static hw_ioctl_method m68hc11spi_ioctl;
#define M6811_SPI_FIRST_REG (M6811_SPCR)
#define M6811_SPI_LAST_REG (M6811_SPDR)
static void
attach_m68hc11spi_regs (struct hw *me,
struct m68hc11spi *controller)
{
hw_attach_address (hw_parent (me), M6811_IO_LEVEL, io_map,
M6811_SPI_FIRST_REG,
M6811_SPI_LAST_REG - M6811_SPI_FIRST_REG + 1,
me);
}
static void
m68hc11spi_finish (struct hw *me)
{
struct m68hc11spi *controller;
controller = HW_ZALLOC (me, struct m68hc11spi);
set_hw_data (me, controller);
set_hw_io_read_buffer (me, m68hc11spi_io_read_buffer);
set_hw_io_write_buffer (me, m68hc11spi_io_write_buffer);
set_hw_ports (me, m68hc11spi_ports);
set_hw_port_event (me, m68hc11spi_port_event);
#ifdef set_hw_ioctl
set_hw_ioctl (me, m68hc11spi_ioctl);
#else
me->to_ioctl = m68hc11spi_ioctl;
#endif
attach_m68hc11spi_regs (me, controller);
controller->spi_event = NULL;
controller->rx_clear_scsr = 0;
}
static void
m68hc11spi_port_event (struct hw *me,
int my_port,
struct hw *source,
int source_port,
int level)
{
SIM_DESC sd;
struct m68hc11spi *controller;
sim_cpu* cpu;
unsigned8 val;
controller = hw_data (me);
sd = hw_system (me);
cpu = STATE_CPU (sd, 0);
switch (my_port)
{
case RESET_PORT:
{
HW_TRACE ((me, "SPI reset"));
controller->rx_clear_scsr = 0;
if (controller->spi_event)
{
hw_event_queue_deschedule (me, controller->spi_event);
controller->spi_event = 0;
}
val = 0;
m68hc11spi_io_write_buffer (me, &val, io_map,
(unsigned_word) M6811_SPCR, 1);
break;
}
default:
hw_abort (me, "Event on unknown port %d", my_port);
break;
}
}
static void
set_bit_port (struct hw *me, sim_cpu *cpu, int port, int mask, int value)
{
uint8 val;
if (value)
val = cpu->ios[port] | mask;
else
val = cpu->ios[port] & ~mask;
m68hc11cpu_set_port (me, cpu, port, val);
}
#define SPI_START_BYTE 0
#define SPI_START_BIT 1
#define SPI_MIDDLE_BIT 2
void
m68hc11spi_clock (struct hw *me, void *data)
{
SIM_DESC sd;
struct m68hc11spi* controller;
sim_cpu *cpu;
int check_interrupt = 0;
controller = hw_data (me);
sd = hw_system (me);
cpu = STATE_CPU (sd, 0);
if (controller->spi_event)
{
hw_event_queue_deschedule (me, controller->spi_event);
controller->spi_event = 0;
}
if (controller->mode == SPI_START_BIT)
{
set_bit_port (me, cpu, M6811_PORTD, (1 << 2),
(controller->tx_char & (1 << controller->tx_bit)));
controller->tx_bit--;
controller->mode = SPI_MIDDLE_BIT;
}
else if (controller->mode == SPI_MIDDLE_BIT)
{
controller->mode = SPI_START_BIT;
}
if (controller->mode == SPI_START_BYTE)
{
controller->mode = SPI_START_BIT;
controller->tx_bit = 7;
set_bit_port (me, cpu, M6811_PORTD, (1 << 4), ~controller->clk_pin);
}
else
{
controller->clk_pin = ~controller->clk_pin;
set_bit_port (me, cpu, M6811_PORTD, (1 << 4), controller->clk_pin);
}
if (controller->mode == SPI_START_BIT && controller->tx_bit < 0)
{
controller->rx_clear_scsr = 0;
cpu->ios[M6811_SPSR] |= M6811_SPIF;
if (cpu->ios[M6811_SPCR] & M6811_SPIE)
check_interrupt = 1;
}
else
{
controller->spi_event = hw_event_queue_schedule (me, controller->clock,
m68hc11spi_clock,
NULL);
}
if (check_interrupt)
interrupts_update_pending (&cpu->cpu_interrupts);
}
io_reg_desc spcr_desc[] = {
{ M6811_SPIE, "SPIE ", "Serial Peripheral Interrupt Enable" },
{ M6811_SPE, "SPE ", "Serial Peripheral System Enable" },
{ M6811_DWOM, "DWOM ", "Port D Wire-OR mode option" },
{ M6811_MSTR, "MSTR ", "Master Mode Select" },
{ M6811_CPOL, "CPOL ", "Clock Polarity" },
{ M6811_CPHA, "CPHA ", "Clock Phase" },
{ M6811_SPR1, "SPR1 ", "SPI Clock Rate Select" },
{ M6811_SPR0, "SPR0 ", "SPI Clock Rate Select" },
{ 0, 0, 0 }
};
io_reg_desc spsr_desc[] = {
{ M6811_SPIF, "SPIF ", "SPI Transfer Complete flag" },
{ M6811_WCOL, "WCOL ", "Write Collision" },
{ M6811_MODF, "MODF ", "Mode Fault" },
{ 0, 0, 0 }
};
static void
m68hc11spi_info (struct hw *me)
{
SIM_DESC sd;
uint16 base = 0;
sim_cpu *cpu;
struct m68hc11spi *controller;
uint8 val;
sd = hw_system (me);
cpu = STATE_CPU (sd, 0);
controller = hw_data (me);
sim_io_printf (sd, "M68HC11 SPI:\n");
base = cpu_get_io_base (cpu);
val = cpu->ios[M6811_SPCR];
print_io_byte (sd, "SPCR", spcr_desc, val, base + M6811_SPCR);
sim_io_printf (sd, "\n");
val = cpu->ios[M6811_SPSR];
print_io_byte (sd, "SPSR", spsr_desc, val, base + M6811_SPSR);
sim_io_printf (sd, "\n");
if (controller->spi_event)
{
signed64 t;
sim_io_printf (sd, " SPI has %d bits to send\n",
controller->tx_bit + 1);
t = hw_event_remain_time (me, controller->spi_event);
sim_io_printf (sd, " SPI current bit-cycle finished in %s\n",
cycle_to_string (cpu, t));
t += (controller->tx_bit + 1) * 2 * controller->clock;
sim_io_printf (sd, " SPI operation finished in %s\n",
cycle_to_string (cpu, t));
}
}
static int
m68hc11spi_ioctl (struct hw *me,
hw_ioctl_request request,
va_list ap)
{
m68hc11spi_info (me);
return 0;
}
static unsigned
m68hc11spi_io_read_buffer (struct hw *me,
void *dest,
int space,
unsigned_word base,
unsigned nr_bytes)
{
SIM_DESC sd;
struct m68hc11spi *controller;
sim_cpu *cpu;
unsigned8 val;
HW_TRACE ((me, "read 0x%08lx %d", (long) base, (int) nr_bytes));
sd = hw_system (me);
cpu = STATE_CPU (sd, 0);
controller = hw_data (me);
switch (base)
{
case M6811_SPSR:
controller->rx_clear_scsr = cpu->ios[M6811_SCSR]
& (M6811_SPIF | M6811_WCOL | M6811_MODF);
case M6811_SPCR:
val = cpu->ios[base];
break;
case M6811_SPDR:
if (controller->rx_clear_scsr)
{
cpu->ios[M6811_SPSR] &= ~controller->rx_clear_scsr;
controller->rx_clear_scsr = 0;
interrupts_update_pending (&cpu->cpu_interrupts);
}
val = controller->rx_char;
break;
default:
return 0;
}
*((unsigned8*) dest) = val;
return 1;
}
static unsigned
m68hc11spi_io_write_buffer (struct hw *me,
const void *source,
int space,
unsigned_word base,
unsigned nr_bytes)
{
SIM_DESC sd;
struct m68hc11spi *controller;
sim_cpu *cpu;
unsigned8 val;
HW_TRACE ((me, "write 0x%08lx %d", (long) base, (int) nr_bytes));
sd = hw_system (me);
cpu = STATE_CPU (sd, 0);
controller = hw_data (me);
val = *((const unsigned8*) source);
switch (base)
{
case M6811_SPCR:
cpu->ios[M6811_SPCR] = val;
switch (val & (M6811_SPR1 | M6811_SPR0))
{
case 0:
controller->clock = 1;
break;
case 1:
controller->clock = 2;
break;
case 2:
controller->clock = 8;
break;
default:
controller->clock = 16;
break;
}
if ((val & M6811_CPOL)
&& (controller->spi_event == 0
|| ((val & M6811_CPHA) && controller->mode == 1)))
controller->clk_pin = 1;
else
controller->clk_pin = 0;
set_bit_port (me, cpu, M6811_PORTD, (1 << 4), controller->clk_pin);
break;
case M6811_SPSR:
break;
case M6811_SPDR:
if (!(cpu->ios[M6811_SPCR] & M6811_SPE))
{
return 0;
}
if (controller->rx_clear_scsr)
{
cpu->ios[M6811_SPSR] &= ~controller->rx_clear_scsr;
controller->rx_clear_scsr = 0;
interrupts_update_pending (&cpu->cpu_interrupts);
}
if (controller->spi_event)
{
cpu->ios[M6811_SPSR] |= M6811_WCOL;
break;
}
controller->tx_char = val;
controller->mode = SPI_START_BYTE;
if (!(cpu->ios[M6811_SPCR] & M6811_CPHA))
controller->clk_pin = ~controller->clk_pin;
cpu->ios[M6811_SPDR] = val;
m68hc11spi_clock (me, NULL);
break;
default:
return 0;
}
return nr_bytes;
}
const struct hw_descriptor dv_m68hc11spi_descriptor[] = {
{ "m68hc11spi", m68hc11spi_finish },
{ "m68hc12spi", m68hc11spi_finish },
{ NULL },
};