aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authors-ol <s+removethis@s-ol.nu>2021-05-25 10:53:20 +0000
committers-ol <s+removethis@s-ol.nu>2021-05-25 12:48:49 +0000
commitb22ce8bf080dd9b35ed8c760155f6156390c011c (patch)
treec50d522f799499270b51a6481d5cdb5d33f11eb3
parentadd JUMPING.md (diff)
downloadsubv-b22ce8bf080dd9b35ed8c760155f6156390c011c.tar.gz
subv-b22ce8bf080dd9b35ed8c760155f6156390c011c.zip
implement LabelRef
-rw-r--r--bits.py298
1 files changed, 279 insertions, 19 deletions
diff --git a/bits.py b/bits.py
index 02c24df..34284a2 100644
--- a/bits.py
+++ b/bits.py
@@ -1,13 +1,152 @@
-class Bitfield(object):
- def __init__(self, val, size):
+import re
+from operator import __and__
+from functools import reduce
+
+class WordBase(object):
+ def slice_allowempty(self, range):
+ if not isinstance(range, slice):
+ range = slice(range, range)
+ hi, lo = range.start, range.stop
+
+ if hi < lo:
+ return empty
+
+ return self[range]
+
+ def __and__(self, other):
+ if isinstance(other, WordBase):
+ 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))
+
+ def as_value(self, **kwargs):
+ """ reduce down to a single Bitfield value.
+
+ >>> BitsUnion(Bitfield(0x12, 8), Bitfield(0x3, 4), Bitfield(0x4, 4)).as_value()
+ Bitfield(0x1234, 16)
+ >>> BitsUnion(Bitfield(0x12, 8), LabelRef("test", 0, "imm", slice(11, 4))).as_value(labels={'test': 0xf34f})
+ Bitfield(0x1234, 16)
+ """
+ return reduce(__and__, (part.as_value(**kwargs) for part in self.parts))
+
+ @property
+ def size(self):
+ """ get the total size.
+
+ >>> BitsUnion(Bitfield(0x12, 8), Bitfield(0x3, 4), Bitfield(0x4, 4)).size
+ 16
+ >>> BitsUnion(Bitfield(0x12, 8), LabelRef("test", 0, "imm", slice(11, 4))).size
+ 16
+ """
+ return sum(part.size for part in self.parts)
+
+ def __getitem__(self, range):
+ """ slice a union given hi and lo bit indices.
+
+ >>> BitsUnion(Bitfield(0x12, 8), Bitfield(0x3, 4), Bitfield(0x4, 4))[11:4]
+ Bitfield(0x23, 8)
+ >>> BitsUnion(Bitfield(0x12, 8), LabelRef("test", 0, "imm", slice(11, 4)))[11:4]
+ BitsUnion(Bitfield(0x2, 4), LabelRef('test', 0, ('imm', 32), 11:8))
+ """
+ if not isinstance(range, slice):
+ range = slice(range, range)
+ hi, lo = range.start, range.stop
+
+ size = self.size
+ 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))
+
+ i = size
+ result = Bitfield(0, 0)
+ for part in self.parts:
+ p_size = part.size
+ i -= p_size
+ if i > hi:
+ # still above hi
+ continue
+
+ r_hi = min(p_size - 1, hi - i)
+ r_lo = max(0, lo - i)
+
+ result = result & part[r_hi:r_lo]
+
+ if i <= lo:
+ # past lo
+ break
+
+ return result
+
+ def __and__(self, other):
+ if isinstance(other, BitsUnion):
+ return BitsUnion(*self.parts, *other.parts)
+ elif isinstance(other, WordBase):
+ return BitsUnion(*self.parts, other)
+ raise NotImplementedError()
+
+class Bitfield(WordBase):
+ def __init__(self, val, size, extra=()):
+ if val < 0:
+ min = -(1 << (size - 1))
+ max = -1 - min
+
+ if val > max or val < min:
+ raise ValueError("value {} out of i{} range [{};{}]".format(val, size, min, max))
+
+ if val < 0:
+ val = (1 << size) + val
+
self.val = val
self.size = size
+ self.extra = extra
def __repr__(self):
- return "Bitfield(0x{:x}, {})".format(self.val, self.size)
+ """ format for debugging.
+ >>> Bitfield(0, 8)
+ Bitfield(0x0, 8)
+ >>> Bitfield(16, 8)
+ Bitfield(0x10, 8)
+ >>> Bitfield(-16, 8)
+ Bitfield(0xf0, 8)
+ >>> Bitfield(3, 5)
+ Bitfield(0x3, 5)
+ >>> Bitfield(3, 4, extra=('hello',))
+ Bitfield(0x3, 4, extra=('hello',))
+ """
+ extra = ''
+ if self.extra:
+ extra = ", extra={!r}".format(self.extra)
+ return "Bitfield(0x{:x}, {}{})".format(self.val, self.size, extra)
def __str__(self):
- return "0x{:x}'{}".format(self.val, self.size)
+ """ format as a word.
+ >>> str(Bitfield(0, 8))
+ '00/8'
+ >>> str(Bitfield(16, 8))
+ '10/8'
+ >>> str(Bitfield(3, 8))
+ '03/8'
+ >>> str(Bitfield(3, 5))
+ '03/5'
+ >>> str(Bitfield(3, 4))
+ '3/4'
+ >>> str(Bitfield(3, 4, extra=('hello',)))
+ '3/hello/4'
+ """
+ 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)])
+
+ def as_value(self, **kwargs):
+ return self
def __getitem__(self, range):
""" slice a bitfield given hi and lo bit indices.
@@ -26,7 +165,7 @@ class Bitfield(object):
>>> Bitfield(0xf, 4)[4:0]
Traceback (most recent call last):
...
- ValueError: slice [4:0] out of range of 0xf'4
+ ValueError: slice [4:0] out of range of f/4
>>> Bitfield(0xf, 4)[2:3]
Traceback (most recent call last):
...
@@ -34,7 +173,7 @@ class Bitfield(object):
>>> Bitfield(0xf, 4)[3:-1]
Traceback (most recent call last):
...
- ValueError: slice [3:-1] out of range of 0xf'4
+ ValueError: slice [3:-1] out of range of f/4
"""
if not isinstance(range, slice):
range = slice(range, range)
@@ -49,16 +188,6 @@ class Bitfield(object):
val = (self.val >> lo) & ((1 << size) - 1)
return Bitfield(val, size)
- def slice_allowempty(self, range):
- if not isinstance(range, slice):
- range = slice(range, range)
- hi, lo = range.start, range.stop
-
- if hi < lo:
- return empty
-
- return self[range]
-
def __and__(self, other):
""" concatenate multiple bitfields.
@@ -67,10 +196,141 @@ class Bitfield(object):
>>> Bitfield(0b110, 3) & Bitfield(0b0110, 4) & Bitfield(0b1, 1)
Bitfield(0xcd, 8)
"""
- if not isinstance(other, Bitfield):
- raise NotImplementedError()
+ if isinstance(other, Bitfield):
+ return Bitfield(self.val << other.size | other.val, self.size + other.size)
+ elif isinstance(other, WordBase):
+ return BitsUnion(self, other)
+ raise NotImplementedError()
+
+global_slice = slice
+class LabelRef(WordBase):
+ mode_re = re.compile(r'^(imm|off)(\d+)?$')
+
+ def __init__(self, label, offset, mode, slice=None):
+ if isinstance(mode, str):
+ match = LabelRef.mode_re.match(mode)
+ assert match, ValueError("invalid label reference tag")
+ mode = (match.group(1), int(match.group(2) or 32))
+
+ self.label = label
+ self.mode = mode
+ self.slice = slice or global_slice(self.mode[1]-1, 0)
+ self.offset = offset
+
+ def __repr__(self):
+ """ format for debugging.
+
+ >>> LabelRef("label", 0, ("imm", 32), slice(31,31))
+ LabelRef('label', 0, ('imm', 32), 31:31)
+ >>> LabelRef("label", 4, ("imm", 32), slice(11,0))
+ LabelRef('label', 4, ('imm', 32), 11:0)
+ >>> LabelRef("test", -4, "off32")
+ LabelRef('test', -4, ('off', 32), 31:0)
+ >>> 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)
+
+ def __str__(self):
+ """ format as a word.
+
+ >>> str(LabelRef("label", 0, ("imm", 32), slice(31,31)))
+ 'label/imm32/[31:31]'
+ >>> str(LabelRef("label", 0, ("imm", 32), slice(11,0)))
+ 'label/imm32/[11:0]'
+ >>> str(LabelRef("test", 4, "off32"))
+ 'test+4/off32/[31:0]'
+ >>> str(LabelRef("test", -12, "off"))
+ 'test-12/off32/[31:0]'
+ """
+ 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)
+
+ @property
+ def size(self):
+ return self.slice.start - self.slice.stop + 1
+
+ def as_value(self, labels={}, **kwargs):
+ """ resolve the reference.
+
+ >>> labels = {'test': 0xabcd1234, 'main': 0x8000}
+ >>> LabelRef("test", 0, "imm").as_value(labels=labels)
+ Bitfield(0xabcd1234, 32)
+ >>> LabelRef("test", 0, "imm", slice(15, 0)).as_value(labels=labels)
+ Bitfield(0x1234, 16)
+ >>> LabelRef("test", 0, "imm", slice(31, 12)).as_value(labels=labels)
+ Bitfield(0xabcd1, 20)
+ >>> LabelRef("test", 4, "imm", slice(15, 0)).as_value(labels=labels)
+ Bitfield(0x1238, 16)
+ >>> LabelRef("test", 4, "imm", slice(15, 4)).as_value(labels=labels)
+ Bitfield(0x123, 12)
+ >>> LabelRef("main", 0, "off").as_value(labels=labels, pc=0x6000)
+ Bitfield(0x2000, 32)
+ >>> LabelRef("main", 0, "off").as_value(labels=labels, pc=0x800a)
+ Bitfield(0xfffffff6, 32)
+ >>> LabelRef("main", 0, "off", slice(7, 0)).as_value(labels=labels, pc=0x8012)
+ Bitfield(0xee, 8)
+ >>> LabelRef("main", 0, "off8").as_value(labels=labels, pc=0x8012)
+ Bitfield(0xee, 8)
+ >>> LabelRef("main", 0, "off4").as_value(labels=labels, pc=0x8012)
+ Traceback (most recent call last):
+ ...
+ ValueError: value -18 out of i4 range [-8;7]
+ >>> LabelRef("main", 4, "off", slice(7, 0)).as_value(labels=labels, pc=0x70fa)
+ Bitfield(0xa, 8)
+ """
+ if self.label not in labels:
+ raise ValueError("label '{}' unresolved".format(self.label))
+
+ 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']
+
+ field = Bitfield(value, self.mode[1])
+ return field[self.slice]
+
+ def __getitem__(self, range):
+ """ slice a label using hi and lo bit indices.
+
+ >>> label = LabelRef("label", 0, "imm32", slice(31,0))
+ >>> label.size
+ 32
+ >>> label[31]
+ LabelRef('label', 0, ('imm', 32), 31:31)
+ >>> label[11:0]
+ LabelRef('label', 0, ('imm', 32), 11:0)
+ >>> label[11:0].size
+ 12
+ >>> label[12:1]
+ LabelRef('label', 0, ('imm', 32), 12:1)
+ >>> label = LabelRef("abc", 0, "off32", slice(31,12))
+ >>> label.size
+ 20
+ >>> label[9:0]
+ LabelRef('abc', 0, ('off', 32), 21:12)
+ >>> label[9:2]
+ LabelRef('abc', 0, ('off', 32), 21:14)
+ >>> label[20:8]
+ Traceback (most recent call last):
+ ...
+ ValueError: slice [20:8] out of range of abc/off32/[31:12]
+ """
+ if not isinstance(range, slice):
+ range = slice(range, range)
+ hi, lo = range.start, range.stop
+
+ 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))
- return Bitfield(self.val << other.size | other.val, self.size + other.size)
+ 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)