summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoriximeow <me@iximeow.net>2025-04-08 23:44:42 -0700
committeriximeow <me@iximeow.net>2025-04-08 23:44:42 -0700
commit2bf4b55491d2100734089652fce2048d711ac71a (patch)
tree4a009d2229ef1decb4ab4001166e4b7a2de2de51
parentf902e9a1df4f7b03c029491e72aca72977d23f5b (diff)
supervisor mode instructions, control register names
-rw-r--r--src/display.rs64
-rw-r--r--src/lib.rs118
-rw-r--r--tests/from_brain.rs34
3 files changed, 203 insertions, 13 deletions
diff --git a/src/display.rs b/src/display.rs
index 071633c..0c8e366 100644
--- a/src/display.rs
+++ b/src/display.rs
@@ -775,6 +775,26 @@ impl fmt::Display for Opcode {
// shouldn't ever really be printed as `addasl` (instruction display has more complex
// rules here.
Opcode::AddAslRegReg => { f.write_str("addasl") },
+ Opcode::Swi => { f.write_str("swi") },
+ Opcode::Cswi => { f.write_str("cswi") },
+ Opcode::Ciad => { f.write_str("ciad") },
+ Opcode::Wait => { f.write_str("wait") },
+ Opcode::Resume => { f.write_str("resume") },
+ Opcode::Stop => { f.write_str("stop") },
+ Opcode::Start => { f.write_str("start") },
+ Opcode::Nmi => { f.write_str("nmi") },
+ Opcode::Setimask => { f.write_str("setimask") },
+ Opcode::Siad => { f.write_str("siad") },
+ Opcode::Brkpt => { f.write_str("brkpt") },
+ Opcode::TlbLock => { f.write_str("tlblock") },
+ Opcode::K0Lock => { f.write_str("k0lock") },
+ Opcode::Crswap => { f.write_str("crswap") },
+ Opcode::Getimask => { f.write_str("getimask") },
+ Opcode::Iassignr => { f.write_str("iassignr") },
+ Opcode::Icdatar => { f.write_str("icdatar") },
+ Opcode::Ictagr => { f.write_str("ictagr") },
+ Opcode::Icinvidx => { f.write_str("icinvidx") },
+
Opcode::SubAsl => { f.write_str("subasl") },
Opcode::AndAsl => { f.write_str("andasl") },
Opcode::AddClb => { f.write_str("addclb") },
@@ -919,18 +939,34 @@ impl fmt::Display for Operand {
write!(f, "R{}", reg)
}
Operand::Cr { reg } => {
+ // V69 Table 2-2 Aliased control registers
+ static CR_NAMES: [&'static str; 32] = [
+ "sa0", "lc0", "sa1", "lc1",
+ "P3:0", "C5", "M0", "M1",
+ "usr", "pc", "ugp", "gp",
+ "cs0", "cs1", "upcyclelo", "upcyclehi",
+ "framelimit", "framekey", "pktcountlo", "pktcounthi",
+ "C20", "C21", "C22", "C23",
+ "C24", "C25", "C26", "C27",
+ "C28", "C29", "utimerlo", "utimerhi",
+ ];
+ f.write_str(CR_NAMES[*reg as usize])
+ }
+ Operand::Sr { reg } => {
+ // TODO: System control register transfer
+ // from v62
match reg {
- 9 => {
- f.write_str("pc")
+ 0 => {
+ f.write_str("sgp0")
+ }
+ 1 => {
+ f.write_str("sgp1")
}
reg => {
- write!(f, "C{}", reg)
+ write!(f, "S{}", reg)
}
}
}
- Operand::Sr { reg } => {
- write!(f, "S{}", reg)
- }
Operand::GprNew { reg } => {
write!(f, "R{}.new", reg)
}
@@ -950,10 +986,22 @@ impl fmt::Display for Operand {
write!(f, "R{}:{}*", reg_low + 1, reg_low)
}
Operand::Cr64b { reg_low } => {
- write!(f, "C{}:{}", reg_low + 1, reg_low)
+ if *reg_low == 14 {
+ f.write_str("upcycle")
+ } else if *reg_low == 18 {
+ f.write_str("pktcount")
+ } else if *reg_low == 30 {
+ f.write_str("utimer")
+ } else {
+ write!(f, "C{}:{}", reg_low + 1, reg_low)
+ }
}
Operand::Sr64b { reg_low } => {
- write!(f, "S{}:{}", reg_low + 1, reg_low)
+ if *reg_low == 0 {
+ f.write_str("sgp1:0")
+ } else {
+ write!(f, "S{}:{}", reg_low + 1, reg_low)
+ }
}
Operand::PredicateReg { reg } => {
write!(f, "P{}", reg)
diff --git a/src/lib.rs b/src/lib.rs
index bb36e66..6e64f97 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -825,6 +825,26 @@ pub enum Opcode {
// `add(x, asl(y, z))` that would be used below. terrible.
AddAslRegReg,
+ Swi,
+ Cswi,
+ Ciad,
+ Wait,
+ Resume,
+ Stop,
+ Start,
+ Nmi,
+ Setimask,
+ Siad,
+ Brkpt,
+ TlbLock,
+ K0Lock,
+ Crswap,
+ Getimask,
+ Iassignr,
+ Icdatar,
+ Ictagr,
+ Icinvidx,
+
AndAnd = 0x8000,
AndOr,
OrAnd,
@@ -2366,6 +2386,14 @@ fn decode_instruction<
let xxxxx = reg_b16(inst);
handler.on_source_decoded(Operand::gpr(xxxxx))?;
handler.on_source_decoded(Operand::imm_u8(u_8 as u8))?;
+ } else if minbits == 0b110 {
+ handler.on_opcode_decoded(Opcode::Icdatar)?;
+ handler.on_dest_decoded(Operand::gpr(reg_b0(inst)))?;
+ handler.on_source_decoded(Operand::gpr(reg_b16(inst)))?;
+ } else if minbits == 0b111 {
+ handler.on_opcode_decoded(Opcode::Ictagr)?;
+ handler.on_dest_decoded(Operand::gpr(reg_b0(inst)))?;
+ handler.on_source_decoded(Operand::gpr(reg_b16(inst)))?;
} else {
opcode_check!(false);
}
@@ -2378,6 +2406,9 @@ fn decode_instruction<
handler.on_opcode_decoded(Opcode::Icinva)?;
handler.on_source_decoded(Operand::gpr(reg_b16(inst)))?;
opcode_check!(inst & 0x3800 == 0x0000);
+ } else if minbits == 0b0111 {
+ handler.on_opcode_decoded(Opcode::Icinvidx)?;
+ handler.on_source_decoded(Operand::gpr(reg_b16(inst)))?;
} else if minbits == 0b1110 {
handler.on_opcode_decoded(Opcode::Isync)?;
opcode_check!(inst & 0x1f23ff == 0x000002);
@@ -2597,6 +2628,71 @@ fn decode_instruction<
_ => { return Err(DecodeError::InvalidOpcode); }
};
}
+ 0b0100000 => {
+ // 000 -> swi
+ // 001 -> cswi
+ // 011 -> ciad
+ // TODO:
+ static OPS: [Option<Opcode>; 8] = [
+ Some(Opcode::Swi), Some(Opcode::Cswi), None, Some(Opcode::Ciad),
+ None, None, None, None,
+ ];
+ handler.on_opcode_decoded(decode_opcode!(OPS[((inst >> 5) & 0b111) as usize]))?;
+ handler.on_source_decoded(Operand::gpr(reg_b16(inst)))?;
+ }
+ 0b0100010 => {
+ // 000 -> wait
+ // 010 -> resume
+ // TODO:
+ static OPS: [Option<Opcode>; 8] = [
+ Some(Opcode::Wait), None, Some(Opcode::Resume), None,
+ None, None, None, None,
+ ];
+ handler.on_opcode_decoded(decode_opcode!(OPS[((inst >> 5) & 0b111) as usize]))?;
+ handler.on_source_decoded(Operand::gpr(reg_b16(inst)))?;
+ }
+ 0b0100011 => {
+ // 000 -> stop
+ // 001 -> start
+ // 010 -> nmi
+ // TODO:
+ static OPS: [Option<Opcode>; 8] = [
+ Some(Opcode::Stop), Some(Opcode::Start), Some(Opcode::Nmi), None,
+ None, None, None, None,
+ ];
+ handler.on_opcode_decoded(decode_opcode!(OPS[((inst >> 5) & 0b111) as usize]))?;
+ handler.on_source_decoded(Operand::gpr(reg_b16(inst)))?;
+ }
+ 0b0100100 => {
+ // TODO: double check encoding and manual
+ // 000 -> setimask
+ // 011 -> siad
+ static OPS: [Option<Opcode>; 8] = [
+ Some(Opcode::Setimask), None, None, Some(Opcode::Siad),
+ None, None, None, None,
+ ];
+ let opc = (inst >> 5) & 0b111;
+ handler.on_opcode_decoded(decode_opcode!(OPS[opc as usize]))?;
+ if opc == 0b000 {
+ handler.on_source_decoded(Operand::pred(reg_b0(inst) & 0b11))?;
+ }
+ handler.on_source_decoded(Operand::gpr(reg_b16(inst)))?;
+ }
+ 0b0101000 => {
+ handler.on_opcode_decoded(Opcode::Crswap)?;
+ handler.on_source_decoded(Operand::gpr(reg_b16(inst)))?;
+ handler.on_source_decoded(Operand::sr(0))?;
+ }
+ 0b0101001 => {
+ handler.on_opcode_decoded(Opcode::Crswap)?;
+ handler.on_source_decoded(Operand::gpr(reg_b16(inst)))?;
+ handler.on_source_decoded(Operand::sr(1))?;
+ }
+ 0b0110000 => {
+ handler.on_opcode_decoded(Opcode::Getimask)?;
+ handler.on_dest_decoded(Operand::gpr(reg_b0(inst)))?;
+ handler.on_source_decoded(Operand::gpr(reg_b16(inst)))?;
+ }
0b0111000 => {
// not in V73! store to supervisor register?
let sssss = reg_b16(inst);
@@ -2607,8 +2703,14 @@ fn decode_instruction<
}
// TODO: 1001000 loop0 goes here
0b1100001 => {
- opcode_check!((inst >> 5) & 0b111 == 0);
- handler.on_opcode_decoded(Opcode::Barrier)?;
+ // 000 -> brkpt
+ // 001 -> tlblock
+ // 011 -> k0lock
+ static OPS: [Option<Opcode>; 8] = [
+ Some(Opcode::Brkpt), Some(Opcode::TlbLock), None, Some(Opcode::K0Lock),
+ None, None, None, None,
+ ];
+ handler.on_opcode_decoded(decode_opcode!(OPS[((inst >> 5) & 0b111) as usize]))?;
}
0b1101000 => {
// not in V73! store to supervisor register?
@@ -2618,6 +2720,18 @@ fn decode_instruction<
handler.on_dest_decoded(Operand::srpair(ddddddd as u8)?)?;
handler.on_source_decoded(Operand::gprpair(sssss)?)?;
}
+ 0b1101100 => {
+ operand_check!(reg_b0(inst) == 0);
+ // TODO:
+ handler.on_opcode_decoded(Opcode::Crswap)?;
+ handler.on_source_decoded(Operand::gprpair(reg_b16(inst))?)?;
+ handler.on_source_decoded(Operand::srpair(0)?)?;
+ }
+ 0b1110011 => {
+ handler.on_opcode_decoded(Opcode::Iassignr)?;
+ handler.on_dest_decoded(Operand::gpr(reg_b0(inst)))?;
+ handler.on_source_decoded(Operand::gpr(reg_b16(inst)))?;
+ }
0b1110100 | 0b1110101 |
0b1110110 | 0b1110111 => {
// not in V73! load supervisor register?
diff --git a/tests/from_brain.rs b/tests/from_brain.rs
index 14be5ff..df6880a 100644
--- a/tests/from_brain.rs
+++ b/tests/from_brain.rs
@@ -18,8 +18,36 @@ fn test_invalid(bytes: &[u8], expected: DecodeError) {
assert_eq!(err, expected);
}
+// mentioned in the V62 manual, not later?
+// not sure if these are still what they seem in later versions, but until demonstrated
+// otherwise...
#[test]
fn supervisor() {
+ test_display(&0b0101_010_1101_00010_11_0_01000_000_00110u32.to_le_bytes(), "{ R6 = icdatar(R2) }");
+ test_display(&0b0101_010_1111_00010_11_0_01000_000_00110u32.to_le_bytes(), "{ R6 = ictagr(R2) }");
+ test_display(&0b0101_011_0111_00010_11_0_01000_000_00110u32.to_le_bytes(), "{ icinvidx(R2) }");
+
+ test_display(&0b0110_01_00000_00110_11_0_00010_000_10110u32.to_le_bytes(), "{ swi(R6) }");
+ test_display(&0b0110_01_00000_00110_11_0_00010_001_10110u32.to_le_bytes(), "{ cswi(R6) }");
+ test_display(&0b0110_01_00000_00110_11_0_00010_011_10110u32.to_le_bytes(), "{ ciad(R6) }");
+ test_display(&0b0110_01_00010_00110_11_0_00010_000_10110u32.to_le_bytes(), "{ wait(R6) }");
+ test_display(&0b0110_01_00010_00110_11_0_00010_010_10110u32.to_le_bytes(), "{ resume(R6) }");
+ test_display(&0b0110_01_00011_00110_11_0_00010_000_10110u32.to_le_bytes(), "{ stop(R6) }");
+ test_display(&0b0110_01_00011_00110_11_0_00010_001_10110u32.to_le_bytes(), "{ start(R6) }");
+ test_display(&0b0110_01_00011_00110_11_0_00010_010_10110u32.to_le_bytes(), "{ nmi(R6) }");
+ test_display(&0b0110_01_00100_00110_11_0_00010_000_10110u32.to_le_bytes(), "{ setimask(P2, R6) }");
+ test_display(&0b0110_01_00100_00110_11_0_00010_011_10110u32.to_le_bytes(), "{ siad(R6) }");
+ test_display(&0b0110_01_01000_00110_11_0_00010_000_10110u32.to_le_bytes(), "{ crswap(R6, sgp0) }");
+ test_display(&0b0110_01_01001_00110_11_0_00010_000_10110u32.to_le_bytes(), "{ crswap(R6, sgp1) }");
+ test_display(&0b0110_01_10000_00110_11_0_00010_000_10110u32.to_le_bytes(), "{ R22 = getimask(R6) }");
+ test_display(&0b0110_11_00001_00110_11_0_00010_000_10110u32.to_le_bytes(), "{ brkpt }");
+ test_display(&0b0110_11_00001_00110_11_0_00010_001_00000u32.to_le_bytes(), "{ tlblock }");
+ test_display(&0b0110_11_00001_00110_11_0_00010_011_00000u32.to_le_bytes(), "{ k0lock }");
+ test_display(&0b0110_11_01100_00110_11_0_00010_000_00000u32.to_le_bytes(), "{ crswap(R7:6, sgp1:0) }");
+ test_invalid(&0b0110_11_01100_00110_11_0_00010_000_00001u32.to_le_bytes(), DecodeError::InvalidOperand);
+ test_display(&0b0110_11_10011_00110_11_0_00010_011_10110u32.to_le_bytes(), "{ R22 = iassignr(R6) }");
+
+ // ok
test_display(&0b0110_0111000_00010_11_0011010_0000110u32.to_le_bytes(), "{ S6 = R2 }");
test_display(&0b0110_1101000_00010_11_0011010_0000110u32.to_le_bytes(), "{ S7:6 = R3:2 }");
@@ -264,6 +292,7 @@ fn inst_0101() {
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 }");
@@ -305,6 +334,7 @@ fn inst_0110() {
test_display(&0b0110_0010010_00110_11_0_00100_011_01000u32.to_le_bytes(), "{ diag1(R7:6, R5:4) }");
test_display(&0b0110_0011001_00110_11_0_000101_00_10110u32.to_le_bytes(), "{ C23:22 = R7:6 }");
+
test_display(&0b0110_1000000_00110_11_0_000101_00_10110u32.to_le_bytes(), "{ R23:22 = C7:6 }");
test_display(&0b0110_1001000_00110_11_0_000101_00_01010u32.to_le_bytes(), "{ loop0($+#36, #0xd2) }");
@@ -313,7 +343,7 @@ fn inst_0110() {
test_display(&0b0110_1001110_00110_11_0_000101_00_01010u32.to_le_bytes(), "{ P3 = sp2loop0($+#36, #0xd2) }");
test_display(&0b0110_1001111_00110_11_0_000101_00_01010u32.to_le_bytes(), "{ P3 = sp3loop0($+#36, #0xd2) }");
- test_display(&0b0110_1010000_00110_11_0_000101_00_10110u32.to_le_bytes(), "{ R22 = C6 }");
+ test_display(&0b0110_1010000_00110_11_0_000101_00_10110u32.to_le_bytes(), "{ R22 = M0 }");
test_display(&0b0110_1010010_01001_11_0_000101_00_10110u32.to_le_bytes(), "{ R22 = add(pc, #0x5) }");
test_display(&0b0110_1011000_00011_11_0_000100_00_00001u32.to_le_bytes(), "{ P1 = and(P2, P3) }");
@@ -335,8 +365,6 @@ fn inst_0110() {
test_display(&0b0110_1011111_00011_11_0_000100_00_10001u32.to_le_bytes(), "{ P1 = or(P2, !P3) }");
test_display(&0b0110_1011111_10011_11_0_000100_00_10001u32.to_le_bytes(), "{ P1 = or(P3, or(P2, !P0)) }");
- test_display(&0b0110_1100001_01001_11_0_000000_00_00000u32.to_le_bytes(), "{ barrier }");
-
test_display(&0b0110_1111111_01010_11_0_001100_10_00011u32.to_le_bytes(), "{ R3 = movlen(R6, R11:10) }");
}