risc-v-disassembler 0.0.1

Decodes RISC-V instructions into a rust enum.
Documentation
use crate::macros::{extract_bits, sign_extend32};
use crate::instructions::{DecodedInstruction32, DecodeInstruction32, Instruction32};
use crate::DisassemblerError;

impl DecodeInstruction32 for Instruction32 {
    fn decode_instruction32(&self) -> Result<DecodedInstruction32, DisassemblerError> {
        let opcode = extract_bits!(*self, 0, 6)?;
        let decoded = match opcode {
            0b0110011 => decode_rtype32(*self)?,
            0b0010011 | 0b0000011 | 0b1100111 => decode_itype32(*self)?,
            0b0100011 => decode_stype32(*self)?,
            0b1100011 => decode_btype32(*self)?,
            0b0110111 | 0b0010111 => decode_utype32(*self)?,
            0b1101111 => decode_jtype32(*self)?,
            _ => return Err(DisassemblerError::InvalidOpcode(opcode as u8)),
        };
        Ok(decoded)
    }
}

fn decode_rtype32(instruction: Instruction32) -> Result<DecodedInstruction32, DisassemblerError> {
    let opcode = extract_bits!(instruction, 0, 6)? as u8;
    let rd = extract_bits!(instruction, 7, 11)? as u8;
    let funct3 = extract_bits!(instruction, 12, 14)? as u8;
    let rs1 = extract_bits!(instruction, 15, 19)? as u8;
    let rs2 = extract_bits!(instruction, 20, 24)? as u8;
    let funct7 = extract_bits!(instruction, 25, 31)? as u8;
    Ok(DecodedInstruction32::RType {
        opcode,
        rd,
        funct3,
        rs1,
        rs2,
        funct7,
    })
}

fn decode_itype32(instruction: Instruction32) -> Result<DecodedInstruction32, DisassemblerError> {
    let opcode = extract_bits!(instruction, 0, 6)? as u8;
    let rd = extract_bits!(instruction, 7, 11)? as u8;
    let funct3 = extract_bits!(instruction, 12, 14)? as u8;
    let rs1 = extract_bits!(instruction, 15, 19)? as u8;
    let imm_bits= extract_bits!(instruction, 20, 31)? as u16;
    let imm = sign_extend32!(imm_bits as u32, 12)?;
    Ok(DecodedInstruction32::IType {
        opcode,
        rd,
        funct3,
        rs1,
        imm,
    })
}

fn decode_stype32(instruction: Instruction32) -> Result<DecodedInstruction32, DisassemblerError> {
    let opcode = extract_bits!(instruction, 0, 6)? as u8;
    let imm4_0 = extract_bits!(instruction, 7, 11)? as u16;
    let funct3 = extract_bits!(instruction, 12, 14)? as u8;
    let rs1 = extract_bits!(instruction, 15, 19)? as u8;
    let rs2 = extract_bits!(instruction, 20, 24)? as u8;
    let imm11_5 = extract_bits!(instruction, 25, 31)? as u16;
    let imm_bits = imm11_5 << 5 | imm4_0; 
    let imm = sign_extend32!(imm_bits as u32, 12)?;

    Ok(DecodedInstruction32::SType {
        opcode,
        imm,
        funct3,
        rs1,
        rs2,
    })
}

fn decode_btype32(instruction: Instruction32) -> Result<DecodedInstruction32, DisassemblerError> {
    let opcode = extract_bits!(instruction, 0, 6)? as u8;
    let imm11 = extract_bits!(instruction, 7, 7)? as u16;
    let imm4_1 = extract_bits!(instruction, 8, 11)? as u16;
    let funct3 = extract_bits!(instruction, 12, 14)? as u8;
    let rs1 = extract_bits!(instruction, 15, 19)? as u8;
    let rs2 = extract_bits!(instruction, 20, 24)? as u8;
    let imm10_5 = extract_bits!(instruction, 25, 30)? as u16;
    let imm12 = extract_bits!(instruction, 31, 31)? as u16;

    let imm_bits = imm12 << 12 | imm11<< 11 | imm10_5<< 5 | imm4_1 << 1 | 0;
    let imm = sign_extend32!(imm_bits as u32, 13)?;

    Ok(DecodedInstruction32::BType {
        opcode,
        imm: imm,
        funct3,
        rs1,
        rs2,
    })
}

fn decode_utype32(instruction: Instruction32) -> Result<DecodedInstruction32, DisassemblerError> {
    let opcode = extract_bits!(instruction, 0, 6)? as u8;
    let rd = extract_bits!(instruction, 7, 11)? as u8;
    let mut imm_bits = extract_bits!(instruction, 12, 31)? as u32;
    imm_bits <<= 12;
    let imm = sign_extend32!(imm_bits, 32)?;

    Ok(DecodedInstruction32::UType {
        opcode,
        rd,
        imm,
    })
}

