|  | /*  dv-m68hc11.c -- CPU 68HC11&68HC12 as a device. | 
|  | Copyright (C) 1999-2017 Free Software Foundation, Inc. | 
|  | Written by Stephane Carrez (stcarrez@nerim.fr) | 
|  | (From a driver model Contributed by Cygnus Solutions.) | 
|  |  | 
|  | This program is free software; you can redistribute it and/or modify | 
|  | it under the terms of the GNU General Public License as published by | 
|  | the Free Software Foundation; either version 3 of the License, or | 
|  | (at your option) any later version. | 
|  |  | 
|  | This program is distributed in the hope that it will be useful, | 
|  | but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | GNU General Public License for more details. | 
|  |  | 
|  | You should have received a copy of the GNU General Public License | 
|  | along with this program.  If not, see <http://www.gnu.org/licenses/>. | 
|  |  | 
|  | */ | 
|  |  | 
|  |  | 
|  | #include "sim-main.h" | 
|  | #include "sim-hw.h" | 
|  | #include "hw-main.h" | 
|  | #include "sim-options.h" | 
|  | #include "hw-base.h" | 
|  | #include <limits.h> | 
|  |  | 
|  | /* DEVICE | 
|  |  | 
|  | m68hc11cpu - m68hc11 cpu virtual device | 
|  | m68hc12cpu - m68hc12 cpu virtual device | 
|  |  | 
|  | DESCRIPTION | 
|  |  | 
|  | Implements the external m68hc11/68hc12 functionality.  This includes | 
|  | the delivery of of interrupts generated from other devices and the | 
|  | handling of device specific registers. | 
|  |  | 
|  |  | 
|  | PROPERTIES | 
|  |  | 
|  | reg <base> <size> | 
|  |  | 
|  | Register base (should be 0x1000 0x03f for C11, 0x0000 0x3ff for HC12). | 
|  |  | 
|  | clock <hz> | 
|  |  | 
|  | Frequency of the quartz used by the processor. | 
|  |  | 
|  | mode [single | expanded | bootstrap | test] | 
|  |  | 
|  | Cpu operating mode (the MODA and MODB external pins). | 
|  |  | 
|  |  | 
|  | PORTS | 
|  |  | 
|  | reset (input) | 
|  |  | 
|  | Reset the cpu and generates a cpu-reset event (used to reset | 
|  | other devices). | 
|  |  | 
|  | nmi (input) | 
|  |  | 
|  | Deliver a non-maskable interrupt to the processor. | 
|  |  | 
|  |  | 
|  | set-port-a (input) | 
|  | set-port-c (input) | 
|  | set-pord-d (input) | 
|  |  | 
|  | Allow an external device to set the value of port A, C or D inputs. | 
|  |  | 
|  |  | 
|  | cpu-reset (output) | 
|  |  | 
|  | Event generated after the CPU performs a reset. | 
|  |  | 
|  |  | 
|  | port-a (output) | 
|  | port-b (output) | 
|  | port-c (output) | 
|  | port-d (output) | 
|  |  | 
|  | Event generated when the value of the output port A, B, C or D | 
|  | changes. | 
|  |  | 
|  |  | 
|  | BUGS | 
|  |  | 
|  | When delivering an interrupt, this code assumes that there is only | 
|  | one processor (number 0). | 
|  |  | 
|  | */ | 
|  |  | 
|  | enum | 
|  | { | 
|  | OPTION_OSC_SET = OPTION_START, | 
|  | OPTION_OSC_CLEAR, | 
|  | OPTION_OSC_INFO | 
|  | }; | 
|  |  | 
|  | static DECLARE_OPTION_HANDLER (m68hc11_option_handler); | 
|  |  | 
|  | static const OPTION m68hc11_options[] = | 
|  | { | 
|  | { {"osc-set", required_argument, NULL, OPTION_OSC_SET }, | 
|  | '\0', "BIT,FREQ", "Set the oscillator on input port BIT", | 
|  | m68hc11_option_handler }, | 
|  | { {"osc-clear", required_argument, NULL, OPTION_OSC_CLEAR }, | 
|  | '\0', "BIT", "Clear oscillator on input port BIT", | 
|  | m68hc11_option_handler }, | 
|  | { {"osc-info", no_argument, NULL, OPTION_OSC_INFO }, | 
|  | '\0', NULL, "Print information about current input oscillators", | 
|  | m68hc11_option_handler }, | 
|  |  | 
|  | { {NULL, no_argument, NULL, 0}, '\0', NULL, NULL, NULL } | 
|  | }; | 
|  |  | 
|  | struct input_osc | 
|  | { | 
|  | signed64         on_time; | 
|  | signed64         off_time; | 
|  | signed64         repeat; | 
|  | struct hw_event *event; | 
|  | const char      *name; | 
|  | uint8            mask; | 
|  | uint8            value; | 
|  | uint16           addr; | 
|  | }; | 
|  |  | 
|  | #define NR_PORT_A_OSC (4) | 
|  | #define NR_PORT_B_OSC (0) | 
|  | #define NR_PORT_C_OSC (8) | 
|  | #define NR_PORT_D_OSC (6) | 
|  | #define NR_OSC (NR_PORT_A_OSC + NR_PORT_B_OSC + NR_PORT_C_OSC + NR_PORT_D_OSC) | 
|  | struct m68hc11cpu { | 
|  | /* Pending interrupts for delivery by event handler.  */ | 
|  | int              pending_reset; | 
|  | int              pending_nmi; | 
|  | int              pending_level; | 
|  | struct hw_event  *event; | 
|  | unsigned_word    attach_address; | 
|  | int              attach_size; | 
|  | int              attach_space; | 
|  | int              last_oscillator; | 
|  | struct input_osc oscillators[NR_OSC]; | 
|  | }; | 
|  |  | 
|  |  | 
|  |  | 
|  | /* input port ID's */ | 
|  |  | 
|  | enum { | 
|  | RESET_PORT, | 
|  | NMI_PORT, | 
|  | IRQ_PORT, | 
|  | CPU_RESET_PORT, | 
|  | SET_PORT_A, | 
|  | SET_PORT_C, | 
|  | SET_PORT_D, | 
|  | CPU_WRITE_PORT, | 
|  | PORT_A, | 
|  | PORT_B, | 
|  | PORT_C, | 
|  | PORT_D, | 
|  | CAPTURE | 
|  | }; | 
|  |  | 
|  |  | 
|  | static const struct hw_port_descriptor m68hc11cpu_ports[] = { | 
|  |  | 
|  | /* Interrupt inputs.  */ | 
|  | { "reset",     RESET_PORT,     0, input_port, }, | 
|  | { "nmi",       NMI_PORT,       0, input_port, }, | 
|  | { "irq",       IRQ_PORT,       0, input_port, }, | 
|  |  | 
|  | { "set-port-a", SET_PORT_A,    0, input_port, }, | 
|  | { "set-port-c", SET_PORT_C,    0, input_port, }, | 
|  | { "set-port-d", SET_PORT_D,    0, input_port, }, | 
|  |  | 
|  | { "cpu-write-port", CPU_WRITE_PORT,    0, input_port, }, | 
|  |  | 
|  | /* Events generated for connection to other devices.  */ | 
|  | { "cpu-reset", CPU_RESET_PORT, 0, output_port, }, | 
|  |  | 
|  | /* Events generated when the corresponding port is | 
|  | changed by the program.  */ | 
|  | { "port-a",    PORT_A,         0, output_port, }, | 
|  | { "port-b",    PORT_B,         0, output_port, }, | 
|  | { "port-c",    PORT_C,         0, output_port, }, | 
|  | { "port-d",    PORT_D,         0, output_port, }, | 
|  |  | 
|  | { "capture",   CAPTURE,        0, output_port, }, | 
|  |  | 
|  | { NULL, }, | 
|  | }; | 
|  |  | 
|  | static hw_io_read_buffer_method m68hc11cpu_io_read_buffer; | 
|  | static hw_io_write_buffer_method m68hc11cpu_io_write_buffer; | 
|  | static hw_ioctl_method m68hc11_ioctl; | 
|  |  | 
|  | /* Finish off the partially created hw device.  Attach our local | 
|  | callbacks.  Wire up our port names etc.  */ | 
|  |  | 
|  | static hw_port_event_method m68hc11cpu_port_event; | 
|  |  | 
|  | static void make_oscillator (struct m68hc11cpu *controller, | 
|  | const char *id, uint16 addr, uint8 mask); | 
|  | static struct input_osc *find_oscillator (struct m68hc11cpu *controller, | 
|  | const char *id); | 
|  | static void reset_oscillators (struct hw *me); | 
|  |  | 
|  | static void | 
|  | dv_m6811_attach_address_callback (struct hw *me, | 
|  | int level, | 
|  | int space, | 
|  | address_word addr, | 
|  | address_word nr_bytes, | 
|  | struct hw *client) | 
|  | { | 
|  | HW_TRACE ((me, "attach - level=%d, space=%d, addr=0x%lx, sz=%ld, client=%s", | 
|  | level, space, (unsigned long) addr, (unsigned long) nr_bytes, | 
|  | hw_path (client))); | 
|  |  | 
|  | if (space != io_map) | 
|  | { | 
|  | sim_core_attach (hw_system (me), | 
|  | NULL, /*cpu*/ | 
|  | level, | 
|  | access_read_write_exec, | 
|  | space, addr, | 
|  | nr_bytes, | 
|  | 0, /* modulo */ | 
|  | client, | 
|  | NULL); | 
|  | } | 
|  | else | 
|  | { | 
|  | /*printf("Attach from sub device: %d\n", (long) addr);*/ | 
|  | sim_core_attach (hw_system (me), | 
|  | NULL, /*cpu*/ | 
|  | level, | 
|  | access_io, | 
|  | space, addr, | 
|  | nr_bytes, | 
|  | 0, /* modulo */ | 
|  | client, | 
|  | NULL); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void | 
|  | dv_m6811_detach_address_callback (struct hw *me, | 
|  | int level, | 
|  | int space, | 
|  | address_word addr, | 
|  | address_word nr_bytes, | 
|  | struct hw *client) | 
|  | { | 
|  | sim_core_detach (hw_system (me), NULL, /*cpu*/ | 
|  | level, space, addr); | 
|  | } | 
|  |  | 
|  | static void | 
|  | m68hc11_delete (struct hw* me) | 
|  | { | 
|  | struct m68hc11cpu *controller; | 
|  |  | 
|  | controller = hw_data (me); | 
|  |  | 
|  | reset_oscillators (me); | 
|  | hw_detach_address (me, M6811_IO_LEVEL, | 
|  | controller->attach_space, | 
|  | controller->attach_address, | 
|  | controller->attach_size, me); | 
|  | } | 
|  |  | 
|  |  | 
|  | static void | 
|  | attach_m68hc11_regs (struct hw *me, | 
|  | struct m68hc11cpu *controller) | 
|  | { | 
|  | SIM_DESC sd; | 
|  | sim_cpu *cpu; | 
|  | reg_property_spec reg; | 
|  | const char *cpu_mode; | 
|  |  | 
|  | 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, | 
|  | &controller->attach_space, | 
|  | &controller->attach_address, | 
|  | me); | 
|  | hw_unit_size_to_attach_size (hw_parent (me), | 
|  | ®.size, | 
|  | &controller->attach_size, me); | 
|  |  | 
|  | hw_attach_address (hw_parent (me), M6811_IO_LEVEL, | 
|  | controller->attach_space, | 
|  | controller->attach_address, | 
|  | controller->attach_size, | 
|  | me); | 
|  | set_hw_delete (me, m68hc11_delete); | 
|  |  | 
|  | /* Get cpu frequency.  */ | 
|  | sd = hw_system (me); | 
|  | cpu = STATE_CPU (sd, 0); | 
|  | if (hw_find_property (me, "clock") != NULL) | 
|  | { | 
|  | cpu->cpu_frequency = hw_find_integer_property (me, "clock"); | 
|  | } | 
|  | else | 
|  | { | 
|  | cpu->cpu_frequency = 8*1000*1000; | 
|  | } | 
|  |  | 
|  | if (hw_find_property (me, "use_bank") != NULL) | 
|  | hw_attach_address (hw_parent (me), 0, | 
|  | exec_map, | 
|  | cpu->bank_start, | 
|  | cpu->bank_end - cpu->bank_start, | 
|  | me); | 
|  |  | 
|  | cpu_mode = "expanded"; | 
|  | if (hw_find_property (me, "mode") != NULL) | 
|  | cpu_mode = hw_find_string_property (me, "mode"); | 
|  |  | 
|  | if (strcmp (cpu_mode, "test") == 0) | 
|  | cpu->cpu_mode = M6811_MDA | M6811_SMOD; | 
|  | else if (strcmp (cpu_mode, "bootstrap") == 0) | 
|  | cpu->cpu_mode = M6811_SMOD; | 
|  | else if (strcmp (cpu_mode, "single") == 0) | 
|  | cpu->cpu_mode = 0; | 
|  | else | 
|  | cpu->cpu_mode = M6811_MDA; | 
|  |  | 
|  | controller->last_oscillator = 0; | 
|  |  | 
|  | /* Create oscillators for input port A.  */ | 
|  | make_oscillator (controller, "A7", M6811_PORTA, 0x80); | 
|  | make_oscillator (controller, "A2", M6811_PORTA, 0x04); | 
|  | make_oscillator (controller, "A1", M6811_PORTA, 0x02); | 
|  | make_oscillator (controller, "A0", M6811_PORTA, 0x01); | 
|  |  | 
|  | /* port B is output only.  */ | 
|  |  | 
|  | /* Create oscillators for input port C.  */ | 
|  | make_oscillator (controller, "C0", M6811_PORTC, 0x01); | 
|  | make_oscillator (controller, "C1", M6811_PORTC, 0x02); | 
|  | make_oscillator (controller, "C2", M6811_PORTC, 0x04); | 
|  | make_oscillator (controller, "C3", M6811_PORTC, 0x08); | 
|  | make_oscillator (controller, "C4", M6811_PORTC, 0x10); | 
|  | make_oscillator (controller, "C5", M6811_PORTC, 0x20); | 
|  | make_oscillator (controller, "C6", M6811_PORTC, 0x40); | 
|  | make_oscillator (controller, "C7", M6811_PORTC, 0x80); | 
|  |  | 
|  | /* Create oscillators for input port D.  */ | 
|  | make_oscillator (controller, "D0", M6811_PORTD, 0x01); | 
|  | make_oscillator (controller, "D1", M6811_PORTD, 0x02); | 
|  | make_oscillator (controller, "D2", M6811_PORTD, 0x04); | 
|  | make_oscillator (controller, "D3", M6811_PORTD, 0x08); | 
|  | make_oscillator (controller, "D4", M6811_PORTD, 0x10); | 
|  | make_oscillator (controller, "D5", M6811_PORTD, 0x20); | 
|  |  | 
|  | /* Add oscillator commands.  */ | 
|  | sim_add_option_table (sd, 0, m68hc11_options); | 
|  | } | 
|  |  | 
|  | static void | 
|  | m68hc11cpu_finish (struct hw *me) | 
|  | { | 
|  | struct m68hc11cpu *controller; | 
|  |  | 
|  | controller = HW_ZALLOC (me, struct m68hc11cpu); | 
|  | set_hw_data (me, controller); | 
|  | set_hw_io_read_buffer (me, m68hc11cpu_io_read_buffer); | 
|  | set_hw_io_write_buffer (me, m68hc11cpu_io_write_buffer); | 
|  | set_hw_ports (me, m68hc11cpu_ports); | 
|  | set_hw_port_event (me, m68hc11cpu_port_event); | 
|  | set_hw_attach_address (me, dv_m6811_attach_address_callback); | 
|  | set_hw_detach_address (me, dv_m6811_detach_address_callback); | 
|  | #ifdef set_hw_ioctl | 
|  | set_hw_ioctl (me, m68hc11_ioctl); | 
|  | #else | 
|  | me->to_ioctl = m68hc11_ioctl; | 
|  | #endif | 
|  |  | 
|  | /* Initialize the pending interrupt flags.  */ | 
|  | controller->pending_level = 0; | 
|  | controller->pending_reset = 0; | 
|  | controller->pending_nmi = 0; | 
|  | controller->event = NULL; | 
|  |  | 
|  | attach_m68hc11_regs (me, controller); | 
|  | } | 
|  |  | 
|  | /* An event arrives on an interrupt port.  */ | 
|  |  | 
|  | static void | 
|  | deliver_m68hc11cpu_interrupt (struct hw *me, void *data) | 
|  | { | 
|  | } | 
|  |  | 
|  | static void | 
|  | make_oscillator (struct m68hc11cpu *controller, const char *name, | 
|  | uint16 addr, uint8 mask) | 
|  | { | 
|  | struct input_osc *osc; | 
|  |  | 
|  | if (controller->last_oscillator >= NR_OSC) | 
|  | hw_abort (0, "Too many oscillators"); | 
|  |  | 
|  | osc = &controller->oscillators[controller->last_oscillator]; | 
|  | osc->name = name; | 
|  | osc->addr = addr; | 
|  | osc->mask = mask; | 
|  | controller->last_oscillator++; | 
|  | } | 
|  |  | 
|  | /* Find the oscillator given the input port name.  */ | 
|  | static struct input_osc * | 
|  | find_oscillator (struct m68hc11cpu *controller, const char *name) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | for (i = 0; i < controller->last_oscillator; i++) | 
|  | if (strcasecmp (controller->oscillators[i].name, name) == 0) | 
|  | return &controller->oscillators[i]; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static void | 
|  | oscillator_handler (struct hw *me, void *data) | 
|  | { | 
|  | struct input_osc *osc = (struct input_osc*) data; | 
|  | SIM_DESC sd; | 
|  | sim_cpu *cpu; | 
|  | signed64 dt; | 
|  | uint8 val; | 
|  |  | 
|  | sd = hw_system (me); | 
|  | cpu = STATE_CPU (sd, 0); | 
|  |  | 
|  | /* Change the input bit.  */ | 
|  | osc->value ^= osc->mask; | 
|  | val = cpu->ios[osc->addr] & ~osc->mask; | 
|  | val |= osc->value; | 
|  | m68hc11cpu_set_port (me, cpu, osc->addr, val); | 
|  |  | 
|  | /* Setup event to toggle the bit.  */ | 
|  | if (osc->value) | 
|  | dt = osc->on_time; | 
|  | else | 
|  | dt = osc->off_time; | 
|  |  | 
|  | if (dt && --osc->repeat >= 0) | 
|  | { | 
|  | sim_events *events = STATE_EVENTS (sd); | 
|  |  | 
|  | dt += events->nr_ticks_to_process; | 
|  | osc->event = hw_event_queue_schedule (me, dt, oscillator_handler, osc); | 
|  | } | 
|  | else | 
|  | osc->event = 0; | 
|  | } | 
|  |  | 
|  | static void | 
|  | reset_oscillators (struct hw *me) | 
|  | { | 
|  | struct m68hc11cpu *controller = hw_data (me); | 
|  | int i; | 
|  |  | 
|  | for (i = 0; i < controller->last_oscillator; i++) | 
|  | { | 
|  | if (controller->oscillators[i].event) | 
|  | { | 
|  | hw_event_queue_deschedule (me, controller->oscillators[i].event); | 
|  | controller->oscillators[i].event = 0; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | static void | 
|  | m68hc11cpu_port_event (struct hw *me, | 
|  | int my_port, | 
|  | struct hw *source, | 
|  | int source_port, | 
|  | int level) | 
|  | { | 
|  | struct m68hc11cpu *controller = hw_data (me); | 
|  | SIM_DESC sd; | 
|  | sim_cpu *cpu; | 
|  |  | 
|  | sd  = hw_system (me); | 
|  | cpu = STATE_CPU (sd, 0); | 
|  | switch (my_port) | 
|  | { | 
|  | case RESET_PORT: | 
|  | HW_TRACE ((me, "port-in reset")); | 
|  |  | 
|  | /* The reset is made in 3 steps: | 
|  | - First, cleanup the current sim_cpu struct. | 
|  | - Reset the devices. | 
|  | - Restart the cpu for the reset (get the CPU mode from the | 
|  | CONFIG register that gets initialized by EEPROM device).  */ | 
|  | cpu_reset (cpu); | 
|  | reset_oscillators (me); | 
|  | hw_port_event (me, CPU_RESET_PORT, 1); | 
|  | cpu_restart (cpu); | 
|  | break; | 
|  |  | 
|  | case NMI_PORT: | 
|  | controller->pending_nmi = 1; | 
|  | HW_TRACE ((me, "port-in nmi")); | 
|  | break; | 
|  |  | 
|  | case IRQ_PORT: | 
|  | /* level == 0 means that the interrupt was cleared.  */ | 
|  | if(level == 0) | 
|  | controller->pending_level = -1; /* signal end of interrupt */ | 
|  | else | 
|  | controller->pending_level = level; | 
|  | HW_TRACE ((me, "port-in level=%d", level)); | 
|  | break; | 
|  |  | 
|  | case SET_PORT_A: | 
|  | m68hc11cpu_set_port (me, cpu, M6811_PORTA, level); | 
|  | break; | 
|  |  | 
|  | case SET_PORT_C: | 
|  | m68hc11cpu_set_port (me, cpu, M6811_PORTC, level); | 
|  | break; | 
|  |  | 
|  | case SET_PORT_D: | 
|  | m68hc11cpu_set_port (me, cpu, M6811_PORTD, level); | 
|  | break; | 
|  |  | 
|  | case CPU_WRITE_PORT: | 
|  | break; | 
|  |  | 
|  | default: | 
|  | hw_abort (me, "bad switch"); | 
|  | break; | 
|  | } | 
|  |  | 
|  | /* Schedule an event to be delivered immediately after current | 
|  | instruction.  */ | 
|  | if(controller->event != NULL) | 
|  | hw_event_queue_deschedule(me, controller->event); | 
|  | controller->event = | 
|  | hw_event_queue_schedule (me, 0, deliver_m68hc11cpu_interrupt, NULL); | 
|  | } | 
|  |  | 
|  |  | 
|  | io_reg_desc config_desc[] = { | 
|  | { M6811_NOSEC, "NOSEC ", "Security Mode Disable" }, | 
|  | { M6811_NOCOP, "NOCOP ", "COP System Disable" }, | 
|  | { M6811_ROMON, "ROMON ", "Enable On-chip Rom" }, | 
|  | { M6811_EEON,  "EEON  ", "Enable On-chip EEprom" }, | 
|  | { 0,  0, 0 } | 
|  | }; | 
|  |  | 
|  | io_reg_desc hprio_desc[] = { | 
|  | { M6811_RBOOT, "RBOOT ", "Read Bootstrap ROM" }, | 
|  | { M6811_SMOD,  "SMOD  ", "Special Mode" }, | 
|  | { M6811_MDA,   "MDA   ", "Mode Select A" }, | 
|  | { M6811_IRV,   "IRV   ", "Internal Read Visibility" }, | 
|  | { 0,  0, 0 } | 
|  | }; | 
|  |  | 
|  | io_reg_desc option_desc[] = { | 
|  | { M6811_ADPU,  "ADPU  ", "A/D Powerup" }, | 
|  | { M6811_CSEL,  "CSEL  ", "A/D/EE Charge pump clock source select" }, | 
|  | { M6811_IRQE,  "IRQE  ", "IRQ Edge/Level sensitive" }, | 
|  | { M6811_DLY,   "DLY   ", "Stop exit turn on delay" }, | 
|  | { M6811_CME,   "CME   ", "Clock Monitor Enable" }, | 
|  | { M6811_CR1,   "CR1   ", "COP timer rate select (CR1)" }, | 
|  | { M6811_CR0,   "CR0   ", "COP timer rate select (CR0)" }, | 
|  | { 0,  0, 0 } | 
|  | }; | 
|  |  | 
|  | static void | 
|  | m68hc11_info (struct hw *me) | 
|  | { | 
|  | SIM_DESC sd; | 
|  | uint16 base = 0; | 
|  | sim_cpu *cpu; | 
|  | struct m68hc11sio *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:\n"); | 
|  |  | 
|  | val = cpu->ios[M6811_HPRIO]; | 
|  | print_io_byte (sd, "HPRIO ", hprio_desc, val, base + M6811_HPRIO); | 
|  | switch (cpu->cpu_mode) | 
|  | { | 
|  | case M6811_MDA | M6811_SMOD: | 
|  | sim_io_printf (sd, "[test]\n"); | 
|  | break; | 
|  | case M6811_SMOD: | 
|  | sim_io_printf (sd, "[bootstrap]\n"); | 
|  | break; | 
|  | case M6811_MDA: | 
|  | sim_io_printf (sd, "[extended]\n"); | 
|  | break; | 
|  | default: | 
|  | sim_io_printf (sd, "[single]\n"); | 
|  | break; | 
|  | } | 
|  |  | 
|  | val = cpu->ios[M6811_CONFIG]; | 
|  | print_io_byte (sd, "CONFIG", config_desc, val, base + M6811_CONFIG); | 
|  | sim_io_printf (sd, "\n"); | 
|  |  | 
|  | val = cpu->ios[M6811_OPTION]; | 
|  | print_io_byte (sd, "OPTION", option_desc, val, base + M6811_OPTION); | 
|  | sim_io_printf (sd, "\n"); | 
|  |  | 
|  | val = cpu->ios[M6811_INIT]; | 
|  | print_io_byte (sd, "INIT  ", 0, val, base + M6811_INIT); | 
|  | sim_io_printf (sd, "Ram = 0x%04x IO = 0x%04x\n", | 
|  | (((uint16) (val & 0xF0)) << 8), | 
|  | (((uint16) (val & 0x0F)) << 12)); | 
|  |  | 
|  |  | 
|  | cpu_info (sd, cpu); | 
|  | interrupts_info (sd, &cpu->cpu_interrupts); | 
|  | } | 
|  |  | 
|  | static int | 
|  | m68hc11_ioctl (struct hw *me, | 
|  | hw_ioctl_request request, | 
|  | va_list ap) | 
|  | { | 
|  | m68hc11_info (me); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* 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; | 
|  | } | 
|  |  | 
|  | /* generic read/write */ | 
|  |  | 
|  | static unsigned | 
|  | m68hc11cpu_io_read_buffer (struct hw *me, | 
|  | void *dest, | 
|  | int space, | 
|  | unsigned_word base, | 
|  | unsigned nr_bytes) | 
|  | { | 
|  | SIM_DESC sd; | 
|  | struct m68hc11cpu *controller = hw_data (me); | 
|  | sim_cpu *cpu; | 
|  | unsigned byte = 0; | 
|  | int result; | 
|  |  | 
|  | HW_TRACE ((me, "read 0x%08lx %d", (long) base, (int) nr_bytes)); | 
|  |  | 
|  | sd  = hw_system (me); | 
|  | cpu = STATE_CPU (sd, 0); | 
|  |  | 
|  | if (base >= cpu->bank_start && base < cpu->bank_end) | 
|  | { | 
|  | address_word virt_addr = phys_to_virt (cpu, base); | 
|  | if (virt_addr != base) | 
|  | return sim_core_read_buffer (sd, cpu, space, dest, | 
|  | virt_addr, nr_bytes); | 
|  | } | 
|  |  | 
|  | /* Handle reads for the sub-devices.  */ | 
|  | base -= controller->attach_address; | 
|  | result = sim_core_read_buffer (sd, cpu, | 
|  | io_map, dest, base, nr_bytes); | 
|  | if (result > 0) | 
|  | return result; | 
|  |  | 
|  | while (nr_bytes) | 
|  | { | 
|  | if (base >= controller->attach_size) | 
|  | break; | 
|  |  | 
|  | memcpy (dest, &cpu->ios[base], 1); | 
|  | dest = (char*) dest + 1; | 
|  | base++; | 
|  | byte++; | 
|  | nr_bytes--; | 
|  | } | 
|  | return byte; | 
|  | } | 
|  |  | 
|  | void | 
|  | m68hc11cpu_set_port (struct hw *me, sim_cpu *cpu, | 
|  | unsigned addr, uint8 val) | 
|  | { | 
|  | uint8 mask; | 
|  | uint8 delta; | 
|  | int check_interrupts = 0; | 
|  | int i; | 
|  |  | 
|  | switch (addr) | 
|  | { | 
|  | case M6811_PORTA: | 
|  | if (cpu->ios[M6811_PACTL] & M6811_DDRA7) | 
|  | mask = 3; | 
|  | else | 
|  | mask = 0x83; | 
|  |  | 
|  | val = val & mask; | 
|  | val |= cpu->ios[M6811_PORTA] & ~mask; | 
|  | delta = val ^ cpu->ios[M6811_PORTA]; | 
|  | cpu->ios[M6811_PORTA] = val; | 
|  | if (delta & 0x80) | 
|  | { | 
|  | /* Pulse accumulator is enabled.  */ | 
|  | if ((cpu->ios[M6811_PACTL] & M6811_PAEN) | 
|  | && !(cpu->ios[M6811_PACTL] & M6811_PAMOD)) | 
|  | { | 
|  | int inc; | 
|  |  | 
|  | /* Increment event counter according to rising/falling edge.  */ | 
|  | if (cpu->ios[M6811_PACTL] & M6811_PEDGE) | 
|  | inc = (val & 0x80) ? 1 : 0; | 
|  | else | 
|  | inc = (val & 0x80) ? 0 : 1; | 
|  |  | 
|  | cpu->ios[M6811_PACNT] += inc; | 
|  |  | 
|  | /* Event counter overflowed.  */ | 
|  | if (inc && cpu->ios[M6811_PACNT] == 0) | 
|  | { | 
|  | cpu->ios[M6811_TFLG2] |= M6811_PAOVI; | 
|  | check_interrupts = 1; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Scan IC3, IC2 and IC1.  Bit number is 3 - i.  */ | 
|  | for (i = 0; i < 3; i++) | 
|  | { | 
|  | uint8 mask = (1 << i); | 
|  |  | 
|  | if (delta & mask) | 
|  | { | 
|  | uint8 edge; | 
|  | int captured; | 
|  |  | 
|  | edge = cpu->ios[M6811_TCTL2]; | 
|  | edge = (edge >> (2 * i)) & 0x3; | 
|  | switch (edge) | 
|  | { | 
|  | case 0: | 
|  | captured = 0; | 
|  | break; | 
|  | case 1: | 
|  | captured = (val & mask) != 0; | 
|  | break; | 
|  | case 2: | 
|  | captured = (val & mask) == 0; | 
|  | break; | 
|  | default: | 
|  | captured = 1; | 
|  | break; | 
|  | } | 
|  | if (captured) | 
|  | { | 
|  | cpu->ios[M6811_TFLG1] |= (1 << i); | 
|  | hw_port_event (me, CAPTURE, M6811_TIC1 + 3 - i); | 
|  | check_interrupts = 1; | 
|  | } | 
|  | } | 
|  | } | 
|  | break; | 
|  |  | 
|  | case M6811_PORTC: | 
|  | mask = cpu->ios[M6811_DDRC]; | 
|  | val = val & mask; | 
|  | val |= cpu->ios[M6811_PORTC] & ~mask; | 
|  | cpu->ios[M6811_PORTC] = val; | 
|  | break; | 
|  |  | 
|  | case M6811_PORTD: | 
|  | mask = cpu->ios[M6811_DDRD]; | 
|  | val = val & mask; | 
|  | val |= cpu->ios[M6811_PORTD] & ~mask; | 
|  | cpu->ios[M6811_PORTD] = val; | 
|  | break; | 
|  |  | 
|  | default: | 
|  | break; | 
|  | } | 
|  |  | 
|  | if (check_interrupts) | 
|  | interrupts_update_pending (&cpu->cpu_interrupts); | 
|  | } | 
|  |  | 
|  | static void | 
|  | m68hc11cpu_io_write (struct hw *me, sim_cpu *cpu, | 
|  | unsigned_word addr, uint8 val) | 
|  | { | 
|  | switch (addr) | 
|  | { | 
|  | case M6811_PORTA: | 
|  | hw_port_event (me, PORT_A, val); | 
|  | break; | 
|  |  | 
|  | case M6811_PIOC: | 
|  | break; | 
|  |  | 
|  | case M6811_PORTC: | 
|  | hw_port_event (me, PORT_C, val); | 
|  | break; | 
|  |  | 
|  | case M6811_PORTB: | 
|  | hw_port_event (me, PORT_B, val); | 
|  | break; | 
|  |  | 
|  | case M6811_PORTCL: | 
|  | break; | 
|  |  | 
|  | case M6811_DDRC: | 
|  | break; | 
|  |  | 
|  | case M6811_PORTD: | 
|  | hw_port_event (me, PORT_D, val); | 
|  | break; | 
|  |  | 
|  | case M6811_DDRD: | 
|  | break; | 
|  |  | 
|  | case M6811_TMSK2: | 
|  |  | 
|  | break; | 
|  |  | 
|  | /* Change the RAM and I/O mapping.  */ | 
|  | case M6811_INIT: | 
|  | { | 
|  | uint8 old_bank = cpu->ios[M6811_INIT]; | 
|  |  | 
|  | cpu->ios[M6811_INIT] = val; | 
|  |  | 
|  | /* Update IO mapping.  Detach from the old address | 
|  | and attach to the new one.  */ | 
|  | if ((old_bank & 0x0F) != (val & 0x0F)) | 
|  | { | 
|  | struct m68hc11cpu *controller = hw_data (me); | 
|  |  | 
|  | hw_detach_address (hw_parent (me), M6811_IO_LEVEL, | 
|  | controller->attach_space, | 
|  | controller->attach_address, | 
|  | controller->attach_size, | 
|  | me); | 
|  | controller->attach_address = (val & 0x0F0) << 12; | 
|  | hw_attach_address (hw_parent (me), M6811_IO_LEVEL, | 
|  | controller->attach_space, | 
|  | controller->attach_address, | 
|  | controller->attach_size, | 
|  | me); | 
|  | } | 
|  | if ((old_bank & 0xF0) != (val & 0xF0)) | 
|  | { | 
|  | ; | 
|  | } | 
|  | return; | 
|  | } | 
|  |  | 
|  | /* Writing the config is similar to programing the eeprom. | 
|  | The config register value is the last byte of the EEPROM. | 
|  | This last byte is not mapped in memory (that's why we have | 
|  | to add '1' to 'end_addr').  */ | 
|  | case M6811_CONFIG: | 
|  | { | 
|  | return; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* COP reset.  */ | 
|  | case M6811_COPRST: | 
|  | if (val == 0xAA && cpu->ios[addr] == 0x55) | 
|  | { | 
|  | val = 0; | 
|  | /* COP reset here.  */ | 
|  | } | 
|  | break; | 
|  |  | 
|  | default: | 
|  | break; | 
|  |  | 
|  | } | 
|  | cpu->ios[addr] = val; | 
|  | } | 
|  |  | 
|  | static unsigned | 
|  | m68hc11cpu_io_write_buffer (struct hw *me, | 
|  | const void *source, | 
|  | int space, | 
|  | unsigned_word base, | 
|  | unsigned nr_bytes) | 
|  | { | 
|  | SIM_DESC sd; | 
|  | struct m68hc11cpu *controller = hw_data (me); | 
|  | unsigned byte; | 
|  | sim_cpu *cpu; | 
|  | int result; | 
|  |  | 
|  | HW_TRACE ((me, "write 0x%08lx %d", (long) base, (int) nr_bytes)); | 
|  |  | 
|  | sd = hw_system (me); | 
|  | cpu = STATE_CPU (sd, 0); | 
|  |  | 
|  | if (base >= cpu->bank_start && base < cpu->bank_end) | 
|  | { | 
|  | address_word virt_addr = phys_to_virt (cpu, base); | 
|  | if (virt_addr != base) | 
|  | return sim_core_write_buffer (sd, cpu, space, source, | 
|  | virt_addr, nr_bytes); | 
|  | } | 
|  | base -= controller->attach_address; | 
|  | result = sim_core_write_buffer (sd, cpu, | 
|  | io_map, source, base, nr_bytes); | 
|  | if (result > 0) | 
|  | return result; | 
|  |  | 
|  | byte = 0; | 
|  | while (nr_bytes) | 
|  | { | 
|  | uint8 val; | 
|  | if (base >= controller->attach_size) | 
|  | break; | 
|  |  | 
|  | val = *((uint8*) source); | 
|  | m68hc11cpu_io_write (me, cpu, base, val); | 
|  | source = (char*) source + 1; | 
|  | base++; | 
|  | byte++; | 
|  | nr_bytes--; | 
|  | } | 
|  | return byte; | 
|  | } | 
|  |  | 
|  | const struct hw_descriptor dv_m68hc11_descriptor[] = { | 
|  | { "m68hc11", m68hc11cpu_finish }, | 
|  | { "m68hc12", m68hc11cpu_finish }, | 
|  | { NULL }, | 
|  | }; | 
|  |  |