diff --git a/configure b/configure
index 82cace1bc9..286e5e4c22 100755
--- a/configure
+++ b/configure
@@ -1356,6 +1356,7 @@ fi
: ${cross_prefix_sparc="$cross_prefix_sparc64"}
: ${cross_prefix_tricore="tricore-"}
: ${cross_prefix_x86_64="x86_64-linux-gnu-"}
+: ${cross_prefix_cr16c="cr16-c-elf-"}
: ${cross_cc_aarch64_be="$cross_cc_aarch64"}
: ${cross_cc_cflags_aarch64_be="-mbig-endian"}
diff --git a/gdb-xml/cr16c-core.xml b/gdb-xml/cr16c-core.xml
new file mode 100644
index 0000000000..69ab85ae1a
--- /dev/null
+++ b/gdb-xml/cr16c-core.xml
@@ -0,0 +1,71 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/hw/char/Kconfig b/hw/char/Kconfig
index 020c0a84bb..1262084bd0 100644
--- a/hw/char/Kconfig
+++ b/hw/char/Kconfig
@@ -95,3 +95,8 @@ config IP_OCTAL_232
bool
default y
depends on IPACK
+
+config DIALOG_UART
+ bool
+ depends on HAVE_RUST
+ select X_DIALOG_UART_RUST
diff --git a/hw/cr16c/Kconfig b/hw/cr16c/Kconfig
index 6c52b8bf54..8118288ae1 100644
--- a/hw/cr16c/Kconfig
+++ b/hw/cr16c/Kconfig
@@ -1,5 +1,8 @@
config VIRT
bool
+ select UNIMP
+ select DIALOG_UART
config DE410
bool
+ select DIALOG_UART
diff --git a/hw/cr16c/meson.build b/hw/cr16c/meson.build
index 43bb8b3e6f..e956fa0326 100644
--- a/hw/cr16c/meson.build
+++ b/hw/cr16c/meson.build
@@ -1,6 +1,7 @@
cr16c_ss = ss.source_set()
cr16c_ss.add(files('virt.c'))
cr16c_ss.add(files('sc14461.c'))
+cr16c_ss.add(files('sc14480.c'))
cr16c_ss.add(files('de410.c'))
cr16c_ss.add(files('boot.c'))
hw_arch += {'cr16c': cr16c_ss}
diff --git a/hw/cr16c/sc14480.c b/hw/cr16c/sc14480.c
new file mode 100644
index 0000000000..ef0dc4f08e
--- /dev/null
+++ b/hw/cr16c/sc14480.c
@@ -0,0 +1,67 @@
+#include "sc14480.h"
+#include "cpu-qom.h"
+#include "system/address-spaces.h"
+#include "hw/core/cpu.h"
+#include "hw/cr16c/boot.h"
+#include "qapi/error.h"
+#include "qemu/datadir.h"
+#include "qemu/units.h"
+
+
+static void sc14480_realize(DeviceState* dev, Error** errp)
+{
+ SC14480McuState* s = SC14480_MCU(dev);
+
+ object_initialize_child(OBJECT(dev), "cpu", &s->cpu, TYPE_CR16C_CPU);
+ object_property_set_bool(OBJECT(&s->cpu), "realized", true, &error_abort);
+
+ memory_region_init_ram(&s->non_shared_ram_or_icache, OBJECT(dev), "non_shared_ram_or_icache", 24*KiB, &error_fatal);
+ memory_region_init_ram(&s->non_shared_ram_or_dcache, OBJECT(dev), "non_shared_ram_or_dcache", 8*KiB, &error_fatal);
+ memory_region_init_ram(&s->shared_int_ram, OBJECT(dev), "shared_int_ram", 64*KiB, &error_fatal);
+ memory_region_init_rom(&s->boot_rom, OBJECT(dev), "boot_rom", 2*KiB, &error_fatal);
+ memory_region_add_subregion(get_system_memory(), 0x00000, &s->non_shared_ram_or_icache);
+ memory_region_add_subregion(get_system_memory(), 0x08000, &s->non_shared_ram_or_dcache);
+ memory_region_add_subregion(get_system_memory(), 0x10000, &s->shared_int_ram);
+ memory_region_add_subregion(get_system_memory(), 0xFEF00, &s->boot_rom);
+
+ char* bios_path = qemu_find_file(QEMU_FILE_TYPE_BIOS, "sc14480-bios.img");
+ if (!cr16c_load_firmware(&s->cpu, &s->boot_rom, bios_path)) {
+ exit(1);
+ }
+}
+
+static void sc14480_reset_hold(Object *obj, ResetType type) {
+ SC14480McuClass* mcu_class = SC14480_MCU_GET_CLASS(obj);
+ SC14480McuState* mcu_state = SC14480_MCU(obj);
+ CR16CCPU* cpu = &mcu_state->cpu;
+ CPUState* cpu_state = CPU(cpu);
+
+ if (mcu_class->parent_phases.hold) {
+ mcu_class->parent_phases.hold(obj, type);
+ }
+
+ cpu_reset(cpu_state);
+ cpu_set_pc(cpu_state, 0xFEF00);
+}
+
+static void sc14480_class_init(ObjectClass* oc, const void* data)
+{
+ DeviceClass* dc = DEVICE_CLASS(oc);
+ SC14480McuClass* sc14480 = SC14480_MCU_CLASS(oc);
+ ResettableClass *rc = RESETTABLE_CLASS(oc);
+
+ resettable_class_set_parent_phases(rc, NULL, sc14480_reset_hold, NULL, &sc14480->parent_phases);
+
+ dc->realize = sc14480_realize;
+}
+
+static const TypeInfo sc14480_mcu_types[] = {
+ {
+ .name = TYPE_SC14480_MCU,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(SC14480McuState),
+ .class_size = sizeof(SC14480McuClass),
+ .class_init = sc14480_class_init,
+ },
+};
+DEFINE_TYPES(sc14480_mcu_types)
diff --git a/hw/cr16c/sc14480.h b/hw/cr16c/sc14480.h
new file mode 100644
index 0000000000..54fad26104
--- /dev/null
+++ b/hw/cr16c/sc14480.h
@@ -0,0 +1,30 @@
+#ifndef HW_SC14480_H
+#define HW_SC14480_H
+
+#include "cpu.h"
+#include "hw/sysbus.h"
+
+#define TYPE_SC14480_MCU "SC14480"
+
+typedef struct SC14480McuState SC14480McuState;
+DECLARE_INSTANCE_CHECKER(SC14480McuState, SC14480_MCU, TYPE_SC14480_MCU)
+
+struct SC14480McuState {
+ SysBusDevice parent_obj;
+
+ CR16CCPU cpu;
+
+ MemoryRegion non_shared_ram_or_icache;
+ MemoryRegion non_shared_ram_or_dcache;
+ MemoryRegion shared_int_ram;
+ MemoryRegion boot_rom;
+};
+
+typedef struct SC14480McuClass {
+ SysBusDeviceClass parent_class;
+
+ ResettablePhases parent_phases;
+} SC14480McuClass;
+DECLARE_CLASS_CHECKERS(SC14480McuClass, SC14480_MCU, TYPE_SC14480_MCU)
+
+#endif /* HW_SC14480_H */
diff --git a/hw/cr16c/virt.c b/hw/cr16c/virt.c
index 6798ea0a74..4d3a375876 100644
--- a/hw/cr16c/virt.c
+++ b/hw/cr16c/virt.c
@@ -2,14 +2,17 @@
#include "cpu-qom.h"
#include "system/address-spaces.h"
#include "hw/boards.h"
+#include "hw/misc/unimp.h"
#include "qapi/error.h"
#include "qemu/units.h"
#include "qom/object.h"
+#include "system/system.h"
typedef struct VirtMachineState {
MachineState parent_obj;
CR16CCPU cpu;
MemoryRegion flash;
+ MemoryRegion mmio_alias;
} VirtMachineState;
typedef struct VirtMachineClass {
@@ -20,16 +23,59 @@ typedef struct VirtMachineClass {
DECLARE_OBJ_CHECKERS(VirtMachineState, VirtMachineClass, VIRT_MACHINE, TYPE_VIRT_MACHINE)
+// SC14480 vectors, all with exception priority 4
+enum {
+ // lowest
+ ACCESS12_INT = 16,
+ KEYB_INT = 17,
+ DMA1_INT = 17, // secondary function with DINT
+ // RESERVED_INT = 18,
+ CT_CLASSD_INT = 19,
+ UART_RI_INT = 20,
+ DMA2_INT = 20, // secondary function with DINT
+ UART_TI_INT = 21,
+ DMA3_INT = 21, // secondary function with DINT
+ SPI_INT = 22,
+ DMA0_INT = 22, // secondary function with DINT
+ TIM0_INT = 23,
+ TIM1_INT = 24,
+ CLK100_INT = 25,
+ DIP_INT = 26,
+ AD_INT = 27,
+ SPI2_INT = 28,
+ DSP_INT = 29,
+ // RESERVED = 30,
+ // highest
+};
+// TODO map these from qemu irqs somewhere
+
+DeviceState *sc144uart_create(hwaddr addr, qemu_irq irq, Chardev *chr);
static void virt_init(MachineState* machine)
{
+
VirtMachineState* m_state = VIRT_MACHINE(machine);
object_initialize_child(OBJECT(machine), "cpu", &m_state->cpu, TYPE_CR16C_CPU);
object_property_set_bool(OBJECT(&m_state->cpu), "realized", true, &error_abort);
- memory_region_init_ram(&m_state->flash, NULL, "flash", 16*MiB, &error_fatal);
+ memory_region_init_ram(&m_state->flash, NULL, "flash", 16*MiB - 64*KiB, &error_fatal);
memory_region_add_subregion(get_system_memory(), 0, &m_state->flash);
+ create_unimplemented_device("mmio", 0xFF0000, 0xFFFBFF - 0xFF0000);
+
+ qemu_irq pic[10]; // TODO pick a reasonable value and name it
+ for (int n = 0; n < 10; n++) {
+ pic[n] = qdev_get_gpio_in(&m_state->cpu.parent_obj.parent_obj, n);
+ }
+ (void)pic;
+ sc144uart_create(0xFF4900, pic[0], NULL);//serial_hd(0));
+ //memory_region_init_alias(&m_state->mmio_alias, NULL, "mmio_alias", &m_state->flash, 0xF0000, 0xFFFFF - 0xF0000);
+
+ create_unimplemented_device("icu", 0xFFFC00, 0xFFFFFF - 0xFFFC00); // interrupt controller unit
+
+ // present on some CR16C implementations (external RAM?)
+ create_unimplemented_device("unknown", 0x1000000, 256*MiB - 16*MiB);
+
if (machine->firmware) {
if (!cr16c_load_firmware(&m_state->cpu, &m_state->flash, machine->firmware)) {
exit(1);
diff --git a/meson.build b/meson.build
index d9293294d8..e645b16e2e 100644
--- a/meson.build
+++ b/meson.build
@@ -755,6 +755,7 @@ warn_flags = [
'-Wno-string-plus-int',
'-Wno-tautological-type-limit-compare',
'-Wno-typedef-redefinition',
+ '-Wno-unused-function',
]
if host_os != 'darwin'
diff --git a/rust/Cargo.lock b/rust/Cargo.lock
index 0c1df625df..7efd85822d 100644
--- a/rust/Cargo.lock
+++ b/rust/Cargo.lock
@@ -1,6 +1,6 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
-version = 3
+version = 4
[[package]]
name = "anyhow"
@@ -91,6 +91,25 @@ dependencies = [
"qemu_macros",
]
+[[package]]
+name = "dialog_uart"
+version = "0.1.0"
+dependencies = [
+ "bilge",
+ "bilge-impl",
+ "bits",
+ "bql",
+ "chardev",
+ "common",
+ "glib-sys",
+ "hwcore",
+ "migration",
+ "qom",
+ "system",
+ "trace",
+ "util",
+]
+
[[package]]
name = "either"
version = "1.12.0"
diff --git a/rust/Cargo.toml b/rust/Cargo.toml
index ace0baf9bd..bb0133f641 100644
--- a/rust/Cargo.toml
+++ b/rust/Cargo.toml
@@ -2,6 +2,7 @@
resolver = "2"
members = [
"hw/char/pl011",
+ "hw/char/dialog_uart",
"hw/timer/hpet",
"tests",
]
diff --git a/rust/hw/char/Kconfig b/rust/hw/char/Kconfig
index 5fe800c480..15de52dc9b 100644
--- a/rust/hw/char/Kconfig
+++ b/rust/hw/char/Kconfig
@@ -1,2 +1,5 @@
config X_PL011_RUST
bool
+
+config X_DIALOG_UART_RUST
+ bool
diff --git a/rust/hw/char/dialog_uart/Cargo.toml b/rust/hw/char/dialog_uart/Cargo.toml
new file mode 100644
index 0000000000..282ce246b0
--- /dev/null
+++ b/rust/hw/char/dialog_uart/Cargo.toml
@@ -0,0 +1,31 @@
+[package]
+name = "dialog_uart"
+version = "0.1.0"
+authors = ["fridtjof ", "Manos Pitsidianakis "]
+description = "dialog_uart device model for QEMU"
+resolver = "2"
+publish = false
+
+edition.workspace = true
+homepage.workspace = true
+license.workspace = true
+repository.workspace = true
+rust-version.workspace = true
+
+[dependencies]
+glib-sys.workspace = true
+bilge = { version = "0.2.0" }
+bilge-impl = { version = "0.2.0" }
+bits = { path = "../../../bits" }
+common = { path = "../../../common" }
+util = { path = "../../../util" }
+bql = { path = "../../../bql" }
+migration = { path = "../../../migration" }
+qom = { path = "../../../qom" }
+chardev = { path = "../../../chardev" }
+system = { path = "../../../system" }
+hwcore = { path = "../../../hw/core" }
+trace = { path = "../../../trace" }
+
+[lints]
+workspace = true
diff --git a/rust/hw/char/dialog_uart/build.rs b/rust/hw/char/dialog_uart/build.rs
new file mode 120000
index 0000000000..5f5060db35
--- /dev/null
+++ b/rust/hw/char/dialog_uart/build.rs
@@ -0,0 +1 @@
+../../../util/build.rs
\ No newline at end of file
diff --git a/rust/hw/char/dialog_uart/meson.build b/rust/hw/char/dialog_uart/meson.build
new file mode 100644
index 0000000000..1e2aef20ac
--- /dev/null
+++ b/rust/hw/char/dialog_uart/meson.build
@@ -0,0 +1,51 @@
+# TODO: Remove this comment when the clang/libclang mismatch issue is solved.
+#
+# Rust bindings generation with `bindgen` might fail in some cases where the
+# detected `libclang` does not match the expected `clang` version/target. In
+# this case you must pass the path to `clang` and `libclang` to your build
+# command invocation using the environment variables CLANG_PATH and
+# LIBCLANG_PATH
+_libdialog_uart_bindings_inc_rs = rust.bindgen(
+ input: 'wrapper.h',
+ dependencies: common_ss.all_dependencies(),
+ output: 'bindings.inc.rs',
+ include_directories: bindings_incdir,
+ bindgen_version: ['>=0.60.0'],
+ args: bindgen_args_common,
+ c_args: bindgen_c_args,
+)
+
+_libdialog_uart_rs = static_library(
+ 'dialog_uart',
+ structured_sources(
+ [
+ 'src/lib.rs',
+ 'src/bindings.rs',
+ 'src/device.rs',
+ 'src/registers.rs',
+ ],
+ {'.' : _libdialog_uart_bindings_inc_rs},
+ ),
+ override_options: ['rust_std=2021', 'build.rust_std=2021'],
+ rust_abi: 'rust',
+ dependencies: [
+ bilge_rs,
+ bilge_impl_rs,
+ bits_rs,
+ common_rs,
+ glib_sys_rs,
+ util_rs,
+ migration_rs,
+ bql_rs,
+ qom_rs,
+ chardev_rs,
+ system_rs,
+ hwcore_rs,
+ trace_rs
+ ],
+)
+
+rust_devices_ss.add(when: 'CONFIG_X_DIALOG_UART_RUST', if_true: [declare_dependency(
+ link_whole: [_libdialog_uart_rs],
+ variables: {'crate': 'dialog_uart'},
+)])
diff --git a/rust/hw/char/dialog_uart/src/bindings.rs b/rust/hw/char/dialog_uart/src/bindings.rs
new file mode 100644
index 0000000000..654be9185b
--- /dev/null
+++ b/rust/hw/char/dialog_uart/src/bindings.rs
@@ -0,0 +1,32 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#![allow(
+ dead_code,
+ improper_ctypes_definitions,
+ improper_ctypes,
+ non_camel_case_types,
+ non_snake_case,
+ non_upper_case_globals,
+ unnecessary_transmutes,
+ unsafe_op_in_unsafe_fn,
+ clippy::pedantic,
+ clippy::restriction,
+ clippy::style,
+ clippy::missing_const_for_fn,
+ clippy::ptr_offset_with_cast,
+ clippy::useless_transmute,
+ clippy::missing_safety_doc,
+ clippy::too_many_arguments
+)]
+
+//! `bindgen`-generated declarations.
+
+use glib_sys::{
+ gboolean, guint, GArray, GByteArray, GHashTable, GHashTableIter, GIOCondition, GList,
+ GMainContext, GPollFD, GPtrArray, GSList, GSource, GSourceFunc, GString,
+};
+
+#[cfg(MESON)]
+include!("bindings.inc.rs");
+
+#[cfg(not(MESON))]
+include!(concat!(env!("OUT_DIR"), "/bindings.inc.rs"));
diff --git a/rust/hw/char/dialog_uart/src/device.rs b/rust/hw/char/dialog_uart/src/device.rs
new file mode 100644
index 0000000000..c3c0c89f75
--- /dev/null
+++ b/rust/hw/char/dialog_uart/src/device.rs
@@ -0,0 +1,505 @@
+// Copyright 2024, Linaro Limited
+// Copyright 2026, fridtjof
+// Author(s): Manos Pitsidianakis , fridtjof
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+use std::{ffi::CStr};
+
+use bql::BqlRefCell;
+use chardev::{CharFrontend, Chardev, Event};
+use common::uninit_field_mut;
+use hwcore::{
+ Clock, ClockEvent, DeviceImpl, DeviceMethods, DeviceState, IRQState, InterruptSource,
+ ResetType, ResettablePhasesImpl, SysBusDevice, SysBusDeviceImpl, SysBusDeviceMethods,
+};
+use migration::{
+ impl_vmstate_forward, impl_vmstate_struct, vmstate_fields, vmstate_of,
+ vmstate_subsections, vmstate_unused, VMStateDescription, VMStateDescriptionBuilder,
+};
+use qom::{prelude::*, ObjectImpl, Owned, ParentField, ParentInit};
+use system::{hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder};
+use util::{log::Log, log_mask_ln, ResultExt};
+
+use crate::registers::{self, Interrupt, RegisterOffset};
+
+// TODO: You must disable the UART before any of the control registers are
+// reprogrammed. When the UART is disabled in the middle of transmission or
+// reception, it completes the current character before stopping
+
+/// QEMU sourced constant.
+pub const PL011_FIFO_DEPTH: u32 = 16;
+
+// FIFOs use 32-bit indices instead of usize, for compatibility with
+// the migration stream produced by the C version of this device.
+#[repr(transparent)]
+#[derive(Debug, Default)]
+pub struct Fifo([registers::Data; PL011_FIFO_DEPTH as usize]);
+impl_vmstate_forward!(Fifo);
+
+impl Fifo {
+ const fn len(&self) -> u32 {
+ self.0.len() as u32
+ }
+}
+
+impl std::ops::IndexMut for Fifo {
+ fn index_mut(&mut self, idx: u32) -> &mut Self::Output {
+ &mut self.0[idx as usize]
+ }
+}
+
+impl std::ops::Index for Fifo {
+ type Output = registers::Data;
+
+ fn index(&self, idx: u32) -> &Self::Output {
+ &self.0[idx as usize]
+ }
+}
+
+#[repr(C)]
+#[derive(Debug, Default)]
+pub struct SC144UartRegisters {
+ #[doc(alias = "ctrl")]
+ pub control: registers::Control,
+ pub dmacr: u32,
+ pub int_enabled: Interrupt,
+ pub int_level: Interrupt,
+ pub read_fifo: Fifo,
+ pub ilpr: u32,
+ pub ifl: u32,
+ pub read_pos: u32,
+ pub read_count: u32,
+ pub read_trigger: u32,
+}
+
+#[repr(C)]
+#[derive(qom::Object, hwcore::Device)]
+/// PL011 Device Model in QEMU
+pub struct SC144UartState {
+ pub parent_obj: ParentField,
+ pub iomem: MemoryRegion,
+ #[doc(alias = "chr")]
+ #[property(rename = "chardev")]
+ pub char_backend: CharFrontend,
+ pub regs: BqlRefCell,
+ /// QEMU interrupts
+ ///
+ /// ```text
+ /// * sysbus MMIO region 0: device registers
+ /// * sysbus IRQ 0: `UARTINTR` (combined interrupt line)
+ /// * sysbus IRQ 1: `UARTRXINTR` (receive FIFO interrupt line)
+ /// * sysbus IRQ 2: `UARTTXINTR` (transmit FIFO interrupt line)
+ /// * sysbus IRQ 3: `UARTRTINTR` (receive timeout interrupt line)
+ /// * sysbus IRQ 4: `UARTMSINTR` (momem status interrupt line)
+ /// * sysbus IRQ 5: `UARTEINTR` (error interrupt line)
+ /// ```
+ #[doc(alias = "irq")]
+ pub interrupts: [InterruptSource; IRQMASK.len()],
+ #[doc(alias = "clk")]
+ pub clock: Owned,
+ #[doc(alias = "migrate_clk")]
+ #[property(rename = "migrate-clk", default = true)]
+ pub migrate_clock: bool,
+}
+
+qom_isa!(SC144UartState : SysBusDevice, DeviceState, Object);
+
+#[repr(C)]
+pub struct SC144UartClass {
+ parent_class: ::Class,
+}
+
+trait PL011Impl: SysBusDeviceImpl + IsA {}
+
+impl SC144UartClass {
+ fn class_init(&mut self) {
+ self.parent_class.class_init::();
+ }
+}
+
+unsafe impl ObjectType for SC144UartState {
+ type Class = SC144UartClass;
+ const TYPE_NAME: &'static CStr = crate::TYPE_SC144UART;
+}
+
+impl PL011Impl for SC144UartState {}
+
+impl ObjectImpl for SC144UartState {
+ type ParentType = SysBusDevice;
+
+ const INSTANCE_INIT: Option)> = Some(Self::init);
+ const INSTANCE_POST_INIT: Option = Some(Self::post_init);
+ const CLASS_INIT: fn(&mut Self::Class) = Self::Class::class_init::;
+}
+
+impl DeviceImpl for SC144UartState {
+ const VMSTATE: Option> = Some(VMSTATE_SC144UART);
+ const REALIZE: Option util::Result<()>> = Some(Self::realize);
+}
+
+impl ResettablePhasesImpl for SC144UartState {
+ const HOLD: Option = Some(Self::reset_hold);
+}
+
+impl SysBusDeviceImpl for SC144UartState {}
+
+impl SC144UartRegisters {
+ pub(self) fn read(&mut self, offset: RegisterOffset) -> (bool, u16) {
+ use RegisterOffset::*;
+
+ let mut update_irq = false;
+ let result = match offset {
+ CTRL => u16::from(self.control).into(),
+ RXTX => self.read_data_register(&mut update_irq),
+ CLEAR_TX_INT | CLEAR_RX_INT => 0,
+ ERROR => 0, // TODO par_status, dma_parity_error
+ };
+ (update_irq, result)
+ }
+
+ pub(self) fn write(
+ &mut self,
+ offset: RegisterOffset,
+ value: u16,
+ _char_frontend: &CharFrontend,
+ ) -> bool {
+ eprintln!("write offset {offset:?} value {value}");
+ use RegisterOffset::*;
+ match offset {
+ RXTX => return self.write_data_register(value),
+ CTRL => {
+ // ??? Need to implement the enable bit.
+ self.control = value.into();
+ },
+ CLEAR_TX_INT | CLEAR_RX_INT => todo!(),
+ ERROR => todo!()
+ }
+ false
+ }
+
+ fn read_data_register(&mut self, update: &mut bool) -> u16 {
+ let c = self.read_fifo[self.read_pos];
+
+ if self.read_count > 0 {
+ self.read_count -= 1;
+ self.read_pos = (self.read_pos + 1) & (self.fifo_depth() - 1);
+ }
+ if self.read_count == 0 {
+ }
+ if self.read_count + 1 == self.read_trigger {
+ self.int_level &= !Interrupt::RX;
+ }
+ *update = true;
+ u16::from(c)
+ }
+
+ fn write_data_register(&mut self, value: u16) -> bool {
+ if !self.control.enable_transmit() {
+ log_mask_ln!(Log::GuestError, " SC144 UART data written to disabled TX UART");
+ }
+ // interrupts always checked
+ let _ = false && self.fifo_rx_put(value.into());
+ self.int_level |= Interrupt::TX;
+ true
+ }
+
+ pub fn reset(&mut self) {
+ self.dmacr = 0;
+ self.int_enabled = 0.into();
+ self.int_level = 0.into();
+ self.ilpr = 0;
+ self.read_trigger = 1;
+ self.ifl = 0x12;
+ self.control.reset();
+ self.reset_rx_fifo();
+ self.reset_tx_fifo();
+ }
+
+ pub fn reset_rx_fifo(&mut self) {
+ self.read_count = 0;
+ self.read_pos = 0;
+ }
+
+ pub fn reset_tx_fifo(&mut self) {}
+
+ #[inline]
+ pub fn fifo_enabled(&self) -> bool {
+ false
+ }
+
+ #[inline]
+ pub fn fifo_depth(&self) -> u32 {
+ 1
+ }
+
+ #[must_use]
+ pub fn fifo_rx_put(&mut self, value: registers::Data) -> bool {
+ let depth = self.fifo_depth();
+ assert!(depth > 0);
+ let slot = (self.read_pos + self.read_count) & (depth - 1);
+ self.read_fifo[slot] = value;
+ self.read_count += 1;
+ if self.read_count == depth {}
+
+ if self.read_count == self.read_trigger {
+ self.int_level |= Interrupt::RX;
+ return true;
+ }
+ false
+ }
+
+ pub fn post_load(&mut self) -> Result<(), migration::InvalidError> {
+ /* Sanity-check input state */
+ if self.read_pos >= self.read_fifo.len() || self.read_count > self.read_fifo.len() {
+ return Err(migration::InvalidError);
+ }
+
+ if !self.fifo_enabled() && self.read_count > 0 && self.read_pos > 0 {
+ // Older versions of PL011 didn't ensure that the single
+ // character in the FIFO in FIFO-disabled mode is in
+ // element 0 of the array; convert to follow the current
+ // code's assumptions.
+ self.read_fifo[0] = self.read_fifo[self.read_pos];
+ self.read_pos = 0;
+ }
+
+ Ok(())
+ }
+}
+
+impl SC144UartState {
+ /// Initializes a pre-allocated, uninitialized instance of `SC144UartState`.
+ ///
+ /// # Safety
+ ///
+ /// `self` must point to a correctly sized and aligned location for the
+ /// `SC144UartState` type. It must not be called more than once on the same
+ /// location/instance. All its fields are expected to hold uninitialized
+ /// values with the sole exception of `parent_obj`.
+ unsafe fn init(mut this: ParentInit) {
+ static PL011_OPS: MemoryRegionOps = MemoryRegionOpsBuilder::::new()
+ .read(&SC144UartState::read)
+ .write(&SC144UartState::write)
+ .native_endian()
+ .impl_sizes(2, 2)
+ .build();
+
+ // SAFETY: this and this.iomem are guaranteed to be valid at this point
+ MemoryRegion::init_io(
+ &mut uninit_field_mut!(*this, iomem),
+ &PL011_OPS,
+ "sc144uart",
+ 10,
+ );
+
+ uninit_field_mut!(*this, regs).write(Default::default());
+
+ let clock = DeviceState::init_clock_in(
+ &mut this,
+ "clk",
+ &Self::clock_update,
+ ClockEvent::ClockUpdate,
+ );
+ uninit_field_mut!(*this, clock).write(clock);
+ }
+
+ const fn clock_update(&self, _event: ClockEvent) {
+ /* pl011_trace_baudrate_change(s); */
+ }
+
+ pub fn clock_needed(&self) -> bool {
+ self.migrate_clock
+ }
+
+ fn post_init(&self) {
+ self.init_mmio(&self.iomem);
+ for irq in self.interrupts.iter() {
+ self.init_irq(irq);
+ }
+ }
+
+ fn read(&self, offset: hwaddr, _size: u32) -> u64 {
+ match RegisterOffset::try_from(offset) {
+ Err(_) => {
+ log_mask_ln!(Log::GuestError, "SC144UartState::read: Bad offset {offset}");
+ 0
+ }
+ Ok(field) => {
+ let (update_irq, result) = self.regs.borrow_mut().read(field);
+ if update_irq {
+ self.update();
+ self.char_backend.accept_input();
+ }
+ result.into()
+ }
+ }
+ }
+
+ fn write(&self, offset: hwaddr, value: u64, _size: u32) {
+ let mut update_irq = false;
+ if let Ok(field) = RegisterOffset::try_from(offset) {
+ // qemu_chr_fe_write_all() calls into the can_receive
+ // callback, so handle writes before entering PL011Registers.
+ if field == RegisterOffset::RXTX {
+ // ??? Check if transmitter is enabled.
+ let ch: [u8; 1] = [value as u8];
+ // XXX this blocks entire thread. Rewrite to use
+ // qemu_chr_fe_write and background I/O callbacks
+ let _ = self.char_backend.write_all(&ch);
+ }
+
+ update_irq = self
+ .regs
+ .borrow_mut()
+ .write(field, value as u16, &self.char_backend);
+ } else {
+ log_mask_ln!(
+ Log::GuestError,
+ "SC144UartState::write: Bad offset {offset} value {value}"
+ );
+ }
+ if update_irq {
+ self.update();
+ }
+ }
+
+ fn can_receive(&self) -> u32 {
+ let regs = self.regs.borrow();
+ // trace_pl011_can_receive(s->lcr, s->read_count, r);
+ regs.fifo_depth() - regs.read_count
+ }
+
+ fn receive(&self, buf: &[u8]) {
+ let mut regs = self.regs.borrow_mut();
+
+ let mut update_irq = false;
+ for &c in buf {
+ let c: u16 = c.into();
+ update_irq |= regs.fifo_rx_put(c.into());
+ }
+
+ // Release the BqlRefCell before calling self.update()
+ drop(regs);
+ if update_irq {
+ self.update();
+ }
+ }
+
+ fn event(&self, _event: Event) {
+ let update_irq = false;
+ let regs = self.regs.borrow_mut();
+ // Release the BqlRefCell before calling self.update()
+ drop(regs);
+
+ if update_irq {
+ self.update()
+ }
+ }
+
+ fn realize(&self) -> util::Result<()> {
+ self.char_backend
+ .enable_handlers(self, Self::can_receive, Self::receive, Self::event);
+ Ok(())
+ }
+
+ fn reset_hold(&self, _type: ResetType) {
+ self.regs.borrow_mut().reset();
+ }
+
+ fn update(&self) {
+ let regs = self.regs.borrow();
+ let flags = regs.int_level & regs.int_enabled;
+ for (irq, i) in self.interrupts.iter().zip(IRQMASK) {
+ irq.set(flags.any_set(i));
+ }
+ }
+
+ pub fn post_load(&self, _version_id: u8) -> Result<(), migration::InvalidError> {
+ self.regs.borrow_mut().post_load()
+ }
+}
+
+/// Which bits in the interrupt status matter for each outbound IRQ line ?
+const IRQMASK: [Interrupt; 6] = [
+ Interrupt::all(),
+ Interrupt::RX,
+ Interrupt::TX,
+ Interrupt::RT,
+ Interrupt::MS,
+ Interrupt::E,
+];
+
+/// # Safety
+///
+/// We expect the FFI user of this function to pass a valid pointer for `chr`
+/// and `irq`.
+#[no_mangle]
+pub unsafe extern "C" fn sc144uart_create(
+ addr: u64,
+ irq: *mut IRQState,
+ chr: *mut Chardev,
+) -> *mut DeviceState {
+ // SAFETY: The callers promise that they have owned references.
+ // They do not gift them to sc144uart_create, so use `Owned::from`.
+ let irq = unsafe { Owned::::from(&*irq) };
+
+ let dev = SC144UartState::new();
+ if !chr.is_null() {
+ let chr = unsafe { Owned::::from(&*chr) };
+ dev.prop_set_chr("chardev", &chr);
+ }
+ dev.sysbus_realize().unwrap_fatal();
+ dev.mmio_map(0, addr);
+ dev.connect_irq(0, &irq);
+
+ // The pointer is kept alive by the QOM tree; drop the owned ref
+ dev.as_mut_ptr()
+}
+
+/// Migration subsection for [`SC144UartState`] clock.
+static VMSTATE_SC144UART_CLOCK: VMStateDescription =
+ VMStateDescriptionBuilder::::new()
+ .name(c"sc144uart/clock")
+ .version_id(1)
+ .minimum_version_id(1)
+ .needed(&SC144UartState::clock_needed) // needed: Some(sc144uart_clock_needed),
+ .fields(vmstate_fields! {
+ vmstate_of!(SC144UartState, clock),
+ })
+ .build();
+
+impl_vmstate_struct!(
+ SC144UartRegisters,
+ VMStateDescriptionBuilder::::new()
+ .name(c"sc144uart/regs")
+ .version_id(2)
+ .minimum_version_id(2)
+ .fields(vmstate_fields! {
+ vmstate_of!(SC144UartRegisters, control),
+ vmstate_of!(SC144UartRegisters, dmacr),
+ vmstate_of!(SC144UartRegisters, int_enabled),
+ vmstate_of!(SC144UartRegisters, int_level),
+ vmstate_of!(SC144UartRegisters, read_fifo),
+ vmstate_of!(SC144UartRegisters, ilpr),
+ vmstate_of!(SC144UartRegisters, ifl),
+ vmstate_of!(SC144UartRegisters, read_pos),
+ vmstate_of!(SC144UartRegisters, read_count),
+ vmstate_of!(SC144UartRegisters, read_trigger),
+ })
+ .build()
+);
+
+pub const VMSTATE_SC144UART: VMStateDescription =
+ VMStateDescriptionBuilder::::new()
+ .name(c"pl011")
+ .version_id(2)
+ .minimum_version_id(2)
+ .post_load(&SC144UartState::post_load)
+ .fields(vmstate_fields! {
+ vmstate_unused!(core::mem::size_of::()),
+ vmstate_of!(SC144UartState, regs),
+ })
+ .subsections(vmstate_subsections! {
+ VMSTATE_SC144UART_CLOCK
+ })
+ .build();
diff --git a/rust/hw/char/dialog_uart/src/lib.rs b/rust/hw/char/dialog_uart/src/lib.rs
new file mode 100644
index 0000000000..58452e559d
--- /dev/null
+++ b/rust/hw/char/dialog_uart/src/lib.rs
@@ -0,0 +1,21 @@
+// Copyright 2024, Linaro Limited
+// Author(s): Manos Pitsidianakis
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+//! SC144xx UART QEMU Device Model
+//!
+//! This library implements a device model for the UART device
+//! found in SC144xx DECT SoCs in QEMU.
+//!
+//! # Library crate
+//!
+//! See [`SC144State`](crate::device::SC144State) for the device model type and
+//! the [`registers`] module for register types.
+
+mod device;
+mod registers;
+mod bindings;
+
+pub use device::sc144uart_create;
+
+pub const TYPE_SC144UART: &::std::ffi::CStr = c"sc144uart";
diff --git a/rust/hw/char/dialog_uart/src/registers.rs b/rust/hw/char/dialog_uart/src/registers.rs
new file mode 100644
index 0000000000..aeadb00497
--- /dev/null
+++ b/rust/hw/char/dialog_uart/src/registers.rs
@@ -0,0 +1,195 @@
+// Copyright 2024, Linaro Limited
+// Author(s): Manos Pitsidianakis
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+//! Device registers exposed as typed structs which are backed by arbitrary
+//! integer bitmaps. [`Data`], [`Control`], [`LineControl`], etc.
+
+// For more detail see the PL011 Technical Reference Manual DDI0183:
+// https://developer.arm.com/documentation/ddi0183/latest/
+
+use bilge::prelude::*;
+use bits::bits;
+use migration::{impl_vmstate_bitsized, impl_vmstate_forward};
+
+/*;; UART registers
+
+sfr = "UART_CTRL_REG", "Memory", 0xFF4900, 2, base=16
+sfr = "UART_CTRL_REG.UART_REN", "Memory", 0xFF4900, 2, base=16, bitRange=0
+sfr = "UART_CTRL_REG.UART_TEN", "Memory", 0xFF4900, 2, base=16, bitRange=1
+sfr = "UART_CTRL_REG.BAUDRATE", "Memory", 0xFF4900, 2, base=16, bitRange=2-4
+sfr = "UART_CTRL_REG.TI", "Memory", 0xFF4900, 2, base=16, bitRange=5
+sfr = "UART_CTRL_REG.RI", "Memory", 0xFF4900, 2, base=16, bitRange=6
+sfr = "UART_CTRL_REG.UART_MODE", "Memory", 0xFF4900, 2, base=16, bitRange=7
+sfr = "UART_CTRL_REG.IRDA_EN", "Memory", 0xFF4900, 2, base=16, bitRange=8
+sfr = "UART_CTRL_REG.INV_URX", "Memory", 0xFF4900, 2, base=16, bitRange=9
+sfr = "UART_CTRL_REG.INV_UTX", "Memory", 0xFF4900, 2, base=16, bitRange=10
+
+sfr = "UART_ERROR_REG", "Memory", 0xFF4908, 2, base=16
+sfr = "UART_ERROR_REG.PAR_STATUS", "Memory", 0xFF4908, 2, base=16, bitRange=0
+sfr = "UART_ERROR_REG.DMA_PARITY_ERROR", "Memory", 0xFF4908, 2, base=16, bitRange=1*/
+
+/// Offset of each register from the base memory address of the device.
+#[doc(alias = "offset")]
+#[allow(non_camel_case_types)]
+#[repr(u64)]
+#[derive(Debug, Eq, PartialEq, common::TryInto)]
+pub enum RegisterOffset {
+ /// Data Register
+ ///
+ /// has a bunch of bitflags
+ #[doc(alias = "UART_CTRL_REG")]
+ CTRL = 0x000,
+ /// RX/TX Register
+ #[doc(alias = "UART_RX_TX_REG")]
+ RXTX = 0x002,
+ /// Clear TX Interrupt Register
+ #[doc(alias = "UART_CLEAR_TX_INT_REG")]
+ CLEAR_TX_INT = 0x004,
+ /// Clear RX Interrupt Register
+ #[doc(alias = "UART_CLEAR_RX_INT_REG")]
+ CLEAR_RX_INT = 0x006,
+ /// Error Register
+ #[doc(alias = "UART_ERROR_REG")]
+ ERROR = 0x008,
+}
+
+/// Data Register, `UART_RX_TX_REG`
+///
+/// The `UART_RX_TX_REG` register is the data register;
+/// write for TX and read for RX.
+/// It is an 8-bit register, where bits 7..0 are the data.
+#[bitsize(16)]
+#[derive(Clone, Copy, Default, DebugBits, FromBits)]
+#[doc(alias = "UART_RX_TX_REG")]
+pub struct Data {
+ pub data: u8,
+ _reserved: u8,
+}
+impl_vmstate_bitsized!(Data);
+
+/// Control Register, `UART_CTRL_REG`
+///
+/// The `UART_CTRL_REG` register is the control register. It contains various
+/// enable bits, and the bits to write to set the usual outbound RS232
+/// modem control signals. All bits reset to 0 except TXE and RXE.
+#[bitsize(16)]
+#[doc(alias = "UART_CTRL_REG")]
+#[derive(Clone, Copy, DebugBits, FromBits)]
+pub struct Control {
+ /// `UART_REN` Receive enable
+ pub enable_receive: bool,
+ /// `UART_TEN` Transmit enable
+ pub enable_transmit: bool,
+ /// `BAUDRATE` Baudrate
+ pub baudrate: BaudRate,
+ /// `TI` Transmit interrupt. Read only
+ pub tx_interrupt: bool,
+ /// `RI` Receive interrupt. Read only
+ pub rx_interrupt: bool,
+ /// `UART_MODE`
+ pub uart_mode: u1,
+ /// `IRDA_EN` enable RZI mode for IrDA.
+ /// QEMU does not model this.
+ pub enable_irda: bool,
+ /// `INV_URX`
+ pub invert_rx: bool,
+ /// `INV_UTX`
+ pub invert_tx: bool,
+ /// 15:11 - Reserved, do not modify, read as zero.
+ _reserved_zero_no_modify: u5,
+}
+impl_vmstate_bitsized!(Control);
+
+impl Control {
+ pub fn reset(&mut self) {
+ *self = 0.into();
+ }
+}
+
+impl Default for Control {
+ fn default() -> Self {
+ let mut ret: Self = 0.into();
+ ret.reset();
+ ret
+ }
+}
+
+#[bitsize(3)]
+#[derive(Clone, Copy, Debug, Eq, FromBits, PartialEq)]
+/// `UART_MODE` "Enable FIFOs" or Device mode, field of [Line Control
+/// register](LineControl).
+pub enum BaudRate {
+ Baud9600 = 0b000,
+ Baud19200 = 0b001,
+ Baud57600 = 0b010,
+ Baud115200 = 0b011,
+ BaudFSYS = 0b100, // See datasheet
+ Reserved1 = 0b101,
+ Reserved2 = 0b110,
+ Reserved3 = 0b111,
+}
+
+/// Error Register, `UART_ERROR_REG`
+///
+/// The `UART_ERROR_REG` register is the error register.
+/// It contains bits related to parity errors.
+/// All bits reset to 0 except TXE and RXE.
+#[bitsize(16)]
+#[doc(alias = "UART_ERROR_REG")]
+#[derive(Clone, Copy, DebugBits, FromBits)]
+pub struct ErrorReg {
+ /// `PAR_STATUS` Indicates a parity error, updated every byte
+ pub parity_status: bool,
+ /// `DMA_PARITY_ERROR` TODO
+ pub dma_parity_error: bool,
+ /// 15:2 - Reserved, do not modify, read as zero.
+ _reserved_zero_no_modify: u14,
+}
+impl_vmstate_bitsized!(ErrorReg);
+
+impl ErrorReg {
+ pub fn reset(&mut self) {
+ *self = 0.into();
+ }
+}
+
+impl Default for ErrorReg {
+ fn default() -> Self {
+ let mut ret: Self = 0.into();
+ ret.reset();
+ ret
+ }
+}
+
+#[bitsize(1)]
+#[derive(Clone, Copy, Debug, Eq, FromBits, PartialEq)]
+/// `UART_MODE` "Enable FIFOs" or Device mode, field of [Line Control
+/// register](LineControl).
+pub enum Mode {
+ Character = 0,
+ /// 1 = transmit and receive FIFO buffers are enabled (FIFO mode).
+ FIFO = 1,
+}
+
+bits! {
+ /// Interrupt status bits in UARTRIS, UARTMIS, UARTIMSC
+ #[derive(Default)]
+ pub struct Interrupt(u32) {
+ OE = 1 << 10,
+ BE = 1 << 9,
+ PE = 1 << 8,
+ FE = 1 << 7,
+ RT = 1 << 6,
+ TX = 1 << 5,
+ RX = 1 << 4,
+ DSR = 1 << 3,
+ DCD = 1 << 2,
+ CTS = 1 << 1,
+ RI = 1 << 0,
+
+ E = bits!(Self as u32: OE | BE | PE | FE),
+ MS = bits!(Self as u32: RI | DSR | DCD | CTS),
+ }
+}
+impl_vmstate_forward!(Interrupt);
diff --git a/rust/hw/char/dialog_uart/wrapper.h b/rust/hw/char/dialog_uart/wrapper.h
new file mode 100644
index 0000000000..87a5a589c8
--- /dev/null
+++ b/rust/hw/char/dialog_uart/wrapper.h
@@ -0,0 +1,51 @@
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2024 Linaro Ltd.
+ *
+ * Authors: Manos Pitsidianakis
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+/*
+ * This header file is meant to be used as input to the `bindgen` application
+ * in order to generate C FFI compatible Rust bindings.
+ */
+
+#ifndef __CLANG_STDATOMIC_H
+#define __CLANG_STDATOMIC_H
+/*
+ * Fix potential missing stdatomic.h error in case bindgen does not insert the
+ * correct libclang header paths on its own. We do not use stdatomic.h symbols
+ * in QEMU code, so it's fine to declare dummy types instead.
+ */
+typedef enum memory_order {
+ memory_order_relaxed,
+ memory_order_consume,
+ memory_order_acquire,
+ memory_order_release,
+ memory_order_acq_rel,
+ memory_order_seq_cst,
+} memory_order;
+#endif /* __CLANG_STDATOMIC_H */
+
+#include "qemu/osdep.h"
+#include "hw/char/pl011.h"
diff --git a/rust/hw/char/meson.build b/rust/hw/char/meson.build
index 5716dc43ef..c54529c186 100644
--- a/rust/hw/char/meson.build
+++ b/rust/hw/char/meson.build
@@ -1 +1,2 @@
+subdir('dialog_uart')
subdir('pl011')
diff --git a/target/cr16c/cpu.c b/target/cr16c/cpu.c
index 8d7df6d9b6..eb816f9d6f 100644
--- a/target/cr16c/cpu.c
+++ b/target/cr16c/cpu.c
@@ -14,6 +14,7 @@
static void cr16c_cpu_disas_set_info(CPUState *cpu, disassemble_info *info) {
info->mach = bfd_arch_cr16c;
info->endian = BFD_ENDIAN_LITTLE;
+ info->print_insn = cr16c_print_insn;
}
static void cr16c_cpu_realizefn(DeviceState *dev, Error **errp)
@@ -54,12 +55,16 @@ static void cr16c_cpu_reset_hold(Object *obj, ResetType type)
env->r[0] = env->pc;
env->r[1] = env->pc >> 16;
- env->f_n = 0;
- env->f_z = 0;
- env->f_f = 0;
- env->f_l = 0;
- env->f_c = 0;
- // TODO: Rest of flags from PSR once implemented
+ env->psr_n = 0;
+ env->psr_z = 0;
+ env->psr_f = 0;
+ env->psr_l = 0;
+ env->psr_c = 0;
+ env->psr_t = 0;
+ env->psr_u = 0;
+ env->psr_e = 0;
+ env->psr_p = 0;
+ env->psr_i = 0;
env->pc = 0;
}
@@ -97,11 +102,14 @@ static void cr16c_cpu_dump_state(CPUState *cs, FILE *f, int flags)
qemu_fprintf(f, "SP: " TARGET_FMT_lx "\n", env->r[CR16C_REGNO_SP]);
qemu_fprintf(f, "-- Flags --\n");
- qemu_fprintf(f, "N: %2d\n", env->f_n);
- qemu_fprintf(f, "Z: %2d\n", env->f_z);
- qemu_fprintf(f, "F: %2d\n", env->f_f);
- qemu_fprintf(f, "L: %2d\n", env->f_l);
- qemu_fprintf(f, "C: %2d\n", env->f_c);
+ qemu_fprintf(f, "PSR: %04x\n", cr16c_cpu_pack_psr(env));
+ qemu_fprintf(f, ". I P E N Z F U L T C\n");
+ qemu_fprintf(f, ". %d %d %d %d %d %d %d %d %d %d\n",
+ env->psr_i, env->psr_p, env->psr_e, env->psr_n, env->psr_z, env->psr_f, env->psr_u, env->psr_l, env->psr_t, env->psr_c);
+ qemu_fprintf(f, "CFG: %04x\n", cr16c_cpu_pack_cfg(env));
+ qemu_fprintf(f, ". SR ED LIC IC DC\n");
+ qemu_fprintf(f, ". %d %d %d %d %d\n",
+ env->cfg_sr, env->cfg_ed, env->cfg_lic, env->cfg_ic, env->cfg_dc);
qemu_fprintf(f, "\n");
}
@@ -118,13 +126,46 @@ static void cr16c_cpu_set_pc(CPUState *cs, vaddr value)
cpu->env.pc = value;
}
+static vaddr cr16c_cpu_get_pc(CPUState *cs)
+{
+ CR16CCPU *cpu = CR16C_CPU(cs);
+ return cpu->env.pc;
+}
+
static int cr16c_cpu_mmu_index(CPUState *cs, bool ifetch)
{
return 0;
}
+// Priority:
+// DBG > IAD > NMI > Interrupt > ISE > PSR.P = 1, some instruction suspend stuff
+// interrupt dispatch vectors
+#define INT_RESERVED_0 0
+#define INT_NMI 1
+#define INT_RESERVED_2 2
+#define INT_RESERVED_3 3
+#define INT_RESERVED_4 4
+#define INT_SVC 5 // Supervisor Call Trap
+#define INT_DVZ 6 // Divide By Zero
+#define INT_FLG 7 // Flag
+#define INT_BPT 8 // Breakpoint
+#define INT_TRC 9 // Trace
+#define INT_UND 10 // Undefined instruction
+#define INT_RESERVED_11 11
+#define INT_IAD 12 // Illegal Address
+#define INT_RESERVED_13 13
+#define INT_DBG 14 // Debug
+#define INT_ISE 15 // In-System Emulator
+#define INT_MASKABLE_MIN 16
+#define INT_MASKABLE_MAX 127
+
static bool cr16c_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
{
+ //CPUCR16CState *env = cpu_env(cs);
+
+ if (interrupt_request & CPU_INTERRUPT_HARD) {
+
+ }
// TODO
return false;
}
@@ -153,6 +194,11 @@ bool cr16c_cpu_tlb_fill(CPUState *cs, vaddr addr, int size,
return true;
}
+void cr16c_restore_state_to_opc(CPUState *cpu, const TranslationBlock *tb,
+ const uint64_t *data) {
+ cpu_env(cpu)->pc = data[0];
+}
+
#include "hw/core/sysemu-cpu-ops.h"
@@ -168,12 +214,12 @@ static const struct TCGCPUOps cr16c_tcg_ops = {
.cpu_exec_reset = cpu_reset,
.translate_code = cr16c_translate_code,
.get_tb_cpu_state = cr16c_cpu_get_tb_cpu_state,
+ .restore_state_to_opc = cr16c_restore_state_to_opc,
.tlb_fill = cr16c_cpu_tlb_fill,
.pointer_wrap = cpu_pointer_wrap_notreached,
.cpu_exec_halt = cr16c_cpu_has_work,
};
-
static void cr16c_cpu_class_init(ObjectClass *oc, const void *data)
{
CR16CCPUClass *acc = CR16C_CPU_CLASS(oc);
@@ -188,11 +234,27 @@ static void cr16c_cpu_class_init(ObjectClass *oc, const void *data)
cc->dump_state = cr16c_cpu_dump_state;
cc->set_pc = cr16c_cpu_set_pc;
+ cc->get_pc = cr16c_cpu_get_pc;
cc->sysemu_ops = &cr16c_sysemu_ops;
cc->disas_set_info = cr16c_cpu_disas_set_info;
cc->tcg_ops = &cr16c_tcg_ops;
}
+static void cr16c_cpu_set_int(void *opaque, int irq, int level)
+{
+ // TODO
+}
+
+static void cr16c_cpu_initfn(Object *obj)
+{
+ CR16CCPU *cpu = CR16C_CPU(obj);
+
+ /* Set the number of interrupts supported by the CPU. */
+ qdev_init_gpio_in(DEVICE(cpu), cr16c_cpu_set_int,
+ //sizeof(cpu->env.intsrc) * 8);
+ 127 - 15);
+}
+
static const TypeInfo cr16c_cpu_archs[] = {
{
.name = TYPE_CR16C_CPU,
@@ -200,6 +262,7 @@ static const TypeInfo cr16c_cpu_archs[] = {
.instance_size = sizeof(CR16CCPU),
.class_size = sizeof(CR16CCPUClass),
.class_init = cr16c_cpu_class_init,
+ .instance_init = cr16c_cpu_initfn
},
};
diff --git a/target/cr16c/cpu.h b/target/cr16c/cpu.h
index a6f8cb017d..4f7afba78c 100644
--- a/target/cr16c/cpu.h
+++ b/target/cr16c/cpu.h
@@ -4,6 +4,36 @@
#include "qemu/osdep.h"
#include "cpu-qom.h"
#include "exec/cpu-defs.h"
+#include "hw/registerfields.h"
+
+/* PSR define */
+REG16(PSR, 0)
+FIELD(PSR, C, 0, 1)
+FIELD(PSR, T, 1, 1)
+FIELD(PSR, L, 2, 1)
+FIELD(PSR, U, 3, 1)
+// bit 4 is reserved
+FIELD(PSR, F, 5, 1)
+FIELD(PSR, Z, 6, 1)
+FIELD(PSR, N, 7, 1)
+// bit 8 is reserved
+FIELD(PSR, E, 9, 1)
+FIELD(PSR, P, 10, 1)
+FIELD(PSR, I, 11, 1)
+// 12-15 are reserved as well
+
+/* CFG define */
+REG16(CFG, 0)
+// bit 0, 1 are fixed 0
+FIELD(CFG, DC, 2, 1)
+FIELD(CFG, LDC, 3, 1)
+FIELD(CFG, IC, 4, 1)
+FIELD(CFG, LIC, 5, 1)
+// bit 6, 7 are fixed 0
+FIELD(CFG, ED, 8, 1)
+FIELD(CFG, SR, 9, 1)
+// bit 10-15 are reserved
+
#define CR16C_REG_COUNT 16
#define CR16C_FIRST_32B_REG 12
@@ -25,11 +55,24 @@ typedef struct CPUArchState {
uint32_t r[CR16C_REG_COUNT]; /* General purpose registers: 12x16-bit + 2x32-bit */
- uint32_t f_n;
- uint32_t f_z;
- uint32_t f_f;
- uint32_t f_l;
- uint32_t f_c;
+ uint32_t psr_n; // Negative bit
+ uint32_t psr_z; // Zero bit
+ uint32_t psr_f; // Flag bit
+ uint32_t psr_l; // Low flag bit
+ uint32_t psr_c; // Carry bit
+
+ uint32_t psr_t; // Trace bit
+ uint32_t psr_u; // User mode bit
+ uint32_t psr_e; // local maskable interrupt Enable bit
+ uint32_t psr_p; // trace trap Pending bit
+ uint32_t psr_i; // global maskable Interrupt enable bit
+
+ uint32_t cfg_dc; // Data Cache bit
+ uint32_t cfg_ldc; // Lock Data Cache bit
+ uint32_t cfg_ic; // Instruction Cache bit
+ uint32_t cfg_lic; // Lock Instruction Cache bit
+ uint32_t cfg_ed; // Extended Dispatch bit
+ uint32_t cfg_sr; // Short Register bit
} CPUCR16CState;
struct ArchCPU {
@@ -60,4 +103,35 @@ void cr16c_cpu_synchronize_from_tb(CPUState *cs, const TranslationBlock *tb);
void cr16c_translate_code(CPUState *cs, TranslationBlock *tb,
int *max_insns, vaddr pc, void *host_pc);
+void cr16c_restore_state_to_opc(CPUState *cpu, const TranslationBlock *tb,
+ const uint64_t *data);
+
+static inline uint32_t cr16c_cpu_pack_psr(const CPUCR16CState *env)
+{
+ uint32_t psr = 0;
+ psr = FIELD_DP32(psr, PSR, C, env->psr_c);
+ psr = FIELD_DP32(psr, PSR, T, env->psr_t);
+ psr = FIELD_DP32(psr, PSR, L, env->psr_l);
+ psr = FIELD_DP32(psr, PSR, U, env->psr_u);
+ psr = FIELD_DP32(psr, PSR, N, env->psr_n);
+ psr = FIELD_DP32(psr, PSR, Z, env->psr_z);
+ psr = FIELD_DP32(psr, PSR, F, env->psr_f);
+ psr = FIELD_DP32(psr, PSR, E, env->psr_e);
+ psr = FIELD_DP32(psr, PSR, P, env->psr_p);
+ psr = FIELD_DP32(psr, PSR, I, env->psr_i);
+ return psr;
+}
+
+static inline uint32_t cr16c_cpu_pack_cfg(const CPUCR16CState *env)
+{
+ uint32_t cfg = 0;
+ cfg = FIELD_DP32(cfg, CFG, DC, env->cfg_dc);
+ cfg = FIELD_DP32(cfg, CFG, LDC, env->cfg_ldc);
+ cfg = FIELD_DP32(cfg, CFG, IC, env->cfg_ic);
+ cfg = FIELD_DP32(cfg, CFG, LIC, env->cfg_lic);
+ cfg = FIELD_DP32(cfg, CFG, ED, env->cfg_ed);
+ cfg = FIELD_DP32(cfg, CFG, SR, env->cfg_sr);
+ return cfg;
+}
+
#endif // !QEMU_CR16C_CPU_H
diff --git a/target/cr16c/disas.c b/target/cr16c/disas.c
new file mode 100644
index 0000000000..1460411d09
--- /dev/null
+++ b/target/cr16c/disas.c
@@ -0,0 +1,258 @@
+/*
+ * CR16C disassembler, adapted from the AVR one
+ *
+ * Copyright (c) 2025 fridtjof
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "qemu/osdep.h"
+#include "cpu.h"
+
+// TODO
+typedef struct {
+ disassemble_info *info;
+ uint16_t next_word;
+ bool next_word_used;
+} DisasContext;
+/*
+static uint64_t decode_load_bytes(DisasContext *ctx, uint64_t insn, int i, int n) {
+ for(; i < n; i+=2) {
+ insn |= (uint64_t)translator_lduw(ctx->env, &ctx->base, ctx->base.pc_next) << (48 - i * 8);
+ ctx->base.pc_next += 2;
+ }
+ if (i == n)
+ return insn;
+ else {
+ gen_helper_raise_illegal_instruction(tcg_env);
+ return 0;
+ }
+}
+
+
+static int16_t u4_load_s16(DisasContext *ctx, int val) {
+ if (val == 0x9) {
+ return -1;
+ }
+ else if (val == 0xB) {
+ int16_t val_ld = cpu_ldsw_le_data(ctx->env, ctx->base.pc_next);
+ ctx->base.pc_next += 2;
+ return val_ld;
+ }
+ return val;
+}
+
+static uint16_t load_u16(DisasContext *ctx) {
+ ctx->next_word_used = 1;
+ uint16_t imm = cpu_lduw_code(ctx->env, ctx->base.pc_next);
+ ctx->base.pc_next += 2;
+ return imm;
+}
+
+static uint8_t get_disp4(DisasContext *ctx, uint8_t disp) {
+ return disp << 1;
+}
+
+static int32_t disp8_get_dest(DisasContext* ctx, int32_t disp) {
+ int32_t dest = ctx->base.pc_next - 2;
+ if (disp == 0xFFFFFF80) {
+ dest += cpu_ldsw_le_data(ctx->env, ctx->base.pc_next);
+ ctx->base.pc_next += 2;
+ }
+ else {
+ dest += disp << 1;
+ }
+ return dest;
+};*/
+
+static uint32_t reloc_abs20(DisasContext *ctx, uint32_t addr) {
+ if (addr > 0xEFFFF) {
+ addr |= 0xF00000;
+ }
+ return addr;
+}
+
+/* Include the auto-generated decoder. */
+//static bool decode_insn(DisasContext *ctx, uint16_t insn);
+//#include "decode-insn.c.inc"
+
+#define output(mnemonic, format, ...) \
+(pctx->info->fprintf_func(pctx->info->stream, "%-9s " format, \
+mnemonic, ##__VA_ARGS__))
+
+int cr16c_print_insn(bfd_vma addr, disassemble_info *info)
+{
+ DisasContext ctx = { info };
+ DisasContext *pctx = &ctx;
+ bfd_byte buffer[4];
+ uint16_t insn;
+ int status;
+
+ status = info->read_memory_func(addr, buffer, 2, info);
+ if (status != 0) {
+ info->memory_error_func(status, addr, info);
+ return -1;
+ }
+ insn = bfd_getl16(buffer);
+ (void)insn;
+
+ status = info->read_memory_func(addr + 2, buffer + 2, 2, info);
+ if (status == 0) {
+ ctx.next_word = bfd_getl16(buffer + 2);
+ }
+
+ //if (!decode_insn(&ctx, insn)) {
+ output(".db", "0x%02x, 0x%02x", buffer[0], buffer[1]);
+ //}
+
+ if (!ctx.next_word_used) {
+ return 2;
+ } else if (status == 0) {
+ return 4;
+ }
+ info->memory_error_func(status, addr + 2, info);
+ return -1;
+}
+
+#define INSN(opcode, format, ...) \
+static bool trans_##opcode(DisasContext *pctx, arg_##opcode * a) \
+{ \
+output(#opcode, format, ##__VA_ARGS__); \
+return true; \
+}
+
+#define INSN_MNEMONIC(opcode, mnemonic, format, ...) \
+static bool trans_##opcode(DisasContext *pctx, arg_##opcode * a) \
+{ \
+output(mnemonic, format, ##__VA_ARGS__); \
+return true; \
+}
+
+/*
+static const char* width[] = {
+ "x", "B", "W", "x", "D"
+};
+
+INSN(UNIMPLEMENTED, "")
+
+INSN(LOAD, "")
+INSN(LOAD_rrp, "")
+INSN(LOAD_abs, "")
+INSN(LOAD_ind_abs, "")
+//INSN(LOADD, "")
+INSN(STOR, "")
+INSN(STOR_rrp, "")
+INSN(STOR_rrp_imm, "")
+INSN(STOR_abs, "")
+INSN(STOR_ind_abs, "")
+INSN(STOR_abs_imm, "")
+INSN(STOR_abs_rrp_imm, "")
+//INSN(STORD, "")
+INSN(STOR_imm, "$0x%x, r%d", a->imm, a->ra)
+INSN(LOADM, "$0x%x", a->cnt)
+//INSN(LOADMP, "$0x%x", a->cnt)
+INSN(STORM, "$0x%x", a->cnt)
+//INSN(STORMP, "$0x%x", a->cnt)
+
+INSN(MOV_reg, "%s r%d, r%d", width[a->width], a->rs, a->rd)
+INSN(MOV_imm, "%s $0x%x, r%d", width[a->width], a->imm, a->rd)
+INSN(MOVD_reg, "r%dr%d, r%dr%d", a->rs + 1, a->rs, a->rd + 1, a->rd)
+INSN(MOVXB, "r%d, r%d", a->rs, a->rd)
+INSN(MOVZB, "r%d, r%d", a->rs, a->rd)
+INSN(MOVXW, "r%d, r%d", a->rs, a->rd)
+INSN(MOVZW, "r%d, r%d", a->rs, a->rd)
+
+INSN(ADD_imm, "$0x%x, r%d", a->imm, a->rd)
+INSN(ADD_reg, "r%d, r%d", a->rs, a->rd)
+INSN(ADDU_imm, "$0x%x, r%d", a->imm, a->rd)
+INSN(ADDU_reg, "r%d, r%d", a->rs, a->rd)
+//INSN(ADDC_imm, "")
+//INSN(ADDC_reg, "r%d, r%d", a->rs, a->rd)
+INSN(ADDD_imm, "$0x%x, r%dr%d", a->imm, a->rd + 1, a->rd)
+INSN(ADDD_rp, "r%dr%d, r%dr%d", a->rs + 1, a->rs, a->rd + 1, a->rd)
+
+INSN(MACQW, "")
+INSN(MACSW, "")
+INSN(MACUW, "")
+INSN(MUL_imm, "")
+INSN(MULB_reg, "r%d, r%d", a->rs, a->rd)
+INSN(MULW_reg, "r%d, r%d", a->rs, a->rd)
+INSN(MULSB_reg, "r%d, r%d", a->rs, a->rd)
+INSN(MULSW_reg, "r%d, r%d", a->rs, a->rd)
+INSN(MULUW_reg, "r%d, r%d", a->rs, a->rd)
+INSN(SUB_imm, "")
+INSN(SUB_reg, "r%d, r%d", a->rs, a->rd)
+INSN(SUBD_imm, "")
+INSN(SUBD_rp, "")
+//INSN(SUBC_imm, "")
+//INSN(SUBC_reg, "")
+
+INSN(CMP_imm, "")
+INSN(CMP_reg, "")
+//INSN(CMPD_imm, "")
+INSN(CMPD_reg, "")
+INSN(BR0, "") // BEQ0, BNE0
+
+INSN(AND_imm, "")
+INSN(AND_reg, "")
+INSN(ANDD_imm, "")
+INSN(ANDD_rp, "")
+INSN(OR_imm, "")
+INSN(OR_reg, "")
+INSN(ORD_imm, "")
+INSN(ORD_rp, "")
+INSN(XOR_imm, "")
+INSN(XOR_reg, "")
+INSN(XORD_imm, "")
+INSN(XORD_rp, "")
+INSN(SCOND, "")
+
+INSN(ASHU_imm_l, "")
+INSN(ASHU_imm_r, "")
+INSN(ASHU_reg, "")
+INSN(ASHUD_imm_l, "")
+INSN(ASHUD_imm_r, "")
+INSN(ASHUD_rp, "")
+INSN(LSH_imm_r, "")
+INSN(LSH_reg, "")
+INSN(LSHD_imm_r, "")
+INSN(LSHD_rp, "")
+
+// TODO
+//INSN(TBIT, "")
+INSN(SBIT_abs, "")
+INSN(CBIT_abs, "")
+
+// TODO
+INSN(LPR, "")
+INSN(LPRD, "")
+INSN(SPR, "")
+INSN(SPRD, "")
+
+INSN(BRCOND, "")
+INSN(BAL_ra, "")
+// INSN(BR, "") // BRCOND condition "ALWAYS"
+INSN(EXCP, "")
+INSN(JCOND, "")
+//INSN(JAL, "")
+//INSN(JUMP, "") // JCOND condition "ALWAYS"
+//INSN(JUSR, "")
+//INSN(RETX, "")
+INSN(push, "")
+INSN(pop, "")
+
+//INSN(CINV, "")
+//INSN(NOP, "")
+//INSN(WAIT, "")
+*/
diff --git a/target/cr16c/helper.c b/target/cr16c/helper.c
index c214ec664e..4cd8ceae79 100644
--- a/target/cr16c/helper.c
+++ b/target/cr16c/helper.c
@@ -8,7 +8,9 @@
void helper_raise_illegal_instruction(CPUCR16CState* env) {
qemu_printf("Illegal instruction, CPU state was:\n");
cr16c_debug_dump_state(env_cpu(env));
- qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
+ //qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
+ qemu_system_debug_request();
+ //qemu_system_reset_request(SHUTDOWN)
}
void helper_raise_unimplemented_instruction(void) {
@@ -17,5 +19,11 @@ void helper_raise_unimplemented_instruction(void) {
}
void helper_exit(CPUCR16CState *env) {
+ qemu_printf("Guest requested exit\n");
exit(env->r[0]);
}
+
+void helper_dump_regs(CPUCR16CState* env) {
+ qemu_printf("State dump requested:\n");
+ cr16c_debug_dump_state(env_cpu(env));
+}
diff --git a/target/cr16c/helper.h b/target/cr16c/helper.h
index cb1ffd1699..2c9d962887 100644
--- a/target/cr16c/helper.h
+++ b/target/cr16c/helper.h
@@ -1,3 +1,4 @@
DEF_HELPER_1(raise_illegal_instruction, void, env);
DEF_HELPER_0(raise_unimplemented_instruction, void);
DEF_HELPER_1(exit, void, env);
+DEF_HELPER_1(dump_regs, void, env);
diff --git a/target/cr16c/insn.decode b/target/cr16c/insn.decode
index 590ba1ba3c..9e041f4c7d 100644
--- a/target/cr16c/insn.decode
+++ b/target/cr16c/insn.decode
@@ -253,16 +253,35 @@ TBIT_mem_abs 0000 0000 0001 0001 1111 .... .... .... .... .... .... .... wid
BRCOND 0001 .... cond:4 .... dest=%br_disp8
+BR0 0000 11 word:1 ne:1 dest:4 src:4
+
JCOND 0000 1010 cond:4 ra:4
+#JAL 0000 0000 1101 ....
+
EXCP 0000 0000 1100 id:4
+&bal dest
+
+@bal .... .... dest:23 . &bal
+#@bal_3a .... .... .... .... .... p1_1916:4 p2:4 p1_2320:4 p1_1501:15 .
+
+BAL_ra 1100 0000 .... .... .... .... .... ...- @bal # fmt 5
+#BAL_rp 0000 0000 1000 0000 0010 .... .... .... .... .... .... .... @bal_3a # fmt 3a
+
+&pop rt ra count dest
+@pop .... ... rt:1 ra:1 count:3 dest:4 &pop
+&push ra count src
+@push .... .... ra:1 count:3 src:4 &push
+
+pop 0000 001. .... .... @pop # fmt 14, includes popret via rt bit
+push 0000 0001 .... .... @push # fmt 14
### Load and Store ###
&load rd ra dbase disp width
&load_rrp rd rrp disp width
-&load_abs rd addr width
+&load_abs rd addr width remap
&load_ind_abs rd ri addr width
%load_disp20 40:4 16:s16
@@ -274,10 +293,10 @@ EXCP 0000 0000 1100 id:4
@load_disp20_reg .... .... .... .... .... .... rd:4 ra:4 .... .... .... .... disp=%load_disp20 &load
@load_disp20_rrp .... .... .... .... .... .... rd:4 rrp:4 .... .... .... .... disp=%load_disp20 &load_rrp
@load_disp20_rp .... .... .... .... .... .... rd:4 ra:4 .... .... .... .... disp=%load_disp20 &load
-@load_abs20 .... .... rd:4 .... .... .... .... .... addr=%addr_abs20 &load_abs
+@load_abs20 .... .... rd:4 .... .... .... .... .... addr=%addr_abs20 remap=1 &load_abs
@load_ind_abs .... ... ri:1 rd:4 addr:20 &load_ind_abs
@load_disp4_reg .... .... rd:4 ra:4 &load
-@load_abs24 .... .... .... .... .... .... rd:4 .... .... .... .... .... addr=%addr_disp24 &load_abs
+@load_abs24 .... .... .... .... .... .... rd:4 .... .... .... .... .... addr=%addr_disp24 remap=0 &load_abs
#@load_disp14_rrp .... .... .... rrp:4 .... .... rd:4 .... disp=%load_disp14 &load_rrp
{
@@ -326,12 +345,12 @@ LOAD_abs 0000 0000 0001 0010 1011 .... .... .... .... .... .... ....
&stor rs ra dbase disp width
&stor_rrp rs rrp disp width
-&stor_abs rs addr width
+&stor_abs rs addr width remap
&stor_ind_abs rs ri addr width
&stor_imm imm ra dbase disp width
&stor_rrp_imm imm rrp disp width
-&stor_abs_imm imm addr width
+&stor_abs_imm imm addr width remap
&stor_abs_rrp_imm imm ri addr width
%stor_disp20 40:s4 16:s16
@@ -343,10 +362,10 @@ LOAD_abs 0000 0000 0001 0010 1011 .... .... .... .... .... .... ....
@stor_disp20_reg .... .... .... .... .... .... rs:4 ra:4 .... .... .... .... disp=%stor_disp20 &stor
@stor_disp20_rrp .... .... .... .... .... .... rs:4 rrp:4 .... .... .... .... disp=%stor_disp20 &stor_rrp
@stor_disp20_rp .... .... .... .... .... .... rs:4 ra:4 .... .... .... .... disp=%stor_disp20 &stor
-@stor_abs20 .... .... rs:4 .... .... .... .... .... addr=%addr_abs20 &stor_abs
+@stor_abs20 .... .... rs:4 .... .... .... .... .... addr=%addr_abs20 remap=1 &stor_abs
@stor_ind_abs .... ... ri:1 rs:4 addr:20 &stor_ind_abs
@stor_disp4_reg .... .... rs:4 ra:4 &stor
-@stor_abs24 .... .... .... .... .... .... rs:4 .... .... .... .... .... addr=%addr_disp24 &stor_abs
+@stor_abs24 .... .... .... .... .... .... rs:4 .... .... .... .... .... addr=%addr_disp24 remap=0 &stor_abs
#@stord_disp14_rrp .... .... .... rrp:4 .... .... rs:4 .... disp=%stor_disp14 &stor_rrp
@stor_disp14_rrp_imm .... .... .... rrp:4 .... .... imm:4 .... disp=%stor_disp14 &stor_rrp_imm
@@ -354,9 +373,9 @@ LOAD_abs 0000 0000 0001 0010 1011 .... .... .... .... .... .... ....
@stor_disp20_reg_imm .... .... .... .... .... .... imm:4 ra:4 .... .... .... .... disp=%stor_disp20 &stor_imm
@stor_disp20_rrp_imm .... .... .... .... .... .... imm:4 rrp:4 .... .... .... .... disp=%stor_disp20 &stor_rrp_imm
@stor_disp20_rp_imm .... .... .... .... .... .... imm:4 ra:4 .... .... .... .... disp=%stor_disp20 &stor_imm
-@stor_abs_imm .... .... imm:4 addr:20 &stor_abs_imm
+@stor_abs_imm .... .... imm:4 addr:20 remap=1 &stor_abs_imm
@stor_abs_rrp_imm .... ... ri:1 imm:4 addr:20 &stor_abs_rrp_imm
-@stor_abs24_imm .... .... .... .... .... .... imm:4 .... .... .... .... .... addr=%addr_disp24 &stor_abs_imm
+@stor_abs24_imm .... .... .... .... .... .... imm:4 .... .... .... .... .... addr=%addr_disp24 remap=0 &stor_abs_imm
{
STOR_rrp 1111 1110 rs:4 rrp:4 width=1 disp=0 &stor_rrp
@@ -427,3 +446,21 @@ LOADM 0000 0000 1010 0... pair=0 @ldstm
LOADM 0000 0000 1010 1... pair=1 @ldstm
STORM 0000 0000 1011 0... pair=0 @ldstm
STORM 0000 0000 1011 1... pair=1 @ldstm
+
+# Processor Register Manipulation
+#LPR Rsrc, Rproc Load processor register
+#LPRD RPsrc, Rprocd Load double processor register
+#SPR Rproc, Rdest Store processor register
+#SPRD Rprocd, RPdest Store double processor register
+
+# fmt escape2
+
+# followed by another word p4_4(mode) p3_4(res) p2_4(pr) p1_4(reg)
+&lp_sp p2 p1
+@lp_sp .... .... .... .... .... .... p2:4 p1:4 &lp_sp
+
+# ope
+LPR 0000 0000 0001 0100 0000 0000 .... .... @lp_sp
+LPRD 0000 0000 0001 0100 0001 0000 .... .... @lp_sp
+SPR 0000 0000 0001 0100 0010 0000 .... .... @lp_sp
+SPRD 0000 0000 0001 0100 0011 0000 .... .... @lp_sp
diff --git a/target/cr16c/machine.c b/target/cr16c/machine.c
index 7e89d64964..fa65d7a90b 100644
--- a/target/cr16c/machine.c
+++ b/target/cr16c/machine.c
@@ -8,11 +8,11 @@ const VMStateDescription vms_cr16c_cpu = {
.fields = (VMStateField[]) {
VMSTATE_UINT32(env.pc, CR16CCPU),
VMSTATE_UINT32_ARRAY(env.r, CR16CCPU, CR16C_REG_COUNT),
- VMSTATE_UINT32(env.f_n, CR16CCPU),
- VMSTATE_UINT32(env.f_z, CR16CCPU),
- VMSTATE_UINT32(env.f_f, CR16CCPU),
- VMSTATE_UINT32(env.f_l, CR16CCPU),
- VMSTATE_UINT32(env.f_c, CR16CCPU),
+ VMSTATE_UINT32(env.psr_n, CR16CCPU),
+ VMSTATE_UINT32(env.psr_z, CR16CCPU),
+ VMSTATE_UINT32(env.psr_f, CR16CCPU),
+ VMSTATE_UINT32(env.psr_l, CR16CCPU),
+ VMSTATE_UINT32(env.psr_c, CR16CCPU),
VMSTATE_END_OF_LIST(),
}
};
diff --git a/target/cr16c/meson.build b/target/cr16c/meson.build
index 88ce674ed5..f5a4a5d2e6 100644
--- a/target/cr16c/meson.build
+++ b/target/cr16c/meson.build
@@ -8,6 +8,7 @@ cr16c_ss.add(files(
'cpu.c',
'helper.c',
'translate.c',
+ 'disas.c',
))
cr16c_system_ss = ss.source_set()
diff --git a/target/cr16c/translate.c b/target/cr16c/translate.c
index f61499b725..d93302a27f 100644
--- a/target/cr16c/translate.c
+++ b/target/cr16c/translate.c
@@ -6,6 +6,7 @@
#include "exec/translator.h"
#include "accel/tcg/cpu-ldst.h"
#include "qemu/typedefs.h"
+#include "qemu/qemu-print.h"
#include "tcg/tcg-cond.h"
#include "tcg/tcg-op-common.h"
#include "tcg/tcg-op.h"
@@ -37,6 +38,27 @@ enum {
CR16C_COND_UC,
};
+/* Representation of register pair encoding */
+enum {
+ CR16C_RP_R1R0,
+ CR16C_RP_R2R1,
+ CR16C_RP_R3R2,
+ CR16C_RP_R4R3,
+ CR16C_RP_R5R4,
+ CR16C_RP_R6R5,
+ CR16C_RP_R7R6,
+ CR16C_RP_R8R7,
+ CR16C_RP_R9R8,
+ CR16C_RP_R10R9,
+ CR16C_RP_R11R10,
+ CR16C_RP_R12LR11,
+ // TODO these are ~funky, depend on CFG.SR and PSR.U
+ /*CR16C_RP_R12,
+ CR16C_RP_R13,
+ CR16C_RP_RA,
+ CR16C_RP_SP,*/
+};
+
typedef struct DisasContext {
DisasContextBase base;
CPUCR16CState* env;
@@ -44,12 +66,37 @@ typedef struct DisasContext {
/* Registers */
-static TCGv pc;
-static TCGv f_n;
-static TCGv f_z;
-static TCGv f_f;
-static TCGv f_l;
-static TCGv f_c;
+static TCGv pc; // 24 bit
+// ISPH/L (Interrupt Stack Pointer)
+// USPH/L (User Stack Pointer)
+// INTBASEH/L (Interrupt Base)
+
+// PSR 16
+// these flags are part of PSR
+static TCGv psr_n;
+static TCGv psr_z;
+static TCGv psr_f;
+static TCGv psr_l;
+static TCGv psr_c;
+static TCGv psr_t;
+static TCGv psr_u;
+static TCGv psr_e;
+static TCGv psr_p;
+static TCGv psr_i;
+
+// CFG
+// Most notably: SR bit "Short Register"
+// => switches to register pairings for reassembled code originally written for CR16B
+static TCGv cfg_dc;
+static TCGv cfg_ldc;
+static TCGv cfg_ic;
+static TCGv cfg_lic;
+static TCGv cfg_ed;
+static TCGv cfg_sr;
+
+// debug registers (optional) "depends on the configuration of the chip"
+
+/* General purpose registers, incl. RA and SP */
static TCGv r[CR16C_REG_COUNT];
@@ -241,22 +288,22 @@ static void gen_ADD_imm(TCGv_i32 rd, int32_t imm, bool add_carry, uint8_t width)
tcg_gen_addi_i32(temp, temp, imm & ((1 << width * 8) - 1));
if(add_carry) {
- tcg_gen_andi_i32(f_c, f_c, 1);
- tcg_gen_add_i32(temp, temp, f_c);
+ tcg_gen_andi_i32(psr_c, psr_c, 1);
+ tcg_gen_add_i32(temp, temp, psr_c);
}
/* Carry flag */
- tcg_gen_shri_i32(f_c, temp, width * 8);
+ tcg_gen_shri_i32(psr_c, temp, width * 8);
/* Overflow flag */
- tcg_gen_xor_i32(f_f, temp, rd);
+ tcg_gen_xor_i32(psr_f, temp, rd);
if(imm < 0) {
- tcg_gen_and_i32(f_f, f_f, rd);
+ tcg_gen_and_i32(psr_f, psr_f, rd);
}
else {
- tcg_gen_andc_i32(f_f, f_f, rd);
+ tcg_gen_andc_i32(psr_f, psr_f, rd);
}
- tcg_gen_shri_i32(f_f, f_f, width*8 - 1);
+ tcg_gen_shri_i32(psr_f, psr_f, width*8 - 1);
tcg_gen_deposit_i32(rd, rd, temp, 0, width*8);
}
@@ -277,19 +324,19 @@ static void gen_ADD_reg(TCGv reg1, TCGv reg2, bool plus_carry, uint8_t width) {
tcg_gen_add_i32(temp1, temp1, temp2);
if(plus_carry) {
- tcg_gen_andi_i32(f_c, f_c, 1);
- tcg_gen_add_i32(temp1, temp1, f_c);
+ tcg_gen_andi_i32(psr_c, psr_c, 1);
+ tcg_gen_add_i32(temp1, temp1, psr_c);
}
tcg_gen_deposit_i32(reg1, reg1, temp1, 0, width*8);
/* Carry flag */
- tcg_gen_shri_i32(f_c, temp1, width*8);
+ tcg_gen_shri_i32(psr_c, temp1, width*8);
/* Overflow flag */
- tcg_gen_xor_i32(f_f, reg1, reg2);
- tcg_gen_and_i32(f_f, f_f, tempf_f);
- tcg_gen_shri_i32(f_f, f_f, width*8-1);
+ tcg_gen_xor_i32(psr_f, reg1, reg2);
+ tcg_gen_and_i32(psr_f, psr_f, tempf_f);
+ tcg_gen_shri_i32(psr_f, psr_f, width*8-1);
}
static void gen_ADDD_imm(int regnl, int32_t imm) {
@@ -312,16 +359,16 @@ static void gen_ADDD_imm(int regnl, int32_t imm) {
tcg_gen_extrl_i64_i32(regl, temp_res);
/* Carry flag */
- tcg_gen_extrh_i64_i32(f_c, temp_res);
+ tcg_gen_extrh_i64_i32(psr_c, temp_res);
/* Overflow flag */
if (imm < 0) {
- tcg_gen_andc_i32(f_f, temp_h, regl);
+ tcg_gen_andc_i32(psr_f, temp_h, regl);
}
else {
- tcg_gen_andc_i32(f_f, regl, temp_h);
+ tcg_gen_andc_i32(psr_f, regl, temp_h);
}
- tcg_gen_shri_i32(f_f, f_f, 31);
+ tcg_gen_shri_i32(psr_f, psr_f, 31);
if (regnl < CR16C_FIRST_32B_REG) {
tcg_gen_shri_i32(regh, regl, 16);
@@ -348,7 +395,7 @@ static bool gen_ADDD_rp(int rdn, TCGv_i32 rs, bool plus_one) {
tcg_gen_extu_i32_i64(temp2, temp2_32);
/* Prepare overflow flag */
- tcg_gen_xor_i32(f_f, temp2_32, rs);
+ tcg_gen_xor_i32(psr_f, temp2_32, rs);
tcg_gen_add_i64(temp1, temp1, temp2);
if(plus_one) {
@@ -361,12 +408,12 @@ static bool gen_ADDD_rp(int rdn, TCGv_i32 rs, bool plus_one) {
}
/* Carry flag */
- tcg_gen_extrh_i64_i32(f_c, temp1);
+ tcg_gen_extrh_i64_i32(psr_c, temp1);
/* Overflow flag */
tcg_gen_xor_i32(temp2_32, temp2_32, rdl);
- tcg_gen_andc_i32(f_f, temp2_32, f_f);
- tcg_gen_shri_i32(f_f, f_f, 31);
+ tcg_gen_andc_i32(psr_f, temp2_32, psr_f);
+ tcg_gen_shri_i32(psr_f, psr_f, 31);
return true;
}
@@ -488,24 +535,24 @@ static bool trans_SUB_imm(DisasContext *ctx, arg_SUB_imm *a) {
int32_t imm = -a->imm;
if (a->add_carry) {
imm--;
- tcg_gen_xori_i32(f_c, f_c, 1);
+ tcg_gen_xori_i32(psr_c, psr_c, 1);
}
gen_ADD_imm(r[a->rd], imm, a->add_carry, a->width);
- tcg_gen_xori_i32(f_c, f_c, 1);
+ tcg_gen_xori_i32(psr_c, psr_c, 1);
return true;
}
static bool trans_SUB_reg(DisasContext *ctx, arg_SUB_reg *a) {
TCGv_i32 temp = tcg_temp_new_i32();
if (a->add_carry) {
- tcg_gen_xori_i32(f_c, f_c, 1);
+ tcg_gen_xori_i32(psr_c, psr_c, 1);
tcg_gen_xori_i32(temp, r[a->rs], (1 << (a->width*8)) - 1);
} else {
tcg_gen_neg_i32(temp, r[a->rs]);
}
gen_ADD_reg(r[a->rd], temp, a->add_carry, a->width);
- tcg_gen_xori_i32(f_c, f_c, 1);
+ tcg_gen_xori_i32(psr_c, psr_c, 1);
return true;
}
@@ -516,13 +563,13 @@ static bool trans_SUBD_rp(DisasContext *ctx, arg_SUBD_rp *a) {
}
tcg_gen_xori_i32(templ, r[a->rs], 0xFFFFFFFF);
gen_ADDD_rp(a->rd, templ, true);
- tcg_gen_xori_i32(f_c, f_c, 1);
+ tcg_gen_xori_i32(psr_c, psr_c, 1);
return true;
}
static bool trans_SUBD_imm(DisasContext *ctx, arg_SUBD_imm *a) {
gen_ADDD_imm(a->rd, -a->imm);
- tcg_gen_xori_i32(f_c, f_c, 1);
+ tcg_gen_xori_i32(psr_c, psr_c, 1);
return true;
}
@@ -651,9 +698,9 @@ static bool trans_MACSW(DisasContext *ctx, arg_MACSW *a) {
/* Integer Comparison */
static void gen_cmp(TCGv_i32 src1, TCGv_i32 src2) {
- tcg_gen_setcond_i32(TCG_COND_EQ, f_z, src1, src2);
- tcg_gen_setcond_i32(TCG_COND_GT, f_l, src1, src2);
- tcg_gen_setcond_i32(TCG_COND_GTU, f_n, src1, src2);
+ tcg_gen_setcond_i32(TCG_COND_EQ, psr_z, src1, src2);
+ tcg_gen_setcond_i32(TCG_COND_GT, psr_l, src1, src2);
+ tcg_gen_setcond_i32(TCG_COND_GTU, psr_n, src1, src2);
}
static bool trans_CMP_imm(DisasContext *ctx, arg_CMP_imm *a) {
@@ -696,14 +743,19 @@ static bool trans_CMP_reg(DisasContext *ctx, arg_CMP_reg *a) {
}
static bool trans_CMPD_reg(DisasContext *ctx, arg_CMPD_reg *a) {
+ TCGv tmp_a = tcg_temp_new_i32();
+ TCGv tmp_b = tcg_temp_new_i32();
+ tcg_gen_mov_i32(tmp_a, r[a->rs1]);
+ tcg_gen_mov_i32(tmp_b, r[a->rs2]);
+
if (a->rs1 < CR16C_FIRST_32B_REG) {
- tcg_gen_deposit_i32(r[a->rs1], r[a->rs1], r[a->rs1+1], 16, 16);
+ tcg_gen_deposit_i32(tmp_a, tmp_a, r[a->rs1+1], 16, 16);
}
if (a->rs2 < CR16C_FIRST_32B_REG) {
- tcg_gen_deposit_i32(r[a->rs2], r[a->rs2], r[a->rs2+1], 16, 16);
+ tcg_gen_deposit_i32(tmp_b, tmp_b, r[a->rs2+1], 16, 16);
}
- gen_cmp(r[a->rs2], r[a->rs1]);
+ gen_cmp(tmp_b, tmp_a);
return true;
}
@@ -835,48 +887,48 @@ static bool trans_XORD_rp(DisasContext *ctx, arg_XORD_rp *a) {
static bool trans_SCOND(DisasContext *ctx, arg_SCOND *a) {
switch (a->imm) {
case CR16C_COND_EQ:
- tcg_gen_andi_i32(r[a->rd], f_z, 1);
+ tcg_gen_andi_i32(r[a->rd], psr_z, 1);
break;
case CR16C_COND_NE:
- tcg_gen_andc_i32(r[a->rd], tcg_constant_i32(1), f_z);
+ tcg_gen_andc_i32(r[a->rd], tcg_constant_i32(1), psr_z);
break;
case CR16C_COND_CS:
- tcg_gen_andi_i32(r[a->rd], f_c, 1);
+ tcg_gen_andi_i32(r[a->rd], psr_c, 1);
break;
case CR16C_COND_CC:
- tcg_gen_andc_i32(r[a->rd], tcg_constant_i32(1), f_c);
+ tcg_gen_andc_i32(r[a->rd], tcg_constant_i32(1), psr_c);
break;
case CR16C_COND_HI:
- tcg_gen_mov_i32(r[a->rd], f_l);
+ tcg_gen_mov_i32(r[a->rd], psr_l);
break;
case CR16C_COND_LS:
- tcg_gen_andc_i32(r[a->rd], tcg_constant_i32(1), f_l);
+ tcg_gen_andc_i32(r[a->rd], tcg_constant_i32(1), psr_l);
break;
case CR16C_COND_GT:
- tcg_gen_mov_i32(r[a->rd], f_n);
+ tcg_gen_mov_i32(r[a->rd], psr_n);
break;
case CR16C_COND_LE:
- tcg_gen_andc_i32(r[a->rd], tcg_constant_i32(1), f_n);
+ tcg_gen_andc_i32(r[a->rd], tcg_constant_i32(1), psr_n);
break;
case CR16C_COND_FS:
- tcg_gen_andi_i32(r[a->rd], f_f, 1);
+ tcg_gen_andi_i32(r[a->rd], psr_f, 1);
break;
case CR16C_COND_FC:
- tcg_gen_andc_i32(r[a->rd], tcg_constant_i32(1), f_f);
+ tcg_gen_andc_i32(r[a->rd], tcg_constant_i32(1), psr_f);
break;
case CR16C_COND_LO:
- tcg_gen_nor_i32(r[a->rd], f_z, f_l);
+ tcg_gen_nor_i32(r[a->rd], psr_z, psr_l);
tcg_gen_andi_i32(r[a->rd], r[a->rd], 1);
break;
case CR16C_COND_HS:
- tcg_gen_or_i32(r[a->rd], f_z, f_l);
+ tcg_gen_or_i32(r[a->rd], psr_z, psr_l);
break;
case CR16C_COND_LT:
- tcg_gen_nor_i32(r[a->rd], f_z, f_n);
+ tcg_gen_nor_i32(r[a->rd], psr_z, psr_n);
tcg_gen_andi_i32(r[a->rd], r[a->rd], 1);
break;
case CR16C_COND_GE:
- tcg_gen_or_i32(r[a->rd], f_z, f_n);
+ tcg_gen_or_i32(r[a->rd], psr_z, psr_n);
break;
}
@@ -1078,20 +1130,20 @@ static bool trans_LSHD_rp(DisasContext *ctx, arg_LSHD_rp *a) {
static void gen_CBIT(TCGv_i32 addr, uint8_t pos, uint8_t width) {
uint32_t mask = ~(1 << pos);
MemOp memop = width == 2 ? MO_UW : MO_UB;
- tcg_gen_atomic_fetch_and_i32(f_f, addr, tcg_constant_i32(mask), 0, memop);
- tcg_gen_shri_i32(f_f, f_f, pos);
+ tcg_gen_atomic_fetch_and_i32(psr_f, addr, tcg_constant_i32(mask), 0, memop);
+ tcg_gen_shri_i32(psr_f, psr_f, pos);
}
static void gen_SBIT(TCGv_i32 addr, uint8_t pos, uint8_t width) {
uint32_t mask = 1 << pos;
MemOp memop = width == 2 ? MO_UW : MO_UB;
- tcg_gen_atomic_fetch_or_i32(f_f, addr, tcg_constant_i32(mask), 0, memop);
- tcg_gen_shri_i32(f_f, f_f, pos);
+ tcg_gen_atomic_fetch_or_i32(psr_f, addr, tcg_constant_i32(mask), 0, memop);
+ tcg_gen_shri_i32(psr_f, psr_f, pos);
}
static bool gen_TBIT_mem(TCGv_i32 addr, uint8_t pos, uint8_t width) {
- tcg_gen_qemu_ld_i32(f_f, addr, 0, unsigned_op_by_width[width]);
- tcg_gen_shri_i32(f_f, f_f, pos);
+ tcg_gen_qemu_ld_i32(psr_f, addr, 0, unsigned_op_by_width[width]);
+ tcg_gen_shri_i32(psr_f, psr_f, pos);
return true;
}
@@ -1171,13 +1223,13 @@ static bool trans_TBIT_mem_abs(DisasContext *ctx, arg_TBIT_mem_abs *a) {
static bool trans_TBIT_reg_imm(DisasContext *ctx, arg_TBIT_reg_imm *a) {
- tcg_gen_shri_i32(f_f, r[a->rs], a->pos);
+ tcg_gen_shri_i32(psr_f, r[a->rs], a->pos);
return true;
}
static bool trans_TBIT_reg_reg(DisasContext *ctx, arg_TBIT_reg_reg *a) {
- tcg_gen_shr_i32(f_f, r[a->rs], r[a->rp]);
+ tcg_gen_shr_i32(psr_f, r[a->rs], r[a->rp]);
return true;
}
@@ -1187,6 +1239,10 @@ static bool trans_TBIT_reg_reg(DisasContext *ctx, arg_TBIT_reg_reg *a) {
/* For now this instruction is abused as semihosting instruction for tests */
static bool trans_EXCP(DisasContext *ctx, arg_EXCP *a) {
+ if (a->id == 14) {// DBG
+ gen_helper_dump_regs(tcg_env);
+ return true;
+ }
gen_helper_exit(tcg_env);
ctx->base.is_jmp = DISAS_NORETURN;
@@ -1198,61 +1254,61 @@ static void gen_br_cond(DisasContext* ctx, int cond, TCGLabel* l) {
TCGv temp;
/* Flags may have higher bits set */
- tcg_gen_andi_i32(f_n, f_n, 1);
- tcg_gen_andi_i32(f_z, f_z, 1);
- tcg_gen_andi_i32(f_f, f_f, 1);
- tcg_gen_andi_i32(f_l, f_l, 1);
- tcg_gen_andi_i32(f_c, f_c, 1);
+ tcg_gen_andi_i32(psr_n, psr_n, 1);
+ tcg_gen_andi_i32(psr_z, psr_z, 1);
+ tcg_gen_andi_i32(psr_f, psr_f, 1);
+ tcg_gen_andi_i32(psr_l, psr_l, 1);
+ tcg_gen_andi_i32(psr_c, psr_c, 1);
switch (cond) {
case CR16C_COND_EQ:
- tcg_gen_brcondi_i32(TCG_COND_EQ, f_z, 1, l);
+ tcg_gen_brcondi_i32(TCG_COND_EQ, psr_z, 1, l);
break;
case CR16C_COND_NE:
- tcg_gen_brcondi_i32(TCG_COND_NE, f_z, 1, l);
+ tcg_gen_brcondi_i32(TCG_COND_NE, psr_z, 1, l);
break;
case CR16C_COND_CS:
- tcg_gen_brcondi_i32(TCG_COND_EQ, f_c, 1, l);
+ tcg_gen_brcondi_i32(TCG_COND_EQ, psr_c, 1, l);
break;
case CR16C_COND_CC:
- tcg_gen_brcondi_i32(TCG_COND_NE, f_c, 1, l);
+ tcg_gen_brcondi_i32(TCG_COND_NE, psr_c, 1, l);
break;
case CR16C_COND_HI:
- tcg_gen_brcondi_i32(TCG_COND_EQ, f_l, 1, l);
+ tcg_gen_brcondi_i32(TCG_COND_EQ, psr_l, 1, l);
break;
case CR16C_COND_LS:
- tcg_gen_brcondi_i32(TCG_COND_NE, f_l, 1, l);
+ tcg_gen_brcondi_i32(TCG_COND_NE, psr_l, 1, l);
break;
case CR16C_COND_GT:
- tcg_gen_brcondi_i32(TCG_COND_EQ, f_n, 1, l);
+ tcg_gen_brcondi_i32(TCG_COND_EQ, psr_n, 1, l);
break;
case CR16C_COND_LE:
- tcg_gen_brcondi_i32(TCG_COND_NE, f_n, 1, l);
+ tcg_gen_brcondi_i32(TCG_COND_NE, psr_n, 1, l);
break;
case CR16C_COND_FS:
- tcg_gen_brcondi_i32(TCG_COND_EQ, f_f, 1, l);
+ tcg_gen_brcondi_i32(TCG_COND_EQ, psr_f, 1, l);
break;
case CR16C_COND_FC:
- tcg_gen_brcondi_i32(TCG_COND_NE, f_f, 1, l);
+ tcg_gen_brcondi_i32(TCG_COND_NE, psr_f, 1, l);
break;
case CR16C_COND_LO:
temp = tcg_temp_new_i32();
- tcg_gen_or_i32(temp, f_z, f_l);
+ tcg_gen_or_i32(temp, psr_z, psr_l);
tcg_gen_brcondi_i32(TCG_COND_EQ, temp, 0, l);
break;
case CR16C_COND_HS:
temp = tcg_temp_new_i32();
- tcg_gen_and_i32(temp, f_z, f_l);
+ tcg_gen_and_i32(temp, psr_z, psr_l);
tcg_gen_brcondi_i32(TCG_COND_NE, temp, 1, l);
break;
case CR16C_COND_LT:
temp = tcg_temp_new_i32();
- tcg_gen_or_i32(temp, f_z, f_n);
+ tcg_gen_or_i32(temp, psr_z, psr_n);
tcg_gen_brcondi_i32(TCG_COND_EQ, temp, 0, l);
break;
case CR16C_COND_GE:
temp = tcg_temp_new_i32();
- tcg_gen_and_i32(temp, f_z, f_n);
+ tcg_gen_and_i32(temp, psr_z, psr_n);
tcg_gen_brcondi_i32(TCG_COND_NE, temp, 1, l);
break;
case CR16C_COND_ALWAYS:
@@ -1277,6 +1333,30 @@ static bool trans_BRCOND(DisasContext* ctx, arg_BRCOND *a) {
return true;
}
+static bool trans_BR0(DisasContext* ctx, arg_BR0* a) {
+ int disp5 = (a->dest + 1) * 2; // disp*2+
+
+ TCGv tmp = tcg_temp_new_i32();
+ if (a->word == 1) {
+ tcg_gen_ext16u_i32(tmp, r[a->src]);
+ } else {
+ tcg_gen_ext8u_i32(tmp, r[a->src]);
+ }
+
+ TCGLabel* l = gen_new_label();
+ tcg_gen_brcondi_i32(a->ne == 1 ? TCG_COND_NE : TCG_COND_EQ, tmp, 0, l);
+
+ gen_goto(&ctx->base, ctx->base.pc_next, 0);
+
+ gen_set_label(l);
+
+ vaddr pc_this = ctx->base.pc_next - 2;
+ gen_goto(&ctx->base, pc_this + disp5, 1);
+
+ ctx->base.is_jmp = DISAS_NORETURN;
+ return true;
+}
+
static bool trans_JCOND(DisasContext* ctx, arg_JCOND *a) {
TCGLabel* l = gen_new_label();
@@ -1318,10 +1398,23 @@ static bool trans_LOAD(DisasContext *ctx, arg_LOAD *a) {
return true;
}
+// TODO this should already be in reloc_abs20. see how to replace this in the decoder
+static int abs20_remap(int addr) {
+ if (addr > 0xEFFFF)
+ return addr | 0xF00000;
+ return addr;
+}
+
static bool trans_LOAD_abs(DisasContext *ctx, arg_LOAD_abs *a) {
TCGv_i32 temp = tcg_temp_new_i32();
- tcg_gen_qemu_ld_i32(temp, tcg_constant_i32(a->addr), 0, unsigned_op_by_width[a->width]);
+ // See Table 5-7, footnote f, which applies for abs20
+ int addr = a->addr;
+ if (a->remap) {
+ addr = abs20_remap(addr);
+ }
+
+ tcg_gen_qemu_ld_i32(temp, tcg_constant_i32(addr), 0, unsigned_op_by_width[a->width]);
gen_move_dest(temp, a->rd, a->width);
return true;
@@ -1362,7 +1455,7 @@ static bool trans_LOADM(DisasContext *ctx, arg_LOADM *a) {
return true;
}
-
+// TODO this is at fault for 32 bit values ending up in the register backing values
static void gen_combine_rp(int reg_id, int width) {
if (width == 4 && reg_id < CR16C_FIRST_32B_REG) {
tcg_gen_deposit_i32(r[reg_id], r[reg_id], r[reg_id+1], 16, 16);
@@ -1390,8 +1483,18 @@ static bool trans_STOR_rrp(DisasContext *ctx, arg_STOR_rrp *a) {
}
static bool trans_STOR_abs(DisasContext *ctx, arg_STOR_abs *a) {
+ int32_t addr = a->addr;
+
+ // Table 5-7 describes the addressing options for the
+ // first [single reg -> dest] and second [STORD] formats of the instruction.
+ // See Table 5-7, footnote f, which applies for abs20
+
+ if (a->remap) {
+ addr = abs20_remap(addr);
+ }
+
gen_combine_rp(a->rs, a->width);
- tcg_gen_qemu_st_i32(r[a->rs], tcg_constant_i32(a->addr), 0, unsigned_op_by_width[a->width]);
+ tcg_gen_qemu_st_i32(r[a->rs], tcg_constant_i32(addr), 0, unsigned_op_by_width[a->width]);
return true;
}
@@ -1406,7 +1509,16 @@ static bool trans_STOR_ind_abs(DisasContext *ctx, arg_STOR_ind_abs *a) {
}
static bool trans_STOR_abs_imm(DisasContext *ctx, arg_STOR_abs_imm *a) {
- tcg_gen_qemu_st_i32(tcg_constant_i32(a->imm), tcg_constant_i32(a->addr), 0, unsigned_op_by_width[a->width]);
+ int32_t addr = a->addr;
+
+ // Table 5-4 describes the addressing options for the
+ // third [imm -> dest] format of the instruction.
+ // See Table 5-4, footnote e, which applies for abs20
+ if (a->remap) {
+ addr = abs20_remap(addr);
+ }
+
+ tcg_gen_qemu_st_i32(tcg_constant_i32(a->imm), tcg_constant_i32(addr), 0, unsigned_op_by_width[a->width]);
return true;
}
@@ -1456,6 +1568,146 @@ static bool trans_STORM(DisasContext *ctx, arg_STORM *a) {
return true;
}
+enum {
+ CR16C_PRD_DBS = 0,
+ CR16C_PRD_DSR = 1,
+ CR16C_PRD_DCR = 2,
+ CR16C_PRD_CAR0 = 4,
+ CR16C_PRD_CAR1 = 6,
+ CR16C_PRD_CFG = 8,
+ CR16C_PRD_PSR = 9,
+ CR16C_PRD_INTBASE = 10,
+ CR16C_PRD_ISP = 12,
+ CR16C_PRD_USP = 14,
+};
+
+enum {
+ CR16C_PR_DBS = 0,
+ CR16C_PR_DSR = 1,
+ CR16C_PR_DCRL = 2,
+ CR16C_PR_DCRH = 3,
+ CR16C_PR_CAR0L = 4,
+ CR16C_PR_CAR0H = 5,
+ CR16C_PR_CAR1L = 6,
+ CR16C_PR_CAR1H = 7,
+ CR16C_PR_CFG = 8,
+ CR16C_PR_PSR = 9,
+ CR16C_PR_INTBASEL = 10,
+ CR16C_PR_INTBASEH = 11,
+ CR16C_PR_ISPL = 12,
+ CR16C_PR_ISPH = 13,
+ CR16C_PR_USPL = 14,
+ CR16C_PR_USPH = 15,
+};
+
+// src -> proc register
+static bool trans_LPR(DisasContext *ctx, arg_LPR* a) {
+ gen_helper_raise_unimplemented_instruction();
+ return true;
+}
+
+static bool trans_LPRD(DisasContext *ctx, arg_LPRD* a) {
+ gen_helper_raise_unimplemented_instruction();
+ return true;
+}
+
+// proc register -> src
+static bool trans_SPR(DisasContext *ctx, arg_SPR* a) {
+ gen_helper_raise_unimplemented_instruction();
+ return true;
+}
+
+static bool trans_SPRD(DisasContext *ctx, arg_SPRD* a) {
+ gen_helper_raise_unimplemented_instruction();
+ return true;
+}
+
+static bool trans_BAL_ra(DisasContext *ctx, arg_BAL_ra *a) {
+#define BAL_LEN 4
+ // TODO BAL_rp would be 3 words/6 bytes (fmt 3a)
+ vaddr pc_this = ctx->base.pc_next - BAL_LEN;
+ vaddr dest_offset = a->dest*2;
+ qemu_printf("!!!bal %04lx dest=%d\n", pc_this, a->dest);
+
+ // 1) store next PC in ra TODO BAL_rp can choose where
+ tcg_gen_movi_i32(r[CR16C_REGNO_RA], ctx->base.pc_next);
+
+ // 2) sign extend from 23 -> "25" bits
+ int32_t dest_sextend = sextract32(dest_offset, 0, 23);
+
+ qemu_printf("=> 0x%lx\n", pc_this + dest_sextend);
+ ctx->base.is_jmp = DISAS_NORETURN;
+ gen_goto(&ctx->base, pc_this + dest_sextend, 0);
+
+ // TODO IAD trap
+ // TODO CFG.SR handling
+ return true;
+}
+
+static bool trans_pop(DisasContext *ctx, arg_pop *a) {
+ // count is 3 bits, so its range is 0-7
+ // however, it encodes counts 1-8
+ int32_t count = a->count + 1;
+ // todo if cfg.sr = 1
+ if (true) {
+ if (a->count + a->dest > 15) {
+ // invalid
+ return false;
+ }
+ } else {
+ // TODO cfg.sr = 0
+ }
+ for (int i = 0; i < count; ++i) {
+ // pop regular registers
+ // TODO register pair popping???
+ tcg_gen_qemu_ld_tl(r[a->dest + i], r[CR16C_REGNO_SP], 0, MO_UW);
+ tcg_gen_addi_tl(r[CR16C_REGNO_SP], r[CR16C_REGNO_SP], 2);
+ }
+ // TODO memory spaces(???)
+ // pop RA if requested
+ if (a->ra) {
+ tcg_gen_qemu_ld_tl(r[CR16C_REGNO_RA], r[CR16C_REGNO_SP], 0, MO_32);
+ tcg_gen_addi_tl(r[CR16C_REGNO_SP], r[CR16C_REGNO_SP], 4);
+ }
+
+ if (a->rt) {
+ // basically, JUMP RA
+ ctx->base.is_jmp = DISAS_NORETURN;
+
+ tcg_gen_mov_i32(pc, r[CR16C_REGNO_RA]);
+ tcg_gen_lookup_and_goto_ptr();
+ }
+ return true;
+}
+
+static bool trans_push(DisasContext *ctx, arg_push *a) {
+ int32_t count = a->count + 1;
+
+ // we need to push the registers in reverse order so that they are in the correct places for POP, e.g.:
+ // SP = 0x100A
+ // # popret $3, R1, RA
+ // 0x1000 R1
+ // 0x1002 R2
+ // 0x1004 R3
+ // 0x1006 RA # 4 bytes!!
+ // 0x100A xxxxx
+
+ // TODO memory spaces
+ // push RA
+ if (a->ra) {
+ tcg_gen_subi_tl(r[CR16C_REGNO_SP], r[CR16C_REGNO_SP], 4);
+ tcg_gen_qemu_st_tl(r[CR16C_REGNO_RA], r[CR16C_REGNO_SP], 0, MO_32);
+ }
+
+ // push regular registers
+ for (int i = count - 1; i >= 0; --i) {
+ // TODO register pairs
+ tcg_gen_subi_tl(r[CR16C_REGNO_SP], r[CR16C_REGNO_SP], 2);
+ tcg_gen_qemu_st_tl(r[a->src + i], r[CR16C_REGNO_SP], 0, MO_16);
+ }
+
+ return true;
+}
/* Some instructions aren't implemented yet, eg. because of some binutils that make them hard to verify and we'll fix first */
static bool trans_UNIMPLEMENTED(DisasContext *ctx, arg_UNIMPLEMENTED *a) {
@@ -1473,6 +1725,7 @@ static void cr16c_tr_translate_insn(DisasContextBase *base, CPUState *cs) {
uint64_t insn = decode_load(ctx);
if(!decode(ctx, insn)) {
// TOOD: Illegal Instruction
+ error_report("cr16c_tr_translate_insn, illegal instr, insn: 0x%04lx @ 0x%04x\n", insn, ctx->env->pc);
gen_helper_raise_illegal_instruction(tcg_env);
base->is_jmp = DISAS_NORETURN;
base->pc_next += 2;
@@ -1507,11 +1760,23 @@ void cr16c_translate_init(void) {
r[i] = tcg_global_mem_new_i32(tcg_env, offsetof(CPUCR16CState, r[i]), cr16c_cpu_r_names[i]);
}
pc = tcg_global_mem_new_i32(tcg_env, offsetof(CPUCR16CState, pc), "pc");
- f_n = tcg_global_mem_new_i32(tcg_env, offsetof(CPUCR16CState, f_n), "f_n");
- f_z = tcg_global_mem_new_i32(tcg_env, offsetof(CPUCR16CState, f_z), "f_z");
- f_f = tcg_global_mem_new_i32(tcg_env, offsetof(CPUCR16CState, f_f), "f_f");
- f_l = tcg_global_mem_new_i32(tcg_env, offsetof(CPUCR16CState, f_l), "f_l");
- f_c = tcg_global_mem_new_i32(tcg_env, offsetof(CPUCR16CState, f_c), "f_c");
+ psr_n = tcg_global_mem_new_i32(tcg_env, offsetof(CPUCR16CState, psr_n), "psr_n");
+ psr_z = tcg_global_mem_new_i32(tcg_env, offsetof(CPUCR16CState, psr_z), "psr_z");
+ psr_f = tcg_global_mem_new_i32(tcg_env, offsetof(CPUCR16CState, psr_f), "psr_f");
+ psr_l = tcg_global_mem_new_i32(tcg_env, offsetof(CPUCR16CState, psr_l), "psr_l");
+ psr_c = tcg_global_mem_new_i32(tcg_env, offsetof(CPUCR16CState, psr_c), "psr_c");
+ psr_t = tcg_global_mem_new_i32(tcg_env, offsetof(CPUCR16CState, psr_t), "psr_t");
+ psr_u = tcg_global_mem_new_i32(tcg_env, offsetof(CPUCR16CState, psr_u), "psr_u");
+ psr_e = tcg_global_mem_new_i32(tcg_env, offsetof(CPUCR16CState, psr_e), "psr_e");
+ psr_p = tcg_global_mem_new_i32(tcg_env, offsetof(CPUCR16CState, psr_p), "psr_p");
+ psr_i = tcg_global_mem_new_i32(tcg_env, offsetof(CPUCR16CState, psr_i), "psr_i");
+
+ cfg_dc = tcg_global_mem_new_i32(tcg_env, offsetof(CPUCR16CState, cfg_dc), "cfg_dc");
+ cfg_ldc = tcg_global_mem_new_i32(tcg_env, offsetof(CPUCR16CState, cfg_ldc), "cfg_ldc");
+ cfg_ic = tcg_global_mem_new_i32(tcg_env, offsetof(CPUCR16CState, cfg_ic), "cfg_ic");
+ cfg_lic = tcg_global_mem_new_i32(tcg_env, offsetof(CPUCR16CState, cfg_lic), "cfg_lic");
+ cfg_ed = tcg_global_mem_new_i32(tcg_env, offsetof(CPUCR16CState, cfg_ed), "cfg_ed");
+ cfg_sr = tcg_global_mem_new_i32(tcg_env, offsetof(CPUCR16CState, cfg_sr), "cfg_sr");
}
void cr16c_translate_code(CPUState *cs, TranslationBlock *tb,
diff --git a/tests/tcg/cr16c/macros.inc b/tests/tcg/cr16c/macros.inc
index 4979add0c7..530ee2bc36 100644
--- a/tests/tcg/cr16c/macros.inc
+++ b/tests/tcg/cr16c/macros.inc
@@ -52,6 +52,10 @@
excp UND
.endm
+.macro FAIL
+ br .fail
+.endm
+
.macro FAIL_HANDLER
.fail:
movw $1, r0
diff --git a/tests/tcg/cr16c/test04-cmps.S b/tests/tcg/cr16c/test04-cmps.S
index 817f0b840b..75a22acfc8 100644
--- a/tests/tcg/cr16c/test04-cmps.S
+++ b/tests/tcg/cr16c/test04-cmps.S
@@ -363,5 +363,13 @@ _start:
EXPECTD 0x87654321, r13
RESET
+
+ /* CMPd register width confusion */
+ MOVD $0x7fff1234, (r1,r0) /* r0 = 0x1234, r1 = 0x7fff */
+ MOVD $0, (r12)
+ cmpd (r12), (r1,r0) /* this should obviously not affect any of the pair registers */
+ cmpw r1, r0 /* this should set PSR.N and PSR.L (because r1 > r0) */
+ EXPECT_COND gt
+
ENDING
FAIL_HANDLER
diff --git a/tests/tcg/cr16c/test10-pushpop.S b/tests/tcg/cr16c/test10-pushpop.S
new file mode 100644
index 0000000000..8ef80b3e06
--- /dev/null
+++ b/tests/tcg/cr16c/test10-pushpop.S
@@ -0,0 +1,35 @@
+#include "macros.inc"
+
+.global _start
+
+.text
+_start:
+ /* Initialize registers */
+ RESET
+
+ movd $0x4000, (sp)
+begin:
+ /** BAL **/
+ bal (ra), sub1
+
+ movw $0x1234, r1
+ movw $0x4242, r2
+ bal (ra), sub2
+ EXPECT 0x1234, r1
+ EXPECT 0x4242, r2
+
+ ENDING
+ FAIL_HANDLER
+
+sub1:
+ jump (ra) /* should return */
+ FAIL
+
+
+sub2:
+ PUSH $0x2, r1, ra
+ movw $-1, r1
+ movw $-1, r2
+ POPRET $0x2, r1, ra /* should return */
+ FAIL
+
diff --git a/tests/tcg/cr16c/test11-branches.S b/tests/tcg/cr16c/test11-branches.S
new file mode 100644
index 0000000000..29acb2547a
--- /dev/null
+++ b/tests/tcg/cr16c/test11-branches.S
@@ -0,0 +1,43 @@
+#include "macros.inc"
+
+.global _start
+
+.text
+_start:
+ /* Initialize registers */
+ RESET
+
+ movd $0x4000, (sp)
+begin:
+ /* todo: test all addressing modes here somehow */
+
+ // registers
+ // register pairs
+ // immediate
+ // relative (disp variants) modes
+ // absolute
+ // (uh oh) index
+ /** BAL **/
+ bal (ra), sub1
+
+ movw $0x1234, r1
+ movw $0x4242, r2
+ bal (ra), sub2
+ EXPECT 0x1234, r1
+ EXPECT 0x4242, r2
+
+ ENDING
+ FAIL_HANDLER
+
+sub1:
+ jump (ra) /* should return */
+ FAIL
+
+
+sub2:
+ PUSH $0x2, r1, ra
+ movw $-1, r1
+ movw $-1, r2
+ POPRET $0x2, r1, ra /* should return */
+ FAIL
+