From patchwork Wed Jun 8 21:11:13 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michael Rolnik X-Patchwork-Id: 632509 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3rQ1VV0sJcz9sDX for ; Thu, 9 Jun 2016 07:17:38 +1000 (AEST) Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b=IrDaXh3x; dkim-atps=neutral Received: from localhost ([::1]:59550 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1bAkr2-00080J-3D for incoming@patchwork.ozlabs.org; Wed, 08 Jun 2016 17:17:36 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:48484) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1bAklT-0002v2-0J for qemu-devel@nongnu.org; Wed, 08 Jun 2016 17:11:56 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1bAklO-000719-6e for qemu-devel@nongnu.org; Wed, 08 Jun 2016 17:11:50 -0400 Received: from mail-wm0-x242.google.com ([2a00:1450:400c:c09::242]:33749) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1bAklK-0006zn-A1 for qemu-devel@nongnu.org; Wed, 08 Jun 2016 17:11:46 -0400 Received: by mail-wm0-x242.google.com with SMTP id r5so5984401wmr.0 for ; Wed, 08 Jun 2016 14:11:42 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=7/nz455/o7sRQNhZKvzj87QKCGyRMw5MpDKEVDQs/rE=; b=IrDaXh3xSBE3YU0GBXArQfCeWJnx3FLXbJB2WfuQoELr9cMQywoB6br9KHrkY4AvLL afyqZFz8Ys1WnKw/RB6f+KOPqxxZMSUkF9FvsNh9NbNJnwX7jCUB2cKm2b/0DxJAoiWu hWOuaQEP2cZSiz9cdGWP4XP+URl242iRVtiD7RYPbU/qg/bigKpxWsqI0DIk0TzRdESr fgG6D/gZdqOP3s3HYG/rYjgkmA+As4XT+113Jt+v+ZS0XNtsts27Wwt7xZKuszlhHWhU saCr+MIlCQJ2nvQDWgues9D5w3kV/1mZfxkQClB0B6IdLTQ1QL7jw1KCNLSV8IyQXELd aQwg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=7/nz455/o7sRQNhZKvzj87QKCGyRMw5MpDKEVDQs/rE=; b=Jh4vqww1izPiY47eR5lgq5jYFogb8d6f7nQega5XXNQultC2cRDNeDhMt8xgSOc0MP tlGKxFbyScliuk9PhFxFrEUJGtJLEHWmTQ55WxcQmUZnj8HqlrcsfgwHQXPUQSOPEQ9c mFo02o5ssyLioEEvBSInHVdRR5iDS/IboFgR0HXFMhU7g3qM9nAqudM1Tk3NTHDHkKNv 3AKE3qdXihSZlApKBfchlkOxh7unwnGFIYNcewDU/GC70iyruOH1RbdiOXWUXfSvuYrj lTgRgAXy1/2aOdwbZdc1Rb9WhMCvwdbnJ8mYv0K0sGtpSEEjQzXYK3U8qUXuWgVW+agT u6nw== X-Gm-Message-State: ALyK8tLY0uHa2b195SbOnWzThkI865r6MDagIPWcjrD13xDs51yBm4Vo6jIeBygwpcya+w== X-Received: by 10.28.133.210 with SMTP id h201mr6435277wmd.86.1465420301222; Wed, 08 Jun 2016 14:11:41 -0700 (PDT) Received: from a0999b0126e1.ant.amazon.com ([5.102.195.79]) by smtp.gmail.com with ESMTPSA id uq7sm3465032wjc.19.2016.06.08.14.11.39 (version=TLS1 cipher=AES128-SHA bits=128/128); Wed, 08 Jun 2016 14:11:40 -0700 (PDT) From: Michael Rolnik X-Google-Original-From: Michael Rolnik To: qemu-devel@nongnu.org Date: Thu, 9 Jun 2016 00:11:13 +0300 Message-Id: <1465420282-68685-2-git-send-email-rolnik@amazon.com> X-Mailer: git-send-email 2.4.9 (Apple Git-60) In-Reply-To: <1465420282-68685-1-git-send-email-rolnik@amazon.com> References: <1465420282-68685-1-git-send-email-rolnik@amazon.com> X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 2a00:1450:400c:c09::242 Subject: [Qemu-devel] [PATCH v5 01/10] target-avr: AVR cores support is added. 1. basic CPU structure 2. registers 3. no instructions X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: peter.maydell@linaro.org, Michael Rolnik , rth@twiddle.net Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: "Qemu-devel" From: Michael Rolnik From: Michael Rolnik Signed-off-by: Michael Rolnik --- arch_init.c | 2 + configure | 7 +- default-configs/avr-softmmu.mak | 21 +++ include/disas/bfd.h | 6 + include/sysemu/arch_init.h | 1 + target-avr/Makefile.objs | 23 +++ target-avr/cpu-qom.h | 84 +++++++++++ target-avr/cpu.c | 304 ++++++++++++++++++++++++++++++++++++++++ target-avr/cpu.h | 158 +++++++++++++++++++++ target-avr/gdbstub.c | 99 +++++++++++++ target-avr/helper.c | 87 ++++++++++++ target-avr/helper.h | 21 +++ target-avr/machine.c | 54 +++++++ target-avr/machine.h | 21 +++ target-avr/translate.c | 290 ++++++++++++++++++++++++++++++++++++++ 15 files changed, 1177 insertions(+), 1 deletion(-) create mode 100644 default-configs/avr-softmmu.mak create mode 100644 target-avr/Makefile.objs create mode 100644 target-avr/cpu-qom.h create mode 100644 target-avr/cpu.c create mode 100644 target-avr/cpu.h create mode 100644 target-avr/gdbstub.c create mode 100644 target-avr/helper.c create mode 100644 target-avr/helper.h create mode 100644 target-avr/machine.c create mode 100644 target-avr/machine.h create mode 100644 target-avr/translate.c diff --git a/arch_init.c b/arch_init.c index fa05973..be6e6de 100644 --- a/arch_init.c +++ b/arch_init.c @@ -80,6 +80,8 @@ int graphic_depth = 32; #define QEMU_ARCH QEMU_ARCH_UNICORE32 #elif defined(TARGET_TRICORE) #define QEMU_ARCH QEMU_ARCH_TRICORE +#elif defined(TARGET_AVR) +#define QEMU_ARCH QEMU_ARCH_AVR #endif const uint32_t arch_type = QEMU_ARCH; diff --git a/configure b/configure index 8c2f90b..41e550e 100755 --- a/configure +++ b/configure @@ -5603,7 +5603,7 @@ target_name=$(echo $target | cut -d '-' -f 1) target_bigendian="no" case "$target_name" in - armeb|lm32|m68k|microblaze|mips|mipsn32|mips64|moxie|or32|ppc|ppcemb|ppc64|ppc64abi32|s390x|sh4eb|sparc|sparc64|sparc32plus|xtensaeb) + avr|armeb|lm32|m68k|microblaze|mips|mipsn32|mips64|moxie|or32|ppc|ppcemb|ppc64|ppc64abi32|s390x|sh4eb|sparc|sparc64|sparc32plus|xtensaeb) target_bigendian=yes ;; esac @@ -5652,6 +5652,8 @@ case "$target_name" in x86_64) TARGET_BASE_ARCH=i386 ;; + avr) + ;; alpha) ;; arm|armeb) @@ -5848,6 +5850,9 @@ disas_config() { for i in $ARCH $TARGET_BASE_ARCH ; do case "$i" in + avr) + disas_config "AVR" + ;; alpha) disas_config "ALPHA" ;; diff --git a/default-configs/avr-softmmu.mak b/default-configs/avr-softmmu.mak new file mode 100644 index 0000000..003465d --- /dev/null +++ b/default-configs/avr-softmmu.mak @@ -0,0 +1,21 @@ +# +# QEMU AVR CPU +# +# Copyright (c) 2016 Michael Rolnik +# +# 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.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, see +# +# + +# Default configuration for avr-softmmu diff --git a/include/disas/bfd.h b/include/disas/bfd.h index a112e9c..b13fc53 100644 --- a/include/disas/bfd.h +++ b/include/disas/bfd.h @@ -213,6 +213,12 @@ enum bfd_architecture #define bfd_mach_m32r 0 /* backwards compatibility */ bfd_arch_mn10200, /* Matsushita MN10200 */ bfd_arch_mn10300, /* Matsushita MN10300 */ + bfd_arch_avr, /* Atmel AVR microcontrollers. */ +#define bfd_mach_avr1 1 +#define bfd_mach_avr2 2 +#define bfd_mach_avr3 3 +#define bfd_mach_avr4 4 +#define bfd_mach_avr5 5 bfd_arch_cris, /* Axis CRIS */ #define bfd_mach_cris_v0_v10 255 #define bfd_mach_cris_v32 32 diff --git a/include/sysemu/arch_init.h b/include/sysemu/arch_init.h index d690dfa..8c75777 100644 --- a/include/sysemu/arch_init.h +++ b/include/sysemu/arch_init.h @@ -23,6 +23,7 @@ enum { QEMU_ARCH_UNICORE32 = (1 << 14), QEMU_ARCH_MOXIE = (1 << 15), QEMU_ARCH_TRICORE = (1 << 16), + QEMU_ARCH_AVR = (1 << 17), }; extern const uint32_t arch_type; diff --git a/target-avr/Makefile.objs b/target-avr/Makefile.objs new file mode 100644 index 0000000..2a10104 --- /dev/null +++ b/target-avr/Makefile.objs @@ -0,0 +1,23 @@ +# +# QEMU AVR CPU +# +# Copyright (c) 2016 Michael Rolnik +# +# 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.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, see +# +# + +obj-y += translate.o cpu.o helper.o +obj-y += gdbstub.o +obj-$(CONFIG_SOFTMMU) += machine.o diff --git a/target-avr/cpu-qom.h b/target-avr/cpu-qom.h new file mode 100644 index 0000000..bf588ca --- /dev/null +++ b/target-avr/cpu-qom.h @@ -0,0 +1,84 @@ +/* + * QEMU AVR CPU + * + * Copyright (c) 2016 Michael Rolnik + * + * 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.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * + */ + +#ifndef QEMU_AVR_CPU_QOM_H +#define QEMU_AVR_CPU_QOM_H + +#include "qom/cpu.h" + +#define TYPE_AVR_CPU "avr" + +#define AVR_CPU_CLASS(klass) \ + OBJECT_CLASS_CHECK(AVRCPUClass, (klass), TYPE_AVR_CPU) +#define AVR_CPU(obj) \ + OBJECT_CHECK(AVRCPU, (obj), TYPE_AVR_CPU) +#define AVR_CPU_GET_CLASS(obj) \ + OBJECT_GET_CLASS(AVRCPUClass, (obj), TYPE_AVR_CPU) + +/** +* AVRCPUClass: +* @parent_realize: The parent class' realize handler. +* @parent_reset: The parent class' reset handler. +* @vr: Version Register value. +* +* A AVR CPU model. +*/ +typedef struct AVRCPUClass { + CPUClass parent_class; + + DeviceRealize parent_realize; + void (*parent_reset)(CPUState *cpu); +} AVRCPUClass; + +/** +* AVRCPU: +* @env: #CPUAVRState +* +* A AVR CPU. +*/ +typedef struct AVRCPU { + /*< private >*/ + CPUState parent_obj; + /*< public >*/ + + CPUAVRState env; +} AVRCPU; + +static inline AVRCPU *avr_env_get_cpu(CPUAVRState *env) +{ + return container_of(env, AVRCPU, env); +} + +#define ENV_GET_CPU(e) CPU(avr_env_get_cpu(e)) +#define ENV_OFFSET offsetof(AVRCPU, env) + +#ifndef CONFIG_USER_ONLY +extern const struct VMStateDescription vmstate_avr_cpu; +#endif + +void avr_cpu_do_interrupt(CPUState *cpu); +bool avr_cpu_exec_interrupt(CPUState *cpu, int int_req); +void avr_cpu_dump_state(CPUState *cs, FILE *f, + fprintf_function cpu_fprintf, int flags); +hwaddr avr_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); +int avr_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int avr_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); + +#endif diff --git a/target-avr/cpu.c b/target-avr/cpu.c new file mode 100644 index 0000000..99bd788 --- /dev/null +++ b/target-avr/cpu.c @@ -0,0 +1,304 @@ +/* + * QEMU AVR CPU + * + * Copyright (c) 2016 Michael Rolnik + * + * 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.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "cpu.h" +#include "qemu-common.h" +#include "migration/vmstate.h" +#include "machine.h" + +static void avr_cpu_set_pc(CPUState *cs, vaddr value) +{ + AVRCPU *cpu = AVR_CPU(cs); + + cpu->env.pc_w = value / 2; /* internally PC points to words */ +} + +static bool avr_cpu_has_work(CPUState *cs) +{ + AVRCPU *cpu = AVR_CPU(cs); + CPUAVRState *env = &cpu->env; + + return (cs->interrupt_request + & (CPU_INTERRUPT_HARD + | CPU_INTERRUPT_RESET)) + && cpu_interrupts_enabled(env); +} +static void avr_cpu_synchronize_from_tb(CPUState *cs, TranslationBlock *tb) +{ + AVRCPU *cpu = AVR_CPU(cs); + CPUAVRState *env = &cpu->env; + + env->pc_w = tb->pc / 2; /* internally PC points to words */ +} + +static void avr_cpu_reset(CPUState *s) +{ + AVRCPU *cpu = AVR_CPU(s); + AVRCPUClass *mcc = AVR_CPU_GET_CLASS(cpu); + CPUAVRState *env = &cpu->env; + + mcc->parent_reset(s); + + env->pc_w = 0; + env->sregI = 1; + env->sregC = 0; + env->sregZ = 0; + env->sregN = 0; + env->sregV = 0; + env->sregS = 0; + env->sregH = 0; + env->sregT = 0; + + env->rampD = 0; + env->rampX = 0; + env->rampY = 0; + env->rampZ = 0; + env->eind = 0; + env->sp = 0; + + memset(env->io, 0, sizeof(env->io)); + memset(env->r, 0, sizeof(env->r)); + + tlb_flush(s, 1); +} + +static void avr_cpu_disas_set_info(CPUState *cpu, disassemble_info *info) +{ + info->mach = bfd_arch_avr; + info->print_insn = NULL; +} + +static void avr_cpu_realizefn(DeviceState *dev, Error **errp) +{ + CPUState *cs = CPU(dev); + AVRCPUClass *mcc = AVR_CPU_GET_CLASS(dev); + + qemu_init_vcpu(cs); + cpu_reset(cs); + + mcc->parent_realize(dev, errp); +} + +static void avr_cpu_set_int(void *opaque, int irq, int level) +{ + AVRCPU *cpu = opaque; + CPUAVRState *env = &cpu->env; + CPUState *cs = CPU(cpu); + + uint64_t mask = (1ull << irq); + if (level) { + env->intsrc |= mask; + cpu_interrupt(cs, CPU_INTERRUPT_HARD); + } else { + env->intsrc &= ~mask; + if (env->intsrc == 0) { + cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); + } + } +} + +static void avr_cpu_initfn(Object *obj) +{ + CPUState *cs = CPU(obj); + AVRCPU *cpu = AVR_CPU(obj); + static int inited; + + cs->env_ptr = &cpu->env; + cpu_exec_init(cs, &error_abort); + +#ifndef CONFIG_USER_ONLY + qdev_init_gpio_in(DEVICE(cpu), avr_cpu_set_int, 37); +#endif + + if (tcg_enabled() && !inited) { + inited = 1; + avr_translate_init(); + } +} + +static ObjectClass *avr_cpu_class_by_name(const char *cpu_model) +{ + ObjectClass *oc; + char *typename; + char **cpuname; + + if (!cpu_model) { + return NULL; + } + + cpuname = g_strsplit(cpu_model, ",", 1); + typename = g_strdup_printf("%s-" TYPE_AVR_CPU, cpuname[0]); + oc = object_class_by_name(typename); + + g_strfreev(cpuname); + g_free(typename); + + if (!oc + || !object_class_dynamic_cast(oc, TYPE_AVR_CPU) + || object_class_is_abstract(oc)) { + return NULL; + } + + return oc; +} + +static void avr_cpu_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + CPUClass *cc = CPU_CLASS(oc); + AVRCPUClass *mcc = AVR_CPU_CLASS(oc); + + mcc->parent_realize = dc->realize; + dc->realize = avr_cpu_realizefn; + + mcc->parent_reset = cc->reset; + cc->reset = avr_cpu_reset; + + cc->class_by_name = avr_cpu_class_by_name; + + cc->has_work = avr_cpu_has_work; + cc->do_interrupt = avr_cpu_do_interrupt; + cc->cpu_exec_interrupt = avr_cpu_exec_interrupt; + cc->dump_state = avr_cpu_dump_state; + cc->set_pc = avr_cpu_set_pc; +#if !defined(CONFIG_USER_ONLY) + cc->memory_rw_debug = avr_cpu_memory_rw_debug; +#endif +#ifdef CONFIG_USER_ONLY + cc->handle_mmu_fault = avr_cpu_handle_mmu_fault; +#else + cc->get_phys_page_debug = avr_cpu_get_phys_page_debug; + cc->vmsd = &vmstate_avr_cpu; +#endif + cc->disas_set_info = avr_cpu_disas_set_info; + cc->synchronize_from_tb = avr_cpu_synchronize_from_tb; + cc->gdb_read_register = avr_cpu_gdb_read_register; + cc->gdb_write_register = avr_cpu_gdb_write_register; + cc->gdb_num_core_regs = 35; + + /* + * Reason: avr_cpu_initfn() calls cpu_exec_init(), which saves + * the object in cpus -> dangling pointer after final + * object_unref(). + */ + dc->cannot_destroy_with_object_finalize_yet = true; +} + +static void avr_any_initfn(Object *obj) +{ + /* Set cpu feature flags */ +} + +typedef struct AVRCPUInfo { + const char *name; + void (*initfn)(Object *obj); +} AVRCPUInfo; + +static const AVRCPUInfo avr_cpus[] = { + { .name = "any", .initfn = avr_any_initfn }, +}; + +static gint avr_cpu_list_compare(gconstpointer a, gconstpointer b) +{ + ObjectClass *class_a = (ObjectClass *)a; + ObjectClass *class_b = (ObjectClass *)b; + const char *name_a; + const char *name_b; + + name_a = object_class_get_name(class_a); + name_b = object_class_get_name(class_b); + if (strcmp(name_a, "any-" TYPE_AVR_CPU) == 0) { + return 1; + } else if (strcmp(name_b, "any-" TYPE_AVR_CPU) == 0) { + return -1; + } else { + return strcmp(name_a, name_b); + } +} + +static void avr_cpu_list_entry(gpointer data, gpointer user_data) +{ + ObjectClass *oc = data; + CPUListState *s = user_data; + const char *typename; + char *name; + + typename = object_class_get_name(oc); + name = g_strndup(typename, strlen(typename) - strlen("-" TYPE_AVR_CPU)); + (*s->cpu_fprintf)(s->file, " %s\n", name); + g_free(name); +} + +void avr_cpu_list(FILE *f, fprintf_function cpu_fprintf) +{ + CPUListState s = { + .file = f, + .cpu_fprintf = cpu_fprintf, + }; + GSList *list; + + list = object_class_get_list(TYPE_AVR_CPU, false); + list = g_slist_sort(list, avr_cpu_list_compare); + (*cpu_fprintf)(f, "Available CPUs:\n"); + g_slist_foreach(list, avr_cpu_list_entry, &s); + g_slist_free(list); +} +AVRCPU *cpu_avr_init(const char *cpu_model) +{ + return AVR_CPU(cpu_generic_init(TYPE_AVR_CPU, cpu_model)); +} + +static void cpu_register(const AVRCPUInfo *info) +{ + TypeInfo type_info = { + .parent = TYPE_AVR_CPU, + .instance_size = sizeof(AVRCPU), + .instance_init = info->initfn, + .class_size = sizeof(AVRCPUClass), + }; + + type_info.name = g_strdup_printf("%s-" TYPE_AVR_CPU, info->name); + type_register(&type_info); + g_free((void *)type_info.name); +} + +static const TypeInfo avr_cpu_type_info = { + .name = TYPE_AVR_CPU, + .parent = TYPE_CPU, + .instance_size = sizeof(AVRCPU), + .instance_init = avr_cpu_initfn, + .class_size = sizeof(AVRCPUClass), + .class_init = avr_cpu_class_init, + .abstract = true, +}; + +static void avr_cpu_register_types(void) +{ + int i; + type_register_static(&avr_cpu_type_info); + + for (i = 0; i < ARRAY_SIZE(avr_cpus); i++) { + cpu_register(&avr_cpus[i]); + } +} + +type_init(avr_cpu_register_types) diff --git a/target-avr/cpu.h b/target-avr/cpu.h new file mode 100644 index 0000000..abfa0af --- /dev/null +++ b/target-avr/cpu.h @@ -0,0 +1,158 @@ + /* + * QEMU AVR CPU + * + * Copyright (c) 2016 Michael Rolnik + * + * 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.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * + */ + +#if !defined(CPU_AVR_H) +#define CPU_AVR_H + +#include "qemu-common.h" + +#define TARGET_LONG_BITS 32 + +#define CPUArchState struct CPUAVRState + +#include "exec/cpu-defs.h" +#include "fpu/softfloat.h" + +/* + TARGET_PAGE_BITS cannot be more than 8 bits because + 1. all IO registers occupy [0x0000 .. 0x00ff] address range, and they + should be implemented as a device and not memory + 2. SRAM starts at the address 0x0100 +*/ +#define TARGET_PAGE_BITS 8 +#define TARGET_PHYS_ADDR_SPACE_BITS 24 +#define TARGET_VIRT_ADDR_SPACE_BITS 24 +#define NB_MMU_MODES 2 + +#define MMU_CODE_IDX 0 +#define MMU_DATA_IDX 1 + +#define EXCP_RESET 1 +#define EXCP_INT(n) (EXCP_RESET + (n) + 1) + +#define PHYS_ADDR_MASK 0xfff00000 +#define PHYS_CODE_BASE 0x00000000 +#define PHYS_DATA_BASE 0x00800000 + +typedef struct CPUAVRState CPUAVRState; + +struct CPUAVRState { + uint32_t pc_w; /* 0x003fffff up to 22 bits */ + + uint32_t sregC; /* 0x00000001 1 bits */ + uint32_t sregZ; /* 0x0000ffff 16 bits, negative logic */ + uint32_t sregN; /* 0x00000001 1 bits */ + uint32_t sregV; /* 0x00000001 1 bits */ + uint32_t sregS; /* 0x00000001 1 bits */ + uint32_t sregH; /* 0x00000001 1 bits */ + uint32_t sregT; /* 0x00000001 1 bits */ + uint32_t sregI; /* 0x00000001 1 bits */ + + uint32_t rampD; /* 0x00ff0000 8 bits */ + uint32_t rampX; /* 0x00ff0000 8 bits */ + uint32_t rampY; /* 0x00ff0000 8 bits */ + uint32_t rampZ; /* 0x00ff0000 8 bits */ + uint32_t eind; /* 0x00ff0000 8 bits */ + + uint32_t io[64]; /* 8 bits each */ + uint32_t r[32]; /* 8 bits each */ + uint32_t sp; /* 16 bits */ + + + uint64_t intsrc; /* interrupt sources */ + + /* Those resources are used only in QEMU core */ + CPU_COMMON +}; + +#define cpu_list avr_cpu_list +#define cpu_exec cpu_avr_exec +#define cpu_signal_handler cpu_avr_signal_handler + +#include "exec/cpu-all.h" +#include "cpu-qom.h" + +static inline int cpu_mmu_index(CPUAVRState *env, bool ifetch) +{ + return ifetch ? MMU_CODE_IDX : MMU_DATA_IDX; +} + +void avr_translate_init(void); + +AVRCPU *cpu_avr_init(const char *cpu_model); + +#define cpu_init(cpu_model) CPU(cpu_avr_init(cpu_model)) + +void avr_cpu_list(FILE *f, fprintf_function cpu_fprintf); +int cpu_avr_exec(CPUState *cpu); +int cpu_avr_signal_handler(int host_signum, void *pinfo, void *puc); +int avr_cpu_handle_mmu_fault(CPUState *cpu, vaddr address, int rw, + int mmu_idx); +int avr_cpu_memory_rw_debug(CPUState *cs, vaddr address, uint8_t *buf, + int len, bool is_write); + +#ifndef CONFIG_USER_ONLY +QEMU_NORETURN void avr_cpu_unassigned_access(CPUState *cpu, hwaddr addr, + bool is_write, bool is_exec, + int unused, unsigned size); +#endif + +static inline void cpu_get_tb_cpu_state(CPUAVRState *env, target_ulong *pc, + target_ulong *cs_base, uint32_t *pflags) +{ + *pc = env->pc_w * 2; + *cs_base = 0; + *pflags = 0; +} + +static inline int cpu_interrupts_enabled(CPUAVRState *env1) +{ + return env1->sregI != 0; +} + +static inline uint8_t cpu_get_sreg(CPUAVRState *env) +{ + uint8_t sreg; + sreg = (env->sregC & 0x01) << 0 + | (env->sregZ == 0 ? 1 : 0) << 1 + | (env->sregN) << 2 + | (env->sregV) << 3 + | (env->sregS) << 4 + | (env->sregH) << 5 + | (env->sregT) << 6 + | (env->sregI) << 7; + return sreg; +} + +static inline void cpu_set_sreg(CPUAVRState *env, uint8_t sreg) +{ + env->sregC = (sreg >> 0) & 0x01; + env->sregZ = (sreg >> 1) & 0x01 ? 0 : 1; + env->sregN = (sreg >> 2) & 0x01; + env->sregV = (sreg >> 3) & 0x01; + env->sregS = (sreg >> 4) & 0x01; + env->sregH = (sreg >> 5) & 0x01; + env->sregT = (sreg >> 6) & 0x01; + env->sregI = (sreg >> 7) & 0x01; +} + +#include "exec/exec-all.h" + +#endif /* !defined (CPU_AVR_H) */ diff --git a/target-avr/gdbstub.c b/target-avr/gdbstub.c new file mode 100644 index 0000000..d9a3666 --- /dev/null +++ b/target-avr/gdbstub.c @@ -0,0 +1,99 @@ +/* + * QEMU AVR CPU + * + * Copyright (c) 2016 Michael Rolnik + * + * 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.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * + */ + +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "exec/gdbstub.h" + +int avr_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +{ + AVRCPU *cpu = AVR_CPU(cs); + CPUAVRState *env = &cpu->env; + + /* R */ + if (n < 32) { + return gdb_get_reg8(mem_buf, env->r[n]); + } + + /* SREG */ + if (n == 32) { + uint8_t sreg = 0; + + sreg = (env->sregC & 0x01) << 0 + | (env->sregZ & 0x01) << 1 + | (env->sregN & 0x01) << 2 + | (env->sregV & 0x01) << 3 + | (env->sregS & 0x01) << 4 + | (env->sregH & 0x01) << 5 + | (env->sregT & 0x01) << 6 + | (env->sregI & 0x01) << 7; + return gdb_get_reg8(mem_buf, sreg); + } + + /* SP */ + if (n == 33) { + return gdb_get_reg16(mem_buf, env->sp & 0x0000ffff); + } + + /* PC */ + if (n == 34) { + return gdb_get_reg32(mem_buf, env->pc_w * 2); + } + + return 0; +} + +int avr_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) +{ + AVRCPU *cpu = AVR_CPU(cs); + CPUAVRState *env = &cpu->env; + uint16_t tmp = ldl_p(mem_buf); + + /* R */ + if (n < 32) { + env->r[n] = tmp; + } + + /* SREG */ + if (n == 32) { + env->sregC = (tmp >> 0) & 0x01; + env->sregZ = (tmp >> 1) & 0x01; + env->sregN = (tmp >> 2) & 0x01; + env->sregV = (tmp >> 3) & 0x01; + env->sregS = (tmp >> 4) & 0x01; + env->sregH = (tmp >> 5) & 0x01; + env->sregT = (tmp >> 6) & 0x01; + env->sregI = (tmp >> 7) & 0x01; + } + + /* SP */ + if (n == 33) { + env->sp = tmp; + return 2; + } + + /* PC */ + if (n == 34) { + env->pc_w = tmp / 2; + return 4; + } + + return 1; +} diff --git a/target-avr/helper.c b/target-avr/helper.c new file mode 100644 index 0000000..ad8f83e --- /dev/null +++ b/target-avr/helper.c @@ -0,0 +1,87 @@ +/* + * QEMU AVR CPU + * + * Copyright (c) 2016 Michael Rolnik + * + * 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.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * + */ + +#include "qemu/osdep.h" + +#include "cpu.h" +#include "hw/irq.h" +#include "include/hw/sysbus.h" +#include "include/sysemu/sysemu.h" +#include "exec/exec-all.h" +#include "exec/cpu_ldst.h" +#include "qemu/host-utils.h" +#include "exec/helper-proto.h" + +bool avr_cpu_exec_interrupt(CPUState *cs, int interrupt_request) +{ + return false; +} + +void avr_cpu_do_interrupt(CPUState *cs) +{ +} + +int avr_cpu_memory_rw_debug(CPUState *cs, vaddr addr, uint8_t *buf, + int len, bool is_write) +{ + return cpu_memory_rw_debug(cs, addr, buf, len, is_write); +} + +hwaddr avr_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) +{ + return addr; /* I assume 1:1 address correspondance */ +} + +int avr_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int rw, int mmu_idx) +{ + cs->exception_index = EXCP_DEBUG; + cpu_dump_state(cs, stderr, fprintf, 0); + return 1; +} + +void tlb_fill(CPUState *cs, target_ulong vaddr, int is_write, + int mmu_idx, uintptr_t retaddr) +{ + target_ulong page_size = TARGET_PAGE_SIZE; + int prot = 0; + MemTxAttrs attrs = {}; + uint32_t paddr; + + vaddr &= TARGET_PAGE_MASK; + + if (mmu_idx == MMU_CODE_IDX) { + paddr = PHYS_CODE_BASE + vaddr; + prot = PAGE_READ | PAGE_EXEC; + } else { + paddr = PHYS_DATA_BASE + vaddr; + prot = PAGE_READ | PAGE_WRITE; + } + + tlb_set_page_with_attrs(cs, vaddr, paddr, attrs, prot, mmu_idx, page_size); +} + +void helper_debug(CPUAVRState *env) +{ + CPUState *cs = CPU(avr_env_get_cpu(env)); + + cs->exception_index = EXCP_DEBUG; + cpu_loop_exit(cs); +} + diff --git a/target-avr/helper.h b/target-avr/helper.h new file mode 100644 index 0000000..b5ef3bf --- /dev/null +++ b/target-avr/helper.h @@ -0,0 +1,21 @@ +/* + * QEMU AVR CPU + * + * Copyright (c) 2016 Michael Rolnik + * + * 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.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * + */ + +DEF_HELPER_1(debug, void, env) diff --git a/target-avr/machine.c b/target-avr/machine.c new file mode 100644 index 0000000..39f1ee6 --- /dev/null +++ b/target-avr/machine.c @@ -0,0 +1,54 @@ +/* + * QEMU AVR CPU + * + * Copyright (c) 2016 Michael Rolnik + * + * 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.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * + */ + +#include "qemu/osdep.h" +#include "hw/hw.h" +#include "cpu.h" +#include "hw/boards.h" +#include "machine.h" + +const VMStateDescription vmstate_avr_cpu = { + .name = "cpu", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(r, CPUAVRState, 32), + + VMSTATE_UINT32(sregC, CPUAVRState), + VMSTATE_UINT32(sregZ, CPUAVRState), + VMSTATE_UINT32(sregN, CPUAVRState), + VMSTATE_UINT32(sregV, CPUAVRState), + VMSTATE_UINT32(sregS, CPUAVRState), + VMSTATE_UINT32(sregH, CPUAVRState), + VMSTATE_UINT32(sregT, CPUAVRState), + VMSTATE_UINT32(sregI, CPUAVRState), + + VMSTATE_UINT32(rampD, CPUAVRState), + VMSTATE_UINT32(rampX, CPUAVRState), + VMSTATE_UINT32(rampY, CPUAVRState), + VMSTATE_UINT32(rampZ, CPUAVRState), + + VMSTATE_UINT32(eind, CPUAVRState), + VMSTATE_UINT32(sp, CPUAVRState), + VMSTATE_UINT32(pc_w, CPUAVRState), + + VMSTATE_END_OF_LIST() + } +}; diff --git a/target-avr/machine.h b/target-avr/machine.h new file mode 100644 index 0000000..4cc8d6b --- /dev/null +++ b/target-avr/machine.h @@ -0,0 +1,21 @@ +/* + * QEMU AVR CPU + * + * Copyright (c) 2016 Michael Rolnik + * + * 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.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * + */ + +extern const VMStateDescription vmstate_avr_cpu; diff --git a/target-avr/translate.c b/target-avr/translate.c new file mode 100644 index 0000000..81ee44e --- /dev/null +++ b/target-avr/translate.c @@ -0,0 +1,290 @@ +/* + * QEMU AVR CPU + * + * Copyright (c) 2016 Michael Rolnik + * + * 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.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * + */ + +#include "qemu/osdep.h" + +#include "cpu.h" +#include "exec/exec-all.h" +#include "disas/disas.h" +#include "tcg-op.h" +#include "exec/cpu_ldst.h" + +#include "exec/helper-proto.h" +#include "exec/helper-gen.h" +#include "exec/log.h" + +typedef struct DisasContext DisasContext; +typedef struct InstInfo InstInfo; + +/*This is the state at translation time. */ +struct DisasContext { + struct TranslationBlock *tb; + + /*Routine used to access memory */ + int memidx; + int bstate; + int singlestep; +}; + +enum { + BS_NONE = 0, /* Nothing special (none of the below */ + BS_STOP = 1, /* We want to stop translation for any reason */ + BS_BRANCH = 2, /* A branch condition is reached */ + BS_EXCP = 3, /* An exception condition is reached */ +}; + +static TCGv_env cpu_env; + +static TCGv cpu_pc; + +static TCGv cpu_Cf; +static TCGv cpu_Zf; +static TCGv cpu_Nf; +static TCGv cpu_Vf; +static TCGv cpu_Sf; +static TCGv cpu_Hf; +static TCGv cpu_Tf; +static TCGv cpu_If; + +static TCGv cpu_rampD; +static TCGv cpu_rampX; +static TCGv cpu_rampY; +static TCGv cpu_rampZ; + +static TCGv cpu_io[64]; +static TCGv cpu_r[32]; +static TCGv cpu_eind; +static TCGv cpu_sp; + +#include "exec/gen-icount.h" +#define REG(x) (cpu_r[x]) + +void avr_translate_init(void) +{ + int i; + static int done_init; + + if (done_init) { + return; + } +#define AVR_REG_OFFS(x) offsetof(CPUAVRState, x) + cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env"); + cpu_pc = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(pc_w), "pc"); + cpu_Cf = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sregC), "Cf"); + cpu_Zf = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sregZ), "Zf"); + cpu_Nf = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sregN), "Nf"); + cpu_Vf = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sregV), "Vf"); + cpu_Sf = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sregS), "Sf"); + cpu_Hf = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sregH), "Hf"); + cpu_Tf = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sregT), "Tf"); + cpu_If = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sregI), "If"); + cpu_rampD = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(rampD), "rampD"); + cpu_rampX = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(rampX), "rampX"); + cpu_rampY = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(rampY), "rampY"); + cpu_rampZ = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(rampZ), "rampZ"); + cpu_eind = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(eind), "eind"); + cpu_sp = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sp), "sp"); + + for (i = 0; i < 64; i++) { + char name[16]; + + sprintf(name, "io[%d]", i); + + cpu_io[i] = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(io[i]), name); + } + for (i = 0; i < 32; i++) { + char name[16]; + + sprintf(name, "r[%d]", i); + + cpu_r[i] = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(r[i]), name); + } + + done_init = 1; +} + +static inline void gen_goto_tb(CPUAVRState *env, DisasContext *ctx, int n, + target_ulong dest) +{ + TranslationBlock *tb; + + tb = ctx->tb; + + if ((tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK) + && (ctx->singlestep == 0)) { + tcg_gen_goto_tb(n); + tcg_gen_movi_i32(cpu_pc, dest); + tcg_gen_exit_tb((uintptr_t)tb + n); + } else { + tcg_gen_movi_i32(cpu_pc, dest); + + if (ctx->singlestep) { + gen_helper_debug(cpu_env); + } + tcg_gen_exit_tb(0); + } +} + +/*generate intermediate code for basic block 'tb'. */ +void gen_intermediate_code(CPUAVRState *env, struct TranslationBlock *tb) +{ + AVRCPU *cpu = avr_env_get_cpu(env); + CPUState *cs = CPU(cpu); + DisasContext ctx; + target_ulong pc_start; + int num_insns, + max_insns; + target_ulong cpc; + target_ulong npc; + + pc_start = tb->pc / 2; + ctx.tb = tb; + ctx.memidx = 0; + ctx.bstate = BS_NONE; + ctx.singlestep = cs->singlestep_enabled; + num_insns = 0; + max_insns = tb->cflags & CF_COUNT_MASK; + + if (max_insns == 0) { + max_insns = CF_COUNT_MASK; + } + if (max_insns > TCG_MAX_INSNS) { + max_insns = TCG_MAX_INSNS; + } + + gen_tb_start(tb); + + /* decode first instruction */ + cpc = pc_start; + npc = cpc + 1; + do { + /* translate current instruction */ + tcg_gen_insn_start(cpc); + num_insns++; + + /* just skip to next instruction */ + cpc++; + npc++; + ctx.bstate = BS_NONE; + + if (unlikely(cpu_breakpoint_test(cs, cpc * 2, BP_ANY))) { + tcg_gen_movi_i32(cpu_pc, cpc); + gen_helper_debug(cpu_env); + ctx.bstate = BS_EXCP; + /*The address covered by the breakpoint must be included in + [tb->pc, tb->pc + tb->size) in order to for it to be + properly cleared -- thus we increment the PC here so that + the logic setting tb->size below does the right thing. */ + goto done_generating; + } + + if (num_insns >= max_insns) { + break; /* max translated instructions limit reached */ + } + if (ctx.singlestep) { + break; /* single step */ + } + if ((cpc & (TARGET_PAGE_SIZE - 1)) == 0) { + break; /* page boundary */ + } + } while (ctx.bstate == BS_NONE && !tcg_op_buf_full()); + + if (tb->cflags & CF_LAST_IO) { + gen_io_end(); + } + + if (ctx.singlestep) { + if (ctx.bstate == BS_STOP || ctx.bstate == BS_NONE) { + tcg_gen_movi_tl(cpu_pc, npc); + } + gen_helper_debug(cpu_env); + tcg_gen_exit_tb(0); + } else { + switch (ctx.bstate) { + case BS_STOP: + case BS_NONE: + gen_goto_tb(env, &ctx, 0, npc); + break; + case BS_EXCP: + tcg_gen_exit_tb(0); + break; + default: + break; + } + } + +done_generating: + gen_tb_end(tb, num_insns); + + tb->size = (npc - pc_start) * 2; + tb->icount = num_insns; +} + +void restore_state_to_opc(CPUAVRState *env, TranslationBlock *tb, + target_ulong *data) +{ + env->pc_w = data[0]; +} + +void avr_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, + int flags) +{ + AVRCPU *cpu = AVR_CPU(cs); + CPUAVRState *env = &cpu->env; + + cpu_fprintf(f, "\n"); + cpu_fprintf(f, "PC: %06x\n", env->pc_w); + cpu_fprintf(f, "SP: %04x\n", env->sp); + cpu_fprintf(f, "rampD: %02x\n", env->rampD >> 16); + cpu_fprintf(f, "rampX: %02x\n", env->rampX >> 16); + cpu_fprintf(f, "rampY: %02x\n", env->rampY >> 16); + cpu_fprintf(f, "rampZ: %02x\n", env->rampZ >> 16); + cpu_fprintf(f, "EIND: %02x\n", env->eind); + cpu_fprintf(f, "X: %02x%02x\n", env->r[27], env->r[26]); + cpu_fprintf(f, "Y: %02x%02x\n", env->r[29], env->r[28]); + cpu_fprintf(f, "Z: %02x%02x\n", env->r[31], env->r[30]); + cpu_fprintf(f, "SREG: [ %c %c %c %c %c %c %c %c ]\n", + env->sregI ? 'I' : '-', + env->sregT ? 'T' : '-', + env->sregH ? 'H' : '-', + env->sregS ? 'S' : '-', + env->sregV ? 'V' : '-', + env->sregN ? '-' : 'N', /* Zf has negative logic */ + env->sregZ ? 'Z' : '-', + env->sregC ? 'I' : '-'); + + cpu_fprintf(f, "\n"); + for (int i = 0; i < ARRAY_SIZE(env->r); i++) { + cpu_fprintf(f, "R[%02d]: %02x ", i, env->r[i]); + + if ((i % 8) == 7) { + cpu_fprintf(f, "\n"); + } + } + + cpu_fprintf(f, "\n"); + for (int i = 0; i < ARRAY_SIZE(env->io); i++) { + cpu_fprintf(f, "IO[%02d]: %02x ", i, env->io[i]); + + if ((i % 8) == 7) { + cpu_fprintf(f, "\n"); + } + } +}