/*
 *  PowerPC CPU initialization for qemu.
 * 
 *  Copyright (c) 2003-2007 Jocelyn Mayer
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

/* A lot of PowerPC definition have been included here.
 * Most of them are not usable for now but have been kept
 * inside "#if defined(TODO) ... #endif" statements to make tests easier.
 */

//#define PPC_DUMP_CPU
//#define PPC_DEBUG_SPR

struct ppc_def_t {
    const unsigned char *name;
    uint32_t pvr;
    uint32_t pvr_mask;
    uint64_t insns_flags;
    uint32_t flags;
    uint64_t msr_mask;
};

/* Generic callbacks:
 * do nothing but store/retrieve spr value
 */
static void spr_read_generic (void *opaque, int sprn)
{
    gen_op_load_spr(sprn);
}

static void spr_write_generic (void *opaque, int sprn)
{
    gen_op_store_spr(sprn);
}

/* SPR common to all PowerPC */
/* XER */
static void spr_read_xer (void *opaque, int sprn)
{
    gen_op_load_xer();
}

static void spr_write_xer (void *opaque, int sprn)
{
    gen_op_store_xer();
}

/* LR */
static void spr_read_lr (void *opaque, int sprn)
{
    gen_op_load_lr();
}

static void spr_write_lr (void *opaque, int sprn)
{
    gen_op_store_lr();
}

/* CTR */
static void spr_read_ctr (void *opaque, int sprn)
{
    gen_op_load_ctr();
}

static void spr_write_ctr (void *opaque, int sprn)
{
    gen_op_store_ctr();
}

/* User read access to SPR */
/* USPRx */
/* UMMCRx */
/* UPMCx */
/* USIA */
/* UDECR */
static void spr_read_ureg (void *opaque, int sprn)
{
    gen_op_load_spr(sprn + 0x10);
}

/* SPR common to all non-embedded PowerPC */
/* DECR */
#if !defined(CONFIG_USER_ONLY)
static void spr_read_decr (void *opaque, int sprn)
{
    gen_op_load_decr();
}

static void spr_write_decr (void *opaque, int sprn)
{
    gen_op_store_decr();
}
#endif

/* SPR common to all non-embedded PowerPC, except 601 */
/* Time base */
static void spr_read_tbl (void *opaque, int sprn)
{
    gen_op_load_tbl();
}

static void spr_read_tbu (void *opaque, int sprn)
{
    gen_op_load_tbu();
}

#if !defined(CONFIG_USER_ONLY)
static void spr_write_tbl (void *opaque, int sprn)
{
    gen_op_store_tbl();
}

static void spr_write_tbu (void *opaque, int sprn)
{
    gen_op_store_tbu();
}
#endif

#if !defined(CONFIG_USER_ONLY)
/* IBAT0U...IBAT0U */
/* IBAT0L...IBAT7L */
static void spr_read_ibat (void *opaque, int sprn)
{
    gen_op_load_ibat(sprn & 1, (sprn - SPR_IBAT0U) / 2);
}

static void spr_read_ibat_h (void *opaque, int sprn)
{
    gen_op_load_ibat(sprn & 1, (sprn - SPR_IBAT4U) / 2);
}

static void spr_write_ibatu (void *opaque, int sprn)
{
    DisasContext *ctx = opaque;

    gen_op_store_ibatu((sprn - SPR_IBAT0U) / 2);
    RET_STOP(ctx);
}

static void spr_write_ibatu_h (void *opaque, int sprn)
{
    DisasContext *ctx = opaque;

    gen_op_store_ibatu((sprn - SPR_IBAT4U) / 2);
    RET_STOP(ctx);
}

static void spr_write_ibatl (void *opaque, int sprn)
{
    DisasContext *ctx = opaque;

    gen_op_store_ibatl((sprn - SPR_IBAT0L) / 2);
    RET_STOP(ctx);
}

static void spr_write_ibatl_h (void *opaque, int sprn)
{
    DisasContext *ctx = opaque;

    gen_op_store_ibatl((sprn - SPR_IBAT4L) / 2);
    RET_STOP(ctx);
}

/* DBAT0U...DBAT7U */
/* DBAT0L...DBAT7L */
static void spr_read_dbat (void *opaque, int sprn)
{
    gen_op_load_dbat(sprn & 1, (sprn - SPR_DBAT0U) / 2);
}

static void spr_read_dbat_h (void *opaque, int sprn)
{
    gen_op_load_dbat(sprn & 1, (sprn - SPR_DBAT4U) / 2);
}

static void spr_write_dbatu (void *opaque, int sprn)
{
    DisasContext *ctx = opaque;

    gen_op_store_dbatu((sprn - SPR_DBAT0U) / 2);
    RET_STOP(ctx);
}

static void spr_write_dbatu_h (void *opaque, int sprn)
{
    DisasContext *ctx = opaque;

    gen_op_store_dbatu((sprn - SPR_DBAT4U) / 2);
    RET_STOP(ctx);
}

static void spr_write_dbatl (void *opaque, int sprn)
{
    DisasContext *ctx = opaque;

    gen_op_store_dbatl((sprn - SPR_DBAT0L) / 2);
    RET_STOP(ctx);
}

static void spr_write_dbatl_h (void *opaque, int sprn)
{
    DisasContext *ctx = opaque;

    gen_op_store_dbatl((sprn - SPR_DBAT4L) / 2);
    RET_STOP(ctx);
}

/* SDR1 */
static void spr_read_sdr1 (void *opaque, int sprn)
{
    gen_op_load_sdr1();
}

static void spr_write_sdr1 (void *opaque, int sprn)
{
    DisasContext *ctx = opaque;

    gen_op_store_sdr1();
    RET_STOP(ctx);
}

/* 64 bits PowerPC specific SPRs */
/* ASR */
#if defined(TARGET_PPC64)
static void spr_read_asr (void *opaque, int sprn)
{
    gen_op_load_asr();
}

static void spr_write_asr (void *opaque, int sprn)
{
    DisasContext *ctx = opaque;

    gen_op_store_asr();
    RET_STOP(ctx);
}
#endif
#endif /* !defined(CONFIG_USER_ONLY) */

/* PowerPC 601 specific registers */
/* RTC */
static void spr_read_601_rtcl (void *opaque, int sprn)
{
    gen_op_load_601_rtcl();
}

static void spr_read_601_rtcu (void *opaque, int sprn)
{
    gen_op_load_601_rtcu();
}

#if !defined(CONFIG_USER_ONLY)
static void spr_write_601_rtcu (void *opaque, int sprn)
{
    gen_op_store_601_rtcu();
}

static void spr_write_601_rtcl (void *opaque, int sprn)
{
    gen_op_store_601_rtcl();
}
#endif

/* Unified bats */
#if !defined(CONFIG_USER_ONLY)
static void spr_read_601_ubat (void *opaque, int sprn)
{
    gen_op_load_601_bat(sprn & 1, (sprn - SPR_IBAT0U) / 2);
}

static void spr_write_601_ubatu (void *opaque, int sprn)
{
    DisasContext *ctx = opaque;

    gen_op_store_601_batu((sprn - SPR_IBAT0U) / 2);
    RET_STOP(ctx);
}

static void spr_write_601_ubatl (void *opaque, int sprn)
{
    DisasContext *ctx = opaque;

    gen_op_store_601_batl((sprn - SPR_IBAT0L) / 2);
    RET_STOP(ctx);
}
#endif

/* PowerPC 40x specific registers */
#if !defined(CONFIG_USER_ONLY)
static void spr_read_40x_pit (void *opaque, int sprn)
{
    gen_op_load_40x_pit();
}

static void spr_write_40x_pit (void *opaque, int sprn)
{
    gen_op_store_40x_pit();
}

static void spr_write_booke_tcr (void *opaque, int sprn)
{
    gen_op_store_booke_tcr();
}

static void spr_write_booke_tsr (void *opaque, int sprn)
{
    gen_op_store_booke_tsr();
}
#endif

/* PowerPC 403 specific registers */
/* PBL1 / PBU1 / PBL2 / PBU2 */
#if !defined(CONFIG_USER_ONLY)
static void spr_read_403_pbr (void *opaque, int sprn)
{
    gen_op_load_403_pb(sprn - SPR_403_PBL1);
}

static void spr_write_403_pbr (void *opaque, int sprn)
{
    DisasContext *ctx = opaque;

    gen_op_store_403_pb(sprn - SPR_403_PBL1);
    RET_STOP(ctx);
}

static void spr_write_pir (void *opaque, int sprn)
{
    gen_op_store_pir();
}
#endif

#if defined(CONFIG_USER_ONLY)
#define spr_register(env, num, name, uea_read, uea_write,                     \
                     oea_read, oea_write, initial_value)                      \
do {                                                                          \
     _spr_register(env, num, name, uea_read, uea_write, initial_value);       \
} while (0)
static inline void _spr_register (CPUPPCState *env, int num,
                                  const unsigned char *name,
                                  void (*uea_read)(void *opaque, int sprn),
                                  void (*uea_write)(void *opaque, int sprn),
                                  target_ulong initial_value)
#else
static inline void spr_register (CPUPPCState *env, int num,
                                 const unsigned char *name,
                                 void (*uea_read)(void *opaque, int sprn),
                                 void (*uea_write)(void *opaque, int sprn),
                                 void (*oea_read)(void *opaque, int sprn),
                                 void (*oea_write)(void *opaque, int sprn),
                                 target_ulong initial_value)
#endif
{
    ppc_spr_t *spr;

    spr = &env->spr_cb[num];
    if (spr->name != NULL ||env-> spr[num] != 0x00000000 ||
#if !defined(CONFIG_USER_ONLY)
        spr->oea_read != NULL || spr->oea_write != NULL ||
#endif
        spr->uea_read != NULL || spr->uea_write != NULL) {
        printf("Error: Trying to register SPR %d (%03x) twice !\n", num, num);
        exit(1);
    }
#if defined(PPC_DEBUG_SPR)
    printf("*** register spr %d (%03x) %s val " ADDRX "\n", num, num, name,
           initial_value);
#endif
    spr->name = name;
    spr->uea_read = uea_read;
    spr->uea_write = uea_write;
#if !defined(CONFIG_USER_ONLY)
    spr->oea_read = oea_read;
    spr->oea_write = oea_write;
#endif
    env->spr[num] = initial_value;
}

