Compare commits

...

34 commits

Author SHA1 Message Date
fridtjof
4abbc2b163 wip 2026-05-04 14:31:19 +02:00
fridtjof
c37a4513c2 wip! untested: rust uart 2026-05-04 14:31:19 +02:00
fridtjof
35dd059fbf translate: don't touch registers in CMPD 2026-05-04 14:31:19 +02:00
fridtjof
a52b787a70 wip! disas: empty insn printers 2026-05-04 14:31:19 +02:00
fridtjof
6291ca5273 wip! cpu: fixup empty restore_state_to_opc 2026-05-04 14:31:19 +02:00
fridtjof
1d77b209d2 virt: fix mmio region size 2026-05-04 14:31:19 +02:00
fridtjof
0fcddb1ecf translate: fix popret goto usage 2026-05-04 14:31:19 +02:00
fridtjof
ef4679fca8 decode: fix abs20 remap for load abs 2026-05-04 14:31:19 +02:00
fridtjof
0efe75f98b Revert "wip! maybe??? fix a subtle bug in MOVD imm"
This reverts commit 574767b51e623f6686d3ad70588600b21aa79441.
2026-05-04 14:31:19 +02:00
fridtjof
d97f52906b Revert "wip! some correctness fixes to deal with host register storage vs actual target register size"
This reverts commit e8d1cba15aa8bcd942c87da9e7de3f6ccfd706e3.
2026-05-04 14:31:19 +02:00
fridtjof
d31b85c530 wip! tests: future branch tests 2026-05-04 14:31:19 +02:00
fridtjof
39d33a065a tests: add cmp register width confusion test 2026-05-04 14:31:19 +02:00
fridtjof
42e103ca1b wip! some correctness fixes to deal with host register storage vs actual target register size 2026-05-04 14:31:19 +02:00
fridtjof
2badeffc2e translate: wip lpr/spr (noop still lol) 2026-05-04 14:31:19 +02:00
fridtjof
bfb5c542f1 disas: make buildable for now 2026-05-04 14:31:19 +02:00
fridtjof
1564c02e54 wip! useless gdb target xml (untested, needs actual gdb support) 2026-05-04 14:31:19 +02:00
fridtjof
010f44408a wip! broken disas, doesnt build right now 2026-05-04 14:31:19 +02:00
fridtjof
848cef2def wip! catch writes to >16M memory which is not a part of standard cr16c but is present on some implementations 2026-05-04 14:31:19 +02:00
fridtjof
83b9b646e8 wip! todo! annotate another source of >16 bit values in backing registers 2026-05-04 14:31:18 +02:00
fridtjof
06f9921116 wip! maybe??? fix a subtle bug in MOVD imm 2026-05-04 14:31:18 +02:00
fridtjof
75a4c5793e wip! translate: implement BR{EQ,NE}0{B,W} 2026-05-04 14:31:18 +02:00
fridtjof
f4503efb20 CR16C: translate: fix pop edge case for CFG.SR=1
e.g. popret 0x2, RA_L is valid, but raised illegal instruction here
2026-05-04 14:31:18 +02:00
fridtjof
5b25a18c95 (still necessary?) translate: implement remap behavior for STORi with abs20 2026-05-04 14:31:18 +02:00
fridtjof
a45d83e2ca virt: add MMIO region as unimpl device for r/w logging 2026-05-04 14:31:18 +02:00
fridtjof
7037504166 wip! status register management 2026-05-04 14:31:18 +02:00
fridtjof
ecdcafd570 helper: report exit by guest 2026-05-04 14:31:18 +02:00
fridtjof
2b96aa2f41 wip! implement LPR(D), SPR(D) (stubs only) 2026-05-04 14:31:18 +02:00
fridtjof
31a4ca9d2c wip! pushpop bal tests 2026-05-04 14:31:18 +02:00
fridtjof
3382fc1700 wip! implement BAL, PUSH, POP(RET) 2026-05-04 14:31:18 +02:00
fridtjof
7b0d3b1a53 wip! hack! dump_regs "semihosting" helper through excp dbg 2026-05-04 14:31:18 +02:00
fridtjof
0fc68742a3 wip! cpu: implement some ops that previously crashed because they're not there 2026-05-04 14:31:18 +02:00
fridtjof
d3c73feda0 wip! random debugging stuff, halt/debug on illegal insn 2026-05-04 14:31:18 +02:00
fridtjof
0b6b0ee425 configure: add cr16c toolchain prefix 2026-05-04 14:31:18 +02:00
fridtjof
22697cd1d5
CR16C: Rename f_* to psr_* 2026-05-04 14:27:42 +02:00
34 changed files with 2056 additions and 123 deletions

