#include <stdint.h>
#include <86box/86box.h>
#include "cpu.h"
#include <86box/mem.h>
#include <86box/plat_unused.h>

#include "x86.h"
#include "x86_flags.h"
#include "x86seg_common.h"
#include "x86seg.h"
#include "386_common.h"
#include "x87_sf.h"
#include "x87.h"
#include "codegen.h"
#include "codegen_accumulate.h"
#include "codegen_ir.h"
#include "codegen_ops.h"
#include "codegen_ops_fpu_arith.h"
#include "codegen_ops_helpers.h"

uint32_t
ropFADD(UNUSED(codeblock_t *block), ir_data_t *ir, UNUSED(uint8_t opcode), uint32_t fetchdat, UNUSED(uint32_t op_32), uint32_t op_pc)
{
    int src_reg = fetchdat & 7;

    uop_FP_ENTER(ir);
    uop_FADD(ir, IREG_ST(0), IREG_ST(0), IREG_ST(src_reg));
    uop_MOV_IMM(ir, IREG_tag(0), TAG_VALID);

    return op_pc;
}
uint32_t
ropFADDr(UNUSED(codeblock_t *block), ir_data_t *ir, UNUSED(uint8_t opcode), uint32_t fetchdat, UNUSED(uint32_t op_32), uint32_t op_pc)
{
    int dest_reg = fetchdat & 7;

    uop_FP_ENTER(ir);
    uop_FADD(ir, IREG_ST(dest_reg), IREG_ST(dest_reg), IREG_ST(0));
    uop_MOV_IMM(ir, IREG_tag(dest_reg), TAG_VALID);

    return op_pc;
}
uint32_t
ropFADDP(codeblock_t *block, ir_data_t *ir, UNUSED(uint8_t opcode), uint32_t fetchdat, UNUSED(uint32_t op_32), uint32_t op_pc)
{
    int dest_reg = fetchdat & 7;

    uop_FP_ENTER(ir);
    uop_FADD(ir, IREG_ST(dest_reg), IREG_ST(dest_reg), IREG_ST(0));
    uop_MOV_IMM(ir, IREG_tag(dest_reg), TAG_VALID);
    fpu_POP(block, ir);

    return op_pc;
}

uint32_t
ropFCOM(UNUSED(codeblock_t *block), ir_data_t *ir, UNUSED(uint8_t opcode), uint32_t fetchdat, UNUSED(uint32_t op_32), uint32_t op_pc)
{
    int src_reg = fetchdat & 7;

    uop_FP_ENTER(ir);
    uop_FCOM(ir, IREG_temp0_W, IREG_ST(0), IREG_ST(src_reg));
    uop_AND_IMM(ir, IREG_NPXS, IREG_NPXS, ~(FPU_SW_C0 | FPU_SW_C2 | FPU_SW_C3));
    uop_OR(ir, IREG_NPXS, IREG_NPXS, IREG_temp0_W);

    return op_pc;
}
uint32_t
ropFCOMP(codeblock_t *block, ir_data_t *ir, UNUSED(uint8_t opcode), uint32_t fetchdat, UNUSED(uint32_t op_32), uint32_t op_pc)
{
    int src_reg = fetchdat & 7;

    uop_FP_ENTER(ir);
    uop_FCOM(ir, IREG_temp0_W, IREG_ST(0), IREG_ST(src_reg));
    uop_AND_IMM(ir, IREG_NPXS, IREG_NPXS, ~(FPU_SW_C0 | FPU_SW_C2 | FPU_SW_C3));
    uop_OR(ir, IREG_NPXS, IREG_NPXS, IREG_temp0_W);
    fpu_POP(block, ir);

    return op_pc;
}
uint32_t
ropFCOMPP(codeblock_t *block, ir_data_t *ir, UNUSED(uint8_t opcode), UNUSED(uint32_t fetchdat), UNUSED(uint32_t op_32), uint32_t op_pc)
{
    uop_FP_ENTER(ir);
    uop_FCOM(ir, IREG_temp0_W, IREG_ST(0), IREG_ST(1));
    uop_AND_IMM(ir, IREG_NPXS, IREG_NPXS, ~(FPU_SW_C0 | FPU_SW_C2 | FPU_SW_C3));
    uop_OR(ir, IREG_NPXS, IREG_NPXS, IREG_temp0_W);
    fpu_POP2(block, ir);

    return op_pc;
}

