📄 dv-m68hc11.c
字号:
/* dv-m68hc11.c -- CPU 68HC11&68HC12 as a device. Copyright (C) 1999, 2000, 2001, 2002, 2003 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 2 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, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */#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 voiddv_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 voiddv_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 voidm68hc11_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 voidattach_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 voidm68hc11cpu_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 voiddeliver_m68hc11cpu_interrupt (struct hw *me, void *data){}static voidmake_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 voidoscillator_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 voidreset_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 voidm68hc11cpu_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" },
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -