diff --git a/src/isa/alu/bytecode.rs b/src/isa/alu/bytecode.rs index 5cbf49e..db68e23 100644 --- a/src/isa/alu/bytecode.rs +++ b/src/isa/alu/bytecode.rs @@ -25,18 +25,21 @@ use core::ops::RangeInclusive; use super::{CtrlInstr, MaybeU128, RegInstr}; -use crate::core::{SiteId, A}; +use crate::core::{RegA, SiteId, A}; use crate::isa::bytecode::CodeEofError; +#[cfg(feature = "GFA")] +use crate::isa::FieldInstr; use crate::isa::{Bytecode, BytecodeRead, BytecodeWrite, Instr, InstructionSet, ReservedInstr}; use crate::Site; impl + Bytecode> Bytecode for Instr { - fn op_range() -> RangeInclusive { todo!() } + fn op_range() -> RangeInclusive { 0..=0xFF } fn opcode_byte(&self) -> u8 { match self { Instr::Ctrl(instr) => instr.opcode_byte(), Instr::Reg(instr) => Bytecode::::opcode_byte(instr), + #[cfg(feature = "GFA")] Instr::GFqA(instr) => Bytecode::::opcode_byte(instr), Instr::Reserved(instr) => Bytecode::::opcode_byte(instr), Instr::Ext(instr) => instr.opcode_byte(), @@ -48,6 +51,7 @@ impl + Bytecode> Bytecode for Instr< match self { Instr::Ctrl(instr) => instr.encode_operands(writer), Instr::Reg(instr) => instr.encode_operands(writer), + #[cfg(feature = "GFA")] Instr::GFqA(instr) => instr.encode_operands(writer), Instr::Reserved(instr) => instr.encode_operands(writer), Instr::Ext(instr) => instr.encode_operands(writer), @@ -59,33 +63,87 @@ impl + Bytecode> Bytecode for Instr< Self: Sized, R: BytecodeRead, { - todo!() + match opcode { + op if CtrlInstr::::op_range().contains(&op) => { + CtrlInstr::::decode_operands(reader, op).map(Self::Ctrl) + } + op if >::op_range().contains(&op) => { + >::decode_operands(reader, op).map(Self::Reg) + } + #[cfg(feature = "GFA")] + op if >::op_range().contains(&op) => { + >::decode_operands(reader, op).map(Self::GFqA) + } + 0x80..=0xFF => Ext::decode_operands(reader, opcode).map(Self::Ext), + _ => ReservedInstr::decode_operands(reader, opcode).map(Self::Reserved), + } } } impl Bytecode for ReservedInstr { - fn op_range() -> RangeInclusive { todo!() } + fn op_range() -> RangeInclusive { 0..=0x7F } - fn opcode_byte(&self) -> u8 { todo!() } + fn opcode_byte(&self) -> u8 { self.0 } - fn encode_operands(&self, writer: &mut W) -> Result<(), W::Error> + fn encode_operands(&self, _writer: &mut W) -> Result<(), W::Error> where W: BytecodeWrite { - todo!() + Ok(()) } - fn decode_operands(reader: &mut R, opcode: u8) -> Result + fn decode_operands(_reader: &mut R, opcode: u8) -> Result where Self: Sized, R: BytecodeRead, { - todo!() + Ok(ReservedInstr(opcode)) } } +impl CtrlInstr { + const START: u8 = 0; + const END: u8 = Self::START + Self::STOP; + + const NOP: u8 = 0; + const NOCO: u8 = 1; + const CHK: u8 = 2; + const FAIL: u8 = 3; + const RSET: u8 = 4; + const JMP: u8 = 5; + const JIFNE: u8 = 6; + const JIFAIL: u8 = 7; + const SH: u8 = 8; + const SHNE: u8 = 9; + const SHFAIL: u8 = 10; + const EXEC: u8 = 11; + const FN: u8 = 12; + const CALL: u8 = 13; + const RET: u8 = 14; + const STOP: u8 = 15; +} + impl Bytecode for CtrlInstr { - fn op_range() -> RangeInclusive { todo!() } + fn op_range() -> RangeInclusive { Self::START..=Self::END } - fn opcode_byte(&self) -> u8 { todo!() } + fn opcode_byte(&self) -> u8 { + match self { + CtrlInstr::Nop => Self::NOP, + CtrlInstr::Chk => Self::CHK, + CtrlInstr::NotCo => Self::NOCO, + CtrlInstr::FailCk => Self::FAIL, + CtrlInstr::RsetCk => Self::RSET, + CtrlInstr::Jmp { .. } => Self::JMP, + CtrlInstr::JifCo { .. } => Self::JIFNE, + CtrlInstr::JifCk { .. } => Self::JIFAIL, + CtrlInstr::Sh { .. } => Self::SH, + CtrlInstr::ShNe { .. } => Self::SHNE, + CtrlInstr::ShFail { .. } => Self::SHFAIL, + CtrlInstr::Exec { .. } => Self::EXEC, + CtrlInstr::Fn { .. } => Self::FN, + CtrlInstr::Call { .. } => Self::CALL, + CtrlInstr::Ret => Self::RET, + CtrlInstr::Stop => Self::STOP, + } + } fn encode_operands(&self, writer: &mut W) -> Result<(), W::Error> where W: BytecodeWrite { @@ -101,8 +159,8 @@ impl Bytecode for CtrlInstr { CtrlInstr::Jmp { pos } | CtrlInstr::JifCo { pos } | CtrlInstr::JifCk { pos } | CtrlInstr::Fn { pos } => { writer.write_fixed(pos.to_le_bytes())? } - CtrlInstr::Shift { shift } | CtrlInstr::ShIfCo { shift } | CtrlInstr::ShIfCk { shift } => { - writer.write_byte(shift.to_le_bytes()[0])? + CtrlInstr::Sh { shift } | CtrlInstr::ShNe { shift } | CtrlInstr::ShFail { shift } => { + writer.write_fixed(shift.to_le_bytes())? } CtrlInstr::Call { site } | CtrlInstr::Exec { site } => { let site = Site::new(site.prog_id, site.offset); @@ -118,14 +176,83 @@ impl Bytecode for CtrlInstr { Self: Sized, R: BytecodeRead, { - todo!() + Ok(match opcode { + Self::NOP => Self::Nop, + Self::CHK => Self::Chk, + Self::FAIL => Self::FailCk, + Self::RSET => Self::RsetCk, + Self::NOCO => Self::NotCo, + Self::RET => Self::Ret, + Self::STOP => Self::Stop, + + Self::JMP => CtrlInstr::Jmp { + pos: reader.read_fixed(u16::from_le_bytes)?, + }, + Self::JIFNE => CtrlInstr::JifCo { + pos: reader.read_fixed(u16::from_le_bytes)?, + }, + Self::JIFAIL => CtrlInstr::JifCk { + pos: reader.read_fixed(u16::from_le_bytes)?, + }, + Self::FN => CtrlInstr::Fn { + pos: reader.read_fixed(u16::from_le_bytes)?, + }, + + Self::SH => CtrlInstr::Sh { + shift: reader.read_fixed(i8::from_le_bytes)?, + }, + Self::SHNE => CtrlInstr::ShNe { + shift: reader.read_fixed(i8::from_le_bytes)?, + }, + Self::SHFAIL => CtrlInstr::ShFail { + shift: reader.read_fixed(i8::from_le_bytes)?, + }, + + Self::CALL => { + let prog_id = reader.read_ref()?; + let offset = reader.read_word()?; + let site = Site::new(prog_id, offset); + CtrlInstr::Call { site } + } + Self::EXEC => { + let prog_id = reader.read_ref()?; + let offset = reader.read_word()?; + let site = Site::new(prog_id, offset); + CtrlInstr::Exec { site } + } + + _ => unreachable!(), + }) } } +impl RegInstr { + const START: u8 = 16; + const END: u8 = Self::START + Self::EQ; + + const CLR: u8 = 16; + const PUT: u8 = 17; + const PIF: u8 = 18; + const TEST: u8 = 19; + const CPY: u8 = 20; + const SWP: u8 = 21; + const EQ: u8 = 22; +} + impl Bytecode for RegInstr { - fn op_range() -> RangeInclusive { todo!() } + fn op_range() -> RangeInclusive { Self::START..=Self::END } - fn opcode_byte(&self) -> u8 { todo!() } + fn opcode_byte(&self) -> u8 { + match self { + RegInstr::Clr { .. } => Self::CLR, + RegInstr::Put { .. } => Self::PUT, + RegInstr::Pif { .. } => Self::PIF, + RegInstr::Test { .. } => Self::TEST, + RegInstr::Cpy { .. } => Self::CPY, + RegInstr::Swp { .. } => Self::SWP, + RegInstr::Eq { .. } => Self::EQ, + } + } fn encode_operands(&self, writer: &mut W) -> Result<(), W::Error> where W: BytecodeWrite { @@ -170,6 +297,49 @@ impl Bytecode for RegInstr { Self: Sized, R: BytecodeRead, { - todo!() + Ok(match opcode { + RegInstr::CLR => { + let dst = RegA::from(reader.read_byte()?); + RegInstr::Clr { dst } + } + RegInstr::PUT | RegInstr::PIF => { + let dst = RegA::from(reader.read_byte()?); + let val = match dst.a() { + A::A8 => reader.read_byte().map(|v| v as u128), + A::A16 => reader.read_word().map(|v| v as u128), + A::A32 => reader.read_fixed(u32::from_le_bytes).map(|v| v as u128), + A::A64 => reader.read_fixed(u64::from_le_bytes).map(|v| v as u128), + A::A128 => reader.read_fixed(u128::from_le_bytes), + } + .ok() + .into(); + + if opcode == RegInstr::PUT { + RegInstr::Put { dst, val } + } else { + RegInstr::Pif { dst, val } + } + } + RegInstr::TEST => { + let src = RegA::from(reader.read_byte()?); + RegInstr::Test { src } + } + RegInstr::CPY => { + let dst = RegA::from(reader.read_byte()?); + let src = RegA::from(reader.read_byte()?); + RegInstr::Cpy { dst, src } + } + RegInstr::SWP => { + let src_dst1 = RegA::from(reader.read_byte()?); + let src_dst2 = RegA::from(reader.read_byte()?); + RegInstr::Swp { src_dst1, src_dst2 } + } + RegInstr::EQ => { + let src1 = RegA::from(reader.read_byte()?); + let src2 = RegA::from(reader.read_byte()?); + RegInstr::Eq { src1, src2 } + } + _ => unreachable!(), + }) } } diff --git a/src/isa/alu/exec.rs b/src/isa/alu/exec.rs index 3cb1503..1d64858 100644 --- a/src/isa/alu/exec.rs +++ b/src/isa/alu/exec.rs @@ -146,15 +146,15 @@ impl Instruction for CtrlInstr { return ExecStep::Jump(pos); } } - CtrlInstr::Shift { shift } => { + CtrlInstr::Sh { shift } => { return shift_jump(shift); } - CtrlInstr::ShIfCo { shift } => { + CtrlInstr::ShNe { shift } => { if core.co() { return shift_jump(shift); } } - CtrlInstr::ShIfCk { shift } => { + CtrlInstr::ShFail { shift } => { if core.ck() == Status::Fail { return shift_jump(shift); } diff --git a/src/isa/alu/instr.rs b/src/isa/alu/instr.rs index e9980e8..f709c00 100644 --- a/src/isa/alu/instr.rs +++ b/src/isa/alu/instr.rs @@ -35,6 +35,15 @@ pub enum MaybeU128 { NoData, } +impl From> for MaybeU128 { + fn from(value: Option) -> Self { + match value { + None => MaybeU128::NoData, + Some(val) => MaybeU128::U128(val), + } + } +} + /// Control flow instructions. #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Display)] #[display(inner)] @@ -47,6 +56,10 @@ pub enum CtrlInstr { #[display("chk")] Chk, + /// Invert `co` register. + #[display("not co")] + NotCo, + /// Set `ck` register to a failed state. #[display("put ck, :fail")] FailCk, @@ -55,10 +68,6 @@ pub enum CtrlInstr { #[display("put ck, :ok")] RsetCk, - /// Invert `co` register. - #[display("not co")] - NotCo, - /// Jump to location (unconditionally). #[display("jmp {pos:04X}:h")] Jmp { pos: u16 }, @@ -73,15 +82,15 @@ pub enum CtrlInstr { /// Relative jump. #[display("jmp {shift:+03X}:h")] - Shift { shift: i8 }, + Sh { shift: i8 }, /// Relative jump if `co` is true. #[display("jif co, {shift:+03X}:h")] - ShIfCo { shift: i8 }, + ShNe { shift: i8 }, /// Relative jump if `ck` is in a failed state. #[display("jif ck, {shift:+03X}:h")] - ShIfCk { shift: i8 }, + ShFail { shift: i8 }, /// External jump. #[display("jmp {site}")] diff --git a/src/isa/arch.rs b/src/isa/arch.rs index c4bf37f..ffe46f9 100644 --- a/src/isa/arch.rs +++ b/src/isa/arch.rs @@ -26,7 +26,7 @@ use core::fmt::{Debug, Display}; use amplify::confinement::TinyOrdSet; use strict_encoding::stl::AlphaCapsNum; -use strict_encoding::RString; +use strict_encoding::{RString, StrictDumb}; #[cfg(feature = "GFA")] use super::FieldInstr; @@ -51,11 +51,15 @@ macro_rules! isa { #[derive(Wrapper, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, From)] #[wrapper(Deref, Display, FromStr)] -#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] -#[strict_type(lib = LIB_NAME_ALUVM, dumb = { Self::from("DUMB") })] +#[derive(StrictType, StrictEncode, StrictDecode)] +#[strict_type(lib = LIB_NAME_ALUVM)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(transparent))] pub struct IsaId(RString); +impl StrictDumb for IsaId { + fn strict_dumb() -> Self { Self::from("DUMB") } +} + impl From<&'static str> for IsaId { fn from(id: &'static str) -> Self { Self(RString::from(id)) } } diff --git a/src/isa/gfa/exec.rs b/src/isa/gfa/exec.rs index 0905ec9..75ef5f1 100644 --- a/src/isa/gfa/exec.rs +++ b/src/isa/gfa/exec.rs @@ -29,32 +29,6 @@ use crate::core::{Reg, RegA, SiteId}; use crate::isa::{ExecStep, Instruction}; use crate::{Core, Site}; -macro_rules! A { - [$reg:ident @ $core:ident] => { - checked!($core.a($reg)) - }; - [$a:ident : $idx:ident @ $core:ident] => {{ - checked!($core.a(RegA::with($a, $idx.into()))) - }}; -} - -macro_rules! check { - ($condition:expr) => {{ - if !($condition) { - return ExecStep::NextFail; - } - }}; -} - -macro_rules! checked { - ($core:ident . $op:ident($($arg:expr),*)) => {{ - let Some(val) = $core.$op( $( $arg ),* ) else { - return ExecStep::NextFail; - }; - val - }}; -} - impl Instruction for FieldInstr { type Context<'ctx> = (); diff --git a/src/isa/macros.rs b/src/isa/macros.rs new file mode 100644 index 0000000..908d459 --- /dev/null +++ b/src/isa/macros.rs @@ -0,0 +1,41 @@ +// Reference rust implementation of AluVM (arithmetic logic unit virtual machine). +// To find more on AluVM please check +// +// SPDX-License-Identifier: Apache-2.0 +// +// Written in 2021-2024 by +// Dr Maxim Orlovsky +// +// Copyright (C) 2021-2024 UBIDECO Labs, +// Laboratories for Distributed and Cognitive Computing, Switzerland. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +macro_rules! A { + [$reg:ident @ $core:ident] => { + checked!($core.a($reg)) + }; + [$a:ident : $idx:ident @ $core:ident] => {{ + checked!($core.a(RegA::with($a, $idx.into()))) + }}; +} + +macro_rules! checked { + ($core:ident . $op:ident($($arg:expr),*)) => {{ + let Some(val) = $core.$op( $( $arg ),* ) else { + return ExecStep::NextFail; + }; + val + }}; +} diff --git a/src/isa/mod.rs b/src/isa/mod.rs index 6b40e07..5531975 100644 --- a/src/isa/mod.rs +++ b/src/isa/mod.rs @@ -24,6 +24,8 @@ //! AluVM instruction set architecture. +#[macro_use] +mod macros; mod instr; mod bytecode; mod arch;