uint32_t
ropFDIV(UNUSED(codeblock_t *block), ir_data_t *ir, UNUSED(uint8_t opcode), uint32_t fetchdat, UNUSED(uint32_t op_32), uint32_t op_pc)
{
    int src_reg = fetchdat & 7;

    uop_FP_ENTER(ir);
    uop_FDIV(ir, IREG_ST(0), IREG_ST(0), IREG_ST(src_reg));
    uop_MOV_IMM(ir, IREG_tag(0), TAG_VALID);

    return op_pc;
}
uint32_t
ropFDIVR(UNUSED(codeblock_t *block), ir_data_t *ir, UNUSED(uint8_t opcode), uint32_t fetchdat, UNUSED(uint32_t op_32), uint32_t op_pc)
{
    int src_reg = fetchdat & 7;

    uop_FP_ENTER(ir);
    uop_FDIV(ir, IREG_ST(0), IREG_ST(src_reg), IREG_ST(0));
    uop_MOV_IMM(ir, IREG_tag(0), TAG_VALID);

    return op_pc;
}
uint32_t
ropFDIVr(UNUSED(codeblock_t *block), ir_data_t *ir, UNUSED(uint8_t opcode), uint32_t fetchdat, UNUSED(uint32_t op_32), uint32_t op_pc)
{
    int dest_reg = fetchdat & 7;

    uop_FP_ENTER(ir);
    uop_FDIV(ir, IREG_ST(dest_reg), IREG_ST(dest_reg), IREG_ST(0));
    uop_MOV_IMM(ir, IREG_tag(dest_reg), TAG_VALID);

    return op_pc;
}
uint32_t
ropFDIVRr(UNUSED(codeblock_t *block), ir_data_t *ir, UNUSED(uint8_t opcode), uint32_t fetchdat, UNUSED(uint32_t op_32), uint32_t op_pc)
{
    int dest_reg = fetchdat & 7;

    uop_FP_ENTER(ir);
    uop_FDIV(ir, IREG_ST(dest_reg), IREG_ST(0), IREG_ST(dest_reg));
    uop_MOV_IMM(ir, IREG_tag(dest_reg), TAG_VALID);

    return op_pc;
}
uint32_t
ropFDIVP(codeblock_t *block, ir_data_t *ir, UNUSED(uint8_t opcode), uint32_t fetchdat, UNUSED(uint32_t op_32), uint32_t op_pc)
{
    int dest_reg = fetchdat & 7;

    uop_FP_ENTER(ir);
    uop_FDIV(ir, IREG_ST(dest_reg), IREG_ST(dest_reg), IREG_ST(0));
    uop_MOV_IMM(ir, IREG_tag(dest_reg), TAG_VALID);
    fpu_POP(block, ir);

    return op_pc;
}
uint32_t
ropFDIVRP(codeblock_t *block, ir_data_t *ir, UNUSED(uint8_t opcode), uint32_t fetchdat, UNUSED(uint32_t op_32), uint32_t op_pc)
{
    int dest_reg = fetchdat & 7;

    uop_FP_ENTER(ir);
    uop_FDIV(ir, IREG_ST(dest_reg), IREG_ST(0), IREG_ST(dest_reg));
    uop_MOV_IMM(ir, IREG_tag(dest_reg), TAG_VALID);
    fpu_POP(block, ir);

    return op_pc;
}

uint32_t
ropFMUL(UNUSED(codeblock_t *block), ir_data_t *ir, UNUSED(uint8_t opcode), uint32_t fetchdat, UNUSED(uint32_t op_32), uint32_t op_pc)
{
    int src_reg = fetchdat & 7;

    uop_FP_ENTER(ir);
    uop_FMUL(ir, IREG_ST(0), IREG_ST(0), IREG_ST(src_reg));
    uop_MOV_IMM(ir, IREG_tag(0), TAG_VALID);

    return op_pc;
}
uint32_t
ropFMULr(UNUSED(codeblock_t *block), ir_data_t *ir, UNUSED(uint8_t opcode), uint32_t fetchdat, UNUSED(uint32_t op_32), uint32_t op_pc)
{
    int dest_reg = fetchdat & 7;

    uop_FP_ENTER(ir);
    uop_FMUL(ir, IREG_ST(dest_reg), IREG_ST(dest_reg), IREG_ST(0));
    uop_MOV_IMM(ir, IREG_tag(dest_reg), TAG_VALID);

    return op_pc;
}
uint32_t
ropFMULP(codeblock_t *block, ir_data_t *ir, UNUSED(uint8_t opcode), uint32_t fetchdat, UNUSED(uint32_t op_32), uint32_t op_pc)
{
    int dest_reg = fetchdat & 7;

    uop_FP_ENTER(ir);
    uop_FMUL(ir, IREG_ST(dest_reg), IREG_ST(dest_reg), IREG_ST(0));
    uop_MOV_IMM(ir, IREG_tag(dest_reg), TAG_VALID);
    fpu_POP(block, ir);

    return op_pc;
}