fn decode_jtype32(instruction: Instruction32) -> Result<DecodedInstruction32, DisassemblerError> {
    let opcode = extract_bits!(instruction, 0, 6)? as u8;
    let rd = extract_bits!(instruction, 7, 11)? as u8;
    let imm19_12 = extract_bits!(instruction, 12, 19)? as u32;
    let imm11 = extract_bits!(instruction, 20, 20)? as u32;
    let imm10_1 = extract_bits!(instruction, 21, 30)? as u32;
    let imm20 = extract_bits!(instruction, 31, 31)? as u32;

    let imm_bits = imm20 << 20 | imm19_12 << 12 | imm11 << 11 | imm10_1 << 1 | 0;
    let imm = sign_extend32!(imm_bits, 21)?;

    Ok(DecodedInstruction32::JType {
        opcode,
        rd,
        imm,
    })
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_decode_instruction32() {
        let rtype_instruction : Instruction32 = 0x007212b3;
        let itype_instruction : Instruction32 = 0x02a24193;
        let stype_instruction : Instruction32 = 0x045219a3;
        let btype_instruction : Instruction32 = 0x00620c63;
        let utype_instruction : Instruction32 = 0x00038197;
        let jtype_instruction : Instruction32 = 0x062001ef;

        match rtype_instruction.decode_instruction32().unwrap() {
            DecodedInstruction32::RType { .. } => {
                assert!(true);
            }
            _ => panic!("R-type instruction decoded as a different type"),
        }

        match itype_instruction.decode_instruction32().unwrap() {
            DecodedInstruction32::IType { .. } => {
                assert!(true);
            }
            _ => panic!("I-type instruction decoded as a different type"),
        }

        match stype_instruction.decode_instruction32().unwrap() {
            DecodedInstruction32::SType { .. } => {
                assert!(true);
            }
            _ => panic!("S-type instruction decoded as a different type"),
        }

        match btype_instruction.decode_instruction32().unwrap() {
            DecodedInstruction32::BType { .. } => {
                assert!(true);
            }
            _ => panic!("B-type instruction decoded as a different type"),
        }

        match utype_instruction.decode_instruction32().unwrap() {
            DecodedInstruction32::UType { .. } => {
                assert!(true);
            }
            _ => panic!("U-type instruction decoded as a different type"),
        }

        match jtype_instruction.decode_instruction32().unwrap() {
            DecodedInstruction32::JType { .. } => {
                assert!(true);
            }
            _ => panic!("J-type instruction decoded as a different type"),
        }

        
    }

    #[test]
    fn test_decode_rtype32() {
        let instruction : Instruction32 = 0x00308033;
        let result = decode_rtype32(instruction).unwrap();
        match result {
            DecodedInstruction32::RType { opcode, rd, funct3, rs1, rs2, funct7 } => {
                assert_eq!(opcode, 0b011_0011, "Opcode mismatch");
                assert_eq!(rd, 0b0_0000, "RD mismatch");
                assert_eq!(funct3, 0b000, "Funct3 mismatch");
                assert_eq!(rs1, 1, "RS1 mismatch");
                assert_eq!(rs2, 3, "RS2 mismatch");
                assert_eq!(funct7, 0b000_0000, "Funct7 mismatch");
            }
            _ => panic!("R-type instruction decoded as a different type"),
        }
    }

    #[test]
    fn test_decode_itype32() {
        let instruction : Instruction32 = 0x02a0e013;
        let result = decode_itype32(instruction).unwrap();
        match result {
            DecodedInstruction32::IType { opcode, rd, funct3, rs1, imm } => {
                assert_eq!(opcode, 0b001_0011, "Opcode mismatch");
                assert_eq!(rd, 0, "RD mismatch");
                assert_eq!(funct3, 0b110, "Funct3 mismatch");
                assert_eq!(rs1, 1, "RS1 mismatch");
                assert_eq!(imm, 42, "Immediate mismatch");
            }
            _ => panic!("I-type instruction decoded as a different type"),
        }

        let instruction : Instruction32 = 0xfd60e013;
        let result = decode_itype32(instruction).unwrap();
        match result {
            DecodedInstruction32::IType { opcode, rd, funct3, rs1, imm } => {
                assert_eq!(opcode, 0b001_0011, "Opcode mismatch");
                assert_eq!(rd, 0, "RD mismatch");
                assert_eq!(funct3, 0b110, "Funct3 mismatch");
                assert_eq!(rs1, 1, "RS1 mismatch");
                assert_eq!(imm, -42, "Immediate mismatch");
            }
            _ => panic!("I-type instruction decoded as a different type"),
        }
    }

    #[test]
    fn test_decode_stype32() {
        let instruction : Instruction32 = 0x003107a3;
        let result = decode_stype32(instruction).unwrap();
        match result {
            DecodedInstruction32::SType { opcode, imm, funct3, rs1, rs2 } => {
                assert_eq!(opcode, 0b010_0011, "Opcode mismatch");
                assert_eq!(imm, 15, "Immediate mismatch");
                assert_eq!(funct3, 0b000, "Funct3 mismatch");
                assert_eq!(rs1, 2, "RS1 mismatch");
                assert_eq!(rs2, 3, "RS2 mismatch");
            }
            _ => panic!("S-type instruction decoded as a different type"),
        }

        let instruction : Instruction32 = 0xfe3108a3;
        let result = decode_stype32(instruction).unwrap();
        match result {
            DecodedInstruction32::SType { opcode, imm, funct3, rs1, rs2 } => {
                assert_eq!(opcode, 0b010_0011, "Opcode mismatch");
                assert_eq!(imm, -15, "Immediate mismatch");
                assert_eq!(funct3, 0b000, "Funct3 mismatch");
                assert_eq!(rs1, 2, "RS1 mismatch");
                assert_eq!(rs2, 3, "RS2 mismatch");
            }
            _ => panic!("S-type instruction decoded as a different type"),
        }
    }

    #[test]
    fn test_decode_btype32() {
        let instruction : Instruction32 = 0x02619d63;
        let result = decode_btype32(instruction).unwrap();
        match result {
            DecodedInstruction32::BType { opcode, imm, funct3, rs1, rs2 } => {
                assert_eq!(opcode, 0b110_0011, "Opcode mismatch");
                assert_eq!(imm, 58, "Immediate mismatch");
                assert_eq!(funct3, 0b001, "Funct3 mismatch");
                assert_eq!(rs1, 3, "RS1 mismatch");
                assert_eq!(rs2, 6, "RS2 mismatch");
            }
            _ => panic!("B-type instruction decoded as a different type"),
        }

        let instruction : Instruction32 = 0xfc6193e3;
        let result = decode_btype32(instruction).unwrap();
        match result {
            DecodedInstruction32::BType { opcode, imm, funct3, rs1, rs2 } => {
                assert_eq!(opcode, 0b110_0011, "Opcode mismatch");
                assert_eq!(imm, -58, "Immediate mismatch");
                assert_eq!(funct3, 0b001, "Funct3 mismatch");
                assert_eq!(rs1, 3, "RS1 mismatch");
                assert_eq!(rs2, 6, "RS2 mismatch");
            }
            _ => panic!("B-type instruction decoded as a different type"),
        }
    }

    #[test]
    fn test_decode_utype32() {
        let instruction : Instruction32 = 0x0005b3b7;
        let result = decode_utype32(instruction).unwrap();
        match result {
            DecodedInstruction32::UType { opcode, rd, imm } => {
                assert_eq!(opcode, 0b011_0111, "Opcode mismatch");
                assert_eq!(rd, 7, "RD mismatch");
                assert_eq!(imm, 91<<12, "Immediate mismatch");
            }
            _ => panic!("U-type instruction decoded as a different type"),
        }

        let instruction : Instruction32 = 0xfffa53b7;
        let result = decode_utype32(instruction).unwrap();
        match result {
            DecodedInstruction32::UType { opcode, rd, imm } => {
                assert_eq!(opcode, 0b011_0111, "Opcode mismatch");
                assert_eq!(rd, 7, "RD mismatch");
                assert_eq!(imm, (-91)<<12, "Immediate mismatch");
            }
            _ => panic!("U-type instruction decoded as a different type"),
        }
    }


    #[test]
    fn test_decode_jtype32() {
        let instruction : Instruction32 = 0x0360066f;
        let result = decode_jtype32(instruction).unwrap();
        match result {
            DecodedInstruction32::JType { opcode, rd, imm } => {
                assert_eq!(opcode, 0b110_1111, "Opcode mismatch");
                assert_eq!(rd, 12, "RD mismatch");
                assert_eq!(imm, 54, "Immediate mismatch");
            }
            _ => panic!("J-type instruction decoded as a different type"),
        }

        let instruction : Instruction32 = 0xfcbff66f;
        let result = decode_jtype32(instruction).unwrap();
        match result {
            DecodedInstruction32::JType { opcode, rd, imm } => {
                assert_eq!(opcode, 0b110_1111, "Opcode mismatch");
                assert_eq!(rd, 12, "RD mismatch");
                assert_eq!(imm, -54, "Immediate mismatch");
            }
            _ => panic!("J-type instruction decoded as a different type"),
        }
    }

}