From 93f87fdc57a1a7106137a173f048267d9bb2d052 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Thu, 3 Sep 2020 15:39:59 -0700 Subject: [PATCH] expose methods to python for conversion between SPR values and structs used to represent them --- src/python.rs | 58 +++++++++++++++++++++++- tests/test_power_instruction_analyzer.py | 57 +++++++++++++++++++++++ 2 files changed, 113 insertions(+), 2 deletions(-) diff --git a/src/python.rs b/src/python.rs index 4370c16..502353c 100644 --- a/src/python.rs +++ b/src/python.rs @@ -6,7 +6,11 @@ use crate::{ CarryFlags, ConditionRegister, Instr, InstructionInput, InstructionOutput, OverflowFlags, }; -use pyo3::{exceptions::ValueError, prelude::*, wrap_pyfunction, PyObjectProtocol}; +use pyo3::{ + exceptions::{IndexError, OverflowError, ValueError}, + prelude::*, + wrap_pyfunction, PyObjectProtocol, +}; use std::{borrow::Cow, cell::RefCell, fmt}; trait ToPythonRepr { @@ -251,6 +255,19 @@ fn power_instruction_analyzer(_py: Python, m: &PyModule) -> PyResult<()> { } } + #[pymethods] + impl PyOverflowFlags { + #[text_signature = "(xer)"] + #[staticmethod] + pub fn from_xer(xer: u64) -> OverflowFlags { + OverflowFlags::from_xer(xer) + } + #[text_signature = "($self)"] + pub fn to_xer(&self) -> u64 { + self.value.to_xer() + } + } + wrap_type! { #[pymodule(m)] #[pyclass(name = CarryFlags)] @@ -265,6 +282,19 @@ fn power_instruction_analyzer(_py: Python, m: &PyModule) -> PyResult<()> { } } + #[pymethods] + impl PyCarryFlags { + #[text_signature = "(xer)"] + #[staticmethod] + pub fn from_xer(xer: u64) -> CarryFlags { + CarryFlags::from_xer(xer) + } + #[text_signature = "($self)"] + pub fn to_xer(&self) -> u64 { + self.value.to_xer() + } + } + wrap_type! { #[pymodule(m)] #[pyclass(name = ConditionRegister)] @@ -283,12 +313,36 @@ fn power_instruction_analyzer(_py: Python, m: &PyModule) -> PyResult<()> { } } + #[pymethods] + impl PyConditionRegister { + #[text_signature = "(bits)"] + #[staticmethod] + fn from_4_bits(bits: u8) -> PyResult { + if bits > 0xF { + OverflowError::into("int too big to convert")?; + } + Ok(ConditionRegister::from_4_bits(bits)) + } + #[text_signature = "(cr, field_index)"] + #[staticmethod] + fn from_cr_field(cr: u32, mut field_index: isize) -> PyResult { + // adjust for python-style indexes + if field_index < 0 { + field_index += ConditionRegister::CR_FIELD_COUNT as isize; + } + if field_index < 0 || field_index >= ConditionRegister::CR_FIELD_COUNT as isize { + IndexError::into("field_index out of range")?; + } + Ok(ConditionRegister::from_cr_field(cr, field_index as usize)) + } + } + wrap_type! { #[pymodule(m)] #[pyclass(name = InstructionInput)] #[wrapped(value: InstructionInput)] #[args(ra="None", rb="None", rc="None", carry="None", overflow="None")] - #[text_signature = "(ra, rb, rc, carry, overflow)"] + #[text_signature = "(ra=None, rb=None, rc=None, carry=None, overflow=None)"] struct PyInstructionInput { #[set = set_ra] ra: Option, diff --git a/tests/test_power_instruction_analyzer.py b/tests/test_power_instruction_analyzer.py index 43c86b5..15b3854 100644 --- a/tests/test_power_instruction_analyzer.py +++ b/tests/test_power_instruction_analyzer.py @@ -10,6 +10,16 @@ class TestOverflowFlags(unittest.TestCase): self.assertEqual(pia.OverflowFlags.__text_signature__, "(so, ov, ov32)") + def test_from_to_xer(self): + v = pia.OverflowFlags.from_xer(0x186864BDF558B0F6) + self.assertEqual(str(v), + '{"so":true,"ov":true,"ov32":true}') + self.assertEqual(hex(v.to_xer()), '0xc0080000') + v = pia.OverflowFlags.from_xer(0x72242678A4DB14BB) + self.assertEqual(str(v), + '{"so":true,"ov":false,"ov32":true}') + self.assertEqual(hex(v.to_xer()), '0x80080000') + def test_fields(self): v = pia.OverflowFlags(so=False, ov=False, ov32=True) self.assertEqual(v.so, False) @@ -35,6 +45,20 @@ class TestCarryFlags(unittest.TestCase): self.assertEqual(pia.CarryFlags.__text_signature__, "(ca, ca32)") + def test_from_to_xer(self): + v = pia.CarryFlags.from_xer(0x186864BDF558B0F6) + self.assertEqual(str(v), + '{"ca":true,"ca32":false}') + self.assertEqual(hex(v.to_xer()), '0x20000000') + v = pia.CarryFlags.from_xer(0xDAF403DEF4ECEBB9) + self.assertEqual(str(v), + '{"ca":true,"ca32":true}') + self.assertEqual(hex(v.to_xer()), '0x20040000') + v = pia.CarryFlags.from_xer(0x7B276724952F507F) + self.assertEqual(str(v), + '{"ca":false,"ca32":true}') + self.assertEqual(hex(v.to_xer()), '0x40000') + def test_fields(self): v = pia.CarryFlags(ca=False, ca32=True) self.assertEqual(v.ca, False) @@ -72,6 +96,39 @@ class TestConditionRegister(unittest.TestCase): v.so = False self.assertEqual(v.so, False) + def test_from_4_bits(self): + with self.assertRaises(OverflowError): + pia.ConditionRegister.from_4_bits(-1) + with self.assertRaisesRegex(OverflowError, "int too big to convert"): + pia.ConditionRegister.from_4_bits(0x10) + v = pia.ConditionRegister.from_4_bits(0xD) + self.assertEqual(str(v), + '{"lt":true,"gt":true,"eq":false,"so":true}') + v = pia.ConditionRegister.from_4_bits(0x4) + self.assertEqual(str(v), + '{"lt":false,"gt":true,"eq":false,"so":false}') + + def test_from_cr_field(self): + with self.assertRaisesRegex(IndexError, "^field_index out of range$"): + pia.ConditionRegister.from_cr_field(0x0, -9) + with self.assertRaisesRegex(IndexError, "^field_index out of range$"): + pia.ConditionRegister.from_cr_field(0x0, 8) + cr = 0x6C42D586 + values = [ + '{"lt":false,"gt":true,"eq":true,"so":false}', + '{"lt":true,"gt":true,"eq":false,"so":false}', + '{"lt":false,"gt":true,"eq":false,"so":false}', + '{"lt":false,"gt":false,"eq":true,"so":false}', + '{"lt":true,"gt":true,"eq":false,"so":true}', + '{"lt":false,"gt":true,"eq":false,"so":true}', + '{"lt":true,"gt":false,"eq":false,"so":false}', + '{"lt":false,"gt":true,"eq":true,"so":false}', + ] + for i in range(-8, 8): + with self.subTest(i=i): + v = pia.ConditionRegister.from_cr_field(cr, i) + self.assertEqual(str(v), values[i]) + def test_str_repr(self): v = pia.ConditionRegister(lt=False, gt=True, eq=False, so=True) self.assertEqual(str(v), -- 2.30.2