+/* Setup an oscillator on an input port.
+
+ TON represents the time in seconds that the input port should be set to 1.
+ TOFF is the time in seconds for the input port to be set to 0.
+
+ The oscillator frequency is therefore 1 / (ton + toff).
+
+ REPEAT indicates the number of 1 <-> 0 transitions until the oscillator
+ stops. */
+int
+m68hc11cpu_set_oscillator (SIM_DESC sd, const char *port,
+ double ton, double toff, signed64 repeat)
+{
+ sim_cpu *cpu;
+ struct input_osc *osc;
+ double f;
+
+ cpu = STATE_CPU (sd, 0);
+
+ /* Find oscillator that corresponds to the input port. */
+ osc = find_oscillator (hw_data (cpu->hw_cpu), port);
+ if (osc == 0)
+ return -1;
+
+ /* Compute the ON time in cpu cycles. */
+ f = (double) (cpu->cpu_frequency) * ton;
+ osc->on_time = (signed64) (f / 4.0);
+ if (osc->on_time < 1)
+ osc->on_time = 1;
+
+ /* Compute the OFF time in cpu cycles. */
+ f = (double) (cpu->cpu_frequency) * toff;
+ osc->off_time = (signed64) (f / 4.0);
+ if (osc->off_time < 1)
+ osc->off_time = 1;
+
+ osc->repeat = repeat;
+ if (osc->event)
+ hw_event_queue_deschedule (cpu->hw_cpu, osc->event);
+
+ osc->event = hw_event_queue_schedule (cpu->hw_cpu,
+ osc->value ? osc->on_time
+ : osc->off_time,
+ oscillator_handler, osc);
+ return 0;
+}
+
+/* Clear the oscillator. */
+int
+m68hc11cpu_clear_oscillator (SIM_DESC sd, const char *port)
+{
+ sim_cpu *cpu;
+ struct input_osc *osc;
+
+ cpu = STATE_CPU (sd, 0);
+ osc = find_oscillator (hw_data (cpu->hw_cpu), port);
+ if (osc == 0)
+ return -1;
+
+ if (osc->event)
+ hw_event_queue_deschedule (cpu->hw_cpu, osc->event);
+ osc->event = 0;
+ osc->repeat = 0;
+ return 0;
+}
+
+static int
+get_frequency (const char *s, double *f)
+{
+ char *p;
+
+ *f = strtod (s, &p);
+ if (s == p)
+ return -1;
+
+ if (*p)
+ {
+ if (strcasecmp (p, "khz") == 0)
+ *f = *f * 1000.0;
+ else if (strcasecmp (p, "mhz") == 0)
+ *f = *f * 1000000.0;
+ else if (strcasecmp (p, "hz") != 0)
+ return -1;
+ }
+ return 0;
+}
+
+static SIM_RC
+m68hc11_option_handler (SIM_DESC sd, sim_cpu *cpu,
+ int opt, char *arg, int is_command)
+{
+ struct m68hc11cpu *controller;
+ double f;
+ char *p;
+ int i;
+ int title_printed = 0;
+
+ if (cpu == 0)
+ cpu = STATE_CPU (sd, 0);
+
+ controller = hw_data (cpu->hw_cpu);
+ switch (opt)
+ {
+ case OPTION_OSC_SET:
+ p = strchr (arg, ',');
+ if (p)
+ *p++ = 0;
+
+ if (p == 0)
+ sim_io_eprintf (sd, "No frequency specified\n");
+ else if (get_frequency (p, &f) < 0 || f < 1.0e-8)
+ sim_io_eprintf (sd, "Invalid frequency: '%s'\n", p);
+ else if (m68hc11cpu_set_oscillator (sd, arg,
+ 1.0 / (f * 2.0),
+ 1.0 / (f * 2.0), LONG_MAX))
+ sim_io_eprintf (sd, "Invalid input port: '%s'\n", arg);
+ break;
+
+ case OPTION_OSC_CLEAR:
+ if (m68hc11cpu_clear_oscillator (sd, arg) != 0)
+ sim_io_eprintf (sd, "Invalid input port: '%s'\n", arg);
+ break;
+
+ case OPTION_OSC_INFO:
+ for (i = 0; i < controller->last_oscillator; i++)
+ {
+ signed64 t;
+ struct input_osc *osc;
+
+ osc = &controller->oscillators[i];
+ if (osc->event)
+ {
+ double f;
+ int cur_value;
+ int next_value;
+ char freq[32];
+
+ if (title_printed == 0)
+ {
+ title_printed = 1;
+ sim_io_printf (sd, " PORT Frequency Current"
+ " Next Transition time\n");
+ }
+
+ f = (double) (osc->on_time + osc->off_time);
+ f = (double) (cpu->cpu_frequency / 4) / f;
+ t = hw_event_remain_time (cpu->hw_cpu, osc->event);
+
+ if (f > 10000.0)
+ sprintf (freq, "%6.2f", f / 1000.0);
+ else
+ sprintf (freq, "%6.2f", f);
+ cur_value = osc->value ? 1 : 0;
+ next_value = osc->value ? 0 : 1;
+ if (f > 10000.0)
+ sim_io_printf (sd, " %4.4s %8.8s khz"
+ " %d %d %35.35s\n",
+ osc->name, freq,
+ cur_value, next_value,
+ cycle_to_string (cpu, t,
+ PRINT_TIME | PRINT_CYCLE));
+ else
+ sim_io_printf (sd, " %4.4s %8.8s hz "
+ " %d %d %35.35s\n",
+ osc->name, freq,
+ cur_value, next_value,
+ cycle_to_string (cpu, t,
+ PRINT_TIME | PRINT_CYCLE));
+ }
+ }
+ break;
+ }
+
+ return SIM_RC_OK;
+}
+