1
configure vendored
View file

@ -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"}

71
gdb-xml/cr16c-core.xml Normal file
View file

@ -0,0 +1,71 @@
<?xml version="1.0"?>
<!-- Copyright (C) 2019 Free Software Foundation, Inc.
Copying and distribution of this file, with or without modification,
are permitted in any medium without royalty provided the copyright
notice and this notice are preserved. -->
<!DOCTYPE feature SYSTEM "gdb-target.dtd">
<feature name="org.gnu.gdb.cr16c.core">
<reg name="r0" bitsize="16" type="uint16"/>
<reg name="r1" bitsize="16" type="uint16"/>
<reg name="r2" bitsize="16" type="uint16"/>
<reg name="r3" bitsize="16" type="uint16"/>
<reg name="r4" bitsize="16" type="uint16"/>
<reg name="r5" bitsize="16" type="uint16"/>
<reg name="r6" bitsize="16" type="uint16"/>
<reg name="r7" bitsize="16" type="uint16"/>
<reg name="r8" bitsize="16" type="uint16"/>
<reg name="r9" bitsize="16" type="uint16"/>
<reg name="r10" bitsize="16" type="uint16"/>
<reg name="r11" bitsize="16" type="uint16"/>
<reg name="r12" bitsize="32" type="uint16"/>
<reg name="r13" bitsize="32" type="uint16"/>
<reg name="ra" bitsize="32" type="code_ptr"/>
<reg name="sp" bitsize="32" type="data_ptr"/>
<!-- TODO from here -->
<flags id="psw_flags" size="4">
<field name="C" start="0" end="0"/>
<field name="Z" start="1" end="1"/>
<field name="S" start="2" end="2"/>
<field name="O" start="3" end="3"/>
<field name="I" start="16" end="16"/>
<field name="U" start="17" end="17"/>
<field name="PM" start="20" end="20"/>
<field name="IPL" start="24" end="27"/>
</flags>
<flags id="fpsw_flags" size="4">
<field name="RM" start="0" end="1"/>
<field name="CV" start="2" end="2"/>
<field name="CO" start="3" end="3"/>
<field name="CZ" start="4" end="4"/>
<field name="CU" start="5" end="5"/>
<field name="CX" start="6" end="6"/>
<field name="CE" start="7" end="7"/>
<field name="DN" start="8" end="8"/>
<field name="EV" start="10" end="10"/>
<field name="EO" start="11" end="11"/>
<field name="EZ" start="12" end="12"/>
<field name="EU" start="13" end="13"/>
<field name="EX" start="14" end="14"/>
<field name="FV" start="26" end="26"/>
<field name="FO" start="27" end="27"/>
<field name="FZ" start="28" end="28"/>
<field name="FU" start="29" end="29"/>
<field name="FX" start="30" end="30"/>
<field name="FS" start="31" end="31"/>
</flags>
<reg name="usp" bitsize="32" type="data_ptr"/>
<reg name="isp" bitsize="32" type="data_ptr"/>
<reg name="psw" bitsize="32" type="psw_flags"/>
<reg name="pc" bitsize="32" type="code_ptr"/>
<reg name="intb" bitsize="32" type="data_ptr"/>
<reg name="bpsw" bitsize="32" type="psw_flags"/>
<reg name="bpc" bitsize="32" type="code_ptr"/>
<reg name="fintv" bitsize="32" type="code_ptr"/>
<reg name="fpsw" bitsize="32" type="fpsw_flags"/>
<reg name="acc" bitsize="64" type="uint64"/>
</feature>

View file

@ -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

View file

@ -1,5 +1,8 @@
config VIRT
bool
select UNIMP
select DIALOG_UART
config DE410
bool
select DIALOG_UART

View file

@ -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}

67
hw/cr16c/sc14480.c Normal file
View file

@ -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)

30
hw/cr16c/sc14480.h Normal file
View file

@ -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 */

View file

@ -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);

View file

@ -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'

21
rust/Cargo.lock generated
View file

@ -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"