uint32_t
ropFSUB(UNUSED(codeblock_t *block), ir_data_t *ir, UNUSED(uint8_t opcode), uint32_t fetchdat, UNUSED(uint32_t op_32), uint32_t op_pc)
{
    int src_reg = fetchdat & 7;

    uop_FP_ENTER(ir);
    uop_FSUB(ir, IREG_ST(0), IREG_ST(0), IREG_ST(src_reg));
    uop_MOV_IMM(ir, IREG_tag(0), TAG_VALID);

    return op_pc;
}
uint32_t
ropFSUBR(UNUSED(codeblock_t *block), ir_data_t *ir, UNUSED(uint8_t opcode), uint32_t fetchdat, UNUSED(uint32_t op_32), uint32_t op_pc)
{
    int src_reg = fetchdat & 7;

    uop_FP_ENTER(ir);
    uop_FSUB(ir, IREG_ST(0), IREG_ST(src_reg), IREG_ST(0));
    uop_MOV_IMM(ir, IREG_tag(0), TAG_VALID);

    return op_pc;
}
uint32_t
ropFSUBr(UNUSED(codeblock_t *block), ir_data_t *ir, UNUSED(uint8_t opcode), uint32_t fetchdat, UNUSED(uint32_t op_32), uint32_t op_pc)
{
    int dest_reg = fetchdat & 7;

    uop_FP_ENTER(ir);
    uop_FSUB(ir, IREG_ST(dest_reg), IREG_ST(dest_reg), IREG_ST(0));
    uop_MOV_IMM(ir, IREG_tag(dest_reg), TAG_VALID);

    return op_pc;
}
uint32_t
ropFSUBRr(UNUSED(codeblock_t *block), ir_data_t *ir, UNUSED(uint8_t opcode), uint32_t fetchdat, UNUSED(uint32_t op_32), uint32_t op_pc)
{
    int dest_reg = fetchdat & 7;

    uop_FP_ENTER(ir);
    uop_FSUB(ir, IREG_ST(dest_reg), IREG_ST(0), IREG_ST(dest_reg));
    uop_MOV_IMM(ir, IREG_tag(dest_reg), TAG_VALID);

    return op_pc;
}
uint32_t
ropFSUBP(codeblock_t *block, ir_data_t *ir, UNUSED(uint8_t opcode), uint32_t fetchdat, UNUSED(uint32_t op_32), uint32_t op_pc)
{
    int dest_reg = fetchdat & 7;

    uop_FP_ENTER(ir);
    uop_FSUB(ir, IREG_ST(dest_reg), IREG_ST(dest_reg), IREG_ST(0));
    uop_MOV_IMM(ir, IREG_tag(dest_reg), TAG_VALID);
    fpu_POP(block, ir);

    return op_pc;
}
uint32_t
ropFSUBRP(codeblock_t *block, ir_data_t *ir, UNUSED(uint8_t opcode), uint32_t fetchdat, UNUSED(uint32_t op_32), uint32_t op_pc)
{
    int dest_reg = fetchdat & 7;

    uop_FP_ENTER(ir);
    uop_FSUB(ir, IREG_ST(dest_reg), IREG_ST(0), IREG_ST(dest_reg));
    uop_MOV_IMM(ir, IREG_tag(dest_reg), TAG_VALID);
    fpu_POP(block, ir);

    return op_pc;
}

uint32_t
ropFUCOM(UNUSED(codeblock_t *block), ir_data_t *ir, UNUSED(uint8_t opcode), uint32_t fetchdat, UNUSED(uint32_t op_32), uint32_t op_pc)
{
    int src_reg = fetchdat & 7;

    uop_FP_ENTER(ir);
    uop_FCOM(ir, IREG_temp0_W, IREG_ST(0), IREG_ST(src_reg));
    uop_AND_IMM(ir, IREG_NPXS, IREG_NPXS, ~(FPU_SW_C0 | FPU_SW_C2 | FPU_SW_C3));
    uop_OR(ir, IREG_NPXS, IREG_NPXS, IREG_temp0_W);

    return op_pc;
}
uint32_t
ropFUCOMP(codeblock_t *block, ir_data_t *ir, UNUSED(uint8_t opcode), uint32_t fetchdat, UNUSED(uint32_t op_32), uint32_t op_pc)
{
    int src_reg = fetchdat & 7;

    uop_FP_ENTER(ir);
    uop_FCOM(ir, IREG_temp0_W, IREG_ST(0), IREG_ST(src_reg));
    uop_AND_IMM(ir, IREG_NPXS, IREG_NPXS, ~(FPU_SW_C0 | FPU_SW_C2 | FPU_SW_C3));
    uop_OR(ir, IREG_NPXS, IREG_NPXS, IREG_temp0_W);
    fpu_POP(block, ir);

    return op_pc;
}
uint32_t
ropFUCOMPP(codeblock_t *block, ir_data_t *ir, UNUSED(uint8_t opcode), UNUSED(uint32_t fetchdat), UNUSED(uint32_t op_32), uint32_t op_pc)
{
    uop_FP_ENTER(ir);
    uop_FCOM(ir, IREG_temp0_W, IREG_ST(0), IREG_ST(1));
    uop_AND_IMM(ir, IREG_NPXS, IREG_NPXS, ~(FPU_SW_C0 | FPU_SW_C2 | FPU_SW_C3));
    uop_OR(ir, IREG_NPXS, IREG_NPXS, IREG_temp0_W);
    fpu_POP2(block, ir);

    return op_pc;
}

