diff --git a/target-lm32/cpu-qom.h b/target-lm32/cpu-qom.h
index 723f604..9f15093 100644
--- a/target-lm32/cpu-qom.h
+++ b/target-lm32/cpu-qom.h
@@ -60,6 +60,12 @@ typedef struct LM32CPU {
     /*< public >*/
 
     CPULM32State env;
+
+    uint32_t revision;
+    uint8_t num_interrupts;
+    uint8_t num_breakpoints;
+    uint8_t num_watchpoints;
+    uint32_t features;
 } LM32CPU;
 
 static inline LM32CPU *lm32_env_get_cpu(CPULM32State *env)
diff --git a/target-lm32/cpu.c b/target-lm32/cpu.c
index 869878c..2b207ad 100644
--- a/target-lm32/cpu.c
+++ b/target-lm32/cpu.c
@@ -29,6 +29,87 @@ static void lm32_cpu_set_pc(CPUState *cs, vaddr value)
     cpu->env.pc = value;
 }
 
+/* Sort alphabetically by type name. */
+static gint lm32_cpu_list_compare(gconstpointer a, gconstpointer b)
+{
+    ObjectClass *class_a = (ObjectClass *)a;
+    ObjectClass *class_b = (ObjectClass *)b;
+    const char *name_a, *name_b;
+
+    name_a = object_class_get_name(class_a);
+    name_b = object_class_get_name(class_b);
+    return strcmp(name_a, name_b);
+}
+
+static void lm32_cpu_list_entry(gpointer data, gpointer user_data)
+{
+    ObjectClass *oc = data;
+    CPUListState *s = user_data;
+    const char *typename = object_class_get_name(oc);
+    char *name;
+
+    name = g_strndup(typename, strlen(typename) - strlen("-" TYPE_LM32_CPU));
+    (*s->cpu_fprintf)(s->file, "  %s\n", name);
+    g_free(name);
+}
+
+
+void lm32_cpu_list(FILE *f, fprintf_function cpu_fprintf)
+{
+    CPUListState s = {
+        .file = f,
+        .cpu_fprintf = cpu_fprintf,
+    };
+    GSList *list;
+
+    list = object_class_get_list(TYPE_LM32_CPU, false);
+    list = g_slist_sort(list, lm32_cpu_list_compare);
+    (*cpu_fprintf)(f, "Available CPUs:\n");
+    g_slist_foreach(list, lm32_cpu_list_entry, &s);
+    g_slist_free(list);
+}
+
+static void lm32_cpu_init_cfg_reg(LM32CPU *cpu)
+{
+    CPULM32State *env = &cpu->env;
+    uint32_t cfg = 0;
+
+    if (cpu->features & LM32_FEATURE_MULTIPLY) {
+        cfg |= CFG_M;
+    }
+
+    if (cpu->features & LM32_FEATURE_DIVIDE) {
+        cfg |= CFG_D;
+    }
+
+    if (cpu->features & LM32_FEATURE_SHIFT) {
+        cfg |= CFG_S;
+    }
+
+    if (cpu->features & LM32_FEATURE_SIGN_EXTEND) {
+        cfg |= CFG_X;
+    }
+
+    if (cpu->features & LM32_FEATURE_I_CACHE) {
+        cfg |= CFG_IC;
+    }
+
+    if (cpu->features & LM32_FEATURE_D_CACHE) {
+        cfg |= CFG_DC;
+    }
+
+    if (cpu->features & LM32_FEATURE_CYCLE_COUNT) {
+        cfg |= CFG_CC;
+    }
+
+    cfg |= (cpu->num_interrupts << CFG_INT_SHIFT);
+    cfg |= (cpu->num_breakpoints << CFG_BP_SHIFT);
+    cfg |= (cpu->num_watchpoints << CFG_WP_SHIFT);
+    cfg |= (cpu->revision << CFG_REV_SHIFT);
+
+    env->cfg = cfg;
+}
+
 /* CPUClass::reset() */
 static void lm32_cpu_reset(CPUState *s)
 {
@@ -41,6 +122,7 @@ static void lm32_cpu_reset(CPUState *s)
     /* reset cpu state */
     memset(env, 0, offsetof(CPULM32State, breakpoints));
 
+    lm32_cpu_init_cfg_reg(cpu);
     tlb_flush(env, 1);
 }
 
@@ -74,6 +156,91 @@ static void lm32_cpu_initfn(Object *obj)
     }
 }
 
