exec
This commit is contained in:
Generated
+169
-1
@@ -7,7 +7,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "6b602bfe940d21c130f3895acd65221e8a61270debe89d628b9cb4e3ccb8569b"
|
checksum = "6b602bfe940d21c130f3895acd65221e8a61270debe89d628b9cb4e3ccb8569b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "csn_asm"
|
name = "asm"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
@@ -15,6 +15,112 @@ dependencies = [
|
|||||||
"thiserror",
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "atty"
|
||||||
|
version = "0.2.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||||
|
dependencies = [
|
||||||
|
"hermit-abi",
|
||||||
|
"libc",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "autocfg"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "0.1.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "chrono"
|
||||||
|
version = "0.4.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "942f72db697d8767c22d46a598e01f2d3b475501ea43d0db4f16d90259182d0b"
|
||||||
|
dependencies = [
|
||||||
|
"num-integer",
|
||||||
|
"num-traits",
|
||||||
|
"time",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "colored"
|
||||||
|
version = "1.9.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f4ffc801dacf156c5854b9df4f425a626539c3a6ef7893cc0c5084a23f0b6c59"
|
||||||
|
dependencies = [
|
||||||
|
"atty",
|
||||||
|
"lazy_static",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crsn"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"asm",
|
||||||
|
"log",
|
||||||
|
"runtime",
|
||||||
|
"simple_logger",
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hermit-abi"
|
||||||
|
version = "0.1.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4c30f6d0bc6b00693347368a67d41b58f2fb851215ff1da49e90fe2c5c667151"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lazy_static"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.77"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f2f96b10ec2560088a8e76961b00d47107b3a625fecb76dedb29ee7ccbf98235"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "log"
|
||||||
|
version = "0.4.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-integer"
|
||||||
|
version = "0.1.43"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8d59457e662d541ba17869cf51cf177c0b5f0cbf476c66bdc90bf1edac4f875b"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-traits"
|
||||||
|
version = "0.2.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.21"
|
version = "1.0.21"
|
||||||
@@ -33,12 +139,35 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "runtime"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"asm",
|
||||||
|
"log",
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sexp"
|
name = "sexp"
|
||||||
version = "1.1.4"
|
version = "1.1.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9c8fa7ac9df84000b0238cf497cb2d3056bac2ff2a7d8cf179d2803b4b58571f"
|
checksum = "9c8fa7ac9df84000b0238cf497cb2d3056bac2ff2a7d8cf179d2803b4b58571f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "simple_logger"
|
||||||
|
version = "1.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "13a53ed2efd04911c8280f2da7bf9abd350c931b86bc7f9f2386fbafbf525ff9"
|
||||||
|
dependencies = [
|
||||||
|
"atty",
|
||||||
|
"chrono",
|
||||||
|
"colored",
|
||||||
|
"log",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.41"
|
version = "1.0.41"
|
||||||
@@ -70,8 +199,47 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "time"
|
||||||
|
version = "0.1.44"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"wasi",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-xid"
|
name = "unicode-xid"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
|
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasi"
|
||||||
|
version = "0.10.0+wasi-snapshot-preview1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi"
|
||||||
|
version = "0.3.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-i686-pc-windows-gnu",
|
||||||
|
"winapi-x86_64-pc-windows-gnu",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-i686-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-x86_64-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
|||||||
+3
-2
@@ -1,5 +1,6 @@
|
|||||||
[workspace]
|
[workspace]
|
||||||
members = [
|
members = [
|
||||||
"csn_asm",
|
"asm",
|
||||||
|
"runtime",
|
||||||
|
"crsn"
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "csn_asm"
|
name = "asm"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
authors = ["Ondřej Hruška <ondra@ondrovo.com>"]
|
authors = ["Ondřej Hruška <ondra@ondrovo.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
@@ -6,46 +6,41 @@ use std::borrow::Cow;
|
|||||||
pub type DebugMsg = Cow<'static, str>;
|
pub type DebugMsg = Cow<'static, str>;
|
||||||
|
|
||||||
/// Immediate value
|
/// Immediate value
|
||||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
pub type Value = u64;
|
||||||
pub struct Value(pub i64);
|
|
||||||
|
|
||||||
impl From<i64> for Value {
|
pub fn is_positive(val : Value) -> bool {
|
||||||
fn from(n: i64) -> Self {
|
0 == (val & 0x8000_0000_0000_0000)
|
||||||
Self(n)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Value {
|
pub fn is_negative(val : Value) -> bool {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
0 != (val & 0x8000_0000_0000_0000)
|
||||||
if f.alternate() {
|
|
||||||
write!(f, "{:#010x}", self.0)
|
|
||||||
} else {
|
|
||||||
write!(f, "{}", self.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Value {
|
pub fn as_signed(val : Value) -> i64 {
|
||||||
pub fn as_u64(self) -> u64 {
|
i64::from_ne_bytes(val.to_ne_bytes())
|
||||||
u64::from_ne_bytes(self.0.to_ne_bytes())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn as_u32(self) -> Option<u32> {
|
|
||||||
u32::try_from(self.as_u64()).ok()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn as_i32(self) -> Option<i32> {
|
|
||||||
i32::try_from(self.0).ok()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Immediate address
|
/// Immediate address
|
||||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
#[derive(Default, Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
|
||||||
pub struct Addr(pub u64);
|
pub struct Addr(pub u64);
|
||||||
|
|
||||||
|
impl Addr {
|
||||||
|
pub fn advance(&mut self, add: i64) {
|
||||||
|
if add < 0 {
|
||||||
|
self.0 = self.0.wrapping_sub(-add as u64);
|
||||||
|
} else {
|
||||||
|
self.0 = self.0.wrapping_add(add as u64);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Display for Addr {
|
impl Display for Addr {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
write!(f, "@{:#010x}", self.0)
|
if self.0 > 0x7fff_ffff_ffff_ffff {
|
||||||
|
write!(f, "{}", i64::from_ne_bytes(self.0.to_ne_bytes()))
|
||||||
|
} else {
|
||||||
|
write!(f, "{:#010x}", self.0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,6 +50,12 @@ impl From<u64> for Addr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<Addr> for u64 {
|
||||||
|
fn from(addr: Addr) -> Self {
|
||||||
|
addr.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Label name
|
/// Label name
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||||
pub enum Label {
|
pub enum Label {
|
||||||
@@ -58,4 +58,8 @@ impl Mask {
|
|||||||
pub fn as_bitmask(self) -> u64 {
|
pub fn as_bitmask(self) -> u64 {
|
||||||
((1 << self.len) - 1) << self.offset
|
((1 << self.len) - 1) << self.offset
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_default(self) -> bool {
|
||||||
|
self == Self::default()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,188 @@
|
|||||||
|
use super::error::AsmError;
|
||||||
|
|
||||||
|
|
||||||
|
pub mod literal;
|
||||||
|
pub mod reg;
|
||||||
|
pub mod mask;
|
||||||
|
|
||||||
|
pub use reg::Register;
|
||||||
|
pub use mask::Mask;
|
||||||
|
use literal::Addr;
|
||||||
|
|
||||||
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
|
use crate::data::literal::{Value, is_negative, as_signed};
|
||||||
|
use std::fmt::{Debug, Formatter, Display};
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
/// Data source disposition
|
||||||
|
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||||
|
pub enum DataDisp {
|
||||||
|
/// Constant value
|
||||||
|
Immediate(Value),
|
||||||
|
/// Constant memory address
|
||||||
|
ImmediatePtr(Addr),
|
||||||
|
/// Register
|
||||||
|
Register(Register),
|
||||||
|
/// Pointer into memory, stored in a numbered register
|
||||||
|
RegisterPtr(Register),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for DataDisp {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
DataDisp::Immediate(v) => {
|
||||||
|
write!(f, "{}", as_signed(*v))
|
||||||
|
}
|
||||||
|
DataDisp::ImmediatePtr(a) => {
|
||||||
|
write!(f, "@0x{:#18x}", a.0)
|
||||||
|
}
|
||||||
|
DataDisp::Register(r) => {
|
||||||
|
write!(f, "{}", r)
|
||||||
|
}
|
||||||
|
DataDisp::RegisterPtr(r) => {
|
||||||
|
write!(f, "@{}", r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<SrcDisp> for DataDisp {
|
||||||
|
fn from(s: SrcDisp) -> Self {
|
||||||
|
match s {
|
||||||
|
SrcDisp::Immediate(x) => DataDisp::Immediate(x),
|
||||||
|
SrcDisp::ImmediatePtr(x) => DataDisp::ImmediatePtr(x),
|
||||||
|
SrcDisp::Register(x) => DataDisp::Register(x),
|
||||||
|
SrcDisp::RegisterPtr(x) => DataDisp::RegisterPtr(x),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<DstDisp> for DataDisp {
|
||||||
|
fn from(s: DstDisp) -> Self {
|
||||||
|
match s {
|
||||||
|
DstDisp::ImmediatePtr(x) => DataDisp::ImmediatePtr(x),
|
||||||
|
DstDisp::Register(x) => DataDisp::Register(x),
|
||||||
|
DstDisp::RegisterPtr(x) => DataDisp::RegisterPtr(x),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Data source disposition
|
||||||
|
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||||
|
pub enum SrcDisp {
|
||||||
|
/// Constant value
|
||||||
|
Immediate(Value),
|
||||||
|
/// Constant memory address
|
||||||
|
ImmediatePtr(Addr),
|
||||||
|
/// Register
|
||||||
|
Register(Register),
|
||||||
|
/// Pointer into memory, stored in a numbered register
|
||||||
|
RegisterPtr(Register),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<DataDisp> for SrcDisp {
|
||||||
|
type Error = AsmError;
|
||||||
|
|
||||||
|
fn try_from(value: DataDisp) -> Result<Self, Self::Error> {
|
||||||
|
match value {
|
||||||
|
DataDisp::Immediate(x) => Ok(SrcDisp::Immediate(x)),
|
||||||
|
DataDisp::ImmediatePtr(x) => Ok(SrcDisp::ImmediatePtr(x)),
|
||||||
|
DataDisp::Register(x) => Ok(SrcDisp::Register(x)),
|
||||||
|
DataDisp::RegisterPtr(x) => Ok(SrcDisp::RegisterPtr(x)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Data destination disposition
|
||||||
|
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||||
|
pub enum DstDisp {
|
||||||
|
/// Constant memory address
|
||||||
|
ImmediatePtr(Addr),
|
||||||
|
/// Register
|
||||||
|
Register(Register),
|
||||||
|
/// Pointer into memory, stored in a numbered register
|
||||||
|
RegisterPtr(Register),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<DstDisp> for SrcDisp {
|
||||||
|
fn from(s: DstDisp) -> Self {
|
||||||
|
match s {
|
||||||
|
DstDisp::ImmediatePtr(x) => SrcDisp::ImmediatePtr(x),
|
||||||
|
DstDisp::Register(x) => SrcDisp::Register(x),
|
||||||
|
DstDisp::RegisterPtr(x) => SrcDisp::RegisterPtr(x),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<DataDisp> for DstDisp {
|
||||||
|
type Error = AsmError;
|
||||||
|
|
||||||
|
fn try_from(value: DataDisp) -> Result<Self, Self::Error> {
|
||||||
|
match value {
|
||||||
|
DataDisp::Immediate(_x) => Err(AsmError::ValueAsOutput),
|
||||||
|
DataDisp::ImmediatePtr(x) => Ok(DstDisp::ImmediatePtr(x)),
|
||||||
|
DataDisp::Register(x) => Ok(DstDisp::Register(x)),
|
||||||
|
DataDisp::RegisterPtr(x) => Ok(DstDisp::RegisterPtr(x)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Data source argument (read-only)
|
||||||
|
#[derive(Clone, Copy, Eq, PartialEq)]
|
||||||
|
pub struct Rd(SrcDisp, Mask);
|
||||||
|
|
||||||
|
impl Rd {
|
||||||
|
pub fn new(src: SrcDisp) -> Self {
|
||||||
|
Rd(src, Mask::default())
|
||||||
|
}
|
||||||
|
pub fn d(self) -> SrcDisp {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
pub fn mask(self) -> Mask {
|
||||||
|
self.1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Data destination argument (read-write)
|
||||||
|
#[derive(Clone, Copy, Eq, PartialEq)]
|
||||||
|
pub struct Wr(DstDisp, Mask);
|
||||||
|
|
||||||
|
impl Wr {
|
||||||
|
pub fn new(dst: DstDisp) -> Self {
|
||||||
|
Wr(dst, Mask::default())
|
||||||
|
}
|
||||||
|
pub fn d(self) -> DstDisp {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
pub fn mask(self) -> Mask {
|
||||||
|
self.1
|
||||||
|
}
|
||||||
|
pub fn as_rd(&self) -> Rd {
|
||||||
|
Rd(self.0.into(), self.1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for Rd {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "Rd(")?;
|
||||||
|
let disp : DataDisp = self.0.into();
|
||||||
|
write!(f, "{}", disp);
|
||||||
|
if !self.mask().is_default() {
|
||||||
|
write!(f, ",{:?}", self.mask());
|
||||||
|
}
|
||||||
|
write!(f, ")")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for Wr {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "Wr(")?;
|
||||||
|
let disp : DataDisp = self.0.into();
|
||||||
|
write!(f, "{}", disp);
|
||||||
|
if !self.mask().is_default() {
|
||||||
|
write!(f, ",{:?}", self.mask());
|
||||||
|
}
|
||||||
|
write!(f, ")")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,8 +7,8 @@ pub enum Cond {
|
|||||||
NotEqual,
|
NotEqual,
|
||||||
Zero,
|
Zero,
|
||||||
NotZero,
|
NotZero,
|
||||||
Less,
|
Lower,
|
||||||
LessOrEqual,
|
LowerOrEqual,
|
||||||
Greater,
|
Greater,
|
||||||
GreaterOrEqual,
|
GreaterOrEqual,
|
||||||
Positive,
|
Positive,
|
||||||
@@ -28,8 +28,8 @@ impl Display for Cond {
|
|||||||
Cond::NotEqual => "ne",
|
Cond::NotEqual => "ne",
|
||||||
Cond::Zero => "z",
|
Cond::Zero => "z",
|
||||||
Cond::NotZero => "nz",
|
Cond::NotZero => "nz",
|
||||||
Cond::Less => "lt",
|
Cond::Lower => "lt",
|
||||||
Cond::LessOrEqual => "le",
|
Cond::LowerOrEqual => "le",
|
||||||
Cond::Greater => "gt",
|
Cond::Greater => "gt",
|
||||||
Cond::GreaterOrEqual => "ge",
|
Cond::GreaterOrEqual => "ge",
|
||||||
Cond::Positive => "pos",
|
Cond::Positive => "pos",
|
||||||
@@ -64,10 +64,10 @@ impl Not for Cond {
|
|||||||
Cond::NotOverflow => Cond::Overflow,
|
Cond::NotOverflow => Cond::Overflow,
|
||||||
Cond::NotCarry => Cond::Carry,
|
Cond::NotCarry => Cond::Carry,
|
||||||
|
|
||||||
Cond::Less => Cond::GreaterOrEqual,
|
Cond::Lower => Cond::GreaterOrEqual,
|
||||||
Cond::Greater => Cond::LessOrEqual,
|
Cond::Greater => Cond::LowerOrEqual,
|
||||||
Cond::LessOrEqual => Cond::Greater,
|
Cond::LowerOrEqual => Cond::Greater,
|
||||||
Cond::GreaterOrEqual => Cond::Less,
|
Cond::GreaterOrEqual => Cond::Lower,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -80,7 +80,7 @@ pub fn lower(ops: Vec<HLOp>) -> Result<Vec<Op>, Error> {
|
|||||||
HLOp::Jump(target) => {
|
HLOp::Jump(target) => {
|
||||||
if let Some(dest) = label_positions.get(&target) {
|
if let Some(dest) = label_positions.get(&target) {
|
||||||
let skip = *dest as isize - n as isize + skipped;
|
let skip = *dest as isize - n as isize + skipped;
|
||||||
cleaned.push(Op::Skip(Rd::new(SrcDisp::Immediate(Value(skip as i64)))));
|
cleaned.push(Op::Skip(Rd::new(SrcDisp::Immediate(skip as Value))));
|
||||||
} else {
|
} else {
|
||||||
return Err(Error::Asm(AsmError::LabelNotDefined(target)));
|
return Err(Error::Asm(AsmError::LabelNotDefined(target)));
|
||||||
}
|
}
|
||||||
@@ -88,7 +88,7 @@ pub fn lower(ops: Vec<HLOp>) -> Result<Vec<Op>, Error> {
|
|||||||
HLOp::JumpIf(cond, target) => {
|
HLOp::JumpIf(cond, target) => {
|
||||||
if let Some(dest) = label_positions.get(&target) {
|
if let Some(dest) = label_positions.get(&target) {
|
||||||
let skip = *dest as isize - n as isize + skipped;
|
let skip = *dest as isize - n as isize + skipped;
|
||||||
cleaned.push(Op::SkipIf(cond, Rd::new(SrcDisp::Immediate(Value(skip as i64)))));
|
cleaned.push(Op::SkipIf(cond, Rd::new(SrcDisp::Immediate(skip as Value))));
|
||||||
} else {
|
} else {
|
||||||
return Err(Error::Asm(AsmError::LabelNotDefined(target)));
|
return Err(Error::Asm(AsmError::LabelNotDefined(target)));
|
||||||
}
|
}
|
||||||
@@ -22,6 +22,8 @@ pub enum HLOp {
|
|||||||
/// A low level instruction
|
/// A low level instruction
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
pub enum Op {
|
pub enum Op {
|
||||||
|
/// Do nothing
|
||||||
|
Nop,
|
||||||
/// Mark a far jump target (can be jumped to from another routine).
|
/// Mark a far jump target (can be jumped to from another routine).
|
||||||
/// This label is preserved in optimized code.
|
/// This label is preserved in optimized code.
|
||||||
FarLabel(Label),
|
FarLabel(Label),
|
||||||
@@ -48,7 +50,8 @@ pub enum Op {
|
|||||||
|
|
||||||
/// Copy a value
|
/// Copy a value
|
||||||
Mov(Wr, Rd),
|
Mov(Wr, Rd),
|
||||||
/// Compare two values and set conditional flags
|
/// Compare two values and set conditional flags.
|
||||||
|
/// If the values are identical, evaluate if they are zero, positive, or negative (the "tst" op)
|
||||||
Cmp(Rd, Rd),
|
Cmp(Rd, Rd),
|
||||||
// Increment a value
|
// Increment a value
|
||||||
Inc(Wr),
|
Inc(Wr),
|
||||||
@@ -17,7 +17,7 @@ mod tests {
|
|||||||
use crate::parse::{parse, parse_instructions};
|
use crate::parse::{parse, parse_instructions};
|
||||||
use crate::instr::{HLOp, Op, Flatten, Instr, lower};
|
use crate::instr::{HLOp, Op, Flatten, Instr, lower};
|
||||||
use crate::data::{Wr, DstDisp, Register, SrcDisp, Rd};
|
use crate::data::{Wr, DstDisp, Register, SrcDisp, Rd};
|
||||||
use crate::data::literal::{Value, Addr, Label};
|
use crate::data::literal::{Addr, Label};
|
||||||
use std::sync::atomic::AtomicU32;
|
use std::sync::atomic::AtomicU32;
|
||||||
use crate::instr::Cond;
|
use crate::instr::Cond;
|
||||||
|
|
||||||
@@ -86,17 +86,17 @@ mod tests {
|
|||||||
// (mov r15 7)
|
// (mov r15 7)
|
||||||
HLOp::L(Op::Mov(
|
HLOp::L(Op::Mov(
|
||||||
Wr::new(DstDisp::Register(Register::Gen(15))),
|
Wr::new(DstDisp::Register(Register::Gen(15))),
|
||||||
Rd::new(SrcDisp::Immediate(Value(7))),
|
Rd::new(SrcDisp::Immediate(7)),
|
||||||
)),
|
)),
|
||||||
// (mov r15 0xabcd)
|
// (mov r15 0xabcd)
|
||||||
HLOp::L(Op::Mov(
|
HLOp::L(Op::Mov(
|
||||||
Wr::new(DstDisp::Register(Register::Gen(15))),
|
Wr::new(DstDisp::Register(Register::Gen(15))),
|
||||||
Rd::new(SrcDisp::Immediate(Value(0xabcd))),
|
Rd::new(SrcDisp::Immediate(0xabcd)),
|
||||||
)),
|
)),
|
||||||
// (mov r7 0b11110000)
|
// (mov r7 0b11110000)
|
||||||
HLOp::L(Op::Mov(
|
HLOp::L(Op::Mov(
|
||||||
Wr::new(DstDisp::Register(Register::Gen(7))),
|
Wr::new(DstDisp::Register(Register::Gen(7))),
|
||||||
Rd::new(SrcDisp::Immediate(Value(0b11110000))),
|
Rd::new(SrcDisp::Immediate(0b11110000)),
|
||||||
)),
|
)),
|
||||||
// (mov r7 arg1)
|
// (mov r7 arg1)
|
||||||
HLOp::L(Op::Mov(
|
HLOp::L(Op::Mov(
|
||||||
@@ -250,7 +250,7 @@ mod tests {
|
|||||||
)),
|
)),
|
||||||
HLOp::Jump(Label::Numbered(0)),
|
HLOp::Jump(Label::Numbered(0)),
|
||||||
HLOp::Label(Label::Numbered(1)),
|
HLOp::Label(Label::Numbered(1)),
|
||||||
HLOp::JumpIf(Cond::LessOrEqual, Label::Numbered(0)),
|
HLOp::JumpIf(Cond::LowerOrEqual, Label::Numbered(0)),
|
||||||
HLOp::L(Op::Mov(
|
HLOp::L(Op::Mov(
|
||||||
Wr::new(DstDisp::Register(Register::Gen(0))),
|
Wr::new(DstDisp::Register(Register::Gen(0))),
|
||||||
Rd::new(SrcDisp::Register(Register::Gen(0))),
|
Rd::new(SrcDisp::Register(Register::Gen(0))),
|
||||||
@@ -317,8 +317,8 @@ mod tests {
|
|||||||
Wr::new(DstDisp::Register(Register::Gen(0))),
|
Wr::new(DstDisp::Register(Register::Gen(0))),
|
||||||
Rd::new(SrcDisp::Register(Register::Gen(0))),
|
Rd::new(SrcDisp::Register(Register::Gen(0))),
|
||||||
),
|
),
|
||||||
Op::Skip(Rd::new(SrcDisp::Immediate(Value(-1)))),
|
Op::Skip(Rd::new(SrcDisp::Immediate(-1))),
|
||||||
Op::Skip(Rd::new(SrcDisp::Immediate(Value(-2)))),
|
Op::Skip(Rd::new(SrcDisp::Immediate(-2))),
|
||||||
Op::Mov(
|
Op::Mov(
|
||||||
Wr::new(DstDisp::Register(Register::Gen(0))),
|
Wr::new(DstDisp::Register(Register::Gen(0))),
|
||||||
Rd::new(SrcDisp::Register(Register::Gen(0))),
|
Rd::new(SrcDisp::Register(Register::Gen(0))),
|
||||||
@@ -327,8 +327,8 @@ mod tests {
|
|||||||
Wr::new(DstDisp::Register(Register::Gen(0))),
|
Wr::new(DstDisp::Register(Register::Gen(0))),
|
||||||
Rd::new(SrcDisp::Register(Register::Gen(0))),
|
Rd::new(SrcDisp::Register(Register::Gen(0))),
|
||||||
),
|
),
|
||||||
Op::Skip(Rd::new(SrcDisp::Immediate(Value(-5)))),
|
Op::Skip(Rd::new(SrcDisp::Immediate(-5))),
|
||||||
Op::SkipIf(Cond::Equal, Rd::new(SrcDisp::Immediate(Value(-6)))),
|
Op::SkipIf(Cond::Equal, Rd::new(SrcDisp::Immediate(-6))),
|
||||||
Op::Barrier(Some("Routine \"foo\" overrun".into())),
|
Op::Barrier(Some("Routine \"foo\" overrun".into())),
|
||||||
], cleaned);
|
], cleaned);
|
||||||
}
|
}
|
||||||
@@ -22,8 +22,8 @@ pub fn parse_cond(text: &str) -> Result<Cond, Error> {
|
|||||||
"ne" | "<>" | "!=" | "≠" => Cond::NotEqual,
|
"ne" | "<>" | "!=" | "≠" => Cond::NotEqual,
|
||||||
"z" | "0" => Cond::Zero,
|
"z" | "0" => Cond::Zero,
|
||||||
"nz" | "<>0" | "!0" => Cond::NotZero,
|
"nz" | "<>0" | "!0" => Cond::NotZero,
|
||||||
"lt" | "<" => Cond::Less,
|
"lt" | "<" => Cond::Lower,
|
||||||
"le" | "<=" | "≤" => Cond::LessOrEqual,
|
"le" | "<=" | "≤" => Cond::LowerOrEqual,
|
||||||
"gt" | ">" => Cond::Greater,
|
"gt" | ">" => Cond::Greater,
|
||||||
"ge" | ">=" | "≥" => Cond::GreaterOrEqual,
|
"ge" | ">=" | "≥" => Cond::GreaterOrEqual,
|
||||||
"pos" | "+" | ">0" => Cond::Positive,
|
"pos" | "+" | ">0" => Cond::Positive,
|
||||||
@@ -23,7 +23,7 @@ pub fn parse_data_disp(tok: Option<Sexp>) -> Result<DataDisp, Error> {
|
|||||||
|
|
||||||
match &tok {
|
match &tok {
|
||||||
Sexp::Atom(Atom::I(val)) => {
|
Sexp::Atom(Atom::I(val)) => {
|
||||||
Ok(DataDisp::Immediate(Value(*val)))
|
Ok(DataDisp::Immediate(unsafe { std::mem::transmute(*val) }))
|
||||||
},
|
},
|
||||||
Sexp::Atom(Atom::S(s)) => {
|
Sexp::Atom(Atom::S(s)) => {
|
||||||
if let Some(reference) = s.strip_prefix('@') {
|
if let Some(reference) = s.strip_prefix('@') {
|
||||||
@@ -34,7 +34,7 @@ pub fn parse_data_disp(tok: Option<Sexp>) -> Result<DataDisp, Error> {
|
|||||||
Ok(DataDisp::RegisterPtr(reg::parse_reg(reference)?))
|
Ok(DataDisp::RegisterPtr(reg::parse_reg(reference)?))
|
||||||
}
|
}
|
||||||
} else if s.starts_with(|c : char| c.is_ascii_digit()) {
|
} else if s.starts_with(|c : char| c.is_ascii_digit()) {
|
||||||
Ok(DataDisp::Immediate(Value(parse_i64(s)?)))
|
Ok(DataDisp::Immediate(unsafe { std::mem::transmute(parse_i64(s)?) }))
|
||||||
} else {
|
} else {
|
||||||
Ok(DataDisp::Register(reg::parse_reg(s)?))
|
Ok(DataDisp::Register(reg::parse_reg(s)?))
|
||||||
}
|
}
|
||||||
@@ -40,17 +40,17 @@ pub fn parse_op(keyword: &str, far : bool, mut arg_tokens: impl Iterator<Item=Se
|
|||||||
HLOp::L(Op::Routine(dest))
|
HLOp::L(Op::Routine(dest))
|
||||||
}
|
}
|
||||||
|
|
||||||
"sk" => {
|
"s" => {
|
||||||
HLOp::L(Op::Skip(parse_rd(arg_tokens.next())?))
|
HLOp::L(Op::Skip(parse_rd(arg_tokens.next())?))
|
||||||
}
|
}
|
||||||
|
|
||||||
"csk" => {
|
"sif" => {
|
||||||
let cond = parse_cond(&expect_string_atom(arg_tokens.next())?)?;
|
let cond = parse_cond(&expect_string_atom(arg_tokens.next())?)?;
|
||||||
let offs = parse_rd(arg_tokens.next())?;
|
let offs = parse_rd(arg_tokens.next())?;
|
||||||
HLOp::L(Op::SkipIf(cond, offs))
|
HLOp::L(Op::SkipIf(cond, offs))
|
||||||
}
|
}
|
||||||
|
|
||||||
"cj" => {
|
"jif" => {
|
||||||
let cond = parse_cond(&expect_string_atom(arg_tokens.next())?)?;
|
let cond = parse_cond(&expect_string_atom(arg_tokens.next())?)?;
|
||||||
let dest = parse_label(arg_tokens.next())?;
|
let dest = parse_label(arg_tokens.next())?;
|
||||||
HLOp::JumpIf(cond, dest)
|
HLOp::JumpIf(cond, dest)
|
||||||
@@ -84,6 +84,11 @@ pub fn parse_op(keyword: &str, far : bool, mut arg_tokens: impl Iterator<Item=Se
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
"tst" => {
|
||||||
|
let arg = parse_rd(arg_tokens.next())?;
|
||||||
|
HLOp::L(Op::Cmp(arg, arg))
|
||||||
|
}
|
||||||
|
|
||||||
"inc" => {
|
"inc" => {
|
||||||
HLOp::L(Op::Inc(
|
HLOp::L(Op::Inc(
|
||||||
parse_wr(arg_tokens.next())?
|
parse_wr(arg_tokens.next())?
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
[package]
|
||||||
|
name = "crsn"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Ondřej Hruška <ondra@ondrovo.com>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
asm = { path = "../asm" }
|
||||||
|
runtime = { path = "../runtime" }
|
||||||
|
simple_logger = "1.9.0"
|
||||||
|
log = "0.4.11"
|
||||||
|
thiserror = "1.0.20"
|
||||||
|
anyhow = "1.0.32"
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
#[macro_use]
|
||||||
|
extern crate log;
|
||||||
|
|
||||||
|
use simple_logger::SimpleLogger;
|
||||||
|
use runtime::run_thread::{RunThread, ThreadToken};
|
||||||
|
use runtime::program::Program;
|
||||||
|
use asm::data::literal::Addr;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
SimpleLogger::new().init().unwrap();
|
||||||
|
|
||||||
|
// ;(dec r0 (z? (ret)))
|
||||||
|
let program = "
|
||||||
|
(
|
||||||
|
(main
|
||||||
|
(ld r0 2)
|
||||||
|
(:again)
|
||||||
|
(dec r0)
|
||||||
|
(jif nz :again)
|
||||||
|
(fault \"that's it\")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
";
|
||||||
|
|
||||||
|
let parsed = asm::assemble(program).unwrap();
|
||||||
|
|
||||||
|
debug!("---");
|
||||||
|
for op in &parsed {
|
||||||
|
debug!("{:?}", op);
|
||||||
|
}
|
||||||
|
debug!("---");
|
||||||
|
|
||||||
|
let thread = RunThread::new(ThreadToken(0), Program::new(parsed), Addr(0), &[]);
|
||||||
|
|
||||||
|
thread.start().join().unwrap();
|
||||||
|
}
|
||||||
@@ -1,97 +0,0 @@
|
|||||||
use super::error::AsmError;
|
|
||||||
|
|
||||||
|
|
||||||
pub mod literal;
|
|
||||||
pub mod reg;
|
|
||||||
pub mod mask;
|
|
||||||
|
|
||||||
pub use reg::Register;
|
|
||||||
pub use mask::Mask;
|
|
||||||
use literal::Addr;
|
|
||||||
|
|
||||||
use std::convert::TryFrom;
|
|
||||||
|
|
||||||
use crate::data::literal::Value;
|
|
||||||
|
|
||||||
/// Data source disposition
|
|
||||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
|
||||||
pub enum DataDisp {
|
|
||||||
/// Constant value
|
|
||||||
Immediate(Value),
|
|
||||||
/// Constant memory address
|
|
||||||
ImmediatePtr(Addr),
|
|
||||||
/// Register
|
|
||||||
Register(Register),
|
|
||||||
/// Pointer into memory, stored in a numbered register
|
|
||||||
RegisterPtr(Register),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Data source disposition
|
|
||||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
|
||||||
pub enum SrcDisp {
|
|
||||||
/// Constant value
|
|
||||||
Immediate(Value),
|
|
||||||
/// Constant memory address
|
|
||||||
ImmediatePtr(Addr),
|
|
||||||
/// Register
|
|
||||||
Register(Register),
|
|
||||||
/// Pointer into memory, stored in a numbered register
|
|
||||||
RegisterPtr(Register),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<DataDisp> for SrcDisp {
|
|
||||||
type Error = AsmError;
|
|
||||||
|
|
||||||
fn try_from(value: DataDisp) -> Result<Self, Self::Error> {
|
|
||||||
match value {
|
|
||||||
DataDisp::Immediate(x) => Ok(SrcDisp::Immediate(x)),
|
|
||||||
DataDisp::ImmediatePtr(x) => Ok(SrcDisp::ImmediatePtr(x)),
|
|
||||||
DataDisp::Register(x) => Ok(SrcDisp::Register(x)),
|
|
||||||
DataDisp::RegisterPtr(x) => Ok(SrcDisp::RegisterPtr(x)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Data destination disposition
|
|
||||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
|
||||||
pub enum DstDisp {
|
|
||||||
/// Constant memory address
|
|
||||||
ImmediatePtr(Addr),
|
|
||||||
/// Register
|
|
||||||
Register(Register),
|
|
||||||
/// Pointer into memory, stored in a numbered register
|
|
||||||
RegisterPtr(Register),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<DataDisp> for DstDisp {
|
|
||||||
type Error = AsmError;
|
|
||||||
|
|
||||||
fn try_from(value: DataDisp) -> Result<Self, Self::Error> {
|
|
||||||
match value {
|
|
||||||
DataDisp::Immediate(_x) => Err(AsmError::ValueAsOutput),
|
|
||||||
DataDisp::ImmediatePtr(x) => Ok(DstDisp::ImmediatePtr(x)),
|
|
||||||
DataDisp::Register(x) => Ok(DstDisp::Register(x)),
|
|
||||||
DataDisp::RegisterPtr(x) => Ok(DstDisp::RegisterPtr(x)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Data source argument (read-only)
|
|
||||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
|
||||||
pub struct Rd(SrcDisp, Mask);
|
|
||||||
|
|
||||||
impl Rd {
|
|
||||||
pub fn new(src : SrcDisp) -> Self {
|
|
||||||
Rd(src, Mask::default())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Data destination argument (read-write)
|
|
||||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
|
||||||
pub struct Wr(DstDisp, Mask);
|
|
||||||
|
|
||||||
impl Wr {
|
|
||||||
pub fn new(dst : DstDisp) -> Self {
|
|
||||||
Wr(dst, Mask::default())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
[package]
|
||||||
|
name = "runtime"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Ondřej Hruška <ondra@ondrovo.com>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
asm = { path = "../asm" }
|
||||||
|
thiserror = "1.0.20"
|
||||||
|
anyhow = "1.0.32"
|
||||||
|
log = "0.4.11"
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
use crate::run_thread::{ThreadToken, RunThread};
|
||||||
|
use asm::instr::{Op, Cond};
|
||||||
|
use crate::fault::Fault;
|
||||||
|
use crate::frame::StackFrame;
|
||||||
|
use asm::data::literal::{Value, is_positive, is_negative};
|
||||||
|
|
||||||
|
pub type CyclesSpent = usize;
|
||||||
|
|
||||||
|
pub struct EvalRes {
|
||||||
|
pub cycles: u8,
|
||||||
|
pub advance: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RunThread {
|
||||||
|
pub fn eval_op(&mut self) -> Result<EvalRes, Fault> {
|
||||||
|
let mut cycles = 1;
|
||||||
|
let mut advance = 1;
|
||||||
|
|
||||||
|
let mut frame = &mut self.frame;
|
||||||
|
|
||||||
|
let op = self.program.read(frame.pc);
|
||||||
|
debug!("{} | {:?}", frame.pc, op);
|
||||||
|
|
||||||
|
/* Operations can be given different execution times when run in slow mode. */
|
||||||
|
/* Presently, all that do anything use 1 cycle. */
|
||||||
|
|
||||||
|
match op {
|
||||||
|
Op::Nop => {}
|
||||||
|
Op::FarLabel(_) | Op::Routine(_) => {
|
||||||
|
/* this is nop, but without any cost - just markers */
|
||||||
|
cycles = 0;
|
||||||
|
}
|
||||||
|
Op::Barrier(msg) => {
|
||||||
|
return Err(Fault::Barrier {
|
||||||
|
msg: msg.clone().unwrap_or_else(|| "No msg".into())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Op::Fault(msg) => {
|
||||||
|
return Err(Fault::FaultInstr {
|
||||||
|
msg: msg.clone().unwrap_or_else(|| "No msg".into())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Op::FarJump(_) => unimplemented!(),
|
||||||
|
Op::Call(_, _) => unimplemented!(),
|
||||||
|
Op::Ret(_) => unimplemented!(),
|
||||||
|
Op::Skip(val) => {
|
||||||
|
let steps = frame.read(*val)?;
|
||||||
|
advance = i64::from_ne_bytes(steps.to_ne_bytes());
|
||||||
|
}
|
||||||
|
Op::SkipIf(cond, val) => {
|
||||||
|
if frame.status.test(*cond) {
|
||||||
|
let steps = frame.read(*val)?;
|
||||||
|
advance = i64::from_ne_bytes(steps.to_ne_bytes());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Op::Mov(dst, src) => {
|
||||||
|
let val = frame.read(*src)?;
|
||||||
|
frame.write(*dst, val)?;
|
||||||
|
}
|
||||||
|
Op::Cmp(a, b) => {
|
||||||
|
frame.status.clear();
|
||||||
|
let a = frame.read(*a)?;
|
||||||
|
let b = frame.read(*b)?;
|
||||||
|
frame.status.equal = a == b;
|
||||||
|
frame.status.zero = a == 0 && b == 0;
|
||||||
|
frame.status.lower = a < b;
|
||||||
|
frame.status.greater = a > b;
|
||||||
|
frame.status.positive = is_positive(a) && is_positive(b);
|
||||||
|
frame.status.negative = is_negative(a) && is_negative(b);
|
||||||
|
}
|
||||||
|
Op::Inc(reg) => {
|
||||||
|
frame.status.clear();
|
||||||
|
let mut val = frame.read(reg.as_rd())?;
|
||||||
|
val = val.wrapping_add(1);
|
||||||
|
frame.status.overflow = (val == 0);
|
||||||
|
frame.status.zero = (val == 0);
|
||||||
|
frame.status.positive = is_positive(val);
|
||||||
|
frame.status.negative = is_negative(val);
|
||||||
|
frame.write(*reg, val)?;
|
||||||
|
}
|
||||||
|
Op::Dec(reg) => {
|
||||||
|
frame.status.clear();
|
||||||
|
let mut val = frame.read(reg.as_rd())?;
|
||||||
|
frame.status.overflow = (val == 0); // will overflow
|
||||||
|
val = val.wrapping_sub(1);
|
||||||
|
frame.status.zero = (val == 0);
|
||||||
|
frame.status.positive = is_positive(val);
|
||||||
|
frame.status.negative = is_negative(val);
|
||||||
|
frame.write(*reg, val)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(EvalRes {
|
||||||
|
cycles,
|
||||||
|
advance,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
use thiserror::Error;
|
||||||
|
use super::span::MemorySpan;
|
||||||
|
use crate::run_thread::ThreadToken;
|
||||||
|
use crate::mlock::ClaimId;
|
||||||
|
use asm::data::literal::DebugMsg;
|
||||||
|
use asm::data::Register;
|
||||||
|
|
||||||
|
#[derive(Error,Debug)]
|
||||||
|
pub enum Fault {
|
||||||
|
#[error("Bad instruction at addr {addr:#10x}: {cause}")]
|
||||||
|
BadInstruction {
|
||||||
|
addr : u32,
|
||||||
|
cause: InstrError,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[error("Runtime hit a barrier instruction: {msg}")]
|
||||||
|
Barrier {
|
||||||
|
msg: DebugMsg,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[error("User fault: {msg}")]
|
||||||
|
FaultInstr {
|
||||||
|
msg: DebugMsg,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[error("Memory region {area:?} is locked by thread {owner:?}")]
|
||||||
|
MemoryLocked {
|
||||||
|
area: MemorySpan,
|
||||||
|
owner: ThreadToken
|
||||||
|
},
|
||||||
|
|
||||||
|
#[error("Memory claim {claim:?} owned by thread {owner:?} does not exist")]
|
||||||
|
ClaimNotExist {
|
||||||
|
claim: ClaimId,
|
||||||
|
owner: ThreadToken
|
||||||
|
},
|
||||||
|
|
||||||
|
#[error("Register does not exist: {reg:?}")]
|
||||||
|
RegisterNotExist {
|
||||||
|
reg: Register,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[error("Register is read-only: {reg:?}")]
|
||||||
|
RegisterNotWritable {
|
||||||
|
reg: Register,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Error,Debug)]
|
||||||
|
pub enum InstrError {
|
||||||
|
#[error("Instruction not recognized")]
|
||||||
|
UnknownInstruction,
|
||||||
|
|
||||||
|
#[error("Invalid bit span")]
|
||||||
|
BadBitSpan,
|
||||||
|
|
||||||
|
#[error("Operands data size differs")]
|
||||||
|
UnevenOperandSize,
|
||||||
|
}
|
||||||
@@ -0,0 +1,150 @@
|
|||||||
|
use asm::data::literal::{Addr, Value};
|
||||||
|
use asm::data::{Rd, SrcDisp, Register, Wr, DstDisp};
|
||||||
|
use crate::fault::Fault;
|
||||||
|
use asm::instr::Cond;
|
||||||
|
|
||||||
|
pub const REG_COUNT: usize = 8;
|
||||||
|
|
||||||
|
#[derive(Default, Clone, Debug)]
|
||||||
|
pub struct StatusFlags {
|
||||||
|
/// Arguments are equal
|
||||||
|
pub equal: bool,
|
||||||
|
/// Register is zero
|
||||||
|
pub zero: bool,
|
||||||
|
/// A < B
|
||||||
|
pub lower: bool,
|
||||||
|
/// A > B
|
||||||
|
pub greater: bool,
|
||||||
|
/// Register is positive
|
||||||
|
pub positive: bool,
|
||||||
|
/// Register is negative
|
||||||
|
pub negative: bool,
|
||||||
|
/// Overflow (multiplication etc.)
|
||||||
|
pub overflow: bool,
|
||||||
|
/// Arithmetic carry
|
||||||
|
pub carry: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StatusFlags {
|
||||||
|
pub fn clear(&mut self) {
|
||||||
|
*self = Self::default();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn test(&self, cond: Cond) -> bool {
|
||||||
|
match cond {
|
||||||
|
Cond::Equal => self.equal,
|
||||||
|
Cond::NotEqual => !self.equal,
|
||||||
|
Cond::Zero => self.zero,
|
||||||
|
Cond::NotZero => !self.zero,
|
||||||
|
Cond::Lower => self.lower,
|
||||||
|
Cond::LowerOrEqual => self.lower || self.equal,
|
||||||
|
Cond::Greater => self.greater,
|
||||||
|
Cond::GreaterOrEqual => self.greater || self.equal,
|
||||||
|
Cond::Positive => self.positive,
|
||||||
|
Cond::NonPositive => !self.positive,
|
||||||
|
Cond::Negative => self.negative,
|
||||||
|
Cond::NonNegative => !self.negative,
|
||||||
|
Cond::Overflow => self.overflow,
|
||||||
|
Cond::NotOverflow => !self.overflow,
|
||||||
|
Cond::Carry => self.carry,
|
||||||
|
Cond::NotCarry => !self.carry
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Clone, Debug)]
|
||||||
|
pub struct StackFrame {
|
||||||
|
/// Program counter, address of the executed instruction
|
||||||
|
pub pc: Addr,
|
||||||
|
/// Status flags
|
||||||
|
pub status: StatusFlags,
|
||||||
|
/// Argument registers
|
||||||
|
pub arg: [Value; REG_COUNT],
|
||||||
|
/// Result registers
|
||||||
|
pub res: [Value; REG_COUNT],
|
||||||
|
/// General purpose registers
|
||||||
|
pub gen: [Value; REG_COUNT],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StackFrame {
|
||||||
|
/// Create a new stack frame at a given address
|
||||||
|
pub fn new(addr: Addr, args: &[Value]) -> Self {
|
||||||
|
let mut sf = StackFrame::default();
|
||||||
|
sf.pc = addr;
|
||||||
|
for n in 0..(args.len().min(REG_COUNT)) {
|
||||||
|
sf.arg[n] = args[n];
|
||||||
|
}
|
||||||
|
sf
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read(&mut self, rd: Rd) -> Result<u64, Fault> {
|
||||||
|
match rd.d() {
|
||||||
|
SrcDisp::Immediate(v) => Ok(v),
|
||||||
|
SrcDisp::ImmediatePtr(_) => {
|
||||||
|
unimplemented!("Immediate ptr")
|
||||||
|
}
|
||||||
|
SrcDisp::Register(Register::Res(rn)) => {
|
||||||
|
if rn >= REG_COUNT as u8 {
|
||||||
|
Err(Fault::RegisterNotExist { reg: Register::Res(rn) }) // TODO use match after @ when stabilized https://github.com/rust-lang/rust/issues/65490
|
||||||
|
} else {
|
||||||
|
debug!("Rd {:?} = {}", rd, self.res[rn as usize]);
|
||||||
|
Ok(self.res[rn as usize])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SrcDisp::Register(Register::Arg(rn)) => {
|
||||||
|
if rn >= REG_COUNT as u8 {
|
||||||
|
Err(Fault::RegisterNotExist { reg: Register::Arg(rn) })
|
||||||
|
} else {
|
||||||
|
debug!("Rd {:?} = {}", rd, self.arg[rn as usize]);
|
||||||
|
Ok(self.arg[rn as usize])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SrcDisp::Register(Register::Gen(rn)) => {
|
||||||
|
if rn >= REG_COUNT as u8 {
|
||||||
|
Err(Fault::RegisterNotExist { reg: Register::Gen(rn) })
|
||||||
|
} else {
|
||||||
|
debug!("Rd {:?} = {}", rd, self.gen[rn as usize]);
|
||||||
|
Ok(self.gen[rn as usize])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SrcDisp::RegisterPtr(_) => {
|
||||||
|
unimplemented!("Register ptr")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write(&mut self, wr: Wr, val: Value) -> Result<(), Fault> {
|
||||||
|
debug!("WR {:?} := {}", wr, val);
|
||||||
|
|
||||||
|
match wr.d() {
|
||||||
|
DstDisp::ImmediatePtr(_) => {
|
||||||
|
unimplemented!("Immediate ptr")
|
||||||
|
}
|
||||||
|
DstDisp::Register(Register::Res(rn)) => {
|
||||||
|
if rn >= REG_COUNT as u8 {
|
||||||
|
Err(Fault::RegisterNotExist { reg: Register::Res(rn) }) // TODO use match after @ when stabilized https://github.com/rust-lang/rust/issues/65490
|
||||||
|
} else {
|
||||||
|
Err(Fault::RegisterNotWritable { reg: Register::Res(rn) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DstDisp::Register(Register::Arg(rn)) => {
|
||||||
|
if rn >= REG_COUNT as u8 {
|
||||||
|
Err(Fault::RegisterNotExist { reg: Register::Arg(rn) })
|
||||||
|
} else {
|
||||||
|
Err(Fault::RegisterNotWritable { reg: Register::Res(rn) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DstDisp::Register(Register::Gen(rn)) => {
|
||||||
|
if rn >= REG_COUNT as u8 {
|
||||||
|
Err(Fault::RegisterNotExist { reg: Register::Gen(rn) })
|
||||||
|
} else {
|
||||||
|
self.gen[rn as usize] = val;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DstDisp::RegisterPtr(_) => {
|
||||||
|
unimplemented!("Register ptr")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
#[macro_use] extern crate log;
|
||||||
|
|
||||||
|
pub mod run_thread;
|
||||||
|
pub mod mlock;
|
||||||
|
pub mod sparse;
|
||||||
|
pub mod fault;
|
||||||
|
pub mod span;
|
||||||
|
pub mod exec;
|
||||||
|
pub mod frame;
|
||||||
|
pub mod program;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
#[test]
|
||||||
|
fn it_works() {
|
||||||
|
assert_eq!(2 + 2, 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
use std::sync::atomic::{AtomicU32, Ordering};
|
||||||
|
use std::fmt;
|
||||||
|
use std::fmt::Formatter;
|
||||||
|
use crate::run_thread::ThreadToken;
|
||||||
|
use crate::fault::Fault;
|
||||||
|
use crate::span::MemorySpan;
|
||||||
|
|
||||||
|
/// Records memory claims and protects from illegal access
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
struct MemoryGuard {
|
||||||
|
claims: Vec<Claim>,
|
||||||
|
counter: AtomicU32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Eq, PartialEq, Debug, Ord, PartialOrd)]
|
||||||
|
pub struct ClaimId(pub u32);
|
||||||
|
|
||||||
|
impl fmt::Display for ClaimId {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
struct Claim {
|
||||||
|
owner: ThreadToken,
|
||||||
|
span: MemorySpan,
|
||||||
|
id: ClaimId,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MemoryGuard {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Default::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Claim a memory area
|
||||||
|
pub fn claim(&mut self, owner: ThreadToken, span: MemorySpan) -> Result<ClaimId, Fault> {
|
||||||
|
// naive
|
||||||
|
for claim in &self.claims {
|
||||||
|
if claim.span.intersects(span) && claim.owner != owner {
|
||||||
|
return Err(Fault::MemoryLocked {
|
||||||
|
area: claim.span,
|
||||||
|
owner: claim.owner,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let id = self.next_id();
|
||||||
|
|
||||||
|
self.claims.push(Claim {
|
||||||
|
id,
|
||||||
|
owner,
|
||||||
|
span,
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a unique claim ID and increment the counter
|
||||||
|
pub fn next_id(&self) -> ClaimId {
|
||||||
|
ClaimId(self.counter.fetch_and(1, Ordering::Relaxed))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the next claim ID (ID is incremented after calling "next").
|
||||||
|
/// May be used for release_owned_after()
|
||||||
|
pub fn epoch(&self) -> ClaimId {
|
||||||
|
ClaimId(self.counter.load(Ordering::Relaxed))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Release a claim by claim ID
|
||||||
|
pub fn release(&mut self, owner: ThreadToken, claim: ClaimId) -> Result<(), Fault> {
|
||||||
|
match self.claims.iter().position(|c| c.id == claim && c.owner == owner) {
|
||||||
|
Some(pos) => {
|
||||||
|
self.claims.swap_remove(pos);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
None => Err(Fault::ClaimNotExist { claim, owner }),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Release all owned by a thread (thread ends)
|
||||||
|
pub fn release_owned(&mut self, owner: ThreadToken) {
|
||||||
|
self.claims.retain(|c| c.owner != owner);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Release all owned by a thread, with claim ID >= a given value
|
||||||
|
/// (return from a subroutine)
|
||||||
|
pub fn release_owned_after(&mut self, owner: ThreadToken, epoch : ClaimId) {
|
||||||
|
self.claims.retain(|c| c.owner != owner || c.id >= epoch);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
use asm::instr::Op;
|
||||||
|
use asm::data::literal::Addr;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Program {
|
||||||
|
ops: Vec<Op>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Program {
|
||||||
|
pub fn new(ops : Vec<Op>) -> Self {
|
||||||
|
Self {
|
||||||
|
ops,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read(&self, addr: Addr) -> &Op {
|
||||||
|
if addr.0 >= self.ops.len() as u64 {
|
||||||
|
&Op::Nop
|
||||||
|
} else {
|
||||||
|
&self.ops[addr.0 as usize]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
use std::time::Duration;
|
||||||
|
use std::thread::JoinHandle;
|
||||||
|
|
||||||
|
use asm::data::literal::{Addr, Value};
|
||||||
|
use asm::instr::Op;
|
||||||
|
use crate::exec;
|
||||||
|
use asm::data::{Rd, SrcDisp, reg::Register, Wr, DstDisp};
|
||||||
|
use crate::fault::Fault;
|
||||||
|
use crate::frame::StackFrame;
|
||||||
|
use crate::program::Program;
|
||||||
|
use crate::exec::EvalRes;
|
||||||
|
|
||||||
|
const CYCLE_TIME : Duration = Duration::from_millis(100);
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Eq, PartialEq, Debug, Ord, PartialOrd)]
|
||||||
|
pub struct ThreadToken(pub u32);
|
||||||
|
|
||||||
|
pub struct RunThread {
|
||||||
|
/// Thread ID
|
||||||
|
pub id: ThreadToken,
|
||||||
|
/// Active stack frame
|
||||||
|
pub frame: StackFrame,
|
||||||
|
/// Call stack
|
||||||
|
pub call_stack: Vec<StackFrame>,
|
||||||
|
/// Program to run
|
||||||
|
pub program: Program,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RunThread {
|
||||||
|
pub fn new(id: ThreadToken, program: Program, pc: Addr, args: &[u64]) -> Self {
|
||||||
|
let sf = StackFrame::new(pc, args);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
id,
|
||||||
|
frame: sf,
|
||||||
|
call_stack: vec![],
|
||||||
|
program,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn start(self) -> JoinHandle<()> {
|
||||||
|
std::thread::spawn(move || {
|
||||||
|
self.run();
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(mut self) {
|
||||||
|
'run: loop {
|
||||||
|
match self.eval_op() {
|
||||||
|
Ok(EvalRes {
|
||||||
|
cycles, advance
|
||||||
|
}) => {
|
||||||
|
std::thread::sleep(CYCLE_TIME * (cycles as u32));
|
||||||
|
debug!("PC += {}", advance);
|
||||||
|
self.frame.pc.advance(advance);
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("Fault: {:?}", e);
|
||||||
|
break 'run;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
|
||||||
|
|
||||||
|
use asm::data::literal::Addr;
|
||||||
|
|
||||||
|
#[derive(Debug,Clone,Copy)]
|
||||||
|
pub struct MemorySpan {
|
||||||
|
addr: usize,
|
||||||
|
len: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MemorySpan {
|
||||||
|
pub fn new(addr: Addr, len: usize) -> MemorySpan {
|
||||||
|
if len == 0 {
|
||||||
|
panic!("Cannot create empty span!");
|
||||||
|
}
|
||||||
|
|
||||||
|
Self {
|
||||||
|
addr: addr.0 as usize,
|
||||||
|
len,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get start address
|
||||||
|
pub fn start(&self) -> Addr {
|
||||||
|
Addr(self.addr as u64)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get end address (last included byte)
|
||||||
|
pub fn last(&self) -> Addr {
|
||||||
|
Addr((self.addr + self.len - 1) as u64)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if this intersects another span
|
||||||
|
pub fn intersects(&self, other: MemorySpan) -> bool {
|
||||||
|
!(
|
||||||
|
self.last() < other.start()
|
||||||
|
|| self.start() > other.last()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if this is a strict subset of another span
|
||||||
|
pub fn inside(&self, other: MemorySpan) -> bool {
|
||||||
|
self.start() >= other.start()
|
||||||
|
&& self.last() <= other.last()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,317 @@
|
|||||||
|
use std::io::Write;
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct SparseBuffer {
|
||||||
|
chunks: Vec<(usize, Vec<u8>)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SparseBuffer {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Default::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write(&mut self, addr: usize, bytes: &[u8]) {
|
||||||
|
if self.chunks.is_empty() {
|
||||||
|
self.chunks.push((addr, Vec::from(bytes)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum InsertLoc {
|
||||||
|
Head,
|
||||||
|
Append(usize),
|
||||||
|
Tail,
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut loc = None;
|
||||||
|
for (i, (at, _ch)) in self.chunks.iter().enumerate() {
|
||||||
|
if *at > addr {
|
||||||
|
if i == 0 {
|
||||||
|
loc = Some(InsertLoc::Head);
|
||||||
|
} else {
|
||||||
|
loc = Some(InsertLoc::Append(i - 1));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let loc = if let Some(l) = loc {
|
||||||
|
l
|
||||||
|
} else {
|
||||||
|
InsertLoc::Tail
|
||||||
|
};
|
||||||
|
|
||||||
|
match loc {
|
||||||
|
InsertLoc::Head => self.write_head(addr, bytes),
|
||||||
|
InsertLoc::Append(i) => self.write_after(i, addr, bytes),
|
||||||
|
InsertLoc::Tail => self.write_tail(addr, bytes),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_head(&mut self, addr: usize, bytes: &[u8]) {
|
||||||
|
self.chunks.insert(0, (addr, vec![]));
|
||||||
|
self.write_after(0, addr, bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_after(&mut self, index: usize, addr: usize, bytes: &[u8]) {
|
||||||
|
if index == self.chunks.len() - 1 {
|
||||||
|
self.write_tail(addr, bytes);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let end_addr = addr + bytes.len();
|
||||||
|
|
||||||
|
// This means we have at least two chunks.
|
||||||
|
// The written area can:
|
||||||
|
// - fit within the chunk
|
||||||
|
// - extend the chunk, but still end before the second's start address
|
||||||
|
// - extend the chunk, overflowing into one or more following chunks
|
||||||
|
|
||||||
|
let (a, slice) = self.chunks.iter().nth(index + 1).unwrap();
|
||||||
|
let second_start = *a;
|
||||||
|
let _second_len = slice.len();
|
||||||
|
|
||||||
|
let (a, slice) = self.chunks.get_mut(index).unwrap();
|
||||||
|
let first_addr = *a;
|
||||||
|
let first_len = slice.len();
|
||||||
|
|
||||||
|
if end_addr <= first_addr + first_len {
|
||||||
|
(&mut slice[(addr - first_addr) as usize..]).write(bytes);
|
||||||
|
} else if end_addr <= second_start {
|
||||||
|
slice.truncate((addr - first_addr) as usize);
|
||||||
|
slice.extend_from_slice(bytes);
|
||||||
|
} else {
|
||||||
|
// overflows into one or more chunks
|
||||||
|
slice.truncate((addr - first_addr) as usize);
|
||||||
|
slice.extend_from_slice(&bytes[..(second_start - addr) as usize]);
|
||||||
|
|
||||||
|
// recurse
|
||||||
|
self.write_after(index + 1, second_start, &bytes[(second_start - addr) as usize..]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_tail(&mut self, addr: usize, bytes: &[u8]) {
|
||||||
|
let (a, slice) = self.chunks.last_mut().unwrap();
|
||||||
|
|
||||||
|
let last_addr = *a;
|
||||||
|
let last_len = slice.len();
|
||||||
|
let end_addr = addr + bytes.len();
|
||||||
|
|
||||||
|
assert!(addr >= last_addr);
|
||||||
|
|
||||||
|
if end_addr <= last_addr + last_len {
|
||||||
|
// Entirely contained within the last chunk
|
||||||
|
(&mut slice[((addr - last_addr) as usize)..]).write(bytes);
|
||||||
|
} else if addr > last_addr + last_len {
|
||||||
|
self.chunks.push((addr, Vec::from(bytes)));
|
||||||
|
} else {
|
||||||
|
// The write slice starts within the last chunk, but extends past its end.
|
||||||
|
slice.truncate((addr - last_addr) as usize);
|
||||||
|
slice.extend_from_slice(bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn coalesce(&mut self) {
|
||||||
|
let mut prev = Option::<(usize, Vec<u8>)>::None;
|
||||||
|
let mut merged = vec![];
|
||||||
|
for chunk in self.chunks.drain(..) {
|
||||||
|
if let Some(prevchunk) = &mut prev {
|
||||||
|
if chunk.0 == prevchunk.0 + prevchunk.1.len() {
|
||||||
|
prevchunk.1.extend_from_slice(&chunk.1[..]);
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
merged.push(prev.take().unwrap());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
prev = Some(chunk);
|
||||||
|
}
|
||||||
|
if let Some(prevchunk) = prev {
|
||||||
|
merged.push(prevchunk);
|
||||||
|
}
|
||||||
|
self.chunks = merged;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::SparseBuffer;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_empty() {
|
||||||
|
let mut sparse = SparseBuffer::new();
|
||||||
|
|
||||||
|
sparse.write(100, &[0, 1, 2, 3, 4]);
|
||||||
|
assert_eq!(vec![(100, vec![0, 1, 2, 3, 4])], sparse.chunks);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_append_sparse() {
|
||||||
|
let mut sparse = SparseBuffer::new();
|
||||||
|
|
||||||
|
sparse.write(0, &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
|
||||||
|
sparse.write(100, &[7, 8, 9, 10]);
|
||||||
|
assert_eq!(vec![
|
||||||
|
(0, vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]),
|
||||||
|
(100, vec![7, 8, 9, 10])
|
||||||
|
], sparse.chunks);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_append_inside_last() {
|
||||||
|
let mut sparse = SparseBuffer::new();
|
||||||
|
|
||||||
|
sparse.write(0, &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
|
||||||
|
sparse.write(7, &[70, 80, 90]);
|
||||||
|
assert_eq!(vec![
|
||||||
|
(0, vec![0, 1, 2, 3, 4, 5, 6, 70, 80, 90, 10]),
|
||||||
|
], sparse.chunks);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_append_extend_last() {
|
||||||
|
let mut sparse = SparseBuffer::new();
|
||||||
|
|
||||||
|
sparse.write(0, &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
|
||||||
|
sparse.write(5, &[50, 60, 70, 80, 90, 100, 110, 120, 130, 140]);
|
||||||
|
assert_eq!(vec![(0, vec![0, 1, 2, 3, 4, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140])], sparse.chunks);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_prepend_sparse() {
|
||||||
|
let mut sparse = SparseBuffer::new();
|
||||||
|
|
||||||
|
sparse.write(100, &[0, 1, 2, 3, 4]);
|
||||||
|
sparse.write(10, &[70, 80, 90, 100]);
|
||||||
|
|
||||||
|
assert_eq!(vec![
|
||||||
|
(10, vec![70, 80, 90, 100]),
|
||||||
|
(100, vec![0, 1, 2, 3, 4])
|
||||||
|
], sparse.chunks);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_within_first() {
|
||||||
|
let mut sparse = SparseBuffer::new();
|
||||||
|
|
||||||
|
sparse.write(0, &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
|
||||||
|
sparse.write(4, &[40, 50, 60]);
|
||||||
|
|
||||||
|
assert_eq!(vec![
|
||||||
|
(0, vec![0, 1, 2, 3, 40, 50, 60, 7, 8, 9]),
|
||||||
|
], sparse.chunks);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_grows_first() {
|
||||||
|
let mut sparse = SparseBuffer::new();
|
||||||
|
|
||||||
|
sparse.write(0, &[0, 1, 2, 3, 4, 5]);
|
||||||
|
sparse.write(10, &[10, 11, 12]);
|
||||||
|
sparse.write(4, &[40, 50, 60]);
|
||||||
|
|
||||||
|
assert_eq!(vec![
|
||||||
|
(0, vec![0, 1, 2, 3, 40, 50, 60]),
|
||||||
|
(10, vec![10, 11, 12]),
|
||||||
|
], sparse.chunks);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_grows_first2() {
|
||||||
|
let mut sparse = SparseBuffer::new();
|
||||||
|
|
||||||
|
sparse.write(0, &[0, 1, 2, 3, 4, 5]);
|
||||||
|
sparse.write(10, &[10, 11, 12]);
|
||||||
|
sparse.write(4, &[40, 50, 60, 70, 80, 90]);
|
||||||
|
|
||||||
|
assert_eq!(vec![
|
||||||
|
(0, vec![0, 1, 2, 3, 40, 50, 60, 70, 80, 90]),
|
||||||
|
(10, vec![10, 11, 12]),
|
||||||
|
], sparse.chunks);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_overflow_first() {
|
||||||
|
let mut sparse = SparseBuffer::new();
|
||||||
|
|
||||||
|
sparse.write(0, &[0, 1, 2, 3, 4, 5]);
|
||||||
|
sparse.write(10, &[10, 11, 12, 13, 14, 15]);
|
||||||
|
sparse.write(4, &[40, 50, 60, 70, 80, 90, 100, 110, 120]);
|
||||||
|
|
||||||
|
assert_eq!(vec![
|
||||||
|
(0, vec![0, 1, 2, 3, 40, 50, 60, 70, 80, 90]),
|
||||||
|
(10, vec![100, 110, 120, 13, 14, 15]),
|
||||||
|
], sparse.chunks);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_join_tail() {
|
||||||
|
let mut sparse = SparseBuffer::new();
|
||||||
|
|
||||||
|
sparse.write(0, &[0, 1, 2]);
|
||||||
|
sparse.write(3, &[3, 4, 5]);
|
||||||
|
sparse.write(6, &[6, 7, 8]);
|
||||||
|
|
||||||
|
assert_eq!(vec![
|
||||||
|
(0, vec![0, 1, 2, 3, 4, 5, 6, 7, 8]),
|
||||||
|
], sparse.chunks);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_overflow_multiple() {
|
||||||
|
let mut sparse = SparseBuffer::new();
|
||||||
|
|
||||||
|
sparse.write(0, &[0, 1, 2]);
|
||||||
|
sparse.write(4, &[4, 5, 6]);
|
||||||
|
sparse.write(8, &[8, 9, 10]);
|
||||||
|
|
||||||
|
assert_eq!(vec![
|
||||||
|
(0, vec![0, 1, 2]),
|
||||||
|
(4, vec![4, 5, 6]),
|
||||||
|
(8, vec![8, 9, 10]),
|
||||||
|
], sparse.chunks);
|
||||||
|
|
||||||
|
sparse.write(2, &[20, 30, 40, 50, 60, 70, 80, 90]);
|
||||||
|
|
||||||
|
assert_eq!(vec![
|
||||||
|
(0, vec![0, 1, 20, 30]),
|
||||||
|
(4, vec![40, 50, 60, 70]),
|
||||||
|
(8, vec![80, 90, 10]),
|
||||||
|
], sparse.chunks);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_overflow_multiple2() {
|
||||||
|
let mut sparse = SparseBuffer::new();
|
||||||
|
|
||||||
|
sparse.write(0, &[0, 1, 2]);
|
||||||
|
sparse.write(4, &[4, 5, 6]);
|
||||||
|
sparse.write(8, &[8, 9, 10]);
|
||||||
|
|
||||||
|
assert_eq!(vec![
|
||||||
|
(0, vec![0, 1, 2]),
|
||||||
|
(4, vec![4, 5, 6]),
|
||||||
|
(8, vec![8, 9, 10]),
|
||||||
|
], sparse.chunks);
|
||||||
|
sparse.coalesce();
|
||||||
|
|
||||||
|
// no change, as expected
|
||||||
|
assert_eq!(vec![
|
||||||
|
(0, vec![0, 1, 2]),
|
||||||
|
(4, vec![4, 5, 6]),
|
||||||
|
(8, vec![8, 9, 10]),
|
||||||
|
], sparse.chunks);
|
||||||
|
|
||||||
|
sparse.write(2, &[20, 30, 40, 50, 60, 70, 80, 90, 100, 110]);
|
||||||
|
|
||||||
|
assert_eq!(vec![
|
||||||
|
(0, vec![0, 1, 20, 30]),
|
||||||
|
(4, vec![40, 50, 60, 70]),
|
||||||
|
(8, vec![80, 90, 100, 110]),
|
||||||
|
], sparse.chunks);
|
||||||
|
|
||||||
|
// join contiguous
|
||||||
|
sparse.coalesce();
|
||||||
|
assert_eq!(vec![
|
||||||
|
(0, vec![0, 1, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110]),
|
||||||
|
], sparse.chunks);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user