aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.target5
-rwxr-xr-xconfigure2
-rw-r--r--cpu-exec.c18
-rw-r--r--exec-all.h2
-rw-r--r--hw/an5206.c89
-rw-r--r--hw/mcf5206.c813
-rw-r--r--linux-user/main.c7
-rw-r--r--softmmu_header.h4
-rw-r--r--target-m68k/cpu.h29
-rw-r--r--target-m68k/exec.h4
-rw-r--r--target-m68k/helper.c64
-rw-r--r--target-m68k/op-hacks.h30
-rw-r--r--target-m68k/op.c101
-rw-r--r--target-m68k/op_helper.c140
-rw-r--r--target-m68k/op_mem.h46
-rw-r--r--target-m68k/qregs.def1
-rw-r--r--target-m68k/translate.c392
-rw-r--r--vl.c2
-rw-r--r--vl.h6
19 files changed, 1548 insertions, 207 deletions
diff --git a/Makefile.target b/Makefile.target
index 65a4882bc3..d24f4a567c 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -308,7 +308,7 @@ LIBOBJS+= op_helper.o helper.o
endif
ifeq ($(TARGET_BASE_ARCH), m68k)
-LIBOBJS+= helper.o
+LIBOBJS+= op_helper.o helper.o
endif
ifeq ($(TARGET_BASE_ARCH), alpha)
@@ -466,6 +466,9 @@ endif
ifeq ($(TARGET_BASE_ARCH), sh4)
VL_OBJS+= shix.o sh7750.o sh7750_regnames.o tc58128.o
endif
+ifeq ($(TARGET_BASE_ARCH), m68k)
+VL_OBJS+= an5206.o mcf5206.o ptimer.o
+endif
ifdef CONFIG_GDBSTUB
VL_OBJS+=gdbstub.o
endif
diff --git a/configure b/configure
index 7436c5fc7b..8b2743f25d 100755
--- a/configure
+++ b/configure
@@ -467,7 +467,7 @@ fi
if test -z "$target_list" ; then
# these targets are portable
if [ "$softmmu" = "yes" ] ; then
- target_list="i386-softmmu ppc-softmmu sparc-softmmu x86_64-softmmu mips-softmmu mipsel-softmmu mips64-softmmu mips64el-softmmu arm-softmmu ppc64-softmmu ppcemb-softmmu "
+ target_list="i386-softmmu ppc-softmmu sparc-softmmu x86_64-softmmu mips-softmmu mipsel-softmmu mips64-softmmu mips64el-softmmu arm-softmmu ppc64-softmmu ppcemb-softmmu m68k-softmmu"
fi
# the following are Linux specific
if [ "$linux_user" = "yes" ] ; then
diff --git a/cpu-exec.c b/cpu-exec.c
index fcac463bae..ea18b73ff0 100644
--- a/cpu-exec.c
+++ b/cpu-exec.c
@@ -196,7 +196,7 @@ static inline TranslationBlock *tb_find_fast(void)
cs_base = 0;
pc = env->PC;
#elif defined(TARGET_M68K)
- flags = env->fpcr & M68K_FPCR_PREC;
+ flags = (env->fpcr & M68K_FPCR_PREC) | (env->sr & SR_S);
cs_base = 0;
pc = env->pc;
#elif defined(TARGET_SH4)
@@ -297,7 +297,7 @@ int cpu_exec(CPUState *env1)
return EXCP_HALTED;
}
}
-#elif defined(TARGET_ALPHA)
+#elif defined(TARGET_ALPHA) || defined(TARGET_M68K)
if (env1->halted) {
if (env1->interrupt_request & CPU_INTERRUPT_HARD) {
env1->halted = 0;
@@ -390,6 +390,8 @@ int cpu_exec(CPUState *env1)
do_interrupt(env);
#elif defined(TARGET_ALPHA)
do_interrupt(env);
+#elif defined(TARGET_M68K)
+ do_interrupt(0);
#endif
}
env->exception_index = -1;
@@ -542,6 +544,18 @@ int cpu_exec(CPUState *env1)
if (interrupt_request & CPU_INTERRUPT_HARD) {
do_interrupt(env);
}
+#elif defined(TARGET_M68K)
+ if (interrupt_request & CPU_INTERRUPT_HARD
+ && ((env->sr & SR_I) >> SR_I_SHIFT)
+ < env->pending_level) {
+ /* Real hardware gets the interrupt vector via an
+ IACK cycle at this point. Current emulated
+ hardware doesn't rely on this, so we
+ provide/save the vector when the interrupt is
+ first signalled. */
+ env->exception_index = env->pending_vector;
+ do_interrupt(1);
+ }
#endif
/* Don't use the cached interupt_request value,
do_interrupt may have updated the EXITTB flag. */
diff --git a/exec-all.h b/exec-all.h
index c6b7bd1986..dbfe457614 100644
--- a/exec-all.h
+++ b/exec-all.h
@@ -584,6 +584,8 @@ static inline target_ulong get_phys_addr_code(CPUState *env, target_ulong addr)
is_user = ((env->sr & SR_MD) == 0);
#elif defined (TARGET_ALPHA)
is_user = ((env->ps >> 3) & 3);
+#elif defined (TARGET_M68K)
+ is_user = ((env->sr & SR_S) == 0);
#else
#error unimplemented CPU
#endif
diff --git a/hw/an5206.c b/hw/an5206.c
new file mode 100644
index 0000000000..1306d1964d
--- /dev/null
+++ b/hw/an5206.c
@@ -0,0 +1,89 @@
+/*
+ * Arnewsh 5206 ColdFire system emulation.
+ *
+ * Copyright (c) 2007 CodeSourcery.
+ *
+ * This code is licenced under the GPL
+ */
+
+#include "vl.h"
+
+#define KERNEL_LOAD_ADDR 0x10000
+#define AN5206_MBAR_ADDR 0x10000000
+#define AN5206_RAMBAR_ADDR 0x20000000
+
+/* Stub functions for hardware that doesn't exist. */
+void pic_info(void)
+{
+}
+
+void irq_info(void)
+{
+}
+
+void DMA_run (void)
+{
+}
+
+/* Board init. */
+
+static void an5206_init(int ram_size, int vga_ram_size, int boot_device,
+ DisplayState *ds, const char **fd_filename, int snapshot,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename, const char *cpu_model)
+{
+ CPUState *env;
+ int kernel_size;
+ uint64_t elf_entry;
+ target_ulong entry;
+
+ env = cpu_init();
+ if (!cpu_model)
+ cpu_model = "m5206";
+ cpu_m68k_set_model(env, cpu_model);
+
+ /* Initialize CPU registers. */
+ env->vbr = 0;
+ /* TODO: allow changing MBAR and RAMBAR. */
+ env->mbar = AN5206_MBAR_ADDR | 1;
+ env->rambar0 = AN5206_RAMBAR_ADDR | 1;
+
+ /* DRAM at address zero */
+ cpu_register_physical_memory(0, ram_size,
+ qemu_ram_alloc(ram_size) | IO_MEM_RAM);
+
+ /* Internal SRAM. */
+ cpu_register_physical_memory(AN5206_RAMBAR_ADDR, 512,
+ qemu_ram_alloc(512) | IO_MEM_RAM);
+
+ mcf5206_init(AN5206_MBAR_ADDR, env);
+
+ /* Load kernel. */
+ if (!kernel_filename) {
+ fprintf(stderr, "Kernel image must be specified\n");
+ exit(1);
+ }
+
+ kernel_size = load_elf(kernel_filename, 0, &elf_entry, NULL, NULL);
+ entry = elf_entry;
+ if (kernel_size < 0) {
+ kernel_size = load_uboot(kernel_filename, &entry, NULL);
+ }
+ if (kernel_size < 0) {
+ kernel_size = load_image(kernel_filename,
+ phys_ram_base + KERNEL_LOAD_ADDR);
+ entry = KERNEL_LOAD_ADDR;
+ }
+ if (kernel_size < 0) {
+ fprintf(stderr, "qemu: could not load kernel '%s'\n", kernel_filename);
+ exit(1);
+ }
+
+ env->pc = entry;
+}
+
+QEMUMachine an5206_machine = {
+ "an5206",
+ "Arnewsh 5206",
+ an5206_init,
+};
diff --git a/hw/mcf5206.c b/hw/mcf5206.c
new file mode 100644
index 0000000000..0da791293d
--- /dev/null
+++ b/hw/mcf5206.c
@@ -0,0 +1,813 @@
+/*
+ * Motorola ColdFire MCF5206 SoC embedded peripheral emulation.
+ *
+ * Copyright (c) 2007 CodeSourcery.
+ *
+ * This code is licenced under the GPL
+ */
+#include "vl.h"
+
+/* General purpose timer module. */
+typedef struct {
+ uint16_t tmr;
+ uint16_t trr;
+ uint16_t tcr;
+ uint16_t ter;
+ ptimer_state *timer;
+ qemu_irq irq;
+ int irq_state;
+} m5206_timer_state;
+
+#define TMR_RST 0x01
+#define TMR_CLK 0x06
+#define TMR_FRR 0x08
+#define TMR_ORI 0x10
+#define TMR_OM 0x20
+#define TMR_CE 0xc0
+
+#define TER_CAP 0x01
+#define TER_REF 0x02
+
+static void m5206_timer_update(m5206_timer_state *s)
+{
+ if ((s->tmr & TMR_ORI) != 0 && (s->ter & TER_REF))
+ qemu_irq_raise(s->irq);
+ else
+ qemu_irq_lower(s->irq);
+}
+
+static void m5206_timer_reset(m5206_timer_state *s)
+{
+ s->tmr = 0;
+ s->trr = 0;
+}
+
+static void m5206_timer_recalibrate(m5206_timer_state *s)
+{
+ int prescale;
+ int mode;
+
+ ptimer_stop(s->timer);
+
+ if ((s->tmr & TMR_RST) == 0)
+ return;
+
+ prescale = (s->tmr >> 8) + 1;
+ mode = (s->tmr >> 1) & 3;
+ if (mode == 2)
+ prescale *= 16;
+
+ if (mode == 3 || mode == 0)
+ cpu_abort(cpu_single_env,
+ "m5206_timer: mode %d not implemented\n", mode);
+ if ((s->tmr & TMR_FRR) == 0)
+ cpu_abort(cpu_single_env,
+ "m5206_timer: free running mode not implemented\n");
+
+ /* Assume 66MHz system clock. */
+ ptimer_set_freq(s->timer, 66000000 / prescale);
+
+ ptimer_set_limit(s->timer, s->trr, 0);
+
+ ptimer_run(s->timer, 0);
+}
+
+static void m5206_timer_trigger(void *opaque)
+{
+ m5206_timer_state *s = (m5206_timer_state *)opaque;
+ s->ter |= TER_REF;
+ m5206_timer_update(s);
+}
+
+static uint32_t m5206_timer_read(m5206_timer_state *s, uint32_t addr)
+{
+ switch (addr) {
+ case 0:
+ return s->tmr;
+ case 4:
+ return s->trr;
+ case 8:
+ return s->tcr;
+ case 0xc:
+ return s->trr - ptimer_get_count(s->timer);
+ case 0x11:
+ return s->ter;
+ default:
+ return 0;
+ }
+}
+
+static void m5206_timer_write(m5206_timer_state *s, uint32_t addr, uint32_t val)
+{
+ switch (addr) {
+ case 0:
+ if ((s->tmr & TMR_RST) != 0 && (val & TMR_RST) == 0) {
+ m5206_timer_reset(s);
+ }
+ s->tmr = val;
+ m5206_timer_recalibrate(s);
+ break;
+ case 4:
+ s->trr = val;
+ m5206_timer_recalibrate(s);
+ break;
+ case 8:
+ s->tcr = val;
+ break;
+ case 0xc:
+ ptimer_set_count(s->timer, val);
+ break;
+ case 0x11:
+ s->ter &= ~val;
+ break;
+ default:
+ break;
+ }
+ m5206_timer_update(s);
+}
+
+static m5206_timer_state *m5206_timer_init(qemu_irq irq)
+{
+ m5206_timer_state *s;
+ QEMUBH *bh;
+
+ s = (m5206_timer_state *)qemu_mallocz(sizeof(m5206_timer_state));
+ bh = qemu_bh_new(m5206_timer_trigger, s);
+ s->timer = ptimer_init(bh);
+ s->irq = irq;
+ m5206_timer_reset(s);
+ return s;
+}
+
+/* UART */
+
+typedef struct {
+ uint8_t mr[2];
+ uint8_t sr;
+ uint8_t isr;
+ uint8_t imr;
+ uint8_t bg1;
+ uint8_t bg2;
+ uint8_t fifo[4];
+ uint8_t tb;
+ int current_mr;
+ int fifo_len;
+ int tx_enabled;
+ int rx_enabled;
+ qemu_irq irq;
+ CharDriverState *chr;
+} m5206_uart_state;
+
+/* UART Status Register bits. */
+#define M5206_UART_RxRDY 0x01
+#define M5206_UART_FFULL 0x02
+#define M5206_UART_TxRDY 0x04
+#define M5206_UART_TxEMP 0x08
+#define M5206_UART_OE 0x10
+#define M5206_UART_PE 0x20
+#define M5206_UART_FE 0x40
+#define M5206_UART_RB 0x80
+
+/* Interrupt flags. */
+#define M5206_UART_TxINT 0x01
+#define M5206_UART_RxINT 0x02
+#define M5206_UART_DBINT 0x04
+#define M5206_UART_COSINT 0x80
+
+/* UMR1 flags. */
+#define M5206_UART_BC0 0x01
+#define M5206_UART_BC1 0x02
+#define M5206_UART_PT 0x04
+#define M5206_UART_PM0 0x08
+#define M5206_UART_PM1 0x10
+#define M5206_UART_ERR 0x20
+#define M5206_UART_RxIRQ 0x40
+#define M5206_UART_RxRTS 0x80
+
+static void m5206_uart_update(m5206_uart_state *s)
+{
+ s->isr &= ~(M5206_UART_TxINT | M5206_UART_RxINT);
+ if (s->sr & M5206_UART_TxRDY)
+ s->isr |= M5206_UART_TxINT;
+ if ((s->sr & ((s->mr[0] & M5206_UART_RxIRQ)
+ ? M5206_UART_FFULL : M5206_UART_RxRDY)) != 0)
+ s->isr |= M5206_UART_RxINT;
+
+ qemu_set_irq(s->irq, (s->isr & s->imr) != 0);
+}
+
+static uint32_t m5206_uart_read(m5206_uart_state *s, uint32_t addr)
+{
+ switch (addr) {
+ case 0x00:
+ return s->mr[s->current_mr];
+ case 0x04:
+ return s->sr;
+ case 0x0c:
+ {
+ uint8_t val;
+ int i;
+
+ if (s->fifo_len == 0)
+ return 0;
+
+ val = s->fifo[0];
+ s->fifo_len--;
+ for (i = 0; i < s->fifo_len; i++)
+ s->fifo[i] = s->fifo[i + 1];
+ s->sr &= ~M5206_UART_FFULL;
+ if (s->fifo_len == 0)
+ s->sr &= ~M5206_UART_RxRDY;
+ m5206_uart_update(s);
+ return val;
+ }
+ case 0x10:
+ /* TODO: Implement IPCR. */
+ return 0;
+ case 0x14:
+ return s->isr;
+ case 0x18:
+ return s->bg1;
+ case 0x1c:
+ return s->bg2;
+ default:
+ return 0;
+ }
+}
+
+/* Update TxRDY flag and set data if present and enabled. */
+static void m5206_uart_do_tx(m5206_uart_state *s)
+{
+ if (s->tx_enabled && (s->sr & M5206_UART_TxEMP) == 0) {
+ if (s->chr)
+ qemu_chr_write(s->chr, (unsigned char *)&s->tb, 1);
+ s->sr |= M5206_UART_TxEMP;
+ }
+ if (s->tx_enabled) {
+ s->sr |= M5206_UART_TxRDY;
+ } else {
+ s->sr &= ~M5206_UART_TxRDY;
+ }
+}
+
+static void m5206_do_command(m5206_uart_state *s, uint8_t cmd)
+{
+ /* Misc command. */
+ switch ((cmd >> 4) & 3) {
+ case 0: /* No-op. */
+ break;
+ case 1: /* Reset mode register pointer. */
+ s->current_mr = 0;
+ break;
+ case 2: /* Reset receiver. */
+ s->rx_enabled = 0;
+ s->fifo_len = 0;
+ s->sr &= ~(M5206_UART_RxRDY | M5206_UART_FFULL);
+ break;
+ case 3: /* Reset transmitter. */
+ s->tx_enabled = 0;
+ s->sr |= M5206_UART_TxEMP;
+ s->sr &= ~M5206_UART_TxRDY;
+ break;
+ case 4: /* Reset error status. */
+ break;
+ case 5: /* Reset break-change interrupt. */
+ s->isr &= ~M5206_UART_DBINT;
+ break;
+ case 6: /* Start break. */
+ case 7: /* Stop break. */
+ break;
+ }
+
+ /* Transmitter command. */
+ switch ((cmd >> 2) & 3) {
+ case 0: /* No-op. */
+ break;
+ case 1: /* Enable. */
+ s->tx_enabled = 1;
+ m5206_uart_do_tx(s);
+ break;
+ case 2: /* Disable. */
+ s->tx_enabled = 0;
+ m5206_uart_do_tx(s);
+ break;
+ case 3: /* Reserved. */
+ fprintf(stderr, "m5206_uart: Bad TX command\n");
+ break;
+ }
+
+ /* Receiver command. */
+ switch (cmd & 3) {
+ case 0: /* No-op. */
+ break;
+ case 1: /* Enable. */
+ s->rx_enabled = 1;
+ break;
+ case 2:
+ s->rx_enabled = 0;
+ break;
+ case 3: /* Reserved. */
+ fprintf(stderr, "m5206_uart: Bad RX command\n");
+ break;
+ }
+}
+
+static void m5206_uart_write(m5206_uart_state *s, uint32_t addr, uint32_t val)
+{
+ switch (addr) {
+ case 0x00:
+ s->mr[s->current_mr] = val;
+ s->current_mr = 1;
+ break;
+ case 0x04:
+ /* CSR is ignored. */
+ break;
+ case 0x08: /* Command Register. */
+ m5206_do_command(s, val);
+ break;
+ case 0x0c: /* Transmit Buffer. */
+ s->sr &= ~M5206_UART_TxEMP;
+ s->tb = val;
+ m5206_uart_do_tx(s);
+ break;
+ case 0x10:
+ /* ACR is ignored. */
+ break;
+ case 0x14:
+ s->imr = val;
+ break;
+ default:
+ break;
+ }
+ m5206_uart_update(s);
+}
+
+static void m5206_uart_reset(m5206_uart_state *s)
+{
+ s->fifo_len = 0;
+ s->mr[0] = 0;
+ s->mr[1] = 0;
+ s->sr = M5206_UART_TxEMP;
+ s->tx_enabled = 0;
+ s->rx_enabled = 0;
+ s->isr = 0;
+ s->imr = 0;
+}
+
+static void m5206_uart_push_byte(m5206_uart_state *s, uint8_t data)
+{
+ /* Break events overwrite the last byte if the fifo is full. */
+ if (s->fifo_len == 4)
+ s->fifo_len--;
+
+ s->fifo[s->fifo_len] = data;
+ s->fifo_len++;
+ s->sr |= M5206_UART_RxRDY;
+ if (s->fifo_len == 4)
+ s->sr |= M5206_UART_FFULL;
+
+ m5206_uart_update(s);
+}
+
+static void m5206_uart_event(void *opaque, int event)
+{
+ m5206_uart_state *s = (m5206_uart_state *)opaque;
+
+ switch (event) {
+ case CHR_EVENT_BREAK:
+ s->isr |= M5206_UART_DBINT;
+ m5206_uart_push_byte(s, 0);
+ break;
+ default:
+ break;
+ }
+}
+
+static int m5206_uart_can_receive(void *opaque)
+{
+ m5206_uart_state *s = (m5206_uart_state *)opaque;
+
+ return s->rx_enabled && (s->sr & M5206_UART_FFULL) == 0;
+}
+
+static void m5206_uart_receive(void *opaque, const uint8_t *buf, int size)
+{
+ m5206_uart_state *s = (m5206_uart_state *)opaque;
+
+ m5206_uart_push_byte(s, buf[0]);
+}
+
+static m5206_uart_state *m5206_uart_init(qemu_irq irq, CharDriverState *chr)
+{
+ m5206_uart_state *s;
+
+ s = qemu_mallocz(sizeof(m5206_uart_state));
+ s->chr = chr;
+ s->irq = irq;
+ if (chr) {
+ qemu_chr_add_handlers(chr, m5206_uart_can_receive, m5206_uart_receive,
+ m5206_uart_event, s);
+ }
+ m5206_uart_reset(s);
+ return s;
+}
+
+/* System Integration Module. */
+
+typedef struct {
+ CPUState *env;
+ m5206_timer_state *timer[2];
+ m5206_uart_state *uart[2];
+ uint8_t scr;
+ uint8_t icr[14];
+ uint16_t imr; /* 1 == interrupt is masked. */
+ uint16_t ipr;
+ uint8_t rsr;
+ uint8_t swivr;
+ uint8_t par;
+ /* Include the UART vector registers here. */
+ uint8_t uivr[2];
+} m5206_mbar_state;
+
+/* Interrupt controller. */
+
+static int m5206_find_pending_irq(m5206_mbar_state *s)
+{
+ int level;
+ int vector;
+ uint16_t active;
+ int i;
+
+ level = 0;
+ vector = 0;
+ active = s->ipr & ~s->imr;
+ if (!active)
+ return 0;
+
+ for (i = 1; i < 14; i++) {
+ if (active & (1 << i)) {
+ if ((s->icr[i] & 0x1f) > level) {
+ level = s->icr[i] & 0x1f;
+ vector = i;
+ }
+ }
+ }
+
+ if (level < 4)
+ vector = 0;
+
+ return vector;
+}
+
+static void m5206_mbar_update(m5206_mbar_state *s)
+{
+ int irq;
+ int vector;
+ int level;
+
+ irq = m5206_find_pending_irq(s);
+ if (irq) {
+ int tmp;
+ tmp = s->icr[irq];
+ level = (tmp >> 2) & 7;
+ if (tmp & 0x80) {
+ /* Autovector. */
+ vector = 24 + level;
+ } else {
+ switch (irq) {
+ case 8: /* SWT */
+ vector = s->swivr;
+ break;
+ case 12: /* UART1 */
+ vector = s->uivr[0];
+ break;
+ case 13: /* UART2 */
+ vector = s->uivr[1];
+ break;
+ default:
+ /* Unknown vector. */
+ fprintf(stderr, "Unhandled vector for IRQ %d\n", irq);
+ vector = 0xf;
+ break;
+ }
+ }
+ } else {
+ level = 0;
+ vector = 0;
+ }
+ m68k_set_irq_level(s->env, level, vector);
+}
+
+static void m5206_mbar_set_irq(void *opaque, int irq, int level)
+{
+ m5206_mbar_state *s = (m5206_mbar_state *)opaque;
+ if (level) {
+ s->ipr |= 1 << irq;
+ } else {
+ s->ipr &= ~(1 << irq);
+ }
+ m5206_mbar_update(s);
+}
+
+/* System Integration Module. */
+
+static void m5206_mbar_reset(m5206_mbar_state *s)
+{
+ s->scr = 0xc0;
+ s->icr[1] = 0x04;
+ s->icr[2] = 0x08;
+ s->icr[3] = 0x0c;
+ s->icr[4] = 0x10;
+ s->icr[5] = 0x14;
+ s->icr[6] = 0x18;
+ s->icr[7] = 0x1c;
+ s->icr[8] = 0x1c;
+ s->icr[9] = 0x80;
+ s->icr[10] = 0x80;
+ s->icr[11] = 0x80;
+ s->icr[12] = 0x00;
+ s->icr[13] = 0x00;
+ s->imr = 0x3ffe;
+ s->rsr = 0x80;
+ s->swivr = 0x0f;
+ s->par = 0;
+}
+
+static uint32_t m5206_mbar_read(m5206_mbar_state *s, uint32_t offset)
+{
+ if (offset >= 0x100 && offset < 0x120) {
+ return m5206_timer_read(s->timer[0], offset - 0x100);
+ } else if (offset >= 0x120 && offset < 0x140) {
+ return m5206_timer_read(s->timer[1], offset - 0x120);
+ } else if (offset >= 0x140 && offset < 0x160) {
+ return m5206_uart_read(s->uart[0], offset - 0x140);
+ } else if (offset >= 0x180 && offset < 0x1a0) {
+ return m5206_uart_read(s->uart[1], offset - 0x180);
+ }
+ switch (offset) {
+ case 0x03: return s->scr;
+ case 0x14 ... 0x20: return s->icr[offset - 0x13];
+ case 0x36: return s->imr;
+ case 0x3a: return s->ipr;
+ case 0x40: return s->rsr;
+ case 0x41: return 0;
+ case 0x42: return s->swivr;
+ case 0x50:
+ /* DRAM mask register. */
+ /* FIXME: currently hardcoded to 128Mb. */
+ {
+ uint32_t mask = ~0;
+ while (mask > ram_size)
+ mask >>= 1;
+ return mask & 0x0ffe0000;
+ }
+ case 0x5c: return 1; /* DRAM bank 1 empty. */
+ case 0xcb: return s->par;
+ case 0x170: return s->uivr[0];
+ case 0x1b0: return s->uivr[1];
+ }
+ cpu_abort(cpu_single_env, "Bad MBAR read offset 0x%x", (int)offset);
+ return 0;
+}
+
+static void m5206_mbar_write(m5206_mbar_state *s, uint32_t offset,
+ uint32_t value)
+{
+ if (offset >= 0x100 && offset < 0x120) {
+ m5206_timer_write(s->timer[0], offset - 0x100, value);
+ return;
+ } else if (offset >= 0x120 && offset < 0x140) {
+ m5206_timer_write(s->timer[1], offset - 0x120, value);
+ return;
+ } else if (offset >= 0x140 && offset < 0x160) {
+ m5206_uart_write(s->uart[0], offset - 0x140, value);
+ return;
+ } else if (offset >= 0x180 && offset < 0x1a0) {
+ m5206_uart_write(s->uart[1], offset - 0x180, value);
+ return;
+ }
+ switch (offset) {
+ case 0x03:
+ s->scr = value;
+ break;
+ case 0x14 ... 0x20:
+ s->icr[offset - 0x13] = value;
+ m5206_mbar_update(s);
+ break;
+ case 0x36:
+ s->imr = value;
+ m5206_mbar_update(s);
+ break;
+ case 0x40:
+ s->rsr &= ~value;
+ break;
+ case 0x41:
+ /* TODO: implement watchdog. */
+ break;
+ case 0x42:
+ s->swivr = value;
+ break;
+ case 0xcb:
+ s->par = value;
+ break;
+ case 0x170:
+ s->uivr[0] = value;
+ break;
+ case 0x178: case 0x17c: case 0x1c8: case 0x1bc:
+ /* Not implemented: UART Output port bits. */
+ break;
+ case 0x1b0:
+ s->uivr[1] = value;
+ break;
+ default:
+ cpu_abort(cpu_single_env, "Bad MBAR write offset 0x%x", (int)offset);
+ break;
+ }
+}
+
+/* Internal peripherals use a variety of register widths.
+ This lookup table allows a single routine to handle all of them. */
+static const int m5206_mbar_width[] =
+{
+ /* 000-040 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2,
+ /* 040-080 */ 1, 2, 2, 2, 4, 1, 2, 4, 1, 2, 4, 2, 2, 4, 2, 2,
+ /* 080-0c0 */ 4, 2, 2, 4, 2, 2, 4, 2, 2, 4, 2, 2, 4, 2, 2, 4,
+ /* 0c0-100 */ 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* 100-140 */ 2, 2, 2, 2, 1, 0, 0, 0, 2, 2, 2, 2, 1, 0, 0, 0,
+ /* 140-180 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ /* 180-1c0 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ /* 1c0-200 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+};
+
+static uint32_t m5206_mbar_readw(void *opaque, target_phys_addr_t offset);
+static uint32_t m5206_mbar_readl(void *opaque, target_phys_addr_t offset);
+
+static uint32_t m5206_mbar_readb(void *opaque, target_phys_addr_t offset)
+{
+ m5206_mbar_state *s = (m5206_mbar_state *)opaque;
+ offset &= 0x3ff;
+ if (offset > 0x200) {
+ cpu_abort(cpu_single_env, "Bad MBAR read offset 0x%x", (int)offset);
+ }
+ if (m5206_mbar_width[offset >> 2] > 1) {
+ uint16_t val;
+ val = m5206_mbar_readw(opaque, offset & ~1);
+ if ((offset & 1) == 0) {
+ val >>= 8;
+ }
+ return val & 0xff;
+ }
+ return m5206_mbar_read(s, offset);
+}
+
+static uint32_t m5206_mbar_readw(void *opaque, target_phys_addr_t offset)
+{
+ m5206_mbar_state *s = (m5206_mbar_state *)opaque;
+ int width;
+ offset &= 0x3ff;
+ if (offset > 0x200) {
+ cpu_abort(cpu_single_env, "Bad MBAR read offset 0x%x", (int)offset);
+ }
+ width = m5206_mbar_width[offset >> 2];
+ if (width > 2) {
+ uint32_t val;
+ val = m5206_mbar_readl(opaque, offset & ~3);
+ if ((offset & 3) == 0)
+ val >>= 16;
+ return val & 0xffff;
+ } else if (width < 2) {
+ uint16_t val;
+ val = m5206_mbar_readb(opaque, offset) << 8;
+ val |= m5206_mbar_readb(opaque, offset + 1);
+ return val;
+ }
+ return m5206_mbar_read(s, offset);
+}
+
+static uint32_t m5206_mbar_readl(void *opaque, target_phys_addr_t offset)
+{
+ m5206_mbar_state *s = (m5206_mbar_state *)opaque;
+ int width;
+ offset &= 0x3ff;
+ if (offset > 0x200) {
+ cpu_abort(cpu_single_env, "Bad MBAR read offset 0x%x", (int)offset);
+ }
+ width = m5206_mbar_width[offset >> 2];
+ if (width < 4) {
+ uint32_t val;
+ val = m5206_mbar_readw(opaque, offset) << 16;
+ val |= m5206_mbar_readw(opaque, offset + 2);
+ return val;
+ }
+ return m5206_mbar_read(s, offset);
+}
+
+static void m5206_mbar_writew(void *opaque, target_phys_addr_t offset,
+ uint32_t value);
+static void m5206_mbar_writel(void *opaque, target_phys_addr_t offset,
+ uint32_t value);
+
+static void m5206_mbar_writeb(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ m5206_mbar_state *s = (m5206_mbar_state *)opaque;
+ int width;
+ offset &= 0x3ff;
+ if (offset > 0x200) {
+ cpu_abort(cpu_single_env, "Bad MBAR write offset 0x%x", (int)offset);
+ }
+ width = m5206_mbar_width[offset >> 2];
+ if (width > 1) {
+ uint32_t tmp;
+ tmp = m5206_mbar_readw(opaque, offset & ~1);
+ if (offset & 1) {
+ tmp = (tmp & 0xff00) | value;
+ } else {
+ tmp = (tmp & 0x00ff) | (value << 8);
+ }
+ m5206_mbar_writew(opaque, offset & ~1, tmp);
+ return;
+ }
+ m5206_mbar_write(s, offset, value);
+}
+
+static void m5206_mbar_writew(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ m5206_mbar_state *s = (m5206_mbar_state *)opaque;
+ int width;
+ offset &= 0x3ff;
+ if (offset > 0x200) {
+ cpu_abort(cpu_single_env, "Bad MBAR write offset 0x%x", (int)offset);
+ }
+ width = m5206_mbar_width[offset >> 2];
+ if (width > 2) {
+ uint32_t tmp;
+ tmp = m5206_mbar_readl(opaque, offset & ~3);
+ if (offset & 3) {
+ tmp = (tmp & 0xffff0000) | value;
+ } else {
+ tmp = (tmp & 0x0000ffff) | (value << 16);
+ }
+ m5206_mbar_writel(opaque, offset & ~3, tmp);
+ return;
+ } else if (width < 2) {
+ m5206_mbar_writeb(opaque, offset, value >> 8);
+ m5206_mbar_writeb(opaque, offset + 1, value & 0xff);
+ return;
+ }
+ m5206_mbar_write(s, offset, value);
+}
+
+static void m5206_mbar_writel(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ m5206_mbar_state *s = (m5206_mbar_state *)opaque;
+ int width;
+ offset &= 0x3ff;
+ if (offset > 0x200) {
+ cpu_abort(cpu_single_env, "Bad MBAR write offset 0x%x", (int)offset);
+ }
+ width = m5206_mbar_width[offset >> 2];
+ if (width < 4) {
+ m5206_mbar_writew(opaque, offset, value >> 16);
+ m5206_mbar_writew(opaque, offset + 2, value & 0xffff);
+ return;
+ }
+ m5206_mbar_write(s, offset, value);
+}
+
+static CPUReadMemoryFunc *m5206_mbar_readfn[] = {
+ m5206_mbar_readb,
+ m5206_mbar_readw,
+ m5206_mbar_readl
+};
+
+static CPUWriteMemoryFunc *m5206_mbar_writefn[] = {
+ m5206_mbar_writeb,
+ m5206_mbar_writew,
+ m5206_mbar_writel
+};
+
+qemu_irq *mcf5206_init(uint32_t base, CPUState *env)
+{
+ m5206_mbar_state *s;
+ qemu_irq *pic;
+ int iomemtype;
+
+ s = (m5206_mbar_state *)qemu_mallocz(sizeof(m5206_mbar_state));
+ iomemtype = cpu_register_io_memory(0, m5206_mbar_readfn,
+ m5206_mbar_writefn, s);
+ cpu_register_physical_memory(base, 0x00000fff, iomemtype);
+
+ pic = qemu_allocate_irqs(m5206_mbar_set_irq, s, 14);
+ s->timer[0] = m5206_timer_init(pic[9]);
+ s->timer[1] = m5206_timer_init(pic[10]);
+ s->uart[0] = m5206_uart_init(pic[12], serial_hds[0]);
+ s->uart[1] = m5206_uart_init(pic[13], serial_hds[1]);
+ s->env = env;
+
+ m5206_mbar_reset(s);
+ return pic;
+}
+
diff --git a/linux-user/main.c b/linux-user/main.c
index ccb8c269d6..0f4dbdadff 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -1977,13 +1977,12 @@ int main(int argc, char **argv)
}
#elif defined(TARGET_M68K)
{
- m68k_def_t *def;
- def = m68k_find_by_name("cfv4e");
- if (def == NULL) {
+ if (cpu_model == NULL)
+ cpu_model = "cfv4e";
+ if (cpu_m68k_set_model(env, cpu_model)) {
cpu_abort(cpu_single_env,
"Unable to find m68k CPU definition\n");
}
- cpu_m68k_register(cpu_single_env, def);
env->pc = regs->pc;
env->dregs[0] = regs->d0;
env->dregs[1] = regs->d1;
diff --git a/softmmu_header.h b/softmmu_header.h
index 8c6cf74adb..317288448d 100644
--- a/softmmu_header.h
+++ b/softmmu_header.h
@@ -65,6 +65,8 @@
#define CPU_MEM_INDEX ((env->sr & SR_MD) == 0)
#elif defined (TARGET_ALPHA)
#define CPU_MEM_INDEX ((env->ps >> 3) & 3)
+#elif defined (TARGET_M68K)
+#define CPU_MEM_INDEX ((env->sr & SR_S) == 0)
#else
#error unsupported CPU
#endif
@@ -86,6 +88,8 @@
#define CPU_MEM_INDEX ((env->sr & SR_MD) == 0)
#elif defined (TARGET_ALPHA)
#define CPU_MEM_INDEX ((env->ps >> 3) & 3)
+#elif defined (TARGET_M68K)
+#define CPU_MEM_INDEX ((env->sr & SR_S) == 0)
#else
#error unsupported CPU
#endif
diff --git a/target-m68k/cpu.h b/target-m68k/cpu.h
index de37baf9a9..c916ad7cd5 100644
--- a/target-m68k/cpu.h
+++ b/target-m68k/cpu.h
@@ -1,7 +1,7 @@
/*
* m68k virtual CPU header
*
- * Copyright (c) 2005-2006 CodeSourcery
+ * Copyright (c) 2005-2007 CodeSourcery
* Written by Paul Brook
*
* This library is free software; you can redistribute it and/or
@@ -50,6 +50,8 @@
#define EXCP_UNSUPPORTED 61
#define EXCP_ICE 13
+#define EXCP_RTE 0x100
+
typedef struct CPUM68KState {
uint32_t dregs[8];
uint32_t aregs[8];
@@ -76,6 +78,12 @@ typedef struct CPUM68KState {
struct {
uint32_t ar;
} mmu;
+
+ /* Control registers. */
+ uint32_t vbr;
+ uint32_t mbar;
+ uint32_t rambar0;
+
/* ??? remove this. */
uint32_t t1;
@@ -84,7 +92,10 @@ typedef struct CPUM68KState {
int exception_index;
int interrupt_request;
int user_mode_only;
- uint32_t address;
+ int halted;
+
+ int pending_vector;
+ int pending_level;
uint32_t qregs[MAX_QREGS];
@@ -94,6 +105,7 @@ typedef struct CPUM68KState {
CPUM68KState *cpu_m68k_init(void);
int cpu_m68k_exec(CPUM68KState *s);
void cpu_m68k_close(CPUM68KState *s);
+void do_interrupt(int is_hw);
/* you can call this signal handler from your SIGBUS and SIGSEGV
signal handlers to inform the virtual CPU of exceptions. non zero
is returned if the signal was handled by the virtual CPU. */
@@ -120,12 +132,19 @@ enum {
#define CCF_V 0x02
#define CCF_Z 0x04
#define CCF_N 0x08
-#define CCF_X 0x01
+#define CCF_X 0x10
+
+#define SR_I_SHIFT 8
+#define SR_I 0x0700
+#define SR_M 0x1000
+#define SR_S 0x2000
+#define SR_T 0x8000
typedef struct m68k_def_t m68k_def_t;
-m68k_def_t *m68k_find_by_name(const char *);
-void cpu_m68k_register(CPUM68KState *, m68k_def_t *);
+int cpu_m68k_set_model(CPUM68KState *env, const char * name);
+
+void m68k_set_irq_level(CPUM68KState *env, int level, uint8_t vector);
#define M68K_FPCR_PREC (1 << 6)
diff --git a/target-m68k/exec.h b/target-m68k/exec.h
index ef4adeb391..254f5581ff 100644
--- a/target-m68k/exec.h
+++ b/target-m68k/exec.h
@@ -40,8 +40,12 @@ static inline void regs_to_env(void)
int cpu_m68k_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
int is_user, int is_softmmu);
+#if !defined(CONFIG_USER_ONLY)
+#include "softmmu_exec.h"
+#endif
void cpu_m68k_flush_flags(CPUM68KState *env, int cc_op);
float64 helper_sub_cmpf64(CPUM68KState *env, float64 src0, float64 src1);
+void helper_movec(CPUM68KState *env, int reg, uint32_t val);
void cpu_loop_exit(void);
diff --git a/target-m68k/helper.c b/target-m68k/helper.c
index 6b8f18d15d..640fe4fb8f 100644
--- a/target-m68k/helper.c
+++ b/target-m68k/helper.c
@@ -1,7 +1,7 @@
/*
* m68k op helpers
*
- * Copyright (c) 2006 CodeSourcery
+ * Copyright (c) 2006-2007 CodeSourcery
* Written by Paul Brook
*
* This library is free software; you can redistribute it and/or
@@ -147,3 +147,65 @@ float64 helper_sub_cmpf64(CPUM68KState *env, float64 src0, float64 src1)
}
return res;
}
+
+void helper_movec(CPUM68KState *env, int reg, uint32_t val)
+{
+ switch (reg) {
+ case 0x02: /* CACR */
+ /* Ignored. */
+ break;
+ case 0x801: /* VBR */
+ env->vbr = val;
+ break;
+ /* TODO: Implement control registers. */
+ default:
+ cpu_abort(env, "Unimplemented control register write 0x%x = 0x%x\n",
+ reg, val);
+ }
+}
+
+/* MMU */
+
+/* TODO: This will need fixing once the MMU is implemented. */
+target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong addr)
+{
+ return addr;
+}
+
+#if defined(CONFIG_USER_ONLY)
+
+int cpu_m68k_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
+ int is_user, int is_softmmu)
+{
+ env->exception_index = EXCP_ACCESS;
+ env->mmu.ar = address;
+ return 1;
+}
+
+#else
+
+int cpu_m68k_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
+ int is_user, int is_softmmu)
+{
+ int prot;
+
+ address &= TARGET_PAGE_MASK;
+ prot = PAGE_READ | PAGE_WRITE;
+ return tlb_set_page(env, address, address, prot, is_user, is_softmmu);
+}
+
+/* Notify CPU of a pending interrupt. Prioritization and vectoring should
+ be handled by the interrupt controller. Real hardware only requests
+ the vector when the interrupt is acknowledged by the CPU. For
+ simplicitly we calculate it when the interrupt is signalled. */
+void m68k_set_irq_level(CPUM68KState *env, int level, uint8_t vector)
+{
+ env->pending_level = level;
+ env->pending_vector = vector;
+ if (level)
+ cpu_interrupt(env, CPU_INTERRUPT_HARD);
+ else
+ cpu_reset_interrupt(env, CPU_INTERRUPT_HARD);
+}
+
+#endif
diff --git a/target-m68k/op-hacks.h b/target-m68k/op-hacks.h
index c7865638ee..01a158d83e 100644
--- a/target-m68k/op-hacks.h
+++ b/target-m68k/op-hacks.h
@@ -27,16 +27,38 @@ static inline int gen_im32(uint32_t i)
return qreg;
}
-static inline void gen_op_ldf32(int dest, int addr)
+static inline void gen_op_ldf32_raw(int dest, int addr)
{
- gen_op_ld32(dest, addr);
+ gen_op_ld32_raw(dest, addr);
}
-static inline void gen_op_stf32(int addr, int dest)
+static inline void gen_op_stf32_raw(int addr, int dest)
{
- gen_op_st32(addr, dest);
+ gen_op_st32_raw(addr, dest);
}
+#if !defined(CONFIG_USER_ONLY)
+static inline void gen_op_ldf32_user(int dest, int addr)
+{
+ gen_op_ld32_user(dest, addr);
+}
+
+static inline void gen_op_stf32_user(int addr, int dest)
+{
+ gen_op_st32_user(addr, dest);
+}
+
+static inline void gen_op_ldf32_kernel(int dest, int addr)
+{
+ gen_op_ld32_kernel(dest, addr);
+}
+
+static inline void gen_op_stf32_kernel(int addr, int dest)
+{
+ gen_op_st32_kernel(addr, dest);
+}
+#endif
+
static inline void gen_op_pack_32_f32(int dest, int src)
{
gen_op_mov32(dest, src);
diff --git a/target-m68k/op.c b/target-m68k/op.c
index 34878c0215..ad6f403f5c 100644
--- a/target-m68k/op.c
+++ b/target-m68k/op.c
@@ -1,7 +1,7 @@
/*
* m68k micro operations
*
- * Copyright (c) 2006 CodeSourcery
+ * Copyright (c) 2006-2007 CodeSourcery
* Written by Paul Brook
*
* This library is free software; you can redistribute it and/or
@@ -86,7 +86,7 @@ void set_opf64(int qreg, float64 val)
}
}
-#define OP(name) void OPPROTO op_##name (void)
+#define OP(name) void OPPROTO glue(op_,name) (void)
OP(mov32)
{
@@ -316,77 +316,6 @@ OP(ext16s32)
FORCE_RET();
}
-/* Load/store ops. */
-OP(ld8u32)
-{
- uint32_t addr = get_op(PARAM2);
- set_op(PARAM1, ldub(addr));
- FORCE_RET();
-}
-
-OP(ld8s32)
-{
- uint32_t addr = get_op(PARAM2);
- set_op(PARAM1, ldsb(addr));
- FORCE_RET();
-}
-
-OP(ld16u32)
-{
- uint32_t addr = get_op(PARAM2);
- set_op(PARAM1, lduw(addr));
- FORCE_RET();
-}
-
-OP(ld16s32)
-{
- uint32_t addr = get_op(PARAM2);
- set_op(PARAM1, ldsw(addr));
- FORCE_RET();
-}
-
-OP(ld32)
-{
- uint32_t addr = get_op(PARAM2);
- set_op(PARAM1, ldl(addr));
- FORCE_RET();
-}
-
-OP(st8)
-{
- uint32_t addr = get_op(PARAM1);
- stb(addr, get_op(PARAM2));
- FORCE_RET();
-}
-
-OP(st16)
-{
- uint32_t addr = get_op(PARAM1);
- stw(addr, get_op(PARAM2));
- FORCE_RET();
-}
-
-OP(st32)
-{
- uint32_t addr = get_op(PARAM1);
- stl(addr, get_op(PARAM2));
- FORCE_RET();
-}
-
-OP(ldf64)
-{
- uint32_t addr = get_op(PARAM2);
- set_opf64(PARAM1, ldfq(addr));
- FORCE_RET();
-}
-
-OP(stf64)
-{
- uint32_t addr = get_op(PARAM1);
- stfq(addr, get_opf64(PARAM2));
- FORCE_RET();
-}
-
OP(flush_flags)
{
int cc_op = PARAM1;
@@ -454,6 +383,13 @@ OP(divs)
FORCE_RET();
}
+OP(halt)
+{
+ env->halted = 1;
+ RAISE_EXCEPTION(EXCP_HLT);
+ FORCE_RET();
+}
+
OP(raise_exception)
{
RAISE_EXCEPTION(PARAM1);
@@ -679,3 +615,22 @@ OP(compare_quietf64)
set_op(PARAM1, float64_compare_quiet(op0, op1, &CPU_FP_STATUS));
FORCE_RET();
}
+
+OP(movec)
+{
+ int op1 = get_op(PARAM1);
+ uint32_t op2 = get_op(PARAM2);
+ helper_movec(env, op1, op2);
+}
+
+/* Memory access. */
+
+#define MEMSUFFIX _raw
+#include "op_mem.h"
+
+#if !defined(CONFIG_USER_ONLY)
+#define MEMSUFFIX _user
+#include "op_mem.h"
+#define MEMSUFFIX _kernel
+#include "op_mem.h"
+#endif
diff --git a/target-m68k/op_helper.c b/target-m68k/op_helper.c
new file mode 100644
index 0000000000..7455e31659
--- /dev/null
+++ b/target-m68k/op_helper.c
@@ -0,0 +1,140 @@
+/*
+ * M68K helper routines
+ *
+ * Copyright (c) 2007 CodeSourcery
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "exec.h"
+
+#if defined(CONFIG_USER_ONLY)
+
+void do_interrupt(int is_hw)
+{
+ env->exception_index = -1;
+}
+
+#else
+
+#define MMUSUFFIX _mmu
+#define GETPC() (__builtin_return_address(0))
+
+#define SHIFT 0
+#include "softmmu_template.h"
+
+#define SHIFT 1
+#include "softmmu_template.h"
+
+#define SHIFT 2
+#include "softmmu_template.h"
+
+#define SHIFT 3
+#include "softmmu_template.h"
+
+/* Try to fill the TLB and return an exception if error. If retaddr is
+ NULL, it means that the function was called in C code (i.e. not
+ from generated code or from helper.c) */
+/* XXX: fix it to restore all registers */
+void tlb_fill (target_ulong addr, int is_write, int is_user, void *retaddr)
+{
+ TranslationBlock *tb;
+ CPUState *saved_env;
+ target_phys_addr_t pc;
+ int ret;
+
+ /* XXX: hack to restore env in all cases, even if not called from
+ generated code */
+ saved_env = env;
+ env = cpu_single_env;
+ ret = cpu_m68k_handle_mmu_fault(env, addr, is_write, is_user, 1);
+ if (__builtin_expect(ret, 0)) {
+ if (retaddr) {
+ /* now we have a real cpu fault */
+ pc = (target_phys_addr_t)retaddr;
+ tb = tb_find_pc(pc);
+ if (tb) {
+ /* the PC is inside the translated code. It means that we have
+ a virtual CPU fault */
+ cpu_restore_state(tb, env, pc, NULL);
+ }
+ }
+ cpu_loop_exit();
+ }
+ env = saved_env;
+}
+
+static void do_rte(void)
+{
+ uint32_t sp;
+ uint32_t fmt;
+
+ sp = env->aregs[7];
+ fmt = ldl_kernel(sp);
+ env->pc = ldl_kernel(sp + 4);
+ sp |= (fmt >> 28) & 3;
+ env->sr = fmt & 0xffff;
+ env->aregs[7] = sp + 8;
+}
+
+void do_interrupt(int is_hw)
+{
+ uint32_t sp;
+ uint32_t fmt;
+ uint32_t retaddr;
+ uint32_t vector;
+
+ fmt = 0;
+ retaddr = env->pc;
+
+ if (!is_hw) {
+ switch (env->exception_index) {
+ case EXCP_RTE:
+ /* Return from an exception. */
+ do_rte();
+ return;
+ }
+ if (env->exception_index >= EXCP_TRAP0
+ && env->exception_index <= EXCP_TRAP15) {
+ /* Move the PC after the trap instruction. */
+ retaddr += 2;
+ }
+ }
+
+ /* TODO: Implement USP. */
+ sp = env->aregs[7];
+
+ vector = env->exception_index << 2;
+
+ fmt |= 0x40000000;
+ fmt |= (sp & 3) << 28;
+ fmt |= vector << 16;
+ fmt |= env->sr;
+
+ /* ??? This could cause MMU faults. */
+ sp &= ~3;
+ sp -= 4;
+ stl_kernel(sp, retaddr);
+ sp -= 4;
+ stl_kernel(sp, fmt);
+ env->aregs[7] = sp;
+ env->sr |= SR_S;
+ if (is_hw) {
+ env->sr = (env->sr & ~SR_I) | (env->pending_level << SR_I_SHIFT);
+ }
+ /* Jump to vector. */
+ env->pc = ldl_kernel(env->vbr + vector);
+}
+
+#endif
diff --git a/target-m68k/op_mem.h b/target-m68k/op_mem.h
new file mode 100644
index 0000000000..556829f691
--- /dev/null
+++ b/target-m68k/op_mem.h
@@ -0,0 +1,46 @@
+/* Load/store ops. */
+#define MEM_LD_OP(name,suffix) \
+OP(glue(glue(ld,name),MEMSUFFIX)) \
+{ \
+ uint32_t addr = get_op(PARAM2); \
+ set_op(PARAM1, glue(glue(ld,suffix),MEMSUFFIX)(addr)); \
+ FORCE_RET(); \
+}
+
+MEM_LD_OP(8u32,ub)
+MEM_LD_OP(8s32,sb)
+MEM_LD_OP(16u32,uw)
+MEM_LD_OP(16s32,sw)
+MEM_LD_OP(32,l)
+
+#undef MEM_LD_OP
+
+#define MEM_ST_OP(name,suffix) \
+OP(glue(glue(st,name),MEMSUFFIX)) \
+{ \
+ uint32_t addr = get_op(PARAM1); \
+ glue(glue(st,suffix),MEMSUFFIX)(addr, get_op(PARAM2)); \
+ FORCE_RET(); \
+}
+
+MEM_ST_OP(8,b)
+MEM_ST_OP(16,w)
+MEM_ST_OP(32,l)
+
+#undef MEM_ST_OP
+
+OP(glue(ldf64,MEMSUFFIX))
+{
+ uint32_t addr = get_op(PARAM2);
+ set_opf64(PARAM1, glue(ldfq,MEMSUFFIX)(addr));
+ FORCE_RET();
+}
+
+OP(glue(stf64,MEMSUFFIX))
+{
+ uint32_t addr = get_op(PARAM1);
+ glue(stfq,MEMSUFFIX)(addr, get_opf64(PARAM2));
+ FORCE_RET();
+}
+
+#undef MEMSUFFIX
diff --git a/target-m68k/qregs.def b/target-m68k/qregs.def
index d8c1944082..08e7e85156 100644
--- a/target-m68k/qregs.def
+++ b/target-m68k/qregs.def
@@ -24,6 +24,7 @@ DEFF64(F6, fregs[6])
DEFF64(F7, fregs[7])
DEFF64(FP_RESULT, fp_result)
DEFO32(PC, pc)
+DEFO32(SR, sr)
DEFO32(CC_OP, cc_op)
DEFR(T0, AREG1, QMODE_I32)
DEFO32(CC_DEST, cc_dest)
diff --git a/target-m68k/translate.c b/target-m68k/translate.c
index 3b25635820..eff32867b8 100644
--- a/target-m68k/translate.c
+++ b/target-m68k/translate.c
@@ -1,7 +1,7 @@
/*
* m68k translation
*
- * Copyright (c) 2005-2006 CodeSourcery
+ * Copyright (c) 2005-2007 CodeSourcery
* Written by Paul Brook
*
* This library is free software; you can redistribute it and/or
@@ -30,6 +30,8 @@
#include "disas.h"
#include "m68k-qreg.h"
+//#define DEBUG_DISPATCH 1
+
static inline void qemu_assert(int cond, const char *msg)
{
if (!cond) {
@@ -43,6 +45,7 @@ typedef struct DisasContext {
target_ulong pc;
int is_jmp;
int cc_op;
+ int user;
uint32_t fpcr;
struct TranslationBlock *tb;
int singlestep_enabled;
@@ -50,6 +53,12 @@ typedef struct DisasContext {
#define DISAS_JUMP_NEXT 4
+#if defined(CONFIG_USER_ONLY)
+#define IS_USER(s) 1
+#else
+#define IS_USER(s) s->user
+#endif
+
/* XXX: move that elsewhere */
/* ??? Fix exceptions. */
static void *gen_throws_exception;
@@ -68,6 +77,25 @@ enum {
};
#include "gen-op.h"
+
+#if defined(CONFIG_USER_ONLY)
+#define gen_st(s, name, addr, val) gen_op_st##name##_raw(addr, val)
+#define gen_ld(s, name, val, addr) gen_op_ld##name##_raw(val, addr)
+#else
+#define gen_st(s, name, addr, val) do { \
+ if (IS_USER(s)) \
+ gen_op_st##name##_user(addr, val); \
+ else \
+ gen_op_st##name##_kernel(addr, val); \
+ } while (0)
+#define gen_ld(s, name, val, addr) do { \
+ if (IS_USER(s)) \
+ gen_op_ld##name##_user(val, addr); \
+ else \
+ gen_op_ld##name##_kernel(val, addr); \
+ } while (0)
+#endif
+
#include "op-hacks.h"
#define OS_BYTE 0
@@ -101,40 +129,49 @@ static m68k_def_t m68k_cpu_defs[] = {
typedef void (*disas_proc)(DisasContext *, uint16_t);
+#ifdef DEBUG_DISPATCH
+#define DISAS_INSN(name) \
+ static void real_disas_##name (DisasContext *s, uint16_t insn); \
+ static void disas_##name (DisasContext *s, uint16_t insn) { \
+ if (logfile) fprintf(logfile, "Dispatch " #name "\n"); \
+ real_disas_##name(s, insn); } \
+ static void real_disas_##name (DisasContext *s, uint16_t insn)
+#else
#define DISAS_INSN(name) \
static void disas_##name (DisasContext *s, uint16_t insn)
+#endif
/* Generate a load from the specified address. Narrow values are
sign extended to full register width. */
-static inline int gen_load(int opsize, int addr, int sign)
+static inline int gen_load(DisasContext * s, int opsize, int addr, int sign)
{
int tmp;
switch(opsize) {
case OS_BYTE:
tmp = gen_new_qreg(QMODE_I32);
if (sign)
- gen_op_ld8s32(tmp, addr);
+ gen_ld(s, 8s32, tmp, addr);
else
- gen_op_ld8u32(tmp, addr);
+ gen_ld(s, 8u32, tmp, addr);
break;
case OS_WORD:
tmp = gen_new_qreg(QMODE_I32);
if (sign)
- gen_op_ld16s32(tmp, addr);
+ gen_ld(s, 16s32, tmp, addr);
else
- gen_op_ld16u32(tmp, addr);
+ gen_ld(s, 16u32, tmp, addr);
break;
case OS_LONG:
tmp = gen_new_qreg(QMODE_I32);
- gen_op_ld32(tmp, addr);
+ gen_ld(s, 32, tmp, addr);
break;
case OS_SINGLE:
tmp = gen_new_qreg(QMODE_F32);
- gen_op_ldf32(tmp, addr);
+ gen_ld(s, f32, tmp, addr);
break;
case OS_DOUBLE:
tmp = gen_new_qreg(QMODE_F64);
- gen_op_ldf64(tmp, addr);
+ gen_ld(s, f64, tmp, addr);
break;
default:
qemu_assert(0, "bad load size");
@@ -144,23 +181,23 @@ static inline int gen_load(int opsize, int addr, int sign)
}
/* Generate a store. */
-static inline void gen_store(int opsize, int addr, int val)
+static inline void gen_store(DisasContext *s, int opsize, int addr, int val)
{
switch(opsize) {
case OS_BYTE:
- gen_op_st8(addr, val);
+ gen_st(s, 8, addr, val);
break;
case OS_WORD:
- gen_op_st16(addr, val);
+ gen_st(s, 16, addr, val);
break;
case OS_LONG:
- gen_op_st32(addr, val);
+ gen_st(s, 32, addr, val);
break;
case OS_SINGLE:
- gen_op_stf32(addr, val);
+ gen_st(s, f32, addr, val);
break;
case OS_DOUBLE:
- gen_op_stf64(addr, val);
+ gen_st(s, f64, addr, val);
break;
default:
qemu_assert(0, "bad store size");
@@ -170,13 +207,13 @@ static inline void gen_store(int opsize, int addr, int val)
/* Generate an unsigned load if VAL is 0 a signed load if val is -1,
otherwise generate a store. */
-static int gen_ldst(int opsize, int addr, int val)
+static int gen_ldst(DisasContext *s, int opsize, int addr, int val)
{
if (val > 0) {
- gen_store(opsize, addr, val);
+ gen_store(s, opsize, addr, val);
return 0;
} else {
- return gen_load(opsize, addr, val != 0);
+ return gen_load(s, opsize, addr, val != 0);
}
}
@@ -191,7 +228,7 @@ static int gen_lea_indexed(DisasContext *s, int opsize, int base)
int tmp;
offset = s->pc;
- ext = lduw(s->pc);
+ ext = lduw_code(s->pc);
s->pc += 2;
tmp = ((ext >> 12) & 7) + ((ext & 0x8000) ? QREG_A0 : QREG_D0);
/* ??? Check W/L bit. */
@@ -216,9 +253,9 @@ static int gen_lea_indexed(DisasContext *s, int opsize, int base)
static inline uint32_t read_im32(DisasContext *s)
{
uint32_t im;
- im = ((uint32_t)lduw(s->pc)) << 16;
+ im = ((uint32_t)lduw_code(s->pc)) << 16;
s->pc += 2;
- im |= lduw(s->pc);
+ im |= lduw_code(s->pc);
s->pc += 2;
return im;
}
@@ -343,7 +380,7 @@ static int gen_lea(DisasContext *s, uint16_t insn, int opsize)
case 5: /* Indirect displacement. */
reg += QREG_A0;
tmp = gen_new_qreg(QMODE_I32);
- ext = lduw(s->pc);
+ ext = lduw_code(s->pc);
s->pc += 2;
gen_op_add32(tmp, reg, gen_im32((int16_t)ext));
return tmp;
@@ -353,7 +390,7 @@ static int gen_lea(DisasContext *s, uint16_t insn, int opsize)
case 7: /* Other */
switch (reg) {
case 0: /* Absolute short. */
- offset = ldsw(s->pc);
+ offset = ldsw_code(s->pc);
s->pc += 2;
return gen_im32(offset);
case 1: /* Absolute long. */
@@ -362,7 +399,7 @@ static int gen_lea(DisasContext *s, uint16_t insn, int opsize)
case 2: /* pc displacement */
tmp = gen_new_qreg(QMODE_I32);
offset = s->pc;
- offset += ldsw(s->pc);
+ offset += ldsw_code(s->pc);
s->pc += 2;
return gen_im32(offset);
case 3: /* pc index+displacement. */
@@ -391,7 +428,7 @@ static inline int gen_ea_once(DisasContext *s, uint16_t insn, int opsize,
if (addrp)
*addrp = tmp;
}
- return gen_ldst(opsize, tmp, val);
+ return gen_ldst(s, opsize, tmp, val);
}
/* Generate code to load/store a value ito/from an EA. If VAL > 0 this is
@@ -424,10 +461,10 @@ static int gen_ea(DisasContext *s, uint16_t insn, int opsize, int val,
}
case 2: /* Indirect register */
reg += QREG_A0;
- return gen_ldst(opsize, reg, val);
+ return gen_ldst(s, opsize, reg, val);
case 3: /* Indirect postincrement. */
reg += QREG_A0;
- result = gen_ldst(opsize, reg, val);
+ result = gen_ldst(s, opsize, reg, val);
/* ??? This is not exception safe. The instruction may still
fault after this point. */
if (val > 0 || !addrp)
@@ -443,7 +480,7 @@ static int gen_ea(DisasContext *s, uint16_t insn, int opsize, int val,
if (addrp)
*addrp = tmp;
}
- result = gen_ldst(opsize, tmp, val);
+ result = gen_ldst(s, opsize, tmp, val);
/* ??? This is not exception safe. The instruction may still
fault after this point. */
if (val > 0 || !addrp) {
@@ -467,16 +504,16 @@ static int gen_ea(DisasContext *s, uint16_t insn, int opsize, int val,
switch (opsize) {
case OS_BYTE:
if (val)
- offset = ldsb(s->pc + 1);
+ offset = ldsb_code(s->pc + 1);
else
- offset = ldub(s->pc + 1);
+ offset = ldub_code(s->pc + 1);
s->pc += 2;
break;
case OS_WORD:
if (val)
- offset = ldsw(s->pc);
+ offset = ldsw_code(s->pc);
else
- offset = lduw(s->pc);
+ offset = lduw_code(s->pc);
s->pc += 2;
break;
case OS_LONG:
@@ -622,6 +659,14 @@ DISAS_INSN(scc)
gen_set_label(l1);
}
+/* Force a TB lookup after an instruction that changes the CPU state. */
+static void gen_lookup_tb(DisasContext *s)
+{
+ gen_flush_cc_op(s);
+ gen_op_mov32(QREG_PC, gen_im32(s->pc));
+ s->is_jmp = DISAS_UPDATE;
+}
+
/* Generate a jump to to the address in qreg DEST. */
static void gen_jmp(DisasContext *s, int dest)
{
@@ -735,7 +780,7 @@ DISAS_INSN(divl)
int reg;
uint16_t ext;
- ext = lduw(s->pc);
+ ext = lduw_code(s->pc);
s->pc += 2;
if (ext & 0x87f8) {
gen_exception(s, s->pc - 4, EXCP_UNSUPPORTED);
@@ -903,13 +948,13 @@ DISAS_INSN(sats)
gen_logic_cc(s, tmp);
}
-static void gen_push(int val)
+static void gen_push(DisasContext *s, int val)
{
int tmp;
tmp = gen_new_qreg(QMODE_I32);
gen_op_sub32(tmp, QREG_SP, gen_im32(4));
- gen_store(OS_LONG, tmp, val);
+ gen_store(s, OS_LONG, tmp, val);
gen_op_mov32(QREG_SP, tmp);
}
@@ -922,7 +967,7 @@ DISAS_INSN(movem)
int tmp;
int is_load;
- mask = lduw(s->pc);
+ mask = lduw_code(s->pc);
s->pc += 2;
tmp = gen_lea(s, insn, OS_LONG);
addr = gen_new_qreg(QMODE_I32);
@@ -935,10 +980,10 @@ DISAS_INSN(movem)
else
reg = AREG(i, 0);
if (is_load) {
- tmp = gen_load(OS_LONG, addr, 0);
+ tmp = gen_load(s, OS_LONG, addr, 0);
gen_op_mov32(reg, tmp);
} else {
- gen_store(OS_LONG, addr, reg);
+ gen_store(s, OS_LONG, addr, reg);
}
if (mask != 1)
gen_op_add32(addr, addr, gen_im32(4));
@@ -963,7 +1008,7 @@ DISAS_INSN(bitop_im)
opsize = OS_LONG;
op = (insn >> 6) & 3;
- bitnum = lduw(s->pc);
+ bitnum = lduw_code(s->pc);
s->pc += 2;
if (bitnum & 0xff00) {
disas_undef(s, insn);
@@ -1155,9 +1200,8 @@ DISAS_INSN(clr)
gen_logic_cc(s, gen_im32(0));
}
-DISAS_INSN(move_from_ccr)
+static int gen_get_ccr(DisasContext *s)
{
- int reg;
int dest;
gen_flush_flags(s);
@@ -1165,8 +1209,17 @@ DISAS_INSN(move_from_ccr)
gen_op_get_xflag(dest);
gen_op_shl32(dest, dest, gen_im32(4));
gen_op_or32(dest, dest, QREG_CC_DEST);
+ return dest;
+}
+
+DISAS_INSN(move_from_ccr)
+{
+ int reg;
+ int ccr;
+
+ ccr = gen_get_ccr(s);
reg = DREG(insn, 0);
- gen_partset_reg(OS_WORD, reg, dest);
+ gen_partset_reg(OS_WORD, reg, ccr);
}
DISAS_INSN(neg)
@@ -1184,7 +1237,16 @@ DISAS_INSN(neg)
s->cc_op = CC_OP_SUB;
}
-DISAS_INSN(move_to_ccr)
+static void gen_set_sr_im(DisasContext *s, uint16_t val, int ccr_only)
+{
+ gen_op_logic_cc(gen_im32(val & 0xf));
+ gen_op_update_xflag_tst(gen_im32((val & 0x10) >> 4));
+ if (!ccr_only) {
+ gen_op_mov32(QREG_SR, gen_im32(val & 0xff00));
+ }
+}
+
+static void gen_set_sr(DisasContext *s, uint16_t insn, int ccr_only)
{
int src1;
int reg;
@@ -1199,19 +1261,26 @@ DISAS_INSN(move_to_ccr)
gen_op_shr32(src1, reg, gen_im32(4));
gen_op_and32(src1, src1, gen_im32(1));
gen_op_update_xflag_tst(src1);
+ if (!ccr_only) {
+ gen_op_and32(QREG_SR, reg, gen_im32(0xff00));
+ }
}
- else if ((insn & 0x3f) != 0x3c)
+ else if ((insn & 0x3f) == 0x3c)
{
- uint8_t val;
- val = ldsb(s->pc);
+ uint16_t val;
+ val = lduw_code(s->pc);
s->pc += 2;
- gen_op_logic_cc(gen_im32(val & 0xf));
- gen_op_update_xflag_tst(gen_im32((val & 0x10) >> 4));
+ gen_set_sr_im(s, val, ccr_only);
}
else
disas_undef(s, insn);
}
+DISAS_INSN(move_to_ccr)
+{
+ gen_set_sr(s, insn, 1);
+}
+
DISAS_INSN(not)
{
int reg;
@@ -1244,7 +1313,7 @@ DISAS_INSN(pea)
int tmp;
tmp = gen_lea(s, insn, OS_LONG);
- gen_push(tmp);
+ gen_push(s, tmp);
}
DISAS_INSN(ext)
@@ -1322,7 +1391,7 @@ DISAS_INSN(mull)
/* The upper 32 bits of the product are discarded, so
muls.l and mulu.l are functionally equivalent. */
- ext = lduw(s->pc);
+ ext = lduw_code(s->pc);
s->pc += 2;
if (ext & 0x87ff) {
gen_exception(s, s->pc - 4, EXCP_UNSUPPORTED);
@@ -1343,12 +1412,12 @@ DISAS_INSN(link)
int reg;
int tmp;
- offset = ldsw(s->pc);
+ offset = ldsw_code(s->pc);
s->pc += 2;
reg = AREG(insn, 0);
tmp = gen_new_qreg(QMODE_I32);
gen_op_sub32(tmp, QREG_SP, gen_im32(4));
- gen_store(OS_LONG, tmp, reg);
+ gen_store(s, OS_LONG, tmp, reg);
if (reg != QREG_SP)
gen_op_mov32(reg, tmp);
gen_op_add32(QREG_SP, tmp, gen_im32(offset));
@@ -1363,7 +1432,7 @@ DISAS_INSN(unlk)
src = gen_new_qreg(QMODE_I32);
reg = AREG(insn, 0);
gen_op_mov32(src, reg);
- tmp = gen_load(OS_LONG, src, 0);
+ tmp = gen_load(s, OS_LONG, src, 0);
gen_op_mov32(reg, tmp);
gen_op_add32(QREG_SP, src, gen_im32(4));
}
@@ -1376,7 +1445,7 @@ DISAS_INSN(rts)
{
int tmp;
- tmp = gen_load(OS_LONG, QREG_SP, 0);
+ tmp = gen_load(s, OS_LONG, QREG_SP, 0);
gen_op_add32(QREG_SP, QREG_SP, gen_im32(4));
gen_jmp(s, tmp);
}
@@ -1390,7 +1459,7 @@ DISAS_INSN(jump)
tmp = gen_lea(s, insn, OS_LONG);
if ((insn & 0x40) == 0) {
/* jsr */
- gen_push(gen_im32(s->pc));
+ gen_push(s, gen_im32(s->pc));
}
gen_jmp(s, tmp);
}
@@ -1460,14 +1529,14 @@ DISAS_INSN(branch)
op = (insn >> 8) & 0xf;
offset = (int8_t)insn;
if (offset == 0) {
- offset = ldsw(s->pc);
+ offset = ldsw_code(s->pc);
s->pc += 2;
} else if (offset == -1) {
offset = read_im32(s);
}
if (op == 1) {
/* bsr */
- gen_push(gen_im32(s->pc));
+ gen_push(s, gen_im32(s->pc));
}
gen_flush_cc_op(s);
if (op > 1) {
@@ -1752,68 +1821,154 @@ DISAS_INSN(ff1)
cpu_abort(NULL, "Unimplemented insn: ff1");
}
+static int gen_get_sr(DisasContext *s)
+{
+ int ccr;
+ int sr;
+
+ ccr = gen_get_ccr(s);
+ sr = gen_new_qreg(QMODE_I32);
+ gen_op_and32(sr, QREG_SR, gen_im32(0xffe0));
+ gen_op_or32(sr, sr, ccr);
+ return sr;
+}
+
DISAS_INSN(strldsr)
{
uint16_t ext;
uint32_t addr;
addr = s->pc - 2;
- ext = lduw(s->pc);
+ ext = lduw_code(s->pc);
s->pc += 2;
- if (ext != 0x46FC)
+ if (ext != 0x46FC) {
gen_exception(s, addr, EXCP_UNSUPPORTED);
- else
+ return;
+ }
+ ext = lduw_code(s->pc);
+ s->pc += 2;
+ if (IS_USER(s) || (ext & SR_S) == 0) {
gen_exception(s, addr, EXCP_PRIVILEGE);
+ return;
+ }
+ gen_push(s, gen_get_sr(s));
+ gen_set_sr_im(s, ext, 0);
}
DISAS_INSN(move_from_sr)
{
- gen_exception(s, s->pc - 2, EXCP_PRIVILEGE);
+ int reg;
+ int sr;
+
+ if (IS_USER(s)) {
+ gen_exception(s, s->pc - 2, EXCP_PRIVILEGE);
+ return;
+ }
+ sr = gen_get_sr(s);
+ reg = DREG(insn, 0);
+ gen_partset_reg(OS_WORD, reg, sr);
}
DISAS_INSN(move_to_sr)
{
- gen_exception(s, s->pc - 2, EXCP_PRIVILEGE);
+ if (IS_USER(s)) {
+ gen_exception(s, s->pc - 2, EXCP_PRIVILEGE);
+ return;
+ }
+ gen_set_sr(s, insn, 0);
+ gen_lookup_tb(s);
}
DISAS_INSN(move_from_usp)
{
- gen_exception(s, s->pc - 2, EXCP_PRIVILEGE);
+ if (IS_USER(s)) {
+ gen_exception(s, s->pc - 2, EXCP_PRIVILEGE);
+ return;
+ }
+ /* TODO: Implement USP. */
+ gen_exception(s, s->pc - 2, EXCP_ILLEGAL);
}
DISAS_INSN(move_to_usp)
{
- gen_exception(s, s->pc - 2, EXCP_PRIVILEGE);
+ if (IS_USER(s)) {
+ gen_exception(s, s->pc - 2, EXCP_PRIVILEGE);
+ return;
+ }
+ /* TODO: Implement USP. */
+ gen_exception(s, s->pc - 2, EXCP_ILLEGAL);
}
DISAS_INSN(halt)
{
- gen_exception(s, s->pc, EXCP_HLT);
+ gen_flush_cc_op(s);
+ gen_jmp(s, gen_im32(s->pc));
+ gen_op_halt();
}
DISAS_INSN(stop)
{
- gen_exception(s, s->pc - 2, EXCP_PRIVILEGE);
+ uint16_t ext;
+
+ if (IS_USER(s)) {
+ gen_exception(s, s->pc - 2, EXCP_PRIVILEGE);
+ return;
+ }
+
+ ext = lduw_code(s->pc);
+ s->pc += 2;
+
+ gen_set_sr_im(s, ext, 0);
+ disas_halt(s, insn);
}
DISAS_INSN(rte)
{
- gen_exception(s, s->pc - 2, EXCP_PRIVILEGE);
+ if (IS_USER(s)) {
+ gen_exception(s, s->pc - 2, EXCP_PRIVILEGE);
+ return;
+ }
+ gen_exception(s, s->pc - 2, EXCP_RTE);
}
DISAS_INSN(movec)
{
- gen_exception(s, s->pc - 2, EXCP_PRIVILEGE);
+ uint16_t ext;
+ int reg;
+
+ if (IS_USER(s)) {
+ gen_exception(s, s->pc - 2, EXCP_PRIVILEGE);
+ return;
+ }
+
+ ext = lduw_code(s->pc);
+ s->pc += 2;
+
+ if (ext & 0x8000) {
+ reg = AREG(ext, 12);
+ } else {
+ reg = DREG(ext, 12);
+ }
+ gen_op_movec(gen_im32(ext & 0xfff), reg);
+ gen_lookup_tb(s);
}
DISAS_INSN(intouch)
{
- gen_exception(s, s->pc - 2, EXCP_PRIVILEGE);
+ if (IS_USER(s)) {
+ gen_exception(s, s->pc - 2, EXCP_PRIVILEGE);
+ return;
+ }
+ /* ICache fetch. Implement as no-op. */
}
DISAS_INSN(cpushl)
{
- gen_exception(s, s->pc - 2, EXCP_PRIVILEGE);
+ if (IS_USER(s)) {
+ gen_exception(s, s->pc - 2, EXCP_PRIVILEGE);
+ return;
+ }
+ /* Cache push/invalidate. Implement as no-op. */
}
DISAS_INSN(wddata)
@@ -1823,7 +1978,12 @@ DISAS_INSN(wddata)
DISAS_INSN(wdebug)
{
- gen_exception(s, s->pc - 2, EXCP_PRIVILEGE);
+ if (IS_USER(s)) {
+ gen_exception(s, s->pc - 2, EXCP_PRIVILEGE);
+ return;
+ }
+ /* TODO: Implement wdebug. */
+ qemu_assert(0, "WDEBUG not implemented");
}
DISAS_INSN(trap)
@@ -1843,7 +2003,7 @@ DISAS_INSN(fpu)
int round;
int opsize;
- ext = lduw(s->pc);
+ ext = lduw_code(s->pc);
s->pc += 2;
opmode = ext & 0x7f;
switch ((ext >> 13) & 7) {
@@ -1928,10 +2088,10 @@ DISAS_INSN(fpu)
if (ext & mask) {
if (ext & (1 << 13)) {
/* store */
- gen_op_stf64(addr, dest);
+ gen_st(s, f64, addr, dest);
} else {
/* load */
- gen_op_ldf64(dest, addr);
+ gen_ld(s, f64, dest, addr);
}
if (ext & (mask - 1))
gen_op_add32(addr, addr, gen_im32(8));
@@ -2060,10 +2220,10 @@ DISAS_INSN(fbcc)
int l1;
addr = s->pc;
- offset = ldsw(s->pc);
+ offset = ldsw_code(s->pc);
s->pc += 2;
if (insn & (1 << 6)) {
- offset = (offset << 16) | lduw(s->pc);
+ offset = (offset << 16) | lduw_code(s->pc);
s->pc += 2;
}
@@ -2143,6 +2303,18 @@ DISAS_INSN(fbcc)
gen_jmp_tb(s, 1, addr + offset);
}
+DISAS_INSN(frestore)
+{
+ /* TODO: Implement frestore. */
+ qemu_assert(0, "FRESTORE not implemented");
+}
+
+DISAS_INSN(fsave)
+{
+ /* TODO: Implement fsave. */
+ qemu_assert(0, "FSAVE not implemented");
+}
+
static disas_proc opcode_table[65536];
static void
@@ -2168,11 +2340,10 @@ register_opcode (disas_proc proc, uint16_t opcode, uint16_t mask)
i <<= 1;
from = opcode & ~(i - 1);
to = from + i;
- for (i = from; i < to; i++)
- {
+ for (i = from; i < to; i++) {
if ((i & mask) == opcode)
opcode_table[i] = proc;
- }
+ }
}
/* Register m68k opcode handlers. Order is important.
@@ -2274,6 +2445,8 @@ register_m68k_insns (m68k_def_t *def)
INSN(undef_fpu, f000, f000, CF_A);
INSN(fpu, f200, ffc0, CF_FPU);
INSN(fbcc, f280, ffc0, CF_FPU);
+ INSN(frestore, f340, ffc0, CF_FPU);
+ INSN(fsave, f340, ffc0, CF_FPU);
INSN(intouch, f340, ffc0, CF_A);
INSN(cpushl, f428, ff38, CF_A);
INSN(wddata, fb00, ff00, CF_A);
@@ -2287,7 +2460,7 @@ static void disas_m68k_insn(CPUState * env, DisasContext *s)
{
uint16_t insn;
- insn = lduw(s->pc);
+ insn = lduw_code(s->pc);
s->pc += 2;
opcode_table[insn](s, insn);
@@ -2576,6 +2749,7 @@ gen_intermediate_code_internal(CPUState *env, TranslationBlock *tb,
dc->cc_op = CC_OP_DYNAMIC;
dc->singlestep_enabled = env->singlestep_enabled;
dc->fpcr = env->fpcr;
+ dc->user = (env->sr & SR_S) == 0;
nb_gen_labels = 0;
lj = -1;
do {
@@ -2675,6 +2849,19 @@ int gen_intermediate_code_pc(CPUState *env, TranslationBlock *tb)
return gen_intermediate_code_internal(env, tb, 1);
}
+void cpu_reset(CPUM68KState *env)
+{
+ memset(env, 0, offsetof(CPUM68KState, breakpoints));
+#if !defined (CONFIG_USER_ONLY)
+ env->sr = 0x2700;
+#endif
+ /* ??? FP regs should be initialized to NaN. */
+ env->cc_op = CC_OP_FLAGS;
+ /* TODO: We should set PC from the interrupt vector. */
+ env->pc = 0;
+ tlb_flush(env, 1);
+}
+
CPUM68KState *cpu_m68k_init(void)
{
CPUM68KState *env;
@@ -2684,10 +2871,7 @@ CPUM68KState *cpu_m68k_init(void)
return NULL;
cpu_exec_init(env);
- memset(env, 0, sizeof(CPUM68KState));
- /* ??? FP regs should be initialized to NaN. */
- cpu_single_env = env;
- env->cc_op = CC_OP_FLAGS;
+ cpu_reset(env);
return env;
}
@@ -2696,23 +2880,20 @@ void cpu_m68k_close(CPUM68KState *env)
free(env);
}
-m68k_def_t *m68k_find_by_name(const char *name)
+int cpu_m68k_set_model(CPUM68KState *env, const char * name)
{
m68k_def_t *def;
- def = m68k_cpu_defs;
- while (def->name)
- {
+ for (def = m68k_cpu_defs; def->name; def++) {
if (strcmp(def->name, name) == 0)
- return def;
- def++;
- }
- return NULL;
-}
+ break;
+ }
+ if (!def->name)
+ return 1;
-void cpu_m68k_register(CPUM68KState *env, m68k_def_t *def)
-{
register_m68k_insns(def);
+
+ return 0;
}
void cpu_dump_state(CPUState *env, FILE *f,
@@ -2737,24 +2918,3 @@ void cpu_dump_state(CPUState *env, FILE *f,
cpu_fprintf (f, "FPRESULT = %12g\n", env->fp_result);
}
-/* ??? */
-target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong addr)
-{
- return addr;
-}
-
-#if defined(CONFIG_USER_ONLY)
-
-int cpu_m68k_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
- int is_user, int is_softmmu)
-{
- env->exception_index = EXCP_ACCESS;
- env->mmu.ar = address;
- return 1;
-}
-
-#else
-
-#error not implemented
-
-#endif
diff --git a/vl.c b/vl.c
index 02c3c9cf79..373435b4ac 100644
--- a/vl.c
+++ b/vl.c
@@ -6833,6 +6833,8 @@ void register_machines(void)
qemu_register_machine(&shix_machine);
#elif defined(TARGET_ALPHA)
/* XXX: TODO */
+#elif defined(TARGET_M68K)
+ qemu_register_machine(&an5206_machine);
#else
#error unsupported CPU
#endif
diff --git a/vl.h b/vl.h
index 88ebdbccf6..330861e09c 100644
--- a/vl.h
+++ b/vl.h
@@ -1597,6 +1597,12 @@ void ptimer_stop(ptimer_state *s);
#include "hw/pxa.h"
+/* mcf5206.c */
+qemu_irq *mcf5206_init(uint32_t base, CPUState *env);
+
+/* an5206.c */
+extern QEMUMachine an5206_machine;
+
#include "gdbstub.h"
#endif /* defined(QEMU_TOOL) */