diff options
-rw-r--r-- | notes/todo | 128 | ||||
-rw-r--r-- | src/display.rs | 47 | ||||
-rw-r--r-- | src/lib.rs | 257 | ||||
-rw-r--r-- | tests/from_brain.rs | 91 |
4 files changed, 422 insertions, 101 deletions
@@ -297,39 +297,39 @@ A L I A S A L I A S A L I A S A L I A S A L I A S | Rd=zxtb(Rs) |0 1 0 0|1 i i 1 0 1 1|i i i i i| P P |i i i i i i i i i|d d d d d| Rdd=memuh(gp+#u16:3) - LD/slot 0,1 |0 1 0 0|1 i i 1 1 0 0|i i i i i| P P |i i i i i i i i i|d d d d d| Rd=memw(gp+#u16:3) - LD/slot 0,1 |0 1 0 0|1 i i 1 1 1 0|i i i i i| P P |i i i i i i i i i|d d d d d| Rdd=memd(gp+#u16:3) - LD/slot 0,1 -|0 1 0 1|0 0 0 0 1 0 1|s s s s s| P P |- - - - - - - - -|- - - - -| callr Rs - CR/slot 2 -|0 1 0 1|0 0 0 0 1 1 0|s s s s s| P P |- - - - - - - - -|- - - - -| callrh Rs - CR/slot 2 -|0 1 0 1|0 0 0 1 0 0 0|s s s s s| P P |- - - - u u - - -|- - - - -| if (Pu) callr Rs - CR/slot 2 -|0 1 0 1|0 0 0 1 0 0 1|s s s s s| P P |- - - - u u - - -|- - - - -| if (!Pu) callr Rs - CR/slot 2 -|0 1 0 1|0 0 1 0 1 0 0|s s s s s| P P |- - - - - - - - -|- - - - -| jumpr Rs - CR/slot 2 -|0 1 0 1|0 0 1 0 1 0 1|s s s s s| P P |- - - - - - - - -|- - - - -| hintjr(Rs) - CR/slot 2,3 -|0 1 0 1|0 0 1 0 1 1 0|s s s s s| P P |- - - - - - - - -|- - - - -| jumprh Rs - CR/slot 2 -|0 1 0 1|0 0 1 1 0 1 0|s s s s s| P P |- 0 0 - u u - - -|- - - - -| if (Pu) jumpr:nt Rs - CR/slot 2 -|0 1 0 1|0 0 1 1 0 1 0|s s s s s| P P |- 0 1 - u u - - -|- - - - -| if (Pu.new) jumpr:nt Rs - CR/slot 2 -|0 1 0 1|0 0 1 1 0 1 0|s s s s s| P P |- 1 0 - u u - - -|- - - - -| if (Pu) jumpr:t Rs - CR/slot 2 -|0 1 0 1|0 0 1 1 0 1 0|s s s s s| P P |- 1 1 - u u - - -|- - - - -| if (Pu.new) jumpr:t Rs - CR/slot 2 -|0 1 0 1|0 0 1 1 0 1 1|s s s s s| P P |- 0 0 - u u - - -|- - - - -| if (!Pu) jumpr:nt Rs - CR/slot 2 -|0 1 0 1|0 0 1 1 0 1 1|s s s s s| P P |- 0 1 - u u - - -|- - - - -| if (!Pu.new) jumpr:nt Rs - CR/slot 2 -|0 1 0 1|0 0 1 1 0 1 1|s s s s s| P P |- 1 0 - u u - - -|- - - - -| if (!Pu) jumpr:t Rs - CR/slot 2 -|0 1 0 1|0 0 1 1 0 1 1|s s s s s| P P |- 1 1 - u u - - -|- - - - -| if (!Pu.new) jumpr:t Rs - CR/slot 2 -|0 1 0 1|0 1 0 0 0 0 -|- - - - -| P P |- i i i i i - - -|i i i - -| trap0(#u8) - SYSTEM/slot 2 -|0 1 0 1|0 1 0 0 0 1 -|- - - i i| P P |- i i i i i - - -|i i i - -| pause(#u10) - SYSTEM/slot 2 -|0 1 0 1|0 1 0 0 1 0 -|x x x x x| P P |- i i i i i - - -|i i i - -| trap1(Rx,#u8) - SYSTEM/slot 2 -|0 1 0 1|0 1 1 0 1 1 0|s s s s s| P P |0 0 0 - - - - - -|- - - - -| icinva(Rs) - SYSTEM/slot 2 -|0 1 0 1|0 1 1 1 1 1 0|0 0 0 0 0| P P |0 - - - 0 0 0 0 0|0 0 0 1 0| isync - SYSTEM/slot 2 -|0 1 0 1|0 1 1 1 1 1 1|x x x x x| P P |0 1 - - - - 0 0 0|- - - - -| unpause - SYSTEM/slot 2 -|0 1 0 1|1 0 0 i i i i|i i i i i| P P |i i i i i i i i i|i i i i -| jump #r22:2 - J/slot 0,1,2,3 -|0 1 0 1|1 0 1 i i i i|i i i i i| P P |i i i i i i i i i|i i i i 0| call #r22:2 - CR/slot 2,3 -|0 1 0 1|1 1 0 0 i i 0|i i i i i| P P |i 0 0 - u u i i i|i i i i -| if (Pu) jump:nt #r15:2 - J/slot 0,1,2,3 -|0 1 0 1|1 1 0 0 i i 0|i i i i i| P P |i 0 1 - u u i i i|i i i i -| if (Pu.new) jump:nt #r15:2 - J/slot 0,1,2,3 -|0 1 0 1|1 1 0 0 i i 0|i i i i i| P P |i 1 0 - u u i i i|i i i i -| if (Pu) jump:t #r15:2 - J/slot 0,1,2,3 -|0 1 0 1|1 1 0 0 i i 0|i i i i i| P P |i 1 1 - u u i i i|i i i i -| if (Pu.new) jump:t #r15:2 - J/slot 0,1,2,3 -|0 1 0 1|1 1 0 0 i i 1|i i i i i| P P |i 0 0 - u u i i i|i i i i -| if (!Pu) jump:nt #r15:2 - J/slot 0,1,2,3 -|0 1 0 1|1 1 0 0 i i 1|i i i i i| P P |i 0 1 - u u i i i|i i i i -| if (!Pu.new) jump:nt #r15:2 - J/slot 0,1,2,3 -|0 1 0 1|1 1 0 0 i i 1|i i i i i| P P |i 1 0 - u u i i i|i i i i -| if (!Pu) jump:t #r15:2 - J/slot 0,1,2,3 -|0 1 0 1|1 1 0 0 i i 1|i i i i i| P P |i 1 1 - u u i i i|i i i i -| if (!Pu.new) jump:t #r15:2 - J/slot 0,1,2,3 -|0 1 0 1|1 1 0 1 i i 0|i i i i i| P P |i - 0 - u u i i i|i i i i -| if (Pu) call #r15:2 - CR/slot 2,3 -|0 1 0 1|1 1 0 1 i i 1|i i i i i| P P |i - 0 - u u i i i|i i i i -| if (!Pu) call #r15:2 - CR/slot 2,3 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + |0 1 1 0|0 0 0 0 0 0 0|s s s s s| P P |- i i i i i - - -|i i - - -| loop0(#r7:2,Rs) - CR/slot 3 |0 1 1 0|0 0 0 0 0 0 1|s s s s s| P P |- i i i i i - - -|i i - - -| loop1(#r7:2,Rs) - CR/slot 3 |0 1 1 0|0 0 0 0 1 0 1|s s s s s| P P |- i i i i i - - -|i i - - -| p3=sp1loop0(#r7:2,Rs) - CR/slot 3 @@ -1417,9 +1417,9 @@ A L I A S A L I A S A L I A S A L I A S A L I A S | Rd=zxtb(Rs) |1 1 1 0|1 1 1 1|1 1 0|s s s s s| P P |0 t t t t t|0 0 1|x x x x x| Rx|=xor(Rs,Rt) - XTYPE ALU/slot 2,3 |1 1 1 0|1 1 1 1|1 1 0|s s s s s| P P |0 t t t t t|0 1 0|x x x x x| Rx^=and(Rs,Rt) - XTYPE ALU/slot 2,3 |1 1 1 0|1 1 1 1|1 1 0|s s s s s| P P |0 t t t t t|0 1 1|x x x x x| Rx^=or(Rs,Rt) - XTYPE ALU/slot 2,3 -|1 1 1 1|0 0 0 1 0 0 0|s s s s s| P P |- t t t t t - - -|d d d d d| Rd=and(Rs,Rt) - ALU32/slots 0 1 2 3 -|1 1 1 1|0 0 0 1 0 0 1|s s s s s| P P |- t t t t t - - -|d d d d d| Rd=or(Rs,Rt) - ALU32/slots 0 1 2 3 -|1 1 1 1|0 0 0 1 0 1 1|s s s s s| P P |- t t t t t - - -|d d d d d| Rd=xor(Rs,Rt) - ALU32/slots 0 1 2 3 +test |1 1 1 1|0 0 0 1 0 0 0|s s s s s| P P |- t t t t t - - -|d d d d d| Rd=and(Rs,Rt) - ALU32/slots 0 1 2 3 +test |1 1 1 1|0 0 0 1 0 0 1|s s s s s| P P |- t t t t t - - -|d d d d d| Rd=or(Rs,Rt) - ALU32/slots 0 1 2 3 +test |1 1 1 1|0 0 0 1 0 1 1|s s s s s| P P |- t t t t t - - -|d d d d d| Rd=xor(Rs,Rt) - ALU32/slots 0 1 2 3 |1 1 1 1|0 0 0 1 1 0 0|s s s s s| P P |- t t t t t - - -|d d d d d| Rd=and(Rt,~Rs) - ALU32/slots 0 1 2 3 |1 1 1 1|0 0 0 1 1 0 1|s s s s s| P P |- t t t t t - - -|d d d d d| Rd=and(Rt,~Rs) - ALU32/slots 0 1 2 3 |1 1 1 1|0 0 1 0 - 0 0|s s s s s| P P |- t t t t t - - -|0 0 0 d d| Pd=cmp.eq(Rs,Rt) - ALU32 PRED/slots 0 1 2 3 @@ -1428,10 +1428,10 @@ A L I A S A L I A S A L I A S A L I A S A L I A S | Rd=zxtb(Rs) |1 1 1 1|0 0 1 0 - 1 0|s s s s s| P P |- t t t t t - - -|1 0 0 d d| Pd=!cmp.gt(Rs,Rt) - ALU32 PRED/slots 0 1 2 3 |1 1 1 1|0 0 1 0 - 1 1|s s s s s| P P |- t t t t t - - -|0 0 0 d d| Pd=cmp.gtu(Rs,Rt) - ALU32 PRED/slots 0 1 2 3 |1 1 1 1|0 0 1 0 - 1 1|s s s s s| P P |- t t t t t - - -|1 0 0 d d| Pd=!cmp.gtu(Rs,Rt) - ALU32 PRED/slots 0 1 2 3 -|1 1 1 1|0 0 1 1 0 0 0|s s s s s| P P |- t t t t t - - -|d d d d d| Rd=add(Rs,Rt) - ALU32/slots 0 1 2 3 -|1 1 1 1|0 0 1 1 0 0 1|s s s s s| P P |- t t t t t - - -|d d d d d| Rd=sub(Rt,Rs) - ALU32/slots 0 1 2 3 -|1 1 1 1|0 0 1 1 0 1 0|s s s s s| P P |- t t t t t - - -|1 0 0 d d| Rd=cmp.eq(Rs,Rt) - ALU32 PRED/slots 0 1 2 3 -|1 1 1 1|0 0 1 1 0 1 1|s s s s s| P P |- t t t t t - - -|1 0 0 d d| Rd=!cmp.eq(Rs,Rt) - ALU32 PRED/slots 0 1 2 3 +test |1 1 1 1|0 0 1 1 0 0 0|s s s s s| P P |- t t t t t - - -|d d d d d| Rd=add(Rs,Rt) - ALU32/slots 0 1 2 3 +test |1 1 1 1|0 0 1 1 0 0 1|s s s s s| P P |- t t t t t - - -|d d d d d| Rd=sub(Rt,Rs) - ALU32/slots 0 1 2 3 +test |1 1 1 1|0 0 1 1 0 1 0|s s s s s| P P |- t t t t t - - -|d d d d d| Rd=cmp.eq(Rs,Rt) - ALU32 PRED/slots 0 1 2 3 +test |1 1 1 1|0 0 1 1 0 1 1|s s s s s| P P |- t t t t t - - -|d d d d d| Rd=!cmp.eq(Rs,Rt) - ALU32 PRED/slots 0 1 2 3 |1 1 1 1|0 0 1 1 1 0 0|s s s s s| P P |0 t t t t t - - -|d d d d d| Rd=combine(Rt.H,Rs.H) - ALU32 PERM/slots 0 1 2 3 |1 1 1 1|0 0 1 1 1 0 1|s s s s s| P P |0 t t t t t - - -|d d d d d| Rd=combine(Rt.H,Rs.L) - ALU32 PERM/slots 0 1 2 3 |1 1 1 1|0 0 1 1 1 1 0|s s s s s| P P |0 t t t t t - - -|d d d d d| Rd=combine(Rt.L,Rs.H) - ALU32 PERM/slots 0 1 2 3 @@ -1450,27 +1450,27 @@ A L I A S A L I A S A L I A S A L I A S A L I A S | Rd=zxtb(Rs) |1 1 1 1|0 1 1 1 - 0 0|s s s s s| P P |- t t t t t - - -|d d d d d| Rd=vavgh(Rs,Rt) - ALU32/slots 0 1 2 3 - |1 1 1 1|0 1 1 1 - 0 1|s s s s s| P P |- t t t t t - - -|d d d d d| Rd=vavgh(Rs,Rt):sat - ALU32/slots 0 1 2 3 - |1 1 1 1|0 1 1 1 - 1 1|s s s s s| P P |- t t t t t - - -|d d d d d| Rd=vnavgh(Rt,Rs) - ALU32/slots 0 1 2 3 - -|1 1 1 1|1 0 0 1 - 0 0|s s s s s| P P |0 t t t t t 0 u u|d d d d d| if (Pu) Rd=and(Rs,Rt) - ALU32 PRED/slots 0 1 2 3 -|1 1 1 1|1 0 0 1 - 0 0|s s s s s| P P |0 t t t t t 1 u u|d d d d d| if (!Pu) Rd=and(Rs,Rt) - ALU32 PRED/slots 0 1 2 3 -|1 1 1 1|1 0 0 1 - 0 0|s s s s s| P P |1 t t t t t 0 u u|d d d d d| if (Pu.new) Rd=and(Rs,Rt) - ALU32 PRED/slots 0 1 2 3 -|1 1 1 1|1 0 0 1 - 0 0|s s s s s| P P |1 t t t t t 1 u u|d d d d d| if (!Pu.new) Rd=and(Rs,Rt) - ALU32 PRED/slots 0 1 2 3 -|1 1 1 1|1 0 0 1 - 0 1|s s s s s| P P |0 t t t t t 0 u u|d d d d d| if (Pu) Rd=or(Rs,Rt) - ALU32 PRED/slots 0 1 2 3 -|1 1 1 1|1 0 0 1 - 0 1|s s s s s| P P |0 t t t t t 1 u u|d d d d d| if (!Pu) Rd=or(Rs,Rt) - ALU32 PRED/slots 0 1 2 3 -|1 1 1 1|1 0 0 1 - 0 1|s s s s s| P P |1 t t t t t 0 u u|d d d d d| if (Pu.new) Rd=or(Rs,Rt) - ALU32 PRED/slots 0 1 2 3 -|1 1 1 1|1 0 0 1 - 0 1|s s s s s| P P |1 t t t t t 1 u u|d d d d d| if (!Pu.new) Rd=or(Rs,Rt) - ALU32 PRED/slots 0 1 2 3 -|1 1 1 1|1 0 0 1 - 1 1|s s s s s| P P |0 t t t t t 0 u u|d d d d d| if (Pu) Rd=xor(Rs,Rt) - ALU32 PRED/slots 0 1 2 3 -|1 1 1 1|1 0 0 1 - 1 1|s s s s s| P P |0 t t t t t 1 u u|d d d d d| if (!Pu) Rd=xor(Rs,Rt) - ALU32 PRED/slots 0 1 2 3 -|1 1 1 1|1 0 0 1 - 1 1|s s s s s| P P |1 t t t t t 0 u u|d d d d d| if (Pu.new) Rd=xor(Rs,Rt) - ALU32 PRED/slots 0 1 2 3 -|1 1 1 1|1 0 0 1 - 1 1|s s s s s| P P |1 t t t t t 1 u u|d d d d d| if (!Pu.new) Rd=xor(Rs,Rt) - ALU32 PRED/slots 0 1 2 3 -|1 1 1 1|1 0 1 1 0 - 0|s s s s s| P P |0 t t t t t 0 u u|d d d d d| if (Pu) Rd=add(Rs,Rt) - ALU32 PRED/slots 0 1 2 3 -|1 1 1 1|1 0 1 1 0 - 0|s s s s s| P P |0 t t t t t 1 u u|d d d d d| if (!Pu) Rd=add(Rs,Rt) - ALU32 PRED/slots 0 1 2 3 -|1 1 1 1|1 0 1 1 0 - 0|s s s s s| P P |1 t t t t t 0 u u|d d d d d| if (Pu.new) Rd=add(Rs,Rt) - ALU32 PRED/slots 0 1 2 3 -|1 1 1 1|1 0 1 1 0 - 0|s s s s s| P P |1 t t t t t 1 u u|d d d d d| if (!Pu.new) Rd=add(Rs,Rt) - ALU32 PRED/slots 0 1 2 3 -|1 1 1 1|1 0 1 1 0 - 1|s s s s s| P P |0 t t t t t 0 u u|d d d d d| if (Pu) Rd=sub(Rt,Rs) - ALU32 PRED/slots 0 1 2 3 -|1 1 1 1|1 0 1 1 0 - 1|s s s s s| P P |0 t t t t t 1 u u|d d d d d| if (!Pu) Rd=sub(Rt,Rs) - ALU32 PRED/slots 0 1 2 3 -|1 1 1 1|1 0 1 1 0 - 1|s s s s s| P P |1 t t t t t 0 u u|d d d d d| if (Pu.new) Rd=sub(Rt,Rs) - ALU32 PRED/slots 0 1 2 3 -|1 1 1 1|1 0 1 1 0 - 1|s s s s s| P P |1 t t t t t 1 u u|d d d d d| if (!Pu.new) Rd=sub(Rt,Rs) - ALU32 PRED/slots 0 1 2 3 -|1 1 1 1|1 1 0 1 0 0 0|s s s s s| P P |0 t t t t t 0 u u|d d d d d| if (Pu) Rdd=contains(Rs,Rt) - ALU32 PRED/slots 0 1 2 3 -|1 1 1 1|1 1 0 1 0 0 0|s s s s s| P P |0 t t t t t 1 u u|d d d d d| if (!Pu) Rdd=contains(Rs,Rt)- ALU32 PRED/slots 0 1 2 3 -|1 1 1 1|1 1 0 1 0 0 0|s s s s s| P P |1 t t t t t 0 u u|d d d d d| if (Pu.new) Rdd=contains(Rs,Rt) - ALU32 PRED/slots 0 1 2 3 -|1 1 1 1|1 1 0 1 0 0 0|s s s s s| P P |1 t t t t t 1 u u|d d d d d| if (!Pu.new) Rdd=contains(Rs,Rt)- ALU32 PRED/slots 0 1 2 3 + + + + + + + + + + + + + + + + + + + + + + + + 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 diff --git a/tests/from_brain.rs b/tests/from_brain.rs index 2440e57..57e492d 100644 --- a/tests/from_brain.rs +++ b/tests/from_brain.rs @@ -39,16 +39,16 @@ fn supervisor() { #[test] fn general() { - test_display(&0b0001_0110000_01001_11_00_0111_000_00100u32.to_le_bytes(), "{ R17 = #7; jump #8 }"); - test_display(&0b0001_0111000_01001_11_00_0111_000_00100u32.to_le_bytes(), "{ R7 = R17; jump #8 }"); + test_display(&0b0001_0110000_01001_11_00_0111_000_00100u32.to_le_bytes(), "{ R17 = #7; jump $+#8 }"); + test_display(&0b0001_0111000_01001_11_00_0111_000_00100u32.to_le_bytes(), "{ R7 = R17; jump $+#8 }"); - test_display(&0b0001_0011011_11001_11_00_0011_000_00010u32.to_le_bytes(), "{ P1 = cmp.gtu(R17, #3); if (!P1.new) jump:nt #-508 }"); - test_display(&0b0001_0011101_11001_11_00_0011_000_00010u32.to_le_bytes(), "{ P1 = tstbit(R17, #0x0); if (P1.new) jump:nt #-508 }"); - test_display(&0b0001_0011111_11001_11_00_0011_000_00010u32.to_le_bytes(), "{ P1 = tstbit(R17, #0x0); if (!P1.new) jump:nt #-508 }"); - test_display(&0b0001_0011111_11001_11_10_0011_000_00010u32.to_le_bytes(), "{ P1 = tstbit(R17, #0x0); if (!P1.new) jump:t #-508 }"); + test_display(&0b0001_0011011_11001_11_00_0011_000_00010u32.to_le_bytes(), "{ P1 = cmp.gtu(R17, #3); if (!P1.new) jump:nt $+#-508 }"); + test_display(&0b0001_0011101_11001_11_00_0011_000_00010u32.to_le_bytes(), "{ P1 = tstbit(R17, #0x0); if (P1.new) jump:nt $+#-508 }"); + test_display(&0b0001_0011111_11001_11_00_0011_000_00010u32.to_le_bytes(), "{ P1 = tstbit(R17, #0x0); if (!P1.new) jump:nt $+#-508 }"); + test_display(&0b0001_0011111_11001_11_10_0011_000_00010u32.to_le_bytes(), "{ P1 = tstbit(R17, #0x0); if (!P1.new) jump:t $+#-508 }"); - test_display(&0b0001_0101011_11001_11_10_0111_000_00010u32.to_le_bytes(), "{ P0 = cmp.gtu(R17, R7); if (!P0.new) jump:t #-508 }"); - test_display(&0b0001_0101011_11001_11_11_0111_000_00010u32.to_le_bytes(), "{ P1 = cmp.gtu(R17, R7); if (!P1.new) jump:t #-508 }"); + test_display(&0b0001_0101011_11001_11_10_0111_000_00010u32.to_le_bytes(), "{ P0 = cmp.gtu(R17, R7); if (!P0.new) jump:t $+#-508 }"); + test_display(&0b0001_0101011_11001_11_11_0111_000_00010u32.to_le_bytes(), "{ P1 = cmp.gtu(R17, R7); if (!P1.new) jump:t $+#-508 }"); test_invalid(&0b0111_0000010_00000_11_0_0_0000_000_00000u32.to_le_bytes(), DecodeError::InvalidOpcode); @@ -120,23 +120,59 @@ fn general() { test_display(&0b0011_1011011_00100_11_1_0_0010_100_11110u32.to_le_bytes(), "{ memh(R4 + R2<<3) = R30.H }"); test_display(&0b0011_1011101_00100_11_1_0_0010_100_10110u32.to_le_bytes(), "{ memw(R4 + R2<<3) = R6.new }"); - test_display(&0b0010_00000001_0100_11_0_00010_100_10110u32.to_le_bytes(), "{ if (cmp.eq(R4.new, R2)) jump:nt #812 }"); - test_display(&0b0010_00000001_0100_11_1_00010_100_10110u32.to_le_bytes(), "{ if (cmp.eq(R4.new, R2)) jump:t #812 }"); - test_display(&0b0010_00000101_0100_11_0_00010_100_10110u32.to_le_bytes(), "{ if (!cmp.eq(R4.new, R2)) jump:nt #812 }"); - test_display(&0b0010_00001001_0100_11_0_00010_100_10110u32.to_le_bytes(), "{ if (cmp.gt(R4.new, R2)) jump:nt #812 }"); - test_display(&0b0010_00001101_0100_11_0_00010_100_10110u32.to_le_bytes(), "{ if (!cmp.gt(R4.new, R2)) jump:nt #812 }"); - test_display(&0b0010_00010001_0100_11_0_00010_100_10110u32.to_le_bytes(), "{ if (cmp.gtu(R4.new, R2)) jump:nt #812 }"); - test_display(&0b0010_00010101_0100_11_0_00010_100_10110u32.to_le_bytes(), "{ if (!cmp.gtu(R4.new, R2)) jump:nt #812 }"); - test_display(&0b0010_00011001_0100_11_0_00010_100_10110u32.to_le_bytes(), "{ if (cmp.gt(R2, R4.new)) jump:nt #812 }"); - test_display(&0b0010_00100101_0100_11_0_00010_100_10110u32.to_le_bytes(), "{ if (!cmp.gtu(R2, R4.new)) jump:nt #812 }"); + test_display(&0b0010_00000001_0100_11_0_00010_100_10110u32.to_le_bytes(), "{ if (cmp.eq(R4.new, R2)) jump:nt $+#812 }"); + test_display(&0b0010_00000001_0100_11_1_00010_100_10110u32.to_le_bytes(), "{ if (cmp.eq(R4.new, R2)) jump:t $+#812 }"); + test_display(&0b0010_00000101_0100_11_0_00010_100_10110u32.to_le_bytes(), "{ if (!cmp.eq(R4.new, R2)) jump:nt $+#812 }"); + test_display(&0b0010_00001001_0100_11_0_00010_100_10110u32.to_le_bytes(), "{ if (cmp.gt(R4.new, R2)) jump:nt $+#812 }"); + test_display(&0b0010_00001101_0100_11_0_00010_100_10110u32.to_le_bytes(), "{ if (!cmp.gt(R4.new, R2)) jump:nt $+#812 }"); + test_display(&0b0010_00010001_0100_11_0_00010_100_10110u32.to_le_bytes(), "{ if (cmp.gtu(R4.new, R2)) jump:nt $+#812 }"); + test_display(&0b0010_00010101_0100_11_0_00010_100_10110u32.to_le_bytes(), "{ if (!cmp.gtu(R4.new, R2)) jump:nt $+#812 }"); + test_display(&0b0010_00011001_0100_11_0_00010_100_10110u32.to_le_bytes(), "{ if (cmp.gt(R2, R4.new)) jump:nt $+#812 }"); + test_display(&0b0010_00100101_0100_11_0_00010_100_10110u32.to_le_bytes(), "{ if (!cmp.gtu(R2, R4.new)) jump:nt $+#812 }"); test_invalid(&0b0010_00101101_0100_11_0_00010_100_10110u32.to_le_bytes(), DecodeError::InvalidOpcode); test_invalid(&0b0010_00110101_0100_11_0_00010_100_10110u32.to_le_bytes(), DecodeError::InvalidOpcode); test_invalid(&0b0010_00111101_0100_11_0_00010_100_10110u32.to_le_bytes(), DecodeError::InvalidOpcode); - test_display(&0b0010_01000001_0100_11_0_00010_100_10110u32.to_le_bytes(), "{ if (cmp.eq(R4.new, #2)) jump:nt #812 }"); - test_display(&0b0010_01010101_0100_11_1_00010_100_10110u32.to_le_bytes(), "{ if (!cmp.gtu(R4.new, #2)) jump:t #812 }"); - test_display(&0b0010_01011001_0100_11_0_00010_100_10110u32.to_le_bytes(), "{ if (tstbit(R4.new, #0)) jump:nt #812 }"); - test_display(&0b0010_01011101_0100_11_1_00010_100_10110u32.to_le_bytes(), "{ if (!tstbit(R4.new, #0)) jump:t #812 }"); - test_display(&0b0010_01101101_0100_11_1_00010_100_10110u32.to_le_bytes(), "{ if (!cmp.gt(R4.new, #-1)) jump:t #812 }"); + test_display(&0b0010_01000001_0100_11_0_00010_100_10110u32.to_le_bytes(), "{ if (cmp.eq(R4.new, #2)) jump:nt $+#812 }"); + test_display(&0b0010_01010101_0100_11_1_00010_100_10110u32.to_le_bytes(), "{ if (!cmp.gtu(R4.new, #2)) jump:t $+#812 }"); + test_display(&0b0010_01011001_0100_11_0_00010_100_10110u32.to_le_bytes(), "{ if (tstbit(R4.new, #0)) jump:nt $+#812 }"); + test_display(&0b0010_01011101_0100_11_1_00010_100_10110u32.to_le_bytes(), "{ if (!tstbit(R4.new, #0)) jump:t $+#812 }"); + test_display(&0b0010_01101101_0100_11_1_00010_100_10110u32.to_le_bytes(), "{ if (!cmp.gt(R4.new, #-1)) jump:t $+#812 }"); + + test_invalid(&0b0101_000_0100_00001_11_0_00000_000_00000u32.to_le_bytes(), DecodeError::InvalidOpcode); + test_display(&0b0101_000_0101_00001_11_0_00000_000_00000u32.to_le_bytes(), "{ callr R1 }"); + test_display(&0b0101_000_0110_00001_11_0_00000_000_00000u32.to_le_bytes(), "{ callrh R1 }"); + test_invalid(&0b0101_000_0111_00001_11_0_00000_000_00000u32.to_le_bytes(), DecodeError::InvalidOpcode); + test_display(&0b0101_000_1000_00001_11_0_00010_000_00000u32.to_le_bytes(), "{ if (P2) callr R1 }"); + test_display(&0b0101_000_1001_00001_11_0_00010_000_00000u32.to_le_bytes(), "{ if (!P2) callr R1 }"); + test_invalid(&0b0101_000_1010_00001_11_0_00000_000_00000u32.to_le_bytes(), DecodeError::InvalidOpcode); + + test_invalid(&0b0101_001_0011_00001_11_0_00000_000_00000u32.to_le_bytes(), DecodeError::InvalidOpcode); + test_display(&0b0101_001_0100_00001_11_0_00000_000_00000u32.to_le_bytes(), "{ jumpr R1 }"); + test_display(&0b0101_001_0101_00001_11_0_00000_000_00000u32.to_le_bytes(), "{ hintjr(R1) }"); + test_display(&0b0101_001_0110_00001_11_0_00000_000_00000u32.to_le_bytes(), "{ jumprh R1 }"); + test_invalid(&0b0101_001_0111_00001_11_0_00000_000_00000u32.to_le_bytes(), DecodeError::InvalidOpcode); + test_invalid(&0b0101_001_1000_00001_11_0_00000_000_00000u32.to_le_bytes(), DecodeError::InvalidOpcode); + test_invalid(&0b0101_001_1001_00001_11_0_00000_000_00000u32.to_le_bytes(), DecodeError::InvalidOpcode); + test_display(&0b0101_001_1010_00001_11_0_01011_000_00000u32.to_le_bytes(), "{ if (P3.new) jumpr:nt R1 }"); + test_display(&0b0101_001_1011_00001_11_0_01011_000_00000u32.to_le_bytes(), "{ if (!P3.new) jumpr:nt R1 }"); + test_invalid(&0b0101_001_1100_00001_11_0_00000_000_00000u32.to_le_bytes(), DecodeError::InvalidOpcode); + + test_display(&0b0101_010_0000_00000_11_0_01000_000_00010u32.to_le_bytes(), "{ trap0(#0x40) }"); + test_display(&0b0101_010_0010_00010_11_0_01000_000_00010u32.to_le_bytes(), "{ pause(#0x240) }"); + test_display(&0b0101_010_0100_00010_11_0_01000_000_00010u32.to_le_bytes(), "{ trap1(R2, #0x40) }"); + test_invalid(&0b0101_010_0110_00010_11_0_01000_000_00010u32.to_le_bytes(), DecodeError::InvalidOpcode); + test_invalid(&0b0101_011_0101_00010_11_0_01000_000_00010u32.to_le_bytes(), DecodeError::InvalidOpcode); + test_display(&0b0101_011_0110_00010_11_0_00000_000_00000u32.to_le_bytes(), "{ icinva(R2) }"); + test_display(&0b0101_011_1110_00000_11_0_00000_000_00010u32.to_le_bytes(), "{ isync }"); + test_display(&0b0101_011_1111_00000_11_0_10000_000_00010u32.to_le_bytes(), "{ unpause }"); + + test_display(&0b0101_100_0000_00000_11_0_00000_000_00010u32.to_le_bytes(), "{ jump $+#4 }"); + test_display(&0b0101_101_0000_00000_11_0_00000_000_00010u32.to_le_bytes(), "{ call $+#4 }"); + + test_display(&0b0101_110_0000_00000_11_0_00001_000_00010u32.to_le_bytes(), "{ if (P1) jump:nt $+#4 }"); + test_display(&0b0101_110_0001_00000_11_0_01001_000_00010u32.to_le_bytes(), "{ if (!P1.new) jump:nt $+#4 }"); + test_display(&0b0101_110_1001_00000_11_0_00001_000_00010u32.to_le_bytes(), "{ if (!P1) call $+#4 }"); + test_invalid(&0b0101_110_1001_00000_11_0_01001_000_00010u32.to_le_bytes(), DecodeError::InvalidOpcode); test_display(&0b0110_0010001_00110_11_0_000101_00_10110u32.to_le_bytes(), "{ C22 = R6 }"); test_display(&0b0110_0011001_00110_11_0_000101_00_10110u32.to_le_bytes(), "{ C23:22 = R7:6 }"); @@ -205,5 +241,16 @@ fn general() { test_display(&0b1011_1000001_00100_11_1_0_0000_001_10110u32.to_le_bytes(), "{ R22 = add(R4, #-31999) }"); + test_display(&0b1111_1001000_00100_11_1_00011_001_00110u32.to_le_bytes(), "{ if (P1.new) R6 = and(R4, R3) }"); + test_display(&0b1111_1001001_00100_11_1_00011_001_00110u32.to_le_bytes(), "{ if (P1.new) R6 = or(R4, R3) }"); + test_invalid(&0b1111_1001010_00100_11_1_00011_001_00110u32.to_le_bytes(), DecodeError::InvalidOpcode); + test_display(&0b1111_1001011_00100_11_1_00011_001_00110u32.to_le_bytes(), "{ if (P1.new) R6 = xor(R4, R3) }"); + + test_display(&0b1111_1011000_00100_11_1_00011_001_00110u32.to_le_bytes(), "{ if (P1.new) R6 = add(R4, R3) }"); + test_display(&0b1111_1011001_00100_11_1_00011_001_00110u32.to_le_bytes(), "{ if (P1.new) R6 = sub(R4, R3) }"); + test_invalid(&0b1111_1011101_00100_11_1_00011_001_00110u32.to_le_bytes(), DecodeError::InvalidOpcode); + + test_display(&0b1111_1101000_00100_11_1_00011_001_00110u32.to_le_bytes(), "{ if (P1.new) R7:6 = contains(R4, R3) }"); + // TODO: testcase for Rn=add(pc,#nn) } |