WORKING ASM FUNCTION CALLS

This commit is contained in:
2024-10-12 02:24:48 -04:00
parent de79445ede
commit cf9f3469ae
14 changed files with 376 additions and 287 deletions

View File

@@ -48,6 +48,7 @@ pub struct SectionHeader {
entry_size: u64, entry_size: u64,
} }
// this is currently specialized for riscv64; obviously add params later
pub fn create(program: Vec<u8>, start_offset: u64) -> Vec<u8> { pub fn create(program: Vec<u8>, start_offset: u64) -> Vec<u8> {
let addr_start = 0x1000; let addr_start = 0x1000;
let page_size = 0x1000; let page_size = 0x1000;

View File

@@ -5,13 +5,14 @@ use std::{
process::Command, process::Command,
}; };
mod riscv64; mod elf;
mod program; mod program;
mod riscv64;
mod target; mod target;
pub fn main() { pub fn main() {
use std::io::prelude::*; use std::io::prelude::*;
let dir = Path::new("build"); let dir = Path::new("./build");
create_dir_all(dir).expect("Failed to create or confirm build directory"); create_dir_all(dir).expect("Failed to create or confirm build directory");
let name = Path::new("test"); let name = Path::new("test");
let path = dir.join(name); let path = dir.join(name);
@@ -26,9 +27,35 @@ pub fn main() {
file.write_all(&riscv64::gen()) file.write_all(&riscv64::gen())
.expect("Failed to write to file"); .expect("Failed to write to file");
file.sync_all().expect("Failed to sync file"); file.sync_all().expect("Failed to sync file");
if let Ok(mut process) = Command::new("qemu-riscv64").arg(path).spawn() { let mut p = Command::new("qemu-riscv64");
let run_gdb = std::env::args().nth(1).is_some_and(|a| a == "d");
let proc = if run_gdb {
p.arg("-g").arg("1234").arg(path).spawn()
} else {
p.arg(path).spawn()
};
if let Ok(mut process) = proc {
let mut print_exit = true;
if run_gdb {
match Command::new("gdb")
.arg("-q")
.arg("-ex")
.arg("target remote :1234")
.arg(path)
.spawn()
{
Ok(mut gdb) => {
gdb.wait().expect("xd");
}
Err(e) => {
print_exit = false;
println!("gdb error: {e:?}");
process.kill().expect("uh oh");
}
}
}
if let Ok(status) = process.wait() { if let Ok(status) = process.wait() {
if status.code().is_none_or(|c| c != 0) { if print_exit && status.code().is_none_or(|c| c != 0) {
println!("{}", status); println!("{}", status);
} }
} }
@@ -39,4 +66,3 @@ pub fn main() {
// riscv64-linux-gnu-gdb -q \ // riscv64-linux-gnu-gdb -q \
// -ex "target remote :1234" \ // -ex "target remote :1234" \
// test // test

View File

@@ -1,53 +1,35 @@
use std::collections::HashMap; use std::collections::HashMap;
pub struct Program {
data: Vec<u8>,
ro_map: HashMap<String, usize>,
}
impl Program {
pub fn new(data: HashMap<String, Vec<u8>>) -> Self {
let mut ro_data = Vec::new();
let mut ro_map = HashMap::new();
for (key, val) in data {
ro_map.insert(key, ro_data.len());
ro_data.extend(val);
}
Self {
data: ro_data,
ro_map,
}
}
}
pub fn create_program<I: Instr>( pub fn create_program<I: Instr>(
ro_data: HashMap<String, Vec<u8>>, ro_data: HashMap<String, Vec<u8>>,
functions: Vec<Function<I>>, functions: Vec<Function<I>>,
) -> Vec<u8> { ) -> (Vec<u8>, Option<u64>) {
let mut data = Vec::new(); let mut data = Vec::new();
let mut ro_map = HashMap::new(); let mut sym_map = HashMap::new();
for (key, val) in ro_data { for (key, val) in ro_data {
ro_map.insert(key, data.len()); sym_map.insert(key, data.len() as u64);
data.extend(val); data.extend(val);
} }
// let mut fn_map = HashMap::new(); let mut start = None;
for fun in functions { for fun in functions {
if fun.label == "_start" {
start = Some(data.len() as u64);
}
sym_map.insert(fun.label, data.len() as u64);
for i in fun.instructions { for i in fun.instructions {
data.extend(i.to_le_bytes()); let pos = data.len() as u64;
i.push(&mut data, &sym_map, pos);
} }
} }
data (data, start)
} }
pub struct Function<I: Instr> { pub struct Function<I: Instr> {
label: String, pub label: String,
instructions: Vec<I>, pub instructions: Vec<I>,
} }
pub trait Instr { pub trait Instr {
fn to_le_bytes(&self) -> impl IntoIterator<Item = u8>; fn push(&self, data: &mut Vec<u8>, ro_map: &HashMap<String, u64>, pos: u64) -> Option<String>;
} }
struct SymbolInstr {
i: usize
}

View File

@@ -1,2 +1,57 @@
pub enum Instruction { use crate::compiler::program::Instr;
use super::*;
pub enum AsmInstruction {
Addi(Reg, Reg, i32),
La(Reg, String),
Jal(Reg, i32),
Jala(String),
Ja(String),
Ret,
Ecall,
}
impl Instr for AsmInstruction {
fn push(
&self,
data: &mut Vec<u8>,
sym_map: &std::collections::HashMap<String, u64>,
pos: u64,
) -> Option<String> {
match self {
Self::Addi(dest, src, imm) => {
data.extend(addi(*dest, *src, BitsI32::new(*imm)).to_le_bytes());
}
Self::La(dest, sym) => {
if let Some(addr) = sym_map.get(sym) {
let offset = *addr as i32 - pos as i32;
data.extend(auipc(*dest, BitsI32::new(0)).to_le_bytes());
data.extend(addi(*dest, *dest, BitsI32::new(offset)).to_le_bytes());
} else {
return Some(sym.to_string());
}
}
Self::Jal(dest, offset) => data.extend(jal(*dest, BitsI32::new(*offset)).to_le_bytes()),
Self::Ja(sym) => {
if let Some(addr) = sym_map.get(sym) {
let offset = *addr as i32 - pos as i32;
data.extend(j(BitsI32::new(offset)).to_le_bytes());
} else {
return Some(sym.to_string());
}
}
Self::Jala(sym) => {
if let Some(addr) = sym_map.get(sym) {
let offset = *addr as i32 - pos as i32;
data.extend(jal(ra, BitsI32::new(offset)).to_le_bytes());
} else {
return Some(sym.to_string());
}
}
Self::Ret => data.extend(ret().to_le_bytes()),
Self::Ecall => data.extend(ecall().to_le_bytes()),
}
None
}
} }

View File

@@ -0,0 +1,78 @@
use crate::util::Bits32;
use super::Reg;
pub struct Instruction(u32);
use Instruction as I;
impl Instruction {
pub fn to_le_bytes(&self) -> impl IntoIterator<Item = u8> {
self.0.to_le_bytes().into_iter()
}
pub fn to_be_bytes(&self) -> impl IntoIterator<Item = u8> {
self.0.to_be_bytes().into_iter()
}
}
pub type Funct3 = Bits32<2, 0>;
pub const fn r_type(
funct7: u32,
rs2: Reg,
rs1: Reg,
funct3: Bits32<2, 0>,
rd: Reg,
opcode: u32,
) -> I {
I((funct7 << 25)
+ (rs2.val() << 20)
+ (rs1.val() << 15)
+ (funct3.val() << 12)
+ (rd.val() << 7)
+ opcode)
}
pub const fn i_type(imm: Bits32<11, 0>, rs1: Reg, funct: Funct3, rd: Reg, opcode: u32) -> I {
I((imm.val() << 20) + (rs1.val() << 15) + (funct.val() << 12) + (rd.val() << 7) + opcode)
}
pub const fn s_type(
rs2: Reg,
rs1: Reg,
funct3: Funct3,
imm: Bits32<11, 0>,
opcode: u32,
) -> I {
I((imm.bits(11, 5) << 25)
+ (rs2.val() << 20)
+ (rs1.val() << 15)
+ (funct3.val() << 12)
+ (imm.bits(4, 0) << 7)
+ opcode)
}
pub const fn b_type(
rs2: Reg,
rs1: Reg,
funct3: Funct3,
imm: Bits32<12, 1>,
opcode: u32,
) -> I {
I((imm.bit(12) << 31)
+ (imm.bits(10, 5) << 25)
+ (rs2.val() << 20)
+ (rs1.val() << 15)
+ (funct3.val() << 8)
+ (imm.bits(4, 1) << 8)
+ (imm.bit(11) << 7)
+ opcode)
}
pub const fn u_type(imm: Bits32<31, 12>, rd: Reg, opcode: u32) -> I {
I((imm.bits(31, 12) << 12) + (rd.val() << 7) + opcode)
}
pub const fn j_type(imm: Bits32<20, 1>, rd: Reg, opcode: u32) -> I {
I((imm.bit(20) << 31)
+ (imm.bits(10, 1) << 21)
+ (imm.bit(11) << 20)
+ (imm.bits(19, 12) << 12)
+ (rd.val() << 7)
+ opcode)
}

View File

@@ -0,0 +1,24 @@
use super::Funct3;
pub mod op {
use super::*;
pub const ADD : Funct3 = Funct3::new(0b000);
pub const SLL : Funct3 = Funct3::new(0b001);
pub const SLT : Funct3 = Funct3::new(0b010);
pub const SLTU: Funct3 = Funct3::new(0b011);
pub const XOR : Funct3 = Funct3::new(0b100);
pub const SR : Funct3 = Funct3::new(0b101);
pub const OR : Funct3 = Funct3::new(0b110);
pub const AND : Funct3 = Funct3::new(0b111);
}
pub mod width {
use super::*;
pub const B : Funct3 = Funct3::new(0b000);
pub const H : Funct3 = Funct3::new(0b001);
pub const W : Funct3 = Funct3::new(0b010);
pub const D : Funct3 = Funct3::new(0b011);
pub const BU: Funct3 = Funct3::new(0b100);
pub const HU: Funct3 = Funct3::new(0b101);
pub const WU: Funct3 = Funct3::new(0b110);
}

View File

@@ -1,58 +0,0 @@
use super::{Reg, OPCODE_MASK};
use crate::{compiler::program::Instr, util::{bit, bits, in_bit_range}};
pub struct Instruction(u32);
use Instruction as I;
impl Instr for Instruction {
fn to_le_bytes(&self) -> impl IntoIterator<Item = u8> {
self.0.to_le_bytes().into_iter()
}
}
pub const fn r_type(funct7: u32, rs2: Reg, rs1: Reg, funct3: u32, rd: Reg, opcode: u32) -> I {
I((funct7 << 25)
+ (rs2.val() << 20)
+ (rs1.val() << 15)
+ (funct3 << 12)
+ (rd.val() << 7)
+ opcode)
}
pub const fn i_type(imm: i32, rs1: Reg, funct3: u32, rd: Reg, opcode: u32) -> I {
debug_assert!(in_bit_range(imm, 11, 0));
I((bits(imm, 11, 0) << 20) + (rs1.val() << 15) + (funct3 << 12) + (rd.val() << 7) + opcode)
}
pub const fn s_type(rs2: Reg, rs1: Reg, funct3: u32, imm: i32, opcode: u32) -> I {
debug_assert!(in_bit_range(imm, 11, 0));
I((bits(imm, 11, 5) << 25)
+ (rs2.val() << 20)
+ (rs1.val() << 15)
+ (funct3 << 12)
+ (bits(imm, 4, 0) << 7)
+ opcode)
}
pub const fn b_type(rs2: Reg, rs1: Reg, funct3: u32, imm: i32, opcode: u32) -> I {
debug_assert!(in_bit_range(imm, 12, 1));
I((bit(imm, 12) << 31)
+ (bits(imm, 10, 5) << 25)
+ (rs2.val() << 20)
+ (rs1.val() << 15)
+ (funct3 << 8)
+ (bits(imm, 4, 1) << 8)
+ (bit(imm, 11) << 7)
+ opcode)
}
pub const fn u_type(imm: i32, rd: Reg, opcode: u32) -> I {
debug_assert!(in_bit_range(imm, 31, 12));
I((bits(imm, 31, 12) << 12) + (rd.val() << 7) + opcode)
}
pub const fn j_type(imm: i32, rd: Reg, opcode: u32) -> I {
debug_assert!(in_bit_range(imm, 20, 1));
I((bit(imm, 20) << 31)
+ (bits(imm, 10, 1) << 21)
+ (bit(imm, 11) << 20)
+ (bits(imm, 19, 12) << 12)
+ (rd.val() << 7)
+ opcode)
}

View File

@@ -1,20 +0,0 @@
pub mod op {
pub const ADD : u32 = 0b000;
pub const SLL : u32 = 0b001;
pub const SLT : u32 = 0b010;
pub const SLTU: u32 = 0b011;
pub const XOR : u32 = 0b100;
pub const SR : u32 = 0b101;
pub const OR : u32 = 0b110;
pub const AND : u32 = 0b111;
}
pub mod width {
pub const B : u32 = 0b000;
pub const H : u32 = 0b001;
pub const W : u32 = 0b010;
pub const D : u32 = 0b011;
pub const BU: u32 = 0b100;
pub const HU: u32 = 0b101;
pub const WU: u32 = 0b110;
}

View File

@@ -1,53 +0,0 @@
mod base;
mod reg;
mod opcode;
mod func;
use base::*;
pub use reg::*;
use Instruction as I;
use opcode::*;
use func::{op::*, width};
pub const fn ecall() -> I {
i_type(0, zero, 0, zero, SYSTEM)
}
pub const fn ebreak() -> I {
i_type(1, zero, 0, zero, SYSTEM)
}
pub const fn auipc(dest: Reg, imm: i32) -> I {
u_type(imm, dest, AUIPC)
}
pub const fn ld(dest: Reg, offset: i32, base: Reg) -> I {
i_type(offset, base, width::D, dest, LOAD)
}
pub const fn lw(dest: Reg, offset: i32, base: Reg) -> I {
i_type(offset, base, width::W, dest, LOAD)
}
pub const fn lb(dest: Reg, offset: i32, base: Reg) -> I {
i_type(offset, base, width::B, dest, LOAD)
}
pub const fn sb(src: Reg, offset: i32, base: Reg) -> I {
s_type(src, base, width::B, offset, STORE)
}
pub const fn sw(src: Reg, offset: i32, base: Reg) -> I {
s_type(src, base, width::W, offset, STORE)
}
pub const fn sd(src: Reg, offset: i32, base: Reg) -> I {
s_type(src, base, width::D, offset, STORE)
}
pub const fn addi(dest: Reg, src: Reg, imm: i32) -> I {
i_type(imm, src, ADD, dest, IMM_OP)
}
pub const fn jal(offset: i32, dest: Reg) -> I {
j_type(offset, dest, JAL)
}
// pseudo instructions that map to a single instruction
pub const fn j(offset: i32) -> I {
jal(offset, zero)
}
pub const fn mv(dest: Reg, src: Reg) -> I {
addi(dest, src, 0)
}

View File

@@ -1,33 +1,81 @@
use crate::compiler::program::Instr;
mod elf;
mod instruction;
mod asm; mod asm;
mod base;
mod funct;
mod opcode;
mod reg;
mod single;
use instruction::*; use std::collections::HashMap;
use super::{elf, program::{create_program, Function}};
use crate::util::BitsI32;
use base::*;
use funct::{op::*, width};
use opcode::*;
pub use reg::*;
use single::*;
pub fn gen() -> Vec<u8> { pub fn gen() -> Vec<u8> {
let mut program = Vec::new(); use asm::AsmInstruction as I;
// let mut program = Vec::new();
// let msg = b"Hello world!\n";
// program.extend(msg);
// program.resize(((program.len() - 1) / 4 + 1) * 4, 0);
// let start = program.len() as u64;
// let instructions = [
// auipc(t0, BitsI32::new(0)),
// addi(t0, t0, BitsI32::new(-(start as i32))),
// addi(a0, zero, BitsI32::new(1)),
// mv(a1, t0),
// addi(a2, zero, BitsI32::new(msg.len() as i32)),
// addi(a7, zero, BitsI32::new(64)),
// addi(t0, zero, const { BitsI32::new(-10) }),
// ecall(),
// // exit
// addi(a0, zero, BitsI32::new(0)),
// addi(a7, zero, BitsI32::new(93)),
// ecall(),
// j(BitsI32::new(0)),
// ];
// for i in instructions {
// program.extend(i.to_le_bytes());
// }
let msg = b"Hello world!\n"; let msg = b"Hello world!\n";
program.extend(msg); let msg2 = b"IT WORKS!!!!\n";
program.resize(((program.len() - 1) / 4 + 1) * 4, 0); let ro_data = HashMap::from([
let start = program.len() as u64; ("msg".to_string(), msg.to_vec()),
let instructions = [ ("msg2".to_string(), msg2.to_vec()),
auipc(t0, 0), ]);
addi(t0, t0, -(start as i32)), let functions = vec![
addi(a0, zero, 1), Function {
mv(a1, t0), label: "print_stuff".to_string(),
addi(a2, zero, msg.len() as i32), instructions: vec![
addi(a7, zero, 64), I::Addi(a0, zero, 1),
addi(t0, zero, 200), I::La(a1, "msg".to_string()),
ecall(), I::Addi(a2, zero, msg.len() as i32),
// exit I::Addi(a7, zero, 64),
addi(a0, zero, 0), I::Ecall,
addi(a7, zero, 93), I::Addi(a0, zero, 1),
ecall(), I::La(a1, "msg2".to_string()),
j(0), I::Addi(a2, zero, msg2.len() as i32),
I::Addi(a7, zero, 64),
I::Ecall,
I::Ret,
]
},
Function {
label: "_start".to_string(),
instructions: vec![
I::Jala("print_stuff".to_string()),
I::Ecall,
I::Addi(a0, zero, 0),
I::Addi(a7, zero, 93),
I::Ecall,
I::Jal(zero, 0),
]
},
]; ];
for i in instructions { let (program, start) = create_program(ro_data, functions);
program.extend(i.to_le_bytes()); elf::create(program, start.expect("no start!"))
}
elf::create(program, start)
} }

View File

@@ -7,3 +7,4 @@ pub const AUIPC : u32 = 0b0010111;
pub const IMM_OP: u32 = 0b0010011; pub const IMM_OP: u32 = 0b0010011;
pub const OP : u32 = 0b0110011; pub const OP : u32 = 0b0110011;
pub const JAL : u32 = 0b1101111; pub const JAL : u32 = 0b1101111;
pub const JALR : u32 = 0b1100111;

View File

@@ -1,5 +1,6 @@
#![allow(non_upper_case_globals)] #![allow(non_upper_case_globals)]
#[derive(Debug, Clone, Copy)]
pub struct Reg(u8); pub struct Reg(u8);
/// hard wired 0 /// hard wired 0

View File

@@ -0,0 +1,51 @@
use crate::util::{Bits32, BitsI32};
use super::*;
use Instruction as I;
pub const fn ecall() -> I {
i_type(Bits32::new(0), zero, Bits32::new(0), zero, SYSTEM)
}
pub const fn ebreak() -> I {
i_type(Bits32::new(1), zero, Bits32::new(0), zero, SYSTEM)
}
pub const fn auipc(dest: Reg, imm: BitsI32<31, 12>) -> I {
u_type(imm.to_u(), dest, AUIPC)
}
pub const fn ld(dest: Reg, offset: BitsI32<11, 0>, base: Reg) -> I {
i_type(offset.to_u(), base, width::D, dest, LOAD)
}
pub const fn lw(dest: Reg, offset: BitsI32<11, 0>, base: Reg) -> I {
i_type(offset.to_u(), base, width::W, dest, LOAD)
}
pub const fn lb(dest: Reg, offset: BitsI32<11, 0>, base: Reg) -> I {
i_type(offset.to_u(), base, width::B, dest, LOAD)
}
pub const fn sb(src: Reg, offset: BitsI32<11, 0>, base: Reg) -> I {
s_type(src, base, width::B, offset.to_u(), STORE)
}
pub const fn sw(src: Reg, offset: BitsI32<11, 0>, base: Reg) -> I {
s_type(src, base, width::W, offset.to_u(), STORE)
}
pub const fn sd(src: Reg, offset: BitsI32<11, 0>, base: Reg) -> I {
s_type(src, base, width::D, offset.to_u(), STORE)
}
pub const fn addi(dest: Reg, src: Reg, imm: BitsI32<11, 0>) -> I {
i_type(imm.to_u(), src, ADD, dest, IMM_OP)
}
pub const fn jal(dest: Reg, offset: BitsI32<20, 1>) -> I {
j_type(offset.to_u(), dest, JAL)
}
pub const fn jalr(dest: Reg, offset: BitsI32<11, 0>, base: Reg) -> I {
i_type(offset.to_u(), base, Bits32::new(0), dest, JALR)
}
// pseudoinstructions that map to a single instruction
pub const fn j(offset: BitsI32<20, 1>) -> I {
jal(zero, offset)
}
pub const fn ret() -> I {
jalr(zero, BitsI32::new(0), ra)
}

View File

@@ -2,74 +2,59 @@ pub const fn u(x: i32) -> u32 {
unsafe { std::mem::transmute(x) } unsafe { std::mem::transmute(x) }
} }
pub const fn low_mask(high: u32, low: u32) -> u32 { pub const fn base_mask(len: u8) -> u32 {
(2u32.unbounded_shl(high - low)).wrapping_sub(1) (2 << len) - 1
} }
pub const fn mask(high: u32, low: u32) -> u32 { pub const fn mask(h: u8, l: u8) -> u32 {
low_mask(high, low).unbounded_shl(low) base_mask(h - l) << l
} }
pub const fn bits(x: i32, high: u32, low: u32) -> u32 { #[derive(Debug, Clone, Copy)]
let x = u(x); pub struct Bits32<const H: u8, const L: u8>(u32);
x.unbounded_shr(low) & low_mask(high, low) #[derive(Debug, Clone, Copy)]
pub struct BitsI32<const H: u8, const L: u8>(u32);
impl<const H: u8, const L: u8> Bits32<H, L> {
pub const fn new(val: u32) -> Self {
let lsh = 31 - H;
let rsh = lsh + L;
debug_assert!(((val << lsh) >> rsh) == (val >> L));
Self(val)
}
pub const fn val(&self) -> u32 {
self.0
}
pub const fn bit(&self, i: u8) -> u32 {
(self.0 >> i) & 1
}
pub const fn bits(&self, h: u8, l: u8) -> u32 {
(self.0 >> l) & base_mask(h - l)
}
} }
pub const fn bit(x: i32, i: u32) -> u32 { impl<const H: u8, const L: u8> BitsI32<H, L> {
let x = u(x); pub const fn new(val: i32) -> Self {
x.unbounded_shr(i) & 2u32 << i let lsh = 31 - H;
let rsh = lsh + L;
assert!(((val << lsh) >> rsh) == (val >> L));
Self(u(val) & mask(H, L))
} }
pub const fn tryy(val: i32) -> Option<Self> {
pub const fn in_bit_range(x: i32, high: u32, low: u32) -> bool { let lsh = 31 - H;
if x < 0 { let rsh = lsh + L;
if high == low { if ((val << lsh) >> rsh) == (val >> L) {
return false; Some(Self(u(val) & mask(H, L)))
}
(bits(x, high - 1, low) | !mask(high - 1, low)) == u(x)
} else { } else {
bits(x, high, low) << low == u(x) None
}
}
pub const fn to_u(self) -> Bits32<H, L> {
Bits32(self.0)
} }
} }
// use std::ops::{Add, Shl, Shr}; // I hate (love) rust https://github.com/rust-lang/rust-project-goals/issues/106
//
// pub const fn u(x: i32) -> u32 {
// unsafe { std::mem::transmute(x) }
// }
//
// pub const fn low_mask(high: u8, low: u8) -> u32 {
// (2u32.unbounded_shl(high - low)).wrapping_sub(1)
// }
//
// pub const fn mask(high: u8, low: u8) -> u32 {
// low_mask(high, low).unbounded_shl(low)
// }
//
// pub const fn bits(x: i32, high: u8, low: u8) -> u32 {
// let x = u(x);
// x.unbounded_shr(low) & low_mask(high, low)
// }
//
// pub const fn bit(x: i32, i: u32) -> u32 {
// let x = u(x);
// x.unbounded_shr(i) & 2u32 << i
// }
//
// pub const fn in_bit_range<T: Shl<u8, Output = T> + Shr<u8, Output = T>>(
// x: T,
// high: u8,
// low: u8,
// ) -> bool {
// if x < 0 {
// if high == low {
// return false;
// }
// (bits(x, high - 1, low) | !mask(high - 1, low)) == u(x)
// } else {
// bits(x, high, low) << low == u(x)
// }
// }
// pub struct Bits< // pub struct Bits<
// T: Shl<u8, Output = T> + Shr<u8, Output = T>, // T: Shl<u8, Output = T> + Shr<u8, Output = T>,
// const S: bool, // const S: bool,
@@ -78,7 +63,7 @@ pub const fn in_bit_range(x: i32, high: u32, low: u32) -> bool {
// >(T); // >(T);
// pub struct U32Bits<const H: u32, const L: u32>(u32); // pub struct U32Bits<const H: u32, const L: u32>(u32);
// //
// impl<T: Shl<u8, Output = T> + Shr<u8, Output = T> + Add<u8, Output = T>, const S: bool, const H: u8, const L: u8> // impl<T: const Shl<u8, Output = T> + const Shr<u8, Output = T>, const S: bool, const H: u8, const L: u8>
// Bits<T, S, H, L> // Bits<T, S, H, L>
// { // {
// pub const fn new(val: T) -> Self { // pub const fn new(val: T) -> Self {
@@ -86,35 +71,3 @@ pub const fn in_bit_range(x: i32, high: u32, low: u32) -> bool {
// Self(val + L) // Self(val + L)
// } // }
// } // }
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn get_bits() {
assert_eq!(bits(0b10111010, 5, 3), 0b111);
assert_eq!(bits(0b10111010, 7, 5), 0b101);
assert_eq!(bits(0b10111010, 2, 0), 0b010);
assert_eq!(bits(0b10111010, 7, 7), 0b1);
assert_eq!(bits(0b1, 0, 0), 0b1);
assert_eq!(bits(0b1, 1, 1), 0b0);
}
#[test]
fn range() {
assert!(!in_bit_range(0b00111100, 5, 3));
assert!(!in_bit_range(0b00111100, 4, 2));
assert!(in_bit_range(0b000111100, 5, 2));
assert!(in_bit_range(0b000000001, 0, 0));
assert!(in_bit_range(0b000001000, 3, 3));
assert!(!in_bit_range(-3, 1, 0));
assert!(!in_bit_range(-5, 2, 2));
assert!(!in_bit_range(-5, 4, 3));
assert!(in_bit_range(-1, 1, 0));
assert!(in_bit_range(-4, 2, 0));
assert!(in_bit_range(-5, 3, 2));
}
}