View file

@ -2,6 +2,7 @@
resolver = "2"
members = [
"hw/char/pl011",
"hw/char/dialog_uart",
"hw/timer/hpet",
"tests",
]

View file

@ -1,2 +1,5 @@
config X_PL011_RUST
bool
config X_DIALOG_UART_RUST
bool

View file

@ -0,0 +1,31 @@
[package]
name = "dialog_uart"
version = "0.1.0"
authors = ["fridtjof <fridtjof@das-labor.org>", "Manos Pitsidianakis <manos.pitsidianakis@linaro.org>"]
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

View file

@ -0,0 +1 @@
../../../util/build.rs

View file

@ -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'},
)])

View file

@ -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"));

View file

@ -0,0 +1,505 @@
// Copyright 2024, Linaro Limited
// Copyright 2026, fridtjof
// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>, fridtjof <fridtjof@das-labor.org>
// 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<u32> for Fifo {
fn index_mut(&mut self, idx: u32) -> &mut Self::Output {
&mut self.0[idx as usize]
}
}
impl std::ops::Index<u32> 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<SysBusDevice>,
pub iomem: MemoryRegion,
#[doc(alias = "chr")]
#[property(rename = "chardev")]
pub char_backend: CharFrontend,
pub regs: BqlRefCell<SC144UartRegisters>,
/// 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<Clock>,
#[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: <SysBusDevice as ObjectType>::Class,
}
trait PL011Impl: SysBusDeviceImpl + IsA<SC144UartState> {}
impl SC144UartClass {
fn class_init<T: PL011Impl>(&mut self) {
self.parent_class.class_init::<T>();
}
}
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<unsafe fn(ParentInit<Self>)> = Some(Self::init);
const INSTANCE_POST_INIT: Option<fn(&Self)> = Some(Self::post_init);
const CLASS_INIT: fn(&mut Self::Class) = Self::Class::class_init::<Self>;
}
impl DeviceImpl for SC144UartState {
const VMSTATE: Option<VMStateDescription<Self>> = Some(VMSTATE_SC144UART);
const REALIZE: Option<fn(&Self) -> util::Result<()>> = Some(Self::realize);
}
impl ResettablePhasesImpl for SC144UartState {
const HOLD: Option<fn(&Self, ResetType)> = 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<Self>) {
static PL011_OPS: MemoryRegionOps<SC144UartState> = MemoryRegionOpsBuilder::<SC144UartState>::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::<IRQState>::from(&*irq) };
let dev = SC144UartState::new();
if !chr.is_null() {
let chr = unsafe { Owned::<Chardev>::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<SC144UartState> =
VMStateDescriptionBuilder::<SC144UartState>::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::<SC144UartRegisters>::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<SC144UartState> =
VMStateDescriptionBuilder::<SC144UartState>::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::<u32>()),
vmstate_of!(SC144UartState, regs),
})
.subsections(vmstate_subsections! {
VMSTATE_SC144UART_CLOCK
})
.build();

View file

@ -0,0 +1,21 @@
// Copyright 2024, Linaro Limited
// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
// 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";

View file

@ -0,0 +1,195 @@
// Copyright 2024, Linaro Limited
// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
// 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);

View file

@ -0,0 +1,51 @@
/*
* QEMU System Emulator
*
* Copyright (c) 2024 Linaro Ltd.
*
* Authors: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
*
* 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"

View file

@ -1 +1,2 @@
subdir('dialog_uart')
subdir('pl011')

View file

@ -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
},
};

View file

@ -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

258
target/cr16c/disas.c Normal file
View file

@ -0,0 +1,258 @@
/*
* CR16C disassembler, adapted from the AVR one
*
* Copyright (c) 2025 fridtjof <fridtjof@das-labor.org>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#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, "")
*/

View file

@ -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));
}

View file

@ -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);

View file

@ -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

View file

@ -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(),
}
};

View file

@ -8,6 +8,7 @@ cr16c_ss.add(files(
'cpu.c',
'helper.c',
'translate.c',
'disas.c',
))
cr16c_system_ss = ss.source_set()

View file

@ -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,

View file

@ -52,6 +52,10 @@
excp UND
.endm
.macro FAIL
br .fail
.endm
.macro FAIL_HANDLER
.fail:
movw $1, r0

View file

@ -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

View file

@ -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

View file

@ -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