+static void lm32_basic_cpu_initfn(Object *obj)
+{
+    LM32CPU *cpu = LM32_CPU(obj);
+
+    cpu->revision = 3;
+    cpu->num_interrupts = 32;
+    cpu->num_breakpoints = 4;
+    cpu->num_watchpoints = 4;
+    cpu->features = LM32_FEATURE_SHIFT
+                  | LM32_FEATURE_SIGN_EXTEND
+                  | LM32_FEATURE_CYCLE_COUNT;
+}
+
+static void lm32_standard_cpu_initfn(Object *obj)
+{
+    LM32CPU *cpu = LM32_CPU(obj);
+
+    cpu->revision = 3;
+    cpu->num_interrupts = 32;
+    cpu->num_breakpoints = 4;
+    cpu->num_watchpoints = 4;
+    cpu->features = LM32_FEATURE_MULTIPLY
+                  | LM32_FEATURE_DIVIDE
+                  | LM32_FEATURE_SHIFT
+                  | LM32_FEATURE_SIGN_EXTEND
+                  | LM32_FEATURE_I_CACHE
+                  | LM32_FEATURE_CYCLE_COUNT;
+}
+
+static void lm32_full_cpu_initfn(Object *obj)
+{
+    LM32CPU *cpu = LM32_CPU(obj);
+
+    cpu->revision = 3;
+    cpu->num_interrupts = 32;
+    cpu->num_breakpoints = 4;
+    cpu->num_watchpoints = 4;
+    cpu->features = LM32_FEATURE_MULTIPLY
+                  | LM32_FEATURE_DIVIDE
+                  | LM32_FEATURE_SHIFT
+                  | LM32_FEATURE_SIGN_EXTEND
+                  | LM32_FEATURE_I_CACHE
+                  | LM32_FEATURE_D_CACHE
+                  | LM32_FEATURE_CYCLE_COUNT;
+}
+
+typedef struct LM32CPUInfo {
+    const char *name;
+    void (*initfn)(Object *obj);
+} LM32CPUInfo;
+
+static const LM32CPUInfo lm32_cpus[] = {
+    {
+        .name = "lm32-basic",
+        .initfn = lm32_basic_cpu_initfn,
+    },
+    {
+        .name = "lm32-standard",
+        .initfn = lm32_standard_cpu_initfn,
+    },
+    {
+        .name = "lm32-full",
+        .initfn = lm32_full_cpu_initfn,
+    },
+};
+
+static ObjectClass *lm32_cpu_class_by_name(const char *cpu_model)
+{
+    ObjectClass *oc;
+    char *typename;
+
+    if (cpu_model == NULL) {
+        return NULL;
+    }
+
+    typename = g_strdup_printf("%s-" TYPE_LM32_CPU, cpu_model);
+    oc = object_class_by_name(typename);
+    g_free(typename);
+    if (oc != NULL && (!object_class_dynamic_cast(oc, TYPE_LM32_CPU) ||
+                       object_class_is_abstract(oc))) {
+        oc = NULL;
+    }
+    return oc;
+}
+
 static void lm32_cpu_class_init(ObjectClass *oc, void *data)
 {
     LM32CPUClass *lcc = LM32_CPU_CLASS(oc);
@@ -86,6 +253,7 @@ static void lm32_cpu_class_init(ObjectClass *oc, void *data)
     lcc->parent_reset = cc->reset;
     cc->reset = lm32_cpu_reset;
 
+    cc->class_by_name = lm32_cpu_class_by_name;
     cc->do_interrupt = lm32_cpu_do_interrupt;
     cc->dump_state = lm32_cpu_dump_state;
     cc->set_pc = lm32_cpu_set_pc;
@@ -98,19 +266,36 @@ static void lm32_cpu_class_init(ObjectClass *oc, void *data)
     cc->gdb_num_core_regs = 32 + 7;
 }
 