#define ropF_arith_mem(name, load_uop)                                                         \
    uint32_t ropFADD##name(codeblock_t *block, ir_data_t *ir, UNUSED(uint8_t opcode),          \
                           uint32_t fetchdat, uint32_t op_32, uint32_t op_pc)                  \
    {                                                                                          \
        x86seg *target_seg;                                                                    \
                                                                                               \
        if ((cpu_state.npxc >> 10) & 3)                                                        \
            return 0;                                                                          \
        uop_FP_ENTER(ir);                                                                      \
        uop_MOV_IMM(ir, IREG_oldpc, cpu_state.oldpc);                                          \
        op_pc--;                                                                               \
        target_seg = codegen_generate_ea(ir, op_ea_seg, fetchdat, op_ssegs, &op_pc, op_32, 0); \
        codegen_check_seg_read(block, ir, target_seg);                                         \
        load_uop(ir, IREG_temp0_D, ireg_seg_base(target_seg), IREG_eaaddr);                    \
        uop_FADD(ir, IREG_ST(0), IREG_ST(0), IREG_temp0_D);                                    \
        uop_MOV_IMM(ir, IREG_tag(0), TAG_VALID);                                               \
                                                                                               \
        return op_pc + 1;                                                                      \
    }                                                                                          \
    uint32_t ropFCOM##name(codeblock_t *block, ir_data_t *ir, UNUSED(uint8_t opcode),          \
                           uint32_t fetchdat, uint32_t op_32, uint32_t op_pc)                  \
    {                                                                                          \
        x86seg *target_seg;                                                                    \
                                                                                               \
        uop_FP_ENTER(ir);                                                                      \
        uop_MOV_IMM(ir, IREG_oldpc, cpu_state.oldpc);                                          \
        op_pc--;                                                                               \
        target_seg = codegen_generate_ea(ir, op_ea_seg, fetchdat, op_ssegs, &op_pc, op_32, 0); \
        codegen_check_seg_read(block, ir, target_seg);                                         \
        load_uop(ir, IREG_temp0_D, ireg_seg_base(target_seg), IREG_eaaddr);                    \
        uop_FCOM(ir, IREG_temp1_W, IREG_ST(0), IREG_temp0_D);                                  \
        uop_AND_IMM(ir, IREG_NPXS, IREG_NPXS, ~(FPU_SW_C0 | FPU_SW_C2 | FPU_SW_C3));           \
        uop_OR(ir, IREG_NPXS, IREG_NPXS, IREG_temp1_W);                                        \
                                                                                               \
        return op_pc + 1;                                                                      \
    }                                                                                          \
    uint32_t ropFCOMP##name(codeblock_t *block, ir_data_t *ir, UNUSED(uint8_t opcode),         \
                            uint32_t fetchdat, uint32_t op_32, uint32_t op_pc)                 \
    {                                                                                          \
        x86seg *target_seg;                                                                    \
                                                                                               \
        uop_FP_ENTER(ir);                                                                      \
        uop_MOV_IMM(ir, IREG_oldpc, cpu_state.oldpc);                                          \
        op_pc--;                                                                               \
        target_seg = codegen_generate_ea(ir, op_ea_seg, fetchdat, op_ssegs, &op_pc, op_32, 0); \
        codegen_check_seg_read(block, ir, target_seg);                                         \
        load_uop(ir, IREG_temp0_D, ireg_seg_base(target_seg), IREG_eaaddr);                    \
        uop_FCOM(ir, IREG_temp1_W, IREG_ST(0), IREG_temp0_D);                                  \
        uop_AND_IMM(ir, IREG_NPXS, IREG_NPXS, ~(FPU_SW_C0 | FPU_SW_C2 | FPU_SW_C3));           \
        uop_OR(ir, IREG_NPXS, IREG_NPXS, IREG_temp1_W);                                        \
        fpu_POP(block, ir);                                                                    \
                                                                                               \
        return op_pc + 1;                                                                      \
    }                                                                                          \
    uint32_t ropFDIV##name(codeblock_t *block, ir_data_t *ir, UNUSED(uint8_t opcode),          \
                           uint32_t fetchdat, uint32_t op_32, uint32_t op_pc)                  \
    {                                                                                          \
        x86seg *target_seg;                                                                    \
                                                                                               \
        uop_FP_ENTER(ir);                                                                      \
        uop_MOV_IMM(ir, IREG_oldpc, cpu_state.oldpc);                                          \
        op_pc--;                                                                               \
        target_seg = codegen_generate_ea(ir, op_ea_seg, fetchdat, op_ssegs, &op_pc, op_32, 0); \
        codegen_check_seg_read(block, ir, target_seg);                                         \
        load_uop(ir, IREG_temp0_D, ireg_seg_base(target_seg), IREG_eaaddr);                    \
        uop_FDIV(ir, IREG_ST(0), IREG_ST(0), IREG_temp0_D);                                    \
        uop_MOV_IMM(ir, IREG_tag(0), TAG_VALID);                                               \
                                                                                               \
        return op_pc + 1;                                                                      \
    }                                                                                          \
    uint32_t ropFDIVR##name(codeblock_t *block, ir_data_t *ir, UNUSED(uint8_t opcode),         \
                            uint32_t fetchdat, uint32_t op_32, uint32_t op_pc)                 \
    {                                                                                          \
        x86seg *target_seg;                                                                    \
                                                                                               \
        uop_FP_ENTER(ir);                                                                      \
        uop_MOV_IMM(ir, IREG_oldpc, cpu_state.oldpc);                                          \
        op_pc--;                                                                               \
        target_seg = codegen_generate_ea(ir, op_ea_seg, fetchdat, op_ssegs, &op_pc, op_32, 0); \
        codegen_check_seg_read(block, ir, target_seg);                                         \
        load_uop(ir, IREG_temp0_D, ireg_seg_base(target_seg), IREG_eaaddr);                    \
        uop_FDIV(ir, IREG_ST(0), IREG_temp0_D, IREG_ST(0));                                    \
        uop_MOV_IMM(ir, IREG_tag(0), TAG_VALID);                                               \
                                                                                               \
        return op_pc + 1;                                                                      \
    }                                                                                          \
    uint32_t ropFMUL##name(codeblock_t *block, ir_data_t *ir, UNUSED(uint8_t opcode),          \
                           uint32_t fetchdat, uint32_t op_32, uint32_t op_pc)                  \
    {                                                                                          \
        x86seg *target_seg;                                                                    \
                                                                                               \
        uop_FP_ENTER(ir);                                                                      \
        uop_MOV_IMM(ir, IREG_oldpc, cpu_state.oldpc);                                          \
        op_pc--;                                                                               \
        target_seg = codegen_generate_ea(ir, op_ea_seg, fetchdat, op_ssegs, &op_pc, op_32, 0); \
        codegen_check_seg_read(block, ir, target_seg);                                         \
        load_uop(ir, IREG_temp0_D, ireg_seg_base(target_seg), IREG_eaaddr);                    \
        uop_FMUL(ir, IREG_ST(0), IREG_ST(0), IREG_temp0_D);                                    \
        uop_MOV_IMM(ir, IREG_tag(0), TAG_VALID);                                               \
                                                                                               \
        return op_pc + 1;                                                                      \
    }                                                                                          \
    uint32_t ropFSUB##name(codeblock_t *block, ir_data_t *ir, UNUSED(uint8_t opcode),          \
                           uint32_t fetchdat, uint32_t op_32, uint32_t op_pc)                  \
    {                                                                                          \
        x86seg *target_seg;                                                                    \
                                                                                               \
        uop_FP_ENTER(ir);                                                                      \
        uop_MOV_IMM(ir, IREG_oldpc, cpu_state.oldpc);                                          \
        op_pc--;                                                                               \
        target_seg = codegen_generate_ea(ir, op_ea_seg, fetchdat, op_ssegs, &op_pc, op_32, 0); \
        codegen_check_seg_read(block, ir, target_seg);                                         \
        load_uop(ir, IREG_temp0_D, ireg_seg_base(target_seg), IREG_eaaddr);                    \
        uop_FSUB(ir, IREG_ST(0), IREG_ST(0), IREG_temp0_D);                                    \
        uop_MOV_IMM(ir, IREG_tag(0), TAG_VALID);                                               \
                                                                                               \
        return op_pc + 1;                                                                      \
    }                                                                                          \
    uint32_t ropFSUBR##name(codeblock_t *block, ir_data_t *ir, UNUSED(uint8_t opcode),         \
                            uint32_t fetchdat, uint32_t op_32, uint32_t op_pc)                 \
    {                                                                                          \
        x86seg *target_seg;                                                                    \
                                                                                               \
        uop_FP_ENTER(ir);                                                                      \
        uop_MOV_IMM(ir, IREG_oldpc, cpu_state.oldpc);                                          \
        op_pc--;                                                                               \
        target_seg = codegen_generate_ea(ir, op_ea_seg, fetchdat, op_ssegs, &op_pc, op_32, 0); \
        codegen_check_seg_read(block, ir, target_seg);                                         \
        load_uop(ir, IREG_temp0_D, ireg_seg_base(target_seg), IREG_eaaddr);                    \
        uop_FSUB(ir, IREG_ST(0), IREG_temp0_D, IREG_ST(0));                                    \
        uop_MOV_IMM(ir, IREG_tag(0), TAG_VALID);                                               \
                                                                                               \
        return op_pc + 1;                                                                      \
    }

