diff options
author | iximeow <me@iximeow.net> | 2024-10-08 00:00:40 -0700 |
---|---|---|
committer | iximeow <me@iximeow.net> | 2024-10-08 00:00:40 -0700 |
commit | f85410928c75a03a8bab3497966f2b6e6c42c2ea (patch) | |
tree | 6090fa1c3210ffddee3b9cd0cce4d9f02a56b534 /src | |
parent | 9b3a249a2e1c5078d541810d8f423e48c3832d77 (diff) |
more progress
Diffstat (limited to 'src')
-rw-r--r-- | src/display.rs | 47 | ||||
-rw-r--r-- | src/lib.rs | 257 |
2 files changed, 289 insertions, 15 deletions
diff --git a/src/display.rs b/src/display.rs index 86de297..94d15f3 100644 --- a/src/display.rs +++ b/src/display.rs @@ -95,17 +95,19 @@ impl fmt::Display for Instruction { return Ok(()); } - static JUMPS: &[Opcode] = &[ + use crate::BranchHint; + + static CONDITIONAL_JUMPS: &[Opcode] = &[ Opcode::JumpEq, Opcode::JumpNeq, Opcode::JumpGt, Opcode::JumpLe, Opcode::JumpGtu, Opcode::JumpLeu, Opcode::JumpBitSet, Opcode::JumpBitClear, ]; - if JUMPS.contains(&self.opcode) { - use crate::BranchHint; - let hint_label = match self.flags.branch_hint.unwrap() { - BranchHint::Taken => { "t" }, - BranchHint::NotTaken => { "nt" }, + if CONDITIONAL_JUMPS.contains(&self.opcode) { + let hint_label = match self.flags.branch_hint { + Some(BranchHint::Taken) => { ":t" }, + Some(BranchHint::NotTaken) => { ":nt" }, + None => { "" }, }; - write!(f, "if ({}({}, {})) jump:{} {}", + write!(f, "if ({}({}, {})) jump{} {}", self.opcode.cmp_str().unwrap(), // TODO: unwrap_unchecked?? self.sources[0], self.sources[1], @@ -114,6 +116,21 @@ impl fmt::Display for Instruction { )?; return Ok(()); } + static UNCONDITIONAL_BRANCHES: &[Opcode] = &[ + Opcode::Call, Opcode::Callr, Opcode::Callrh, + Opcode::Jump, Opcode::Jumpr, Opcode::Jumprh, + ]; + if UNCONDITIONAL_BRANCHES.contains(&self.opcode) { + write!(f, "{}{} {}", + self.opcode, + match self.flags.branch_hint.as_ref() { + Some(BranchHint::Taken) => ":t", + Some(BranchHint::NotTaken) => ":nt", + None => "", + }, + self.dest.unwrap())?; + return Ok(()); + } if let Some(o) = self.dest.as_ref() { write!(f, "{} = ", o)?; } @@ -157,7 +174,12 @@ impl fmt::Display for Opcode { Opcode::BUG => { f.write_str("BUG") }, Opcode::Nop => { f.write_str("nop") }, Opcode::Jump => { f.write_str("jump") }, + Opcode::Jumpr => { f.write_str("jumpr") }, + Opcode::Jumprh => { f.write_str("jumprh") }, Opcode::Call => { f.write_str("call") }, + Opcode::Callr => { f.write_str("callr") }, + Opcode::Callrh => { f.write_str("callrh") }, + Opcode::Hintjr => { f.write_str("hintjr") }, Opcode::Memb => { f.write_str("memb") }, Opcode::Memub => { f.write_str("memub") }, Opcode::Memh => { f.write_str("memh") }, @@ -199,6 +221,8 @@ impl fmt::Display for Opcode { Opcode::And => { f.write_str("and") }, Opcode::Sub => { f.write_str("sub") }, Opcode::Or => { f.write_str("or") }, + Opcode::Xor => { f.write_str("xor") }, + Opcode::Contains => { f.write_str("contains") }, Opcode::JumpEq => { f.write_str("cmp.eq+jump") }, Opcode::JumpNeq => { f.write_str("cmp.neq+jump") }, @@ -258,6 +282,13 @@ impl fmt::Display for Opcode { Opcode::TransferRegisterJump => { f.write_str("transferregisterjump") } Opcode::TransferImmediateJump => { f.write_str("transferimmediatejump") } + + Opcode::Trap0 => { f.write_str("trap0") } + Opcode::Trap1 => { f.write_str("trap1") } + Opcode::Pause => { f.write_str("pause") } + Opcode::Icinva => { f.write_str("icinva") } + Opcode::Isync => { f.write_str("isync") } + Opcode::Unpause => { f.write_str("unpause") } } } } @@ -269,7 +300,7 @@ impl fmt::Display for Operand { f.write_str("BUG (operand)") } Operand::PCRel32 { rel } => { - write!(f, "#{}", rel) + write!(f, "$+#{}", rel) } Operand::Gpr { reg } => { write!(f, "R{}", reg) @@ -350,7 +350,12 @@ pub enum Opcode { // V73 page 214 ("Jump to address") Jump, + Jumpr, + Jumprh, Call, + Callr, + Callrh, + Hintjr, LoadMemb, LoadMemub, @@ -401,15 +406,21 @@ pub enum Opcode { JumpBitSet, JumpBitClear, + /// predicate register used for branch condition is part of normal predication bits TestClrJump, + /// predicate register used for branch condition is part of normal predication bits CmpEqJump, + /// predicate register used for branch condition is part of normal predication bits CmpGtJump, + /// predicate register used for branch condition is part of normal predication bits CmpGtuJump, Add, And, Sub, Or, + Xor, + Contains, Tlbw, Tlbr, @@ -452,6 +463,13 @@ pub enum Opcode { Extractu, Insert, + + Trap0, + Trap1, + Pause, + Icinva, + Isync, + Unpause, } impl Opcode { @@ -1475,20 +1493,109 @@ fn decode_instruction< 0b000 => { // 0 1 0 1 | 0 0 0 ... // call to register, may be predicated - panic!("TODO"); + let op = (inst >> 21) & 0b1111; + let sssss = reg_b16(inst); + handler.on_dest_decoded(Operand::gpr(sssss))?; + + if op >= 0b1000 { + // predicated callr + opcode_check!(op < 0b1010); + handler.on_opcode_decoded(Opcode::Callr)?; + let negated = op & 1 == 1; + let uu = (inst >> 8) & 0b11; + handler.inst_predicated(uu as u8, negated, false)?; + } else { + // unconditional callr/callrh + opcode_check!(op == 0b101 || op == 0b110); + if op == 0b101 { + handler.on_opcode_decoded(Opcode::Callr)?; + } else { + handler.on_opcode_decoded(Opcode::Callrh)?; + } + } }, - 0b000 => { + 0b001 => { // 0 1 0 1 | 0 0 1 ... // jump to register, may be predicated - panic!("TODO"); + let op = (inst >> 21) & 0b1111; + let sssss = reg_b16(inst); + + eprintln!("op is {:04b}", op); + if op >= 0b1000 { + // predicated jumpr + opcode_check!(op == 0b0100 || op == 0b0101 || op == 0b0110 || op == 0b1010 || op == 0b1011); + handler.on_opcode_decoded(Opcode::Jumpr)?; + handler.on_dest_decoded(Operand::gpr(sssss))?; + let negated = op & 1 == 1; + let dotnew = (inst >> 11) & 1 == 1; + let hint_taken = (inst >> 12) & 1 == 1; + let uu = (inst >> 8) & 0b11; + handler.inst_predicated(uu as u8, negated, dotnew)?; + handler.branch_hint(hint_taken)?; + } else { + // unconditional jumpr/jumprh + opcode_check!(op == 0b100 || op == 0b101 || op == 0b110); + if op == 0b100 { + handler.on_opcode_decoded(Opcode::Jumpr)?; + handler.on_dest_decoded(Operand::gpr(sssss))?; + } else if op == 0b101 { + handler.on_opcode_decoded(Opcode::Hintjr)?; + handler.on_source_decoded(Operand::gpr(sssss))?; + } else { + handler.on_opcode_decoded(Opcode::Jumprh)?; + handler.on_dest_decoded(Operand::gpr(sssss))?; + } + } + }, + 0b010 => { + // trap0, pause, trap1 + let minbits = (inst >> 22) & 0b111; + let u_low = (inst >> 2) & 0b111; + let u_mid = (inst >> 8) & 0b11111; + let u_8 = (u_mid << 3) | u_low; + + if minbits == 0b000 { + handler.on_opcode_decoded(Opcode::Trap0)?; + handler.on_source_decoded(Operand::imm_u8(u_8 as u8))?; + } else if minbits == 0b001 { + handler.on_opcode_decoded(Opcode::Pause)?; + let u_high = (inst >> 16) & 0b11; + let u_10 = (u_high << 8) | u_8; + handler.on_source_decoded(Operand::imm_u16(u_10 as u16))?; + } else if minbits == 0b010 { + handler.on_opcode_decoded(Opcode::Trap1)?; + let xxxxx = reg_b16(inst); + handler.on_source_decoded(Operand::gpr(xxxxx))?; + handler.on_source_decoded(Operand::imm_u8(u_8 as u8))?; + } else { + opcode_check!(false); + } + }, + 0b011 => { + // icinva, isync, unpause + let minbits = (inst >> 21) & 0b1111; + + if minbits == 0b0110 { + handler.on_opcode_decoded(Opcode::Icinva)?; + handler.on_source_decoded(Operand::gpr(reg_b16(inst)))?; + opcode_check!(inst & 0x3800 == 0x0000); + } else if minbits == 0b1110 { + handler.on_opcode_decoded(Opcode::Isync)?; + opcode_check!(inst & 0x1f23ff == 0x000002); + } else if minbits == 0b1111 { + handler.on_opcode_decoded(Opcode::Unpause)?; + opcode_check!(inst & 0x10e0 == 0x1000); + } else { + opcode_check!(false); + } }, 0b100 => { // V73 Jump to address // 0 1 0 1 | 1 0 0 i... handler.on_opcode_decoded(Opcode::Jump)?; - let imm = ((inst >> 1) & 0x7fff) | ((inst >> 3) & 0xff8000); + let imm = ((inst >> 1) & 0x1fff) | ((inst >> 3) & 0xffe000); let imm = ((imm as i32) << 10) >> 10; - handler.on_source_decoded(Operand::PCRel32 { rel: imm & !0b11 })?; + handler.on_dest_decoded(Operand::PCRel32 { rel: imm << 2 })?; }, 0b101 => { // V73 Call to address @@ -1498,10 +1605,40 @@ fn decode_instruction< return Err(DecodeError::InvalidOpcode); } handler.on_opcode_decoded(Opcode::Call)?; - let imm = ((inst >> 1) & 0x7fff) | ((inst >> 3) & 0xff8000); + let imm = ((inst >> 1) & 0x1fff) | ((inst >> 3) & 0xffe000); let imm = ((imm as i32) << 10) >> 10; - handler.on_source_decoded(Operand::PCRel32 { rel: imm & !0b11 })?; + handler.on_dest_decoded(Operand::PCRel32 { rel: imm << 2 })?; }, + 0b110 => { + // V73 Predicated jump/call relative + let negated = (inst >> 21) & 1 == 1; + let dotnew = (inst >> 11) & 1 == 1; + let uu = (inst >> 8) & 0b11; + let hint_taken = (inst >> 11) == 1; + + let i_lo = (inst >> 1) & 0x7f; + let i_8 = (inst >> 13) & 1; + let i_mid = (inst >> 16) & 0x1f; + let i_hi = (inst >> 22) & 0b11; + let i15 = i_lo | (i_8 << 7) | (i_mid << 8) | (i_hi << 13); + let i15 = (i15 as i32) << 17 >> 17; + + let is_call = (inst >> 24) & 1 == 1; + + if is_call { + handler.on_opcode_decoded(Opcode::Call)?; + handler.inst_predicated(uu as u8, negated, false)?; + opcode_check!(!dotnew); + } else { + handler.on_opcode_decoded(Opcode::Jump)?; + handler.inst_predicated(uu as u8, negated, dotnew)?; + handler.branch_hint(hint_taken)?; + } + handler.on_dest_decoded(Operand::PCRel32 { rel: i15 << 2 })?; + } + 0b111 => { + return Err(DecodeError::InvalidOpcode); + } _ => { // TODO: exhaustive } @@ -2079,6 +2216,112 @@ fn decode_instruction< handler.on_source_decoded(Operand::gpr(sssss))?; handler.on_source_decoded(Operand::imm_i16(i as i16))?; } + 0b1111 => { + let ddddd = reg_b0(inst); + let ttttt = reg_b8(inst); + let sssss = reg_b16(inst); + let uu = (inst >> 5) & 0b11; + + handler.on_source_decoded(Operand::gpr(sssss))?; + handler.on_source_decoded(Operand::gpr(ttttt))?; + + let majbits = (inst >> 24) & 0b111; + let predicated = (inst >> 27) & 1 == 1; + if !predicated { + // one set of instructions. for most, the opcode bits actually overlap (e.g. + // Rd=and(Rs,Rt) predicated with this bit set). this does not apply for *all* + // though, so just handle the two sets of instructions individually... + let minbits = (inst >> 21) & 0b111; + match majbits { + 0b001 => { + match minbits { + 0b000 => { + handler.on_opcode_decoded(Opcode::And)?; + handler.on_dest_decoded(Operand::gpr(ddddd))?; + }, + 0b001 => { + handler.on_opcode_decoded(Opcode::Or)?; + handler.on_dest_decoded(Operand::gpr(ddddd))?; + }, + 0b011 => { + handler.on_opcode_decoded(Opcode::Xor)?; + handler.on_dest_decoded(Operand::gpr(ddddd))?; + }, + 0b100 => { + }, + 0b101 => { + }, + _ => { + opcode_check!(false); + } + } + }, + 0b010 => { + } + 0b011 => { + match minbits { + 0b000 => { + handler.on_opcode_decoded(Opcode::Add)?; + handler.on_dest_decoded(Operand::gpr(ddddd))?; + }, + 0b001 => { + handler.on_opcode_decoded(Opcode::Sub)?; + handler.on_dest_decoded(Operand::gpr(ddddd))?; + } + 0b010 => { + handler.on_opcode_decoded(Opcode::CmpEq)?; + handler.on_dest_decoded(Operand::gpr(ddddd))?; + } + 0b011 => { + handler.on_opcode_decoded(Opcode::CmpEq)?; + handler.negate_result()?; + handler.on_dest_decoded(Operand::gpr(ddddd))?; + } + _ => { + // will become exhaustive.. + } + } + } + 0b100 => { + } + 0b101 => { + } + 0b110 => { + } + 0b111 => { + } + _ => { + opcode_check!(false); + } + } + } else { + let negated = (inst >> 7) & 1 == 1; + let dotnew = (inst >> 13) & 1 == 1; + handler.inst_predicated(uu as u8, negated, dotnew)?; + if majbits == 0b001 { + handler.on_dest_decoded(Operand::gpr(ddddd))?; + static OPS: [Option<Opcode>; 4] = [ + Some(Opcode::And), Some(Opcode::Or), None, Some(Opcode::Xor) + ]; + let opbits = (inst >> 21) & 0b11; + handler.on_opcode_decoded(decode_opcode!(OPS[opbits as usize]))?; + } else if majbits == 0b011 { + handler.on_dest_decoded(Operand::gpr(ddddd))?; + opcode_check!((inst >> 23) & 1 == 0); + if (inst >> 21) & 1 == 0 { + handler.on_opcode_decoded(Opcode::Add)?; + } else { + handler.on_opcode_decoded(Opcode::Sub)?; + } + } else if majbits == 0b101 { + handler.on_dest_decoded(Operand::gprpair(ddddd)?)?; + handler.on_opcode_decoded(Opcode::Contains)?; + opcode_check!((inst >> 21) & 0b111 == 0b000); + } else { + opcode_check!(false); + } + } + } _ => { eprintln!("iclass: {:04b}", iclass); // TODO: exhaustive |