#include "sim-main.h"
#include "hw-main.h"
#include "sim-assert.h"
enum
{
RESET_PORT,
CAPTURE
};
static const struct hw_port_descriptor m68hc11tim_ports[] =
{
{ "reset", RESET_PORT, 0, input_port, },
{ "capture", CAPTURE, 0, input_port, },
{ NULL, },
};
struct m68hc11tim
{
unsigned long cop_delay;
unsigned long rti_delay;
unsigned long ovf_delay;
signed64 clock_prescaler;
signed64 tcnt_adjust;
signed64 cop_prev_interrupt;
signed64 rti_prev_interrupt;
struct hw_event *rti_timer_event;
struct hw_event *cop_timer_event;
struct hw_event *tof_timer_event;
struct hw_event *cmp_timer_event;
};
static hw_io_read_buffer_method m68hc11tim_io_read_buffer;
static hw_io_write_buffer_method m68hc11tim_io_write_buffer;
static hw_port_event_method m68hc11tim_port_event;
static hw_ioctl_method m68hc11tim_ioctl;
#define M6811_TIMER_FIRST_REG (M6811_TCTN)
#define M6811_TIMER_LAST_REG (M6811_PACNT)
static void
attach_m68hc11tim_regs (struct hw *me,
struct m68hc11tim *controller)
{
hw_attach_address (hw_parent (me), M6811_IO_LEVEL, io_map,
M6811_TIMER_FIRST_REG,
M6811_TIMER_LAST_REG - M6811_TIMER_FIRST_REG + 1,
me);
}
static void
m68hc11tim_finish (struct hw *me)
{
struct m68hc11tim *controller;
controller = HW_ZALLOC (me, struct m68hc11tim);
set_hw_data (me, controller);
set_hw_io_read_buffer (me, m68hc11tim_io_read_buffer);
set_hw_io_write_buffer (me, m68hc11tim_io_write_buffer);
set_hw_ports (me, m68hc11tim_ports);
set_hw_port_event (me, m68hc11tim_port_event);
#ifdef set_hw_ioctl
set_hw_ioctl (me, m68hc11tim_ioctl);
#else
me->to_ioctl = m68hc11tim_ioctl;
#endif
controller->clock_prescaler = 1;
controller->tcnt_adjust = 0;
attach_m68hc11tim_regs (me, controller);
}
static void
m68hc11tim_port_event (struct hw *me,
int my_port,
struct hw *source,
int source_port,
int level)
{
SIM_DESC sd;
struct m68hc11tim *controller;
sim_cpu *cpu;
unsigned8 val;
unsigned16 tcnt;
controller = hw_data (me);
sd = hw_system (me);
cpu = STATE_CPU (sd, 0);
switch (my_port)
{
case RESET_PORT:
{
HW_TRACE ((me, "Timer reset"));
if (controller->rti_timer_event)
{
hw_event_queue_deschedule (me, controller->rti_timer_event);
controller->rti_timer_event = 0;
controller->rti_prev_interrupt = 0;
}
if (controller->cop_timer_event)
{
hw_event_queue_deschedule (me, controller->cop_timer_event);
controller->cop_timer_event = 0;
controller->cop_prev_interrupt = 0;
}
if (controller->tof_timer_event)
{
hw_event_queue_deschedule (me, controller->tof_timer_event);
controller->tof_timer_event = 0;
}
if (controller->cmp_timer_event)
{
hw_event_queue_deschedule (me, controller->cmp_timer_event);
controller->cmp_timer_event = 0;
}
val = 0;
cpu->ios[M6811_TFLG2] = 0;
m68hc11tim_io_write_buffer (me, &val, io_map,
(unsigned_word) M6811_TMSK2, 1);
m68hc11tim_io_write_buffer (me, &val, io_map,
(unsigned_word) M6811_PACTL, 1);
break;
}
case CAPTURE:
tcnt = (uint16) ((cpu->cpu_absolute_cycle - controller->tcnt_adjust)
/ controller->clock_prescaler);
switch (level)
{
case M6811_TIC1:
case M6811_TIC2:
case M6811_TIC3:
cpu->ios[level] = tcnt >> 8;
cpu->ios[level + 1] = tcnt;
break;
default:
hw_abort (me, "Invalid event parameter %d", level);
break;
}
break;
default:
hw_abort (me, "Event on unknown port %d", my_port);
break;
}
}
enum event_type
{
COP_EVENT,
RTI_EVENT,
OVERFLOW_EVENT,
COMPARE_EVENT
};
void
m68hc11tim_timer_event (struct hw *me, void *data)
{
SIM_DESC sd;
struct m68hc11tim *controller;
sim_cpu *cpu;
enum event_type type;
unsigned long delay;
struct hw_event **eventp;
int check_interrupt = 0;
unsigned mask;
unsigned flags;
unsigned long tcnt_internal;
unsigned long tcnt;
int i;
sim_events *events;
controller = hw_data (me);
sd = hw_system (me);
cpu = STATE_CPU (sd, 0);
type = (enum event_type) ((long) data) & 0x0FF;
events = STATE_EVENTS (sd);
delay = 0;
switch (type)
{
case COP_EVENT:
eventp = &controller->cop_timer_event;
delay = controller->cop_delay;
delay = controller->cop_prev_interrupt + controller->cop_delay;
controller->cop_prev_interrupt = delay;
delay = delay - cpu->cpu_absolute_cycle;
check_interrupt = 1;
delay += events->nr_ticks_to_process;
break;
case RTI_EVENT:
eventp = &controller->rti_timer_event;
delay = controller->rti_prev_interrupt + controller->rti_delay;
if (((long) (data) & 0x0100) == 0)
{
cpu->ios[M6811_TFLG2] |= M6811_RTIF;
check_interrupt = 1;
controller->rti_prev_interrupt = delay;
delay += controller->rti_delay;
}
delay = delay - cpu->cpu_absolute_cycle;
delay += events->nr_ticks_to_process;
break;
case OVERFLOW_EVENT:
tcnt_internal = sim_events_time (sd) - controller->tcnt_adjust;
tcnt_internal += events->nr_ticks_to_process;
tcnt_internal &= 0x0ffff * controller->clock_prescaler;
delay = (0x10000 * controller->clock_prescaler) - tcnt_internal;
delay += events->nr_ticks_to_process;
eventp = &controller->tof_timer_event;
if (((long) (data) & 0x100) == 0)
{
cpu->ios[M6811_TFLG2] |= M6811_TOF;
check_interrupt = 1;
}
break;
case COMPARE_EVENT:
eventp = &controller->cmp_timer_event;
events = STATE_EVENTS (sd);
tcnt_internal = sim_events_time (sd) - controller->tcnt_adjust;
tcnt_internal += events->nr_ticks_to_process;
tcnt_internal &= 0x0ffff * controller->clock_prescaler;
tcnt = tcnt_internal / controller->clock_prescaler;
flags = cpu->ios[M6811_TMSK1];
mask = 0x80;
delay = 65536 * controller->clock_prescaler;
for (i = M6811_TOC1; i <= M6811_TOC5; i += 2, mask >>= 1)
{
unsigned long compare;
compare = (cpu->ios[i] << 8) + cpu->ios[i+1];
if (compare == tcnt && (flags & mask))
{
cpu->ios[M6811_TFLG1] |= mask;
check_interrupt++;
}
compare = compare * controller->clock_prescaler;
if (compare > tcnt_internal)
compare = compare - tcnt_internal;
else
compare = compare - tcnt_internal
+ 65536 * controller->clock_prescaler;
if (compare < delay)
delay = compare;
}
if ((flags & 0xF0) == 0)
delay = 0;
break;
default:
eventp = 0;
break;
}
if (*eventp)
{
hw_event_queue_deschedule (me, *eventp);
*eventp = 0;
}
if (delay != 0)
{
*eventp = hw_event_queue_schedule (me, delay,
m68hc11tim_timer_event,
(void*) type);
}
if (check_interrupt)
interrupts_update_pending (&cpu->cpu_interrupts);
}
io_reg_desc tmsk2_desc[] = {
{ M6811_TOI, "TOI ", "Timer Overflow Interrupt Enable" },
{ M6811_RTII, "RTII ", "RTI Interrupt Enable" },
{ M6811_PAOVI, "PAOVI ", "Pulse Accumulator Overflow Interrupt Enable" },
{ M6811_PAII, "PAII ", "Pulse Accumulator Interrupt Enable" },
{ M6811_PR1, "PR1 ", "Timer prescaler (PR1)" },
{ M6811_PR0, "PR0 ", "Timer prescaler (PR0)" },
{ M6811_TPR_1, "TPR_1 ", "Timer prescaler div 1" },
{ M6811_TPR_4, "TPR_4 ", "Timer prescaler div 4" },
{ M6811_TPR_8, "TPR_8 ", "Timer prescaler div 8" },
{ M6811_TPR_16, "TPR_16", "Timer prescaler div 16" },
{ 0, 0, 0 }
};
io_reg_desc tflg2_desc[] = {
{ M6811_TOF, "TOF ", "Timer Overflow Bit" },
{ M6811_RTIF, "RTIF ", "Read Time Interrupt Flag" },
{ M6811_PAOVF, "PAOVF ", "Pulse Accumulator Overflow Interrupt Flag" },
{ M6811_PAIF, "PAIF ", "Pulse Accumulator Input Edge" },
{ 0, 0, 0 }
};
io_reg_desc pactl_desc[] = {
{ M6811_DDRA7, "DDRA7 ", "Data Direction for Port A bit-7" },
{ M6811_PAEN, "PAEN ", "Pulse Accumulator System Enable" },
{ M6811_PAMOD, "PAMOD ", "Pulse Accumulator Mode" },
{ M6811_PEDGE, "PEDGE ", "Pulse Accumulator Edge Control" },
{ M6811_RTR1, "RTR1 ", "RTI Interrupt rate select (RTR1)" },
{ M6811_RTR0, "RTR0 ", "RTI Interrupt rate select (RTR0)" },
{ 0, 0, 0 }
};
static double
to_realtime (sim_cpu *cpu, signed64 t)
{
return (double) (t) / (double) (cpu->cpu_frequency / 4);
}
const char*
cycle_to_string (sim_cpu *cpu, signed64 t)
{
double dt;
char tbuf[32];
static char buf[64];
dt = to_realtime (cpu, t);
if (dt < 0.001)
sprintf (tbuf, "(%3.1f us)", dt * 1000000.0);
else if (dt < 1.0)
sprintf (tbuf, "(%3.1f ms)", dt * 1000.0);
else
sprintf (tbuf, "(%3.1f s)", dt);
sprintf (buf, "%llu cycle%s %10.10s", t,
(t > 1 ? "s" : ""), tbuf);
return buf;
}
static void
m68hc11tim_print_timer (struct hw *me, const char *name,
struct hw_event *event)
{
SIM_DESC sd;
sd = hw_system (me);
if (event == 0)
{
sim_io_printf (sd, " No %s interrupt will be raised.\n", name);
}
else
{
signed64 t;
sim_cpu* cpu;
cpu = STATE_CPU (sd, 0);
t = hw_event_remain_time (me, event);
sim_io_printf (sd, " Next %s interrupt in %s\n",
name, cycle_to_string (cpu, t));
}
}
static void
m68hc11tim_info (struct hw *me)
{
SIM_DESC sd;
uint16 base = 0;
sim_cpu *cpu;
struct m68hc11tim *controller;
uint8 val;
sd = hw_system (me);
cpu = STATE_CPU (sd, 0);
controller = hw_data (me);
sim_io_printf (sd, "M68HC11 Timer:\n");
base = cpu_get_io_base (cpu);
val = cpu->ios[M6811_TMSK2];
print_io_byte (sd, "TMSK2 ", tmsk2_desc, val, base + M6811_TMSK2);
sim_io_printf (sd, "\n");
val = cpu->ios[M6811_TFLG2];
print_io_byte (sd, "TFLG2", tflg2_desc, val, base + M6811_TFLG2);
sim_io_printf (sd, "\n");
val = cpu->ios[M6811_PACTL];
print_io_byte (sd, "PACTL", pactl_desc, val, base + M6811_PACTL);
sim_io_printf (sd, "\n");
val = cpu->ios[M6811_PACNT];
print_io_byte (sd, "PACNT", 0, val, base + M6811_PACNT);
sim_io_printf (sd, "\n");
m68hc11tim_print_timer (me, "RTI", controller->rti_timer_event);
m68hc11tim_print_timer (me, "COP", controller->cop_timer_event);
m68hc11tim_print_timer (me, "OVERFLOW", controller->tof_timer_event);
m68hc11tim_print_timer (me, "COMPARE", controller->cmp_timer_event);
}
static int
m68hc11tim_ioctl (struct hw *me,
hw_ioctl_request request,
va_list ap)
{
m68hc11tim_info (me);
return 0;
}
static unsigned
m68hc11tim_io_read_buffer (struct hw *me,
void *dest,
int space,
unsigned_word base,
unsigned nr_bytes)
{
SIM_DESC sd;
struct m68hc11tim *controller;
sim_cpu *cpu;
unsigned8 val;
unsigned cnt = 0;
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);
while (nr_bytes)
{
switch (base)
{
case M6811_TCTN_H:
val = (uint8) ((cpu->cpu_absolute_cycle - controller->tcnt_adjust)
/ (controller->clock_prescaler * 256));
break;
case M6811_TCTN_L:
val = (uint8) ((cpu->cpu_absolute_cycle - controller->tcnt_adjust)
/ controller->clock_prescaler);
break;
default:
val = cpu->ios[base];
break;
}
*((unsigned8*) dest) = val;
dest++;
base++;
nr_bytes--;
cnt++;
}
return cnt;
}
static unsigned
m68hc11tim_io_write_buffer (struct hw *me,
const void *source,
int space,
unsigned_word base,
unsigned nr_bytes)
{
SIM_DESC sd;
struct m68hc11tim *controller;
sim_cpu *cpu;
unsigned8 val, n;
signed64 adj;
int reset_compare = 0;
int reset_overflow = 0;
int cnt = 0;
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);
while (nr_bytes)
{
val = *((const unsigned8*) source);
switch (base)
{
case M6811_TCTN_L:
adj = ((cpu->cpu_absolute_cycle - controller->tcnt_adjust)
/ (controller->clock_prescaler * (signed64) 256)) & 0x0FF;
adj = cpu->cpu_absolute_cycle
- (adj * controller->clock_prescaler * (signed64) 256)
- ((signed64) adj * controller->clock_prescaler);
controller->tcnt_adjust = adj;
reset_compare = 1;
reset_overflow = 1;
break;
case M6811_TCTN_H:
adj = ((cpu->cpu_absolute_cycle - controller->tcnt_adjust)
/ controller->clock_prescaler) & 0x0ff;
adj = cpu->cpu_absolute_cycle
- ((signed64) val * controller->clock_prescaler * (signed64) 256)
- (adj * controller->clock_prescaler);
controller->tcnt_adjust = adj;
reset_compare = 1;
reset_overflow = 1;
break;
case M6811_TMSK2:
if (cpu->cpu_absolute_cycle >= 64)
{
val &= ~(M6811_PR1 | M6811_PR0);
val |= cpu->ios[M6811_TMSK2] & (M6811_PR1 | M6811_PR0);
}
switch (val & (M6811_PR1 | M6811_PR0))
{
case 0:
n = 1;
break;
case M6811_PR0:
n = 4;
break;
case M6811_PR1:
n = 8;
break;
default:
case M6811_PR1 | M6811_PR0:
n = 16;
break;
}
if (cpu->cpu_absolute_cycle < 64)
{
reset_overflow = 1;
controller->clock_prescaler = n;
}
cpu->ios[base] = val;
interrupts_update_pending (&cpu->cpu_interrupts);
break;
case M6811_PACTL:
n = (1 << ((val & (M6811_RTR1 | M6811_RTR0))));
cpu->ios[base] = val;
controller->rti_delay = (long) (n) * 8192;
m68hc11tim_timer_event (me, (void*) (RTI_EVENT| 0x100));
break;
case M6811_TFLG2:
val &= cpu->ios[M6811_TFLG2];
cpu->ios[M6811_TFLG2] &= ~val;
interrupts_update_pending (&cpu->cpu_interrupts);
break;
case M6811_TMSK1:
cpu->ios[M6811_TMSK1] = val;
interrupts_update_pending (&cpu->cpu_interrupts);
break;
case M6811_TFLG1:
val &= cpu->ios[M6811_TFLG1];
cpu->ios[M6811_TFLG1] &= ~val;
interrupts_update_pending (&cpu->cpu_interrupts);
break;
case M6811_TOC1:
case M6811_TOC2:
case M6811_TOC3:
case M6811_TOC4:
case M6811_TOC5:
cpu->ios[base] = val;
reset_compare = 1;
break;
case M6811_TCTL1:
case M6811_TCTL2:
cpu->ios[base] = val;
break;
default:
cpu->ios[base] = val;
break;
}
base++;
nr_bytes--;
cnt++;
source++;
}
if (reset_compare)
{
m68hc11tim_timer_event (me, (void*) (COMPARE_EVENT));
}
if (reset_overflow)
{
m68hc11tim_timer_event (me, (void*) (OVERFLOW_EVENT| 0x100));
}
return cnt;
}
const struct hw_descriptor dv_m68hc11tim_descriptor[] = {
{ "m68hc11tim", m68hc11tim_finish },
{ "m68hc12tim", m68hc11tim_finish },
{ NULL },
};