import
This commit is contained in:
3
qemu/target-m68k/Makefile.objs
Normal file
3
qemu/target-m68k/Makefile.objs
Normal file
@@ -0,0 +1,3 @@
|
||||
#obj-y += m68k-semi.o
|
||||
obj-y += translate.o op_helper.o helper.o cpu.o
|
||||
obj-y += unicorn.o
|
||||
84
qemu/target-m68k/cpu-qom.h
Normal file
84
qemu/target-m68k/cpu-qom.h
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* QEMU Motorola 68k CPU
|
||||
*
|
||||
* Copyright (c) 2012 SUSE LINUX Products GmbH
|
||||
*
|
||||
* 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.1 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, see
|
||||
* <http://www.gnu.org/licenses/lgpl-2.1.html>
|
||||
*/
|
||||
#ifndef QEMU_M68K_CPU_QOM_H
|
||||
#define QEMU_M68K_CPU_QOM_H
|
||||
|
||||
#include "qom/cpu.h"
|
||||
|
||||
#define TYPE_M68K_CPU "m68k-cpu"
|
||||
|
||||
#define M68K_CPU_CLASS(uc, klass) \
|
||||
OBJECT_CLASS_CHECK(uc, M68kCPUClass, (klass), TYPE_M68K_CPU)
|
||||
#define M68K_CPU(uc, obj) \
|
||||
OBJECT_CHECK(uc, M68kCPU, (obj), TYPE_M68K_CPU)
|
||||
#define M68K_CPU_GET_CLASS(uc, obj) \
|
||||
OBJECT_GET_CLASS(uc, M68kCPUClass, (obj), TYPE_M68K_CPU)
|
||||
|
||||
/**
|
||||
* M68kCPUClass:
|
||||
* @parent_realize: The parent class' realize handler.
|
||||
* @parent_reset: The parent class' reset handler.
|
||||
*
|
||||
* A Motorola 68k CPU model.
|
||||
*/
|
||||
typedef struct M68kCPUClass {
|
||||
/*< private >*/
|
||||
CPUClass parent_class;
|
||||
/*< public >*/
|
||||
|
||||
DeviceRealize parent_realize;
|
||||
void (*parent_reset)(CPUState *cpu);
|
||||
} M68kCPUClass;
|
||||
|
||||
/**
|
||||
* M68kCPU:
|
||||
* @env: #CPUM68KState
|
||||
*
|
||||
* A Motorola 68k CPU.
|
||||
*/
|
||||
typedef struct M68kCPU {
|
||||
/*< private >*/
|
||||
CPUState parent_obj;
|
||||
/*< public >*/
|
||||
|
||||
CPUM68KState env;
|
||||
} M68kCPU;
|
||||
|
||||
static inline M68kCPU *m68k_env_get_cpu(CPUM68KState *env)
|
||||
{
|
||||
return container_of(env, M68kCPU, env);
|
||||
}
|
||||
|
||||
#define ENV_GET_CPU(e) CPU(m68k_env_get_cpu(e))
|
||||
|
||||
#define ENV_OFFSET offsetof(M68kCPU, env)
|
||||
|
||||
void m68k_cpu_do_interrupt(CPUState *cpu);
|
||||
bool m68k_cpu_exec_interrupt(CPUState *cpu, int int_req);
|
||||
void m68k_cpu_dump_state(CPUState *cpu, FILE *f, fprintf_function cpu_fprintf,
|
||||
int flags);
|
||||
hwaddr m68k_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr);
|
||||
int m68k_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg);
|
||||
int m68k_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg);
|
||||
|
||||
void m68k_cpu_exec_enter(CPUState *cs);
|
||||
void m68k_cpu_exec_exit(CPUState *cs);
|
||||
|
||||
#endif
|
||||
231
qemu/target-m68k/cpu.c
Normal file
231
qemu/target-m68k/cpu.c
Normal file
@@ -0,0 +1,231 @@
|
||||
/*
|
||||
* QEMU Motorola 68k CPU
|
||||
*
|
||||
* Copyright (c) 2012 SUSE LINUX Products GmbH
|
||||
*
|
||||
* 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.1 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, see
|
||||
* <http://www.gnu.org/licenses/lgpl-2.1.html>
|
||||
*/
|
||||
|
||||
#include "hw/m68k/m68k.h"
|
||||
#include "cpu.h"
|
||||
#include "qemu-common.h"
|
||||
|
||||
|
||||
static void m68k_cpu_set_pc(CPUState *cs, vaddr value)
|
||||
{
|
||||
M68kCPU *cpu = M68K_CPU(cs->uc, cs);
|
||||
|
||||
cpu->env.pc = value;
|
||||
}
|
||||
|
||||
static bool m68k_cpu_has_work(CPUState *cs)
|
||||
{
|
||||
return cs->interrupt_request & CPU_INTERRUPT_HARD;
|
||||
}
|
||||
|
||||
static void m68k_set_feature(CPUM68KState *env, int feature)
|
||||
{
|
||||
env->features |= (1u << feature);
|
||||
}
|
||||
|
||||
/* CPUClass::reset() */
|
||||
static void m68k_cpu_reset(CPUState *s)
|
||||
{
|
||||
M68kCPU *cpu = M68K_CPU(s->uc, s);
|
||||
M68kCPUClass *mcc = M68K_CPU_GET_CLASS(s->uc, cpu);
|
||||
CPUM68KState *env = &cpu->env;
|
||||
|
||||
mcc->parent_reset(s);
|
||||
|
||||
memset(env, 0, offsetof(CPUM68KState, features));
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
env->sr = 0x2700;
|
||||
#endif
|
||||
m68k_switch_sp(env);
|
||||
/* ??? 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(s, 1);
|
||||
}
|
||||
|
||||
/* CPU models */
|
||||
|
||||
static ObjectClass *m68k_cpu_class_by_name(struct uc_struct *uc, const char *cpu_model)
|
||||
{
|
||||
ObjectClass *oc;
|
||||
char *typename;
|
||||
|
||||
if (cpu_model == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
typename = g_strdup_printf("%s-" TYPE_M68K_CPU, cpu_model);
|
||||
oc = object_class_by_name(uc, typename);
|
||||
g_free(typename);
|
||||
if (oc != NULL && (object_class_dynamic_cast(uc, oc, TYPE_M68K_CPU) == NULL ||
|
||||
object_class_is_abstract(oc))) {
|
||||
return NULL;
|
||||
}
|
||||
return oc;
|
||||
}
|
||||
|
||||
static void m5206_cpu_initfn(struct uc_struct *uc, Object *obj, void *opaque)
|
||||
{
|
||||
M68kCPU *cpu = M68K_CPU(uc, obj);
|
||||
CPUM68KState *env = &cpu->env;
|
||||
|
||||
m68k_set_feature(env, M68K_FEATURE_CF_ISA_A);
|
||||
}
|
||||
|
||||
static void m5208_cpu_initfn(struct uc_struct *uc, Object *obj, void *opaque)
|
||||
{
|
||||
M68kCPU *cpu = M68K_CPU(uc, obj);
|
||||
CPUM68KState *env = &cpu->env;
|
||||
|
||||
m68k_set_feature(env, M68K_FEATURE_CF_ISA_A);
|
||||
m68k_set_feature(env, M68K_FEATURE_CF_ISA_APLUSC);
|
||||
m68k_set_feature(env, M68K_FEATURE_BRAL);
|
||||
m68k_set_feature(env, M68K_FEATURE_CF_EMAC);
|
||||
m68k_set_feature(env, M68K_FEATURE_USP);
|
||||
}
|
||||
|
||||
static void cfv4e_cpu_initfn(struct uc_struct *uc, Object *obj, void *opaque)
|
||||
{
|
||||
M68kCPU *cpu = M68K_CPU(uc, obj);
|
||||
CPUM68KState *env = &cpu->env;
|
||||
|
||||
m68k_set_feature(env, M68K_FEATURE_CF_ISA_A);
|
||||
m68k_set_feature(env, M68K_FEATURE_CF_ISA_B);
|
||||
m68k_set_feature(env, M68K_FEATURE_BRAL);
|
||||
m68k_set_feature(env, M68K_FEATURE_CF_FPU);
|
||||
m68k_set_feature(env, M68K_FEATURE_CF_EMAC);
|
||||
m68k_set_feature(env, M68K_FEATURE_USP);
|
||||
}
|
||||
|
||||
static void any_cpu_initfn(struct uc_struct *uc, Object *obj, void *opaque)
|
||||
{
|
||||
M68kCPU *cpu = M68K_CPU(uc, obj);
|
||||
CPUM68KState *env = &cpu->env;
|
||||
|
||||
m68k_set_feature(env, M68K_FEATURE_CF_ISA_A);
|
||||
m68k_set_feature(env, M68K_FEATURE_CF_ISA_B);
|
||||
m68k_set_feature(env, M68K_FEATURE_CF_ISA_APLUSC);
|
||||
m68k_set_feature(env, M68K_FEATURE_BRAL);
|
||||
m68k_set_feature(env, M68K_FEATURE_CF_FPU);
|
||||
/* MAC and EMAC are mututally exclusive, so pick EMAC.
|
||||
It's mostly backwards compatible. */
|
||||
m68k_set_feature(env, M68K_FEATURE_CF_EMAC);
|
||||
m68k_set_feature(env, M68K_FEATURE_CF_EMAC_B);
|
||||
m68k_set_feature(env, M68K_FEATURE_USP);
|
||||
m68k_set_feature(env, M68K_FEATURE_EXT_FULL);
|
||||
m68k_set_feature(env, M68K_FEATURE_WORD_INDEX);
|
||||
}
|
||||
|
||||
typedef struct M68kCPUInfo {
|
||||
const char *name;
|
||||
void (*instance_init)(struct uc_struct *uc, Object *obj, void *opaque);
|
||||
} M68kCPUInfo;
|
||||
|
||||
static const M68kCPUInfo m68k_cpus[] = {
|
||||
{ .name = "m5206", .instance_init = m5206_cpu_initfn },
|
||||
{ .name = "m5208", .instance_init = m5208_cpu_initfn },
|
||||
{ .name = "cfv4e", .instance_init = cfv4e_cpu_initfn },
|
||||
{ .name = "any", .instance_init = any_cpu_initfn },
|
||||
};
|
||||
|
||||
static void m68k_cpu_realizefn(struct uc_struct *uc, DeviceState *dev, Error **errp)
|
||||
{
|
||||
CPUState *cs = CPU(dev);
|
||||
M68kCPUClass *mcc = M68K_CPU_GET_CLASS(uc, dev);
|
||||
|
||||
cpu_reset(cs);
|
||||
qemu_init_vcpu(cs);
|
||||
|
||||
mcc->parent_realize(cs->uc, dev, errp);
|
||||
}
|
||||
|
||||
static void m68k_cpu_initfn(struct uc_struct *uc, Object *obj, void *opaque)
|
||||
{
|
||||
CPUState *cs = CPU(obj);
|
||||
M68kCPU *cpu = M68K_CPU(uc, obj);
|
||||
CPUM68KState *env = &cpu->env;
|
||||
|
||||
cs->env_ptr = env;
|
||||
cpu_exec_init(env, opaque);
|
||||
|
||||
if (tcg_enabled(uc)) {
|
||||
m68k_tcg_init(uc);
|
||||
}
|
||||
}
|
||||
|
||||
static void m68k_cpu_class_init(struct uc_struct *uc, ObjectClass *c, void *data)
|
||||
{
|
||||
M68kCPUClass *mcc = M68K_CPU_CLASS(uc, c);
|
||||
CPUClass *cc = CPU_CLASS(uc, c);
|
||||
DeviceClass *dc = DEVICE_CLASS(uc, c);
|
||||
|
||||
mcc->parent_realize = dc->realize;
|
||||
dc->realize = m68k_cpu_realizefn;
|
||||
|
||||
mcc->parent_reset = cc->reset;
|
||||
cc->reset = m68k_cpu_reset;
|
||||
|
||||
cc->class_by_name = m68k_cpu_class_by_name;
|
||||
cc->has_work = m68k_cpu_has_work;
|
||||
cc->do_interrupt = m68k_cpu_do_interrupt;
|
||||
cc->cpu_exec_interrupt = m68k_cpu_exec_interrupt;
|
||||
cc->set_pc = m68k_cpu_set_pc;
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
cc->handle_mmu_fault = m68k_cpu_handle_mmu_fault;
|
||||
#else
|
||||
cc->get_phys_page_debug = m68k_cpu_get_phys_page_debug;
|
||||
#endif
|
||||
cc->cpu_exec_enter = m68k_cpu_exec_enter;
|
||||
cc->cpu_exec_exit = m68k_cpu_exec_exit;
|
||||
}
|
||||
|
||||
static void register_cpu_type(void *opaque, const M68kCPUInfo *info)
|
||||
{
|
||||
TypeInfo type_info = {
|
||||
.parent = TYPE_M68K_CPU,
|
||||
.instance_init = info->instance_init,
|
||||
};
|
||||
|
||||
type_info.name = g_strdup_printf("%s-" TYPE_M68K_CPU, info->name);
|
||||
type_register(opaque, &type_info);
|
||||
g_free((void *)type_info.name);
|
||||
}
|
||||
|
||||
void m68k_cpu_register_types(void *opaque)
|
||||
{
|
||||
TypeInfo m68k_cpu_type_info = {
|
||||
.name = TYPE_M68K_CPU,
|
||||
.parent = TYPE_CPU,
|
||||
.instance_userdata = opaque,
|
||||
.instance_size = sizeof(M68kCPU),
|
||||
.instance_init = m68k_cpu_initfn,
|
||||
.abstract = true,
|
||||
.class_size = sizeof(M68kCPUClass),
|
||||
.class_init = m68k_cpu_class_init,
|
||||
};
|
||||
|
||||
int i;
|
||||
|
||||
type_register_static(opaque, &m68k_cpu_type_info);
|
||||
for (i = 0; i < ARRAY_SIZE(m68k_cpus); i++) {
|
||||
register_cpu_type(opaque, &m68k_cpus[i]);
|
||||
}
|
||||
}
|
||||
259
qemu/target-m68k/cpu.h
Normal file
259
qemu/target-m68k/cpu.h
Normal file
@@ -0,0 +1,259 @@
|
||||
/*
|
||||
* m68k virtual CPU header
|
||||
*
|
||||
* Copyright (c) 2005-2007 CodeSourcery
|
||||
* Written by Paul Brook
|
||||
*
|
||||
* 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
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef CPU_M68K_H
|
||||
#define CPU_M68K_H
|
||||
|
||||
#define TARGET_LONG_BITS 32
|
||||
|
||||
#define CPUArchState struct CPUM68KState
|
||||
|
||||
#include "config.h"
|
||||
#include "qemu-common.h"
|
||||
#include "exec/cpu-defs.h"
|
||||
|
||||
#include "fpu/softfloat.h"
|
||||
|
||||
#define MAX_QREGS 32
|
||||
|
||||
#define TARGET_HAS_ICE 1
|
||||
|
||||
#define ELF_MACHINE EM_68K
|
||||
|
||||
#define EXCP_ACCESS 2 /* Access (MMU) error. */
|
||||
#define EXCP_ADDRESS 3 /* Address error. */
|
||||
#define EXCP_ILLEGAL 4 /* Illegal instruction. */
|
||||
#define EXCP_DIV0 5 /* Divide by zero */
|
||||
#define EXCP_PRIVILEGE 8 /* Privilege violation. */
|
||||
#define EXCP_TRACE 9
|
||||
#define EXCP_LINEA 10 /* Unimplemented line-A (MAC) opcode. */
|
||||
#define EXCP_LINEF 11 /* Unimplemented line-F (FPU) opcode. */
|
||||
#define EXCP_DEBUGNBP 12 /* Non-breakpoint debug interrupt. */
|
||||
#define EXCP_DEBEGBP 13 /* Breakpoint debug interrupt. */
|
||||
#define EXCP_FORMAT 14 /* RTE format error. */
|
||||
#define EXCP_UNINITIALIZED 15
|
||||
#define EXCP_TRAP0 32 /* User trap #0. */
|
||||
#define EXCP_TRAP15 47 /* User trap #15. */
|
||||
#define EXCP_UNSUPPORTED 61
|
||||
#define EXCP_ICE 13
|
||||
|
||||
#define EXCP_RTE 0x100
|
||||
#define EXCP_HALT_INSN 0x101
|
||||
|
||||
#define NB_MMU_MODES 2
|
||||
|
||||
typedef struct CPUM68KState {
|
||||
uint32_t dregs[8];
|
||||
uint32_t aregs[8];
|
||||
uint32_t pc;
|
||||
uint32_t sr;
|
||||
|
||||
/* SSP and USP. The current_sp is stored in aregs[7], the other here. */
|
||||
int current_sp;
|
||||
uint32_t sp[2];
|
||||
|
||||
/* Condition flags. */
|
||||
uint32_t cc_op;
|
||||
uint32_t cc_dest;
|
||||
uint32_t cc_src;
|
||||
uint32_t cc_x;
|
||||
|
||||
float64 fregs[8];
|
||||
float64 fp_result;
|
||||
uint32_t fpcr;
|
||||
uint32_t fpsr;
|
||||
float_status fp_status;
|
||||
|
||||
uint64_t mactmp;
|
||||
/* EMAC Hardware deals with 48-bit values composed of one 32-bit and
|
||||
two 8-bit parts. We store a single 64-bit value and
|
||||
rearrange/extend this when changing modes. */
|
||||
uint64_t macc[4];
|
||||
uint32_t macsr;
|
||||
uint32_t mac_mask;
|
||||
|
||||
/* Temporary storage for DIV helpers. */
|
||||
uint32_t div1;
|
||||
uint32_t div2;
|
||||
|
||||
/* MMU status. */
|
||||
struct {
|
||||
uint32_t ar;
|
||||
} mmu;
|
||||
|
||||
/* Control registers. */
|
||||
uint32_t vbr;
|
||||
uint32_t mbar;
|
||||
uint32_t rambar0;
|
||||
uint32_t cacr;
|
||||
|
||||
int pending_vector;
|
||||
int pending_level;
|
||||
|
||||
uint32_t qregs[MAX_QREGS];
|
||||
|
||||
CPU_COMMON
|
||||
|
||||
/* Fields from here on are preserved across CPU reset. */
|
||||
uint32_t features;
|
||||
|
||||
// Unicorn engine
|
||||
struct uc_struct *uc;
|
||||
} CPUM68KState;
|
||||
|
||||
#include "cpu-qom.h"
|
||||
|
||||
void m68k_tcg_init(struct uc_struct *uc);
|
||||
M68kCPU *cpu_m68k_init(struct uc_struct *uc, const char *cpu_model);
|
||||
int cpu_m68k_exec(struct uc_struct *uc, CPUM68KState *s);
|
||||
/* 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. */
|
||||
int cpu_m68k_signal_handler(int host_signum, void *pinfo,
|
||||
void *puc);
|
||||
void cpu_m68k_flush_flags(CPUM68KState *, int);
|
||||
|
||||
enum {
|
||||
CC_OP_DYNAMIC, /* Use env->cc_op */
|
||||
CC_OP_FLAGS, /* CC_DEST = CVZN, CC_SRC = unused */
|
||||
CC_OP_LOGIC, /* CC_DEST = result, CC_SRC = unused */
|
||||
CC_OP_ADD, /* CC_DEST = result, CC_SRC = source */
|
||||
CC_OP_SUB, /* CC_DEST = result, CC_SRC = source */
|
||||
CC_OP_CMPB, /* CC_DEST = result, CC_SRC = source */
|
||||
CC_OP_CMPW, /* CC_DEST = result, CC_SRC = source */
|
||||
CC_OP_ADDX, /* CC_DEST = result, CC_SRC = source */
|
||||
CC_OP_SUBX, /* CC_DEST = result, CC_SRC = source */
|
||||
CC_OP_SHIFT, /* CC_DEST = result, CC_SRC = carry */
|
||||
};
|
||||
|
||||
#define CCF_C 0x01
|
||||
#define CCF_V 0x02
|
||||
#define CCF_Z 0x04
|
||||
#define CCF_N 0x08
|
||||
#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
|
||||
|
||||
#define M68K_SSP 0
|
||||
#define M68K_USP 1
|
||||
|
||||
/* CACR fields are implementation defined, but some bits are common. */
|
||||
#define M68K_CACR_EUSP 0x10
|
||||
|
||||
#define MACSR_PAV0 0x100
|
||||
#define MACSR_OMC 0x080
|
||||
#define MACSR_SU 0x040
|
||||
#define MACSR_FI 0x020
|
||||
#define MACSR_RT 0x010
|
||||
#define MACSR_N 0x008
|
||||
#define MACSR_Z 0x004
|
||||
#define MACSR_V 0x002
|
||||
#define MACSR_EV 0x001
|
||||
|
||||
void m68k_set_irq_level(M68kCPU *cpu, int level, uint8_t vector);
|
||||
void m68k_set_macsr(CPUM68KState *env, uint32_t val);
|
||||
void m68k_switch_sp(CPUM68KState *env);
|
||||
|
||||
#define M68K_FPCR_PREC (1 << 6)
|
||||
|
||||
void do_m68k_semihosting(CPUM68KState *env, int nr);
|
||||
|
||||
/* There are 4 ColdFire core ISA revisions: A, A+, B and C.
|
||||
Each feature covers the subset of instructions common to the
|
||||
ISA revisions mentioned. */
|
||||
|
||||
enum m68k_features {
|
||||
M68K_FEATURE_CF_ISA_A,
|
||||
M68K_FEATURE_CF_ISA_B, /* (ISA B or C). */
|
||||
M68K_FEATURE_CF_ISA_APLUSC, /* BIT/BITREV, FF1, STRLDSR (ISA A+ or C). */
|
||||
M68K_FEATURE_BRAL, /* Long unconditional branch. (ISA A+ or B). */
|
||||
M68K_FEATURE_CF_FPU,
|
||||
M68K_FEATURE_CF_MAC,
|
||||
M68K_FEATURE_CF_EMAC,
|
||||
M68K_FEATURE_CF_EMAC_B, /* Revision B EMAC (dual accumulate). */
|
||||
M68K_FEATURE_USP, /* User Stack Pointer. (ISA A+, B or C). */
|
||||
M68K_FEATURE_EXT_FULL, /* 68020+ full extension word. */
|
||||
M68K_FEATURE_WORD_INDEX /* word sized address index registers. */
|
||||
};
|
||||
|
||||
static inline int m68k_feature(CPUM68KState *env, int feature)
|
||||
{
|
||||
return (env->features & (1u << feature)) != 0;
|
||||
}
|
||||
|
||||
void m68k_cpu_list(FILE *f, fprintf_function cpu_fprintf);
|
||||
|
||||
void register_m68k_insns (CPUM68KState *env);
|
||||
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
/* Linux uses 8k pages. */
|
||||
#define TARGET_PAGE_BITS 13
|
||||
#else
|
||||
/* Smallest TLB entry size is 1k. */
|
||||
#define TARGET_PAGE_BITS 10
|
||||
#endif
|
||||
|
||||
#define TARGET_PHYS_ADDR_SPACE_BITS 32
|
||||
#define TARGET_VIRT_ADDR_SPACE_BITS 32
|
||||
|
||||
static inline CPUM68KState *cpu_init(struct uc_struct *uc, const char *cpu_model)
|
||||
{
|
||||
M68kCPU *cpu = cpu_m68k_init(uc, cpu_model);
|
||||
if (cpu == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
return &cpu->env;
|
||||
}
|
||||
|
||||
#define cpu_exec cpu_m68k_exec
|
||||
#define cpu_gen_code cpu_m68k_gen_code
|
||||
#define cpu_signal_handler cpu_m68k_signal_handler
|
||||
#define cpu_list m68k_cpu_list
|
||||
|
||||
/* MMU modes definitions */
|
||||
#define MMU_MODE0_SUFFIX _kernel
|
||||
#define MMU_MODE1_SUFFIX _user
|
||||
#define MMU_USER_IDX 1
|
||||
static inline int cpu_mmu_index (CPUM68KState *env)
|
||||
{
|
||||
return (env->sr & SR_S) == 0 ? 1 : 0;
|
||||
}
|
||||
|
||||
int m68k_cpu_handle_mmu_fault(CPUState *cpu, vaddr address, int rw,
|
||||
int mmu_idx);
|
||||
|
||||
#include "exec/cpu-all.h"
|
||||
|
||||
static inline void cpu_get_tb_cpu_state(CPUM68KState *env, target_ulong *pc,
|
||||
target_ulong *cs_base, int *flags)
|
||||
{
|
||||
*pc = env->pc;
|
||||
*cs_base = 0;
|
||||
*flags = (env->fpcr & M68K_FPCR_PREC) /* Bit 6 */
|
||||
| (env->sr & SR_S) /* Bit 13 */
|
||||
| ((env->macsr >> 4) & 0xf); /* Bits 0-3 */
|
||||
}
|
||||
|
||||
#include "exec/exec-all.h"
|
||||
|
||||
#endif
|
||||
800
qemu/target-m68k/helper.c
Normal file
800
qemu/target-m68k/helper.c
Normal file
@@ -0,0 +1,800 @@
|
||||
/*
|
||||
* m68k op helpers
|
||||
*
|
||||
* Copyright (c) 2006-2007 CodeSourcery
|
||||
* Written by Paul Brook
|
||||
*
|
||||
* 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
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "cpu.h"
|
||||
|
||||
#include "exec/helper-proto.h"
|
||||
|
||||
#define SIGNBIT (1u << 31)
|
||||
|
||||
M68kCPU *cpu_m68k_init(struct uc_struct *uc, const char *cpu_model)
|
||||
{
|
||||
M68kCPU *cpu;
|
||||
CPUM68KState *env;
|
||||
ObjectClass *oc;
|
||||
|
||||
oc = cpu_class_by_name(uc, TYPE_M68K_CPU, cpu_model);
|
||||
if (oc == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
cpu = M68K_CPU(uc, object_new(uc, object_class_get_name(oc)));
|
||||
env = &cpu->env;
|
||||
|
||||
register_m68k_insns(env);
|
||||
|
||||
object_property_set_bool(uc, OBJECT(cpu), true, "realized", NULL);
|
||||
|
||||
return cpu;
|
||||
}
|
||||
|
||||
void cpu_m68k_flush_flags(CPUM68KState *env, int cc_op)
|
||||
{
|
||||
M68kCPU *cpu = m68k_env_get_cpu(env);
|
||||
int flags;
|
||||
uint32_t src;
|
||||
uint32_t dest;
|
||||
uint32_t tmp;
|
||||
|
||||
#define HIGHBIT 0x80000000u
|
||||
|
||||
#define SET_NZ(x) do { \
|
||||
if ((x) == 0) \
|
||||
flags |= CCF_Z; \
|
||||
else if ((int32_t)(x) < 0) \
|
||||
flags |= CCF_N; \
|
||||
} while (0)
|
||||
|
||||
#define SET_FLAGS_SUB(type, utype) do { \
|
||||
SET_NZ((type)dest); \
|
||||
tmp = dest + src; \
|
||||
if ((utype) tmp < (utype) src) \
|
||||
flags |= CCF_C; \
|
||||
if ((1u << (sizeof(type) * 8 - 1)) & (tmp ^ dest) & (tmp ^ src)) \
|
||||
flags |= CCF_V; \
|
||||
} while (0)
|
||||
|
||||
flags = 0;
|
||||
src = env->cc_src;
|
||||
dest = env->cc_dest;
|
||||
switch (cc_op) {
|
||||
case CC_OP_FLAGS:
|
||||
flags = dest;
|
||||
break;
|
||||
case CC_OP_LOGIC:
|
||||
SET_NZ(dest);
|
||||
break;
|
||||
case CC_OP_ADD:
|
||||
SET_NZ(dest);
|
||||
if (dest < src)
|
||||
flags |= CCF_C;
|
||||
tmp = dest - src;
|
||||
if (HIGHBIT & (src ^ dest) & ~(tmp ^ src))
|
||||
flags |= CCF_V;
|
||||
break;
|
||||
case CC_OP_SUB:
|
||||
SET_FLAGS_SUB(int32_t, uint32_t);
|
||||
break;
|
||||
case CC_OP_CMPB:
|
||||
SET_FLAGS_SUB(int8_t, uint8_t);
|
||||
break;
|
||||
case CC_OP_CMPW:
|
||||
SET_FLAGS_SUB(int16_t, uint16_t);
|
||||
break;
|
||||
case CC_OP_ADDX:
|
||||
SET_NZ(dest);
|
||||
if (dest <= src)
|
||||
flags |= CCF_C;
|
||||
tmp = dest - src - 1;
|
||||
if (HIGHBIT & (src ^ dest) & ~(tmp ^ src))
|
||||
flags |= CCF_V;
|
||||
break;
|
||||
case CC_OP_SUBX:
|
||||
SET_NZ(dest);
|
||||
tmp = dest + src + 1;
|
||||
if (tmp <= src)
|
||||
flags |= CCF_C;
|
||||
if (HIGHBIT & (tmp ^ dest) & (tmp ^ src))
|
||||
flags |= CCF_V;
|
||||
break;
|
||||
case CC_OP_SHIFT:
|
||||
SET_NZ(dest);
|
||||
if (src)
|
||||
flags |= CCF_C;
|
||||
break;
|
||||
default:
|
||||
cpu_abort(CPU(cpu), "Bad CC_OP %d", cc_op);
|
||||
}
|
||||
env->cc_op = CC_OP_FLAGS;
|
||||
env->cc_dest = flags;
|
||||
}
|
||||
|
||||
void HELPER(movec)(CPUM68KState *env, uint32_t reg, uint32_t val)
|
||||
{
|
||||
M68kCPU *cpu = m68k_env_get_cpu(env);
|
||||
|
||||
switch (reg) {
|
||||
case 0x02: /* CACR */
|
||||
env->cacr = val;
|
||||
m68k_switch_sp(env);
|
||||
break;
|
||||
case 0x04: case 0x05: case 0x06: case 0x07: /* ACR[0-3] */
|
||||
/* TODO: Implement Access Control Registers. */
|
||||
break;
|
||||
case 0x801: /* VBR */
|
||||
env->vbr = val;
|
||||
break;
|
||||
/* TODO: Implement control registers. */
|
||||
default:
|
||||
cpu_abort(CPU(cpu), "Unimplemented control register write 0x%x = 0x%x\n",
|
||||
reg, val);
|
||||
}
|
||||
}
|
||||
|
||||
void HELPER(set_macsr)(CPUM68KState *env, uint32_t val)
|
||||
{
|
||||
uint32_t acc;
|
||||
int8_t exthigh;
|
||||
uint8_t extlow;
|
||||
uint64_t regval;
|
||||
int i;
|
||||
if ((env->macsr ^ val) & (MACSR_FI | MACSR_SU)) {
|
||||
for (i = 0; i < 4; i++) {
|
||||
regval = env->macc[i];
|
||||
exthigh = regval >> 40;
|
||||
if (env->macsr & MACSR_FI) {
|
||||
acc = regval >> 8;
|
||||
extlow = regval;
|
||||
} else {
|
||||
acc = regval;
|
||||
extlow = regval >> 32;
|
||||
}
|
||||
if (env->macsr & MACSR_FI) {
|
||||
regval = (((uint64_t)acc) << 8) | extlow;
|
||||
regval |= ((int64_t)exthigh) << 40;
|
||||
} else if (env->macsr & MACSR_SU) {
|
||||
regval = acc | (((int64_t)extlow) << 32);
|
||||
regval |= ((int64_t)exthigh) << 40;
|
||||
} else {
|
||||
regval = acc | (((uint64_t)extlow) << 32);
|
||||
regval |= ((uint64_t)(uint8_t)exthigh) << 40;
|
||||
}
|
||||
env->macc[i] = regval;
|
||||
}
|
||||
}
|
||||
env->macsr = val;
|
||||
}
|
||||
|
||||
void m68k_switch_sp(CPUM68KState *env)
|
||||
{
|
||||
int new_sp;
|
||||
|
||||
env->sp[env->current_sp] = env->aregs[7];
|
||||
new_sp = (env->sr & SR_S && env->cacr & M68K_CACR_EUSP)
|
||||
? M68K_SSP : M68K_USP;
|
||||
env->aregs[7] = env->sp[new_sp];
|
||||
env->current_sp = new_sp;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_USER_ONLY)
|
||||
|
||||
int m68k_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int rw,
|
||||
int mmu_idx)
|
||||
{
|
||||
M68kCPU *cpu = M68K_CPU(cs);
|
||||
|
||||
cs->exception_index = EXCP_ACCESS;
|
||||
cpu->env.mmu.ar = address;
|
||||
return 1;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/* MMU */
|
||||
|
||||
/* TODO: This will need fixing once the MMU is implemented. */
|
||||
hwaddr m68k_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
|
||||
{
|
||||
return addr;
|
||||
}
|
||||
|
||||
int m68k_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int rw,
|
||||
int mmu_idx)
|
||||
{
|
||||
int prot;
|
||||
|
||||
address &= TARGET_PAGE_MASK;
|
||||
prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
|
||||
tlb_set_page(cs, address, address, prot, mmu_idx, TARGET_PAGE_SIZE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 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(M68kCPU *cpu, int level, uint8_t vector)
|
||||
{
|
||||
CPUState *cs = CPU(cpu);
|
||||
CPUM68KState *env = &cpu->env;
|
||||
|
||||
env->pending_level = level;
|
||||
env->pending_vector = vector;
|
||||
if (level) {
|
||||
cpu_interrupt(cs, CPU_INTERRUPT_HARD);
|
||||
} else {
|
||||
cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
uint32_t HELPER(bitrev)(uint32_t x)
|
||||
{
|
||||
x = ((x >> 1) & 0x55555555u) | ((x << 1) & 0xaaaaaaaau);
|
||||
x = ((x >> 2) & 0x33333333u) | ((x << 2) & 0xccccccccu);
|
||||
x = ((x >> 4) & 0x0f0f0f0fu) | ((x << 4) & 0xf0f0f0f0u);
|
||||
return bswap32(x);
|
||||
}
|
||||
|
||||
uint32_t HELPER(ff1)(uint32_t x)
|
||||
{
|
||||
int n;
|
||||
for (n = 32; x; n--)
|
||||
x >>= 1;
|
||||
return n;
|
||||
}
|
||||
|
||||
uint32_t HELPER(sats)(uint32_t val, uint32_t ccr)
|
||||
{
|
||||
/* The result has the opposite sign to the original value. */
|
||||
if (ccr & CCF_V)
|
||||
val = (((int32_t)val) >> 31) ^ SIGNBIT;
|
||||
return val;
|
||||
}
|
||||
|
||||
uint32_t HELPER(subx_cc)(CPUM68KState *env, uint32_t op1, uint32_t op2)
|
||||
{
|
||||
uint32_t res;
|
||||
uint32_t old_flags;
|
||||
|
||||
old_flags = env->cc_dest;
|
||||
if (env->cc_x) {
|
||||
env->cc_x = (op1 <= op2);
|
||||
env->cc_op = CC_OP_SUBX;
|
||||
res = op1 - (op2 + 1);
|
||||
} else {
|
||||
env->cc_x = (op1 < op2);
|
||||
env->cc_op = CC_OP_SUB;
|
||||
res = op1 - op2;
|
||||
}
|
||||
env->cc_dest = res;
|
||||
env->cc_src = op2;
|
||||
cpu_m68k_flush_flags(env, env->cc_op);
|
||||
/* !Z is sticky. */
|
||||
env->cc_dest &= (old_flags | ~CCF_Z);
|
||||
return res;
|
||||
}
|
||||
|
||||
uint32_t HELPER(addx_cc)(CPUM68KState *env, uint32_t op1, uint32_t op2)
|
||||
{
|
||||
uint32_t res;
|
||||
uint32_t old_flags;
|
||||
|
||||
old_flags = env->cc_dest;
|
||||
if (env->cc_x) {
|
||||
res = op1 + op2 + 1;
|
||||
env->cc_x = (res <= op2);
|
||||
env->cc_op = CC_OP_ADDX;
|
||||
} else {
|
||||
res = op1 + op2;
|
||||
env->cc_x = (res < op2);
|
||||
env->cc_op = CC_OP_ADD;
|
||||
}
|
||||
env->cc_dest = res;
|
||||
env->cc_src = op2;
|
||||
cpu_m68k_flush_flags(env, env->cc_op);
|
||||
/* !Z is sticky. */
|
||||
env->cc_dest &= (old_flags | ~CCF_Z);
|
||||
return res;
|
||||
}
|
||||
|
||||
uint32_t HELPER(xflag_lt)(uint32_t a, uint32_t b)
|
||||
{
|
||||
return a < b;
|
||||
}
|
||||
|
||||
void HELPER(set_sr)(CPUM68KState *env, uint32_t val)
|
||||
{
|
||||
env->sr = val & 0xffff;
|
||||
m68k_switch_sp(env);
|
||||
}
|
||||
|
||||
uint32_t HELPER(shl_cc)(CPUM68KState *env, uint32_t val, uint32_t shift)
|
||||
{
|
||||
uint32_t result;
|
||||
uint32_t cf;
|
||||
|
||||
shift &= 63;
|
||||
if (shift == 0) {
|
||||
result = val;
|
||||
cf = env->cc_src & CCF_C;
|
||||
} else if (shift < 32) {
|
||||
result = val << shift;
|
||||
cf = (val >> (32 - shift)) & 1;
|
||||
} else if (shift == 32) {
|
||||
result = 0;
|
||||
cf = val & 1;
|
||||
} else /* shift > 32 */ {
|
||||
result = 0;
|
||||
cf = 0;
|
||||
}
|
||||
env->cc_src = cf;
|
||||
env->cc_x = (cf != 0);
|
||||
env->cc_dest = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t HELPER(shr_cc)(CPUM68KState *env, uint32_t val, uint32_t shift)
|
||||
{
|
||||
uint32_t result;
|
||||
uint32_t cf;
|
||||
|
||||
shift &= 63;
|
||||
if (shift == 0) {
|
||||
result = val;
|
||||
cf = env->cc_src & CCF_C;
|
||||
} else if (shift < 32) {
|
||||
result = val >> shift;
|
||||
cf = (val >> (shift - 1)) & 1;
|
||||
} else if (shift == 32) {
|
||||
result = 0;
|
||||
cf = val >> 31;
|
||||
} else /* shift > 32 */ {
|
||||
result = 0;
|
||||
cf = 0;
|
||||
}
|
||||
env->cc_src = cf;
|
||||
env->cc_x = (cf != 0);
|
||||
env->cc_dest = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t HELPER(sar_cc)(CPUM68KState *env, uint32_t val, uint32_t shift)
|
||||
{
|
||||
uint32_t result;
|
||||
uint32_t cf;
|
||||
|
||||
shift &= 63;
|
||||
if (shift == 0) {
|
||||
result = val;
|
||||
cf = (env->cc_src & CCF_C) != 0;
|
||||
} else if (shift < 32) {
|
||||
result = (int32_t)val >> shift;
|
||||
cf = (val >> (shift - 1)) & 1;
|
||||
} else /* shift >= 32 */ {
|
||||
result = (int32_t)val >> 31;
|
||||
cf = val >> 31;
|
||||
}
|
||||
env->cc_src = cf;
|
||||
env->cc_x = cf;
|
||||
env->cc_dest = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
/* FPU helpers. */
|
||||
uint32_t HELPER(f64_to_i32)(CPUM68KState *env, float64 val)
|
||||
{
|
||||
return float64_to_int32(val, &env->fp_status);
|
||||
}
|
||||
|
||||
float32 HELPER(f64_to_f32)(CPUM68KState *env, float64 val)
|
||||
{
|
||||
return float64_to_float32(val, &env->fp_status);
|
||||
}
|
||||
|
||||
float64 HELPER(i32_to_f64)(CPUM68KState *env, uint32_t val)
|
||||
{
|
||||
return int32_to_float64(val, &env->fp_status);
|
||||
}
|
||||
|
||||
float64 HELPER(f32_to_f64)(CPUM68KState *env, float32 val)
|
||||
{
|
||||
return float32_to_float64(val, &env->fp_status);
|
||||
}
|
||||
|
||||
float64 HELPER(iround_f64)(CPUM68KState *env, float64 val)
|
||||
{
|
||||
return float64_round_to_int(val, &env->fp_status);
|
||||
}
|
||||
|
||||
float64 HELPER(itrunc_f64)(CPUM68KState *env, float64 val)
|
||||
{
|
||||
return float64_trunc_to_int(val, &env->fp_status);
|
||||
}
|
||||
|
||||
float64 HELPER(sqrt_f64)(CPUM68KState *env, float64 val)
|
||||
{
|
||||
return float64_sqrt(val, &env->fp_status);
|
||||
}
|
||||
|
||||
float64 HELPER(abs_f64)(float64 val)
|
||||
{
|
||||
return float64_abs(val);
|
||||
}
|
||||
|
||||
float64 HELPER(chs_f64)(float64 val)
|
||||
{
|
||||
return float64_chs(val);
|
||||
}
|
||||
|
||||
float64 HELPER(add_f64)(CPUM68KState *env, float64 a, float64 b)
|
||||
{
|
||||
return float64_add(a, b, &env->fp_status);
|
||||
}
|
||||
|
||||
float64 HELPER(sub_f64)(CPUM68KState *env, float64 a, float64 b)
|
||||
{
|
||||
return float64_sub(a, b, &env->fp_status);
|
||||
}
|
||||
|
||||
float64 HELPER(mul_f64)(CPUM68KState *env, float64 a, float64 b)
|
||||
{
|
||||
return float64_mul(a, b, &env->fp_status);
|
||||
}
|
||||
|
||||
float64 HELPER(div_f64)(CPUM68KState *env, float64 a, float64 b)
|
||||
{
|
||||
return float64_div(a, b, &env->fp_status);
|
||||
}
|
||||
|
||||
float64 HELPER(sub_cmp_f64)(CPUM68KState *env, float64 a, float64 b)
|
||||
{
|
||||
/* ??? This may incorrectly raise exceptions. */
|
||||
/* ??? Should flush denormals to zero. */
|
||||
float64 res;
|
||||
res = float64_sub(a, b, &env->fp_status);
|
||||
if (float64_is_quiet_nan(res)) {
|
||||
/* +/-inf compares equal against itself, but sub returns nan. */
|
||||
if (!float64_is_quiet_nan(a)
|
||||
&& !float64_is_quiet_nan(b)) {
|
||||
res = float64_zero;
|
||||
if (float64_lt_quiet(a, res, &env->fp_status))
|
||||
res = float64_chs(res);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
uint32_t HELPER(compare_f64)(CPUM68KState *env, float64 val)
|
||||
{
|
||||
return float64_compare_quiet(val, float64_zero, &env->fp_status);
|
||||
}
|
||||
|
||||
/* MAC unit. */
|
||||
/* FIXME: The MAC unit implementation is a bit of a mess. Some helpers
|
||||
take values, others take register numbers and manipulate the contents
|
||||
in-place. */
|
||||
void HELPER(mac_move)(CPUM68KState *env, uint32_t dest, uint32_t src)
|
||||
{
|
||||
uint32_t mask;
|
||||
env->macc[dest] = env->macc[src];
|
||||
mask = MACSR_PAV0 << dest;
|
||||
if (env->macsr & (MACSR_PAV0 << src))
|
||||
env->macsr |= mask;
|
||||
else
|
||||
env->macsr &= ~mask;
|
||||
}
|
||||
|
||||
uint64_t HELPER(macmuls)(CPUM68KState *env, uint32_t op1, uint32_t op2)
|
||||
{
|
||||
int64_t product;
|
||||
int64_t res;
|
||||
|
||||
product = (uint64_t)op1 * op2;
|
||||
res = (product << 24) >> 24;
|
||||
if (res != product) {
|
||||
env->macsr |= MACSR_V;
|
||||
if (env->macsr & MACSR_OMC) {
|
||||
/* Make sure the accumulate operation overflows. */
|
||||
if (product < 0)
|
||||
res = ~(1ll << 50);
|
||||
else
|
||||
res = 1ll << 50;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
uint64_t HELPER(macmulu)(CPUM68KState *env, uint32_t op1, uint32_t op2)
|
||||
{
|
||||
uint64_t product;
|
||||
|
||||
product = (uint64_t)op1 * op2;
|
||||
if (product & (0xffffffull << 40)) {
|
||||
env->macsr |= MACSR_V;
|
||||
if (env->macsr & MACSR_OMC) {
|
||||
/* Make sure the accumulate operation overflows. */
|
||||
product = 1ll << 50;
|
||||
} else {
|
||||
product &= ((1ull << 40) - 1);
|
||||
}
|
||||
}
|
||||
return product;
|
||||
}
|
||||
|
||||
uint64_t HELPER(macmulf)(CPUM68KState *env, uint32_t op1, uint32_t op2)
|
||||
{
|
||||
uint64_t product;
|
||||
uint32_t remainder;
|
||||
|
||||
product = (uint64_t)op1 * op2;
|
||||
if (env->macsr & MACSR_RT) {
|
||||
remainder = product & 0xffffff;
|
||||
product >>= 24;
|
||||
if (remainder > 0x800000)
|
||||
product++;
|
||||
else if (remainder == 0x800000)
|
||||
product += (product & 1);
|
||||
} else {
|
||||
product >>= 24;
|
||||
}
|
||||
return product;
|
||||
}
|
||||
|
||||
void HELPER(macsats)(CPUM68KState *env, uint32_t acc)
|
||||
{
|
||||
int64_t tmp;
|
||||
int64_t result;
|
||||
tmp = env->macc[acc];
|
||||
result = ((tmp << 16) >> 16);
|
||||
if (result != tmp) {
|
||||
env->macsr |= MACSR_V;
|
||||
}
|
||||
if (env->macsr & MACSR_V) {
|
||||
env->macsr |= MACSR_PAV0 << acc;
|
||||
if (env->macsr & MACSR_OMC) {
|
||||
/* The result is saturated to 32 bits, despite overflow occurring
|
||||
at 48 bits. Seems weird, but that's what the hardware docs
|
||||
say. */
|
||||
result = (result >> 63) ^ 0x7fffffff;
|
||||
}
|
||||
}
|
||||
env->macc[acc] = result;
|
||||
}
|
||||
|
||||
void HELPER(macsatu)(CPUM68KState *env, uint32_t acc)
|
||||
{
|
||||
uint64_t val;
|
||||
|
||||
val = env->macc[acc];
|
||||
if (val & (0xffffull << 48)) {
|
||||
env->macsr |= MACSR_V;
|
||||
}
|
||||
if (env->macsr & MACSR_V) {
|
||||
env->macsr |= MACSR_PAV0 << acc;
|
||||
if (env->macsr & MACSR_OMC) {
|
||||
if (val > (1ull << 53))
|
||||
val = 0;
|
||||
else
|
||||
val = (1ull << 48) - 1;
|
||||
} else {
|
||||
val &= ((1ull << 48) - 1);
|
||||
}
|
||||
}
|
||||
env->macc[acc] = val;
|
||||
}
|
||||
|
||||
void HELPER(macsatf)(CPUM68KState *env, uint32_t acc)
|
||||
{
|
||||
int64_t sum;
|
||||
int64_t result;
|
||||
|
||||
sum = env->macc[acc];
|
||||
result = (sum << 16) >> 16;
|
||||
if (result != sum) {
|
||||
env->macsr |= MACSR_V;
|
||||
}
|
||||
if (env->macsr & MACSR_V) {
|
||||
env->macsr |= MACSR_PAV0 << acc;
|
||||
if (env->macsr & MACSR_OMC) {
|
||||
result = (result >> 63) ^ 0x7fffffffffffll;
|
||||
}
|
||||
}
|
||||
env->macc[acc] = result;
|
||||
}
|
||||
|
||||
void HELPER(mac_set_flags)(CPUM68KState *env, uint32_t acc)
|
||||
{
|
||||
uint64_t val;
|
||||
val = env->macc[acc];
|
||||
if (val == 0) {
|
||||
env->macsr |= MACSR_Z;
|
||||
} else if (val & (1ull << 47)) {
|
||||
env->macsr |= MACSR_N;
|
||||
}
|
||||
if (env->macsr & (MACSR_PAV0 << acc)) {
|
||||
env->macsr |= MACSR_V;
|
||||
}
|
||||
if (env->macsr & MACSR_FI) {
|
||||
val = ((int64_t)val) >> 40;
|
||||
if (val != 0 && val != -1)
|
||||
env->macsr |= MACSR_EV;
|
||||
} else if (env->macsr & MACSR_SU) {
|
||||
val = ((int64_t)val) >> 32;
|
||||
if (val != 0 && val != -1)
|
||||
env->macsr |= MACSR_EV;
|
||||
} else {
|
||||
if ((val >> 32) != 0)
|
||||
env->macsr |= MACSR_EV;
|
||||
}
|
||||
}
|
||||
|
||||
void HELPER(flush_flags)(CPUM68KState *env, uint32_t cc_op)
|
||||
{
|
||||
cpu_m68k_flush_flags(env, cc_op);
|
||||
}
|
||||
|
||||
uint32_t HELPER(get_macf)(CPUM68KState *env, uint64_t val)
|
||||
{
|
||||
int rem;
|
||||
uint32_t result;
|
||||
|
||||
if (env->macsr & MACSR_SU) {
|
||||
/* 16-bit rounding. */
|
||||
rem = val & 0xffffff;
|
||||
val = (val >> 24) & 0xffffu;
|
||||
if (rem > 0x800000)
|
||||
val++;
|
||||
else if (rem == 0x800000)
|
||||
val += (val & 1);
|
||||
} else if (env->macsr & MACSR_RT) {
|
||||
/* 32-bit rounding. */
|
||||
rem = val & 0xff;
|
||||
val >>= 8;
|
||||
if (rem > 0x80)
|
||||
val++;
|
||||
else if (rem == 0x80)
|
||||
val += (val & 1);
|
||||
} else {
|
||||
/* No rounding. */
|
||||
val >>= 8;
|
||||
}
|
||||
if (env->macsr & MACSR_OMC) {
|
||||
/* Saturate. */
|
||||
if (env->macsr & MACSR_SU) {
|
||||
if (val != (uint16_t) val) {
|
||||
result = ((val >> 63) ^ 0x7fff) & 0xffff;
|
||||
} else {
|
||||
result = val & 0xffff;
|
||||
}
|
||||
} else {
|
||||
if (val != (uint32_t)val) {
|
||||
result = ((uint32_t)(val >> 63) & 0x7fffffff);
|
||||
} else {
|
||||
result = (uint32_t)val;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* No saturation. */
|
||||
if (env->macsr & MACSR_SU) {
|
||||
result = val & 0xffff;
|
||||
} else {
|
||||
result = (uint32_t)val;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t HELPER(get_macs)(uint64_t val)
|
||||
{
|
||||
if (val == (int32_t)val) {
|
||||
return (int32_t)val;
|
||||
} else {
|
||||
return (val >> 61) ^ ~SIGNBIT;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t HELPER(get_macu)(uint64_t val)
|
||||
{
|
||||
if ((val >> 32) == 0) {
|
||||
return (uint32_t)val;
|
||||
} else {
|
||||
return 0xffffffffu;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t HELPER(get_mac_extf)(CPUM68KState *env, uint32_t acc)
|
||||
{
|
||||
uint32_t val;
|
||||
val = env->macc[acc] & 0x00ff;
|
||||
val = (env->macc[acc] >> 32) & 0xff00;
|
||||
val |= (env->macc[acc + 1] << 16) & 0x00ff0000;
|
||||
val |= (env->macc[acc + 1] >> 16) & 0xff000000;
|
||||
return val;
|
||||
}
|
||||
|
||||
uint32_t HELPER(get_mac_exti)(CPUM68KState *env, uint32_t acc)
|
||||
{
|
||||
uint32_t val;
|
||||
val = (env->macc[acc] >> 32) & 0xffff;
|
||||
val |= (env->macc[acc + 1] >> 16) & 0xffff0000;
|
||||
return val;
|
||||
}
|
||||
|
||||
void HELPER(set_mac_extf)(CPUM68KState *env, uint32_t val, uint32_t acc)
|
||||
{
|
||||
int64_t res;
|
||||
int32_t tmp;
|
||||
res = env->macc[acc] & 0xffffffff00ull;
|
||||
tmp = (int16_t)(val & 0xff00);
|
||||
res |= ((int64_t)tmp) << 32;
|
||||
res |= val & 0xff;
|
||||
env->macc[acc] = res;
|
||||
res = env->macc[acc + 1] & 0xffffffff00ull;
|
||||
tmp = (val & 0xff000000);
|
||||
res |= ((int64_t)tmp) << 16;
|
||||
res |= (val >> 16) & 0xff;
|
||||
env->macc[acc + 1] = res;
|
||||
}
|
||||
|
||||
void HELPER(set_mac_exts)(CPUM68KState *env, uint32_t val, uint32_t acc)
|
||||
{
|
||||
int64_t res;
|
||||
int32_t tmp;
|
||||
res = (uint32_t)env->macc[acc];
|
||||
tmp = (int16_t)val;
|
||||
res |= ((int64_t)tmp) << 32;
|
||||
env->macc[acc] = res;
|
||||
res = (uint32_t)env->macc[acc + 1];
|
||||
tmp = val & 0xffff0000;
|
||||
res |= (int64_t)tmp << 16;
|
||||
env->macc[acc + 1] = res;
|
||||
}
|
||||
|
||||
void HELPER(set_mac_extu)(CPUM68KState *env, uint32_t val, uint32_t acc)
|
||||
{
|
||||
uint64_t res;
|
||||
res = (uint32_t)env->macc[acc];
|
||||
res |= ((uint64_t)(val & 0xffff)) << 32;
|
||||
env->macc[acc] = res;
|
||||
res = (uint32_t)env->macc[acc + 1];
|
||||
res |= (uint64_t)(val & 0xffff0000) << 16;
|
||||
env->macc[acc + 1] = res;
|
||||
}
|
||||
|
||||
void m68k_cpu_exec_enter(CPUState *cs)
|
||||
{
|
||||
M68kCPU *cpu = M68K_CPU(cs->uc, cs);
|
||||
CPUM68KState *env = &cpu->env;
|
||||
|
||||
env->cc_op = CC_OP_FLAGS;
|
||||
env->cc_dest = env->sr & 0xf;
|
||||
env->cc_x = (env->sr >> 4) & 1;
|
||||
}
|
||||
|
||||
void m68k_cpu_exec_exit(CPUState *cs)
|
||||
{
|
||||
M68kCPU *cpu = M68K_CPU(cs->uc, cs);
|
||||
CPUM68KState *env = &cpu->env;
|
||||
|
||||
cpu_m68k_flush_flags(env, env->cc_op);
|
||||
env->cc_op = CC_OP_FLAGS;
|
||||
env->sr = (env->sr & 0xffe0) | env->cc_dest | (env->cc_x << 4);
|
||||
}
|
||||
52
qemu/target-m68k/helper.h
Normal file
52
qemu/target-m68k/helper.h
Normal file
@@ -0,0 +1,52 @@
|
||||
DEF_HELPER_5(uc_tracecode, void, i32, ptr, ptr, i64, ptr)
|
||||
|
||||
DEF_HELPER_1(bitrev, i32, i32)
|
||||
DEF_HELPER_1(ff1, i32, i32)
|
||||
DEF_HELPER_2(sats, i32, i32, i32)
|
||||
DEF_HELPER_2(divu, void, env, i32)
|
||||
DEF_HELPER_2(divs, void, env, i32)
|
||||
DEF_HELPER_3(addx_cc, i32, env, i32, i32)
|
||||
DEF_HELPER_3(subx_cc, i32, env, i32, i32)
|
||||
DEF_HELPER_3(shl_cc, i32, env, i32, i32)
|
||||
DEF_HELPER_3(shr_cc, i32, env, i32, i32)
|
||||
DEF_HELPER_3(sar_cc, i32, env, i32, i32)
|
||||
DEF_HELPER_2(xflag_lt, i32, i32, i32)
|
||||
DEF_HELPER_2(set_sr, void, env, i32)
|
||||
DEF_HELPER_3(movec, void, env, i32, i32)
|
||||
|
||||
DEF_HELPER_2(f64_to_i32, f32, env, f64)
|
||||
DEF_HELPER_2(f64_to_f32, f32, env, f64)
|
||||
DEF_HELPER_2(i32_to_f64, f64, env, i32)
|
||||
DEF_HELPER_2(f32_to_f64, f64, env, f32)
|
||||
DEF_HELPER_2(iround_f64, f64, env, f64)
|
||||
DEF_HELPER_2(itrunc_f64, f64, env, f64)
|
||||
DEF_HELPER_2(sqrt_f64, f64, env, f64)
|
||||
DEF_HELPER_1(abs_f64, f64, f64)
|
||||
DEF_HELPER_1(chs_f64, f64, f64)
|
||||
DEF_HELPER_3(add_f64, f64, env, f64, f64)
|
||||
DEF_HELPER_3(sub_f64, f64, env, f64, f64)
|
||||
DEF_HELPER_3(mul_f64, f64, env, f64, f64)
|
||||
DEF_HELPER_3(div_f64, f64, env, f64, f64)
|
||||
DEF_HELPER_3(sub_cmp_f64, f64, env, f64, f64)
|
||||
DEF_HELPER_2(compare_f64, i32, env, f64)
|
||||
|
||||
DEF_HELPER_3(mac_move, void, env, i32, i32)
|
||||
DEF_HELPER_3(macmulf, i64, env, i32, i32)
|
||||
DEF_HELPER_3(macmuls, i64, env, i32, i32)
|
||||
DEF_HELPER_3(macmulu, i64, env, i32, i32)
|
||||
DEF_HELPER_2(macsats, void, env, i32)
|
||||
DEF_HELPER_2(macsatu, void, env, i32)
|
||||
DEF_HELPER_2(macsatf, void, env, i32)
|
||||
DEF_HELPER_2(mac_set_flags, void, env, i32)
|
||||
DEF_HELPER_2(set_macsr, void, env, i32)
|
||||
DEF_HELPER_2(get_macf, i32, env, i64)
|
||||
DEF_HELPER_1(get_macs, i32, i64)
|
||||
DEF_HELPER_1(get_macu, i32, i64)
|
||||
DEF_HELPER_2(get_mac_extf, i32, env, i32)
|
||||
DEF_HELPER_2(get_mac_exti, i32, env, i32)
|
||||
DEF_HELPER_3(set_mac_extf, void, env, i32, i32)
|
||||
DEF_HELPER_3(set_mac_exts, void, env, i32, i32)
|
||||
DEF_HELPER_3(set_mac_extu, void, env, i32, i32)
|
||||
|
||||
DEF_HELPER_2(flush_flags, void, env, i32)
|
||||
DEF_HELPER_2(raise_exception, void, env, i32)
|
||||
11
qemu/target-m68k/m68k-qreg.h
Normal file
11
qemu/target-m68k/m68k-qreg.h
Normal file
@@ -0,0 +1,11 @@
|
||||
enum {
|
||||
#define DEFO32(name, offset) QREG_##name,
|
||||
#define DEFR(name, reg, mode) QREG_##name,
|
||||
#define DEFF64(name, offset) QREG_##name,
|
||||
QREG_NULL,
|
||||
#include "qregs.def"
|
||||
TARGET_NUM_QREGS = 0x100
|
||||
#undef DEFO32
|
||||
#undef DEFR
|
||||
#undef DEFF64
|
||||
};
|
||||
115
qemu/target-m68k/m68k-semi.c
Normal file
115
qemu/target-m68k/m68k-semi.c
Normal file
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* m68k/ColdFire Semihosting syscall interface
|
||||
*
|
||||
* Copyright (c) 2005-2007 CodeSourcery.
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "cpu.h"
|
||||
#if defined(CONFIG_USER_ONLY)
|
||||
#include "qemu.h"
|
||||
#define SEMIHOSTING_HEAP_SIZE (128 * 1024 * 1024)
|
||||
#else
|
||||
#include "qemu-common.h"
|
||||
#include "exec/softmmu-semi.h"
|
||||
#endif
|
||||
#include "sysemu/sysemu.h"
|
||||
|
||||
#define HOSTED_EXIT 0
|
||||
#define HOSTED_INIT_SIM 1
|
||||
#define HOSTED_OPEN 2
|
||||
#define HOSTED_CLOSE 3
|
||||
#define HOSTED_READ 4
|
||||
#define HOSTED_WRITE 5
|
||||
#define HOSTED_LSEEK 6
|
||||
#define HOSTED_RENAME 7
|
||||
#define HOSTED_UNLINK 8
|
||||
#define HOSTED_STAT 9
|
||||
#define HOSTED_FSTAT 10
|
||||
#define HOSTED_GETTIMEOFDAY 11
|
||||
#define HOSTED_ISATTY 12
|
||||
#define HOSTED_SYSTEM 13
|
||||
|
||||
static void translate_stat(CPUM68KState *env, target_ulong addr, struct stat *s)
|
||||
{
|
||||
}
|
||||
|
||||
static void m68k_semi_return_u32(CPUM68KState *env, uint32_t ret, uint32_t err)
|
||||
{
|
||||
target_ulong args = env->dregs[1];
|
||||
if (put_user_u32(ret, args) ||
|
||||
put_user_u32(err, args + 4)) {
|
||||
/* The m68k semihosting ABI does not provide any way to report this
|
||||
* error to the guest, so the best we can do is log it in qemu.
|
||||
* It is always a guest error not to pass us a valid argument block.
|
||||
*/
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "m68k-semihosting: return value "
|
||||
"discarded because argument block not writable\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void m68k_semi_return_u64(CPUM68KState *env, uint64_t ret, uint32_t err)
|
||||
{
|
||||
target_ulong args = env->dregs[1];
|
||||
if (put_user_u32(ret >> 32, args) ||
|
||||
put_user_u32(ret, args + 4) ||
|
||||
put_user_u32(err, args + 8)) {
|
||||
/* No way to report this via m68k semihosting ABI; just log it */
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "m68k-semihosting: return value "
|
||||
"discarded because argument block not writable\n");
|
||||
}
|
||||
}
|
||||
|
||||
static int m68k_semi_is_fseek;
|
||||
|
||||
static void m68k_semi_cb(CPUState *cs, target_ulong ret, target_ulong err)
|
||||
{
|
||||
M68kCPU *cpu = M68K_CPU(cs->uc, cs);
|
||||
CPUM68KState *env = &cpu->env;
|
||||
|
||||
if (m68k_semi_is_fseek) {
|
||||
/* FIXME: We've already lost the high bits of the fseek
|
||||
return value. */
|
||||
m68k_semi_return_u64(env, ret, err);
|
||||
m68k_semi_is_fseek = 0;
|
||||
} else {
|
||||
m68k_semi_return_u32(env, ret, err);
|
||||
}
|
||||
}
|
||||
|
||||
/* Read the input value from the argument block; fail the semihosting
|
||||
* call if the memory read fails.
|
||||
*/
|
||||
#define GET_ARG(n) do { \
|
||||
if (get_user_ual(arg ## n, args + (n) * 4)) { \
|
||||
result = -1; \
|
||||
errno = EFAULT; \
|
||||
goto failed; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
void do_m68k_semihosting(CPUM68KState *env, int nr)
|
||||
{
|
||||
}
|
||||
225
qemu/target-m68k/op_helper.c
Normal file
225
qemu/target-m68k/op_helper.c
Normal file
@@ -0,0 +1,225 @@
|
||||
/*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "cpu.h"
|
||||
#include "exec/helper-proto.h"
|
||||
#include "exec/cpu_ldst.h"
|
||||
|
||||
#if defined(CONFIG_USER_ONLY)
|
||||
|
||||
void m68k_cpu_do_interrupt(CPUState *cs)
|
||||
{
|
||||
cs->exception_index = -1;
|
||||
}
|
||||
|
||||
static inline void do_interrupt_m68k_hardirq(CPUM68KState *env)
|
||||
{
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
extern int semihosting_enabled;
|
||||
|
||||
/* 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) */
|
||||
void tlb_fill(CPUState *cs, target_ulong addr, int is_write, int mmu_idx,
|
||||
uintptr_t retaddr)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = m68k_cpu_handle_mmu_fault(cs, addr, is_write, mmu_idx);
|
||||
if (unlikely(ret)) {
|
||||
if (retaddr) {
|
||||
/* now we have a real cpu fault */
|
||||
cpu_restore_state(cs, retaddr);
|
||||
}
|
||||
cpu_loop_exit(cs);
|
||||
}
|
||||
}
|
||||
|
||||
static void do_rte(CPUM68KState *env)
|
||||
{
|
||||
uint32_t sp;
|
||||
uint32_t fmt;
|
||||
|
||||
sp = env->aregs[7];
|
||||
fmt = cpu_ldl_kernel(env, sp);
|
||||
env->pc = cpu_ldl_kernel(env, sp + 4);
|
||||
sp |= (fmt >> 28) & 3;
|
||||
env->sr = fmt & 0xffff;
|
||||
m68k_switch_sp(env);
|
||||
env->aregs[7] = sp + 8;
|
||||
}
|
||||
|
||||
static void do_interrupt_all(CPUM68KState *env, int is_hw)
|
||||
{
|
||||
CPUState *cs = CPU(m68k_env_get_cpu(env));
|
||||
uint32_t sp;
|
||||
uint32_t fmt;
|
||||
uint32_t retaddr;
|
||||
uint32_t vector;
|
||||
|
||||
fmt = 0;
|
||||
retaddr = env->pc;
|
||||
|
||||
if (!is_hw) {
|
||||
switch (cs->exception_index) {
|
||||
case EXCP_RTE:
|
||||
/* Return from an exception. */
|
||||
do_rte(env);
|
||||
return;
|
||||
case EXCP_HALT_INSN:
|
||||
cs->halted = 1;
|
||||
cs->exception_index = EXCP_HLT;
|
||||
cpu_loop_exit(cs);
|
||||
return;
|
||||
}
|
||||
if (cs->exception_index >= EXCP_TRAP0
|
||||
&& cs->exception_index <= EXCP_TRAP15) {
|
||||
/* Move the PC after the trap instruction. */
|
||||
retaddr += 2;
|
||||
}
|
||||
}
|
||||
|
||||
vector = cs->exception_index << 2;
|
||||
|
||||
sp = env->aregs[7];
|
||||
|
||||
fmt |= 0x40000000;
|
||||
fmt |= (sp & 3) << 28;
|
||||
fmt |= vector << 16;
|
||||
fmt |= env->sr;
|
||||
|
||||
env->sr |= SR_S;
|
||||
if (is_hw) {
|
||||
env->sr = (env->sr & ~SR_I) | (env->pending_level << SR_I_SHIFT);
|
||||
env->sr &= ~SR_M;
|
||||
}
|
||||
m68k_switch_sp(env);
|
||||
|
||||
/* ??? This could cause MMU faults. */
|
||||
sp &= ~3;
|
||||
sp -= 4;
|
||||
cpu_stl_kernel(env, sp, retaddr);
|
||||
sp -= 4;
|
||||
cpu_stl_kernel(env, sp, fmt);
|
||||
env->aregs[7] = sp;
|
||||
/* Jump to vector. */
|
||||
env->pc = cpu_ldl_kernel(env, env->vbr + vector);
|
||||
}
|
||||
|
||||
void m68k_cpu_do_interrupt(CPUState *cs)
|
||||
{
|
||||
M68kCPU *cpu = M68K_CPU(cs->uc, cs);
|
||||
CPUM68KState *env = &cpu->env;
|
||||
|
||||
do_interrupt_all(env, 0);
|
||||
}
|
||||
|
||||
static inline void do_interrupt_m68k_hardirq(CPUM68KState *env)
|
||||
{
|
||||
do_interrupt_all(env, 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool m68k_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
|
||||
{
|
||||
M68kCPU *cpu = M68K_CPU(cs->uc, cs);
|
||||
CPUM68KState *env = &cpu->env;
|
||||
|
||||
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. */
|
||||
cs->exception_index = env->pending_vector;
|
||||
do_interrupt_m68k_hardirq(env);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void raise_exception(CPUM68KState *env, int tt)
|
||||
{
|
||||
CPUState *cs = CPU(m68k_env_get_cpu(env));
|
||||
|
||||
cs->exception_index = tt;
|
||||
cpu_loop_exit(cs);
|
||||
}
|
||||
|
||||
void HELPER(raise_exception)(CPUM68KState *env, uint32_t tt)
|
||||
{
|
||||
raise_exception(env, tt);
|
||||
}
|
||||
|
||||
void HELPER(divu)(CPUM68KState *env, uint32_t word)
|
||||
{
|
||||
uint32_t num;
|
||||
uint32_t den;
|
||||
uint32_t quot;
|
||||
uint32_t rem;
|
||||
uint32_t flags;
|
||||
|
||||
num = env->div1;
|
||||
den = env->div2;
|
||||
/* ??? This needs to make sure the throwing location is accurate. */
|
||||
if (den == 0) {
|
||||
raise_exception(env, EXCP_DIV0);
|
||||
}
|
||||
quot = num / den;
|
||||
rem = num % den;
|
||||
flags = 0;
|
||||
if (word && quot > 0xffff)
|
||||
flags |= CCF_V;
|
||||
if (quot == 0)
|
||||
flags |= CCF_Z;
|
||||
else if ((int32_t)quot < 0)
|
||||
flags |= CCF_N;
|
||||
env->div1 = quot;
|
||||
env->div2 = rem;
|
||||
env->cc_dest = flags;
|
||||
}
|
||||
|
||||
void HELPER(divs)(CPUM68KState *env, uint32_t word)
|
||||
{
|
||||
int32_t num;
|
||||
int32_t den;
|
||||
int32_t quot;
|
||||
int32_t rem;
|
||||
int32_t flags;
|
||||
|
||||
num = env->div1;
|
||||
den = env->div2;
|
||||
if (den == 0) {
|
||||
raise_exception(env, EXCP_DIV0);
|
||||
}
|
||||
quot = num / den;
|
||||
rem = num % den;
|
||||
flags = 0;
|
||||
if (word && quot != (int16_t)quot)
|
||||
flags |= CCF_V;
|
||||
if (quot == 0)
|
||||
flags |= CCF_Z;
|
||||
else if (quot < 0)
|
||||
flags |= CCF_N;
|
||||
env->div1 = quot;
|
||||
env->div2 = rem;
|
||||
env->cc_dest = flags;
|
||||
}
|
||||
11
qemu/target-m68k/qregs.def
Normal file
11
qemu/target-m68k/qregs.def
Normal file
@@ -0,0 +1,11 @@
|
||||
DEFF64(FP_RESULT, fp_result)
|
||||
DEFO32(PC, pc)
|
||||
DEFO32(SR, sr)
|
||||
DEFO32(CC_OP, cc_op)
|
||||
DEFO32(CC_DEST, cc_dest)
|
||||
DEFO32(CC_SRC, cc_src)
|
||||
DEFO32(CC_X, cc_x)
|
||||
DEFO32(DIV1, div1)
|
||||
DEFO32(DIV2, div2)
|
||||
DEFO32(MACSR, macsr)
|
||||
DEFO32(MAC_MASK, mac_mask)
|
||||
3206
qemu/target-m68k/translate.c
Normal file
3206
qemu/target-m68k/translate.c
Normal file
File diff suppressed because it is too large
Load Diff
96
qemu/target-m68k/unicorn.c
Normal file
96
qemu/target-m68k/unicorn.c
Normal file
@@ -0,0 +1,96 @@
|
||||
/* Unicorn Emulator Engine */
|
||||
/* By Nguyen Anh Quynh <aquynh@gmail.com>, 2015 */
|
||||
|
||||
#include "hw/boards.h"
|
||||
#include "hw/m68k/m68k.h"
|
||||
#include "sysemu/cpus.h"
|
||||
#include "unicorn.h"
|
||||
#include "cpu.h"
|
||||
|
||||
#include "unicorn_common.h"
|
||||
|
||||
#define READ_QWORD(x) ((uint64)x)
|
||||
#define READ_DWORD(x) (x & 0xffffffff)
|
||||
#define READ_WORD(x) (x & 0xffff)
|
||||
#define READ_BYTE_H(x) ((x & 0xffff) >> 8)
|
||||
#define READ_BYTE_L(x) (x & 0xff)
|
||||
|
||||
|
||||
static void m68k_set_pc(struct uc_struct *uc, uint64_t address)
|
||||
{
|
||||
((CPUM68KState *)uc->current_cpu->env_ptr)->pc = address;
|
||||
}
|
||||
|
||||
void m68k_reg_reset(uch handle)
|
||||
{
|
||||
struct uc_struct *uc = (struct uc_struct *) handle;
|
||||
CPUArchState *env;
|
||||
|
||||
env = first_cpu->env_ptr;
|
||||
memset(env->aregs, 0, sizeof(env->aregs));
|
||||
memset(env->dregs, 0, sizeof(env->dregs));
|
||||
|
||||
env->pc = 0;
|
||||
}
|
||||
|
||||
int m68k_reg_read(uch handle, unsigned int regid, void *value)
|
||||
{
|
||||
struct uc_struct *uc = (struct uc_struct *)handle;
|
||||
CPUState *mycpu = first_cpu;
|
||||
|
||||
if (regid >= M68K_REG_A0 && regid <= M68K_REG_A7)
|
||||
*(int32_t *)value = M68K_CPU(uc, mycpu)->env.aregs[regid - M68K_REG_A0];
|
||||
else if (regid >= M68K_REG_D0 && regid <= M68K_REG_D7)
|
||||
*(int32_t *)value = M68K_CPU(uc, mycpu)->env.dregs[regid - M68K_REG_D0];
|
||||
else {
|
||||
switch(regid) {
|
||||
default: break;
|
||||
case M68K_REG_PC:
|
||||
*(int32_t *)value = M68K_CPU(uc, mycpu)->env.pc;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#define WRITE_DWORD(x, w) (x = (x & ~0xffffffff) | (w & 0xffffffff))
|
||||
#define WRITE_WORD(x, w) (x = (x & ~0xffff) | (w & 0xffff))
|
||||
#define WRITE_BYTE_H(x, b) (x = (x & ~0xff00) | (b & 0xff))
|
||||
#define WRITE_BYTE_L(x, b) (x = (x & ~0xff) | (b & 0xff))
|
||||
|
||||
int m68k_reg_write(uch handle, unsigned int regid, void *value)
|
||||
{
|
||||
struct uc_struct *uc = (struct uc_struct *) handle;
|
||||
CPUState *mycpu = first_cpu;
|
||||
|
||||
if (regid >= M68K_REG_A0 && regid <= M68K_REG_A7)
|
||||
M68K_CPU(uc, mycpu)->env.aregs[regid - M68K_REG_A0] = *(int32_t *)value;
|
||||
else if (regid >= M68K_REG_D0 && regid <= M68K_REG_D7)
|
||||
M68K_CPU(uc, mycpu)->env.dregs[regid - M68K_REG_D0] = *(int32_t *)value;
|
||||
else {
|
||||
switch(regid) {
|
||||
default: break;
|
||||
case M68K_REG_PC:
|
||||
M68K_CPU(uc, mycpu)->env.pc = *(uint32_t *)value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
__attribute__ ((visibility ("default")))
|
||||
void m68k_uc_init(struct uc_struct* uc)
|
||||
{
|
||||
register_accel_types(uc);
|
||||
m68k_cpu_register_types(uc);
|
||||
dummy_m68k_machine_init(uc);
|
||||
uc->reg_read = m68k_reg_read;
|
||||
uc->reg_write = m68k_reg_write;
|
||||
uc->reg_reset = m68k_reg_reset;
|
||||
uc->set_pc = m68k_set_pc;
|
||||
uc_common_init(uc);
|
||||
}
|
||||
15
qemu/target-m68k/unicorn.h
Normal file
15
qemu/target-m68k/unicorn.h
Normal file
@@ -0,0 +1,15 @@
|
||||
/* Unicorn Emulator Engine */
|
||||
/* By Nguyen Anh Quynh <aquynh@gmail.com>, 2015 */
|
||||
|
||||
#ifndef UC_QEMU_TARGET_M68K_H
|
||||
#define UC_QEMU_TARGET_M68K_H
|
||||
|
||||
// functions to read & write registers
|
||||
int m68k_reg_read(uch handle, unsigned int regid, void *value);
|
||||
int m68k_reg_write(uch handle, unsigned int regid, void *value);
|
||||
|
||||
void m68k_reg_reset(uch handle);
|
||||
|
||||
void m68k_uc_init(struct uc_struct* uc);
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user