+static void lm32_register_cpu_type(const LM32CPUInfo *info)
+{
+    TypeInfo type_info = {
+        .parent = TYPE_LM32_CPU,
+        .instance_init = info->initfn,
+    };
+
+    type_info.name = g_strdup_printf("%s-" TYPE_LM32_CPU, info->name);
+    type_register(&type_info);
+    g_free((void *)type_info.name);
+}
+
 static const TypeInfo lm32_cpu_type_info = {
     .name = TYPE_LM32_CPU,
     .parent = TYPE_CPU,
     .instance_size = sizeof(LM32CPU),
     .instance_init = lm32_cpu_initfn,
-    .abstract = false,
+    .abstract = true,
     .class_size = sizeof(LM32CPUClass),
     .class_init = lm32_cpu_class_init,
 };
 
 static void lm32_cpu_register_types(void)
 {
+    int i;
+
     type_register_static(&lm32_cpu_type_info);
+    for (i = 0; i < ARRAY_SIZE(lm32_cpus); i++) {
+        lm32_register_cpu_type(&lm32_cpus[i]);
+    }
 }
 
 type_init(lm32_cpu_register_types)
diff --git a/target-lm32/cpu.h b/target-lm32/cpu.h
index dbfe043..101df80 100644
--- a/target-lm32/cpu.h
+++ b/target-lm32/cpu.h
@@ -177,23 +177,20 @@ struct CPULM32State {
     DeviceState *juart_state;
 
     /* processor core features */
-    uint32_t features;
     uint32_t flags;
-    uint8_t num_bps;
-    uint8_t num_wps;
 
 };
 
 #include "cpu-qom.h"
 
 LM32CPU *cpu_lm32_init(const char *cpu_model);
-void cpu_lm32_list(FILE *f, fprintf_function cpu_fprintf);
 int cpu_lm32_exec(CPULM32State *s);
 /* you can call this signal handler from your SIGBUS and SIGSEGV
    signal handlers to inform the virtual CPU of exceptions. non zero
    is returned if the signal was handled by the virtual CPU.  */
 int cpu_lm32_signal_handler(int host_signum, void *pinfo,
                           void *puc);
+void lm32_cpu_list(FILE *f, fprintf_function cpu_fprintf);
 void lm32_translate_init(void);
 void cpu_lm32_set_phys_msb_ignore(CPULM32State *env, int value);
 
@@ -206,7 +203,7 @@ static inline CPULM32State *cpu_init(const char *cpu_model)
     return &cpu->env;
 }
 
-#define cpu_list cpu_lm32_list
+#define cpu_list lm32_cpu_list
 #define cpu_exec cpu_lm32_exec
 #define cpu_gen_code cpu_lm32_gen_code
 #define cpu_signal_handler cpu_lm32_signal_handler
diff --git a/target-lm32/helper.c b/target-lm32/helper.c
index 15bc615..f85ff2e 100644
--- a/target-lm32/helper.c
+++ b/target-lm32/helper.c
@@ -90,136 +90,16 @@ void lm32_cpu_do_interrupt(CPUState *cs)
     }
 }
 
