-
Notifications
You must be signed in to change notification settings - Fork 2
Open
Description
I want to use this library to encode a simple instruction set
use bin_proto::{ BitCodec, BitDecode, BitEncode, LittleEndian};
#[derive(Debug, BitDecode, BitEncode, PartialEq)]
#[bin_proto(discriminant_type = u16, bits = 10)]
#[repr(u8)]
pub enum Opcode {
Nop = 0,
Load(UnaryOp) = 1,
Store(UnaryOp) = 2,
Add(BinaryOp) = 3,
Sub(BinaryOp) = 4,
Mul(BinaryOp) = 5,
Div(BinaryOp) = 6,
Mod(BinaryOp) = 7,
And(BinaryOp) = 8,
Or(BinaryOp) = 9,
Xor(BinaryOp) = 10,
Equal(BinaryOp) = 11,
NotEqual(BinaryOp) = 12,
LessThan(BinaryOp) = 13,
GreaterThan(BinaryOp) = 14,
Not(UnaryOp) = 15,
LogicalNot(UnaryOp) = 16,
Shl(BinaryOp) = 17,
Shr(BinaryOp) = 18,
}
#[derive(Debug, BitDecode, BitEncode, PartialEq)]
pub struct UnaryOp {
pub with: WithFlags,
pub dest: Operands,
pub src: Operands
}
#[derive(Debug, BitDecode, BitEncode, PartialEq)]
pub struct WithFlags {
#[bin_proto(bits = 1)]
pub set_carry: bool,
#[bin_proto(bits = 1)]
pub set_zero: bool,
#[bin_proto(bits = 1)]
pub set_overflow: bool
}
#[derive(Debug, BitDecode, BitEncode, PartialEq)]
pub struct BinaryOp {
pub with: WithFlags,
pub dest: Operands,
pub src1: Operands,
pub src2: Operands
}
#[derive(Debug, BitDecode, BitEncode, PartialEq)]
#[bin_proto(discriminant_type = u8, bits = 2)]
#[repr(u8)]
pub enum Register {
Integer(#[bin_proto(bits = 4)] u8) = 0,
Float(#[bin_proto(bits = 4)] u8) = 1,
}
// Memory addressing (4 bits for mode + data)
#[derive(Debug, BitDecode, BitEncode, PartialEq)]
#[bin_proto(discriminant_type = u8, bits = 3)]
#[repr(u8)]
pub enum MemAccess {
Indirect(Register) = 0, // [reg]
BaseDisp(Register, #[bin_proto(bits = 16)] i16) = 1, // [reg+disp]
Indexed(Register, Register) = 2, // [base+index]
Absolute(usize) = 3, // [addr]
}
#[derive(Debug, BitDecode, BitEncode, PartialEq)]
#[bin_proto(discriminant_type = u8, bits = 4)]
#[repr(u8)]
pub enum Immediate {
I8(#[bin_proto(bits = 8)] i8) = 0,
I16(#[bin_proto(bits = 16)] i16) = 1,
I32(#[bin_proto(bits = 32)] i32) = 2,
I64(#[bin_proto(bits = 64)] i64) = 3,
I128(#[bin_proto(bits = 128)] i128) = 4,
U8(#[bin_proto(bits = 8)] u8) = 5,
U16(#[bin_proto(bits = 16)] u16) = 6,
U32(#[bin_proto(bits = 32)] u32) = 7,
U64(#[bin_proto(bits = 64)] u64) = 8,
U128(#[bin_proto(bits = 128)] u128) = 9,
Pointer(usize) = 10,
}
#[derive(Debug, BitDecode, BitEncode, PartialEq)]
#[bin_proto(discriminant_type = u8, bits = 2)]
#[repr(u8)]
pub enum Operands {
Register(Register) = 0,
Immediate(Immediate) = 1,
MemAccess(MemAccess) = 2,
}
fn main() {
let op_nop = Opcode::Nop;
let encoded_nop = op_nop.encode_bytes(LittleEndian).unwrap();
println!("Encoded NOP: {:?}", encoded_nop);
let op_add = Opcode::Add(BinaryOp {
with: WithFlags {
set_carry: true,
set_zero: true,
set_overflow: false,
},
dest: Operands::Register(Register::Integer(1)),
src1: Operands::Immediate(Immediate::I16(2)),
src2: Operands::Immediate(Immediate::I16(122)),
});
let encoded_add = op_add.encode_bytes(LittleEndian).unwrap();
println!("Encoded ADD: {:?}", encoded_add);
let (decoded_add, read) = Opcode::decode_bytes(&encoded_add, LittleEndian).unwrap();
println!("Decoded ADD: {:?}, read: {read}", decoded_add);
let op_not = Opcode::Not(UnaryOp {
with: WithFlags {
set_carry: false,
set_zero: true,
set_overflow: false,
},
dest: Operands::Register(Register::Integer(1)),
src: Operands::Register(Register::Integer(1)),
});
let encoded_not = op_not.encode_bytes(LittleEndian).unwrap();
println!("Encoded NOT: {:?}", encoded_not);
let (decoded_not, read) = Opcode::decode_bytes(&encoded_not, LittleEndian).unwrap();
println!("Decoded NOT: {:?}, read: {read}", decoded_not);
}I want to make it look like x86 but with a format like this, is it really the most efficient? How do I handle alignment? I noticed that the data I encoded often has some remaining bits left, what's the best practice to skip them in the instruction parsing stream?
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels