aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2015-06-26 11:32:58 +0100
committerPeter Maydell <peter.maydell@linaro.org>2015-06-26 11:32:58 +0100
commitccb0c7e122db72d3a5da798c6414d4912bba828f (patch)
treedceae6e3d7ba56cc60aca3167f25c13c76d37a3e
parent0a4a0312bf8b029cbd32a97db2cad669cf65ac49 (diff)
parent4b3bcd016d83cc75f6a495c1db54b6c77f037adc (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.c11
-rw-r--r--include/exec/softmmu-semi.h13
-rw-r--r--qemu-options.hx10
-rw-r--r--target-mips/Makefile.objs2
-rw-r--r--target-mips/helper.h2
-rw-r--r--target-mips/mips-semi.c358
-rw-r--r--target-mips/translate.c1749
-rw-r--r--target-mips/translate_init.c37
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",