-typedef struct {
-    const char *name;
-    uint32_t revision;
-    uint8_t num_interrupts;
-    uint8_t num_breakpoints;
-    uint8_t num_watchpoints;
-    uint32_t features;
-} LM32Def;
-
-static const LM32Def lm32_defs[] = {
-    {
-        .name = "lm32-basic",
-        .revision = 3,
-        .num_interrupts = 32,
-        .num_breakpoints = 4,
-        .num_watchpoints = 4,
-        .features = (LM32_FEATURE_SHIFT
-                     | LM32_FEATURE_SIGN_EXTEND
-                     | LM32_FEATURE_CYCLE_COUNT),
-    },
-    {
-        .name = "lm32-standard",
-        .revision = 3,
-        .num_interrupts = 32,
-        .num_breakpoints = 4,
-        .num_watchpoints = 4,
-        .features = (LM32_FEATURE_MULTIPLY
-                     | LM32_FEATURE_DIVIDE
-                     | LM32_FEATURE_SHIFT
-                     | LM32_FEATURE_SIGN_EXTEND
-                     | LM32_FEATURE_I_CACHE
-                     | LM32_FEATURE_CYCLE_COUNT),
-    },
-    {
-        .name = "lm32-full",
-        .revision = 3,
-        .num_interrupts = 32,
-        .num_breakpoints = 4,
-        .num_watchpoints = 4,
-        .features = (LM32_FEATURE_MULTIPLY
-                     | LM32_FEATURE_DIVIDE
-                     | LM32_FEATURE_SHIFT
-                     | LM32_FEATURE_SIGN_EXTEND
-                     | LM32_FEATURE_I_CACHE
-                     | LM32_FEATURE_D_CACHE
-                     | LM32_FEATURE_CYCLE_COUNT),
-    }
-};
-
-void cpu_lm32_list(FILE *f, fprintf_function cpu_fprintf)
-{
-    int i;
-
-    cpu_fprintf(f, "Available CPUs:\n");
-    for (i = 0; i < ARRAY_SIZE(lm32_defs); i++) {
-        cpu_fprintf(f, "  %s\n", lm32_defs[i].name);
-    }
-}
-
-static const LM32Def *cpu_lm32_find_by_name(const char *name)
-{
-    int i;
-
-    for (i = 0; i < ARRAY_SIZE(lm32_defs); i++) {
-        if (strcasecmp(name, lm32_defs[i].name) == 0) {
-            return &lm32_defs[i];
-        }
-    }
-
-    return NULL;
-}
-
-static uint32_t cfg_by_def(const LM32Def *def)
-{
-    uint32_t cfg = 0;
-
-    if (def->features & LM32_FEATURE_MULTIPLY) {
-        cfg |= CFG_M;
-    }
-
-    if (def->features & LM32_FEATURE_DIVIDE) {
-        cfg |= CFG_D;
-    }
-
-    if (def->features & LM32_FEATURE_SHIFT) {
-        cfg |= CFG_S;
-    }
-
-    if (def->features & LM32_FEATURE_SIGN_EXTEND) {
-        cfg |= CFG_X;
-    }
-
-    if (def->features & LM32_FEATURE_I_CACHE) {
-        cfg |= CFG_IC;
-    }
-
-    if (def->features & LM32_FEATURE_D_CACHE) {
-        cfg |= CFG_DC;
-    }
-
-    if (def->features & LM32_FEATURE_CYCLE_COUNT) {
-        cfg |= CFG_CC;
-    }
-
-    cfg |= (def->num_interrupts << CFG_INT_SHIFT);
-    cfg |= (def->num_breakpoints << CFG_BP_SHIFT);
-    cfg |= (def->num_watchpoints << CFG_WP_SHIFT);
-    cfg |= (def->revision << CFG_REV_SHIFT);
-
-    return cfg;
-}
-
 LM32CPU *cpu_lm32_init(const char *cpu_model)
 {
     LM32CPU *cpu;
-    CPULM32State *env;
-    const LM32Def *def;
+    ObjectClass *oc;
 
-    def = cpu_lm32_find_by_name(cpu_model);
-    if (!def) {
+    oc = cpu_class_by_name(TYPE_LM32_CPU, cpu_model);
+    if (oc == NULL) {
         return NULL;
     }
-
-    cpu = LM32_CPU(object_new(TYPE_LM32_CPU));
-    env = &cpu->env;
-
-    env->features = def->features;
-    env->num_bps = def->num_breakpoints;
-    env->num_wps = def->num_watchpoints;
-    env->cfg = cfg_by_def(def);
+    cpu = LM32_CPU(object_new(object_class_get_name(oc)));
 
     object_property_set_bool(OBJECT(cpu), true, "realized", NULL);
 
diff --git a/target-lm32/translate.c b/target-lm32/translate.c
index e292e1c..93075e4 100644
--- a/target-lm32/translate.c
+++ b/target-lm32/translate.c
@@ -64,7 +64,6 @@ enum {
 
 /* This is the state at translation time.  */
 typedef struct DisasContext {
-    CPULM32State *env;
     target_ulong pc;
 
     /* Decoder.  */
@@ -82,6 +81,10 @@ typedef struct DisasContext {
 
     struct TranslationBlock *tb;
     int singlestep_enabled;
+
+    uint32_t features;
+    uint8_t num_breakpoints;
+    uint8_t num_watchpoints;
 } DisasContext;
 
 static const char *regnames[] = {
@@ -420,7 +423,7 @@ static void dec_divu(DisasContext *dc)
 
     LOG_DIS("divu r%d, r%d, r%d\n", dc->r2, dc->r0, dc->r1);
 
-    if (!(dc->env->features & LM32_FEATURE_DIVIDE)) {
+    if (!(dc->features & LM32_FEATURE_DIVIDE)) {
         qemu_log_mask(LOG_GUEST_ERROR, "hardware divider is not available\n");
         return;
     }
@@ -499,7 +502,7 @@ static void dec_modu(DisasContext *dc)
 
     LOG_DIS("modu r%d, r%d, %d\n", dc->r2, dc->r0, dc->r1);
 
-    if (!(dc->env->features & LM32_FEATURE_DIVIDE)) {
+    if (!(dc->features & LM32_FEATURE_DIVIDE)) {
         qemu_log_mask(LOG_GUEST_ERROR, "hardware divider is not available\n");
         return;
     }
@@ -521,7 +524,7 @@ static void dec_mul(DisasContext *dc)
         LOG_DIS("mul r%d, r%d, r%d\n", dc->r2, dc->r0, dc->r1);
     }
 
-    if (!(dc->env->features & LM32_FEATURE_MULTIPLY)) {
+    if (!(dc->features & LM32_FEATURE_MULTIPLY)) {
         qemu_log_mask(LOG_GUEST_ERROR,
                       "hardware multiplier is not available\n");
         return;
@@ -675,7 +678,7 @@ static void dec_sextb(DisasContext *dc)
 {
     LOG_DIS("sextb r%d, r%d\n", dc->r2, dc->r0);
 
-    if (!(dc->env->features & LM32_FEATURE_SIGN_EXTEND)) {
+    if (!(dc->features & LM32_FEATURE_SIGN_EXTEND)) {
         qemu_log_mask(LOG_GUEST_ERROR,
                       "hardware sign extender is not available\n");
         return;
@@ -688,7 +691,7 @@ static void dec_sexth(DisasContext *dc)
 {
     LOG_DIS("sexth r%d, r%d\n", dc->r2, dc->r0);
 
-    if (!(dc->env->features & LM32_FEATURE_SIGN_EXTEND)) {
+    if (!(dc->features & LM32_FEATURE_SIGN_EXTEND)) {
         qemu_log_mask(LOG_GUEST_ERROR,
                       "hardware sign extender is not available\n");
         return;
@@ -717,7 +720,7 @@ static void dec_sl(DisasContext *dc)
         LOG_DIS("sl r%d, r%d, r%d\n", dc->r2, dc->r0, dc->r1);
     }
 
-    if (!(dc->env->features & LM32_FEATURE_SHIFT)) {
+    if (!(dc->features & LM32_FEATURE_SHIFT)) {
         qemu_log_mask(LOG_GUEST_ERROR, "hardware shifter is not available\n");
         return;
     }
@@ -740,7 +743,7 @@ static void dec_sr(DisasContext *dc)
         LOG_DIS("sr r%d, r%d, r%d\n", dc->r2, dc->r0, dc->r1);
     }
 
-    if (!(dc->env->features & LM32_FEATURE_SHIFT)) {
+    if (!(dc->features & LM32_FEATURE_SHIFT)) {
         if (dc->format == OP_FMT_RI) {
             /* TODO: check r1 == 1 during runtime */
         } else {
@@ -770,7 +773,7 @@ static void dec_sru(DisasContext *dc)
         LOG_DIS("sru r%d, r%d, r%d\n", dc->r2, dc->r0, dc->r1);
     }
 
-    if (!(dc->env->features & LM32_FEATURE_SHIFT)) {
+    if (!(dc->features & LM32_FEATURE_SHIFT)) {
         if (dc->format == OP_FMT_RI) {
             /* TODO: check r1 == 1 during runtime */
         } else {
@@ -880,7 +883,7 @@ static void dec_wcsr(DisasContext *dc)
     case CSR_BP2:
     case CSR_BP3:
         no = dc->csr - CSR_BP0;
-        if (dc->env->num_bps <= no) {
+        if (dc->num_breakpoints <= no) {
             qemu_log_mask(LOG_GUEST_ERROR,
                           "breakpoint #%i is not available\n", no);
             break;
@@ -892,7 +895,7 @@ static void dec_wcsr(DisasContext *dc)
     case CSR_WP2:
     case CSR_WP3:
         no = dc->csr - CSR_WP0;
-        if (dc->env->num_wps <= no) {
+        if (dc->num_watchpoints <= no) {
             qemu_log_mask(LOG_GUEST_ERROR,
                           "watchpoint #%i is not available\n", no);
             break;
@@ -1033,7 +1036,9 @@ void gen_intermediate_code_internal(LM32CPU *cpu,
     int max_insns;
 
     pc_start = tb->pc;
-    dc->env = env;
+    dc->features = cpu->features;
+    dc->num_breakpoints = cpu->num_breakpoints;
+    dc->num_watchpoints = cpu->num_watchpoints;
     dc->tb = tb;
 
     gen_opc_end = tcg_ctx.gen_opc_buf + OPC_MAX_SIZE;
