Browse Source
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1464 c046a42c-6fe2-441c-8c8c-71466251a162stable-0.10
22 changed files with 4701 additions and 1 deletions
@ -0,0 +1,309 @@ |
|||
#include "vl.h" |
|||
|
|||
#define DEBUG_IRQ_COUNT |
|||
|
|||
#define BIOS_FILENAME "mips_bios.bin" |
|||
//#define BIOS_FILENAME "system.bin"
|
|||
#define KERNEL_LOAD_ADDR 0x80010000 |
|||
#define INITRD_LOAD_ADDR 0x80800000 |
|||
|
|||
/* MIPS R4K IRQ controler */ |
|||
#if defined(DEBUG_IRQ_COUNT) |
|||
static uint64_t irq_count[16]; |
|||
#endif |
|||
|
|||
extern FILE *logfile; |
|||
|
|||
void mips_set_irq (int n_IRQ, int level) |
|||
{ |
|||
uint32_t mask; |
|||
|
|||
if (n_IRQ < 0 || n_IRQ >= 8) |
|||
return; |
|||
mask = 0x100 << n_IRQ; |
|||
if (level != 0) { |
|||
#if 1 |
|||
if (logfile) { |
|||
fprintf(logfile, "%s n %d l %d mask %08x %08x\n", |
|||
__func__, n_IRQ, level, mask, cpu_single_env->CP0_Status); |
|||
} |
|||
#endif |
|||
cpu_single_env->CP0_Cause |= mask; |
|||
if ((cpu_single_env->CP0_Status & 0x00000001) && |
|||
(cpu_single_env->CP0_Status & mask)) { |
|||
#if defined(DEBUG_IRQ_COUNT) |
|||
irq_count[n_IRQ]++; |
|||
#endif |
|||
#if 1 |
|||
if (logfile) |
|||
fprintf(logfile, "%s raise IRQ\n", __func__); |
|||
#endif |
|||
cpu_interrupt(cpu_single_env, CPU_INTERRUPT_HARD); |
|||
} |
|||
} else { |
|||
cpu_single_env->CP0_Cause &= ~mask; |
|||
} |
|||
} |
|||
|
|||
void pic_set_irq (int n_IRQ, int level) |
|||
{ |
|||
mips_set_irq(n_IRQ + 2, level); |
|||
} |
|||
|
|||
void pic_info (void) |
|||
{ |
|||
term_printf("IRQ asserted: %02x mask: %02x\n", |
|||
(cpu_single_env->CP0_Cause >> 8) & 0xFF, |
|||
(cpu_single_env->CP0_Status >> 8) & 0xFF); |
|||
} |
|||
|
|||
void irq_info (void) |
|||
{ |
|||
#if !defined(DEBUG_IRQ_COUNT) |
|||
term_printf("irq statistic code not compiled.\n"); |
|||
#else |
|||
int i; |
|||
int64_t count; |
|||
|
|||
term_printf("IRQ statistics:\n"); |
|||
for (i = 0; i < 8; i++) { |
|||
count = irq_count[i]; |
|||
if (count > 0) |
|||
term_printf("%2d: %lld\n", i, count); |
|||
} |
|||
#endif |
|||
} |
|||
|
|||
void cpu_mips_irqctrl_init (void) |
|||
{ |
|||
} |
|||
|
|||
/* MIPS R4K timer */ |
|||
uint32_t cpu_mips_get_random (CPUState *env) |
|||
{ |
|||
uint64_t now = qemu_get_clock(vm_clock); |
|||
|
|||
return (uint32_t)now & 0x0000000F; |
|||
} |
|||
|
|||
uint32_t cpu_mips_get_count (CPUState *env) |
|||
{ |
|||
return env->CP0_Count + |
|||
(uint32_t)muldiv64(qemu_get_clock(vm_clock), |
|||
100 * 1000 * 1000, ticks_per_sec); |
|||
} |
|||
|
|||
static void cpu_mips_update_count (CPUState *env, uint32_t count, |
|||
uint32_t compare) |
|||
{ |
|||
uint64_t now, next; |
|||
uint32_t tmp; |
|||
|
|||
tmp = count; |
|||
if (count == compare) |
|||
tmp++; |
|||
now = qemu_get_clock(vm_clock); |
|||
next = now + muldiv64(compare - tmp, ticks_per_sec, 100 * 1000 * 1000); |
|||
if (next == now) |
|||
next++; |
|||
#if 1 |
|||
if (logfile) { |
|||
fprintf(logfile, "%s: 0x%08llx %08x %08x => 0x%08llx\n", |
|||
__func__, now, count, compare, next - now); |
|||
} |
|||
#endif |
|||
/* Store new count and compare registers */ |
|||
env->CP0_Compare = compare; |
|||
env->CP0_Count = |
|||
count - (uint32_t)muldiv64(now, 100 * 1000 * 1000, ticks_per_sec); |
|||
/* Adjust timer */ |
|||
qemu_mod_timer(env->timer, next); |
|||
} |
|||
|
|||
void cpu_mips_store_count (CPUState *env, uint32_t value) |
|||
{ |
|||
cpu_mips_update_count(env, value, env->CP0_Compare); |
|||
} |
|||
|
|||
void cpu_mips_store_compare (CPUState *env, uint32_t value) |
|||
{ |
|||
cpu_mips_update_count(env, cpu_mips_get_count(env), value); |
|||
pic_set_irq(5, 0); |
|||
} |
|||
|
|||
static void mips_timer_cb (void *opaque) |
|||
{ |
|||
CPUState *env; |
|||
|
|||
env = opaque; |
|||
#if 1 |
|||
if (logfile) { |
|||
fprintf(logfile, "%s\n", __func__); |
|||
} |
|||
#endif |
|||
cpu_mips_update_count(env, cpu_mips_get_count(env), env->CP0_Compare); |
|||
pic_set_irq(5, 1); |
|||
} |
|||
|
|||
void cpu_mips_clock_init (CPUState *env) |
|||
{ |
|||
env->timer = qemu_new_timer(vm_clock, &mips_timer_cb, env); |
|||
env->CP0_Compare = 0; |
|||
cpu_mips_update_count(env, 1, 0); |
|||
} |
|||
|
|||
static void io_writeb (void *opaque, target_phys_addr_t addr, uint32_t value) |
|||
{ |
|||
if (logfile) |
|||
fprintf(logfile, "%s: addr %08x val %08x\n", __func__, addr, value); |
|||
cpu_outb(NULL, addr & 0xffff, value); |
|||
} |
|||
|
|||
static uint32_t io_readb (void *opaque, target_phys_addr_t addr) |
|||
{ |
|||
uint32_t ret = cpu_inb(NULL, addr & 0xffff); |
|||
if (logfile) |
|||
fprintf(logfile, "%s: addr %08x val %08x\n", __func__, addr, ret); |
|||
return ret; |
|||
} |
|||
|
|||
static void io_writew (void *opaque, target_phys_addr_t addr, uint32_t value) |
|||
{ |
|||
if (logfile) |
|||
fprintf(logfile, "%s: addr %08x val %08x\n", __func__, addr, value); |
|||
#ifdef TARGET_WORDS_BIGENDIAN |
|||
value = bswap16(value); |
|||
#endif |
|||
cpu_outw(NULL, addr & 0xffff, value); |
|||
} |
|||
|
|||
static uint32_t io_readw (void *opaque, target_phys_addr_t addr) |
|||
{ |
|||
uint32_t ret = cpu_inw(NULL, addr & 0xffff); |
|||
#ifdef TARGET_WORDS_BIGENDIAN |
|||
ret = bswap16(ret); |
|||
#endif |
|||
if (logfile) |
|||
fprintf(logfile, "%s: addr %08x val %08x\n", __func__, addr, ret); |
|||
return ret; |
|||
} |
|||
|
|||
static void io_writel (void *opaque, target_phys_addr_t addr, uint32_t value) |
|||
{ |
|||
if (logfile) |
|||
fprintf(logfile, "%s: addr %08x val %08x\n", __func__, addr, value); |
|||
#ifdef TARGET_WORDS_BIGENDIAN |
|||
value = bswap32(value); |
|||
#endif |
|||
cpu_outl(NULL, addr & 0xffff, value); |
|||
} |
|||
|
|||
static uint32_t io_readl (void *opaque, target_phys_addr_t addr) |
|||
{ |
|||
uint32_t ret = cpu_inl(NULL, addr & 0xffff); |
|||
|
|||
#ifdef TARGET_WORDS_BIGENDIAN |
|||
ret = bswap32(ret); |
|||
#endif |
|||
if (logfile) |
|||
fprintf(logfile, "%s: addr %08x val %08x\n", __func__, addr, ret); |
|||
return ret; |
|||
} |
|||
|
|||
CPUWriteMemoryFunc *io_write[] = { |
|||
&io_writeb, |
|||
&io_writew, |
|||
&io_writel, |
|||
}; |
|||
|
|||
CPUReadMemoryFunc *io_read[] = { |
|||
&io_readb, |
|||
&io_readw, |
|||
&io_readl, |
|||
}; |
|||
|
|||
void mips_r4k_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) |
|||
{ |
|||
char buf[1024]; |
|||
target_ulong kernel_base, kernel_size, initrd_base, initrd_size; |
|||
unsigned long bios_offset; |
|||
int io_memory; |
|||
int linux_boot; |
|||
int ret; |
|||
|
|||
printf("%s: start\n", __func__); |
|||
linux_boot = (kernel_filename != NULL); |
|||
/* allocate RAM */ |
|||
cpu_register_physical_memory(0, ram_size, IO_MEM_RAM); |
|||
bios_offset = ram_size + vga_ram_size; |
|||
snprintf(buf, sizeof(buf), "%s/%s", bios_dir, BIOS_FILENAME); |
|||
printf("%s: load BIOS '%s' size %d\n", __func__, buf, BIOS_SIZE); |
|||
ret = load_image(buf, phys_ram_base + bios_offset); |
|||
if (ret != BIOS_SIZE) { |
|||
fprintf(stderr, "qemu: could not load MIPS bios '%s'\n", buf); |
|||
exit(1); |
|||
} |
|||
cpu_register_physical_memory((uint32_t)(0x1fc00000), |
|||
BIOS_SIZE, bios_offset | IO_MEM_ROM); |
|||
#if 0 |
|||
memcpy(phys_ram_base + 0x10000, phys_ram_base + bios_offset, BIOS_SIZE); |
|||
cpu_single_env->PC = 0x80010004; |
|||
#else |
|||
cpu_single_env->PC = 0xBFC00004; |
|||
#endif |
|||
if (linux_boot) { |
|||
kernel_base = KERNEL_LOAD_ADDR; |
|||
/* now we can load the kernel */ |
|||
kernel_size = load_image(kernel_filename, phys_ram_base + kernel_base); |
|||
if (kernel_size < 0) { |
|||
fprintf(stderr, "qemu: could not load kernel '%s'\n", |
|||
kernel_filename); |
|||
exit(1); |
|||
} |
|||
/* load initrd */ |
|||
if (initrd_filename) { |
|||
initrd_base = INITRD_LOAD_ADDR; |
|||
initrd_size = load_image(initrd_filename, |
|||
phys_ram_base + initrd_base); |
|||
if (initrd_size < 0) { |
|||
fprintf(stderr, "qemu: could not load initial ram disk '%s'\n", |
|||
initrd_filename); |
|||
exit(1); |
|||
} |
|||
} else { |
|||
initrd_base = 0; |
|||
initrd_size = 0; |
|||
} |
|||
cpu_single_env->PC = KERNEL_LOAD_ADDR; |
|||
} else { |
|||
kernel_base = 0; |
|||
kernel_size = 0; |
|||
initrd_base = 0; |
|||
initrd_size = 0; |
|||
} |
|||
/* XXX: should not be ! */ |
|||
printf("%s: init VGA\n", __func__); |
|||
vga_initialize(NULL, ds, phys_ram_base + ram_size, ram_size, |
|||
vga_ram_size); |
|||
|
|||
|
|||
/* Init internal devices */ |
|||
cpu_mips_clock_init(cpu_single_env); |
|||
cpu_mips_irqctrl_init(); |
|||
|
|||
isa_mem_base = 0x78000000; |
|||
/* Register 64 KB of ISA IO space at random address */ |
|||
io_memory = cpu_register_io_memory(0, io_read, io_write, NULL); |
|||
cpu_register_physical_memory(0x70000000, 0x00010000, io_memory); |
|||
serial_init(0x3f8, 4, serial_hds[0]); |
|||
printf("%s: done\n", __func__); |
|||
} |
|||
|
|||
QEMUMachine mips_machine = { |
|||
"mips", |
|||
"mips r4k platform", |
|||
mips_r4k_init, |
|||
}; |
|||
@ -0,0 +1,415 @@ |
|||
#if !defined (__MIPS_CPU_H__) |
|||
#define __MIPS_CPU_H__ |
|||
|
|||
#include "mips-defs.h" |
|||
#include "cpu-defs.h" |
|||
#include "config.h" |
|||
#include "softfloat.h" |
|||
|
|||
typedef union fpr_t fpr_t; |
|||
union fpr_t { |
|||
double d; |
|||
float f; |
|||
uint32_t u[2]; |
|||
}; |
|||
|
|||
#if defined(MIPS_USES_R4K_TLB) |
|||
typedef struct tlb_t tlb_t; |
|||
struct tlb_t { |
|||
target_ulong VPN; |
|||
target_ulong end; |
|||
uint8_t ASID; |
|||
uint8_t G; |
|||
uint8_t C[2]; |
|||
uint8_t V[2]; |
|||
uint8_t D[2]; |
|||
target_ulong PFN[2]; |
|||
}; |
|||
#endif |
|||
|
|||
typedef struct CPUMIPSState CPUMIPSState; |
|||
struct CPUMIPSState { |
|||
/* General integer registers */ |
|||
target_ulong gpr[32]; |
|||
/* Special registers */ |
|||
target_ulong PC; |
|||
uint32_t HI, LO; |
|||
uint32_t DCR; /* ? */ |
|||
#if defined(MIPS_USES_FPU) |
|||
/* Floating point registers */ |
|||
fpr_t fpr[16]; |
|||
/* Floating point special purpose registers */ |
|||
uint32_t fcr0; |
|||
uint32_t fcr25; |
|||
uint32_t fcr26; |
|||
uint32_t fcr28; |
|||
uint32_t fcsr; |
|||
#endif |
|||
#if defined(MIPS_USES_R4K_TLB) |
|||
tlb_t tlb[16]; |
|||
#endif |
|||
uint32_t CP0_index; |
|||
uint32_t CP0_random; |
|||
uint32_t CP0_EntryLo0; |
|||
uint32_t CP0_EntryLo1; |
|||
uint32_t CP0_Context; |
|||
uint32_t CP0_PageMask; |
|||
uint32_t CP0_Wired; |
|||
uint32_t CP0_BadVAddr; |
|||
uint32_t CP0_Count; |
|||
uint32_t CP0_EntryHi; |
|||
uint32_t CP0_Compare; |
|||
uint32_t CP0_Status; |
|||
#define CP0St_CU3 31 |
|||
#define CP0St_CU2 30 |
|||
#define CP0St_CU1 29 |
|||
#define CP0St_CU0 28 |
|||
#define CP0St_RP 27 |
|||
#define CP0St_RE 25 |
|||
#define CP0St_BEV 22 |
|||
#define CP0St_TS 21 |
|||
#define CP0St_SR 20 |
|||
#define CP0St_NMI 19 |
|||
#define CP0St_IM 8 |
|||
#define CP0St_UM 4 |
|||
#define CP0St_ERL 2 |
|||
#define CP0St_EXL 1 |
|||
#define CP0St_IE 0 |
|||
uint32_t CP0_Cause; |
|||
#define CP0Ca_IV 23 |
|||
uint32_t CP0_EPC; |
|||
uint32_t CP0_PRid; |
|||
uint32_t CP0_Config0; |
|||
#define CP0C0_M 31 |
|||
#define CP0C0_K23 28 |
|||
#define CP0C0_KU 25 |
|||
#define CP0C0_MDU 20 |
|||
#define CP0C0_MM 17 |
|||
#define CP0C0_BM 16 |
|||
#define CP0C0_BE 15 |
|||
#define CP0C0_AT 13 |
|||
#define CP0C0_AR 10 |
|||
#define CP0C0_MT 7 |
|||
#define CP0C0_K0 0 |
|||
uint32_t CP0_Config1; |
|||
#define CP0C1_MMU 25 |
|||
#define CP0C1_IS 22 |
|||
#define CP0C1_IL 19 |
|||
#define CP0C1_IA 16 |
|||
#define CP0C1_DS 13 |
|||
#define CP0C1_DL 10 |
|||
#define CP0C1_DA 7 |
|||
#define CP0C1_PC 4 |
|||
#define CP0C1_WR 3 |
|||
#define CP0C1_CA 2 |
|||
#define CP0C1_EP 1 |
|||
#define CP0C1_FP 0 |
|||
uint32_t CP0_LLAddr; |
|||
uint32_t CP0_WatchLo; |
|||
uint32_t CP0_WatchHi; |
|||
uint32_t CP0_Debug; |
|||
#define CPDB_DBD 31 |
|||
#define CP0DB_DM 30 |
|||
#define CP0DB_LSNM 28 |
|||
#define CP0DB_Doze 27 |
|||
#define CP0DB_Halt 26 |
|||
#define CP0DB_CNT 25 |
|||
#define CP0DB_IBEP 24 |
|||
#define CP0DB_DBEP 21 |
|||
#define CP0DB_IEXI 20 |
|||
#define CP0DB_VER 15 |
|||
#define CP0DB_DEC 10 |
|||
#define CP0DB_SSt 8 |
|||
#define CP0DB_DINT 5 |
|||
#define CP0DB_DIB 4 |
|||
#define CP0DB_DDBS 3 |
|||
#define CP0DB_DDBL 2 |
|||
#define CP0DB_DBp 1 |
|||
#define CP0DB_DSS 0 |
|||
uint32_t CP0_DEPC; |
|||
uint32_t CP0_TagLo; |
|||
uint32_t CP0_DataLo; |
|||
uint32_t CP0_ErrorEPC; |
|||
uint32_t CP0_DESAVE; |
|||
/* Qemu */ |
|||
#if defined (USE_HOST_FLOAT_REGS) && defined(MIPS_USES_FPU) |
|||
double ft0, ft1, ft2; |
|||
#endif |
|||
struct QEMUTimer *timer; /* Internal timer */ |
|||
int interrupt_request; |
|||
jmp_buf jmp_env; |
|||
int exception_index; |
|||
int error_code; |
|||
int user_mode_only; /* user mode only simulation */ |
|||
uint32_t hflags; /* CPU State */ |
|||
/* TMASK defines different execution modes */ |
|||
#define MIPS_HFLAGS_TMASK 0x00FF |
|||
#define MIPS_HFLAG_MODE 0x001F /* execution modes */ |
|||
#define MIPS_HFLAG_UM 0x0001 /* user mode */ |
|||
#define MIPS_HFLAG_ERL 0x0002 /* Error mode */ |
|||
#define MIPS_HFLAG_EXL 0x0004 /* Exception mode */ |
|||
#define MIPS_HFLAG_DM 0x0008 /* Debug mode */ |
|||
#define MIPS_HFLAG_SM 0x0010 /* Supervisor mode */ |
|||
#define MIPS_HFLAG_RE 0x0040 /* Reversed endianness */ |
|||
#define MIPS_HFLAG_DS 0x0080 /* In / out of delay slot */ |
|||
/* Those flags keep the branch state if the translation is interrupted
|
|||
* between the branch instruction and the delay slot |
|||
*/ |
|||
#define MIPS_HFLAG_BMASK 0x0F00 |
|||
#define MIPS_HFLAG_B 0x0100 /* Unconditional branch */ |
|||
#define MIPS_HFLAG_BC 0x0200 /* Conditional branch */ |
|||
#define MIPS_HFLAG_BL 0x0400 /* Likely branch */ |
|||
#define MIPS_HFLAG_BR 0x0800 /* branch to register (can't link TB) */ |
|||
target_ulong btarget; /* Jump / branch target */ |
|||
int bcond; /* Branch condition (if needed) */ |
|||
struct TranslationBlock *current_tb; /* currently executing TB */ |
|||
/* soft mmu support */ |
|||
/* in order to avoid passing too many arguments to the memory
|
|||
write helpers, we store some rarely used information in the CPU |
|||
context) */ |
|||
target_ulong mem_write_pc; /* host pc at which the memory was
|
|||
written */ |
|||
unsigned long mem_write_vaddr; /* target virtual addr at which the
|
|||
memory was written */ |
|||
/* 0 = kernel, 1 = user (may have 2 = kernel code, 3 = user code ?) */ |
|||
CPUTLBEntry tlb_read[2][CPU_TLB_SIZE]; |
|||
CPUTLBEntry tlb_write[2][CPU_TLB_SIZE]; |
|||
/* ice debug support */ |
|||
target_ulong breakpoints[MAX_BREAKPOINTS]; |
|||
int nb_breakpoints; |
|||
int singlestep_enabled; /* XXX: should use CPU single step mode instead */ |
|||
/* user data */ |
|||
void *opaque; |
|||
}; |
|||
|
|||
#include "cpu-all.h" |
|||
|
|||
/* Memory access type :
|
|||
* may be needed for precise access rights control and precise exceptions. |
|||
*/ |
|||
enum { |
|||
/* 1 bit to define user level / supervisor access */ |
|||
ACCESS_USER = 0x00, |
|||
ACCESS_SUPER = 0x01, |
|||
/* 1 bit to indicate direction */ |
|||
ACCESS_STORE = 0x02, |
|||
/* Type of instruction that generated the access */ |
|||
ACCESS_CODE = 0x10, /* Code fetch access */ |
|||
ACCESS_INT = 0x20, /* Integer load/store access */ |
|||
ACCESS_FLOAT = 0x30, /* floating point load/store access */ |
|||
}; |
|||
|
|||
/* Exceptions */ |
|||
enum { |
|||
EXCP_NONE = -1, |
|||
EXCP_RESET = 0, |
|||
EXCP_SRESET, |
|||
EXCP_DSS, |
|||
EXCP_DINT, |
|||
EXCP_NMI, |
|||
EXCP_MCHECK, |
|||
EXCP_EXT_INTERRUPT, |
|||
EXCP_DFWATCH, |
|||
EXCP_DIB, /* 8 */ |
|||
EXCP_IWATCH, |
|||
EXCP_AdEL, |
|||
EXCP_AdES, |
|||
EXCP_TLBF, |
|||
EXCP_IBE, |
|||
EXCP_DBp, |
|||
EXCP_SYSCALL, |
|||
EXCP_BREAK, |
|||
EXCP_CpU, /* 16 */ |
|||
EXCP_RI, |
|||
EXCP_OVERFLOW, |
|||
EXCP_TRAP, |
|||
EXCP_DDBS, |
|||
EXCP_DWATCH, |
|||
EXCP_LAE, /* 22 */ |
|||
EXCP_SAE, |
|||
EXCP_LTLBL, |
|||
EXCP_TLBL, |
|||
EXCP_TLBS, |
|||
EXCP_DBE, |
|||
EXCP_DDBL, |
|||
EXCP_MTCP0 = 0x104, /* mtmsr instruction: */ |
|||
/* may change privilege level */ |
|||
EXCP_BRANCH = 0x108, /* branch instruction */ |
|||
EXCP_ERET = 0x10C, /* return from interrupt */ |
|||
EXCP_SYSCALL_USER = 0x110, /* System call in user mode only */ |
|||
EXCP_FLUSH = 0x109, |
|||
}; |
|||
|
|||
/* MIPS opcodes */ |
|||
#define EXT_SPECIAL 0x100 |
|||
#define EXT_SPECIAL2 0x200 |
|||
#define EXT_REGIMM 0x300 |
|||
#define EXT_CP0 0x400 |
|||
#define EXT_CP1 0x500 |
|||
#define EXT_CP2 0x600 |
|||
#define EXT_CP3 0x700 |
|||
|
|||
enum { |
|||
/* indirect opcode tables */ |
|||
OPC_SPECIAL = 0x00, |
|||
OPC_BREGIMM = 0x01, |
|||
OPC_CP0 = 0x10, |
|||
OPC_CP1 = 0x11, |
|||
OPC_CP2 = 0x12, |
|||
OPC_CP3 = 0x13, |
|||
OPC_SPECIAL2 = 0x1C, |
|||
/* arithmetic with immediate */ |
|||
OPC_ADDI = 0x08, |
|||
OPC_ADDIU = 0x09, |
|||
OPC_SLTI = 0x0A, |
|||
OPC_SLTIU = 0x0B, |
|||
OPC_ANDI = 0x0C, |
|||
OPC_ORI = 0x0D, |
|||
OPC_XORI = 0x0E, |
|||
OPC_LUI = 0x0F, |
|||
/* Jump and branches */ |
|||
OPC_J = 0x02, |
|||
OPC_JAL = 0x03, |
|||
OPC_BEQ = 0x04, /* Unconditional if rs = rt = 0 (B) */ |
|||
OPC_BEQL = 0x14, |
|||
OPC_BNE = 0x05, |
|||
OPC_BNEL = 0x15, |
|||
OPC_BLEZ = 0x06, |
|||
OPC_BLEZL = 0x16, |
|||
OPC_BGTZ = 0x07, |
|||
OPC_BGTZL = 0x17, |
|||
OPC_JALX = 0x1D, /* MIPS 16 only */ |
|||
/* Load and stores */ |
|||
OPC_LB = 0x20, |
|||
OPC_LH = 0x21, |
|||
OPC_LWL = 0x22, |
|||
OPC_LW = 0x23, |
|||
OPC_LBU = 0x24, |
|||
OPC_LHU = 0x25, |
|||
OPC_LWR = 0x26, |
|||
OPC_SB = 0x28, |
|||
OPC_SH = 0x29, |
|||
OPC_SWL = 0x2A, |
|||
OPC_SW = 0x2B, |
|||
OPC_SWR = 0x2E, |
|||
OPC_LL = 0x30, |
|||
OPC_SC = 0x38, |
|||
/* Floating point load/store */ |
|||
OPC_LWC1 = 0x31, |
|||
OPC_LWC2 = 0x32, |
|||
OPC_LDC1 = 0x35, |
|||
OPC_LDC2 = 0x36, |
|||
OPC_SWC1 = 0x39, |
|||
OPC_SWC2 = 0x3A, |
|||
OPC_SDC1 = 0x3D, |
|||
OPC_SDC2 = 0x3E, |
|||
/* Cache and prefetch */ |
|||
OPC_CACHE = 0x2F, |
|||
OPC_PREF = 0x33, |
|||
}; |
|||
|
|||
/* MIPS special opcodes */ |
|||
enum { |
|||
/* Shifts */ |
|||
OPC_SLL = 0x00 | EXT_SPECIAL, |
|||
/* NOP is SLL r0, r0, 0 */ |
|||
/* SSNOP is SLL r0, r0, 1 */ |
|||
OPC_SRL = 0x02 | EXT_SPECIAL, |
|||
OPC_SRA = 0x03 | EXT_SPECIAL, |
|||
OPC_SLLV = 0x04 | EXT_SPECIAL, |
|||
OPC_SRLV = 0x06 | EXT_SPECIAL, |
|||
OPC_SRAV = 0x07 | EXT_SPECIAL, |
|||
/* Multiplication / division */ |
|||
OPC_MULT = 0x18 | EXT_SPECIAL, |
|||
OPC_MULTU = 0x19 | EXT_SPECIAL, |
|||
OPC_DIV = 0x1A | EXT_SPECIAL, |
|||
OPC_DIVU = 0x1B | EXT_SPECIAL, |
|||
/* 2 registers arithmetic / logic */ |
|||
OPC_ADD = 0x20 | EXT_SPECIAL, |
|||
OPC_ADDU = 0x21 | EXT_SPECIAL, |
|||
OPC_SUB = 0x22 | EXT_SPECIAL, |
|||
OPC_SUBU = 0x23 | EXT_SPECIAL, |
|||
OPC_AND = 0x24 | EXT_SPECIAL, |
|||
OPC_OR = 0x25 | EXT_SPECIAL, |
|||
OPC_XOR = 0x26 | EXT_SPECIAL, |
|||
OPC_NOR = 0x27 | EXT_SPECIAL, |
|||
OPC_SLT = 0x2A | EXT_SPECIAL, |
|||
OPC_SLTU = 0x2B | EXT_SPECIAL, |
|||
/* Jumps */ |
|||
OPC_JR = 0x08 | EXT_SPECIAL, |
|||
OPC_JALR = 0x09 | EXT_SPECIAL, |
|||
/* Traps */ |
|||
OPC_TGE = 0x30 | EXT_SPECIAL, |
|||
OPC_TGEU = 0x31 | EXT_SPECIAL, |
|||
OPC_TLT = 0x32 | EXT_SPECIAL, |
|||
OPC_TLTU = 0x33 | EXT_SPECIAL, |
|||
OPC_TEQ = 0x34 | EXT_SPECIAL, |
|||
OPC_TNE = 0x36 | EXT_SPECIAL, |
|||
/* HI / LO registers load & stores */ |
|||
OPC_MFHI = 0x10 | EXT_SPECIAL, |
|||
OPC_MTHI = 0x11 | EXT_SPECIAL, |
|||
OPC_MFLO = 0x12 | EXT_SPECIAL, |
|||
OPC_MTLO = 0x13 | EXT_SPECIAL, |
|||
/* Conditional moves */ |
|||
OPC_MOVZ = 0x0A | EXT_SPECIAL, |
|||
OPC_MOVN = 0x0B | EXT_SPECIAL, |
|||
|
|||
OPC_MOVCI = 0x01 | EXT_SPECIAL, |
|||
|
|||
/* Special */ |
|||
OPC_PMON = 0x05 | EXT_SPECIAL, |
|||
OPC_SYSCALL = 0x0C | EXT_SPECIAL, |
|||
OPC_BREAK = 0x0D | EXT_SPECIAL, |
|||
OPC_SYNC = 0x0F | EXT_SPECIAL, |
|||
}; |
|||
|
|||
enum { |
|||
/* Mutiply & xxx operations */ |
|||
OPC_MADD = 0x00 | EXT_SPECIAL2, |
|||
OPC_MADDU = 0x01 | EXT_SPECIAL2, |
|||
OPC_MUL = 0x02 | EXT_SPECIAL2, |
|||
OPC_MSUB = 0x04 | EXT_SPECIAL2, |
|||
OPC_MSUBU = 0x05 | EXT_SPECIAL2, |
|||
/* Misc */ |
|||
OPC_CLZ = 0x20 | EXT_SPECIAL2, |
|||
OPC_CLO = 0x21 | EXT_SPECIAL2, |
|||
/* Special */ |
|||
OPC_SDBBP = 0x3F | EXT_SPECIAL2, |
|||
}; |
|||
|
|||
/* Branch REGIMM */ |
|||
enum { |
|||
OPC_BLTZ = 0x00 | EXT_REGIMM, |
|||
OPC_BLTZL = 0x02 | EXT_REGIMM, |
|||
OPC_BGEZ = 0x01 | EXT_REGIMM, |
|||
OPC_BGEZL = 0x03 | EXT_REGIMM, |
|||
OPC_BLTZAL = 0x10 | EXT_REGIMM, |
|||
OPC_BLTZALL = 0x12 | EXT_REGIMM, |
|||
OPC_BGEZAL = 0x11 | EXT_REGIMM, |
|||
OPC_BGEZALL = 0x13 | EXT_REGIMM, |
|||
OPC_TGEI = 0x08 | EXT_REGIMM, |
|||
OPC_TGEIU = 0x09 | EXT_REGIMM, |
|||
OPC_TLTI = 0x0A | EXT_REGIMM, |
|||
OPC_TLTIU = 0x0B | EXT_REGIMM, |
|||
OPC_TEQI = 0x0C | EXT_REGIMM, |
|||
OPC_TNEI = 0x0E | EXT_REGIMM, |
|||
}; |
|||
|
|||
enum { |
|||
/* Coprocessor 0 (MMU) */ |
|||
OPC_MFC0 = 0x00 | EXT_CP0, |
|||
OPC_MTC0 = 0x04 | EXT_CP0, |
|||
OPC_TLBR = 0x01 | EXT_CP0, |
|||
OPC_TLBWI = 0x02 | EXT_CP0, |
|||
OPC_TLBWR = 0x06 | EXT_CP0, |
|||
OPC_TLBP = 0x08 | EXT_CP0, |
|||
OPC_ERET = 0x18 | EXT_CP0, |
|||
OPC_DERET = 0x1F | EXT_CP0, |
|||
OPC_WAIT = 0x20 | EXT_CP0, |
|||
}; |
|||
|
|||
int cpu_mips_exec(CPUMIPSState *s); |
|||
CPUMIPSState *cpu_mips_init(void); |
|||
uint32_t cpu_mips_get_clock (void); |
|||
|
|||
#endif /* !defined (__MIPS_CPU_H__) */ |
|||
@ -0,0 +1,183 @@ |
|||
#if !defined(__QEMU_MIPS_EXEC_H__) |
|||
#define __QEMU_MIPS_EXEC_H__ |
|||
|
|||
#define DEBUG_OP |
|||
|
|||
#include "mips-defs.h" |
|||
#include "dyngen-exec.h" |
|||
|
|||
register struct CPUMIPSState *env asm(AREG0); |
|||
|
|||
#if defined (USE_64BITS_REGS) |
|||
typedef int64_t host_int_t; |
|||
typedef uint64_t host_uint_t; |
|||
#else |
|||
typedef int32_t host_int_t; |
|||
typedef uint32_t host_uint_t; |
|||
#endif |
|||
|
|||
register host_uint_t T0 asm(AREG1); |
|||
register host_uint_t T1 asm(AREG2); |
|||
register host_uint_t T2 asm(AREG3); |
|||
register host_int_t Ts0 asm(AREG1); |
|||
register host_int_t Ts1 asm(AREG2); |
|||
register host_int_t Ts2 asm(AREG3); |
|||
|
|||
#define PARAM(n) ((uint32_t)PARAM##n) |
|||
#define SPARAM(n) ((int32_t)PARAM##n) |
|||
|
|||
#if defined (USE_HOST_FLOAT_REGS) |
|||
register double FT0 asm(FREG0); |
|||
register double FT1 asm(FREG1); |
|||
register double FT2 asm(FREG2); |
|||
register float FTS0 asm(FREG0); |
|||
register float FTS1 asm(FREG1); |
|||
register float FTS2 asm(FREG2); |
|||
#else |
|||
#define FT0 (env->ft0.d) |
|||
#define FT1 (env->ft1.d) |
|||
#define FT2 (env->ft2.d) |
|||
#define FTS0 (env->ft0.f) |
|||
#define FTS1 (env->ft1.f) |
|||
#define FTS2 (env->ft2.f) |
|||
#endif |
|||
|
|||
#if defined (DEBUG_OP) |
|||
#define RETURN() __asm__ __volatile__("nop"); |
|||
#else |
|||
#define RETURN() __asm__ __volatile__(""); |
|||
#endif |
|||
|
|||
#include "cpu.h" |
|||
#include "exec-all.h" |
|||
|
|||
#if !defined(CONFIG_USER_ONLY) |
|||
|
|||
#define ldul_user ldl_user |
|||
#define ldul_kernel ldl_kernel |
|||
|
|||
#define ACCESS_TYPE 0 |
|||
#define MEMSUFFIX _kernel |
|||
#define DATA_SIZE 1 |
|||
#include "softmmu_header.h" |
|||
|
|||
#define DATA_SIZE 2 |
|||
#include "softmmu_header.h" |
|||
|
|||
#define DATA_SIZE 4 |
|||
#include "softmmu_header.h" |
|||
|
|||
#define DATA_SIZE 8 |
|||
#include "softmmu_header.h" |
|||
#undef ACCESS_TYPE |
|||
#undef MEMSUFFIX |
|||
|
|||
#define ACCESS_TYPE 1 |
|||
#define MEMSUFFIX _user |
|||
#define DATA_SIZE 1 |
|||
#include "softmmu_header.h" |
|||
|
|||
#define DATA_SIZE 2 |
|||
#include "softmmu_header.h" |
|||
|
|||
#define DATA_SIZE 4 |
|||
#include "softmmu_header.h" |
|||
|
|||
#define DATA_SIZE 8 |
|||
#include "softmmu_header.h" |
|||
#undef ACCESS_TYPE |
|||
#undef MEMSUFFIX |
|||
|
|||
/* these access are slower, they must be as rare as possible */ |
|||
#define ACCESS_TYPE 2 |
|||
#define MEMSUFFIX _data |
|||
#define DATA_SIZE 1 |
|||
#include "softmmu_header.h" |
|||
|
|||
#define DATA_SIZE 2 |
|||
#include "softmmu_header.h" |
|||
|
|||
#define DATA_SIZE 4 |
|||
#include "softmmu_header.h" |
|||
|
|||
#define DATA_SIZE 8 |
|||
#include "softmmu_header.h" |
|||
#undef ACCESS_TYPE |
|||
#undef MEMSUFFIX |
|||
|
|||
#define ldub(p) ldub_data(p) |
|||
#define ldsb(p) ldsb_data(p) |
|||
#define lduw(p) lduw_data(p) |
|||
#define ldsw(p) ldsw_data(p) |
|||
#define ldl(p) ldl_data(p) |
|||
#define ldq(p) ldq_data(p) |
|||
|
|||
#define stb(p, v) stb_data(p, v) |
|||
#define stw(p, v) stw_data(p, v) |
|||
#define stl(p, v) stl_data(p, v) |
|||
#define stq(p, v) stq_data(p, v) |
|||
|
|||
#endif /* !defined(CONFIG_USER_ONLY) */ |
|||
|
|||
static inline void env_to_regs(void) |
|||
{ |
|||
} |
|||
|
|||
static inline void regs_to_env(void) |
|||
{ |
|||
} |
|||
|
|||
#if (HOST_LONG_BITS == 32) |
|||
void do_mult (void); |
|||
void do_multu (void); |
|||
void do_madd (void); |
|||
void do_maddu (void); |
|||
void do_msub (void); |
|||
void do_msubu (void); |
|||
#endif |
|||
__attribute__ (( regparm(2) )) |
|||
void do_mfc0(int reg, int sel); |
|||
__attribute__ (( regparm(2) )) |
|||
void do_mtc0(int reg, int sel); |
|||
void do_tlbwi (void); |
|||
void do_tlbwr (void); |
|||
void do_tlbp (void); |
|||
void do_tlbr (void); |
|||
void do_lwl_raw (void); |
|||
void do_lwr_raw (void); |
|||
void do_swl_raw (void); |
|||
void do_swr_raw (void); |
|||
#if !defined(CONFIG_USER_ONLY) |
|||
void do_lwl_user (void); |
|||
void do_lwl_kernel (void); |
|||
void do_lwr_user (void); |
|||
void do_lwr_kernel (void); |
|||
void do_swl_user (void); |
|||
void do_swl_kernel (void); |
|||
void do_swr_user (void); |
|||
void do_swr_kernel (void); |
|||
#endif |
|||
__attribute__ (( regparm(1) )) |
|||
void do_pmon (int function); |
|||
|
|||
int cpu_mips_handle_mmu_fault (CPUState *env, target_ulong address, int rw, |
|||
int is_user, int is_softmmu); |
|||
void do_interrupt (CPUState *env); |
|||
|
|||
void cpu_loop_exit(void); |
|||
__attribute__ (( regparm(2) )) |
|||
void do_raise_exception_err (uint32_t exception, int error_code); |
|||
__attribute__ (( regparm(1) )) |
|||
void do_raise_exception (uint32_t exception); |
|||
|
|||
void cpu_dump_state(CPUState *env, FILE *f, |
|||
int (*cpu_fprintf)(FILE *f, const char *fmt, ...), |
|||
int flags); |
|||
void cpu_mips_irqctrl_init (void); |
|||
uint32_t cpu_mips_get_random (CPUState *env); |
|||
uint32_t cpu_mips_get_count (CPUState *env); |
|||
void cpu_mips_store_count (CPUState *env, uint32_t value); |
|||
void cpu_mips_store_compare (CPUState *env, uint32_t value); |
|||
void cpu_mips_clock_init (CPUState *env); |
|||
|
|||
#endif /* !defined(__QEMU_MIPS_EXEC_H__) */ |
|||
@ -0,0 +1,461 @@ |
|||
/*
|
|||
* MIPS emulation helpers for qemu. |
|||
* |
|||
* Copyright (c) 2004-2005 Jocelyn Mayer |
|||
* |
|||
* 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" |
|||
|
|||
/* MIPS32 4K MMU emulation */ |
|||
#if MIPS_USES_4K_TLB |
|||
static int map_address (CPUState *env, target_ulong *physical, int *prot, |
|||
target_ulong address, int rw, int access_type) |
|||
{ |
|||
tlb_t *tlb; |
|||
target_ulong tag; |
|||
uint8_t ASID; |
|||
int i, n; |
|||
int ret; |
|||
|
|||
ret = -2; |
|||
tag = (address & 0xFFFFE000); |
|||
ASID = env->CP0_EntryHi & 0x000000FF; |
|||
for (i = 0; i < 16; i++) { |
|||
tlb = &env->tlb[i]; |
|||
/* Check ASID, virtual page number & size */ |
|||
if ((tlb->G == 1 || tlb->ASID == ASID) && |
|||
tlb->VPN == tag && address < tlb->end) { |
|||
/* TLB match */ |
|||
n = (address >> 12) & 1; |
|||
/* Check access rights */ |
|||
if ((tlb->V[n] & 2) && (rw == 0 || (tlb->D[n] & 4))) { |
|||
*physical = tlb->PFN[n] | (address & 0xFFF); |
|||
*prot = PROT_READ; |
|||
if (tlb->D[n]) |
|||
*prot |= PROT_WRITE; |
|||
return 0; |
|||
} else if (!(tlb->V[n] & 2)) { |
|||
return -3; |
|||
} else { |
|||
return -4; |
|||
} |
|||
} |
|||
} |
|||
|
|||
return ret; |
|||
} |
|||
#endif |
|||
|
|||
int get_physical_address (CPUState *env, target_ulong *physical, int *prot, |
|||
target_ulong address, int rw, int access_type) |
|||
{ |
|||
int user_mode; |
|||
int ret; |
|||
|
|||
/* User mode can only access useg */ |
|||
user_mode = ((env->hflags & MIPS_HFLAG_MODE) == MIPS_HFLAG_UM) ? 1 : 0; |
|||
#if 0 |
|||
if (logfile) { |
|||
fprintf(logfile, "user mode %d h %08x\n", |
|||
user_mode, env->hflags); |
|||
} |
|||
#endif |
|||
if (user_mode && address > 0x7FFFFFFFUL) |
|||
return -1; |
|||
ret = 0; |
|||
if (address < 0x80000000UL) { |
|||
if (user_mode || !(env->hflags & MIPS_HFLAG_ERL)) { |
|||
#if MIPS_USES_4K_TLB |
|||
ret = map_address(env, physical, prot, address, rw); |
|||
#else |
|||
*physical = address + 0x40000000UL; |
|||
*prot = PAGE_READ | PAGE_WRITE; |
|||
#endif |
|||
} else { |
|||
*physical = address; |
|||
*prot = PAGE_READ | PAGE_WRITE; |
|||
} |
|||
} else if (address < 0xA0000000UL) { |
|||
/* kseg0 */ |
|||
/* XXX: check supervisor mode */ |
|||
*physical = address - 0x80000000UL; |
|||
*prot = PAGE_READ | PAGE_WRITE; |
|||
} else if (address < 0xC0000000UL) { |
|||
/* kseg1 */ |
|||
/* XXX: check supervisor mode */ |
|||
*physical = address - 0xA0000000UL; |
|||
*prot = PAGE_READ | PAGE_WRITE; |
|||
} else if (address < 0xE0000000UL) { |
|||
/* kseg2 */ |
|||
#if MIPS_USES_4K_TLB |
|||
ret = map_address(env, physical, prot, address, rw); |
|||
#else |
|||
*physical = address; |
|||
*prot = PAGE_READ | PAGE_WRITE; |
|||
#endif |
|||
} else { |
|||
/* kseg3 */ |
|||
/* XXX: check supervisor mode */ |
|||
/* XXX: debug segment is not emulated */ |
|||
#if MIPS_USES_4K_TLB |
|||
ret = map_address(env, physical, prot, address, rw); |
|||
#else |
|||
*physical = address; |
|||
*prot = PAGE_READ | PAGE_WRITE; |
|||
#endif |
|||
} |
|||
#if 0 |
|||
if (logfile) { |
|||
fprintf(logfile, "%08x %d %d => %08x %d (%d)\n", address, rw, |
|||
access_type, *physical, *prot, ret); |
|||
} |
|||
#endif |
|||
|
|||
return ret; |
|||
} |
|||
|
|||
#if defined(CONFIG_USER_ONLY) |
|||
target_ulong cpu_get_phys_page_debug(CPUState *env, target_ulong addr) |
|||
{ |
|||
return addr; |
|||
} |
|||
#else |
|||
target_ulong cpu_get_phys_page_debug(CPUState *env, target_ulong addr) |
|||
{ |
|||
target_ulong phys_addr; |
|||
int prot; |
|||
|
|||
if (get_physical_address(env, &phys_addr, &prot, addr, 0, ACCESS_INT) != 0) |
|||
return -1; |
|||
return phys_addr; |
|||
} |
|||
#endif |
|||
|
|||
#if !defined(CONFIG_USER_ONLY) |
|||
|
|||
#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" |
|||
|
|||
void tlb_fill (target_ulong addr, int is_write, int is_user, void *retaddr) |
|||
{ |
|||
TranslationBlock *tb; |
|||
CPUState *saved_env; |
|||
unsigned long 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_mips_handle_mmu_fault(env, addr, is_write, is_user, 1); |
|||
if (ret) { |
|||
if (retaddr) { |
|||
/* now we have a real cpu fault */ |
|||
pc = (unsigned long)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); |
|||
} |
|||
} |
|||
do_raise_exception_err(env->exception_index, env->error_code); |
|||
} |
|||
env = saved_env; |
|||
} |
|||
|
|||
void cpu_mips_init_mmu (CPUState *env) |
|||
{ |
|||
} |
|||
|
|||
#endif /* !defined(CONFIG_USER_ONLY) */ |
|||
|
|||
int cpu_mips_handle_mmu_fault (CPUState *env, target_ulong address, int rw, |
|||
int is_user, int is_softmmu) |
|||
{ |
|||
target_ulong physical; |
|||
int prot; |
|||
int exception = 0, error_code = 0; |
|||
int access_type; |
|||
int ret = 0; |
|||
|
|||
if (logfile) { |
|||
cpu_dump_state(env, logfile, fprintf, 0); |
|||
fprintf(logfile, "%s pc %08x ad %08x rw %d is_user %d smmu %d\n", |
|||
__func__, env->PC, address, rw, is_user, is_softmmu); |
|||
} |
|||
/* data access */ |
|||
/* XXX: put correct access by using cpu_restore_state()
|
|||
correctly */ |
|||
access_type = ACCESS_INT; |
|||
if (env->user_mode_only) { |
|||
/* user mode only emulation */ |
|||
ret = -2; |
|||
goto do_fault; |
|||
} |
|||
ret = get_physical_address(env, &physical, &prot, |
|||
address, rw, access_type); |
|||
if (logfile) { |
|||
fprintf(logfile, "%s address=%08x ret %d physical %08x prot %d\n", |
|||
__func__, address, ret, physical, prot); |
|||
} |
|||
if (ret == 0) { |
|||
ret = tlb_set_page(env, address & ~0xFFF, physical & ~0xFFF, prot, |
|||
is_user, is_softmmu); |
|||
} else if (ret < 0) { |
|||
do_fault: |
|||
switch (ret) { |
|||
default: |
|||
case -1: |
|||
/* Reference to kernel address from user mode or supervisor mode */ |
|||
/* Reference to supervisor address from user mode */ |
|||
if (rw) |
|||
exception = EXCP_AdES; |
|||
else |
|||
exception = EXCP_AdEL; |
|||
break; |
|||
case -2: |
|||
/* No TLB match for a mapped address */ |
|||
if (rw) |
|||
exception = EXCP_TLBS; |
|||
else |
|||
exception = EXCP_TLBL; |
|||
error_code = 1; |
|||
break; |
|||
case -3: |
|||
/* TLB match with no valid bit */ |
|||
if (rw) |
|||
exception = EXCP_TLBS; |
|||
else |
|||
exception = EXCP_TLBL; |
|||
error_code = 0; |
|||
break; |
|||
case -4: |
|||
/* TLB match but 'D' bit is cleared */ |
|||
exception = EXCP_LTLBL; |
|||
break; |
|||
|
|||
} |
|||
if (ret == -2) { |
|||
exception = EXCP_AdEL; |
|||
} |
|||
/* Raise exception */ |
|||
env->CP0_BadVAddr = address; |
|||
env->CP0_Context = |
|||
(env->CP0_Context & 0x00000FFF) | (address & 0xFFFFF000); |
|||
env->CP0_EntryHi = |
|||
(env->CP0_EntryHi & 0x00000FFF) | (address & 0xFFFFF000); |
|||
env->exception_index = exception; |
|||
env->error_code = error_code; |
|||
ret = 1; |
|||
} |
|||
|
|||
return ret; |
|||
} |
|||
|
|||
void do_interrupt (CPUState *env) |
|||
{ |
|||
target_ulong pc, offset; |
|||
int cause = -1; |
|||
|
|||
if (logfile && env->exception_index != EXCP_EXT_INTERRUPT) { |
|||
fprintf(logfile, "%s enter: PC %08x EPC %08x cause %d excp %d\n", |
|||
__func__, env->PC, env->CP0_EPC, cause, env->exception_index); |
|||
} |
|||
if (env->exception_index == EXCP_EXT_INTERRUPT && |
|||
(env->hflags & MIPS_HFLAG_DM)) |
|||
env->exception_index = EXCP_DINT; |
|||
offset = 0x180; |
|||
switch (env->exception_index) { |
|||
case EXCP_DSS: |
|||
env->CP0_Debug |= 1 << CP0DB_DSS; |
|||
/* Debug single step cannot be raised inside a delay slot and
|
|||
* resume will always occur on the next instruction |
|||
* (but we assume the pc has always been updated during |
|||
* code translation). |
|||
*/ |
|||
env->CP0_DEPC = env->PC; |
|||
goto enter_debug_mode; |
|||
case EXCP_DINT: |
|||
env->CP0_Debug |= 1 << CP0DB_DINT; |
|||
goto set_DEPC; |
|||
case EXCP_DIB: |
|||
env->CP0_Debug |= 1 << CP0DB_DIB; |
|||
goto set_DEPC; |
|||
case EXCP_DBp: |
|||
env->CP0_Debug |= 1 << CP0DB_DBp; |
|||
goto set_DEPC; |
|||
case EXCP_DDBS: |
|||
env->CP0_Debug |= 1 << CP0DB_DDBS; |
|||
goto set_DEPC; |
|||
case EXCP_DDBL: |
|||
env->CP0_Debug |= 1 << CP0DB_DDBL; |
|||
goto set_DEPC; |
|||
set_DEPC: |
|||
if (env->hflags & MIPS_HFLAG_DS) { |
|||
/* If the exception was raised from a delay slot,
|
|||
* come back to the jump |
|||
*/ |
|||
env->CP0_DEPC = env->PC - 4; |
|||
} else { |
|||
env->CP0_DEPC = env->PC; |
|||
} |
|||
enter_debug_mode: |
|||
env->hflags |= MIPS_HFLAG_DM; |
|||
/* EJTAG probe trap enable is not implemented... */ |
|||
pc = 0xBFC00480; |
|||
break; |
|||
case EXCP_RESET: |
|||
#if defined (MIPS_USES_R4K_TLB) |
|||
env->CP0_random = MIPS_TLB_NB - 1; |
|||
#endif |
|||
env->CP0_Wired = 0; |
|||
env->CP0_Config0 = MIPS_CONFIG0; |
|||
#if defined (MIPS_CONFIG1) |
|||
env->CP0_Config1 = MIPS_CONFIG1; |
|||
#endif |
|||
#if defined (MIPS_CONFIG2) |
|||
env->CP0_Config2 = MIPS_CONFIG2; |
|||
#endif |
|||
#if defined (MIPS_CONFIG3) |
|||
env->CP0_Config3 = MIPS_CONFIG3; |
|||
#endif |
|||
env->CP0_WatchLo = 0; |
|||
env->CP0_Status = (1 << CP0St_CU0) | (1 << CP0St_BEV); |
|||
goto set_error_EPC; |
|||
case EXCP_SRESET: |
|||
env->CP0_Status = (1 << CP0St_CU0) | (1 << CP0St_BEV) | |
|||
(1 << CP0St_SR); |
|||
env->CP0_WatchLo = 0; |
|||
goto set_error_EPC; |
|||
case EXCP_NMI: |
|||
env->CP0_Status = (1 << CP0St_CU0) | (1 << CP0St_BEV) | |
|||
(1 << CP0St_NMI); |
|||
set_error_EPC: |
|||
env->hflags = MIPS_HFLAG_ERL; |
|||
if (env->hflags & MIPS_HFLAG_DS) { |
|||
/* If the exception was raised from a delay slot,
|
|||
* come back to the jump |
|||
*/ |
|||
env->CP0_ErrorEPC = env->PC - 4; |
|||
} else { |
|||
env->CP0_ErrorEPC = env->PC; |
|||
} |
|||
pc = 0xBFC00000; |
|||
break; |
|||
case EXCP_MCHECK: |
|||
cause = 24; |
|||
goto set_EPC; |
|||
case EXCP_EXT_INTERRUPT: |
|||
cause = 0; |
|||
if (env->CP0_Cause & (1 << CP0Ca_IV)) |
|||
offset = 0x200; |
|||
goto set_EPC; |
|||
case EXCP_DWATCH: |
|||
cause = 23; |
|||
/* XXX: TODO: manage defered watch exceptions */ |
|||
goto set_EPC; |
|||
case EXCP_AdEL: |
|||
case EXCP_AdES: |
|||
cause = 4; |
|||
goto set_EPC; |
|||
case EXCP_TLBL: |
|||
case EXCP_TLBF: |
|||
cause = 2; |
|||
if (env->error_code == 1 && !(env->hflags & MIPS_HFLAG_EXL)) |
|||
offset = 0x000; |
|||
goto set_EPC; |
|||
case EXCP_IBE: |
|||
cause = 6; |
|||
goto set_EPC; |
|||
case EXCP_DBE: |
|||
cause = 7; |
|||
goto set_EPC; |
|||
case EXCP_SYSCALL: |
|||
cause = 8; |
|||
goto set_EPC; |
|||
case EXCP_BREAK: |
|||
cause = 9; |
|||
goto set_EPC; |
|||
case EXCP_RI: |
|||
cause = 10; |
|||
goto set_EPC; |
|||
case EXCP_CpU: |
|||
cause = 11; |
|||
/* XXX: fill in the faulty unit number */ |
|||
goto set_EPC; |
|||
case EXCP_OVERFLOW: |
|||
cause = 12; |
|||
goto set_EPC; |
|||
case EXCP_TRAP: |
|||
cause = 13; |
|||
goto set_EPC; |
|||
case EXCP_LTLBL: |
|||
cause = 1; |
|||
goto set_EPC; |
|||
case EXCP_TLBS: |
|||
cause = 3; |
|||
set_EPC: |
|||
if (env->CP0_Status & (1 << CP0St_BEV)) { |
|||
pc = 0xBFC00200; |
|||
} else { |
|||
pc = 0x80000000; |
|||
} |
|||
env->hflags |= MIPS_HFLAG_EXL; |
|||
pc += offset; |
|||
env->CP0_Cause = (env->CP0_Cause & ~0x7C) | (cause << 2); |
|||
if (env->hflags & MIPS_HFLAG_DS) { |
|||
/* If the exception was raised from a delay slot,
|
|||
* come back to the jump |
|||
*/ |
|||
env->CP0_EPC = env->PC - 4; |
|||
env->CP0_Cause |= 0x80000000; |
|||
} else { |
|||
env->CP0_EPC = env->PC; |
|||
env->CP0_Cause &= ~0x80000000; |
|||
} |
|||
break; |
|||
default: |
|||
if (logfile) { |
|||
fprintf(logfile, "Invalid MIPS exception %d. Exiting\n", |
|||
env->exception_index); |
|||
} |
|||
printf("Invalid MIPS exception %d. Exiting\n", env->exception_index); |
|||
exit(1); |
|||
} |
|||
env->PC = pc; |
|||
if (logfile && env->exception_index != EXCP_EXT_INTERRUPT) { |
|||
fprintf(logfile, "%s: PC %08x EPC %08x cause %d excp %d\n" |
|||
" S %08x C %08x A %08x D %08x\n", |
|||
__func__, env->PC, env->CP0_EPC, cause, env->exception_index, |
|||
env->CP0_Status, env->CP0_Cause, env->CP0_BadVAddr, |
|||
env->CP0_DEPC); |
|||
} |
|||
env->exception_index = EXCP_NONE; |
|||
} |
|||
@ -0,0 +1,58 @@ |
|||
#if !defined (__QEMU_MIPS_DEFS_H__) |
|||
#define __QEMU_MIPS_DEFS_H__ |
|||
|
|||
/* If we want to use 64 bits host regs... */ |
|||
//#define USE_64BITS_REGS
|
|||
/* If we want to use host float regs... */ |
|||
//#define USE_HOST_FLOAT_REGS
|
|||
|
|||
enum { |
|||
MIPS_R4Kc = 0x00018000, |
|||
MIPS_R4Kp = 0x00018300, |
|||
}; |
|||
|
|||
/* Emulate MIPS R4Kc for now */ |
|||
#define MIPS_CPU MIPS_R4Kc |
|||
|
|||
#if (MIPS_CPU == MIPS_R4Kc) |
|||
/* 32 bits target */ |
|||
#define TARGET_LONG_BITS 32 |
|||
/* real pages are variable size... */ |
|||
#define TARGET_PAGE_BITS 12 |
|||
/* Uses MIPS R4Kx ehancements to MIPS32 architecture */ |
|||
#define MIPS_USES_R4K_EXT |
|||
/* Uses MIPS R4Kc TLB model */ |
|||
#define MIPS_USES_R4K_TLB |
|||
#define MIPS_TLB_NB 16 |
|||
/* Have config1, runs in big-endian mode, uses TLB */ |
|||
#define MIPS_CONFIG0 \ |
|||
((1 << CP0C0_M) | (0x000 << CP0C0_K23) | (0x000 << CP0C0_KU) | \ |
|||
(1 << CP0C0_BE) | (0x001 << CP0C0_MT) | (0x010 << CP0C0_K0)) |
|||
/* 16 TLBs, 64 sets Icache, 16 bytes Icache line, 2-way Icache,
|
|||
* 64 sets Dcache, 16 bytes Dcache line, 2-way Dcache, |
|||
* no performance counters, watch registers present, no code compression, |
|||
* EJTAG present, no FPU |
|||
*/ |
|||
#define MIPS_CONFIG1 \ |
|||
((15 << CP0C1_MMU) | \ |
|||
(0x000 << CP0C1_IS) | (0x3 << CP0C1_IL) | (0x01 << CP0C1_IA) | \ |
|||
(0x000 << CP0C1_DS) | (0x3 << CP0C1_DL) | (0x01 << CP0C1_DA) | \ |
|||
(0 << CP0C1_PC) | (1 << CP0C1_WR) | (0 << CP0C1_CA) | \ |
|||
(1 << CP0C1_EP) | (0 << CP0C1_FP)) |
|||
#elif defined (MIPS_CPU == MIPS_R4Kp) |
|||
/* 32 bits target */ |
|||
#define TARGET_LONG_BITS 32 |
|||
/* real pages are variable size... */ |
|||
#define TARGET_PAGE_BITS 12 |
|||
/* Uses MIPS R4Kx ehancements to MIPS32 architecture */ |
|||
#define MIPS_USES_R4K_EXT |
|||
/* Uses MIPS R4Km FPM MMU model */ |
|||
#define MIPS_USES_R4K_FPM |
|||
#else |
|||
#error "MIPS CPU not defined" |
|||
/* Remainder for other flags */ |
|||
//#define TARGET_MIPS64
|
|||
//define MIPS_USES_FPU
|
|||
#endif |
|||
|
|||
#endif /* !defined (__QEMU_MIPS_DEFS_H__) */ |
|||
@ -0,0 +1,641 @@ |
|||
/*
|
|||
* MIPS emulation micro-operations for qemu. |
|||
* |
|||
* Copyright (c) 2004-2005 Jocelyn Mayer |
|||
* |
|||
* 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 "config.h" |
|||
#include "exec.h" |
|||
|
|||
#define REG 1 |
|||
#include "op_template.c" |
|||
#undef REG |
|||
#define REG 2 |
|||
#include "op_template.c" |
|||
#undef REG |
|||
#define REG 3 |
|||
#include "op_template.c" |
|||
#undef REG |
|||
#define REG 4 |
|||
#include "op_template.c" |
|||
#undef REG |
|||
#define REG 5 |
|||
#include "op_template.c" |
|||
#undef REG |
|||
#define REG 6 |
|||
#include "op_template.c" |
|||
#undef REG |
|||
#define REG 7 |
|||
#include "op_template.c" |
|||
#undef REG |
|||
#define REG 8 |
|||
#include "op_template.c" |
|||
#undef REG |
|||
#define REG 9 |
|||
#include "op_template.c" |
|||
#undef REG |
|||
#define REG 10 |
|||
#include "op_template.c" |
|||
#undef REG |
|||
#define REG 11 |
|||
#include "op_template.c" |
|||
#undef REG |
|||
#define REG 12 |
|||
#include "op_template.c" |
|||
#undef REG |
|||
#define REG 13 |
|||
#include "op_template.c" |
|||
#undef REG |
|||
#define REG 14 |
|||
#include "op_template.c" |
|||
#undef REG |
|||
#define REG 15 |
|||
#include "op_template.c" |
|||
#undef REG |
|||
#define REG 16 |
|||
#include "op_template.c" |
|||
#undef REG |
|||
#define REG 17 |
|||
#include "op_template.c" |
|||
#undef REG |
|||
#define REG 18 |
|||
#include "op_template.c" |
|||
#undef REG |
|||
#define REG 19 |
|||
#include "op_template.c" |
|||
#undef REG |
|||
#define REG 20 |
|||
#include "op_template.c" |
|||
#undef REG |
|||
#define REG 21 |
|||
#include "op_template.c" |
|||
#undef REG |
|||
#define REG 22 |
|||
#include "op_template.c" |
|||
#undef REG |
|||
#define REG 23 |
|||
#include "op_template.c" |
|||
#undef REG |
|||
#define REG 24 |
|||
#include "op_template.c" |
|||
#undef REG |
|||
#define REG 25 |
|||
#include "op_template.c" |
|||
#undef REG |
|||
#define REG 26 |
|||
#include "op_template.c" |
|||
#undef REG |
|||
#define REG 27 |
|||
#include "op_template.c" |
|||
#undef REG |
|||
#define REG 28 |
|||
#include "op_template.c" |
|||
#undef REG |
|||
#define REG 29 |
|||
#include "op_template.c" |
|||
#undef REG |
|||
#define REG 30 |
|||
#include "op_template.c" |
|||
#undef REG |
|||
#define REG 31 |
|||
#include "op_template.c" |
|||
#undef REG |
|||
|
|||
#define TN T0 |
|||
#include "op_template.c" |
|||
#undef TN |
|||
#define TN T1 |
|||
#include "op_template.c" |
|||
#undef TN |
|||
#define TN T2 |
|||
#include "op_template.c" |
|||
#undef TN |
|||
|
|||
void op_dup_T0 (void) |
|||
{ |
|||
T2 = T0; |
|||
RETURN(); |
|||
} |
|||
|
|||
void op_load_HI (void) |
|||
{ |
|||
T0 = env->HI; |
|||
RETURN(); |
|||
} |
|||
|
|||
void op_store_HI (void) |
|||
{ |
|||
env->HI = T0; |
|||
RETURN(); |
|||
} |
|||
|
|||
void op_load_LO (void) |
|||
{ |
|||
T0 = env->LO; |
|||
RETURN(); |
|||
} |
|||
|
|||
void op_store_LO (void) |
|||
{ |
|||
env->LO = T0; |
|||
RETURN(); |
|||
} |
|||
|
|||
/* Load and store */ |
|||
#define MEMSUFFIX _raw |
|||
#include "op_mem.c" |
|||
#undef MEMSUFFIX |
|||
#if !defined(CONFIG_USER_ONLY) |
|||
#define MEMSUFFIX _user |
|||
#include "op_mem.c" |
|||
#undef MEMSUFFIX |
|||
|
|||
#define MEMSUFFIX _kernel |
|||
#include "op_mem.c" |
|||
#undef MEMSUFFIX |
|||
#endif |
|||
|
|||
/* Arithmetic */ |
|||
void op_add (void) |
|||
{ |
|||
T0 += T1; |
|||
RETURN(); |
|||
} |
|||
|
|||
void op_addo (void) |
|||
{ |
|||
target_ulong tmp; |
|||
|
|||
tmp = T0; |
|||
T0 += T1; |
|||
if ((T0 >> 31) ^ (T1 >> 31) ^ (tmp >> 31)) { |
|||
CALL_FROM_TB1(do_raise_exception, EXCP_OVERFLOW); |
|||
} |
|||
RETURN(); |
|||
} |
|||
|
|||
void op_sub (void) |
|||
{ |
|||
T0 -= T1; |
|||
RETURN(); |
|||
} |
|||
|
|||
void op_subo (void) |
|||
{ |
|||
target_ulong tmp; |
|||
|
|||
tmp = T0; |
|||
T0 = (int32_t)T0 - (int32_t)T1; |
|||
if (!((T0 >> 31) ^ (T1 >> 31) ^ (tmp >> 31))) { |
|||
CALL_FROM_TB1(do_raise_exception, EXCP_OVERFLOW); |
|||
} |
|||
RETURN(); |
|||
} |
|||
|
|||
void op_mul (void) |
|||
{ |
|||
T0 = (int32_t)T0 * (int32_t)T1; |
|||
RETURN(); |
|||
} |
|||
|
|||
void op_div (void) |
|||
{ |
|||
if (T1 != 0) { |
|||
env->LO = (int32_t)T0 / (int32_t)T1; |
|||
env->HI = (int32_t)T0 % (int32_t)T1; |
|||
} |
|||
RETURN(); |
|||
} |
|||
|
|||
void op_divu (void) |
|||
{ |
|||
if (T1 != 0) { |
|||
env->LO = T0 / T1; |
|||
env->HI = T0 % T1; |
|||
} |
|||
RETURN(); |
|||
} |
|||
|
|||
/* Logical */ |
|||
void op_and (void) |
|||
{ |
|||
T0 &= T1; |
|||
RETURN(); |
|||
} |
|||
|
|||
void op_nor (void) |
|||
{ |
|||
T0 = ~(T0 | T1); |
|||
RETURN(); |
|||
} |
|||
|
|||
void op_or (void) |
|||
{ |
|||
T0 |= T1; |
|||
RETURN(); |
|||
} |
|||
|
|||
void op_xor (void) |
|||
{ |
|||
T0 ^= T1; |
|||
RETURN(); |
|||
} |
|||
|
|||
void op_sll (void) |
|||
{ |
|||
T0 = T0 << T1; |
|||
RETURN(); |
|||
} |
|||
|
|||
void op_sra (void) |
|||
{ |
|||
T0 = (int32_t)T0 >> T1; |
|||
RETURN(); |
|||
} |
|||
|
|||
void op_srl (void) |
|||
{ |
|||
T0 = T0 >> T1; |
|||
RETURN(); |
|||
} |
|||
|
|||
void op_sllv (void) |
|||
{ |
|||
T0 = T1 << (T0 & 0x1F); |
|||
RETURN(); |
|||
} |
|||
|
|||
void op_srav (void) |
|||
{ |
|||
T0 = (int32_t)T1 >> (T0 & 0x1F); |
|||
RETURN(); |
|||
} |
|||
|
|||
void op_srlv (void) |
|||
{ |
|||
T0 = T1 >> (T0 & 0x1F); |
|||
RETURN(); |
|||
} |
|||
|
|||
void op_clo (void) |
|||
{ |
|||
int n; |
|||
|
|||
if (T0 == (target_ulong)-1) { |
|||
T0 = 32; |
|||
} else { |
|||
for (n = 0; n < 32; n++) { |
|||
if (!(T0 & (1 << 31))) |
|||
break; |
|||
T0 = T0 << 1; |
|||
} |
|||
T0 = n; |
|||
} |
|||
RETURN(); |
|||
} |
|||
|
|||
void op_clz (void) |
|||
{ |
|||
int n; |
|||
|
|||
if (T0 == 0) { |
|||
T0 = 32; |
|||
} else { |
|||
for (n = 0; n < 32; n++) { |
|||
if (T0 & (1 << 31)) |
|||
break; |
|||
T0 = T0 << 1; |
|||
} |
|||
T0 = n; |
|||
} |
|||
RETURN(); |
|||
} |
|||
|
|||
/* 64 bits arithmetic */ |
|||
#if (HOST_LONG_BITS == 64) |
|||
static inline uint64_t get_HILO (void) |
|||
{ |
|||
return ((uint64_t)env->HI << 32) | (uint64_t)env->LO; |
|||
} |
|||
|
|||
static inline void set_HILO (uint64_t HILO) |
|||
{ |
|||
env->LO = HILO & 0xFFFFFFFF; |
|||
env->HI = HILO >> 32; |
|||
} |
|||
|
|||
void op_mult (void) |
|||
{ |
|||
set_HILO((int64_t)T0 * (int64_t)T1); |
|||
RETURN(); |
|||
} |
|||
|
|||
void op_multu (void) |
|||
{ |
|||
set_HILO((uint64_t)T0 * (uint64_t)T1); |
|||
RETURN(); |
|||
} |
|||
|
|||
void op_madd (void) |
|||
{ |
|||
int64_t tmp; |
|||
|
|||
tmp = ((int64_t)T0 * (int64_t)T1); |
|||
set_HILO((int64_t)get_HILO() + tmp); |
|||
RETURN(); |
|||
} |
|||
|
|||
void op_maddu (void) |
|||
{ |
|||
uint64_t tmp; |
|||
|
|||
tmp = ((uint64_t)T0 * (uint64_t)T1); |
|||
set_HILO(get_HILO() + tmp); |
|||
RETURN(); |
|||
} |
|||
|
|||
void op_msub (void) |
|||
{ |
|||
int64_t tmp; |
|||
|
|||
tmp = ((int64_t)T0 * (int64_t)T1); |
|||
set_HILO((int64_t)get_HILO() - tmp); |
|||
RETURN(); |
|||
} |
|||
|
|||
void op_msubu (void) |
|||
{ |
|||
uint64_t tmp; |
|||
|
|||
tmp = ((uint64_t)T0 * (uint64_t)T1); |
|||
set_HILO(get_HILO() - tmp); |
|||
RETURN(); |
|||
} |
|||
#else |
|||
void op_mult (void) |
|||
{ |
|||
CALL_FROM_TB0(do_mult); |
|||
RETURN(); |
|||
} |
|||
|
|||
void op_multu (void) |
|||
{ |
|||
CALL_FROM_TB0(do_multu); |
|||
RETURN(); |
|||
} |
|||
|
|||
void op_madd (void) |
|||
{ |
|||
CALL_FROM_TB0(do_madd); |
|||
RETURN(); |
|||
} |
|||
|
|||
void op_maddu (void) |
|||
{ |
|||
CALL_FROM_TB0(do_maddu); |
|||
RETURN(); |
|||
} |
|||
|
|||
void op_msub (void) |
|||
{ |
|||
CALL_FROM_TB0(do_msub); |
|||
RETURN(); |
|||
} |
|||
|
|||
void op_msubu (void) |
|||
{ |
|||
CALL_FROM_TB0(do_msubu); |
|||
RETURN(); |
|||
} |
|||
#endif |
|||
|
|||
/* Conditional moves */ |
|||
void op_movn (void) |
|||
{ |
|||
if (T1 != 0) |
|||
env->gpr[PARAM1] = T0; |
|||
RETURN(); |
|||
} |
|||
|
|||
void op_movz (void) |
|||
{ |
|||
if (T1 == 0) |
|||
env->gpr[PARAM1] = T0; |
|||
RETURN(); |
|||
} |
|||
|
|||
/* Tests */ |
|||
#define OP_COND(name, cond) \ |
|||
void glue(op_, name) (void) \ |
|||
{ \ |
|||
if (cond) { \ |
|||
T0 = 1; \ |
|||
} else { \ |
|||
T0 = 0; \ |
|||
} \ |
|||
RETURN(); \ |
|||
} |
|||
|
|||
OP_COND(eq, T0 == T1); |
|||
OP_COND(ne, T0 != T1); |
|||
OP_COND(ge, (int32_t)T0 >= (int32_t)T1); |
|||
OP_COND(geu, T0 >= T1); |
|||
OP_COND(lt, (int32_t)T0 < (int32_t)T1); |
|||
OP_COND(ltu, T0 < T1); |
|||
OP_COND(gez, (int32_t)T0 >= 0); |
|||
OP_COND(gtz, (int32_t)T0 > 0); |
|||
OP_COND(lez, (int32_t)T0 <= 0); |
|||
OP_COND(ltz, (int32_t)T0 < 0); |
|||
|
|||
/* Branchs */ |
|||
//#undef USE_DIRECT_JUMP
|
|||
#define EIP env->PC |
|||
|
|||
/* Branch to register */ |
|||
void op_save_breg_target (void) |
|||
{ |
|||
env->btarget = T2; |
|||
} |
|||
|
|||
void op_restore_breg_target (void) |
|||
{ |
|||
T2 = env->btarget; |
|||
} |
|||
|
|||
void op_breg (void) |
|||
{ |
|||
env->PC = T2; |
|||
RETURN(); |
|||
} |
|||
|
|||
/* Unconditional branch */ |
|||
void op_branch (void) |
|||
{ |
|||
JUMP_TB(branch, PARAM1, 0, PARAM2); |
|||
RETURN(); |
|||
} |
|||
|
|||
void op_save_btarget (void) |
|||
{ |
|||
env->btarget = PARAM1; |
|||
RETURN(); |
|||
} |
|||
|
|||
/* Conditional branch */ |
|||
void op_set_bcond (void) |
|||
{ |
|||
T2 = T0; |
|||
RETURN(); |
|||
} |
|||
|
|||
void op_save_bcond (void) |
|||
{ |
|||
env->bcond = T2; |
|||
RETURN(); |
|||
} |
|||
|
|||
void op_restore_bcond (void) |
|||
{ |
|||
T2 = env->bcond; |
|||
RETURN(); |
|||
} |
|||
|
|||
void op_bcond (void) |
|||
{ |
|||
if (T2) { |
|||
JUMP_TB(bcond, PARAM1, 0, PARAM2); |
|||
} else { |
|||
JUMP_TB(bcond, PARAM1, 1, PARAM3); |
|||
} |
|||
RETURN(); |
|||
} |
|||
|
|||
/* Likely branch (used to skip the delay slot) */ |
|||
void op_blikely (void) |
|||
{ |
|||
/* If the test is false, skip the delay slot */ |
|||
if (T2 == 0) { |
|||
env->hflags = PARAM3; |
|||
JUMP_TB(blikely, PARAM1, 1, PARAM2); |
|||
} |
|||
RETURN(); |
|||
} |
|||
|
|||
/* CP0 functions */ |
|||
void op_mfc0 (void) |
|||
{ |
|||
CALL_FROM_TB2(do_mfc0, PARAM1, PARAM2); |
|||
RETURN(); |
|||
} |
|||
|
|||
void op_mtc0 (void) |
|||
{ |
|||
CALL_FROM_TB2(do_mtc0, PARAM1, PARAM2); |
|||
RETURN(); |
|||
} |
|||
|
|||
#if defined(MIPS_USES_R4K_TLB) |
|||
void op_tlbwi (void) |
|||
{ |
|||
CALL_FROM_TB0(do_tlbwi); |
|||
RETURN(); |
|||
} |
|||
|
|||
void op_tlbwr (void) |
|||
{ |
|||
CALL_FROM_TB0(do_tlbwr); |
|||
RETURN(); |
|||
} |
|||
|
|||
void op_tlbp (void) |
|||
{ |
|||
CALL_FROM_TB0(do_tlbp); |
|||
RETURN(); |
|||
} |
|||
|
|||
void op_tlbr (void) |
|||
{ |
|||
CALL_FROM_TB0(do_tlbr); |
|||
RETURN(); |
|||
} |
|||
#endif |
|||
|
|||
/* Specials */ |
|||
void op_pmon (void) |
|||
{ |
|||
CALL_FROM_TB1(do_pmon, PARAM1); |
|||
} |
|||
|
|||
void op_trap (void) |
|||
{ |
|||
if (T0) { |
|||
CALL_FROM_TB1(do_raise_exception, EXCP_TRAP); |
|||
} |
|||
RETURN(); |
|||
} |
|||
|
|||
void op_set_lladdr (void) |
|||
{ |
|||
env->CP0_LLAddr = T2; |
|||
} |
|||
|
|||
void debug_eret (void); |
|||
void op_eret (void) |
|||
{ |
|||
CALL_FROM_TB0(debug_eret); |
|||
if (env->hflags & MIPS_HFLAG_ERL) |
|||
env->PC = env->CP0_ErrorEPC; |
|||
else |
|||
env->PC = env->CP0_EPC; |
|||
env->CP0_LLAddr = 1; |
|||
} |
|||
|
|||
void op_deret (void) |
|||
{ |
|||
CALL_FROM_TB0(debug_eret); |
|||
env->PC = env->CP0_DEPC; |
|||
} |
|||
|
|||
void op_save_state (void) |
|||
{ |
|||
env->hflags = PARAM1; |
|||
RETURN(); |
|||
} |
|||
|
|||
void op_save_pc (void) |
|||
{ |
|||
env->PC = PARAM1; |
|||
RETURN(); |
|||
} |
|||
|
|||
void op_raise_exception (void) |
|||
{ |
|||
CALL_FROM_TB1(do_raise_exception, PARAM1); |
|||
RETURN(); |
|||
} |
|||
|
|||
void op_raise_exception_err (void) |
|||
{ |
|||
CALL_FROM_TB2(do_raise_exception_err, PARAM1, PARAM2); |
|||
RETURN(); |
|||
} |
|||
|
|||
void op_exit_tb (void) |
|||
{ |
|||
EXIT_TB(); |
|||
} |
|||
|
|||
@ -0,0 +1,634 @@ |
|||
/*
|
|||
* MIPS emulation helpers for qemu. |
|||
* |
|||
* Copyright (c) 2004-2005 Jocelyn Mayer |
|||
* |
|||
* 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 <math.h> |
|||
#include "exec.h" |
|||
|
|||
#define MIPS_DEBUG_DISAS |
|||
|
|||
/*****************************************************************************/ |
|||
/* Exceptions processing helpers */ |
|||
void cpu_loop_exit(void) |
|||
{ |
|||
longjmp(env->jmp_env, 1); |
|||
} |
|||
|
|||
__attribute__ (( regparm(2) )) |
|||
void do_raise_exception_err (uint32_t exception, int error_code) |
|||
{ |
|||
#if 1 |
|||
if (logfile && exception < 0x100) |
|||
fprintf(logfile, "%s: %d %d\n", __func__, exception, error_code); |
|||
#endif |
|||
env->exception_index = exception; |
|||
env->error_code = error_code; |
|||
T0 = 0; |
|||
cpu_loop_exit(); |
|||
} |
|||
|
|||
__attribute__ (( regparm(1) )) |
|||
void do_raise_exception (uint32_t exception) |
|||
{ |
|||
do_raise_exception_err(exception, 0); |
|||
} |
|||
|
|||
#define MEMSUFFIX _raw |
|||
#include "op_helper_mem.c" |
|||
#undef MEMSUFFIX |
|||
#if !defined(CONFIG_USER_ONLY) |
|||
#define MEMSUFFIX _user |
|||
#include "op_helper_mem.c" |
|||
#undef MEMSUFFIX |
|||
#define MEMSUFFIX _kernel |
|||
#include "op_helper_mem.c" |
|||
#undef MEMSUFFIX |
|||
#endif |
|||
|
|||
/* 64 bits arithmetic for 32 bits hosts */ |
|||
#if (HOST_LONG_BITS == 32) |
|||
static inline uint64_t get_HILO (void) |
|||
{ |
|||
return ((uint64_t)env->HI << 32) | (uint64_t)env->LO; |
|||
} |
|||
|
|||
static inline void set_HILO (uint64_t HILO) |
|||
{ |
|||
env->LO = HILO & 0xFFFFFFFF; |
|||
env->HI = HILO >> 32; |
|||
} |
|||
|
|||
void do_mult (void) |
|||
{ |
|||
set_HILO((int64_t)T0 * (int64_t)T1); |
|||
} |
|||
|
|||
void do_multu (void) |
|||
{ |
|||
set_HILO((uint64_t)T0 * (uint64_t)T1); |
|||
} |
|||
|
|||
void do_madd (void) |
|||
{ |
|||
int64_t tmp; |
|||
|
|||
tmp = ((int64_t)T0 * (int64_t)T1); |
|||
set_HILO((int64_t)get_HILO() + tmp); |
|||
} |
|||
|
|||
void do_maddu (void) |
|||
{ |
|||
uint64_t tmp; |
|||
|
|||
tmp = ((uint64_t)T0 * (uint64_t)T1); |
|||
set_HILO(get_HILO() + tmp); |
|||
} |
|||
|
|||
void do_msub (void) |
|||
{ |
|||
int64_t tmp; |
|||
|
|||
tmp = ((int64_t)T0 * (int64_t)T1); |
|||
set_HILO((int64_t)get_HILO() - tmp); |
|||
} |
|||
|
|||
void do_msubu (void) |
|||
{ |
|||
uint64_t tmp; |
|||
|
|||
tmp = ((uint64_t)T0 * (uint64_t)T1); |
|||
set_HILO(get_HILO() - tmp); |
|||
} |
|||
#endif |
|||
|
|||
/* CP0 helpers */ |
|||
__attribute__ (( regparm(2) )) |
|||
void do_mfc0 (int reg, int sel) |
|||
{ |
|||
const unsigned char *rn; |
|||
|
|||
if (sel != 0 && reg != 16 && reg != 28) { |
|||
rn = "invalid"; |
|||
goto print; |
|||
} |
|||
switch (reg) { |
|||
case 0: |
|||
T0 = env->CP0_index; |
|||
rn = "Index"; |
|||
break; |
|||
case 1: |
|||
T0 = cpu_mips_get_random(env); |
|||
rn = "Random"; |
|||
break; |
|||
case 2: |
|||
T0 = env->CP0_EntryLo0; |
|||
rn = "EntryLo0"; |
|||
break; |
|||
case 3: |
|||
T0 = env->CP0_EntryLo1; |
|||
rn = "EntryLo1"; |
|||
break; |
|||
case 4: |
|||
T0 = env->CP0_Context; |
|||
rn = "Context"; |
|||
break; |
|||
case 5: |
|||
T0 = env->CP0_PageMask; |
|||
rn = "PageMask"; |
|||
break; |
|||
case 6: |
|||
T0 = env->CP0_Wired; |
|||
rn = "Wired"; |
|||
break; |
|||
case 8: |
|||
T0 = env->CP0_BadVAddr; |
|||
rn = "BadVaddr"; |
|||
break; |
|||
case 9: |
|||
T0 = cpu_mips_get_count(env); |
|||
rn = "Count"; |
|||
break; |
|||
case 10: |
|||
T0 = env->CP0_EntryHi; |
|||
rn = "EntryHi"; |
|||
break; |
|||
case 11: |
|||
T0 = env->CP0_Compare; |
|||
rn = "Compare"; |
|||
break; |
|||
case 12: |
|||
T0 = env->CP0_Status; |
|||
if (env->hflags & MIPS_HFLAG_UM) |
|||
T0 |= CP0St_UM; |
|||
if (env->hflags & MIPS_HFLAG_ERL) |
|||
T0 |= CP0St_ERL; |
|||
if (env->hflags & MIPS_HFLAG_EXL) |
|||
T0 |= CP0St_EXL; |
|||
rn = "Status"; |
|||
break; |
|||
case 13: |
|||
T0 = env->CP0_Cause; |
|||
rn = "Cause"; |
|||
break; |
|||
case 14: |
|||
T0 = env->CP0_EPC; |
|||
rn = "EPC"; |
|||
break; |
|||
case 15: |
|||
T0 = env->CP0_PRid; |
|||
rn = "PRid"; |
|||
break; |
|||
case 16: |
|||
switch (sel) { |
|||
case 0: |
|||
T0 = env->CP0_Config0; |
|||
rn = "Config"; |
|||
break; |
|||
case 1: |
|||
T0 = env->CP0_Config1; |
|||
rn = "Config1"; |
|||
break; |
|||
default: |
|||
rn = "Unknown config register"; |
|||
break; |
|||
} |
|||
break; |
|||
case 17: |
|||
T0 = env->CP0_LLAddr >> 4; |
|||
rn = "LLAddr"; |
|||
break; |
|||
case 18: |
|||
T0 = env->CP0_WatchLo; |
|||
rn = "WatchLo"; |
|||
break; |
|||
case 19: |
|||
T0 = env->CP0_WatchHi; |
|||
rn = "WatchHi"; |
|||
break; |
|||
case 23: |
|||
T0 = env->CP0_Debug; |
|||
if (env->hflags & MIPS_HFLAG_DM) |
|||
T0 |= 1 << CP0DB_DM; |
|||
rn = "Debug"; |
|||
break; |
|||
case 24: |
|||
T0 = env->CP0_DEPC; |
|||
rn = "DEPC"; |
|||
break; |
|||
case 28: |
|||
switch (sel) { |
|||
case 0: |
|||
T0 = env->CP0_TagLo; |
|||
rn = "TagLo"; |
|||
break; |
|||
case 1: |
|||
T0 = env->CP0_DataLo; |
|||
rn = "DataLo"; |
|||
break; |
|||
default: |
|||
rn = "unknown sel"; |
|||
break; |
|||
} |
|||
break; |
|||
case 30: |
|||
T0 = env->CP0_ErrorEPC; |
|||
rn = "ErrorEPC"; |
|||
break; |
|||
case 31: |
|||
T0 = env->CP0_DESAVE; |
|||
rn = "DESAVE"; |
|||
break; |
|||
default: |
|||
rn = "unknown"; |
|||
break; |
|||
} |
|||
print: |
|||
#if defined MIPS_DEBUG_DISAS |
|||
if (loglevel & CPU_LOG_TB_IN_ASM) { |
|||
fprintf(logfile, "%08x mfc0 %s => %08x (%d %d)\n", |
|||
env->PC, rn, T0, reg, sel); |
|||
} |
|||
#endif |
|||
return; |
|||
} |
|||
|
|||
__attribute__ (( regparm(2) )) |
|||
void do_mtc0 (int reg, int sel) |
|||
{ |
|||
const unsigned char *rn; |
|||
uint32_t val, old, mask; |
|||
int i, raise; |
|||
|
|||
if (sel != 0 && reg != 16 && reg != 28) { |
|||
val = -1; |
|||
old = -1; |
|||
rn = "invalid"; |
|||
goto print; |
|||
} |
|||
switch (reg) { |
|||
case 0: |
|||
val = (env->CP0_index & 0x80000000) | (T0 & 0x0000000F); |
|||
old = env->CP0_index; |
|||
env->CP0_index = val; |
|||
rn = "Index"; |
|||
break; |
|||
case 2: |
|||
val = T0 & 0x03FFFFFFF; |
|||
old = env->CP0_EntryLo0; |
|||
env->CP0_EntryLo0 = val; |
|||
rn = "EntryLo0"; |
|||
break; |
|||
case 3: |
|||
val = T0 & 0x03FFFFFFF; |
|||
old = env->CP0_EntryLo1; |
|||
env->CP0_EntryLo1 = val; |
|||
rn = "EntryLo1"; |
|||
break; |
|||
case 4: |
|||
val = (env->CP0_Context & 0xFF000000) | (T0 & 0x00FFFFF0); |
|||
old = env->CP0_Context; |
|||
env->CP0_Context = val; |
|||
rn = "Context"; |
|||
break; |
|||
case 5: |
|||
val = T0 & 0x01FFE000; |
|||
old = env->CP0_PageMask; |
|||
env->CP0_PageMask = val; |
|||
rn = "PageMask"; |
|||
break; |
|||
case 6: |
|||
val = T0 & 0x0000000F; |
|||
old = env->CP0_Wired; |
|||
env->CP0_Wired = val; |
|||
rn = "Wired"; |
|||
break; |
|||
case 9: |
|||
val = T0; |
|||
old = cpu_mips_get_count(env); |
|||
cpu_mips_store_count(env, val); |
|||
rn = "Count"; |
|||
break; |
|||
case 10: |
|||
val = T0 & 0xFFFFF0FF; |
|||
old = env->CP0_EntryHi; |
|||
env->CP0_EntryHi = val; |
|||
rn = "EntryHi"; |
|||
break; |
|||
case 11: |
|||
val = T0; |
|||
old = env->CP0_Compare; |
|||
cpu_mips_store_compare(env, val); |
|||
rn = "Compare"; |
|||
break; |
|||
case 12: |
|||
val = T0 & 0xFA78FF01; |
|||
if (T0 & (1 << CP0St_UM)) |
|||
env->hflags |= MIPS_HFLAG_UM; |
|||
else |
|||
env->hflags &= ~MIPS_HFLAG_UM; |
|||
if (T0 & (1 << CP0St_ERL)) |
|||
env->hflags |= MIPS_HFLAG_ERL; |
|||
else |
|||
env->hflags &= ~MIPS_HFLAG_ERL; |
|||
if (T0 & (1 << CP0St_EXL)) |
|||
env->hflags |= MIPS_HFLAG_EXL; |
|||
else |
|||
env->hflags &= ~MIPS_HFLAG_EXL; |
|||
old = env->CP0_Status; |
|||
env->CP0_Status = val; |
|||
/* If we unmasked an asserted IRQ, raise it */ |
|||
mask = 0x0000FC00; |
|||
if (loglevel & CPU_LOG_TB_IN_ASM) { |
|||
fprintf(logfile, "Status %08x => %08x Cause %08x (%08x %08x %08x)\n", |
|||
old, val, env->CP0_Cause, old & mask, val & mask, |
|||
env->CP0_Cause & mask); |
|||
} |
|||
#if 1 |
|||
if ((val & (1 << CP0St_IE)) && !(old & (1 << CP0St_IE)) && |
|||
!(env->hflags & MIPS_HFLAG_EXL) && |
|||
!(env->hflags & MIPS_HFLAG_ERL) && |
|||
!(env->hflags & MIPS_HFLAG_DM) && |
|||
(env->CP0_Cause & mask)) { |
|||
if (logfile) |
|||
fprintf(logfile, "Raise pending IRQs\n"); |
|||
env->interrupt_request |= CPU_INTERRUPT_HARD; |
|||
do_raise_exception(EXCP_EXT_INTERRUPT); |
|||
} else if (!(val & 0x00000001) && (old & 0x00000001)) { |
|||
env->interrupt_request &= ~CPU_INTERRUPT_HARD; |
|||
} |
|||
#endif |
|||
rn = "Status"; |
|||
break; |
|||
case 13: |
|||
val = (env->CP0_Cause & 0xB000F87C) | (T0 & 0x000C00300); |
|||
old = env->CP0_Cause; |
|||
env->CP0_Cause = val; |
|||
#if 0 |
|||
/* Check if we ever asserted a software IRQ */ |
|||
for (i = 0; i < 2; i++) { |
|||
mask = 0x100 << i; |
|||
if ((val & mask) & !(old & mask)) |
|||
mips_set_irq(i); |
|||
} |
|||
#endif |
|||
rn = "Cause"; |
|||
break; |
|||
case 14: |
|||
val = T0; |
|||
old = env->CP0_EPC; |
|||
env->CP0_EPC = val; |
|||
rn = "EPC"; |
|||
break; |
|||
case 16: |
|||
switch (sel) { |
|||
case 0: |
|||
#if defined(MIPS_USES_R4K_TLB) |
|||
val = (env->CP0_Config0 & 0x8017FF80) | (T0 & 0x7E000001); |
|||
#else |
|||
val = (env->CP0_Config0 & 0xFE17FF80) | (T0 & 0x00000001); |
|||
#endif |
|||
old = env->CP0_Config0; |
|||
env->CP0_Config0 = val; |
|||
rn = "Config0"; |
|||
break; |
|||
default: |
|||
val = -1; |
|||
old = -1; |
|||
rn = "bad config selector"; |
|||
break; |
|||
} |
|||
break; |
|||
case 18: |
|||
val = T0; |
|||
old = env->CP0_WatchLo; |
|||
env->CP0_WatchLo = val; |
|||
rn = "WatchLo"; |
|||
break; |
|||
case 19: |
|||
val = T0 & 0x40FF0FF8; |
|||
old = env->CP0_WatchHi; |
|||
env->CP0_WatchHi = val; |
|||
rn = "WatchHi"; |
|||
break; |
|||
case 23: |
|||
val = (env->CP0_Debug & 0x8C03FC1F) | (T0 & 0x13300120); |
|||
if (T0 & (1 << CP0DB_DM)) |
|||
env->hflags |= MIPS_HFLAG_DM; |
|||
else |
|||
env->hflags &= ~MIPS_HFLAG_DM; |
|||
old = env->CP0_Debug; |
|||
env->CP0_Debug = val; |
|||
rn = "Debug"; |
|||
break; |
|||
case 24: |
|||
val = T0; |
|||
old = env->CP0_DEPC; |
|||
env->CP0_DEPC = val; |
|||
rn = "DEPC"; |
|||
break; |
|||
case 28: |
|||
switch (sel) { |
|||
case 0: |
|||
val = T0 & 0xFFFFFCF6; |
|||
old = env->CP0_TagLo; |
|||
env->CP0_TagLo = val; |
|||
rn = "TagLo"; |
|||
break; |
|||
default: |
|||
val = -1; |
|||
old = -1; |
|||
rn = "invalid sel"; |
|||
break; |
|||
} |
|||
break; |
|||
case 30: |
|||
val = T0; |
|||
old = env->CP0_ErrorEPC; |
|||
env->CP0_ErrorEPC = val; |
|||
rn = "EPC"; |
|||
break; |
|||
case 31: |
|||
val = T0; |
|||
old = env->CP0_DESAVE; |
|||
env->CP0_DESAVE = val; |
|||
rn = "DESAVE"; |
|||
break; |
|||
default: |
|||
val = -1; |
|||
old = -1; |
|||
rn = "unknown"; |
|||
break; |
|||
} |
|||
print: |
|||
#if defined MIPS_DEBUG_DISAS |
|||
if (loglevel & CPU_LOG_TB_IN_ASM) { |
|||
fprintf(logfile, "%08x mtc0 %s %08x => %08x (%d %d %08x)\n", |
|||
env->PC, rn, T0, val, reg, sel, old); |
|||
} |
|||
#endif |
|||
return; |
|||
} |
|||
|
|||
/* TLB management */ |
|||
#if defined(MIPS_USES_R4K_TLB) |
|||
__attribute__ (( regparm(1) )) |
|||
static void invalidate_tb (int idx) |
|||
{ |
|||
tlb_t *tlb; |
|||
target_ulong addr, end; |
|||
|
|||
tlb = &env->tlb[idx]; |
|||
if (tlb->V[0]) { |
|||
addr = tlb->PFN[0]; |
|||
end = addr + (tlb->end - tlb->VPN); |
|||
tb_invalidate_page_range(addr, end); |
|||
} |
|||
if (tlb->V[1]) { |
|||
addr = tlb->PFN[1]; |
|||
end = addr + (tlb->end - tlb->VPN); |
|||
tb_invalidate_page_range(addr, end); |
|||
} |
|||
} |
|||
|
|||
__attribute__ (( regparm(1) )) |
|||
static void fill_tb (int idx) |
|||
{ |
|||
tlb_t *tlb; |
|||
int size; |
|||
|
|||
/* XXX: detect conflicting TLBs and raise a MCHECK exception when needed */ |
|||
tlb = &env->tlb[idx]; |
|||
tlb->VPN = env->CP0_EntryHi & 0xFFFFE000; |
|||
tlb->ASID = env->CP0_EntryHi & 0x000000FF; |
|||
size = env->CP0_PageMask >> 13; |
|||
size = 4 * (size + 1); |
|||
tlb->end = tlb->VPN + (1 << (8 + size)); |
|||
tlb->G = env->CP0_EntryLo0 & env->CP0_EntryLo1 & 1; |
|||
tlb->V[0] = env->CP0_EntryLo0 & 2; |
|||
tlb->D[0] = env->CP0_EntryLo0 & 4; |
|||
tlb->C[0] = (env->CP0_EntryLo0 >> 3) & 0x7; |
|||
tlb->PFN[0] = (env->CP0_EntryLo0 >> 6) << 12; |
|||
tlb->V[1] = env->CP0_EntryLo1 & 2; |
|||
tlb->D[1] = env->CP0_EntryLo1 & 4; |
|||
tlb->C[1] = (env->CP0_EntryLo1 >> 3) & 0x7; |
|||
tlb->PFN[1] = (env->CP0_EntryLo1 >> 6) << 12; |
|||
} |
|||
|
|||
void do_tlbwi (void) |
|||
{ |
|||
invalidate_tb(env->CP0_index & 0xF); |
|||
fill_tb(env->CP0_index & 0xF); |
|||
} |
|||
|
|||
void do_tlbwr (void) |
|||
{ |
|||
int r = cpu_mips_get_random(env); |
|||
|
|||
invalidate_tb(r); |
|||
fill_tb(r); |
|||
} |
|||
|
|||
void do_tlbp (void) |
|||
{ |
|||
tlb_t *tlb; |
|||
target_ulong tag; |
|||
uint8_t ASID; |
|||
int i; |
|||
|
|||
tag = (env->CP0_EntryHi & 0xFFFFE000); |
|||
ASID = env->CP0_EntryHi & 0x000000FF; |
|||
for (i = 0; i < 16; i++) { |
|||
tlb = &env->tlb[i]; |
|||
/* Check ASID, virtual page number & size */ |
|||
if ((tlb->G == 1 || tlb->ASID == ASID) && tlb->VPN == tag) { |
|||
/* TLB match */ |
|||
env->CP0_index = i; |
|||
break; |
|||
} |
|||
} |
|||
if (i == 16) { |
|||
env->CP0_index |= 0x80000000; |
|||
} |
|||
} |
|||
|
|||
void do_tlbr (void) |
|||
{ |
|||
tlb_t *tlb; |
|||
int size; |
|||
|
|||
tlb = &env->tlb[env->CP0_index & 0xF]; |
|||
env->CP0_EntryHi = tlb->VPN | tlb->ASID; |
|||
size = (tlb->end - tlb->VPN) >> 12; |
|||
env->CP0_PageMask = (size - 1) << 13; |
|||
env->CP0_EntryLo0 = tlb->V[0] | tlb->D[0] | (tlb->C[0] << 3) | |
|||
(tlb->PFN[0] >> 6); |
|||
env->CP0_EntryLo1 = tlb->V[1] | tlb->D[1] | (tlb->C[1] << 3) | |
|||
(tlb->PFN[1] >> 6); |
|||
} |
|||
#endif |
|||
|
|||
__attribute__ (( regparm(1) )) |
|||
void op_dump_ldst (const unsigned char *func) |
|||
{ |
|||
if (loglevel) |
|||
fprintf(logfile, "%s => %08x %08x\n", __func__, T0, T1); |
|||
} |
|||
|
|||
void dump_sc (void) |
|||
{ |
|||
if (loglevel) { |
|||
fprintf(logfile, "%s %08x at %08x (%08x)\n", __func__, |
|||
T1, T0, env->CP0_LLAddr); |
|||
} |
|||
} |
|||
|
|||
void debug_eret (void) |
|||
{ |
|||
if (loglevel) { |
|||
fprintf(logfile, "ERET: pc %08x EPC %08x ErrorEPC %08x (%d)\n", |
|||
env->PC, env->CP0_EPC, env->CP0_ErrorEPC, |
|||
env->hflags & MIPS_HFLAG_ERL ? 1 : 0); |
|||
} |
|||
} |
|||
|
|||
__attribute__ (( regparm(1) )) |
|||
void do_pmon (int function) |
|||
{ |
|||
function /= 2; |
|||
switch (function) { |
|||
case 2: /* TODO: char inbyte(int waitflag); */ |
|||
if (env->gpr[4] == 0) |
|||
env->gpr[2] = -1; |
|||
/* Fall through */ |
|||
case 11: /* TODO: char inbyte (void); */ |
|||
env->gpr[2] = -1; |
|||
break; |
|||
case 3: |
|||
case 12: |
|||
printf("%c", env->gpr[4] & 0xFF); |
|||
break; |
|||
case 17: |
|||
break; |
|||
case 158: |
|||
{ |
|||
unsigned char *fmt = (void *)env->gpr[4]; |
|||
printf("%s", fmt); |
|||
} |
|||
break; |
|||
} |
|||
} |
|||
@ -0,0 +1,143 @@ |
|||
void glue(do_lwl, MEMSUFFIX) (void) |
|||
{ |
|||
#if defined (DEBUG_OP) |
|||
target_ulong sav = T0; |
|||
#endif |
|||
uint32_t tmp; |
|||
|
|||
tmp = glue(ldl, MEMSUFFIX)(T0 & ~3); |
|||
/* XXX: this is valid only in big-endian mode
|
|||
* should be reverted for little-endian... |
|||
*/ |
|||
switch (T0 & 3) { |
|||
case 0: |
|||
T0 = tmp; |
|||
break; |
|||
case 1: |
|||
T0 = (tmp << 8) | (T1 & 0x000000FF); |
|||
break; |
|||
case 2: |
|||
T0 = (tmp << 16) | (T1 & 0x0000FFFF); |
|||
break; |
|||
case 3: |
|||
T0 = (tmp << 24) | (T1 & 0x00FFFFFF); |
|||
break; |
|||
} |
|||
#if defined (DEBUG_OP) |
|||
if (logfile) { |
|||
fprintf(logfile, "%s: %08x - %08x %08x => %08x\n", |
|||
__func__, sav, tmp, T1, T0); |
|||
} |
|||
#endif |
|||
RETURN(); |
|||
} |
|||
|
|||
void glue(do_lwr, MEMSUFFIX) (void) |
|||
{ |
|||
#if defined (DEBUG_OP) |
|||
target_ulong sav = T0; |
|||
#endif |
|||
uint32_t tmp; |
|||
|
|||
tmp = glue(ldl, MEMSUFFIX)(T0 & ~3); |
|||
/* XXX: this is valid only in big-endian mode
|
|||
* should be reverted for little-endian... |
|||
*/ |
|||
switch (T0 & 3) { |
|||
case 0: |
|||
T0 = (tmp >> 24) | (T1 & 0xFFFFFF00); |
|||
break; |
|||
case 1: |
|||
T0 = (tmp >> 16) | (T1 & 0xFFFF0000); |
|||
break; |
|||
case 2: |
|||
T0 = (tmp >> 8) | (T1 & 0xFF000000); |
|||
break; |
|||
case 3: |
|||
T0 = tmp; |
|||
break; |
|||
} |
|||
#if defined (DEBUG_OP) |
|||
if (logfile) { |
|||
fprintf(logfile, "%s: %08x - %08x %08x => %08x\n", |
|||
__func__, sav, tmp, T1, T0); |
|||
} |
|||
#endif |
|||
RETURN(); |
|||
} |
|||
|
|||
void glue(do_swl, MEMSUFFIX) (void) |
|||
{ |
|||
#if defined (DEBUG_OP) |
|||
target_ulong sav; |
|||
#endif |
|||
uint32_t tmp; |
|||
|
|||
tmp = glue(ldl, MEMSUFFIX)(T0 & ~3); |
|||
#if defined (DEBUG_OP) |
|||
sav = tmp; |
|||
#endif |
|||
/* XXX: this is valid only in big-endian mode
|
|||
* should be reverted for little-endian... |
|||
*/ |
|||
switch (T0 & 3) { |
|||
case 0: |
|||
tmp = T1; |
|||
break; |
|||
case 1: |
|||
tmp = (tmp & 0xFF000000) | (T1 >> 8); |
|||
break; |
|||
case 2: |
|||
tmp = (tmp & 0xFFFF0000) | (T1 >> 16); |
|||
break; |
|||
case 3: |
|||
tmp = (tmp & 0xFFFFFF00) | (T1 >> 24); |
|||
break; |
|||
} |
|||
glue(stl, MEMSUFFIX)(T0 & ~3, tmp); |
|||
#if defined (DEBUG_OP) |
|||
if (logfile) { |
|||
fprintf(logfile, "%s: %08x - %08x %08x => %08x\n", |
|||
__func__, T0, sav, T1, tmp); |
|||
} |
|||
#endif |
|||
RETURN(); |
|||
} |
|||
|
|||
void glue(do_swr, MEMSUFFIX) (void) |
|||
{ |
|||
#if defined (DEBUG_OP) |
|||
target_ulong sav; |
|||
#endif |
|||
uint32_t tmp; |
|||
|
|||
tmp = glue(ldl, MEMSUFFIX)(T0 & ~3); |
|||
#if defined (DEBUG_OP) |
|||
sav = tmp; |
|||
#endif |
|||
/* XXX: this is valid only in big-endian mode
|
|||
* should be reverted for little-endian... |
|||
*/ |
|||
switch (T0 & 3) { |
|||
case 0: |
|||
tmp = (tmp & 0x00FFFFFF) | (T1 << 24); |
|||
break; |
|||
case 1: |
|||
tmp = (tmp & 0x0000FFFF) | (T1 << 16); |
|||
break; |
|||
case 2: |
|||
tmp = (tmp & 0x000000FF) | (T1 << 8); |
|||
break; |
|||
case 3: |
|||
tmp = T1; |
|||
break; |
|||
} |
|||
glue(stl, MEMSUFFIX)(T0 & ~3, tmp); |
|||
#if defined (DEBUG_OP) |
|||
if (logfile) { |
|||
fprintf(logfile, "%s: %08x - %08x %08x => %08x\n", |
|||
__func__, T0, sav, T1, tmp); |
|||
} |
|||
#endif |
|||
RETURN(); |
|||
} |
|||
@ -0,0 +1,113 @@ |
|||
/*
|
|||
* MIPS emulation memory micro-operations for qemu. |
|||
* |
|||
* Copyright (c) 2004-2005 Jocelyn Mayer |
|||
* |
|||
* 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 |
|||
*/ |
|||
|
|||
/* Standard loads and stores */ |
|||
void glue(op_lb, MEMSUFFIX) (void) |
|||
{ |
|||
T0 = glue(ldsb, MEMSUFFIX)(T0); |
|||
RETURN(); |
|||
} |
|||
|
|||
void glue(op_lbu, MEMSUFFIX) (void) |
|||
{ |
|||
T0 = glue(ldub, MEMSUFFIX)(T0); |
|||
RETURN(); |
|||
} |
|||
|
|||
void glue(op_sb, MEMSUFFIX) (void) |
|||
{ |
|||
glue(stb, MEMSUFFIX)(T0, T1); |
|||
RETURN(); |
|||
} |
|||
|
|||
void glue(op_lh, MEMSUFFIX) (void) |
|||
{ |
|||
T0 = glue(ldsw, MEMSUFFIX)(T0); |
|||
RETURN(); |
|||
} |
|||
|
|||
void glue(op_lhu, MEMSUFFIX) (void) |
|||
{ |
|||
T0 = glue(lduw, MEMSUFFIX)(T0); |
|||
RETURN(); |
|||
} |
|||
|
|||
void glue(op_sh, MEMSUFFIX) (void) |
|||
{ |
|||
glue(stw, MEMSUFFIX)(T0, T1); |
|||
RETURN(); |
|||
} |
|||
|
|||
void glue(op_lw, MEMSUFFIX) (void) |
|||
{ |
|||
T0 = glue(ldl, MEMSUFFIX)(T0); |
|||
RETURN(); |
|||
} |
|||
|
|||
void glue(op_sw, MEMSUFFIX) (void) |
|||
{ |
|||
glue(stl, MEMSUFFIX)(T0, T1); |
|||
RETURN(); |
|||
} |
|||
|
|||
/* "half" load and stores */ |
|||
void glue(op_lwl, MEMSUFFIX) (void) |
|||
{ |
|||
CALL_FROM_TB0(glue(do_lwl, MEMSUFFIX)); |
|||
RETURN(); |
|||
} |
|||
|
|||
void glue(op_lwr, MEMSUFFIX) (void) |
|||
{ |
|||
CALL_FROM_TB0(glue(do_lwr, MEMSUFFIX)); |
|||
RETURN(); |
|||
} |
|||
|
|||
void glue(op_swl, MEMSUFFIX) (void) |
|||
{ |
|||
CALL_FROM_TB0(glue(do_swl, MEMSUFFIX)); |
|||
RETURN(); |
|||
} |
|||
|
|||
void glue(op_swr, MEMSUFFIX) (void) |
|||
{ |
|||
CALL_FROM_TB0(glue(do_swr, MEMSUFFIX)); |
|||
RETURN(); |
|||
} |
|||
|
|||
void glue(op_ll, MEMSUFFIX) (void) |
|||
{ |
|||
T1 = T0; |
|||
T0 = glue(ldl, MEMSUFFIX)(T0); |
|||
env->CP0_LLAddr = T1; |
|||
RETURN(); |
|||
} |
|||
|
|||
void glue(op_sc, MEMSUFFIX) (void) |
|||
{ |
|||
CALL_FROM_TB0(dump_sc); |
|||
if (T0 == env->CP0_LLAddr) { |
|||
glue(stl, MEMSUFFIX)(T0, T1); |
|||
T0 = 1; |
|||
} else { |
|||
T0 = 0; |
|||
} |
|||
RETURN(); |
|||
} |
|||
@ -0,0 +1,65 @@ |
|||
/*
|
|||
* MIPS emulation micro-operations templates for reg load & store for qemu. |
|||
* |
|||
* Copyright (c) 2004-2005 Jocelyn Mayer |
|||
* |
|||
* 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 |
|||
*/ |
|||
|
|||
#if defined(REG) |
|||
void glue(op_load_gpr_T0_gpr, REG) (void) |
|||
{ |
|||
T0 = env->gpr[REG]; |
|||
RETURN(); |
|||
} |
|||
|
|||
void glue(op_store_T0_gpr_gpr, REG) (void) |
|||
{ |
|||
env->gpr[REG] = T0; |
|||
RETURN(); |
|||
} |
|||
|
|||
void glue(op_load_gpr_T1_gpr, REG) (void) |
|||
{ |
|||
T1 = env->gpr[REG]; |
|||
RETURN(); |
|||
} |
|||
|
|||
void glue(op_store_T1_gpr_gpr, REG) (void) |
|||
{ |
|||
env->gpr[REG] = T1; |
|||
RETURN(); |
|||
} |
|||
|
|||
void glue(op_load_gpr_T2_gpr, REG) (void) |
|||
{ |
|||
T2 = env->gpr[REG]; |
|||
RETURN(); |
|||
} |
|||
#endif |
|||
|
|||
#if defined (TN) |
|||
void glue(op_set_, TN) (void) |
|||
{ |
|||
TN = PARAM1; |
|||
RETURN(); |
|||
} |
|||
|
|||
void glue (op_reset_, TN) (void) |
|||
{ |
|||
TN = 0; |
|||
RETURN(); |
|||
} |
|||
#endif |
|||
File diff suppressed because it is too large
Loading…
Reference in new issue