/* Generic PowerPC SPRs */
static void gen_spr_generic (CPUPPCState *env)
{
    /* Integer processing */
    spr_register(env, SPR_XER, "XER",
                 &spr_read_xer, &spr_write_xer,
                 &spr_read_xer, &spr_write_xer,
                 0x00000000);
    /* Branch contol */
    spr_register(env, SPR_LR, "LR",
                 &spr_read_lr, &spr_write_lr,
                 &spr_read_lr, &spr_write_lr,
                 0x00000000);
    spr_register(env, SPR_CTR, "CTR",
                 &spr_read_ctr, &spr_write_ctr,
                 &spr_read_ctr, &spr_write_ctr,
                 0x00000000);
    /* Interrupt processing */
    spr_register(env, SPR_SRR0, "SRR0",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    spr_register(env, SPR_SRR1, "SRR1",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* Processor control */
    spr_register(env, SPR_SPRG0, "SPRG0",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    spr_register(env, SPR_SPRG1, "SPRG1",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    spr_register(env, SPR_SPRG2, "SPRG2",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    spr_register(env, SPR_SPRG3, "SPRG3",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
}

/* SPR common to all non-embedded PowerPC, including 601 */
static void gen_spr_ne_601 (CPUPPCState *env)
{
    /* Exception processing */
    spr_register(env, SPR_DSISR, "DSISR",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    spr_register(env, SPR_DAR, "DAR",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* Timer */
    spr_register(env, SPR_DECR, "DECR",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_decr, &spr_write_decr,
                 0x00000000);
    /* Memory management */
    spr_register(env, SPR_SDR1, "SDR1",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_sdr1, &spr_write_sdr1,
                 0x00000000);
}

/* BATs 0-3 */
static void gen_low_BATs (CPUPPCState *env)
{
    spr_register(env, SPR_IBAT0U, "IBAT0U",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_ibat, &spr_write_ibatu,
                 0x00000000);
    spr_register(env, SPR_IBAT0L, "IBAT0L",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_ibat, &spr_write_ibatl,
                 0x00000000);
    spr_register(env, SPR_IBAT1U, "IBAT1U",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_ibat, &spr_write_ibatu,
                 0x00000000);
    spr_register(env, SPR_IBAT1L, "IBAT1L",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_ibat, &spr_write_ibatl,
                 0x00000000);
    spr_register(env, SPR_IBAT2U, "IBAT2U",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_ibat, &spr_write_ibatu,
                 0x00000000);
    spr_register(env, SPR_IBAT2L, "IBAT2L",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_ibat, &spr_write_ibatl,
                 0x00000000);
    spr_register(env, SPR_IBAT3U, "IBAT3U",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_ibat, &spr_write_ibatu,
                 0x00000000);
    spr_register(env, SPR_IBAT3L, "IBAT3L",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_ibat, &spr_write_ibatl,
                 0x00000000);
    spr_register(env, SPR_DBAT0U, "DBAT0U",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_dbat, &spr_write_dbatu,
                 0x00000000);
    spr_register(env, SPR_DBAT0L, "DBAT0L",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_dbat, &spr_write_dbatl,
                 0x00000000);
    spr_register(env, SPR_DBAT1U, "DBAT1U",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_dbat, &spr_write_dbatu,
                 0x00000000);
    spr_register(env, SPR_DBAT1L, "DBAT1L",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_dbat, &spr_write_dbatl,
                 0x00000000);
    spr_register(env, SPR_DBAT2U, "DBAT2U",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_dbat, &spr_write_dbatu,
                 0x00000000);
    spr_register(env, SPR_DBAT2L, "DBAT2L",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_dbat, &spr_write_dbatl,
                 0x00000000);
    spr_register(env, SPR_DBAT3U, "DBAT3U",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_dbat, &spr_write_dbatu,
                 0x00000000);
    spr_register(env, SPR_DBAT3L, "DBAT3L",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_dbat, &spr_write_dbatl,
                 0x00000000);
    env->nb_BATs = 4;
}

/* BATs 4-7 */
static void gen_high_BATs (CPUPPCState *env)
{
    spr_register(env, SPR_IBAT4U, "IBAT4U",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_ibat_h, &spr_write_ibatu_h,
                 0x00000000);
    spr_register(env, SPR_IBAT4L, "IBAT4L",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_ibat_h, &spr_write_ibatl_h,
                 0x00000000);
    spr_register(env, SPR_IBAT5U, "IBAT5U",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_ibat_h, &spr_write_ibatu_h,
                 0x00000000);
    spr_register(env, SPR_IBAT5L, "IBAT5L",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_ibat_h, &spr_write_ibatl_h,
                 0x00000000);
    spr_register(env, SPR_IBAT6U, "IBAT6U",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_ibat_h, &spr_write_ibatu_h,
                 0x00000000);
    spr_register(env, SPR_IBAT6L, "IBAT6L",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_ibat_h, &spr_write_ibatl_h,
                 0x00000000);
    spr_register(env, SPR_IBAT7U, "IBAT7U",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_ibat_h, &spr_write_ibatu_h,
                 0x00000000);
    spr_register(env, SPR_IBAT7L, "IBAT7L",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_ibat_h, &spr_write_ibatl_h,
                 0x00000000);
    spr_register(env, SPR_DBAT4U, "DBAT4U",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_dbat_h, &spr_write_dbatu_h,
                 0x00000000);
    spr_register(env, SPR_DBAT4L, "DBAT4L",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_dbat_h, &spr_write_dbatl_h,
                 0x00000000);
    spr_register(env, SPR_DBAT5U, "DBAT5U",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_dbat_h, &spr_write_dbatu_h,
                 0x00000000);
    spr_register(env, SPR_DBAT5L, "DBAT5L",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_dbat_h, &spr_write_dbatl_h,
                 0x00000000);
    spr_register(env, SPR_DBAT6U, "DBAT6U",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_dbat_h, &spr_write_dbatu_h,
                 0x00000000);
    spr_register(env, SPR_DBAT6L, "DBAT6L",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_dbat_h, &spr_write_dbatl_h,
                 0x00000000);
    spr_register(env, SPR_DBAT7U, "DBAT7U",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_dbat_h, &spr_write_dbatu_h,
                 0x00000000);
    spr_register(env, SPR_DBAT7L, "DBAT7L",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_dbat_h, &spr_write_dbatl_h,
                 0x00000000);
    env->nb_BATs = 8;
}

/* Generic PowerPC time base */
static void gen_tbl (CPUPPCState *env)
{
    spr_register(env, SPR_VTBL,  "TBL",
                 &spr_read_tbl, SPR_NOACCESS,
                 &spr_read_tbl, SPR_NOACCESS,
                 0x00000000);
    spr_register(env, SPR_TBL,   "TBL",
                 SPR_NOACCESS, SPR_NOACCESS,
                 SPR_NOACCESS, &spr_write_tbl,
                 0x00000000);
    spr_register(env, SPR_VTBU,  "TBU",
                 &spr_read_tbu, SPR_NOACCESS,
                 &spr_read_tbu, SPR_NOACCESS,
                 0x00000000);
    spr_register(env, SPR_TBU,   "TBU",
                 SPR_NOACCESS, SPR_NOACCESS,
                 SPR_NOACCESS, &spr_write_tbu,
                 0x00000000);
}

/* Softare table search registers */
static void gen_6xx_7xx_soft_tlb (CPUPPCState *env, int nb_tlbs, int nb_ways)
{
    env->nb_tlb = nb_tlbs;
    env->nb_ways = nb_ways;
    env->id_tlbs = 1;
    spr_register(env, SPR_DMISS, "DMISS",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, SPR_NOACCESS,
                 0x00000000);
    spr_register(env, SPR_DCMP, "DCMP",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, SPR_NOACCESS,
                 0x00000000);
    spr_register(env, SPR_HASH1, "HASH1",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, SPR_NOACCESS,
                 0x00000000);
    spr_register(env, SPR_HASH2, "HASH2",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, SPR_NOACCESS,
                 0x00000000);
    spr_register(env, SPR_IMISS, "IMISS",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, SPR_NOACCESS,
                 0x00000000);
    spr_register(env, SPR_ICMP, "ICMP",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, SPR_NOACCESS,
                 0x00000000);
    spr_register(env, SPR_RPA, "RPA",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
}

/* SPR common to MPC755 and G2 */
static void gen_spr_G2_755 (CPUPPCState *env)
{
    /* SGPRs */
    spr_register(env, SPR_SPRG4, "SPRG4",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    spr_register(env, SPR_SPRG5, "SPRG5",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    spr_register(env, SPR_SPRG6, "SPRG6",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    spr_register(env, SPR_SPRG7, "SPRG7",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* External access control */
    /* XXX : not implemented */
    spr_register(env, SPR_EAR, "EAR",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
}

/* SPR common to all 7xx PowerPC implementations */
static void gen_spr_7xx (CPUPPCState *env)
{
    /* Breakpoints */
    /* XXX : not implemented */
    spr_register(env, SPR_DABR, "DABR",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* XXX : not implemented */
    spr_register(env, SPR_IABR, "IABR",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* Cache management */
    /* XXX : not implemented */
    spr_register(env, SPR_ICTC, "ICTC",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* XXX : not implemented */
    spr_register(env, SPR_L2CR, "L2CR",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* Performance monitors */
    /* XXX : not implemented */
    spr_register(env, SPR_MMCR0, "MMCR0",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* XXX : not implemented */
    spr_register(env, SPR_MMCR1, "MMCR1",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* XXX : not implemented */
    spr_register(env, SPR_PMC1, "PMC1",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* XXX : not implemented */
    spr_register(env, SPR_PMC2, "PMC2",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* XXX : not implemented */
    spr_register(env, SPR_PMC3, "PMC3",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* XXX : not implemented */
    spr_register(env, SPR_PMC4, "PMC4",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* XXX : not implemented */
    spr_register(env, SPR_SIA, "SIA",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, SPR_NOACCESS,
                 0x00000000);
    spr_register(env, SPR_UMMCR0, "UMMCR0",
                 &spr_read_ureg, SPR_NOACCESS,
                 &spr_read_ureg, SPR_NOACCESS,
                 0x00000000);
    spr_register(env, SPR_UMMCR1, "UMMCR1",
                 &spr_read_ureg, SPR_NOACCESS,
                 &spr_read_ureg, SPR_NOACCESS,
                 0x00000000);
    spr_register(env, SPR_UPMC1, "UPMC1",
                 &spr_read_ureg, SPR_NOACCESS,
                 &spr_read_ureg, SPR_NOACCESS,
                 0x00000000);
    spr_register(env, SPR_UPMC2, "UPMC2",
                 &spr_read_ureg, SPR_NOACCESS,
                 &spr_read_ureg, SPR_NOACCESS,
                 0x00000000);
    spr_register(env, SPR_UPMC3, "UPMC3",
                 &spr_read_ureg, SPR_NOACCESS,
                 &spr_read_ureg, SPR_NOACCESS,
                 0x00000000);
    spr_register(env, SPR_UPMC4, "UPMC4",
                 &spr_read_ureg, SPR_NOACCESS,
                 &spr_read_ureg, SPR_NOACCESS,
                 0x00000000);
    spr_register(env, SPR_USIA, "USIA",
                 &spr_read_ureg, SPR_NOACCESS,
                 &spr_read_ureg, SPR_NOACCESS,
                 0x00000000);
    /* Thermal management */
    /* XXX : not implemented */
    spr_register(env, SPR_THRM1, "THRM1",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* XXX : not implemented */
    spr_register(env, SPR_THRM2, "THRM2",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* XXX : not implemented */
    spr_register(env, SPR_THRM3, "THRM3",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* External access control */
    /* XXX : not implemented */
    spr_register(env, SPR_EAR, "EAR",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
}

/* SPR specific to PowerPC 604 implementation */
static void gen_spr_604 (CPUPPCState *env)
{
    /* Processor identification */
    spr_register(env, SPR_PIR, "PIR",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_pir,
                 0x00000000);
    /* Breakpoints */
    /* XXX : not implemented */
    spr_register(env, SPR_IABR, "IABR",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* XXX : not implemented */
    spr_register(env, SPR_DABR, "DABR",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* Performance counters */
    /* XXX : not implemented */
    spr_register(env, SPR_MMCR0, "MMCR0",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* XXX : not implemented */
    spr_register(env, SPR_MMCR1, "MMCR1",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* XXX : not implemented */
    spr_register(env, SPR_PMC1, "PMC1",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* XXX : not implemented */
    spr_register(env, SPR_PMC2, "PMC2",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* XXX : not implemented */
    spr_register(env, SPR_PMC3, "PMC3",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* XXX : not implemented */
    spr_register(env, SPR_PMC4, "PMC4",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* XXX : not implemented */
    spr_register(env, SPR_SIA, "SIA",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, SPR_NOACCESS,
                 0x00000000);
    /* XXX : not implemented */
    spr_register(env, SPR_SDA, "SDA",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, SPR_NOACCESS,
                 0x00000000);
    /* External access control */
    /* XXX : not implemented */
    spr_register(env, SPR_EAR, "EAR",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
}

/* SPR specific to PowerPC 603 implementation */
static void gen_spr_603 (CPUPPCState *env)
{
    /* External access control */
    /* XXX : not implemented */
    spr_register(env, SPR_EAR, "EAR",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
}

/* SPR specific to PowerPC G2 implementation */
static void gen_spr_G2 (CPUPPCState *env)
{
    /* Memory base address */
    /* MBAR */
    spr_register(env, SPR_MBAR, "MBAR",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* System version register */
    /* SVR */
    spr_register(env, SPR_SVR, "SVR",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, SPR_NOACCESS,
                 0x00000000);
    /* Exception processing */
    spr_register(env, SPR_BOOKE_CSRR0, "CSRR0",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    spr_register(env, SPR_BOOKE_CSRR1, "CSRR1",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* Breakpoints */
    /* XXX : not implemented */
    spr_register(env, SPR_DABR, "DABR",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* XXX : not implemented */
    spr_register(env, SPR_DABR2, "DABR2",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* XXX : not implemented */
    spr_register(env, SPR_IABR, "IABR",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* XXX : not implemented */
    spr_register(env, SPR_IABR2, "IABR2",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* XXX : not implemented */
    spr_register(env, SPR_IBCR, "IBCR",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* XXX : not implemented */
    spr_register(env, SPR_DBCR, "DBCR",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
}

/* SPR specific to PowerPC 602 implementation */
static void gen_spr_602 (CPUPPCState *env)
{
    /* ESA registers */
    /* XXX : not implemented */
    spr_register(env, SPR_SER, "SER",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* XXX : not implemented */
    spr_register(env, SPR_SEBR, "SEBR",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* XXX : not implemented */
    spr_register(env, SPR_ESASR, "ESASR",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* Floating point status */
    /* XXX : not implemented */
    spr_register(env, SPR_SP, "SP",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* XXX : not implemented */
    spr_register(env, SPR_LT, "LT",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* Watchdog timer */
    /* XXX : not implemented */
    spr_register(env, SPR_TCR, "TCR",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* Interrupt base */
    spr_register(env, SPR_IBR, "IBR",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
}

/* SPR specific to PowerPC 601 implementation */
static void gen_spr_601 (CPUPPCState *env)
{
    /* Multiplication/division register */
    /* MQ */
    spr_register(env, SPR_MQ, "MQ",
                 &spr_read_generic, &spr_write_generic,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* RTC registers */
    spr_register(env, SPR_601_RTCU, "RTCU",
                 SPR_NOACCESS, SPR_NOACCESS,
                 SPR_NOACCESS, &spr_write_601_rtcu,
                 0x00000000);
    spr_register(env, SPR_601_VRTCU, "RTCU",
                 &spr_read_601_rtcu, SPR_NOACCESS,
                 &spr_read_601_rtcu, SPR_NOACCESS,
                 0x00000000);
    spr_register(env, SPR_601_RTCL, "RTCL",
                 SPR_NOACCESS, SPR_NOACCESS,
                 SPR_NOACCESS, &spr_write_601_rtcl,
                 0x00000000);
    spr_register(env, SPR_601_VRTCL, "RTCL",
                 &spr_read_601_rtcl, SPR_NOACCESS,
                 &spr_read_601_rtcl, SPR_NOACCESS,
                 0x00000000);
    /* Timer */
#if 0 /* ? */
    spr_register(env, SPR_601_UDECR, "UDECR",
                 &spr_read_decr, SPR_NOACCESS,
                 &spr_read_decr, SPR_NOACCESS,
                 0x00000000);
#endif
    /* External access control */
    /* XXX : not implemented */
    spr_register(env, SPR_EAR, "EAR",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* Memory management */
    spr_register(env, SPR_IBAT0U, "IBAT0U",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_601_ubat, &spr_write_601_ubatu,
                 0x00000000);
    spr_register(env, SPR_IBAT0L, "IBAT0L",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_601_ubat, &spr_write_601_ubatl,
                 0x00000000);
    spr_register(env, SPR_IBAT1U, "IBAT1U",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_601_ubat, &spr_write_601_ubatu,
                 0x00000000);
    spr_register(env, SPR_IBAT1L, "IBAT1L",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_601_ubat, &spr_write_601_ubatl,
                 0x00000000);
    spr_register(env, SPR_IBAT2U, "IBAT2U",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_601_ubat, &spr_write_601_ubatu,
                 0x00000000);
    spr_register(env, SPR_IBAT2L, "IBAT2L",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_601_ubat, &spr_write_601_ubatl,
                 0x00000000);
    spr_register(env, SPR_IBAT3U, "IBAT3U",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_601_ubat, &spr_write_601_ubatu,
                 0x00000000);
    spr_register(env, SPR_IBAT3L, "IBAT3L",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_601_ubat, &spr_write_601_ubatl,
                 0x00000000);
}

/* PowerPC BookE SPR */
static void gen_spr_BookE (CPUPPCState *env)
{
    /* Processor identification */
    spr_register(env, SPR_BOOKE_PIR, "PIR",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_pir,
                 0x00000000);
    /* Interrupt processing */
    spr_register(env, SPR_BOOKE_CSRR0, "CSRR0",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    spr_register(env, SPR_BOOKE_CSRR1, "CSRR1",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    spr_register(env, SPR_BOOKE_DSRR0, "DSRR0",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    spr_register(env, SPR_BOOKE_DSRR1, "DSRR1",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    spr_register(env, SPR_BOOKE_MCSRR0, "MCSRR0",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    spr_register(env, SPR_BOOKE_MCSRR1, "MCSRR1",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* Debug */
    /* XXX : not implemented */
    spr_register(env, SPR_BOOKE_IAC1, "IAC1",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* XXX : not implemented */
    spr_register(env, SPR_BOOKE_IAC2, "IAC2",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* XXX : not implemented */
    spr_register(env, SPR_BOOKE_IAC3, "IAC3",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* XXX : not implemented */
    spr_register(env, SPR_BOOKE_IAC4, "IAC4",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* XXX : not implemented */
    spr_register(env, SPR_BOOKE_DAC1, "DAC1",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* XXX : not implemented */
    spr_register(env, SPR_BOOKE_DAC2, "DAC2",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* XXX : not implemented */
    spr_register(env, SPR_BOOKE_DVC1, "DVC1",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* XXX : not implemented */
    spr_register(env, SPR_BOOKE_DVC2, "DVC2",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* XXX : not implemented */
    spr_register(env, SPR_BOOKE_DBCR0, "DBCR0",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* XXX : not implemented */
    spr_register(env, SPR_BOOKE_DBCR1, "DBCR1",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* XXX : not implemented */
    spr_register(env, SPR_BOOKE_DBCR2, "DBCR2",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* XXX : not implemented */
    spr_register(env, SPR_BOOKE_DBSR, "DBSR",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    spr_register(env, SPR_BOOKE_DEAR, "DEAR",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    spr_register(env, SPR_BOOKE_ESR, "ESR",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    spr_register(env, SPR_BOOKE_IVPR, "IVPR",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* Exception vectors */
    spr_register(env, SPR_BOOKE_IVPR, "IVPR",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    spr_register(env, SPR_BOOKE_IVOR0, "IVOR0",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    spr_register(env, SPR_BOOKE_IVOR1, "IVOR1",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    spr_register(env, SPR_BOOKE_IVOR2, "IVOR2",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    spr_register(env, SPR_BOOKE_IVOR3, "IVOR3",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    spr_register(env, SPR_BOOKE_IVOR4, "IVOR4",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    spr_register(env, SPR_BOOKE_IVOR5, "IVOR5",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    spr_register(env, SPR_BOOKE_IVOR6, "IVOR6",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    spr_register(env, SPR_BOOKE_IVOR7, "IVOR7",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    spr_register(env, SPR_BOOKE_IVOR8, "IVOR8",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    spr_register(env, SPR_BOOKE_IVOR9, "IVOR9",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    spr_register(env, SPR_BOOKE_IVOR10, "IVOR10",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    spr_register(env, SPR_BOOKE_IVOR11, "IVOR11",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    spr_register(env, SPR_BOOKE_IVOR12, "IVOR12",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    spr_register(env, SPR_BOOKE_IVOR13, "IVOR13",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    spr_register(env, SPR_BOOKE_IVOR14, "IVOR14",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    spr_register(env, SPR_BOOKE_IVOR15, "IVOR15",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    spr_register(env, SPR_BOOKE_IVOR32, "IVOR32",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    spr_register(env, SPR_BOOKE_IVOR33, "IVOR33",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    spr_register(env, SPR_BOOKE_IVOR34, "IVOR34",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    spr_register(env, SPR_BOOKE_IVOR35, "IVOR35",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    spr_register(env, SPR_BOOKE_IVOR36, "IVOR36",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    spr_register(env, SPR_BOOKE_IVOR37, "IVOR37",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    spr_register(env, SPR_BOOKE_PID, "PID",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    spr_register(env, SPR_BOOKE_TCR, "TCR",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_booke_tcr,
                 0x00000000);
    spr_register(env, SPR_BOOKE_TSR, "TSR",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_booke_tsr,
                 0x00000000);
    /* Timer */
    spr_register(env, SPR_DECR, "DECR",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_decr, &spr_write_decr,
                 0x00000000);
    spr_register(env, SPR_BOOKE_DECAR, "DECAR",
                 SPR_NOACCESS, SPR_NOACCESS,
                 SPR_NOACCESS, &spr_write_generic,
                 0x00000000);
    /* SPRGs */
    spr_register(env, SPR_USPRG0, "USPRG0",
                 &spr_read_generic, &spr_write_generic,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    spr_register(env, SPR_SPRG4, "SPRG4",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    spr_register(env, SPR_USPRG4, "USPRG4",
                 &spr_read_ureg, SPR_NOACCESS,
                 &spr_read_ureg, SPR_NOACCESS,
                 0x00000000);
    spr_register(env, SPR_SPRG5, "SPRG5",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    spr_register(env, SPR_USPRG5, "USPRG5",
                 &spr_read_ureg, SPR_NOACCESS,
                 &spr_read_ureg, SPR_NOACCESS,
                 0x00000000);
    spr_register(env, SPR_SPRG6, "SPRG6",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    spr_register(env, SPR_USPRG6, "USPRG6",
                 &spr_read_ureg, SPR_NOACCESS,
                 &spr_read_ureg, SPR_NOACCESS,
                 0x00000000);
    spr_register(env, SPR_SPRG7, "SPRG7",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    spr_register(env, SPR_USPRG7, "USPRG7",
                 &spr_read_ureg, SPR_NOACCESS,
                 &spr_read_ureg, SPR_NOACCESS,
                 0x00000000);
}

/* FSL storage control registers */
static void gen_spr_BookE_FSL (CPUPPCState *env)
{
    /* TLB assist registers */
    spr_register(env, SPR_BOOKE_MAS0, "MAS0",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    spr_register(env, SPR_BOOKE_MAS1, "MAS2",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    spr_register(env, SPR_BOOKE_MAS2, "MAS3",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    spr_register(env, SPR_BOOKE_MAS3, "MAS4",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    spr_register(env, SPR_BOOKE_MAS4, "MAS5",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    spr_register(env, SPR_BOOKE_MAS6, "MAS6",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    spr_register(env, SPR_BOOKE_MAS7, "MAS7",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    if (env->nb_pids > 1) {
        spr_register(env, SPR_BOOKE_PID1, "PID1",
                     SPR_NOACCESS, SPR_NOACCESS,
                     &spr_read_generic, &spr_write_generic,
                     0x00000000);
    }
    if (env->nb_pids > 2) {
        spr_register(env, SPR_BOOKE_PID2, "PID2",
                     SPR_NOACCESS, SPR_NOACCESS,
                     &spr_read_generic, &spr_write_generic,
                     0x00000000);
    }
    spr_register(env, SPR_BOOKE_MMUCFG, "MMUCFG",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, SPR_NOACCESS,
                 0x00000000); /* TOFIX */
    spr_register(env, SPR_BOOKE_MMUCSR0, "MMUCSR0",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000); /* TOFIX */
    switch (env->nb_ways) {
    case 4:
        spr_register(env, SPR_BOOKE_TLB3CFG, "TLB3CFG",
                     SPR_NOACCESS, SPR_NOACCESS,
                     &spr_read_generic, SPR_NOACCESS,
                     0x00000000); /* TOFIX */
        /* Fallthru */
    case 3:
        spr_register(env, SPR_BOOKE_TLB2CFG, "TLB2CFG",
                     SPR_NOACCESS, SPR_NOACCESS,
                     &spr_read_generic, SPR_NOACCESS,
                     0x00000000); /* TOFIX */
        /* Fallthru */
    case 2:
        spr_register(env, SPR_BOOKE_TLB1CFG, "TLB1CFG",
                     SPR_NOACCESS, SPR_NOACCESS,
                     &spr_read_generic, SPR_NOACCESS,
                     0x00000000); /* TOFIX */
        /* Fallthru */
    case 1:
        spr_register(env, SPR_BOOKE_TLB0CFG, "TLB0CFG",
                     SPR_NOACCESS, SPR_NOACCESS,
                     &spr_read_generic, SPR_NOACCESS,
                     0x00000000); /* TOFIX */
        /* Fallthru */
    case 0:
    default:
        break;
    }
}

/* SPR specific to PowerPC 440 implementation */
static void gen_spr_440 (CPUPPCState *env)
{
    /* Cache control */
    /* XXX : not implemented */
    spr_register(env, SPR_440_DNV0, "DNV0",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* XXX : not implemented */
    spr_register(env, SPR_440_DNV1, "DNV1",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* XXX : not implemented */
    spr_register(env, SPR_440_DNV2, "DNV2",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* XXX : not implemented */
    spr_register(env, SPR_440_DNV3, "DNV3",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* XXX : not implemented */
    spr_register(env, SPR_440_DVT0, "DVT0",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* XXX : not implemented */
    spr_register(env, SPR_440_DVT1, "DVT1",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* XXX : not implemented */
    spr_register(env, SPR_440_DVT2, "DVT2",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* XXX : not implemented */
    spr_register(env, SPR_440_DVT3, "DVT3",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* XXX : not implemented */
    spr_register(env, SPR_440_DVLIM, "DVLIM",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* XXX : not implemented */
    spr_register(env, SPR_440_INV0, "INV0",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* XXX : not implemented */
    spr_register(env, SPR_440_INV1, "INV1",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* XXX : not implemented */
    spr_register(env, SPR_440_INV2, "INV2",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* XXX : not implemented */
    spr_register(env, SPR_440_INV3, "INV3",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* XXX : not implemented */
    spr_register(env, SPR_440_IVT0, "IVT0",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* XXX : not implemented */
    spr_register(env, SPR_440_IVT1, "IVT1",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* XXX : not implemented */
    spr_register(env, SPR_440_IVT2, "IVT2",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* XXX : not implemented */
    spr_register(env, SPR_440_IVT3, "IVT3",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* XXX : not implemented */
    spr_register(env, SPR_440_IVLIM, "IVLIM",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* Cache debug */
    /* XXX : not implemented */
    spr_register(env, SPR_BOOKE_DCBTRH, "DCBTRH",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, SPR_NOACCESS,
                 0x00000000);
    /* XXX : not implemented */
    spr_register(env, SPR_BOOKE_DCBTRL, "DCBTRL",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, SPR_NOACCESS,
                 0x00000000);
    /* XXX : not implemented */
    spr_register(env, SPR_BOOKE_ICBDR, "ICBDR",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, SPR_NOACCESS,
                 0x00000000);
    /* XXX : not implemented */
    spr_register(env, SPR_BOOKE_ICBTRH, "ICBTRH",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, SPR_NOACCESS,
                 0x00000000);
    /* XXX : not implemented */
    spr_register(env, SPR_BOOKE_ICBTRL, "ICBTRL",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, SPR_NOACCESS,
                 0x00000000);
    /* XXX : not implemented */
    spr_register(env, SPR_440_DBDR, "DBDR",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* Processor control */
    spr_register(env, SPR_4xx_CCR0, "CCR0",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    spr_register(env, SPR_440_RSTCFG, "RSTCFG",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, SPR_NOACCESS,
                 0x00000000);
    /* Storage control */
    spr_register(env, SPR_440_MMUCR, "MMUCR",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
}

/* SPR shared between PowerPC 40x implementations */
static void gen_spr_40x (CPUPPCState *env)
{
    /* Cache */
    /* XXX : not implemented */
    spr_register(env, SPR_40x_DCCR, "DCCR",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* XXX : not implemented */
    spr_register(env, SPR_40x_DCWR, "DCWR",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* XXX : not implemented */
    spr_register(env, SPR_40x_ICCR, "ICCR",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* XXX : not implemented */
    spr_register(env, SPR_BOOKE_ICBDR, "ICBDR",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, SPR_NOACCESS,
                 0x00000000);
    /* Bus access control */
    spr_register(env, SPR_40x_SGR, "SGR",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0xFFFFFFFF);
    spr_register(env, SPR_40x_ZPR, "ZPR",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* MMU */
    spr_register(env, SPR_40x_PID, "PID",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* Exception */
    spr_register(env, SPR_40x_DEAR, "DEAR",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    spr_register(env, SPR_40x_ESR, "ESR",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    spr_register(env, SPR_40x_EVPR, "EVPR",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    spr_register(env, SPR_40x_SRR2, "SRR2",
                 &spr_read_generic, &spr_write_generic,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    spr_register(env, SPR_40x_SRR3, "SRR3",
                 &spr_read_generic, &spr_write_generic,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* Timers */
    spr_register(env, SPR_40x_PIT, "PIT",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_40x_pit, &spr_write_40x_pit,
                 0x00000000);
    spr_register(env, SPR_40x_TCR, "TCR",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_booke_tcr,
                 0x00000000);
    spr_register(env, SPR_40x_TSR, "TSR",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_booke_tsr,
                 0x00000000);
    /* Debug interface */
    /* XXX : not implemented */
    spr_register(env, SPR_40x_DAC1, "DAC1",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    spr_register(env, SPR_40x_DAC2, "DAC2",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* XXX : not implemented */
    spr_register(env, SPR_40x_DBCR0, "DBCR0",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* XXX : not implemented */
    spr_register(env, SPR_40x_DBSR, "DBSR",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 /* Last reset was system reset (system boot */
                 0x00000300);
    /* XXX : not implemented */
    spr_register(env, SPR_40x_IAC1, "IAC1",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    spr_register(env, SPR_40x_IAC2, "IAC2",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
}

/* SPR specific to PowerPC 405 implementation */
static void gen_spr_405 (CPUPPCState *env)
{
    spr_register(env, SPR_4xx_CCR0, "CCR0",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00700000);
    /* Debug */
    /* XXX : not implemented */
    spr_register(env, SPR_405_DBCR1, "DBCR1",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* XXX : not implemented */
    spr_register(env, SPR_405_DVC1, "DVC1",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* XXX : not implemented */
    spr_register(env, SPR_405_DVC2, "DVC2",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* XXX : not implemented */
    spr_register(env, SPR_405_IAC3, "IAC3",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* XXX : not implemented */
    spr_register(env, SPR_405_IAC4, "IAC4",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* Storage control */
    /* XXX : not implemented */
    spr_register(env, SPR_405_SLER, "SLER",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* XXX : not implemented */
    spr_register(env, SPR_405_SU0R, "SU0R",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* SPRG */
    spr_register(env, SPR_USPRG0, "USPRG0",
                 &spr_read_ureg, SPR_NOACCESS,
                 &spr_read_ureg, SPR_NOACCESS,
                 0x00000000);
    spr_register(env, SPR_SPRG4, "SPRG4",
                 SPR_NOACCESS, SPR_NOACCESS,
                 SPR_NOACCESS, &spr_write_generic,
                 0x00000000);
    spr_register(env, SPR_USPRG4, "USPRG4",
                 &spr_read_ureg, SPR_NOACCESS,
                 &spr_read_ureg, SPR_NOACCESS,
                 0x00000000);
    spr_register(env, SPR_SPRG5, "SPRG5",
                 SPR_NOACCESS, SPR_NOACCESS,
                 SPR_NOACCESS, &spr_write_generic,
                 0x00000000);
    spr_register(env, SPR_USPRG5, "USPRG5",
                 &spr_read_ureg, SPR_NOACCESS,
                 &spr_read_ureg, SPR_NOACCESS,
                 0x00000000);
    spr_register(env, SPR_SPRG6, "SPRG6",
                 SPR_NOACCESS, SPR_NOACCESS,
                 SPR_NOACCESS, &spr_write_generic,
                 0x00000000);
    spr_register(env, SPR_USPRG6, "USPRG6",
                 &spr_read_ureg, SPR_NOACCESS,
                 &spr_read_ureg, SPR_NOACCESS,
                 0x00000000);
    spr_register(env, SPR_SPRG7, "SPRG7",
                 SPR_NOACCESS, SPR_NOACCESS,
                 SPR_NOACCESS, &spr_write_generic,
                 0x00000000);
    spr_register(env, SPR_USPRG7, "USPRG7",
                 &spr_read_ureg, SPR_NOACCESS,
                 &spr_read_ureg, SPR_NOACCESS,
                 0x00000000);
    /* Debug */
    /* XXX : not implemented */
    spr_register(env, SPR_40x_DAC2, "DAC2",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* XXX : not implemented */
    spr_register(env, SPR_40x_IAC2, "IAC2",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
}

/* SPR shared between PowerPC 401 & 403 implementations */
static void gen_spr_401_403 (CPUPPCState *env)
{
    /* Time base */
    spr_register(env, SPR_403_VTBL,  "TBL",
                 &spr_read_tbl, SPR_NOACCESS,
                 &spr_read_tbl, SPR_NOACCESS,
                 0x00000000);
    spr_register(env, SPR_403_TBL,   "TBL",
                 SPR_NOACCESS, SPR_NOACCESS,
                 SPR_NOACCESS, &spr_write_tbl,
                 0x00000000);
    spr_register(env, SPR_403_VTBU,  "TBU",
                 &spr_read_tbu, SPR_NOACCESS,
                 &spr_read_tbu, SPR_NOACCESS,
                 0x00000000);
    spr_register(env, SPR_403_TBU,   "TBU",
                 SPR_NOACCESS, SPR_NOACCESS,
                 SPR_NOACCESS, &spr_write_tbu,
                 0x00000000);
    /* Debug */
    /* XXX: not implemented */
    spr_register(env, SPR_403_CDBCR, "CDBCR",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
}

/* SPR specific to PowerPC 403 implementation */
static void gen_spr_403 (CPUPPCState *env)
{
    /* MMU */
    spr_register(env, SPR_403_PBL1,  "PBL1",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_403_pbr, &spr_write_403_pbr,
                 0x00000000);
    spr_register(env, SPR_403_PBU1,  "PBU1",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_403_pbr, &spr_write_403_pbr,
                 0x00000000);
    spr_register(env, SPR_403_PBL2,  "PBL2",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_403_pbr, &spr_write_403_pbr,
                 0x00000000);
    spr_register(env, SPR_403_PBU2,  "PBU2",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_403_pbr, &spr_write_403_pbr,
                 0x00000000);
    /* Debug */
    /* XXX : not implemented */
    spr_register(env, SPR_40x_DAC2, "DAC2",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
    /* XXX : not implemented */
    spr_register(env, SPR_40x_IAC2, "IAC2",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
}

/* SPR specific to PowerPC compression coprocessor extension */
#if defined (TODO)
static void gen_spr_compress (CPUPPCState *env)
{
    spr_register(env, SPR_401_SKR, "SKR",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, &spr_write_generic,
                 0x00000000);
}
#endif

// XXX: TODO (64 bits PowerPC SPRs)
/*
 * ASR => SPR 280 (64 bits)
 * FPECR => SPR 1022 (?)
 * VRSAVE => SPR 256 (Altivec)
 * SCOMC => SPR 276 (64 bits ?)
 * SCOMD => SPR 277 (64 bits ?)
 * HSPRG0 => SPR 304 (hypervisor)
 * HSPRG1 => SPR 305 (hypervisor)
 * HDEC => SPR 310 (hypervisor)
 * HIOR => SPR 311 (hypervisor)
 * RMOR => SPR 312 (970)
 * HRMOR => SPR 313 (hypervisor)
 * HSRR0 => SPR 314 (hypervisor)
 * HSRR1 => SPR 315 (hypervisor)
 * LPCR => SPR 316 (970)
 * LPIDR => SPR 317 (970)
 * ... and more (thermal management, performance counters, ...)
 */

static void init_ppc_proc (CPUPPCState *env, ppc_def_t *def)
{
    env->reserve = -1;
    /* Default MMU definitions */
    env->nb_BATs = -1;
    env->nb_tlb = 0;
    env->nb_ways = 0;
    /* XXX: missing:
     * 32 bits PowerPC:
     * - MPC5xx(x)
     * - MPC8xx(x)
     * - RCPU (same as MPC5xx ?)
     */
    spr_register(env, SPR_PVR, "PVR",
                 SPR_NOACCESS, SPR_NOACCESS,
                 &spr_read_generic, SPR_NOACCESS,
                 def->pvr);
    printf("%s: PVR %08x mask %08x => %08x\n", __func__,
           def->pvr, def->pvr_mask, def->pvr & def->pvr_mask);
    switch (def->pvr & def->pvr_mask) {
    /* Embedded PowerPC from IBM                           */
    case CPU_PPC_401A1:   /* 401 A1 family                 */
    case CPU_PPC_401B2:   /* 401 B2 family                 */
    case CPU_PPC_401C2:   /* 401 C2 family                 */
    case CPU_PPC_401D2:   /* 401 D2 family                 */
    case CPU_PPC_401E2:   /* 401 E2 family                 */
    case CPU_PPC_401F2:   /* 401 F2 family                 */
    case CPU_PPC_401G2:   /* 401 G2 family                 */
    case CPU_PPC_IOP480:  /* IOP 480 family                */
    case CPU_PPC_COBRA:   /* IBM Processor for Network Resources */
        gen_spr_generic(env);
        gen_spr_40x(env);
        gen_spr_401_403(env);
#if defined (TODO)
        /* XXX: optional ? */
        gen_spr_compress(env);
#endif
        env->nb_BATs = 0;
        env->nb_tlb = 64;
        env->nb_ways = 1;
        env->id_tlbs = 0;
        break;

    case CPU_PPC_403GA:   /* 403 GA family                 */
    case CPU_PPC_403GB:   /* 403 GB family                 */
    case CPU_PPC_403GC:   /* 403 GC family                 */
    case CPU_PPC_403GCX:  /* 403 GCX family                */
        gen_spr_generic(env);
        gen_spr_40x(env);
        gen_spr_401_403(env);
        gen_spr_403(env);
        env->nb_BATs = 0;
        env->nb_tlb = 64;
        env->nb_ways = 1;
        env->id_tlbs = 0;
        break;

    case CPU_PPC_405CR:   /* 405 GP/CR family              */
    case CPU_PPC_405EP:   /* 405 EP family                 */
    case CPU_PPC_405GPR:  /* 405 GPR family                */
    case CPU_PPC_405D2:   /* 405 D2 family                 */
    case CPU_PPC_405D4:   /* 405 D4 family                 */
        gen_spr_generic(env);
        /* Time base */
        gen_tbl(env);
        gen_spr_40x(env);
        gen_spr_405(env);
        env->nb_BATs = 0;
        env->nb_tlb = 64;
        env->nb_ways = 1;
        env->id_tlbs = 0;
        break;

    case CPU_PPC_NPE405H: /* NPe405 H family               */
    case CPU_PPC_NPE405H2:
    case CPU_PPC_NPE405L: /* Npe405 L family               */
        gen_spr_generic(env);
        /* Time base */
        gen_tbl(env);
        gen_spr_40x(env);
        gen_spr_405(env);
        env->nb_BATs = 0;
        env->nb_tlb = 64;
        env->nb_ways = 1;
        env->id_tlbs = 0;
        break;

#if defined (TODO)
    case CPU_PPC_STB01000:
#endif
#if defined (TODO)
    case CPU_PPC_STB01010:
#endif
#if defined (TODO)
    case CPU_PPC_STB0210:
#endif
    case CPU_PPC_STB03:   /* STB03 family                  */
#if defined (TODO)
    case CPU_PPC_STB043:  /* STB043 family                  */
#endif
#if defined (TODO)
    case CPU_PPC_STB045:  /* STB045 family                  */
#endif
    case CPU_PPC_STB25:   /* STB25 family                  */
#if defined (TODO)
    case CPU_PPC_STB130:  /* STB130 family                 */
#endif
        gen_spr_generic(env);
        /* Time base */
        gen_tbl(env);
        gen_spr_40x(env);
        gen_spr_405(env);
        env->nb_BATs = 0;
        env->nb_tlb = 64;
        env->nb_ways = 1;
        env->id_tlbs = 0;
        break;

    case CPU_PPC_440EP:   /* 440 EP family                 */
    case CPU_PPC_440GP:   /* 440 GP family                 */
    case CPU_PPC_440GX:   /* 440 GX family                 */
    case CPU_PPC_440GXc:  /* 440 GXc family                */
    case CPU_PPC_440GXf:  /* 440 GXf family                */
    case CPU_PPC_440SP:   /* 440 SP family                 */
    case CPU_PPC_440SP2:
    case CPU_PPC_440SPE:  /* 440 SPE family                */
        gen_spr_generic(env);
        /* Time base */
        gen_tbl(env);
        gen_spr_BookE(env);
        gen_spr_440(env);
        env->nb_BATs = 0;
        env->nb_tlb = 64;
        env->nb_ways = 1;
        env->id_tlbs = 0;
        break;

    /* Embedded PowerPC from Freescale                     */
#if defined (TODO)
    case CPU_PPC_5xx:
        break;
#endif
#if defined (TODO)
    case CPU_PPC_8xx:     /* MPC821 / 823 / 850 / 860      */
        break;
#endif
#if defined (TODO)
    case CPU_PPC_82xx_HIP3:    /* MPC8240 / 8260                */
    case CPU_PPC_82xx_HIP4:    /* MPC8240 / 8260                */
        break;
#endif
#if defined (TODO)
    case CPU_PPC_827x:    /* MPC 827x / 828x               */
        break;
#endif

    /* XXX: Use MPC8540 PVR to implement a test PowerPC BookE target */
    case CPU_PPC_e500v110:
    case CPU_PPC_e500v120:
    case CPU_PPC_e500v210:
    case CPU_PPC_e500v220:
        gen_spr_generic(env);
        /* Time base */
        gen_tbl(env);
        gen_spr_BookE(env);
        gen_spr_BookE_FSL(env);
        env->nb_BATs = 0;
        env->nb_tlb = 64;
        env->nb_ways = 1;
        env->id_tlbs = 0;
        break;

#if defined (TODO)
    case CPU_PPC_e600:
        break;
#endif

    /* 32 bits PowerPC                                     */
    case CPU_PPC_601:     /* PowerPC 601                   */
        gen_spr_generic(env);
        gen_spr_ne_601(env);
        gen_spr_601(env);
        /* Hardware implementation registers */
        /* XXX : not implemented */
        spr_register(env, SPR_HID0, "HID0",
                     SPR_NOACCESS, SPR_NOACCESS,
                     &spr_read_generic, &spr_write_generic,
                     0x00000000);
        /* XXX : not implemented */
        spr_register(env, SPR_HID1, "HID1",
                     SPR_NOACCESS, SPR_NOACCESS,
                     &spr_read_generic, &spr_write_generic,
                     0x00000000);
        /* XXX : not implemented */
        spr_register(env, SPR_601_HID2, "HID2",
                     SPR_NOACCESS, SPR_NOACCESS,
                     &spr_read_generic, &spr_write_generic,
                     0x00000000);
        /* XXX : not implemented */
        spr_register(env, SPR_601_HID5, "HID5",
                     SPR_NOACCESS, SPR_NOACCESS,
                     &spr_read_generic, &spr_write_generic,
                     0x00000000);
        /* XXX : not implemented */
#if 0 /* ? */
        spr_register(env, SPR_601_HID15, "HID15",
                     SPR_NOACCESS, SPR_NOACCESS,
                     &spr_read_generic, &spr_write_generic,
                     0x00000000);
#endif
        env->nb_tlb = 64;
        env->nb_ways = 2;
        env->id_tlbs = 0;
        env->id_tlbs = 0;
        break;

    case CPU_PPC_602:     /* PowerPC 602                   */
        gen_spr_generic(env);
        gen_spr_ne_601(env);
        /* Memory management */
        gen_low_BATs(env);
        /* Time base */
        gen_tbl(env);
        gen_6xx_7xx_soft_tlb(env, 64, 2);
        gen_spr_602(env);
        /* hardware implementation registers */
        /* XXX : not implemented */
        spr_register(env, SPR_HID0, "HID0",
                     SPR_NOACCESS, SPR_NOACCESS,
                     &spr_read_generic, &spr_write_generic,
                     0x00000000);
        /* XXX : not implemented */
        spr_register(env, SPR_HID1, "HID1",
                     SPR_NOACCESS, SPR_NOACCESS,
                     &spr_read_generic, &spr_write_generic,
                     0x00000000);
        break;

    case CPU_PPC_603:     /* PowerPC 603                   */
    case CPU_PPC_603E:    /* PowerPC 603e                  */
    case CPU_PPC_603E7v:
    case CPU_PPC_603E7v2:
    case CPU_PPC_603P:    /* PowerPC 603p                  */
    case CPU_PPC_603R:    /* PowerPC 603r                  */
        gen_spr_generic(env);
        gen_spr_ne_601(env);
        /* Memory management */
        gen_low_BATs(env);
        /* Time base */
        gen_tbl(env);
        gen_6xx_7xx_soft_tlb(env, 64, 2);
        gen_spr_603(env);
        /* hardware implementation registers */
        /* XXX : not implemented */
        spr_register(env, SPR_HID0, "HID0",
                     SPR_NOACCESS, SPR_NOACCESS,
                     &spr_read_generic, &spr_write_generic,
                     0x00000000);
        /* XXX : not implemented */
        spr_register(env, SPR_HID1, "HID1",
                     SPR_NOACCESS, SPR_NOACCESS,
                     &spr_read_generic, &spr_write_generic,
                     0x00000000);
        break;
        
    case CPU_PPC_G2:      /* PowerPC G2 family             */
    case CPU_PPC_G2H4:
    case CPU_PPC_G2gp:
    case CPU_PPC_G2ls:
    case CPU_PPC_G2LE:    /* PowerPC G2LE family           */
    case CPU_PPC_G2LEgp:
    case CPU_PPC_G2LEls:
        gen_spr_generic(env);
        gen_spr_ne_601(env);
        /* Memory management */
        gen_low_BATs(env);
        /* Time base */
        gen_tbl(env);
        /* Memory management */
        gen_high_BATs(env);
        gen_6xx_7xx_soft_tlb(env, 64, 2);
        gen_spr_G2_755(env);
        gen_spr_G2(env);
        /* Hardware implementation register */
        /* XXX : not implemented */
        spr_register(env, SPR_HID0, "HID0",
                     SPR_NOACCESS, SPR_NOACCESS,
                     &spr_read_generic, &spr_write_generic,
                     0x00000000);
        /* XXX : not implemented */
        spr_register(env, SPR_HID1, "HID1",
                     SPR_NOACCESS, SPR_NOACCESS,
                     &spr_read_generic, &spr_write_generic,
                     0x00000000);
        /* XXX : not implemented */
        spr_register(env, SPR_HID2, "HID2",
                     SPR_NOACCESS, SPR_NOACCESS,
                     &spr_read_generic, &spr_write_generic,
                     0x00000000);
        break;

    case CPU_PPC_604:     /* PowerPC 604                   */
    case CPU_PPC_604E:    /* PowerPC 604e                  */
    case CPU_PPC_604R:    /* PowerPC 604r                  */
        gen_spr_generic(env);
        gen_spr_ne_601(env);
        /* Memory management */
        gen_low_BATs(env);
        /* Time base */
        gen_tbl(env);
        gen_spr_604(env);
        /* Hardware implementation registers */
        /* XXX : not implemented */
        spr_register(env, SPR_HID0, "HID0",
                     SPR_NOACCESS, SPR_NOACCESS,
                     &spr_read_generic, &spr_write_generic,
                     0x00000000);
        /* XXX : not implemented */
        spr_register(env, SPR_HID1, "HID1",
                     SPR_NOACCESS, SPR_NOACCESS,
                     &spr_read_generic, &spr_write_generic,
                     0x00000000);
        break;

    case CPU_PPC_74x:     /* PowerPC 740 / 750             */
    case CPU_PPC_740E:
    case CPU_PPC_750E:
    case CPU_PPC_74xP:    /* PowerPC 740P / 750P           */
    case CPU_PPC_750CXE21: /* IBM PowerPC 750cxe            */
    case CPU_PPC_750CXE22:
    case CPU_PPC_750CXE23:
    case CPU_PPC_750CXE24:
    case CPU_PPC_750CXE24b:
    case CPU_PPC_750CXE31:
    case CPU_PPC_750CXE31b:
    case CPU_PPC_750CXR:
        gen_spr_generic(env);
        gen_spr_ne_601(env);
        /* Memory management */
        gen_low_BATs(env);
        /* Time base */
        gen_tbl(env);
        gen_spr_7xx(env);
        /* Hardware implementation registers */
        /* XXX : not implemented */
        spr_register(env, SPR_HID0, "HID0",
                     SPR_NOACCESS, SPR_NOACCESS,
                     &spr_read_generic, &spr_write_generic,
                     0x00000000);
        /* XXX : not implemented */
        spr_register(env, SPR_HID1, "HID1",
                     SPR_NOACCESS, SPR_NOACCESS,
                     &spr_read_generic, &spr_write_generic,
                     0x00000000);
        break;

    case CPU_PPC_750FX10: /* IBM PowerPC 750 FX            */
    case CPU_PPC_750FX20:
    case CPU_PPC_750FX21:
    case CPU_PPC_750FX22:
    case CPU_PPC_750FX23:
    case CPU_PPC_750GX10: /* IBM PowerPC 750 GX            */
    case CPU_PPC_750GX11:
    case CPU_PPC_750GX12:
        gen_spr_generic(env);
        gen_spr_ne_601(env);
        /* Memory management */
        gen_low_BATs(env);
        /* PowerPC 750fx & 750gx has 8 DBATs and 8 IBATs */
        gen_high_BATs(env);
        /* Time base */
        gen_tbl(env);
        gen_spr_7xx(env);
        /* Hardware implementation registers */
        /* XXX : not implemented */
        spr_register(env, SPR_HID0, "HID0",
                     SPR_NOACCESS, SPR_NOACCESS,
                     &spr_read_generic, &spr_write_generic,
                     0x00000000);
        /* XXX : not implemented */
        spr_register(env, SPR_HID1, "HID1",
                     SPR_NOACCESS, SPR_NOACCESS,
                     &spr_read_generic, &spr_write_generic,
                     0x00000000);
        /* XXX : not implemented */
        spr_register(env, SPR_750_HID2, "HID2",
                     SPR_NOACCESS, SPR_NOACCESS,
                     &spr_read_generic, &spr_write_generic,
                     0x00000000);
        break;

    case CPU_PPC_755_10:  /* PowerPC 755                   */
    case CPU_PPC_755_11:
    case CPU_PPC_755_20:
    case CPU_PPC_755D:
    case CPU_PPC_755E:
        gen_spr_generic(env);
        gen_spr_ne_601(env);
        /* Memory management */
        gen_low_BATs(env);
        /* Time base */
        gen_tbl(env);
        /* Memory management */
        gen_high_BATs(env);
        gen_6xx_7xx_soft_tlb(env, 64, 2);
        gen_spr_G2_755(env);
        /* L2 cache control */
        /* XXX : not implemented */
        spr_register(env, SPR_ICTC, "ICTC",
                     SPR_NOACCESS, SPR_NOACCESS,
                     &spr_read_generic, &spr_write_generic,
                     0x00000000);
        /* XXX : not implemented */
        spr_register(env, SPR_L2PM, "L2PM",
                     SPR_NOACCESS, SPR_NOACCESS,
                     &spr_read_generic, &spr_write_generic,
                     0x00000000);
        /* Hardware implementation registers */
        /* XXX : not implemented */
        spr_register(env, SPR_HID0, "HID0",
                     SPR_NOACCESS, SPR_NOACCESS,
                     &spr_read_generic, &spr_write_generic,
                     0x00000000);
        /* XXX : not implemented */
        spr_register(env, SPR_HID1, "HID1",
                     SPR_NOACCESS, SPR_NOACCESS,
                     &spr_read_generic, &spr_write_generic,
                     0x00000000);
        /* XXX : not implemented */
        spr_register(env, SPR_HID2, "HID2",
                     SPR_NOACCESS, SPR_NOACCESS,
                     &spr_read_generic, &spr_write_generic,
                     0x00000000);
        break;

#if defined (TODO)
    /* G4 family */
    case CPU_PPC_7400:    /* PowerPC 7400                  */
    case CPU_PPC_7410C:   /* PowerPC 7410                  */
    case CPU_PPC_7410D:
    case CPU_PPC_7410E:
    case CPU_PPC_7441:    /* PowerPC 7441                  */
    case CPU_PPC_7445:    /* PowerPC 7445                  */
    case CPU_PPC_7447:    /* PowerPC 7447                  */
    case CPU_PPC_7447A:   /* PowerPC 7447A                 */
    case CPU_PPC_7448:    /* PowerPC 7448                  */
    case CPU_PPC_7450:    /* PowerPC 7450                  */
    case CPU_PPC_7450b:
    case CPU_PPC_7451:    /* PowerPC 7451                  */
    case CPU_PPC_7451G:
    case CPU_PPC_7455:    /* PowerPC 7455                  */
    case CPU_PPC_7455F:
    case CPU_PPC_7455G:
    case CPU_PPC_7457:    /* PowerPC 7457                  */
    case CPU_PPC_7457C:
    case CPU_PPC_7457A:   /* PowerPC 7457A                 */
        break;
#endif

    /* 64 bits PowerPC                                     */
#if defined (TARGET_PPC64)
#if defined (TODO)
    case CPU_PPC_620:     /* PowerPC 620                   */
    case CPU_PPC_630:     /* PowerPC 630 (Power 3)         */
    case CPU_PPC_631:     /* PowerPC 631 (Power 3+)        */
    case CPU_PPC_POWER4:  /* Power 4                       */
    case CPU_PPC_POWER4P: /* Power 4+                      */
    case CPU_PPC_POWER5:  /* Power 5                       */
    case CPU_PPC_POWER5P: /* Power 5+                      */
#endif
    case CPU_PPC_970:     /* PowerPC 970                   */
    case CPU_PPC_970FX10: /* PowerPC 970 FX                */
    case CPU_PPC_970FX20:
    case CPU_PPC_970FX21:
    case CPU_PPC_970FX30:
    case CPU_PPC_970FX31:
    case CPU_PPC_970MP10: /* PowerPC 970 MP                */
    case CPU_PPC_970MP11:
#if defined (TODO)
    case CPU_PPC_CELL10:  /* Cell family                   */
    case CPU_PPC_CELL20:
    case CPU_PPC_CELL30:
    case CPU_PPC_CELL31:
#endif
#if defined (TODO)
    case CPU_PPC_RS64:    /* Apache (RS64/A35)             */
    case CPU_PPC_RS64II:  /* NorthStar (RS64-II/A50)       */
    case CPU_PPC_RS64III: /* Pulsar (RS64-III)             */
    case CPU_PPC_RS64IV:  /* IceStar/IStar/SStar (RS64-IV) */
#endif
        break;
#endif /* defined (TARGET_PPC64) */

#if defined (TODO)
        /* POWER                                               */
    case CPU_POWER:       /* POWER                         */
    case CPU_POWER2:      /* POWER2                        */
        break;
#endif

    default:
        gen_spr_generic(env);
        break;
    }
    if (env->nb_BATs == -1)
        env->nb_BATs = 4;
    /* Allocate TLBs buffer when needed */
    if (env->nb_tlb != 0) {
        int nb_tlb = env->nb_tlb;
        if (env->id_tlbs != 0)
            nb_tlb *= 2;
        env->tlb = qemu_mallocz(nb_tlb * sizeof(ppc_tlb_t));
        /* Pre-compute some useful values */
        env->tlb_per_way = env->nb_tlb / env->nb_ways;
    }
}

#if defined(PPC_DUMP_CPU)
static void dump_sprs (CPUPPCState *env)
{
    ppc_spr_t *spr;
    uint32_t pvr = env->spr[SPR_PVR];
    uint32_t sr, sw, ur, uw;
    int i, j, n;

    printf("* SPRs for PVR=%08x\n", pvr);
    for (i = 0; i < 32; i++) {
        for (j = 0; j < 32; j++) {
            n = (i << 5) | j;
            spr = &env->spr_cb[n];
#if !defined(CONFIG_USER_ONLY)
            sw = spr->oea_write != NULL && spr->oea_write != SPR_NOACCESS;
            sr = spr->oea_read != NULL && spr->oea_read != SPR_NOACCESS;
#else
            sw = 0;
            sr = 0;
#endif
            uw = spr->uea_write != NULL && spr->uea_write != SPR_NOACCESS;
            ur = spr->uea_read != NULL && spr->uea_read != SPR_NOACCESS;
            if (sw || sr || uw || ur) {
                printf("%4d (%03x) %8s s%c%c u%c%c\n",
                       (i << 5) | j, (i << 5) | j, spr->name,
                       sw ? 'w' : '-', sr ? 'r' : '-',
                       uw ? 'w' : '-', ur ? 'r' : '-');
            }
        }
    }
    fflush(stdout);
    fflush(stderr);
}
#endif

/*****************************************************************************/
#include <stdlib.h>
#include <string.h>

int fflush (FILE *stream);

/* Opcode types */
enum {
    PPC_DIRECT   = 0, /* Opcode routine        */
    PPC_INDIRECT = 1, /* Indirect opcode table */
};

static inline int is_indirect_opcode (void *handler)
{
    return ((unsigned long)handler & 0x03) == PPC_INDIRECT;
}

static inline opc_handler_t **ind_table(void *handler)
{
    return (opc_handler_t **)((unsigned long)handler & ~3);
}

/* Instruction table creation */
/* Opcodes tables creation */
static void fill_new_table (opc_handler_t **table, int len)
{
    int i;

    for (i = 0; i < len; i++)
        table[i] = &invalid_handler;
}

static int create_new_table (opc_handler_t **table, unsigned char idx)
{
    opc_handler_t **tmp;

    tmp = malloc(0x20 * sizeof(opc_handler_t));
    if (tmp == NULL)
        return -1;
    fill_new_table(tmp, 0x20);
    table[idx] = (opc_handler_t *)((unsigned long)tmp | PPC_INDIRECT);

    return 0;
}

static int insert_in_table (opc_handler_t **table, unsigned char idx,
                            opc_handler_t *handler)
{
    if (table[idx] != &invalid_handler)
        return -1;
    table[idx] = handler;

    return 0;
}

static int register_direct_insn (opc_handler_t **ppc_opcodes,
                                 unsigned char idx, opc_handler_t *handler)
{
    if (insert_in_table(ppc_opcodes, idx, handler) < 0) {
        printf("*** ERROR: opcode %02x already assigned in main "
               "opcode table\n", idx);
        return -1;
    }

    return 0;
}

static int register_ind_in_table (opc_handler_t **table,
                                  unsigned char idx1, unsigned char idx2,
                                  opc_handler_t *handler)
{
    if (table[idx1] == &invalid_handler) {
        if (create_new_table(table, idx1) < 0) {
            printf("*** ERROR: unable to create indirect table "
                   "idx=%02x\n", idx1);
            return -1;
        }
    } else {
        if (!is_indirect_opcode(table[idx1])) {
            printf("*** ERROR: idx %02x already assigned to a direct "
                   "opcode\n", idx1);
            return -1;
        }
    }
    if (handler != NULL &&
        insert_in_table(ind_table(table[idx1]), idx2, handler) < 0) {
        printf("*** ERROR: opcode %02x already assigned in "
               "opcode table %02x\n", idx2, idx1);
        return -1;
    }

    return 0;
}

static int register_ind_insn (opc_handler_t **ppc_opcodes,
                              unsigned char idx1, unsigned char idx2,
                              opc_handler_t *handler)
{
    int ret;

    ret = register_ind_in_table(ppc_opcodes, idx1, idx2, handler);

    return ret;
}

static int register_dblind_insn (opc_handler_t **ppc_opcodes, 
                                 unsigned char idx1, unsigned char idx2,
                                 unsigned char idx3, opc_handler_t *handler)
{
    if (register_ind_in_table(ppc_opcodes, idx1, idx2, NULL) < 0) {
        printf("*** ERROR: unable to join indirect table idx "
               "[%02x-%02x]\n", idx1, idx2);
        return -1;
    }
    if (register_ind_in_table(ind_table(ppc_opcodes[idx1]), idx2, idx3,
                              handler) < 0) {
        printf("*** ERROR: unable to insert opcode "
               "[%02x-%02x-%02x]\n", idx1, idx2, idx3);
        return -1;
    }

    return 0;
}

static int register_insn (opc_handler_t **ppc_opcodes, opcode_t *insn)
{
    if (insn->opc2 != 0xFF) {
        if (insn->opc3 != 0xFF) {
            if (register_dblind_insn(ppc_opcodes, insn->opc1, insn->opc2,
                                     insn->opc3, &insn->handler) < 0)
                return -1;
        } else {
            if (register_ind_insn(ppc_opcodes, insn->opc1,
                                  insn->opc2, &insn->handler) < 0)
                return -1;
        }
    } else {
        if (register_direct_insn(ppc_opcodes, insn->opc1, &insn->handler) < 0)
            return -1;
    }

    return 0;
}

static int test_opcode_table (opc_handler_t **table, int len)
{
    int i, count, tmp;

    for (i = 0, count = 0; i < len; i++) {
        /* Consistency fixup */
        if (table[i] == NULL)
            table[i] = &invalid_handler;
        if (table[i] != &invalid_handler) {
            if (is_indirect_opcode(table[i])) {
                tmp = test_opcode_table(ind_table(table[i]), 0x20);
                if (tmp == 0) {
                    free(table[i]);
                    table[i] = &invalid_handler;
                } else {
                    count++;
                }
            } else {
                count++;
            }
        }
    }

    return count;
}

static void fix_opcode_tables (opc_handler_t **ppc_opcodes)
{
    if (test_opcode_table(ppc_opcodes, 0x40) == 0)
        printf("*** WARNING: no opcode defined !\n");
}

/*****************************************************************************/
static int create_ppc_opcodes (CPUPPCState *env, ppc_def_t *def)
{
    opcode_t *opc, *start, *end;

    fill_new_table(env->opcodes, 0x40);
#if defined(PPC_DUMP_CPU)
    printf("* PowerPC instructions for PVR %08x: %s flags %016" PRIx64
           " %08x\n",
           def->pvr, def->name, def->insns_flags, def->flags);
#endif
    if (&opc_start < &opc_end) {
        start = &opc_start;
        end = &opc_end;
    } else {
        start = &opc_end;
        end = &opc_start;
    }
    for (opc = start + 1; opc != end; opc++) {
        if ((opc->handler.type & def->insns_flags) != 0) {
            if (register_insn(env->opcodes, opc) < 0) {
                printf("*** ERROR initializing PowerPC instruction "
                       "0x%02x 0x%02x 0x%02x\n", opc->opc1, opc->opc2,
                       opc->opc3);
                return -1;
            }
#if defined(PPC_DUMP_CPU)
            if (opc1 != 0x00) {
                if (opc->opc3 == 0xFF) {
                    if (opc->opc2 == 0xFF) {
                        printf(" %02x -- -- (%2d ----) : %s\n",
                               opc->opc1, opc->opc1, opc->oname);
                    } else {
                        printf(" %02x %02x -- (%2d %4d) : %s\n",
                               opc->opc1, opc->opc2, opc->opc1, opc->opc2,
                               opc->oname);
                    }
                } else {
                    printf(" %02x %02x %02x (%2d %4d) : %s\n",
                           opc->opc1, opc->opc2, opc->opc3,
                           opc->opc1, (opc->opc3 << 5) | opc->opc2,
                           opc->oname);
                }
            }
#endif
        }
    }
    fix_opcode_tables(env->opcodes);
    fflush(stdout);
    fflush(stderr);

    return 0;
}

int cpu_ppc_register (CPUPPCState *env, ppc_def_t *def)
{
    env->msr_mask = def->msr_mask;
    env->flags = def->flags;
    if (create_ppc_opcodes(env, def) < 0)
        return -1;
    init_ppc_proc(env, def);
#if defined(PPC_DUMP_CPU)
    dump_sprs(env);
    if (env->tlb != NULL) {
        printf("%d %s TLB in %d ways\n", env->nb_tlb,
               env->id_tlbs ? "splitted" : "merged", env->nb_ways);
    }
#endif

    return 0;
}

void do_compute_hflags (CPUPPCState *env);
CPUPPCState *cpu_ppc_init (void)
{
    CPUPPCState *env;

    env = qemu_mallocz(sizeof(CPUPPCState));
    if (!env)
        return NULL;
    cpu_exec_init(env);
    tlb_flush(env, 1);
#if defined (DO_SINGLE_STEP) && 0
    /* Single step trace mode */
    msr_se = 1;
    msr_be = 1;
#endif
    msr_fp = 1; /* Allow floating point exceptions */
    msr_me = 1; /* Allow machine check exceptions  */
#if defined(CONFIG_USER_ONLY)
    msr_pr = 1;
#else
    env->nip = 0xFFFFFFFC;
#endif
    do_compute_hflags(env);
    env->reserve = -1;
    return env;
}

void cpu_ppc_close(CPUPPCState *env)
{
    /* Should also remove all opcode tables... */
    free(env);
}

/*****************************************************************************/
/* PowerPC CPU definitions */
static ppc_def_t ppc_defs[] =
    {
        /* Embedded PowerPC */
#if defined (TODO)
        /* PowerPC 401 */
        {
            .name        = "401",
            .pvr         = CPU_PPC_401,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_401,
            .flags       = PPC_FLAGS_401,
            .msr_mask    = xxx,
        },
#endif
#if defined (TODO)
        /* IOP480 (401 microcontroler) */
        {
            .name        = "iop480",
            .pvr         = CPU_PPC_IOP480,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_401,
            .flags       = PPC_FLAGS_401,
            .msr_mask    = xxx,
        },
#endif
#if defined (TODO)
        /* IBM Processor for Network Resources */
        {
            .name        = "Cobra",
            .pvr         = CPU_PPC_COBRA,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_401,
            .flags       = PPC_FLAGS_401,
            .msr_mask    = xxx,
        },
#endif
#if defined (TODO)
        /* Generic PowerPC 403 */
        {
            .name        = "403",
            .pvr         = CPU_PPC_403,
            .pvr_mask    = 0xFFFFFF00,
            .insns_flags = PPC_INSNS_403,
            .flags       = PPC_FLAGS_403,
            .msr_mask    = 0x000000000007D23D,
        },
#endif
#if defined (TODO)
        /* PowerPC 403 GA */
        {
            .name        = "403ga",
            .pvr         = CPU_PPC_403GA,
            .pvr_mask    = 0xFFFFFF00,
            .insns_flags = PPC_INSNS_403,
            .flags       = PPC_FLAGS_403,
            .msr_mask    = 0x000000000007D23D,
        },
#endif
#if defined (TODO)
        /* PowerPC 403 GB */
        {
            .name        = "403gb",
            .pvr         = CPU_PPC_403GB,
            .pvr_mask    = 0xFFFFFF00,
            .insns_flags = PPC_INSNS_403,
            .flags       = PPC_FLAGS_403,
            .msr_mask    = 0x000000000007D23D,
        },
#endif
#if defined (TODO)
        /* PowerPC 403 GC */
        {
            .name        = "403gc",
            .pvr         = CPU_PPC_403GC,
            .pvr_mask    = 0xFFFFFF00,
            .insns_flags = PPC_INSNS_403,
            .flags       = PPC_FLAGS_403,
            .msr_mask    = 0x000000000007D23D,
        },
#endif
#if defined (TODO)
        /* PowerPC 403 GCX */
        {
            .name        = "403gcx",
            .pvr         = CPU_PPC_403GCX,
            .pvr_mask    = 0xFFFFFF00,
            .insns_flags = PPC_INSNS_403,
            .flags       = PPC_FLAGS_403,
            .msr_mask    = 0x000000000007D23D,
        },
#endif
#if defined (TODO)
        /* Generic PowerPC 405 */
        {
            .name        = "405",
            .pvr         = CPU_PPC_405,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_405,
            .flags       = PPC_FLAGS_405,
            .msr_mask    = 0x00000000020EFF30,
        },
#endif
#if defined (TODO)
        /* PowerPC 405 CR */
        {
            .name        = "405cr",
            .pvr         = CPU_PPC_405,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_405,
            .flags       = PPC_FLAGS_405,
            .msr_mask    = 0x00000000020EFF30,
        },
#endif
#if defined (TODO)
        /* PowerPC 405 GP */
        {
            .name        = "405gp",
            .pvr         = CPU_PPC_405,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_405,
            .flags       = PPC_FLAGS_405,
            .msr_mask    = 0x00000000020EFF30,
        },
#endif
#if defined (TODO)
        /* PowerPC 405 EP */
        {
            .name        = "405ep",
            .pvr         = CPU_PPC_405EP,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_405,
            .flags       = PPC_FLAGS_405,
            .msr_mask    = 0x00000000020EFF30,
        },
#endif
#if defined (TODO)
        /* PowerPC 405 GPR */
        {
            .name        = "405gpr",
            .pvr         = CPU_PPC_405GPR,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_405,
            .flags       = PPC_FLAGS_405,
            .msr_mask    = 0x00000000020EFF30,
        },
#endif
#if defined (TODO)
        /* PowerPC 405 D2 */
        {
            .name        = "405d2",
            .pvr         = CPU_PPC_405D2,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_405,
            .flags       = PPC_FLAGS_405,
            .msr_mask    = 0x00000000020EFF30,
        },
#endif
#if defined (TODO)
        /* PowerPC 405 D4 */
        {
            .name        = "405d4",
            .pvr         = CPU_PPC_405D4,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_405,
            .flags       = PPC_FLAGS_405,
            .msr_mask    = 0x00000000020EFF30,
        },
#endif
#if defined (TODO)
        /* Npe405 H */
        {
            .name        = "Npe405H",
            .pvr         = CPU_PPC_NPE405H,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_405,
            .flags       = PPC_FLAGS_405,
            .msr_mask    = 0x00000000020EFF30,
        },
#endif
#if defined (TODO)
        /* Npe405 L */
        {
            .name        = "Npe405L",
            .pvr         = CPU_PPC_NPE405L,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_405,
            .flags       = PPC_FLAGS_405,
            .msr_mask    = 0x00000000020EFF30,
        },
#endif
#if defined (TODO)
        /* STB010000 */
        {
            .name        = "STB01000",
            .pvr         = CPU_PPC_STB01000,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_405,
            .flags       = PPC_FLAGS_405,
            .msr_mask    = 0x00000000020EFF30,
        },
#endif
#if defined (TODO)
        /* STB01010 */
        {
            .name        = "STB01010",
            .pvr         = CPU_PPC_STB01010,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_405,
            .flags       = PPC_FLAGS_405,
            .msr_mask    = 0x00000000020EFF30,
        },
#endif
#if defined (TODO)
        /* STB0210 */
        {
            .name        = "STB0210",
            .pvr         = CPU_PPC_STB0210,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_405,
            .flags       = PPC_FLAGS_405,
            .msr_mask    = 0x00000000020EFF30,
        },
#endif
#if defined (TODO)
        /* STB03xx */
        {
            .name        = "STB03",
            .pvr         = CPU_PPC_STB03,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_405,
            .flags       = PPC_FLAGS_405,
            .msr_mask    = 0x00000000020EFF30,
        },
#endif
#if defined (TODO)
        /* STB043x */
        {
            .name        = "STB043",
            .pvr         = CPU_PPC_STB043,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_405,
            .flags       = PPC_FLAGS_405,
            .msr_mask    = 0x00000000020EFF30,
        },
#endif
#if defined (TODO)
        /* STB045x */
        {
            .name        = "STB045",
            .pvr         = CPU_PPC_STB045,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_405,
            .flags       = PPC_FLAGS_405,
            .msr_mask    = 0x00000000020EFF30,
        },
#endif
#if defined (TODO)
        /* STB25xx */
        {
            .name        = "STB25",
            .pvr         = CPU_PPC_STB25,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_405,
            .flags       = PPC_FLAGS_405,
            .msr_mask    = 0x00000000020EFF30,
        },
#endif
#if defined (TODO)
        /* STB130 */
        {
            .name        = "STB130",
            .pvr         = CPU_PPC_STB130,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_405,
            .flags       = PPC_FLAGS_405,
            .msr_mask    = 0x00000000020EFF30,
        },
#endif
        /* Xilinx PowerPC 405 cores */
#if defined (TODO)
        {
            .name        = "x2vp4",
            .pvr         = CPU_PPC_X2VP4,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_405,
            .flags       = PPC_FLAGS_405,
            .msr_mask    = 0x00000000020EFF30,
        },
        {
            .name        = "x2vp7",
            .pvr         = CPU_PPC_X2VP7,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_405,
            .flags       = PPC_FLAGS_405,
            .msr_mask    = 0x00000000020EFF30,
        },
        {
            .name        = "x2vp20",
            .pvr         = CPU_PPC_X2VP20,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_405,
            .flags       = PPC_FLAGS_405,
            .msr_mask    = 0x00000000020EFF30,
        },
        {
            .name        = "x2vp50",
            .pvr         = CPU_PPC_X2VP50,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_405,
            .flags       = PPC_FLAGS_405,
            .msr_mask    = 0x00000000020EFF30,
        },
#endif
#if defined (TODO)
        /* PowerPC 440 EP */
        {
            .name        = "440ep",
            .pvr         = CPU_PPC_440EP,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_440,
            .flags       = PPC_FLAGS_440,
            .msr_mask    = 0x000000000006D630,
        },
#endif
#if defined (TODO)
        /* PowerPC 440 GR */
        {
            .name        = "440gr",
            .pvr         = CPU_PPC_440GR,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_440,
            .flags       = PPC_FLAGS_440,
            .msr_mask    = 0x000000000006D630,
        },
#endif
#if defined (TODO)
        /* PowerPC 440 GP */
        {
            .name        = "440gp",
            .pvr         = CPU_PPC_440GP,
            .pvr_mask    = 0xFFFFFF00,
            .insns_flags = PPC_INSNS_440,
            .flags       = PPC_FLAGS_440,
            .msr_mask    = 0x000000000006D630,
        },
#endif
#if defined (TODO)
        /* PowerPC 440 GX */
        {
            .name        = "440gx",
            .pvr         = CPU_PPC_440GX,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_405,
            .flags       = PPC_FLAGS_440,
            .msr_mask    = 0x000000000006D630,
        },
#endif
#if defined (TODO)
        /* PowerPC 440 GXc */
        {
            .name        = "440gxc",
            .pvr         = CPU_PPC_440GXC,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_405,
            .flags       = PPC_FLAGS_440,
            .msr_mask    = 0x000000000006D630,
        },
#endif
#if defined (TODO)
        /* PowerPC 440 GXf */
        {
            .name        = "440gxf",
            .pvr         = CPU_PPC_440GXF,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_405,
            .flags       = PPC_FLAGS_440,
            .msr_mask    = 0x000000000006D630,
        },
#endif
#if defined (TODO)
        /* PowerPC 440 SP */
        {
            .name        = "440sp",
            .pvr         = CPU_PPC_440SP,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_405,
            .flags       = PPC_FLAGS_440,
            .msr_mask    = 0x000000000006D630,
        },
#endif
#if defined (TODO)
        /* PowerPC 440 SP2 */
        {
            .name        = "440sp2",
            .pvr         = CPU_PPC_440SP2,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_405,
            .flags       = PPC_FLAGS_440,
            .msr_mask    = 0x000000000006D630,
        },
#endif
#if defined (TODO)
        /* PowerPC 440 SPE */
        {
            .name        = "440spe",
            .pvr         = CPU_PPC_440SPE,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_405,
            .flags       = PPC_FLAGS_440,
            .msr_mask    = 0x000000000006D630,
        },
#endif
        /* Fake generic BookE PowerPC */
        {
            .name        = "BookE",
            .pvr         = CPU_PPC_e500,
            .pvr_mask    = 0xFFFFFFFF,
            .insns_flags = PPC_INSNS_BOOKE,
            .flags       = PPC_FLAGS_BOOKE,
            .msr_mask    = 0x000000000006D630,
        },
        /* PowerPC 460 cores - TODO */
        /* PowerPC MPC 5xx cores - TODO */
        /* PowerPC MPC 8xx cores - TODO */
        /* PowerPC MPC 8xxx cores - TODO */
        /* e200 cores - TODO */
        /* e500 cores - TODO */
        /* e600 cores - TODO */

        /* 32 bits "classic" PowerPC */
#if defined (TODO)
        /* PowerPC 601 */
        {
            .name        = "601",
            .pvr         = CPU_PPC_601,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_601,
            .flags       = PPC_FLAGS_601,
            .msr_mask    = 0x000000000000FD70,
        },
#endif
#if defined (TODO)
        /* PowerPC 602 */
        {
            .name        = "602",
            .pvr         = CPU_PPC_602,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_602,
            .flags       = PPC_FLAGS_602,
            .msr_mask    = 0x0000000000C7FF73,
        },
#endif
        /* PowerPC 603 */
        {
            .name        = "603",
            .pvr         = CPU_PPC_603,
            .pvr_mask    = 0xFFFFFFFF,
            .insns_flags = PPC_INSNS_603,
            .flags       = PPC_FLAGS_603,
            .msr_mask    = 0x000000000007FF73,
        },
        /* PowerPC 603e */
        {
            .name        = "603e",
            .pvr         = CPU_PPC_603E,
            .pvr_mask    = 0xFFFFFFFF,
            .insns_flags = PPC_INSNS_603,
            .flags       = PPC_FLAGS_603,
            .msr_mask    = 0x000000000007FF73,
        },
        {
            .name        = "Stretch",
            .pvr         = CPU_PPC_603E,
            .pvr_mask    = 0xFFFFFFFF,
            .insns_flags = PPC_INSNS_603,
            .flags       = PPC_FLAGS_603,
            .msr_mask    = 0x000000000007FF73,
        },
        /* PowerPC 603p */
        {
            .name        = "603p",
            .pvr         = CPU_PPC_603P,
            .pvr_mask    = 0xFFFFFFFF,
            .insns_flags = PPC_INSNS_603,
            .flags       = PPC_FLAGS_603,
            .msr_mask    = 0x000000000007FF73,
        },
        /* PowerPC 603e7 */
        {
            .name        = "603e7",
            .pvr         = CPU_PPC_603E7,
            .pvr_mask    = 0xFFFFFFFF,
            .insns_flags = PPC_INSNS_603,
            .flags       = PPC_FLAGS_603,
            .msr_mask    = 0x000000000007FF73,
        },
        /* PowerPC 603e7v */
        {
            .name        = "603e7v",
            .pvr         = CPU_PPC_603E7v,
            .pvr_mask    = 0xFFFFFFFF,
            .insns_flags = PPC_INSNS_603,
            .flags       = PPC_FLAGS_603,
            .msr_mask    = 0x000000000007FF73,
        },
        /* PowerPC 603e7v2 */
        {
            .name        = "603e7v2",
            .pvr         = CPU_PPC_603E7v2,
            .pvr_mask    = 0xFFFFFFFF,
            .insns_flags = PPC_INSNS_603,
            .flags       = PPC_FLAGS_603,
            .msr_mask    = 0x000000000007FF73,
        },
        /* PowerPC 603r */
        {
            .name        = "603r",
            .pvr         = CPU_PPC_603R,
            .pvr_mask    = 0xFFFFFFFF,
            .insns_flags = PPC_INSNS_603,
            .flags       = PPC_FLAGS_603,
            .msr_mask    = 0x000000000007FF73,
        },
        {
            .name        = "Goldeneye",
            .pvr         = CPU_PPC_603R,
            .pvr_mask    = 0xFFFFFFFF,
            .insns_flags = PPC_INSNS_603,
            .flags       = PPC_FLAGS_603,
            .msr_mask    = 0x000000000007FF73,
        },
#if defined (TODO)
        /* XXX: TODO: according to Motorola UM, this is a derivative to 603e */
        {
            .name        = "G2",
            .pvr         = CPU_PPC_G2,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_G2,
            .flags       = PPC_FLAGS_G2,
            .msr_mask    = 0x000000000006FFF2,
        },
        {
            .name        = "G2h4",
            .pvr         = CPU_PPC_G2H4,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_G2,
            .flags       = PPC_FLAGS_G2,
            .msr_mask    = 0x000000000006FFF2,
        },
        {
            .name        = "G2gp",
            .pvr         = CPU_PPC_G2gp,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_G2,
            .flags       = PPC_FLAGS_G2,
            .msr_mask    = 0x000000000006FFF2,
        },
        {
            .name        = "G2ls",
            .pvr         = CPU_PPC_G2ls,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_G2,
            .flags       = PPC_FLAGS_G2,
            .msr_mask    = 0x000000000006FFF2,
        },
        { /* Same as G2, with LE mode support */
            .name        = "G2le",
            .pvr         = CPU_PPC_G2LE,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_G2,
            .flags       = PPC_FLAGS_G2,
            .msr_mask    = 0x000000000007FFF3,
        },
        {
            .name        = "G2legp",
            .pvr         = CPU_PPC_G2LEgp,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_G2,
            .flags       = PPC_FLAGS_G2,
            .msr_mask    = 0x000000000007FFF3,
        },
        {
            .name        = "G2lels",
            .pvr         = CPU_PPC_G2LEls,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_G2,
            .flags       = PPC_FLAGS_G2,
            .msr_mask    = 0x000000000007FFF3,
        },
#endif
        /* PowerPC 604 */
        {
            .name        = "604",
            .pvr         = CPU_PPC_604,
            .pvr_mask    = 0xFFFFFFFF,
            .insns_flags = PPC_INSNS_604,
            .flags       = PPC_FLAGS_604,
            .msr_mask    = 0x000000000005FF77,
        },
        /* PowerPC 604e */
        {
            .name        = "604e",
            .pvr         = CPU_PPC_604E,
            .pvr_mask    = 0xFFFFFFFF,
            .insns_flags = PPC_INSNS_604,
            .flags       = PPC_FLAGS_604,
            .msr_mask    = 0x000000000005FF77,
        },
        /* PowerPC 604r */
        {
            .name        = "604r",
            .pvr         = CPU_PPC_604R,
            .pvr_mask    = 0xFFFFFFFF,
            .insns_flags = PPC_INSNS_604,
            .flags       = PPC_FLAGS_604,
            .msr_mask    = 0x000000000005FF77,
        },
        /* generic G3 */
        {
            .name        = "G3",
            .pvr         = CPU_PPC_74x,
            .pvr_mask    = 0xFFFFFFFF,
            .insns_flags = PPC_INSNS_7x0,
            .flags       = PPC_FLAGS_7x0,
            .msr_mask    = 0x000000000007FF77,
        },
        /* MPC740 (G3) */
        {
            .name        = "740",
            .pvr         = CPU_PPC_74x,
            .pvr_mask    = 0xFFFFFFFF,
            .insns_flags = PPC_INSNS_7x0,
            .flags       = PPC_FLAGS_7x0,
            .msr_mask    = 0x000000000007FF77,
        },
        {
            .name        = "Arthur",
            .pvr         = CPU_PPC_74x,
            .pvr_mask    = 0xFFFFFFFF,
            .insns_flags = PPC_INSNS_7x0,
            .flags       = PPC_FLAGS_7x0,
            .msr_mask    = 0x000000000007FF77,
        },
#if defined (TODO)
        /* MPC745 (G3) */
        {
            .name        = "745",
            .pvr         = CPU_PPC_74x,
            .pvr_mask    = 0xFFFFF000,
            .insns_flags = PPC_INSNS_7x5,
            .flags       = PPC_FLAGS_7x5,
            .msr_mask    = 0x000000000007FF77,
        },
        {
            .name        = "Goldfinger",
            .pvr         = CPU_PPC_74x,
            .pvr_mask    = 0xFFFFF000,
            .insns_flags = PPC_INSNS_7x5,
            .flags       = PPC_FLAGS_7x5,
            .msr_mask    = 0x000000000007FF77,
        },
#endif
        /* MPC750 (G3) */
        {
            .name        = "750",
            .pvr         = CPU_PPC_74x,
            .pvr_mask    = 0xFFFFFFFF,
            .insns_flags = PPC_INSNS_7x0,
            .flags       = PPC_FLAGS_7x0,
            .msr_mask    = 0x000000000007FF77,
        },
#if defined (TODO)
        /* MPC755 (G3) */
        {
            .name        = "755",
            .pvr         = CPU_PPC_755,
            .pvr_mask    = 0xFFFFF000,
            .insns_flags = PPC_INSNS_7x5,
            .flags       = PPC_FLAGS_7x5,
            .msr_mask    = 0x000000000007FF77,
        },
#endif
        /* MPC740P (G3) */
        {
            .name        = "740p",
            .pvr         = CPU_PPC_74xP,
            .pvr_mask    = 0xFFFFFFFF,
            .insns_flags = PPC_INSNS_7x0,
            .flags       = PPC_FLAGS_7x0,
            .msr_mask    = 0x000000000007FF77,
        },
        {
            .name        = "Conan/Doyle",
            .pvr         = CPU_PPC_74xP,
            .pvr_mask    = 0xFFFFFFFF,
            .insns_flags = PPC_INSNS_7x0,
            .flags       = PPC_FLAGS_7x0,
            .msr_mask    = 0x000000000007FF77,
        },
#if defined (TODO)
        /* MPC745P (G3) */
        {
            .name        = "745p",
            .pvr         = CPU_PPC_74xP,
            .pvr_mask    = 0xFFFFF000,
            .insns_flags = PPC_INSNS_7x5,
            .flags       = PPC_FLAGS_7x5,
            .msr_mask    = 0x000000000007FF77,
        },
#endif
        /* MPC750P (G3) */
        {
            .name        = "750p",
            .pvr         = CPU_PPC_74xP,
            .pvr_mask    = 0xFFFFFFFF,
            .insns_flags = PPC_INSNS_7x0,
            .flags       = PPC_FLAGS_7x0,
            .msr_mask    = 0x000000000007FF77,
        },
#if defined (TODO)
        /* MPC755P (G3) */
        {
            .name        = "755p",
            .pvr         = CPU_PPC_74xP,
            .pvr_mask    = 0xFFFFF000,
            .insns_flags = PPC_INSNS_7x5,
            .flags       = PPC_FLAGS_7x5,
            .msr_mask    = 0x000000000007FF77,
        },
#endif
        /* IBM 750CXe (G3 embedded) */
        {
            .name        = "750cxe",
            .pvr         = CPU_PPC_750CXE,
            .pvr_mask    = 0xFFFFFFFF,
            .insns_flags = PPC_INSNS_7x0,
            .flags       = PPC_FLAGS_7x0,
            .msr_mask    = 0x000000000007FF77,
        },
        /* IBM 750FX (G3 embedded) */
        {
            .name        = "750fx",
            .pvr         = CPU_PPC_750FX,
            .pvr_mask    = 0xFFFFFFFF,
            .insns_flags = PPC_INSNS_7x0,
            .flags       = PPC_FLAGS_7x0,
            .msr_mask    = 0x000000000007FF77,
        },
        /* IBM 750GX (G3 embedded) */
        {
            .name        = "750gx",
            .pvr         = CPU_PPC_750GX,
            .pvr_mask    = 0xFFFFFFFF,
            .insns_flags = PPC_INSNS_7x0,
            .flags       = PPC_FLAGS_7x0,
            .msr_mask    = 0x000000000007FF77,
        },
#if defined (TODO)
        /* generic G4 */
        {
            .name        = "G4",
            .pvr         = CPU_PPC_7400,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_74xx,
            .flags       = PPC_FLAGS_74xx,
            .msr_mask    = 0x000000000205FF77,
        },
#endif
#if defined (TODO)
        /* PowerPC 7400 (G4) */
        {
            .name        = "7400",
            .pvr         = CPU_PPC_7400,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_74xx,
            .flags       = PPC_FLAGS_74xx,
            .msr_mask    = 0x000000000205FF77,
        },
        {
            .name        = "Max",
            .pvr         = CPU_PPC_7400,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_74xx,
            .flags       = PPC_FLAGS_74xx,
            .msr_mask    = 0x000000000205FF77,
        },
#endif
#if defined (TODO)
        /* PowerPC 7410 (G4) */
        {
            .name        = "7410",
            .pvr         = CPU_PPC_7410,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_74xx,
            .flags       = PPC_FLAGS_74xx,
            .msr_mask    = 0x000000000205FF77,
        },
        {
            .name        = "Nitro",
            .pvr         = CPU_PPC_7410,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_74xx,
            .flags       = PPC_FLAGS_74xx,
            .msr_mask    = 0x000000000205FF77,
        },
#endif
        /* XXX: 7441 */
        /* XXX: 7445 */
        /* XXX: 7447 */
        /* XXX: 7447A */
#if defined (TODO)
        /* PowerPC 7450 (G4) */
        {
            .name        = "7450",
            .pvr         = CPU_PPC_7450,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_74xx,
            .flags       = PPC_FLAGS_74xx,
            .msr_mask    = 0x000000000205FF77,
        },
        {
            .name        = "Vger",
            .pvr         = CPU_PPC_7450,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_74xx,
            .flags       = PPC_FLAGS_74xx,
            .msr_mask    = 0x000000000205FF77,
        },
#endif
        /* XXX: 7451 */
#if defined (TODO)
        /* PowerPC 7455 (G4) */
        {
            .name        = "7455",
            .pvr         = CPU_PPC_7455,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_74xx,
            .flags       = PPC_FLAGS_74xx,
            .msr_mask    = 0x000000000205FF77,
        },
        {
            .name        = "Apollo 6",
            .pvr         = CPU_PPC_7455,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_74xx,
            .flags       = PPC_FLAGS_74xx,
            .msr_mask    = 0x000000000205FF77,
        },
#endif
#if defined (TODO)
        /* PowerPC 7457 (G4) */
        {
            .name        = "7457",
            .pvr         = CPU_PPC_7457,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_74xx,
            .flags       = PPC_FLAGS_74xx,
            .msr_mask    = 0x000000000205FF77,
        },
        {
            .name        = "Apollo 7",
            .pvr         = CPU_PPC_7457,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_74xx,
            .flags       = PPC_FLAGS_74xx,
            .msr_mask    = 0x000000000205FF77,
        },
#endif
#if defined (TODO)
        /* PowerPC 7457A (G4) */
        {
            .name        = "7457A",
            .pvr         = CPU_PPC_7457A,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_74xx,
            .flags       = PPC_FLAGS_74xx,
            .msr_mask    = 0x000000000205FF77,
        },
        {
            .name        = "Apollo 7 PM",
            .pvr         = CPU_PPC_7457A,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_74xx,
            .flags       = PPC_FLAGS_74xx,
            .msr_mask    = 0x000000000205FF77,
        },
#endif
        /* 64 bits PowerPC */
#if defined (TARGET_PPC64)
#if defined (TODO)
        /* PowerPC 620 */
        {
            .name        = "620",
            .pvr         = CPU_PPC_620,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_620,
            .flags       = PPC_FLAGS_620,
            .msr_mask    = 0x800000000005FF73,
        },
#endif
#if defined (TODO)
        /* PowerPC 630 (POWER3) */
        {
            .name        = "630",
            .pvr         = CPU_PPC_630,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_630,
            .flags       = PPC_FLAGS_630,
            .msr_mask    = xxx,
        }
        {
            .name        = "POWER3",
            .pvr         = CPU_PPC_630,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_630,
            .flags       = PPC_FLAGS_630,
            .msr_mask    = xxx,
        }
#endif
#if defined (TODO)
        /* PowerPC 631 (Power 3+)*/
        {
            .name        = "631",
            .pvr         = CPU_PPC_631,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_631,
            .flags       = PPC_FLAGS_631,
            .msr_mask    = xxx,
        },
        {
            .name        = "POWER3+",
            .pvr         = CPU_PPC_631,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_631,
            .flags       = PPC_FLAGS_631,
            .msr_mask    = xxx,
        },
#endif
#if defined (TODO)
        /* POWER4 */
        {
            .name        = "POWER4",
            .pvr         = CPU_PPC_POWER4,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_POWER4,
            .flags       = PPC_FLAGS_POWER4,
            .msr_mask    = xxx,
        },
#endif
#if defined (TODO)
        /* POWER4p */
        {
            .name        = "POWER4+",
            .pvr         = CPU_PPC_POWER4P,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_POWER4,
            .flags       = PPC_FLAGS_POWER4,
            .msr_mask    = xxx,
        },
#endif
#if defined (TODO)
        /* POWER5 */
        {
            .name        = "POWER5",
            .pvr         = CPU_PPC_POWER5,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_POWER5,
            .flags       = PPC_FLAGS_POWER5,
            .msr_mask    = xxx,
        },
#endif
#if defined (TODO)
        /* POWER5+ */
        {
            .name        = "POWER5+",
            .pvr         = CPU_PPC_POWER5P,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_POWER5,
            .flags       = PPC_FLAGS_POWER5,
            .msr_mask    = xxx,
        },
#endif
#if defined (TODO)
        /* PowerPC 970 */
        {
            .name        = "970",
            .pvr         = CPU_PPC_970,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_970,
            .flags       = PPC_FLAGS_970,
            .msr_mask    = 0x900000000204FF36,
        },
#endif
#if defined (TODO)
        /* PowerPC 970FX (G5) */
        {
            .name        = "970fx",
            .pvr         = CPU_PPC_970FX,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_970FX,
            .flags       = PPC_FLAGS_970FX,
            .msr_mask    = 0x800000000204FF36,
        },
#endif
#if defined (TODO)
        /* RS64 (Apache/A35) */
        /* This one seems to support the whole POWER2 instruction set
         * and the PowerPC 64 one.
         */
        {
            .name        = "RS64",
            .pvr         = CPU_PPC_RS64,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_RS64,
            .flags       = PPC_FLAGS_RS64,
            .msr_mask    = xxx,
        },
        {
            .name        = "Apache",
            .pvr         = CPU_PPC_RS64,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_RS64,
            .flags       = PPC_FLAGS_RS64,
            .msr_mask    = xxx,
        },
        {
            .name        = "A35",
            .pvr         = CPU_PPC_RS64,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_RS64,
            .flags       = PPC_FLAGS_RS64,
            .msr_mask    = xxx,
        },
#endif
#if defined (TODO)
        /* RS64-II (NorthStar/A50) */
        {
            .name        = "RS64-II",
            .pvr         = CPU_PPC_RS64II,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_RS64,
            .flags       = PPC_FLAGS_RS64,
            .msr_mask    = xxx,
        },
        {
            .name        = "NortStar",
            .pvr         = CPU_PPC_RS64II,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_RS64,
            .flags       = PPC_FLAGS_RS64,
            .msr_mask    = xxx,
        },
        {
            .name        = "A50",
            .pvr         = CPU_PPC_RS64II,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_RS64,
            .flags       = PPC_FLAGS_RS64,
            .msr_mask    = xxx,
        },
#endif
#if defined (TODO)
        /* RS64-III (Pulsar) */
        {
            .name        = "RS64-III",
            .pvr         = CPU_PPC_RS64III,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_RS64,
            .flags       = PPC_FLAGS_RS64,
            .msr_mask    = xxx,
        },
        {
            .name        = "Pulsar",
            .pvr         = CPU_PPC_RS64III,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_RS64,
            .flags       = PPC_FLAGS_RS64,
            .msr_mask    = xxx,
        },
#endif
#if defined (TODO)
        /* RS64-IV (IceStar/IStar/SStar) */
        {
            .name        = "RS64-IV",
            .pvr         = CPU_PPC_RS64IV,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_RS64,
            .flags       = PPC_FLAGS_RS64,
            .msr_mask    = xxx,
        },
        {
            .name        = "IceStar",
            .pvr         = CPU_PPC_RS64IV,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_RS64,
            .flags       = PPC_FLAGS_RS64,
            .msr_mask    = xxx,
        },
        {
            .name        = "IStar",
            .pvr         = CPU_PPC_RS64IV,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_RS64,
            .flags       = PPC_FLAGS_RS64,
            .msr_mask    = xxx,
        },
        {
            .name        = "SStar",
            .pvr         = CPU_PPC_RS64IV,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_RS64,
            .flags       = PPC_FLAGS_RS64,
            .msr_mask    = xxx,
        },
#endif
        /* POWER */
#if defined (TODO)
        /* Original POWER */
        {
            .name        = "POWER",
            .pvr         = CPU_POWER,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_POWER,
            .flags       = PPC_FLAGS_POWER,
            .msr_mask    = xxx,
        },
#endif
#endif /* defined (TARGET_PPC64) */
#if defined (TODO)
        /* POWER2 */
        {
            .name        = "POWER2",
            .pvr         = CPU_POWER2,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_POWER,
            .flags       = PPC_FLAGS_POWER,
            .msr_mask    = xxx,
        },
#endif
        /* Generic PowerPCs */
#if defined (TODO)
        {
            .name        = "ppc64",
            .pvr         = CPU_PPC_970,
            .pvr_mask    = 0xFFFF0000,
            .insns_flags = PPC_INSNS_PPC64,
            .flags       = PPC_FLAGS_PPC64,
            .msr_mask    = 0xA00000000204FF36,
        },
#endif
        {
            .name        = "ppc32",
            .pvr         = CPU_PPC_604,
            .pvr_mask    = 0xFFFFFFFF,
            .insns_flags = PPC_INSNS_PPC32,
            .flags       = PPC_FLAGS_PPC32,
            .msr_mask    = 0x000000000005FF77,
        },
        /* Fallback */
        {
            .name        = "ppc",
            .pvr         = CPU_PPC_604,
            .pvr_mask    = 0xFFFFFFFF,
            .insns_flags = PPC_INSNS_PPC32,
            .flags       = PPC_FLAGS_PPC32,
            .msr_mask    = 0x000000000005FF77,
        },
    };

int ppc_find_by_name (const unsigned char *name, ppc_def_t **def)
{
    int i, ret;

    ret = -1;
    *def = NULL;
    for (i = 0; strcmp(ppc_defs[i].name, "ppc") != 0; i++) {
        if (strcasecmp(name, ppc_defs[i].name) == 0) {
            *def = &ppc_defs[i];
            ret = 0;
            break;
        }
    }

    return ret;
}

int ppc_find_by_pvr (uint32_t pvr, ppc_def_t **def)
{
    int i, ret;

    ret = -1;
    *def = NULL;
    for (i = 0; ppc_defs[i].name != NULL; i++) {
        if ((pvr & ppc_defs[i].pvr_mask) ==
            (ppc_defs[i].pvr & ppc_defs[i].pvr_mask)) {
            *def = &ppc_defs[i];
            ret = 0;
            break;
        }
    }

    return ret;
}

void ppc_cpu_list (FILE *f, int (*cpu_fprintf)(FILE *f, const char *fmt, ...))
{
    int i;

    for (i = 0; ; i++) {
        (*cpu_fprintf)(f, "PowerPC %16s PVR %08x mask %08x\n",
                       ppc_defs[i].name,
                       ppc_defs[i].pvr, ppc_defs[i].pvr_mask);
        if (strcmp(ppc_defs[i].name, "ppc") == 0)
            break;
    }
}
