diff options
author | Peter Maydell <peter.maydell@linaro.org> | 2015-06-26 11:32:58 +0100 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2015-06-26 11:32:58 +0100 |
commit | ccb0c7e122db72d3a5da798c6414d4912bba828f (patch) | |
tree | dceae6e3d7ba56cc60aca3167f25c13c76d37a3e | |
parent | 0a4a0312bf8b029cbd32a97db2cad669cf65ac49 (diff) | |
parent | 4b3bcd016d83cc75f6a495c1db54b6c77f037adc (diff) |
Merge remote-tracking branch 'remotes/lalrae/tags/mips-20150626' into staging
MIPS patches 2015-06-26
Changes:
* MIPS UHI semihosting support
* microMIPS32 R6 support
# gpg: Signature made Fri Jun 26 10:42:33 2015 BST using RSA key ID 0B29DA6B
# gpg: Good signature from "Leon Alrae <leon.alrae@imgtec.com>"
# gpg: WARNING: This key is not certified with a trusted signature!
# gpg: There is no indication that the signature belongs to the owner.
# Primary key fingerprint: 8DD3 2F98 5495 9D66 35D4 4FC0 5211 8E3C 0B29 DA6B
* remotes/lalrae/tags/mips-20150626:
target-mips: add mips32r6-generic CPU definition
target-mips: microMIPS32 R6 POOL16{A, C} instructions
target-mips: microMIPS32 R6 Major instructions
target-mips: microMIPS32 R6 POOL32{I, C} instructions
target-mips: microMIPS32 R6 POOL32F instructions
target-mips: microMIPS32 R6 POOL32A{XF} instructions
target-mips: microMIPS32 R6 branches and jumps
target-mips: add microMIPS32 R6 opcode enum
target-mips: signal RI for removed instructions in microMIPS R6
target-mips: raise RI exceptions when FIR.PS = 0
target-mips: rearrange gen_compute_compact_branch
target-mips: refactor {D}LSA, {D}ALIGN, {D}BITSWAP
target-mips: remove an unused argument
target-mips: add microMIPS TLBINV, TLBINVF
target-mips: fix {RD, WR}PGPR in microMIPS
target-mips: convert host to MIPS errno values when required
target-mips: add Unified Hosting Interface (UHI) support
target-mips: remove identical code in different branch
hw/mips: Do not clear BEV for MIPS malta kernel load
include/softmmu-semi.h: Make semihosting support 64-bit clean
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
-rw-r--r-- | hw/mips/mips_malta.c | 11 | ||||
-rw-r--r-- | include/exec/softmmu-semi.h | 13 | ||||
-rw-r--r-- | qemu-options.hx | 10 | ||||
-rw-r--r-- | target-mips/Makefile.objs | 2 | ||||
-rw-r--r-- | target-mips/helper.h | 2 | ||||
-rw-r--r-- | target-mips/mips-semi.c | 358 | ||||
-rw-r--r-- | target-mips/translate.c | 1749 | ||||
-rw-r--r-- | target-mips/translate_init.c | 37 |
8 files changed, 1675 insertions, 507 deletions
diff --git a/hw/mips/mips_malta.c b/hw/mips/mips_malta.c index 786a8f0638..3082e75340 100644 --- a/hw/mips/mips_malta.c +++ b/hw/mips/mips_malta.c @@ -53,6 +53,7 @@ #include "qemu/error-report.h" #include "hw/empty_slot.h" #include "sysemu/kvm.h" +#include "exec/semihost.h" //#define DEBUG_BOARD_INIT @@ -634,7 +635,13 @@ static void write_bootloader (CPUMIPSState *env, uint8_t *base, /* Second part of the bootloader */ p = (uint32_t *) (base + 0x580); - stl_p(p++, 0x24040002); /* addiu a0, zero, 2 */ + + if (semihosting_get_argc()) { + /* Preserve a0 content as arguments have been passed */ + stl_p(p++, 0x00000000); /* nop */ + } else { + stl_p(p++, 0x24040002); /* addiu a0, zero, 2 */ + } stl_p(p++, 0x3c1d0000 | (((ENVP_ADDR - 64) >> 16) & 0xffff)); /* lui sp, high(ENVP_ADDR) */ stl_p(p++, 0x37bd0000 | ((ENVP_ADDR - 64) & 0xffff)); /* ori sp, sp, low(ENVP_ADDR) */ stl_p(p++, 0x3c050000 | ((ENVP_ADDR >> 16) & 0xffff)); /* lui a1, high(ENVP_ADDR) */ @@ -887,7 +894,7 @@ static void main_cpu_reset(void *opaque) read only location. The kernel location and the arguments table location does not change. */ if (loaderparams.kernel_filename) { - env->CP0_Status &= ~((1 << CP0St_BEV) | (1 << CP0St_ERL)); + env->CP0_Status &= ~(1 << CP0St_ERL); } malta_mips_config(cpu); diff --git a/include/exec/softmmu-semi.h b/include/exec/softmmu-semi.h index 8401f7d587..1819cc2498 100644 --- a/include/exec/softmmu-semi.h +++ b/include/exec/softmmu-semi.h @@ -9,14 +9,14 @@ #ifndef SOFTMMU_SEMI_H #define SOFTMMU_SEMI_H 1 -static inline uint32_t softmmu_tget32(CPUArchState *env, uint32_t addr) +static inline uint32_t softmmu_tget32(CPUArchState *env, target_ulong addr) { uint32_t val; cpu_memory_rw_debug(ENV_GET_CPU(env), addr, (uint8_t *)&val, 4, 0); return tswap32(val); } -static inline uint32_t softmmu_tget8(CPUArchState *env, uint32_t addr) +static inline uint32_t softmmu_tget8(CPUArchState *env, target_ulong addr) { uint8_t val; @@ -28,7 +28,8 @@ static inline uint32_t softmmu_tget8(CPUArchState *env, uint32_t addr) #define get_user_u8(arg, p) ({ arg = softmmu_tget8(env, p) ; 0; }) #define get_user_ual(arg, p) get_user_u32(arg, p) -static inline void softmmu_tput32(CPUArchState *env, uint32_t addr, uint32_t val) +static inline void softmmu_tput32(CPUArchState *env, + target_ulong addr, uint32_t val) { val = tswap32(val); cpu_memory_rw_debug(ENV_GET_CPU(env), addr, (uint8_t *)&val, 4, 1); @@ -36,8 +37,8 @@ static inline void softmmu_tput32(CPUArchState *env, uint32_t addr, uint32_t val #define put_user_u32(arg, p) ({ softmmu_tput32(env, p, arg) ; 0; }) #define put_user_ual(arg, p) put_user_u32(arg, p) -static void *softmmu_lock_user(CPUArchState *env, uint32_t addr, uint32_t len, - int copy) +static void *softmmu_lock_user(CPUArchState *env, + target_ulong addr, target_ulong len, int copy) { uint8_t *p; /* TODO: Make this something that isn't fixed size. */ @@ -48,7 +49,7 @@ static void *softmmu_lock_user(CPUArchState *env, uint32_t addr, uint32_t len, return p; } #define lock_user(type, p, len, copy) softmmu_lock_user(env, p, len, copy) -static char *softmmu_lock_user_string(CPUArchState *env, uint32_t addr) +static char *softmmu_lock_user_string(CPUArchState *env, target_ulong addr) { char *p; char *s; diff --git a/qemu-options.hx b/qemu-options.hx index 987dbf0a8a..e6e3895918 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -3345,20 +3345,22 @@ Set OpenBIOS nvram @var{variable} to given @var{value} (PPC, SPARC only). ETEXI DEF("semihosting", 0, QEMU_OPTION_semihosting, "-semihosting semihosting mode\n", - QEMU_ARCH_ARM | QEMU_ARCH_M68K | QEMU_ARCH_XTENSA | QEMU_ARCH_LM32) + QEMU_ARCH_ARM | QEMU_ARCH_M68K | QEMU_ARCH_XTENSA | QEMU_ARCH_LM32 | + QEMU_ARCH_MIPS) STEXI @item -semihosting @findex -semihosting -Enable semihosting mode (ARM, M68K, Xtensa only). +Enable semihosting mode (ARM, M68K, Xtensa, MIPS only). ETEXI DEF("semihosting-config", HAS_ARG, QEMU_OPTION_semihosting_config, "-semihosting-config [enable=on|off][,target=native|gdb|auto][,arg=str[,...]]\n" \ " semihosting configuration\n", -QEMU_ARCH_ARM | QEMU_ARCH_M68K | QEMU_ARCH_XTENSA | QEMU_ARCH_LM32) +QEMU_ARCH_ARM | QEMU_ARCH_M68K | QEMU_ARCH_XTENSA | QEMU_ARCH_LM32 | +QEMU_ARCH_MIPS) STEXI @item -semihosting-config [enable=on|off][,target=native|gdb|auto][,arg=str[,...]] @findex -semihosting-config -Enable and configure semihosting (ARM, M68K, Xtensa only). +Enable and configure semihosting (ARM, M68K, Xtensa, MIPS only). @table @option @item target=@code{native|gdb|auto} Defines where the semihosting calls will be addressed, to QEMU (@code{native}) diff --git a/target-mips/Makefile.objs b/target-mips/Makefile.objs index 108fd9b501..bc5ed8511f 100644 --- a/target-mips/Makefile.objs +++ b/target-mips/Makefile.objs @@ -1,4 +1,4 @@ obj-y += translate.o dsp_helper.o op_helper.o lmi_helper.o helper.o cpu.o -obj-y += gdbstub.o msa_helper.o +obj-y += gdbstub.o msa_helper.o mips-semi.o obj-$(CONFIG_SOFTMMU) += machine.o obj-$(CONFIG_KVM) += kvm.o diff --git a/target-mips/helper.h b/target-mips/helper.h index 8df98c71b8..2b28e875cf 100644 --- a/target-mips/helper.h +++ b/target-mips/helper.h @@ -1,6 +1,8 @@ DEF_HELPER_3(raise_exception_err, noreturn, env, i32, int) DEF_HELPER_2(raise_exception, noreturn, env, i32) +DEF_HELPER_1(do_semihosting, void, env) + #ifdef TARGET_MIPS64 DEF_HELPER_4(sdl, void, env, tl, tl, int) DEF_HELPER_4(sdr, void, env, tl, tl, int) diff --git a/target-mips/mips-semi.c b/target-mips/mips-semi.c new file mode 100644 index 0000000000..1162c76df9 --- /dev/null +++ b/target-mips/mips-semi.c @@ -0,0 +1,358 @@ +/* + * Unified Hosting Interface syscalls. + * + * Copyright (c) 2015 Imagination Technologies + * + * 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 <sys/stat.h> +#include "cpu.h" +#include "exec/helper-proto.h" +#include "exec/softmmu-semi.h" +#include "exec/semihost.h" + +typedef enum UHIOp { + UHI_exit = 1, + UHI_open = 2, + UHI_close = 3, + UHI_read = 4, + UHI_write = 5, + UHI_lseek = 6, + UHI_unlink = 7, + UHI_fstat = 8, + UHI_argc = 9, + UHI_argnlen = 10, + UHI_argn = 11, + UHI_plog = 13, + UHI_assert = 14, + UHI_pread = 19, + UHI_pwrite = 20, + UHI_link = 22 +} UHIOp; + +typedef struct UHIStat { + int16_t uhi_st_dev; + uint16_t uhi_st_ino; + uint32_t uhi_st_mode; + uint16_t uhi_st_nlink; + uint16_t uhi_st_uid; + uint16_t uhi_st_gid; + int16_t uhi_st_rdev; + uint64_t uhi_st_size; + uint64_t uhi_st_atime; + uint64_t uhi_st_spare1; + uint64_t uhi_st_mtime; + uint64_t uhi_st_spare2; + uint64_t uhi_st_ctime; + uint64_t uhi_st_spare3; + uint64_t uhi_st_blksize; + uint64_t uhi_st_blocks; + uint64_t uhi_st_spare4[2]; +} UHIStat; + +enum UHIOpenFlags { + UHIOpen_RDONLY = 0x0, + UHIOpen_WRONLY = 0x1, + UHIOpen_RDWR = 0x2, + UHIOpen_APPEND = 0x8, + UHIOpen_CREAT = 0x200, + UHIOpen_TRUNC = 0x400, + UHIOpen_EXCL = 0x800 +}; + +/* Errno values taken from asm-mips/errno.h */ +static uint16_t host_to_mips_errno[] = { + [ENAMETOOLONG] = 78, +#ifdef EOVERFLOW + [EOVERFLOW] = 79, +#endif +#ifdef ELOOP + [ELOOP] = 90, +#endif +}; + +static int errno_mips(int err) +{ + if (err < 0 || err >= ARRAY_SIZE(host_to_mips_errno)) { + return EINVAL; + } else if (host_to_mips_errno[err]) { + return host_to_mips_errno[err]; + } else { + return err; + } +} + +static int copy_stat_to_target(CPUMIPSState *env, const struct stat *src, + target_ulong vaddr) +{ + hwaddr len = sizeof(struct UHIStat); + UHIStat *dst = lock_user(VERIFY_WRITE, vaddr, len, 0); + if (!dst) { + errno = EFAULT; + return -1; + } + + dst->uhi_st_dev = tswap16(src->st_dev); + dst->uhi_st_ino = tswap16(src->st_ino); + dst->uhi_st_mode = tswap32(src->st_mode); + dst->uhi_st_nlink = tswap16(src->st_nlink); + dst->uhi_st_uid = tswap16(src->st_uid); + dst->uhi_st_gid = tswap16(src->st_gid); + dst->uhi_st_rdev = tswap16(src->st_rdev); + dst->uhi_st_size = tswap64(src->st_size); + dst->uhi_st_atime = tswap64(src->st_atime); + dst->uhi_st_mtime = tswap64(src->st_mtime); + dst->uhi_st_ctime = tswap64(src->st_ctime); +#ifdef _WIN32 + dst->uhi_st_blksize = 0; + dst->uhi_st_blocks = 0; +#else + dst->uhi_st_blksize = tswap64(src->st_blksize); + dst->uhi_st_blocks = tswap64(src->st_blocks); +#endif + unlock_user(dst, vaddr, len); + return 0; +} + +static int get_open_flags(target_ulong target_flags) +{ + int open_flags = 0; + + if (target_flags & UHIOpen_RDWR) { + open_flags |= O_RDWR; + } else if (target_flags & UHIOpen_WRONLY) { + open_flags |= O_WRONLY; + } else { + open_flags |= O_RDONLY; + } + + open_flags |= (target_flags & UHIOpen_APPEND) ? O_APPEND : 0; + open_flags |= (target_flags & UHIOpen_CREAT) ? O_CREAT : 0; + open_flags |= (target_flags & UHIOpen_TRUNC) ? O_TRUNC : 0; + open_flags |= (target_flags & UHIOpen_EXCL) ? O_EXCL : 0; + + return open_flags; +} + +static int write_to_file(CPUMIPSState *env, target_ulong fd, target_ulong vaddr, + target_ulong len, target_ulong offset) +{ + int num_of_bytes; + void *dst = lock_user(VERIFY_READ, vaddr, len, 1); + if (!dst) { + errno = EFAULT; + return -1; + } + + if (offset) { +#ifdef _WIN32 + num_of_bytes = 0; +#else + num_of_bytes = pwrite(fd, dst, len, offset); +#endif + } else { + num_of_bytes = write(fd, dst, len); + } + + unlock_user(dst, vaddr, 0); + return num_of_bytes; +} + +static int read_from_file(CPUMIPSState *env, target_ulong fd, + target_ulong vaddr, target_ulong len, + target_ulong offset) +{ + int num_of_bytes; + void *dst = lock_user(VERIFY_WRITE, vaddr, len, 0); + if (!dst) { + errno = EFAULT; + return -1; + } + + if (offset) { +#ifdef _WIN32 + num_of_bytes = 0; +#else + num_of_bytes = pread(fd, dst, len, offset); +#endif + } else { + num_of_bytes = read(fd, dst, len); + } + + unlock_user(dst, vaddr, len); + return num_of_bytes; +} + +static int copy_argn_to_target(CPUMIPSState *env, int arg_num, + target_ulong vaddr) +{ + int strsize = strlen(semihosting_get_arg(arg_num)) + 1; + char *dst = lock_user(VERIFY_WRITE, vaddr, strsize, 0); + if (!dst) { + return -1; + } + + strcpy(dst, semihosting_get_arg(arg_num)); + + unlock_user(dst, vaddr, strsize); + return 0; +} + +#define GET_TARGET_STRING(p, addr) \ + do { \ + p = lock_user_string(addr); \ + if (!p) { \ + gpr[2] = -1; \ + gpr[3] = EFAULT; \ + goto uhi_done; \ + } \ + } while (0) + +#define FREE_TARGET_STRING(p, gpr) \ + do { \ + unlock_user(p, gpr, 0); \ + } while (0) + +void helper_do_semihosting(CPUMIPSState *env) +{ + target_ulong *gpr = env->active_tc.gpr; + const UHIOp op = gpr[25]; + char *p, *p2; + + switch (op) { + case UHI_exit: + qemu_log("UHI(%d): exit(%d)\n", op, (int)gpr[4]); + exit(gpr[4]); + case UHI_open: + GET_TARGET_STRING(p, gpr[4]); + if (!strcmp("/dev/stdin", p)) { + gpr[2] = 0; + } else if (!strcmp("/dev/stdout", p)) { + gpr[2] = 1; + } else if (!strcmp("/dev/stderr", p)) { + gpr[2] = 2; + } else { + gpr[2] = open(p, get_open_flags(gpr[5]), gpr[6]); + gpr[3] = errno_mips(errno); + } + FREE_TARGET_STRING(p, gpr[4]); + break; + case UHI_close: + if (gpr[4] < 3) { + /* ignore closing stdin/stdout/stderr */ + gpr[2] = 0; + goto uhi_done; + } + gpr[2] = close(gpr[4]); + gpr[3] = errno_mips(errno); + break; + case UHI_read: + gpr[2] = read_from_file(env, gpr[4], gpr[5], gpr[6], 0); + gpr[3] = errno_mips(errno); + break; + case UHI_write: + gpr[2] = write_to_file(env, gpr[4], gpr[5], gpr[6], 0); + gpr[3] = errno_mips(errno); + break; + case UHI_lseek: + gpr[2] = lseek(gpr[4], gpr[5], gpr[6]); + gpr[3] = errno_mips(errno); + break; + case UHI_unlink: + GET_TARGET_STRING(p, gpr[4]); + gpr[2] = remove(p); + gpr[3] = errno_mips(errno); + FREE_TARGET_STRING(p, gpr[4]); + break; + case UHI_fstat: + { + struct stat sbuf; + memset(&sbuf, 0, sizeof(sbuf)); + gpr[2] = fstat(gpr[4], &sbuf); + gpr[3] = errno_mips(errno); + if (gpr[2]) { + goto uhi_done; + } + gpr[2] = copy_stat_to_target(env, &sbuf, gpr[5]); + gpr[3] = errno_mips(errno); + } + break; + case UHI_argc: + gpr[2] = semihosting_get_argc(); + break; + case UHI_argnlen: + if (gpr[4] >= semihosting_get_argc()) { + gpr[2] = -1; + goto uhi_done; + } + gpr[2] = strlen(semihosting_get_arg(gpr[4])); + break; + case UHI_argn: + if (gpr[4] >= semihosting_get_argc()) { + gpr[2] = -1; + goto uhi_done; + } + gpr[2] = copy_argn_to_target(env, gpr[4], gpr[5]); + break; + case UHI_plog: + GET_TARGET_STRING(p, gpr[4]); + p2 = strstr(p, "%d"); + if (p2) { + int char_num = p2 - p; + char *buf = g_malloc(char_num + 1); + strncpy(buf, p, char_num); + buf[char_num] = '\0'; + gpr[2] = printf("%s%d%s", buf, (int)gpr[5], p2 + 2); + g_free(buf); + } else { + gpr[2] = printf("%s", p); + } + FREE_TARGET_STRING(p, gpr[4]); + break; + case UHI_assert: + GET_TARGET_STRING(p, gpr[4]); + GET_TARGET_STRING(p2, gpr[5]); + printf("assertion '"); + printf("\"%s\"", p); + printf("': file \"%s\", line %d\n", p2, (int)gpr[6]); + FREE_TARGET_STRING(p2, gpr[5]); + FREE_TARGET_STRING(p, gpr[4]); + abort(); + break; + case UHI_pread: + gpr[2] = read_from_file(env, gpr[4], gpr[5], gpr[6], gpr[7]); + gpr[3] = errno_mips(errno); + break; + case UHI_pwrite: + gpr[2] = write_to_file(env, gpr[4], gpr[5], gpr[6], gpr[7]); + gpr[3] = errno_mips(errno); + break; +#ifndef _WIN32 + case UHI_link: + GET_TARGET_STRING(p, gpr[4]); + GET_TARGET_STRING(p2, gpr[5]); + gpr[2] = link(p, p2); + gpr[3] = errno_mips(errno); + FREE_TARGET_STRING(p2, gpr[5]); + FREE_TARGET_STRING(p, gpr[4]); + break; +#endif + default: + fprintf(stderr, "Unknown UHI operation %d\n", op); + abort(); + } +uhi_done: + return; +} diff --git a/target-mips/translate.c b/target-mips/translate.c index a2dbad5ed2..73028572c9 100644 --- a/target-mips/translate.c +++ b/target-mips/translate.c @@ -29,6 +29,7 @@ #include "exec/helper-proto.h" #include "exec/helper-gen.h" #include "sysemu/kvm.h" +#include "exec/semihost.h" #include "trace-tcg.h" @@ -1429,6 +1430,7 @@ typedef struct DisasContext { uint64_t PAMask; bool mvh; int CP0_LLAddr_shift; + bool ps; } DisasContext; enum { @@ -1723,6 +1725,15 @@ static target_long addr_add(DisasContext *ctx, target_long base, return sum; } +static inline void gen_move_low32(TCGv ret, TCGv_i64 arg) +{ +#if defined(TARGET_MIPS64) + tcg_gen_ext32s_tl(ret, arg); +#else + tcg_gen_trunc_i64_tl(ret, arg); +#endif +} + static inline void check_cp0_enabled(DisasContext *ctx) { if (unlikely(!(ctx->hflags & MIPS_HFLAG_CP0))) @@ -1816,6 +1827,16 @@ static inline void check_insn_opc_removed(DisasContext *ctx, int flags) } } +/* This code generates a "reserved instruction" exception if the + CPU does not support 64-bit paired-single (PS) floating point data type */ +static inline void check_ps(DisasContext *ctx) +{ + if (unlikely(!ctx->ps)) { + generate_exception(ctx, EXCP_RI); + } + check_cp1_64bitmode(ctx); +} + #ifdef TARGET_MIPS64 /* This code generates a "reserved instruction" exception if 64-bit instructions are not enabled. */ @@ -1849,7 +1870,7 @@ static inline void gen_cmp ## type ## _ ## fmt(DisasContext *ctx, int n, \ TCGv_i##bits fp1 = tcg_temp_new_i##bits (); \ switch (ifmt) { \ case FMT_PS: \ - check_cp1_64bitmode(ctx); \ + check_ps(ctx); \ break; \ case FMT_D: \ if (abs) { \ @@ -3188,45 +3209,46 @@ static inline void gen_r6_ld(target_long addr, int reg, int memidx, tcg_temp_free(t0); } -static inline void gen_pcrel(DisasContext *ctx, int rs, int16_t imm) +static inline void gen_pcrel(DisasContext *ctx, int opc, target_ulong pc, + int rs) { target_long offset; target_long addr; - switch (MASK_OPC_PCREL_TOP2BITS(ctx->opcode)) { + switch (MASK_OPC_PCREL_TOP2BITS(opc)) { case OPC_ADDIUPC: if (rs != 0) { offset = sextract32(ctx->opcode << 2, 0, 21); - addr = addr_add(ctx, ctx->pc, offset); + addr = addr_add(ctx, pc, offset); tcg_gen_movi_tl(cpu_gpr[rs], addr); } break; case R6_OPC_LWPC: offset = sextract32(ctx->opcode << 2, 0, 21); - addr = addr_add(ctx, ctx->pc, offset); + addr = addr_add(ctx, pc, offset); gen_r6_ld(addr, rs, ctx->mem_idx, MO_TESL); break; #if defined(TARGET_MIPS64) case OPC_LWUPC: check_mips_64(ctx); offset = sextract32(ctx->opcode << 2, 0, 21); - addr = addr_add(ctx, ctx->pc, offset); + addr = addr_add(ctx, pc, offset); gen_r6_ld(addr, rs, ctx->mem_idx, MO_TEUL); break; #endif default: - switch (MASK_OPC_PCREL_TOP5BITS(ctx->opcode)) { + switch (MASK_OPC_PCREL_TOP5BITS(opc)) { case OPC_AUIPC: if (rs != 0) { - offset = imm << 16; - addr = addr_add(ctx, ctx->pc, offset); + offset = sextract32(ctx->opcode, 0, 16) << 16; + addr = addr_add(ctx, pc, offset); tcg_gen_movi_tl(cpu_gpr[rs], addr); } break; case OPC_ALUIPC: if (rs != 0) { - offset = imm << 16; - addr = ~0xFFFF & addr_add(ctx, ctx->pc, offset); + offset = sextract32(ctx->opcode, 0, 16) << 16; + addr = ~0xFFFF & addr_add(ctx, pc, offset); tcg_gen_movi_tl(cpu_gpr[rs], addr); } break; @@ -3237,7 +3259,7 @@ static inline void gen_pcrel(DisasContext *ctx, int rs, int16_t imm) case R6_OPC_LDPC + (3 << 16): check_mips_64(ctx); offset = sextract32(ctx->opcode << 3, 0, 21); - addr = addr_add(ctx, (ctx->pc & ~0x7), offset); + addr = addr_add(ctx, (pc & ~0x7), offset); gen_r6_ld(addr, rs, ctx->mem_idx, MO_TEQ); break; #endif @@ -4845,17 +4867,94 @@ static void gen_bshfl (DisasContext *ctx, uint32_t op2, int rt, int rd) tcg_temp_free(t0); } -#ifndef CONFIG_USER_ONLY -/* CP0 (MMU and control) */ -static inline void gen_move_low32(TCGv ret, TCGv_i64 arg) +static void gen_lsa(DisasContext *ctx, int opc, int rd, int rs, int rt, + int imm2) +{ + TCGv t0; + TCGv t1; + if (rd == 0) { + /* Treat as NOP. */ + return; + } + t0 = tcg_temp_new(); + t1 = tcg_temp_new(); + gen_load_gpr(t0, rs); + gen_load_gpr(t1, rt); + tcg_gen_shli_tl(t0, t0, imm2 + 1); + tcg_gen_add_tl(cpu_gpr[rd], t0, t1); + if (opc == OPC_LSA) { + tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]); + } + + tcg_temp_free(t1); + tcg_temp_free(t0); + + return; +} + +static void gen_align(DisasContext *ctx, int opc, int rd, int rs, int rt, + int bp) { + TCGv t0; + if (rd == 0) { + /* Treat as NOP. */ + return; + } + t0 = tcg_temp_new(); + gen_load_gpr(t0, rt); + if (bp == 0) { + tcg_gen_mov_tl(cpu_gpr[rd], t0); + } else { + TCGv t1 = tcg_temp_new(); + gen_load_gpr(t1, rs); + switch (opc) { + case OPC_ALIGN: + { + TCGv_i64 t2 = tcg_temp_new_i64(); + tcg_gen_concat_tl_i64(t2, t1, t0); + tcg_gen_shri_i64(t2, t2, 8 * (4 - bp)); + gen_move_low32(cpu_gpr[rd], t2); + tcg_temp_free_i64(t2); + } + break; #if defined(TARGET_MIPS64) - tcg_gen_ext32s_tl(ret, arg); -#else - tcg_gen_trunc_i64_tl(ret, arg); + case OPC_DALIGN: + tcg_gen_shli_tl(t0, t0, 8 * bp); + tcg_gen_shri_tl(t1, t1, 8 * (8 - bp)); + tcg_gen_or_tl(cpu_gpr[rd], t1, t0); + break; +#endif + } + tcg_temp_free(t1); + } + + tcg_temp_free(t0); +} + +static void gen_bitswap(DisasContext *ctx, int opc, int rd, int rt) +{ + TCGv t0; + if (rd == 0) { + /* Treat as NOP. */ + return; + } + t0 = tcg_temp_new(); + gen_load_gpr(t0, rt); + switch (opc) { + case OPC_BITSWAP: + gen_helper_bitswap(cpu_gpr[rd], t0); + break; +#if defined(TARGET_MIPS64) + case OPC_DBITSWAP: + gen_helper_dbitswap(cpu_gpr[rd], t0); + break; #endif + } + tcg_temp_free(t0); } +#ifndef CONFIG_USER_ONLY +/* CP0 (MMU and control) */ static inline void gen_mthc0_entrylo(TCGv arg, target_ulong off) { TCGv_i64 t0 = tcg_temp_new_i64(); @@ -8344,7 +8443,8 @@ static void gen_compute_branch1(DisasContext *ctx, uint32_t op, /* R6 CP1 Branches */ static void gen_compute_branch1_r6(DisasContext *ctx, uint32_t op, - int32_t ft, int32_t offset) + int32_t ft, int32_t offset, + int delayslot_size) { target_ulong btarget; const char *opn = "cp1 cond branch"; @@ -8387,7 +8487,15 @@ static void gen_compute_branch1_r6(DisasContext *ctx, uint32_t op, MIPS_DEBUG("%s: cond %02x target " TARGET_FMT_lx, opn, ctx->hflags, btarget); ctx->btarget = btarget; - ctx->hflags |= MIPS_HFLAG_BDS32; + + switch (delayslot_size) { + case 2: + ctx->hflags |= MIPS_HFLAG_BDS16; + break; + case 4: + ctx->hflags |= MIPS_HFLAG_BDS32; + break; + } out: tcg_temp_free_i64(t0); @@ -8912,7 +9020,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, }; enum { BINOP, CMPOP, OTHEROP } optype = OTHEROP; uint32_t func = ctx->opcode & 0x3f; - switch (op1) { case OPC_ADD_S: { @@ -9405,8 +9512,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, opn = "cvt.l.s"; break; case OPC_CVT_PS_S: - check_insn_opc_removed(ctx, ISA_MIPS32R6); - check_cp1_64bitmode(ctx); + check_ps(ctx); { TCGv_i64 fp64 = tcg_temp_new_i64(); TCGv_i32 fp32_0 = tcg_temp_new_i32(); @@ -10023,8 +10129,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, opn = "cvt.d.l"; break; case OPC_CVT_PS_PW: - check_insn_opc_removed(ctx, ISA_MIPS32R6); - check_cp1_64bitmode(ctx); + check_ps(ctx); { TCGv_i64 fp0 = tcg_temp_new_i64(); @@ -10036,7 +10141,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, opn = "cvt.ps.pw"; break; case OPC_ADD_PS: - check_cp1_64bitmode(ctx); + check_ps(ctx); { TCGv_i64 fp0 = tcg_temp_new_i64(); TCGv_i64 fp1 = tcg_temp_new_i64(); @@ -10051,7 +10156,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, opn = "add.ps"; break; case OPC_SUB_PS: - check_cp1_64bitmode(ctx); + check_ps(ctx); { TCGv_i64 fp0 = tcg_temp_new_i64(); TCGv_i64 fp1 = tcg_temp_new_i64(); @@ -10066,7 +10171,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, opn = "sub.ps"; break; case OPC_MUL_PS: - check_cp1_64bitmode(ctx); + check_ps(ctx); { TCGv_i64 fp0 = tcg_temp_new_i64(); TCGv_i64 fp1 = tcg_temp_new_i64(); @@ -10081,7 +10186,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, opn = "mul.ps"; break; case OPC_ABS_PS: - check_cp1_64bitmode(ctx); + check_ps(ctx); { TCGv_i64 fp0 = tcg_temp_new_i64(); @@ -10093,7 +10198,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, opn = "abs.ps"; break; case OPC_MOV_PS: - check_cp1_64bitmode(ctx); + check_ps(ctx); { TCGv_i64 fp0 = tcg_temp_new_i64(); @@ -10104,7 +10209,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, opn = "mov.ps"; break; case OPC_NEG_PS: - check_cp1_64bitmode(ctx); + check_ps(ctx); { TCGv_i64 fp0 = tcg_temp_new_i64(); @@ -10116,12 +10221,12 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, opn = "neg.ps"; break; case OPC_MOVCF_PS: - check_cp1_64bitmode(ctx); + check_ps(ctx); gen_movcf_ps(ctx, fs, fd, (ft >> 2) & 0x7, ft & 0x1); opn = "movcf.ps"; break; case OPC_MOVZ_PS: - check_cp1_64bitmode(ctx); + check_ps(ctx); { TCGLabel *l1 = gen_new_label(); TCGv_i64 fp0; @@ -10137,7 +10242,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, opn = "movz.ps"; break; case OPC_MOVN_PS: - check_cp1_64bitmode(ctx); + check_ps(ctx); { TCGLabel *l1 = gen_new_label(); TCGv_i64 fp0; @@ -10154,7 +10259,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, opn = "movn.ps"; break; case OPC_ADDR_PS: - check_cp1_64bitmode(ctx); + check_ps(ctx); { TCGv_i64 fp0 = tcg_temp_new_i64(); TCGv_i64 fp1 = tcg_temp_new_i64(); @@ -10169,7 +10274,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, opn = "addr.ps"; break; case OPC_MULR_PS: - check_cp1_64bitmode(ctx); + check_ps(ctx); { TCGv_i64 fp0 = tcg_temp_new_i64(); TCGv_i64 fp1 = tcg_temp_new_i64(); @@ -10184,7 +10289,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, opn = "mulr.ps"; break; case OPC_RECIP2_PS: - check_cp1_64bitmode(ctx); + check_ps(ctx); { TCGv_i64 fp0 = tcg_temp_new_i64(); TCGv_i64 fp1 = tcg_temp_new_i64(); @@ -10199,7 +10304,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, opn = "recip2.ps"; break; case OPC_RECIP1_PS: - check_cp1_64bitmode(ctx); + check_ps(ctx); { TCGv_i64 fp0 = tcg_temp_new_i64(); @@ -10211,7 +10316,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, opn = "recip1.ps"; break; case OPC_RSQRT1_PS: - check_cp1_64bitmode(ctx); + check_ps(ctx); { TCGv_i64 fp0 = tcg_temp_new_i64(); @@ -10223,7 +10328,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, opn = "rsqrt1.ps"; break; case OPC_RSQRT2_PS: - check_cp1_64bitmode(ctx); + check_ps(ctx); { TCGv_i64 fp0 = tcg_temp_new_i64(); TCGv_i64 fp1 = tcg_temp_new_i64(); @@ -10250,7 +10355,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, opn = "cvt.s.pu"; break; case OPC_CVT_PW_PS: - check_cp1_64bitmode(ctx); + check_ps(ctx); { TCGv_i64 fp0 = tcg_temp_new_i64(); @@ -10274,7 +10379,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, opn = "cvt.s.pl"; break; case OPC_PLL_PS: - check_cp1_64bitmode(ctx); + check_ps(ctx); { TCGv_i32 fp0 = tcg_temp_new_i32(); TCGv_i32 fp1 = tcg_temp_new_i32(); @@ -10289,7 +10394,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, opn = "pll.ps"; break; case OPC_PLU_PS: - check_cp1_64bitmode(ctx); + check_ps(ctx); { TCGv_i32 fp0 = tcg_temp_new_i32(); TCGv_i32 fp1 = tcg_temp_new_i32(); @@ -10304,7 +10409,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, opn = "plu.ps"; break; case OPC_PUL_PS: - check_cp1_64bitmode(ctx); + check_ps(ctx); { TCGv_i32 fp0 = tcg_temp_new_i32(); TCGv_i32 fp1 = tcg_temp_new_i32(); @@ -10319,7 +10424,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, opn = "pul.ps"; break; case OPC_PUU_PS: - check_cp1_64bitmode(ctx); + check_ps(ctx); { TCGv_i32 fp0 = tcg_temp_new_i32(); TCGv_i32 fp1 = tcg_temp_new_i32(); @@ -10478,7 +10583,7 @@ static void gen_flt3_arith (DisasContext *ctx, uint32_t opc, switch (opc) { case OPC_ALNV_PS: - check_cp1_64bitmode(ctx); + check_ps(ctx); { TCGv t0 = tcg_temp_local_new(); TCGv_i32 fp = tcg_temp_new_i32(); @@ -10553,7 +10658,7 @@ static void gen_flt3_arith (DisasContext *ctx, uint32_t opc, opn = "madd.d"; break; case OPC_MADD_PS: - check_cp1_64bitmode(ctx); + check_ps(ctx); { TCGv_i64 fp0 = tcg_temp_new_i64(); TCGv_i64 fp1 = tcg_temp_new_i64(); @@ -10608,7 +10713,7 @@ static void gen_flt3_arith (DisasContext *ctx, uint32_t opc, opn = "msub.d"; break; case OPC_MSUB_PS: - check_cp1_64bitmode(ctx); + check_ps(ctx); { TCGv_i64 fp0 = tcg_temp_new_i64(); TCGv_i64 fp1 = tcg_temp_new_i64(); @@ -10663,7 +10768,7 @@ static void gen_flt3_arith (DisasContext *ctx, uint32_t opc, opn = "nmadd.d"; break; case OPC_NMADD_PS: - check_cp1_64bitmode(ctx); + check_ps(ctx); { TCGv_i64 fp0 = tcg_temp_new_i64(); TCGv_i64 fp1 = tcg_temp_new_i64(); @@ -10718,7 +10823,7 @@ static void gen_flt3_arith (DisasContext *ctx, uint32_t opc, opn = "nmsub.d"; break; case OPC_NMSUB_PS: - check_cp1_64bitmode(ctx); + check_ps(ctx); { TCGv_i64 fp0 = tcg_temp_new_i64(); TCGv_i64 fp1 = tcg_temp_new_i64(); @@ -10884,6 +10989,243 @@ static void gen_branch(DisasContext *ctx, int insn_bytes) } } +/* Compact Branches */ +static void gen_compute_compact_branch(DisasContext *ctx, uint32_t opc, + int rs, int rt, int32_t offset) +{ + int bcond_compute = 0; + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + int m16_lowbit = (ctx->hflags & MIPS_HFLAG_M16) != 0; + + if (ctx->hflags & MIPS_HFLAG_BMASK) { +#ifdef MIPS_DEBUG_DISAS + LOG_DISAS("Branch in delay / forbidden slot at PC 0x" TARGET_FMT_lx + "\n", ctx->pc); +#endif + generate_exception(ctx, EXCP_RI); + goto out; + } + + /* Load needed operands and calculate btarget */ + switch (opc) { + /* compact branch */ + case OPC_BOVC: /* OPC_BEQZALC, OPC_BEQC */ + case OPC_BNVC: /* OPC_BNEZALC, OPC_BNEC */ + gen_load_gpr(t0, rs); + gen_load_gpr(t1, rt); + bcond_compute = 1; + ctx->btarget = addr_add(ctx, ctx->pc + 4, offset); + if (rs <= rt && rs == 0) { + /* OPC_BEQZALC, OPC_BNEZALC */ + tcg_gen_movi_tl(cpu_gpr[31], ctx->pc + 4 + m16_lowbit); + } + break; + case OPC_BLEZC: /* OPC_BGEZC, OPC_BGEC */ + case OPC_BGTZC: /* OPC_BLTZC, OPC_BLTC */ + gen_load_gpr(t0, rs); + gen_load_gpr(t1, rt); + bcond_compute = 1; + ctx->btarget = addr_add(ctx, ctx->pc + 4, offset); + break; + case OPC_BLEZALC: /* OPC_BGEZALC, OPC_BGEUC */ + case OPC_BGTZALC: /* OPC_BLTZALC, OPC_BLTUC */ + if (rs == 0 || rs == rt) { + /* OPC_BLEZALC, OPC_BGEZALC */ + /* OPC_BGTZALC, OPC_BLTZALC */ + tcg_gen_movi_tl(cpu_gpr[31], ctx->pc + 4 + m16_lowbit); + } + gen_load_gpr(t0, rs); + gen_load_gpr(t1, rt); + bcond_compute = 1; + ctx->btarget = addr_add(ctx, ctx->pc + 4, offset); + break; + case OPC_BC: + case OPC_BALC: + ctx->btarget = addr_add(ctx, ctx->pc + 4, offset); + break; + case OPC_BEQZC: + case OPC_BNEZC: + if (rs != 0) { + /* OPC_BEQZC, OPC_BNEZC */ + gen_load_gpr(t0, rs); + bcond_compute = 1; + ctx->btarget = addr_add(ctx, ctx->pc + 4, offset); + } else { + /* OPC_JIC, OPC_JIALC */ + TCGv tbase = tcg_temp_new(); + TCGv toffset = tcg_temp_new(); + + gen_load_gpr(tbase, rt); + tcg_gen_movi_tl(toffset, offset); + gen_op_addr_add(ctx, btarget, tbase, toffset); + tcg_temp_free(tbase); + tcg_temp_free(toffset); + } + break; + default: + MIPS_INVAL("Compact branch/jump"); + generate_exception(ctx, EXCP_RI); + goto out; + } + + if (bcond_compute == 0) { + /* Uncoditional compact branch */ + switch (opc) { + case OPC_JIALC: + tcg_gen_movi_tl(cpu_gpr[31], ctx->pc + 4 + m16_lowbit); + /* Fallthrough */ + case OPC_JIC: + ctx->hflags |= MIPS_HFLAG_BR; + break; + case OPC_BALC: + tcg_gen_movi_tl(cpu_gpr[31], ctx->pc + 4 + m16_lowbit); + /* Fallthrough */ + case OPC_BC: + ctx->hflags |= MIPS_HFLAG_B; + break; + default: + MIPS_INVAL("Compact branch/jump"); + generate_exception(ctx, EXCP_RI); + goto out; + } + + /* Generating branch here as compact branches don't have delay slot */ + gen_branch(ctx, 4); + } else { + /* Conditional compact branch */ + TCGLabel *fs = gen_new_label(); + save_cpu_state(ctx, 0); + + switch (opc) { + case OPC_BLEZALC: /* OPC_BGEZALC, OPC_BGEUC */ + if (rs == 0 && rt != 0) { + /* OPC_BLEZALC */ + tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_LE), t1, 0, fs); + } else if (rs != 0 && rt != 0 && rs == rt) { + /* OPC_BGEZALC */ + tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_GE), t1, 0, fs); + } else { + /* OPC_BGEUC */ + tcg_gen_brcond_tl(tcg_invert_cond(TCG_COND_GEU), t0, t1, fs); + } + break; + case OPC_BGTZALC: /* OPC_BLTZALC, OPC_BLTUC */ + if (rs == 0 && rt != 0) { + /* OPC_BGTZALC */ + tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_GT), t1, 0, fs); + } else if (rs != 0 && rt != 0 && rs == rt) { + /* OPC_BLTZALC */ + tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_LT), t1, 0, fs); + } else { + /* OPC_BLTUC */ + tcg_gen_brcond_tl(tcg_invert_cond(TCG_COND_LTU), t0, t1, fs); + } + break; + case OPC_BLEZC: /* OPC_BGEZC, OPC_BGEC */ + if (rs == 0 && rt != 0) { + /* OPC_BLEZC */ + tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_LE), t1, 0, fs); + } else if (rs != 0 && rt != 0 && rs == rt) { + /* OPC_BGEZC */ + tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_GE), t1, 0, fs); + } else { + /* OPC_BGEC */ + tcg_gen_brcond_tl(tcg_invert_cond(TCG_COND_GE), t0, t1, fs); + } + break; + case OPC_BGTZC: /* OPC_BLTZC, OPC_BLTC */ + if (rs == 0 && rt != 0) { + /* OPC_BGTZC */ + tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_GT), t1, 0, fs); + } else if (rs != 0 && rt != 0 && rs == rt) { + /* OPC_BLTZC */ + tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_LT), t1, 0, fs); + } else { + /* OPC_BLTC */ + tcg_gen_brcond_tl(tcg_invert_cond(TCG_COND_LT), t0, t1, fs); + } + break; + case OPC_BOVC: /* OPC_BEQZALC, OPC_BEQC */ + case OPC_BNVC: /* OPC_BNEZALC, OPC_BNEC */ + if (rs >= rt) { + /* OPC_BOVC, OPC_BNVC */ + TCGv t2 = tcg_temp_new(); + TCGv t3 = tcg_temp_new(); + TCGv t4 = tcg_temp_new(); + TCGv input_overflow = tcg_temp_new(); + + gen_load_gpr(t0, rs); + gen_load_gpr(t1, rt); + tcg_gen_ext32s_tl(t2, t0); + tcg_gen_setcond_tl(TCG_COND_NE, input_overflow, t2, t0); + tcg_gen_ext32s_tl(t3, t1); + tcg_gen_setcond_tl(TCG_COND_NE, t4, t3, t1); + tcg_gen_or_tl(input_overflow, input_overflow, t4); + + tcg_gen_add_tl(t4, t2, t3); + tcg_gen_ext32s_tl(t4, t4); + tcg_gen_xor_tl(t2, t2, t3); + tcg_gen_xor_tl(t3, t4, t3); + tcg_gen_andc_tl(t2, t3, t2); + tcg_gen_setcondi_tl(TCG_COND_LT, t4, t2, 0); + tcg_gen_or_tl(t4, t4, input_overflow); + if (opc == OPC_BOVC) { + /* OPC_BOVC */ + tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_NE), t4, 0, fs); + } else { + /* OPC_BNVC */ + tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_EQ), t4, 0, fs); + } + tcg_temp_free(input_overflow); + tcg_temp_free(t4); + tcg_temp_free(t3); + tcg_temp_free(t2); + } else if (rs < rt && rs == 0) { + /* OPC_BEQZALC, OPC_BNEZALC */ + if (opc == OPC_BEQZALC) { + /* OPC_BEQZALC */ + tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_EQ), t1, 0, fs); + } else { + /* OPC_BNEZALC */ + tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_NE), t1, 0, fs); + } + } else { + /* OPC_BEQC, OPC_BNEC */ + if (opc == OPC_BEQC) { + /* OPC_BEQC */ + tcg_gen_brcond_tl(tcg_invert_cond(TCG_COND_EQ), t0, t1, fs); + } else { + /* OPC_BNEC */ + tcg_gen_brcond_tl(tcg_invert_cond(TCG_COND_NE), t0, t1, fs); + } + } + break; + case OPC_BEQZC: + tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_EQ), t0, 0, fs); + break; + case OPC_BNEZC: + tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_NE), t0, 0, fs); + break; + default: + MIPS_INVAL("Compact conditional branch/jump"); + generate_exception(ctx, EXCP_RI); + goto out; + } + + /* Generating branch here as compact branches don't have delay slot */ + gen_goto_tb(ctx, 1, ctx->btarget); + gen_set_label(fs); + + ctx->hflags |= MIPS_HFLAG_FBNSLOT; + MIPS_DEBUG("Compact conditional branch"); + } + +out: + tcg_temp_free(t0); + tcg_temp_free(t1); +} + /* ISA extensions (ASEs) */ /* MIPS16 extension to MIPS32 */ @@ -11549,6 +11891,15 @@ static int decode_extended_mips16_opc (CPUMIPSState *env, DisasContext *ctx) return 4; } +static inline bool is_uhi(int sdbbp_code) +{ +#ifdef CONFIG_USER_ONLY + return false; +#else + return semihosting_enabled() && sdbbp_code == 1; +#endif +} + static int decode_mips16_opc (CPUMIPSState *env, DisasContext *ctx) { int rx, ry; @@ -11848,13 +12199,13 @@ static int decode_mips16_opc (CPUMIPSState *env, DisasContext *ctx) } break; case RR_SDBBP: - /* XXX: not clear which exception should be raised - * when in debug mode... - */ - check_insn(ctx, ISA_MIPS32); - if (!(ctx->hflags & MIPS_HFLAG_DM)) { - generate_exception(ctx, EXCP_DBp); + if (is_uhi(extract32(ctx->opcode, 5, 6))) { + gen_helper_do_semihosting(cpu_env); } else { + /* XXX: not clear which exception should be raised + * when in debug mode... + */ + check_insn(ctx, ISA_MIPS32); generate_exception(ctx, EXCP_DBp); } break; @@ -12038,6 +12389,8 @@ enum { LBU16 = 0x02, MOVE16 = 0x03, ADDI32 = 0x04, + R6_LUI = 0x04, + AUI = 0x04, LBU32 = 0x05, SB32 = 0x06, LB32 = 0x07, @@ -12060,56 +12413,88 @@ enum { POOL32S = 0x16, /* MIPS64 */ DADDIU32 = 0x17, /* MIPS64 */ - /* 0x1f is reserved */ POOL32C = 0x18, LWGP16 = 0x19, LW16 = 0x1a, POOL16E = 0x1b, XORI32 = 0x1c, JALS32 = 0x1d, + BOVC = 0x1d, + BEQC = 0x1d, + BEQZALC = 0x1d, ADDIUPC = 0x1e, + PCREL = 0x1e, + BNVC = 0x1f, + BNEC = 0x1f, + BNEZALC = 0x1f, - /* 0x20 is reserved */ - RES_20 = 0x20, + R6_BEQZC = 0x20, + JIC = 0x20, POOL16F = 0x21, SB16 = 0x22, BEQZ16 = 0x23, + BEQZC16 = 0x23, SLTI32 = 0x24, BEQ32 = 0x25, + BC = 0x25, SWC132 = 0x26, LWC132 = 0x27, - /* 0x28 and 0x29 are reserved */ - RES_28 = 0x28, + /* 0x29 is reserved */ RES_29 = 0x29, + R6_BNEZC = 0x28, + JIALC = 0x28, SH16 = 0x2a, BNEZ16 = 0x2b, + BNEZC16 = 0x2b, SLTIU32 = 0x2c, BNE32 = 0x2d, + BALC = 0x2d, SDC132 = 0x2e, LDC132 = 0x2f, - /* 0x30 and 0x31 are reserved */ - RES_30 = 0x30, + /* 0x31 is reserved */ RES_31 = 0x31, + BLEZALC = 0x30, + BGEZALC = 0x30, + BGEUC = 0x30, SWSP16 = 0x32, B16 = 0x33, + BC16 = 0x33, ANDI32 = 0x34, J32 = 0x35, + BGTZC = 0x35, + BLTZC = 0x35, + BLTC = 0x35, SD32 = 0x36, /* MIPS64 */ LD32 = 0x37, /* MIPS64 */ - /* 0x38 and 0x39 are reserved */ - RES_38 = 0x38, + /* 0x39 is reserved */ RES_39 = 0x39, + BGTZALC = 0x38, + BLTZALC = 0x38, + BLTUC = 0x38, SW16 = 0x3a, LI16 = 0x3b, JALX32 = 0x3c, JAL32 = 0x3d, + BLEZC = 0x3d, + BGEZC = 0x3d, + BGEC = 0x3d, SW32 = 0x3e, LW32 = 0x3f }; +/* PCREL Instructions perform PC-Relative address calculation. bits 20..16 */ +enum { + ADDIUPC_00 = 0x00, + ADDIUPC_07 = 0x07, + AUIPC = 0x1e, + ALUIPC = 0x1f, + LWPC_08 = 0x08, + LWPC_0F = 0x0F, +}; + /* POOL32A encoding of minor opcode field */ enum { @@ -12119,6 +12504,8 @@ enum { SRL32 = 0x1, SRA = 0x2, ROTR = 0x3, + SELEQZ = 0x5, + SELNEZ = 0x6, SLLV = 0x0, SRLV = 0x1, @@ -12137,11 +12524,21 @@ enum { SLTU = 0xe, MOVN = 0x0, + R6_MUL = 0x0, MOVZ = 0x1, + MUH = 0x1, + MULU = 0x2, + MUHU = 0x3, LWXS = 0x4, + R6_DIV = 0x4, + MOD = 0x5, + R6_DIVU = 0x6, + MODU = 0x7, /* The following can be distinguished by their lower 6 bits. */ INS = 0x0c, + LSA = 0x0f, + ALIGN = 0x1f, EXT = 0x2c, POOL32AXF = 0x3c }; @@ -12194,6 +12591,7 @@ enum { /* end of microMIPS32 DSP */ /* bits 15..12 for 0x2c */ + BITSWAP = 0x0, SEB = 0x2, SEH = 0x3, CLO = 0x4, @@ -12220,7 +12618,10 @@ enum { /* bits 15..12 for 0x3c */ JALR = 0x0, JR = 0x0, /* alias */ + JALRC = 0x0, + JRC = 0x0, JALR_HB = 0x1, + JALRC_HB = 0x1, JALRS = 0x4, JALRS_HB = 0x5, @@ -12233,6 +12634,8 @@ enum { TLBR = 0x1, TLBWI = 0x2, TLBWR = 0x3, + TLBINV = 0x4, + TLBINVF = 0x5, WAIT = 0x9, IRET = 0xd, DERET = 0xe, @@ -12302,32 +12705,39 @@ enum { enum { /* These are the bit 7..6 values */ ADD_FMT = 0x0, - MOVN_FMT = 0x0, SUB_FMT = 0x1, - MOVZ_FMT = 0x1, MUL_FMT = 0x2, DIV_FMT = 0x3, /* These are the bit 8..6 values */ + MOVN_FMT = 0x0, RSQRT2_FMT = 0x0, MOVF_FMT = 0x0, + RINT_FMT = 0x0, + SELNEZ_FMT = 0x0, + MOVZ_FMT = 0x1, LWXC1 = 0x1, MOVT_FMT = 0x1, + CLASS_FMT = 0x1, + SELEQZ_FMT = 0x1, PLL_PS = 0x2, SWXC1 = 0x2, + SEL_FMT = 0x2, PLU_PS = 0x3, LDXC1 = 0x3, + MOVN_FMT_04 = 0x4, PUL_PS = 0x4, SDXC1 = 0x4, RECIP2_FMT = 0x4, + MOVZ_FMT_05 = 0x05, PUU_PS = 0x5, LUXC1 = 0x5, @@ -12335,8 +12745,10 @@ enum { SUXC1 = 0x6, ADDR_PS = 0x6, PREFX = 0x6, + MADDF_FMT = 0x6, MULR_PS = 0x7, + MSUBF_FMT = 0x7, MADD_S = 0x01, MADD_D = 0x09, @@ -12353,10 +12765,17 @@ enum { NMSUB_D = 0x2a, NMSUB_PS = 0x32, + MIN_FMT = 0x3, + MAX_FMT = 0xb, + MINA_FMT = 0x23, + MAXA_FMT = 0x2b, POOL32FXF = 0x3b, CABS_COND_FMT = 0x1c, /* MIPS3D */ - C_COND_FMT = 0x3c + C_COND_FMT = 0x3c, + + CMP_CONDN_S = 0x5, + CMP_CONDN_D = 0x15 }; /* POOL32Fxf encoding of minor opcode extension field */ @@ -12409,10 +12828,15 @@ enum { BGTZ = 0x06, BEQZC = 0x07, TLTI = 0x08, + BC1EQZC = 0x08, TGEI = 0x09, + BC1NEZC = 0x09, TLTIU = 0x0a, + BC2EQZC = 0x0a, TGEIU = 0x0b, + BC2NEZC = 0x0a, TNEI = 0x0c, + R6_SYNCI = 0x0c, LUI = 0x0d, TEQI = 0x0e, SYNCI = 0x10, @@ -12465,6 +12889,26 @@ enum { JRADDIUSP = 0x30 }; +/* R6 POOL16C encoding of minor opcode field (bits 0..5) */ + +enum { + R6_NOT16 = 0x00, + R6_AND16 = 0x01, + R6_LWM16 = 0x02, + R6_JRC16 = 0x03, + MOVEP = 0x04, + MOVEP_07 = 0x07, + R6_XOR16 = 0x08, + R6_OR16 = 0x09, + R6_SWM16 = 0x0a, + JALRC16 = 0x0b, + MOVEP_0C = 0x0c, + MOVEP_0F = 0x0f, + JRCADDIUSP = 0x13, + R6_BREAK16 = 0x1b, + R6_SDBBP16 = 0x3b +}; + /* POOL16D encoding of minor opcode field */ enum { @@ -12703,13 +13147,13 @@ static void gen_pool16c_insn(DisasContext *ctx) generate_exception(ctx, EXCP_BREAK); break; case SDBBP16: - /* XXX: not clear which exception should be raised - * when in debug mode... - */ - check_insn(ctx, ISA_MIPS32); - if (!(ctx->hflags & MIPS_HFLAG_DM)) { - generate_exception(ctx, EXCP_DBp); + if (is_uhi(extract32(ctx->opcode, 0, 4))) { + gen_helper_do_semihosting(cpu_env); } else { + /* XXX: not clear which exception should be raised + * when in debug mode... + */ + check_insn(ctx, ISA_MIPS32); generate_exception(ctx, EXCP_DBp); } break; @@ -12729,6 +13173,110 @@ static void gen_pool16c_insn(DisasContext *ctx) } } +static inline void gen_movep(DisasContext *ctx, int enc_dest, int enc_rt, + int enc_rs) +{ + int rd, rs, re, rt; + static const int rd_enc[] = { 5, 5, 6, 4, 4, 4, 4, 4 }; + static const int re_enc[] = { 6, 7, 7, 21, 22, 5, 6, 7 }; + static const int rs_rt_enc[] = { 0, 17, 2, 3, 16, 18, 19, 20 }; + rd = rd_enc[enc_dest]; + re = re_enc[enc_dest]; + rs = rs_rt_enc[enc_rs]; + rt = rs_rt_enc[enc_rt]; + if (rs) { + tcg_gen_mov_tl(cpu_gpr[rd], cpu_gpr[rs]); + } else { + tcg_gen_movi_tl(cpu_gpr[rd], 0); + } + if (rt) { + tcg_gen_mov_tl(cpu_gpr[re], cpu_gpr[rt]); + } else { + tcg_gen_movi_tl(cpu_gpr[re], 0); + } +} + +static void gen_pool16c_r6_insn(DisasContext *ctx) +{ + int rt = mmreg((ctx->opcode >> 7) & 0x7); + int rs = mmreg((ctx->opcode >> 4) & 0x7); + + switch (ctx->opcode & 0xf) { + case R6_NOT16: + gen_logic(ctx, OPC_NOR, rt, rs, 0); + break; + case R6_AND16: + gen_logic(ctx, OPC_AND, rt, rt, rs); + break; + case R6_LWM16: + { + int lwm_converted = 0x11 + extract32(ctx->opcode, 8, 2); + int offset = extract32(ctx->opcode, 4, 4); + gen_ldst_multiple(ctx, LWM32, lwm_converted, 29, offset << 2); + } + break; + case R6_JRC16: /* JRCADDIUSP */ + if ((ctx->opcode >> 4) & 1) { + /* JRCADDIUSP */ + int imm = extract32(ctx->opcode, 5, 5); + gen_compute_branch(ctx, OPC_JR, 2, 31, 0, 0, 0); + gen_arith_imm(ctx, OPC_ADDIU, 29, 29, imm << 2); + } else { + /* JRC16 */ + int rs = extract32(ctx->opcode, 5, 5); + gen_compute_branch(ctx, OPC_JR, 2, rs, 0, 0, 0); + } + break; + case MOVEP ... MOVEP_07: + case MOVEP_0C ... MOVEP_0F: + { + int enc_dest = uMIPS_RD(ctx->opcode); + int enc_rt = uMIPS_RS2(ctx->opcode); + int enc_rs = (ctx->opcode & 3) | ((ctx->opcode >> 1) & 4); + gen_movep(ctx, enc_dest, enc_rt, enc_rs); + } + break; + case R6_XOR16: + gen_logic(ctx, OPC_XOR, rt, rt, rs); + break; + case R6_OR16: + gen_logic(ctx, OPC_OR, rt, rt, rs); + break; + case R6_SWM16: + { + int swm_converted = 0x11 + extract32(ctx->opcode, 8, 2); + int offset = extract32(ctx->opcode, 4, 4); + gen_ldst_multiple(ctx, SWM32, swm_converted, 29, offset << 2); + } + break; + case JALRC16: /* BREAK16, SDBBP16 */ + switch (ctx->opcode & 0x3f) { + case JALRC16: + case JALRC16 + 0x20: + /* JALRC16 */ + gen_compute_branch(ctx, OPC_JALR, 2, (ctx->opcode >> 5) & 0x1f, + 31, 0, 0); + break; + case R6_BREAK16: + /* BREAK16 */ + generate_exception(ctx, EXCP_BREAK); + break; + case R6_SDBBP16: + /* SDBBP16 */ + if (ctx->hflags & MIPS_HFLAG_SBRI) { + generate_exception(ctx, EXCP_RI); + } else { + generate_exception(ctx, EXCP_DBp); + } + break; + } + break; + default: + generate_exception(ctx, EXCP_RI); + break; + } +} + static void gen_ldxs (DisasContext *ctx, int base, int index, int rd) { TCGv t0 = tcg_temp_new(); @@ -12900,6 +13448,10 @@ static void gen_pool32axf (CPUMIPSState *env, DisasContext *ctx, int rt, int rs) break; case 0x2c: switch (minor) { + case BITSWAP: + check_insn(ctx, ISA_MIPS32R6); + gen_bitswap(ctx, OPC_BITSWAP, rs, rt); + break; case SEB: gen_bshfl(ctx, OPC_SEB, rs, rt); break; @@ -12922,15 +13474,19 @@ static void gen_pool32axf (CPUMIPSState *env, DisasContext *ctx, int rt, int rs) gen_bshfl(ctx, OPC_WSBH, rs, rt); break; case MULT: + check_insn_opc_removed(ctx, ISA_MIPS32R6); mips32_op = OPC_MULT; goto do_mul; case MULTU: + check_insn_opc_removed(ctx, ISA_MIPS32R6); mips32_op = OPC_MULTU; goto do_mul; case DIV: + check_insn_opc_removed(ctx, ISA_MIPS32R6); mips32_op = OPC_DIV; goto do_div; case DIVU: + check_insn_opc_removed(ctx, ISA_MIPS32R6); mips32_op = OPC_DIVU; goto do_div; do_div: @@ -12938,15 +13494,19 @@ static void gen_pool32axf (CPUMIPSState *env, DisasContext *ctx, int rt, int rs) gen_muldiv(ctx, mips32_op, 0, rs, rt); break; case MADD: + check_insn_opc_removed(ctx, ISA_MIPS32R6); mips32_op = OPC_MADD; goto do_mul; case MADDU: + check_insn_opc_removed(ctx, ISA_MIPS32R6); mips32_op = OPC_MADDU; goto do_mul; case MSUB: + check_insn_opc_removed(ctx, ISA_MIPS32R6); mips32_op = OPC_MSUB; goto do_mul; case MSUBU: + check_insn_opc_removed(ctx, ISA_MIPS32R6); mips32_op = OPC_MSUBU; do_mul: check_insn(ctx, ISA_MIPS32); @@ -12972,13 +13532,20 @@ static void gen_pool32axf (CPUMIPSState *env, DisasContext *ctx, int rt, int rs) break; case 0x3c: switch (minor) { - case JALR: - case JALR_HB: - gen_compute_branch(ctx, OPC_JALR, 4, rs, rt, 0, 4); - ctx->hflags |= MIPS_HFLAG_BDS_STRICT; + case JALR: /* JALRC */ + case JALR_HB: /* JALRC_HB */ + if (ctx->insn_flags & ISA_MIPS32R6) { + /* JALRC, JALRC_HB */ + gen_compute_branch(ctx, OPC_JALR, 4, rs, rt, 0, 0); + } else { + /* JALR, JALR_HB */ + gen_compute_branch(ctx, OPC_JALR, 4, rs, rt, 0, 4); + ctx->hflags |= MIPS_HFLAG_BDS_STRICT; + } break; case JALRS: case JALRS_HB: + check_insn_opc_removed(ctx, ISA_MIPS32R6); gen_compute_branch(ctx, OPC_JALR, 4, rs, rt, 0, 2); ctx->hflags |= MIPS_HFLAG_BDS_STRICT; break; @@ -12991,12 +13558,12 @@ static void gen_pool32axf (CPUMIPSState *env, DisasContext *ctx, int rt, int rs) case RDPGPR: check_cp0_enabled(ctx); check_insn(ctx, ISA_MIPS32R2); - gen_load_srsgpr(rt, rs); + gen_load_srsgpr(rs, rt); break; case WRPGPR: check_cp0_enabled(ctx); check_insn(ctx, ISA_MIPS32R2); - gen_store_srsgpr(rt, rs); + gen_store_srsgpr(rs, rt); break; default: goto pool32axf_invalid; @@ -13017,6 +13584,12 @@ static void gen_pool32axf (CPUMIPSState *env, DisasContext *ctx, int rt, int rs) case TLBWR: mips32_op = OPC_TLBWR; goto do_cp0; + case TLBINV: + mips32_op = OPC_TLBINV; + goto do_cp0; + case TLBINVF: + mips32_op = OPC_TLBINVF; + goto do_cp0; case WAIT: mips32_op = OPC_WAIT; goto do_cp0; @@ -13075,11 +13648,15 @@ static void gen_pool32axf (CPUMIPSState *env, DisasContext *ctx, int rt, int rs) ctx->bstate = BS_STOP; break; case SDBBP: - check_insn(ctx, ISA_MIPS32); - if (!(ctx->hflags & MIPS_HFLAG_DM)) { - generate_exception(ctx, EXCP_DBp); + if (is_uhi(extract32(ctx->opcode, 16, 10))) { + gen_helper_do_semihosting(cpu_env); } else { - generate_exception(ctx, EXCP_DBp); + check_insn(ctx, ISA_MIPS32); + if (ctx->hflags & MIPS_HFLAG_SBRI) { + generate_exception(ctx, EXCP_RI); + } else { + generate_exception(ctx, EXCP_DBp); + } } break; default: @@ -13105,6 +13682,7 @@ static void gen_pool32axf (CPUMIPSState *env, DisasContext *ctx, int rt, int rs) } break; case 0x35: + check_insn_opc_removed(ctx, ISA_MIPS32R6); switch (minor) { case MFHI32: gen_HILO(ctx, OPC_MFHI, 0, rs); @@ -13377,6 +13955,7 @@ static void gen_pool32fxf(DisasContext *ctx, int rt, int rs) case COND_FLOAT_MOV(MOVT, 5): case COND_FLOAT_MOV(MOVT, 6): case COND_FLOAT_MOV(MOVT, 7): + check_insn_opc_removed(ctx, ISA_MIPS32R6); gen_movci(ctx, rt, rs, (ctx->opcode >> 13) & 0x7, 1); break; case COND_FLOAT_MOV(MOVF, 0): @@ -13387,6 +13966,7 @@ static void gen_pool32fxf(DisasContext *ctx, int rt, int rs) case COND_FLOAT_MOV(MOVF, 5): case COND_FLOAT_MOV(MOVF, 6): case COND_FLOAT_MOV(MOVF, 7): + check_insn_opc_removed(ctx, ISA_MIPS32R6); gen_movci(ctx, rt, rs, (ctx->opcode >> 13) & 0x7, 0); break; default: @@ -13396,8 +13976,7 @@ static void gen_pool32fxf(DisasContext *ctx, int rt, int rs) } } -static void decode_micromips32_opc (CPUMIPSState *env, DisasContext *ctx, - uint16_t insn_hw1) +static void decode_micromips32_opc(CPUMIPSState *env, DisasContext *ctx) { int32_t offset; uint16_t insn; @@ -13437,6 +14016,14 @@ static void decode_micromips32_opc (CPUMIPSState *env, DisasContext *ctx, do_shifti: gen_shift_imm(ctx, mips32_op, rt, rs, rd); break; + case SELEQZ: + check_insn(ctx, ISA_MIPS32R6); + gen_cond_move(ctx, OPC_SELEQZ, rd, rs, rt); + break; + case SELNEZ: + check_insn(ctx, ISA_MIPS32R6); + gen_cond_move(ctx, OPC_SELNEZ, rd, rs, rt); + break; default: goto pool32a_invalid; } @@ -13458,6 +14045,7 @@ static void decode_micromips32_opc (CPUMIPSState *env, DisasContext *ctx, mips32_op = OPC_SUBU; goto do_arith; case MUL: + check_insn_opc_removed(ctx, ISA_MIPS32R6); mips32_op = OPC_MUL; do_arith: gen_arith(ctx, mips32_op, rd, rs, rt); @@ -13509,16 +14097,52 @@ static void decode_micromips32_opc (CPUMIPSState *env, DisasContext *ctx, minor = (ctx->opcode >> 6) & 0xf; switch (minor) { /* Conditional moves */ - case MOVN: - mips32_op = OPC_MOVN; - goto do_cmov; - case MOVZ: - mips32_op = OPC_MOVZ; - do_cmov: - gen_cond_move(ctx, mips32_op, rd, rs, rt); + case MOVN: /* MUL */ + if (ctx->insn_flags & ISA_MIPS32R6) { + /* MUL */ + gen_r6_muldiv(ctx, R6_OPC_MUL, rd, rs, rt); + } else { + /* MOVN */ + gen_cond_move(ctx, OPC_MOVN, rd, rs, rt); + } break; - case LWXS: - gen_ldxs(ctx, rs, rt, rd); + case MOVZ: /* MUH */ + if (ctx->insn_flags & ISA_MIPS32R6) { + /* MUH */ + gen_r6_muldiv(ctx, R6_OPC_MUH, rd, rs, rt); + } else { + /* MOVZ */ + gen_cond_move(ctx, OPC_MOVZ, rd, rs, rt); + } + break; + case MULU: + check_insn(ctx, ISA_MIPS32R6); + gen_r6_muldiv(ctx, R6_OPC_MULU, rd, rs, rt); + break; + case MUHU: + check_insn(ctx, ISA_MIPS32R6); + gen_r6_muldiv(ctx, R6_OPC_MUHU, rd, rs, rt); + break; + case LWXS: /* DIV */ + if (ctx->insn_flags & ISA_MIPS32R6) { + /* DIV */ + gen_r6_muldiv(ctx, R6_OPC_DIV, rd, rs, rt); + } else { + /* LWXS */ + gen_ldxs(ctx, rs, rt, rd); + } + break; + case MOD: + check_insn(ctx, ISA_MIPS32R6); + gen_r6_muldiv(ctx, R6_OPC_MOD, rd, rs, rt); + break; + case R6_DIVU: + check_insn(ctx, ISA_MIPS32R6); + gen_r6_muldiv(ctx, R6_OPC_DIVU, rd, rs, rt); + break; + case MODU: + check_insn(ctx, ISA_MIPS32R6); + gen_r6_muldiv(ctx, R6_OPC_MODU, rd, rs, rt); break; default: goto pool32a_invalid; @@ -13527,6 +14151,16 @@ static void decode_micromips32_opc (CPUMIPSState *env, DisasContext *ctx, case INS: gen_bitops(ctx, OPC_INS, rt, rs, rr, rd); return; + case LSA: + check_insn(ctx, ISA_MIPS32R6); + gen_lsa(ctx, OPC_LSA, rd, rs, rt, + extract32(ctx->opcode, 9, 2)); + break; + case ALIGN: + check_insn(ctx, ISA_MIPS32R6); + gen_align(ctx, OPC_ALIGN, rd, rs, rt, + extract32(ctx->opcode, 9, 2)); + break; case EXT: gen_bitops(ctx, OPC_EXT, rt, rs, rr, rd); return; @@ -13589,47 +14223,61 @@ static void decode_micromips32_opc (CPUMIPSState *env, DisasContext *ctx, check_cp1_enabled(ctx); switch (minor) { case ALNV_PS: + check_insn_opc_removed(ctx, ISA_MIPS32R6); mips32_op = OPC_ALNV_PS; goto do_madd; case MADD_S: + check_insn_opc_removed(ctx, ISA_MIPS32R6); mips32_op = OPC_MADD_S; goto do_madd; case MADD_D: + check_insn_opc_removed(ctx, ISA_MIPS32R6); mips32_op = OPC_MADD_D; goto do_madd; case MADD_PS: + check_insn_opc_removed(ctx, ISA_MIPS32R6); mips32_op = OPC_MADD_PS; goto do_madd; case MSUB_S: + check_insn_opc_removed(ctx, ISA_MIPS32R6); mips32_op = OPC_MSUB_S; goto do_madd; case MSUB_D: + check_insn_opc_removed(ctx, ISA_MIPS32R6); mips32_op = OPC_MSUB_D; goto do_madd; case MSUB_PS: + check_insn_opc_removed(ctx, ISA_MIPS32R6); mips32_op = OPC_MSUB_PS; goto do_madd; case NMADD_S: + check_insn_opc_removed(ctx, ISA_MIPS32R6); mips32_op = OPC_NMADD_S; goto do_madd; case NMADD_D: + check_insn_opc_removed(ctx, ISA_MIPS32R6); mips32_op = OPC_NMADD_D; goto do_madd; case NMADD_PS: + check_insn_opc_removed(ctx, ISA_MIPS32R6); mips32_op = OPC_NMADD_PS; goto do_madd; case NMSUB_S: + check_insn_opc_removed(ctx, ISA_MIPS32R6); mips32_op = OPC_NMSUB_S; goto do_madd; case NMSUB_D: + check_insn_opc_removed(ctx, ISA_MIPS32R6); mips32_op = OPC_NMSUB_D; goto do_madd; case NMSUB_PS: + check_insn_opc_removed(ctx, ISA_MIPS32R6); mips32_op = OPC_NMSUB_PS; do_madd: gen_flt3_arith(ctx, mips32_op, rd, rr, rs, rt); break; case CABS_COND_FMT: + check_insn_opc_removed(ctx, ISA_MIPS32R6); cond = (ctx->opcode >> 6) & 0xf; cc = (ctx->opcode >> 13) & 0x7; fmt = (ctx->opcode >> 10) & 0x3; @@ -13648,6 +14296,7 @@ static void decode_micromips32_opc (CPUMIPSState *env, DisasContext *ctx, } break; case C_COND_FMT: + check_insn_opc_removed(ctx, ISA_MIPS32R6); cond = (ctx->opcode >> 6) & 0xf; cc = (ctx->opcode >> 13) & 0x7; fmt = (ctx->opcode >> 10) & 0x3; @@ -13665,6 +14314,14 @@ static void decode_micromips32_opc (CPUMIPSState *env, DisasContext *ctx, goto pool32f_invalid; } break; + case CMP_CONDN_S: + check_insn(ctx, ISA_MIPS32R6); + gen_r6_cmp_s(ctx, (ctx->opcode >> 6) & 0x1f, rt, rs, rd); + break; + case CMP_CONDN_D: + check_insn(ctx, ISA_MIPS32R6); + gen_r6_cmp_d(ctx, (ctx->opcode >> 6) & 0x1f, rt, rs, rd); + break; case POOL32FXF: gen_pool32fxf(ctx, rt, rs); break; @@ -13684,6 +14341,7 @@ static void decode_micromips32_opc (CPUMIPSState *env, DisasContext *ctx, mips32_op = OPC_PUU_PS; goto do_ps; case CVT_PS_S: + check_insn_opc_removed(ctx, ISA_MIPS32R6); mips32_op = OPC_CVT_PS_S; do_ps: gen_farith(ctx, mips32_op, rt, rs, rd, 0); @@ -13692,25 +14350,44 @@ static void decode_micromips32_opc (CPUMIPSState *env, DisasContext *ctx, goto pool32f_invalid; } break; + case MIN_FMT: + check_insn(ctx, ISA_MIPS32R6); + switch ((ctx->opcode >> 9) & 0x3) { + case FMT_SDPS_S: + gen_farith(ctx, OPC_MIN_S, rt, rs, rd, 0); + break; + case FMT_SDPS_D: + gen_farith(ctx, OPC_MIN_D, rt, rs, rd, 0); + break; + default: + goto pool32f_invalid; + } + break; case 0x08: /* [LS][WDU]XC1 */ switch ((ctx->opcode >> 6) & 0x7) { case LWXC1: + check_insn_opc_removed(ctx, ISA_MIPS32R6); mips32_op = OPC_LWXC1; goto do_ldst_cp1; case SWXC1: + check_insn_opc_removed(ctx, ISA_MIPS32R6); mips32_op = OPC_SWXC1; goto do_ldst_cp1; case LDXC1: + check_insn_opc_removed(ctx, ISA_MIPS32R6); mips32_op = OPC_LDXC1; goto do_ldst_cp1; case SDXC1: + check_insn_opc_removed(ctx, ISA_MIPS32R6); mips32_op = OPC_SDXC1; goto do_ldst_cp1; case LUXC1: + check_insn_opc_removed(ctx, ISA_MIPS32R6); mips32_op = OPC_LUXC1; goto do_ldst_cp1; case SUXC1: + check_insn_opc_removed(ctx, ISA_MIPS32R6); mips32_op = OPC_SUXC1; do_ldst_cp1: gen_flt3_ldst(ctx, mips32_op, rd, rd, rt, rs); @@ -13719,8 +14396,22 @@ static void decode_micromips32_opc (CPUMIPSState *env, DisasContext *ctx, goto pool32f_invalid; } break; + case MAX_FMT: + check_insn(ctx, ISA_MIPS32R6); + switch ((ctx->opcode >> 9) & 0x3) { + case FMT_SDPS_S: + gen_farith(ctx, OPC_MAX_S, rt, rs, rd, 0); + break; + case FMT_SDPS_D: + gen_farith(ctx, OPC_MAX_D, rt, rs, rd, 0); + break; + default: + goto pool32f_invalid; + } + break; case 0x18: /* 3D insns */ + check_insn_opc_removed(ctx, ISA_MIPS32R6); fmt = (ctx->opcode >> 9) & 0x3; switch ((ctx->opcode >> 6) & 0x7) { case RSQRT2_FMT: @@ -13766,41 +14457,74 @@ static void decode_micromips32_opc (CPUMIPSState *env, DisasContext *ctx, } break; case 0x20: - /* MOV[FT].fmt and PREFX */ + /* MOV[FT].fmt, PREFX, RINT.fmt, CLASS.fmt*/ cc = (ctx->opcode >> 13) & 0x7; fmt = (ctx->opcode >> 9) & 0x3; switch ((ctx->opcode >> 6) & 0x7) { - case MOVF_FMT: - switch (fmt) { - case FMT_SDPS_S: - gen_movcf_s(ctx, rs, rt, cc, 0); - break; - case FMT_SDPS_D: - gen_movcf_d(ctx, rs, rt, cc, 0); - break; - case FMT_SDPS_PS: - gen_movcf_ps(ctx, rs, rt, cc, 0); - break; - default: - goto pool32f_invalid; + case MOVF_FMT: /* RINT_FMT */ + if (ctx->insn_flags & ISA_MIPS32R6) { + /* RINT_FMT */ + switch (fmt) { + case FMT_SDPS_S: + gen_farith(ctx, OPC_RINT_S, 0, rt, rs, 0); + break; + case FMT_SDPS_D: + gen_farith(ctx, OPC_RINT_D, 0, rt, rs, 0); + break; + default: + goto pool32f_invalid; + } + } else { + /* MOVF_FMT */ + switch (fmt) { + case FMT_SDPS_S: + gen_movcf_s(ctx, rs, rt, cc, 0); + break; + case FMT_SDPS_D: + gen_movcf_d(ctx, rs, rt, cc, 0); + break; + case FMT_SDPS_PS: + check_ps(ctx); + gen_movcf_ps(ctx, rs, rt, cc, 0); + break; + default: + goto pool32f_invalid; + } } break; - case MOVT_FMT: - switch (fmt) { - case FMT_SDPS_S: - gen_movcf_s(ctx, rs, rt, cc, 1); - break; - case FMT_SDPS_D: - gen_movcf_d(ctx, rs, rt, cc, 1); - break; - case FMT_SDPS_PS: - gen_movcf_ps(ctx, rs, rt, cc, 1); - break; - default: - goto pool32f_invalid; + case MOVT_FMT: /* CLASS_FMT */ + if (ctx->insn_flags & ISA_MIPS32R6) { + /* CLASS_FMT */ + switch (fmt) { + case FMT_SDPS_S: + gen_farith(ctx, OPC_CLASS_S, 0, rt, rs, 0); + break; + case FMT_SDPS_D: + gen_farith(ctx, OPC_CLASS_D, 0, rt, rs, 0); + break; + default: + goto pool32f_invalid; + } + } else { + /* MOVT_FMT */ + switch (fmt) { + case FMT_SDPS_S: + gen_movcf_s(ctx, rs, rt, cc, 1); + break; + case FMT_SDPS_D: + gen_movcf_d(ctx, rs, rt, cc, 1); + break; + case FMT_SDPS_PS: + check_ps(ctx); + gen_movcf_ps(ctx, rs, rt, cc, 1); + break; + default: + goto pool32f_invalid; + } } break; case PREFX: + check_insn_opc_removed(ctx, ISA_MIPS32R6); break; default: goto pool32f_invalid; @@ -13815,11 +14539,38 @@ static void decode_micromips32_opc (CPUMIPSState *env, DisasContext *ctx, mips32_op = OPC_##prfx##_D; \ goto do_fpop; \ case FMT_SDPS_PS: \ + check_ps(ctx); \ mips32_op = OPC_##prfx##_PS; \ goto do_fpop; \ default: \ goto pool32f_invalid; \ } + case MINA_FMT: + check_insn(ctx, ISA_MIPS32R6); + switch ((ctx->opcode >> 9) & 0x3) { + case FMT_SDPS_S: + gen_farith(ctx, OPC_MINA_S, rt, rs, rd, 0); + break; + case FMT_SDPS_D: + gen_farith(ctx, OPC_MINA_D, rt, rs, rd, 0); + break; + default: + goto pool32f_invalid; + } + break; + case MAXA_FMT: + check_insn(ctx, ISA_MIPS32R6); + switch ((ctx->opcode >> 9) & 0x3) { + case FMT_SDPS_S: + gen_farith(ctx, OPC_MAXA_S, rt, rs, rd, 0); + break; + case FMT_SDPS_D: + gen_farith(ctx, OPC_MAXA_D, rt, rs, rd, 0); + break; + default: + goto pool32f_invalid; + } + break; case 0x30: /* regular FP ops */ switch ((ctx->opcode >> 6) & 0x3) { @@ -13848,13 +14599,90 @@ static void decode_micromips32_opc (CPUMIPSState *env, DisasContext *ctx, break; case 0x38: /* cmovs */ - switch ((ctx->opcode >> 6) & 0x3) { - case MOVN_FMT: + switch ((ctx->opcode >> 6) & 0x7) { + case MOVN_FMT: /* SELNEZ_FMT */ + if (ctx->insn_flags & ISA_MIPS32R6) { + /* SELNEZ_FMT */ + switch ((ctx->opcode >> 9) & 0x3) { + case FMT_SDPS_S: + gen_sel_s(ctx, OPC_SELNEZ_S, rd, rt, rs); + break; + case FMT_SDPS_D: + gen_sel_d(ctx, OPC_SELNEZ_D, rd, rt, rs); + break; + default: + goto pool32f_invalid; + } + } else { + /* MOVN_FMT */ + FINSN_3ARG_SDPS(MOVN); + } + break; + case MOVN_FMT_04: + check_insn_opc_removed(ctx, ISA_MIPS32R6); FINSN_3ARG_SDPS(MOVN); break; - case MOVZ_FMT: + case MOVZ_FMT: /* SELEQZ_FMT */ + if (ctx->insn_flags & ISA_MIPS32R6) { + /* SELEQZ_FMT */ + switch ((ctx->opcode >> 9) & 0x3) { + case FMT_SDPS_S: + gen_sel_s(ctx, OPC_SELEQZ_S, rd, rt, rs); + break; + case FMT_SDPS_D: + gen_sel_d(ctx, OPC_SELEQZ_D, rd, rt, rs); + break; + default: + goto pool32f_invalid; + } + } else { + /* MOVZ_FMT */ + FINSN_3ARG_SDPS(MOVZ); + } + break; + case MOVZ_FMT_05: + check_insn_opc_removed(ctx, ISA_MIPS32R6); FINSN_3ARG_SDPS(MOVZ); break; + case SEL_FMT: + check_insn(ctx, ISA_MIPS32R6); + switch ((ctx->opcode >> 9) & 0x3) { + case FMT_SDPS_S: + gen_sel_s(ctx, OPC_SEL_S, rd, rt, rs); + break; + case FMT_SDPS_D: + gen_sel_d(ctx, OPC_SEL_D, rd, rt, rs); + break; + default: + goto pool32f_invalid; + } + break; + case MADDF_FMT: + check_insn(ctx, ISA_MIPS32R6); + switch ((ctx->opcode >> 9) & 0x3) { + case FMT_SDPS_S: + mips32_op = OPC_MADDF_S; + goto do_fpop; + case FMT_SDPS_D: + mips32_op = OPC_MADDF_D; + goto do_fpop; + default: + goto pool32f_invalid; + } + break; + case MSUBF_FMT: + check_insn(ctx, ISA_MIPS32R6); + switch ((ctx->opcode >> 9) & 0x3) { + case FMT_SDPS_S: + mips32_op = OPC_MSUBF_S; + goto do_fpop; + case FMT_SDPS_D: + mips32_op = OPC_MSUBF_D; + goto do_fpop; + default: + goto pool32f_invalid; + } + break; default: goto pool32f_invalid; } @@ -13876,51 +14704,87 @@ static void decode_micromips32_opc (CPUMIPSState *env, DisasContext *ctx, minor = (ctx->opcode >> 21) & 0x1f; switch (minor) { case BLTZ: + check_insn_opc_removed(ctx, ISA_MIPS32R6); gen_compute_branch(ctx, OPC_BLTZ, 4, rs, -1, imm << 1, 4); break; case BLTZAL: + check_insn_opc_removed(ctx, ISA_MIPS32R6); gen_compute_branch(ctx, OPC_BLTZAL, 4, rs, -1, imm << 1, 4); ctx->hflags |= MIPS_HFLAG_BDS_STRICT; break; case BLTZALS: + check_insn_opc_removed(ctx, ISA_MIPS32R6); gen_compute_branch(ctx, OPC_BLTZAL, 4, rs, -1, imm << 1, 2); ctx->hflags |= MIPS_HFLAG_BDS_STRICT; break; case BGEZ: + check_insn_opc_removed(ctx, ISA_MIPS32R6); gen_compute_branch(ctx, OPC_BGEZ, 4, rs, -1, imm << 1, 4); break; case BGEZAL: + check_insn_opc_removed(ctx, ISA_MIPS32R6); gen_compute_branch(ctx, OPC_BGEZAL, 4, rs, -1, imm << 1, 4); ctx->hflags |= MIPS_HFLAG_BDS_STRICT; break; case BGEZALS: + check_insn_opc_removed(ctx, ISA_MIPS32R6); gen_compute_branch(ctx, OPC_BGEZAL, 4, rs, -1, imm << 1, 2); ctx->hflags |= MIPS_HFLAG_BDS_STRICT; break; case BLEZ: + check_insn_opc_removed(ctx, ISA_MIPS32R6); gen_compute_branch(ctx, OPC_BLEZ, 4, rs, -1, imm << 1, 4); break; case BGTZ: + check_insn_opc_removed(ctx, ISA_MIPS32R6); gen_compute_branch(ctx, OPC_BGTZ, 4, rs, -1, imm << 1, 4); break; /* Traps */ - case TLTI: - mips32_op = OPC_TLTI; - goto do_trapi; - case TGEI: - mips32_op = OPC_TGEI; - goto do_trapi; + case TLTI: /* BC1EQZC */ + if (ctx->insn_flags & ISA_MIPS32R6) { + /* BC1EQZC */ + check_cp1_enabled(ctx); + gen_compute_branch1_r6(ctx, OPC_BC1EQZ, rs, imm << 1, 0); + } else { + /* TLTI */ + mips32_op = OPC_TLTI; + goto do_trapi; + } + break; + case TGEI: /* BC1NEZC */ + if (ctx->insn_flags & ISA_MIPS32R6) { + /* BC1NEZC */ + check_cp1_enabled(ctx); + gen_compute_branch1_r6(ctx, OPC_BC1NEZ, rs, imm << 1, 0); + } else { + /* TGEI */ + mips32_op = OPC_TGEI; + goto do_trapi; + } + break; case TLTIU: + check_insn_opc_removed(ctx, ISA_MIPS32R6); mips32_op = OPC_TLTIU; goto do_trapi; case TGEIU: + check_insn_opc_removed(ctx, ISA_MIPS32R6); mips32_op = OPC_TGEIU; goto do_trapi; - case TNEI: - mips32_op = OPC_TNEI; - goto do_trapi; + case TNEI: /* SYNCI */ + if (ctx->insn_flags & ISA_MIPS32R6) { + /* SYNCI */ + /* Break the TB to be able to sync copied instructions + immediately */ + ctx->bstate = BS_STOP; + } else { + /* TNEI */ + mips32_op = OPC_TNEI; + goto do_trapi; + } + break; case TEQI: + check_insn_opc_removed(ctx, ISA_MIPS32R6); mips32_op = OPC_TEQI; do_trapi: gen_trap(ctx, mips32_op, rs, -1, imm); @@ -13928,6 +14792,7 @@ static void decode_micromips32_opc (CPUMIPSState *env, DisasContext *ctx, case BNEZC: case BEQZC: + check_insn_opc_removed(ctx, ISA_MIPS32R6); gen_compute_branch(ctx, minor == BNEZC ? OPC_BNE : OPC_BEQ, 4, rs, 0, imm << 1, 0); /* Compact branches don't have a delay slot, so just let @@ -13935,28 +14800,35 @@ static void decode_micromips32_opc (CPUMIPSState *env, DisasContext *ctx, target. */ break; case LUI: + check_insn_opc_removed(ctx, ISA_MIPS32R6); gen_logic_imm(ctx, OPC_LUI, rs, 0, imm); break; case SYNCI: + check_insn_opc_removed(ctx, ISA_MIPS32R6); /* Break the TB to be able to sync copied instructions immediately */ ctx->bstate = BS_STOP; break; case BC2F: case BC2T: + check_insn_opc_removed(ctx, ISA_MIPS32R6); /* COP2: Not implemented. */ generate_exception_err(ctx, EXCP_CpU, 2); break; case BC1F: + check_insn_opc_removed(ctx, ISA_MIPS32R6); mips32_op = (ctx->opcode & (1 << 16)) ? OPC_BC1FANY2 : OPC_BC1F; goto do_cp1branch; case BC1T: + check_insn_opc_removed(ctx, ISA_MIPS32R6); mips32_op = (ctx->opcode & (1 << 16)) ? OPC_BC1TANY2 : OPC_BC1T; goto do_cp1branch; case BC1ANY4F: + check_insn_opc_removed(ctx, ISA_MIPS32R6); mips32_op = OPC_BC1FANY4; goto do_cp1mips3d; case BC1ANY4T: + check_insn_opc_removed(ctx, ISA_MIPS32R6); mips32_op = OPC_BC1TANY4; do_cp1mips3d: check_cop1x(ctx); @@ -13983,38 +14855,48 @@ static void decode_micromips32_opc (CPUMIPSState *env, DisasContext *ctx, break; case POOL32C: minor = (ctx->opcode >> 12) & 0xf; + offset = sextract32(ctx->opcode, 0, + (ctx->insn_flags & ISA_MIPS32R6) ? 9 : 12); switch (minor) { case LWL: + check_insn_opc_removed(ctx, ISA_MIPS32R6); mips32_op = OPC_LWL; goto do_ld_lr; case SWL: + check_insn_opc_removed(ctx, ISA_MIPS32R6); mips32_op = OPC_SWL; goto do_st_lr; case LWR: + check_insn_opc_removed(ctx, ISA_MIPS32R6); mips32_op = OPC_LWR; goto do_ld_lr; case SWR: + check_insn_opc_removed(ctx, ISA_MIPS32R6); mips32_op = OPC_SWR; goto do_st_lr; #if defined(TARGET_MIPS64) case LDL: check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); + check_insn_opc_removed(ctx, ISA_MIPS32R6); mips32_op = OPC_LDL; goto do_ld_lr; case SDL: check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); + check_insn_opc_removed(ctx, ISA_MIPS32R6); mips32_op = OPC_SDL; goto do_st_lr; case LDR: check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); + check_insn_opc_removed(ctx, ISA_MIPS32R6); mips32_op = OPC_LDR; goto do_ld_lr; case SDR: check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); + check_insn_opc_removed(ctx, ISA_MIPS32R6); mips32_op = OPC_SDR; goto do_st_lr; case LWU: @@ -14032,23 +14914,27 @@ static void decode_micromips32_opc (CPUMIPSState *env, DisasContext *ctx, mips32_op = OPC_LL; goto do_ld_lr; do_ld_lr: - gen_ld(ctx, mips32_op, rt, rs, SIMM(ctx->opcode, 0, 12)); + gen_ld(ctx, mips32_op, rt, rs, offset); break; do_st_lr: gen_st(ctx, mips32_op, rt, rs, SIMM(ctx->opcode, 0, 12)); break; case SC: - gen_st_cond(ctx, OPC_SC, rt, rs, SIMM(ctx->opcode, 0, 12)); + gen_st_cond(ctx, OPC_SC, rt, rs, offset); break; #if defined(TARGET_MIPS64) case SCD: check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); - gen_st_cond(ctx, OPC_SCD, rt, rs, SIMM(ctx->opcode, 0, 12)); + gen_st_cond(ctx, OPC_SCD, rt, rs, offset); break; #endif case PREF: /* Treat as no-op */ + if ((ctx->insn_flags & ISA_MIPS32R6) && (rt >= 24)) { + /* hint codes 24-31 are reserved and signal RI */ + generate_exception(ctx, EXCP_RI); + } break; default: MIPS_INVAL("pool32c"); @@ -14056,9 +14942,16 @@ static void decode_micromips32_opc (CPUMIPSState *env, DisasContext *ctx, break; } break; - case ADDI32: - mips32_op = OPC_ADDI; - goto do_addi; + case ADDI32: /* AUI, LUI */ + if (ctx->insn_flags & ISA_MIPS32R6) { + /* AUI, LUI */ + gen_logic_imm(ctx, OPC_LUI, rt, rs, imm); + } else { + /* ADDI32 */ + mips32_op = OPC_ADDI; + goto do_addi; + } + break; case ADDIU32: mips32_op = OPC_ADDIU; do_addi: @@ -14088,29 +14981,89 @@ static void decode_micromips32_opc (CPUMIPSState *env, DisasContext *ctx, gen_slt_imm(ctx, mips32_op, rt, rs, imm); break; case JALX32: + check_insn_opc_removed(ctx, ISA_MIPS32R6); offset = (int32_t)(ctx->opcode & 0x3FFFFFF) << 2; gen_compute_branch(ctx, OPC_JALX, 4, rt, rs, offset, 4); ctx->hflags |= MIPS_HFLAG_BDS_STRICT; break; - case JALS32: - offset = (int32_t)(ctx->opcode & 0x3FFFFFF) << 1; - gen_compute_branch(ctx, OPC_JAL, 4, rt, rs, offset, 2); - ctx->hflags |= MIPS_HFLAG_BDS_STRICT; + case JALS32: /* BOVC, BEQC, BEQZALC */ + if (ctx->insn_flags & ISA_MIPS32R6) { + if (rs >= rt) { + /* BOVC */ + mips32_op = OPC_BOVC; + } else if (rs < rt && rs == 0) { + /* BEQZALC */ + mips32_op = OPC_BEQZALC; + } else { + /* BEQC */ + mips32_op = OPC_BEQC; + } + gen_compute_compact_branch(ctx, mips32_op, rs, rt, imm << 1); + } else { + /* JALS32 */ + offset = (int32_t)(ctx->opcode & 0x3FFFFFF) << 1; + gen_compute_branch(ctx, OPC_JAL, 4, rt, rs, offset, 2); + ctx->hflags |= MIPS_HFLAG_BDS_STRICT; + } break; - case BEQ32: - gen_compute_branch(ctx, OPC_BEQ, 4, rt, rs, imm << 1, 4); + case BEQ32: /* BC */ + if (ctx->insn_flags & ISA_MIPS32R6) { + /* BC */ + gen_compute_compact_branch(ctx, OPC_BC, 0, 0, + sextract32(ctx->opcode << 1, 0, 27)); + } else { + /* BEQ32 */ + gen_compute_branch(ctx, OPC_BEQ, 4, rt, rs, imm << 1, 4); + } break; - case BNE32: - gen_compute_branch(ctx, OPC_BNE, 4, rt, rs, imm << 1, 4); + case BNE32: /* BALC */ + if (ctx->insn_flags & ISA_MIPS32R6) { + /* BALC */ + gen_compute_compact_branch(ctx, OPC_BALC, 0, 0, + sextract32(ctx->opcode << 1, 0, 27)); + } else { + /* BNE32 */ + gen_compute_branch(ctx, OPC_BNE, 4, rt, rs, imm << 1, 4); + } break; - case J32: - gen_compute_branch(ctx, OPC_J, 4, rt, rs, - (int32_t)(ctx->opcode & 0x3FFFFFF) << 1, 4); + case J32: /* BGTZC, BLTZC, BLTC */ + if (ctx->insn_flags & ISA_MIPS32R6) { + if (rs == 0 && rt != 0) { + /* BGTZC */ + mips32_op = OPC_BGTZC; + } else if (rs != 0 && rt != 0 && rs == rt) { + /* BLTZC */ + mips32_op = OPC_BLTZC; + } else { + /* BLTC */ + mips32_op = OPC_BLTC; + } + gen_compute_compact_branch(ctx, mips32_op, rs, rt, imm << 1); + } else { + /* J32 */ + gen_compute_branch(ctx, OPC_J, 4, rt, rs, + (int32_t)(ctx->opcode & 0x3FFFFFF) << 1, 4); + } break; - case JAL32: - gen_compute_branch(ctx, OPC_JAL, 4, rt, rs, - (int32_t)(ctx->opcode & 0x3FFFFFF) << 1, 4); - ctx->hflags |= MIPS_HFLAG_BDS_STRICT; + case JAL32: /* BLEZC, BGEZC, BGEC */ + if (ctx->insn_flags & ISA_MIPS32R6) { + if (rs == 0 && rt != 0) { + /* BLEZC */ + mips32_op = OPC_BLEZC; + } else if (rs != 0 && rt != 0 && rs == rt) { + /* BGEZC */ + mips32_op = OPC_BGEZC; + } else { + /* BGEC */ + mips32_op = OPC_BGEC; + } + gen_compute_compact_branch(ctx, mips32_op, rs, rt, imm << 1); + } else { + /* JAL32 */ + gen_compute_branch(ctx, OPC_JAL, 4, rt, rs, + (int32_t)(ctx->opcode & 0x3FFFFFF) << 1, 4); + ctx->hflags |= MIPS_HFLAG_BDS_STRICT; + } break; /* Floating point (COP1) */ case LWC132: @@ -14127,14 +15080,98 @@ static void decode_micromips32_opc (CPUMIPSState *env, DisasContext *ctx, do_cop1: gen_cop1_ldst(ctx, mips32_op, rt, rs, imm); break; - case ADDIUPC: - { + case ADDIUPC: /* PCREL: ADDIUPC, AUIPC, ALUIPC, LWPC */ + if (ctx->insn_flags & ISA_MIPS32R6) { + /* PCREL: ADDIUPC, AUIPC, ALUIPC, LWPC */ + switch ((ctx->opcode >> 16) & 0x1f) { + case ADDIUPC_00 ... ADDIUPC_07: + gen_pcrel(ctx, OPC_ADDIUPC, ctx->pc & ~0x3, rt); + break; + case AUIPC: + gen_pcrel(ctx, OPC_AUIPC, ctx->pc, rt); + break; + case ALUIPC: + gen_pcrel(ctx, OPC_ALUIPC, ctx->pc, rt); + break; + case LWPC_08 ... LWPC_0F: + gen_pcrel(ctx, R6_OPC_LWPC, ctx->pc & ~0x3, rt); + break; + default: + generate_exception(ctx, EXCP_RI); + break; + } + } else { + /* ADDIUPC */ int reg = mmreg(ZIMM(ctx->opcode, 23, 3)); int offset = SIMM(ctx->opcode, 0, 23) << 2; gen_addiupc(ctx, reg, offset, 0, 0); } break; + case BNVC: /* BNEC, BNEZALC */ + check_insn(ctx, ISA_MIPS32R6); + if (rs >= rt) { + /* BNVC */ + mips32_op = OPC_BNVC; + } else if (rs < rt && rs == 0) { + /* BNEZALC */ + mips32_op = OPC_BNEZALC; + } else { + /* BNEC */ + mips32_op = OPC_BNEC; + } + gen_compute_compact_branch(ctx, mips32_op, rs, rt, imm << 1); + break; + case R6_BNEZC: /* JIALC */ + check_insn(ctx, ISA_MIPS32R6); + if (rt != 0) { + /* BNEZC */ + gen_compute_compact_branch(ctx, OPC_BNEZC, rt, 0, + sextract32(ctx->opcode << 1, 0, 22)); + } else { + /* JIALC */ + gen_compute_compact_branch(ctx, OPC_JIALC, 0, rs, imm); + } + break; + case R6_BEQZC: /* JIC */ + check_insn(ctx, ISA_MIPS32R6); + if (rt != 0) { + /* BEQZC */ + gen_compute_compact_branch(ctx, OPC_BEQZC, rt, 0, + sextract32(ctx->opcode << 1, 0, 22)); + } else { + /* JIC */ + gen_compute_compact_branch(ctx, OPC_JIC, 0, rs, imm); + } + break; + case BLEZALC: /* BGEZALC, BGEUC */ + check_insn(ctx, ISA_MIPS32R6); + if (rs == 0 && rt != 0) { + /* BLEZALC */ + mips32_op = OPC_BLEZALC; + } else if (rs != 0 && rt != 0 && rs == rt) { + /* BGEZALC */ + mips32_op = OPC_BGEZALC; + } else { + /* BGEUC */ + mips32_op = OPC_BGEUC; + } + gen_compute_compact_branch(ctx, mips32_op, rs, rt, imm << 1); + break; + case BGTZALC: /* BLTZALC, BLTUC */ + check_insn(ctx, ISA_MIPS32R6); + if (rs == 0 && rt != 0) { + /* BGTZALC */ + mips32_op = OPC_BGTZALC; + } else if (rs != 0 && rt != 0 && rs == rt) { + /* BLTZALC */ + mips32_op = OPC_BLTZALC; + } else { + /* BLTUC */ + mips32_op = OPC_BLTUC; + } + gen_compute_compact_branch(ctx, mips32_op, rs, rt, imm << 1); + break; /* Loads and stores */ case LB32: mips32_op = OPC_LB; @@ -14249,8 +15286,14 @@ static int decode_micromips_opc (CPUMIPSState *env, DisasContext *ctx) opc = OPC_SUBU; break; } - - gen_arith(ctx, opc, rd, rs1, rs2); + if (ctx->insn_flags & ISA_MIPS32R6) { + /* In the Release 6 the register number location in + * the instruction encoding has changed. + */ + gen_arith(ctx, opc, rs1, rd, rs2); + } else { + gen_arith(ctx, opc, rd, rs1, rs2); + } } break; case POOL16B: @@ -14274,7 +15317,11 @@ static int decode_micromips_opc (CPUMIPSState *env, DisasContext *ctx) } break; case POOL16C: - gen_pool16c_insn(ctx); + if (ctx->insn_flags & ISA_MIPS32R6) { + gen_pool16c_r6_insn(ctx); + } else { + gen_pool16c_insn(ctx); + } break; case LWGP16: { @@ -14286,6 +15333,7 @@ static int decode_micromips_opc (CPUMIPSState *env, DisasContext *ctx) } break; case POOL16F: + check_insn_opc_removed(ctx, ISA_MIPS32R6); if (ctx->opcode & 1) { generate_exception(ctx, EXCP_RI); } else { @@ -14293,18 +15341,7 @@ static int decode_micromips_opc (CPUMIPSState *env, DisasContext *ctx) int enc_dest = uMIPS_RD(ctx->opcode); int enc_rt = uMIPS_RS2(ctx->opcode); int enc_rs = uMIPS_RS1(ctx->opcode); - int rd, rs, re, rt; - static const int rd_enc[] = { 5, 5, 6, 4, 4, 4, 4, 4 }; - static const int re_enc[] = { 6, 7, 7, 21, 22, 5, 6, 7 }; - static const int rs_rt_enc[] = { 0, 17, 2, 3, 16, 18, 19, 20 }; - - rd = rd_enc[enc_dest]; - re = re_enc[enc_dest]; - rs = rs_rt_enc[enc_rs]; - rt = rs_rt_enc[enc_rt]; - - gen_arith(ctx, OPC_ADDU, rd, rs, 0); - gen_arith(ctx, OPC_ADDU, re, rt, 0); + gen_movep(ctx, enc_dest, enc_rt, enc_rs); } break; case LBU16: @@ -14411,15 +15448,18 @@ static int decode_micromips_opc (CPUMIPSState *env, DisasContext *ctx) break; } break; - case B16: + case B16: /* BC16 */ gen_compute_branch(ctx, OPC_BEQ, 2, 0, 0, - SIMM(ctx->opcode, 0, 10) << 1, 4); + sextract32(ctx->opcode, 0, 10) << 1, + (ctx->insn_flags & ISA_MIPS32R6) ? 0 : 4); break; - case BNEZ16: - case BEQZ16: + case BNEZ16: /* BNEZC16 */ + case BEQZ16: /* BEQZC16 */ gen_compute_branch(ctx, op == BNEZ16 ? OPC_BNE : OPC_BEQ, 2, mmreg(uMIPS_RD(ctx->opcode)), - 0, SIMM(ctx->opcode, 0, 7) << 1, 4); + 0, sextract32(ctx->opcode, 0, 7) << 1, + (ctx->insn_flags & ISA_MIPS32R6) ? 0 : 4); + break; case LI16: { @@ -14430,17 +15470,13 @@ static int decode_micromips_opc (CPUMIPSState *env, DisasContext *ctx) tcg_gen_movi_tl(cpu_gpr[reg], imm); } break; - case RES_20: - case RES_28: case RES_29: - case RES_30: case RES_31: - case RES_38: case RES_39: generate_exception(ctx, EXCP_RI); break; default: - decode_micromips32_opc (env, ctx, op); + decode_micromips32_opc(env, ctx); return 4; } @@ -16176,242 +17212,6 @@ static void gen_mipsdsp_accinsn(DisasContext *ctx, uint32_t op1, uint32_t op2, /* End MIPSDSP functions. */ -/* Compact Branches */ -static void gen_compute_compact_branch(DisasContext *ctx, uint32_t opc, - int rs, int rt, int32_t offset) -{ - int bcond_compute = 0; - TCGv t0 = tcg_temp_new(); - TCGv t1 = tcg_temp_new(); - - if (ctx->hflags & MIPS_HFLAG_BMASK) { -#ifdef MIPS_DEBUG_DISAS - LOG_DISAS("Branch in delay / forbidden slot at PC 0x" TARGET_FMT_lx - "\n", ctx->pc); -#endif - generate_exception(ctx, EXCP_RI); - goto out; - } - - /* Load needed operands and calculate btarget */ - switch (opc) { - /* compact branch */ - case OPC_BOVC: /* OPC_BEQZALC, OPC_BEQC */ - case OPC_BNVC: /* OPC_BNEZALC, OPC_BNEC */ - gen_load_gpr(t0, rs); - gen_load_gpr(t1, rt); - bcond_compute = 1; - ctx->btarget = addr_add(ctx, ctx->pc + 4, offset); - if (rs <= rt && rs == 0) { - /* OPC_BEQZALC, OPC_BNEZALC */ - tcg_gen_movi_tl(cpu_gpr[31], ctx->pc + 4); - } - break; - case OPC_BLEZC: /* OPC_BGEZC, OPC_BGEC */ - case OPC_BGTZC: /* OPC_BLTZC, OPC_BLTC */ - gen_load_gpr(t0, rs); - gen_load_gpr(t1, rt); - bcond_compute = 1; - ctx->btarget = addr_add(ctx, ctx->pc + 4, offset); - break; - case OPC_BLEZALC: /* OPC_BGEZALC, OPC_BGEUC */ - case OPC_BGTZALC: /* OPC_BLTZALC, OPC_BLTUC */ - if (rs == 0 || rs == rt) { - /* OPC_BLEZALC, OPC_BGEZALC */ - /* OPC_BGTZALC, OPC_BLTZALC */ - tcg_gen_movi_tl(cpu_gpr[31], ctx->pc + 4); - } - gen_load_gpr(t0, rs); - gen_load_gpr(t1, rt); - bcond_compute = 1; - ctx->btarget = addr_add(ctx, ctx->pc + 4, offset); - break; - case OPC_BC: - case OPC_BALC: - ctx->btarget = addr_add(ctx, ctx->pc + 4, offset); - break; - case OPC_BEQZC: - case OPC_BNEZC: - if (rs != 0) { - /* OPC_BEQZC, OPC_BNEZC */ - gen_load_gpr(t0, rs); - bcond_compute = 1; - ctx->btarget = addr_add(ctx, ctx->pc + 4, offset); - } else { - /* OPC_JIC, OPC_JIALC */ - TCGv tbase = tcg_temp_new(); - TCGv toffset = tcg_temp_new(); - - gen_load_gpr(tbase, rt); - tcg_gen_movi_tl(toffset, offset); - gen_op_addr_add(ctx, btarget, tbase, toffset); - tcg_temp_free(tbase); - tcg_temp_free(toffset); - } - break; - default: - MIPS_INVAL("Compact branch/jump"); - generate_exception(ctx, EXCP_RI); - goto out; - } - - if (bcond_compute == 0) { - /* Uncoditional compact branch */ - switch (opc) { - case OPC_JIALC: - tcg_gen_movi_tl(cpu_gpr[31], ctx->pc + 4); - /* Fallthrough */ - case OPC_JIC: - ctx->hflags |= MIPS_HFLAG_BR; - break; - case OPC_BALC: - tcg_gen_movi_tl(cpu_gpr[31], ctx->pc + 4); - /* Fallthrough */ - case OPC_BC: - ctx->hflags |= MIPS_HFLAG_B; - break; - default: - MIPS_INVAL("Compact branch/jump"); - generate_exception(ctx, EXCP_RI); - goto out; - } - - /* Generating branch here as compact branches don't have delay slot */ - gen_branch(ctx, 4); - } else { - /* Conditional compact branch */ - TCGLabel *fs = gen_new_label(); - save_cpu_state(ctx, 0); - - switch (opc) { - case OPC_BLEZALC: /* OPC_BGEZALC, OPC_BGEUC */ - if (rs == 0 && rt != 0) { - /* OPC_BLEZALC */ - tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_LE), t1, 0, fs); - } else if (rs != 0 && rt != 0 && rs == rt) { - /* OPC_BGEZALC */ - tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_GE), t1, 0, fs); - } else { - /* OPC_BGEUC */ - tcg_gen_brcond_tl(tcg_invert_cond(TCG_COND_GEU), t0, t1, fs); - } - break; - case OPC_BGTZALC: /* OPC_BLTZALC, OPC_BLTUC */ - if (rs == 0 && rt != 0) { - /* OPC_BGTZALC */ - tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_GT), t1, 0, fs); - } else if (rs != 0 && rt != 0 && rs == rt) { - /* OPC_BLTZALC */ - tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_LT), t1, 0, fs); - } else { - /* OPC_BLTUC */ - tcg_gen_brcond_tl(tcg_invert_cond(TCG_COND_LTU), t0, t1, fs); - } - break; - case OPC_BLEZC: /* OPC_BGEZC, OPC_BGEC */ - if (rs == 0 && rt != 0) { - /* OPC_BLEZC */ - tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_LE), t1, 0, fs); - } else if (rs != 0 && rt != 0 && rs == rt) { - /* OPC_BGEZC */ - tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_GE), t1, 0, fs); - } else { - /* OPC_BGEC */ - tcg_gen_brcond_tl(tcg_invert_cond(TCG_COND_GE), t0, t1, fs); - } - break; - case OPC_BGTZC: /* OPC_BLTZC, OPC_BLTC */ - if (rs == 0 && rt != 0) { - /* OPC_BGTZC */ - tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_GT), t1, 0, fs); - } else if (rs != 0 && rt != 0 && rs == rt) { - /* OPC_BLTZC */ - tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_LT), t1, 0, fs); - } else { - /* OPC_BLTC */ - tcg_gen_brcond_tl(tcg_invert_cond(TCG_COND_LT), t0, t1, fs); - } - break; - case OPC_BOVC: /* OPC_BEQZALC, OPC_BEQC */ - case OPC_BNVC: /* OPC_BNEZALC, OPC_BNEC */ - if (rs >= rt) { - /* OPC_BOVC, OPC_BNVC */ - TCGv t2 = tcg_temp_new(); - TCGv t3 = tcg_temp_new(); - TCGv t4 = tcg_temp_new(); - TCGv input_overflow = tcg_temp_new(); - - gen_load_gpr(t0, rs); - gen_load_gpr(t1, rt); - tcg_gen_ext32s_tl(t2, t0); - tcg_gen_setcond_tl(TCG_COND_NE, input_overflow, t2, t0); - tcg_gen_ext32s_tl(t3, t1); - tcg_gen_setcond_tl(TCG_COND_NE, t4, t3, t1); - tcg_gen_or_tl(input_overflow, input_overflow, t4); - - tcg_gen_add_tl(t4, t2, t3); - tcg_gen_ext32s_tl(t4, t4); - tcg_gen_xor_tl(t2, t2, t3); - tcg_gen_xor_tl(t3, t4, t3); - tcg_gen_andc_tl(t2, t3, t2); - tcg_gen_setcondi_tl(TCG_COND_LT, t4, t2, 0); - tcg_gen_or_tl(t4, t4, input_overflow); - if (opc == OPC_BOVC) { - /* OPC_BOVC */ - tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_NE), t4, 0, fs); - } else { - /* OPC_BNVC */ - tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_EQ), t4, 0, fs); - } - tcg_temp_free(input_overflow); - tcg_temp_free(t4); - tcg_temp_free(t3); - tcg_temp_free(t2); - } else if (rs < rt && rs == 0) { - /* OPC_BEQZALC, OPC_BNEZALC */ - if (opc == OPC_BEQZALC) { - /* OPC_BEQZALC */ - tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_EQ), t1, 0, fs); - } else { - /* OPC_BNEZALC */ - tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_NE), t1, 0, fs); - } - } else { - /* OPC_BEQC, OPC_BNEC */ - if (opc == OPC_BEQC) { - /* OPC_BEQC */ - tcg_gen_brcond_tl(tcg_invert_cond(TCG_COND_EQ), t0, t1, fs); - } else { - /* OPC_BNEC */ - tcg_gen_brcond_tl(tcg_invert_cond(TCG_COND_NE), t0, t1, fs); - } - } - break; - case OPC_BEQZC: - tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_EQ), t0, 0, fs); - break; - case OPC_BNEZC: - tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_NE), t0, 0, fs); - break; - default: - MIPS_INVAL("Compact conditional branch/jump"); - generate_exception(ctx, EXCP_RI); - goto out; - } - - /* Generating branch here as compact branches don't have delay slot */ - gen_goto_tb(ctx, 1, ctx->btarget); - gen_set_label(fs); - - ctx->hflags |= MIPS_HFLAG_FBNSLOT; - MIPS_DEBUG("Compact conditional branch"); - } - -out: - tcg_temp_free(t0); - tcg_temp_free(t1); -} - static void decode_opc_special_r6(CPUMIPSState *env, DisasContext *ctx) { int rs, rt, rd, sa; @@ -16425,18 +17225,7 @@ static void decode_opc_special_r6(CPUMIPSState *env, DisasContext *ctx) op1 = MASK_SPECIAL(ctx->opcode); switch (op1) { case OPC_LSA: - if (rd != 0) { - int imm2 = extract32(ctx->opcode, 6, 3); - TCGv t0 = tcg_temp_new(); - TCGv t1 = tcg_temp_new(); - gen_load_gpr(t0, rs); - gen_load_gpr(t1, rt); - tcg_gen_shli_tl(t0, t0, imm2 + 1); - tcg_gen_add_tl(t0, t0, t1); - tcg_gen_ext32s_tl(cpu_gpr[rd], t0); - tcg_temp_free(t1); - tcg_temp_free(t0); - } + gen_lsa(ctx, op1, rd, rs, rt, extract32(ctx->opcode, 6, 2)); break; case OPC_MULT ... OPC_DIVU: op2 = MASK_R6_MULDIV(ctx->opcode); @@ -16472,26 +17261,20 @@ static void decode_opc_special_r6(CPUMIPSState *env, DisasContext *ctx) } break; case R6_OPC_SDBBP: - if (ctx->hflags & MIPS_HFLAG_SBRI) { - generate_exception(ctx, EXCP_RI); + if (is_uhi(extract32(ctx->opcode, 6, 20))) { + gen_helper_do_semihosting(cpu_env); } else { - generate_exception(ctx, EXCP_DBp); + if (ctx->hflags & MIPS_HFLAG_SBRI) { + generate_exception(ctx, EXCP_RI); + } else { + generate_exception(ctx, EXCP_DBp); + } } break; #if defined(TARGET_MIPS64) case OPC_DLSA: check_mips_64(ctx); - if (rd != 0) { - int imm2 = extract32(ctx->opcode, 6, 3); - TCGv t0 = tcg_temp_new(); - TCGv t1 = tcg_temp_new(); - gen_load_gpr(t0, rs); - gen_load_gpr(t1, rt); - tcg_gen_shli_tl(t0, t0, imm2 + 1); - tcg_gen_add_tl(cpu_gpr[rd], t0, t1); - tcg_temp_free(t1); - tcg_temp_free(t0); - } + gen_lsa(ctx, op1, rd, rs, rt, extract32(ctx->opcode, 6, 2)); break; case R6_OPC_DCLO: case R6_OPC_DCLZ: @@ -16845,16 +17628,15 @@ static void decode_opc_special2_legacy(CPUMIPSState *env, DisasContext *ctx) gen_cl(ctx, op1, rd, rs); break; case OPC_SDBBP: - /* XXX: not clear which exception should be raised - * when in debug mode... - */ - check_insn(ctx, ISA_MIPS32); - if (!(ctx->hflags & MIPS_HFLAG_DM)) { - generate_exception(ctx, EXCP_DBp); + if (is_uhi(extract32(ctx->opcode, 6, 20))) { + gen_helper_do_semihosting(cpu_env); } else { + /* XXX: not clear which exception should be raised + * when in debug mode... + */ + check_insn(ctx, ISA_MIPS32); generate_exception(ctx, EXCP_DBp); } - /* Treat as NOP. */ break; #if defined(TARGET_MIPS64) case OPC_DCLO: @@ -16916,35 +17698,15 @@ static void decode_opc_special3_r6(CPUMIPSState *env, DisasContext *ctx) /* Treat as NOP. */ break; } - TCGv t0 = tcg_temp_new(); - gen_load_gpr(t0, rt); - op2 = MASK_BSHFL(ctx->opcode); switch (op2) { case OPC_ALIGN ... OPC_ALIGN_END: - sa &= 3; - if (sa == 0) { - tcg_gen_mov_tl(cpu_gpr[rd], t0); - } else { - TCGv t1 = tcg_temp_new(); - TCGv_i64 t2 = tcg_temp_new_i64(); - gen_load_gpr(t1, rs); - tcg_gen_concat_tl_i64(t2, t1, t0); - tcg_gen_shri_i64(t2, t2, 8 * (4 - sa)); -#if defined(TARGET_MIPS64) - tcg_gen_ext32s_i64(cpu_gpr[rd], t2); -#else - tcg_gen_trunc_i64_i32(cpu_gpr[rd], t2); -#endif - tcg_temp_free_i64(t2); - tcg_temp_free(t1); - } + gen_align(ctx, OPC_ALIGN, rd, rs, rt, sa & 3); break; case OPC_BITSWAP: - gen_helper_bitswap(cpu_gpr[rd], t0); + gen_bitswap(ctx, op2, rd, rt); break; } - tcg_temp_free(t0); } break; #if defined(TARGET_MIPS64) @@ -16961,29 +17723,16 @@ static void decode_opc_special3_r6(CPUMIPSState *env, DisasContext *ctx) /* Treat as NOP. */ break; } - TCGv t0 = tcg_temp_new(); - gen_load_gpr(t0, rt); - op2 = MASK_DBSHFL(ctx->opcode); switch (op2) { case OPC_DALIGN ... OPC_DALIGN_END: - sa &= 7; - if (sa == 0) { - tcg_gen_mov_tl(cpu_gpr[rd], t0); - } else { - TCGv t1 = tcg_temp_new(); - gen_load_gpr(t1, rs); - tcg_gen_shli_tl(t0, t0, 8 * sa); - tcg_gen_shri_tl(t1, t1, 8 * (8 - sa)); - tcg_gen_or_tl(cpu_gpr[rd], t1, t0); - tcg_temp_free(t1); - } + gen_align(ctx, OPC_DALIGN, rd, rs, rt, sa & 7); break; case OPC_DBITSWAP: - gen_helper_dbitswap(cpu_gpr[rd], t0); + gen_bitswap(ctx, op2, rd, rt); break; } - tcg_temp_free(t0); + } break; #endif @@ -19082,7 +19831,7 @@ static void decode_opc(CPUMIPSState *env, DisasContext *ctx) if (ctx->insn_flags & ISA_MIPS32R6) { /* OPC_BC1EQZ */ gen_compute_branch1_r6(ctx, MASK_CP1(ctx->opcode), - rt, imm << 2); + rt, imm << 2, 4); } else { /* OPC_BC1ANY2 */ check_cop1x(ctx); @@ -19095,7 +19844,7 @@ static void decode_opc(CPUMIPSState *env, DisasContext *ctx) check_cp1_enabled(ctx); check_insn(ctx, ISA_MIPS32R6); gen_compute_branch1_r6(ctx, MASK_CP1(ctx->opcode), - rt, imm << 2); + rt, imm << 2, 4); break; case OPC_BC1ANY4: check_cp1_enabled(ctx); @@ -19110,8 +19859,7 @@ static void decode_opc(CPUMIPSState *env, DisasContext *ctx) (rt >> 2) & 0x7, imm << 2); break; case OPC_PS_FMT: - check_cp1_enabled(ctx); - check_insn_opc_removed(ctx, ISA_MIPS32R6); + check_ps(ctx); /* fall through */ case OPC_S_FMT: case OPC_D_FMT: @@ -19372,7 +20120,7 @@ static void decode_opc(CPUMIPSState *env, DisasContext *ctx) break; case OPC_PCREL: check_insn(ctx, ISA_MIPS32R6); - gen_pcrel(ctx, rs, imm); + gen_pcrel(ctx, ctx->opcode, ctx->pc, rs); break; default: /* Invalid */ MIPS_INVAL("major opcode"); @@ -19420,6 +20168,8 @@ gen_intermediate_code_internal(MIPSCPU *cpu, TranslationBlock *tb, /* Restore delay slot state from the tb context. */ ctx.hflags = (uint32_t)tb->flags; /* FIXME: maybe use 64 bits here? */ ctx.ulri = (env->CP0_Config3 >> CP0C3_ULRI) & 1; + ctx.ps = ((env->active_fpu.fcr0 >> FCR0_PS) & 1) || + (env->insn_flags & (INSN_LOONGSON2E | INSN_LOONGSON2F)); restore_cpu_state(env, &ctx); #ifdef CONFIG_USER_ONLY ctx.mem_idx = MIPS_HFLAG_UM; @@ -19489,6 +20239,12 @@ gen_intermediate_code_internal(MIPSCPU *cpu, TranslationBlock *tb, forbidden slot */ is_slot = 1; } + if ((ctx.hflags & MIPS_HFLAG_M16) && + (ctx.hflags & MIPS_HFLAG_FBNSLOT)) { + /* Force to generate branch as microMIPS R6 doesn't restrict + branches in the forbidden slot. */ + is_slot = 1; + } } if (is_slot) { gen_branch(&ctx, insn_bytes); @@ -19927,6 +20683,11 @@ void cpu_state_reset(CPUMIPSState *env) restore_flush_mode(env); restore_pamask(env); cs->exception_index = EXCP_NONE; + + if (semihosting_get_argc()) { + /* UHI interface can be used to obtain argc and argv */ + env->active_tc.gpr[4] = -1; + } } void restore_state_to_opc(CPUMIPSState *env, TranslationBlock *tb, int pc_pos) diff --git a/target-mips/translate_init.c b/target-mips/translate_init.c index 30605dab06..ddfaff8052 100644 --- a/target-mips/translate_init.c +++ b/target-mips/translate_init.c @@ -424,6 +424,43 @@ static const mips_def_t mips_defs[] = .insn_flags = CPU_MIPS32R5 | ASE_MIPS16 | ASE_MSA, .mmu_type = MMU_TYPE_R4000, }, + { + /* A generic CPU supporting MIPS32 Release 6 ISA. + FIXME: Support IEEE 754-2008 FP. + Eventually this should be replaced by a real CPU model. */ + .name = "mips32r6-generic", + .CP0_PRid = 0x00010000, + .CP0_Config0 = MIPS_CONFIG0 | (0x2 << CP0C0_AR) | + (MMU_TYPE_R4000 << CP0C0_MT), + .CP0_Config1 = MIPS_CONFIG1 | (1 << CP0C1_FP) | (31 << CP0C1_MMU) | + (2 << CP0C1_IS) | (4 << CP0C1_IL) | (3 << CP0C1_IA) | + (2 << CP0C1_DS) | (4 << CP0C1_DL) | (3 << CP0C1_DA) | + (0 << CP0C1_PC) | (1 << CP0C1_WR) | (1 << CP0C1_EP), + .CP0_Config2 = MIPS_CONFIG2, + .CP0_Config3 = MIPS_CONFIG3 | (1 << CP0C3_BP) | (1 << CP0C3_BI) | + (2 << CP0C3_ISA) | (1 << CP0C3_ULRI) | + (1 << CP0C3_RXI) | (1U << CP0C3_M), + .CP0_Config4 = MIPS_CONFIG4 | (0xfc << CP0C4_KScrExist) | + (3 << CP0C4_IE) | (1U << CP0C4_M), + .CP0_Config5 = MIPS_CONFIG5 | (1 << CP0C5_LLB), + .CP0_Config5_rw_bitmask = (1 << CP0C5_SBRI) | (1 << CP0C5_FRE) | + (1 << CP0C5_UFE), + .CP0_LLAddr_rw_bitmask = 0, + .CP0_LLAddr_shift = 0, + .SYNCI_Step = 32, + .CCRes = 2, + .CP0_Status_rw_bitmask = 0x3058FF1F, + .CP0_PageGrain = (1 << CP0PG_IEC) | (1 << CP0PG_XIE) | + (1U << CP0PG_RIE), + .CP0_PageGrain_rw_bitmask = 0, + .CP1_fcr0 = (1 << FCR0_FREP) | (1 << FCR0_F64) | (1 << FCR0_L) | + (1 << FCR0_W) | (1 << FCR0_D) | (1 << FCR0_S) | + (0x00 << FCR0_PRID) | (0x0 << FCR0_REV), + .SEGBITS = 32, + .PABITS = 32, + .insn_flags = CPU_MIPS32R6 | ASE_MICROMIPS, + .mmu_type = MMU_TYPE_R4000, + }, #if defined(TARGET_MIPS64) { .name = "R4000", |