blob: 7d74cc34f8a1ff9d6f508bb60752da83ecf34934 [file] [log] [blame]
/*
* MIPS Loongson 64-bit translation routines
*
* Copyright (c) 2004-2005 Jocelyn Mayer
* Copyright (c) 2006 Marius Groeger (FPU operations)
* Copyright (c) 2006 Thiemo Seufer (MIPS32R2 support)
* Copyright (c) 2011 Richard Henderson <rth@twiddle.net>
* Copyright (c) 2021 Philippe Mathieu-Daudé
*
* This code is licensed under the GNU GPLv2 and later.
*/
#include "qemu/osdep.h"
#include "translate.h"
/* Include the auto-generated decoder. */
#include "decode-godson2.c.inc"
#include "decode-loong-ext.c.inc"
/*
* Word or double-word Fixed-point instructions.
* ---------------------------------------------
*
* Fixed-point multiplies and divisions write only
* one result into general-purpose registers.
*/
static bool gen_lext_DIV_G(DisasContext *s, int rd, int rs, int rt,
bool is_double)
{
TCGv t0, t1;
TCGLabel *l1, *l2, *l3;
if (rd == 0) {
/* Treat as NOP. */
return true;
}
t0 = tcg_temp_new();
t1 = tcg_temp_new();
l1 = gen_new_label();
l2 = gen_new_label();
l3 = gen_new_label();
gen_load_gpr(t0, rs);
gen_load_gpr(t1, rt);
if (!is_double) {
tcg_gen_ext32s_tl(t0, t0);
tcg_gen_ext32s_tl(t1, t1);
}
tcg_gen_brcondi_tl(TCG_COND_NE, t1, 0, l1);
tcg_gen_movi_tl(cpu_gpr[rd], 0);
tcg_gen_br(l3);
gen_set_label(l1);
tcg_gen_brcondi_tl(TCG_COND_NE, t0, is_double ? LLONG_MIN : INT_MIN, l2);
tcg_gen_brcondi_tl(TCG_COND_NE, t1, -1LL, l2);
tcg_gen_mov_tl(cpu_gpr[rd], t0);
tcg_gen_br(l3);
gen_set_label(l2);
tcg_gen_div_tl(cpu_gpr[rd], t0, t1);
if (!is_double) {
tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]);
}
gen_set_label(l3);
return true;
}
static bool trans_DIV_G(DisasContext *s, arg_muldiv *a)
{
return gen_lext_DIV_G(s, a->rd, a->rs, a->rt, false);
}
static bool trans_DDIV_G(DisasContext *s, arg_muldiv *a)
{
return gen_lext_DIV_G(s, a->rd, a->rs, a->rt, true);
}
static bool gen_lext_DIVU_G(DisasContext *s, int rd, int rs, int rt,
bool is_double)
{
TCGv t0, t1;
TCGLabel *l1, *l2;
if (rd == 0) {
/* Treat as NOP. */
return true;
}
t0 = tcg_temp_new();
t1 = tcg_temp_new();
l1 = gen_new_label();
l2 = gen_new_label();
gen_load_gpr(t0, rs);
gen_load_gpr(t1, rt);
if (!is_double) {
tcg_gen_ext32u_tl(t0, t0);
tcg_gen_ext32u_tl(t1, t1);
}
tcg_gen_brcondi_tl(TCG_COND_NE, t1, 0, l1);
tcg_gen_movi_tl(cpu_gpr[rd], 0);
tcg_gen_br(l2);
gen_set_label(l1);
tcg_gen_divu_tl(cpu_gpr[rd], t0, t1);
if (!is_double) {
tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]);
}
gen_set_label(l2);
return true;
}
static bool trans_DIVU_G(DisasContext *s, arg_muldiv *a)
{
return gen_lext_DIVU_G(s, a->rd, a->rs, a->rt, false);
}
static bool trans_DDIVU_G(DisasContext *s, arg_muldiv *a)
{
return gen_lext_DIVU_G(s, a->rd, a->rs, a->rt, true);
}
static bool gen_lext_MOD_G(DisasContext *s, int rd, int rs, int rt,
bool is_double)
{
TCGv t0, t1;
TCGLabel *l1, *l2, *l3;
if (rd == 0) {
/* Treat as NOP. */
return true;
}
t0 = tcg_temp_new();
t1 = tcg_temp_new();
l1 = gen_new_label();
l2 = gen_new_label();
l3 = gen_new_label();
gen_load_gpr(t0, rs);
gen_load_gpr(t1, rt);
if (!is_double) {
tcg_gen_ext32u_tl(t0, t0);
tcg_gen_ext32u_tl(t1, t1);
}
tcg_gen_brcondi_tl(TCG_COND_EQ, t1, 0, l1);
tcg_gen_brcondi_tl(TCG_COND_NE, t0, is_double ? LLONG_MIN : INT_MIN, l2);
tcg_gen_brcondi_tl(TCG_COND_NE, t1, -1LL, l2);
gen_set_label(l1);
tcg_gen_movi_tl(cpu_gpr[rd], 0);
tcg_gen_br(l3);
gen_set_label(l2);
tcg_gen_rem_tl(cpu_gpr[rd], t0, t1);
if (!is_double) {
tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]);
}
gen_set_label(l3);
return true;
}
static bool trans_MOD_G(DisasContext *s, arg_muldiv *a)
{
return gen_lext_MOD_G(s, a->rd, a->rs, a->rt, false);
}
static bool trans_DMOD_G(DisasContext *s, arg_muldiv *a)
{
return gen_lext_MOD_G(s, a->rd, a->rs, a->rt, true);
}
static bool gen_lext_MODU_G(DisasContext *s, int rd, int rs, int rt,
bool is_double)
{
TCGv t0, t1;
TCGLabel *l1, *l2;
if (rd == 0) {
/* Treat as NOP. */
return true;
}
t0 = tcg_temp_new();
t1 = tcg_temp_new();
l1 = gen_new_label();
l2 = gen_new_label();
gen_load_gpr(t0, rs);
gen_load_gpr(t1, rt);
if (!is_double) {
tcg_gen_ext32u_tl(t0, t0);
tcg_gen_ext32u_tl(t1, t1);
}
tcg_gen_brcondi_tl(TCG_COND_NE, t1, 0, l1);
tcg_gen_movi_tl(cpu_gpr[rd], 0);
tcg_gen_br(l2);
gen_set_label(l1);
tcg_gen_remu_tl(cpu_gpr[rd], t0, t1);
if (!is_double) {
tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]);
}
gen_set_label(l2);
return true;
}
static bool trans_MODU_G(DisasContext *s, arg_muldiv *a)
{
return gen_lext_MODU_G(s, a->rd, a->rs, a->rt, false);
}
static bool trans_DMODU_G(DisasContext *s, arg_muldiv *a)
{
return gen_lext_MODU_G(s, a->rd, a->rs, a->rt, true);
}
static bool gen_lext_MULT_G(DisasContext *s, int rd, int rs, int rt,
bool is_double)
{
TCGv t0, t1;
if (rd == 0) {
/* Treat as NOP. */
return true;
}
t0 = tcg_temp_new();
t1 = tcg_temp_new();
gen_load_gpr(t0, rs);
gen_load_gpr(t1, rt);
tcg_gen_mul_tl(cpu_gpr[rd], t0, t1);
if (!is_double) {
tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]);
}
return true;
}
static bool trans_MULTu_G(DisasContext *s, arg_muldiv *a)
{
return gen_lext_MULT_G(s, a->rd, a->rs, a->rt, false);
}
static bool trans_DMULTu_G(DisasContext *s, arg_muldiv *a)
{
return gen_lext_MULT_G(s, a->rd, a->rs, a->rt, true);
}
bool decode_ext_loongson(DisasContext *ctx, uint32_t insn)
{
if (!decode_64bit_enabled(ctx)) {
return false;
}
if ((ctx->insn_flags & INSN_LOONGSON2E) && decode_godson2(ctx, ctx->opcode)) {
return true;
}
if ((ctx->insn_flags & ASE_LEXT) && decode_loong_ext(ctx, ctx->opcode)) {
return true;
}
return false;
}