aboutsummaryrefslogtreecommitdiffstats
path: root/bits.py
diff options
context:
space:
mode:
Diffstat (limited to 'bits.py')
-rw-r--r--bits.py108
1 files changed, 70 insertions, 38 deletions
diff --git a/bits.py b/bits.py
index dd49665..a40c363 100644
--- a/bits.py
+++ b/bits.py
@@ -2,6 +2,7 @@ import re
from operator import __and__
from functools import reduce
+
class WordBase(object):
def slice_allowempty(self, range):
if not isinstance(range, slice):
@@ -18,15 +19,16 @@ class WordBase(object):
return BitsUnion(self, other)
raise NotImplementedError()
+
class BitsUnion(WordBase):
def __init__(self, *parts):
self.parts = parts
def __repr__(self):
- return "BitsUnion({})".format(', '.join(repr(part) for part in self.parts))
+ return "BitsUnion({})".format(", ".join(repr(part) for part in self.parts))
def as_value(self, **kwargs):
- """ reduce down to a single Bitfield value.
+ """reduce down to a single Bitfield value.
>>> BitsUnion(Bitfield(0x12, 8), Bitfield(0x3, 4), Bitfield(0x4, 4)).as_value()
Bitfield(0x1234, 16)
@@ -37,7 +39,7 @@ class BitsUnion(WordBase):
@property
def size(self):
- """ get the total size.
+ """get the total size.
>>> BitsUnion(Bitfield(0x12, 8), Bitfield(0x3, 4), Bitfield(0x4, 4)).size
16
@@ -47,7 +49,7 @@ class BitsUnion(WordBase):
return sum(part.size for part in self.parts)
def __getitem__(self, range):
- """ slice a union given hi and lo bit indices.
+ """slice a union given hi and lo bit indices.
>>> BitsUnion(Bitfield(0x12, 8), Bitfield(0x3, 4), Bitfield(0x4, 4))[11:4]
Bitfield(0x23, 8)
@@ -62,7 +64,9 @@ class BitsUnion(WordBase):
if hi < lo:
raise ValueError("cant slice reverse range")
elif lo < 0 or hi >= size:
- raise ValueError("slice [{0.start}:{0.stop}] out of range of {1}".format(range, self))
+ raise ValueError(
+ "slice [{0.start}:{0.stop}] out of range of {1}".format(range, self)
+ )
i = size
result = Bitfield(0, 0)
@@ -91,6 +95,7 @@ class BitsUnion(WordBase):
return BitsUnion(*self.parts, other)
raise NotImplementedError()
+
class Bitfield(WordBase):
def __init__(self, val, size, extra=()):
if val < 0:
@@ -98,7 +103,9 @@ class Bitfield(WordBase):
max = -1 - min
if val > max or val < min:
- raise ValueError("value {} out of i{} range [{};{}]".format(val, size, min, max))
+ raise ValueError(
+ "value {} out of i{} range [{};{}]".format(val, size, min, max)
+ )
if val < 0:
val = (1 << size) + val
@@ -108,7 +115,7 @@ class Bitfield(WordBase):
self.extra = extra
def __repr__(self):
- """ format for debugging.
+ """format for debugging.
>>> Bitfield(0, 8)
Bitfield(0x0, 8)
>>> Bitfield(16, 8)
@@ -120,13 +127,13 @@ class Bitfield(WordBase):
>>> Bitfield(3, 4, extra=('hello',))
Bitfield(0x3, 4, extra=('hello',))
"""
- extra = ''
+ extra = ""
if self.extra:
extra = ", extra={!r}".format(self.extra)
return "Bitfield(0x{:x}, {}{})".format(self.val, self.size, extra)
def __str__(self):
- """ format as a word.
+ """format as a word.
>>> str(Bitfield(0, 8))
'00/8'
>>> str(Bitfield(16, 8))
@@ -142,14 +149,14 @@ class Bitfield(WordBase):
"""
digits = 1 + ((self.size - 1) // 4)
base = "{:x}".format(self.val)
- base = (digits - len(base)) * '0' + base
- return '/'.join([base, *self.extra, str(self.size)])
+ base = (digits - len(base)) * "0" + base
+ return "/".join([base, *self.extra, str(self.size)])
def as_value(self, **kwargs):
return self
def __getitem__(self, range):
- """ slice a bitfield given hi and lo bit indices.
+ """slice a bitfield given hi and lo bit indices.
>>> Bitfield(0x7f, 8)[7:4]
Bitfield(0x7, 4)
@@ -182,14 +189,16 @@ class Bitfield(WordBase):
if hi < lo:
raise ValueError("cant slice reverse range")
elif lo < 0 or hi >= self.size:
- raise ValueError("slice [{0.start}:{0.stop}] out of range of {1}".format(range, self))
+ raise ValueError(
+ "slice [{0.start}:{0.stop}] out of range of {1}".format(range, self)
+ )
size = hi - lo + 1
val = (self.val >> lo) & ((1 << size) - 1)
return Bitfield(val, size)
def __and__(self, other):
- """ concatenate multiple bitfields.
+ """concatenate multiple bitfields.
>>> Bitfield(0b00, 2) & Bitfield(0b10, 2)
Bitfield(0x2, 4)
@@ -202,9 +211,12 @@ class Bitfield(WordBase):
return BitsUnion(self, other)
raise NotImplementedError()
+
global_slice = slice
+
+
class LabelRef(WordBase):
- mode_re = re.compile(r'^(imm|off)(\d+)?$')
+ mode_re = re.compile(r"^(imm|off)(\d+)?$")
def __init__(self, label, offset, mode, slice=None):
if isinstance(mode, str):
@@ -214,11 +226,11 @@ class LabelRef(WordBase):
self.label = label
self.mode = mode
- self.slice = slice or global_slice(self.mode[1]-1, 0)
+ self.slice = slice or global_slice(self.mode[1] - 1, 0)
self.offset = offset
def __repr__(self):
- """ format for debugging.
+ """format for debugging.
>>> LabelRef("label", 0, ("imm", 32), slice(31,31))
LabelRef('label', 0, ('imm', 32), 31:31)
@@ -229,10 +241,12 @@ class LabelRef(WordBase):
>>> LabelRef("test", 0, "off")
LabelRef('test', 0, ('off', 32), 31:0)
"""
- return "LabelRef({0!r}, {1}, {2}, {3.start}:{3.stop})".format(self.label, self.offset, self.mode, self.slice)
+ return "LabelRef({0!r}, {1}, {2}, {3.start}:{3.stop})".format(
+ self.label, self.offset, self.mode, self.slice
+ )
def __str__(self):
- """ format as a word.
+ """format as a word.
>>> str(LabelRef("label", 0, ("imm", 32), slice(31,31)))
'label/imm32/[31:31]'
@@ -243,18 +257,20 @@ class LabelRef(WordBase):
>>> str(LabelRef("test", -12, "off"))
'test-12/off32/[31:0]'
"""
- offset = ''
+ offset = ""
if self.offset != 0:
offset = "{:+}".format(self.offset)
- return "{0}{1}/{2[0]}{2[1]}/[{3.start}:{3.stop}]".format(self.label, offset, self.mode, self.slice)
+ return "{0}{1}/{2[0]}{2[1]}/[{3.start}:{3.stop}]".format(
+ self.label, offset, self.mode, self.slice
+ )
@property
def size(self):
return self.slice.start - self.slice.stop + 1
def as_value(self, labels={}, **kwargs):
- """ resolve the reference.
+ """resolve the reference.
>>> labels = {'test': 0xabcd1234, 'main': 0x8000}
>>> LabelRef("test", 0, "imm").as_value(labels=labels)
@@ -287,15 +303,17 @@ class LabelRef(WordBase):
value = labels[self.label] + self.offset
- if self.mode[0] == 'off':
- assert 'pc' in kwargs, TypeError("'pc' is required to resolve offset-references")
- value = value - kwargs['pc']
+ if self.mode[0] == "off":
+ assert "pc" in kwargs, TypeError(
+ "'pc' is required to resolve offset-references"
+ )
+ value = value - kwargs["pc"]
field = Bitfield(value, self.mode[1])
return field[self.slice]
def __getitem__(self, range):
- """ slice a label using hi and lo bit indices.
+ """slice a label using hi and lo bit indices.
>>> label = LabelRef("label", 0, "imm32", slice(31,0))
>>> label.size
@@ -327,15 +345,19 @@ class LabelRef(WordBase):
if hi < lo:
raise ValueError("cant slice reverse range")
elif lo < 0 or hi >= self.size:
- raise ValueError("slice [{0.start}:{0.stop}] out of range of {1}".format(range, self))
+ raise ValueError(
+ "slice [{0.start}:{0.stop}] out of range of {1}".format(range, self)
+ )
new_slice = slice(self.slice.stop + hi, self.slice.stop + lo)
return LabelRef(self.label, self.offset, self.mode, new_slice)
+
empty = Bitfield(0, 0)
+
def u(num, bits):
- """ parse an unsigned integer into a bitfield.
+ """parse an unsigned integer into a bitfield.
>>> u(0x08, 8)
Bitfield(0x8, 8)
@@ -355,13 +377,15 @@ def u(num, bits):
raise ValueError("negative value not allowed: {}".format(num))
if num.bit_length() > bits:
- raise ValueError("value {} (u{}) too large for u{} field"
- .format(num, num.bit_length(), bits))
+ raise ValueError(
+ "value {} (u{}) too large for u{} field".format(num, num.bit_length(), bits)
+ )
return Bitfield(num, bits)
+
def i(num, bits):
- """ parse a signed integer into a bitfield.
+ """parse a signed integer into a bitfield.
>>> i(8, 8)
Bitfield(0x8, 8)
@@ -385,16 +409,20 @@ def i(num, bits):
max = -1 - min
if num > max or num < min:
- raise ValueError("value {} (i{}) too large for i{} field [{};{}]"
- .format(num, num.bit_length()+1, bits, min, max))
+ raise ValueError(
+ "value {} (i{}) too large for i{} field [{};{}]".format(
+ num, num.bit_length() + 1, bits, min, max
+ )
+ )
if num < 0:
num = (1 << bits) + num
return u(num, bits)
+
def from_part(part):
- """ parse a size-tagged subv part into a bit.
+ """parse a size-tagged subv part into a bit.
>>> from_part((0x12, 2))
Bitfield(0x12, 2)
@@ -405,12 +433,14 @@ def from_part(part):
size = part[1]
return Bitfield(val, int(size))
+
global_slice = slice
-ref_re = re.compile(r'^([^\[+-]+)(?:([+-]\d+))?$')
-slice_re = re.compile(r'^\[(\d+):(\d+)]$')
+ref_re = re.compile(r"^([^\[+-]+)(?:([+-]\d+))?$")
+slice_re = re.compile(r"^\[(\d+):(\d+)]$")
+
def ref(val, default_slice=None):
- """ add a default slice spec to labels if missing.
+ """add a default slice spec to labels if missing.
>>> ref(('label', 'imm12'))
LabelRef('label', 0, ('imm', 12), 11:0)
@@ -451,6 +481,8 @@ def ref(val, default_slice=None):
if default_slice:
expected = default_slice.start - default_slice.stop + 1
- assert expected == ref.size, ValueError("expected {} bit slice, got {}".format(expected, ref))
+ assert expected == ref.size, ValueError(
+ "expected {} bit slice, got {}".format(expected, ref)
+ )
return ref