// clang-format off
ropF_arith_mem(s, uop_MEM_LOAD_SINGLE)
ropF_arith_mem(d, uop_MEM_LOAD_DOUBLE)
// clang-format on

#define ropFI_arith_mem(name, temp_reg)                                                        \
    uint32_t ropFIADD##name(codeblock_t *block, ir_data_t *ir, UNUSED(uint8_t opcode),         \
                            uint32_t fetchdat, uint32_t op_32, uint32_t op_pc)                 \
    {                                                                                          \
        x86seg *target_seg;                                                                    \
                                                                                               \
        uop_FP_ENTER(ir);                                                                      \
        uop_MOV_IMM(ir, IREG_oldpc, cpu_state.oldpc);                                          \
        op_pc--;                                                                               \
        target_seg = codegen_generate_ea(ir, op_ea_seg, fetchdat, op_ssegs, &op_pc, op_32, 0); \
        codegen_check_seg_read(block, ir, target_seg);                                         \
        uop_MEM_LOAD_REG(ir, temp_reg, ireg_seg_base(target_seg), IREG_eaaddr);                \
        uop_MOV_DOUBLE_INT(ir, IREG_temp0_D, temp_reg);                                        \
        uop_FADD(ir, IREG_ST(0), IREG_ST(0), IREG_temp0_D);                                    \
        uop_MOV_IMM(ir, IREG_tag(0), TAG_VALID);                                               \
                                                                                               \
        return op_pc + 1;                                                                      \
    }                                                                                          \
    uint32_t ropFICOM##name(codeblock_t *block, ir_data_t *ir, UNUSED(uint8_t opcode),         \
                            uint32_t fetchdat, uint32_t op_32, uint32_t op_pc)                 \
    {                                                                                          \
        x86seg *target_seg;                                                                    \
                                                                                               \
        uop_FP_ENTER(ir);                                                                      \
        uop_MOV_IMM(ir, IREG_oldpc, cpu_state.oldpc);                                          \
        op_pc--;                                                                               \
        target_seg = codegen_generate_ea(ir, op_ea_seg, fetchdat, op_ssegs, &op_pc, op_32, 0); \
        codegen_check_seg_read(block, ir, target_seg);                                         \
        uop_MEM_LOAD_REG(ir, temp_reg, ireg_seg_base(target_seg), IREG_eaaddr);                \
        uop_MOV_DOUBLE_INT(ir, IREG_temp0_D, temp_reg);                                        \
        uop_FCOM(ir, IREG_temp1_W, IREG_ST(0), IREG_temp0_D);                                  \
        uop_AND_IMM(ir, IREG_NPXS, IREG_NPXS, ~(FPU_SW_C0 | FPU_SW_C2 | FPU_SW_C3));           \
        uop_OR(ir, IREG_NPXS, IREG_NPXS, IREG_temp1_W);                                        \
                                                                                               \
        return op_pc + 1;                                                                      \
    }                                                                                          \
    uint32_t ropFICOMP##name(codeblock_t *block, ir_data_t *ir, UNUSED(uint8_t opcode),        \
                             uint32_t fetchdat, uint32_t op_32, uint32_t op_pc)                \
    {                                                                                          \
        x86seg *target_seg;                                                                    \
                                                                                               \
        uop_FP_ENTER(ir);                                                                      \
        uop_MOV_IMM(ir, IREG_oldpc, cpu_state.oldpc);                                          \
        op_pc--;                                                                               \
        target_seg = codegen_generate_ea(ir, op_ea_seg, fetchdat, op_ssegs, &op_pc, op_32, 0); \
        codegen_check_seg_read(block, ir, target_seg);                                         \
        uop_MEM_LOAD_REG(ir, temp_reg, ireg_seg_base(target_seg), IREG_eaaddr);                \
        uop_MOV_DOUBLE_INT(ir, IREG_temp0_D, temp_reg);                                        \
        uop_FCOM(ir, IREG_temp1_W, IREG_ST(0), IREG_temp0_D);                                  \
        uop_AND_IMM(ir, IREG_NPXS, IREG_NPXS, ~(FPU_SW_C0 | FPU_SW_C2 | FPU_SW_C3));           \
        uop_OR(ir, IREG_NPXS, IREG_NPXS, IREG_temp1_W);                                        \
        fpu_POP(block, ir);                                                                    \
                                                                                               \
        return op_pc + 1;                                                                      \
    }                                                                                          \
    uint32_t ropFIDIV##name(codeblock_t *block, ir_data_t *ir, UNUSED(uint8_t opcode),         \
                            uint32_t fetchdat, uint32_t op_32, uint32_t op_pc)                 \
    {                                                                                          \
        x86seg *target_seg;                                                                    \
                                                                                               \
        uop_FP_ENTER(ir);                                                                      \
        uop_MOV_IMM(ir, IREG_oldpc, cpu_state.oldpc);                                          \
        op_pc--;                                                                               \
        target_seg = codegen_generate_ea(ir, op_ea_seg, fetchdat, op_ssegs, &op_pc, op_32, 0); \
        codegen_check_seg_read(block, ir, target_seg);                                         \
        uop_MEM_LOAD_REG(ir, temp_reg, ireg_seg_base(target_seg), IREG_eaaddr);                \
        uop_MOV_DOUBLE_INT(ir, IREG_temp0_D, temp_reg);                                        \
        uop_FDIV(ir, IREG_ST(0), IREG_ST(0), IREG_temp0_D);                                    \
        uop_MOV_IMM(ir, IREG_tag(0), TAG_VALID);                                               \
                                                                                               \
        return op_pc + 1;                                                                      \
    }                                                                                          \
    uint32_t ropFIDIVR##name(codeblock_t *block, ir_data_t *ir, UNUSED(uint8_t opcode),        \
                             uint32_t fetchdat, uint32_t op_32, uint32_t op_pc)                \
    {                                                                                          \
        x86seg *target_seg;                                                                    \
                                                                                               \
        uop_FP_ENTER(ir);                                                                      \
        uop_MOV_IMM(ir, IREG_oldpc, cpu_state.oldpc);                                          \
        op_pc--;                                                                               \
        target_seg = codegen_generate_ea(ir, op_ea_seg, fetchdat, op_ssegs, &op_pc, op_32, 0); \
        codegen_check_seg_read(block, ir, target_seg);                                         \
        uop_MEM_LOAD_REG(ir, temp_reg, ireg_seg_base(target_seg), IREG_eaaddr);                \
        uop_MOV_DOUBLE_INT(ir, IREG_temp0_D, temp_reg);                                        \
        uop_FDIV(ir, IREG_ST(0), IREG_temp0_D, IREG_ST(0));                                    \
        uop_MOV_IMM(ir, IREG_tag(0), TAG_VALID);                                               \
                                                                                               \
        return op_pc + 1;                                                                      \
    }                                                                                          \
    uint32_t ropFIMUL##name(codeblock_t *block, ir_data_t *ir, UNUSED(uint8_t opcode),         \
                            uint32_t fetchdat, uint32_t op_32, uint32_t op_pc)                 \
    {                                                                                          \
        x86seg *target_seg;                                                                    \
                                                                                               \
        uop_FP_ENTER(ir);                                                                      \
        uop_MOV_IMM(ir, IREG_oldpc, cpu_state.oldpc);                                          \
        op_pc--;                                                                               \
        target_seg = codegen_generate_ea(ir, op_ea_seg, fetchdat, op_ssegs, &op_pc, op_32, 0); \
        codegen_check_seg_read(block, ir, target_seg);                                         \
        uop_MEM_LOAD_REG(ir, temp_reg, ireg_seg_base(target_seg), IREG_eaaddr);                \
        uop_MOV_DOUBLE_INT(ir, IREG_temp0_D, temp_reg);                                        \
        uop_FMUL(ir, IREG_ST(0), IREG_ST(0), IREG_temp0_D);                                    \
        uop_MOV_IMM(ir, IREG_tag(0), TAG_VALID);                                               \
                                                                                               \
        return op_pc + 1;                                                                      \
    }                                                                                          \
    uint32_t ropFISUB##name(codeblock_t *block, ir_data_t *ir, UNUSED(uint8_t opcode),         \
                            uint32_t fetchdat, uint32_t op_32, uint32_t op_pc)                 \
    {                                                                                          \
        x86seg *target_seg;                                                                    \
                                                                                               \
        uop_FP_ENTER(ir);                                                                      \
        uop_MOV_IMM(ir, IREG_oldpc, cpu_state.oldpc);                                          \
        op_pc--;                                                                               \
        target_seg = codegen_generate_ea(ir, op_ea_seg, fetchdat, op_ssegs, &op_pc, op_32, 0); \
        codegen_check_seg_read(block, ir, target_seg);                                         \
        uop_MEM_LOAD_REG(ir, temp_reg, ireg_seg_base(target_seg), IREG_eaaddr);                \
        uop_MOV_DOUBLE_INT(ir, IREG_temp0_D, temp_reg);                                        \
        uop_FSUB(ir, IREG_ST(0), IREG_ST(0), IREG_temp0_D);                                    \
        uop_MOV_IMM(ir, IREG_tag(0), TAG_VALID);                                               \
                                                                                               \
        return op_pc + 1;                                                                      \
    }                                                                                          \
    uint32_t ropFISUBR##name(codeblock_t *block, ir_data_t *ir, UNUSED(uint8_t opcode),        \
                             uint32_t fetchdat, uint32_t op_32, uint32_t op_pc)                \
    {                                                                                          \
        x86seg *target_seg;                                                                    \
                                                                                               \
        uop_FP_ENTER(ir);                                                                      \
        uop_MOV_IMM(ir, IREG_oldpc, cpu_state.oldpc);                                          \
        op_pc--;                                                                               \
        target_seg = codegen_generate_ea(ir, op_ea_seg, fetchdat, op_ssegs, &op_pc, op_32, 0); \
        codegen_check_seg_read(block, ir, target_seg);                                         \
        uop_MEM_LOAD_REG(ir, temp_reg, ireg_seg_base(target_seg), IREG_eaaddr);                \
        uop_MOV_DOUBLE_INT(ir, IREG_temp0_D, temp_reg);                                        \
        uop_FSUB(ir, IREG_ST(0), IREG_temp0_D, IREG_ST(0));                                    \
        uop_MOV_IMM(ir, IREG_tag(0), TAG_VALID);                                               \
                                                                                               \
        return op_pc + 1;                                                                      \
    }

