diff --git a/.gitignore b/.gitignore index 2c6ddfd..6559e83 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,4 @@ bundles dist **/*.egg-info .vscode +.venv diff --git a/README.rst b/README.rst index 9b83f08..96dcbc8 100644 --- a/README.rst +++ b/README.rst @@ -32,8 +32,6 @@ This is easily achieved by downloading Installing from PyPI ===================== -.. note:: This library is not available on PyPI yet. Install documentation is included - as a standard element. Stay tuned for PyPI availability! On supported GNU/Linux systems like the Raspberry Pi, you can install the driver locally `from PyPI `_. To install for current user: diff --git a/adafruit_pioasm.py b/adafruit_pioasm.py index 266d962..263e986 100644 --- a/adafruit_pioasm.py +++ b/adafruit_pioasm.py @@ -115,7 +115,12 @@ class Program: # pylint: disable=too-few-public-methods raise SyntaxError(f"Invalid jmp target {repr(target)}") if len(instruction) > 2: - assembled[-1] |= CONDITIONS.index(instruction[1]) << 5 + try: + assembled[-1] |= CONDITIONS.index(instruction[1]) << 5 + except ValueError as exc: + raise ValueError( + f"Invalid jmp condition '{instruction[1]}'" + ) from exc elif instruction[0] == "wait": # instr delay p sr index @@ -163,7 +168,10 @@ class Program: # pylint: disable=too-few-public-methods source = instruction[-1] source_split = mov_splitter(source) if len(source_split) == 1: - assembled[-1] |= MOV_SOURCES.index(source) + try: + assembled[-1] |= MOV_SOURCES.index(source) + except ValueError as exc: + raise ValueError(f"Invalid mov source '{source}'") from exc else: assembled[-1] |= MOV_SOURCES.index(source_split[1]) if source[:1] == "!": @@ -195,7 +203,10 @@ class Program: # pylint: disable=too-few-public-methods elif instruction[0] == "set": # instr delay dst data assembled.append(0b111_00000_000_00000) - assembled[-1] |= SET_DESTINATIONS.index(instruction[1]) << 5 + try: + assembled[-1] |= SET_DESTINATIONS.index(instruction[1]) << 5 + except ValueError as exc: + raise ValueError(f"Invalid set destination '{instruction[1]}'") from exc value = int(instruction[-1]) if not 0 <= value <= 31: raise RuntimeError("Set value out of range") diff --git a/docs/index.rst b/docs/index.rst index ed5921d..96d9e30 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -24,6 +24,7 @@ Table of Contents :caption: Tutorials Getting Started with Raspberry Pi Pico and CircuitPython + An Introduction to RP2040 PIO with CircuitPython .. toctree:: :caption: Related Products diff --git a/tests/testpioasm.py b/tests/testpioasm.py index 684702a..0069b0e 100644 --- a/tests/testpioasm.py +++ b/tests/testpioasm.py @@ -18,7 +18,7 @@ def nice_opcode(o): return o[:3] + "_" + o[3:8] + "_" + o[8:] -class TestNop(unittest.TestCase): +class AssembleChecks(unittest.TestCase): def assertAssemblesTo(self, source, expected): actual = adafruit_pioasm.assemble(source) expected_bin = [nice_opcode(x) for x in expected] @@ -29,13 +29,18 @@ class TestNop(unittest.TestCase): f"Assembling {source!r}: Expected {expected_bin}, got {actual_bin}", ) - def assertAssemblyFails(self, source): - self.assertRaises(RuntimeError, adafruit_pioasm.assemble, source) + def assertAssemblyFails(self, source, match=None, errtype=RuntimeError): + if match: + self.assertRaisesRegex(errtype, match, adafruit_pioasm.assemble, source) + else: + self.assertRaises(errtype, adafruit_pioasm.assemble, source) def assertPioKwargs(self, source, **kw): program = adafruit_pioasm.Program(source) self.assertEqual(kw, program.pio_kwargs) + +class TestNop(AssembleChecks): def testNonsense(self): self.assertAssemblyFails("nope") @@ -64,6 +69,12 @@ class TestNop(unittest.TestCase): ".side_set 1 opt\nnop side 0 [7]", [0b101_10111_010_00_010] ) + def testSet(self): + # non happy path + self.assertAssemblyFails( + "set isr, 1", match="Invalid set destination 'isr'", errtype=ValueError + ) + def testJmp(self): self.assertAssemblesTo("l:\njmp l", [0b000_00000_000_00000]) self.assertAssemblesTo("l:\njmp 7", [0b000_00000_000_00111]) @@ -75,6 +86,10 @@ class TestNop(unittest.TestCase): self.assertAssemblesTo("jmp x!=y, l\nl:", [0b000_00000_101_00001]) self.assertAssemblesTo("jmp pin, l\nl:", [0b000_00000_110_00001]) self.assertAssemblesTo("jmp !osre, l\nl:", [0b000_00000_111_00001]) + # non happy path + self.assertAssemblyFails( + "jmp x--., l\nl:", match="Invalid jmp condition 'x--.'", errtype=ValueError + ) def testWait(self): self.assertAssemblesTo("wait 0 gpio 0", [0b001_00000_0_00_00000]) @@ -99,3 +114,23 @@ class TestNop(unittest.TestCase): self.assertPioKwargs("", sideset_count=0, sideset_enable=False) self.assertPioKwargs(".side_set 1", sideset_count=1, sideset_enable=False) self.assertPioKwargs(".side_set 3 opt", sideset_count=3, sideset_enable=True) + + +class TestMov(AssembleChecks): + def testMovNonHappy(self): + # non happy path + self.assertAssemblyFails( + "mov x, blah", match="Invalid mov source 'blah'", errtype=ValueError + ) + + def testMovInvert(self): + # test moving and inverting + self.assertAssemblesTo("mov x, ~ x", [0b101_00000_001_01_001]) + self.assertAssemblesTo("mov x, ~ x", [0b101_00000_001_01_001]) + self.assertAssemblesTo("mov x, ~x", [0b101_00000_001_01_001]) + self.assertAssemblesTo("mov x, !x", [0b101_00000_001_01_001]) + + def testMovReverse(self): + # test moving and reversing bits + self.assertAssemblesTo("mov x, :: x", [0b101_00000_001_10_001]) + self.assertAssemblesTo("mov x, ::x", [0b101_00000_001_10_001])