use core::num::Wrapping; // Permet d'autoriser les overflow pour les opérations voulues

#[allow(non_snake_case)] // supprimer le warning snake case (quand les noms de variables ont des majuscules)
#[derive(Debug)] 
pub struct Instruction {
    pub value : u64,

    pub opcode : u8, 
    pub rs1 : u8, 
    pub rs2 : u8, 
    pub rs3 : u8, 
    pub rd : u8, 
    pub funct7 : u8, 
    pub funct7_smaller : u8, 
    pub funct3 : u8, 
    pub shamt : u8, // shamt = imm[5:0] or imm[4:0] (depend of opcode)

    pub imm12_I : u16,
    pub imm12_S : u16,

    pub imm12_I_signed : i16, 
    pub imm12_S_signed : i16, 
    pub imm13 : i16, 
    pub imm13_signed : i16,

    pub imm31_12 : u32, 
    pub imm21_1 : u32,

    pub imm31_12_signed : i32, 
    pub imm21_1_signed : i32,
}

#[allow(non_snake_case)]
pub fn decode(val : u64) -> Instruction {

    let value = val;

    let opcode          = (val & 0x7f) as u8;
    let rs1             = ((val >> 15) & 0x1f) as u8;
    let rs2             = ((val >> 20) & 0x1f) as u8;
    let rs3             = ((val >> 27) & 0x1f) as u8;
    let rd              = ((val >> 7) & 0x1f) as u8;
    let funct7          = ((val >> 25) & 0x7f) as u8;
    let funct7_smaller  = funct7 & 0x3e;
    
    let funct3          = ((val >> 12) & 0x7) as u8;
    let imm12_I         = ((val >> 20) & 0xfff) as u16;
    let imm12_S         = (((val >> 20) & 0xfe0) + ((val >> 7) & 0x1f)) as u16;
    
    let imm12_I_signed  = if imm12_I >= 2048 { (Wrapping(imm12_I) - Wrapping(4096)).0 } else { imm12_I } as i16;
    let imm12_S_signed  = if imm12_S >= 2048 { (Wrapping(imm12_S) - Wrapping(4096)).0 } else { imm12_S } as i16;
    
    let imm13           = (((val >> 19) & 0x1000) + ((val >> 20) & 0x7e0) +
                        ((val >> 7) & 0x1e) + ((val << 4) & 0x800)) as i16;
    let imm13_signed    = if imm13 >= 4096 { imm13 - 8192 } else { imm13 };
    
    let imm31_12        = (val & 0xfffff000) as u32;
    let imm31_12_signed = imm31_12 as i32;
    
    let imm21_1         = ((val & 0xff000) + ((val >> 9) & 0x800) +
                        ((val >> 20) & 0x7fe) + ((val >> 11) & 0x100000)) as u32;
    let imm21_1_signed  = if imm21_1 >= 1048576 { (Wrapping(imm21_1) - Wrapping(2097152)).0 } else { imm21_1 } as i32;
    
    let shamt           = ((val >> 20) & 0x3f) as u8;
    
    Instruction {
        value,

        opcode,
        rs1,
        rs2,
        rs3,
        rd,
        funct7,
        funct7_smaller,
    
        funct3,
        imm12_I,
        imm12_S,
    
        imm12_I_signed,
        imm12_S_signed,
    
        imm13,
        imm13_signed,
    
        imm31_12,
        imm31_12_signed,
    
        imm21_1,
        imm21_1_signed,
    
        shamt
    }
}