// clang-format off
ropFI_arith_mem(l, IREG_temp0)
ropFI_arith_mem(w, IREG_temp0_W)
// clang-format on

uint32_t
ropFABS(UNUSED(codeblock_t *block), ir_data_t *ir, UNUSED(uint8_t opcode), UNUSED(uint32_t fetchdat), UNUSED(uint32_t op_32), uint32_t op_pc)
{
    uop_FP_ENTER(ir);
    uop_FABS(ir, IREG_ST(0), IREG_ST(0));
    uop_MOV_IMM(ir, IREG_tag(0), TAG_VALID);

    return op_pc;
}

uint32_t
ropFCHS(UNUSED(codeblock_t *block), ir_data_t *ir, UNUSED(uint8_t opcode), UNUSED(uint32_t fetchdat), UNUSED(uint32_t op_32), uint32_t op_pc)
{
    uop_FP_ENTER(ir);
    uop_FCHS(ir, IREG_ST(0), IREG_ST(0));
    uop_MOV_IMM(ir, IREG_tag(0), TAG_VALID);

    return op_pc;
}
uint32_t
ropFSQRT(UNUSED(codeblock_t *block), ir_data_t *ir, UNUSED(uint8_t opcode), UNUSED(uint32_t fetchdat), UNUSED(uint32_t op_32), uint32_t op_pc)
{
    uop_FP_ENTER(ir);
    uop_FSQRT(ir, IREG_ST(0), IREG_ST(0));
    uop_MOV_IMM(ir, IREG_tag(0), TAG_VALID);

    return op_pc;
}
uint32_t
ropFTST(UNUSED(codeblock_t *block), ir_data_t *ir, UNUSED(uint8_t opcode), UNUSED(uint32_t fetchdat), UNUSED(uint32_t op_32), uint32_t op_pc)
{
    uop_FP_ENTER(ir);
    uop_FTST(ir, IREG_temp0_W, IREG_ST(0));
    uop_AND_IMM(ir, IREG_NPXS, IREG_NPXS, ~(FPU_SW_C0 | FPU_SW_C2 | FPU_SW_C3));
    uop_OR(ir, IREG_NPXS, IREG_NPXS, IREG_temp0_W);

    return op_pc;
}
